The definitive "teach yourself machine code" text?

The place for codemasters or beginners to talk about programming any language for the Spectrum.
User avatar
PeterJ
Site Admin
Posts: 6877
Joined: Thu Nov 09, 2017 7:19 pm
Location: Surrey, UK

Re: The definitive "teach yourself machine code" text?

Post by PeterJ »

Dr beep wrote: Sun Oct 04, 2020 5:05 pm

I would suggest a simple program that uses RST 16 to display a character and slowly expand that code into a moving character and finally a controlled moving character. This will give you ideas how to show progress and how input can be used in games.

First job is to get familiar with the input and output on a ZX Spectrum
Agreed!
BenHanson
Drutt
Posts: 9
Joined: Sat Oct 03, 2020 2:22 pm

Re: The definitive "teach yourself machine code" text?

Post by BenHanson »

Also have a look at http://www.primrosebank.net/computers/z ... blyThe.pdf

You can reuse some of the ROM routines.
User avatar
TMD2003
Rick Dangerous
Posts: 2042
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

Re: The definitive "teach yourself machine code" text?

Post by TMD2003 »

After finishing the first of the four sides of the New Generation tutorial tapes, plus what I've picked up off this thread, would this be a reasonable summary of the registers?

Use A for doing any kind of calculations.
Use BC if you want the result to be returned to BASIC (i.e. via PRINT USR ..... or LET variable = USR .....).
Use DE if you'll need to keep on swapping with HL (because so far EX DE,HL is the only DE-specific instruction I've come across).
Use HL to store addresses that need to be easily referenced and retrieved.

As for the "BC loop" that occurred earlier in this thread (i.e. DEC BC | LD A,B | OR C | DJ NZ ...), is there any reason why that wouldn't work with DE instead of BC? I suppose I could modify that successful routine and try it, and see what happens...
Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
BenHanson
Drutt
Posts: 9
Joined: Sat Oct 03, 2020 2:22 pm

Re: The definitive "teach yourself machine code" text?

Post by BenHanson »

DJNZ is decrement B and Jump relative Not Zero

Yes you can:

DEC DE
LD A, D
OR E
JR NZ, ...
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: The definitive "teach yourself machine code" text?

Post by Ast A. Moore »

TMD2003 wrote: Sun Oct 04, 2020 9:22 pm Use A for doing any kind of calculations.
Mostly, yes.
TMD2003 wrote: Sun Oct 04, 2020 9:22 pmUse BC if you want the result to be returned to BASIC (i.e. via PRINT USR ..... or LET variable = USR .....).
Well, this is a very specific case that has to do with the Spectrum’s operating system. BC (sometimes referred to as the Byte Counter) is used in block copy/compare/input/output operations and the B register itself is used by the DJNZ instruction.
TMD2003 wrote: Sun Oct 04, 2020 9:22 pmUse DE if you'll need to keep on swapping with HL (because so far EX DE,HL is the only DE-specific instruction I've come across).
DE (sometimes referred to as the DEstination) is also used in block copy operations and points to the destination address of a byte (or block of bytes) to be copied to.
TMD2003 wrote: Sun Oct 04, 2020 9:22 pmUse HL to store addresses that need to be easily referenced and retrieved.
HL is the most versatile of the register pairs. It is somewhat similar to the A register, as it is the place where the results of arithmetic operations will be stored (e.g. ADD HL,DE, SBC HL,BC). It is also the only register pair that can point to a byte in memory to be loaded directly with a value (e.g. LD (HL),n) or used in conjunction with the stack pointer SP.
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
Einar Saukas
Bugaboo
Posts: 3113
Joined: Wed Nov 15, 2017 2:48 pm

Re: The definitive "teach yourself machine code" text?

Post by Einar Saukas »

OK, here's the solution in 17 bytes:

Code: Select all

org $fefe
    ld hl, 22528
    ld de, 22528+352
    ld bc, $610F
    ld a, 48
    call x1
x1:
    call $0cf0
I hope my math is correct, I don't have an emulator to test it right now...
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: The definitive "teach yourself machine code" text?

Post by Ast A. Moore »

Einar Saukas wrote: Tue Oct 06, 2020 7:20 pm OK, here's the solution in 17 bytes:
Cheater! :lol:

A great trick, though. (Well, two tricks.)
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
cmal
Manic Miner
Posts: 630
Joined: Fri Jun 05, 2020 1:05 am
Location: California

Re: The definitive "teach yourself machine code" text?

Post by cmal »

A clever solution indeed! :o I guess no one said you couldn't use ROM routines. I tested it and it works.
User avatar
Morkin
Bugaboo
Posts: 3274
Joined: Mon Nov 13, 2017 8:50 am
Location: Bristol, UK

Re: The definitive "teach yourself machine code" text?

Post by Morkin »

...Am still trying to get my head around that - what's the significance in the ORG $FEFE...? It doesn't seem to work elsewhere... :o
My Speccy site: thirdharmoniser.com
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: The definitive "teach yourself machine code" text?

Post by Ast A. Moore »

Morkin wrote: Tue Oct 06, 2020 11:44 pm ...Am still trying to get my head around that - what's the significance in the ORG $FEFE...? It doesn't seem to work elsewhere... :o
It strategically places the code so that the POP BC in the ROM’s subroutine loads the correct values into the B and C registers, namely—the low and high bytes of the memory address immediately after the CALL $0cf0 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
cmal
Manic Miner
Posts: 630
Joined: Fri Jun 05, 2020 1:05 am
Location: California

Re: The definitive "teach yourself machine code" text?

Post by cmal »

Yup, Einar's routine is extremely unrelocatable.
User avatar
Morkin
Bugaboo
Posts: 3274
Joined: Mon Nov 13, 2017 8:50 am
Location: Bristol, UK

Re: The definitive "teach yourself machine code" text?

Post by Morkin »

Ah, OK, I see. So not easily relocatable but still clever.. :lol:
My Speccy site: thirdharmoniser.com
User avatar
TMD2003
Rick Dangerous
Posts: 2042
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

Re: The definitive "teach yourself machine code" text?

Post by TMD2003 »

Sound the trumpets long and loud in a PLAY command or a series of BEEPs, I have written my first ever machine code routine with no outside help from anyone! And if I can do it, anyone can, at least anyone who's covered the first side of New Generation's Machine Code Tutor.

The routine switches the bright channel on or off in a 4x4 square on rows 11-14, with the column position adjustable as required. All I have to do is POKE a couple of values into two two-byte addresses and the machine code does the rest. There are no loops, as I'm using all of HL, BC, DE and A to hold and manipulate the values - and all I need is some LDs, ADDs and INCs (and RET...) - no DJNZ or any loop instructions, no PUSH and POP to and from the stack, I don't know how to do that yet, only that it exists (I'll get there soon enough, mind). But it works, it does what I needed it to do, and there was plenty of space to hold it amongst some undefined characters in a set. As long as I POKE the right values (64 for on, 192 for off - work out why...) and then LET u=USR 64600, it's instant. No waiting for two nested FOR n/m=0 TO 4 loops in BASIC now!

And to see the result - it's coming soon to a Crap Games Competition near you! That is, as soon as I've got it all finished and [mention]PROSM[/mention] reviews it and puts it up for download. That might take a while, so maybe, just maybe, I'll show a quick preview of this and my previous submission on the BASIC Dumping Ground...
Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: The definitive "teach yourself machine code" text?

Post by Ast A. Moore »

TMD2003 wrote: Thu Oct 08, 2020 10:51 pm so maybe, just maybe, I'll show a quick preview of this . . .
Please do!
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.
AndyC
Dynamite Dan
Posts: 1406
Joined: Mon Nov 13, 2017 5:12 am

Re: The definitive "teach yourself machine code" text?

Post by AndyC »

Nice going. You taken your first steps into a larger world... ;)
User avatar
TMD2003
Rick Dangerous
Posts: 2042
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

Re: The definitive "teach yourself machine code" text?

Post by TMD2003 »

"Ooooooouuuuuuuuurrrrrgh!"
- Tom G. Warrior, 1984 or so - present

This is the sound of success, victory, a job well done, and the rewarding devourment of a corned beef sandwich with extra HP sauce. I've written my second machine code routine, with some instructions that I'm not supposed to know about yet!

Code: Select all

63998: defb 18636 (where 18636 is actually any address of the top display file row of any PRINT AT position - some BASIC pokes it here)
64000:
ld hl,(63998)
ld b,a
rra
or b
ld (hl),a
inc h
ld b,a
rra
or b
ld (hl),a
inc h
ld b,a
rra
or b
ld (hl),a
inc h
ld b,a
rra
or b
ld (hl),a
inc h
ld b,a
rra
or b
ld (hl),a
inc h
ld b,a
rra
or b
ld (hl),a
inc h
ld b,a
rra
or b
ld (hl),a
inc h
ld b,a
rra
or b
ld (hl),a
ret
Yes, it's clunky, I haven't worked out loops yet (though I'm sure that BC loop will be handy), but I'm sure any expert will be able to spot what RANDOMIZE USR 64000 does, in its roundabout way. Remember, I only started this machine code lark a little more than a week ago and I've had other things to do in that time...
Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
+3code

Re: The definitive "teach yourself machine code" text?

Post by +3code »

I remember this pic:
viewtopic.php?p=13186#p13186
User avatar
TMD2003
Rick Dangerous
Posts: 2042
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

Re: The definitive "teach yourself machine code" text?

Post by TMD2003 »

+3code wrote: Tue Oct 13, 2020 10:53 pm I remember this pic:
viewtopic.php?p=13186#p13186
Definitely the cat this time. So far, it's two successes out of two. That's not going to last, I know that!
Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
User avatar
TMD2003
Rick Dangerous
Posts: 2042
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

Re: The definitive "teach yourself machine code" text?

Post by TMD2003 »

I've been amusing myself recently with a bit more machine code. And as much of what I'm trying to write involves the screen, here's something I'm trying to achieve but there appears to be a blockage in the way.

Think of a BASIC statement like this:

10 PRINT AT r,c;: LET u=USR 60000

So I set the PRINT AT position, and the idea is that the first thing the routine at 60000 does is checks the PRINT AT position in the system variables - because it must be there, and then... does something with that information, and there are a lot of things I'd like to do with it.

In the +2 manual I see this:
23566 - Store bytes of colour, AT and TAB controls going to TV. (2 bytes)

So what I did is, took the fragment of the ZX81 Hex Bin Converter program, so that I could convert the value of the system variable to binary and see better what's going on. Then I bolted on a BASIC routine to set the PRINT AT position to every possible value, PEEK 23566/23567 immediately after moving it, convert the value to binary and print the results on the ZX Printer so they'd be in a long, long list that was easily visible.

The second byte (23567) corresponds to the row in the PRINT AT command, but the first byte (23566) is always 22. That's not the screen attributes (which would be 56 for every square by default), so I have no idea what it is supposed to be. And there can be nothing that affects the stored PRINT AT position between moving it and PEEKing the value, unless there's some kind of Heisenberg-esque routine where PEEKing the value changes it to something else, in this case 22.

Where is the column for the PRINT AT command stored?
Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: The definitive "teach yourself machine code" text?

Post by Ast A. Moore »

Try this:

Code: Select all

10 PRINT AT 0,0; PEEK 23688, PEEK 23689
Then play around with the AT arguments.
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
TMD2003
Rick Dangerous
Posts: 2042
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

Re: The definitive "teach yourself machine code" text?

Post by TMD2003 »

Turns out that what I was looking for all along was 23684 - which is a direct store of the top row of the display file corresponding to the PRINT AT position and I don't have to make any calculations (although I've now worked out how to do that in machine code).

However, if I need to use rows 22 and 23, 23684 won't do - so I'll have a look at 23688/23689 and see if I can do what I was thinking of with that.
Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
User avatar
TMD2003
Rick Dangerous
Posts: 2042
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

Re: The definitive "teach yourself machine code" text?

Post by TMD2003 »

The quest continues! I have made it through the New Generation Software tutor, and I'm armed with a lot of knowledge that I'll need, and a fair bit that I won't just yet (i.e. anything to do with interrupts).

I tried compiling a very basic (also BASIC) flood fill routine with the Softek and MCoderII compilers to see if I could get any joy out of them, and was annoyed that I had to save the compiler code along with the (tiny) routine, just tomake it work, as both compilers included CALLs to their own routines. Jonathan Cauldwell's Turbo Compiler looked promising as it produced code that is independent of the compiler, but it doesn't recognise the POINT function.

But I discovered something while trying to work the compiler. The BASIC routine initially starts with defining two variables, xf and yf, the PLOT position where the fill will start. So that I would know straight away where these values would be in the compiled code, I went looking for opcodes that I thought would be useless and would never be seen in any machine code routine, ever - so when I ran the compiled code through a disassembler to see how it worked,

So I chose LET x=127 and LET y=100, which correspond to LD A,A and LD H,H respectively. Sure enough, the 127 and 100 stuck out like Manchester United fans on the Kop, and I could easily identify where I'd have to POKE new values from BASIC to make the flood fill routine work from any point on the screen.

So what, exactly, is the point of these opcodes? And by that I mean, any opcode where a register is loaded with itself.

Was it just to keep a neat pattern in the hex codes? It's not lost on me that, for instance, RLC A and RLCA are the same code, only plus or minus a CB depending on which one we want. I see it as if LPRINT and LLIST were E/F mode plus the same key to make PRINT and LIST on the Spectrum or ZX81 keyboard. At least ASN/ACS/ATN/VAL$ were the E-plus-shift equivalents of SIN/COS/TAN/VAL on the Spectrum, which made sense.
Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
User avatar
PeterJ
Site Admin
Posts: 6877
Joined: Thu Nov 09, 2017 7:19 pm
Location: Surrey, UK

Re: The definitive "teach yourself machine code" text?

Post by PeterJ »

TMD2003 wrote: Thu Nov 19, 2020 12:41 pm So what, exactly, is the point of these opcodes? And by that I mean, any opcode where a register is loaded with itself.
https://stackoverflow.com/questions/501 ... -to-itself
User avatar
Morkin
Bugaboo
Posts: 3274
Joined: Mon Nov 13, 2017 8:50 am
Location: Bristol, UK

Re: The definitive "teach yourself machine code" text?

Post by Morkin »

TMD2003 wrote: Thu Nov 19, 2020 12:41 pmSure enough, the 127 and 100 stuck out like Manchester United fans on the Kop
:lol: :lol:

You're right though - I find if I'm ever skimming through code in my emulator debugger, as soon as I see things like "LD A,A", "LD B,B" etc. it lets me know in an instant that I'm looking at a block of data rather than a block of code.

(Either that or the programmer was insane.)
My Speccy site: thirdharmoniser.com
User avatar
TMD2003
Rick Dangerous
Posts: 2042
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

Re: The definitive "teach yourself machine code" text?

Post by TMD2003 »

"I am even correcting Toni Baker now."

I'm going through Mastering Machine Code on your Spectrum to pick up on the bits that New Generation's Tutor missed - and to see how Toni's approach differs from that of Malcolm Evans. NGSCMT doesn't mention what any of the RST instructions do, apart from RST 0 = NEW - even the PRINT routine doesn't get a look. Toni Baker, on the other hand, introduces it about a quarter of the way through the book and shows how it can be used to print simple text on the screen, with colours - but does something unfathomable, seemingly just to show off the stack.

I've converted it from "use the BASIC hex loader that's steadily built up more and more over the course of Chapter 7" to .asm so that I could put all my notes in there:

Code: Select all

s_print:
pop hl        ; get the return address off the stack and put it in HL
ld a,(hl)     ; load the contents of the return address into A
inc hl        ; move the eventual return address up one byte
push hl       ; push the new return address back onto the stack
and a         ; leave A alone but clear the flags first
              ; also BATMAN SLAPS ROBIN because Robin said "CP 0" here
              ; AND A has the same effect, only in one byte instead of two
ret z         ; return from subroutine if zero flag is set, which it
              ; only will be if A was 0 before the AND instruction...
              ; right?
rst $10       ; ROM PRINT routine for one byte
jr s_print    ; return to first byte of s_print, only the return address
              ; is now one byte higher than it was originally
start:
xor a         ; clear A
ld (23612),a  ; load system variable TV FLAG with A (i.e. 0)
call s_print
; message and final byte 0 need to be immediately after the CALL,
; by the looks of things...
; there *must* be a better way to do this rather than fiddling around
; changing the return address from the stack!
; in other words, we can put the message *anywhere*, preferably after
; the final RET... but for now:
defb 17        ; PAPER control
defb 6         ; PAPER 6
defb 16        ; INK control
defb 2         ; INK 2
defb 18        ; FLASH control
defb 1         ; FLASH 1
defm "Oh, what a beautiful morning"
defb 0         ; last byte = 0 to end the routine
ret

; Assemble to 61440: RANDOMIZE USE 61449 to execute.
So it starts at nine bytes after where it's assembled so that the s_print subroutine comes first. That's the first thing I'd switch round. But during that subroutine, we have to:
- retrieve the return address from the stack
- read the first byte of the PRINT data
- increase that return address by one
- shove the new return address back onto the stack

...and that has to be done every time, and all because all the data in the DEFBs and the DEFM is before the RET. I mean, why, Toni? Why? Wouldn't it be a lot better to enclose the routine in as small a space as possible, give the start address of the message a label, point HL there, and then never have to worry about keeping tabs on the return address?

Well, I certainly think it is, so here's my version:

Code: Select all

start:
xor a         ; clear A
ld (23612),a  ; load system variable TV FLAG with A (i.e. 0)
ld hl,text    ; put the first address of where the message is stored into HL
call s_print
ret

s_print:
ld a,(hl)     ; load the contents of the relevant byte of the message into A
and a         ; clear the flags
cp 27         ; set zero flag if A=27
ret z         ; return from subroutine if zero flag is set, hence 27 acts as a "return"
              ; character, and we can use 0 in the BRIGHT and INK controls.
rst $10       ; ROM PRINT routine for one byte
inc hl        ; move the message address up one byte
jr s_print    ; return to first byte of s_print

text:
defb 17        ; PAPER control
defb 5         ; PAPER 5
defb 16        ; INK control
defb 3         ; INK 3
defb 19        ; BRIGHT control
defb 1         ; BRIGHT 1
defm "Winds light to variable"
defb 19        ; BRIGHT control
defb 0         ; BRIGHT 0
defb 16        ; INK control
defb 0         ; INK 0
defb 17        ; PAPER control
defb 7         ; PAPER 7
defb 27        ; 27 is unused in the character set - ideal to flag the end of the routine
So I've moved START to the... start, hence "assemble to x and then RANDOMIZE USR x" (rather than x+9), and pointed HL at "text" at the very end of the routine, after the RET from s_print, so there's no need to keep manually increasing the return address.

What I further found to be a problem is that Toni's routine uses 0 as the code to return from the routine - which is all well and good when setting the text to INK 2, PAPER 6, FLASH 1 - but what about when we want to switch off the FLASH control and turn the INK back to black afterwards? Houston, we have a problem. Toni must have been thinking "aha, I can save a byte or two because AND A doubles as CP 0, and Batman won't belt Robin round the face about 35 years in the future!" - but the addition of that CP command means I can change the value to be compared to look for anything in the character set - or not in the character set, as is the case with the non-existent CHR$ 27.

Also, I tried cutting the DEFB lines for defining the colours to DEFWs, to save a bit of text space, but I think that's more trouble than it's worth.

If this was a game of chess, 1K or otherwise: pawn takes queen.
Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
Post Reply