The definitive "teach yourself machine code" text?

The place for codemasters or beginners to talk about programming any language for the Spectrum.
User avatar
cmal
Manic Miner
Posts: 629
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: 3265
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: 2040
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: 1403
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: 2040
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: 2040
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: 2040
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: 2040
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: 2040
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: 6873
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: 3265
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: 2040
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!
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 »

Slightly faster and more compact:

Code: Select all

	xor a         ; clear A
	ld (23612),a  ; load system variable TV FLAG with A (i.e. 0)
	call s_print

	defb 17,5	;PAPER 5
	defb 16,3	;INK 3
	defb 19,1	;BRIGHT 1
	defm "Winds light to variable"
	defb 19,0	;BRIGHT 0
	defb 16,0	;INK 0
	defb 17,7	;PAPER 7
	defb 27		;EOS

	ret

s_print:
	pop hl		;pop the first byte from the stack
	ld a,(hl)	; load the contents of the relevant byte of the message into A
	inc hl    	;next byte
	push hl		;back onto the stack
	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
	jr s_print	;loop around
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: 2040
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

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

Post by TMD2003 »

Right, that's useful - I had no idea about the stackable DEFBs.

On the other hand, I should probably not have sounded the trumpet quite so loudly and proudly. I'm now on page 91-92 of Mastering Machine Code and I've slammed hard into a brick wall...
Toni Baker wrote:And now for some clever stuff. This is a subroutine. Its purpose is to point HL to a new square in the display file, and DE to a new square in the attributes file. It requires that HL is already pointing to the first row of pixels of some square, and that DE is already pointing to the attribute byte for that square. (Again, see figure 7.1.) It further requires that BC holds the displacement required (counting FFFF as one square left, 0001 as one square right, FFE0 as one square up and 0020 as one square down – ie the displacement simply counting squares). The subroutine has quite an interesting feature at the end:

Code: Select all

MOVE:
LD A,L
RR H
RR H
RR H
ADD HL,BC
RL H
RL H
RL H
EX DE,HL
ADD HL,BC
EX DE,HL
XOR L
BIT 4,A
RET Z
SCF
BIT 3,A
RET Z
LD A,L
RLA
XOR L
BIT 4,A
RET NZ
SCF
RET
The subroutine assumes that you will never want to move more than seven squares in any one direction in one go. In addition to moving HL and DE, it will return no carry if the displacement was acceptable or carry if the displacement has accidentally crossed the left or right edge of the screen. Notice the use of the instruction EX DE,HL (EXchange DE with HL) and the way in which it is used to add BC to DE. This is quite a useful trick. The logic behind the subroutine is as follows:
If bit four is unchanged then the displacement is allowed. Otherwise...
If bit three is unchanged then the displacement is not allowed.
Otherwise...
If bit three is not equal to bit four then the displacement is allowed.
Otherwise...
The displacement is not allowed.
I shall leave it as an exercise for you to see (1) how this logic works, and (2) how this logic is implemented in the program.
And I do look through these routines, to see if I can find out how they work. This one, though... I know what the individual opcodes do, it's just that stringing them all together, I've completely lost track of what is tangibly being achieved, and why it's being achieved. Take those seven lines that start with the first RR H and end with the third RL H; from what I can piece together, the effect is "add BC to HL, but don't bother adding bit 2 of H, the one that represents 1024 in the whole value". But I can't see why this is happening, or what the point of it is, especially when it doesn't do what Toni specifically said ti does when it was presented as a standalone routine above:
Toni Baker wrote:...we still have the problem that because the screen is divided up into segments (see figure 7.1) then we will encounter problems if we try to cross from one segment to another. For instance, if the address of one particular square is HL then the address of the next square on the screen is usually HL + 0001. If HL happens to contain 40FF though, then the next print position is 4800 – not 4100 (which is the second row of pixels of the first square on the screen). The following routine will always move HL to the next print position:
...followed by that routine. By "moves HL to the next PRINT position" I read it as "it moves HL from (the first row of) PRINT AT (r,c) to PRINT AT (r,c+1) - or if c=31 it moves to PRINT AT (r+1,0)." What it acutally does, from what I can gather from testing it, is move the PRINT position down 8 squares, i.e. to the next third of the screen. (I haven't tested what it does if the PRINT AT position is already in row 16 or more.)

But this was just the first bit of the routine I've quoted!

In the next three lines, sandwiched by EX DE,HL, this effectively adds BD to DE, with an otherwise-unchanged HL as the middleman. Not sure why the routine does that, it just does. And then... XOR L. I know that that does, individually, but why it is here as part of this routine has now completely fallen into the fog of mystery.

Maybe I should not be getting ideas above my station...

Pawn gets promoted, becomes queen. Queen takes pawn.

I think I'll go back to experimenting with the ZX81 for a while.
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 »

I suggest you familiarize yourself with a debugger and use it to single-step through/trace code.
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: 2040
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

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

Post by TMD2003 »

I already do - although Spin's debugger is the only one I have, and it'd make life a lot easier if the double registers could be separated. If I want to see what's going on in A, it's a royal pain if all I get to see is the combined AF, even more so if there are multiple cycles to go through, such as this other gem:
Toni Baker wrote:To make sure you have understood using the stack and conditional instructions, write a program which will PUSH every number between one and fifty (decimal) on to the stack (using PUSH AF) and then somehow manage a successful return to BASIC without crashing. (Hint: CP 32h (Compare with fifty decimal) is quite a useful instruction here.)
All right then...

Code: Select all

ld (23728),sp
ld a,1
loop:
push af
cp 50
jp z,final
inc a
jr loop
final:
ld sp,(23728)
ret
So I went into the debugger and what did I see? AF, when what I wanted to see was A, by itself. Now, while I'm sure everything was OK, on the grounds that the routine worked (I think the idea was to remember to save the value of SP elsewhere and read it back at the end of the loop), wouldn't it be a much better idea to do the constant stack-pushing from BC (i.e. B=0, C=1-50) and so not only is it possible to see the value as it really is, without being welded to the constantly-changing flag register, it slao means I could PRINT USR ... and see that the end result was 50.

Code: Select all

ld (23728),sp
ld bc,0
ld a,1
loop:
ld c,a
push bc
cp 50
jp z,final
inc a
jr loop
final:
ld sp,(23728)
ret
And that worked fine.

Later on, coming back to the first use of the RST $10 print routine from the ROM, the debugger didn't just "do the print", it cycled through the whole routine in ROM, every time it went through the loop. I could have done without that...

I reckon, what I will need is a debugger that shows all the registers unambiguously - i.e. for BC, it shows the values of B, C and BC - in decimal, hex, and binary. Is there one so detailed?
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: 2040
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

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

Post by TMD2003 »

Also also:

If there are any recommendations of an external assembler and/or debugger for the ZX81 (and even the ZX80), I'd like to know about those. I've just written this wonderful routine that should fill the (16K) ZX81 screen with dollar signs (I'll get it to do something better later), but it stops after a single iteration with the C error (which is only ever seen when dealing with VAL errors).

Code: Select all

ld hl,(D_FILE)
ld a,24
loop1:
ld b,32
loop2:
inc hl
call shash
djnz loop2
inc hl
dec a
jr nz,loop1
ret
shash:
ld (hl),13
ret
I've tried the same routine on the Spectrum, assembling to 60000 instead of 16514, with ld hl,22528 as the initial line. And sure enough, it does the Spectrum equivalent of what I'd need - jumps over a byte, then pokes 00001101 into the next 32 bytes, turning the squares blue. The jumped-over byte is the NEWLINE at the beginning of every line in the ZX81 display file which must not be overwritten, so no spaces should appear on the ZX81 screen and there should be a perfectly rectangular 24x32 screen full of dollars... although there isn't. The Spectrum produces a blue screen with a diagonal stripe of white squares where those bytes were skipped over (and a few of the 13s will overflow into the next part of memory - fortunately it doesn't crash), proving at least that the loops are working fine.

So the ZX81 is doing something weird that the Spectrum doesn't do, and it can't just be to do with the display file. I've dropped in the same enquiry on Sinclair ZX World, but this place gets more traffic so I might get an answer sooner.

Moving between the two machines might be harder than I'd thought...
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'm not a machine code programmer, but programming a simple little tilemapper (using the ROM routines for PRINT AT) gave me a better understanding of how it works. I think it is a good exercise to learn.
User avatar
TMD2003
Rick Dangerous
Posts: 2040
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

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

Post by TMD2003 »

Right, the next machine code inquisition, sparked by finding out that the SAM Coupé has four built-in sound effects...

Is there, somewhere out there, a collection of short machine code sound routines that were published in a magazine as a type-in? I'm sure I've come across one at some stage, but didn't make a note of it. I ask because there are sever of these sounds that occurred frequently in early games.

For instance: many of the sound effects in Michael Levers' Defender, which first appeared as a Sinclair Programs type-in, aren't exactly unique. But the one I was really looking for was the explosion sound in pretty much any of Kevin Bezant's early Spectrum games. Pick any of those, and the chances are you're going to find it (though it lasts a bit longer in Rapedes than in any of the others). Anyway, I put Sheer Panic through a disassembler (seeing as it's a 16K-suitable game at only 8700 bytes) in the hope of finding the tell-tale CALL 949, and there was a tangled web of code with only two instances of that call - and the one I could isolate as a complete routine was just a single beep. But there are plenty of JRs to the address where the second half of the sound routine is...

(That said, I should probably type in that machine code sounds listing from Dilwyn Jones' book that [mention]PeterJ[/mention] pointed me in the direction of the other day, and see if that's any help...)
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: 2040
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

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

Post by TMD2003 »

[mention]Ast A. Moore[/mention] - this is probably right up your street...

Following on from the above, I thought I'd do a few tests to see how CALL 949 works. So I wrote a BASIC routine that would assign values to HL and DE, convert them to high byte/low byte format, POKE those into addresses 60000-60003, and then let the machine code routine make the sound. That was then sent into Audacity, where I made a recording that meant I could measure the length of time each beep took, and the frequency of each beep.

From this, I conclude that DE is the number of cycles in each beep - but the frequency in HL is harder to calculate, mostly because it's limited by how accurately I can measure the length of a cycle (or, in this case, 10 cycles).

Is there an exact equation out there that tells me, if I put a value x into HL, I will get a frequency ax+b from the Spectrum?

And before anyone mentions it:
(1) Yes, I realise it will be a different equation for the 128K models - I'm looking for one for the 16K/48K rubber-keyed original.
(2) Yes, I have looked through Jonathan Cauldwell's authoritative tome, but he takes a different approach - starting with the frequencies of real notes, then translating that into HL and DE. I'm looking for "the frequency the Spectrum will output for a given value in HL".

I have a second routine to try, where I'll vary the value of HL so that 1/HL increases in linear steps rather than HL, and also using DE=65535 - that will at least get me a more accurate graph than I have already.
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 »

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: 2040
Joined: Fri Apr 10, 2020 9:23 am
Location: Airstrip One
Contact:

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

Post by TMD2003 »

Time for a mild bit of thread necromancy, I reckon. Before what was Christmas for just about everyone else, I was looking into ULAPlus to experiment with the new colours it could produce. It requires the OUT command with two two-byte values to start the routine and redefine the colours.

Instantly I thought of writing simple machine code routines to achieve the same thing - but I went back to the Machine Code Tutor and the OUT opcodes only handle one-byte values. What's the workaround, because there surely must be one (otherwise titles such as the ULAPlus version of Subacuatic would never have existed). Also, if I'm redefining the entire palette, is this something that the OTIR opcode might be useful for - say, store all the required codes in successive addresses and then OTIR them in one shot rather than OUTing them one by one?
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