Z80 things that weren't obvious that you wish you'd known earlier

The place for codemasters or beginners to talk about programming any language for the Spectrum.
User avatar
Morkin
Bugaboo
Posts: 3277
Joined: Mon Nov 13, 2017 8:50 am
Location: Bristol, UK

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Morkin »

Laughably simple things that might have convinced me to continue trying to learn assembly in the 80s and not giving up after 10 minutes:

LD isn't a free-for-all, e.g. you can't do LD BC,DE

You can use INC H to move down one pixel on screen

How quick it is compared to BASIC
My Speccy site: thirdharmoniser.com
Ralf
Rick Dangerous
Posts: 2288
Joined: Mon Nov 13, 2017 11:59 am
Location: Poland

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Ralf »

Some more general, not technical stuff:

1. You need really to write a lot of comments in your code. Don't listen to guys who claim that
your code should be "self-documenting". They haven't written in their lives a single line in assembler.
And they are usually trying to justify that they are just lazy ;)

2. Formatting of your code is also important

3. You can optimize your code by speed, by size but also by readability. Sometimes it's better to choose
a bit slower and longer but easier solution without all these cool tricks which you won't understand yourself
after a month.

4. Don't be overambitious. Start projects that you will be able to finish.
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by presh »

Ralf wrote: Tue Apr 20, 2021 11:27 am Some more general, not technical stuff:

1. You need really to write a lot of comments in your code. Don't listen to guys who claim that
your code should be "self-documenting". They haven't written in their lives a single line in assembler.
And they are usually trying to justify that they are just lazy ;)

...

3. You can optimize your code by speed, by size but also by readability. Sometimes it's better to choose
a bit slower and longer but easier solution without all these cool tricks which you won't understand yourself
after a month.
100% agree with these!

I always start by writing the most readable code possible, then optimising once I know it's working (and keeping a backup of the original, just in case!)

Also I tend to plan my code out with pseudocode comments rather than diving straight in. Then it's "just" a case of translating the English into Z80. When something goes wrong, it's much easier to spot logic errors by being able to read what should happen at each point, instead of trying to figure it out from the (potentially erroneous) code.

Having a list of input, output and trashed registers at the top of CALLable subroutines is a huge time saver too. Much easier than digging through the code every time & trying to figure out which parameters it's expecting, what needs PUSH/POPing either side of the CALL, etc.
User avatar
lister_of_smeg
Microbot
Posts: 145
Joined: Thu Nov 16, 2017 1:44 pm

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by lister_of_smeg »

Turtle_Quality wrote: Mon Apr 19, 2021 11:35 pm The lack of BIT A,r has always been a bit annoying, and I now see that my self modifying code solution to build the SET, BIT or RES command to order, well it's slower than doing up to 7 RLCA followed by an AND or OR instruction , as well as being harder to follow on any Assembler / Debugger. What is the fastest way to do these ?
Something like this?

Code: Select all

	ld	h, table/256
	and	(hl) 
		
	align	256
table: 	db	1,2,4,8,16,32,64,128
Bit position in L
and (hl) for BIT
or (hl) for SET
or (hl); xor (hl) for RES

Or am I misunderstanding what you mean by BIT/SET/RES A,r? (I'm assuming r is a register containing 0-7)
User avatar
Turtle_Quality
Manic Miner
Posts: 506
Joined: Fri Dec 07, 2018 10:19 pm

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Turtle_Quality »

Perfect thanks
Definition of loop : see loop
User avatar
ketmar
Manic Miner
Posts: 713
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by ketmar »

in my youth i didn't had a proper manual, only a list of commands, so i was sure that most commands have equal timings, with memory r/w being very slightly slower than others. and i felt so smart seeing blocks of LDI in games and thought: "heh, they don't even know about LDIR. what a morons!" because CPU doesn't need to advance PC and read LDIR each time, and it must be faster than alot of LDI, right?
Ralf
Rick Dangerous
Posts: 2288
Joined: Mon Nov 13, 2017 11:59 am
Location: Poland

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Ralf »

in my youth i didn't had a proper manual, only a list of commands
I'm not so sure if proper manual is required. List of commands with timings and effects would be really enough.

Personally I would advise to avoid the well-known Rodnay Zaks book:
https://archive.org/details/Programming ... 1/mode/2up

In the past I have seen some people treating it like a kind of "Bible". Well, it isn't:

- the style is too dry and not suitable for a beginner
- the content is too close to hardware for a programmer's needs
-it lacks any references to Zx Spectrum

So if you want to learn asm, don't start with Zaks.
User avatar
ketmar
Manic Miner
Posts: 713
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by ketmar »

Ralf wrote: Tue Apr 20, 2021 4:46 pm
in my youth i didn't had a proper manual, only a list of commands
I'm not so sure if proper manual is required. List of commands with timings and effects would be really enough.
sure, but there weren't any timings. only the table with opcodes and mnemonics. i knew enough about computers to make sense of it, but was thinking that all operations were done in one or two ticks, regardless of complexity, and that opcode fetching is free.
User avatar
Bedazzle
Manic Miner
Posts: 305
Joined: Sun Mar 24, 2019 9:03 am

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Bedazzle »

Morkin wrote: Tue Apr 20, 2021 9:58 am You can use INC H to move down one pixel on screen
It isn't for the last byte of 8x8 cell.

P.S.
For the topic starter:
- register R for the random number generation must be used with care; given numbers are far from random - it is constantly increasing, and bit 7 is always the same
Tommo
Dizzy
Posts: 92
Joined: Sat Mar 20, 2021 3:23 pm

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Tommo »

For me it's mainly non-Z80-specific stuff that is nevertheless useful for low-budget computing in general, none of it especially original:

(a+b)^2 - (a-b)^2 = (a^2 + b^2 + 2ab) - (a^2 + b^2 - 2ab) = 4ab, so if you have 256 bytes to spare then a lookup table of x^2/4 lets you multiply 8-bit numbers very quickly.

Linear feedback shift registers (i.e. LFSRs) provide cheap random-number generators. E.g. (extemporaneous, please forgive errors):

Code: Select all

    CCF
    RRA
    JR NC, done
    
    XOR $AF
.done
If you seed A with 0 that'll have exactly one state. But if you seed A with anything other than 0, that'll proceed through the other 255 possible numbers in a non-obvious order. If you don't like $AF, use any of the other constants from here to ensure you hit all 255 states. You can seed with (R << 1) | 1 or something like that before each game.

If you have a large number of independent objects on screen then keep them sorted by x to decrease collision detection costs. In your sorted list do the equivalent of:

Code: Select all

    for(i = 0; i < num_objects; i++) {
        for(c = i+1; c < num_objects; c++) {
            if(obj[c].left > obj[i].right) break;
            
            // Collision test obj[c] and obj[j] here.
        }
    }
... and use insertion sort to maintain your sorted list, because the sorting on frame n is probably also the proper sorting on frame n+1, or if not then it'll be very close, and insertion sort will detect that in linear time and is likely to fix discrepancies relatively quickly.

If your memory footprint allows it, you can use the Minkowski sum of two objects to do pixel-perfect collision detection with a single lookup. It's a separate lookup table for each pair of objects though, and if the objects are (a, b) and (c, d) in size then the table needs to contain (a+c)*(b+d) bits — if you can't be bothered with the mathsy stuff, just figure that it's a lookup table of whether a collision has happened given the relative position of the sprites, after you've done the basic bounding box stuff.

On the plus side, it's an easy thing to compute at runtime so if you have a flick-screen JSW-style game in mind then you need reserve only a few slots for those tables and can calculate just the ones you need during each screen transition.
User avatar
Morkin
Bugaboo
Posts: 3277
Joined: Mon Nov 13, 2017 8:50 am
Location: Bristol, UK

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Morkin »

Bedazzle wrote: Wed Apr 21, 2021 1:53 pm
Morkin wrote: Tue Apr 20, 2021 9:58 am You can use INC H to move down one pixel on screen
It isn't for the last byte of 8x8 cell.
Yep, good point..! :oops:
My Speccy site: thirdharmoniser.com
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by presh »

HL is much more useful than BC/DE.

I don't know why it took me so long to realise this, but anything you can do with a register can be done with (HL)
and conversely, none of the following can be done with (BC) or (DE)

e.g.

Code: Select all

LD D, (HL)
LD (HL), number
OR (HL)
INC (HL)
BIT b, (HL)
SET b, (HL)
And because some instructions modify the value at the same time - e.g. INC (HL) - it allows for faster AND smaller code. Consider:

Code: Select all

LD HL, lives    ; 10/3
DEC (HL)        ;  4/1
JP Z, game_over ; 10/3
; 24 Ts, 7 bytes
vs

Code: Select all

LD A, (lives)   ; 13/3
DEC A           ;  4/1
LD (lives), A   ; 13/3
JP Z, game_over ; 10/3
; 40Ts, 10 bytes
HL is also quicker than BC/DE for certain tasks:

Code: Select all

LD HL, (nn) ; 16 Ts
LD BC, (nn) ; 20 Ts
LD DE, (nn) ; 20 Ts
More obviously is the ability to do 16-bit addition / subtraction (though as already mentioned, you have to use ADC instead of ADD if you need the Z, S or P/V flags!) - again, faster AND smaller.

e.g. with flags:

Code: Select all

OR A        ;  4/1 (reset carry)
ADC HL, DE  ; 15/2
; 19 Ts, 3 bytes
or even quicker if you don't need the flags:

Code: Select all

ADD HL, DE  ; 11/1
; 11 Ts, 1 byte
vs

Code: Select all

LD A, L     ; 4/1
ADD A, E    ; 4/1
LD L, A     ; 4/1
LD A, H     ; 4/1
ADC A, D    ; 4/1
LD H, A     ; 4/1
; 24Ts, 6 bytes
In fact its ownly downside (and something definitely worthy of the thread title, from personal experience!) seem to be interoperability with IXH and IXL (or IYH / IYL):

Code: Select all

; LD BC, IX
LD B, IXH   ; ok
LD C, IXL   ; ok

; LD HL, IX
LD H, IXH   ; nope!
LD L, IHL   ; nope!

; instead, have to do:
LD D, IXH
LD E, IHL
EX DE, HL

; or, if DE is unavailable:
LD A, IXH
LD H, A
LD A, IXL
LD L, A
User avatar
Turtle_Quality
Manic Miner
Posts: 506
Joined: Fri Dec 07, 2018 10:19 pm

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Turtle_Quality »

presh wrote: Tue May 04, 2021 5:34 pm HL is much more useful than BC/DE.

True that, though I think all those can be done with IX / IY also, slower but with the displacement byte when it's indirect ; such as OR (IX+d)
Definition of loop : see loop
User avatar
Bubu
Manic Miner
Posts: 542
Joined: Fri Apr 02, 2021 8:24 pm
Location: Spain

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Bubu »

Z80 things that weren't obvious that you wish you'd known earlier:

When I started programming with Z80, I thought that using IM2 would be a symbol of professional developing... but I never found a real use or utility to implement interrupts: it makes code run so slow, avoids to have sounds meanwhile playing... Never understood the reason for using IM2. My Anteater game doesn't use it at all ( well, only for detecting the coin at the beginning :D )
If something works, don't touch it !!!! at all !!!
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Ast A. Moore »

presh wrote: Tue May 04, 2021 5:34 pm

Code: Select all

; LD BC, IX
LD B, IXH   ; ok
LD C, IXL   ; ok

; LD HL, IX
LD H, IXH   ; nope!
LD L, IHL   ; nope!

; instead, have to do:
LD D, IXH
LD E, IHL
EX DE, HL

; or, if DE is unavailable:
LD A, IXH
LD H, A
LD A, IXL
LD L, A
Or simply:

Code: Select all

push ix		;2 bytes
pop hl 		;(or any other register) 1 byte
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
Bubu
Manic Miner
Posts: 542
Joined: Fri Apr 02, 2021 8:24 pm
Location: Spain

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Bubu »

Or even:

Code: Select all

ex de, hl
ex de, ix
If something works, don't touch it !!!! at all !!!
User avatar
Manu128k
Dizzy
Posts: 83
Joined: Tue Aug 13, 2019 9:45 pm
Location: Spain
Contact:

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Manu128k »

This thread is pure gold for any Z80 ASM apprentice (like me)
Last edited by Manu128k on Tue May 04, 2021 7:58 pm, edited 1 time in total.
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Ast A. Moore »

Bubu wrote: Tue May 04, 2021 7:51 pm Or even:

Code: Select all

ex de, hl
ex de, ix
Nope. EX DE,IX is not a valid Z80 instruction.
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
Bubu
Manic Miner
Posts: 542
Joined: Fri Apr 02, 2021 8:24 pm
Location: Spain

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Bubu »

Ast A. Moore wrote: Tue May 04, 2021 7:58 pm Nope. EX DE,IX is not a valid Z80 instruction.

Ooops, I wanted to check if you were aware :lol: :lol: :lol:
If something works, don't touch it !!!! at all !!!
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by presh »

Ast A. Moore wrote: Tue May 04, 2021 7:48 pm Or simply:

Code: Select all

push ix		;2 bytes
pop hl 		;(or any other register) 1 byte
Ah yes! Forgot the push/pop trick!

Though it's 1 T-state slower than my code, it's also 3 bytes smaller (half the size!) and doesn't require a sacrificial register!

And given all that, my point still stands: HL is usually nice, giving us both speed and size improvements, not making us choose! :x ;)

Code: Select all

PUSH IX   ; 15/2
POP HL    ; 10/1
; 25 Ts, 3 bytes


LD A, IXH   ; 8/2
LD H, A     ; 4/1
LD A, IXL   ; 8/2
LD L, A     ; 4/1
; 24 Ts, 6 bytes
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by presh »

Turtle_Quality wrote: Tue May 04, 2021 7:24 pm I think all those can be done with IX / IY also, slower but with the displacement byte when it's indirect ; such as OR (IX+d)
True. IX & IY get a lot of stick for being slow, but I very recently converted a chunk of code to use them. It was much quicker to use IX, rather than shunting data structures back & forth to where they needed to be for the original code to access them. Also prone to bugs having 2 copies of a data structure knocking about in different places, very easy to overwrite the wrong value and wonder why it hasn't updated!
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by presh »

Joefish wrote: Tue Apr 20, 2021 9:04 am the SBC A trick for sign-extending.
The what now...??! :shock:
User avatar
rastersoft
Microbot
Posts: 151
Joined: Mon Feb 22, 2021 3:55 pm

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by rastersoft »

Bubu wrote: Tue May 04, 2021 7:29 pm Z80 things that weren't obvious that you wish you'd known earlier:

When I started programming with Z80, I thought that using IM2 would be a symbol of professional developing... but I never found a real use or utility to implement interrupts: it makes code run so slow, avoids to have sounds meanwhile playing... Never understood the reason for using IM2. My Anteater game doesn't use it at all ( well, only for detecting the coin at the beginning :D )
Well, it depends. In "Escape from M.O.N.J.A.S." I really needed IM2: I work on a temporary framebuffer, and I have to wait for an interrupt before copying it into the screen buffer. But doing a HALT would mean to waste a lot of CPU power, so what I do is paint in the buffer, when I ended I set a flag to indicate to the interrupt routine that it can dump the temporary framebuffer, and then I start to pre-calculate things for the next frame (like the next position of all the characters, which objects are near the main character, and so on), and when I did everything that I could before having to paint the new frame, THEN I wait for the interrupt to end copying the screen. This allowed me to gain between one and two FPS.
Tommo
Dizzy
Posts: 92
Joined: Sat Mar 20, 2021 3:23 pm

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Tommo »

On my Sam Coupé 3d stuff, I use the end-of-frame interrupt to maintain a frame counter and as the signal to switch frame buffers.

The latter is fairly obvious; the former allows for frame-rate independent movement. So even if the drawing rate slows down, objects continue to move at the same rate.
User avatar
Joefish
Rick Dangerous
Posts: 2059
Joined: Tue Nov 14, 2017 10:26 am

Re: Z80 things that weren't obvious that you wish you'd known earlier

Post by Joefish »

Joefish wrote: Tue Apr 20, 2021 9:04 amthe SBC A trick for sign-extending.
presh wrote: Tue May 04, 2021 9:15 pmThe what now...??! :shock:
To sign-extend an 8-bit number in A to a 16-bit number in BC:

LD C,A
RLCA
SBC A,A
LD B,A

First LD C,A copies the 8-bit number into C
Then RLCA shifts the top (sign) bit off the original copy of that number and into the CARRY flag.
Then if you did SUB A,A you'd get 0, but because you do SBC A,A you get 0 and then subtract the CARRY flag bit, so you get either 0 or -1.
Then LD B,A puts either 0 or -1 (all bits set) into B.
Post Reply