!pr2
Convert Lo-Res Pictures to Hi-res....Bob Sander-Cederlof

Most Apple dot-matrix printer interfaces now include the firmware to print hi-res graphics pictures directly from a screen image.  However, most do not provide any way to print lo-res graphics pictures.  With the program presented here you can convert a lo-res graphics image into a hi-res picture, ready to be printed by your interface firmware.

Even if you don't ever plan to do such a thing, there are some neat coding tricks in the following program, which you might be able to apply to other hi-res programs.

Lines 1070-1200 demonstrate the use of my lo- to hi-res conversion program.  Notice that I started with the label "T".  I find I am using that label all the time, lately.  I think I started using it as a short mnemonic for "TEST".  It is convenient, because in the S-C Macro Assembler environment I can test the program I just assembled by typing "MGO" and the label I want to start at.  I find my fingers can now type "MGOT" without my brain even realizing it happened.

The first thing my demo does is call PLOT to create a lo-res picture.  I didn't have any real lo-res art around, so I simply drew a 4-by-4 pattern showing all 16 lo-res colors.  PLOT fills 16 (4x4) pixels with color 0, 16 with color 1, and so on:

lo-res   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15

   0     0  0  0  0  4  4  4  4  8  8  8  8  C  C  C  C
   1     0  0  0  0  4  4  4  4  8  8  8  8  C  C  C  C
   2     0  0  0  0  4  4  4  4  8  8  8  8  C  C  C  C
   3     0  0  0  0  4  4  4  4  8  8  8  8  C  C  C  C
    ----
  15     3  3  3  3  7  7  7  7  B  B  B  B  F  F  F  F

The rest of the lo-res screen I did not change, so it will normally show the lo-res equivalent of whatever text was on the screen.  Of course if you were really trying to use my CONVERT program you would draw your real lo-res picture.

Lines 1090-1120 turn on the lo-res graphics display, and line 1130 waits until I press a key on the keyboard.  After running this much of the program, and studying the dot patterns on the screen, I realized that it is not possible to exactly reproduce the lo-res colors on the hi-res screen (unless I used //e or //c double hi-res).  However, by mixing various patterns of dots within the 28 dots (7x4) each lo-res pixel maps to, I can come close to the same color.  I don't really know how close I came, because I do not have a color monitor.  However, I can at least tell by inspection that all 16 colors map to different dot patterns that will be distinguishable colors.

The PAUSE.FOR.ANY.KEY subroutine will return EQ status if the key I press is RETURN, and NE status if it is any other key.  Line 1140 will terminate my test program if RETURN was typed.  If it was not RETURN, line 1150 turns on hi-res graphics and line 1160 calls the convert program.  Then line 1170 waits for another keypress.  Again, RETURN will terminate the test, and any other key will flop back to let me see the lo-res display again.  Line 1190 turns the text display back on.

CONVERT is very straightforward.  The outer loop, using the X-register, runs from 23 down to 0.  This corresponds to the 24 text lines on the screen, or 48 lo-res rows.  If your lo-res picture does not use the bottom 4 lines (8 rows), change line 1300 to "LDX #19".

The inner loop, using the Y-register, runs from 39 down to 0, corresponding to the 40 columns of lo-res pixels.  Each of the 960 bytes addressed by X and Y contains two lo-res pixels.  The top lo-res row (HLIN 0,39 AT 0) is in the low-order nybble of each of 40 bytes starting at $400.  The second row (HLIN 0,39 AT 1) is in the high-order nybble of the same 40 bytes.  The third and fourth rows are in the 40 bytes starting at $480, and so on.  The starting addresses for each row-pair are exactly the same as those for the 24 lines of the text screen.  They also happen to be very closely related to the starting addresses for the corresponding rows on the hi-res screen.

I stored the 24 starting addresses in two tables, LOL and LOH.  LOL contains the low-half of each address, and LOH the high.  Lines 1320-1360 pick up the base address for the current row- pair and put it in pointer LBAS.  Lines 1340 and 1370-1380 set up a similar pointer for the hi-res screen.  Note that the only difference is that the lo-res screen starts at $400, and the hi-res screen starts at $2000.  This address points at the first byte (first seven dots) of the top line of the eight hi- res lines that are in the same position as the lo-res row-pair.

Each lo-res pixel will be mapped to four lines on the hi-res screen, and will be seven dots (or one byte) wide.  Each of the 960 lo-res bytes has two pixels, so each byte uses eight lines on the hi-res screen.  The right lo-res nybble will be the top four lines, and the left nybble will be the bottom four.  After studying the tables of hi-res addresses, I noticed that each set of eight lines follow a very regular pattern.  Given the address for the leftmost byte of the top line of a set of eight lines, I can compute the addresses for the next seven lines by successively adding 4 to the high byte of the address.  Thus the base addresses for the first eight lines are $2000, $2400, $2800, $2C00, $3000, $3400, $3800, and $3C00.  I can always get the base address for the first of the eight by subtracting $400 and adding $2000 to the corresponding lo-res pointer.  Line 1370 does that operation in one step with "EOR #$24".

Lines 1400-1480 pick up the current lo-res byte and feed first the right nybble and then the left nybble to PROCESS.NYBBLE.  For indexing purposes I multiply the nybble by 8, so that the lo-res color is in the A-register like this:  xCCCCxxx.  More on that later.  Lines 1490-1530 are the south ends of the two nested loops, equivalent to NEXT Y and NEXT X.  By the way, please don't get confused by the terms Y and X.  They refer in my program to 6502 registers, not Cartesian coordinates.  Just to keep your minds nimble, I use the Y-register for the X-coordinate.  The X-register is half the lo-res Y-coordinate.

I mentioned above the problem of coming up with patterns of 28 dots to approximate the lo-res colors.  There are only six solid hi-res colors, which correspond exactly to lo-res colors 0, 3, 6, 9, 12, and 15.  The other 10 lo-res colors take double the normal hi-res resolution to reproduce exactly.  However, as Don Lancaster explains in detail in his "Enhancing Your Apple II -- Volume I", you can produce thousands of shades in hi-res by using dot patterns.  I picked 12 of his patterns based on the names he gave them, since I did not have a color monitor.  His patterns fit in a 28-dot by two line array.  Since each byte stores seven dots, it takes 28 dots before the some of the patterns repeat.  Using two lines with different or offset patterns gives even more variety.

The table SHADES in lines 1900-2050 give sixteen patterns.  The first four bytes of each color are for the first line of 28 dots, and the other four bytes give the second line of 28 dots.  Each lo-res pixel will use only one pair of bytes from the set of eight, depending on which column it is in.  The last two bits of the lo-res column number (in the Y-register) select which byte pair to use.

Lines 1650-1700 build an index to the byte pair by merging those two bits with the color*8.  Then by addressing "SHADE,X" I get the first byte of a pair, and by using "SHADE+4,X" I get the second one.  Each lo-res pixel will use the four hi-res bytes by repeating the pair selected from SHADES.

The rest of the code in PROCESS.NYBBLE involves putting the selected color bytes into the hi-res area.  HBAS points at the top line of the four to be stored into, and the Y-register points to the byte on that line; so "STA (HBAS),Y" will store into that byte.  COMMON.CODE (so named because of a lack of creativity on my part this morning when I discovered that the same eight lines appeared twice) gets and puts two color bytes.  The first byte goes into (HBAS),Y; then I add 4 to the high byte of HBAS (since I KNOW it is zero, ORA can be used to add the bit) and store the second byte at the new (HBAS),Y.  The "EOR #$0C" at line 1720 changes $24 to $28 or $34 to $38.  Similarly, the "EOR #$1C" at line 1750 changes $2C to $30 or $3C to $20.  This last possibility leaves HBAS prepared for the next column, automatically!

Some of the same tricks could be used in writing a program to copy text from the text screen to the hi-res graphics screen, or for a general purpose routine to write characters onto the hi-res screen.  Instead of using a color map, we would need a table of dot-matrix characters.  Maybe this is just how everyone does it, but I don't recall seeing all of these tricks in any previous code.  Especially the idea of getting the hi-res base pointer by merely toggling two bits in the equivalent text pointer, and the idea of generating successive hi-res pointers by merely adding 4 to that base pointer.

When I wrote this program I wasn't really worrying about speed or space.  Nevertheless, as you can see, it is fairly compact.  As for speed, it takes less than a second.
