Sunday, 30 June 2013

My brain is hurting

I'm a bit prone to grandiosity.

The directory I'm working in at the moment is called "OS" - operating system. This on the right is an operating system. Can a watch from the early 80s with 2k of ROM and the equivalent of 64 bytes of RAM be said to have an "Operating System" ?

Well, anyway, I've been writing OS/BIOS type routines for some time today. Having got the Mk2. Indirect write going, I've been tackling the bad 'un that has had be thinking for a while.

The Watchman has three digits (plus an extra '1', plus a colon) in its display area. (So no 24 hour clock ....). These are wired up to six of the segment pins of the SM5 (a1b1a2b2ba3b3) which are multiplexed to with H1-H4 to give 24 possible segment combination of which 23 are used (3 x 7 segments, 1 colon, 1 '1' digit).

The code I've been working on - which now works - does the following - it reads three nibbles from RAM (which are the three digits) and 2 bits from a fourth (one for the colon, one for the '1' digit). For the three digits in turn it looks up the seven segment pattern on for that digit (there are 2 tables, it's a 4 bit processor) and stores it in the correct place on the display.

This is slightly harder than it looks, firstly because of the indirection thing again, and also because the digits are not in the correct order in memory i.e. the digit order is designed for the LCD display not to be coded.

The (hopefully) first and last version is here..... , it has all the characteristics of 4 bit assembler code - the bits of code are in the wrong order (there are four 0,1,2,3 and they are in the order 1,0,2,3), it's incomprehensible even with comments and it's very long winded to do something relatively simple (and there's an entirely different routine which clears the display stuck in the middle of it .....) and half of it is near identical to the other half (the two bits that do the digit->7 segment pattern->write look up)

But it does work :)

So, the next and last messy bit of code is the same sort of mapping, but this one is mapping a coordinate system onto the main bit of the display (the dots and circles). Even this far out, this too is going to be fun to write.

The other main bit is tone generation. Even with a 16Khz clock processor, it's going to be spending most of it's time either doing nothing or generating sound. I would like this too to be automated.

A slightly less barmy answer

I had an idea last night. (Gives you some idea how dull my nights are).

The carry transfer method is a bit slow, the indexed jump method is a bit unwieldy. So why not combine the two ?

So, in the current implementation, the value to be written is halved and the LSB is stored in the carry, which gives us only 8 values to write out. So it's about twice the speed of the bitshifting one (14 cycles rather than 33) and about half as long again, but still fits comfortably in one page.

What it is doing is (in C) something like the code below. It does look very bizarre as a way of copying into memory but it works because we can preserve the carry flag when loading 'addr' into the RAM address register, but we can't preserve the accumulator.

It's a recurring nightmare writing this that there's a really obvious simple solution to this which I can't see...... and these posts will then make me look really dim.

// same code effectively as memory[addr] = acc
carry = acc % 2           // put LSB of acc in carry bit
acc = acc / 2             // rotate A right e.g. halve it.
switch (acc) {
   case 0:     memory[addr] = 0+carry;break;
   case 1:     memory[addr] = 2+carry;break;
   case 7:     memory[addr] = 14+carry;break;



How to do indirect writes - the barmy answer.

Yes, I've finally come up with the answer.

Regular reader(s) (he posted optimistically) may remember I have a problem that I cannot do an indirect write in the SM-510.

For example, I cannot have a value in one memory location and use that as an address to store another value in.

I'd come to the conclusion that this was actually impossible to do, short of huge indexed jump type solutions. The reason being there was only the two storage locations inside the CPU and one was needed to load a value into the other from memory, hence it was impossible to load a value into each from memory.

But I was wrong ! There is another storage location - the carry flag.

So what this solution does, is to a bit at a time rotate the value to be written into the carry flag, then rotate that bit into the place where it is being saved. As you can imagine this isn't the most efficient method of transferring four bits but it does work.

It is basically this four times, once for each bit in question:

    exc 0
    exc 0
    exc 0
    exc 0

It might actually be better in practice to do it via an indexed jump solution, because it's quicker, albeit not as demented and smart-alecky.

Here's a challenge if you want to stay awake. Set up a scenario - say Memory location 4 contains 7, which is the memory location you want to set to 6. So A = 6, B = 4 and RAM(B) = 7.  Now figure out how it works  to put 6 in RAM(7) :)

(it's actually simpler than it looks. The first rot shifts the source data bit, the second shifts it into the target data. the exc 0/exbla/exc 0 sequences set up the target data and unpick it again)

Actually my current version adds 1 to RAM(B) (an incb/skip before the last exbla in the fourth bit shift) so it can be called sequentially to copy memory locations one after the other.

The TMS1x00 does this in one instruction basically, TMY.