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.
Sunday, 30 June 2013
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;
}
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:
rot
exc 0
exbla
exc 0
rot
exc 0
exbla
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.
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:
rot
exc 0
exbla
exc 0
rot
exc 0
exbla
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.
Saturday, 29 June 2013
Exclusive Or the hard way.
I thought following yesterday's post you might want to see what exclusive or on a TMS1000 is like. This is it :)
(It isn't the most incomprehensible thing I've ever written. That goes to my multi-way scrolling and sprite engine for the TI83 calculator - about half the code was self modifying just to make it run fast enough)
ExclusiveOr:
tcy XORResult ; clear XOR Result
tcmiy 0
tcy XORCounter ; set counter to 4
tcmiy 4
ExclusiveOrLoop:
tcy XORResult ; shift the result one bit left.
tma
amaac
tam ; store it back.
tcy XORNibble1 ; read Nibble 1
tma
tcy XORNibble2 ; point at Nibble 2
tbit1 3 ; if bit 3 of Nibble 2 is set
br XOBit3Set ; add 8 to A, eg xor of MSB of Nibble
br XOBit3Unchanged ; because add = exor with no carry in
XOBit3Set: ; no carry in is why we cant just add
a8aac
XOBit3Unchanged: ; bit 3 of A is XOR bit 3 of N1,N2.
alec 7 ; if it is set, set LSB of result
br XONextBit
tcy XORResult ; this bit will be shifted into place
sbit 0
XONextBit:
tcy XORNibble1 ; shift nibble 1 left
tma
amaac
tam
tcy XORNibble2 ; shift nibble 2 left
tma
amaac
tam
tcy XORCounter ; Decrement XOR Counter
dman
tam ; write back
mnez ; check counter not zero
br ExclusiveOrLoop ; and keep going till all four bits.
tcy XORResult ; Load the result into A
tma
retn
(It isn't the most incomprehensible thing I've ever written. That goes to my multi-way scrolling and sprite engine for the TI83 calculator - about half the code was self modifying just to make it run fast enough)
ExclusiveOr:
tcy XORResult ; clear XOR Result
tcmiy 0
tcy XORCounter ; set counter to 4
tcmiy 4
ExclusiveOrLoop:
tcy XORResult ; shift the result one bit left.
tma
amaac
tam ; store it back.
tcy XORNibble1 ; read Nibble 1
tma
tcy XORNibble2 ; point at Nibble 2
tbit1 3 ; if bit 3 of Nibble 2 is set
br XOBit3Set ; add 8 to A, eg xor of MSB of Nibble
br XOBit3Unchanged ; because add = exor with no carry in
XOBit3Set: ; no carry in is why we cant just add
a8aac
XOBit3Unchanged: ; bit 3 of A is XOR bit 3 of N1,N2.
alec 7 ; if it is set, set LSB of result
br XONextBit
tcy XORResult ; this bit will be shifted into place
sbit 0
XONextBit:
tcy XORNibble1 ; shift nibble 1 left
tma
amaac
tam
tcy XORNibble2 ; shift nibble 2 left
tma
amaac
tam
tcy XORCounter ; Decrement XOR Counter
dman
tam ; write back
mnez ; check counter not zero
br ExclusiveOrLoop ; and keep going till all four bits.
tcy XORResult ; Load the result into A
tma
retn
Kevin Savetz's book.
As a slightly relevant aside, I did get a prize in the last RC, kindly donated by Kevin Savetz (I'm guessing this is him :) )
If you ever want an entertaining read and you are a 70s/80s computer geek, it's highly recommended.
Actually a lot of the book could be about me, (if you cross out where it says "Atari" and replace it with "Sharp MZ-80K and BBC Micro.", I never owned any Atari kit before an Atari ST.
If you ever want an entertaining read and you are a 70s/80s computer geek, it's highly recommended.
Actually a lot of the book could be about me, (if you cross out where it says "Atari" and replace it with "Sharp MZ-80K and BBC Micro.", I never owned any Atari kit before an Atari ST.
Friday, 28 June 2013
Some minor changes and thoughts......
Firstly some minor updates. I have changed a couple of things on the assembler and emulator - the assembler now returns an error code on an error (handy for scripts) and the emulator now sounds somewhat better (pitch wise), but also worse as I've made it so it does what the real thing does and modulate another tone. It now sounds generically "cheap buzzer" awful, a bit like playing music underwater.
Now, if you've read this far you are probably wondering who this bloke is (it isn't me, too much hair). This is a fellow called Alan Turing who did a lot of the early development work in computing, including working at Bletchley Park in the war.
He also had this concept of "Turing completeness" which was used to differentiate between "not really computers" like the Code breaking machine Colossus or the work of the German Konrad Zuse. Both of these were nearly-but-not-quite computers, they missed things that meant you couldn't write some programs with it - I think Zuse's early machines didn't have conditional branching.
I nearly abandoned this RetroChallenge because I think the SM-5, while not exactly not Turing complete, is broken.
When I was looking at the COP411 (a NatSemi MCU) I wondered why they had this odd, single, exchange memory location $1F with the accumulator instruction (XAD 3,15). It just looked wierd. Why not allow the swap of any memory location with the accumulator directly ?
Now I know. The SM-5's big problem is that it can't do an indirect write very well.
You have two registers to work with, more or less, known as A and B, A is the accumulator and B is the memory pointer. Most modern CPUs have an instruction like
sta $4032
which for a 6502 stores the accumulator at location 0x4032. Most old fashioned 4 bitters don't have this. You load the "Memory Pointer" - B in Sharp/Nat Semi, XY in Texas with the address then write the accumulator, e.g.
lbi $3F
x 0
loads B with $3F and then saves A at that memory location (actually it swaps A and the memory location).
The big problem with the SM-5 is that it doesn't have a mechanism - that I can work out, that allows you to do an indirect save (in any kind of sensible fashion.
For example, you might want to save the value in A in the memory location whose address is in $40, rather than $40 itself. So suppose memory location $40 contains $17, your 'save' would go to location $17.
I don't think you can do this on an SM-5. You have the value in A, but to load B, you have to put the address ($40) in B, load memory location into A (then A will be $17), then copy it into B .... but in doing this you have overwritten A.
If you save A so you can load B this is fine, except that to get A back you have to put the address of where you stored it in B.
That's why (I think) the COP411 has XAD 3,15. It allows you to load A without putting an address in B first. I reckon this is a last minute "oh XXXX" design decision :)
- there are ways round it but they are all bonkers - e.g. having 16 identical copies of
lbi <some value>
lda 0
lbi 4
x 0
rtn0
except for the save address (the 4) and doing an indexed jump to the right one, and things like that. But you have to code for the processor you have.
Exclusive OR for example. Neither this nor the TMS1x00 has it (or AND or OR for that matter), and I needed it for the TMS1000 project (Simon). I ended up writing a sort of 'check and rotate in a loop' bit of code to just exclusive or two values, and it took nearly a whole page of the ROM space.
That's why the Sharp SM-5 series is broken (the other things like using T for every instruction going, I can live with .....)
TBH, I did seriously consider abandoning it for another project. But it's not called RetroChallenge for nothing.
Now, if you've read this far you are probably wondering who this bloke is (it isn't me, too much hair). This is a fellow called Alan Turing who did a lot of the early development work in computing, including working at Bletchley Park in the war.
He also had this concept of "Turing completeness" which was used to differentiate between "not really computers" like the Code breaking machine Colossus or the work of the German Konrad Zuse. Both of these were nearly-but-not-quite computers, they missed things that meant you couldn't write some programs with it - I think Zuse's early machines didn't have conditional branching.
I nearly abandoned this RetroChallenge because I think the SM-5, while not exactly not Turing complete, is broken.
When I was looking at the COP411 (a NatSemi MCU) I wondered why they had this odd, single, exchange memory location $1F with the accumulator instruction (XAD 3,15). It just looked wierd. Why not allow the swap of any memory location with the accumulator directly ?
Now I know. The SM-5's big problem is that it can't do an indirect write very well.
You have two registers to work with, more or less, known as A and B, A is the accumulator and B is the memory pointer. Most modern CPUs have an instruction like
sta $4032
which for a 6502 stores the accumulator at location 0x4032. Most old fashioned 4 bitters don't have this. You load the "Memory Pointer" - B in Sharp/Nat Semi, XY in Texas with the address then write the accumulator, e.g.
lbi $3F
x 0
loads B with $3F and then saves A at that memory location (actually it swaps A and the memory location).
The big problem with the SM-5 is that it doesn't have a mechanism - that I can work out, that allows you to do an indirect save (in any kind of sensible fashion.
For example, you might want to save the value in A in the memory location whose address is in $40, rather than $40 itself. So suppose memory location $40 contains $17, your 'save' would go to location $17.
I don't think you can do this on an SM-5. You have the value in A, but to load B, you have to put the address ($40) in B, load memory location into A (then A will be $17), then copy it into B .... but in doing this you have overwritten A.
If you save A so you can load B this is fine, except that to get A back you have to put the address of where you stored it in B.
That's why (I think) the COP411 has XAD 3,15. It allows you to load A without putting an address in B first. I reckon this is a last minute "oh XXXX" design decision :)
- there are ways round it but they are all bonkers - e.g. having 16 identical copies of
lbi <some value>
lda 0
lbi 4
x 0
rtn0
except for the save address (the 4) and doing an indexed jump to the right one, and things like that. But you have to code for the processor you have.
Exclusive OR for example. Neither this nor the TMS1x00 has it (or AND or OR for that matter), and I needed it for the TMS1000 project (Simon). I ended up writing a sort of 'check and rotate in a loop' bit of code to just exclusive or two values, and it took nearly a whole page of the ROM space.
That's why the Sharp SM-5 series is broken (the other things like using T for every instruction going, I can live with .....)
TBH, I did seriously consider abandoning it for another project. But it's not called RetroChallenge for nothing.
Monday, 24 June 2013
Another bug fix
The TM, TML and RTN instructions weren't doing their job, this is now fixed.
Additionally there is a procedure mechanism using TM which works as follows:
Definition
proc pname
..
(do stuff)
..
rtn0
Call
tm pname
it also has a pseudo operation 'extpage' which works like nextpage except that it skips pages 0 and 16 of ROM memory which are used for this mechanism. Procedures can be defined retrospectively. Only 32 (currently) are allowed.
The point of this is that you can write page independent code. The assembler takes care of patching up the procedure links (it does this via pages 0 and 16, and possibly later 17), and you don't have to bother with TML (which only works in certain pages). This can now just be ignored.
The stack is only two levels deep so it's still not going to be very structured though.....
Additionally there is a procedure mechanism using TM which works as follows:
Definition
proc pname
..
(do stuff)
..
rtn0
Call
tm pname
it also has a pseudo operation 'extpage' which works like nextpage except that it skips pages 0 and 16 of ROM memory which are used for this mechanism. Procedures can be defined retrospectively. Only 32 (currently) are allowed.
The point of this is that you can write page independent code. The assembler takes care of patching up the procedure links (it does this via pages 0 and 16, and possibly later 17), and you don't have to bother with TML (which only works in certain pages). This can now just be ignored.
The stack is only two levels deep so it's still not going to be very structured though.....
First (tuneless but working) release of emulator with working code.
Yep, a first release. Only version 0.1 but it does work :)
There are three components. There is the assembler, the emulator (pictured) and the source code and binary for the demo
The latter is a zip with two files in it - an assembler source (hardware.asm) and a binary object (test.bin). You can assemble the source file using the jar file in the assembler using
java -jar sharpasm.jar hardware.asm
This will actually produce a file called hardware.bin. I used test.bin because that's the name of the default file in the emulator , e.g. you can either start it with
java -jar watchman.jar hardware.bin
or
java -jar watchman.jar
the former loading hardware.bin and the latter loading test.bin. It starts off much as shown (the reason for the odd patterns on the watch display is random data in RAM).
If you run it (Debug/Run or press F5) the display will change continually. All it's doing is counting 0000-1111 in binary and filling display memory with that nibble, so you get a cycling pattern. It's a very dull demo, it just is there to test the buttons and buzzer are working.
If you give the watch window the focus the keys will work. They are Z (left), X (right), L (fire) and M (mode). All they do is stop the pattern circulating and fill the display memory with the bit pattern representing those keys. Pressing the mode key causes the buzzer to start as well, this is really bad at the moment and I'll make it better. As it's modulating a 4Khz tone it will sound better on the emulator than it would really.
If the keys don't seem to be working is probably because you don't have the focus on the watch (right hand) window.
But it does work :)
There are three components. There is the assembler, the emulator (pictured) and the source code and binary for the demo
The latter is a zip with two files in it - an assembler source (hardware.asm) and a binary object (test.bin). You can assemble the source file using the jar file in the assembler using
java -jar sharpasm.jar hardware.asm
This will actually produce a file called hardware.bin. I used test.bin because that's the name of the default file in the emulator , e.g. you can either start it with
java -jar watchman.jar hardware.bin
or
java -jar watchman.jar
the former loading hardware.bin and the latter loading test.bin. It starts off much as shown (the reason for the odd patterns on the watch display is random data in RAM).
If you run it (Debug/Run or press F5) the display will change continually. All it's doing is counting 0000-1111 in binary and filling display memory with that nibble, so you get a cycling pattern. It's a very dull demo, it just is there to test the buttons and buzzer are working.
If you give the watch window the focus the keys will work. They are Z (left), X (right), L (fire) and M (mode). All they do is stop the pattern circulating and fill the display memory with the bit pattern representing those keys. Pressing the mode key causes the buzzer to start as well, this is really bad at the moment and I'll make it better. As it's modulating a 4Khz tone it will sound better on the emulator than it would really.
If the keys don't seem to be working is probably because you don't have the focus on the watch (right hand) window.
But it does work :)
Friday, 21 June 2013
Complex wiring completed.
I've got the wiring done now. This is the mapping between pixels in RAM Memory (actually using the test bed) and the LCD display. I can turn bits off and on and the display mirrors it - as in the game "Blastaway" (Breakout in practice) shown here.
This is not totally consistent because the LCD design is set up for manufacturing not developing. Coders work round oddities to keep the manufacturing costs down.
Next thing to do is to wire this up to the actual emulated CPU and get some real code running.
The CPU core has run real code, mainly to test that it's functioning at the right speed - I set up 16 nibbles as a decimal counter and set it going. Multiplying the number of instruction cycles per loop by the counter gives you a rough total of the cycles done, and it can be timed with a watch (approximately).
This is what the code looks like (the first bit zeros data memory, not guaranteed on reset).
lbl 0,0 ; clear row 0
clear:
lax 0
exci 0
t clear
nocarry: ; come back here to increment counter
lbl 0 ; point to first digit
increment:
lda 0 ; add 1 to digit
adx 1
exc 0
lda 0 ; retrieve , add 6 for decimal overflow
adx 6
t nocarry
exci 0 ; dec overflow, write the zero back and bump BL.
t increment ; loop back don't set B to zero as inc next digit.
The keyboard and sound has to be done too but these are much simpler.
This is not totally consistent because the LCD design is set up for manufacturing not developing. Coders work round oddities to keep the manufacturing costs down.
Next thing to do is to wire this up to the actual emulated CPU and get some real code running.
The CPU core has run real code, mainly to test that it's functioning at the right speed - I set up 16 nibbles as a decimal counter and set it going. Multiplying the number of instruction cycles per loop by the counter gives you a rough total of the cycles done, and it can be timed with a watch (approximately).
This is what the code looks like (the first bit zeros data memory, not guaranteed on reset).
lbl 0,0 ; clear row 0
clear:
lax 0
exci 0
t clear
nocarry: ; come back here to increment counter
lbl 0 ; point to first digit
increment:
lda 0 ; add 1 to digit
adx 1
exc 0
lda 0 ; retrieve , add 6 for decimal overflow
adx 6
t nocarry
exci 0 ; dec overflow, write the zero back and bump BL.
t increment ; loop back don't set B to zero as inc next digit.
The keyboard and sound has to be done too but these are much simpler.
Wednesday, 19 June 2013
Testing tools.
Today haven't done much. I have created the tool on the right.
It's a picture of a chunk of memory in the Microcontroller - nibbles $60-$7F, modelled on the LCD display area in the datasheet.
It's for testing the display (see ... below ?) - bits in memory correspond to bits on the LCD screen.
It allows me to test the wiring is write by changing bits in memory without having to do it via code - just clicking on a bit toggles it and sends notification of a bit change to a listener. I can connect this to the LCD hardware and check that toggling bits here turns the right bits of LCD hardware on and off
This is one of the joys of interfaces. I have an "IHardware" interface which is basically the external bits of the microcontroller. As long as I stick with the contract it doesn't matter what's driving it or what it drives - it will work.
Hopefully :)
You may wonder with my previous Retrochallenge projects why I choose things with very limited display hardware. The honest reason is that I cannot draw for toffee :) So something with fixed or simple graphics avoids that problem. (It's part of the reason I like writing games for the Studio 2).
It's a picture of a chunk of memory in the Microcontroller - nibbles $60-$7F, modelled on the LCD display area in the datasheet.
It's for testing the display (see ... below ?) - bits in memory correspond to bits on the LCD screen.
It allows me to test the wiring is write by changing bits in memory without having to do it via code - just clicking on a bit toggles it and sends notification of a bit change to a listener. I can connect this to the LCD hardware and check that toggling bits here turns the right bits of LCD hardware on and off
This is one of the joys of interfaces. I have an "IHardware" interface which is basically the external bits of the microcontroller. As long as I stick with the contract it doesn't matter what's driving it or what it drives - it will work.
Hopefully :)
You may wonder with my previous Retrochallenge projects why I choose things with very limited display hardware. The honest reason is that I cannot draw for toffee :) So something with fixed or simple graphics avoids that problem. (It's part of the reason I like writing games for the Studio 2).
Tuesday, 18 June 2013
Display under construction
So, now there's a display component - sort of - that will act as the display panel for the Watch Display.
This isn't a real Java component though. It's a classic 'game' design - there are a collection of BufferedImages() that draw onto another BufferedImage() which is then copied to the display.
You could split this up into components and layouts, but it would slow it down and it would be interesting to get it right.
This isn't a real Java component though. It's a classic 'game' design - there are a collection of BufferedImages() that draw onto another BufferedImage() which is then copied to the display.
You could split this up into components and layouts, but it would slow it down and it would be interesting to get it right.
Minor updates
Have been testing the CPU Core this morning, and fixed a couple of small bugs round the divider timer (twice as fast as it should be on the flag tests), so the generator code is updated to reflect those features.
Also, the assembler has had the ability to do data statements added. I'm not quite sure why I bothered with this as there appears to be no way of reading from the ROM.
You can create a table because LAX skips subsequent lax instructions. So if you have:
lax 4
lax 7
lax 14
lax 3
..
..
and jump in at (say) "lax 7" it executes lax 7 (put 7 in the accumulator) but ignores all subsequent lax instructions. But if you want to read 8 bit values, you can't. It's a bit wasteful.
It may even make sense to create a psuedo op "table" which generates the above with
table 4,7,14,3
Next thing is to create the display object which will simulate the LCD screen.
Also, the assembler has had the ability to do data statements added. I'm not quite sure why I bothered with this as there appears to be no way of reading from the ROM.
You can create a table because LAX skips subsequent lax instructions. So if you have:
lax 4
lax 7
lax 14
lax 3
..
..
and jump in at (say) "lax 7" it executes lax 7 (put 7 in the accumulator) but ignores all subsequent lax instructions. But if you want to read 8 bit values, you can't. It's a bit wasteful.
It may even make sense to create a psuedo op "table" which generates the above with
table 4,7,14,3
Next thing is to create the display object which will simulate the LCD screen.
Monday, 17 June 2013
First working assembler
The first version of the Sharp SM-5 assembler is released and seems to work pretty much okay. It is one of the links on the right.
It comes in a zip file with a demo source file and an assembled binary file. To run it just run java -jar sharpasm.jar demo.asm and it runs and assembles a file demo.bin. It always produces a 2,816 byte binary file as output.
Source will be released later on, likely at the end of the Retrochallenge sometime. It's very much version 0.1 at the moment, though I think it works.
L8R: Minor update to v0.11, missed something silly out.
It comes in a zip file with a demo source file and an assembled binary file. To run it just run java -jar sharpasm.jar demo.asm and it runs and assembles a file demo.bin. It always produces a 2,816 byte binary file as output.
Source will be released later on, likely at the end of the Retrochallenge sometime. It's very much version 0.1 at the moment, though I think it works.
L8R: Minor update to v0.11, missed something silly out.
Sunday, 16 June 2013
First working emulator.
I will release all the source code and runnable versions of the programs and so on, but not yet (it will be mostly Java)
However I have managed to get a working SM-510 emulator going (see picture). I haven't bought a Mac, it is ArchLinux with Cupertino decorations (same as Mac).
This is part of my emulator framework, which does 99% of the work here ; all you have to do is tell it what to put in the CPU area, the code memory area and the data memory area. It also provides stuff like an expression evaluator for assemblers and a beeper that tend to get used a fair bit (have you ever tried to write code to simply beep in C# ???)
It won't work for really complicated processors but it works fine for the 4 and 8 bit processors I've tried it on and some really old ones (I have been tinkering with really old computers like SEAC and the TX-0, TX-0 is 18 bits wide and SEAC is 40. One big mid innings tweak was to change the datatype from int to long to cope with the very wide instruction words)
The framework does all the work of the time synchronisation and single stepping and breakpoints and all that kind of stuff. It's a little primitive but it does basically work. As you can see from the picture I haven't quite got the hang of Swing layouts yet, hence the big white gap at the bottom :)
All you need to do is to subclass the Processor class and ask it to implement a couple of interfaces so the controller can interrogate and control it. Going from the processor class which just emulated the CPU to this working emulator took about 20 minutes.
There is one cheat though. Many of these early CPUs do not use a program counter that goes up in ones, but a recycling shift register (look up LFSR on wikipedia) which generates a predictable pseudo random sequence. Presumably because it is much lighter on silicon. So instead of numbers going 0,1,2,3 it goes 0,13,23,14,7,57,48 or something like that. I have simply missed that out. I'm pretty certain that the developer tools (as with the TMS series) sort this out for you and you just pretend it doesn't exist. Otherwise all your code lines would be mixed up.
At the moment, this cannot run anything at all (the code and data in the display above is just random numbers used for testing). There are some very good free macroassemblers out there - ASL will assemble all kinds of wierd and wonderful processors, but nothing that will Assemble SM-510 source.
So the next task is to create an assembler so I can actually get some real code running.
Then I will implement the hardware interface - this system here works but the I/O commands go into the ether - a dummy implementation of the IHardware interface is connected to it. This means the display will look much the same but it will acquire a partner window, which is the watch display face.
However I have managed to get a working SM-510 emulator going (see picture). I haven't bought a Mac, it is ArchLinux with Cupertino decorations (same as Mac).
This is part of my emulator framework, which does 99% of the work here ; all you have to do is tell it what to put in the CPU area, the code memory area and the data memory area. It also provides stuff like an expression evaluator for assemblers and a beeper that tend to get used a fair bit (have you ever tried to write code to simply beep in C# ???)
It won't work for really complicated processors but it works fine for the 4 and 8 bit processors I've tried it on and some really old ones (I have been tinkering with really old computers like SEAC and the TX-0, TX-0 is 18 bits wide and SEAC is 40. One big mid innings tweak was to change the datatype from int to long to cope with the very wide instruction words)
The framework does all the work of the time synchronisation and single stepping and breakpoints and all that kind of stuff. It's a little primitive but it does basically work. As you can see from the picture I haven't quite got the hang of Swing layouts yet, hence the big white gap at the bottom :)
All you need to do is to subclass the Processor class and ask it to implement a couple of interfaces so the controller can interrogate and control it. Going from the processor class which just emulated the CPU to this working emulator took about 20 minutes.
There is one cheat though. Many of these early CPUs do not use a program counter that goes up in ones, but a recycling shift register (look up LFSR on wikipedia) which generates a predictable pseudo random sequence. Presumably because it is much lighter on silicon. So instead of numbers going 0,1,2,3 it goes 0,13,23,14,7,57,48 or something like that. I have simply missed that out. I'm pretty certain that the developer tools (as with the TMS series) sort this out for you and you just pretend it doesn't exist. Otherwise all your code lines would be mixed up.
At the moment, this cannot run anything at all (the code and data in the display above is just random numbers used for testing). There are some very good free macroassemblers out there - ASL will assemble all kinds of wierd and wonderful processors, but nothing that will Assemble SM-510 source.
So the next task is to create an assembler so I can actually get some real code running.
Then I will implement the hardware interface - this system here works but the I/O commands go into the ether - a dummy implementation of the IHardware interface is connected to it. This means the display will look much the same but it will acquire a partner window, which is the watch display face.
Macro Instruction Set Generation
Having got that out of my system, I have actually been doing something productive. I've added the CPU Core generator to the links on the right.
This is a bit like a specialised macro processor - it's a program in Python and a definition file which contains macro style definitions for the processor. It looks a bit like this (these are three real examples).
0B "EXBLA" _temp = A;A = BL;BL = _temp
5F "LBL @" _temp = fetch();BL = _temp & 0xF;BM = _temp >> 4
18-1B "LDA {O}" A = {M};BM = BM ^ 0x{O}
The first one is a simple instruction "EXBLA" which exchanges A and Bl. The second one is a two byte instruction (that's what the '@' is for that fetches an operand and puts it into BL and BM. The final one is three instructions , 18-1B which generate instructions LDA 0, LDA 1, LDA 2, LDA 3 - each reads memory into A and the exclusive or's the Bm register with 0-3 respectively.
The macroprocessor scans it, chucks the comments and produces two files. One is the innards of a huge switch() statement - for the first one it would generated something like:
case 0x0B: /* exbla */
_temp = A;A = BL;BL = _temp;
break;
and it also creates an array of all the mnemonic names which the disassembler built into the emulation shell uses. These are cut and pasted into Eclipse.
This does produce a very long and unwieldy case statement. But it does have some advantages. Any error can be corrected by changing the definition file and will fix everything consequently. It keeps the Java compiler generating a table jump rather than a sequence of If statements which it does if the case is sparsely populated. Where it wastes is things like TM which has 64 near identical instructions which vary only in their single parameter.But what I've found is that this doesn't seem to balloon the code, which is probably down to the compiler.
So then that's wrapped in a simple processor shell which has some methods for handling the more complex instructions. Hardware stuff is passed of to an Interface which represents the I/O pins.
The next stage is to make it work as an emulator. Which I've actually already done so I will write another post immediately following this.
This is a bit like a specialised macro processor - it's a program in Python and a definition file which contains macro style definitions for the processor. It looks a bit like this (these are three real examples).
0B "EXBLA" _temp = A;A = BL;BL = _temp
5F "LBL @" _temp = fetch();BL = _temp & 0xF;BM = _temp >> 4
18-1B "LDA {O}" A = {M};BM = BM ^ 0x{O}
The first one is a simple instruction "EXBLA" which exchanges A and Bl. The second one is a two byte instruction (that's what the '@' is for that fetches an operand and puts it into BL and BM. The final one is three instructions , 18-1B which generate instructions LDA 0, LDA 1, LDA 2, LDA 3 - each reads memory into A and the exclusive or's the Bm register with 0-3 respectively.
The macroprocessor scans it, chucks the comments and produces two files. One is the innards of a huge switch() statement - for the first one it would generated something like:
case 0x0B: /* exbla */
_temp = A;A = BL;BL = _temp;
break;
and it also creates an array of all the mnemonic names which the disassembler built into the emulation shell uses. These are cut and pasted into Eclipse.
This does produce a very long and unwieldy case statement. But it does have some advantages. Any error can be corrected by changing the definition file and will fix everything consequently. It keeps the Java compiler generating a table jump rather than a sequence of If statements which it does if the case is sparsely populated. Where it wastes is things like TM which has 64 near identical instructions which vary only in their single parameter.But what I've found is that this doesn't seem to balloon the code, which is probably down to the compiler.
So then that's wrapped in a simple processor shell which has some methods for handling the more complex instructions. Hardware stuff is passed of to an Interface which represents the I/O pins.
The next stage is to make it work as an emulator. Which I've actually already done so I will write another post immediately following this.
Instruction Set
One thing I've learnt about messing around with retrocomputing is when you dig into something, never assume the designer was sober or sane.
There are design decisions that make you think "What ?" on a regular basis. Mixing up connections for no apparent reason. Sometimes the whole design is bonkers - for example the "Invisible Alien Neutralizer".
When I reversed engineered the BIOS in the Odyssey 2 years ago, there was a bit of code that had me baffled, it was bit hacking in an incomprehensible manner. I couldn't step through it and in the end just marked it 'unknown purpose'. Another fellow filled in the gaps later on. It was a routine to calculate the exclusive or of 2 bytes. Which would be fine if the 8048 in the Odyssey 2 didn't actually have an Exclusive Or instruction built in.
The SM510 is actually relatively sensible. Still there are wierd things.
The person who designed it has an obsession with "T" in mnemonics. So in the branching code it is used for Transfer (what everyone else calls branch or jump) - T (transfer in page) or TL (Transfer long). Subroutines are indicated with an "M" - I guess for Memory (of return address) so you get TM (see later) and TML (Transfer memory long). Everyone else uses Jump, Jump Subroutine, Branch, Call, but what the heck. Transfer if used is used for register to register transfer (e.g. TAX in the 6502). These conventions date back to things like the 4004 and the TMS1000 in Microcomputing and further back - the PDP-8 has skip instructions.
This wouldn't have been so bad if all the skip instructions didn't begin with T as well. So you have TC (test on carry), TAM (test A = memory) and so on. Everyone else in the world uses variants on SKIP for skip tests SKZ, SKIPZ and so on, but this lot use TA0 (test accumulator is zero). Some of them are backwards (TC tests if carry is zero for example .....). I'm seriously considering producing an alternate mnemonic set just to make it clearer. The indirect jump is not "JIN" or something like that but "ATPL" (A transfer to P-Lower) which isn't actually what it does anyway. (it transfers into the lower 4 bits only).
On the subject of subroutines, what do you think RTN0 and RTN1 do ? Return 0 and 1 ? Wronnnnng. RTN0 is return. RTN1 is return and skip. The COP series (NS 4 bitters) has the same thing but uses RET and RETSK.
Then there's ADD11. Now, I thought this was a copier smear when I saw this first, but it's in all the documents. Have a guess at what it does ?
Nope, it doesn't ADD11. It has nothing at all to do with 11, or two 1's (Though there is an ADX 11 which does add the constant 11 - the X is probably for consistency with LAX 11 which is load 11 constant, wich still doesn't explain the X anyway).
The stock Add (add memory to A) is called ADD - it just adds it and changes nothing else. ADD11 is add with carry in, carry out and skip on carry. I did wonder if it was related to the opcode value, but as that's 09 that's somewhat unlikely.
Why not ADC, ADCSK or something that gives some resemblance to what the opcode actually does ?
Then there's TM. It took me ages to figure out what this actually does. According to the data sheet it does:
TM x C0-FF R<-S<-PC+1,PU<-0,PM<-0
PL<-x(I5-I0),PU<-y(I7-I6)
IDX yz 00-FF PL<-z(I5-I0),PM<-(0100) base2
which is pretty obvious ...... no (the datasheet doesn't tell you what x,y,z are). And it says it's a two byte instruction. It isn't. Well, it sort of is, it's just the two bytes are nowhere near each other. Most two byte instructions are consecutive.
It describes it as an indexed jump, which means whoever wrote it didn't know the difference between indexing and indirection. I figured it out with the help of the Patent and deductive reasoning.
What it actually is is a one byte subroutine call. The TM x (e.g. TM 20) refers to a byte in Page 0. This byte is split into two bits - the lower 6 bits go to the program counter, the upper 2 bits have 16 added to them and go in the page register.
This baffled me for ages because the instruction set actually has a two byte long subroutine call (TL) which on the face of it does the same thing. The advantage that the TM call has is that each call uses only one byte of code memory - and one byte of memory in page 0 which is the indirect address - but the byte in page 0 is only required once. So if you call the same subroutine 10 times you use 11 bytes, whereas TL uses 20.
The instruction does a second fetch at the point of the comma in the middle line. Of course, it doesn't bother to say this.
There is an example of it in the datasheet which takes half a page to make this utterly unclear.
Finally the leap of faith. There are two different 3A opcode instructions (ADX 10 and DC). Both add 10 to the accumulator and leave the carry flag unchanged. One skips on carry. The other doesn't. You can't really miss them in the datasheet as they are seperated by one instruction. Well, Sharp, which one is it ?
I hate to guess, but I've gone for the 'skip on carry' one because all the other add constant instructions are skip on carry out. It would be mind numbing to have an add constant instruction which skipped on carry for every value other than 10 being added.
But that gets back to the start ; don't assume sobriety or sanity. In the absence of a Sharp SM5 programmers guide though, in this case I have to.
There are design decisions that make you think "What ?" on a regular basis. Mixing up connections for no apparent reason. Sometimes the whole design is bonkers - for example the "Invisible Alien Neutralizer".
When I reversed engineered the BIOS in the Odyssey 2 years ago, there was a bit of code that had me baffled, it was bit hacking in an incomprehensible manner. I couldn't step through it and in the end just marked it 'unknown purpose'. Another fellow filled in the gaps later on. It was a routine to calculate the exclusive or of 2 bytes. Which would be fine if the 8048 in the Odyssey 2 didn't actually have an Exclusive Or instruction built in.
The SM510 is actually relatively sensible. Still there are wierd things.
The person who designed it has an obsession with "T" in mnemonics. So in the branching code it is used for Transfer (what everyone else calls branch or jump) - T (transfer in page) or TL (Transfer long). Subroutines are indicated with an "M" - I guess for Memory (of return address) so you get TM (see later) and TML (Transfer memory long). Everyone else uses Jump, Jump Subroutine, Branch, Call, but what the heck. Transfer if used is used for register to register transfer (e.g. TAX in the 6502). These conventions date back to things like the 4004 and the TMS1000 in Microcomputing and further back - the PDP-8 has skip instructions.
This wouldn't have been so bad if all the skip instructions didn't begin with T as well. So you have TC (test on carry), TAM (test A = memory) and so on. Everyone else in the world uses variants on SKIP for skip tests SKZ, SKIPZ and so on, but this lot use TA0 (test accumulator is zero). Some of them are backwards (TC tests if carry is zero for example .....). I'm seriously considering producing an alternate mnemonic set just to make it clearer. The indirect jump is not "JIN" or something like that but "ATPL" (A transfer to P-Lower) which isn't actually what it does anyway. (it transfers into the lower 4 bits only).
On the subject of subroutines, what do you think RTN0 and RTN1 do ? Return 0 and 1 ? Wronnnnng. RTN0 is return. RTN1 is return and skip. The COP series (NS 4 bitters) has the same thing but uses RET and RETSK.
Then there's ADD11. Now, I thought this was a copier smear when I saw this first, but it's in all the documents. Have a guess at what it does ?
Nope, it doesn't ADD11. It has nothing at all to do with 11, or two 1's (Though there is an ADX 11 which does add the constant 11 - the X is probably for consistency with LAX 11 which is load 11 constant, wich still doesn't explain the X anyway).
The stock Add (add memory to A) is called ADD - it just adds it and changes nothing else. ADD11 is add with carry in, carry out and skip on carry. I did wonder if it was related to the opcode value, but as that's 09 that's somewhat unlikely.
Why not ADC, ADCSK or something that gives some resemblance to what the opcode actually does ?
Then there's TM. It took me ages to figure out what this actually does. According to the data sheet it does:
TM x C0-FF R<-S<-PC+1,PU<-0,PM<-0
PL<-x(I5-I0),PU<-y(I7-I6)
IDX yz 00-FF PL<-z(I5-I0),PM<-(0100) base2
which is pretty obvious ...... no (the datasheet doesn't tell you what x,y,z are). And it says it's a two byte instruction. It isn't. Well, it sort of is, it's just the two bytes are nowhere near each other. Most two byte instructions are consecutive.
It describes it as an indexed jump, which means whoever wrote it didn't know the difference between indexing and indirection. I figured it out with the help of the Patent and deductive reasoning.
What it actually is is a one byte subroutine call. The TM x (e.g. TM 20) refers to a byte in Page 0. This byte is split into two bits - the lower 6 bits go to the program counter, the upper 2 bits have 16 added to them and go in the page register.
This baffled me for ages because the instruction set actually has a two byte long subroutine call (TL) which on the face of it does the same thing. The advantage that the TM call has is that each call uses only one byte of code memory - and one byte of memory in page 0 which is the indirect address - but the byte in page 0 is only required once. So if you call the same subroutine 10 times you use 11 bytes, whereas TL uses 20.
The instruction does a second fetch at the point of the comma in the middle line. Of course, it doesn't bother to say this.
There is an example of it in the datasheet which takes half a page to make this utterly unclear.
Finally the leap of faith. There are two different 3A opcode instructions (ADX 10 and DC). Both add 10 to the accumulator and leave the carry flag unchanged. One skips on carry. The other doesn't. You can't really miss them in the datasheet as they are seperated by one instruction. Well, Sharp, which one is it ?
I hate to guess, but I've gone for the 'skip on carry' one because all the other add constant instructions are skip on carry out. It would be mind numbing to have an add constant instruction which skipped on carry for every value other than 10 being added.
But that gets back to the start ; don't assume sobriety or sanity. In the absence of a Sharp SM5 programmers guide though, in this case I have to.
Sharp SM510 Innards
This (right) is a block diagram of the SM510 4 Bit Microcontroller made by Sharp.
Almost all of these microcontrollers are very very similar of this era, they are probably based around the TMS1x00 series' design.
There's some ROM, some RAM which is accessed via an index register pair (BM:BL on this processor - it's a pair because you need more than 4 bits usually !), and some input and output ports.
Most of the connections here are to the LCD the processor is driving (a1-a16,b1-b16,H1-H4). a1-a16 and b1-b16 are the segments, and H1-H4 are common selection lines, allowing 32 x 4 segments in total , or 128 segments.
Other than that there are a few oddities. The 'W' register on the left is a Serial in Parallel out shift register written to by two instructions - write one/zero and shift (WS and WR), these output on the S-Lines on the left.
There is a divider which can be tested (the DIV(15) bottom middle) which clocks at the Osc rate which is 32,768Hz, which as any good assembler programmer knows is 2^15Hz. Divide this by two 15 times and you get one Hz, which is ideal for a Watch or Clock device which is what this chip is for, one pulse a second.
R1 and R2 provide a 4,096 Hz tone which can be then modulated under program control. The later versions of this (see the SM511 datasheet) have automatic melody generation and even later ones have automatic sound effect generation. But on the SM510 you have to modulate it by hand. Oh joy.
ACL is the reset, K1-K4 are inputs that can be read into the Accumulator, and B and BA are inputs that can be tested individually by an instruction. A lot of these early microcontrollers have instructions that effect I/O directly - this one has (as an example) ATR which copies the 2 LSBs of the Accumulator into R1 and R2. Modern MCUs almost all have memory mapped I/O so instead of having WS and WR you'd have a register to write to to update the W register (and therefore S1-S8)
It's quite slow ; it clocks at 16,384Hz normally, a 61us instruction cycle which is even slower than a TMS1000 (50,000 Hz). But if you are developing a watch all you need to do is wake the processor up every second (this MCU has a sleep mode), update the LCD display and go to sleep again.
However against this is the fact that you don't need to refresh the display. The display RAM (top right) which is between $60 and $80 is directly mapped onto the an,bn,hn lines, so to light an LCD segment all you have to do is set a memory bit, which is a single instruction. The MCU does all the work for you.
(This is probably partly responsible for the slower speed. The MCU will have to do 2 things at once - drive the LCD and run the Processor code, and dual ported Memory is expensive. It's more likely that the RAM access is locked for one or the other)
Having done some messing with the MB Microvision, this is much easier. Coding for the Microvision is a bit like coding for the 2600, in that you have to generate the display and refresh it at about 20 or so fps, it is not fun to put it mildly if you have the TMS instruction set which is very basic. With a Microvision (a 16x16 grid) you write 16 bits of row data, then 16 bits of column data, any pixel which is on in both the row and column lights up (actually it goes dark :) ) so this means usually you write a row at a time, with one column bit set, then you do the next one, round and round you go for ever.
Almost all of these microcontrollers are very very similar of this era, they are probably based around the TMS1x00 series' design.
There's some ROM, some RAM which is accessed via an index register pair (BM:BL on this processor - it's a pair because you need more than 4 bits usually !), and some input and output ports.
Most of the connections here are to the LCD the processor is driving (a1-a16,b1-b16,H1-H4). a1-a16 and b1-b16 are the segments, and H1-H4 are common selection lines, allowing 32 x 4 segments in total , or 128 segments.
Other than that there are a few oddities. The 'W' register on the left is a Serial in Parallel out shift register written to by two instructions - write one/zero and shift (WS and WR), these output on the S-Lines on the left.
There is a divider which can be tested (the DIV(15) bottom middle) which clocks at the Osc rate which is 32,768Hz, which as any good assembler programmer knows is 2^15Hz. Divide this by two 15 times and you get one Hz, which is ideal for a Watch or Clock device which is what this chip is for, one pulse a second.
R1 and R2 provide a 4,096 Hz tone which can be then modulated under program control. The later versions of this (see the SM511 datasheet) have automatic melody generation and even later ones have automatic sound effect generation. But on the SM510 you have to modulate it by hand. Oh joy.
ACL is the reset, K1-K4 are inputs that can be read into the Accumulator, and B and BA are inputs that can be tested individually by an instruction. A lot of these early microcontrollers have instructions that effect I/O directly - this one has (as an example) ATR which copies the 2 LSBs of the Accumulator into R1 and R2. Modern MCUs almost all have memory mapped I/O so instead of having WS and WR you'd have a register to write to to update the W register (and therefore S1-S8)
It's quite slow ; it clocks at 16,384Hz normally, a 61us instruction cycle which is even slower than a TMS1000 (50,000 Hz). But if you are developing a watch all you need to do is wake the processor up every second (this MCU has a sleep mode), update the LCD display and go to sleep again.
However against this is the fact that you don't need to refresh the display. The display RAM (top right) which is between $60 and $80 is directly mapped onto the an,bn,hn lines, so to light an LCD segment all you have to do is set a memory bit, which is a single instruction. The MCU does all the work for you.
(This is probably partly responsible for the slower speed. The MCU will have to do 2 things at once - drive the LCD and run the Processor code, and dual ported Memory is expensive. It's more likely that the RAM access is locked for one or the other)
Having done some messing with the MB Microvision, this is much easier. Coding for the Microvision is a bit like coding for the 2600, in that you have to generate the display and refresh it at about 20 or so fps, it is not fun to put it mildly if you have the TMS instruction set which is very basic. With a Microvision (a 16x16 grid) you write 16 bits of row data, then 16 bits of column data, any pixel which is on in both the row and column lights up (actually it goes dark :) ) so this means usually you write a row at a time, with one column bit set, then you do the next one, round and round you go for ever.
Saturday, 15 June 2013
Introduction to Hardware
You can see the hardware by looking at the Patent, Fig 1 (on the right here). This shows a picture of the watch itself, a picture of the LCD display, and a full circuit diagram (over a couple of sub diagrams).
The core processor is an SM510, part of the SM5 Sharp 4 Bit Microcontroller series. Unfortunately, I can't find the databook for this, but I have managed to find the SM511/2 Datasheet which is similar but slightly different.
Unusually the Patent gives a long list of information about the Microcontroller itself - good thing otherwise I'd be stuffed, and between this and the SM511/2 Datasheet I've figured out the minor details.
The SM510 has an LCD Driver built in, so lighting an LCD is just a matter of setting a bit in RAM. The processor takes care of the rest.
The LCD has a slightly unusual pattern for its graphics with the 6 x 6 "split filled circle" graphics. The 3.5 digit 7 Segment Display at the top is for the watch part (or displaying the score).
There is a 32,768Hz Crystal for accurately keeping time, and a Piezo buzzer. This is slightly unusual in that when turned on it provides a 4Khz tone automatically which you then modulate. It is possible to do this quite effectively. If you have a look at the Homebrew Channel F "Pacman" game, for example, despite it only having three tones it actually has a pretty good attempt at producing "Pacman" classic sounds.
Next time a little more about the Sharp Microcontroller which does all the work.
The core processor is an SM510, part of the SM5 Sharp 4 Bit Microcontroller series. Unfortunately, I can't find the databook for this, but I have managed to find the SM511/2 Datasheet which is similar but slightly different.
Unusually the Patent gives a long list of information about the Microcontroller itself - good thing otherwise I'd be stuffed, and between this and the SM511/2 Datasheet I've figured out the minor details.
The SM510 has an LCD Driver built in, so lighting an LCD is just a matter of setting a bit in RAM. The processor takes care of the rest.
The LCD has a slightly unusual pattern for its graphics with the 6 x 6 "split filled circle" graphics. The 3.5 digit 7 Segment Display at the top is for the watch part (or displaying the score).
There is a 32,768Hz Crystal for accurately keeping time, and a Piezo buzzer. This is slightly unusual in that when turned on it provides a 4Khz tone automatically which you then modulate. It is possible to do this quite effectively. If you have a look at the Homebrew Channel F "Pacman" game, for example, despite it only having three tones it actually has a pretty good attempt at producing "Pacman" classic sounds.
Next time a little more about the Sharp Microcontroller which does all the work.
I'm not sure .....
if it ever existed. The Watchman watch I discovered is definitely in the patents database - but whether it ever existed, in prototype or finished form I don't know.
There are a whole heap of things that are right on the border of existence - the Atari Space Invaders handheld on the right (COP 420 processor, 32 x 32 LCD apparently) probably existed in prototype form, but was certainly never released. Or it could just have been a box mockup.
It's not as if it is completely off the wall. The patent (see links box) was filed by Jay Smith III on behalf of Smith Engineering. Jay is the designer of both the Microvision and the Vectrex.
So I would guess it was either a fully worked design or prototype that was built - the whole design is certainly plausible and is given in quite impressive detail but never got built in any quantity or released, as far as I can tell.
So , anyway I plan to create it to a 'playable' level - in emulation, not a watch obviously. It has all the retrogoodies I like - it's fairly ridiculous, it's very low level technology (4 bit CPU) and it's original :)
There's all sorts of interesting things in the database. There are the original designs for the Microvision, Total Control 4, Entex Select-a-Game amongst others. There's some highly speculative designs here as well that probably were never built and some rather wild and wonderful things that were.
I was quite tempted to use the "Invisible Alien Neutraliser" patent but I couldn't think of anything to do with it. It is basically a handheld console with a phototransistor attached, it can read the ambient light level. The amazing thing about this is it actually was made ........... one wonders about the sense involved in releasing such a thing :)
Anyway, in the next post I will expand on the hardware a little.
There are a whole heap of things that are right on the border of existence - the Atari Space Invaders handheld on the right (COP 420 processor, 32 x 32 LCD apparently) probably existed in prototype form, but was certainly never released. Or it could just have been a box mockup.
It's not as if it is completely off the wall. The patent (see links box) was filed by Jay Smith III on behalf of Smith Engineering. Jay is the designer of both the Microvision and the Vectrex.
So I would guess it was either a fully worked design or prototype that was built - the whole design is certainly plausible and is given in quite impressive detail but never got built in any quantity or released, as far as I can tell.
So , anyway I plan to create it to a 'playable' level - in emulation, not a watch obviously. It has all the retrogoodies I like - it's fairly ridiculous, it's very low level technology (4 bit CPU) and it's original :)
There's all sorts of interesting things in the database. There are the original designs for the Microvision, Total Control 4, Entex Select-a-Game amongst others. There's some highly speculative designs here as well that probably were never built and some rather wild and wonderful things that were.
I was quite tempted to use the "Invisible Alien Neutraliser" patent but I couldn't think of anything to do with it. It is basically a handheld console with a phototransistor attached, it can read the ambient light level. The amazing thing about this is it actually was made ........... one wonders about the sense involved in releasing such a thing :)
Anyway, in the next post I will expand on the hardware a little.
Watchman is a ...... what ?
Well, yes, this is something really obscure and wierd again. Well, I like wierd and obscure bits of hardware.
This thing on the right is an actual "original" Watchman made by Tomy, the predecessor of the machine I'm developing for. Like most "Watches with Games" (see these guys for a ridiculously long list) these ones are single game watches - the most common one was a bowling game.
There are a lot more things like this - Game and Watches (this is a Nintendo classic). These are much better games, but not actually really useable as watches. You can't really fasten one to your wrist - it is more like "Game and Portable Small Clock".
This too has the major issue that the game itself is a one shot based on the LCD - you can't do a different game with it really.
The Watchman I am going to write some games for is more like the above watch in looks - but its display is just about flexible enough to support different games. The original appears to have had four games. There is one major problem with this whole rather mad thing though, which I'll deal with next time.
Thursday, 13 June 2013
Watchman Development
This is the blog for the Retrochallenge for Summer 2013, in which I will be doing some game development for the "Watchman".
Unfortunately (not really !) the family and I are booked to go on holiday to Orlando in July for a fortnight so I am going to cheat a bit and fit my Retrochallenge activities from now to the end of July otherwise it will be a bit short of time.
I do not think the missus and the brats would be very pleased if I told them I had to stay at home to do the Retrochallenge :)
Unfortunately (not really !) the family and I are booked to go on holiday to Orlando in July for a fortnight so I am going to cheat a bit and fit my Retrochallenge activities from now to the end of July otherwise it will be a bit short of time.
I do not think the missus and the brats would be very pleased if I told them I had to stay at home to do the Retrochallenge :)
Subscribe to:
Posts (Atom)