Error creating a compile for a zxn dot command

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

This next one uses z88dk's print functionality instead of the ROM's. This means you can print strings that are stored in the dot command without copying them to ram first. So we return to the regular dot command in 8k-16k limited to 8k size. The startup value will 30 in the compile so that z88dk's stdio driver will use RST$10 for printing.

A zpragma.inc file is going to be added to the compile this time to specify that printf should support "%B" (16-bit binary number) and "%s" (0-terminated string). "%B" is not supported in the default library build so that's why this pragma file is needed. The example is a little more complicated than I wanted it to be for this reason but it's essential for the joke.

"zpragma.inc"

Code: Select all

// determine which converters printf supports

#pragma printf = "%B %s"
"zzz3.asm"

Code: Select all

; zcc +zxn -vn -startup=30 -clib=sdcc_iy zzz3.asm -o zzz3 -pragma-include:zpragma.inc -subtype=dot -create-app
; copy "ZZZ3" to /bin on the sd card and run with ".ZZZ3"

SECTION code_user

PUBLIC _main

EXTERN asm_write, asm_puts, asm_printf

_main:

   ; asm_write prints a buffer
        
        ld hl,1  ; use stream attached to fd=1 (normally stdout)
        ld de,msg
        ld bc,msg_end - msg

        call asm_write

   ; asm_puts prints a 0-terminated string to stdout
        
        ld hl,msg
        call asm_puts

   ; asm_printf prints a complex string
        ; the sdcc library requires parameters to be pushed in right to left order on the stack
        
        ld hl,string
        push hl                     ; %s - zero terminated string
        ld hl,2
        push hl                     ; %B - binary number
        ld hl,format
        push hl                     ; format string
        call asm_printf
        pop af
        pop af
        pop af                      ; clean up stack

   ; report success to esxdos

   ld hl,0
   ret

msg:

   defm "Hello World!\n"

msg_end:

   defb 0    ; 0-terminate string for asm_puts

format:

   defm "There are %B types of people in the world:\nThose who understand %s and those who don't.\n", 0

string:

   defm "binary", 0
I've printed three ways here.

asm_write ( https://github.com/z88dk/z88dk/blob/mas ... te.asm#L24 ) is very much like basic's PR_STRING in that a buffer of ascii text is given along with a length. This is a very lightweight print routine.

asm_puts ( https://github.com/z88dk/z88dk/blob/mas ... m_puts.asm ) is also fairly lightweight and will print a 0-terminated string.

asm_printf ( https://github.com/z88dk/z88dk/blob/mas ... tf.asm#L29 ) is C's monster print function and as you can see it can insert numbers, strings, etc into some output text as it is printed. This is an expensive function (in size) but it's quite nice to use if you need its functionality. The pragma in the zpragma.inc file limits printf to implement only converters that your program uses and this is helpful in keeping its size small.

The z88dk library is quite large and all you have to do to use functions in it is to declare the function EXTERN and then call it. The EXTERN directive tells the linker to find the code either in another file in your project or in the library.

In this case the advantage to using z88dk's io is that it doesn't use the rom (except for RST$10 in this particular startup) so that it's perfectly fine to print a string stored in the dot command itself. The downside is that because the rom is not used, the dot command will be bigger. "asm_printf" is a big contributor to this binary (probably 1500+ bytes) so if you don't need it, not using it will make the dot much smaller.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I should add the return value in HL is specific to z88dk since z88dk's crt looks at HL to generate the error for esxdos.

If making a bare binary without crt or using another assembler, the return value must be what esxdos expects:
https://github.com/z88dk/z88dk/blob/mas ... sm.m4#L241
castingflame
Member
Posts: 15
Joined: Wed Apr 18, 2018 12:54 pm

Post by castingflame »

Turns out that I was sooooooo close :cool:


I compared our examples and could not see what you were doing fundamentally different (in your first example). Turns out, I just needed to retry my own last example as as a standard dot command inside ZEsarux or the hardware. I had made the correct change then never tried it as a standard dot command! As a very wise man once said, doh! I had been using the

Code: Select all

msg: defw "Hello World!"
in all my personal code examples but simplified it to one byte.

The thing I did not do is

Code: Select all

ld hl,0   ; report success to esxdos
I have just read your post about the specifics of HL for z88dk or esxDOS.


I will go over everything else you have written and create examples for each. You have been a massive help and can't thank you enough. Hopefully tomorrow I will be putting it all to use trying to talk to my hardware project.


Well its a good job I purchased some fancy coloured polypocket folders to put all this great info in! The z88dk one is nearly full already :)

Okay, another cuppa and next example time...
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

castingflame wrote:The thing I did not do is

Code: Select all

ld hl,0   ; report success to esxdos
I have just read your post about the specifics of HL for z88dk or esxDOS.
Yes that will do it. You have to be careful with the return value to esxdos because one combination of register settings (or HL > 255 with z88dk's crt) will treat the return value as a pointer to a user-supplied error string that is then printed to screen. The error string must end with the last char having bit 7 set so this could lead to some weird behaviour.

The one thing I didn't do is replace PR_STRING with some code that just looped and used RST$10 to print each character.

Something like:

Code: Select all

MY_PRSTRING:

   ; de = msg
   ; bc = len > 0

   ex de,hl

prstring_loop:

   push bc
   push hl

   ld a,(hl)
   rst $10

   pop hl
   pop bc

   cpi   ;; hl++, bc--
   jp pe, prstring_loop

   ret
There I get to show "cpi" used in an idiomatic way to test if bc==0 yet.

Anyway good to hear you are making progress.
castingflame
Member
Posts: 15
Joined: Wed Apr 18, 2018 12:54 pm

Post by castingflame »

Today I have been attempting to compile a 3rd party standard dot command, ... again. It was written for sjasmplus and I can compile it myself which works fully. I have been trying on and off for the last 2 weeks to get it to work correctly with z88dk, but even after the education, still no joy.:mad::(:(:(


This is the format of how the code is laid out. (Name changed to protect the innocent) ...

Code: Select all

;Compile:     .\sjasmplus  dotty.asm

DEVICE   ZXSPECTRUM48

        output "Dotty"

        org 0x2000

        
PORT1      equ 0x00
PORT2      equ 0x00 
PORT3      equ 0x00


MAIN:
         ld a,h
...
....
...
  


Msg1:    db "Hello",13
             db "World!",13,13,0
                                        

SUN:                defb 0        
MOON:      defb 0
Which I have changed to

Code: Select all

; zcc +zxn -vn -startup=31 -clib=sdcc_iy dotty.asm -o dottyd -subtype=dot -create-app


SECTION code_user        

        
PORT1      equ 0x00
PORT2      equ 0x00 
PORT3      equ 0x00


PUBLIC _main                        

_main:
        ld a,h

...


Msg1:    defm "Hello",13
             defm "World!",13,13,0
                                        

SUN:                defb 0        
MOON:      defb 0
to work as a standard dot command. I have also changed the 'db' statements to 'defm' as the zcc falls over at that point. Is that the correct change? The command does execute but it goes straight into its error condition. It displays messages correctly but then the bottom line of the screen (message line) on the Next is filled with a few '?????????' and other random characters. I had that in my own code this week but that feels like a lifetime ago.

I then tried it as an extended dot command with the data above 0x8000 like this ...


Code: Select all

; zcc +zxn -vn -startup=31 -clib=sdcc_iy dotty.asm -o dottyx -subtype=dotx -create-app


SECTION code_dot        ;place in dot memory 8k-16k (0x2000 - 0x3FFF)


PUBLIC _main                        

_main:
        ld a,h

...



SECTION data_dtx         ;0x8000 / 32768

Msg1:    defm "Hello",13
             defm "World!",13,13,0
                                        

SUN:                defb 0        
MOON:      defb 0


PORT1      equ 0x00
PORT2      equ 0x00 
PORT3      equ 0x00
I really do not want to start using sjasmplus at this point. If this is all a bit useless and you need the original source and my versions of it A.A. you know where to ask! I do not wish to post them here.

Thanks as ever.:|
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Offline we got the sample code to work.

The main issue with dot commands written for other assemblers is that those commands are "bare" in the sense there is no code running before or after the dot command is started. With the z88dk dot command subtypes, the crt is present and roughly does this:

1. save basic state (SP, IY, HL')
2. process the command line (default is into argc/argv)
3. call _main (the dot command entry point)
4. restore basic state (SP,IY,HL')
5. return to esxdos with error indicated in HL

1&4 mean code does not have to worry about what registers basic requires to be unaltered (unless the rom is called). It also allows exit from the dot command at any point and the stack will be properly unwound. (exit can occur by jumping to "asm_exit" with hl=error_code, in which case the exit stack will be run, or more directly by jumping to "__Exit" in the crt with hl=error_code which will not run any exit stack. The exit stack is 0 size by default but you can create one to register functions to run before exiting the program).

5 changes how error codes are communicated to esxdos. With z88dk's crts, exit from the dot command has HL=0 to indicate no error, 0 < HL < 256 selects a canned esxdos error message (see https://github.com/z88dk/z88dk/blob/mas ... os.m4#L108 ) and HL > 256 has hl pointing at a custom error string with last char having bit 7 set. Bare esxdos requires this: https://github.com/z88dk/z88dk/blob/mas ... sm.m4#L241 (the comments in lines 241-243).

The crt is important because it allows us to implement things like dotx, dotn and parse the command line more completely. This will be especially useful when we start to evaluate basic expressions on the command line so that variables from basic can be used as arguments.

The changes made to the pure dot command code were:

1.
A pragma file was added to tell the z88dk crt that the command line should be passed from esxdos unaltered. By default the crt will parse the esxdos command line into argc,argv.

zpragma.inc

Code: Select all

// pass along original esxdos command line

#pragma output CRT_ENABLE_COMMANDLINE = 2
To use the pragma, "-pragma-include:zpragma.inc" was added to the zcc line.

2.
At each return point from the pure dot command, the return status was placed in hl prior to return. In this case, the program always returned with ok so "ld hl,0" was added prior to these return points. If it's more convenient, a jump to library function "error_znc" ( https://github.com/z88dk/z88dk/blob/mas ... or_znc.asm ) can also be done. This function sets hl=0 and resets the carry flag before returning. You can also jump to "error_znc - n" where n=1..3 to clear up to three items from the stack.

The rest were asm syntax differences. The original source was from sjasm so colons had to be added after labels, "db" was changed to "defm" or "defb" depending on whether strings were there. "org 0x2000" was replaced with "SECTION code_user" to place the code in a section in the dot's memory map. Lastly the entry point was renamed "_main" and made public with "PUBLIC _main".
Post Reply