Sunday 17 December 2023

ZX80 BASIC on a Minstrel 4D - Part 4 - ZX80 Autoload

I have been working on porting ZX80 BASIC to the Minstrel 4D. Hardware that was designed to run the Jupiter Ace, without making any hardware changes.

The next target for things to get working were two excellent flicker-free ZX80 games from Paul Farrow.

Following on from the post testing games on the ZX80 BASIC port on the Minstrel 4D, I had got as far as testing Paul's ZX80 version of Mazogs - http://www.fruitcake.plus.com/Sinclair/ZX80/FlickerFree/ZX80_Mazogs.htm

I was pleased to see that working well.

But when I tested ZX80 Kong, that unfortunately did not work, just giving an odd single line listing. - http://www.fruitcake.plus.com/Sinclair/ZX80/FlickerFree/ZX80_Kong.htm

ZX80 Pacman was the same.

I wasn't sure what was going on here, other flicker-free mechanisms did appear to be working, so why were these games not running?

I remembered that unusually for the ZX80 these games ran directly after loading, rather than needing RUN or GOTO 100 etc. to be typed.

The page includes the following lines:

The game is written entirely in machine code, and uses the technique developed by Martin Knorth to auto-run upon loading. It first performs an integrity test to ensure that it has loaded from cassette correctly and should an error be detected then a return to BASIC is made and an error message displayed.

I contacted Paul and asked if he could tell me more, to see if that was indeed the problem, and he kindly replied with the information and link to the appropraite page.

I added a quick bodge to test the theory, and it worked first time.

First the "Lightening Software" logo page, and then the Kong title screen.

That all appeared to be working, so time to try the game.

And yes, it works fine. There are a couple of caveats. The game appeared to be running slow, but I think that was just the inefficient bodge to test proof of principle. It should hopefully run at the correct speed once that has been implemented better. (it did)

The keys default to the arrow keys. Left and Right are the same, but up and down are reversed between the ZX80 and Jupiter Ace, so you need to press the 7 key, marked down, to move up.

There quite a lot of other control options, unfortunately (although fully justifiably), it does not include support for the Boldfield joystick port on the Minstrel 4D. There are other keyboard options, including QA OP + M and others. However, to get to these you have to press the C key. The keyboard scanning routine has been replaced, so it has the same problem as Hampton's maze, the keys on the bottom row are offset one place, so you have to press X rather than C.

Most programs use the ROM keyboard scan routines, which have been adjusted for the Minstrel 4D keyboard layout, but anything with it's own scan routines is going to have this issue.

ZX80 Pacman also worked -http://www.fruitcake.plus.com/Sinclair/ZX80/FlickerFree/ZX80_Pacman.htm

This had the same issues with the keyboard scanning on the bottom row of keys, but otherwise played great.

It is interesting to note there is no clicking in the flicker-free routines used in these games, I think they are using OUT $FF,A rather than the OUT $FE,A that was triggering the clicking.

How does the autostart work?

So, what had caused them not to work? Well, a clue can be gleaned from the loading screen I was seeing with line one with just a black square.

The technique used was devised by Martin Korth, and is the "nocash LD H,L trick".

See https://problemkaputt.de/zxdocs.htm#zx80zx81cassettefilecontent

Remember from "How the ZX80 Generates Video" that the screen information in the display file is executed as it is stepped through. If address bit 15 is set, the ZX80 hardware is in screen drawing mode. 

http://blog.tynemouthsoftware.co.uk/2023/10/how-the-zx80-generates-video.html

When the ZX80 hardware sees an execute cycle, data bit 6 is used to see if this is a printable character. If it is one of $00-3F (normal characters) or $80-$BF (inverse characters), then bit 6 is not set. The hardware detects this and the Z80 is sent a NOP instruction to execute instead. When bit 6 is set, the value from the display file is executed. The normal use for this is $76 the newline character which is also the opcode for "Halt".

The special listing seen above is constructed such that line one contains a value which does have bit 6 set, and so is executed as the screen is being drawn.

This is $65, LOAD H,L

The HL register pair is used to store a pointer to the display file, with address bit 15 set, so it is treated specially by the hardware. The is the display file mirror in upper RAM, in the $C000-$FFFF region.

The position is directly after the program in memory, so it varies with the program size.

The technique requires the program size to be padded out so it ends at a point where the lower byte of the address is $3D.

Say the program ends at $503D. The display file will start at $503E, and to generate the display bit 15 of the address is set to make this $D03E. The code jumps to that address is set and the Z80 will start to "execute" the display file. The hardware detects bit 15 has been set and starts to draw the display (the sync pulse will have already been generated).

When it hits the $65, HL will be holding $D040. HL is a 16 bit pair made from registers H and L, with H holding $D0 and L holding $40. The LD H, L instruction will load $40 in H, making HL $4040.

When it gets to the next display line, it will be expecting to jump back into the display file using the HL pointer, but instead will jump to $4040. This address does not have bit 15 set, so will not be interpreted as display data and instead will be executed as normal code.

$4040 will be the entry point into the user program, so it will start to execute. Neat.

Making that work on the Minstrel 4D

This was not working on the Minstrel 4D as the display hardware is very different, so does not execute the display file in the same way, so the LD command was not being executed and the program was not starting.

My initial hack to get this working was to check each character as it was being drawn to the screen, and when it hit a $65, it would jump to $4040 and execute the code there.

You can that in this photo taken at just the right time. (I had to do that twice as I had to take all these pictures again. Apparently my camera will happily take pictures, make the noise, show the preview etc. all with no SD card installed. Ask me how I know.) 

Loading is complete, and the listing has started, but as soon as the $65 was going to be written, it instead jumped to $4040, which is executing some code before it finally clears the screen and starts the game.

That was not ideal as that meant an extra delay on each character to test against $65 and then $76 (halt / newline). Instead I modified the LOAD completion routine. It only runs after the load is compelte, and checks for the presence of the specially crafted trick line. If it detects that, it jumps to $4040 instead of jumping to the routine which will list the program as normal on LOAD completion.

This returned the game to normal speed and it went neatly from the end of loading to the game splash screen.


Trying out the autoloader

I thought it might be interesting to try out this autoload mechanism.

I was looking around for some simple test program I could make autostart, when I thought that I could use the Macronics games I had previously tested.

Those were small ZX80 games that were published as type in listings as well as sold on tape, so were quite small. I was already looking at generating versions patched to use OUT $FF,A like the ZX80 ROM does, rather than OUT $FE,A that Macronics used and unfortunately causes the speaker to tick on the Minstrel 4D.

There are disassemblies and details analysis of these games on Paul Farrow's site:

I started with the disassembly, planning to rebase the code to start at $4040, to fit in with the autostart mechanism. I think that would have worked with the 3K game, but things were too tight for the two 1K games.

It's easy to forget the screen display on a ZX80 is 32x24, so 768 bytes, and the unexpanded machine has only 1024 bytes of RAM. That leaves only 256 bytes for the code, stack, variables etc. This is why the 1K version of space invaders uses a partial screen as there is not enough RAM to make it full size.

(hard to believe you can have a playable game in just 256 bytes when you think of modern games where even the update patches can be several gigabytes).

The original programs used a BASIC loader. The code was present in hex values in PRINT statements. This was extracted and copies to RAM at $4000 (the base of ZX80 RAM) and executed. In the process, this overwrote the original PRINT statements, but the extracted code was smaller than the container, so no problem there.

I decided the easiest approach was to do something similar. I wrote a loader program with the appropriate autostart header. This will jump to $4040 after loading, so at $4040 I jump to a simple routine at the end of the file to copy the code into place and then jump to the entry point in the game, the same as the BASIC loader did.

With a couple of caveats, that worked perfectly.

The first caveat is one I forgot at first, when executing jumps to $4040, it is actually in the process of drawing the screen, so interrupts are enabled, and interrupt is wired directly to the A6 line. When code execution reaches a point where A6 is low (e.g. $4060), it will fire an interrupt and jump to whatever you currently have in the HL register, which is probably not what you want. So the first instruction has to be DI to disable interrupts until you are ready to start drawing the screen.

The second is the file has to be a specific size, $xx3D bytes. The way I first tried this out with breakout, I was hoping it would be $413D, but unfortunately the code for copying just knocked it over by one byte and it would have needed to be $423D bytes long with 254 zeroes padding it at the end.

That was not ideal, but there was some padding before $4040, so I move the setup code there. I still had to jump to the end of the file but only for the LDIR and JP instructions to activate the copy and jump to the entry point.

I have created autostart versions of Breakout, and the 1K and 3K versions of Space Invaders. These will autostart on loading, and include the patch to use OUT $FF,A.

https://github.com/tynemouthsoftware/ZX80-4K

I have put those on my github if anyone is interested. They also work on a real ZX80 or Minstrel 2 with the original ZX80 4K integer BASIC ROM.


Advertisements

Minstrel 2

Minstrel 2 kits and ready built units are available from my SellMyRetro store.

http://blog.tynemouthsoftware.co.uk/2023/09/minstrel-and-mini-pet-kit-updates.html

Minstrel 4D

Minstrel 4D kits and ready built units are available from thefuturewas8bit.com

https://www.thefuturewas8bit.com/shop/tynemouth-products/minstrel4d.html


Patreon

You can support me via Patreon, and get access to advance previews of blog posts and behind the scenes updates. These are often in more detail than I can fit in here. This also includes access to my Patreon only Discord server for even more regular updates.

https://www.patreon.com/tynemouthsoftware