Author Topic: My journey into Z80 and CPC  (Read 524 times)

0 Members and 1 Guest are viewing this topic.

Offline issalig

  • CPC464
  • **
  • Posts: 46
  • Country: es
  • Liked: 20
  • Likes Given: 47
My journey into Z80 and CPC
« on: 20:55, 17 May 21 »
Hi all, I am learning z80 for CPC and I am documenting it. It is a "work in progress" document and I plan to incude RSX calls, foreground/background ROMs, and some programming for USIFAC.

If you are a noob like me it will help you, if you are a master you can help to improve it (I am sure it has a lot of errors).https://github.com/issalig/cpc/blob/main/doc/cpcz80adventures.md
« Last Edit: 20:56, 30 May 21 by issalig »

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.788
  • Country: gb
    • redbox
  • Liked: 406
  • Likes Given: 287
Re: My journey into Z80 and CPC
« Reply #1 on: 22:51, 17 May 21 »
I am learning z80 for CPC and I am documenting it. It is a "work in progress" document and I plan to incude RSX calls, foreground/background ROMs, and some programming for USIFAC.

The link above is incorrect, it should be: https://github.com/issalig/misc/blob/master/cpcz80adventures.md

Great to see you're learning Z80.  Prepare for your first optimisation lesson...

Code: [Select]
        TXT_OUTPUT      equ &bb5a   

        org      &1200          ; our code will start at &1200

main:                         
        ld      hl,message      ; load address of string in HL
        call    printString     ; print it
        ret

printString:
        ld      a,(hl)          ; load char index stored in HL into A
        or      a               ; if 0 then Z flag will be set
        ret     z               ; returns if Z flag is set
        inc     hl              ; hl=hl+1
        call    TXT_OUTPUT      ; call TXT_OUTPUT
        jr      printString

message:
        defb    "Hello World!",0

First thing is you need to put an extra RETurn in your main section so the assembly routine can return to BASIC or whatever other routine.  This is a good habit to get into.

Secondly, I've replaced the termination of the string with 0 instead of 255.  The CP 255 has been replaced with OR A (which is faster).  Can you work out how this works?  :)
« Last Edit: 22:54, 17 May 21 by redbox »

Offline issalig

  • CPC464
  • **
  • Posts: 46
  • Country: es
  • Liked: 20
  • Likes Given: 47
Re: My journey into Z80 and CPC
« Reply #2 on: 23:34, 17 May 21 »
First thing is you need to put an extra RETurn in your main section so the assembly routine can return to BASIC or whatever other routine.  This is a good habit to get into.

Secondly, I've replaced the termination of the string with 0 instead of 255.  The CP 255 has been replaced with OR A (which is faster).  Can you work out how this works?  :)
[EDIT] @redbox, I like the main structure with final ret, but as I already had one ret I do not see why I need an extra one.

So OR r takes 4 cycles vs CP n that takes 7 cycles like OR n   (http://map.grauw.nl/resources/z80instr.php)

Thanks for your advices, I will also include this information to the guide.

« Last Edit: 10:35, 18 May 21 by issalig »

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.788
  • Country: gb
    • redbox
  • Liked: 406
  • Likes Given: 287
Re: My journey into Z80 and CPC
« Reply #3 on: 13:26, 18 May 21 »
I like the main structure with final ret, but as I already had one ret I do not see why I need an extra one.

The Chibiakumas routine relies on using the firmware jump (JP PrintChar) in the NewLine subroutine to return you to BASIC.  This is bad practice and will only cause you headaches later on!

Also, the NewLine routine is redudant because you can include the control codes for CR and LF in your message string.

Here's an example of calling the printString subroutine twice - if you don't include the RET here after calling them, the printString routine would be run again after main had printed the message2 string:

Code: [Select]
        TXT_OUTPUT      equ &bb5a   

        org      &1200          ; our code will start at &1200

main:                         
        ld      hl,message      ; load address of string in HL (contains CR and LF in string)
        call    printString     ; print it
        ld      hl,message2
        call    printString
        ret                     ; exit main loop (to BASIC or other z80 routine)

printString:
        ld      a,(hl)          ; load char index stored in HL into A
        or      a               ; if 0 then Z flag will be set
        ret     z               ; returns if Z flag is set
        inc     hl              ; hl=hl+1
        call    TXT_OUTPUT      ; call TXT_OUTPUT
        jr      printString

message:
        defb    "Hello World!",13,10,0
message2:
        defb    "Next Line!",0

So OR r takes 4 cycles vs CP n that takes 7 cycles like OR n

Yes, it's quicker and also 1 byte smaller.  OR r when r is 0 has the effect of setting the Z flag so we can make use of RET Z.

Offline m_dr_m

  • CPC6128
  • ****
  • Posts: 261
  • Country: se
  • http://orgams.wikidot.com/
    • OrgaMS!
  • Liked: 172
  • Likes Given: 187
Re: My journey into Z80 and CPC
« Reply #4 on: 14:38, 18 May 21 »
So OR r takes 4 cycles vs CP n that takes 7 cycles like OR n 
Z80 cycles are relevant up to a certain point. For CPC specifically, oddly enough, I recommend:
https://64nops.wordpress.com/2021/01/13/perfectly-accurate-z80-flags-and-cpc-timing/

Beware premature optimisation anyway!  The `or a` is fine since idiomatic anyway.



Offline issalig

  • CPC464
  • **
  • Posts: 46
  • Country: es
  • Liked: 20
  • Likes Given: 47
Re: My journey into Z80 and CPC
« Reply #5 on: 14:59, 18 May 21 »
The Chibiakumas routine relies on using the firmware jump (JP PrintChar) in the NewLine subroutine to return you to BASIC.  This is bad practice and will only cause you headaches later on!
...

Here's an example of calling the printString subroutine twice - if you don't include the RET here after calling them, the printString routine would be run again after main had printed the message2 string:

Thanks, now I got it,   :doh: So if we hare here is because someone CALLed and  RET is the best friend of CALL, so they should be always go together :).
If a RET is missing, PC will increment and execute code until a RET is found and behavior will be unexpected. Doesn't it?

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.788
  • Country: gb
    • redbox
  • Liked: 406
  • Likes Given: 287
Re: My journey into Z80 and CPC
« Reply #6 on: 15:11, 18 May 21 »
Beware premature optimisation anyway!  The `or a` is fine since idiomatic anyway.

I agree!  It was meant more as tongue in cheek because it's common practice (null terminated string).

So if we hare here is because someone CALLed and RET is the best friend of CALL, so they should be always go together :).

I really like that way of saying it, RET is the best friend of CALL.  I will use that saying in future!

If a RET is missing, PC will increment and execute code until a RET is found and behavior will be unexpected. Doesn't it?

Exactly.

Offline andycadley

  • Supporter
  • 6128 Plus
  • *
  • Posts: 979
  • Liked: 479
  • Likes Given: 79
Re: My journey into Z80 and CPC
« Reply #7 on: 17:05, 18 May 21 »
Thanks, now I got it,   :doh: So if we hare here is because someone CALLed and  RET is the best friend of CALL, so they should be always go together :) .
If a RET is missing, PC will increment and execute code until a RET is found and behavior will be unexpected. Doesn't it?


Strictly speaking, the CPU doesn't care if CALLs and RETs pair up. The issue is really only what is theoretically on the top of the stack. If you CALL a routine, the return address gets implicitly PUSHed onto the stack and at some point you need to either POP it off, manipulate the stack pointer or do a RET (which implicitly POPs the result).


It's not uncommon, if the last thing before a RET is a CALL, to optimise the process by replacing the CALL with a JP and relying on the RET at the end of the second routine to handle effectively RETurning from both routines. In cases of tail recursion, this can in fact be critical to avoiding stack overflow.


Abusing the stack is generally a more advanced Z80 technique though, so not something you want to rush into.