Page 3 of 4

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

Posted: Sat Nov 21, 2020 11:24 am
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.

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

Posted: Sun Dec 06, 2020 12:31 am
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...)

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

Posted: Sun Dec 06, 2020 11:23 am
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.

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

Posted: Sun Dec 06, 2020 12:00 pm
by Ast A. Moore

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

Posted: Fri Jan 15, 2021 1:40 pm
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?

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

Posted: Fri Jan 15, 2021 2:17 pm
by PROSM
TMD2003 wrote: Fri Jan 15, 2021 1:40 pm [...]
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
[...]
The most common way to address a 16-bit I/O port on the Z80 is to load the BC register pair with your port address, then issue the OUT (C),A instruction, like this:

Code: Select all

LD BC,$BF3B			; register select port
OUT (C),A			; send contents of accumulator to port
The whole BC pair gets put on the address bus, contrary to what the instruction might lead you to believe. The instructions only handle 8-bit port addresses due to Intel 8080 compatibility requirements.
TMD2003 wrote: Fri Jan 15, 2021 1:40 pm [...]
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?
[...]
No, because you need to address two different ports, the register select port and the data port. On top of that, OTIR uses the B register for the loop counter, so your port address would get clobbered.

There is an example routine for palette definition on the Sinclair Wiki which might work for your purposes.

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

Posted: Fri Jan 15, 2021 2:31 pm
by Sparky
I've had this book forever it feels like and now its free from google books, A must read IMO
"Programming the Z80"

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

Posted: Fri Oct 01, 2021 11:47 pm
by TMD2003
Right! Time for the next inquiry on machine code problems. And it looks simple, doesn't it...

I am writing a memory test routine. It's simple enough - the quick and dirty version is just this:

Code: Select all

	ld hl,(23730)	; RAMTOP
	ld de,(23653)	; STKEND
	; RAMTOP-STKEND is the memory remaining
	and a		; reset flags
	sbc hl,de	; or SBC HL,BC
	; result remains in HL - must be transferred to BC
	ld b,h
	ld c,l
	ret
Everything here is fine, ORG it to *address* and then PRINT USR *address* and I get the number of bytes remaining on screen.

I already have a BASIC routine that goes a bit further - it prints the values of RAMTOP, STKEND, and then the memory remaining, and this is what I'd like to do in machine code. However... the problem lies in actually printing the values of RAMTOP and STKEND within the code itself.

Code: Select all

	ld hl,(23730)
	ld de,(23635)
...is all well and good, but how do I use the RST 16 routine to print the actual, decimal values that are stored in the two bytes each of HL and DE?

Is this going to involve a huge amount of bit manipulation, DAA, or any of the other binary-coded-decimal operations I have yet to find a use for?

Or... EDIT: is it better to dump the value I want into BC, and then is there a CALL from the ROM that'll do what I'm looking for?

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

Posted: Sat Oct 02, 2021 11:09 am
by Mpk
This might work :
LD BC, (23730)
CALL 11563 ; CONVERTS BC TO 5-BYTE NUMBER AND PUSHES TO CALC STACK
CALL 11747 ; Print and clear top of Calc Stack

LD BC, (23635)
CALL 11563 ; CONVERTS BC TO 5-BYTE NUMBER AND PUSHES TO CALC STACK
CALL 11747 ; Print and clear top of Calc Stack

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

Posted: Sat Oct 02, 2021 1:26 pm
by TMD2003
Mpk wrote: Sat Oct 02, 2021 11:09 am This might work :
And it did!
LD BC, (23730)
CALL 11563 ; CONVERTS BC TO 5-BYTE NUMBER AND PUSHES TO CALC STACK
CALL 11747 ; Print and clear top of Calc Stack

LD BC, (23635)
CALL 11563 ; CONVERTS BC TO 5-BYTE NUMBER AND PUSHES TO CALC STACK
CALL 11747 ; Print and clear top of Calc Stack
Exactly what I was looking for. Congraturation, Well mown, corned beef sandwich, A Winner Is You, etc etc etc etc etc. You all know the drill by now.

How about some extra HP sauce with it, and extra extra chips with chip spice from somewhere around Hull?

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

Posted: Sat Oct 02, 2021 2:58 pm
by +3code
TMD2003 wrote: Fri Jan 15, 2021 1:40 pm 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?
Not sure if this helps: I use this small code to set palettes:

Code: Select all

;set a 64 colours palette  for
;the ULA+
;save the binary as "64c.pal"
;+3code
;
	org 44084

;ULA+ on
;this is equivalent to BASIC
;OUT 48955,64: OUT 65339,1
;
	ld	bc,48955
	ld	e,64
	out	(c),e

	ld	bc,65339
	ld	e,1
	out	(c),e

;counters
	ld	hl,data
	ld	d,64
	dec	hl

;loop
rut1:
	inc 	hl

;this is equivalent to the BASIC
;OUT 48955,d: OUT 65339,a
	ld	bc,48955
	dec	d
	out	(c),d

	ld	bc,65339
	ld	a,(hl)
	out	(c),a

	jr	nz,rut1

	ret

;here the values for the palette
data:
	defb 0, 0, 0, 0, 0, 0, 0, 0
	defb 0, 0, 0, 0, 0, 0, 0, 0
	defb 0, 0, 0, 0, 0, 0, 0, 0
	defb 0, 0, 0, 0, 0, 0, 0, 0
	defb 0, 0, 0, 0, 0, 0, 0, 0
	defb 0, 0, 0, 0, 0, 0, 0, 0
	defb 0, 0, 0, 0, 0, 0, 0, 0
	defb 0, 0, 0, 0, 0, 0, 0, 0
I really first create the palette in ZX-Paintbrush, creating a tap file with it. As this .tap file includes an "embebed" LOAD"" statement that I don't will, I load it in the emulator and then run a BASIC program as:

Code: Select all

   5 CLEAR 44083: LET org=44084
   7 LOAD "64c.pal" CODE
  10 DIM a(64)
  15 REM reading palette from ULA+
  20 FOR b=1 TO 64
  30 OUT 48955,b-1
  40 LET c=IN 65339
  50 LET a(b)=c
  60 NEXT b
  90 LET e=65
 100 FOR d=org+36 TO org+99
 105 LET e=e-1: POKE d,a(e)
 120 NEXT d: INPUT "File name:",a$
 125 PRINT "Saving palette ";a$
 130 SAVE a$ CODE org,100
So I have the palettes stored and ready with RAND USR org. But it's only my personal solution, I'm not an asm expert.

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

Posted: Mon Mar 28, 2022 1:30 am
by TMD2003
I finally got round to it - I've practically abandoned any hope of getting the Emulation File from the knackered hard drive back, so I've been through NGS' Complete Machine Code Tutor again and typed out all the text into a LibreOffice document. Most of it, at this stage, I didn't need to do. But it also served as a reminder that any thoughts I ever had about being able to make any more than short BASIC-enhancing routines are ideas so far above my station that even the Hubble telescope couldn't see them.

Typing out the first chapter on the various LD instructions was a detailing something so easy that I could probably have understood it when I still had a ZX81 (aged less than eight). But the final chapter in interrupts... I could live a thousand lifetimes and never put a DI or a RETN or IM 2 in any of my routines. I've read it twice and it's still incomprehensible gobbledegook.

How did Matthew Smith grasp this so easily that he went from buying a ZX81 to releasing Manic Miner within two years? Is he really a Nobel Prize-level genius? Is that a prerequisite?

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

Posted: Mon Mar 28, 2022 7:50 am
by Ast A. Moore
TMD2003 wrote: Mon Mar 28, 2022 1:30 am But the final chapter in interrupts... I could live a thousand lifetimes and never put a DI or a RETN or IM 2 in any of my routines. I've read it twice and it's still incomprehensible gobbledegook.
What’s tripping you up specifically about interrupts?

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

Posted: Mon Mar 28, 2022 7:54 am
by AndyC
I think the trick is realising that it really isn't as complicated as you might imagine. An interrupt is just a CALL that happens in response to an external event and, on the Speccy at least, the only external event is a 50hz clock pulse. If a certain piece of code mustn't be interrupted, say because it's manipulating the stack and a sudden CALL would PUSH the wrong values, it can temporarily disable the possibility with DI and then re-enable it later with EI. Everything else about interrupt handling is really just fluff around how you configure where the handler routine lives.

It is a bit of a faff actually configuring it on the Speccy because the only way to put your own interrupt handler in is to use IM2. But IM2 has special requirements of external hardware that the Speccy doesn't guarantee. The idea is you should have a table of jump addresses and each device should notify the Z80 which one it is by providing an even number on the bus at the same time it signals an interrupt. Then device 0 would jump to the address at offset 0 in the table, device 2 would jump to the address at offset 2 etc.

Since you can't predict the value on the bus (or even that it is even) you have to set up a table in such a way that no matter what offset is picked it jumps somewhere predictable. It's kind of weird, but really only requires copying one of the "standard" ways of doing it and then putting your interrupt handler (or at least a jump to it) at a fix d place in RAM. Or play with the +2A/+3 "all RAM" mode, where you can put your own handler at 38h at use IM1 instead.

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

Posted: Mon Mar 28, 2022 10:09 am
by Ralf
Interrupts may seem at the beginning tricky because it's basically a code that:

- you never call, yet it is executed.
- if you don't deal with it, it will get executed at some totally random, unpredictable places
- it can overwrite your registers so when program returns to your main code it may crash
- it writes to the stack too so if you do anything will stack it will mess your work as well

Generally you cannot pretend that interrupts don't exist. You need either to disable it (DI command) or deal with it.

Although I must admit in my first games I did almost nothing with interrupts ;) I used HALT to synchronise the frames and my drawing
but my programs worked with default IM 1 setting and called default interrupt code in ROM. And I even didn't know that it does so ;)
It worked, by trial and error I just learnt that using IY register will crash your program so I didn't use it.

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

Posted: Mon Mar 28, 2022 10:17 am
by AndyC
Well the ROM routine does everything "right" so as long as you aren't doing devious things with the stack or messing with IY and the alternate registers (which the Speccy OS reserves) you kind of can pretend interrupts don't exist.

But if you want access to all the registers or want to start doing more stack manipulating, then you do at least need to embrace disabling interrupts.

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

Posted: Mon Mar 28, 2022 2:06 pm
by Turtle_Quality
AndyC wrote: Mon Mar 28, 2022 10:17 am Well the ROM routine does everything "right" so as long as you aren't doing devious things with the stack or messing with IY and the alternate registers (which the Speccy OS reserves) you kind of can pretend interrupts don't exist.

But if you want access to all the registers or want to start doing more stack manipulating, then you do at least need to embrace disabling interrupts.
Disabling or minimising .. I want the Interrupts for timing, but my interrupt code consists of
EI ;
RETI ;

(It's so minimal it doesn't even have comments)

The need for EI is a common trip-up by the way, I couldn't work out why my interrupt was only working once. Turned out the processor disables the interrupts when performing an interrupt.

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

Posted: Mon Mar 28, 2022 9:45 pm
by TMD2003
Ast A. Moore wrote: Mon Mar 28, 2022 7:50 am
TMD2003 wrote: Mon Mar 28, 2022 1:30 am But the final chapter in interrupts... I could live a thousand lifetimes and never put a DI or a RETN or IM 2 in any of my routines. I've read it twice and it's still incomprehensible gobbledegook.
What’s tripping you up specifically about interrupts?
What I was really trying to say was, this is something so far beyond my level that I cannot even attempt to use it yet. If the entire concept of interrupts can be likened to the final year of a Masters degree, I am still working my way through primary school - and it is very, very unlikely I will ever get even close to this level.

But nevertheless, I thought I'd attempt something more challenging - something I have yet to tackle and (more to the point) is something I'll find useful.

It's how to handle BASIC variables via machine code.

To put this in context, I've also been re-typing all of the 40 Best Machine Code Routines, and some of those late in the book access the variables area.

All I know so far is: the variables area starts at the address held in VARS and ends at one less than the address held in E_LINE. And while I still have trouble decoding the way that numerical variables are held (mainly because they're all five-byte floating point values that are also beyond my level at this stage), string variables I can see by PEEKing this area.

I see: the letter of the variable, always as a capital, then two bytes for the number of characters, then the characters themselves. Superficially, it seems easy to search for a particular string variable.

So I have started with this BASIC program that should test whether or not there is a method that is absolutely failsafe in finding a required string variable. I have chosen to look for Q$.

To cause the maximum number of hurdles, I've set up the following:
- Define a string Z$ with twelve characters in it, one of which is Q.
- Define four blank strings A$-D$.
- Define an array L(4) into which random values between 10 and 20 are inserted.
- Populate A$-D$ to a length of L(n) (n=1-4 corresponding to A$-D$), with the characters from Z$. It is likely that all of these will contain one or more Qs.
- INPUT LINE Q$, which defines the string I will search for.
- (eventually...) Execute the machine code routine, search for the variable Q$ and have my evil way with it.

Code: Select all

  10 LET z$="VQFods634#%&"
  20 LET a$="": LET b$="": LET c$="": LET d$=""
  30 DIM l(4)
  40 FOR n=1 TO 4
  50 LET l(n)=INT (RND*11)+10
  60 NEXT n
  70 PRINT "LENGTH OF A$ = ";l(1)
  80 PRINT "LENGTH OF B$ = ";l(2)
  90 PRINT "LENGTH OF C$ = ";l(3)
 100 PRINT "LENGTH OF D$ = ";l(4)
 110 FOR n=1 TO l(1): LET z=INT (RND*12)+1: LET a$=a$+z$(z): NEXT n: PRINT "A$ = ";a$
 120 FOR n=1 TO l(2): LET z=INT (RND*12)+1: LET b$=b$+z$(z): NEXT n: PRINT "B$ = ";b$
 130 FOR n=1 TO l(3): LET z=INT (RND*12)+1: LET c$=c$+z$(z): NEXT n: PRINT "C$ = ";c$
 140 FOR n=1 TO l(4): LET z=INT (RND*12)+1: LET d$=d$+z$(z): NEXT n: PRINT "D$ = ";d$
 150 INPUT "Q$ = "; LINE q$
 199 REM view variables area
 200 FOR n=(PEEK 23627+256*PEEK 23628) TO (PEEK 23641+256*PEEK 23642)-1
 210 PRINT n;"   "; INK (2 AND PEEK n<32);PEEK n; INK 0;TAB 12;CHR$ ((32 AND PEEK n<32)+(PEEK n AND PEEK n>=32))
 220 NEXT n
So far, all I know is:
- LD HL,(23267) / LD DE,(23641) / DEC DE will set the start and end points of the variables ares where I want them (unless I find it's a better idea to use IX instead of HL, which it might be). If I was then to LD A,(DE), A would read 128. (Or at least, it should, if I've done everything right.)
- At some stage there will be CP 81, followed by JR NZ (backwards) to cycle round until we reach a Q... then we get to the tricky bit.

And that tricky bit is: how do we establish beyond any doubt that the Q we're looking at is the one we want? There is at least one Q in the variables area (within Z$) and another unspecified but probably non-zero number of Qs after that, before we get to the Q that signals that this is the variable Q$.

You may say "Aha! Look at the value immediately after it!", but there's a problem. In this case, all the Qs in Z$ and the junk variables A$-D$ will be followed by values between 32-128, but in a general case that won't necessarily be true; there may be control characters inside other string variables with values under 32. And likewise, there's no guarantee that the value after the Q we want will be under 32, because the string length can easily be 32 or more, or greater than 288, or...

It must be possible, because every time BASIC recalls Q$, the ROM routine to do so always knows exactly where it is.

Once this has been sorted out, I can make some very rudimentary string handling routines, even if it's something as mundane and milquetoast as a ROT13 encoder, or reversing the order of the characters, for a string Q$ of any length.

So far, if I want to do anything like this, I have to resort to a bit of BASIC to POKE the CODE of each character into a series of addresses above RAMTOP, before I can do what I want with them. And now that I think of it, this is what I had to do on Super Mario Fruit Machine 2; my bit of machine code to print all the reels on screen was supposed to speed it up significantly, but the four POKEs required for each character, of which there were nine, robbed the game of most of the speed bonus. While it might have been possible to hold all the reels as a series of bytes above RAMTOP and then write an easier routine to access those without having to probe the variables area, either I didn't think of that at the time or had already invested too much time into making it work the BASIC way that this would have required a significant rewrite (and the fact that I wrote the game using BASin probably doesn't help on that front).

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

Posted: Tue Mar 29, 2022 12:11 am
by Ast A. Moore
TMD2003 wrote: Mon Mar 28, 2022 9:45 pm
Ast A. Moore wrote: Mon Mar 28, 2022 7:50 am
What’s tripping you up specifically about interrupts?
What I was really trying to say was, this is something so far beyond my level that I cannot even attempt to use it yet.
Well, the concept is pretty simple. An interrupts is an electrical signal; that is to say, it happens on the physical level, and the CPU has a whole pin (well, two pins, in fact) to detect it. How to deal with it when it occurs is up to you. You can even tell the CPU to ignore it altogether (DI stands for Disable Interrupts) and go on your merry way. Much like you can decide what to do when your phone rings when you’re in the middle of something: you can turn the sound/vibration off (“disable interrupts”), let it go to voicemail, or answer it.

Remember, the Spectrum was designed around the Z80, not the other way around. The Z80 itself is a general-purpose CPU capable of communicating with a variety of peripherals. The idea of an interrupt is just a convenient way for a peripheral device to signal the CPU that it needs attention. So, rather than polling each and every device manually inside your code (and wasting precious time and memory), you can let the device explicitly ask for your attention. If we use our phone analogy once again, you can have people call you if they need to talk to you, instead of you calling each and every person in your address book asking them if they need anything from you.

Now, a bare-bones Spectrum has just one such device, and it’s the ULA. It “nags” the CPU fifty times a second right before it starts drawing a frame of video. Since such interrupts occur at a very regular and precise interval, it’s a convenient way of timekeeping and synchronization.

When an interrupt occurs (and you choose not to ignore it), the CPU stops executing the code and jumps to a specific address in memory. It’s like a GOSUB in BASIC:

Code: Select all

...
400 IF phone_ring=1 GOSUB 900
...
...
900 REM deal with a phone call
...
1100 RETURN
The BASIC interpreter uses the interrupts to update the FRAMES system variable and to poll the keyboard, for example. You can write your own ISR (Interrupt Service Routine) or ignore interrupts completely (which in most cases means let the BASIC interpreter deal with it).

That’s the gist of it. If that’s still way over your head (the general concept, that is, no the nitty-gritty details), tell me and I’ll try to explain it in a different way.

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

Posted: Tue Mar 29, 2022 7:17 am
by AndyC
TMD2003 wrote: Mon Mar 28, 2022 9:45 pm
Ast A. Moore wrote: Mon Mar 28, 2022 7:50 am
What’s tripping you up specifically about interrupts?
What I was really trying to say was, this is something so far beyond my level that I cannot even attempt to use it yet. If the entire concept of interrupts can be likened to the final year of a Masters degree, I am still working my way through primary school - and it is very, very unlikely I will ever get even close to this level.

But nevertheless, I thought I'd attempt something more challenging - something I have yet to tackle and (more to the point) is something I'll find useful.

It's how to handle BASIC variables via machine code.

To put this in context, I've also been re-typing all of the 40 Best Machine Code Routines, and some of those late in the book access the variables area.

All I know so far is: the variables area starts at the address held in VARS and ends at one less than the address held in E_LINE. And while I still have trouble decoding the way that numerical variables are held (mainly because they're all five-byte floating point values that are also beyond my level at this stage), string variables I can see by PEEKing this area.

I see: the letter of the variable, always as a capital, then two bytes for the number of characters, then the characters themselves. Superficially, it seems easy to search for a particular string variable.

So I have started with this BASIC program that should test whether or not there is a method that is absolutely failsafe in finding a required string variable. I have chosen to look for Q$.

To cause the maximum number of hurdles, I've set up the following:
- Define a string Z$ with twelve characters in it, one of which is Q.
- Define four blank strings A$-D$.
- Define an array L(4) into which random values between 10 and 20 are inserted.
- Populate A$-D$ to a length of L(n) (n=1-4 corresponding to A$-D$), with the characters from Z$. It is likely that all of these will contain one or more Qs.
- INPUT LINE Q$, which defines the string I will search for.
- (eventually...) Execute the machine code routine, search for the variable Q$ and have my evil way with it.

Code: Select all

  10 LET z$="VQFods634#%&"
  20 LET a$="": LET b$="": LET c$="": LET d$=""
  30 DIM l(4)
  40 FOR n=1 TO 4
  50 LET l(n)=INT (RND*11)+10
  60 NEXT n
  70 PRINT "LENGTH OF A$ = ";l(1)
  80 PRINT "LENGTH OF B$ = ";l(2)
  90 PRINT "LENGTH OF C$ = ";l(3)
 100 PRINT "LENGTH OF D$ = ";l(4)
 110 FOR n=1 TO l(1): LET z=INT (RND*12)+1: LET a$=a$+z$(z): NEXT n: PRINT "A$ = ";a$
 120 FOR n=1 TO l(2): LET z=INT (RND*12)+1: LET b$=b$+z$(z): NEXT n: PRINT "B$ = ";b$
 130 FOR n=1 TO l(3): LET z=INT (RND*12)+1: LET c$=c$+z$(z): NEXT n: PRINT "C$ = ";c$
 140 FOR n=1 TO l(4): LET z=INT (RND*12)+1: LET d$=d$+z$(z): NEXT n: PRINT "D$ = ";d$
 150 INPUT "Q$ = "; LINE q$
 199 REM view variables area
 200 FOR n=(PEEK 23627+256*PEEK 23628) TO (PEEK 23641+256*PEEK 23642)-1
 210 PRINT n;"   "; INK (2 AND PEEK n<32);PEEK n; INK 0;TAB 12;CHR$ ((32 AND PEEK n<32)+(PEEK n AND PEEK n>=32))
 220 NEXT n
So far, all I know is:
- LD HL,(23267) / LD DE,(23641) / DEC DE will set the start and end points of the variables ares where I want them (unless I find it's a better idea to use IX instead of HL, which it might be). If I was then to LD A,(DE), A would read 128. (Or at least, it should, if I've done everything right.)
- At some stage there will be CP 81, followed by JR NZ (backwards) to cycle round until we reach a Q... then we get to the tricky bit.

And that tricky bit is: how do we establish beyond any doubt that the Q we're looking at is the one we want? There is at least one Q in the variables area (within Z$) and another unspecified but probably non-zero number of Qs after that, before we get to the Q that signals that this is the variable Q$.

You may say "Aha! Look at the value immediately after it!", but there's a problem. In this case, all the Qs in Z$ and the junk variables A$-D$ will be followed by values between 32-128, but in a general case that won't necessarily be true; there may be control characters inside other string variables with values under 32. And likewise, there's no guarantee that the value after the Q we want will be under 32, because the string length can easily be 32 or more, or greater than 288, or...

It must be possible, because every time BASIC recalls Q$, the ROM routine to do so always knows exactly where it is.

Once this has been sorted out, I can make some very rudimentary string handling routines, even if it's something as mundane and milquetoast as a ROT13 encoder, or reversing the order of the characters, for a string Q$ of any length.

So far, if I want to do anything like this, I have to resort to a bit of BASIC to POKE the CODE of each character into a series of addresses above RAMTOP, before I can do what I want with them. And now that I think of it, this is what I had to do on Super Mario Fruit Machine 2; my bit of machine code to print all the reels on screen was supposed to speed it up significantly, but the four POKEs required for each character, of which there were nine, robbed the game of most of the speed bonus. While it might have been possible to hold all the reels as a series of bytes above RAMTOP and then write an easier routine to access those without having to probe the variables area, either I didn't think of that at the time or had already invested too much time into making it work the BASIC way that this would have required a significant rewrite (and the fact that I wrote the game using BASin probably doesn't help on that front).
Honestly, interoperating with BASIC is one of the more complex things you can do in machine code, because the BASIC interpreter is actually a pretty complex system.

So how does BASIC know which variables are which. Simple, it doesn't just scan memory looking for the right sequence of bytes - it starts at the beginning and "walks" the structure, checking if it is the right variable and, if not, advancing by the correct amount to the start of the next variable. The information on how to do this is all in the manual, so it's not impossible to work out.

It is likely to be much, much harder than just writing your entire program in m/code though.

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

Posted: Sun Sep 25, 2022 12:50 pm
by TMD2003
Continuing with using this thread as a "general questions about Z80 machine code that the experts will know about" inquiry platform, I thought of this the other day while using IX for its intended purpose:
AndyC wrote: Mon Mar 28, 2022 10:17 am Well the ROM routine does everything "right" so as long as you aren't doing devious things with the stack or messing with IY and the alternate registers (which the Speccy OS reserves) you kind of can pretend interrupts don't exist.
If I needed to use IY in a routine called from BASIC, is it likely to work if I start the whole thing with PUSH IY and then POP IY before the final RET?

(And, likewise, IX on the ZX81, and probably the ZX80 as well.)

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

Posted: Sun Sep 25, 2022 1:45 pm
by AndyC
Yes, IY just has to hold a very specific value because the ROM uses it to access the system variables. You may need to disable interrupts as well during your code, to avoid the system routines from doing weird things too.

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

Posted: Tue Jun 27, 2023 12:44 pm
by TMD2003
Nine months of not having any major problems with machine code and now, one major headache sails its oil tanker into Headache Harbour and drops anchor.

Is there an emulator, somewhere out there, in which I can monitor the values of the registers in real time?

I'm not asking for much. I have a routine that detects a keypress, and assigns one of 40 unique values into A. If I subtract 48 from this value, it will be between 0 and 42. I then need to multiply this value by 8, dump it into BC, and add this to IX which is already set at the position of the start of a data table. Each "row" of the table contains 8 bytes, so each keypress should point IX the the start of a corresponding row.

Except, I've done something wrong somewhere. All I know is that IX is definitely set correctly at the start of the routine, but I have no way of knowing if the calculation to produce BC is correct or not, or whether what I'm doing with IX+BC is or isn't right.

Failing the opportunity to look at the registers in real time (this seems to be only available with a debugger screen which freezes the emulation - that's no good to me at this stage), I have heard that there's a ROM call that can print the value of the calculator stack on screen. Now, how I get the (unsigned 16-bit integer) value of BC onto the calculator stack in the first place is every bit as much of a mystery, but it'd be good to know how it's done...

EDIT: I now know what I did wrong, but it'd still be good to be able to monitor the real-time registers so all the above still applies, because I'm bound to make a similar error at some stage in the future. How did 1980s programmers deal with having to constantly load and reload assemblers when something serious went wrong, especially if there was pressure to deliver a game on time? Or were they all using Amstrad CPCs passing the code into a Spectrum from a serial cable?

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

Posted: Tue Jun 27, 2023 1:02 pm
by AndyC
Most emulators with a debugger will let you single step through instructions and see the register values, although that can be difficult if you're trying to introduce keyboard state changes at the same time (I'm not aware of a debugger that lets you control keystates while in single step mode though it could be handy).

Alternatively a conditional breakpoint that'll break into you code when a key value has been detected, so you can then trace the logic a step at a time, would probably be your best bet.

Real time register display while running at full speed isn't terribly useful, as the values change far too quickly for the PC to display on screen.

Back in the 80's? We pretty much had to reason through the code with pencil and paper till we figured out what the logical flaw was.

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

Posted: Tue Jun 27, 2023 1:09 pm
by PROSM
I agree with Andy, real-time display probably won't be too useful in this scenario, you're best off using breakpoints. I suppose you could step through the IN instruction and then change the contents of the register, but that's very fiddly.

Alternatively, you could record an RZX of you pressing a few keys in your program, and then play the recording, activating the debugger when appropriate.