A little help with a simple Hello World on SC108 Z80 system please

103 views
Skip to first unread message

Mark Durham

unread,
Jun 15, 2024, 1:44:32 PMJun 15
to RC2014-Z80
Hi all,

I have a small system using some of Steve Cousins boards: SC108 CPU + SC110 Serial/Timer + SC129 Digital I/O and I'm trying to get a simple hello world program written in C running using z88dk.

I'm almost there but just can't figure out what the problem may be. I can build my code and it loads without error. But when I execute it, I get most of the intended character string output and then the system resets like this:

Small Computer Monitor - RC/Z80
*Ready

*g8000
Hello from z[8

Small Computer Monitor - RC/Z80
*

My hello.c file simply consists of:
#include <stdio.h>

int main()
{
    printf("Hello from z88dk!\n");
}


I have a monitor.c file which I took from the z88dk classic homebrew quickstart guide and hopefully correctly modified it to make an API call into the SCM API function $02 to output a character. The file consists of:
#include <stdio.h>

int fputc_cons_native(char c) __naked
{
__asm
    pop     bc  ;return address
    pop     hl  ;character to print in l
    push    hl
    push    bc
    ld      a,l
    ld      c,$02
    rst     $30
__endasm;
}
 
And I'm building the code with the command line:
zcc +z80 -clib=classic hello.c monitor.c -pragma-define:CRT_ORG_CODE=0x8000 -create-app -m -Cz--ihex

The code builds without error and I can load it fine. But when I execute it, the system resets after 12 correct characters. It's always 12 characters too.

It's obviously something very simple I'm missing - likely my inexperience with z88dk!

I'd really like to get this going as I've also got one of Steve's SC104 SIO/2 serial modules that I've modified to include an ATTINY84A to generate clock and discrete signals for the SIO/2 Channel A in synchronous mode so that the SIO/2 can interface with SPI devices. So far I've successfully talked to a Microchip 25LC256 EEPROM but I'd really like to see if it all still hangs together with FatFs and a MicroSD card. Hence the need to get a build environment setup for z88dk.

Thanks for any assistance.

Bill Shen

unread,
Jun 15, 2024, 2:03:36 PMJun 15
to RC2014-Z80
Wonder if you need to wait for serial transmit to complete before exiting the hello world program?  What happen if you print “hello world” multiple times?  Does the problem occurs only at the last “hello”?

Mark Durham

unread,
Jun 15, 2024, 2:16:44 PMJun 15
to RC2014-Z80
Hi Bill,

I modified the code to print out the phrase 3 times and it resets at the same point on the first "Hello from z88dk".

I took a look at the API documentation and it says:
API function $02, output character
Parameters: A = Character to output to console
Returns: A = Character output to console

This function outputs the specified character to the current console output device, usually a serial terminal.
The ASCII value of the character to be output is passed in the A register.
The function does not return until the character has been output.


I did wonder if it may be because I've not configured/initialised the stack in my build batch file and my code is possibly trampling on something important but as my code doesn't "return" to the monitor I'm not so sure that's the issue. 

Steve Cousins

unread,
Jun 15, 2024, 4:19:58 PMJun 15
to RC2014-Z80
Hi Mark,

I'm not familiar with z88dk so I can only guess here, but it might be worth preserving the registers around the API call. See below. 

SCM API calls can alter the value of registers af, bc, de, hl. Perhaps z88dk's monitor functions assume some of the registers are preserved.

Steve

push bc
push de
push hl

ld a,l
ld c,$02
rst $30

pop hl
pop de
pop bc

Derek Cooper

unread,
Jun 16, 2024, 3:10:33 AMJun 16
to RC2014-Z80

I may be wrong here but i thought rst 30 was effectively a call, so should you not have a RET after it otherwise the stack will get out of sync.

Have fun

Derek

Mark Durham

unread,
Jun 16, 2024, 5:15:54 AMJun 16
to RC2014-Z80
Steve, I tried saving and restoring the registers but without success.

Derek, with regards to RST $30, I thought it was the equivalent of CALL $0030 but requiring fewer bytes. I'm fairly sure with my limited knowledge that at the end of RST $30 there would be a RET to get back to the line of code after the RST $30 call.

What I'm not so confident about is the z88dk environment. I tried to see what the implications of the __naked attribute were but I guess I'm using the wrong search keywords. Perhaps it's indicating that registers are not saved on the stack, or maybe that the C function doesn't have a RET at the end of it - possibly because the code is intended to be inserted inline rather than called...

The z88dk web page (see initial post) has a sample routine like this:
int fputc_cons_native(char c) __naked
{
__asm
    pop     bc  ;return address
    pop     hl  ;character to print in l
    push    hl
    push    bc
    ld      a,l
    call    $1000
__endasm;
}

I'm clearly missing something very obvious and I suspect it's staring me right in the face but I can't see it for looking.

Gordon Wainwright

unread,
Jun 16, 2024, 5:20:27 AMJun 16
to RC2014-Z80
Hello
Yes, I can confirm a stack imbalance around the RST30 causing the crash. Why only after the 12th occurrence? I ask myself.

I spent a lot of time recently on this page:  https://github.com/z88dk/z88dk/wiki/Classic--Homebrew, trying to work out how to use bare-metal serial io on my SC720.

My code runs in RAM from @ 0x8000 via SCM, so it made sense to use the SCM APIs.

My hardware.asm was modified as shown.
Also included is my main.c and the code compile line.

My z88dk tools run on Apple silicon hardware; the package was simply downloaded and unzipped. No building from the source was required.

Gordon

SECTION code_user

PUBLIC fputc_cons_native
PUBLIC _fputc_cons_native

PUBLIC fgetc_cons
PUBLIC _fgetc_cons


fputc_cons_native:
_fputc_cons_native:
; lowercase is the original code
;  out     (1),a  removed
; and uppercase code added,  taken from an SCM demo app

    pop     bc  ;return address
    pop     hl  ;character to print in l
    push    hl
    push    bc
    ld      a,l
    PUSH BC             ;Preserve registers
    PUSH DE
    PUSH HL
    LD   C,0x02         ;API 0x02 = Character output
    RST  0x30           ;Call API
    POP  HL             ;Restore registers
    POP  DE
    POP   BC
    ret

fgetc_cons:
_fgetc_cons:
    in      a,(1)
    ld      l,a     ;Return the result in hl
    ld      h,0
    ret



#include <stdio.h>


//https://github.com/z88dk/z88dk/wiki/Classic--Pragmas

// 0x10002 return to caller

//zcc +z80 -clib=classic world.c hardware.asm -pragma-define:CRT_ORG_CODE=0x8000 -pragma-define:CRT_ON_EXIT=0x10002 -create-app -m -Cz--ihex
//z88dk-dis -o 0x8000 -x a.map a.rom

int main(void)

{

     puts_cons("Hello from z88dk!\n");

}
On Sunday 16 June 2024 at 08:10:33 UTC+1 Derek Cooper wrote:

Mark Durham

unread,
Jun 16, 2024, 6:51:01 AMJun 16
to RC2014-Z80
Hi Gordon,

Thanks for the reply. I should have gone with the pure assembler code variant that was on the same Classic Homebrew page. That worked perfectly so I guess there is something going on with the C wrapper that I'm missing. I too thought about stack corruption but couldn't explain why it's always the 12th character. 

..

Ah! That's why. I missed a parameter out of the compilation command. I forgot to include the -pragma-define:REGISTER_SP= bit so the SP was probably pointing somewhere sensitive. I figured it was something obvious.

At least now I have text i/o, I can try out the FatFs library and see if my SC104 modification really does handle SPI comms to a MicroSD card.

Reply all
Reply to author
Forward
0 new messages