Showing posts with label jr200. Show all posts
Showing posts with label jr200. Show all posts

Thursday, 13 December 2018

Panasonic JR-200 emulation

Feeling a bit nostalgic, not only because it's an old computer, but because the early days of this blog was very much about Panasonic JR-200UP.

At that time I had hoped I could code something on the platform, especially as Marq went through the trouble of finding about most of the hardware and I spent time figuring out the tape format.

The lack of an emulator discouraged me, as I tend to code with extremely short build cycles, compiling the code every few seconds almost. The fun and nostalgia of tape loaded binaries fades quite rapidly.

Panyansonic by FIT.
At one point the solution could have been a device that helps transfer the data rapidly to the real computer. Although it showed promise it begun to feel fiddly altogether, and somewhat slow to set up compared to an emulator.

There has been James the Animal Tamer's JR200 emulation that I have never seen running, as it only runs on some Windows version. Also, the scanline/vblank emulation is apparently non-existent. The JR200 quite probably does not have a software-accessible, simple way to track the screen refresh accurately.

The trick is to rig the interrupt to work with the internal timer in that capacity, using an address to catch the currently written attribute, as in the Panyansonic and SR-200 intros made by FIT. I felt the emulator ought to be able to somehow work with this trick.


The Emulator

My emulation project had a few false starts over the years. I had to learn 8-bit assembly more in the meantime and something about how chips work before I begun to have the mindset necessary for building an emulation. Again, I work on Processing/Java.

I did some limited C64 emulation for myself in the recent past, which was a simpler task in the sense that I didn't set the bar very high and there are existing emulators to compare it to. (More about this, maybe, one day).

Left: Incomplete handling of Carry flag at one opcode caused glitching in the SR-200 scrollers. Right: opcode fixed
Here I had to enter the realm of the 6800 processor which is not as familiar to me as the 6502, and hardware that only has been properly documented by Marq, and even that documentation is not entirely complete.

The experience with 6502 was of course very helpful. One early issue was that in 6800, the C flag is treated differently in Subtract with Carry, i.e. the opposite of how it works in 6502. But all in all, the stack commands and how the stack works with JSR, RTS, is quite similar. 6800 stores 16-bit values in HI-LO format instead of LO-HI, which can make things more intuitive.

Over the years I've thought you need to be a genius to write an emulator, but it's not rocket science in the end. To me the key was to make the emulator do something visible in the first hours. So I wrote a few opcodes like INC addr16 and JMP opcode, after which I could already start looking at video emulation.

No joystick yet...
After a preliminary video mode was complete, I made a 256-entry switch-case list that treats each and every opcode as a separate entity. Non-implemented opcodes freeze the emulation and print out the address together with a list of opcodes that have so far been executed. This list can be used to track problems.

As the 6800 is very orthogonal the emulation code could be made much smaller. But it might have resulted in code that either doesn't work at all or works completely, which can be a very frustrating situation. So I guess I'm using an "agile" approach. The downside can be that early errors may be left hanging in some opcodes while rest of the similar instructions work, and these can be difficult to track.

Mind you, the emulation is far, far from complete, which is where the real difficulty lies. For my current purposes it doesn't really have to be complete, as I only wanted to ease the development of JR-200 code, if I ever get interested in that again. So, at least for now, it won't be a public project. I thought if it could run the tiny Nyansonic demo, then all would be well. And it sort of does.

Wednesday, 16 July 2014

Loading JR200 files from Arduino


As a follow-up to Panasonic JR200 tape wave generation some years back, I now had an opportunity to use my specifications for a quick project. Here I have an Arduino UNO work as a "tape" storage for the Panasonic JR200. As the Panasonic JR200 really likes square waves for input, the Arduino digital pins are ideal for the job.

The Arduino produces the waveform over and over from a series of bytes. Using the MLOAD command from the Panasonic end, the computer will load the data next time it loops around. (Resetting the Arduino obviously restarts the "tape")

I spent some time figuring out how to power the Arduino from the Panasonic External Bus, which would be convenient. The first bottom left pin is the Ground. Checking with the multimeter, then, going right, there are five pins between the Ground pin and what appears to be a 5V pin.

Again, I have to say I like the JR200 more and more for the quality of the hardware. I cannot guarantee the below schematic is good for the health of your Panasonic/Arduino/self, but here goes anyway:

Not to scale. The JR200 connectors are as seen at the back of the computer.
I used this setup to load some converted ZX Spectrum loading screens. I noticed the loading can be made somewhat faster, as there's some tolerance in the tape input. At first I used 200 and 400 microsecond square wave lengths to simulate the original speed, but I soon found out that I can use 160 and 320 instead.

So, it might take 26 seconds to practically load a loading screen (4K in two files), that would normally take slightly over 30 seconds. Well, it's a small improvement anyway. At 150ms/300ms, the Panasonic stopped recognizing the signal.


If only I could get rid of the trouble of writing the MLOAD keyword. However, I can at least load BASIC keywords directly into the character display, for example A=USR($1000). So, booting JR200 and using MLOAD would result in a more complex command set, waiting "under the cursor". Machine code can be run from the character display, so a tight package could combine both the necessary keywords to run the code and the code itself.

In glorious flicker-o-vision:


Further ideas:

-Take some signal from the Panasonic to Arduino for triggering the loading. Perhaps the "remote" relay. As the Panasonic gives an audible click when using the MLOAD, there's got to be a relay somewhere.

-The Arduino could just load a short code for high-speed receiving from the joystick port(s).

-Saving files to Arduino. It should be simple enough, but I just can't see the point.


The Arduino sketch:

Copy/Paste to the Arduino editor and upload. Use MLOAD from Panasonic. The example loads just 8 bytes to the character memory. This gives a quick visible effect. For sending your own data, change the contents of the g_data array list. The g_length and g_address are used for setting the start address and the length of the data. The g_offset sets the starting point from which the data is sent, within the g_data array. This only matters if you want to send multiple files.




#include <avr/pgmspace.h>

#define OUTPIN 9
#define MSHORT 200 //160
#define MLONG 400 //320
#define MLONGEST 800 //640

// Panaduino
// Uses Arduino pins to send a tape signal to Panasonic JR-200
// 15.7.2014 Dr. TerrorZ

unsigned int g_level,g_chek,g_blok,g_length,g_address,g_offset;

// The data.
// PROGMEM is necessary for anything longer.
// The actual g_length, g_address and g_offset are set in loop()
// 

prog_uchar g_data[] PROGMEM={1,2,4,8,16,32,64,128};

void switchlevel()
{
  if(g_level==LOW){g_level=HIGH;return;}
  g_level=LOW;
}

void send0_600()
{
  digitalWrite(OUTPIN,LOW);
  delayMicroseconds(MSHORT);
  digitalWrite(OUTPIN,HIGH);
  delayMicroseconds(MSHORT); 
  digitalWrite(OUTPIN,LOW);
  delayMicroseconds(MSHORT);
  digitalWrite(OUTPIN,HIGH);
  delayMicroseconds(MSHORT); 
  digitalWrite(OUTPIN,LOW);
  delayMicroseconds(MSHORT);
  digitalWrite(OUTPIN,HIGH);
  delayMicroseconds(MSHORT);
  digitalWrite(OUTPIN,LOW);
  delayMicroseconds(MSHORT);
  digitalWrite(OUTPIN,HIGH);
  delayMicroseconds(MSHORT);
  g_level=HIGH;
}

void send1_600()
{
  digitalWrite(OUTPIN,LOW);
  delayMicroseconds(MLONG);
  digitalWrite(OUTPIN,HIGH);
  delayMicroseconds(MLONG);
  digitalWrite(OUTPIN,LOW);
  delayMicroseconds(MLONG);
  digitalWrite(OUTPIN,HIGH);
  delayMicroseconds(MLONG);
  g_level=HIGH;  
}

void send0()
{
  digitalWrite(OUTPIN,g_level);
  delayMicroseconds(MSHORT);
  switchlevel();
  digitalWrite(OUTPIN,g_level);
  delayMicroseconds(MSHORT);
  switchlevel();
}

void send1()
{
  digitalWrite(OUTPIN,g_level);
  delayMicroseconds(MLONG);
  switchlevel();  
}

void sendlong1()
{
  digitalWrite(OUTPIN,g_level);
  delayMicroseconds(MLONGEST);
  switchlevel();  
}


void filler600(int a)
{
  int i;
  for(i=1;i<=a;i++){
    send1_600();
  }
}

void filler(int a)
{
  int i;
  for(i=1;i<=a;i++){
    send1();
  }
}

void sendbyte(int a)
{
  int b,c;
  send1();
  send1();
  send1();
  send0();
  b=1;c=a&b;
  if(c!=0){send1();}else{send0();}
  b=2;c=a&b;
  if(c!=0){send1();}else{send0();}
  b=4;c=a&b;
  if(c!=0){send1();}else{send0();}
  b=8;c=a&b;
  if(c!=0){send1();}else{send0();}
  b=16;c=a&b;
  if(c!=0){send1();}else{send0();}
  b=32;c=a&b;
  if(c!=0){send1();}else{send0();}
  b=64;c=a&b;
  if(c!=0){send1();}else{send0();}
  b=128;c=a&b;
  if(c!=0){send1();}else{send0();}  
  g_chek=g_chek+a;
  if(g_chek>255){g_chek=g_chek-256;}
}

void sendbyte600(int a)
{
  int b,c;
  send1_600();
  send1_600();
  send1_600();
  send0_600();
  b=1;c=a&b;
  if(c!=0){send1_600();}else{send0_600();}
  b=2;c=a&b;
  if(c!=0){send1_600();}else{send0_600();}
  b=4;c=a&b;
  if(c!=0){send1_600();}else{send0_600();}
  b=8;c=a&b;
  if(c!=0){send1_600();}else{send0_600();}
  b=16;c=a&b;
  if(c!=0){send1_600();}else{send0_600();}
  b=32;c=a&b;
  if(c!=0){send1_600();}else{send0_600();}
  b=64;c=a&b;
  if(c!=0){send1_600();}else{send0_600();}
  b=128;c=a&b;
  if(c!=0){send1_600();}else{send0_600();}  
  g_chek=g_chek+a;
  if(g_chek>255){g_chek=g_chek-256;}
}


void header(int id)
{  

  //in 600 baud format

  g_chek=0;
  sendbyte600(2);
  sendbyte600(42);
  sendbyte600(0);//block#0
  sendbyte600(26);//fixed length of data for block #0
  sendbyte600(255);
  sendbyte600(255);
  sendbyte600('P');//filename 16
  sendbyte600('D');
  sendbyte600(48+id);
  sendbyte600(0);
  sendbyte600(0);
  sendbyte600(0);
  sendbyte600(0);
  sendbyte600(0);
  sendbyte600(0);
  sendbyte600(0);
  sendbyte600(0);
  sendbyte600(0);
  sendbyte600(0);
  sendbyte600(0);
  sendbyte600(0);
  sendbyte600(0);
  sendbyte600(1);//binary1,basic 0
  sendbyte600(0);//baud rate for the rest. 0=2400, 1=600
  sendbyte600(255);
  sendbyte600(255);
  sendbyte600(255);
  sendbyte600(255);
  sendbyte600(255);
  sendbyte600(255);
  sendbyte600(255);
  sendbyte600(255);  
  sendbyte600(g_chek);//checksum
}

void datablock(int len)
{
  unsigned int i,adhi,adlo,lbytes;
  unsigned int bb;
  unsigned char cc;
  lbytes=len;
  if(len==0){lbytes=256;}
  g_chek=0;//reset checksum
  adhi=g_address/256;
  adlo=g_address-(adhi*256);
  sendbyte(2);
  sendbyte(42);
  sendbyte(g_blok);//block nro
  sendbyte(len);//length 0=256
  sendbyte(adhi);//hi addy
  sendbyte(adlo);//lo addy
  for(i=0;i<=lbytes-1;i++){
    bb=0;
    cc=pgm_read_byte_near(g_data+(g_blok-1)*256+i+g_offset);;
    bb=int(cc);
    sendbyte(bb);
  }
  g_address=g_address+lbytes;
  sendbyte(g_chek); 
}

void lead_out()
{
  int adhi,adlo;
  adhi=g_address/256;
  adlo=g_address-(adhi*256);
  sendbyte(2);
  sendbyte(42);
  sendbyte(255);
  sendbyte(255);
  sendbyte(adhi);//hi addy
  sendbyte(adlo);//lo addy
}

void send_tape(int tapeno)
{
  int i,dblocks;
  int lef;
  
  dblocks=g_length/256;
  
  g_level=LOW;
  g_chek=0;  
  g_blok=1;
  
  //600 baud portion
  filler600(850);
  header(tapeno);
  filler600(51);
  
  //2400 baud portion
  for(i=0;i<=dblocks;i++){
   if(i==dblocks){
     lef=g_length-dblocks*256;
       if(lef==0){datablock(1);}
       if(lef>0){datablock(g_length-dblocks*256);}
     }
   if(i<dblocks){datablock(0);filler(196);}
   g_blok++;
  }
  
  sendlong1();
  filler(188);
  lead_out();
  sendbyte(255);
  filler(150); 
}

void setup()
{
  pinMode(OUTPIN,OUTPUT);
  digitalWrite(9,LOW);
  pinMode(13,OUTPUT);
  digitalWrite(13,LOW);
}

void loop()
{ 
  g_length=8;
  g_address=0xd000;
  g_offset=0x0000;
  send_tape(1);
  
  pinMode(OUTPIN,OUTPUT);
  digitalWrite(9,LOW);
  delay(100);
  
  //for multiple MLOADs, use separate tape loads.
  //delay(1500);
  //g_length=0x0800;
  //g_address=0xc100;
  //g_offset=0x0800;
  //send_tape(2);
}

Monday, 31 December 2012

Sega SC-3000 (And Panasonic, again)



Sega SC-3000 (1983) is one of those gaming computers that do not have a built-in BASIC. I have some stuff lying around for it so it's not a complete waste of space. I enjoyed trying out Sindbad Mystery, one of the more decent games for the platform, a sort of Pac-Man with elements from Pengo. 

Pretty good looking for a 1983 game. The music makes me 
think of Sega's later Wonder Boy, especially when Sindbad reaches the third level.
Given that the SC-3000 does not have a built-in BASIC, it is a bit strange that there are BASIC keywords on the keyboard. Fiddling with the BASIC cart I have, I've found the speed to be reasonable, but it's a bit hard to guess what keywords there might be besides the ones on the keyboard. Apparently the higher level BASIC cartridges have more commands than the smaller ones, possibly for graphics and sound. The keyboard in my unit works so poorly that it's impossible to do anything but a few lines of code.
But what the hell are diereses?

The computer accepts good old Atari joysticks, which is good for games, as the Sega joysticks from the time are atrocious. Some games make use of the Sega second fire button, not available on all joysticks. The computer has tape connectors, a mysterious DIN-5 "video" output and a TV out. All in all, the black case is quite pretty, very flat and sci-fi. The sleek lines are greatly augmented by the flat rubber keyboard which has BASIC keywords imprinted above each key, and...

Wait a minute, haven't we seen something like this before?

JR200U keyboard
SC3000 keyboard
The rubber keyboard is suspiciously similar to the one in Panasonic JR200U. Ever since I got my Sega, I have been wondering about this feature. No, the layout is not identical, but there are enough similarities to make it certain there was some common origin for the two. I may never know the story behind this, but at least I can try to find out how similar the keyboards really are. Could they even be interchangeable?

The ribbon cable in front of JR200U. Connecting the red pins with the blue ones produces key presses.
I started the task with mapping the keyboard input of the Panasonic JR200. From the circuit board it is visually obvious how to start figuring out the columns and rows for the keyboard matrix, and after a little while of poking the connections with a short cable, I could map it for the most parts. (This way I could avoid breaking down the actual keyboard element, which might ruin it.)

Some keys, such as shift and control are left out for now, and I'm unsure about what some of the keys do. Here I have only included the keyboard-relevant parts for the table, as the rest of the 22 cables relate to the power LED, speaker and reset button.

Panasonic JR200U keyboard matrix (incomplete).
Connecting a blue pin with a red pin produces the desired key input.
Now, let's see the conditions under the hood of the Sega. The case top comes off in two parts. As the keyboard part is removed the backside of the case top can be kicked out. The insides are a bit crude, what with the bent, amorphous metal/folio covering most of the motherboard, but luckily the keyboard connectors are immediately visible. There is similar number of cables between the keyboard and the motherboard. This looks promising enough...

Removing the cover from Sega. The black wires connect the keyboard to the motherboard.
Here I found no need to examine the Sega keyboard connector, as the keyboard matrix (and a neat mod) can be found from here. I only rapidly checked that it holds true, poking a cable into the connector while running the BASIC cartridge. The table below is built using that information, assuming a running pin number from left to right, reading from the front of the machine.

SEGA SC3000 Keyboard matrix
The matrix has similarities with the JR200, especially when looking at the top half of my table. However there the similarities end. Compared to JR200 I'd say the Sega version is the messy one. Instead of the neat 8x8 matrix there is a 12x7 grid. It still might be possible to wire the Panasonic keyboard to the SC-3000, but it is not as straightforward as I had hoped. Then again, I'm not going to break my Panasonic anyway.

Saturday, 11 February 2012

Panasonic JR200u tape wave generation


I've previously described the contents of a CJR file and the structuring of the Panasonic JR200U tape format. Here I'll go into converting binaries into WAV audio files that work as JR200 tape input. The purpose is to produce similar audio files as would result when an output from the actual machine is recorded. It's pretty straightforward except for the fact that the tape audio consists of data encoded both in 600 and 2400 baud speeds, and the way of encoding for the two is a bit different.

I'll take a very brutal and practical approach and merely tell how to construct a wave file that can be played back to the Panasonic from an audio software such as Audacity. This is just a crude task of appending bytes into a file in a correct order, and the finer points about signal processing can and will be ignored. First I will describe how bytes are composed in an audio file both in the 600 baud format and the 2400 baud format and how to append them into the WAV as waveform data points. Only after then the overall layout of the wave file with all the necessary "filler" will be given.

The following will work with the assumption that the file consists of a 600 baud header and 2400 baud data blocks. The logic of a full 600 baud file is slightly different, and simply using 600 baud binary with the below description will not work. For completeness sake, this may be addressed in the future, but as the 2400 baud format is the more useful the lower speed is ignored for now.

If you just need a tool for converting CJR files, head to this page.

The header bytes

Looking in detail at a tape audio wave in Audacity, it can be seen the data is encoded in a square wave.



This is the beginning of a header block after a lead in. A block in a normal file always seems to begin with a 2 and 42. (See the previous blog entry for the composition of the CJR files.)

The bytes are of course built out of bits, and these bits are represented in the header wave as a group of longer and shorter waves. The longer wavelength is 1 and the shorter is 0.

So, the above wave will translate into bits like this:

...11111110010000001110010101001110...

The highlighted parts are the same as are highlighted in the image above. The "nybbles" of 1110 are byte markers that precede all actual data bytes. They are not part of the stored data content. The lead-in and all in-between sounds are long series of 1's without the nybbles.

I have previously illustrated the composition of a byte in the header, and here it is again. The format is little-endian binary. This is the composition for the Ascii character 's' (Decimal 115):



The following byte marker actually belongs to the next byte already, which is not visible. Building the header as a wave with this information is then quite simple. For each data byte, the byte marker bits and the data bit waves are appended to the wave file.

But how many audio samples does each bit result in the actual wave file?

I'm assuming a 44100hz sample rate for the WAV, as to simplify the practical task of constructing the file. Note that what follows is not ideal, for reasons given later in the text.

This is the procedure for making the bits:

Do 0: Make the datapoints for a 0-bit:

Write 8 data points of negative signal
Write 1 data point of neutral signal
Write 8 data points of positive signal
Write 1 data point of neutral signal
Write 8 data points of negative signal
Write 1 data point of neutral signal
Write 8 data points of positive signal
Write 1 data point of neutral signal
Write 8 data points of negative signal
Write 1 data point of neutral signal
Write 8 data points of positive signal
Write 1 data point of neutral signal
Write 8 data points of negative signal
Write 1 data point of neutral signal
Write 8 data points of positive signal
Write 1 data point of neutral signal

Do 1: Make the datapoints for a 1-bit:

Write 17 data points of negative signal
Write 1 data point of neutral signal
Write 17 data points of positive signal
Write 1 data point of neutral signal
Write 17 data points of negative signal
Write 1 data point of neutral signal
Write 17 data points of positive signal
Write 1 data point of neutral signal

Both take 72 data points in the wave file. In an 8-bit wave, this would be the actual number of bytes, whereas in a 16-bit wave, the amount will be double. I have myself used 16-bit waves, although 8-bit (or even less) might suffice.

Just to reiterate the obvious, using the above logic the character 's' as in the previous image would be built like this:

Do 1, Do 1, Do 1, Do 0. [The byte marker nybble]
Do 1, Do 1, Do 0, Do 0, Do 1, Do 1, Do 1, Do 0. [The character 's' in ascii]

The 2400 baud format

The 2400 baud format is slightly more difficult to illustrate. When visualized, the bits are not a set of consistently identical peaks and valleys.



The above image is a portion of the wave where the data begins in 2400 baud format. The construction of the short and the long wave does not differ, but the data is 4 times more dense than in 600 baud format.

Although the image highlights the nybbles very clearly, it is not so obvious how the data bytes are composed. The image shows bytes 2 and 42 as before, but they don't look quite the same. So, the difference between 600 and 2400 baud format is not simply a question of changed frequency.

The data is there in the same little-endian way, it is just that the waves representing ones and zeroes can be "upside down". Let's have a visual look at one of the bytes, the 42.



The illustration already makes things much clearer. The first '1' within the byte points "down", whereas the second one points "up". Also, the three one's in the nybble point to alternate directions. But, looking at the two last zeroes, they are identical. So it is the '1' that changes the phase.

Both the above images are misleading in that the nybble byte markers seem identical. They can also be inverted, depending on the previous bit phase, it just happens so the chosen images do not show this.

To cut the explanation, here's the procedure for making the bits in a 2400 baud format:

(The A-signal is initially negative, as the last 1-bit in the 600 baud format part implies.)

Do 0: Make the datapoints for a 0-bit:

Write 8 data points of A-signal
Write 1 data point of neutral signal
Invert the A-signal
Write 8 data points of A-signal
Write 1 data point of neutral signal
Invert the A-signal

Do 1: Make the datapoints for a 1-bit:

Write 17 data points of A-signal
Write 1 data point of neutral signal
Invert the A-signal

So, how each bit "looks like" in the wave depends on what the state of A was after the previous bit was written. So unlike in the 600 baud format, the wave writer needs to keep record of this phase.

A note about the approach

This method as described will not generate precisely same overall wave file lengths as recorded from the Panasonic. Some adjustment may be required to achieve this. In reality, the wavelength is not "72 bytes" long, but a frequency. The described approach is in a way inferior to one where a real frequency is used. This method does work, though, so it will suffice here. Also, this procedural approach is simpler to explain.

In my wave generator, I added a global time skip variable that can be used to adjust the overall synchronization of the wave. The time skip can be adjusted so that the overall audio file lengths would better correspond with those of the actual machine. This means that data points are counted as they are written into the WAV, and each hundredth (say) will not be written. Or if the audio file needs to be lengthened, every hundredth data point will be written twice, or something like that.

The overall wave generation

The more tricky stuff has been gone through and what is left is to give the recipe for generating the whole audio file. For the content of the Blocks, refer to the previous blog post.

600 baud header:

 Generate some silence
 Lead in: More than 800 bits of 1
 Header Block: 33 bytes in the 600 baud format above
 In-Betweener: 51 bits of 1

2400 baud data blocks and footer/tail:

 Data Block 1
 In-betweener: 196 bits of 1
 Data Block 2
 In-betweener: 196 bits of 1
 Data Block 3
 In-betweener: 196 bits of 1
 ...
 ...
 Last Data Block
 A "long 1", See *1
 In-Betweener: 188 bits of 1
 Footer Block (tail)
 Byte 255, See *2
 Lead out: some 150 bits of 1
 Generate some silence (5 seconds between tape parts for example)

*1: The Long 1 is an anomalous piece of waveform that seems to be added at this point. It can be treated as a 1 that has twice as long waveform. I'm not sure if Panasonic really requires this, but it's better to be precise.
*2: The byte 255 is not included in a CJR file.



The above image shows the anomalous waveform near the end of a file, before the footer. Looking the image closer will also reveal the number of samples used for the waves. (36 as opposed to the 17 in the following wave.)

The WAV format

Describing the actual wave file format is really outside the scope of this post, but I'll include the bare description for writing 16-bit, one channel, 44100hz sound files. So, again, the following is just for the purposes of generating the things discussed above, and it is not intended to be a full WAV specification. The alphabetic values are in Ascii.

An easy formula for the entire file size [offset 4-7] is 44+seconds*88200.

0    : R
1    : I
2    : F
3    : F
4-7  : 32 bit value for the entire file size
8    : W
9    : A
10   : V
11   : E
12   : f
13   : m
14   : t
15   : 32 [space]
16   : 16 [16-bit pcm]
17   : 0
18   : 0
19   : 0
20   : 1 [audioformat=1]
21   : 0
22   : 1 [numchannels=mono]
23   : 0
24-27: 44100 [32-bit value for sample rate]
28-31: 88200 [32-bit value for byte rate]
32   : 2 [block alignment]
33   : 0
34   : 16 [bits per sample]
35   : 0
36   : d
37   : a
38   : t
39   : a
40-43: [32 bit value for the wave chunk size]
44-  : [16 bit words for the single channel wave data.]




Sunday, 3 July 2011

Panasonic JR-200UP

The Panasonic JR200
Here's another machine that is nearly 30 years old. It is somewhat obscure, but apparently a few were imported to Finland around 1984. I'll go through some of my initial experiences with this computer, which has been previously unknown to me.

BASIC operation

The Panasonic has a very solid appearance. The casing is bigger than a ZX spectrum, yet considerably smaller than a VIC-20 or a C64. A seventies space typeface declares “Panasonic personal computer 32k memory”. A dry run on the keyboard did not impress, yet it works pretty well in action. The keyboard is not nearly as bad as Laser 200, a bit better than a ZX rubber. With full screen editing and well positioned cursor keys, it's okay for BASIC programming. A feature I like is the inclusion of the BASIC keywords on the keyboard as a kind of reference. It's possible to invoke the keywords with a combination of CTRL+key. Far from saying that this keyboard is comparable to modern keyboards, but it's not a torture.

For the hobbyist (both then and now), the computer is quite a nice treat. Everything works pretty smoothly and the editing is user friendly compared to some other computers at the time. The JR-BASIC is not far from Microsoft BASIC, and for those with previous programming experience it is easy to adapt to. The video and character memory is simply laid out and easy to use even from within BASIC.

Some neat touches make the life of a hobbyist easier. The BASIC interpreter accepts hexadecimal, and there's a built-in function for returning decimal in hex. The character set can be adjusted by directly POKEing into the character set memory space, starting from $D000. Contrast this to the C64, where the same action would have required multiple preparatory pokes plus duplicating the character set from ROM. For learning machine code the 6802-based processor is simple and straightforward.

An interesting feature is a built-in monitor, which can be invoked with the MON command. With the monitor it is possible to change memory contents by entering hex, or examine how the present BASIC program is tokenised, starting from address $0800. Sadly the monitor is somewhat lacking in features, as it does not do disassembly or cannot display the memory contents as characters. However, the 6802 has a clear instruction set and 16-bit addresses are stored in a readable high-low format, both factors which make a hex dump like this a bit more readable. What is a bit unclear that the commands, D, M and G have to be typed in capital letters, which can be a bit annoying as this is not required in BASIC and the hex input itself does not care about the case.

The BASIC line entry is a slightly more picky than some others. It really insists on putting a space before and after the commands. There are no shorthands to the commands, so typing ?1+1 does NOT work. Having to type PRINT each time means the Panasonic works less well as a calculator.

There are supposedly two ways to break into the program, using CTRL-C for exiting normal basic, and a single hard BREAK key which should be able to exit even a stuck machine code loop. In many situations, however, the machine had to be switched off and on. This was quite common practice back in the time, but nowadays it does not feel right to do this. The hard break had to be used when exiting program loops with the PICK keyboard input command. Use of the PICK command makes the loop impervious to the soft break.

The manual is quite sparse. Although it has some useful information on the BASIC instruction set and how to define characters and things like that, it does not have any real examples. The reader is assumed to have a good knowledge about how computers generally work. There's no sample machine code program or a list of 6802 instructions, but these can be found from the Internet.

Graphics

The first impression about the graphics capabilities bring into mind the Sinclair ZX Spectrum, but the presence of the character display points more towards other computers. The palette of Black, Blue, Red, Magenta, Green, Cyan, Yellow and White are very reminiscent of the ZX, and it's nice to see them here. As the Panasonic lacks the variable brightness of the Spectrum, there are only 8 colours available. There are no sprites or hardware scrolling, which is a bit sad, as the CPU is slow for throwing graphics around. The character display and user defined characters alleviates this somewhat, as character displays are very quick for rough graphics. The amount of definable characters is 256+64, enough to give a theoretical resolution of 256x192, comparable to the Spectrum. I can't see why games like Manic Miner could not be reproduced faithfully with some clever on-the-go modification of the character set.

The PLOT command can be used to create graphics with the resolution of 64x48. At first it appears that the computer merely sorts logically some of the characters included in the character set and prints them out as some kind of pseudo-graphics. But it's actually a bit more clever than that. The low resolution graphics is not made out of the character set at all, as it is possible to have four different colours on the same cursor area. Furthermore, there is no need to switch to a different display "mode", as each character on the display in a way has its own mode. Each screen character can be either a standard character, a user defined character, or a low resolution graphics cell. So the low resolution graphics and the normal graphics can coexist on the same display. However, the low resolution graphics has fairly limited uses.

Curiously, even though the BASIC has commands pretty much for any sensible operation, changing the border colour has to be done with POKE $CA00,n. Of course, learning this makes it easy to implement in machine code.

Loading tape files

The Panasonic uses a 8-pin DIN socket for tape input/output. This connection was quite common in machines like the MSX and apparently TRS-80. The pin assignment seems to be quite similar to an MSX, but I have not absolutely checked. As the computer did not come supplied with the standard tape lead, I had to improvise one. Here is how I did it:

The tape connector as seen behind the computer. The cable is not to scale.
GND=Ground, IN=Signal in for loading, OUT=Signal out for recording

I connected the ground to the bases of two microphone style jacks, and connected the signal pins to the jack tips. I connected both to the same ground and it seems to work. I connected the microphone jacks to my Mac Mini audio input and output, and used Audacity as a substitute for a tape recorder.

From the JR BASIC, LOAD is used for loading Basic files, whereas MLOAD is for binaries. A=USR($1000) would then execute the code starting from $1000. Also, the G1000 command within the monitor can also be used.

The Panasonic allows a choice between 600 baud and 2400 baud rates for storage. This can be changed from a switch below the computer. Apparently the 600 baud can be chosen for reliability whereas the 2400 should only be used with Panasonic's dedicated tape recorder. With this setup, the 2400 baud rate has worked fine. Just to be sure I use 44100khz sample rate with 16 bit accuracy. I have not made tests but I believe 8 bits would work just as fine, not so sure about compromising the sample rate though.

Looking at the saved files I started building an understanding on how the data is actually stored into the tape, with the hope of creating audio files from binaries on the Mac. The normal saved recordings are constructed from two parts, the header and the actual data. The header is pretty straightforward but the data part was a bit trickier. Both the header and the data have a checksum, which is built from adding up all the values into an 8-bit counter. Files larger than 255 bytes are divided into page-sized chunks each with their own checksum.

The above diagram depicts how header data is stored in the waveform. The data part uses a bit different format, depending on the chosen baud rate.

In the end I could generate the wave files and load them into the Panasonic. Mostly this was made by imitating the structure of the previously recorded output from the machine. I also spent time trying to find existing software for the Panasonic, and in the end found some .CJR files related to a virtual Panasonic emulator. (by “James the Animal Tamer” in 2002) I did not bother to fire up my PC to find out if the emulator works, as I'm only interested in using the actual machine. There may or may not exist a program called the CJR2WAV or something similar, which would apparently perform the function I was seeking. I did not search comprehensively but it seems to be currently unavailable. Besides, it was more interesting learning experience to try to make my own.

The software

The CJR files were a welcome find. Adjusting my wave generator program I could convert some of the CJR-based programs into wave files that actually could be loaded into the machine. So I was enjoying games like Solitaire, Vortex, Crazy Mazey and so on. There are several games (Junkman Joe, Galactic Chase, Mischievous Mansion) that have a different format and so far I have had no success loading them. Most of the ones I could load were written almost entirely in BASIC, possibly converted from some another computer. I’ll quickly go through some of the more interesting ones.

I loaded the Solitaire first and it is very addictive. The cards can be manipulated with simple keystrokes which is a refreshing change from mouse-based card games. The graphics are likewise very minimal and pleasantly laid out. The program offers four solitaire varieties. Sound is very sparse, with the occasional beep for an invalid move.

Mars Cars is a kind of a loose Pac Man variant, except the enemies do not behave in very intelligent fashion nor is there really a maze to speak of. Some sort of space car is moved in four directions and the player has to catch four treasures guarded by critters. After collecting the treasures the player proceeds to the next screen via the exit. The car can “eat” the obstacles on screen, which forms part of the game strategy. The critters have to be let out of their containment area before the treasure can be safely picked up. If the aliens moved in a less random way the concept could be quite interesting.

Vortex on the first sight appeared to be a fun arcade game. The player has to fire missiles to four directions to counter torpedoes that appear from different sides of the screen. Occasionally a “saucer” appears. Yet the game is devoid of any content or development throughout the game, it merely gets faster all the time until it becomes impossibly fast. There is never more than a single torpedo on screen.

Crazy Mazey is another maze game. This time the player guides a car in a city-like maze, collecting dollar signs and avoiding the pursuing cars. This game utilizes per-pixel moving graphics, not seen in the other games. On the faster level, the game runs pretty smoothly. The enemy cars pursue the player in a very determined and annoying manner!

Santa Paravia is one of those management/diplomacy games which where featured as magazine and book type-in listings in early 80s. You know, the player manages a mine, a city, a nation or a space variant of any of them. The player makes choices of resource allocation and at some point the player is killed randomly because there is a mutiny or plague or whatever. Santa Paravia has an occasional graphical screen that adds some charm to the otherwise text-based proceedings.

Judging from this small sample of games, the Panasonic does not really have that impressive catalogue. The arcade games are a bit lazily done and could have been more interesting with only very little additions. Then again at around 1983 there were really not that many good home computer games on any platform, even though the hardware was already up to it. So perhaps the games are kind of par for the course for this particular vintage. Reputedly the Disney games have more effort put into them, but there’s very little information about them available on the net.

Conclusion

The Panasonic JR-200 is an impressively simple and straightforward computer. In some ways it reminds me of the MSX, but without the video chip hassle. The 6802-based chip has a simple instruction set and although the monitor is not really a full monitor, it does give quick access to beginner level machine code.

Nowadays it has become a truism to say that a hardware is just as good as its' software catalogue. But these were different times. I don't feel right to blame the machine for not having a huge amount of great games, as home home computers at that time had other purposes too. In the 70s, home computers were the domain of hardware hackers and DIY electronics enthusiasts. As the audiences became wider in the beginning of the 80s, a home computer might still have been bought for hobbyist purposes, such as learning to program BASIC and then moving onto machine code. There may not have been so much thought put to whether the computer was able to do something "useful". Messing around and learning was (and is) fun in itself. I cannot imagine a much better introductory machine for this purpose than the JR200. Whereas many computers of that time have become very off-putting by the way of user experience, this computer is still quite neat for typing in programs and seeing how they work. It combines a good quality hardware with simple and usable BASIC and gives full 32K space to play with.

Panasonic JR-200 at Old-computers.com


My work on the tape generation.