The Microcomputer Trainer

Back when I was 7 years old, I wanted to buy a TRS-80 Color Computer, but my dad wanted to make sure that I was committed to programming before spending the $100 -- so he bought me a Science Fair "Microcomputer Trainer" and wanted me to learn to program that first. After I attempts to read the book with my second grade (or was it first?) reading level, I finally managed to do a little programming, but I ended up destroying the system by shorting the clock out with my fingers. My dad was satisfied and bought me the CoCo for Christmas later that year, and I've been programming ever since...

The microcomputer trainer used a simple 4-bit processor with 16 hexadecimal buttons, 4 control buttons, 7 LEDS, a 7 segment HEX LED, and a speaker. The CPU was a Texas Instruments TMS1100, which was based on the Speak and Spell's TMS1000.

I downloaded a copy of the manual here. The primary goal was to teach machine language programming to a young audience.

My question is whether it's possible to teach kids machine language in today's world by using a similar device. If so, what changes would be necessary to keep enough interest to teach a little bit about machine language and pointers? I think a small instruction set would be appropriate, but how small could you make it and still be Turing complete, and still make it fun to use?

To get the ideas started, here is the instruction set for the Microcomputer Trainer:

Main commands:
  • 0: KA = "Key into Ar" - If a key is pressed, then set Ar to key value and set flag to 0; else set flag to 1.
  • 1: AO = "A Output" - Displays contents of Ar on HEX LED and sets flag to 1.
  • 2: CH = "exChange" - Exchanges the contents of Ar with Br, exchanges Zr with Yr, and sets flag to 1.
  • 3: CY = "exChange Ar with Yr" - Exchanges the contents of Yr and Ar
  • 4: AM = "Ar to Memory" - Moves contents of Ar to the memory location indicated by following nibble and sets flag to 1.
  • 5: MA = "Memory to Ar" - Moves contents of memory indicated by following nibble into Ar and sets flag to 1.
  • 6: M+ = "Memory +" - Adds contents of memory pointed to by Yr to Ar, stores the result in Ar, and sets flag to 1 if there is a carry.
  • 7: M- = "Memory -" - Subtracts the contents of Ar from the memory location pointed to by Yr and stores the result in Ar. If an underflow occurs, flag is set to 1.
  • 8: TIA = "Transfer Immediate into Ar" - Moves following nibble into Ar and sets flag to 1.
  • 9: AIA = "Add Immediate into Ar"- Adds following nibble into Ar and sets flag to 1 if there is a carry.
  • A: TIY = "Transfer Immediate into Yr" - Moves following nibble into Yr and sets flag to 1.
  • B: AIY = "Add Immediate into Yr" - Adds following nibble into Yr and sets flag to 1 if there is a carry.
  • C: CIA = "Compare Immediate to Ar" - Sets flag to zero if Ar equals following nibble; else sets flag to 1.
  • D: CIY = "Compare Immediate to Yr" - Sets flag to zero if Yr equals following nibble; else sets flag to 1.
  • E: CAL = "Call" - Execute extended command (see below) if flag is set to 1 in previous command; else do nothing.
  • F: JUMP = "Jump" - If flag is 1, jump to byte address in following 2 nibbles; else do nothing
CALL commands (preceded by 'E'):
  • E0: RSTO = "Reset port O" - Turns off the HEX LED.
  • E1: SETR = "Set LED" - Sets LED according to number indicated by Yr (0-6)
  • E2: RSTR = "Clear LED" -Clears the LED according to number indicated by Yr (0-6).
  • E3: Not Used
  • E4: CMPL = "Complement" - Replaces Ar with its ones-complement (i.e. result of F - Ar)
  • E5: CHNG = "Change registers" - Exchanges Ar, Br, Yr & Zr with Ar', Br', Yr' & Zr'.
  • E6: SIFT = "Shift" - Shifts the contents of Ar one bit to the right and sets the flag to the opposite of the least significant bit of Ar before shifting.
  • E7: ENDS = "End Sound" - Emits an 'end' sound
  • E8: ERRS = "Error" - Emits an 'error' sound
  • E9: SHTS = "Short Sound" - Emits a 'blip' through the speakers
  • EA: LONS = "Long Sound' - Emits a long sound through the speakers
  • EB: SUND = "Sound" - Emits a note according to value in Ar (0 = no sound, 1 = la, 2 = ti, 3 = do, ... E = sol, F = no sound)
  • EC: TIMR = "Timer" - Pauses for the following number of seconds: seconds = (Ar + 1) / 10
  • ED: DSPR = "Display on port R" - Displays contents of memory locations E and F in binary on the 7 LEDs. Memory F contains the right-most 4 LEDs and memory E contains the leftmost 3 LEDs.
  • EE: DEM- = "Decimal conversion of M- result" - similar to DEM+, but subtracts instead of adds.
  • EF: DEM+ = "Decimal conversion of M+ result" - Adds together the decimal contents of Ar and the pointed address to give a decimal answer and stores that answer at the pointed address. If there is a carry, 1 is added to the pointed address less 1. After the command has been executed, the pointer is left pointer one address below the pointed address (If the number to be added is in 54, the answer is put in 54 and the pointer is reduced by 3. If there is a carry, one is added to 53)


  1. This comment has been removed by a blog administrator.

  2. Strange. Someone just gave me one of these for my kids. I spent a few hours programming it. I don't believe my 8 year old twins will be interested. Maybe, we'll see.

    I believe the transistor for the speaker blew (or the speaker itself stopped working) and I have to fix it. 16 mnemonics, 2kilonybble ROM and 128 nybbles of RAM. Who'll ever need more than that?

  3. Hi

    How did you get the instruction set off the trainer microprocessor.

    I want to do similiar with a Hornby Zero One modelo train controller.


  4. To get the instruction set, I copied this information out of the manual that is posted at polylith.com. See links in the article. I don't know anything about the Hornby Zero One controller, so you'll have to ask Dr. Google.

  5. part 1 of 2:

    In the above instruction summary, for the CH instruction it should say "Zr" rather than "Xr".

    "My question is whether it's possible to teach kids machine language in today's world by using a similar device."

    Of course it's possible, for some kids. You could use the *same* device if it's still working.

    "If so, what changes would be necessary to keep enough interest to teach a little bit about machine language and pointers?"

    Necessary? None. (Again, for some kids.)

    "I think a small instruction set would be appropriate, but how small could you make it and still be Turing complete, and still make it fun to use?"

    An instruction set that's [itself] fun to use? Impossible. All programming languages suck. (Instruction sets are just a subset of programming languages, and therefore also all suck.) The Microcomputer Trainer instruction set *particularly* sucks. (6809 is downright heavenly by comparison.) What makes it fun is being able to make stuff happen -- lights going on and off, speakers making noise, keys reacting to your presses, things behaving however you want them to behave -- whatever strikes your fancy. Attach it to some relays, motors, sensors, etc. for more fun. Instruction sets don't much directly contribute to fun -- but bad ones put an unnecessary amount of pain and suffering between you and the fun you seek.

    If you want a "least-sucky assembly-like for kids [or adults]" then it should probably have certain characteristics:
    * memory-to-memory (no registers except program counter and stack pointer)
    * three-operand (no single/dual-operand mutating instructions at all, though when actually desired they can be synthesized by the assembler)
    * compares fused with conditional branches (no need for flags register)
    * all operands can be: direct/indirect * absolute/stack-relative (examples: absolute direct = mem[42], stack-relative direct = mem[sp + 42], absolute indirect = mem[mem[42] + 12], stack-relative indirect = mem[mem[sp + 42] + 12]; of course the assembler should support symbolic names so the syntax doesn't actually have to look like that)
    * source operands can also be: immediate
    * bit addressing (byte addressing just makes it necessary to have a second "addressing mechanism" to get at the bits within bytes)
    * any operand may be any number of bits in length, or perhaps a power-of-two number of bits within some limit (no need for add-with-carry/subtract-with-borrow instructions to deal with "large" operands, no special instructions needed to manipulate individual bits)
    * any operand can be signed or unsigned (and operations between the two actually give the correct answer -- unlike C++ where things like compares and shifts don't work right)
    * all overflows trap to the debugger
    * branch targets are same format as source operands (immediates treated as relative branches, direct/indirect as absolute branches)
    * memory-mapped I/O

    There are neither data registers nor pointer registers. "direct" addressing modes provide "memory-based data". "indirect" addressing modes provide "memory-based pointers".

    There would be 25 different hardware instructions total: (8) math operations (+,-,*,/,%,*2^n,%2^n,log2), (8) logic operations (all 10 bit-wise binary operators, 2 of those synthesized by the assembler flipping operands around), (4) conditional branches (==,!=,>,>=,<,<=, 2 of those synthesized by the assembler flipping operands around), unconditional branch, subroutine call, subroutine return, stack adjust, and trap (for user assertions/preconditions/postconditions/invariants).

  6. part 2 of 2:

    You could of course have even less instructions by dropping a number of the math and logic operations, but that would IMO make it less fun (more painful). You could drop the stack-based addressing, but again, much less fun (more pain). You can't drop indirect addressing mode without causing massive pain. You could drop 'trap' for tiny toy programs without causing too much pain, but as soon as a program starts getting complex having no 'trap' facility will suck. (Though if you have suitable I/O, you don't necessarily need a trap instruction since you could simply create a routine which uses I/O to indicate an error condition before entering an endless loop and call that instead of invoking 'trap' -- hope you don't need it while debugging that I/O code though. Alternatively, you could use a "trap routine" with a debugger breakpoint set in it in place of a 'trap' instruction, but then it only works with a debugger attached and properly configured.)

    More of the instructions could be synthesized, though it results in poorer instruction encoding density and potentially worse performance. (E.g., synthesizing unconditional branch from any of the conditional branches is pretty straight forward. 'immediate' addressing mode can be synthesized by having the assembler prefill memory [in the binary image] with constants and then reference those using direct mode. Similarly 'direct' can be derived from 'indirect', and 'immediate' can therefore be derived from 'indirect'.) But then, you could also have some silly three-instruction computer that only does load, store and nand, and then synthesize all the rest of the instructions you want, including my proposed set. But the instruction encoding density would be ridiculously bad (or execution would be super-slow if synthesized as an interpreter) and such games don't seem to fit the spirit of the question. So I have limited synthesis to where it makes sense (where a real design intended for production might use it because it reduces complexity without impacting performance).

  7. Foo: Thanks for catching the error in CH! I've updated the error.

    You've got lots of good thoughts on a reasonable minimal instruction set and ways to use this set. But something that I really liked about the Microcomputer Trainer was its autonomy (i.e., no external computer with an assembler and debugger are required) and sense of being really close to the heart of the computer. There really wasn't much in the way of abstractions around the raw machine code, and I really enjoyed that feel. It's hard to emulate that with an external computer and debugger.

    True that it was very frustrating developing and debugging programs on the original Microcomputer Trainer, but there was a lot of satisfaction when it worked.

    I suspect that most kids in this high-paced world wouldn't have the patience for something like that (as I would likely not have the time or patience to do it again), but part of me likes to think that maybe my kids will have an interest in programming as they grow up.


Note: Only a member of this blog may post a comment.