Fun fact: Mega Anser could interface with a printer connected to the second controller port (used for print screen, back when the meaning was literal). You could purchase a bundle that included a thermal printer and a cable to connect it to your Mega Drive.
Disclaimer: the information below comes from reverse engineering Mega Anser and hasn't been checked with the printer adapter. Nobody seems to have seen one of these adapters in like forever so right now it's impossible to verify edge cases. In the future we may come up with our own version of the adapter.
- Detecting and setup
- Using the printer
- Compatible printers
- Printing an image
- Printing in color
Detecting and setup
First you need to detect the printer. The only way to do so is to check the peripheral ID: the printer has an ID
of 10 (
If the printer has been found, set bits 2-0 as output, and the rest as input:
move.b #$07, (IoCtrl2) move.b #$07, (IoData2)
Mega Anser expects the printer in the second port.
Using the printer
First, read bit 3 from the port. If it's set (bit 3 = 1), then the printer can't take in more data right now. Come back later. You may want to include the option to cancel the operation too, in case the printing got stuck for whatever reason.
; Check if printer is ready btst #3, (a0) bne @NotReady
Now comes the fun part: we need to load the byte into the shift register,
one bit at a time. To do this, first we write the next bit into bit 0,
$FC. Then we do it again but OR'd with
$FE. Repeat this for all bits (highest bit comes first).
; Go through the whole byte ; Assumes byte is in d0 moveq #8-1, d7 @Loop: ; Push next bit where we need ; to write it (this pushes the ; bit 7 of d0 into bit 0 of d1) add.b d0, d0 addx.b d1, d1 ; Put bit into shift register ; Bit 1 must be cleared too and.b #$01, d1 or.b #$FC, d1 move.b d1, (a0) nop nop ; Then set bit 1 again... or.b #$FE, d1 move.b d1, (a0) nop nop ; Onto next bit dbf d7, @Loop
Finally, you have to send the byte you just copied to the printer. To
do this, you have to first write
; Confirm byte move.b #$FB, (a0) nop nop move.b #$FF, (a0) nop nop
Repeat this operation for every byte you need to write to the printer, and make sure to check for bit 3 for whenever the printer becomes busy (e.g. because it's still printing).
Even though the printer bundle for Mega Anser came with a thermal printer, the interface itself is pretty agnostic, working with any printer with a Centronics interface… although if you remember printing in the DOS days, you probably know that just about every printer did its own thing with escape codes.
Mega Anser sends ESC/P codes to the printer, so for the sake of consistency you'll want to stick to those printers. Thankfully those were pretty common back then.
Mega Anser subset
Mega Anser actually uses a very limited subset of ESC/P (just enough to print a bitmap), and it never prints ASCII text directly. The commands used by Mega Anser are:
00): ignore these.
0D): returns to the beginning of the current line.
0A): advances to the beginning of the next line.
1B 41 nn): changes the line height (how much
LFmoves downwards). The value nn specifies height in 1/60 inches.
1B 4C nn mm ...): prints bitmapped graphics, with a resolution of 120×60 dpi. The width is mmnn pixels, and height is 8 pixels. After this command there's a byte for every column (bit 0 is bottom pixel, bit 7 is top pixel).
Note that it's likely that the bundled printer supports more commands
than those (e.g. it probably supports
to eject the page, but Mega Anser doesn't do it).
Printing an image
Chances are, you just want to print an image. You can use the same ESC/P commands that Mega Anser uses for the job. First of all, make sure your image meets these requirements:
- Image must be monochrome (no, not even grayscale)
- Width can be anything (but keep 640px at most)
- Height must be multiple of 8px
- Pixels are rectangular! (width is half the height)
- Mega Anser gets around this by printing every column twice.
- You could instead exploit this to get finer dither detail.
First you want to set up the page. Output
1B 41 08 to set
the correct line height (8px), then
0D 0A to ensure you
start on a new line.
Now, "slice" the image into horizontal bars that are 8px high. For each
of these bars, use the
ESC L command: output
4C, then the width (first low byte, then high byte), then output
a byte for every 1×8 column (remember, bit 0 is the bottom row). Once
you've done that, output
CR LF (
After you've printed all those bars the image is already on paper, but
optionally if you want to be nice, you can output a
(form feed) to make the printer let go of the paper. Otherwise the user
will have to do it manually with the printer buttons.
print 1B 41 08 0D 0A for every 8px slice print 1B 4C for every column byte = (row << 7) | (row << 6) | (row << 5) | (row << 4) | (row << 3) | (row << 2) | (row << 1) | (row << 0) print byte end print 0D 0A end print 0C
Printing in color
Remember to be nice and let people pick if they want to print in color or monochrome (since we'll be dealing with old printers and it's also possible they just don't have color ink right now).
If you want to go further and support printers that can do color, you
can make use of the relevant ESC/P command to change the ink color.
ESC r (
1B 72 nn) command lets you pick
the ink that will be used from now on. The nn byte specifies
Assuming your image is in CYMK format, what you need to do is to print each 8px high slice four times (once for each ink) before moving onto the next one. You achieve this by doing the following (make sure to print the colors in this order or you risk smearing):
- Select yellow ink (
1B 72 04)
- Print yellow dots for the slice
- Send byte
- Select cyan ink (
1B 72 02)
- Select cyan dots for the slice
- Send byte
- Select magenta ink (
1B 72 01)
- Select magenta dots for the slice
- Send byte
- Select black ink (
1B 72 00)
- Select black dots for the slice
- Send byte
- Send byte
Converting from RGB to CYMK
If you want to print an arbitrary image chances are that you have it in RGB format instead. You can convert from RGB to CYM (no black) easily:
- cyan = 100% - red
- magenta = 100% - green
- yellow = 100% - blue
That alone is already enough to get a full color image, but you probably also want to use black ink to improve contrast (i.e. CYMK). To convert from RGB to CYMK:
- Convert from RGB to CYM as earlier
- Find the smallest value from cyan, magenta or yellow
- Substract that value from all three
- Add that value to black
The above works fine even with dithered images (in fact, if each layer is monochrome then it's even easier since each component can only be either 0% or 100%).