In the above diagram, fine lines represent single-bit (and serial) data paths,
while thicker lines represent multiple bits of data in parallel (typically 4).
Note, the hardware implements both parallel and serial paths,
depending on registers and access operations.
Hardware Registers
The registers in the block diagram are explained here:
Reg Name | Num Bits | Access | Meaning/Use | |
---|---|---|---|---|
S | 4 | R/W | Machine State * | |
T | 4 | R/W | Unspecified * | |
U | 4 | R/W | Memory Address, Hi * | |
V | 4 | R/W | Memory Address, Lo * | |
KA | 4 | R/W | Input Data, Hi * | |
KB | 4 | R/W | Input Data, Lo * | |
CA | 4 | R/W | Data to/from RAM * | |
CB | 4 | R/W | Data to/from RAM * | |
TO | 2 | W/O | Typewriter Output, Hi | |
RO | 4 | W/O | Typewriter Output, Lo | |
UDA | 3 | W/O | UART Decoder Input, Hi | |
UDB | 4 | W/O | UART Decoder Input, Lo | |
M | 4 | n/a | Current Memory Address, Hi | |
N | 4 | n/a | Current Memory Address, Lo | |
CC | 1 | n/a | Internal Carry Flag | |
Zo | 1 | n/a | Zero Flag | |
SC | 1 | n/a | Carry Flag | |
S' | 4 | n/a | Saved S | |
SC' | 1 | n/a | Saved SC | |
KBD | 1 | n/a | Key Pressed (input ready) | |
TML | 1 | W/O | Tape Motor, Left | |
DIL | 2 | W/O | Tape Write (record) Data, Left | |
LTCK/LDK | 2 | R/O | Tape Read Data (one-shot), Left | |
TMR | 1 | W/O | Tape Motor, Right | |
DIR | 2 | W/O | Tape Write (record) Data, Right | |
RTCK/RDK | 2 | R/O | Tape Read Data (one-shot), Right | |
CURRENT | 11 | n/a | Current Instruction Address | |
STACK1 | 10 | n/a | Top of Stack | |
STACK2 | 10 | n/a | 2nd on Stack | |
* Also used as general purpose registers, when non-conflicting |
Microcode execution happens continuously, as long as the calculator has power. Each instruction contains the address of the next instruction, so there is no traditional "Instruction Counter" that increments. There is a "current instruction address" register, and two stack registers, used to control the flow of the microcode. In addition, certain keyboard conditions cause a hard-coded address to be forced into the current address register.
In normal operation, the current address is applied to the ROM and the resulting 42-bit word is latched into the instruction register. For normal instructions, the "next address" field of the instruction is fed-back into the current address register, with the low-order 2 bits coming from a decoding of the condition address fields, JL and JH.
For "call" instructions, the previous value of the current address register is saved into the "STACK1" register, also saving the "STACK1" previous contents into "STACK2".
For "return" instructions, the contents of the "next address" field is ignored and the current address register is loaded from STACK1 with the low-order bit forced to "1", and STACK1 is loaded form STACK2.
Note, a return instruction always returns to the odd address after the call-from address. This means that call instructions should always be at even addresses.
Microcode instruction execution sequence is terminated, and a new instruction address is forced, when certain keys are pressed. Because this is handled in hardware, these keys will immediately terminate any routine in progress and force the calculator to jump to the function programmed for that key.
Key | RESET * | REWIND | FORWARD | (STOP) | (APR) |
---|---|---|---|---|---|
uCode Address |
000 | 001 | 002 | 003 | 004 |
In addition, the microcode logic allows for hard-wired overriding of certain instructions. There are 12 locations reserved for overrides.
TBD what the overrides do.
Microcode Instruction Timing
The system clock starts with a 4MHz crystal that feeds a flip-flop (producing a 2MHz signal), and a 5-bit Johnson Counter producing a series of "phase" clocks which repeat at 400KHz. One microcode instruction is entirely executed within the Johnson Counter cycle. This establishes the microcode instruction time (a "cycle") of 2.5uS, or 400,000 instructions per second.
The phase-clock signals, in combination with the 4MHz and 2MHz clocks, orchestrate the operation of the calculator. Early pulses are used to prepare hardware and save ("freeze") data, middle pulses are used to serially transfer data around the calculator registers, and late pulses are used to finish-up and finalize operations (such as accessing RAM or loading the next microcode instruction). Here are some key timing signals:
The instruction cycle is divided into three main phases: Early Latching, Serial Data Transfer, and Late Latching. The following activities are performed in the various phases:
Early Latching |
Machine State saved: S' = S SC' = SC |
T,U,V latched to memory address decoders L,M,N * | |
Serial Data Transfer |
Data shifted over A, B, and Z buses, to/from selected registers and through ALU * |
"return" pop stack * | |
"call" push stack * | |
Late Latching |
RAM = CA * |
CA = RAM * | |
CB = ROM * | |
CURRENT = next | |
TO,RO = keyboard/peripheral * | |
* Activities are dependent on selected microcode ops of instruction |
Some notes on timing:
In summary, interpreting (and writing) microcode instructions requires careful consideration of these timing anomalies.
Editor's Note: While the schematics seem to imply that input data is forced into KA and KB on every instruction while KBD == 1, proper operation seems to dictate the KA and KB are latched only on instructions that use KBD in a conditional address (JL == 110). Also, unlike the schematics, KBD must be reset when JL == 110 and not exclusively by ST == 1001 (RESET).
Here is some sample microcode
Microcode Instruction Format
Each microcode instruction is 42 bits long. The instruction fields are
as follows (layout for Solid State ROM).
Loc | L27 | L26 | L25 | L24 | L23 | L22 | L21 | L20 | L17 | L19 | L18 | |||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ROMs | CM5910 | CM5920 | CM5930 | CM5940 | CM5950 | CM5960 | CM5970 | CM5980 | CM5990 | CM6000 | CM6010 | |||||||||||||||||||||||||||||||||
Bits | 41 | 40 | 39 | 38 | 37 | 36 | 35 | 34 | 33 | 32 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | - | - |
Fields | AI | BI | ZO | AOP | AC | BC | MOP | KK | ST | SUB | JAD | JH | JL | - | ||||||||||||||||||||||||||||||
Locations shown for logiblock 6293 |
The instruction fields have the following effects on the hardware.
The notation REG<n> refers to Bit "n" of register "REG".
KK | |
---|---|
XXXX | Immediate operand |
|
|
| |||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|
Source for A bus | Source for B bus | Dest. for Z bus | |||||||
---|---|---|---|---|---|---|---|---|---|
AC | AI | affect | BI | affect | ZO | ST | affect | ||
0 | XXX | Abus = 0000 | 000 | Bbus = 0000 | 000 | 0000* | no-op | ||
1 | 000 | Abus = S | 000 | 1111 | S = Zbus | ||||
1 | 001 | Abus = T | 001 | Bbus = KK | 001 | XXXX | T = Zbus | ||
1 | 010 | Abus = U | 010 | Bbus = D1 | 010 | XXXX | U = Zbus | ||
1 | 011 | Abus = V | 011 | Bbus = D2 | 011 | XXXX | V = Zbus | ||
1 | 100 | Abus = KA | 100 | Bbus = KA | 100 | XXXX | KA = Zbus | ||
1 | 101 | Abus = KB | 101 | Bbus = KB | 101 | XXXX | KB = Zbus | ||
1 | 110 | Abus = CA | 110 | Bbus = CA | 110 | XXXX | CA = Zbus | ||
1 | 111 | Abus = CB | 111 | Bbus = CB | 111 | XXXX | CB = Zbus | ||
* ST != 1111: no-op but ALU flags updated normally |
KA | KB | ||||||||
---|---|---|---|---|---|---|---|---|---|
3 | 2 | 1 | 0 | 3 | 2 | 1 | 0 | ||
MOP == 1010 | In | n/a | ROP | LOP | L/RTCK | L/RDK | |||
MOP == 1011 | Out | n/a | DIN | n/a | DIN | ||||
MOP == 1100 | In | n/a | RHS | LHS | R/B | L/S | |||
JL == 110 | In | Key/Periph Hi | Key/Periph Lo |
D1 | D2 | D3 | |||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
3 | 2 | 1 | 0 | 3 | 2 | 1 | 0 | 3 | 2 | 1 | 0 | ||||||||||||||||||||||||||||||||||||
TRANS. | RECORD | DOUBLE | RIGHT | SKIP | * | - | - | ORG | ADJ | ||||||||||||||||||||||||||||||||||||||
|
*
|
|
|||||||||||||||||||||||||||||||||||||||||||||
D2 is cleared when read (BI=3) |
MOPs for I/O | |||
---|---|---|---|
MOP | KK | BI | function |
0111 | xxx1 | CSL = BI<0> | |
xx1x | ELN = BI<0> | ||
x1xx | ERN = BI<0> | ||
1xxx | NAN = BI<0> | ||
1000 | xxx1 | KBLO/KBLK = BI<0> | |
xx1x | Bell/Beep | ||
x1xx | xxx0 | Print TO<1:0>,RO<3:0> (1) | |
xxx1 | Typewriter carriage function (1) | ||
1001 | x000 | KA = D3 | |
x001 | KA = Lo nibble UART decoder | ||
x010 | KA = Hi nibble UART decoder | ||
x011 | KA = LCB/LCA ; PE ; DR ; IDLE | ||
x100 | KA = CTS ; SHC ; PRINT ; ATTN | ||
x101 | n/a | ||
x110 | n/a | ||
x111 | n/a | ||
1010 | KB = ROP ; LOP ; TCK ; DK (2) | ||
1011 |
DIN1=KA<0> DIN0=KB<0> (2) | ||
1101 | RIGHT=KK<0> RC=BI<0> DIN1=0 DIN0=0 HI[RIGHT]=KK<2> TM=1 H+L=KK<3> FWD=KK<1> | ||
1100 | KB = RHS ; LHS ; R/B ; L/S | ||
1110 | RIGHT=KK<0> RC=BI<0> HI[RIGHT]=KK<2> TM=0 H+L=KK<3> FWD=KK<1> | ||
1111 | x000 | UART RESET | |
x001 | TBRL | ||
x010 | BREAK | ||
x011 | UDA,UDB=KA,KB (Zbus) | ||
x100 | DTR=0 | ||
x101 | RTS=0 (3) | ||
x110 | RTS=1 (3) | ||
x111 | DTR=1 | ||
Note 1: TO,RO = KA,KB | |||
Note 2: Selects left or right data stream based on previous RIGHT/LEFT setting. | |||
Note 3: RTS drives UART Decoder input: 0: RR<5:0> (recv), 1: UD<6:0> (xmit) Decoder converts between tilt-rotate and ACSII |
Carriage Function (Output) Codes | ||
---|---|---|
KA | KB | function |
00 | xx | space (sp) |
01 | xx | backspace (bs) |
02 | xx | tab |
03 | xx | cr/lf |
04 | xx | shift up |
05 | xx | shift down |
08 | xx | index |
09 | xx | set tab |
10 | xx | clear tab |
Print (Output) Character Codes | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
KA\KB | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 |
00 | - | y | q | p | = | j | / | , | ; | f | g | |||||
01 | w | s | i | ' | . | ½ | o | a | r | v | m | |||||
02 | b | h | k | e | n | t | l | c | d | u | x | |||||
03 | 9 | 0 | 6 | 5 | 2 | z | 4 | 8 | 7 | 3 | 1 | |||||
S H I F T E D |
_ | Y | Q | P | + | J | ? | , | : | F | G | |||||
W | S | I | " | . | ¼ | O | A | R | V | M | ||||||
B | H | K | E | N | T | L | C | D | U | X | ||||||
( | ) | ¢ | % | @ | Z | $ | * | & | # | ! | ||||||
Keyboard (Input) Character Codes | ||||||||||||||||
KA\KB | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 |
00 | - | y | CODE sp |
sp | q | p | = | j | CODE - |
/ | CODE / |
CODE g |
, | ; | f | g |
01 | w | s | CODE bs |
bs | i | ' | . | [ (½) |
CODE w |
o | CODE o |
CODE m |
a | r | v | m |
02 | b | h | CODE tab |
tab | k | e | n | t | CODE b |
l | CODE l |
CODE x |
c | d | u | x |
03 | 9 | 0 | CODE cr/lf |
cr/lf | 6 | 5 | 2 | z | CODE 9 |
4 | CODE 4 |
CODE 1 |
8 | 7 | 3 | 1 |
08 | _ | Y | Q | P | + | J | ? | : | F | G | ||||||
09 | W | S | | SET TAB |
I | " | { (¼) |
O | A | R | V | M | |||||
10 | B | H | K | E | N | T | L | C | D | U | X | |||||
11 | ( | ) | ^ (¢) |
% | @ | Z | $ | * | & | # | ! | |||||
04 | SEARCH (ON) |
|||||||||||||||
05 | SEARCH (OFF) |
|||||||||||||||
06 | CODE MEMO |
MEMO (OUT) | ||||||||||||||
07 | CODE B.L. |
BACK LINE |
Internal Character Encoding | |||||||
---|---|---|---|---|---|---|---|
CA | CB | ||||||
3 | 2 | 1 | 0 | 3 | 2 | 1 | 0 |
SHIFT | UNDERLINE | tilt | rotate |
Memory Usage | |
---|---|
RAM Address | Purpose |
0xff (15,15) | chars in buffer |
0xfe (15,14) | index |
0xfd (15,13) | chars after carriage |
0xfc (15,12) | carriage position |
0xfb (15,11) | |
0xfa (15,10) | index |
0xf9 (15,09) | saved registers |
0xf8 (15,08) | |
0xf7 (15,07) | Current char |
0xf6 (15,06) | |
0xf5 (15,05) | state |
0xf4 (15,04) | mode |
0xf3 (15,03) | |
0xf2 (15,02) | saved registers |
0xf1 (15,01) | right margin 1 |
0xf0 (15,00) | adjust margin 1 |
0xef (14,15) | |
0xee (14,14) | |
0xed (14,13) | index? |
0xec (14,12) | |
0xeb (14,11) | |
0xea (14,10) | |
0xe9 (14,09) | |
0xe8 (14,08) | |
0xe7 (14,07) | |
0xe6 (14,06) | |
0xe5 (14,05) | |
0xe4 (14,04) | |
0xe3 (14,03) | |
0xe2 (14,02) | #/page |
0xe1 (14,01) | lines/page |
0xe0 (14,00) | page modes |
0xdf (13,15) | FIFO tail count |
0xd0 - 0xde (13,00 - 13,14) | Keyboard FIFO |
0xcf (12,15) | Num Tab Stops 1 |
0xc8 - 0xce (12,08 - 12,14) | Tab Stops 1 |
0x64 - 0xc7 (06,04 - 12,07) | Aux Line/Tape buffer |
0x00 - 0x63 (00,00 - 06,03) | Line/Tape buffer |
Note 1: These fields are recorded in a CODE b tape block, in the order (15,00) (15,01) (12,08 - 12,14) (12,15) |
Line/Tape buffer is used to place text during RECORD mode, and holds lines read from tape during PLAY mode.
Aux Line/Tape buffer is used to hold SEARCH text, and additional/previous text during ADJUST and JUSTIFY.
Keyboard FIFO appears to be intended to simulate interrupt-driven keyboard input. The microcode frequently calls a "keyboard poll" routine that takes a keyboard code (if ready) and appends it to the FIFO by indexing with FIFO tail count and incrementing that tail count. Other routines "pop" the first keyboard code off the FIFO using a shift-left method. The FIFO tail count occupies only the low-order nibble of 0xdf.
Location | Bits | |||||||
---|---|---|---|---|---|---|---|---|
CA | CB | |||||||
3 | 2 | 1 | 0 | 3 | 2 | 1 | 0 | |
15,6 | - | - | - | - | - | - | - | naj |
15,5 | - | UART | - | - | - | - | - | - |
15,4 | JUSTIFY | ADJUST | SKIP | key | AUTO | - | PARA+LINE | PARA+WORD |
15,3 | - | - | - | - | - | - | - | - |
14,0 | learn(c) | - | learn(d) | - | - | endpage | ||
15,x | - | - | - | - | - | - | - | - |
endpage=4:play, 5:stop, 6:eject, 0:*play, 1:*stop, 2:*eject |
The Wang 1200 Series used a dual-channel recording system, consuming both "sides" of a cassette in a single pass. This means that each cassette had, in affect, only one "side". This also means the Wang 1200's had special tape drives (heads) such that both "front" and "back" sides could be recorded at once - these were called "two head" drives, not to be confused with Stereo cassette drives that had two heads (channels) on each side of the tape.
The two channels were used to record "0" and "1" bits separately, i.e. one channel recorded only "0" bits while the other recorded only "1" bits. A transition on a given channel indicates a bit of that channel's type ("0" or "1"). It appears that there were never transitions on both channels at the same time. For at least this reason, that it would be impossible to determine which bit belonged in what order in the re-assembled data.
Here is an example of writing "now" to tape:
The unit of time for transitions was 50 cycles. In other words, there could/would be a transition (bit) every 50 cycles. This results in a data rate of 4000 bits per second. Assuming standard tape speed, this makes a tape density of about 2133 bpi (compare to mainframe "9-track" tapes which were 800 or 1600 bpi, and later 6250 bpi). The smallest common audio cassette was 15 minutes (C15, 7.5 minutes per side) which would hold about 225,000 bytes, not counting overhead for line-buffer formatting.
There was no parity or other error detection or correction encoded, so the only way to gain reliability was to "double record" each line for redundancy (consuming twice as much tape).
Each "block" written to tape has the following format:
Each line takes 92,498 cycles on tape, or about 0.46 second. This equates to about 978 lines per C15 tape.