Going to be working on this again for the summer just because I find it fun coding on the QL in Digital 'C's compiler. So the big ticket item to add is floating point, but before I can do that, I wanted to reclaim some code space. The executable is currently 48K in size and I don't want to have it get any bigger, as I do want at least 16K for program space (recall my goal is running ZX81 speed on an unexpanded QL).
I decided to re-write the graphics plotting function for the character set. Results show that it runs about 3% slower but I gained back over 1/6th of the code (i.e. cut away 8K to bring it down to 40K). Previously, I would plot all the lines for each character set. The code read nicely and was it was easy to follow what I did:
Code: Select all
/* prints single ZX81 font character */
ZXputc (ch, ink, pap)
char ch; int ink, pap;
{
block(stdout, 8, 8, col, row, pap);
switch (ch)
{
/* LETTERS */
case 'A':
block(stdout, 4, 1, col+2, row+1, ink);
block(stdout, 1, 5, col+1, row+2, ink);
block(stdout, 1, 5, col+6, row+2, ink);
block(stdout, 4, 1, col+2, row+4, ink);
return;
case 'B':
block(stdout, 5, 1, col+1, row+1, ink);
block(stdout, 1, 5, col+1, row+2, ink);
block(stdout, 1, 1, col+6, row+2, ink);
block(stdout, 4, 1, col+2, row+3, ink);
block(stdout, 1, 2, col+6, row+4, ink);
block(stdout, 4, 1, col+2, row+6, ink);
return;
...
/* UNKOWN is ? */
default:
block(stdout, 4, 1, col+2, row+1, ink);
block(stdout, 1, 1, col+1, row+2, ink);
block(stdout, 1, 1, col+6, row+2, ink);
block(stdout, 1, 1, col+5, row+3, ink);
block(stdout, 1, 1, col+4, row+4, ink);
block(stdout, 1, 1, col+4, row+6, ink);
return;
}
}
So I ended up creating a small loop that takes an input string where each character represents two values (so 2 characters per a block statement's 4 inputs). Each value needs 3 bits (i.e. they range from 0 to 7), making up 6 bits for the pair, and is preceded by bit sequence 01 to ensure it remains in the printable ASCII range of 64 to 127.
So the above code now looks like this:
Code: Select all
/* ZX81 font character */
ZXputc (ch, ink, pap)
char ch; int ink, pap;
{
int i, p1, p2;
char* pt;
block(stdout, 8, 8, col, row, pap);
switch (ch)
{
/* LETTERS */
case 'A':
pt = "aQMJMraT";
break;
case 'B':
pt = "iIMJIraSJtaV";
break;
...
/* UNKOWN is ? */
default:
pt = "aQIJIrIkIdIf";
}
while (pt[0])
{
p1 = pt[0];
p2 = pt[1];
block(stdout, p1 >> 3 & 7, p1 & 7, col+(p2 >> 3 & 7), row+(p2 & 7), ink);
pt = pt + 2;
}
}
Current space usage averages to 10 bytes per character for the 54 characters that include: letters, numbers, and symbols. The ZX81 character set takes up a 7x7 grid for its bitmap representation -- it's really 8x8 but one line vertically and horizontally is left blank so that characters don't run into each other. At best that's 49 bits which takes up 7 bytes, but really should be 8 bytes for the full 8x8 grid. That's not too bad space-wise for my representation, and the extra two bytes is likely caused by adding 01 to each 6-bit coordinates to ensure only printable characters in each string.
This also may lend itself for future upgrade to efficiently represent the entire ZX81 character set, since a good chunk of it is multi-character. As each pt char array (one per case) is assigned a literal value, I'm already taking up space in code for those strings. If I created an array of char arrays (i.e. and array of pt's) then I could have multiple mapping and in the future, if I wanted to spell PRINT I could just send along an index sequence into the pt array of strings and feed it to a slightly more complex while loop at the end. For now I'm just printing a ? when I don't support the character.