The Apple 2 (usually written Apple ][) computer was one of the first widely popular home computers. At the time, most home computers came as kits and included no keyboard or text display; you got some switches for input and blinky lights for output, and if you really wanted to spend some cash, you got a teletype machine to go with it.
So when Steve Wozniak designed an affordable home computer that actually came with a full keyboard and glowing green screen, people got pretty excited! The first computer in my house was actually a TI-99/4A (on sale for $50 at K-Mart), but we quickly outgrew that, and got an Apple //e, which served us well for years.
Recently I found myself feeling a bit nostalgic for that old green screen, and decided to see if I could recapture some of its look and feel in Mini Micro. Mini Micro itself is a "neo-retro" virtual computer, but its screen has much higher resolution (960x640) compared to the early Apples (280 x 192). This is reflected in the appearance of the text, which on Mini Micro, is normally quite sharp:
Each character on a Mini Micro text display is 24 pixels high, whereas on the Apple ][, characters were only 8 pixels high.
The Apple ][ Text Font
Somewhere on the Internet I found this image containing all the characters the Apple ][ could display:
This image is 16 columns wide, and 8 rows high, for a total of 128 characters — the complete ASCII character set. There's nothing tricky about the arrangement; each character is 8 pixels wide and high, and they're in ASCII order, starting at the top left and proceeding row by row.
In short... this is set up exactly like a tile set you might use with Mini Micro's TileDisplay!
...in a TileDisplay
To display text using characters from this image, I launched Mini Micro, reset to a clean state, and used edit to enter this code:
clear text.row = 0 display(4).mode = displayMode.tile td = display(4) td.tileSet = file.loadImage("AppleIIChars.png") td.extent = [45,26] // columns and rows of text srcW = td.tileSet.width / 16 srcH = td.tileSet.height / 8 td.tileSetTileSize = [srcW, srcH] td.cellSize = [24, 24] // size of each character on screen td.overlap = [3,0] // 3-pixel horizontal overlap td.scrollX = -7; td.scrollY = -7 td.clear "@".code Why not fire up Mini Micro yourself, and follow along?
This code sets display 4 to "tile" mode, loads the AppleIIChars image as the tile set, and tells Mini Micro how big the source tiles are, and how big those should appear on screen. I also apply a small horizontal overlap, since even though the cells are 8x8 in the image, the Apple actually used only 7 pixels (width) per character. Finally, I set scrollX and scrollY to center the tiles on the screen, since there is a bit of extra space around the edges that we can't quite use.
The line td.clear "@".code tells the TileDisplay to clear to the at-sign symbol. (The code method gets the ASCII code for the first character of the string it's called on.) The result looks like this:
Not a bad start!
Making an a2 module
Next, I wanted some code that lets me start manipulating this TileDisplay with Applesoft-like commands. To keep things neat, I put these functions into a map called a2 — that way it's easy to tell the difference between a2.print and regular print.
But the first function we want is something to clear the screen, and in Applesoft, that was called HOME (because it both cleared the screen and returned the cursor to its home position in the top-left corner). So:
a2 = {} a2.home = function() td.clear a2.row = td.extent[1] - 1 a2.col = 0 xrange = range(0, td.extent[0]) yrange = range(0, td.extent[1]) td.setCellTint xrange, yrange, "#22CC22" end function a2.home Notice the other trick this code does: it tints the entire display green (color "#22CC22"), to match the appearance of the original Apple green screens.
If you're following along at home, and run your program at this point, it will appear to be doing not much — the screen just clears, and you're left with a standard Mini Micro prompt. Our tile display is there, but it's invisible. We need a bit more code!
a2.nextLine = function() self.col = 0 self.row = self.row - 1 end function a2.putchar = function(c) i = c.code if i > 127 then i = i - 128 if c != char(13) then td.setCell self.col, self.row, i if c == char(13) or self.col == td.extent[0] then self.nextLine else self.col = self.col + 1 end if end function a2.print = function(s) for c in s a2.putchar c end for a2.putchar char(13) end function Now we're getting somewhere! After running this code, the screen is still blank, but you can now type something like:
a2.print "Hello!" and it should appear in nice blocky text at the top of the screen!
Much Printing
I finished my demo program with this code:
a2.print "Hello world!" a2.print "Welcome to the Apple II ""font""" a2.print "(really a TileDisplay)." a2.print a2.print "Very much like the Apple II display, except" a2.print "a little bigger (45x26 rather than 40x24)." a2.print "The quick brown fox jumps over the lazy dog." a2.print "Neat, huh?" ...and the result looks like this:
Of course you can print whatever text you like!
Regular, or Extra Crispy?
My one complaint with this result was that the text looked too crisp! The displays of those days did not have nice clean square pixels. Instead, each pixel was a glowing little blob of light, and because of how a CRT display works, these blobs ran together much better horizontally than vertically.
Because we're drawing the characters three times bigger than their definition in the tile set, we have an opportunity to recapture some of that extra flavor. But to do so, we need to make the image three times bigger, and modify it a bit.
I first tried to do this in an image editor. Scaling the image up 3X is easy enough, but then I wanted to add a bit of glow horizontally, and darken every third row to give the appearance of scan lines. And I couldn't figure out how to do that in the image app. So, I ended up doing it with MiniScript code instead! I wrote this separate program:
clear gfx.scale = 3 img = file.loadImage("AppleIIChars.png") gfx.drawImage img, 0, 0, img.width*3, img.height*3 // Add a bit of horizontal bleed for x in range(0, img.width*3) for y in range(0, img.height*3) c = gfx.pixel(x,y) if c != "#000000FF" then continue if gfx.pixel(x+1, y) == "#FFFFFFFF" or gfx.pixel(x-1, y) == "#FFFFFFFF" then gfx.setPixel x,y, "#AAAAAAFF" end if end for end for // Then add scan lines for x in range(0, img.width*3) for y in range(0, img.height*3, 3) c = gfx.pixel(x,y) if c == "#000000FF" then continue gfx.setPixel x, y, color.lerp(c, "#000000FF", 0.25) end for end for img = gfx.getImage(0, 0, img.width*3, img.height*3) file.saveImage "AppleIICharsBig.png", img This draws the original image to the gfx display, three times bigger — and that display itself is scaled 3X just to make it easier to see what's going on (so each character now appears 9X bigger than the original image). Then we have a loop that adds some horizontal bleed, by looking for black pixels next to a white pixel, and changing those to gray. And then a second loop which darkens (using color.lerp) every third row. Finally, the result is saved out to a new file, "AppleIICharsBig.png", which comes out like this:
Going back to our first program, to use this new tile set, we only need to change the line that loads the image:
td.tileSet = file.loadImage("AppleIICharsBig.png") And now when we run it, we get:
Beautiful!
Taking it Farther
My
a2.nextLinemethod does not handle scrolling the text up when it gets to the bottom of the screen (row 0). Can you make it do that?If you really want to drive down nostalgia lane, how about hooking this display up to MiniBASIC?
We baked our scanlines into the character images, which is fine if we're only displaying text... but what if we draw graphics? Can you think of a way that you could make scanlines appear on all display types, including sprites and pixel graphics?
Have fun, and happy coding!









Top comments (0)