bwinkel67 wrote: ↑Mon Mar 21, 2022 3:24 am
So it seems to do two consecutive HALTs before doing a LD. ... What does the ZX81 do when it sees a HALT instruction?
Machine code is typically stored in a REM statement on the ZX81 as a convenient place to keep it so that it remains at a fixed location and gets saved to tape via the SAVE command. However, the BASIC listing routine does not know it contains machine code and will just try to display it like any other BASIC line. Hence you see it as a mix of seemingly random characters and tokens. Aside from looking a bit messy, this can actually cause problems.
If a $76 byte is encountered then the BASIC listing routine thinks this means the end of the line has been reached. It then tries to interpret the bytes that follow as the next BASIC line. This can result in some very corrupt looking listings until real BASIC lines are encountered and things get back into sync.
For example, type:
1 REM XX
2 PRINT "HELLO"
POKE 16514,118
LIST
You'll see line 2 is displayed corrupted because the BASIC listing routine thinks the next line starts two bytes earlier than it really does due it encountering the $76 poked in. However, you can still RUN the program and can LIST 2 successfully.
Another problem concerns hidden floating point numbers. After every numeric literal in a BASIC statement, 6 hidden bytes follow that define the actual value in floating point form. This sequence begins with a byte of $7E. Should the machine code in a REM contain a byte of $7E then the BASIC listing routine will assume it to imply a hidden floating point value and so will skip over the following 5 bytes. Should the $7E occur within 5 bytes of the end of the REM statement then the BASIC listing routine gets confused and continues into the following line.
For example, type:
1 REM XX
2 PRINT "HELLO"
POKE 16514,126
LIST
You'll see line 1 and 2 merged. You can still RUN the program and can LIST line 2 successfully.
A more serious problem is if the REM statement is so long that when an attempt is made to display it, all characters cannot fit within a single screen. Normally you would expect to get an error report 5 but the BASIC listing routine can get really confused resulting in an infinite loop as it continually tries to display the line.
To overcome this problem you either make sure you never attempt to list the line containing the machine code or you can fool the BASIC listing routine into thinking it has reached the end of the BASIC program by poking two $76 bytes at the start of the REM (as Martin pointed out). The first byte tricks the BASIC listing routine into thinking it has reached the end of the line, as described above. The second byte makes the BASIC listing routine think it has reached the end of the BASIC program. This works because any value above 63 ($3F) is treated as indicating the end of the BASIC program. Although the largest BASIC line you can enter directly is 9999 ($270F), the BASIC system actually supports line numbers up to 16383 ($3FFF). You can poke the line number bytes of a line to have a value greater than 9999 and it will run fine, but with just display oddly in a LISTing. So by poking the second byte with $76 means the BASIC listing routine thinks it has reached the end of the BASIC program and so displays nothing further. Using $76 is just a convenient value since $76 normally implies 'end of line'.
With the above program, type:
POKE 16514,118
POKE 16515,118
LIST
and you'll only see line 1. You can still run the program successfully, and you can still LIST 2 successfully. But after typing LIST 2, press newline to redisplay the automatic listing and it will show from line 1 again. To overcome this type:
POKE 16419,2
LIST 2
Press newline again and the automatic listing begins from line 2. Location 16419 and 16420 hold the line number value for system variable S_TOP, which controls with line that appears at the start of the automatic listing.
A final reason for hidding the machine code in a line could be as a very simply protection mechanism to stop people LISTing the program and and seeing secret messages that a game might display, etc.
I suspect MCODER2 puts in the two $76 bytes to avoid the listing issues above, especially the infinite loop problem since it can create very long REM lines.
bwinkel67 wrote: ↑Mon Mar 21, 2022 11:27 am
If you do a USR call to either 20499 or 20498, the program continues to run, going to 20500 and printing "HELLO WORLD" to the screen, so it doesn't seem to HALT. So what's going on here?
The simple answer is you are not supposed to execute the program in this way. However, it works as you have found (and as Paul and Martin have said) because of the way the ZX81 border line generation mechanism works. Each HALT will halt the Z80 but only until the end of the next border line has been output. A non-maskable interrupt (NMI) then occurs and the program moves onto the next location. So after two border lines have been output then the program finally moves onto 20500 and runs the compiled program. So at worst you have simply introduced a delay equivalent to the duration of two border lines, i.e. 128us.