Jump to content
IGNORED

Need a readout on an algorithm in The Learning phone (PLATO)


Recommended Posts

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

Link to comment
Share on other sites

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

  • Like 1
Link to comment
Share on other sites

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

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

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 by tschak909
Link to comment
Share on other sites

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

Link to comment
Share on other sites

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:
korcyHH.png
shit.
-Thom
Link to comment
Share on other sites

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 by _The Doctor__
  • Like 2
Link to comment
Share on other sites

*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:

 

IHWSXrd.png

 

It works on the Commodore 64:

00PaE26.jpg

 

It works on the Commodore 128 with 64k of VDC ram to provide a 512x512 interlaced full PLATO screen:

KRQeocr.jpg

 

There is a preliminary bring-up on the Atari, but the serial driver needs to be relocated:

ZNXW43X.png

 

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

  • Like 3
Link to comment
Share on other sites

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 by _The Doctor__
Link to comment
Share on other sites

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 by tschak909
Link to comment
Share on other sites

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 by _The Doctor__
Link to comment
Share on other sites

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:

unknown.png

 

unknown.png

 

But certain bit patterns produce weird stipple like patterns (the N, the P, etc..), as seen here:

YQm7QKD.png

 

Versus:

 

sr9xO5h.png

 

 

Anyone have an idea why this would be happening? I'm going to keep churning through it.

 

-Thom

  • Like 1
Link to comment
Share on other sites

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 by _The Doctor__
Link to comment
Share on other sites

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

 

post-9462-0-99969000-1533225393_thumb.png

 

post-9462-0-75267000-1533225415_thumb.png

 

compared to TLP:

 

post-9462-0-76803200-1533225605_thumb.png

 

post-9462-0-71444500-1533226540_thumb.png

 

I am documenting the @#(%@#(% out of this for others to understand...

  • Like 2
Link to comment
Share on other sites

  • 5 months later...

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

Link to comment
Share on other sites

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:

 

unknown.png

 

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

  • Like 4
Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...