Discovering Panasonic JR-200U
I had never even seen a Panasonic JR-200U before 2012, but when I did, I became curious. What kind of secrets does this oddball 8-bit contain? This page is a collection of several useful bits and pieces I’ve come across so far. Corrections and additions are most welcome. Some of the findings, such as the timings, might be correct only for the PAL JR-200UP model, since that’s the only one I have at my disposal.
The main processor is an MN1800A, a clone of Motorola MC6802, reputedly running at 0.89 MHz. Not much to see here:
- Two 8-bit accumulators, A and B
- 16-bit index register, IX (X)
- 16-bit stack pointer, SP (S)
- 16-bit program counter, PC
- Condition code register, CCR
MC6800 features quite many addressing modes. Among the most interesting ones is the direct mode, which lets you access the first 256 bytes (the zero page) of the RAM quickly. Most of that area is occupied by system and BASIC variables by default.
The following chips can be found on the circuit board:
- MN1800A — CPU, 6802 clone
- HD7416P — some logic gate
- HD61L204F — must be the display controller
- HM6116P-3 — 2k static RAM: 2k VRAM and 2k character RAM
- HM4865P-3 — 8k dynamic RAM, four chips make 32k altogether
- MN4864CA2 and CB2 — 8k rom chips
- MN1271 — I/O, clock and beeper
- MN1544CJR — 4-bit microprocessor, used for I/O. Contains 4k of ROM including the charset, and 128 bytes of RAM.
- Several 74LS logic chips
Tero Heikkinen took some high resolution photos of the guts an put them online. Check out the images here.
JR-200 has only one screen resolution, 256×192 pixels with eight colors, but bit 7 of the character color lets you select an additional blocky low-resolution mode for each character position. There’s two kilos of VRAM plus two more for the character bitmaps. Unfortunately, there’s not enough space for two full pages. The text resolution is 32×24 (768 positions) and the bitmap for each 8×8-pixel character can be redefined. The border color can be set separately. Eight colors are available in a typical three-bit GRB format:
- 0 — black
- 1 — blue
- 2 — red
- 3 — magenta
- 4 — green
- 5 — cyan
- 6 — yellow
- 7 — white (guess what)
Bits 0–2 of the color table define the fg color of the respective character position, and bits 3–5 the bg color. Bit 6 selects the charset base address, and bit 7 switches the character position in question to lowres mode, where a 8×8 pixel region is split into four 4×4 blocks, effectively yielding a 64×48 resolution where each pixel can be set individually. The upper half gets its colors from the character position table, and the other half from the color table.
When bit 6 is zero, the character bitmaps are read from $D000. Value 1 sets the base address to $C000, which is rather inconvenient, since it overlaps with the character positions and colormap, except at two locations: $C000–$C0FF and $C400–$C4FF. Thus, characters 0–31 and 128–159 can be relocated without overlaps.
So far it’s unknown whether there’s any exact way to sync to the screen refresh. There’s no VBI, and judging by the existing games, there’s no way to read the refresh phase easily. But we’d like to do smooth graphics, right? Luckily, the timer can be reprogrammed to serve as a VBI. For exact 50 Hz PAL sync the values are 3414 for three frames and then 3413 for one. Screen refresh can be — to a certain extent — followed by reading the memory addresses starting from $CA00, which return the last attribute shown on the screen. The timing is a bit tricky, but by drawing a suitable pattern on the screen and setting the clock according to that it’s possible to create a robust VBI. My example solution: vbi.s
The graphical capabilities of the Spectrum computers aren’t that far from the Panasonic and there are truckloads of great pictures available, so I wrote a little converter in Processing to turn 256×192 images into something you can display on the JR-200. This directory contains some converted images — apologies if your hard work was ruined in the process 320 blocks aren’t usually enough to reproduce the original exactly. Run them like this:
Sound and timers
According to Wikipedia and Old-Computers.com there should be three separate square wave channels capable of five octaves. JR-100, on the other hand, only had a simple one-channel beeper. MESS source code implies that JR-200 might contain a good old PSG, AY-3-8910, but on the circuit board there’s no such thing to be found. Some experimenting and ROM disassembly finally revealed the sad state of affairs: there is no sound chip. Instead, it’s the main clock and I/O chip, MN1271, that produces the beeps.
MN1271 contains two 16-bit timers (E and F) plus four 8-bit counters (A–D). The labels I’m using here might be a bit off, but it doesn’t really matter in practice. E is used as the system timer and generates an IRQ every 0.1 seconds, whereas C, D and F are connected to the speaker and used for square wave generation. That’s three channels, but C and D can only produce a narrow range of different frequencies, being 8-bit. No noise, volume settings, envelopes or filters, just three square waves out of which two are very limited. With low frequencies the output starts to look more like saw wave, since the level fades towards zero.
Each timer has a control byte and a 8-bit or 16-bit counter. The three lowest control bits must contain $6 in order to keep the timer running. Any other value will stop the timer. The higher bits define the operation mode of the timer (see below). Under normal conditions the control byte of E contains $4E. The beeper timers’ control byte depends on the octave that is being played back or zero if the channel is off. All the timers can generate an IRQ, so in that sense E is not special at all. Known control bits so far:
- 7 — not used (?)
- 6 — interrupt on counter zero
- 5 — not used (?)
- 4..3 — divisor selection. 00 = 1, 01 = 8, 10 = 64, 11 = 256. They correspond to or values 0,$8,$10 and $18 .
- 2..0 — Timer runs if 110 (6), stops otherwise
To reprogram the timer speed all that is needed is a new counter value. My best guess on the base frequency at the moment is 1.3655 MHz, which you divide by the desired divisor and frequency to get the counter value. Note that 1 is the smallest possible value, since 0 is considered to be 256. For 16-bit counters poke the hi byte and then the lo byte to their respective addresses. See the memory map below for the memory locations of each timer. For square waves you need to divide by 2*frequency, since a full wave requires two changes. Here’s a list of the closest 16-bit counter values for notes using divisor 8: jr200notes.txt (might be revised later).
Since there were apparently no existing tools for creating music I spent one weekend writing a little music converter and a player routine that let you convert XM tunes to Panasonic beeps. The composer needs to be aware of the harsh limitations imposed both by the simple routine and the less-than-stellar sound chip, but at least this is a bit easier than typing hex into assembler data statements. Without further ado, go ahead and download Musagi.
Keyboard and joysticks
The keyboard and joysticks are connected to MN1544, which checks their status and interrupts the main CPU when there’s activity. Normally the system doesn’t read the joysticks at all, just the keyboard (see memory map below for the locations of the related buffers). The last keypress can be read from $C801, but it’s not enough for games, since you can’t tell whether the key is still pressed or not.
Even the BASIC interpreter features a function called STICK, which lets you read the keyboard and joysticks as directly as possible. From assembly the same subroutine can be called at $E8CB (slowjoy.s). It’s still impossible to read whether a particular key is pressed or not, but at least the joysticks work perfectly. The biggest problem with the approach is that it takes about 1/4th of a frame to complete the call, since the subroutine needs to wait for three interrupts coming from the 1544. Direct communication with the 1544 is kind of hairy business, but luckily the joysticks can be read fast by first setting the correct mode, doing something useful while the three interrupts take place, and reading the result only then (fastjoy.s).
- $0000–$7FFF: 32k RAM
- $0000: key click (0 off, $40 on)
- $0001, $0002, $0003: direct keyboard and joystick call data area
- $0004: 8-bit 0.1 second down counter
- $0005: 8-bit 0.1 second down counter running from 10 to 1
- $0006: 16-bit one second down counter
- $000D: unhandled keypress counter, used for other communication with 1544 too
- $002B: tape load speed (0 = 2400 bps, 1 = 600 bps)
- $002C: tempo, 109 by default
- $002D, $002E, $002F: PLAY note duration counters
- $0030, $0032, $0034: PLAY note buffer pointers
- $0036, $0039: tempo counters
- User-provided interrupt handler addresses. $0000 if not used.
- $0100: NMI
- $0102: sw interrupt
- $0104: IRQ
- User hook addresses
- $0106: unknown hook
- $0108: unknown hook
- $010A: unknown hook
- $010C: timer hook, runs every 0.1 seconds
- $010E: one second timer hook
- $0114: transfer buffer from 1544 (?)
- $0116: 16-bit keyboard buffer pointer
- $0150: keyboard circular buffer of 32 bytes, alternatively keyboard data for STICK(0)
- $0151: joystick data for STICK(1)
- $0152: joystick data for STICK(2)
- $07FF: default stack
- $0800: start address of BASIC programs
- $8000–$9FFF: not used
- $A000–$BFFF: 8k BASIC ROM
- Video RAM
- $C000–$C7FF: alternative character bitmap (overlaps with other tables)
- $C100–$C3FF: character positions
- $C500–$C7FF: character colors
- $D000–$D7FF: character bitmap
- $C800–$CFFF: Memory-mapped I/O
- MN1544 interface
- $C800: keyboard mask (?), normally 0 — any other value messes up the keyboard
- $C801: keyboard read. This is already ASCII.
- $C802: interrupt mask (?). Bit 0 enables the keyboard interrupt.
- $C803: misc communication with MN1544. Bit 6 controls the keyboard beep. Used also for 1544 ROM to CPU RAM copy.
- $C805: printer port write (?)
- $C806: bit 6 = tape control relay
- $C807: tape read. Loud positive values turn bit 7 on.
- $C80A: keyboard ACK, read after the keyboard port
- $C80B: tape communication (?)
- $C80C: tape communication (?)
- $C80D: tape communication (?)
- $C810: tempo timer control, contains $59. Doesn’t work quite like the other timers.
- $C811: tempo timer counter, in bpm
- $C812: timer C (beeper) control
- $C813: timer C counter, 8-bit
- $C814: timer D (beeper) control
- $C815: timer D counter, 8-bit
- $C816: timer E (system clock) control
- $C817: timer E counter hi, default $41
- $C818: timer E counter lo, default $8A
- $C819: timer F (beeper) control
- $C81A: timer F counter hi
- $C81B: timer F counter lo
- $C81C: interrupt status register 1 (read). Bit 0 = keyboard.
- $C81D: interrupt status register 2 (read). Bit 4 = timer, bit 1 = tempo timer.
- $C81E: interrupt mask register 1. Bits as above, default value 1.
- $C81F: interrupt mask register 2. Bits as above, default value $12.
- The I/O area wraps to the beginning every 32 bytes until $C9FF
- $CA00: border color — in fact any address up to $CBFF works the same
- When read, the addresses starting from $CA00 seem to return the last attribute displayed on the screen and some unknown values while in the border area.
- MN1544 interface
- $D800–$DFFF: cartridge region
- $E000–$FFFF: 8k OS ROM
- Interrupt handlers
- $E6E4: reset handler
- $E790: NMI handler
- $E7DC: software interrupt handler
- $E7E2: IRQ handler
- $E892: idle loop
- $E8CB: direct keyboard and joystick call. Return values stored at $0001, $0002 and $0003.
- $FED8: error messages
- $FFA4: startup text
- 16-bit interrupt vectors
- $FFF8: IRQ
- $FFFA: software interrupt
- $FFFC: NMI, triggered when break is pressed
- $FFFE: reset vector
- Note that the vectors are in ROM and, therefore, can’t be directly changed. Use the optional handlers and hooks starting from $0100.
- Interrupt handlers
Still missing quite a lot of relevant stuff and details. I’ll add more after testing on real equipment. These things are not that well documented at all.
I don’t know if there ever was a floppy drive available for JR-200. I very much doubt it, but even if there was, practically nobody would own it, so we’re stuck with cassettes. A very typical approach these days is to emulate a tape drive by playing back sound files from a modern host computer to the cassette port. For an in-depth description of the tape format and the associated CJR emulator files check the Old Machinery blog (see below). Big thanks to Tero Heikkinen for documenting the format!
I made an attempt at writing a binary to CJR converter that turns compiled programs into tape image files. Source here: bin2cjr.c. Seems to work. To play back the generated CJR to the real machine, use cjr2wav.c. Appears to work well with 2400 baud files, but not 600 at the moment. For easy development, here’s a Makefile that puts it all together. If you’re using a Mac, replace the playsound command with afplay. On the Panasonic do something like this:
Raw wav files tend to be a bit on the big side — a tiny 7k CJR turns into a 2 MB wav with the above converter. Gzipping the wav makes it way smaller, just 55k. Lossy formats, such as mp3, might distort the waveform too much, but at least 128kbit files seemed to work just fine. The file size decreased to 763k, but the resulting mp3 can’t be effectively compressed any longer. YMMV.
The concept is still under development, but here’s an LZSS compressor and a 6800 machine language decompressor routine for your enjoyment. Need to consider them a bit further, but especially for sparse files the gains could be significant.
- Old Machinery’s entry on JR-200U (funnily enough a Finnish page)
- MESS source code, a useful resource even with its shortcomings
- Virtual Panasonic JR-200U. The best emulator for JR-200. An easy introduction to JR-200, but don’t expect too much if you’re after hardware tricks and exact timings. Windows-only, although runs quite ok with Wine.
- Disassembled versions of the basic and system ROMs. Not sure about their correctness, but in theory it’s all in there.
- Old-Computers.com’s entry on the ancestor, National JR-100
- CRASM, one of the many cross-assemblers that can deal with the 6800. My example sources are for CRASM.
- Motorola 6800 microprocessor architecture
- Motorola 6800 Instruction Set Summary — a quick reference
- MC6800 Application Manual
- My source code directory. Everything is under public domain, so use as you see fit.
- Putting it all together: Panyansonic, a little demo. And a video for those who don’t own the real deal.
- And another: SR-200, featuring smooth scrolling