ZXSimulator
Posted: Tue Mar 17, 2020 5:40 am
So with everything being cancelled it gave me some time to start on my fun project. Plan is to take my BASIC interpreter I prototyped on the QL back in the 90's for my Mac product and convert it to ZX81 Basic. I actually modeled it a bit in-between the ZX81 BASIC and SuperBASIC so it shouldn't need too much modification.
Note this is just for fun and I set myself a couple of ground rules: 1) should run on an unexpanded QL, and 2) will use Digital 'C' to write it. Goal is to be able to run up to 16K programs written for the ZX81 in BASIC with no machine code support. It will not emulate the ZX81 and in fact it will likely only support LOAD, and RUN commands (i.e. not editing/writing BASIC programs in the simulator, just run things).
So I've been testing ways to generate the character set. I may need to learn how to change built-in fonts but for now I played with the block command in Digital 'C' and got some ok results. One good thing is that this will make the simulator work in all graphics mode. I use the JSU ROM and it starts using 8 pixel-height characters to squeeze in everything into the NTSC picture which means a lot of what I wrote in the 90's needed to be reformatted for the 10 point font that is standard in JS ROM. For JSU in monitor mode (F1) it uses 10 points but in TV (F2) it switches to 8 points.
Timing-wise, printing a graphic character is about 1/2 as fast for the alternating pixel pattern (likely the most block calls it would have to do) as a ZX81. I can be more efficient with characters by writing a generalized function that goes through the 6x6 grid (36 bits can be stored in 5 bytes for 2.5K of memory) and for some of the graphics characters I can write specialized functions. The less block calls the closer to real-time I'm guessing. The interpreter itself is pretty fast already and so once I figure out how to write the character set I'll just write a zxprintf function that will replace the fprintf function in Digital 'C' and start spitting out ZX81 font strings and graphics. Also, I only need to focus on 64 characters and the other 64 are just inverted versions which I think I can do by just flipping the paper and ink colors.
So here I played with some timings on an unexpanded QL:
So timings on unexpanded QL were 10.5 seconds for the for-loop version, 9 seconds for hard-coded version, and 4.7 seconds on ZX81 (code below). So about half-speed. But that's for the worst-case scenario.
BTW, I finally figure out how to convert a .wav file to a .p file and then into text. The last part there are two tools, one is a command line one called zxtools.exe which yields the text below and uses the special escape characters for graphics.
Note this is just for fun and I set myself a couple of ground rules: 1) should run on an unexpanded QL, and 2) will use Digital 'C' to write it. Goal is to be able to run up to 16K programs written for the ZX81 in BASIC with no machine code support. It will not emulate the ZX81 and in fact it will likely only support LOAD, and RUN commands (i.e. not editing/writing BASIC programs in the simulator, just run things).
So I've been testing ways to generate the character set. I may need to learn how to change built-in fonts but for now I played with the block command in Digital 'C' and got some ok results. One good thing is that this will make the simulator work in all graphics mode. I use the JSU ROM and it starts using 8 pixel-height characters to squeeze in everything into the NTSC picture which means a lot of what I wrote in the 90's needed to be reformatted for the 10 point font that is standard in JS ROM. For JSU in monitor mode (F1) it uses 10 points but in TV (F2) it switches to 8 points.
Timing-wise, printing a graphic character is about 1/2 as fast for the alternating pixel pattern (likely the most block calls it would have to do) as a ZX81. I can be more efficient with characters by writing a generalized function that goes through the 6x6 grid (36 bits can be stored in 5 bytes for 2.5K of memory) and for some of the graphics characters I can write specialized functions. The less block calls the closer to real-time I'm guessing. The interpreter itself is pretty fast already and so once I figure out how to write the character set I'll just write a zxprintf function that will replace the fprintf function in Digital 'C' and start spitting out ZX81 font strings and graphics. Also, I only need to focus on 64 characters and the other 64 are just inverted versions which I think I can do by just flipping the paper and ink colors.
So here I played with some timings on an unexpanded QL:
Code: Select all
/*
* Author: Michael Jonas (copyright 2020)
* Date: 3/16/20
* ZX81 graphics test prototype
*/
#include stdio_h
main ()
{
int x, y;
char cstr[80];
initQLscr();
cstr[0] = ' ';
while (cstr[0] != '.')
{
for (y=10; y<130; y=y+8)
for (x=10; x<130; x=x+8)
pat8(x, y);
fprintf(stdout, "Pause: ");
fgets(cstr, 80, stdin);
cls(stdin);
for (y=10; y<130; y=y+8)
for (x=10; x<130; x=x+8)
pat8b(x, y);
fprintf(stdout, "Enter '.' to quit: ");
fgets(cstr, 80, stdin);
cls(stdin);
}
} /* end main */
/* initializes the display */
initQLscr ()
{
window(stdin,440,124,35,35);
paper(stdin,7);
ink(stdin,0);
border(stdin,2,3);
mode(4);
} /* end initQLscr */
/* alternating pattern */
pat8 (x, y)
int x, y;
{
int i, j;
/* clear entire 8x8 square first */
/* then fill in black parts only */
block(stdout, 8, 8, x, y, 7);
for (i=0; i<8; i++)
for (j=0; j<8; j++)
if ((j+i)%2 == 0)
block(stdout, 1, 1, x+j, y+i, 0);
}
/* hard-coded pattern */
pat8b (x, y)
int x, y;
{
/* clear entire 8x8 square first */
/* then fill in black parts only */
block(stdout, 8, 8, x, y, 7);
block(stdout, 1, 1, x, y, 0);
block(stdout, 1, 1, x+2, y, 0);
block(stdout, 1, 1, x+4, y, 0);
block(stdout, 1, 1, x+6, y, 0);
block(stdout, 1, 1, x+1, y+1, 0);
block(stdout, 1, 1, x+3, y+1, 0);
block(stdout, 1, 1, x+5, y+1, 0);
block(stdout, 1, 1, x+7, y+1, 0);
block(stdout, 1, 1, x, y+2, 0);
block(stdout, 1, 1, x+2, y+2, 0);
block(stdout, 1, 1, x+4, y+2, 0);
block(stdout, 1, 1, x+6, y+2, 0);
block(stdout, 1, 1, x+1, y+3, 0);
block(stdout, 1, 1, x+3, y+3, 0);
block(stdout, 1, 1, x+5, y+3, 0);
block(stdout, 1, 1, x+7, y+3, 0);
block(stdout, 1, 1, x, y+4, 0);
block(stdout, 1, 1, x+2, y+4, 0);
block(stdout, 1, 1, x+4, y+4, 0);
block(stdout, 1, 1, x+6, y+4, 0);
block(stdout, 1, 1, x+1, y+5, 0);
block(stdout, 1, 1, x+3, y+5, 0);
block(stdout, 1, 1, x+5, y+5, 0);
block(stdout, 1, 1, x+7, y+5, 0);
block(stdout, 1, 1, x, y+6, 0);
block(stdout, 1, 1, x+2, y+6, 0);
block(stdout, 1, 1, x+4, y+6, 0);
block(stdout, 1, 1, x+6, y+6, 0);
block(stdout, 1, 1, x+1, y+7, 0);
block(stdout, 1, 1, x+3, y+7, 0);
block(stdout, 1, 1, x+5, y+7, 0);
block(stdout, 1, 1, x+7, y+7, 0);
}
BTW, I finally figure out how to convert a .wav file to a .p file and then into text. The last part there are two tools, one is a command line one called zxtools.exe which yields the text below and uses the special escape characters for graphics.
Code: Select all
10 FOR I=1 TO 14
20 FOR J=1 TO 15
30 PRINT "\##";
40 NEXT J
50 PRINT
60 NEXT I