Sunday, 23 December 2012

Everything else

Having addressed the two major problems, there are some upsides.

The main one is the use of GI's CP1610 Processor (also used in the Intellivision). I've always liked this chip. I did some ARM assembler before it and there's actually quite a strong similarity between the two - the CP1610 is quite a nice device to program.
There are, however, problems.

Firstly the processor only operates when the screen is in 'blank'.

A NTSC or PAL TV Signal consists of 60 or 50 frames a second, consisting of 262 or 312 lines per frame. Video is displayed only for some of these (up to about 190 maximum in NTSC) - this comes from the signal driving a CRT whose beam sweeps left to right, top to bottom to paint the picture. When it reaches the bottom, it turns off and goes back to the top, this is the 'vertical blank'.

Each of the double character slots is 32 lines (according to the documents), six of these is 192 display lines, so the processor is only running (262-192)/262 = 26% of the time.  This gives us yet another handicap to overcome.

It does this so the GPU Chip can access the RAM when displaying the screen - so there are no "arguments" if the CPU or GPU want to read memory at the same time. Though most computers of the day avoided this in other ways, this one either can't or won't.

This might be because the RAM chip is accessed via the ROM chip rather than both being on the same physical bus as on virtually every other machine ever.

The Clock Speed is another thing I don't know. The intellivision clocks at 895Khz which is a speed directly related to the 3.58Mhz colour clock that NTSC uses. (a quarter of it). The Unisonic only has one Crystal, this has to be 3.58Mhz otherwise the colour won't work, so I'm having an intelligent guess that it too will be clocked at a quarter of this. This amounts to about 100,000 instructions a second, but because the processor is off most of the time it's more like 25,000 instructions a second which is pretty slow.

The CP1610 is also peculiar in that it is a 10 bit word instruction, so you have to use GIs ROM chips. (everything else being multiples of 8)

Just to complicate things further, the system memory is 8 bit bytes and byte addressed and the processor operates on 16 bit data.

Mattels' Intellivision, very much the big brother of this system, has 3 types of memory - 8 bit scratch, 8 bit graphics and 16 bit system. The Unisonic, though, only has 8 bit RAM, implemented using a pair of 2112s, a standard 256x4 Static RAM chip of the time. (2/3 of this is display memory)

This has unfortunate consequences. The CP1610's subroutine system works at its simplest level by copying the program counter register (r7) about like this.

              call MySubroutine
MySubroutine: <do stuff>
              mov r5,r7

The "call" instruction does not, unlike most other processors (4004 onwards, 6800, 6502 etc), save the return address on a stack. It copies the program counter (r7) into register (r5) and then does a jump. So to return from subroutine you copy the return address (in r5) back to the program counter (hence mov r5,r7)

Now, this works great, and is very quick, if you only have one level of subroutine call. So if you add another level in , that second one will change r5 when you do the second call, so your subroutine has to save R5 on a stack itself if it calls another subroutine (or itself recursively)

            mvo@ r5,r6
            call AnotherSubroutine
            mvi@ r6,r7

The '@' is indirection. The mvo stores r5 at a memory location pointed to by r6 (and r6 is then incremented). The mvi@ pre decrements, and reloads that stored value into r7 (the program counter) - you could copy it back into R5 and then copy it to R7.

Now, in the Unisonic it doesn't work. This is because the CP1610s support for "SBDB" - the system it uses to access 8 bit memory on a 16 bit data register, doesn't work for the stack pointer (R6). So you cannot use the standard CP1610 subroutine system (which is supported by the assembler) and you need four times as much code. You have to do it a byte a a time, save one half of the return address, then the other.

            mvo@    r5,r6               
            movr    r5,r3               
            swap    r3
            mvo@    r3,r6 

            mvi@    r6,r3                
            swap    r3
            xor@    r6,r3               
            movr    r3,r7               

So each subroutine call requires extra instructions and time. (the above code writes the lower 8 bits, then the upper 8 bits - swap exchanges the upper and lower halves of the 16 bit word). You can jump into a single instance of the exit code, but the preamble has to be in every subroutine.

This makes the 16 bit data on the processor almost completely useless. You can't use the subroutine system, and memory is byte accessed for the graphics (because 1 8 bit byte is the same as 1 character on the screen). You can use the SBDB system to access word data in memory but there won't be much of that - there isn't much scratch RAM so I would reckon most if not all the data access is done byte wide.

Oh joy.

When I write the emulator code I'm not actually going to code the SBDB because it is of so little use, which has the advantage of significantly simplifying it (there are no ROM Dumps, I'm doing the whole thing from scratch)

The sound is very much like the Channel F's. It can play one of three tones - 500Hz, 1Khz and 2Khz. These are quite high pitched (A in the standard scale is 440Hz) but this is just about sufficient for beeps and warbles and so on.

It probably would be better to have all of these an octave lower (2Khz is a very high pitch) but compared to the rest of the system, it's simple and it works.

No comments:

Post a Comment