Z80 things that weren't obvious that you wish you'd known earlier
Re: Z80 things that weren't obvious that you wish you'd known earlier
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
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
Re: Z80 things that weren't obvious that you wish you'd known earlier
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.
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.
Re: Z80 things that weren't obvious that you wish you'd known earlier
100% agree with these!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.
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.
- 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
Something like this?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 ?
Code: Select all
ld h, table/256
and (hl)
align 256
table: db 1,2,4,8,16,32,64,128
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)
- 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
Perfect thanks
Definition of loop : see loop
Re: Z80 things that weren't obvious that you wish you'd known earlier
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?
Re: Z80 things that weren't obvious that you wish you'd known earlier
I'm not so sure if proper manual is required. List of commands with timings and effects would be really enough.in my youth i didn't had a proper manual, only a list of commands
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.
Re: Z80 things that weren't obvious that you wish you'd known earlier
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.
Re: Z80 things that weren't obvious that you wish you'd known earlier
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
Re: Z80 things that weren't obvious that you wish you'd known earlier
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):
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:
... 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.
(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 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.
}
}
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.
Re: Z80 things that weren't obvious that you wish you'd known earlier
Yep, good point..!
My Speccy site: thirdharmoniser.com
Re: Z80 things that weren't obvious that you wish you'd known earlier
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.
And because some instructions modify the value at the same time - e.g. INC (HL) - it allows for faster AND smaller code. Consider:
vs
HL is also quicker than BC/DE for certain tasks:
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:
or even quicker if you don't need the flags:
vs
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):
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)
Code: Select all
LD HL, lives ; 10/3
DEC (HL) ; 4/1
JP Z, game_over ; 10/3
; 24 Ts, 7 bytes
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
Code: Select all
LD HL, (nn) ; 16 Ts
LD BC, (nn) ; 20 Ts
LD DE, (nn) ; 20 Ts
e.g. with flags:
Code: Select all
OR A ; 4/1 (reset carry)
ADC HL, DE ; 15/2
; 19 Ts, 3 bytes
Code: Select all
ADD HL, DE ; 11/1
; 11 Ts, 1 byte
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
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
- 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
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 )
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 )
If something works, don't touch it !!!! at all !!!
- 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
Or simply:presh wrote: ↑Tue May 04, 2021 5:34 pmCode: 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
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.
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.
Re: Z80 things that weren't obvious that you wish you'd known earlier
Or even:
Code: Select all
ex de, hl
ex de, ix
If something works, don't touch it !!!! at all !!!
Re: Z80 things that weren't obvious that you wish you'd known earlier
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.
- 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
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.
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.
Re: Z80 things that weren't obvious that you wish you'd known earlier
Ooops, I wanted to check if you were aware
If something works, don't touch it !!!! at all !!!
Re: Z80 things that weren't obvious that you wish you'd known earlier
Ah yes! Forgot the push/pop trick!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
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!
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
Re: Z80 things that weren't obvious that you wish you'd known earlier
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!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)
- 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
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.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 )
Re: Z80 things that weren't obvious that you wish you'd known earlier
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.
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.
Re: Z80 things that weren't obvious that you wish you'd known earlier
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.