tschak909 Posted June 11, 2018 Share Posted June 11, 2018 I am smack dab in the middle of implementing a new PLATO terminal for all 8-bit machines (including the Atari), and I'm trying to see how the font-resizing is done on the fly from the original 8 16-bit words (8x16) font to the target size of (5x6), so that I can reimplement it in C. PLATO terminals can, and do download fonts from the central system, they receive them as a series of memory write commands that write into the character set memory. The PLATO cartridge has routines which translate these addresses (0x3800) into relative offsets (0x0000, 0x0001, etc.) and I understand how that works... what I'm curious about, is that the PLATO cartridge seems to be doing image processing to squeeze the character set bitmaps down to fit in the allotted space as the download occurs. How is this happening? Relevant code is here: https://github.com/jduerstock/tlp/blob/master/tlp.asm#L2589 -Thom Quote Link to comment Share on other sites More sharing options...
tschak909 Posted June 11, 2018 Author Share Posted June 11, 2018 For cross reference, here is an earlier, but original source code listing for the Atari PLATO cart: https://paste.ubuntu.com/p/pnpkxDxc54/ Quote Link to comment Share on other sites More sharing options...
tschak909 Posted June 12, 2018 Author Share Posted June 12, 2018 As luck would have it, 16kram has been reading through this routine as part of figuring out why character set corruption happens everywhere else except Altirra, and has documented the whole routine. This thing is insane! https://github.com/michaelsternberg/tlp/blob/592be5172c3434409db28e7895937ebd2e070a93/tlp.asm#L2620 -Thom 1 Quote Link to comment Share on other sites More sharing options...
Rybags Posted June 13, 2018 Share Posted June 13, 2018 Have to wonder how such a transform looks, it's a pretty big reduction. I was thinking about a similar approach for doing a Star Wars type scroller - the problem there being that the number of tables needed could potentially be huge. Quote Link to comment Share on other sites More sharing options...
phaeron Posted June 13, 2018 Share Posted June 13, 2018 This mainly looks like box filtering + thresholding. Probably could be tightened up quite a bit as there's a bunch of X/Y addressing that looks unnecessary. Quote Link to comment Share on other sites More sharing options...
tschak909 Posted June 13, 2018 Author Share Posted June 13, 2018 Have to wonder how such a transform looks, it's a pretty big reduction. I was thinking about a similar approach for doing a Star Wars type scroller - the problem there being that the number of tables needed could potentially be huge. It actually works, you should try the PLATO cartridge sometime. Come over to my website. -Thom 1 Quote Link to comment Share on other sites More sharing options...
tschak909 Posted July 20, 2018 Author Share Posted July 20, 2018 (edited) can I convince somebody to take a whack at turning this into C? I've been attempting to visualize the algorithm in my head enough to do so and my head is seizing up.. could be my life's external stress, though... this is the LAST major piece that I need to implement so that PLATOTerm can be finished... (this is another way of saying, help, I suck at this type of algorithm work) -Thom Edited July 20, 2018 by tschak909 Quote Link to comment Share on other sites More sharing options...
tschak909 Posted July 27, 2018 Author Share Posted July 27, 2018 So far, have gotten the pixel transposition working, and algorithm A is working: transpose: // Transpose character data. for (curr_word=0;curr_word<8;curr_word++) { temp_word=word_data[curr_word]; for (u=16; u-->0; ) { if (temp_word & 1<<u) { pix_cnt++; PIX_WEIGHTS[TAB_0_25[TAB_0_5[u]]+TAB_0_4[curr_word]]++; char_data[u]|=BTAB[curr_word]; } } } printf("pix_cnt %d\n",pix_cnt); Algorithm A: if (pix_cnt <= 54 || pix_cnt < 85) { // Algorithm A - approx Half of pixels are set printf("using algorithm A\n\n"); for (v=6; v-->0; ) { for (w=5; w-->0; ) { if (PIX_WEIGHTS[TAB_0_25[v]+w] >= PIX_THRESH[TAB_0_25[v]+w]) shrunk_char_data[v]|=BTAB[w]; } } } Algorithm A is used when approximately half of the 128 possible pixels in the original bitmap are filled. Algorithm B is used for everything else, either sparsely populated bitmaps, or densely populated ones... but I'm getting thrown for a loop because the Y index is being rewritten...am trying to not just blindly copy the algorithm, but understand what's going on, and why... (any insight anyone could give would be helpful...) -Thom Quote Link to comment Share on other sites More sharing options...
tschak909 Posted July 27, 2018 Author Share Posted July 27, 2018 I tried a first pass at algorithm B and tried to hook it up to the terminal code, with the following code: /** * PLATOTerm64 - A PLATO Terminal for the Commodore 64 * Based on Steve Peltz's PAD * * Author: Thomas Cherryhomes <thom.cherryhomes at gmail dot com> * * terminal_char_load.c - Character set loading routine for 5x6 font. */ #include <string.h> #include "../terminal.h" #include "../protocol.h" // Temporary PLATO character data, 8x16 matrix static unsigned char char_data[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; static unsigned char MTAB[]={0x7F,0xBf,0xDF,0xEF,0xF7,0xFB,0xFD,0xFE}; // flip one bit off (AND) static unsigned char BTAB[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; // flip one bit on (OR) static unsigned char BTAB_5[]={0x08,0x10,0x10,0x20,0x20,0x40,0x80,0x80}; // flip one bit on for the 5x6 matrix (OR) static unsigned char TAB_0_5[]={0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02, 0x03,0x03,0x04,0x04,0x04,0x05,0x05,0x05}; // return 0..5 given index 0 to 15. static unsigned char TAB_0_4[]={0x00,0x00,0x01,0x02,0x02,0x03,0x03,0x04}; // return 0..4 given index 0 to 7 static unsigned char PIX_THRESH[]={0x03,0x02,0x03,0x03,0x02, // Pixel threshold table. 0x03,0x02,0x03,0x03,0x02, 0x02,0x01,0x02,0x02,0x01, 0x02,0x01,0x02,0x02,0x01, 0x03,0x02,0x03,0x03,0x02, 0x03,0x02,0x03,0x03,0x02}; static unsigned char PIX_WEIGHTS[]={0x00,0x00,0x00,0x00,0x00, // Pixel weights 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00}; static unsigned char TAB_0_25[]={0,5,10,15,20,25}; // Given index 0 of 5, return multiple of 5. static unsigned char pix_cnt; // total # of pixels static unsigned char curr_word; // current word static unsigned char u,v,w; // loop counters extern unsigned char fontm23[768]; extern unsigned short fontptr[160]; /** * terminal_char_load - Store a character into the user definable * character set. */ void terminal_char_load(padWord charnum, charData theChar) { // Transpose character data. for (curr_word=0;curr_word<8;curr_word++) { for (u=16; u-->0; ) { if (theChar[curr_word] & 1<<u) { pix_cnt++; PIX_WEIGHTS[TAB_0_25[TAB_0_5[u]]+TAB_0_4[curr_word]]++; char_data[u]|=BTAB[curr_word]; } } } // Determine algorithm to use for number of pixels. // Algorithm A is used when roughly half of the # of pixels are set. // Algorithm B is used either when the image is densely or sparsely populated (based on pix_cnt). if ((pix_cnt <= 54) && (pix_cnt < 85)) { // Algorithm A - approx Half of pixels are set for (v=6; v-->0; ) { for (w=5; w-->0; ) { if (PIX_WEIGHTS[TAB_0_25[v]+w] >= PIX_THRESH[TAB_0_25[v]+w]) fontm23[fontptr[charnum]+v]|=BTAB[w]; } } } else if ((pix_cnt < 54) || (pix_cnt >= 85)) { // Algorithm B - Sparsely or heavily populated bitmaps for (v=16; v-->0; ) { for (w=8; w-->0; ) { if (char_data[v] & (1<<w)) { fontm23[fontptr[charnum]+v]|=BTAB_5[w]; } } } } } Tried in my test harness and got: pix_cnt 66 using algorithm A 11111100 11111100 01111110 01111110 00111111 00111111 00011111 00011111 00001111 00001111 00000111 00000111 00000011 00000011 00000001 00000001 ----------- |5|3|6|4|0| |1|3|6|6|2| |0|0|4|4|2| |0|0|2|4|2| |0|0|0|5|3| |0|0|0|1|3| ----------- 11110000 01111000 00111000 00111000 00011000 00001000 but terminal output gives me: shit. -Thom Quote Link to comment Share on other sites More sharing options...
_The Doctor__ Posted July 27, 2018 Share Posted July 27, 2018 (edited) At first I thought you were concentrating on getting the Atari 8 bit version improved and breaking it free of some embedded serial issues, but it seems as if your kind of doing a little of this a little of that and work harder on getting this to other platforms.... in all honesty you might have better luck going after the Apple 2 as a target port... it just seems to work out better tackling a move in that direction and then swinging round to the c64.... If you can keep it all straight- that in itself would be amazing... so many directions at once... I know you want it all and I hope you find the right people to help out. I really think you need some mathematicians to help you out, more so than some of us code monkeys. My suggestion is to ask some colleges that focus on these areas to give it a go... you will get a handful of choices and some better insight from those that are immersed in it, I'm sure a prof or two would find such things fun as well. all that being said it's still nice to follow along and get some thoughts and a look at what you are up to. Edited July 27, 2018 by _The Doctor__ 2 Quote Link to comment Share on other sites More sharing options...
tschak909 Posted July 27, 2018 Author Share Posted July 27, 2018 *takes-a-deep-breath* (tries not to get irritated, please give me some latitude as I have been churning through THIS ONE ALGORITHM for weeks, now, while also porting this to 12 platforms AT THE SAME TIME.) I've been porting this to multiple platforms, using the same core C code, this has been, for the most part, successful. The LAST part of the functionality that I needed, was LITERALLY the custom character set loading/scaling, which I am tackling, now. It works on the Apple II: It works on the Commodore 64: It works on the Commodore 128 with 64k of VDC ram to provide a 512x512 interlaced full PLATO screen: There is a preliminary bring-up on the Atari, but the serial driver needs to be relocated: and more are coming...all from the same C code, all being built for every damned 8-bit and 16-bit system that I can construct a toolchain for. I do appreciate the advice, but I've got a mission here, and I am getting shit done, I am asking for help so that it can be done SOONER, rather than LATER, and at this point need far more productive answers. -Thom 3 Quote Link to comment Share on other sites More sharing options...
_The Doctor__ Posted July 27, 2018 Share Posted July 27, 2018 (edited) getting quick help from some mathematicians at some colleges to get it done was not going to be productive?... they're way smarter at algorithms, encrypting, decryption, a whole multitude of other things. - Yes working on everything except that last one... got it. and since I saw no answers posted for your current conundrum I thought it helpful to direct you to a group or crew of folks who live for this sort of thing. Sorry to have irritated you... I am certain you would have known everything there was to know and was in the learning phone without any nudges. I actually had some one interested in helping you from Lafayette College (his name is Michael), after he read your post he basically walked away... good job! I'm taken aback and don't know what to say, other than best wishes and good luck. Edited July 27, 2018 by _The Doctor__ Quote Link to comment Share on other sites More sharing options...
tschak909 Posted July 27, 2018 Author Share Posted July 27, 2018 (edited) Now you're just being full of it. Seriously, don't say "I was gonna give some help but you said something I didn't like, so NYAH!" ... I smell stuff like this from people, and I call it out, reflexively.If you had ACTUALLY READ the initial source code I posted, you'd see that I DO _NOT_ need a mathematician to make sense of this. I needed a read-out of the algorithm. It's fine, I got along without it, but I was trying to get some help, so that I didn't need to spend two and a half weeks churning through it on paper, to understand what was going on. As I said, I have multiple speaking engagements, am doing (QUITE SUCCESSFULLY) multiple ports of the software (FROM THE SAME CODEBASE), and am having to slice my time ultra thin to pull this all off. All of the things a mathematician would be needed for, have already been done, in the algorithm description... even Avery was able to indicate the nature of the algorithm, which actually helped a great deal, as it gave me a direction to do a ton of research into the backgrounds of how such algorithms worked... turned out that the algorithm presented here is highly simplified from what is usually presented in most examples on the subject. what you're saying, made zero sense. -Thom Edited July 27, 2018 by tschak909 Quote Link to comment Share on other sites More sharing options...
_The Doctor__ Posted July 27, 2018 Share Posted July 27, 2018 (edited) If I knew this was all about you pounding your chest and letting everyone know how wonderful and successful you are I wouldn't have bothered at all sorry again... your right I didn't read everything you posted and to be honest part way through your last post I just skipped to make this reply. I am certain you didn't need any of us really..... I think you have everything and everyone you need. Judging by what little I have read and you last partially read post you didn't need anything at all... your just thinking out loud... such a tolerant and understanding individual such as yourself should be able to forgive these transgressions..... so you needed some one to skim through it and tell you what they thought it was doing, resembled, or looked like.... okay, avery did that. but you also posted that it wasn't as expected... followed by the ever colorful expletive 'sh*t' and since you are frustrated at the failed output, feel free to take it out on me.... I thought you needed help, and I took it upon myself to find someone to help you with the math and code... since he clearly isn't interested in helping you now. And I had the pleasure of your impassioned response, I can only apologize for over stepping some bounds I clearly was unaware of. NO ONE WAS TRYING TO STEAL YOUR THUNDER! Good day, _The Door Mat__ Edited July 27, 2018 by _The Doctor__ Quote Link to comment Share on other sites More sharing options...
tschak909 Posted July 28, 2018 Author Share Posted July 28, 2018 Looks like I'm almost there. So far, just an edge case that's not quite right. Given the following code: /** * PLATOTerm64 - A PLATO Terminal for the Commodore 64 * Based on Steve Peltz's PAD * * Author: Thomas Cherryhomes <thom.cherryhomes at gmail dot com> * * terminal_char_load.c - Character set loading routine for 5x6 font. */ #include <cbm.h> #include <c64.h> #include <string.h> #include <stdio.h> #include <stdarg.h> #include "../terminal.h" #include "../protocol.h" // Temporary PLATO character data, 8x16 matrix static unsigned char char_data[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; static unsigned char BTAB[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; // flip one bit on (OR) static unsigned char BTAB_5[]={0x08,0x10,0x10,0x20,0x20,0x40,0x80,0x80}; // flip one bit on for the 5x6 matrix (OR) static unsigned char TAB_0_5[]={0x05,0x05,0x05,0x04,0x04,0x04,0x03,0x03,0x02,0x02,0x01,0x01,0x01,0x00,0x00,0x00}; static unsigned char TAB_0_5i[]={0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02,0x03,0x03,0x04,0x04,0x04,0x05,0x05,0x05}; static unsigned char TAB_0_4[]={0x00,0x00,0x01,0x02,0x02,0x03,0x03,0x04}; // return 0..4 given index 0 to 7 static unsigned char PIX_THRESH[]={0x03,0x02,0x03,0x03,0x02, // Pixel threshold table. 0x03,0x02,0x03,0x03,0x02, 0x02,0x01,0x02,0x02,0x01, 0x02,0x01,0x02,0x02,0x01, 0x03,0x02,0x03,0x03,0x02, 0x03,0x02,0x03,0x03,0x02}; static unsigned char PIX_WEIGHTS[]={0x00,0x00,0x00,0x00,0x00, // Pixel weights 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00}; static unsigned char TAB_0_25[]={0,5,10,15,20,25}; // Given index 0 of 5, return multiple of 5. static unsigned char pix_cnt; // total # of pixels static unsigned char curr_word; // current word static unsigned char u,v; // loop counters extern unsigned char fontm23[768]; extern unsigned short fontptr[160]; /** * terminal_char_load - Store a character into the user definable * character set. */ void terminal_char_load(padWord charnum, charData theChar) { // Clear char data. memset(char_data,0,sizeof(char_data)); memset(PIX_WEIGHTS,0,sizeof(PIX_WEIGHTS)); memset(&fontm23[fontptr[charnum]],0,6); // Transpose character data. for (curr_word=0;curr_word<8;curr_word++) { for (u=16; u-->0; ) { if (theChar[curr_word] & 1<<u) { pix_cnt++; PIX_WEIGHTS[TAB_0_25[TAB_0_5[u]]+TAB_0_4[curr_word]]++; char_data[u^0x0F&0x0F]|=BTAB[curr_word]; } } } // Determine algorithm to use for number of pixels. // Algorithm A is used when roughly half of the # of pixels are set. // Algorithm B is used either when the image is densely or sparsely populated (based on pix_cnt). if ((54 <= pix_cnt) && (pix_cnt < 85)) { // Algorithm A - approx Half of pixels are set for (u=6; u-->0; ) { for (v=5; v-->0; ) { if (PIX_WEIGHTS[TAB_0_25[u]+v] >= PIX_THRESH[TAB_0_25[u]+v]) fontm23[fontptr[charnum]+u]|=BTAB[v]; } } } else if ((pix_cnt < 54) || (pix_cnt >= 85)) { // Algorithm B - Sparsely or heavily populated bitmaps for (u=16; u-->0; ) { for (v=8; v-->0; ) { if (char_data[u] & (1<<v)) { fontm23[fontptr[charnum]+TAB_0_5i[u]]|=BTAB_5[v]; } } } } } This produces: But certain bit patterns produce weird stipple like patterns (the N, the P, etc..), as seen here: Versus: Anyone have an idea why this would be happening? I'm going to keep churning through it. -Thom 1 Quote Link to comment Share on other sites More sharing options...
Kyle22 Posted July 29, 2018 Share Posted July 29, 2018 (edited) Could it be related to character cell size and the algorithm? Math pack floating point error? Edited July 29, 2018 by Kyle22 Quote Link to comment Share on other sites More sharing options...
_The Doctor__ Posted July 29, 2018 Share Posted July 29, 2018 (edited) The relationship of the starting char set grid row and columns from one computer to another requires the transform algorithm to be tweaked and the preference affinity be adjusted. While a troglodyte such as myself couldn't possibly know anything about such things, the precision of the math pack (if floating point is used could affect the output) but that's a theoretical thing not necessarily directed about anything in this instance. It is an outlier. When looking at the output I see choices that do not match up possibly because it's origin 'tlp perhaps' knows nothing of the extra pixels (cells) involved. Or for some machines, the lack of (pixels) cells involved. it might also be noted the typography from machine to machine is changed, as the normal rules for the Atari were changed using the char grid from edge to edge. while it might not be completely useful, you might find this page interesting, and the comments below from some others who were in ranks on this sort of thing... be advised that remembrance is not always perfect and there may be a misstatement or two (please forgive that) but it close enough to shed light on some fascinating font finesse https://damieng.com/blog/2011/02/20/typography-in-8-bits-system-fonts the percentage of pixels in each character grid is different because the ratio has changed depending on the target machine, the choice for which method of transform by pixel count may need to be adjusted. While I could go on further, I'm probably just full of it or something and really shouldn't post such things. It's just my empty head dreaming up stuff I'm sure. My memory is not the best, and I really haven't looked at the math too deeply, might be some mistake.... Edited July 29, 2018 by _The Doctor__ Quote Link to comment Share on other sites More sharing options...
tschak909 Posted August 2, 2018 Author Share Posted August 2, 2018 16kRAM took a whack at the algorithm, and I folded in the functional changes... The sparse characters now mostly render correctly, but those that are borderline dense now completely fail due to the xor...which is odd, because the algorithm deliberately tries to account for that... https://gist.github.com/tschak909/ad4542db2114c34c279c3a6d3fb03776 compared to TLP: I am documenting the @#(%@#(% out of this for others to understand... 2 Quote Link to comment Share on other sites More sharing options...
tschak909 Posted January 9, 2019 Author Share Posted January 9, 2019 I am tacking another whack at this, Again, for reminder, original code is here: ;******************************************************************************* ;* * ;* load_memory * ;* * ;* Receive Downloaded Character Set * ;* * ;******************************************************************************* ; DESCRIPTION ; This routine converts the 16x8 bitmap of a PLATO programmable character set ; into a 5x6 bitmap for the full-screen display and an 8x12 bitmap for the ; zoomed display. ; ; VAR: Which of the 8 PLATO_WORDs in the 8x16 bitmap is currently being ; processed (set in caller). ; TMP: Number of pixels set ("on") in the 8x16 bitmap ; SUM: Pointer to 5x6 bitmap used in full-screen display ; YOUT: Initially, pointer to transposed version of original 8x16 PLATO bitmap. ; In the end, pointer to 8x12 bitmap used in zoomed display ; The first operation is to transpose the 16x8 bitmap to an 8x16 bitmap. The ; lowest pixel is shifted to the carry register. If the pixel is unset, then the ; loop skips to its next iteration. Otherwise the corresponding bit is set in ; (YOUT),Y using the mask table MTAB to first unset the one pixel for the current ; bitfield (row) and later the table BTAB to set the one pixel. ; ; ; (YOUT),Y ; X --> 7 6 5 4 3 2 1 0 ; +-+-+-+-+-+-+-+-+ ; Y--> F | | | | | | | |o| ; <-PLATO_WORD+1-><--PLATO_WORD--> E | | | | | | | |o| ; Y--> 0 1 2 3 4 5 6 7 8 9 A B C D E F D | | | | | | |o|o| ; +-------------------------------+ C | | | | | | |o|o| ; VAR--> 0 |o:o: : : : : : : : : : : : : : | B | | | | | |o|o|o| ; 1 |o:o:o:o: : : : : : : : : : : : | A | | | | | |o|o|o| ; 2 |o:o:o:o:o:o: : : : : : : : : : | 9 | | | | |o|o|o|o| ; 3 |o:o:o:o:o:o:o:o: : : : : : : : | 8 | | | | |o|o|o|o| ; 4 |o:o:o:o:o:o:o:o:o:o: : : : : : | 7 | | | |o|o|o|o|o| ; 5 |o:o:o:o:o:o:o:o:o:o:o:o: : : : | 6 | | | |o|o|o|o|o| ; 6 | : :o:o:o:o:o:o:o:o:o:o:o: : | 5 | | |o|o|o|o|o|o| ; 7 | : : : :o:o:o:o:o:o:o:o:o:o:o| 4 | | |o|o|o|o|o|o| ; +-------------------------------+ 3 | |o|o|o|o|o|o| | ; 2 | |o|o|o|o|o|o| | ; 1 |o|o|o|o|o|o| | | ; 0 |o|o|o|o|o|o| | | ; +-+-+-+-+-+-+-+-+ ; ; During the transposition process, a sequence of table lookups are used to ; define groupings of pixels (illustrated below in the (YOUT,Y) table). The number ; of pixels found in each grouping is tallied and stored in the table at $3E10. ; ; A lookup table at $LB9D6 contains a threshold for each pixel group. If the ; tally for the source pixel group is greater than or equal to the corresponding ; threshold then a pixel is set in the resulting 5x6 bitmap. ; ; (YOUT,Y) ; Table Groupings of ; Y LB9BE,Y orig PLATO ; | | 8x16 bitmap LB9F4,X (X=LB9BE,Y) ; v v 7 6 5 4 3 2 1 0 | Table Table ; +---+-+---+---+-+ | $3E10,X LB9D6,X ; F 5 | | | | |o| | (X=LB9F4,X + LB9CE,X (0..29)) ; E 5 | | | | |o| | ; D 5 | | | | o|o| | Tallies Thresholds ; +---+-+---+---+-+ v 4 3 2 1 0 4 3 2 1 0 ; C 4 | | | | o|o| +-+-+-+-+-+ +-+-+-+-+-+ ; B 4 | | | |o o|o| 0 |0|0|0|1|3| 0 |3|2|3|3|2| ; A 4 | | | |o o|o| +-+-+-+-+-+ +-+-+-+-+-+ ; +---+-+---+---+-+ 5 |0|0|0|5|3| 5 |3|2|3|3|2| ; 9 3 | | | o|o o|o| +-+-+-+-+-+ +-+-+-+-+-+ ; 8 3 | | | o|o o|o| 10 |0|0|2|4|2| >=? 10 |2|1|2|2|1| ; +---+-+---+---+-+ +-+-+-+-+-+ +-+-+-+-+-+ ; 7 2 | | |o o|o o|o| 15 |0|0|4|4|2| 15 |2|1|2|2|1| ; 6 2 | | |o o|o o|o| +-+-+-+-+-+ +-+-+-+-+-+ ; +---+-+---+---+-+ 20 |1|3|6|6|2| 20 |3|2|3|3|2| ; 5 1 | |o|o o|o o|o| +-+-+-+-+-+ +-+-+-+-+-+ ; 4 1 | |o|o o|o o|o| 25 |5|3|6|4|0| 25 |3|2|3|3|2| ; 3 1 | o|o|o o|o o| | +-+-+-+-+-+ +-+-+-+-+-+ ; +---+-+---+---+-+ <-5 bytes-> <-5 bytes-> ; 2 0 | o|o|o o|o o| | ; 1 0 |o o|o|o o|o | | | | ; 0 0 |o o|o|o o|o | | \| |/ ; +---+-+---+---+-+ \ / ; <--- 1 byte ----> v ; ; (SUM),Y ; Resulting ; 5x6 bitmap ; 7 6 5 4 3 2 1 0 ; +-+-+-+-+-+-+-+-+ ; 5 | | | | |o| | | | ; +-+-+-+-+-+-+-+-+ ; 4 | | | |o|o| | | | ; +-+-+-+-+-+-+-+-+ ; 3 | | |o|o|o| | | | ; +-+-+-+-+-+-+-+-+ ; 2 | | |o|o|o| | | | ; +-+-+-+-+-+-+-+-+ ; 1 | |o|o|o|o| | | | ; +-+-+-+-+-+-+-+-+ ; 0 |o|o|o|o| | | | | ; +-+-+-+-+-+-+-+-+ ; <--- 1 byte ----> ; ;------------------------------------------------------------------------------- ; (excerpt from s0ascers 3.2.3.1.2.3) ;------------------------------------------------------------------------------- ; Load character set mode is initiated by the escape sequence ESC P (1B 50 hex). ; Each character consists of eight words of data. Each word is a vertical strip ; of the 16 high by 8 wide character. Bit one is at the bottom and bit 16 is at ; the top when the character is displayed on the screen. Many terminals' screen ; memories are not mapped to accommodate display of characters in this format, ; so it may be desirable to reformat the characters so that they are 16 bytes of ; eight-bit horizontal strips. The reformatting should take place after each ; character is received (i.e., after every eight words). ; ; The following is an example of a character and the data ; words that would be transmitted from PLATO to form it: ; ; Bit ; --------------- ; 16 | | | | | | | | | ; 15 | | | | | | | | | ; 14 | | | | | | | | | ; 13 | | | | | | | | | ; 12 | | | | | | | | | ; 11 | | | | | | | | | ; 10 | |o|o|o|o| | | | ; 9 | | | | | |o| | | ; 8 | |o|o|o|o|o| | | ; 7 |o| | | | |o| | | ; 6 |o| | | | |o| | | ; 5 | |o|o|o|o| |o| | ; 4 | | | | | | | | | ; 3 | | | | | | | | | ; 2 | | | | | | | | | ; 1 | | | | | | | | | ; --------------- ; ; Word 1 2 3 4 5 6 7 8 ; ; The eight words for this character are (in hex): ; ; 0060 ; 0290 ; 0290 ; 0290 ; 0290 ; 01E0 ; 0010 ; 0000 ; ; The programmable character sets are not always completely ; utilized. If there are gaps in the character set, new load ; memory address commands will be sent to skip over the holes. ; The resident must account for this when receiving character ; sets. ;------------------------------------------------------------------------------- load_memory: ; A6F6 jsr unpack_word ; Get PLATO_WORD ; Let X = current word being processed (8 16-bit words per character) ; Let Y = loop for each bit in word ldx VAR ; Get current word A6F9 A6 D8 .. ldy #$0F ; Loop for each bit in word A6FB A0 0F .. ;------------------------------------------------------------------------------- ; Transpose 16x8 bitmap into 8x16 ;------------------------------------------------------------------------------- ; Shift lowest pixel into Carry @DLX: lsr PLATO_WORD+1 ; 16-bit shift ror PLATO_WORD ; Shift lowest bit into Carry ; Use MTAB,X to clear the Xth bit in (YOUT),Y lda MTAB,X ; Get mask and (YOUT),Y ; Clear Xth bit. Leave others untouched. bcc @DL0 ; No carry, no work --> A706 90 1E .. inc TMP ; Keep total number pixels set in 8x16 bitmap pha ; Stash bitfield with cleared pixel ;------------------------------------------------------------------------------- ; Assign groupings for pixels using a series of tables. Map an offset to a pixel ; in the 5x6 bitmap (0..30) using the current bit/location being processed in ; the 8x16 bitmap from PLATO. ;------------------------------------------------------------------------------- lda LB9BE,Y ; Get value 0..5 using Y from table. tax ; Use it as an index lda LB9F4,X ; Get value 0..25 (multiples of 5) from table. sta LEND ; Use it as base offset later. ldx VAR ; Which PLATO_WORD are we on? 0..7 lda LB9CE,X ; Use it to get value 0..4 from table clc ; Using 0..25 + 0..4 values... adc LEND ; derive 0..29. tax ; Use it as an index. inc $3E10,X ; Tally pixels set in current 5x6 grouping ;------------------------------------------------------------------------------- ; Back to working on transposition of 16x8 bitmap to 8x16 bitmap. ; Save current bitfield to memory ;------------------------------------------------------------------------------- pla ; Retrieve bitfield with cleared pixel from earlier ldx VAR ; Which PLATO_WORD are we on? 0..7 ora BTAB,X ; Set pixel using mask table @DL0: sta (YOUT),Y ; Save updated 8x16 bits dey ; Next Y bpl @DLX ; ;------------------------------------------------------------------------------- ; RTS after the 8 PLATO_WORDs in a 16x8 bitmap have been processed. ;------------------------------------------------------------------------------- inx ; Next PLATO_WORD cpx #$08 ; Done yet? bcs :+ ; Not yet --> stx VAR ; Let VAR = 8 rts ; ;------------------------------------------------------------------------------- ; Save data for 5x6 bitmap ; Clear 6 byte array at (SUM) that will hold the 5x6 character bitmap ;------------------------------------------------------------------------------- : lda #$00 ; Let A = 0 tax ; Let X = 0 ldy #$05 ; Let Y = 5 : sta (SUM),Y ; write zero dey ; bpl :- ; next Y ;------------------------------------------------------------------------------- ; TMP contains number of pixels set in the 8x16 bitmap ;------------------------------------------------------------------------------- lda TMP ; A73D A5 D9 .. cmp #$36 ; 54 out of 128? 2/5? A73F C9 36 .6 bcc LA771 ; TMP < 54? --> A741 90 2E .. cmp #$55 ; 85 out of 128? 2/3? A744 C9 55 .U bcs LA771 ; TMP >= 85? --> A746 B0 29 .) ;------------------------------------------------------------------------------- ; TRACK A: Here if TMP >= 54 and TMP < 85 ;------------------------------------------------------------------------------- ;** Iterate over 6 rows of 5 pixels ldy #$05 ; A748 A0 05 .. ;------------------------------------------------------------------------------- ; Outer Loop 6 times ;------------------------------------------------------------------------------- @LOOPI: ldx #$04 ; A74A A2 04 .. stx LEND ; A74C 86 AB .. ;------------------------------------------------------------------------------- ; Inner Loop 5 times ;------------------------------------------------------------------------------- ; LB9F4: .byte 0,5,10,15,20,25 ; Let X = 25+4, 25+3, .., 25+0, 20+4, 20+3, .., 20+0, .. @LOOPJ: lda LB9F4,Y ; Get multiple of 5 for outer loop A74E B9 F4 B9 ... clc ; A751 18 . adc LEND ; Add to inner loop index A752 65 AB e. tax ; Copy to X for index into lookup table A754 AA . ; Use X as index into table ; LB9D6:.byte 3,2,3,3,2 ; 0..4 ; .byte 3,2,3,3,2 ; 5..9 ; .byte 2,1,2,2,1 ; 10..14 ; .byte 2,1,2,2,1 ; 15..19 ; .byte 3,2,3,3,2 ; 20..24 ; .byte 3,2,3,3,2 ; 25..29 lda LB9D6,X ; Get Lookup A755 BD D6 B9 ... cmp $3E10,X ; Compare to pixel weight A758 DD 10 3E ..> bcc :+ ; Lookup <= weight? Set pixel --> bne :++ ; Lookup > weight? Skip pixel --> A75D D0 09 .. ; BTAB: .byte %10000000 ; $80 ; .byte %01000000 ; $40 ; .byte %00100000 ; $20 ; .byte %00010000 ; $10 ; .byte %00001000 ; $08 ; Here if Lookup <= weight ; Set pixel in 5x6 bitmap. : lda (SUM),Y ; Get current bitfield ldx LEND ; Get index (curr pixel) A761 A6 AB .. ora BTAB,X ; Set pixel A763 1D 42 B9 .B. sta (SUM),Y ; Save updated bitfield ; Here if Lookup != weight (or arriving from above) : dec LEND ; Next LEND A768 C6 AB .. bpl @LOOPJ dey ; Next Y A76C 88 . bpl @LOOPI ; A76D 10 DB .. bmi LA7AD ; Skip TRACK B --> A76F 30 3C 0< ;------------------------------------------------------------------------------- ; TRACK B: Here if TMP < 54 or TMP >= 85 ; X=$00 -> TMP < 54 ; X=$FF -> TMP >= 85 ;------------------------------------------------------------------------------- LA771: stx TMP ; A771 86 D9 .. bit TMP ; Set V if TMP >= 85 A773 24 D9 $. ;------------------------------------------------------------------------------- ; Outer Loop 16 times ;------------------------------------------------------------------------------- ldy #$0F ; Let Y = 16 A775 A0 0F .. LA777: lda (YOUT),Y ; Get original 8x16 PLATO bitfield bvc :+ ; if TMP < 54 --> eor #$FF ; if TMP >= 85 invert bits A77B 49 FF I. : sta VAR ; VAR = curr row of 8x16 PLATO bitfield A77D 85 D8 .. ;------------------------------------------------------------------------------- ; Inner Loop 8 times ;------------------------------------------------------------------------------- ldx #$07 ; Iterate 8 times (for each row in bitmap) A77F A2 07 .. lda #$00 ; A will hold bitfield. Start with clean slate. A781 A9 00 .. : rol VAR ; Pull leftmost bit off bitfield into Carry A783 26 D8 &. bcc :+ ; Is the bit set? No? --> A785 90 03 .. ;LB9B6: .byte %00001000 0 ; .byte %00010000 1 ; .byte %00010000 2 ; .byte %00100000 3 ; .byte %00100000 4 ; .byte %01000000 5 ; .byte %10000000 6 ; .byte %10000000 7 ora LB9B6,x ; Yes? Set bit in A A787 1D B6 B9 ... : dex ; A78A CA . bpl :-- ; Next X A78B 10 F6 .. ;------------------------------------------------------------------------------- ; End of Inner Loop ;------------------------------------------------------------------------------- ;LB9BE: .byte $00,$00,$00,$01,$01,$01,$02,$02 ; .byte $03,$03,$04,$04,$04,$05,$05,$05 pha ; Stash current bitfield into stack A78D 48 H lda LB9BE,Y ; Get $00..$05 from lookup table A78E B9 BE B9 ... sty VAR ; Stash outer loop index into VAR A791 84 D8 .. tay ; Move index from lookup table into Y A793 A8 . pla ; Restore current bitfield from stack A794 68 h ora (SUM),Y ; Merge current bitfield with A sta (SUM),Y ; Save 5x6 bitfield to bitmap ldy VAR ; Restore outer loop index into Y A799 A4 D8 .. dey ; A79B 88 . bpl LA777 ; Next Y A79C 10 D9 .. ;------------------------------------------------------------------------------- ; End of Outer Loop ;------------------------------------------------------------------------------- bvc LA7AD ; A79E 50 0D P. ;------------------------------------------------------------------------------- ; Loop 6 times ;------------------------------------------------------------------------------- ldy #$05 ; A7A0 A0 05 .. @LOOP: lda (SUM),Y eor #$FF ; A7A4 49 FF I. and #$F8 ; last 3 bits unused in 5x6 A7A6 29 F8 ). sta (SUM),Y ; Save bitfield for 5x6 dey ; A7AA 88 . bpl @LOOP ; Next Y --> A7AB 10 F5 .. ;------------------------------------------------------------------------------- ; End of Loop ;------------------------------------------------------------------------------- And my current C code is here: /** * PLATOTerm64 - A PLATO Terminal for the Commodore 64 * Based on Steve Peltz's PAD * * Author: Thomas Cherryhomes <thom.cherryhomes at gmail dot com> * * terminal_char_load.c - Character set loading routine for 5x6 font. */ #include <string.h> #include <stdio.h> #include <stdarg.h> #include "../terminal.h" #include "../protocol.h" #define FONTPTR(a) (((a << 1) + a) << 1) // Temporary PLATO character data, 8x16 matrix static unsigned char char_data[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; static unsigned char BTAB[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; // flip one bit on (OR) static unsigned char BTAB_5[]={0x08,0x10,0x10,0x20,0x20,0x40,0x80,0x80}; // flip one bit on for the 5x6 matrix (OR) static unsigned char TAB_0_5[]={0x05,0x05,0x05,0x04,0x04,0x04,0x03,0x03,0x02,0x02,0x01,0x01,0x01,0x00,0x00,0x00}; static unsigned char TAB_0_5i[]={0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02,0x03,0x03,0x04,0x04,0x04,0x05,0x05,0x05}; static unsigned char TAB_0_4[]={0x00,0x00,0x01,0x02,0x02,0x03,0x03,0x04}; // return 0..4 given index 0 to 7 static unsigned char PIX_THRESH[]={0x03,0x02,0x03,0x03,0x02, // Pixel threshold table. 0x03,0x02,0x03,0x03,0x02, 0x02,0x01,0x02,0x02,0x01, 0x02,0x01,0x02,0x02,0x01, 0x03,0x02,0x03,0x03,0x02, 0x03,0x02,0x03,0x03,0x02}; static unsigned char PIX_WEIGHTS[]={0x00,0x00,0x00,0x00,0x00, // Pixel weights 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00}; static unsigned char TAB_0_25[]={0,5,10,15,20,25}; // Given index 0 of 5, return multiple of 5. static unsigned char pix_cnt; // total # of pixels static unsigned char curr_word; // current word static unsigned char u,v; // loop counters extern unsigned char* fontm23; /** * terminal_char_load - Store a character into the user definable * character set. */ void terminal_char_load(padWord charnum, charData theChar) { // Clear char data. memset(char_data,0,sizeof(char_data)); memset(PIX_WEIGHTS,0,sizeof(PIX_WEIGHTS)); memset(&fontm23[FONTPTR(charnum)],0,6); // Transpose character data. for (curr_word=0;curr_word<8;curr_word++) { for (u=16; u-->0; ) { if (theChar[curr_word] & 1<<u) { pix_cnt++; PIX_WEIGHTS[TAB_0_25[TAB_0_5[u]]+TAB_0_4[curr_word]]++; char_data[u^0x0F&0x0F]|=BTAB[curr_word]; } } } // Determine algorithm to use for number of pixels. // Algorithm A is used when roughly half of the # of pixels are set. // Algorithm B is used either when the image is densely or sparsely populated (based on pix_cnt). if ((54 <= pix_cnt) && (pix_cnt < 85)) { // Algorithm A - approx Half of pixels are set for (u=6; u-->0; ) { for (v=5; v-->0; ) { if (PIX_WEIGHTS[TAB_0_25[u]+v] >= PIX_THRESH[TAB_0_25[u]+v]) fontm23[FONTPTR(charnum)+u]|=BTAB[v]; } } } else if ((pix_cnt < 54) || (pix_cnt >= 85)) { // Algorithm B - Sparsely or heavily populated bitmaps for (u=16; u-->0; ) { for (v=8; v-->0; ) { if (char_data[u] & (1<<v)) { fontm23[FONTPTR(charnum)+TAB_0_5i[u]]|=BTAB_5[v]; } } } } } I'm really at a loss, here. By all accounts, it's almost there, but there's enough going wrong that densely populated bitmaps are not being rendered correctly. -Thom Quote Link to comment Share on other sites More sharing options...
tschak909 Posted January 12, 2019 Author Share Posted January 12, 2019 After almost a year, problem is now solved. The error lie in the fact that the 6502 version of PLATOTerm is compiled with --static-globals, which eliminates the local variable stack frame for a not so small increase in performance, (and in effect, making the variable scopes exactly like ACTION!), and I was not re-initializing pix_cnt (which is used to determine which algorithm to use for pixel reduction). The result is quite marked especially when looking at stylized fonts e.g. in 0checkers: The final code snippet is here: /** * PLATOTerm64 - A PLATO Terminal for the Commodore 64 * Based on Steve Peltz's PAD * * Author: Thomas Cherryhomes <thom.cherryhomes at gmail dot com> * * terminal_char_load.c - Character set loading routine for 5x6 font. */ #include <string.h> #include <stdio.h> #include <stdarg.h> #include <peekpoke.h> #include "../terminal.h" #include "../protocol.h" /* #define FONTPTR(a) (((a << 1) + a) << 1) */ #define FONTPTR(aa) (aa*6) static unsigned char char_data[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; static unsigned char shrunk_char_data[]={0x00,0x00,0x00,0x00,0x00,0x00}; const unsigned char BTAB[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; // flip one bit on (OR) const unsigned char BTAB_5[]={0x08,0x10,0x10,0x20,0x20,0x40,0x80,0x80}; // flip one bit on for the 5x6 matrix (OR) const unsigned char TAB_0_5[]={0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02,0x03,0x03,0x04,0x04,0x04,0x05,0x05,0x05}; const unsigned char TAB_0_4[]={0x00,0x00,0x01,0x02,0x02,0x03,0x03,0x04}; // return 0..4 given index 0 to 7 const unsigned char TAB_0_25[]={0,5,10,15,20,25}; // Given index 0 of 5, return multiple of 5. const unsigned char TAB_0_25i[]={25,20,15,10,5,0}; const unsigned char PIX_THRESH[]={0x03,0x02,0x03,0x03,0x02, // Pixel threshold table. 0x03,0x02,0x03,0x03,0x02, 0x02,0x01,0x02,0x02,0x01, 0x02,0x01,0x02,0x02,0x01, 0x03,0x02,0x03,0x03,0x02, 0x03,0x02,0x03,0x03,0x02}; static unsigned char PIX_WEIGHTS[]={0x00,0x00,0x00,0x00,0x00, // Pixel weights 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00}; static unsigned char pix_cnt; static unsigned char curr_word; static unsigned char bb,cc,dd; static unsigned char algo_switch; static unsigned char flip_bits; extern unsigned char fontm23[768]; /** * Algorithm A - Approximately 54 to 85 pixels set, apply box filter */ void terminal_char_load_algo_a(void) { } /** * Algorithm B - Either less than 53 pixels, or more than 84 pixels set, use a simple * scaling table. */ void terminal_char_load_algo_b(unsigned char flip_bits) { } /** * terminal_char_load - Store a character into the user definable * character set. */ void terminal_char_load(padWord charnum, charData theChar) { // Clear char data. memset(char_data,0x00,sizeof(char_data)); memset(PIX_WEIGHTS,0,sizeof(PIX_WEIGHTS)); memset(&fontm23[FONTPTR(charnum)],0,6); pix_cnt=0; // Transpose character data. for (curr_word=0;curr_word<8;curr_word++) { for (bb=16; bb-->0; ) { if (theChar[curr_word] & 1<<bb) { pix_cnt++; PIX_WEIGHTS[TAB_0_25[TAB_0_5[bb]]+TAB_0_4[curr_word]]++; char_data[bb^0x0f]|=BTAB[curr_word]; } } } if ((54 <= pix_cnt) && (pix_cnt < 85)) { for (bb=6; bb-->0; ) { for (cc=5; cc-->0; ) { if (PIX_WEIGHTS[TAB_0_25i[bb]+cc] >= PIX_THRESH[TAB_0_25i[bb]+cc]) fontm23[FONTPTR(charnum)+bb]|=BTAB[cc]; } } } else if ((pix_cnt < 54) || (pix_cnt >= 85)) { for (bb=16; bb-->0; ) { // If dense, flip bits around. if (pix_cnt>=85) char_data[bb]^=0xff; for (cc=8; cc-->0; ) { if (char_data[bb] & (1<<cc)) fontm23[FONTPTR(charnum)+TAB_0_5[bb]]|=BTAB_5[cc]; } } // Flip the bits back if densely packed. if (pix_cnt>=85) { for (bb=6; bb-->0; ) { fontm23[FONTPTR(charnum)+bb]^=0xFF; fontm23[FONTPTR(charnum)+bb]&=0xF8; } } } } -Thom 4 Quote Link to comment Share on other sites More sharing options...
_The Doctor__ Posted January 12, 2019 Share Posted January 12, 2019 Nice to see, it's so close now.. so darn close to tlp.... Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.