You are on page 1of 607

Block I

Apollo Guidance Computer (AGC)


How to build one in your basement

Part 1: Overview

John Pultorak
December, 2004
Abstract
This report describes my successful project to build a working reproduction of the 1964
prototype for the Block I Apollo Guidance Computer. The AGC is the flight computer for the
Ap ollo m oon lan din gs, and is the wo rld’s first in tegra ted c ircuit com pu ter.

I built it in my ba sem ent. It took me 4 yea rs.

If you like, you can build one too. It will take you less time, and yours will be better than
mine.

I docum ented m y project in 9 separate .pdf files:

Part 1 O v ervie w : Introdu ces the p roject.

Part 2 CTL Module: Design and construction of the control module.

Part 3 PROC M odule: Design and construction of the processing (CPU) modu le.

Part 4 MEM Module: Design and construction of the mem ory module.

Part 5 IO Module: Design and construction of the display/keyboard (DSKY) m odule.

Part 6 Assem bler: A cross-a ssem bler for AG C softw are dev elopm ent.

Part 7 C+ + S imu lator: A low-level simulator that runs assemb led AGC code.

Part 8 Flight Software: My translation of portions of the COLOSSUS 249 flight


software.

Part 9 Test & Che ckou t: A suite of test programs in AG C assembly langu age.
Why build an AGC?
Early com pute rs are interes ting. B ecau se they ’re simp le, you ca n (if you like) actu ally
understand the entire computer, from hardw are to software.

The AGC is the most interesting early computer because: a) it flew the first men to the
moon; and b) it’s the world’s first integrated circuit (IC, or microchip) computer. It also has
interesting architectural features.

Original AGC:
Designed by M.I.T. in 1964
World's first microchip computer
Prototype computer for Apollo moon landing
Memory: 12K fixed (ROM), 1K eraseable (RAM)
Clock: 1.024 MHz
Com pu ting : 11 instru ction s, 16 bit w ord
Logic: ~5 000 ICs (3 -input N OR g ates, RTL logic)

My AGC:
Built from origina l M.I.T. desig n do cum ents
Started November 2000, completed October 2004
~1 5K han d-w rapped wire con nec tion s; ~ 35 00 feet of w ire
Cost (parts only): $2,980.
Lab or: ~ 25 00 hou rs
Logic: ~5 00 ICs (LST TL logic)
Runs flight software (1969 program name: COLOSSUS 249)

How did I build it?


I collected orig inal d ocum ents from libraries, tech nical se rvices, an d the in ternet. I had to
pay for some (~$350.) Mostly, they’re available for free now.

I wrot e an AG C so ftwa re sim ula tion (in C ++ ) an d cross-a ssem bler ( to pro gram it). Th en, I
wrote test and ch eckout program s in AGC assem bly langu age to check th e simu lator against
specifications in the original do cum ents.

I downloaded a big chunk of original flight software from M.I.T. and got it running on my
sim ula tor.

The simulator was my baseline for logic design. I put my logic design into a circuit design
tool (CircuitMaker) which “captures” schematics and (digitally) simulates circuits. Using
CircuitMak er, I “unit-tested” each A GC su bsystem logic design (the re are about 20 ).

Then , I assem bled my subs ystem logic sim ulatio ns into one rea lly hu ge dig ital circuit
sim ulatio n of the e ntire AG C an d “inte gration -tested” this for abo ut 6 m onth s. I eventu ally
ran my entire suite of AGC test and checkout software on this circuit simulation. (The
simu lation wa s extremely slow , running a t about a 50 0,000 : 1 rate, but very detailed).

I boug ht pa rts, mos tly from JAM ECO , and w ire-wra ppe d the e ntire com pute r on 15 circuit
boards in my basem ent. I built the “relay rack” out of 1x2 inch pine boards, rails for
shelv ing (c an yo u spo t them ?), plexig lass, screw s, and spray p aint.
Three AGCs
To succeed, I had to b uild the AG C three tim es. Or rather, I had to bu ild three AGC s. Each
one is a fully functional computer. Each one is a step toward building the next. The last one
is the one I wanted.

Here’s the “faces” of my three AG Cs:

AG C # 1: m y C+ + A GC sim ula tor.


It has eve ry register an d logic
signal in the original
doc um entation.

I use it to develop and debug


AG C assem bly lan gu age cod e.

When I got this working, I knew I


understood the AGC. I knew I
could do the rest of the proje ct.

AG C # 2: m y Circu itMa ker dig ital circuit


simulator AGC. What you see here are front
panel log ic indicators for signals and registers.
The remaining pa rt: about 60 or so pages of
logic diagrams; the same schem atics you’ll see
in the CTL, PROC, MEM, and IO .pdf files that
follow this overview.

This AGC literally runs about a half-million


times slower than the first! I used it to debug
m y log ic de sign .

When I got this working, I knew I still had a


yea r (14 m onths, a ctua lly) o f tedio us w ork
ahead.
AG C # 3: Th is is
the final one.

My hardware AGC
is built into a
relay rack to give
it th e sa m e
prototyped
appearance as
the original. The
dim ens ions are
abo ut 3 x 5 feet.

The up per le ft
m odu le (CT L) is
the control logic.

The low er left


m odu le (PR OC ) is
the CPU.

The lower right


m odu le (ME M) is
the memory.

The up per le ft
module (IO) has
the inputs and
outputs. The
keyboard and
display (DSKY)
for use r inp ut a re
on th e up per-
m iddle -right.

The white KYNAR


wires look like
cobwebs to me.

This was
pho tograp hed in
my back yard.
Can you see the
blue sky and tree
branch reflections
in the upper
panels?
Here I am, plugging COLOSSUS 249 flight
software EPRO Ms into Z IF sockets.

We lugged the AGC out of the basement and


took it ou tdoors for th ese ph otogra phs . It’s in
the bac kya rd, pro pp ed a gainst the b ack of m y
house.

A close-up of COLOSSU S EPROM s. I put red


tape over the quartz windows to keep them
from erasing th em selves.

A b oug ht th e tog gle s wit che s a few years


ago from a surplus ven dor. They w ere so
cheap, I bought a 100 of them. Aren’t they
great?

I really like flashing ligh ts and con trol panels.


There ’s no wa y to deb ug so m ethin g this
co mp lex w ith ou t th em .

I bought LED s in bulk from the sam e place


that sold m e the switches.
AGC Demonstration
Here’s a demonstration of my AGC. It’s running my version of the COLOSSUS 249 flight
software load:

At power-on, the AGC initializes to major mode 00


(POO ).

Display CM clock:
<VERB> <0> <6> <NOUN> <3> <6> <ENTER>

The red buttons were from Radio Shack.

The AGC samples and displays the CM clock. 58


hours, 44 m inutes, 34.27 seconds.
Test display lights:
<VERB> <3> <5> <ENTER>

All D SK Y lam ps an d disp lay seg m ents illu m inate


for 5 sec. The Verb and Noun displays flash. After
5 sec, the DSKY lamps extinguish. The 88888
displays are left in the registers.
Load component 1 for dataset at
octal address 50 with octal 123:
<VERB> <2> < 1> <NOUN> <0> <1>
<ENTER>

Verb/no un disp lay flashes: wa iting for address


<5> <0> <ENTER>

Verb/noun display flash continues: waiting for


data
<1> <2> <3> <ENTER>

Octal word from R1 is loaded into memory at


address 50.
Start a monitor program to
continuously display elapsed time
from the CM clock:

<VERB> <1> < 6> <NOUN> <3> <6>


<ENTER>

The m onitor p rogram starts to con tinuo usly


display the CM clock, updating the display about
about 1 second intervals. The time shown is 58
hours, 47 m inutes, and 23.67 secon ds.
Display component 1 of dataset at
octal address 50:

<VERB> <0> < 1>

The “key rel” light flashes beca use the CM clock


monitor program has been suspended.

<NOUN> <0> <1> <ENTER>


verb/nou n display flashe s: waiting for address

<5> <0> <ENTER>

The o ctal w ord from add ress 50 is displa yed in


R1 . In a p revio us o pera tion , we had set th e w ord
to 123.
Increment the address:
<NOUN> <1> <5> <ENTER>

The octal word in address 51 is displayed in R1,


incremented address in R3.
<ENTER>

The octal word in address 52 is displayed in R1,


incremented address in R3.
Resume the CM clock monitor
program:
<KEY REL>

Verb 16, noun 36 reappears, along with the


clock d isplay . The ke y release light g oes ou t.

(I’m not sure wh y the photos suddenly turned


darker; I th ink som eon e m ust h ave turn ed o ff a
light in an adjoining room)

Terminate the CM clock monitor


program:
<VERB> <3> <4> <ENTER>
Change major mode to P00:
<VERB> <3> <7> <ENTER>

Verb/noun display flashes: waiting for major


mod e.

Enter major mode P00:


<0> <0> <ENTER>

AGC starts POO.


AGC Project Diary
October - November, 2000:
Thinking about building a computer. Decided to reproduce a minicomputer from the
196 0's or ‘70's. Started gath ering te chn ical doc um enta tion on DEC PDP 8 an d Da ta
General NOVA.

Found a book in the local library (Ceruzzi, “A history of Modern Computing”) with a
page on the AGC. Decided to build a NASA flight computer; Gemini or Apollo. Gemini
interests m e m ore, beca use it’s the e arlier. Dow nloa ded Tom ayko ’s “Com pute rs in
Sp acefligh t.”

December 2000:
My search for Gemini computer documentation on CASI and NTIS is fruitless. IBM
federal systems division is gone. Found some interesting AGC docum entation. Asked
for, and received, E.C. Hall’s “History of the Apollo Guidance Compu ter” for
Christm as.

January - February 2001:


Decided to buld an AGC. Ordered about $300. of technical documents from CASI and
NTIS. Discovered you can’t judge a documen t by it’s title.

Sent e-mail to Goddard Space Flight Center; got a copy of the AGC Basic Training
Ma nua l, a progra m m er’s ma nua l for the Bloc k II. Wen t to Com pU SA and bou ght a
Microsoft C++ compiler. Starting to build a AGC Block II software simulation.

Sent an e-mail to Draper Labs (former MIT Instrumentation Lab where AGC was
designed) asking for R-393 (Block I “Logic Description”). This might be the key
doc um ent. Draper respo nd s by sendin g R -39 3 (free !).
JAC KPO T!
Abandoning Block II simulator. I am building a Block I AGC.

March - May 2001:


Rap id prog ress on th e Block I simu lation . Wrote an A GC cross-asse m bler in C ++ ; it
produces object code readable by the AGC simulator. Generating test code to debug
AGC basic instructions.

Design ed th e sim ula tor to m ap as clo sely as possib le to th e R- 39 3 hardwa re


registers, buses, microinstructions, and control signals. Broke the simulation into 20
sub system s that m ap to A GC hard wa re. I will even tually use th e sim ulato r code to
guide my hardware logic design.

Finished emulation of all 11 normal and extracode instructions. Wrote my first Block I
assembly language program! Starting to simulate the priority counters and
interrupts.

June - August 2001:


Finished v1.0 of the Block I simu lator. Wrote a suite of AGC assem bly langu age test
and checkout programs: TECO1 checks basic instructions; TECO2 checks extracode
instruc tions; T ECO 3 che cks editin g registe rs. Everyth ing w orks, inclu ding interrup ts
and cou nters.

Found MIT website with AGC history and interesting online documentation.
E-mailed Eldon Hall, designer of the AGC, telling him about my project. His reply was
gracious an d encou raging. W rote man y em ails to others asking for Block I source
code.

I order a 2.048 MHz crystal from D igiKey. My first hardware purchase.

September - October 2001:


Can’t find any original Block I source code, except tiny fragm ents in the docu me nts.
Recoded m y own EXEC , WAITLIST, and interrupt handlers using specifications from
R-393 and others. I’m starting to become a good AGC programmer. Now my
simulator can multitask!

Dis cove red B lock II flight softw are (C OLOS SU S 2 49 ) is no w d ow nloadable from MIT .
300+ pages of blurry assembler source code listing. Is that an eight, a six, number
zero with a slash th roug h it, or th e lette r “O”, or m ayb e “G”? Prin ted a hard cop y. I
think I can m ake m ost of it out.

The second half of COLOSSUS is missing! The missing part has the INTERBANK
COMM UNICATION, EXEC, and WAITLIST. E-mailed MIT. Their reply: they don’t have
the missing portion.

November - December 2001:


Tried to reproduce DSKY code using flowcharts from Green’s “Keyboard and Display
System Program”. Green calls it PINBALL GAME. Very confusing. Started writing a
C+ + sim ula tion from Gree n’s flow cha rts. Th ing s are b ecom ing clearer.

Loca ted P INB ALL in m y CO LOSS US fragm ent. Abandon ed effort to code m y ow n. I
will use the real thing.

January - February 2002:


Rety ped mo st of CO LOS SU S PIN BA LL ba ck into m achin e-read able form . 95% is
alread y Block I instruction s; recode d the rem ainin g 5% into Blo ck I equ ivalen t.
Finished all the octal display verbs (1-5) and decimal verb (6) and the load verbs
(21- 25), b ut the y’re only teste d for octal loa ds so far. No un ta bles are stubb ed, bu t I
can manually set them for different scaling.

Integrated PINBALL into m y EXEC and W AITLIST. Coded up a few missing pieces for
interbank com mu nication. Also ha d to code a m ath library (yikes). But it works.

The AGC simulator is running at abou t 1/30 the speed of the original. I need to speed
it up.

March - May 2002:


Bought a new , faster PC. The simu lator now runs at about 1/5 speed. Recoded some
simulator pieces. Now its 1:1.

Finished PINBALL. All regular verbs (0-39) work. Also, normal nouns 1, 2, 3, 9, 15,
26, an d 36 . Very coo l. My fav orites: verb 16, no un 3 6, a m onitor rou tine to
con tinu ous ly displa y the AG C clo ck, an d ve rb 35 wh ich d oes th e lam p tes t.

Now that I ha ve som e proficien cy, I am reluctan t to leave A GC softwa re, but it’s tim e
to start hardware logic design.
June - December 2002:
Decided to use 74LS logic family. Started designing AGC logic, subsystem-by-
subsystem, using the C++ simulator code as my guide. Began with MBF. Posted a
subsystem block diagram on the w all. I’m coloring in blocks as I finish each
subsystem design.

Entered logic de sign s into th e Circu itMa ker PC tool. Usin g Circu itMa ker’s digita l circuit
sim u la tio n to u nit-test ea ch su bsy stem .

Struggling with ALU and read/write bus design. The original AGC OR’ed everything
onto the bus, and the default bus state was logical zero: sometimes, they read the
bu s witho ut w riting to clea r registers. O ther tim es, they w rote m ultip le reg isters
sim ulta neo usly to OR the d ata. The se trick s won’t w ork w ith tri- state bus driv ers. I
identify where tricks are used and add ALU logic to handle the cases. My ALU sort-of
feeds back on itself. Confusing, but it should work.

Logic design the old way: Karnaugh maps, excitation tables, and bubble-pushing.
Fun, sort of.

Logic design is now finished, except for the DSKY. Un it tests are done.

I start ordering pa rts from JAME CO. Th e first order is for more than 2 00 ICs.

January 2003:
DSKY logic design is now finished and unit tested in CircuitMaker. All blocks on my
dia gram are co lored in. W ill the sub syste m s work to geth er?

February - May 2003:


Using Circu itM ake r to int egra te su bsy stem s. Dia gram s for each su bsy stem are
hooked into CircuitMaker “macros”; rectangular components with all inputs and
outp uts for tha t subs ystem . “Wired ” all sub system ma cros toge ther. W ill it run? I call
it AG C2 , to differe ntia te it from the C ++ sim ula tor, w hich I now call A GC 1.

Now I have two AG Cs! When I build the hardware, that will make three.

Started debugging a TC instruction, the simplest, in AGC2. Worked it through, step-


by-step, fixing logic and interface errors. Finally, it works. Debugging the remaining
8 basic instructions.

Massive snowstorm; snowed in, 3 days straight. Lots of time for AGC2 debugging and
testing.

I estim ate m y po wer budg et an d ord er a 1 5A 5V sup ply. Mo re ICs a nd sock ets are
ordered, too.

Sick of hand-assembling test code for AGC2. W rote a version of the cross-assemb ler
that produ ces object code for AGC 2. Broke TECO 1 into 8 pa rts; one for each
instruction. One-by-one, I get all portions of TECO1 to run on AGC2.

Broke TECO2 and TECO3 into pieces and got them to run on AG C2 also.
INTEGRATION TESTING IS DONE!

How to build it? There are too m any sub system s and interfaces.
June - August 2003
Grouped the 20 subsystems into 4 “assemblies” (soon to be called “modules”): I/O,
CTL, PROC, and M EM. This is more manag eable.

W rote C+ + cod e that ch ecks th e netlists p rodu ced b y Circu itMa ker for fan-ou t.
Esta blish ed a lim it of 8 T TL lo ads, an d ad ded buffers w here necessa ry. Ad ded buffers
between the 4 modules to make the fan-in for each module/module interface 1 TTL
load.

Stuffed IC sockets into 13 circuit boards; each board is 13"x5". What connectors and
cables to use between boards? Between modules? Should I worry about bus
termina tion? W hat kind of chassis?

Decided to build each module as a small relay rack. Board-to-board connections


inside each module are wire-wrapped--no connectors. Between modules, 40-pin IDE
cab les, m atin g to 3 0-p in w ire-w rap con nec tors, are for m odu le/m odu le interfaces.

Too lazy to pull 500 IC sockets and redo the boards. I’ll work in connectors and
additio nal bu ffers w here I can. B etter b uy the lo ng est ID E cab les (3 feet). M ore w orry
about bus termination.

Mod ule/Mo dule interfaces are now defined: 6 IDE ca bles.

Built a rack for the I/O module out of 1x2 pine boards. Spray-painted gray; it looks
pretty g ood. H ired m y eng ineerin g stud ent son to wire-w rap som e I/O m odu le logic
during su mm er vacation. He w ires most of the DS KY. It lights up an d m ultiplexes,
but operation is erratic; set aside for now.

September - December 2003:


Built control panels for PROC, CTL, and MEM modules by mounting switches on a
woo den frame . Used thick styre ne p lastic for a facep late, ha nd- lettered w ith ind elible
marker. It doesn’t look too bad.

Built indicator light panels for PROC, CTL, and MEM by punching holes in styrene
plastic with a push-pin and then shoving LEDs through the plastic into the PCB.
Hu nd reds of LED s; m y thum b has a bliste r.

Built 3 more relay racks for the PROC, CTL, and MEM modules. Laid all the boards out
on the racks. Will the IDE cables reach? Yes, but in some cases, barely.

Bought an EPR OM program mer. Learned Motorola S-format. Wrote yet another
version of the cross-assembler that outputs S-Record (s2f) format object code.
Burned EPRO Ms with TECO 1, TECO2, TECO3, and the A GC flight software.

Modified the C++ sim ulator (AGC1) so it dumps its microinstructions (subsystem
CPM-A ) in Motorola S-format. Burned EPR OMS for CPM-A . Created a special version
of AGC1 that reads CPM-A S-format microinstructions to verify the tables and reads
AG C ob ject co de in S-form at to v erify th e cross-as sem bler.

January - April 2004


Powered up, and started debugging the partly completed I/O module. Corrected a
desig n error a nd a few m ino r wirin g errors; cle an ed up som e cold- solder join ts. It
now w orks reliably. Finished wiring the I/O modu le. It’s difficult to test, but it seems
to work.
May - September 2004
Wired the power connections for all sockets on the CTL module; added a bypassing
cap acito r for every 2 p ackages, an d a 1 0u f tant alu m cap acito r for each row of ICs.
W ired the LED la m ps an d drive rs, and th en th e logic for ea ch su bsyste m . Plugg ed all
the CTL ch ips into the sockets.

Dis cove red a ll con trol sig nals from CPM -A w ere in verte d: th e EPRO M ta bles we re
generated from AGC 1, which uses positive logic. The hardware AGC uses negative
logic, because TTL inputs float high; I wanted floating inputs to assume the inactive
state during assembly and test. Pried the EPROMs out of their sockets, bit-flipped the
tables, erased and reprogram me d the chips, an d reinserted them . Now it wo rks.

Completed wiring for the other modules. Now to hook them up.

September - October 2004


Built a large rack to hold the 4 modules. Screwed the 4 modules into the rack and
hooked up the IDE cables. Powered it on. Everything lights up. No smoke. Amazing!
It runs part of TECO1, getting through dozens of instructions including subroutine
calls, before b om bing out.

Trying to debug the AGC by burning test EPROMs, single-stepping the clock, and
comparing the results to the AGC1 C++ simulator. It’s acting flaky. Could it be a
su pp ly p ro blem ?

Tore out m y flimsy pow er distribution; replaced it with heavy alu min um bus bars.
Supply lines look cleaner on the scope, but the operation is just as flaky as before.
Ma ybe w orse. It’s bom bing out in d ifferent plac es. Is there som e com m on elem ent?

Com mon element: the problem always involves read bus m icroinstructions. The ALU
sets a default state on the read bus if no other subsystem is using it. My bus
arbitratio n sch em e stinks : if a read sig nal is a sserted, th e ALU disab les its defau lt
state, but propagation delays cause both drivers to enable simultaneously for a brief
period. Is this the problem?

I kludg e-in log ic to disab le the rea d bu s durin g CL K1 . This giv es the rea d bu s logic
tim e to settle. I add propa gation delay to the C LK1 inpu t to TPG so the b us is
disabled before the TPG state transition occurs. Will this fix the problem?

No. It gets farth er alon g, bu t still bom bs ou t on read bus o peratio ns. It’s time to
download internet advice on bus termination. I add 220/330 ohm passive
termination to the read bus input in the ALU. IT WORKS!! TECO1 and TECO3 run
flawlessly.

TECO2 bombs out while testing the DV (divide) instruction. It produces different
results than the AGC1 simulator in the tests that involve division by zero. Do I care?
I decide I do n’t.

I load the COLOSSUS EPROMs. The AGC flight software hangs at first; but I add
some passive termination to the “busy” bus driver (for the read bus, of course) and
then it works flaw lessly too. The project is finished (except, I have to write up th ese
reports!)
AGC Block I Diagram
I constructed this diagram in the first months of my project. It shows the AGC subsystem s
as b oxe s. I ga ve them thre e-lette r na m es. M y diag ram is deriv ed from a sim ilar blo ck II
diagram I found in R-700.

I used th is diag ram to


organize my simulator and
logic d esign s. But, w hen it
became time to build the
AGC, I felt this diagram
had too m any interfaces;
too much complexity.

To sim plify thin gs, I


g ro up ed th e su bsy stem s
from this diagram into 4
mo dules.

The KBD, INP, OUT, and


DS P su bsy stem s were
external interfaces, so I
gro up ed the m into an IO
module. I’ll tell you about
it in pa rt 5 of this repo rt.

The ADR, MEM, MBF, and


PAR subs ystem s dealt w ith
m em ory, so they wen t into
a MEM modu le (described
in part 4).

The MO N, CLK, SCL, TPG, SEQ, and CPM subsystems generated the timing and control
puls es tha t run all th e other su bsyste m s, so I gathe red the m into a C TL (con trol) m odu le (in
part 2).

What remained? The ALU, CRG, INT, and CTR subsystems. I put them in a PROC
(processing) m odule (pa rt 3).
How my AGC differs from the original
For my purposes, the original AGC is described in a M.I.T. document called R-393 (A.
Hopkin s, R. Alonso, and H . Blair-Sm ith, "Logical Description for the Apollo G uidan ce
Com puter (AG C4)", R-39 3, MIT Instrum entation Lab oratory, Cam bridge, MA , Mar. 1963 ).

Logic Design
The original AGC4 was built almost entirely from 1964-era 3-input NOR gate ICs; about
5,000 of them. Original gate-level logic designs are not available.

Log ic for m y rep lica w as rec reate d using spec ifications in R -39 3, an d arc hite cture
information/diagrams from R-700. Since R-39 3 defines (in detail) AGC registers, register
tran sfers, m icroin struc tion s, an d m ost co ntrol pu lses (l ogic signals), th e arch itectu re of m y
replica clo sely m irrors the origin al to a low level.

The logic design for my replica uses late 1960's-early 1970's 74LS TTL logic, which affords
about a 10-to-1 reduction in package count. Flip-flop and register chips are used instead of
constructing ea ch elem ent from N OR g ates.

The replica successfully runs those portions of the original COLOSSUS flight software that
hav e been loade d on to it.

Clock
The original AGC4 divided its 2.048 MHz clock down into 4-phase 1.024 MHz signals to drive
asynch ronous, level-triggered, register logic.

My replica d ivides th e 2.04 8 M Hz clo ck into a 2 ph ased , non- overlap ping 1.02 4 M Hz clo ck to
drive synchron ous, edge-triggered register logic. Phase 1 of the clock (CLK1 ) steps a
sequ encer (T PG), w hich sets up the con trol signa ls. Those signa ls hav e tim e to prop aga te
(settle) between phase 1 and phase 2. Phase 2 (CLK2) clocks the registers using the control
signals established in phase 1. Data transfer occurs at phase 2.

Timing Pulses and Control States


The original AGC4 operation was controlled by 12 non-overlapping timing pulses (TP1 -
TP12) generated from a 4-bit gray code counter. Two SR FFs (called R1 and R2) were used
to generate 3 states to control standby operation (STANDBY, POWERON, RUN). R-393
iden tifies a start/stop logic su bsyste m wh ich ga tes the tim e pu lse gen erator to run and halt
AGC4 for ground test purposes but the internals were not defined.

My control states are integrated into one state machine with 16 states. The 12 timing pulses
(TP1 - TP12) are implem ented as states along with 2 add itional states for standby operation
(STBY, PWR ON). The remaining 2 states (RLSE, WA IT) handle front panel switch inputs for
single stepping.

Interpolation of Gaps in Control Logic


R-393 defines control pulses for TP1-TP11, but does not define control pulses for TP12.
Interfaces b etwe en in terrupts, in volun tary cou nters, an d the m ain co ntrol log ic are no t well
defined. For this reason, logic in the gaps in R-393 had to be interpolated, based upon
functional requ iremen ts.

Number and Address of Involuntary Counters


The number of involuntary counters and their address range is ambiguously defined in R-
393. Table 1-1 in R-393 says AG C4 has 20 (base 10 , I assume) counters. This is supported
by Figure 2-5 which numbers counters from 1-20. However, Table 3-1 which shows AGC
special registers assigns the cou nters to addresses 00 30 throug h 005 6 (base 8 ) which
translates to 23 (base 10) counters. And Table 5-1, section D gives the counter addresses
from 00 34 for OVC TR throu gh 00 56 for TRK R R, w hich translates to 19 (base 10 ) counters.
So, its unclear whether AGC4 had 19, 20, or 23 counters and whether the counter addresses
start at 0030 or 0034.

To resolv e the a m bigu ity, I set the startin g ad dress for the coun ters to 34 (octal), w hich is
the starting address used in the Block II AGC (COLOS SUS p rogram listing). For convenience,
I only im plem ented coun ters that w ere used by the AG C EX EC a nd W AITLIS T, the rea l-tim e
clock, and the overflow (5 in all). These are:

address counter
34 OVCTR
35 TIME2
36 TIME1
37 TIME3
40 TIME4

I also used the Block II ordering of TIME1 and TIME2, for compatibility with the COLOSSUS
flight software. In the AGC 4, TIME1 is at the low er address.

Address of Interrupt Vectors and GOPROG


In the Block II AGC, GOPRO G (the starting address for execution) is at the bottom of fixed
memory at address 4000, and the interrupt vectors follow at 4004, 4010, 4014, etc. By
extension, it would seem that the Block I AGC GO PROG would be at the bottom of fixed
memory at 2000, followed by interrupt vectors at 2004, 2010, 2014, etc. However, R-393
has the interrupt vectors starting at the bottom of fixed memory at 2000 (according to the
RUPT3 sequence on pages 3-67/3-68). R-393 doesn’t identify the address for the Block I
GOPROG.

For compatibility with the COLOSSU S source code (which I translated and cross-assembled
for Block I), I set GOPROG to 2000, with the interrupt vectors beginning at 2004.

Number of Interrupts
The original AGC had 5 vectored interrupts: UPRUPT, ERUPT, KEYRUPT, T3RUPT, and
DS RU PT. U PR UP T w as u sed for up link ed co m m ands; E RU PT w as g ene rated wh en h ardwa re
errors w ere detected. S ince I didn ’t plan to us e the se in terrupts, I elim ina ted th e ha rdw are
that supports them from my replica.

My replica implements the remaining 3 interrupts: KEYRUPT, T3RUPT, and DSRUPT


(T4RU PT).

Priority Counter sequences


The o rigina l AGC 4 im plem ented 3 sequ ences : PINC , MINC , and S HINC . PINC in crem ents
counter locations in mem ory; MINC decrements them. SHINC implem ents a shift operation
used to shift telemetry bits into the AGC. After 16 bits were shifted into an AGC word, an
interrupt UPRUPT was triggered, so the AGC could pull the telemetry word out of the
cou nter.

Since I’m not receiving telemetry, I didn’t implement the SHINC subsequence or the UPRUPT
interrup t.
Memory
AG C m em ory is 16 -bit w ords, orga nized into 10 24 w ord ba nks. Th e low est ban k (ba nk 0 ) is
erasable mem ory, originally core, but implemented in my replica as static RAM. All banks
abo ve ba nk 0 are fixed m em ory (origin ally im plem ented as rope co re, but im plem ented in
my replica as EPROM). AGC4 initially had 12K words of fixed memory. My replica has 15K.

Buses
The original AGC OR’ed everything onto the bus, and the default bus state was logical zero:
Registers were som etimes cleared by reading the default state off the bus. Other tim es,
seve ral reg isters w ere sim ulta neo usly read to OR the d ata. Beca use thes e tricks won’t w ork
well w ith tri-state b us driv ers, I identified th ese insta nces a nd a dde d logic to the A LU to
h an dle th em .

Flight Software
The orig ina l Blo ck I fligh t softw are is not a vailable (at leas t, to m e). Th e Blo ck II softw are
(CO LOS SU S 24 9) is av ailab le. Block II is an exten sion of th e Block I instruction set.
However, most of the Block II software was originally coded as Block I, so translating the
Block II code bac k to Blo ck I only in volves chan ging abou t 5% of the instru ctions b ack to
their Block I equivalents. This is what I did.

Back in 2002, only a portion of the COLO SSUS Block II code was available. Some key
portions, such as the EXEC and WAITLIST, were missing. I coded these parts in, using
inform ation from th e M.I.T. doc um ents a nd th e interface s and function al requ irem ents
implied by their use in the portions of COLOSSU S that were available.
Sources
Many of these sources are now (2004) available (free!) online at
http://hrst.mit.edu /hrs/apollo/pu blic/

R. Alonso, J. H. Laning , Jr. and H . Blair-Sm ith, "Preliminary M OD 3 C Program me r's Manual",
E-1077, MIT Instrumentation Laboratory, Cambridge, MA, Nov. 1961.
Useful information on AGC 4's predecessor: AGC 3. AGC 3 had fewer instructions (8 vs.
11) and a shorter instruction cycle (8 vs 12 timing pu lses). It is primarily helpful for
its presentation of AGC B lock I program ming techniques a nd exa mp les.

A. I. Green and J. J. Rocchio, "Keyboard and Display System Program for AGC (Program
Sunrise)", E-1574, MIT Instrumentation Laboratory, Cambridge, MA, Aug. 1964.
Flowcharts for DSKY software; no source code. Gives input codes for the DSKY
keyboa rd and the output cod es for display characters and registers.

E. C. Hall, "Journey to the Moon: The History of the Apollo Guidance Computer", AIAA,
Reston VA, 1996.
Nice information on the AGC development history with photos; R-700 (also by E.C.
Hall) is a better technical summary.

E. C . Ha ll, "M IT's R ole in P roject A pollo, V olu m e III, Co m pu ter S ub system ", R-700 , MIT
Charles Stark Draper Laboratory, Cambridge, MA, Aug. 1972.
An excellent overview of the AGC; more technical than Hall's "Journey to the Moon"
book. It contains an excellent diagram of the Block II register architecture and a n ice
diagram of a bit-slice of the register/bus logic. My copy from N TIS is somewhat poor
quality. There is also a useful discussion of the AGC test and checkout software.

A. H opkin s, "G uid an ce C om pu ter D esign , Part VI"


Extracted from som e (unkn own ) larger docum ent. An excellent overview of the B lock
II AGC with emp hasis on the I/O circuits. A very useful discussion of numb er
rep rese nta tion an d overflow ha nd ling in th e AGC , wh ich is un con ven tion al.

A. Hopkin s, R. Alonso, and H . Blair-Sm ith, "Logical Description for the Apollo G uidan ce
Computer (AGC4)", R-393, MIT Instrumentation Laboratory, Cambridge, MA, Mar. 1963.
My prim ary sou rce. It has a n early com plete spe cification of the A GC 4 (Bloc k I)
instruction set, register transfers, and control pulses. Information on the logic design
is largely absent, however. There are some internal inconsistencies and gaps in the
definition of the control logic: particularly at TP12, in the memory cycle, and at the
intersection of the control logic with the interrupts an d involuntary cou nters.
Unfortun ately, there are few diagram s; its mostly text and tables. There are also
som e exam ples of double-precision m ath routines.

B. I. S avag e an d A . Dra ke, "AG C4 Ba sic T rain ing Ma nu al, V olu m e I", E-205 2, M IT
Instrumentation Laboratory, Cambridge, MA, Jan. 1967.
The software manual for the Block II AGC. It has a fairly complete presentation of the
instruction set, but lacks example code.
Now, what?
There’s 8 more parts to this report. Download and read the parts you’re interested in.

Perhaps you want to build your own A GC. You can use m y software and logic designs, or
develop your own. There’s lots of room for improvement in my work. You could use it as
your sta rting p oint.

If you like, you can contact m e at: agcproject@msn .com


Block I
Apollo Guidance Computer (AGC)
How to build one in your basement

Part 2: Control (CTL) Module

John Pultorak
December, 2004
Abstract
This report describes my successful project to build a working reproduction of the 1964
prototype for the Block I Apollo Guidance Computer. The AGC is the flight computer for the
Ap ollo m oon lan din gs, and is the wo rld’s first in tegra ted c ircuit com pu ter.

I built it in my ba sem ent. It took me 4 yea rs.

If you like, you can build one too. It will take you less time, and yours will be better than
mine.

I docum ented m y project in 9 separate .pdf files:

Part 1 O v ervie w : Introdu ces the p roject.

Part 2 CTL Module: Design and construction of the control module.

Part 3 PROC M odule: Design and construction of the processing (CPU) modu le.

Part 4 MEM Module: Design and construction of the mem ory module.

Part 5 IO Module: Design and construction of the display/keyboard (DSKY) m odule.

Part 6 Assem bler: A cross-a ssem bler for AG C softw are dev elopm ent.

Part 7 C+ + S imu lator: A low-level simulator that runs assemb led AGC code.

Part 8 Flight Software: My translation of portions of the COLOSSUS 249 flight


software.

Part 9 Test & Che ckou t: A suite of test programs in AG C assembly langu age.
Overview
The Control Module (CTL) has 9 subsystems: CMI, MON, CLK, SCL, TPG, SEQ, CPM-A,
CPM -B, CPM -C

CMI (Control Module external


Interface)
The CMI interfaces the other control
module subsystems (described below)
to external AGC modules. 40-pin IDE
connectors interface to the PROC,
MEM , and IO modules. Inputs from
those modules are buffered to 1 LSTTL
load.

MO N (A GC M onitor)
The monitor subsystem has the
front-panel switches that control AGC
operation, and also implements the
pow er-up reset circuit.

CLK (Clock)
The AGC is controlled by a 2.048 MHz
crystal clock . The cloc k is divid ed to
produce a 2-phase, non-overlapping
1.024 MH z AGC system clock for
nom inal operation. For test purpo ses,
a low-frequency RC clock can also be
selected, or the clock can be
single-stepped.

SCL (Scaler)
The 1.024 MHz AGC clock is divided by
two to produce a 512 kHz signal called
the MASTER FREQUENC Y; this signal
is further divided through a SCALER,
first by five to produce a 102.4 kHz
signal. This is then divided by two
through 17 successive stages called F1
(51.2 kH z) through F17 (0.78 125 H z).
The F10 stage (100 Hz) is fed back into the AGC to increment the real-time clock and other
priority counters in the PROC module. The F17 stage is used to intermittently run the AGC
when it operates in the STANDBY mode.

TPG (Tim e Pu lse Gen erator)


AGC instructions are implemented in groups of 12 steps, called timing pulses. The timing
pulses, named TP1 through TP12, are produced by the Time Pulse Generator (TPG). Each set
of 12 timing pulses is called an instruction subsequence. Simple instructions, such as TC,
execute in a single subsequence of 12 pulses. More complex instructions require several
subseq uences.

SEQ (Seq uen ce Ge nerato r)


The sequence generator has the SQ register, which holds the next op-code, and the CTR
register--a counter used to count instruction subsequences during multiplication. The
sequence generator also has the branch register (BR) which controls conditional processing
during instruction execution, and the STAGE registers (STA and STB) which select the
instruction subsequences for complex instructions that have more than one subsequ ence.

CPM-A (Control
Pulse Matrix A)
The CPM-A is the
com bina tiona l logic
matrix that
imp lemen ts most
of the control logic.
It is driven by
inputs from the SQ
register (which
selects the
instruction), the
STB stage register
(which selects the
instruction
subsequence), and
the BR branch
register (which
selects conditional
steps in a
subseq uence).

CPM-B (Control
Pulse Matrix B)
The CP M-B
decodes read and
write control
signals for special
me mo ry location associated w ith input/ou tput registers, central registers (A, Q, Z, and LP),
and the editing registers used for rotation and shifting.

CPM-C (Control Pulse Matrix C)


The CPM -C decodes control signals primarily associated with interrupts, the mem ory cycle,
and th e selection of new instruction s and instruction subsequ ences.
This is a functiona l diagram show ing the interrelationsh ips betwe en the Tim e Pulse
Gen erator (TP G), the Con trol Pulse Ma trix (CPM -A, B, an d C), a nd th e registers th at are in
the Sequ ence
Generator (SE Q).

The d iagram is
m ine, bu t the style
is borrowed from
original AGC
documentation:
con trol sig nals are
represented by
diamonds. The
arrows show the
direction of data
flow. When a control
signal is asserted,
data is allow ed to
flow through the
diamon d. For
example, when WSQ
is asserted, the
opcode is written
from th e Rea d/W rite
bus into the SQ
regis ter.

Registers (LOOPC TR, STA, STB, SQ, BR, and SN I) are represented by rectangles. The lower
bit of the 2-bit STA register is set by ST1. The upper bit is set by any one of the 3 control
signals flowing into it. The STA register is cleared by CLRSTA.

Wh en W STB is asserted, the S TA register is copied to STB . STB an d the SQ register select
the instruction subsequence.

The instruction subsequence, time pulse generator, BR register, and SNI all feed into the
Control Pulse M atrix to select the active control pulses.

The diag ram w as develope d by an alyzing th e R-39 3 docum ent. It was one of m y first
diagrams; a sort-of conceptual breakthrough that became my gateway for understanding
the AGC control modu le.
The instruction
subsequences
executed by the
AGC are shown
in this diagram.
Each yellow
circle is a
subseq uence; a
set of 12 steps,
with each step
generating 0-5
control pulses.
Eleven steps
(TP 1-T P11 ) are
in the yellow
circle; the 12 th
step, which
selects the next
subseq uence
(TP12), is in the
orange circle.
This is discussed
in m ore deta il in
the TPG, SEQ,
and C PM-A
subsystem s.
A late a dditio n to the CTL d esign fixed a p roblem with the read bus lo gic. Du e to
propa gation delay s (and som e poor d esign on m y part), th e tri-state bu ffers from m ultiple
registers were simultaneously enabled for brief periods causing some transients. I “kludged”
in a design change that suppressed output to the read bus during CLK1, thereby giving the
bus control logic time to settle.
CTL Internal Subsystem Interconnections
This diagram show s internal interconnections for the subsystems in the CTL module.
CTL Module External Interfaces
The CTL m odule interfaces to the PROC, MEM, and IO m odules through 40-pin IDE ribbon
cables.
J100-CTL: CTL-to-PROC I/F
J100 is a 40-pin IDE ribbon cable that connects the CTL m odule to the PROC m odule.

OUT PUTS (from CTL):

PIN signal fu ll n am e state definition


1 WA3 WRITE ADDR 3 (74) 0=W rite reg at address 3 (LP)
2 WA2 WRITE ADDR 2 (73) 0=Write reg at address 2 (Z)
3 WA1 WRITE ADDR 1 (72) 0=Write reg at address 1 (Q)
4 WA0 WRITE ADDR 0 (71) 0=Write reg at address 0 (A)
5 RA3 READ ADDR 3 (60) 0=Read reg at address 3 (LP)
6 RA2 READ ADDR 2 (59) 0=Read reg at address 2 (Z)
7 RA1 READ ADDR 1 (58) 0=Read reg at address 1 (Q)
8 RA0 READ ADDR 0 (57) 0=Read reg at address 0 (A)
9 WZ WRITE Z (50) 0=W rite Z
10 WYx WRITE Y NO RESET (49) 0= W rite Y (do n ot reset)
11 WY WRITE Y (48) 0=W rite Y
12 WX WRITE X (47) 0=W rite X
13 WQ WRITE Q (45) 0=W rite Q
14 WOVR WRITE OVF (41) 0=W rite overflow
15 WOVI WRITE OVF RUPT INH (40) 0= W rite overflow RU PT inh ibit
16 WOVC WRITE OVF CNTR (39) 0=W rite overflow counter
17 WLP WRITE LP (38) 0=W rite LP
18 WB WRITE B (36) 0=W rite B
19 WA LP WRITE A/LP (35) 0=W rite A and LP
20 WA WRITE A (34) 0=W rite A
21 F10X F10 SCALER ONESHOT 1=timed out (100.0 Hz)
23 R24 READ 24 (25) 0=Read 24
24 R22 READ 22 (24) 0=Read 22
25 R2 READ 2 (23) 0=Read 2
26 R1C READ 1 COMP (22) 0=R ead 1 comp limented
27 R1 READ 1 (21) 0=Read 1
28 RZ READ Z (20) 0=Read Z
29 RU READ U (19) 0=Read sum
30 RSCT READ CNTR ADDR (18) 0=R ead selected cou nter address
31 RSB READ SIGN (17) 0= Rea d sign bit
32 RRPA READ RUPT ADDR (16) 0=R ead RU PT add ress
33 RQ READ Q (15) 0=Read Q
34 RLP READ LP (13) 0=R ead LP
35 RC READ C (11) 0=Read C
36 RB14 READ BIT 14 (10) 0=Read bit 14
37 RB READ B (9) 0=Read B
38 RA READ A (8) 0=Read A
39 KRPT KNOCK DOW N RUPT (6) 0= Kn ock do wn Rup t priority
40 CI SET CARRY IN (1) 0= Carry in
J101-CTL: CTL-to-PROC I/F
J101 is a 40-pin IDE ribbon cable that connects the CTL m odule to the PROC m odule.

INPU TS (to CTL ):

PIN signal fu ll n am e state definition


21 SB_01 SUB SEL 01 SB_01 is LSB; SB_02 is MSB
22 SB_02 SUB SEL 02 00=no counter; 01=PINC; 10=MINC
23 IRQ INT RQST 0=interrupt requested.
25 WB_01 WRITE BUS 01 (lsb)
26 WB_02 WRITE BUS 02
27 WB_03 WRITE BUS 03
28 WB_04 WRITE BUS 04
29 WB_05 WRITE BUS 05
30 WB_06 WRITE BUS 06
31 WB_07 WRITE BUS 07
32 WB_08 WRITE BUS 08
33 WB_09 WRITE BUS 09
34 WB_10 WRITE BUS 10
35 WB_11 WRITE BUS 11
36 WB_12 WRITE BUS 12
37 WB_13 WRITE BUS 13
38 WB_14 WRITE BUS 14
39 WB_15 WRITE BUS 15 US (overflow ) bit
40 WB_16 WRITE BUS 16 SG (sign ) bit

OUT PUTS (from CTL):

PIN signal fu ll n am e state definition


1 R2000 READ 2000 (101) 0=Read 2000
2 WPCTR WRITE PCTR (98) 0=W rite PCTR (latch priority counter
sequence)
3 RPT READ RUPT (94) 0=Read RUPT opcode
4 INH SET INHINT (93) 0=Set INHINT
5 CLRP CLEAR RPCELL (92) 0=Clear RPCE LL
6 CLINH1 CLEAR INHINT1 (88) 0=Clear INHINT1
7 CLINH CLEAR INHINT (87) 0=Clear INHINT
8 GENRST GENERAL RESET (86) 0=G eneral Reset
19 CLK1 CLOCK1 1.024 MHz AGC clock 1 (normally low)
20 CLK2 CLOCK2 1.024 MHz AGC clock 2 (normally low)
J102-CTL: CTL-to-MEM I/F
J102 is a 40-pin IDE ribbon cable that connects the CTL m odule to the MEM m odule.

INPU TS (to CTL ):

PIN signal fu ll n am e state definition


31 EQU_16 ADDRESS = 016 (1) 0=CADR in register S = 016
32 EQU_17 ADDRESS = 017 (2) 0=CADR in register S = 017
33 GTR_17 ADDRESS > 017 (3) 0=CADR in register S > 017
34 EQU_25 ADDRESS = 025 (4) 0=CADR in register S = 025
35 GTR_27 ADDRESS > 027 (5) 0=CADR in register S > 027
36 GTR_1777 ADDRESS > 01777 (6) 0=CADR in register S > 01777
37 AD_1 ADDRESS (1) where AD_4 is MSB, AD_1 is LSB:
38 AD_2 ADDRESS (2) (low-order bits of address)
38 AD_3 ADDRESS (3)
40 AD_4 ADDRESS (4)

OUT PUTS (from CTL):

PIN signal fu ll n am e state definition


1 WE WRITE EMEM (97) 0=W rite E-MEM from G
2 S BW G WRITE G (95) 0= W rite G from m em ory
3 GENRST GENERAL RESET (86) 0=G eneral Reset
4 W23 WRITE ADDR 23 (85) 0=Write into SL
5 W22 WRITE ADDR 22 (84) 0=W rite into CYL
6 W21 WRITE ADDR 21 (83) 0=Write into SR
7 W20 WRITE ADDR 20 (82) 0=W rite into CYR
8 WGn WRITE G NORMAL (81) 0=W rite G (norm al gates)
9 WBK WRITE BNK (80) 0=W rite BNK reg
10 RBK READ BNK (70) 0=R ead BNK reg
11 WS WRITE S (46) 0=W rite S
12 WP2 WRITE P2 (44) 0=W rite P2
13 WPx WRITE P NO RESET (43) 0= W rite P (do n ot reset)
14 WP WRITE P (42) 0=W rite P
15 WGx WRITE G NO RESET (37) 0= W rite G (do not reset)
16 TP TEST PARITY (30) 0= Test pa rity
17 RP2 READ PARITY 2 (14) 0=Read parity 2
18 RG READ G (12) 0=Read G
19 GP GEN PARITY (5) 0= Gen erate Pa rity
20 CLG CLR G (2) 0= Clear G
21 CLK2 CLOCK2 1.024 MHz AGC clock 2 (normally low)
22 CLK1 CLOCK1 1.024 MHz AGC clock 1 (normally low)
23 NPURST POWER U P RESET 0=reset, 1=normal operation.
24 SW CLK DEBOUNCE CLOCK low freq clk for switch debou nce
25 FCLK CLOCK MODE 1= free-runn ing clk mo de; 0= sing le clk
mode
J103-CTL: CTL-to-I/O I/F
J100 is a 40-pin IDE ribbon cable that connects the CTL m odule to the I/O m odule.

INPU TS (to CTL ):

PIN signal fu ll n am e state definition


40 OUT1_8 STANDBY ENA BLED 1=standby enabled; works with STANDBY
ALLOW ED sw itch

OUT PUTS (from CTL):

PIN signal fu ll n am e state definition


1 CLK1 CLOCK1 1.024 MHz AGC clock 1 (normally low)
2 CLK2 CLOCK2 1.024 MHz AGC clock 2 (normally low)
3 NSA STANDBY ALLOW ED 0=standb y allowed
5 GENRST GENERAL RESET (86) 0=clear the DSKY, OUT1, and OUT2.
6 WA11 WRITE OUT1 (76) 0=write into OUT1 from write bus
7 WA10 WRITE OUT0 (75) 0=write into OUT0 (DSKY) from write bus
8 RA11 READ OUT1 (66) 0=output OUT1 register to read bus
9 RA4 READ IN0 (61) 0=output IN0 register to read bus
20 STBY STANDBY 0= AG C is in th e stan dby state
MON (AGC Monitor)
MON INPUTS:

I/F signal fu ll n am e state definition

SW-RUN RUN/STEP SELECT SW


NSW-RUN

SW-INS STEP MODE SW


NSW-INS

SW-FCL CLOCK MODE SW


NSW-FCL

SW-SA STANDBY ALLOWED SW


NSW-SA

SW-STP INST STEP SW


NSW -STP

SW-MCL CLOCK STEP SW


NSW-MCL

SW-RST MASTER RESET SW

MON OUTPUTS:

signal fu ll n am e state definition


NRUN RUN /HALT 0=run, 1= step
INST INST STEP MODE 1=instruction step, 0=sequen ce step
NSTEP SINGLE STEP 0=step (momentary)
NSA STANDBY ALLOW ED 0=standb y allowed
MCLK CLOCK STEP 1= step (m om enta ry); trigge rs a sing le
clock pulse. Ignored if FCLK is 1.
FCLK CLOCK MODE 1=continuous clock output at 1.024 MHz,
0=sin gle clock
NPURST POWER U P RESET 0=reset, 1=normal operation.
SENAB SCALER ENABLE 1= ena ble cou nting ; 0= hold
CTL CONTROL PANEL SWITCHES

Clo ck Co ntrol:

RATE Controls the slow clock rate when the 1MHZ/SLOW switch is in the SLOW
position.

1MHZ/SLOW Selects the free-running clock rate when the RUN/STEP switch is in the RUN
position. The 1M HZ po sition gates a 2M Hz signa l into the 2-ph ased clock
which produces a 1M Hz 2-phased clock rate. The SLOW position gates a low
frequency clock; the frequency is controlled by RATE.

RUN/STEP Selects a free-running (RUN) or a single-stepped (STEP) clock. In the RUN


position, the rate is controlled by the 1MHZ/SLOW switch. In the STEP
position, the clock steps each time the STEP button is depressed.

STEP Manually steps the clock when the RUN/STEP switch is in the STEP position.
The clock is 2-phased, so each press steps an alternate phase.

Scaler:

ENAB/DISAB Enables or disables the SCALER.


F10 Manually triggers the F10 stage of the SCALER.
F13 Manually triggers the F13 stage of the SCALER.
F17 Manually triggers the F17 stage of the SCALER.
Ex ec ution Co ntrol:

RUN/STEP In the RU N position, the A GC free-runs at a rate determ ined by th e clock


controls. In the STEP position, the AGC single-steps to the next instruction or
next sequence when the STEP button is depressed; the rate is determined by
the clock controls.

INST/SEQ Selects whether the AGC single-steps all the way to the next instruction
(INST) or just to the next sequence (SEQ). Some instructions, such as TC,
have a sin gle sequen ce; on these instructions, the sw itch has the sam e effect
in either position.

STEP Single-steps the AGC when the RUN/STEP switch is in the STEP position.

RESET Reset the entire AGC. Puts the TPG into the standby state.

Standby:

ALLOW/DISA The ALLOWED position authorizes the AGC software to put the AGC in standby
mode. The AGC is released from standby mode by a signal from F17 in the
SCALER. If the scaler switch is in the DISAB position, the scaler is disabled,
and the A GC will rem ain in the stan dby state. W hen the stan dby switch is in
the DISAB position, the standby mode is inhibited.

Normal Operation:

Set the following switch positions for nominal operation:

switch position
1MHZ/SLOW 1MHZ
RUN/STEP RU N (cloc k contro l)
ENAB/DISAB EN AB (scaler)
RUN/STEP RU N (ex ecution control)
ALLOW/DISA ALLOWED (standby)
CTL CONTROL SWITCH CONNECTIONS

PIN signal state definition


1 BUS# 10, line 1 Execution control: RUN/STEP
2 BUS# 10, line 2

3 BUS# 10, line 3 Execution control: INST/SEQ


4 BUS# 10, line 4

5 BUS# 10, line 5 Clock control: RUN/STEP


6 BUS# 10, line 6

7 BUS# 10, line 7 STANDBY ALLOW ED/DISABLED


8 BUS# 10, line 8

9 BUS# 10, line 9 Execution control: STEP


10 BUS#10, line 10

11 BUS#10, line 11 Clock control: STEP


12 BUS#10, line 12

13 BUS#10, line 13 RESET

14 BUS#10, line 14 SCALER DISAB

15 RATE (SLOW) CLOCK CO NTROL RATE

16 1MHZ Clock control: 1MHZ/SLOW


17 SLOW

18 F10 MANUAL TRIGGER F10


19 F13 MANUAL TRIGGER F13
20 F17 MANUAL TRIGGER F17
CTL INDICATORS

The CTL module has a panel of indicator lamps (LEDs) to show the state of CTL registers and
critical logic signals.

These indicator
lamps show the
curren t state of all
registers and some
add itiona l,
im portan t logic
signals produced by
the CTL mod ule.
The matrix of lamps
in the lower portion
show active
subseq uences. M uch
of the con trol logic
is negative (active
low) where an
illu m in ated la m p
means that the
signal is NOT
asserted. At the
time the photo was
taken the AGC was
running
the COLOSSUS 249
flight software load, executing Verb 16, Noun 36: a monitor verb which displays the AGC
real time clock.
CLK (Clock)
The original AGC used asynchronous logic driven by a 4-phase clock. This recreation uses
synchronous logic driven by a 2-phase non-
overlap ping clock. Th e sync hron ous clo ck logic
wa s desig ned to produ ce the follow ing sta te
transitions:

FFA FFB
0 0 idle state
0 1 decoded for phase 1
1 1
1 0 decoded for phase 2

The outputs of FFA and FFB are decoded by


combinational logic to produce the non-
overlappin g pha se 1 and phase 2 clock signals.
The se que nce is a rrang ed so th ere is a sing le
logic lev el transitio n for each state tran sition to
prevent transients.
CLK INPUTS:

I/F signal fu ll n am e state definition


MON:
MCLK CLOCK STEP 1= step (m om enta ry); trigge rs a sing le
clock pulse. Ignored if FCLK is 1.
FCLK CLOCK MODE 1= con tinu ous clock outp ut at 1.024 MH z,
0=single clock whenever MCLK is 1.
NPURST POWER U P RESET 0=pow er up reset

OUTPUTS:

signal fu ll n am e state definition


CLK1 CLOCK1 1.024 MHz AGC clock 1 (normally low)
CLK2 CLOCK2 1.024 MHz AGC clock 2 (normally low)
SCL (Scaler)
The 1 .024 MH z AG C clock is
divided by two to produce a
512 kHz signal called the
MA STE R FR EQU ENC Y; this
signal is further divided
through a SCALER, first by
five to produce a 102.4 kHz
signal. This is then divided
by two through 17
successive stages called F1
(51.2 kHz) through F17
(0.78125 Hz). The F10 stage
(100 Hz) is fed back into the
AGC to increment the
real-time clock and other
priority counters in the PROC
m odu le. The F 17 sta ge is
used to intermittently run
the A GC wh en it op erates in
the STAND BY m ode.

The F10, F13, and F17


outputs of the SCALER feed
into a synchronous one-shot
that produces a short output
pulse on the rising edge of
the in put.
SCALER (SCL) ONE-SHOT LOGIC DESIGN

Boolean operators:
* (AN D), + (OR ), ' (NOT)

Scale r Divid e-by- 10 E xcitation Table :


The divide-by-10 state machine is implemented with a 74161 parallel counter. Control Mode
for 74 16 1 Pa rallel Cou nter:

NPE CET
0 x LOAD
1 1 COUNT
1 0 HOLD

Current Next Par In


State D C B A D C B A NPE CET D C B A
0 0 0 0 0 0 0 0 1 1 1
1 0 0 0 1 0 0 1 0 1 1
2 0 0 1 0 0 0 1 1 1 1
3 0 0 1 1 0 1 0 0 1 1
4 0 1 0 0 0 1 0 1 1 1
5 0 1 0 1 0 1 1 0 1 1
6 0 1 1 0 0 1 1 1 1 1
7 0 1 1 1 1 0 0 0 1 1
8 1 0 0 0 1 0 0 1 1 1
9 1 0 0 1 0 0 0 0 0 x 0 0 0 0

NPE = (D * A)'
CET = 1

On e-sho t Excitatio n Ta ble:


The on e-shot state m achine is im plem ented w ith two J-K F Fs.

JK flip-flop excitation table:


J K
0 0 Qn-1 (no chan ge)
0 1 0
1 0 1
1 1 Qn-1' (toggle)

FN is th e one -shot trig ger; Q (A) is th e one -shot o utpu t.

Cur Inp Nxt ffB ffA


State B A FN B A J K J K

IDLE 0 0 0 0 0 0 x 0 x
0 0 1 0 1 0 x 1 x
HIGH 0 1 0 1 0 1 x x 1
0 1 1 1 0 1 x x 1

LOW 1 0 0 0 0 x 1 0 x
1 0 1 1 0 x 0 0 x

UNUSED 1 1 0 x x x x x x
1 1 1 x x x x x x

The e xcitation table a nd lo gic eq uatio ns w ere derive d from the CL K su bsyste m state
machine which also performs a one-shot function. The CLK subsystems's FCLK signal was
factored out by setting it to zero. The MCLK input is the one-shot trigger; it was renamed
FN.

ff(B) J = QA
ff(B) K = FN'

ff(A) J = QB' * FN
ff(A) K = QA
SCL INPUTS:

I/F signal fu ll n am e state definition


CLK:
CLK1 CLOCK1 1.024 M Hz AG C clock

MON:
SW-SEN SCALER ENABLE 1=enable counting;
0= hold
NPURST POWER U P RESET 0=pow er up reset

SCL OUTPUTS:
signal fu ll n am e state definition
F10X F10 SCALER ONESHOT 1=timed out (100.0 Hz)
F13X F13 SCALER ONESHOT 1=timed out ( 12.5 Hz)
F17X F17 SCALER ONESHOT 1=timed out ( 0.78125
Hz)

F1 SCALER OUT F1 51.2 KHz square wave


F2 SCALER OUT F2 25.6 KHz square wave
F3 SCALER OUT F3 12.8 KHz square wave
F4 SCALER OUT F4 6,4 KHz square wave
F5 SCALER OUT F5 3.2 KHz square wave
F6 SCALER OUT F6 1.6 KHz square wave
F7 SCALER OUT F7 0.8 KHz square wave
F8 SCALER OUT F8 0.4 KHz square wave
F9 SCALER OUT F9 0.2 KHz square wave
F10 SCALER OUT F10 0.1 KHz square wave (100 Hz)
F11 SCALER OUT F11 50.0 Hz square wave
F12 SCALER OUT F12 25.0 Hz square wave
F13 SCALER OUT F13 12.5 Hz square wave
F14 SCALER OUT F14 6.25 Hz square wave
F15 SCALER OUT F15 3.125 Hz square wave
F16 SCALER OUT F16 1.5625 Hz square wave
F17 SCALER OUT F17 0.78125 Hz square wave

Note: One shot outputs are active for one clock pulse.
State transitions occur on the rising edge of CLK1
TPG (Time Pulse Generator)
TIME PULSE GENERATOR (TPG) LOGIC DESIGN

Boolean operators:
* (AN D), + (OR ), ' (NOT)

TPG Interna l Con trol Sign als:


Thes e are loca l to the TP G; if the sig nal is asserte
d, TPG m akes a state transition. These decoded
signals are the inputs to the excitation table.

TPG_0 = PURST' * ( FCLK' + F17X )

TPG_1 = FCLK' + F13X

TPG_2 = RUN + (SNI' * INST)

TPG_3 = SNI * OUT1_8 * SA

TPG_4 = STEP'

TPG_5 = STEP + RUN

TPG Excitatio n Ta ble:


Con trol M ode for 74 16 1 Pa rallel Cou nter:

NPE CET
0 x LOAD
1 1 COUNT
1 0 HOLD

*denotes active low

Current *Current TPG_x Next Par In


State D C B A Decoder 0 1 2 3 4 5 D C B A *NPE CET D C B A
STBY 0 0 0 0 NSTBY 0 x x x x x 0 0 0 0 1 0
1 x x x x x 0 0 0 1 1 1

PWRON0 0 0 1 NPWRON x 0 x x x x 0 0 0 1 1 0
x 1 x x x x 0 0 1 0 1 1

TP1 0 0 1 0 NTP1 x x x x x x 0 0 1 1 1 1
TP2 0 0 1 1 NTP2 x x x x x x 0 1 0 0 1 1
TP3 0 1 0 0 NTP3 x x x x x x 0 1 0 1 1 1
TP4 0 1 0 1 NTP4 x x x x x x 0 1 1 0 1 1
TP5 0 1 1 0 NTP5 x x x x x x 0 1 1 1 1 1
TP6 0 1 1 1 NTP6 x x x x x x 1 0 0 0 1 1
TP7 1 0 0 0 NTP7 x x x x x x 1 0 0 1 1 1
TP8 1 0 0 1 NTP8 x x x x x x 1 0 1 0 1 1
TP9 1 0 1 0 NTP9 x x x x x x 1 0 1 1 1 1
TP10 1 0 1 1 NTP10 x x x x x x 1 1 0 0 1 1
TP11 1 1 0 0 NTP11 x x x x x x 1 1 0 1 1 1
TP12 1 1 0 1 NTP12 x x x 1 x x 0 0 0 0 0 x 0 0 0 0
x x 1 0 x x 0 0 1 0 0 x 0 0 1 0
x x 0 0 x x 1 1 1 0 1 1

SRLSE 1 1 1 0 NSRLSE x x x x 0 x 1 1 1 0 1 0
x x x x 1 x 1 1 1 1 1 1

W AIT 1 1 1 1 NW AIT x x x x x 1 0 0 1 0 0 x 0 0 1 0
x x x x x 0 1 1 1 1 1 0

M ax term s:

NP E = (NTP 12 + TPG_ 3') * (NT P1 2 + TPG_ 2' + TPG_ 3) * (NW AIT + TPG_ 5')

CET = (NSTBY + TPG_0) * (NPWRON + TPG_1) * (NSRLSE + TPG_4) * (NWAIT + TPG_5)

Par In A,B,C = 0

Par In B = (N TP12 ' * TPG_3 )'


TPG INPUTS:

I/F signal fu ll n am e state definition


MON:
NRUN RUN /HALT 0=run, 1= step
INST INST STEP MODE 1=instruction step,
0=sequen ce step
NSTEP SINGLE STEP 0=step (momentary)
FCLK CLOCK MODE 1= contin uou s, 0= sing le
clock
NSA STANDBY ALLOW ED 0=standb y allowed
NPURST POWER U P RESET 0=pow er up reset

CLK:
CLK1 CLOCK1 1024 MHz AGC clock 1

SEQ:
SNI SELECT NEXT INST 1=select next instruction

SCL:
F17X F17 SCALER ONESHOT 1=timed out
F13X F13 SCALER ONESHOT 1=timed out

OUT:
OUT1_8 STANDBY ENA BLED 1=standby enabled; works with STANDBY
ALLOW ED sw itch

TPG OUTPUTS:

signal fu ll n am e state definition


TPG_Q3 TPG STATE where Q0 is LSB, Q3 is MSB:
TPG_Q2 00 = STBY
TPG_Q1 01 = PWRON
TPG_Q0 02 = TP1
03 = TP2
04 = TP3
05 = TP4
06 = TP5
07 = TP6
08 = TP7
09 = TP8
10 = TP9
11 = TP10
12 = TP11
13 = TP12
14 = SRLSE
15 = W AIT
SEQ (Sequence Generator)
The se que nce g enera tor conta ins the stage reg isters and bran ch regis ters that (a long with
the time pulse generator) control execution of the microinstruction sequence.

Som e back-of-the-en velope desig n that w ent into the bran ch


register logic is shown in the next few charts. This is the
conceptual design for the branch register. It has 2 flip-flops
nam ed B R2 and BR1 . The flip-flop s are set by the con trol signa ls
shown as diamonds in this diagram.

The J and K
inputs to the BR1
flip-flo p are
deve loped in this
chart.
This chart shows
the J an d K in puts
to the BR2 flip-flop.
Logic to sen se a 1's
compliment minus
zero from the w rite
bus is also
developed.
Here are some charts showing the stage
register design. This is the conceptual
design. Th ere are (2) 2-bit stage registers:
ST A and STB .

This is my initial cut at STA and ST B. My initial attempt at CLSTB is scratched out and then
developed into the correct solution on the later charts.
This is the J input to the STA2 flip-
flop. The initial design is at the
top. In the m idd le, I
“D eM org an iz e” it u sin g so me
bubble-pushing to get the final
im p lem en ta tio n a t th e b otto m.

Here’s the J and K inp uts to


STB1 and STB2. This is the
logic that transfers the
contents of the STA flip-
flops to STB.
The J a nd K inpu ts to STB 1 an d ST B2 m ove th roug h a m ulti-step DeM organ izing process in
the next 2 charts to reach their final state:
SEQ INPUTS:

I/F signal fu ll n am e state definition


CLK:
CLK2 CLOCK 2 data transfer occurs on falling edge

CPM-C:
GENRST GENERAL RESET 0=G eneral Reset
WSQ WRITE SQ 0=Write SQ
CLISQ CLEAR SNI 0=Clear SNI
CLSTA CLEAR STA 0= Clear sta te cou nter A
(STA)
WSTB WRITE STB 0= W rite sta ge co un ter B
(STB)
CLSTB CLEAR STB 0= Clear sta te cou nter B
(STB)
SETSTB SET ST1 0=Set the ST1 bit of STB

CPM-A:
TRSM TEST RESUME 0=Test for resume
TSGN TEST SIGN 0=Test sign
TSGN2 TEST SIGN 2 0=Test sign 2
ST1 SET STAGE 1 0=Stag e 1
ST2 SET STAGE 2 0=Stag e 2
CLCTR CLR LOOP CTR 0=Clear loop counter
CTR INCR LOOP CTR 0=Loop coun ter
TMZ TEST MINUS ZERO 0= Test for m inu s zero
TOV TEST OVF 0=Test for overflow
NISQ NEW INSTRUCT 0=New instruction to the
SQ reg

WBUS:
WB_01 WRITE BUS 01
... ...
WB_14 WRITE BUS 14
WB_15 WRITE BUS 15 US (overflow) bit for write bus
WB_16 WRITE BUS 16 SG (sign) bit for write bus

ADR:
EQU_25 ADDRESS = 025 0=CADR in register S evaluates to = 025

SEQ OUTPUTS:

I/F signal fu ll n am e state definition


CPM-A:
BR1 BRANCH REG 1 where BR1 is MSB, BR2 is LSB
BR2 BRANCH REG 2 BR00 =0 BR1=0, BR2=0
BR01 =1 BR1=0, BR2=1
BR10 =2 BR1=1, BR2=0
BR11 =3 BR1=1, BR2=1
SQ_3 INST REG where SQ_3 is MSB, SQ_0 is LSB
SQ_2
SQ_1
SQ_0

STB_1 STAGE REG where STB_1 is MSB, STB_0 is LSB


STB_0

LOOP6 LOOPCNTR EQ 6 0=LOOPCNTR is holding the number 6.

SNI SELECT NEXT INST 1= selec t nex t instruction ( SN I register)


CPM-A (Control Pulse Matrix A)
In th is AG C rep lica, th e CP M- A su bsequ enc es are im plem ented in EPR OM (the y w ere
implem ented as a diode matrix in the original). The address into the EPROM is constructed
as follows (bit 1 is the LSB ):

bit
13,14 CTR subsequence (2)
9-12: register SQ (4)
7,8: register STB (2)
3-6: register SG (4)
2: register BR1 (1)
1: register BR2 (1)

Bits 7-14 (STB, SQ, and CTR ) select the instruction subsequence. Bits 1-6 select the control
pulses (control logic signals) that are asserted from that subsequ ence.

SELECTING THE INSTRUCTION SUBSEQUENCE

The 11 AG C in struc tion s, priority coun ter op eratio ns, a nd interrup t ope rations a re
im plem ented in 22 instructio n sub sequ ences . Som e instru ctions (T C) h ave a single
subseq uence; oth ers have several sub sequen ces.

The instruction subsequence is choosen by CTR, SQ, and STB. These form bits 7-14 of the
CPM -A EPR OM address.

CTR (EPROM address bits 13-14)

The CTR signal has 2 lines: SB_02 is the MSB; SB_01 is the LSB. The signal comes from the
CTR subs ystem in the P RO C m odu le. It indicate s wh ether p rocessin g ne eds to b e briefly
interrup ted to in sert a PINC or MIN C su bseq uen ce to increm ent or d ecrem ent a p riority
cou nter.
CTR00: SB_02=0, SB_01=0 no coun ter; do the next sub sequen ce
CTR01: SB_02=0, SB_01=1 PINC
CTR10: SB_02=1, SB_01=0 MINC
CTR11: SB_02=1, SB_01=1 both; they cancel out, so do the next
subseq uence

Register SQ (EPROM address bits 9-12)

The 4-bit SQ register holds the currently executing instruction. The code in the SQ register
is the sam e as the op cod e for these four instructions.

NMEM SQ REG OPCODE USAGE DESCRIPTION CYCLES


TC 00 00 TC K Transfer Control 1 MCT
CCS 01 01 CCS K Count, Compare, Skip 2 MCT
INDEX 02 02 INDEX K Index 2 MCT
XCH 03 03 XCH K Exchange 2 MCT

The SQ register code for these four instructions is the op code + 010 (octal). This happens
because all of these instructions have bit 15 set (the sign (SG) bit) while in mem ory. When
the instruction is copied from memory to the memory buffer register (G) to register B, the
SG bit moves from bit 15 to bit 16 and the sign is copied back into bit 15 (US). Therefore,
the CS op code (04) becomes (14), and so on.

NMEM SQ REG OPCODE USAGE DESCRIPTION CYCLES


CS 014 04 CS K Clear and Subtract 2 MCT
TS 015 05 TS K Transfer to Storage 2 MCT
AD 016 06 AD K Add 2 or 3 MCT
MASK 017 07 MASK K Bitwise AND 2 MCT

These are the three extended instructions. They are accessed by executing an INDEX 5777
before each instruction. By convention, address 5777 contains 47777. The INDEX instruction
adds 47777 to the extended instruction to form the SQ op code. For example, the INDEX
adds 4 to the 4 op code for MP to produce the 11 (octal; the addition generates an
end-arou nd-carry). SQ reg ister code (the 7777 part is a negative zero).

NMEM SQ REG OPCODE USAGE DESCRIPTION CYCLES


MP 011 04 MP K Multiply 10 MCT
DV 012 05 DV K Divide 18 MCT
SU 013 06 SU K Subtract 4 or 5 MCT

STB (EPROM address bits 7-8)

The stage register B (STB) selects the subsequence for a given instruction. Some instructions
have m ultiple subsequences; others (TC) have only one.

The sta ge reg ister has 2 bits: ST B2 is th e MS B; S TB1 is the LS B. All in struction s initiallly
begin with the stage register set to zero. If an instruction needs more than one subsequ ence,
the stage register is incremented to select the next subsequence.
STB00: STB2=0, STB1=0
STB01: STB2=0, STB1=1
STB10: STB2=1, STB1=0
STB11: STB2=1, STB1=1

INSTRUCTION SUBSEQUENCES

There are 22 in struction subseq uences:

TC0 0
CCS0 1
CCS1 2
NDX0 3
NDX1 4
RSM3 5
XCH0 6
CS0 7
TS0 8
AD0 9
MASK0 10
MP0 11
MP1 12
MP3 13
DV0 14
DV1 15
SU0 16
RUPT1 17
RUPT3 18
STD2 19
PINC0 20
MINC0 21

If the CTR signals are 01 (SB_02=0, SB_01=1) the PINC subsequence is selected. If the CTR
signals are 10 (SB_02= 1, SB_01= 0) the MINC subsequen ce is selected. Otherwise,
subsequences for each instruction are selected using the 4-bit SQ register and the 2-bit stage
register (STB2, STB1, where STB2 is the MSB). Some instructions (TC) have only one
subsequence. At the start of each instruction, the stage counter is initially set to zero. The
interrupt call and resturn sequences are also stored at SQ=00.

SQ STB00 STB01 STB10 STB11


TC/RUPT 00: TC0 RUPT1 STD2 RUPT3
CCS 01: CCS0 CCS1 ---- ----
INDEX 02: NDX0 NDX1 ---- RSM3
XCH 03: XCH0 ---- STD2 ----

04: ---- ---- ---- ----


05: ---- ---- ---- ----
06: ---- ---- ---- ----
07: ---- ---- ---- ----
10: ---- ---- ---- ----

MP 11: MP0 MP1 ---- MP3


DV 12: DV0 DV1 STD2 ----
SU 13: SU0 ---- STD2 ----

CS 14: CS0 ---- STD2 ----


TS 15: TS0 ---- STD2 ----
AD 16: AD0 ---- STD2 ----
MASK 17: MASK0 ---- STD2 ----

SELECTING THE CONTROL PULSES

Each subsequence consists of 12 steps (TP1 - TP12), with each step asserting up to 5 control
puls es (con trol logic sig nals) . Steps T P1-T P11 are un ique to each subs equ ence; step TP 12 is
common to all subsequences. Control pulses for TP12 are discussed in CPM-C.

For som e steps, sele ction of th e contro l pulse s is also con dition al on th e state of th e 2-b it
branch reg ister (BR1 an d BR 2: BR 1 is the MS B and BR2 is the LSB):
BR00 BR1=0, BR2=0
BR01 BR1=0, BR2=1
BR10 BR1=1, BR2=0
BR11 BR1=1, BR2=1

Bits 1-6 of the CPM -A EPR OM address is formed as follows:


3-6: register SG (4)
2: register BR1 (1)
1: register BR2 (1)

The colu m n on the fa r left is th e step ; colu m ns to the rig ht sp ecify th e con trol pulse s tha t are
asserted for that step. Som e steps have n o control pulses.

subsequence TC0:
TP1 RB WY WS CI ----
TP2
TP3 WG ---- ---- ---- ----
TP4 RA WOVI ---- ---- ----
TP5
TP6
TP7 RG RSC WB WP ----
TP8 RZ WQ GP TP ----
TP9 RB WSC WG ---- ----
TP10 RU WZ ---- ---- ----
TP11 NISQ ---- ---- ---- ----

subsequence CCS0:
TP1 RB WS ---- ---- ----
TP2 RZ WY ---- ---- ----
TP3 WG ---- ---- ---- ----
TP4
TP5
TP6 RG RSC WB TSGN WP
TP7 BR00, RC TMZ ---- ---- ----
BR01, RC TMZ ---- ---- ----
BR10, RB TMZ ---- ---- ----
BR11, RB TMZ ---- ---- ----
TP8 BR00, GP TP ---- ---- ----
BR01, R1 WX GP TP ----
BR10, R2 WX GP TP ----
BR11, R1 R2 WX GP TP,
TP9 RB WSC WG ---- ----
TP10 BR00, RC WA ---- ---- ----
BR01, W A R1C ---- ---- ----
BR10, RB WA ---- ---- ----
BR11, W A R1C ---- ---- ----
TP11 RU ST1 WZ ---- ----

subsequence CCS1:
TP1 RZ WY WS CI ----
TP2
TP3 WG ---- ---- ---- ----
TP4 RU WZ ---- ---- ----
TP5 RA WY CI ---- ----
TP6
TP7 RG RSC WB WP ----
TP8 RU WB GP TP ----
TP9
TP10 RC WA WOVI ---- ----
TP11 RG RSC WB NISQ ----

subsequence NDX0:
TP1 RB WS ---- ---- ----
TP2
TP3 WG ---- ---- ---- ----
TP4 RA WOVI ---- ---- ----
TP5
TP6
TP7 RG RSC WB WP ----
TP8 GP TP ---- ---- ----
TP9 RB WSC WG ---- ----
TP10 TRSM ---- ---- ---- ----
TP11 ST1 ---- ---- ---- ----

subsequence NDX1:
TP1 RZ WY WS CI ----
TP2
TP3 WG ---- ---- ---- ----
TP4 RU WZ ---- ---- ----
TP5
TP6 RB WY ---- ---- ----
TP7 RG RSC WB WP ----
TP8 RB WX GP TP ----
TP9 RB WSC WG ---- ----
TP10
TP11 RU WB WOVI NISQ ----

subsequence RSM3:
TP1 R24 WS ---- ---- ----
TP2
TP3 WG ---- ---- ---- ----
TP4
TP5
TP6
TP7 RG WZ ---- ---- ----
TP8
TP9
TP10
TP11 NISQ ---- ---- ---- ----

subsequence XCH0:
TP1 RB WS ---- ---- ----
TP2 RA WP ---- ---- ----
TP3 WG ---- ---- ---- ----
TP4 WP2 ---- ---- ---- ----
TP5
TP6
TP7 RG RSC WB WP, ----
TP8 GP TP ---- ---- ----
TP9 RA WSC WG RP2 ----
TP10 RB WA WOVI ---- ----
TP11 ST2 ---- ---- ---- ----

subsequence CS0:
TP1 RB WS ---- ---- ----
TP2
TP3 WG ---- ---- ---- ----
TP4
TP5
TP6
TP7 RG RSC WB WP, ----
TP8 GP TP ---- ---- ----
TP9 RB WSC WG ---- ----
TP10 RC WA WOVI ---- ----
TP11 ST2 ---- ---- ---- ----

subsequence TS0:
TP1 RB WS ---- ---- ----
TP2 RA WB TOV WP ----
TP3 WG ---- ---- ---- ----
TP4 BR00, ---- ---- ---- ---- ----
BR01, RZ WY CI ---- ---- (overflow)
BR10, RZ WY CI ---- ---- (underflow)
BR11, ---- ---- ---- ---- ----
TP5 BR00, ---- ---- ---- ---- ----
BR01, R1 WA ---- ---- ----
BR10, W A R1C ---- ---- ----
BR11, ---- ---- ---- ---- ----
TP6
TP7 BR00, ---- ---- ---- ---- ----
BR01, RU WZ ---- ---- ----
BR10, RU WZ ---- ---- ----
BR11, ---- ---- ---- ---- ----
TP8 GP ---- ---- ---- ----
TP9 RB WSC WG ---- ----
TP10 RA WOVI ---- ---- ----
TP11 ST2 ---- ---- ---- ----

subsequence AD0:
TP1 RB WS ---- ---- ----
TP2 RA WY ---- ---- ----
TP3 WG ---- ---- ---- ----
TP4
TP5
TP6
TP7 RG RSC WB WP ----
TP8 RB WX GP TP ----
TP9 RB WSC WG ---- ----
TP10
TP11 RU WA WOVC ST2 WOVI

SU B_ MA SK 0 perform s a log ical A ND usin g D eM orga n's Th eorem : the inp uts a re inv erted , a
logica l OR is perform ed, an d the res ult is inv erted. Th e im plem enta tion of the OR (at TP8 ) is
somewhat unorthodox: the inverted inputs are in registers U and C. The OR is achieved by
gating both registers onto the read/write bus simultaneously. (The bus only transfers logical
1's; register-to-register transfers are performed by clearing the destination register and then
transferring the 1's from the sou rce register to th e destin ation ). Wh en th e 1's from both
registers are sim ultan eous ly gate d on to the b us, the w ord on the bu s is a logica l OR of both
regis ters.

subsequence MASK0:
TP1 RB WS ---- ---- ----
TP2 RA WB ---- ---- ----
TP3 WG ---- ---- ---- ----
TP4 RC WY ---- ---- ----
TP5
TP6
TP7 RG RSC WB WP ----
TP8 RU RC WA GP TP
TP9
TP10 RA WB ---- ---- ----
TP11 RC WA ST2 WOVI ----

subsequence MP0:
TP1 RB WS ---- ---- ----
TP2 RA WB TSGN ---- ----
TP3 RSC WG ---- ---- ----
TP4 BR00, RB WLP ---- ---- ----
BR01, RB WLP ---- ---- ----
BR10, RC WLP ---- ---- ----
BR11, RC WLP ---- ---- ----
TP5 RLP WA ---- ---- ----
TP6
TP7 BR00, RG WY WP ---- ----
BR01, RG WY WP ---- ----
BR10, RG WB WP ---- ----
BR11, RG WB WP ---- ----
TP8 BR00, GP TP ---- ---- ----
BR01, GP TP ---- ---- ----
BR10, RC WY GP TP ----
BR11, RC WY GP TP ----
TP9 RU WB TSGN2 ---- ----
TP10 BR00, RA WLP TSGN ---- ----
BR01, RA RB14 WLP TSGN ----
BR10, RA WLP TSGN ---- ----
BR11, RA RB14 WLP TSGN ----
TP11 BR00, ST1 WA LP ---- ---- ----
BR01, R1 ST1 WA LP R1C ----
BR10, RU ST1 WA LP ---- ----
BR11, RU ST1 WA LP ---- ----
subsequence MP1:
TP1 RA WY ---- ---- ----
TP2 RLP WA TSGN ---- ----
TP3 BR00, ---- ---- ---- ---- ----
BR01, ---- ---- ---- ---- ----
BR10, RB WX ---- ---- ----
BR11, RB WX ---- ---- ----
TP4 RA WLP ---- ---- ----
TP5 RLP TSGN ---- ---- ----
TP6 RU WA LP ---- ---- ----
TP7 RA WY ---- ---- ----
TP8 BR00, ---- ---- ---- ---- ----
BR01, ---- ---- ---- ---- ----
BR10, RB WX ---- ---- ----
BR11, RB WX ---- ---- ----
TP9 RLP WA ---- ---- ----
TP10 RA WLP CTR ---- ----
TP11 RU ST1 WA LP ---- ----

subsequence MP3:
TP1 RZ WY WS CI ----
TP2 RLP TSGN ---- ---- ----
TP3 WG ---- ---- ---- ----
TP4 RU WZ ---- ---- ----
TP5 RA WY ---- ---- ----
TP6 BR00, ---- ---- ---- ---- ----
BR01, ---- ---- ---- ---- ----
BR10, RB WX ---- ---- ----
BR11, RB WX ---- ---- ----
TP7 RG RSC WB WP ----
TP8 RLP WA GP TP ----
TP9 RB WSC WG ---- ----
TP10 RA WLP ---- ---- ----
TP11 RU WA LP NISQ ---- ----

subsequence DV0:
TP1 RB WS ---- ---- ----
TP2 RA WB TSGN ---- ----
TP3 RSC WG ---- ---- ----
TP4 BR00, RC WA ---- ---- ----
BR01, RC WA ---- ---- ----
BR10, ---- ---- ---- ---- ----
BR11, ---- ---- ---- ---- ----
TP5 BR00, R1 WLP ---- ---- ----
BR01, R1 WLP ---- ---- ----
BR10, R2 WLP ---- ---- ----
BR11, R2 WLP ---- ---- ----
TP6 RA WQ ---- ---- ----
TP7 RG WB TSGN WP ----
TP8 RB WA GP TP ----
TP9 BR00, RLP R2 WB ---- ----
BR01, RLP R2 WB ---- ----
BR10, ---- ---- ---- ---- ----
BR11, ---- ---- ---- ---- ----
TP10 BR00, RB WLP ---- ---- ----
BR01, RB WLP ---- ---- ----
BR10, RC WA ---- ---- ----
BR11, RC WA ---- ---- ----
TP11 R1 ST1 WB ---- ----

subsequence DV1:
TP1 R22 WS ---- ---- ----
TP2 RQ WG ---- ---- ----
TP3 RG WQ WY RSB ----
TP4 RA WX ---- ---- ----
TP5 RLP TSGN2 ---- ---- ----
TP6
TP7 RU TSGN ---- ---- ----
TP8 BR00, ---- ---- ---- ---- ----
BR01, ---- ---- ---- ---- ----
BR10, RU WQ ---- ---- ----
BR11, RU WQ ---- ---- ----
TP9 BR00, RB RSB WG ---- ----
BR01, RB RSB WG ---- ----
BR10, RB WG ---- ---- ----
BR11, RB WG ---- ---- ----
TP10 RG WB TSGN ---- ----
TP11 BR00, ST1 ---- ---- ---- ----
BR01, ST1 ---- ---- ---- ----
BR10, RC WA ST2 ---- ----
BR11, RB WA ST2 ---- ----

subsequence SU0:
TP1 RB WS ---- ---- ----
TP2 RA WY ---- ---- ----
TP3 WG ---- ---- ---- ----
TP4
TP5
TP6
TP7 RG RSC WB WP ----
TP8 RC WX GP TP ----
TP9 RB WSC WG ---- ----
TP10
TP11 RU WA WOVC ST2 WOVI

subsequence RUPT1:
TP1 R24 WY WS CI, ----
TP2
TP3 WG ---- ---- ---- ----
TP4
TP5
TP6
TP7
TP8
TP9 RZ WG ---- ---- ----
TP10 RU WZ ---- ---- ----
TP11 ST1 ST2 ---- ---- ----

subsequence RUPT3:
TP1 RZ WS ---- ---- ----
TP2 RRPA WZ ---- ---- ----
TP3 RZ KRPT WG ---- ----
TP4
TP5
TP6
TP7
TP8
TP9 RB WSC WG ---- ----
TP10
TP11 ST2 ---- ---- ---- ----

subsequence STD2:
TP1 RZ WY WS CI, ----
TP2
TP3 WG ---- ---- ---- ----
TP4 RU WZ ---- ---- ----
TP5
TP6
TP7 RG RSC WB WP ----
TP8 GP TP ---- ---- ----
TP9 RB WSC WG ---- ----
TP10
TP11 NISQ ---- ---- ---- ----

subsequence PINC:
TP1 WS RSCT ---- ---- ----
TP2
TP3 WG ---- ---- ---- ----
TP4 R1 WY ---- ---- ----
TP5
TP6 RG WX WP ---- ----
TP7 TP ---- ---- ---- ----
TP8 WP ---- ---- ---- ----
TP9 RU CLG WPx ---- ----
TP10 RU WGx WOVR ---- ----
TP11

subsequence MINC:
TP1 WS, RSCT ---- ---- ----
TP2
TP3 WG ---- ---- ---- ----
TP4 WY R1C ---- ---- ----
TP5
TP6 RG WX WP ---- ----
TP7 TP ---- ---- ---- ----
TP8 WP ---- ---- ---- ----
TP9 RU CLG WPx ---- ----
TP10 RU WGx WOVR ---- ----
TP11
Here’s some of the analysis used to develop the instruction sequence decoder. This decoder
takes in the STB and SQ inputs and decodes the instruction sequence for display to the
ope rator.
CONTROL SIGNAL DEFINITIONS

SIGNAL # DESCRIPTION
CI 1 Carry in
CLG 2 Clear G
CLCTR 3 Clear loop counter
CTR 4 Loop counter
GP 5 Gen erate Pa rity
KRPT 6 Kn ock do wn Rup t priority
NISQ 7 New instruction to the SQ register
RA 8 Read A
RB 9 Read B
RB14 10 Read bit 14
RC 11 Read C
RG 12 Read G
RLP 13 Read LP
RP2 14 Read parity 2
RQ 15 Read Q
RRPA 16 Read RUP T address
RSB 17 Rea d sign bit
RSCT 18 Read selected counter add ress
RU 19 Read sum
RZ 20 Read Z
R1 21 Read 1
R1C 22 Read 1 com plimented
R2 23 Read 2
R22 24 Read 22
R24 25 Read 24
ST1 26 Stage 1
ST2 27 Stage 2
TMZ 28 Test for m inu s zero
TOV 29 Test for overflow
TP 30 Test pa rity
TRSM 31 Test for resume
TSGN 32 Test sign
TSGN2 33 Test sign 2
WA 34 Write A
WA LP 35 Write A and LP
WB 36 Write B
Wgx 37 W rite G (do not reset)
WLP 38 Write LP
WOVC 39 Write overflow counter
WOVI 40 W rite overflow RU PT inh ibit
WOVR 41 Write overflow
WP 42 Write P
Wpx 43 W rite P (do n ot reset)
WP2 44 Write P2
WQ 45 Write Q
WS 46 Write S
WX 47 Write X
WY 48 Write Y
Wyx 49 W rite Y (do n ot reset)
WZ 50 Write Z

Control signa l outputs from CPM -A used as inputs to CP M-B only (not used outside of CPM ):

SIGNAL # DESCRIPTION
RSC 51 Read special and central (output to B only, not outside CPM)
WSC 52 Write special and central (output to B only, not outside CPM)
WG 53 Write G (output to B only, not outside CPM)

Control signa l outputs from CPM -A used as inputs to CP M-C only (not used outside of CPM ):

SIGNAL # DESCRIPTION
SDV1 54 Subsequence DV1 is currently active
SMP1 55 Subsequence MP1 is currently active
SRSM3 56 Subsequence RSM3 is currently active
CPM-A INPUTS:

I/F signal fu ll n am e state definition


TPG:
TPG_Q3 TPG STATE wh ere Q3 is MS B, Q0 is
LSB:
TPG_Q2 00 = STBY
TPG_Q1 01 = PWRON
TPG_Q0 02 = TP1
03 = TP2
04 = TP3
05 = TP4
06 = TP5
07 = TP6
08 = TP7
09 = TP8
10 = TP9
11 = TP10
12 = TP11
13 = TP12
14 = SRLSE
15 = W AIT
SEQ:

BR1 BRANCH REG 1 BR1 is MSB, BR2 is LSB


BR2 BRANCH REG 2 BR00=0:BR1=0, BR2=0
BR01=1:BR1=0, BR2=1
BR10=2:BR1=1, BR2=0
BR11=3:BR1=1, BR2=1

SQ_3 INST REG where SQ_3 is MSB,


SQ_0 is LSB
SQ_2
SQ_1
SQ_0

STB_1 STAGE REG where STB_1 is MSB,


STB_0 is LSB
STB_0

LOOP6 LOOPCNTR EQ 6 0=LOOPCNTR is holding


the number 6.

SNI SELECT NEXT INST 1= selec t nex t instruction ( SN I register)

CTR:
SB_01 SUB SEL 01 SB_01 is LSB; SB_02 is MSB
SB_02 SUB SEL 02 00=no counter; 01=PINC; 10=MINC

INT:
IRQ INT RQST 0=interrupt requested.

MON:
NRUN RUN /HALT 0=run, 1= step

CPM-A OUTPUTS:

signal fu ll n am e state definition


CI SE T C AR RY IN 0= Carry in
CLG CLR G 0= Clear G
CLCTR CLR LOOP CTR 0=Clear loop counter
CTR INCR LOOP CTR 0=Loop coun ter
GP GEN PARITY 0= Gen erate Pa rity
KRPT KNOCK DOW N RUPT 0= Kn ock do wn Rup t priority
NISQ NEW INSTRUCT 0=N ew instruction to the SQ reg
RA READ A 0=Read A
RB READ B 0=Read B
RB14 READ BIT 14 0=Read bit 14
RC READ C 0=Read C
RG READ G 0=Read G
RLP READ LP 0=R ead LP
RP2 READ PARITY 2 0=Read parity 2
RQ READ Q 0=Read Q
RRPA READ RUPT ADDR 0=R ead RU PT add ress
RSB READ SIGN 0= Rea d sign bit
RSCT READ CNTR ADDR 0=R ead selected cou nter address
RU READ U 0=Read sum
RZ READ Z 0=Read Z
R1 READ 1 0=Read 1
R1C READ 1 COMP 0=R ead 1 comp limented
R2 READ 2 0=Read 2
R22 READ 22 0=Read 22
R24 READ 24 0=Read 24
ST1 SET STAGE 1 0=Stag e 1
ST2 SET STAGE 2 0=Stag e 2
TMZ TEST MINUS ZERO 0= Test for m inu s zero
TOV TEST OVF 0=Test for overflow
TP TEST PARITY 0= Test pa rity
TRSM TEST RESUME 0=Test for resume
TSGN TEST SIGN 0=Test sign
TSGN2 TEST SIGN 2 0=Test sign 2
WA WRITE A 0=W rite A
WA LP WR ITE A/LP 0=W rite A and LP
WB WRITE B 0=W rite B
WGx WRITE G NO RESET 0= W rite G (do not reset)
WLP WR ITE LP 0=W rite LP
WOVC WRITE OVF CNTR 0=W rite overflow counter
WOVI WRITE OVF RUPT INH 0= W rite overflow RU PT inh ibit
WOVR WRITE OVF 0=W rite overflow
WP WRITE P 0=W rite P
WPx WRITE P NO RESET 0= W rite P (do n ot reset)
WP2 WRITE P2 0=W rite P2
WQ WRITE Q 0=W rite Q
WS WRITE S 0=W rite S
WX WRITE X 0=W rite X
WY WRITE Y 0=W rite Y
WYx WRITE Y NO RESET 0= W rite Y (do n ot reset)
WZ WRITE Z 0=W rite Z

OUTPUTS TO CPM -B ONLY; NOT USED OUTS IDE CPM

RSC READ SPECIAL REG 0=Read special and central


WSC WRITE SPECIAL REG 0=W rite special and central
WG WRITE G 0=W rite G

OUTPUTS TO CPM -C ONLY; NOT USED OU TSIDE CPM

SDV1 SUBSEQ DV1 0=Su bsequence DV 1 is selected


SMP1 SUBSEQ M P1 0=Su bsequence MP1 is selected
SRSM3 SUBSEQ RSM3 0=Su bsequence RSM 3 is selected
CPM-B (Control Pulse Matrix B)
Som e AGC registers are mapp ed onto low mem ory addresses (00 - 17 octal), so reading or
writ ing to tho se ad dresses ca use s da ta to b e read from , or written into, flip-flo p reg isters
instead of memory. These addresses include the central registers (A, Q, Z, LP), the bank
register (BNK), an d I/O registers.

Add resses 16 and 17 a re used in con junc tion w ith the INDE X instru ction to in hibit o r enab le
interrupts; this is a trick used to extend the instruction set; to cram m ore instructions into a
3-bit op code.

The ad dresses from 20 -23 are in eraseab le mem ory, but any da ta written into those
addresses is rotated or shifted. This is implemented through the G register in the MEM
mod ule.

Add resses 24 -27 a re reserved for saving the cen tral register b efore servicin g an interrup t.
Registers Z and B are automatically saved by the interrupt subsequence. Registers A and Q
mu st be saved by the interrupt service routine.

SPECIAL REGISTERS

These a dd resses called sp ecia l regis ters. A ll nu m bers are in octal.

addr Flip -Flo p reg isters


00 A regis ter (accum ula tor)
01 Q register
02 Z regis ter (p rogra m cou nter)
03 LP register
04 IN0 inp ut reg ister 0
05 IN1 inp ut reg ister 1
06 IN2 inp ut reg ister 2
07 IN3 inp ut reg ister 3
10 OUT0 outpu t regis ter 0
11 OUT1 outpu t regis ter 1
12 OUT2 outpu t regis ter 2
13 OUT3 outpu t regis ter 3
14 OUT4 outpu t regis ter 4
15 BANK bank register

16 RELINT
17 INHINT

Eraseab le m em ory reg isters


20 CYR cycle right
21 SR shift right
22 CYL cycle left
23 SL shift le ft

24 ZRUPT save register Z


25 BRUPT save register B
26 ARUPT save register A
27 QRUPT save register Q
CPM -B tran slates th e W G, RS C, an d W SC signa ls gen erated by S UB SYS TEM A into signa ls
that read from o r write to these registers. The logic is given below (all num bers are in octal):

if WG is asserted from CPM-A,

...and the address bus = 020: assert:W20


...and the address bus = 021: assert:W21
...and the address bus = 022: assert:W22
...and the address bus = 023: assert:W23

...othe rwis e, if the add ress b us > 17: assert:W Gn (not a cen tral reg ister)

if RSC is asserted from CPM -A

...and the address bus = 00: assert:RA0


...and the address bus = 01: assert:RA1
...and the address bus = 02: assert:RA2
...and the address bus = 03: assert:RA3
...and the address bus = 04: assert:RA4
...and the address bus = 05: assert:RA5
...and the address bus = 06: assert:RA6
...and the address bus = 07: assert:RA7
...and the address bus = 010: assert:RA10
...and the address bus = 011: assert:RA11
...and the address bus = 012: assert:RA12
...and the address bus = 013: assert:RA13
...and the address bus = 014: assert:RA14
...and the address bus = 015: assert:RBK
...and the address bus = 016: do nothing
...and the address bus = 017: do nothing

if WSC is asserted from CPM-A,

...and the address bus = 00: assert:WA0


...and the address bus = 01: assert:WA1
...and the address bus = 02: assert:WA2
...and the address bus = 03: assert:WA3
...and the address bus = 010: assert:WA10
...and the address bus = 011: assert:WA11
...and the address bus = 012: assert:WA12
...and the address bus = 013: assert:WA13
...and the address bus = 014: assert:WA14
...and the address bus = 015: assert:WBK
...and the address bus = 016: do nothing
...and the address bus = 017: do nothing
Here’s a tab le I develo ped to wo rk out th e relation ship s betw een a ddres ses an d CP M-B logic
signals:
CPM-B CONTROL SIGNALS

SIGNAL # DESCRIPTION
RA0 57 Read register at address 0 (A)
RA1 58 Read register at address 1 (Q)
RA2 59 Read register at address 2 (Z)
RA3 60 Read register at address 3 (LP)
RA4 61 Read register at address 4
RA5 62 Read register at address 5
RA6 63 Read register at address 6
RA7 64 Read register at address 7
RA10 65 Rea d registe r at add ress 10 (octal)
RA11 66 Rea d registe r at add ress 11 (octal)
RA12 67 Rea d registe r at add ress 12 (octal)
RA13 68 Rea d registe r at add ress 13 (octal)
RA14 69 Rea d registe r at add ress 14 (octal)
RBK 70 Read BNK
WA0 71 Write register at address 0 (A)
WA1 72 Write register at address 1 (Q)
WA2 73 Write register at address 2 (Z)
WA3 74 Write register at address 3 (LP)
WA10 75 W rite register at a ddres s 10 (o ctal)
WA11 76 W rite register at a ddres s 11 (o ctal)
WA12 77 W rite register at a ddres s 12 (o ctal)
WA13 78 W rite register at a ddres s 13 (o ctal)
WA14 79 W rite register at a ddres s 14 (o ctal)
WBK 80 Write BNK
WGn 81 Write G (n orma l gates)
W20 82 Write into CYR
W21 83 Write into SR
W22 84 Write into CYL
W23 85 Write into SL
CPM-B INPUTS:

I/F signal fu ll n am e state definition


CPM -A
RSC READ SPECIAL REG 0=Read special and central
WSC WRITE SPECIAL REG 0=W rite special and central
WG WRITE G 0=W rite G

ADR:
AD_4 ADDRESS AD_4=MSB, AD_1=LSB:
AD_3 (low -order b its of 14-b it
address)
AD_2
AD_1

GTR_17 ADDRESS > 017 0=CADR in register S >


017
GTR_27 ADDRESS > 027 0=CADR in register S >
027

CPM-B OUTPUTS:

I/F signal fu ll n am e state definition


RA0 READ ADDR 0 0=R ead reg at ad dress 0
(A)
RA1 READ ADDR 1 0=Read reg at address 1 (Q)
RA2 READ ADDR 2 0=Read reg at address 2 (Z)
RA3 READ ADDR 3 0=Read reg at address 3 (LP)
RA4 READ ADDR 4 0=R ead reg at ad dress 4
RA5 READ ADDR 5 0=R ead reg at ad dress 5
RA6 READ ADDR 6 0=R ead reg at ad dress 6
RA7 READ ADDR 7 0=R ead reg at ad dress 7
RA10 READ ADDR 10 0= Rea d reg a t add ress 10 (octal)
RA11 READ ADDR 11 0= Rea d reg a t add ress 11 (octal)
RA12 READ ADDR 12 0= Rea d reg a t add ress 12 (octal)
RA13 READ ADDR 13 0= Rea d reg a t add ress 13 (octal)
RA14 READ ADDR 14 0= Rea d reg a t add ress 14 (octal)
RBK READ BNK 0=R ead BNK reg
WA0 WRITE ADDR 0 0=Write reg at address 0 (A)
WA1 WRITE ADDR 1 0=Write reg at address 1 (Q)
WA2 WRITE ADDR 2 0=Write reg at address 2 (Z)
WA3 WRITE ADDR 3 0=W rite reg at address 3 (LP)
WA10 WRITE ADDR 10 0= W rite reg at ad dress 1 0 (octa l)
WA11 WRITE ADDR 11 0= W rite reg at ad dress 1 1 (octa l)
WA12 WRITE ADDR 12 0= W rite reg at ad dress 1 2 (octa l)
WA13 WRITE ADDR 13 0= W rite reg at ad dress 1 3 (octa l)
WA14 WRITE ADDR 14 0= W rite reg at ad dress 1 4 (octa l)
WBK WRITE BNK 0=W rite BNK reg
WGn WRITE G NORMAL 0=W rite G (norm al gates)
W20 WRITE ADDR 20 0=W rite into CYR
W21 WRITE ADDR 21 0=Write into SR
W22 WRITE ADDR 22 0=W rite into CYL
W23 WRITE ADDR 23 0=Write into SL
CPM-C (Control Pulse Matrix C)
The CPM-C subsystem issues control signals for the memory cycle, selecting the next
instruction, and performing priority counter subsequences. These signals are issued at
specific points in the 12 -step cycle of the time pu lse generator (TPG ).

STBY: assert:GENRST Resets various A GC registers.

PWRON: assert:R2000 Put the starting ad dress on the read b us.


CPM -A ass erts:W B, wh ich cop ies the d ata
into register B, which is the p refetch
register for the next instruction. Since the
opcod e for a bran ch is 0, th e instru ction in
B becom es TC 20 00, wh ich is the first
instruction always executed by the AGC.

TP1: assert:CLISQ SNI < - 0. Moved from TP12 to TP1 becau se


CLIS Q w as g ettin g cle ared in th is ha rdw are
AGC replica before TPG was clocked;
therefore TPG was not seeing the SNI
indication.

TP5: if: the address bus > 17 (no t a cen tral reg ister)
and the address bus < 2000 (not fixed memory; m ust be erasable)
and SDV1 or SMP1 are not asserted (not a loop counter subsequence)
then: a ss ert: SB W G read erasable memory into G by TP6

if: the address bus = 17


then: assert:INH INHINT instruction (INDEX 017)

if: the address bus = 16


then: assert:CLINH RELINT instruction (INDEX 016)

TP6: if: the address bus > 1777 (not eraseable mem ory)
and SDV1 or SMP1 are not asserted (not a loop counter subsequence)
then: a ss ert: SB W G read fixed memory into G register by TP7

TP11: if: the address bus > 17 (no t a cen tral reg ister)
and the address bus < 2000 (not fixed memory; m ust be erasable)
and SDV1 or SMP1 are not asserted (not a loop counter subsequence)
then: a ss ert: WE G register written to memory beginning at
TP11; Memory updates are in G by TP10
for all normal an d extracode instruction s,
but th e PINC and MIN C seq uen ces w rite to
G in TP10 because they need to update the
parity b it.
if: SRSM 3 is asserted
then: assert:CLRP Additional interrupts are inhibited during
servicing of an interrupt; Remove the
inhibition when RES UME is executed
(INDEX 025)
TP12: assert:WPCTR Check the priority counters; service any
waiting inputs on the next mem ory cycle.

if: the SNI register = 1 (if SNI is set, get next instruction)
then:
if: IRQ is asserted (if interrupt requested (see CPM-A for
similar assertion))
then:
assert:RPT Read the in terrupt ve ctor.
assert:SETSTB STB <- 1. Will cause the RUPT1
subsequence to execute.
else: (not an interrupt; a normal instruction)
assert:CLSTB STB < - 0 . The CPM-A will assert RB here,
which, when accompanied by WSQ
(below), will read the next instruction from
register B onto th e bu s. WS Q w ill write it
into SQ.
end if
assert:WSQ W rite the n ext instru ction (o n the write
bu s) into the SQ regis ter.
assert:CLSTA Clear STA regis ter.

assert:CLINH1 Clear INHINT1. Removes inhibition of


interrupts (if they were) AFTER the next
instruction
else: (not a new instruction)
if: CTR00 or CTR11 if previous sequence was not a PINC or
MINC, get next subsequence for same
instruction. if the previous sequence was
PINC or MINC, we already have the
subsequence, but it was interrupted by the
cou nter.
then:
assert:WSTB Copy STB < - STA. Gets next sequence for
same instruction.
assert:CLSTA STA <- 0
This tru th tab le show s the log ic
needed at TP12 to produce the
RPT, SETSTB, CLSTB, CLSTA,
WSQ, WSTB, CLISQ, and CLINH1
signals, given the SNI, IRQ,
NRU N, and S B inpu ts.

This chart shows the logic for


som e of the m ore sim ple
control pulses generated by
CM P-C. S om e pu lses redu ce to
states from the TPG, such as
CLISQ, WPCTR, and CLINH1,
which reduce to TP12.
This is the logic for RPT and SETSTB.

The desig n for CLSTB is a


little more complex. The
truth ta ble to th e left is
reduced through the
Karnaugh map to the
Minterm equ ation below
the m ap. A little bub ble
pushing D eMorganizes
the logic to the final
solution at the bottom
right.
This is the logic for
WE and SBWG, two
signals that control
me mo ry access.
CPM-C CON TROL SIGNALS

SIGNAL # DESCRIPTION
GENRST 86 General Reset
CLINH 87 Clear INHINT
CLINH1 88 Clear INHINT1
CLSTA 89 Clear state counter A (STA)
CLSTB 90 Clear state counter B (STB)
CLISQ 91 Clear SNI
CLRP 92 Clear RPCELL
INH 93 Set INHINT
RPT 94 Read RUPT opcode
S BW G 95 W rite G from m em ory
SETSTB 96 Set the ST1 bit of STB
WE 97 Write E-MEM from G
WPCTR 98 Write PCTR (latch priority counter sequence)
WSQ 99 Write SQ
WSTB 100 Write stage counter B (STB)
R2000 101 Read 2000
CPM INPUTS:

I/F signal fu ll n am e state definition


TPG:
TPG_Q3 TPG STATE wh ere Q3 is MS B, Q0 is
LSB:
TPG_Q2 00 = STBY
TPG_Q1 01 = PWRON
TPG_Q0 02 = TP1
03 = TP2
04 = TP3
05 = TP4
06 = TP5
07 = TP6
08 = TP7
09 = TP8
10 = TP9
11 = TP10
12 = TP11
13 = TP12
14 = SRLSE
15 = W AIT

ADR:
EQU_16 ADDRESS = 016 0=CADR in register S = 016
EQU_17 ADDRESS = 017 0=CADR in register S = 017
GTR_17 ADDRESS > 017 0=CADR in register S > 017
GTR_1777 ADDRESS > 01777 0=CADR in register S > 01777

CPM-A:
SDV1 SUBSEQ DV1 0=Su bsequence DV 1 is selected
SMP1 SUBSEQ M P1 0=Su bsequence MP1 is selected
SRSM3 SUBSEQ RSM3 0=Su bsequence RSM 3 is selected

CTR:
SB_01 SUB SEL 01 SB_01 is LSB; SB_02 is MSB
SB_02 SUB SEL 02 00=no counter; 01=PINC; 10=MINC

SEQ:
SNI SELECT NEXT INST 1= selec t nex t instruction ( SN I register)

INT:
IRQ INT RQST 0=interrupt requested.

MON:
NRUN RUN /HALT 0=run, 1= step
CPM OUTPUTS:

I/F signal fu ll n am e state definition


GENRST GENERAL RESET 0=G eneral Reset
CLINH CLEAR INHINT 0=Clear INHINT
CLINH1 CLEAR INHINT1 0=Clear INHINT1
CLSTA CLEAR STA 0=Clear state counter A (STA)
CLSTB CLEAR STB 0=Clear state counter B (STB)
CLISQ CLEAR SNI 0=Clear SNI
CLRP CLEAR R PCELL 0=Clear RPCE LL
INH SET INHINT 0=Set INHINT
RPT READ RUPT 0=Read RUPT opcode
S BW G WRITE G 0= W rite G from m em ory
SETSTB SET ST1 0=Set the ST1 bit of STB
WE WRITE EMEM 0=W rite E-MEM from G
WPCTR WRITE PCTR 0=W rite PCTR (latch priority counter
sequence)
WSQ WRITE SQ 0=Write SQ
WSTB WRITE STB 0=Write stage counter B (STB)
R2000 READ 2000 0=Read 2000
Fabrication
The C TL m odu le is (3) 1 3"x5 " circuit bo ards, an d 1 co ntrol pa nel.

Module Rack

The m odu le fram ewo rk is desig ned to resem ble a rela y rack, bu t scaled to fit the circuit
board dimensions. It is constructed out of 1"x2" pine and spray-painted semi-gloss gray.

Circu it boards are m oun ted to the ra ck by 2 p hillips sc rew s at either end . Nylon s pacers
(1/4") a re used a s stan doffs to hold the b oard edg es ab ove the ra ck. Th e bo ards are
m oun ted so th e chip s are in th e back and the pin s are w iring a re visible from the front.

Power is distributed by 2 heavy aluminum bus bars mounted vertically, one per side, on the
back of the module. Machine screws are mounted through the bus bars at evenly-spaced
intervals to provide conn ection points for the boards.

Solid copper wire (24 gauge) connects the boards to the bus bars. Ring terminals are used
on the bus bar side of the connection. On the circuit board size, the wires are soldered
directly to the supply rails.

Materials were purchased from Home Depot, ACE Hardware, and Radio Shack.

Circuit Boards

The circuit boards are 13"x5" general purpose prototyping boards, epoxy glass with double-
side plated throu gh pa ds on 0.1" cen ters (JAMEC O 21 477C L).

ICs are m oun ted in le vel 3 m achin e tooled wire-w rap sock ets: 8, 14 , 16, 20 , 24, an d 28 pin
(JAM ECO ). Each socket has the pin-out lab eled with a wire-wrap socket ID ma rker, which
slip s on to th e socke t be fore wrap pin g (J AM EC O). Th e part n um ber is w ritte n onto the ID
m arke r.

Sock ets are arran ged in 4 h orizon tal row s on ea ch bo ard, w ith
about 10 sockets per row.

Power is distributed on the back-side of each board by bare 24-


gau ge solid copp er wire su pply rails solde red at eq ual in tervals
to Klipwrap terminals: 3-prong terminals with a square tail for
wire-wrap ping (JA MEC O 34 163C L). A +5V rail runs above each
row o f sockets an d a g roun d rail run s below . Each ra il conn ects
directly to th e alum inum mo dule pow er bus u sing a ring ta il
con nec tor.

On the pin side of the board, all connections are made with 30
AW G Kyn ar wire-wrap wire (JAM ECO ). Red wire is used for direct
conn ections to the + 5V su pply rail. Black w ire is used for direct conn ections to grou nd. W hite
wire is used for everything else.

Power connections from the supply rails to each ICs are double-wrapped. Bypassing
capa citors (.1 uf d isc ) are sold ered ac ross the su pply rails at the Klipw rap term inals; abou t 1
capacitor for every 2 IC packag es.
All connections were stripped and hand-wrapped using a Radio Shack hand-wrap tool. As
each connection was made, the corresponding line on the schematic was marked with a
colored h igh ligh ter.

DIP resistor networks (JAMECO) plugged into 20-pin wire-wrap sockets were used as current
limiting resistors for the panel ind icators.
CTL P rinted Circuit Board (PCB) A

The A b oard contain s the clock (CLK ), the scaler (SCL), and th e time pu lse generator (TPG ).
CTL P rinted Circuit Board (PCB) B

The B board contains the display indicators, their current-limiting resistor networks, and the
open collector drivers. The display panel is a sheet of white styrene plastic. A push pin was
used to make holes through the plastic, and the LEDs were inserted in rows. The panel was
hand -lette red w ith a n in delible m arke r. A few chip s near the bo ttom righ t of the B bo ard are
associated with subsystems on the C board.
CTL P rinted Circuit Board (PCB) C

The C board contains the sequence generator (SEQ) and control pulse matrixes (CPM-A,
CPM -B, and C PM-C ). The big ICs at the bottom are the EPRO Ms tha t hold the con trol pulse
matrix table for CPM-A. Each EPROM holds the tables for 8 control signals. The large ICs at
the top are decod ers for the CPM-B a nd CP M-C logic.
Parts (ICs)
74LS00 (14) U74,U26,U27,U25,U79,U78,U55,U54,U53,U48,U13,U42,U34,U28
74LS02 (9) U4,U77,U9,U73,U45,U43,U3,U38,U32
74LS04 (15) U62,U29,U76,U69,U64,U57,U71,U59,U58,U56,U49,U41,U40,U39,U33
74LS06 (11) U22,U21,U20,U23,U14,U17,U18,U19,U16,U15,U1
74LS08 (2) U65,U10
74LS10 (4) U12,U70,U52,U51
74LS20 (8) U67,U68,U66,U63,U75,U61,U60,U6
74LS27 (3) U7,U44,U2
74LS32 (3) U72,U11,U5
74LS74 (1) U24
74LS112 (8) U8,U50,U47,U46,U35,U36,U37,U31
74LS138 (1) U99
74LS154 (7) U81,U82,U83,U98,U100,U101,U106
74LS161 (8) U104,U107,U108,U109,U110,U111,U112,U113
74LS244 (10) U80,U84,U85,U93,U94,U95,U96,U97,U102,U103
74LS273 (1) U105
27C128 (7) U86,U87,U88,U89,U90,U91,U92
4001 (1) U30
555 (1) U114

IC’s, sockets, PCB’s, resistors, capacitors, wire-wrap wire were purchased from JAMECO. The
2.048 MHz crystal and the IDE wire-wrap were from DigiKey. Wire ties, wire-wrap tools, and
copper wire were purchased from Radio Shack. IDE ribbon cables were purchased from an
online com pu ter su pp lier.

Power Budget
qty mA (ea) m A (tot)
74LS00 14 2.4 33 .6
74LS02 9 2.4 21 .6
74LS04 15 3.6 54 .0
74LS06 11 3.6 39 .6
74LS08 2 4.4 8.8
74LS10 4 1.8 7.2
74LS20 8 1.2 9.6
74LS27 3 3.4 10 .2
74LS32 3 4.9 14 .7
74LS74 1 4.0 4.0
74LS112 8 4.0 32 .0
74LS138 1 6.3 6.3
74LS154 7 6.2 43 .4
74LS161 8 19 .0 15 2.0
74LS244 10 32 .0 32 0.0
74LS273 1 17 .0 17 .0
27C128 7 25 .0 17 5.0
4001 1 0.4 0.4
555 1 3.0 3.0
LED 61 20 .0 12 20 .0
-------
2.2 Amps total
1.0 Am ps (excludin g LEDs)
EPROM generator program
This C+ + p rogra m gen erate s all file s neede d to p rogra m the C PM -A E PR OM s. Th e files a re
gen erated in M otorola S -Reco rd form at.

/*
* * * * * * * * * * * * * ** * * * * * * * * * * * * ** * * * * * * * * * * * * ** * * * * * * * * * * * * ** * * * * * * *
*
* C PM - A E P RO M G E NE R AT O R
*
* 9 / 1 4 /0 1
*
* * * * * * * * * * * * * ** * * * * * * * * * * * * ** * * * * * * * * * * * * ** * * * * * * * * * * * * ** * * * * * * * *

Versions:
D e r iv e d fr o m A G C C + + s i m u la t o r 1 . 1 5 .

O p e r a tio n :
Ge ne rates all of the C PM- A EPR OM files in M o t o ro l a s 2 f S -Rec o rd fo rma t
suitable for EPROM program me rs.

*/

# i n clu d e < s tr in g . h >


# i n clu d e < s td lib . h >
# i n clu d e < c ty p e .h >
# i n clu d e < io s tr e a m . h >
# i n clu d e < s td io . h >

# d e f in e M A X P U L S E S 1 5
# de fin e M A X _I PU LS ES 5 / / n o m or e t ha n 5 ins tru ct ion -g en er at ed pu lse s a ct ive at an y t im e

enum cpType { // **inferred; not defined in orignal R393 AGC 4 spec.


NO_PULSE=0,

/ / O U T PU T S F RO M S U BS Y S TE M A
CI =1, // C ar ry in
CLG =2, // Clear G
CLCTR =3, / / C l e ar lo o p c ou n t e r
CTR =4, / / L o o p co u n t e r
GP =5, // Gen erate Parity
KRP T =6, // Kno ck dow n Ru pt priority
NISQ =7, / / N e w i n st r uc t io n t o t h e S Q r e gis t e r
RA =8, / / R ead A
RB =9, / / R ead B
RB14 =10, / / R e a d b it 1 4
RC =11, / / R ead C
RG =12, / / R ead G
RLP =13, // Read LP
RP 2 =14, // Read parity 2
RQ =15, / / R ead Q
RRP A =16, // Read RU PT add ress
RSB =17, // R ea d s ign bit
RSCT =18, // Read selected cou nter addre ss
RU =19, // Read sum
RZ =20, / / R ead Z
R1 =21, / / R ead 1
R1C =22, / / R e a d 1 c om p l im e n t e d
R2 =23, / / R ead 2
R22 =24, // Read 22
R24 =25, // Read 24
ST1 =26, // Stage 1
ST2 =27, // Stage 2
TMZ =28, // Test for minus zero
TOV =29, / / Test for o verfl o w
TP =30, // Test p arity
TRSM =31, // T es t fo r r es um e
TSGN =32, / / Test sign
TSGN2 =33, / / T e s t s ig n 2
WA =34, / / W r i te A
WA LP =35, // Write A and LP
WB =36, // W r i te B
WGx =37, // Write G (do no t reset)
WLP =38, // Write LP
WOVC =39, // W r i te o v e rf lo w c o u n te r
WOVI =40, // W rite ov er flow RU PT inh ibit
WOVR =41, // Write ove rfl o w
WP =42, // W r i te P
WPx =43, // Write P (do no t reset)
WP2 =44, // Write P2
WQ =45, // W r i te Q
WS =46, // W r i te S
WX =47, // W r i te X
WY =48, // W r i te Y
WYx =49, // Write Y (do no t reset)
WZ =50, // W r i te Z

// OUTPUTS FROM SUBSYSTEM A; USED AS INPUTS TO SUBSYSTEM B ONLY;


// NOT USED OUTS IDE C PM
RSC =51, / / R ead spec ia l a n d cen t ra l ( o u t p u t t o B o n ly, n o t o u t s id e C P M )
WSC =52, / / Write spe c ia l a n d cen t ra l ( o u t p u t t o B o n ly, n o t o u t s id e C P M )
WG =53, / / W r it e G (o u tp u t t o B o nly , n o t o u ts id e CP M )

// OUTPUTS FROM SUBSYSTEM A; USED AS INPUTS TO SUBSYSTEM C ONLY;


// NOT USED OUTS IDE C PM
SDV1 =54, // Subsequence D V1 is currently active
SMP1 =55, // Subsequence M P1 is currently active
S R S M3 =56, // Subsequence RS M3 is currently active

/ / E X TE R NA L OU T PU T S F RO M S U BS Y S TE M B
//
RA0 =57, // R e a d re g is te r at a dd r es s 0 ( A )
RA1 =58, // R e ad re g is te r a t a dd re ss 1 ( Q )
RA2 =59, // R e a d re g is te r at a dd r es s 2 ( Z )
RA3 =60, // R ead reg i st er a t a d d res s 3 (L P )
RA4 =61, // Read reg ister at address 4
RA5 =62, // Read reg ister at address 5
RA6 =63, // Read reg ister at address 6
RA7 =64, // Read reg ister at address 7
RA10 =65, // R ea d r eg iste r a t a dd re ss 10 (o ct al)
RA11 =66, // R ea d r eg iste r a t a dd re ss 11 (o ct al)
RA12 =67, // R ea d r eg iste r a t a dd re ss 12 (o ct al)
RA13 =68, // R ea d r eg iste r a t a dd re ss 13 (o ct al)
RA14 =69, // R ea d r eg iste r a t a dd re ss 14 (o ct al)
RBK =70, // R e a d BN K
WA0 =71, // W r it e re g is te r at a dd r es s 0 ( A )
WA1 =72, // W r it e r eg is te r a t a dd re ss 1 ( Q )
WA2 =73, // W r it e re g is te r at a dd r es s 2 ( Z )
WA3 =74, // Write regis t er a t a d d res s 3 (L P )
WA10 =75, // W rite re gis te r a t a dd re ss 10 (o ct al)
WA11 =76, // W rite re gis te r a t a dd re ss 11 (o ct al)
WA12 =77, // W rite re gis te r a t a dd re ss 12 (o ct al)
WA13 =78, // W rite re gis te r a t a dd re ss 13 (o ct al)
WA14 =79, // W rite re gis te r a t a dd re ss 14 (o ct al)
WBK =80, // W r it e B NK
WGn =81, // W r i te G ( n o rm a l g at e s )* *
W20 =82, // W r i te in t o C Y R
W21 =83, // W r it e in t o S R
W22 =84, // W r i te in t o C Y L
W23 =85, // W r it e in t o S L

/ / T H E SE A RE T H E L EF TO V E RS -- TH E Y 'R E PR O B AB L Y U S ED IN S UB S YS T EM C
//
GENRST =86, / / G e n e r a l R e s e t* *
C L IN H =87, / / C l e ar IN H I N T * *
C L IN H 1 =88, / / C l e ar IN H I N T 1 * *
CLSTA =89, / / C l e ar s ta t e co u n t e r A ( S T A ) **
CLSTB =90, / / C l e ar s ta t e co u n t e r B ( S T B )* *
C L IS Q =91, / / C l e ar S N I **
CLRP =92, / / C l e ar R P C E L L* *
INH =93, / / S e t IN H I N T * *
RP T =94, / / R e a d R U P T op c o d e **
S BW G =95, // Write G from m em ory
SETSTB =96, / / S e t th e S T 1 b it o f S T B
WE =97, // W r ite E-M E M fr om G
WPCTR =98, // W r i te P C T R ( la t ch p r io r it y co u n t e r s e q u e n ce ) * *
WSQ =99, // W r it e SQ
WSTB =100, // W r i te s t ag e c o u n te r B (S T B ) * *
R2000 =101, // Read 2000 **
};

s ta t ic cp T y pe g lb l_ c p [M A X P U L S E S] ; / / c u rr e n t s e t o f a s se r te d c on t ro l p u ls e s ( M A X P U LS E S )

e num scTy pe { / / identifies subsequence for a given i n st ru c ti o n


SUB0=0, / / S T 2 = 0, ST 1 = 0
SUB1=1, / / S T 2 = 0, ST 1 = 1
SUB2=2, / / S T 2 = 1, ST 1 = 0
S U B 3 =3 / / S T 2 = 1, ST 1 = 1
};

enum brType {
BR00 =0, // B R 1 = 0, BR 2 = 0
BR01 =1, // B R 1 = 0, BR 2 = 1
BR10 =2, // B R 1 = 1, BR 2 = 0
BR11 =3, // B R 1 = 1, BR 2 = 1
NO_BR =4 // N O B R A NC H
};

str uct co ntr o lS ubStep {


b r T y pe b r ; / / n o r m a lly n o b ra n c h (N O _ B R )
c p T y pe p u ls e [ M A X _ IP U L S E S ] ; / / c o n ta in s 0 - M A X P U L S E S c o n tr o l p u ls e s
};

str uct co ntr o lS tep {


c o n tr o lS u b S t e p su b s te p [ 4 ]; // in d e x e d b y b r T y pe ( B R 0 0 , B R 0 1 , B R 1 0, B R 1 1 )
};

s t ru c t su b s e q u e n ce {
c o n tr o lS t e p tp [ 1 1 ]; // in d e x e d b y t p T yp e ( T P1 - T P 1 1 )
};

s t ru c t se q u e n c e {
subsequence* subseq[4] ; // indexed by sc Typ e
};

# d e f in e S T E P _ I N A C T IV E \
NO_BR, {N O_ PU LSE , NO _PU LSE , NO _PU LSE , NO _PU LSE , NO _PU LSE }, \
NO_BR, {N O_ PU LSE , NO _PU LSE , NO _PU LSE , NO _PU LSE , NO _PU LSE }, \
NO_BR, {N O_ PU LSE , NO _PU LSE , NO _PU LSE , NO _PU LSE , NO _PU LSE }, \
NO_BR, {NO_PULSE, N O _ P U LS E , NO_PULSE, NO_PULSE, N O _ P U LS E }

#de fine STE P(p1 , p2, p3 , p4, p5 ) \


NO_BR, { p1 , p2, p 3, p4 , p5} , \
NO_BR, {N O_ PU LSE , NO _PU LSE , NO _PU LSE , NO _PU LSE , NO _PU LSE }, \
NO_BR, {N O_ PU LSE , NO _PU LSE , NO _PU LSE , NO _PU LSE , NO _PU LSE }, \
NO_BR, {NO_PULSE, N O _ P U LS E , N O _ P U L S E , N O _ P U L S E , N O _ P U LS E }

s u bs e qu e n ce S U B _T C 0 = {
S TEP ( RB, WY, WS, CI, N O _ P U L SE ) , // T P 1
S TEP _ INAC TIVE, / / TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP ( RA, W OV I, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 4
S TEP _ INAC TIVE, / / TP 5
S TEP _ INAC TIVE, / / TP 6
S TEP ( RG, RSC, WB, WP , N O _ P U L SE ), // TP 7
S TEP ( RZ, WQ, GP, TP , N O _ P U L SE ), // TP 8
S TEP ( RB, WSC, WG, NO_PULSE, N O _ P U L SE ), // TP 9
S TEP ( RU, WZ, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 10
S TEP ( N IS Q , NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _C C S 0 = {
S TEP ( RB, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP ( RZ, WY, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP _ INAC TIVE, / / TP 4
S TEP _ INAC TIVE, / / TP 5
S TEP ( RG, RSC, WB, TSGN, WP ) , // T P 6
BR00, RC, TMZ, NO_PULSE, NO_PULSE, NO_PULSE, // TP 7
BR01, RC, TMZ, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, RB, TMZ, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, RB, TMZ, NO_PULSE, NO_PULSE, NO_PULSE,
BR00, GP, TP, NO_PULSE, NO_PULSE, NO_PULSE, // TP 8
BR01, R1, WX, GP, TP , NO_PULSE,
BR10, R2, WX, GP, TP , NO_PULSE,
BR11, R1, R2, WX, GP, TP ,
S TEP ( RB, WSC, WG, NO_PULSE, N O _ P U L SE ) , // T P 9
BR00, RC, WA, NO_PULSE, NO_PULSE, NO_PULSE, // TP 10
BR01, WA, R1C, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, RB, WA, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, WA, R1C, NO_PULSE, NO_PULSE, NO_PULSE,
S TEP ( RU, ST1, WZ, NO_PULSE, N O _ P U L SE ) // T P 1 1
};

s u bs e qu e n ce S U B _C C S 1 = {
S TEP ( RZ, WY, WS, CI, N O _ P U L SE ) , // T P 1
S TEP _ INAC TIVE, / / TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP ( RU, WZ, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 4
S TEP ( RA, WY, CI, NO_PULSE, N O _ P U L SE ) , // T P 5
S TEP _ INAC TIVE, / / TP 6
S TEP ( RG, RSC, WB, WP , N O _ P U L SE ) , // T P 7
S TEP ( RU, WB, GP, TP , N O _ P U L SE ) , // T P 8
S TEP _ INAC TIVE, / / TP 9
S TEP ( RC, WA, W OV I, NO_PULSE, N O _ P U L SE ), // TP 10
S TEP ( RG, RSC, WB, N IS Q , N O _ P U L SE ) // T P 1 1
};

s u bs e qu e n ce S U B _N D X 0 = {
S TEP ( RB, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP _ INAC TIVE, / / TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP ( RA, W OV I, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 4
S TEP _ INAC TIVE, / / TP 5
S TEP _ INAC TIVE, / / TP 6
S TEP ( RG, RSC, WB, WP , N O _ P U L SE ), // TP 7
S TEP ( GP, TP, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 8
S TEP ( RB, WSC, WG, NO_PULSE, N O _ P U L SE ), // TP 9
S TEP ( T R SM , NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 10
S TEP ( ST1, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _N D X 1 = {
S TEP ( RZ, WY, WS, CI, N O _ P U L SE ) , // T P 1
S TEP _ INAC TIVE, / / TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP ( RU, WZ, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 4
S TEP _ INAC TIVE, / / TP 5
S TEP ( RB, WY, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 6
S TEP ( RG, RSC, WB, WP , N O _ P U L SE ), // TP 7
S TEP ( RB, WX, GP, TP , N O _ P U L SE ), // TP 8
S TEP ( RB, WSC, WG, NO_PULSE, N O _ P U L SE ), // TP 9
S T E P _ I N A C T IV E , // T P 1 0
S TEP ( RU, WB, W OV I, N IS Q , N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _R S M 3 = {
S TEP ( R24, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP _ INAC TIVE, / / TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP _ INAC TIVE, / / TP 4
S TEP _ INAC TIVE, / / TP 5
S TEP _ INAC TIVE, / / TP 6
S TEP ( RG, WZ, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 7
S TEP _ INAC TIVE, / / TP 8
S TEP _ INAC TIVE, / / TP 9
S T E P _ I N A C T IV E , // T P 1 0
S TEP ( N IS Q , NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _X C H 0 = {
S TEP ( RB, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 1
S TEP ( RA, WP, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 3
S TEP ( WP2, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 4
S TEP _ INAC TIVE, / / TP 5
S TEP _ INAC TIVE, / / TP 6
S TEP ( RG, RSC, WB, WP , N O _ P U L SE ), // TP 7
S TEP ( GP, TP, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 8
S TEP ( RA, WSC, WG, RP2, N O _ P U L SE ), // TP 9
S TEP ( RB, WA, W OV I, NO_PULSE, N O _ P U L SE ), // TP 10
S TEP ( ST2, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _C S 0 = {
S TEP ( RB, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP _ INAC TIVE, / / TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP _ INAC TIVE, / / TP 4
S TEP _ INAC TIVE, / / TP 5
S TEP _ INAC TIVE, / / TP 6
S TEP ( RG, RSC, WB, WP , N O _ P U L SE ), // TP 7
S TEP ( GP, TP, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 8
S TEP ( RB, WSC, WG, NO_PULSE, N O _ P U L SE ), // TP 9
S TEP ( RC, WA, W OV I, NO_PULSE, N O _ P U L SE ), // TP 10
S TEP ( ST2, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _T S 0 = {
S TEP ( RB, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP ( RA, WB, TOV, WP , N O _ P U L SE
), // T P 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
BR00, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, // TP 4
BR01, RZ, WY, CI, NO_PULSE, NO_PULSE, //
o v e r flo w
BR10, RZ, WY, CI, NO_PULSE, NO_PULSE, //
unde r f lo w
BR11, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE,
BR00, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, // TP 5
BR01, R1, WA, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, WA, R1C, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE,
S TEP _ INAC TIVE, / / TP 6
BR00, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, // TP 7
BR01, RU, WZ, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, RU, WZ, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE,
S TEP ( GP, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 8
S TEP ( RB, WSC, WG, NO_PULSE, N O _ P U L SE
), // T P 9
S TEP ( RA, W OV I, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 10
S TEP ( ST2, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _A D 0 = {
S TEP ( RB, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP ( RA, WY, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP _ INAC TIVE, / / TP 4
S TEP _ INAC TIVE, / / TP 5
S TEP _ INAC TIVE, / / TP 6
S TEP ( RG, RSC, WB, WP , N O _ P U L SE ) , // T P 7
S TEP ( RB, WX, GP, TP , N O _ P U L SE ) , // T P 8
S TEP ( RB, WSC, WG, NO_PULSE, N O _ P U L SE ) , // T P 9
S T E P _ I N A C T IV E , // TP 10
S TEP ( RU, WA, WOVC, ST2, WOVI ), // TP 11
};

// No te: A ND is perfo rm ed u sing D eM orga n's T he ore m : the inpu ts are inver ted , a
/ / lo g ic a l O R i s p e r fo r m e d , a n d t h e r e s u lt is in v e r te d . T he im p l e m e n t at io n o f t h e
// OR (at TP8 ) is somewh at unortho dox: the inverted inpu ts are in registers U
/ / a n d C . T he O R i s a c h ie v e d b y g a t in g b o th r e g is t e rs o n to t h e re a d / w rit e b u s
// simu ltaneously. (The bus on ly transfers logical 1's; register-to-register transfers
/ / a r e p e rf o rm e d b y c le a r in g t h e de s t in a t io n r e g is t e r a n d t h en t ra n s fe r r in g t h e
// 1's from the so urce r egister to th e de stination). W hen the 1 's from b oth
// re giste r s a re s imultaneously gated onto the bus, th e wo rd o n t h e b u s i s a l og i ca l
// OR of both reg isters.
s u bs e qu e n ce S U B _M A S K 0 = {
S TEP ( RB, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP ( RA, WB, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP ( RC, WY, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 4
S TEP _ INAC TIVE, / / TP 5
S TEP _ INAC TIVE, / / TP 6
S TEP ( RG, RSC, WB, WP , N O _ P U L SE ) , // T P 7
S TEP ( RU, RC, WA, GP, TP ), // TP 8
( C H AN G E D )
S TEP _ INAC TIVE, / / TP 9
S TEP ( RA, WB, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 10
( C H AN G E D )
S TEP ( RC, WA, ST2, W OV I, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _M P 0 = {
S TEP ( RB, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP ( RA, WB, TSGN, NO_PULSE, N O _ P U L SE ) , // T P 2
S TEP ( RSC, WG, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
BR00, RB, WLP, NO_PULSE, NO_PULSE, NO_PULSE, // TP 4
BR01, RB, WLP, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, RC, WLP, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, RC, WLP, NO_PULSE, NO_PULSE, NO_PULSE,
S TEP ( R LP, WA, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 5
S TEP _ INAC TIVE, / / TP 6
BR00, RG, WY, WP , NO_PULSE, NO_PULSE, // TP 7
BR01, RG, WY, WP , NO_PULSE, NO_PULSE,
BR10, RG, WB, WP , NO_PULSE, NO_PULSE,
BR11, RG, WB, WP , NO_PULSE, NO_PULSE,
BR00, GP, TP, NO_PULSE, NO_PULSE, NO_PULSE, // TP 8
BR01, GP, TP, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, RC, WY, GP, TP , NO_PULSE,
BR11, RC, WY, GP, TP , NO_PULSE,
S TEP ( RU, WB, TSGN2, NO_PULSE, N O _ P U L SE ) , // T P 9
BR00, RA, WLP, TSGN, NO_PULSE, NO_PULSE, // TP 10
BR01, RA, RB14, WL P , TSGN, NO_PULSE,
BR10, RA, WLP, TSGN, NO_PULSE, NO_PULSE,
BR11, RA, RB14, WL P , TSGN, NO_PULSE,
BR00, ST1, WALP, NO_PULSE, NO_PULSE, NO_PULSE, // TP 11
BR01, R1, ST1, WA L P , R1C, NO_PULSE,
BR10, RU, ST1, WA L P , NO_PULSE, NO_PULSE,
BR11, RU, ST1, WA L P , NO_PULSE, NO_PULSE,
};

s u bs e qu e n ce S U B _M P 1 = {
S TEP ( RA, WY, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP ( R LP, WA, TSGN, NO_PULSE, N O _ P U L SE ) , // T P 2
BR00, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, // TP 3
BR01, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, RB, WX, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, RB, WX, NO_PULSE, NO_PULSE, NO_PULSE,
S TEP ( RA, WLP, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 4
S TEP ( R LP, TSGN, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 5
S TEP ( RU, WALP, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 6
S TEP ( RA, WY, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 7
BR00, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, // TP 8
BR01, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, RB, WX, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, RB, WX, NO_PULSE, NO_PULSE, NO_PULSE,
S TEP ( R LP, WA, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 9
S TEP ( RA, WLP, CTR, NO_PULSE, N O _ P U L SE ), // TP 10
S TEP ( RU, ST1, WA L P , NO_PULSE, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _M P 3 = {
S TEP ( RZ, WY, WS, CI, N O _ P U L SE ), // TP 1
S TEP ( R LP, TSGN, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 3
S TEP ( RU, WZ, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 4
S TEP ( RA, WY, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 5
BR00, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, // TP 6
BR01, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, RB, WX, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, RB, WX, NO_PULSE, NO_PULSE, NO_PULSE,
S TEP ( RG, RSC, WB, WP , N O _ P U L SE ), // TP 7
S TEP ( R LP, WA, GP, TP , N O _ P U L SE ), // TP 8
S TEP ( RB, WSC, WG, NO_PULSE, N O _ P U L SE ), // TP 9
S TEP ( RA, WLP, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 10
S TEP ( RU, WALP, N IS Q , NO_PULSE, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _D V 0 = {
S TEP ( RB, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP ( RA, WB, TSGN, NO_PULSE, N O _ P U L SE ) , // T P 2
S TEP ( RSC, WG, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
BR00, RC, WA, NO_PULSE, NO_PULSE, NO_PULSE, // TP 4
BR01, RC, WA, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE,
BR00, R1, WLP, NO_PULSE, NO_PULSE, NO_PULSE, // TP 5
BR01, R1, WLP, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, R2, WLP, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, R2, WLP, NO_PULSE, NO_PULSE, NO_PULSE,
S TEP ( RA, WQ, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 6
S TEP ( RG, WB, TSGN, WP , N O _ P U L SE ) , // T P 7
S TEP ( RB, WA, GP, TP , N O _ P U L SE ) , // T P 8
BR00, R LP, R2, WB, NO_PULSE, NO_PULSE, // TP 9
BR01, R LP, R2, WB, NO_PULSE, NO_PULSE,
BR10, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE,
BR00, RB, WLP, NO_PULSE, NO_PULSE, NO_PULSE, // TP 10
BR01, RB, WLP, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, RC, WA, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, RC, WA, NO_PULSE, NO_PULSE, NO_PULSE,
S TEP ( R1, ST1, WB, NO_PULSE, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _D V 1 = {
S TEP ( R22, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 1
S TEP ( RQ, WG, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 2
S TEP ( RG, WQ, WY, RSB, N O _ P U L SE ), // TP 3
S TEP ( RA, WX, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 4
S TEP ( R LP, TSGN2, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 5
S TEP _ INAC TIVE, / / TP 6
S TEP ( RU, TSGN, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 7
BR00, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, // TP 8
BR01, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, RU, WQ, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, RU, WQ, NO_PULSE, NO_PULSE, NO_PULSE,
BR00, RB, RSB, WG, NO_PULSE, NO_PULSE, // TP 9
BR01, RB, RSB, WG, NO_PULSE, NO_PULSE,
BR10, RB, WG, NO_PULSE, NO_PULSE, NO_PULSE,
BR11, RB, WG, NO_PULSE, NO_PULSE, NO_PULSE,
S TEP ( RG, WB, TSGN, NO_PULSE, N O _ P U L SE ), // TP 10
BR00, ST1, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE, // TP 11
BR01, ST1, NO_PULSE, NO_PULSE, NO_PULSE, NO_PULSE,
BR10, RC, WA, ST2, NO_PULSE, NO_PULSE,
BR11, RB, WA, ST2, NO_PULSE, NO_PULSE,
};

s u bs e qu e n ce S U B _S U 0 = {
S TEP ( RB, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP ( RA, WY, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP _ INAC TIVE, / / TP 4
S TEP _ INAC TIVE, / / TP 5
S TEP _ INAC TIVE, / / TP 6
S TEP ( RG, RSC, WB, WP , N O _ P U L SE ) , // T P 7
S TEP ( RC, WX, GP, TP , N O _ P U L SE ) , // T P 8
S TEP ( RB, WSC, WG, NO_PULSE, N O _ P U L SE ) , // T P 9
S T E P _ I N A C T IV E , // TP 10
S TEP ( RU, WA, WOVC, ST2, WOVI ), // TP 11
};

s u bs e qu e n ce S U B _R U P T1 = {
S TEP ( R24, WY, WS, CI, N O _ P U L SE ) , // T P 1
S TEP _ INAC TIVE, / / TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP _ INAC TIVE, / / TP 4
S TEP _ INAC TIVE, / / TP 5
S TEP _ INAC TIVE, / / TP 6
S TEP _ INAC TIVE, / / TP 7
S TEP _ INAC TIVE, / / TP 8
S TEP ( RZ, WG, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 9
S TEP ( RU, WZ, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 10
S TEP ( ST1, ST2, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _R U P T3 = {
S TEP ( RZ, WS, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP ( RRPA, WZ, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 2
S TEP ( RZ, KRPT, WG, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP _ INAC TIVE, / / TP 4
S TEP _ INAC TIVE, / / TP 5
S TEP _ INAC TIVE, / / TP 6
S TEP _ INAC TIVE, / / TP 7
S TEP _ INAC TIVE, / / TP 8
S TEP ( RB, WSC, WG, NO_PULSE, N O _ P U L SE ) , // T P 9
S T E P _ I N A C T IV E , // TP 10
S TEP ( ST2, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _S T D 2 = {
S TEP ( RZ, WY, WS, CI, N O _ P U L SE ) , // T P 1
S TEP _ INAC TIVE, / / TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP ( RU, WZ, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P
S TEP _ INAC TIVE, / / TP 5
S TEP _ INAC TIVE, / / TP 6
S TEP ( RG, RSC, WB, WP , N O _ P U L SE ) , // T P 7
S TEP ( GP, TP, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 8
S TEP ( RB, WSC, WG, NO_PULSE, N O _ P U L SE ) , // T P 9
S T E P _ I N A C T IV E , // T P 1 0
S TEP ( N IS Q , NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 11
};

s u bs e qu e n ce S U B _P IN C = {
S TEP ( WS, RSCT, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP _ INAC TIVE, / / TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP ( R1, WY, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 4
S TEP _ INAC TIVE, / / TP 5
S TEP ( RG, WX, WP , NO_PULSE, N O _ P U L SE ), // TP 6
S TEP ( TP, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 7
S TEP ( WP, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 8
S TEP ( RU, CLG, WPx, NO_PULSE, N O _ P U L SE ), // TP 9
S TEP ( RU, WG x, WOVR, NO_PULSE, N O _ P U L SE ), // TP 10
S T E P _ I N A C T IV E , // T P 1 1
};

s u bs e qu e n ce S U B _M I N C = {
S TEP ( WS, RSCT, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP _ INAC TIVE, / / TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP ( WY, R1C, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 4
S TEP _ INAC TIVE, / / TP 5
S TEP ( RG, WX, WP , NO_PULSE, N O _ P U L SE ), // TP 6
S TEP ( TP, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 7
S TEP ( WP, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 8
S TEP ( RU, CLG, WPx, NO_PULSE, N O _ P U L SE ), // TP 9
S TEP ( RU, WG x, WOVR, NO_PULSE, N O _ P U L SE ), // TP 10
S T E P _ I N A C T IV E , // T P 1 1
};

s u bs e qu e n ce S U B _S H IN C = {
S TEP ( WS, RSCT, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 1
S TEP _ INAC TIVE, / / TP 2
S TEP ( WG, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 3
S TEP ( WY, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ) , // T P 4
S TEP _ INAC TIVE, / / TP 5
S TEP ( RG, WYx, WX, WP , N O _ P U L SE ), // TP 6
S TEP ( TP, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 7
S TEP ( WP, NO_PULSE, NO_PULSE, NO_PULSE, N O _ P U L SE ), // TP 8
S TEP ( RU, CLG, WPx, NO_PULSE, N O _ P U L SE ), // TP 9
S TEP ( RU, WG x, WOVR, NO_PULSE, N O _ P U L SE ), // TP 10
S T E P _ I N A C T IV E , // T P 1 1
};

char* subse qString [] =


{
"TC0",
"CCS0 ",
"CCS1 ",
"NDX0 ",
"NDX1 ",
"RSM3 ",
"XCH 0",
"CS0",
"TS0",
"AD0",
"MAS K0",
"MP0",
"MP1",
"MP3",
"DV0",
"DV1",
"SU0",
"RUPT1",
"RUPT3",
"STD2 ",
"PINC0",
"MINC0",
"SHINC0 ",
" NO _ S EQ "
};

e num subse q {
TC0 =0,
CCS0 =1,
CCS1 =2,
NDX0 =3,
NDX1 =4,
RSM3 =5,
XCH0 =6,
CS0 =7,
TS0 =8,
AD0 =9,
MASK0 =10,
MP0 =11,
MP1 =12,
MP3 =13,
DV0 =14,
DV1 =15,
SU0 =16,
RUPT1 =17,
RUPT3 =18,
STD2 =19,
P IN C 0 =20,
M I N C0 =21,
S H IN C 0 =22,
NO_SEQ =23
};

enum tpType {
STBY =0,
PWRON =1,

TP 1 =2, / / T I M E P U L S E 1 : s t ar t of m e m o r y cy c le t im e ( M C T )
TP 2 =3,
TP 3 =4,
TP 4 =5,
TP 5 =6,
TP 6 =7, / / EMEM is a va i la b l e in G reg i st er b y TP 6
TP 7 =8, / / FMEM is a va il a b le i n G reg is t er b y TP 7
TP 8 =9,
TP 9 =10,
TP10 =11, / / G r e g is t e r w r it te n t o m e m o r y b e g in n in g a t T P1 0
TP11 =12, / / T I M E P U L S E 1 1: e n d of m e m o r y cy c le t im e ( M C T )
TP12 =13, / / select new s u b s eq u en c e/ sel ect n ew i n st ru c ti o n

SRLSE =14, // step switch release


W AIT =15
};

subseq instructionSu bsequ enceD ecoder(


int S B2_field, int SB1_field, int SQ_field, in t S TB _ fiel d )
{
// Com binationa l logic deco des instru ction an d the stage coun t
/ / t o g et th e in s t ru c t io n s u b se q u e n c e .
s ta tic su b se q de c od e [1 6 ][ 4] = {
{ TC0, RUPT1, STD2, RUPT3 }, // 00
{ CCS0, CCS1, N O _ SE Q , NO_SEQ }, // 01
{ NDX0, NDX1, N O _ SE Q , RSM3 }, // 02
{ XCH0, N O _ SE Q , STD2, NO_SEQ }, // 03

{ N O _ SE Q , N O _ SE Q , N O _ SE Q , NO_SEQ }, // 04
{ N O _ SE Q , N O _ SE Q , N O _ SE Q , NO_SEQ }, // 05
{ N O _ SE Q , N O _ SE Q , N O _ SE Q , NO_SEQ }, // 06
{ N O _ SE Q , N O _ SE Q , N O _ SE Q , NO_SEQ }, // 07
{ N O _ SE Q , N O _ SE Q , N O _ SE Q , NO_SEQ }, // 10

{ MP0, MP1, N O _ SE Q , MP3 } , // 1 1


{ DV0, DV1, STD2, NO_SEQ } , // 1 2
{ SU0, N O _ SE Q , STD2, NO_SEQ } , // 1 3

{ CS0, N O _ SE Q , STD2, NO_SEQ }, // 14


{ TS0, N O _ SE Q , STD2, NO_SEQ }, // 15
{ AD0, N O _ SE Q , STD2, NO_SEQ }, // 16
{ MASK0, N O _ SE Q , STD2, NO_SEQ } // 17

};

if (S B 2 _ f ie ld = = 0 & & S B 1 _ f ie ld = = 1 )
r e tu r n P IN C 0 ;
e ls e if (S B 2 _ f ie ld = = 1 & & S B 1 _ f ie ld = = 0 )
r e tu r n M I N C 0 ;
else
return deco de[S Q_ field][STB_ field];
}

void clearC ontrolPu lses()


{
f or (u n sig n e d i= 0 ; i< M A X P UL S E S; i+ + )
g lb l_ c p [i] = N O _ P U L S E ;
}

v o id a s se r t( c pT y p e * p u ls e )
{
in t j= 0 ;
f or (u n sig n e d i= 0 ; i< M A X P UL S E S & & j< M A X _ IP U LS E S & & p u ls e [j ] ! = NO _ P U LS E ; i+ + )
{
if (g lb l_ c p [i] = = N O _ P U L S E )
{
glbl_cp[i] = pu lse[j];
j + +;
}
}
}

v o id a s se r t( c pT y p e p u ls e )
{
f or (u n sig n e d i= 0 ; i< M A X P UL S E S; i+ + )
{
if (g lb l_ c p [i] = = N O _ P U L S E )
{
g lb l_ c p [i] = p u ls e ;
break;
}
}
}

void get_CP M_A (int CPM_A _addre ss)


{
/ / E P R O M a d dr e ss b it s ( b it 1 is L SB )
// 1: r e g is t e r B R 2
// 2: r e g is t e r B R 1
// 3-6: r e g is t e r SG (4)
// 7,8: r e g is t e r STB (2)
/ / 9 - 1 2 : r e g is t e r SQ (4)
// 13: STB_01
// 14: STB_02
/ /* * * * * * * * * * * * * ** * * * * * * * * * * * * ** * * * * * * * * * * * * ** * * * * * * * * * * * * ** * *
// EP ROM emulator
in t S B 2 _ fie ld = ( C P M _ A _ a d d re s s > > 1 3 ) & 0 x 1 ;
in t S B 1 _ fie ld = ( C P M _ A _ a d d re s s > > 1 2 ) & 0 x 1 ;
int S Q_ field = (C PM _A _a dd res s > > 8 ) & 0 xf;
in t S T B _ fie ld = ( C P M _ A _ a d d re s s > > 6 ) & 0 x 3 ;
int S G_ field = (C PM _A _a dd res s > > 2 ) & 0 xf;
in t B R 1 _ fie ld = ( C P M _ A _ a d d re s s > > 1 ) & 0 x 1 ;
in t B R 2 _ fie ld = ( C P M _ A _ a d d re s s ) & 0x1;

// Dec ode the cu rrent instru ction sub sequ ence (glbl_subse q).
subse q glbl_sub seq = instructionS ubse que nceD ecod er(SB 2_field, SB 1_field, SQ _field, STB _field);

s ta tic su b se q u en c e* s ub sp [] =
{
&SUB_TC0, &SUB_CCS0, &SUB_CCS1, &SUB_NDX0, &SUB_NDX1, &SUB_RSM3,
&SUB_XCH0, &SUB_CS0, &SUB_TS0, &SUB_AD0, &SUB_MASK0, &SUB_MP0,
&SUB_MP1, &SUB_MP3, &SUB_DV0, &SUB_DV1, &SUB_SU0, &SUB_RUPT1,
&SUB_RUPT3, &SUB_STD2, & S U B _P IN C , & S U B _M I N C, &S UB _S HIN C,0
};

// Clear old control pulses.


clearCo ntrolPulses ();

/ / G e t n e w c on t ro l p u ls e s fo r th e c u r re n t in s t ru c t io n s u b se q u e n c e .
if( glb l_ su b se q != N O _S E Q && // TH IS T ES T S O U T OK
S G _ fie ld > = T P 1 &&
S G _ f ie ld < = T P 1 1 )
{
subse que nce* subse qP = subsp [glbl_subse q];
if( subseqP)
{
// index t-2 b ecause TP1= 2, but array is indexed from zero
contro lStep& csref = s ubse qP-> tp[SG _field-2];

brTyp e b = (brTyp e) ((BR 1_field << 1) | BR 2_field);


contro lSubS tep& cssref = csref.sub step[b ];
if (c s sr e f. b r = = N O _ B R )
cssref = csref.sub step[0 ];

c p T y pe * p = c s sr e f. p u ls e ;
asser t(p);
}
}

// Impleme nt these h ere, becau se the instruction seque nce de coder


/ / function is buried in the C PM- A RO M a n d s o , id en t ific a t io n o f
// th e se qu en ces is no t av ailab le ou tside CP M -A. CP M -C ne ed s info
// on these 3 seque nces.
switch ( glbl_subseq)
{
case DV1: assert(SDV1); break;
case MP1: assert(SMP1); break;
c a s e RS M 3 : assert(SRSM3); break;
}

/ /* * * * * * * * * * * * * ** * * * * * * * * * * * * ** * * * * * * * * * * * * ** * * * * * * * * * * * * ** * *

c h ar * c pT y pe S tr in g [] =
{
"NO_PU LSE",

/ / O U T PU T S F RO M S U BS Y S TE M A
"CI" , "CL G" , "CL CT R", "C TR ", "G P", "K RPT ", "N ISQ ", "RA ", "RB ",
"RB 14 ", "RC ", "RG ", "RL P", "R P2" , "RQ ", "RR PA ", "RS B", " RS CT ",
"RU ", "RZ ", "R1 ", "R1 C", " R2 ", "R2 2", "R 24 ", "S T1 ", "S T2 ", "TM Z",
"TO V", " TP" , "TR SM ", "TS GN ", "TS GN 2", "W A", " W ALP ", "W B", " W Gx ",
"W LP", " W OV C", " W OV I", "W OV R", "W P", "W Px" , "W P2" , "W Q" , "W S",
"WX", "W Y", "WYx", "WZ",

// OUTPUTS FROM SUBSYSTEM A; USED AS INPUTS TO SUBSYSTEM B ONLY;


// NOT USED OUTS IDE C PM
//
"RSC", "WS C", "WG",

// OUTPUTS FROM SUBSYSTEM A; USED AS INPUTS TO SUBSYSTEM C ONLY;


// NOT USED OUTS IDE C PM
//
"SDV1 ", "SMP1", "SRSM 3",

/ / E X TE R NA L OU T PU T S F RO M S U BS Y S TE M B
//
"RA 0", "R A1 ", "RA 2", "R A3 ", "RA 4", "R A5 ", "RA 6", "R A7 ", "RA 10 ", "RA 11 ",
"RA 12 ", "RA 13 ", "RA 14 ", "RB K", "W A0 ", "W A1 ", "W A2 ", "W A3 ", "W A1 0",
"WA1 1", "WA12", "WA 13", "WA14 ", "WBK", "WG n", "W20", "W21 ", "W22", "W23",

/ / T H E SE A RE T H E L EF TO V E RS -- TH E Y 'R E PR O B AB L Y U S ED IN S UB S YS T EM C
//
"G EN RS T", "C LINH ", "C LINH 1", "C LST A", " CL ST B", " CL ISQ ", "C LRP ", "IN H" ,
" R P T " , " S B W G " , " S E T S T B ", "W E " , " W P C T R " , " W S Q " , " W S T B " , " R 2 0 0 0 "
};

// f or de bu g p ur po se s o nly
char* printCo ntrolPulses ()
{
static char b uf[MA XPU LSE S*6 ];
strcpy(b uf,"");

f or (u n sig n e d i= 0 ; i< M A X P UL S E S & & g lb l_ c p[ i] != N O _ PU L S E; i+ + )


{
strcat(bu f, cpType String[g lbl_cp[i]]);
strcat(bu f," ");
}
//if(strcmp(bu f,"") == 0) strcat(b uf,"NO NE" );
ret urn bu f;
}

/ / r e tu r n th e E P R O M w o r d c or r e sp o n d in g t o th e p u ls e s
// in glbl_cp.
unsign ed w riteEPR OM (int lowBit)
{
un sig ne d E PR O M w or d = 0x 00 ; // no pu lse s; de fa ult
f or (u n sig n e d i= 0 ; i< M A X P UL S E S & & g lb l_ c p[ i] != N O _ PU L S E; i+ + )
{
int pulse = glbl_cp[i] - lowBit;
if (p u ls e < 0 | | p u ls e > 7 )
c o n tin u e ; // p u ls e is n o t in t h is E P R O M
E P R O M w o r d | = 0 x0 1 < < p u ls e ;
}
// printf("% 02X \n",EP RO Mw ord);
//re turn EPR OMw ord;

// The CPM -A con trol signals are n ega tive logic, so we n eed to
// b it-f lip t he w or d. No sig na l is a 1, an d a n a ss er te d s ign al is
// a 0:
return ((~EP RO Mw ord) & 0xff);
}

co nst unsigne d agcMem Size = 0x3fff+1; / / # of cells i n a 1 6 -b i t a d d res s ra n g e

void writeE PRO M(FILE * fpO bj, int lowBit)


{
// Write a n EP RO M file using M otorola's S -Reco rd form at (s2f).

/ / S o m e p a r a m e t e rs t h at c on t ro l f ile f or m a t . Y o u c a n ch a n g e m a x B y te s
/ / w ithout affecting anything els e. 'a d d res s B ytes ' is d et ermin ed b y
// the ch oose n S-R ecord forma t.
const int m axB ytes = 20; // se t limit on record length
co nst int addressBytes = 3; // 24- bit addre ss ra n g e
c o n s t in t s u m C h e c kB y t e s = 1 ;

const int maxd ata = m axBytes - ad dressByte s - sumC heckB ytes;

int i=0; // current E PRO M ad dress


in t s u m C h e c k = 0 ;
w h i le ( i < a g c M e m S i ze )
{
/ / get dataByteC ount; t h e n u mb er o f b ytes o f E P RO M d a t a p er rec o rd .
int dataByteC ount = maxdata;
if (i + d a t a B yt e C o u n t > = a g c M e m S i ze )
{
da ta B yt eC ou nt = ag cM em S ize - i;
}
/ / w r it e re c o rd h e a d e r ( * * * 3 by t e ad d r e ss a s su m e d * * * )
int totalByteC ount = dataByte Coun t + addre ssBytes + sum Che ckBytes;
fprintf(fpObj, "S 2% 02X % 06X ", totalByte Cou nt, i);
su m Ch eck = t ota lByt eC ou nt & 0x ff;
s u m C h e c k = ( su m C h e c k + ( (i & 0 x ff 0 0 0 0) > > 1 6 ) ) % 2 5 6 ;
s u m C h e c k = ( su m C h e c k + ( (i & 0 x 0 0 ff 0 0 ) > > 8 ) ) % 2 5 6 ;
s u m C h e c k = ( su m C h e c k + ( (i & 0 x 0 0 0 0 ff ) )) % 256;

// write data bytes into record


f or (in t j =0 ; j <d a ta B yt eC o u nt ; j ++ )
{
g e t_ C P M _A ( i+ j ); // ge t C P M -A p u ls e s fo r a d dr e ss i+ j
int data = w riteEPR OM ( l o wB it ) ; / / co mvert p u ls es t o E P RO M fo rma t
fprintf(fpObj, "% 02X ", data);
s u m C h e c k = ( su m C h e c k + d a t a) % 2 5 6 ;

}
/ / t e rm i n a te r e c or d b y a d d in g t h e ch e c k su m a n d a n e w lin e .
fprintf(fpObj, "% 02X \n", (~s um Ch eck) & 0xff);

i += da taBy teCo unt;


}
// write an end-of-file record h ere
i = 0; // use address ze ro for last record
sumCheck = 0x04; // byte count
s u m C h e c k = ( su m C h e c k + ( (i & 0 x ff 0 0 0 0) > > 1 6 ) ) % 2 5 6 ;
s u m C h e c k = ( su m C h e c k + ( (i & 0 x 0 0 ff 0 0 ) > > 8 ) ) % 2 5 6 ;
s u m C h e c k = ( su m C h e c k + ( (i & 0 x 0 0 0 0 ff ) )) % 256;
fprintf(fpObj, "S 804 % 06X % 02X ", i, (~sum Ch eck) & 0xff);

void m ain(int argc, ch ar* a rgv[])


{
F I LE * f pO b j = 0 ;

fpOb j = fope n("C PM 1_8 .hex" , "w");


if(!fpOb j)
{
perro r("fopen failed for object file");
exit(-1);
}
writeEP RO M(fpO bj, 1); // pulses 1 -8
fclose(fpOb j);

fpOb j = fope n("C PM 9_1 6.he x", "w" );


if(!fpOb j)
{
perro r("fopen failed for object file");
exit(-1);
}
w r it e E P R O M ( fp O b j , 9 ) ; / / p u ls e s 9 -1 6
fclose(fpOb j);

fpOb j = fope n("C PM 17_ 24.h ex", "w ");


if(!fpOb j)
{
perro r("fopen failed for object file");
exit(-1);
}
w r it e E P R O M ( fp O b j , 1 7 ) ; / / p u ls e s 1 7- 2 4
fclose(fpOb j);

fpOb j = fope n("C PM 25_ 32.h ex", "w ");


if(!fpOb j)
{
perro r("fopen failed for object file");
exit(-1);
}
w r it e E P R O M ( fp O b j , 2 5 ) ; / / p u ls e s 2 5- 3 2
fclose(fpOb j);

fpOb j = fope n("C PM 33_ 40.h ex", "w ");


if(!fpOb j)
{
perro r("fopen failed for object file");
exit(-1);
}
w r it e E P R O M ( fp O b j , 3 3 ) ; / / p u ls e s 3 3- 4 0
fclose(fpOb j);

fpOb j = fope n("C PM 41_ 48.h ex", "w ");


if(!fpOb j)
{
perro r("fopen failed for object file");
exit(-1);
}
w r it e E P R O M ( fp O b j , 4 1 ) ; / / p u ls e s 4 1- 4 8
fclose(fpOb j);

fpOb j = fope n("C PM 49_ 56.h ex", "w ");


if(!fpOb j)
{
perro r("fopen failed for object file");
exit(-1);
}
w r it e E P R O M ( fp O b j , 4 9 ) ; / / p u ls e s 4 9- 5 6
fclose(fpOb j);

}
Block I
Apollo Guidance Computer (AGC)
How to build one in your basement

Part 3: Processing (PROC) Module

John Pultorak
December, 2004
Abstract
This report describes my successful project to build a working reproduction of the 1964
prototype for the Block I Apollo Guidance Computer. The AGC is the flight computer for the
Ap ollo m oon lan din gs, and is the wo rld’s first in tegra ted c ircuit com pu ter.

I built it in my ba sem ent. It took me 4 yea rs.

If you like, you can build one too. It will take you less time, and yours will be better than
mine.

I docum ented m y project in 9 separate .pdf files:

Part 1 O v ervie w : Introdu ces the p roject.

Part 2 CTL Module: Design and construction of the control module.

Part 3 PROC M odule: Design and construction of the processing (CPU) modu le.

Part 4 MEM Module: Design and construction of the mem ory module.

Part 5 IO Module: Design and construction of the display/keyboard (DSKY) m odule.

Part 6 Assem bler: A cross-a ssem bler for AG C softw are dev elopm ent.

Part 7 C+ + S imu lator: A low-level simulator that runs assemb led AGC code.

Part 8 Flight Software: My translation of portions of the COLOSSUS 249 flight


software.

Part 9 Test & Che ckou t: A suite of test programs in AG C assembly langu age.
Overview
The Processing Module (PROC) has 5 subsystems: PMI, ALU, CRG, INT, CTR

PM I (P roce ssin g M od ule


external Interface)
The PMI interfaces other
p ro cessin g m od ule su bsy stem s
to external AG C m odules.
40-pin IDE connectors interface
to th e other CT L, M EM , an d IO
mo dules. Inputs from those
mod ules are buffered to 1
LSTTL load.

ALU (Arithm etic Log ic Unit)


The A LU co ntain s the 1 6-bit
AD DER to p erfo rm 1's
complement arithmetic and
increment the program counter
(Z register). The ALU also
contains the B and C registers,
and logic to inclusive OR the
contents of the them with the
AD DE R. Th e inc lusive O R oc curs
when the control modu le (CTL)
issues signals to READ the
conte nts of bo th registe rs onto
the READ bus simultaneously.
The ALU transfers data from the
READ bus to the WRITE bus for
regis ter-to -reg ister tra nsfers
and can force the data lines of
the WRITE bus to specific states
to gate variou s consta nts into
the AG C reg isters

CRG (Cen tral Register)


The A GC has fou r 16-b it
“central registers” for general
com pu tatio nal use . The se are
the accumulator (A) for general
computation; the program counter (Z) which contains the address of the next instruction;
the Q register holding the remainder in the DV instruction, and the return address after TC
instructions; and the LP register used to hold the lower produ ct after MP instructions.

INT (Interrupt Priority)


The original AGC had five vectored interrupts. This recreation implem ents three of them:
RUPT1, also called T3RUPT which is used as general-purpose timer by the AGC WAITLIST
software; RUPT3, also called T4RUPT or DSRUPT, which is used to update the DSKY display
at regular intervals; and RUPT4, also called KERUPT, which is triggered by a key press from
the user's keyboard. The AGC responds to each interrupt by temporarily suspending the
current program, executing a short interrupt service routine, and then resuming the
interrupted program.

CTR (Priority Cou nter)


Tw enty m em ory lo catio ns in the o rigin al A GC function ed a s up /do wn cou nters. Th e cou nters
would incremen t (PINC) or decrement (M INC) in response to external inputs. Increment or
decrement was ha ndled by one 12-step subsequen ce of microinstructions inserted between
any two regular instructions. This replica implemen ts 5 of the counters: OVCTR, an overflow
counter increm ented or decrem ented by arithme tic overflow during certain in structions;
TIME2 and TIME1, the AGC real-time clock; TIME3, a general purpose timer incremented by
a 100Hz signal from the SCALER (SCL); and TIME4, a timer used to update the DSKY
display.
The A LU h as a 1 6-bit
parallel adder. The
addend and augend
are supplied by the
X and Y registers.
The sum, called the
U register although
it is not really a
register at a ll, is
gated to the read
bus through the RU
(read U) control
signal. The WX and
W Y con trol signa ls
copy the contents of
the w rite bus in to
the X and Y
registers.

The B register is
loaded from the
write bus with the
W B (w rite B) sign al.
The con tents of B
are output to the
read bus with the RB
(read B) signal. The
inv erted outp ut of B
can be gated to the
read bus with the RC
(read C ) sign al.
The A GC has fou r user-acc essible ce ntral reg isters. The A register is the accum ulato r; Z is
the program counter; Q stores the return address for jumps (TC instruction), and LP stores
the lower product (MP
instruction).

Cen tral register con tents


can be output to the read
bus by asserting the
appropriate read control
pulse (RA, RQ, RZ, or
RLP ). Each register is
also mapped to a
m em ory locatio n, with
register A ma ppe d to
address 0, Q to ad dress
1, Z to 2, and LP to 3.
The R0, R1, R2, and R3
control pulses output
those registers to the
read bus.

The w rite bus co nten ts


can be loaded into a
central register with a
w rite co ntro l p uls e. W A
and W ALP load the A
register; WQ, the Q
register; WZ the Z
register; and WLP and
W ALP the L P reg ister.
The W0, W1, W2, and
W3 control pulses
m apped to m em ory
addresses 0,1,2, and 3
also load those registers.
The in terrupt p riority
subsystem m anages
vectored interrupts. Five
inte rrup ts (0-4) are
imp lemen ted. Each
interrup t is latched by its
own “RP cell” flip-flop.
Sign als from all RP cells
feed into a priority
encoder; a combinational
logic arra y that o utpu ts
the code of the hig hest
priority interrupt in the
RP ce lls. Wh en R PT is
asserted , the priority
code is la tched into
RPCELL. This is decoded
into an add ress wh ich is
the interrupt vector; the
address is written to the
read b us w hen RR PA is
asserted.

After the interrupt code


has b een loa ded into
RPCELL, asserting KRPT
(knock-down R PT) causes
the RP cell for that
interrup t to be reset. Th is
causes the n ext highest
priority interrupt to be
decod ed by the priority
enc ode r.

The INHINT and INHINT1


flip-flops in hibit
interrupts.
The priority counter
logic design is similar
to the interrupt
subsystem. Up (+)
and down (-) count
inpu t signa ls feed into
20 PCELLs, one PCELL
for each counter. The
PCELLs feed into the
priority encoder wh ich
outputs the code of
the h ighe st priority
PCELL having a up or
down input set. The
PCELL code is written
to the PCELL register
wh en W PCT R is
asserted.

The PC ELL m em ory


address, derived from
PCE LL, is written to
read bus when RSCT
is asserted. After the
code is la tched into
the PCE LL reg ister,
the corresponding
PCELL is reset by
asserting WOVR.

The up or down code


for the selected PCELL is written to PSEQ when WPCTR is asserted. This code feeds to the
control lo gic on the CT L m odu le wh ich selec ts the PIN C (in crem ent) or M INC (d ecrem ent)
instruction subsequence to bump the priority counter up or down.

The PIN C and MIN C su bsequ enc es are inse rted b etw een norm al in struc tion sub sequen ces. A
SHINC subsequence implements a bit-shift which is used to load telemetry bits into the AGC
and assemble them into words. SHINC is not implemented in this AGC replica.
PROC Internal Subsystem Interconnections
This diagram show s internal interconnections for the subsystems in the PROC m odule.
PROC Module External Interfaces
The PROC modu le interfaces to the CTL, MEM, and IO m odules through 40-pin IDE ribbon
cables.
J100-PROC: PROC-to-CTL I/F
J100 is a 40-pin IDE cable that connects the PROC modu le to the CTL m odule.

INPU TS (to PR OC):

PIN signal fu ll n am e state definition


1 WA3 WRITE ADDR 3 (74) 0=W rite reg at address 3 (LP)
2 WA2 WRITE ADDR 2 (73) 0=Write reg at address 2 (Z)
3 WA1 WRITE ADDR 1 (72) 0=Write reg at address 1 (Q)
4 WA0 WRITE ADDR 0 (71) 0=Write reg at address 0 (A)
5 RA3 READ ADDR 3 (60) 0=Read reg at address 3 (LP)
6 RA2 READ ADDR 2 (59) 0=Read reg at address 2 (Z)
7 RA1 READ ADDR 1 (58) 0=Read reg at address 1 (Q)
8 RA0 READ ADDR 0 (57) 0=Read reg at address 0 (A)
9 WZ WRITE Z (50) 0=W rite Z
10 WYx WRITE Y NO RESET (49) 0= W rite Y (do n ot reset)
11 WY WRITE Y (48) 0=W rite Y
12 WX WRITE X (47) 0=W rite X
13 WQ WRITE Q (45) 0=W rite Q
14 WOVR WRITE OVF (41) 0=W rite overflow
15 WOVI WRITE OVF RUPT INH (40) 0= W rite overflow RU PT inh ibit
16 WOVC WRITE OVF CNTR (39) 0=W rite overflow counter
17 WLP WRITE LP (38) 0=W rite LP
18 WB WRITE B (36) 0=W rite B
19 WA LP WRITE A/LP (35) 0=W rite A and LP
20 WA WRITE A (34) 0=W rite A

21 F10X F10 SCALER ONESHOT 1=timed out (100.0 Hz)


23 R24 READ 24 (25) 0=Read 24
24 R22 READ 22 (24) 0=Read 22
25 R2 READ 2 (23) 0=Read 2
26 R1C READ 1 COMP (22) 0=R ead 1 comp limented
27 R1 READ 1 (21) 0=Read 1
28 RZ READ Z (20) 0=Read Z
29 RU READ U (19) 0=Read sum
30 RSCT READ CNTR ADDR (18) 0=R ead selected cou nter address
31 RSB READ SIGN (17) 0= Rea d sign bit
32 RRPA READ RUPT ADDR (16) 0=R ead RU PT add ress
33 RQ READ Q (15) 0=Read Q
34 RLP READ LP (13) 0=R ead LP
35 RC READ C (11) 0=Read C
36 RB14 READ BIT 14 (10) 0=Read bit 14
37 RB READ B (9) 0=Read B
38 RA READ A (8) 0=Read A
39 KRPT KNOCK DOW N RUPT (6) 0= Kn ock do wn Rup t priority
40 CI SET CARRY IN (1) 0= Carry in
J101-PROC: PROC-to-CTL I/F
J101 is a 40-pin IDE cable that connects the PROC modu le to the CTL m odule.

INPU TS (to PR OC):

PIN signal fu ll n am e state definition


1 R2000 READ 2000 (101) 0=Read 2000
2 WPCTR WRITE PCTR (98) 0=Write PCTR (latch priority counter seq)
3 RPT READ RUPT (94) 0=Read RUPT opcode
4 INH SET INHINT (93) 0=Set INHINT
5 CLRP CLEAR RPCELL (92) 0=Clear RPCE LL
6 CLINH1 CLEAR INHINT1 (88) 0=Clear INHINT1
7 CLINH CLEAR INHINT (87) 0=Clear INHINT
8 GENRST GENERAL RESET (86) 0=G eneral Reset

19 CLK1 CLOCK1 1.024 MHz AGC clock 1 (normally low)


20 CLK2 CLOCK2 1.024 MHz AGC clock 2 (normally low)

OUT PUTS (from PRO C):

PIN signal fu ll n am e state definition


21 SB_01 SUB SEL 01 SB_01 is LSB; SB_02 is MSB
22 SB_02 SUB SEL 02 00=no counter; 01=PINC; 10=MINC

23 IRQ INT RQST 0=interrupt requested.

25 WB_01 WRITE BUS 01 (lsb)


26 WB_02 WRITE BUS 02
27 WB_03 WRITE BUS 03
28 WB_04 WRITE BUS 04
29 WB_05 WRITE BUS 05
30 WB_06 WRITE BUS 06
31 WB_07 WRITE BUS 07
32 WB_08 WRITE BUS 08
33 WB_09 WRITE BUS 09
34 WB_10 WRITE BUS 10
35 WB_11 WRITE BUS 11
36 WB_12 WRITE BUS 12
37 WB_13 WRITE BUS 13
38 WB_14 WRITE BUS 14
39 WB_15 WRITE BUS 15 US (overflow ) bit
40 WB_16 WRITE BUS 16 SG (sign ) bit
J104-PROC: PROC-to-IO I/F
J104 is a 40-pin IDE cable that connects the PROC modu le to the IO modu le.

INPU TS (to PR OC):

PIN signal fu ll n am e state definition


40 RB_01 READ BUS 01 (lsb)
39 RB_02 READ BUS 02
38 RB_03 READ BUS 03
37 RB_04 READ BUS 04
36 RB_05 READ BUS 05
35 RB_06 READ BUS 06
34 RB_07 READ BUS 07
33 RB_08 READ BUS 08
32 RB_09 READ BUS 09
31 RB_10 READ BUS 10
30 RB_11 READ BUS 11
29 RB_12 READ BUS 12
28 RB_13 READ BUS 13
27 RB_14 READ BUS 14
26 RB_15 READ BUS 15 US (overflow ) bit
25 RB_16 READ BUS 16 SG (sign ) bit

22 BUSY2 READ BUS BUSY 0=OUT register output to read bus


21 BUSY1 READ BUS BUSY 0=INP register output to read bus
20 KB_STR KEY STROBE 1=key pressed strobe; to KEYRU PT. Key
data is valid on the negative edge of
KB_STR. Data is latched until the next
keypress.

OUT PUTS (from PRO C):

PIN signal fu ll n am e state definition


1 WB_01 WRITE BUS 01 (lsb)
2 WB_02 WRITE BUS 02
3 WB_03 WRITE BUS 03
4 WB_04 WRITE BUS 04
5 WB_05 WRITE BUS 05
6 WB_06 WRITE BUS 06
7 WB_07 WRITE BUS 07
8 WB_08 WRITE BUS 08
9 WB_09 WRITE BUS 09
10 WB_10 WRITE BUS 10
11 WB_11 WRITE BUS 11
12 WB_12 WRITE BUS 12
13 WB_13 WRITE BUS 13
14 WB_14 WRITE BUS 14
15 WB_15 WRITE BUS 15 US (overflow ) bit
16 WB_16 WRITE BUS 16 SG (sign ) bit
J105-PROC: PROC-to-MEM I/F
J105 is a 40-pin IDE cable that connects the PROC modu le to the MEM modu le.

INPU TS (to PR OC):

PIN signal fu ll n am e state definition


40 RB_01 READ BUS 01 (lsb)
39 RB_02 READ BUS 02
38 RB_03 READ BUS 03
37 RB_04 READ BUS 04
36 RB_05 READ BUS 05
35 RB_06 READ BUS 06
34 RB_07 READ BUS 07
33 RB_08 READ BUS 08
32 RB_09 READ BUS 09
31 RB_10 READ BUS 10
30 RB_11 READ BUS 11
29 RB_12 READ BUS 12
28 RB_13 READ BUS 13
27 RB_14 READ BUS 14
26 RB_15 READ BUS 15 US (overflow ) bit
25 RB_16 READ BUS 16 SG (sign ) bit

22 BUSY7 READ BUS BUSY 0=BNK register output enabled to read


bus
21 BUSY5 READ BUS BUSY 0=G register output enabled to read bus

OUT PUTS (from PRO C):

PIN signal fu ll n am e state definition


1 WB_01 WRITE BUS 01 (lsb)
2 WB_02 WRITE BUS 02
3 WB_03 WRITE BUS 03
4 WB_04 WRITE BUS 04
5 WB_05 WRITE BUS 05
6 WB_06 WRITE BUS 06
7 WB_07 WRITE BUS 07
8 WB_08 WRITE BUS 08
9 WB_09 WRITE BUS 09
10 WB_10 WRITE BUS 10
11 WB_11 WRITE BUS 11
12 WB_12 WRITE BUS 12
13 WB_13 WRITE BUS 13
14 WB_14 WRITE BUS 14
15 WB_15 WRITE BUS 15 US (overflow ) bit
16 WB_16 WRITE BUS 16 SG (sign ) bit
PROC CONTROL PANEL PUSHBUTTONS

RUPT1 Set the RUPT1 flip-flop (FF). Simulates a TIME3 overflow. Triggers a T3RUPT.

RUPT3 Set the RUPT3 flip-flop (FF). Simulates a TIME4 overflow. Triggers a T4RUPT
(DSR UPT).

RUPT4 Set the RUPT4 flip-flop (FF). Simulates a DSKY keypress. Triggers a KEYRUPT.

TIME1 Set the TIME1 flip-flop (FF). Increments the low-order word of the AGC
real-time clock.

TIME2 Set the TIME2 flip-flop (FF). Increments the high-order word of the AGC
real-time-clock.

TIME3 Set the T IME 3 flip -flop (FF). Increm ents the gen eral p urp ose tim er.

TIME4 Set the T IME 4 flip -flop (FF). Increm ents the disp lay u pd ate tim er.
PROC CONTROL PANEL CONNECTIONS

PIN signal state definition


1 RUPT1 GND= set RUPT1 FF
2 RUPT3 GND= set RUPT3 FF
3 RUPT4 GND= set RUPT4 FF

4 TIME1 GND= set TIME1 FF


5 TIME2 GND= set TIME2 FF
6 TIME3 GND= set TIME3 FF
7 TIME4 GND= set TIME4 FF

8 GND
PROC INDICATORS

The PR OC m odu le ha s a panel of in dica tor lam ps (L ED s) to sh ow the state o f PRO C reg isters
and critical logic signals.

These indicator lamps


show the current state of
all registers and some
additional, important
logic signals produced by
the PROC module. AGC
num bers are represented
in octal, so all register
lamps are in groups of
three. At the time the
photo was taken the
AGC was running
the COLOSSUS 249
flight software load,
executing Verb 16, Noun
36 : a m onitor verb
which displays the AGC
real time clock.
ALU (Arithmetic Logic Unit)
My earliest architectural representation of the ALU logic is shown below:

Th e A LU con tains th e 16-bit ADDER (co lored ora ng e in the diagram ) w hich perform s 1's
com plem ent arith m etic, and increm ents th e prog ram coun ter (Z regis ter). Each orang e box is
a 4-bit parallel adder; collectively, they add 16 bits. The ADDER uses the X, Y, and U
registers:

X: the 1 6-bit e xtens ion reg ister (2 8- bit registe rs in yellow ) that h olds on e of two in puts
to the ADDER.

Y: the 16-bit extension register (also in yellow) that holds the other input to the ADDER.

U: the AD DER output (the 1 's complem ent sum of the contents of registers X and Y).
Outputs to the bus labeled “B” on the diagram.
The ALU also contains the B and C registers:

B: a gen eral-pu rpose b uffer register (sh own as 2 8 -bit registe rs in yellow ), also use d to
pre-fetch the next instruction. At the start of the next instruction sequence, the upper
bits of B (containing the next op code) are copied to the SQ register (in CTL), and the
lower bits (the address) are copied to the S register in (MEM). Output to the bus
labeled “A” on the diagram.

C: not a separate register, but the 1's complement of B.

The ALU contains logic (using


74L S18 1 AL U ch ips, sho wn in
green) to do any of the following:
select the B register; select the
complement of the B register (the
“C” re giste r); sele ct the U reg ister;
select the C register OR’ed with U,
or select log ical zero. Th ose log ic
functions, needed for AGC
operation, are shown in the upper
right corner of the diagram. The
outpu ts of th e 74 LS1 81 selec tor are
gated through a buffer to the read
bus.

The original AGC could inclusive OR


the outputs of any combination of
registers onto the bus, but the
control m odu le only used this
feature for the B/C and U registers.
One of the uses involved the MASK
instruction, which is a logical AND:
DeM organ 's theorem wa s used to
im plem ent the e qu ivalent of a
logical AND by inverting operands
through the C register, performing a
logical OR through the bus, and
then invertin g the res ult.

The 74 LS1 81 logic fun ction s are


gated to the read bus through the
data selector logic, shown in the adjacent diagram.
The ALU also transfers data from the
READ bus to the WRITE bus for
register-to- register da ta m oves: D ata
is output from the source register
onto the READ bu s, then transferred
from the READ bus to the WRITE bus
through the ALU, and finally loaded
from the WRITE bus into the
destina tion regis ter.

The logic that translates the READ


bus to the W RITE bus ca n also force
the da ta lines o f the W RITE b us to
specific states to gate various
arithm etic constants onto the bus.
The accompanying diagram shows
the the control sig nal on th e left
(RB 14, R 1, etc) an d to the right is
the bit pattern that’s OR’ed onto the
bus when the signal is asserted. The
defau lt state of the rea d bu s is
logical zero, so if no other read signal
is ass erted , the n um ber th at ap pea rs
on th e bu s is the con stant;
otherwise, it’s the constant inclusive
OR’ed with the contents of the READ
bus.

The ALU contains REA D bus control


logic. Registers in the MEM modu le,
the central registers in the PROC
m odu le, and the A LU a ll interface to
the R EAD bus th roug h tri-state
buffers. Th ese bu ffers are norm ally in
the high-impedance state, but the
control module (CTL) issues READ
control sig nals to outp ut the conten ts
of specific registers to the READ bus
at certain times. Only one register
should be gated onto the READ bus
at any given time.

When no READ control signals are asserted, the ALU gates its output onto the READ bus by
default. When the control module gates a register onto the READ bus, a BUSY signal is sent
to the ALU mod ule, which causes the ALU to inhibit its output. Because of propagation
delays, the ALU might continue to output to the READ bus for a brief time while another
register is also gated to the bus. To prevent this, all output to the read bus is inhibited during
CLK1. This gives the control signals, which transition on the leading edge of CLK1, enough
setup time to resolve th e conflict.
ALU INPUTS:

I/F signal fu ll n am e state definition


CLK:
CLK1 CLOCK 1 1=read bus setup; inhibit read bus out
CLK2 CLOCK 2 data transfer occurs on falling edge

CPM:
RB READ B 0=output B register to write bus
RC READ C 0=output comp of reg B (C) to write bus
RU READ G 0=output U register to write bus

R1 READ OCTAL 1 0=incl OR 000001 w/write bus


R1C REA D OC TAL -1 0=incl OR 177776 w/write bus
R2 READ OCTAL 2 0=incl OR 000002 w/write bus
R22 READ OCTAL 22 0=incl OR 000022 w/write bus
R24 READ OCTAL 24 0=incl OR 000024 w/write bus
R2000 READ OCTAL 2000 0=incl OR 002000 w/write bus
RB14 READ BIT14 0=incl OR 020000 w/write bus
RSB RE AD SIG N B IT 0=incl OR 100000 w/write bus

WB WRITE B 0= write in to B from write


bus
CI WRITE CI 0=set carry register to 1
WY WRITE Y 0=w rite Y
WX WRITE X 0= write in to X from write
bus
WYX WRITE Y 0= write in to Y from write
bus

RBUS:
RB_01 READ BUS 01
...
RB_14 READ BUS 14
RB_15 READ BUS 15 US (overflow) bit for read
bus
RB_16 READ BUS 16 SG (sign) bit for read bus

INP: BUSY1 READ BUS BUSY 0=valid data from INP on


read bus
OUT: BUSY2 READ BUS BUSY 0=valid data from OU T on
read bus
CTR: BUSY3 READ BUS BUSY 0=valid data from CTR on
read bus
INT: BUSY4 READ BUS BUSY 0=valid data from INT on read bus
MBF: BUSY5 READ BUS BUSY 0=valid data from MBF on read bus
CRG: BUSY6 READ BUS BUSY 0=valid data from CRG on read bus
ADR: BUSY7 READ BUS BUSY 0=valid data from ADR on read bus

MBF OUTPUTS:

I/F signal fu ll n am e state definition


WBUS:
WB_01 WRITE BUS 01
...
WB_14 WRITE BUS 14
WB_15 WRITE BUS 15 US (overflow) bit for write bus
WB_16 WRITE BUS 16 SG (sign) bit for write bus
CRG (Central Register)
The AGC has four 16-bit registers for general computational use. These are called the
"central registers":

A: the 16-bit accumulator, used


for general computation.

Z: the 16-bit p rogra m cou nter,


which contains the address of
the next instruction to be
executed.

Q: the 1 6-bit reg ister used to


hold the remainder in the DV
instruction, and to hold the
return address after TC
instructions.

LP: the 1 6-bit reg ister used to


hold the lower product after
MP instruction s.

Register A and LP shifters

In addition to “normal” control pulses that write each line of the write bus into the
corresponding bit of the registers, the A and LP registers have special write control pulses
that shift bits:

The WALP control pulse bit-shifts into the A and LP register. The table below shows how the
shifter works. The row of 16 comma-separated entries represent bits in the register. The
leftm ost positio n is the register M SB , rightm ost positio n is the LSB . The en try show s the b it
of the WRITE bus tha t’s m apped onto that regis ter bit by th e shifter.

For the WALP pulse, bit 1 of the WRITE bus (B1) is written into bit 14 of the LP register. “BX”
me ans leave tha t bit of the register alone (don’t chang e it).

The same W ALP pulse causes bit 2 of the WR ITE bus (B2) to be written into the lowest bit of
the A register, bit 3 of the WRITE bus (B3) to be written into the next bit, and so forth. “US”
(uncorrected sign) is the overflow bit (bit 15) of the WRITE bus. “SG” is the 1's complement
sign (bit 16) from the W RITE bus.

WALP for register LP:


BX, BX, B1, BX, BX, BX, BX, BX, BX, BX, BX, BX, BX, BX, BX, BX

WALP for register A:


SG, SG, US, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2
Similarly, the WLP control pulse bit-shifts the WRITE bus into the LP register as follows (D0
on bit 14 of the LP reg ister mean s force the bit to zero):

WLP
B1, B1, D0, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2

The logic design for handling bit 14 of the LP


register, which takes control inputs from
W ALP , WLP , and W A3 is sh own here. W A3 is
identical to WLP.

Depending upon the WALP, WLP, or WA3


inputs, bit 14 of LP will either be set to B1 of
the WRITE bus, or forced to zero.
CRG INPUTS:

I/F signal fu ll n am e state definition


CLK:
CLK1 CLOCK 1 1=read bus setup; inhibit read bus output
CLK2 CLOCK 2 data transfer occurs on falling edge

CPM:
RA READ A 0=output A to read bus
RA0 READ A 0=output A to read bus

RQ READ Q 0=output Q to read bus


RA1 READ Q 0=output Q to read bus

RZ READ Z 0=output Z to read bus


RA2 READ Z 0=output Zto read bus

RLP READ LP 0=output LPto read bus


RA3 READ LP 0=output LP to read bus

WA WRITE A 0=load A from write bus


WA0 WRITE A 0=load A from write bus

WQ WRITE Q 0=load Q from write bus


WA1 WRITE Q 0=load Q from write bus

WZ WRITE Z 0=load Z from write bus


WA2 WRITE Z 0=load Z from write bus

WA LP WR ITE A,LP 0= load A ,LP from write


bus
WLP WR ITE LP 0=load LP from write bus

GENRST GENERAL RESET 0=G eneral Reset

WBUS:
WB_01 WRITE BUS 01
...
WB_14 WRITE BUS 14
WB_15 WRITE BUS 15 US (overflow) bit for write bus
WB_16 WRITE BUS 16 SG (sign) bit for write bus

MBF OUTPUTS:

I/F signal fu ll n am e state definition


RBUS:
RB_01 READ BUS 01
...
RB_14 READ BUS 14
RB_15 READ BUS 15 US (overflow) bit for read/write bus
RB_16 READ BUS 16 SG (sign) bit for read/write bus

BUSY READ BUS BUSY 0=output enabled to read bus


INT (Interrupt Priority)
The original AGC had five vectored interrupts. This recreation implements the following 3:

RUPT1 Also ca lled T3 RU PT be caus e it's triggered by ove rflow of the TIME 3 priority
cou nter.

RUPT3 Also ca lled T4 RU PT be caus e it's triggered by ove rflow of the TIME 4 priority
counter. Because the interrupt is used by software to update the DSKY display
at regular intervals, it's sometimes called DSRUPT.

RUPT4 Triggered by a key press from the user's keyboard. Also called KEYRUPT.

The AGC software responds to each interrupt by temporarily suspending the current program,
executing a short interrupt service routine, and then resuming the interrupted program.
INT INPUTS:

I/F signal fu ll n am e state definition


CLK:
CLK1 CLOCK 1 1=read bus setup; inhibit read bus out
CLK2 CLOCK 2 data transfer occurs on falling edge

CPM:
GENRST GENERAL RESET 0= reset IN T reg isters

RRPA READ RUPT ADDRESS 0=o utput R PCELL ad dress


(2004,2010,2014,2020,2024) to read bus

RPT READ RUPT OPCODE 0=load RU PT opcode into RPCELL register


KRPT KN OC K D OW N R UP T PRIO 0=reset RUPT latch currently selected by
RPCELL register
CLRP CLEAR R PCELL 0=clear RPCELL register

WOVI WRITE OVF RUPT INH 0= test overflow ; if


overflow, inhibit interrupt
(set INHINT1)
CLINH1 CLEAR INHINT1 0=clear INHINT1 register

INH SET INHINT 0=set INHINT register


CLINH CLEAR INHINT 0=clear INHINT register

WBUS:
WB_15 WRITE BUS 15 US (overflow ) bit for write
bus
WB_16 WRITE BUS 16 SG (sign) bit for write bus

CTR/KBD:
RUPT1 INTERRUPT 1 0=trigger interrupt 1 (2004 octal; TIME3
overflow)
RUPT3 INTERRUPT 3 0=trigger interrupt 3 (2014 octal; TIME4
overflow)
RUPT4 INTERRUPT 4 0=trigger interrupt 4 (negative edge)
(2020 octal; keyboard activity)

Note: interrupt cells above RUPT4 not implemented.

INT OUTPUTS:

I/F signal fu ll n am e state definition


SEQ:

IRQ INT RQST 0=interrupt requested. Active if allof the


following are true:
a) one or more RUPT FF's are set
b) interrupt is not currently being serviced
c) interrupts are not inhibited

RBUS:
RB_01 READ BUS 01
...
RB_14 READ BUS 14
RB_15 READ BUS 15 US (overflow) bit for read/write bus
RB_16 READ BUS 16 SG (sign) bit for read/write bus

BUSY READ BUS BUSY 0=output enabled to read bus


CTR (Priority Counter)
The Blo ck I AG C h ad 20 m em ory lo catio ns d edic ated as u p/d ow n co un ters (in volu nta ry
counters). The counters would increment or decrement in response to external plus or minus
logic signals. Increment (PINC) or decrement (M INC) was hand led by one subsequence of
microinstructions inserted between any two regular instruction subsequences when counter
inputs occurred.

This replica implem ents 5 counters used by the AGC operating system and user interface:

Counter Addr Description


OVCTR 34 An overflow counter incremented (PINC) or decremented (MINC ) when
overflow cond itions occur during certain instructions.

TIME2 35 The high-order bits of the AGC clock; incremented (PINC) by overflow
of TIME1.

TIME1 36 The low-order bits of the AGC clock; incremented (PINC) by a 100Hz
signal from the SCA LER (SC L) in the control m odule (C TL).

TIME3 37 A general purpose timer incremented by a 100Hz signal from the


SCA LER (SC L) in the control m odule (C TL).

TIME4 40 A special purpose timer used for software update of the DSKY display.
Incremented by a 100Hz signal from the SC ALER (SCL) in the control
mo dule (CT L).

INT/CTR interface

This chart shows how counter


overflows are handled. F10 (from
the scaler) increments TIME1,
TIM E3, a nd TIM E4.

A positive overflow of TIME1


causes an increment of TIME2.

Positive overflow of TIME3


triggers a T3R UPT interrup t.

Positive overflow of TIME4


triggers a DSRUPT (T4RUPT)
interrup t.

The addresses of TIME1 and


TIM E2 are reve rsed for Block II.
This chart shows the Block I
order, but my replica used the
Block II order for com patib ility
with the COLOSSUS flight
software.
CTR Design Problem

During unit testing, I uncovered a bug in my implementation of CTR: The plus inputs set the
P-cell which eventually triggers a PINC subsequence and increments the counter. Minus
inp uts set the M -cell, w hich trigg ers a M INC sub sequen ce an d de crem ents the cou nter.
Nea r-sim ultan eous plus a nd m inus inpu ts shou ld can cel out, p rodu cing no ch ang e in cou nt.

As a co nseq uen ce, the cou nter log ic wa s desig ned so that, if the P- an d M -cells are b oth set,
no counter sequence (PINC or MINC) is selected. The problem is, the P- and M-cells are reset
by WOVR, which only occurs in PINC or MINC. So, if both cells are set--and therefore, no
subsequence is selected--WOVR is never issued, the cells never reset, and all counting
activ ity is d isab led fo r that cou nter.

Here are the options I developed fixing the design:

a) Create a new subsequence similar to PINC or MINC that issues WOVR, but does not
chan ge the coun ter. Select th is new subs equ ence w hen the P- a nd M -cells are sim ultan eous ly
set.

b) Allow the P- and M-cells to trigger PINC and MINC sequen ces, resulting in a net change of
zero to the cou nter.

c) Leave the design as-is, because the M-cell is only used for OVCTR in my implementation,
and th erefore , the p roblem can nev er occur.

I choose option C to avoid redesign and re-unit-testing of CTR.


CTR INPUTS:

I/F signal fu ll n am e state definition


CLK:
CLK1 CLOCK 1 1=read bus setup; inhibit read bus out
CLK2 CLOCK 2 data transfer occurs on falling edge

CPM:
GENRST GENERAL RESET 0= reset C TR regis ters

WPCTR WRITE PSEQ 0= write seq uen ce into


PSEQ
RSCT READ PCELL ADDRESS 0=o utput PC ELL address
(034-043) to read bus

WOVC WRITE OVRFLOW CNTR 0=test overflow and


inc/dec OVCTR
WOVR WRITE OVERFLOW 0=clear selected PCELL
and han dle
counter overflow (if any)

WBUS:
WB_15 WRITE BUS 15 US (overflow) bit for
write bus
WB_16 WRITE BUS 16 SG (sign ) bit for write
bus

EXTERN AL:
P3P P3 CELL + COUNT 0= cou nt u p P3 cou nter
(036 octal; TIME1)

P4P P4 CELL + COUNT 0=coun t up P4 counter


(037 octal; TIME3)

P5P P5 CELL + COUNT 0=coun t up P5 counter


(040 octal, TIME4)

Note: priority cells 6-20 not implemented.

CTR OUTPUTS:

I/F signal fu ll n am e state definition


C OU N TE R O VE RF LO W :

CPO_04 P4 + OVERFLOW 0=P4 cell pos ovf (during WOVR)


note: TIME3 + overflow; con nect
to INT subsystem to trigger T3RUPT
interrup t.

CPO_05 P5 + OVERFLOW 0=P5 cell pos ovf (during WOVR)


note: TIME4 + overflow; con nect
to INT subsystem to trigger T4RUPT
(DS RU PT) in terrupt.

SEQ:
SB_01 SUB SEL 01 SB_01 is LSB; SB_02 is MSB
SB_02 SUB SEL 02 00=no counter; 01=PINC; 10=MINC

RBUS:
RB_01 READ BUS 01
...
RB_14 READ BUS 14
RB_15 READ BUS 15 US (overflow) bit for read/write bus
RB_16 READ BUS 16 SG (sign) bit for read/write bus

BUSY READ BUS BUSY 0=output enabled to read bus


Fabrication
The P RO C m odu le is (4) 13"x5 " circuit bo ards, an d 1 co ntrol pa nel.

Module Rack

The m odule framework is designed


to resemble a relay rack, but scaled
to fit the circuit board dim ensions.
It is constructed out of 1"x2" pine
and spray-painted semi-gloss gray.

Circuit boards are mounted to the


rack by 2 phillips screws at either
end. Nylon spacers (1/4") are used
as sta nd offs to h old the b oard
edges above the rack. The boards
are mounted so the chips are in the
back an d th e pin s are w iring are
visible from the front.

Power is distributed by 2 heavy


aluminu m bu s bars mounted
vertically, one per side, on the back
of the m odu le. M ach ine screw s are
mounted through the bus bars at
evenly-spaced intervals to provide
connection points for the boards.

Solid copper wire (24 gauge) connects the boards to the bus bars. Ring terminals are used
on the bus bar side of the connection. On the circuit board size, the wires are soldered
directly to the supply rails.

Materials were purchased from Home Depot, ACE Hardware, and Radio Shack.

Circuit Boards

The circuit boards are 13"x5" general purpose prototyping boards, epoxy glass with double-
side plated throu gh pa ds on 0.1" cen ters (JAMEC O 21 477C L).

ICs are mounted in level 3 machine tooled wire-wrap sockets: 8, 14,


16, 20, 24, and 28 pin (JAMEC O). Each socket has the pin-out labeled
wit h a w ire-w rap sock et ID m arke r, wh ich slips o nto the socke t befo re
wra pping (JAM ECO). T he p art num ber is writt en o nto the ID m arke r.

Sockets are arranged in 4 horizontal rows on each board, with about


10 sockets per row.

Power is distributed on the back-side of each board by bare 24-gauge


solid copper wire supply rails soldered at equal intervals to Klipwrap terminals: 3-prong
terminals with a square tail for wire-wrapping (JAMECO 341 63CL). A +5V rail runs above
each row of sockets and a ground rail runs below. Each rail connects directly to the aluminum
m odu le po we r bus using a ring tail co nn ector.
On the p in sid e of th e bo ard, all con nec tion s are m ade w ith 3 0 A W G K yna r wire -w rap wire
(JAMECO ). Red wire is used for direct connections to the +5V supp ly rail. Black wire is used
for direct connections to ground. White wire is used for everything else.

Power connections from the supply rails to each ICs are double-wrapped. Bypassing
capa citors (.1 uf d isc ) are sold ered ac ross the su pply rails at the Klipw rap term inals; abou t 1
capacitor for every 2 IC packag es.

All connections were stripped and hand-wrapped using a Radio Shack hand-wrap tool. As
each connection was made, the corresponding line on the schematic was marked with a
colored h igh ligh ter.

DIP resistor networks (JAMECO) plugged into 20-pin wire-wrap sockets were used as current
limiting resistors for the panel ind icators.
PROC Printed Circuit Board (PCB) A

The A board conta ins disp lay driv ers for the B b oard (le ft side), bu ffers for the interfaces to
external modules (bottom right), and priority counter logic (upper right). Sockets for 3 IDE
interface cables to external modules are visible at the bottom.
PROC Printed Circuit Board (PCB) B

The B board contains the display indicators, their current limiting resistor networks, and the
open collector drivers. The display panel is a sheet of white styrene plastic. A push pin was
used to make holes through the plastic and the LEDs were inserted in rows. The panel was
hand -lettered w ith a n in delible m arke r.
PROC Printed Circuit Board (PCB) C

The C board contains the the logic for the interrupt (INT) subsystem (upper half of the
board), and the central registers (CRG; low er half of the board).
PROC Printed Circuit Board (PCB) D

The D board contains the ALU logic. The large 74181 ALU chips are at the bottom right. The
four chips that form the ADDER are in the bottom ha lf of the board, slightly to the right of
the middle.
Parts (ICs)
IC’s, sockets, PCB’s, resistors, capacitors, wire-wrap wire were purchased from JAMECO. IDE
wire-wrap sockets were from DigiKey. W ire ties, wire-wrap tools, and copper wire were from
Ra dio Sh ack. IDE rib bon cab les w ere purch ased from an o nlin e com pu ter su pp lier.

74LS00 (9) U60,U66,U57,U53,U51,U44,U46,U47,U42


74LS02 (4) U70,U6,U49,U2
74LS04 (11) U54,U45,U37,U68,U7,U36,U69,U71,U59,U48,U72
74LS06 (26) U2 8,U 29 ,U3 0,U 31 ,U2 7,U 26 ,U2 3,U 24 ,U2 5,U 22 ,U2 1,U 20 ,U1 8,U 19 ,U1 7,U
16,U15,U12,U13,U14,U11,U10,U35,U34,U33,U32
74LS08 (4) U63,U61,U56,U52
74LS10 (2) U50,U43
74LS20 (2) U67,U73
74LS21 (1) U1
74LS27 (1) U9
74LS32 (5) U4,U41,U40,U39,U38
74LS83 (4) U134,U135,U136,U137
74LS86 (1) U5
74LS112 (7) U65,U64,U62,U58,U55,U8,U3
74LS138 (4) U84,U85,U86,U94
74LS148 (2) U93,U97
74LS151 (2) U87,U88
74LS181 (4) U130,U131,U132,U133
74LS244 (33) U74,U75,U76,U77,U78,U79,U80,U81,U82,U83,U89,U90,U95,U96,U101,
U102,U104,U105,U108,U111,U112,U115,U116,U117,U118,U119,U120,
U121,U122,U124,U125,U128,U129
74LS273 (19) U91,U92,U98,U99,U100,U103,U106,U107,U109,U110,U113,U114,U123
,U126,U127,U138,U139,U140,U141

Power Budget
qty mA (ea) m A (tot)
74LS00 9 2.4 21 .6
74LS02 4 2.4 9.6
74LS04 11 3.6 39 .6
74LS06 26 3.6 93 .6
74LS08 4 4.4 17 .6
74LS10 2 1.8 3.6
74LS20 2 1.2 2.4
74LS21 1 2.2 2.2
74LS27 1 3.4 3.4
74LS32 5 4.9 24 .5
74LS83 4 22 .0 88 .0
74LS86 1 6.1 6.1
74LS112 7 4.0 28 .0
74LS138 4 6.3 25 .2
74LS148 2 12 .0 24 .0
74LS151 2 6.0 12 .0
74LS181 4 21 .0 84 .0
74LS244 33 32 .0 10 56 .0
74LS273 19 17 .0 32 3.0
LED 153 20 .0 30 60 .0
-----
4.9 Amps total
1.9 Am ps (excludin g LEDs)
Block I
Apollo Guidance Computer (AGC)
How to build one in your basement

Part 4: Memory (MEM) Module

John Pultorak
December, 2004
Abstract
This report describes my successful project to build a working reproduction of the 1964
prototype for the Block I Apollo Guidance Computer. The AGC is the flight computer for the
Ap ollo m oon lan din gs, and is the wo rld’s first in tegra ted c ircuit com pu ter.

I built it in my ba sem ent. It took me 4 yea rs.

If you like, you can build one too. It will take you less time, and yours will be better than
mine.

I docum ented m y project in 9 separate .pdf files:

Part 1 O v ervie w : Introdu ces the p roject.

Part 2 CTL Module: Design and construction of the control module.

Part 3 PROC M odule: Design and construction of the processing (CPU) modu le.

Part 4 MEM Module: Design and construction of the mem ory module.

Part 5 IO Module: Design and construction of the display/keyboard (DSKY) m odule.

Part 6 Assem bler: A cross-a ssem bler for AG C softw are dev elopm ent.

Part 7 C+ + S imu lator: A low-level simulator that runs assemb led AGC code.

Part 8 Flight Software: My translation of portions of the COLOSSUS 249 flight


software.

Part 9 Test & Che ckou t: A suite of test programs in AG C assembly langu age.
Overview
The Memory Module (MEM) has 5 subsystems: MMI, ADR, EMM/FMM, MBF, and PAR

MM I (Mem ory Module external


Interface)
The M MI in terfaces oth er m em ory
module subsystems (ADR, EMM/FMM,
MB F, and PAR ; describ ed be low) to
external AGC modules. 40-pin IDE
connectors interface to the PROC and
CTL m odules. Inputs from those
modules are buffered to 1 LSTTL load.
A 1 -pin con nector int erfaces to th e IO
mod ule.

EMM /FMM (Eraseable/Fixed


Mem ory)
The EMM/FMM is the AGC m emory.
AGC mem ory is 16-bit word s,
organized into 1024 word banks. The
lowe st ban k (ba nk 0 ) is erasab le
mem ory (EMM), originally implem ented
as core, but implemented here as RAM.
All banks above bank 0 are fixed
mem ory (originally implemented as
rope core, but implemented here as
EPROM). The Block I AGC initially had
12K word s of fixed m em ory. This
implementation has 15K.

The MSB (bit 16) in memory is an odd


parity b it. The low er 15 b its hold
instructions or data.

MB F (M em ory Bu ffer Reg ister)


The MBF has the 16-bit mem ory buffer
register which holds 16-bit data words m oving to and from mem ory. This is also called the G
regis ter.

The AG C tra nsfers da ta to a nd from m em ory th roug h th e G re giste r during the "m em ory
cycle." The memory cycle takes 12 timing pulses (11.72 microseconds). During AGC
operation, data w ords cycle continuo usly from m em ory to the G register and the n back
again to memory.

There are four locations in eraseable memory, at addresses 20-23 (octal), dubbed "editing
location s" beca use w hate ver wa s stored th ere wo uld em erge sh ifted or rotated by on e bit
position. This shifting is performed in the MBF.

PAR (Parity Ge nerate and T est)


The PA R g ene rates and tests th e m em ory p arity bit. Th e low er 15 bits o f each m em ory w ord
hold AG C instru ctions o r data. Ea ch w ord is prote cted b y a 16 th "od d pa rity" bit. This bit is
set to 1 or 0 by a parity generator circuit so a count of the 1's in each memory word always
prod uce s an odd nu m ber. A parity ch ecking circuit tests the p arity bit d urin g ea ch m em ory
cycle; if the bit doesn't match the expected value, the memory word is assumed to be
corrupted and a PARITY ALARM panel light illum inates on the IO module.

ADR (M emo ry Address)


The ADR constructs the AGC m emory address. The address is formed from the S register
which holds the lower 12-bits that directly address the lowest 4K of memory, and the BANK
register, w hich selects hig her m em ory ba nks w hen add ressing is in the fixe d-sw itchab le
mod e.
The AGC transfers data to and from memory through the G register in a process called the
"m em ory cycle." Th e m em ory cycle ta kes 12 timin g pu lses (11 .72 m icrosecon ds). Th e cycle
beg ins at tim ing p ulse 1 (TP1 ) wh en th e AG C load s the m em ory ad dress to b e fetched into
the S register in ADR. Memory hardware retrieves the data word from memory at the
address specified by the S register. Words from erasable memory are deposited into the G
register by timing pulse 6 (TP6); words from fixed memory are deposited by timing pulse 7.
The retrieved
m em ory w ord is
then available in the
G register for AGC
access during timing
pulses 7 through 10.
After timing p ulse
10, data in the G
register is written
back to memory.

The m em ory address


is formed from the
12-bit S register and
the 4-bit BANK
register. M em ory in
the lowest 4 1K
ban ks is directly
addressed by the S
register. The higher
1K ban ks (5 -12 ) are
address through the
bank register as
described in the ADR
subsystem section of
this do cum ent.

The h igh- order bit in


mem ory (bit 15) is an
odd parity bit. If the
me mo ry word is a
data word, the 14 th bit
is the sig n, an d bits
13 through 1 hold the
magnitude. The
num ber
rep resen tation is 1's
com plem ent.

The first half of the


mem ory cycle copies
data from m em ory to
the G register, The
sign bit in m em ory
(bit 1 4) is copied to bits 1 5 an d 14 of th e G re giste r. Bits 1 3 th roug h 1 in m em ory a re
copied to bits 13 th rough 1 in G. This is performed in the MB F and EMM /FMM subsystem s.
The p arity bit (b it 16 in me m ory) is read by the PAR subs ystem and tested a gain st parity
gen erate d on the m em ory w ord copied in to G. If the pa rity bits fail to m atch , a pa rity alarm
is generated.

The 14 th bit in the G register (and the central registers in the AGC) is called the Uncorrected
Sign (U S). This extra sign bit is used as an overflow in dication in m ulti-word op erations.
Normally, the Sign and Un corrected sign should agree. When overflow or underflow
conditions occur, both signs will disagree, and are reconciled by software in an operation at
the end of a lon g string of mu lti-word com putations.

At the end of the memory cycle, the G register is copied back to memory. The sign bit in G
(bit 15 ) is written to the sig n bit (b it 14) in me m ory. Bits 13 throug h 1 in G are w ritten to
bits 13 through 1 in memory. A new odd parity bit is computed in the PAR subsystem and
written to the 16 th bit in mem ory.
T hese d ia gra m s
show the timing
of the m em ory
cycle. The top
chart is a little
m ore
conceptual; the
bottom cha rt
show s the clock
cycles and
control pulses
directly
associa ted w ith
reading
erasea ble
m em ory
(SBEW G) or
fixed m em ory
(SBFWG) to the
G register (the
‘WG ’ part
m ean s ‘write to
G ’) . T he WE
pulse w rites G
back to memory.

CLK1 steps the


sequencer in the
CTL module that
enables the
control signals.
The signals have
tim e to settle
between CLK1
and CLK 2. Da ta
tran sfer occ urs
on CLK2.
This is a functional
diagram of the ADR,
EMM/FMM, and MBF
subsystems in the
MEM modu le.

The diagram is m ine,


but th e style is
borrowed from
original AGC
documentation:
con trol sig nals are
represented by
diamonds. The
arrows show the
direction of data
flow. When a control
signal is asserted,
data is allow ed to
flow through the
diamond.
For example, when
WE is asserted, the
contents of the G
register are written
into era seab le
me mo ry (bank 0 ).

Note the different


formats for data and
instruc tion w ord in
mem ory, shown at
the bottom of the
d ia gra m .
This is a functional
diagram of the
PAR subs ystem in
the MEM m odule.
The diagram was
developed during
the early stages of
my AGC
arch itectu re
analysis in 2001.
MEM Internal Subsystem Interconnections
This diagram show s internal interconnections for subsystems in the MEM m odule.
MEM Module External Interfaces
The M EM m odule interfaces to the PR OC an d CTL m odules throu gh 40 -pin IDE ribbon cables.
J102-CTL: CTL-to-MEM I/F
J102 is a 40-pin IDE cable that connects the MEM modu le to the CTL m odule.

INPU TS (to CTL ):

PIN signal fu ll n am e state definition


1 WE WRITE EMEM (97) 0=W rite E-MEM from G
2 S BW G WRITE G (95) 0= W rite G from m em ory
3 GENRST GENERAL RESET (86) 0=G eneral Reset
4 W23 WRITE ADDR 23 (85) 0=Write into SL
5 W22 WRITE ADDR 22 (84) 0=W rite into CYL
6 W21 WRITE ADDR 21 (83) 0=Write into SR
7 W20 WRITE ADDR 20 (82) 0=W rite into CYR
8 WGn WRITE G NORMAL (81) 0=W rite G (norm al gates)
9 WBK WRITE BNK (80) 0=W rite BNK reg
10 RBK READ BNK (70) 0=R ead BNK reg
11 WS WRITE S (46) 0=W rite S
12 WP2 WRITE P2 (44) 0=W rite P2
13 WPx WRITE P NO RESET (43) 0= W rite P (do n ot reset)
14 WP WRITE P (42) 0=W rite P
15 WGx WRITE G NO RESET (37) 0= W rite G (do not reset)
16 TP TEST PARITY (30) 0= Test pa rity
17 RP2 READ PARITY 2 (14) 0=Read parity 2
18 RG READ G (12) 0=Read G
19 GP GEN PARITY (5) 0= Gen erate Pa rity
20 CLG CLR G (2) 0= Clear G
21 CLK2 CLOCK2 1.02 4 M Hz A GC clock ph ase 2 (n orm ally
low)
22 CLK1 CLOCK1 1.02 4 M Hz A GC clock ph ase 2 (n orm ally
low)
23 NPURST POWER U P RESET 0=reset, 1=normal operation.
24 SW CLK DEBOUNCE CLOCK low freq clk for switch debounce (not
used)
25 FCLK CLOCK MODE 1= free-runn ing clk mo de; 0= sing le clk
mode

OUT PUTS (from CTL):

PIN signal fu ll n am e state definition


31 EQU_16 ADDRESS = 016 (1) 0=CADR in register S = 016
32 EQU_17 ADDRESS = 017 (2) 0=CADR in register S= 017
33 GTR_17 ADDRESS > 017 (3) 0=CADR in register S > 017
34 EQU_25 ADDRESS = 025 (4) 0=CADR in register S = 025
35 GTR_27 ADDRESS > 027 (5) 0=CADR in register S > 027
36 GTR_1777 ADDRESS > 01777 (6) 0=CADR in register S > 01777
37 AD_1 ADDRESS (1) where AD_4 is MSB, AD_1 is LSB
38 AD_2 ADDRESS (2)
39 AD_3 ADDRESS (3)
40 AD_4 ADDRESS (4)
J105-MEM: MEM -to-PROC I/F
J102 is a 40-pin IDE cable that connects the MEM modu le to the PROC modu le.

INPU TS (to M EM):

PIN signal fu ll n am e state definition


1 WB_01 WRITE BUS 01 (lsb)
2 WB_02 WRITE BUS 02
3 WB_03 WRITE BUS 03
4 WB_04 WRITE BUS 04
5 WB_05 WRITE BUS 05
6 WB_06 WRITE BUS 06
7 WB_07 WRITE BUS 07
8 WB_08 WRITE BUS 08
9 WB_09 WRITE BUS 09
10 WB_10 WRITE BUS 10
11 WB_11 WRITE BUS 11
12 WB_12 WRITE BUS 12
13 WB_13 WRITE BUS 13
14 WB_14 WRITE BUS 14
15 WB_15 WRITE BUS 15 US (overflow ) bit
16 WB_16 WRITE BUS 16 SG (sign ) bit

OUT PUTS (from ME M):

PIN signal fu ll n am e state definition


40 RB_01 READ BUS 01 (lsb)
39 RB_02 READ BUS 02
38 RB_03 READ BUS 03
37 RB_04 READ BUS 04
36 RB_05 READ BUS 05
35 RB_06 READ BUS 06
34 RB_07 READ BUS 07
33 RB_08 READ BUS 08
32 RB_09 READ BUS 09
31 RB_10 READ BUS 10
30 RB_11 READ BUS 11
29 RB_12 READ BUS 12
28 RB_13 READ BUS 13
27 RB_14 READ BUS 14
26 RB_15 READ BUS 15 US (overflow ) bit
25 RB_16 READ BUS 16 SG (sign ) bit

22 BUSY7 READ BUS BUSY 0=BNK register output to read bus


21 BUSY5 READ BUS BUSY 0=G register output to read bus
MEM CONTROL PANEL SWITCHES

AGC/MANUAL Permits memory to be examined and loaded when the switch is in the
MANUAL position and the AGC is halted.

EXAM Loads the address counter with the contents of the switch register. To
work, the following switches m ust also be set: AG C/M ANU AL -->
MANUAL

NEXT Steps the address to the next location. To work, the following switches
must also be set: AGC/MANUAL --> MANUAL

READ/WR ITE Displays m em ory contents in the R EAD position; displays sw itch
register contents in the WRITE position.

LOAD Load memory with data manually entered into the switch register. To
work, the following switches must also be set prior to LOAD: CLOCK
CON TRO L (on CTL m odule) --> STEP; A GC/M ANU AL --> M ANU AL;
READ/WR ITE --> WRITE

CLEAR PARITY Clears a parity alarm indication by resetting the parity alarm (PALM)
register. The parity alarm indicator is on the IO modu le.
MEM CONTROL SWITCH CONNECTIONS

PIN signal state definition


1 READ/WR ITE GND= read
2 AGC/MANUAL GND= manual
3 EXAM GND=examine
4 NEXT GND= examine next
5 CLEAR PARITY GND= clear
6 LOAD LOAD switch conta ct
7 LOAD LOAD switch conta ct

PIN signal fu ll n am e state definition


1 D1 DATA1 LSB on switch bus 101
2 D2 DATA2
3 D3 DATA3
4 D4 DATA4
5 D5 DATA5
6 D6 DATA6
7 D7 DATA7
8 D8 DATA8
9 D9 DATA9
10 D10 DATA10
11 D11 DATA11
12 D12 DATA12
13 D13 DATA13
14 D14 DATA14
15 D15 DATA15
16 D16 DATA16 LSB on switch bus 101
17 GND GND
MEM INDICATORS

These indicator lamps show the


current state of all registers and
som e add itiona l, imp ortant lo gic
signals produced by the MEM
m odu le. AG C n um bers are
represen ted in o ctal, so all
register lamps are in groups of
three. At the time the photo was
taken the AGC was running
the COLOSSUS 249 flight
softw are lo ad, executin g V erb
16 , Nou n 36: a m onitor verb
wh ich disp lays th e AG C real tim e
clock.
ADR (Memory Address)
The AGC has a 14-bit address range. Memory address is selected by the 12-bit S register
and th e 4-b it BA NK regis ter.
S: the 12-bit mem ory address register, which holds the lower portion of
the m em ory address.
BANK: the 4-bit mem ory bank register, which selects the mem ory bank when
addressing is in the fixed-switchable mode.

Each A GC instru ction has a 12 -bit a dd ress fiel d. Th e low er bits (1-1 0) a dd ress m em ory
inside each bank. Bits 11 and 12 select the bank:
00: selects the erasable memory bank; the BANK register is ignored.
01: selects th e low est ban k (ba nk 1 ) of fixed m em ory; the BAN K reg ister is
ignored.
10: selects th e nex t ban k (ba nk 2 ) of fixed m em ory; the BAN K reg ister is
ignored.
11: selects the BANK register, which is used to address any bank above 2.
If the BA NK register con tains 0 , 1, or 2, the B AN K reg ister is
overridden and bank 3 is selected.

Banks 1 and 2 are called "fixed-fixed" mem ory, because they are always available,
regardless of the contents of the BANK register. Banks 3 and above are called
"fixed -sw itchable" becau se th e sele cted ban k is determ ined by the B AN K reg ister.

Type:
E Eraseab le M em ory
FF Fixe d-F ixed Me m ory
FS Fixe d-S wit cha ble M em ory

S R egis ter:
14-b it BANK bits 12-b it
address bank type Reg. 12,11 address
00000 - 01777 0 (E) ignored 00 0000 - 1777
02000 - 03777 1 (FF) ignored 01 2000 - 3777
04000 - 05777 2 (FF) ignored 10 4000 - 5777
06000 - 07777 3 (FS) 0000 - 0011 11 6000 - 7777
10000 - 11777 4 (FS) 0100 11 6000 - 7777
12000 - 13777 5 (FS) 0101 11 6000 - 7777
14000 - 15777 6 (FS) 0110 11 6000 - 7777
16000 - 17777 7 (FS) 0111 11 6000 - 7777
20000 - 21777 8 (FS) 1000 11 6000 - 7777
22000 - 23777 9 (FS) 1001 11 6000 - 7777
24000 - 25777 10 (FS) 1010 11 6000 - 7777
26000 - 27777 11 (FS) 1011 11 6000 - 7777
30000 - 31777 12 (FS) 1100 11 6000 - 7777

BANK register (B) + S register (S) Decode s to address:


B4 B3 B2 B1 S12 S11 A14 A13 A12 A11
X X X X 0 0 0 0 0 0
X X X X 0 1 0 0 0 1
X X X X 1 0 0 0 1 0

0 0 0 0 1 1 0 0 1 1
0 0 0 1 1 1 0 0 1 1
0 0 1 0 1 1 0 0 1 1
0 0 1 1 1 1 0 0 1 1

0 1 0 0 1 1 0 1 0 0
0 1 0 1 1 1 0 1 0 1
0 1 1 0 1 1 0 1 1 0
0 1 1 1 1 1 0 1 1 1

1 0 0 0 1 1 1 0 0 0
1 0 0 1 1 1 1 0 0 1
1 0 1 0 1 1 1 0 1 0
1 0 1 1 1 1 1 0 1 1

1 1 0 0 1 1 1 1 0 0
1 1 0 1 1 1 1 1 0 1
1 1 1 0 1 1 1 1 1 0
1 1 1 1 1 1 1 1 1 1

X= don ’t care

This is an early conceptual diagram of


ADR architecture. The WRITE bus loads
the B NK register or S reg ister. Outp uts
from both registers feed into the bank
decoder to gen erate the bank address.
The A DR gen erates som e logic
signals to test the add ress.
Thes e are inte rnally u sed in
ME M a nd a lso m ade a vailab le
to the external modules. The
GTR signals test whether the
generated address is greater
than 1 777, 27 , or 17 (all octal).

The EQU logic tests whether the


gen erated add ress is equ al to a sp ecific
value: 14, 16, 17, or 25.
The bank select logic chooses
betw een the S regis ter or a
combination of the S register
and the ba nk reg ister to
generate addre ss b its 1 4-11 . It
uses S register bits 11 and 12
to make the decision. The
selection signa l enab les tri-
state buffer A or B to produce
the correct bank ad dress.
ADR INPUTS:

I/F signal fu ll n am e state definition


CLK:
CLK1 CLOCK 1 1=read bus setup; inhibit read bus output
CLK2 CLOCK 2 data transfer occurs on falling edge
CPM:
GENRST GENERAL RESET 0= reset A DR regis ters

RBK READ BANK 0=output BNK register to read bus


WBK WRITE BANK 0=w rite into BNK register
from write bus

WS WRITE S 0=w rite into S register


from write bus
WBUS:
WB_01 WRITE BUS 01
...
WB_14 WRITE BUS 14

ADR OUTPUTS:

I/F signal fu ll n am e state definition


MEM:
AD_11 ADDRESS BUS 11 ban k select bits
...
AD_14 ADDRESS BUS 14

AD_1 ADDRESS BUS 1 low order add ress


...
AD_10 ADDRESS BUS 10

various:
EQU_16 ADDRESS = 016 0=CADR in reg S = 016
EQU_17 ADDRESS = 017 0=CADR in reg S = 017
GTR_17 ADDRESS > 017 0=CADR in reg S > 017
EQU_25 ADDRESS = 025 0=CADR in reg S = 025
GTR_27 ADDRESS > 027 0=CADR in reg S > 027
GTR_1777 ADDRESS > 01777 0=CADR in reg S > 01777

RBUS:
RB_01 READ BUS 01
...
RB_16 READ BUS 16

BUSY READ BUS BUSY 0=output to read bus


EMM/FMM (Eraseable/Fixed Memory)
MEM INPUTS:

I/F signal fu ll n am e state definition


CLK:
CLK2 CLOCK 2 data transfer occurs on CLK2

CPM:
WE WR ITE ERASEABLE 0= write m em ory bu s to
erase able m em ory
S BW G WRITE G (MEM) 0=read eraseable or fixed
memory onto memory bus

ADR: GTR_1777 ADDRESS > 01777 0=CADR in reg S > 01777

AD_01 ADDRESS BUS 01


...
AD_10 ADDRESS BUS 10

AD_11 ADDRESS BUS 11 bank select portion of


address bus
...
AD_14 ADDRESS BUS 14 bank select portion of
address bus

BIDIRECTIONAL (IN/OUT):

I/F signal fu ll n am e state definition


EMM/FMM:
MEM_01 MEMORY_BUS 01 Mem ory word form ats:
... inst: 15-13 :op code; 12 -1:add ress
MEM_15 MEMORY_BUS 15 data: 15:S G; 1 4-1 d ata
MEM_16 MEMORY_BUS 16 parity (odd) bit for memory bus
MBF (Memory Buffer Register)
The AGC has a 16-
bit G register for
transferring data to
and from m emory.
WRITE bus data can
be w ritten direc tly
into the G regis ter,
or can be bit-shifted
before w riting. D ata
from the G register
can be copied to the
READ bus, or to the
me mo ry bus.

The following
section shows how
bits are sh ifted in
bus-to-register-to-
m em ory tra nsfers.
The row of 16
comm a-separated
entries rep resent b it
locations at the
destin ation ; the leftm ost entry is the M SB , and th e rightm ost is the LS B. Th e desig natio n in
each location identifies source bit location. This will become clearer shortly.

In memory, the 16 th bit is the odd parity bit, and the 15th bit is th e sig n. In th e G re giste r,
the bits are flipped: the 16 th bit is the sign, and the 15 th bit is the parity. The WE pulse,
which writes from the G register to memory, causes the sign bit (SG; bit 16 in G) to be
written to bit 15 in m em ory. Parity is written to mem ory from the pa rity generator (PAR ),
not from the p arity bit in G. (T he G parity bit holds th e pa rity rea d from m em ory.)

WE:
BX, SG, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2, B1

Data is written from m em ory to G b y the S BW G con trol pulse . The sig n in m em ory (SG ; bit
15) is written to bit 16 in G. The parity bit (bit 16 in memory) is written to bit 15 in G by the
P AR su bsy stem .

SBWG:
SG, BX, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2, B1

Data is written into G from the WRITE bus using WG (no shift), or one of the following
control pulses that prod uce the followin g shifts:

W20:
B1, BX, SG, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2
W21:
SG, BX, SG, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2

W22:
B14, BX, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2, B1, SG

W23:
SG, BX, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2, B1, SG

Data is read from G to the READ bus as follows. The parity bit (bit 15 in G) is not transferred
to the read bu s; instead, the sign b it is copied onto bits 15 an d 16 of the R EAD bus.

RG:
SG, SG, B14, B13,
B12, B11, B10, B9, B8,
B7, B6, B5, B4, B3, B2, B1

Some of the design that


went into the
combinational logic that
sets bit 16 in the G
register from the READ
bus is shown here. The
MB _B1 6 ou tput co ntrols
the 16th bit for the W20
and W 21 control pu lses.
The MB_C16 output controls the
16 th bit for the W22 and W23
control pulses.
MBF INPUTS:

I/F signal fu ll n am e state definition


CLK:
CLK1 CLOCK 1 1=read bus setup; inhibit read bus output
CLK2 CLOCK 2 data transfer occurs on falling edge

CPM:
RG READ G 0=output G register to read/write bus
WE WR ITE ERASEABLE 0=output G register to memory bus
GTR_17 ADDRESS > 017 0=CADR in register S > 017

S BW G WRITE G (MEM) 0=write G from memory bus


WGN WRITE G 0= write G from rea d/w rite
bus (no sh ift)
WGX WRITE G 0= write G from rea d/w rite
bus (no sh ift)
W20 WRITE G (SHIFT) 0=w rite G
W21 WRITE G (SHIFT) 0=w rite G
W22 WRITE G (SHIFT) 0=w rite G
W23 WRITE G (SHIFT) 0=w rite G

GENRST GENERAL RESET 0=G eneral Reset

WBUS:
WB_01 WRITE BUS 01
...
WB_14 WRITE BUS 14
WB_15 WRITE BUS 15 US (overflow ) bit for write
bus
WB_16 WRITE BUS 16 SG (sign) bit for write bus

MBF OUTPUTS:

I/F signal fu ll n am e state definition


RBUS:
RB_01 READ BUS 01
...
RB_14 READ BUS 14
RB_15 READ BUS 15 US (overflow) bit for read/write bus
RB_16 READ BUS 16 SG (sign) bit for read/write bus

BUSY READ BUS BUSY 0=output enabled to read bus


BIDIRECTIONAL (IN/OUT):

I/F signal fu ll n am e state definition


EMM/FMM:
MEM_01 MEMORY_BUS 01 Mem ory word form ats:
... inst: 15-13 :op code; 12 -1:add ress
MEM_15 MEMORY_BUS 15 data: 15:S G; 1 4-1 d ata
MEM_16 MEMORY_BUS 16 parity (odd) bit for memory bus
PAR (Parity Generation and Test)

The PAR subsystem checks


the m em ory pa rity bit
durin g each me m ory cycle
and gen erates an alarm if
the parity bit in m em ory
does not match the
expected odd parity. The
PAR subsystem also
generates parity from the
contents of G and writes
this parity back to memory.
This block diagram of the PAR
subsystem emerges from the
previous diagram.

This is the combinational


logic u sed to g enera te
the input to the G15
register. Th is is the 1- bit
register that holds bit 15
(the parity bit) for the G
register. The remainder
of the G register (bits 1-
14 and bit 16) is in the
M BF su bsy stem .

The m interms are written


ou t on top . De Morg an ’s
Theo rem and a little
bubble-pushing changes
the OR gate into a NAND.
PAR INPUTS:

I/F signal fu ll n am e state definition


CLK:
CLK2 CLOCK 2 data transfer occurs on
falling edge

CPM:
RG READ G15 0=w rite G15 into P
RP2 READ P2 0=write P2 into G15

WP WRITE P 0=write P (same as WPX)


WPX WRITE P 0=write P (same as WP)
WP2 WRITE P2 0=write P2 from 1-15
generator

S BW G WRITE G15 (MEM) 0= writ e G1 5 from m em ory


parity b it

GP WRITE G15 0=write G15 from 1-15


generator (same as WGX)
WGX WRITE G15 0=write G15 from 1-15
generator (same as GP)
CLG CLEAR G15 0=clear G15

TP TEST PARITY 0=test parity from P-15

ADR:
GTR_27 ADDRESS > 027 0=CADR in Register S evaluates to > 027

WBUS:
WB_01 WRITE BUS 01
...
WB_14 WRITE BUS 14
WB_15 WRITE BUS 15 US (overflow) bit for write bus
WB_16 WRITE BUS 16 SG (sign) bit for write bus

MON:
CLRPLM CLEAR PA RITY ALM 0= clea r parity ala rm

PAR OUTPUTS:

I/F signal fu ll n am e state definition


MON
PARALM PARITY ALARM 1= parity alarm
BIDIRECTIONAL (IN/OUT):

I/F signal fu ll n am e state definition


EMM/FMM:
MEM_16 MEMORY_BUS 16 parity (odd) bit for memory bus
Fabrication
The M EM mo dule is (3) 13 "x5" circu it board s, and 1 con trol pan el.

Module Rack

The m odule framework is designed


to resemble a relay rack, but
scaled to fit the c ircuit boa rd
dimensions. It is constructed out
of 1"x2" pine and spray-painted
semi-gloss gray.

Circuit boards are mounted to the


rack by 2 phillips screws at either
end . Nylon s pacers (1 /4") are
used as standoffs to hold the
board edges above the rack. The
boards are mounted so the chips
are in the b ack and the p ins a re
wirin g are visib le from the front.

Power is distributed by 2 heavy


aluminu m bu s bars mounted
vertically, one per side, on the
back of the module. Machine
screws are mounted through the bus bars at evenly-spaced intervals to provide connection
points for the boards.

Solid copper wire (24 gauge) connects the boards to the bus bars. Ring terminals are used
on the bus bar side of the connection. On the circuit board size, the wires are soldered
directly to the supply rails.

Materials were purchased from Home Depot, ACE Hardware, and Radio Shack.

Circuit Boards

The circuit boards are 13 "x5" general p urpose prototypin g boards,


epo xy g lass with dou ble- side plated th roug h pads on 0.1" centers
(JAM ECO 2147 7CL).

ICs are mounted in level 3 machine tooled wire-wrap sockets: 8, 14,


16, 20, 24, and 28 pin (JAMEC O). Each socket has the pin-out labeled
wit h a w ire-w rap sock et ID m arke r, wh ich slips o nto the socke t befo re
wra pping (JAM ECO). T he p art num ber is writt en o nto the ID m arke r.

Sockets are arranged in 4 horizontal rows on each board, with about


10 sockets per row.

Pow er is di stribu ted o n th e ba ck-side o f each boa rd by bare 24-ga ug e solid copp er w ire
supply rails soldered at equal intervals to Klipwrap terminals: 3-prong terminals with a
squ are tail for w ire-wra ppin g (JA ME CO 341 63C L). A + 5V ra il runs a bove each row of soc kets
and a ground rail runs below. Each rail connects directly to the aluminum module power bus
usin g a rin g tail con nec tor.

On the p in sid e of th e bo ard, all con nec tion s are m ade w ith 3 0 A W G K yna r wire -w rap wire
(JAMECO ). Red wire is used for direct connections to the +5V supp ly rail. Black wire is used
for direct connections to ground. White wire is used for everything else.

Power connections from the supply rails to each ICs are double-wrapped. Bypassing
capacitors (.1 uf disc ) are soldered across the supply rails at the Klipwrap terminals; about
1 capacitor for every 2 IC packag es.

All connections were stripped and hand-wrapped using a Radio Shack hand-wrap tool. As
each connection was made, the corresponding line on the schematic was marked with a
colored h igh ligh ter.

DIP resistor networks (JAMECO) plugged into 20-pin wire-wrap sockets were used as current
limiting resistors for the panel ind icators.
MEM Printed Circuit Board (PCB) A

The A b oard contain s the zero-insertion force (ZIF) sockets for the fixed mem ory EPRO Ms,
and the 2 4 0-pin IDE con necto rs that in terface to the other m odu les. The e raseab le
me mo ries (U101 , U102 ) are also clearly visible next to the EPRO Ms. The M em ory Address
(AD R) an d Erase able/ Fixed Mem ory (EM M/F MM ) sub system s are prim arily loca ted on this
board.
MEM Printed Circuit Board (PCB) B

The B board contains the display indicators, their current-limiting resistor networks, and the
open collector drivers. The display panel is a sheet of white styrene plastic. A push pin was
used to make holes through the plastic and the LEDs were inserted in rows. The panel was

hand -lette red w ith a n in delible m arke r.


MEM Printed Circuit Board (PCB) C

The C b oard prim arily contains the M em ory Buffer (MBF) a nd Parity Ge neration an d Test
(PAR ) subsystem s.
Parts (ICs)
74LS00 (11) U50,U25,U26,U27,U40,U32,U33,U34,U43,U44,U53
74LS02 (3) U52,U31,U55
74LS04 (12) U39,U28,U51,U56,U48,U46,U45,U41,U30,U35,U36,U54
74LS06 (21) U2 0,U 19 ,U1 8,U 15 ,U1 6,U 17 ,U1 4,U 13 ,U1 2,U 10 ,U1 1,U 9,U 6,U 7,U 8,U 5,U
4,U21,U22,U23,U24
74LS10 (3) U49,U37,U42
74LS20 (2) U47,U29
74LS27 (2) U2,U38
74LS74 (1) U1
74LS86 (1) U3
74LS154 (1) U72
74LS193 (4) U63,U64,U65,U66
74LS244 (31) U57,U58,U59,U60,U61,U62,U67,U68,U69,U70,U71,U73,U83,U84,U85,
U86,U87,U88,U89,U90,U91,U92,U93,U94,U95,U96,U97,U98,U103,U10
4,U105
74LS273 (10) U74,U75,U76,U79,U80,U81,U82,U106,U107,U108
74S280 (2) U77,U78
27C128 (2) U99,U100
2016 (2) U101,U102

Power Budget
qty mA (ea) m A (tot)
74LS00 11 2.4 26 .4
74LS02 3 2.4 7.2
74LS04 12 3.6 43 .2
74LS06 21 3.6 75 .6
74LS10 3 1.8 5.4
74LS20 2 1.2 2.4
74LS27 2 3.4 6.8
74LS74 1 4.0 4.0
74LS86 1 6.1 6.1
74LS154 1 6.2 6.2
74LS193 4 19 .0 76 .0
74LS244 31 32 .0 99 2.0
74LS273 10 17 .0 17 0.0
74LS280 2 16 .0 32 .0
27C128 2 25 .0 50 .0
2016 2 25 .0 50 .0
LED 124 20 .0 24 80 .0
-------
4.0 Amps total
1.6 Am ps (excludin g LEDs)
Block I
Apollo Guidance Computer (AGC)

How to build one in your basement

Part 5: Input/Output (IO) Module

John Pultorak
December, 2004
Abstract
This report describes my successful project to build a working reproduction of the 1964
prototype for the Block I Apollo Guidance Computer. The AGC is the flight computer for the
Ap ollo m oon lan din gs, and is the wo rld’s first in tegra ted c ircuit com pu ter.

I built it in my ba sem ent. It took me 4 yea rs.

If you like, you can build one too. It will take you less time, and yours will be better than
mine.

I docum ented m y project in 9 separate .pdf files:

Part 1 O v ervie w : Introdu ces the p roject.

Part 2 CTL Module: Design and construction of the control module.

Part 3 PROC M odule: Design and construction of the processing (CPU) modu le.

Part 4 MEM Module: Design and construction of the mem ory module.

Part 5 IO Module: Design and construction of the display/keyboard (DSKY) m odule.

Part 6 Assem bler: A cross-a ssem bler for AG C softw are dev elopm ent.

Part 7 C+ + S imu lator: A low-level simulator that runs assemb led AGC code.

Part 8 Flight Software: My translation of portions of the COLOSSUS 249 flight


software.

Part 9 Test & Che ckou t: A suite of test programs in AG C assembly langu age.
Overview
The I/O Module (IO) has 5 subsystems: IMI, KBD, INP, OUT, DSP

IM I (I/O M od ule
external Interface)
The IMI interfaces the
I/O module to other
AG C m odu les. 40-p in
IDE con nec tors
interface to the PROC
and CTL modules. A 1-
pin connector interfaces
to the MEM m odule.
Inputs taken those
m odu les are bu ffered to
1 LSTTL load.

KBD (Keyboard)
An 18-button keyboard;
the AGC’s flight
software user input
interface. The
keyb oard/d isplay unit is
called the DSKY.

INP (Input
Registers)
The A GC has 4 16-b it
input registers that
receive data from the
keyb oard a nd d iscrete
logic signals. IN0 reads
from the keyboard and
the STANDBY ALLOWED
discrete signal. IN1-IN3
are no t imp lem ented in
this replica.

OUT (Output
Registers)
The A GC has 5 16-b it
output registers that
drive the DSKY display
and oth er spa cecra ft
subsystems. OUT0
writes to the DSKY
display; OUT1 drives
the 6 discrete indicator
lamps on the AGC DSKY
display; OUT2-OUT4
are not implemented in this replica.
DSP (Display)
A m atrix of green 7-segment displays; the output side of the AGC’s flight software user
interface. There are (3) 5-digit displays with +/- signs which can display decimal or octal
data, and (3) 2-digit displays to show the current program (PROG), verb, and noun.

An ad ditional pan el of 6 indicator lam ps show s AGC status and a larm states.
AGC input registers and
control signals that read
them onto the b us a re
show n in th e diag ram to
the righ t. The 5 -bit
keyboard code and
STA NDB Y ALLO WE D sw itch
are read from “register 0"
(IN0) when the R4 (RA4)
con trol pulse is asserted .
The sign al is R4 becau se
the reg ister is m app ed to
memory address 4. The
registers are actua lly
buffers, not latches.
Keyboard codes are latched
inte rnally in the k eyb oard
su bsy stem .

The IN1, IN2, and IN3


registers are not
im plem ented in this
replica.

AGC output registers are shown in the next


figure. OUT0 drives the 7-segment DSKY
displays. W10 (WA10) loads data from the
write b us into the D SK Y disp lay; th e “10" is
because it is mapped onto memory location 10
(octal). Each 16-bit word controls 2 digits on
the dis play . The d igit pa ir is selected b y a 4-b it
code at the top of the word.

OUT1 drives discrete indicator lamps on a


panel adjacent to the 7-segment display. W11
(WA11) writes to the register, and R11 (RA11)
reads from it.

The OUT2 , OUT3, and OU T4 registers are not


implemented in this replica.
IO Internal Subsystem Interconnections
This diagram show s internal interconnections for the subsystems in the IO m odule.
IO Module External Interfaces
The IO m odule interfaces to the CT L and PR OC m odules throu gh 40 -pin IDE ribbon cables.
J103-IO: CTL-to-I/O I/F

INPU TS (to IO):

PIN signal fu ll n am e state definition


1 CLK1 CLOCK1 1.02 4 M Hz A GC clock ph ase 2 (n orm ally
low)
2 CLK2 CLOCK2 1.02 4 M Hz A GC clock ph ase 2 (n orm ally
low)
3 NSA STANDBY ALLOW ED 0=standb y allowed

5 GENRST GENERAL RESET (86) 0=clear the DSKY, OUT1, and OUT2.
6 WA11 WRITE OUT1 (76) 0=write into OUT1 from write bus
7 WA10 WRITE OUT0 (75) 0=write into OUT0 (DSKY) from write bus
8 RA11 READ OUT1 (66) 0=output OUT1 register to read bus
9 RA4 READ IN0 (61) 0=output IN0 register to read bus

20 STBY STANDBY 0= AG C is in th e stan dby state

OUT PUTS (from IO):

PIN signal fu ll n am e state definition


40 OUT1_8 STANDBY ENA BLED 1=standby enabled; works with STANDBY
ALLOW ED sw itch
J104-IO: PROC-to-IO I/F

INPU TS (to IO):

PIN signal fu ll n am e state definition


1 WB_01 WRITE BUS 01 (lsb)
2 WB_02 WRITE BUS 02
3 WB_03 WRITE BUS 03
4 WB_04 WRITE BUS 04
5 WB_05 WRITE BUS 05
6 WB_06 WRITE BUS 06
7 WB_07 WRITE BUS 07
8 WB_08 WRITE BUS 08
9 WB_09 WRITE BUS 09
10 WB_10 WRITE BUS 10
11 WB_11 WRITE BUS 11
12 WB_12 WRITE BUS 12
13 WB_13 WRITE BUS 13
14 WB_14 WRITE BUS 14
15 WB_15 WRITE BUS 15 US (overflow ) bit
16 WB_16 WRITE BUS 16 SG (sign ) bit

OUT PUTS (from IO):

PIN signal fu ll n am e state definition


40 RB_01 READ BUS 01 (lsb)
39 RB_02 READ BUS 02
38 RB_03 READ BUS 03
37 RB_04 READ BUS 04
36 RB_05 READ BUS 05
35 RB_06 READ BUS 06
34 RB_07 READ BUS 07
33 RB_08 READ BUS 08
32 RB_09 READ BUS 09
31 RB_10 READ BUS 10
30 RB_11 READ BUS 11
29 RB_12 READ BUS 12
28 RB_13 READ BUS 13
27 RB_14 READ BUS 14
26 RB_15 READ BUS 15 US (overflow ) bit
25 RB_16 READ BUS 16 SG (sign ) bit

22 BUSY2 READ BUS BUSY 0=OUT registers output to read bus


21 BUSY1 READ BUS BUSY 0=INP registers output to read bus
20 KB_STR KEY STROBE 1=key pressed strobe; to KEYRU PT. Key
data is valid on the negative edge of
KB_STR. Data is latched until the next
keypress.
IO DISPLAY/KEYBOARD (DSKY)
The keyboard/display portion of the IO module contains a keyboard, a bank of 7-segment
displays, a pan el of discrete indicator lamps, an d a board of display drivers.
DSKY KEYBOARD

The DSKY has an 18-button keyboard:

0-9 Decim al (or octal) digits.

+ Plus sign for decim al entries.

- Minu s sign for decima l entries.

VERB Tells the AG C the next 2 digits


entered will be a VERB.

NOUN Tells the AG C the next 2 digits


entered will be a NOUN.

ENTER Tells the AG C the data entry is


finished.

CLEAR Clears an error in entry.

ERR RST Resets the OPR ERR alarm lamp.

KEY REL Tells the AGC it can have control of


the dis play . If the AGC wa nts
control of the display, the KEL REL
lamp will be flashing.
DSKY 7-SEGMENT DISPLAY

COMP ACTY A green indicator lamp that


illuminates when the AG C is not
idle. The lamp is controlled by the
“dum m y job”, the lowe st priority
job in the A GC EX EC softw are ’s
non-preemptive multitasking.
When the dummy job is running,
the lam p is extinguish ed becau se
the AGC is idle. When the dummy
job exits because there is a higher
priority job running, the lamp
illuminates. The light is driven by
bit 1 of OUT1.

PROG The 2-digit code for the current


AGC program. Driven by OUT0.

VERB The 2-digit code for the selected


VER B. Verbs are actions;
directives for the AGC to do
something, such as loading or
displaying mem ory data. Driven
by OUT0.

NOUN The 2-digit code for the selected


NOUN. The noun is the thing
acte d upon by th e verb . Nou ns u sua lly refer to m em ory lo catio ns, w hich are
mapped to some AGC function. Driven by OUT0.

R1 Register 1. The uppermost of the three 5-digit displays. Registers R1, R2, and
R3 can display data in octal or decimal. Octal data is displayed without a sign.
Decimal data is indicated by the presence of a + or - sign in front of the data.
The displays I used in my replica cannot display a + sign, so I modified the
logic slig htly: d ecim al da ta is represe nted by a leftm ost decim al poin t.
Negative decimal numbers have a - sign and the decimal point; positive
numbers just have the decimal point. Driven by OUT0.

R2 Register 2. The middle of the three 5-digit displays. Driven by OUT0.

R3 Register 3. The bottommost of the three 5-digit displays. Driven by OUT0.


IO DISCRETE INDICATORS

The DSKY has a panel of discrete indicator lamps (LEDs) to show status or caution and
wa rning signa ls. Four of th e lam ps are d riven b y bits in outp ut registe r 1 (OU T1). Th e parity
alarm is driven by a signal from the MEM module. The standby lamp is driven by the
standby state of the time pulse generator (TPG) in the CTL module.

UPLINK ACTY Uplink activity. Illum inates when data is uplinked to the AGC. Driven
by bit 3 (UPTL) of OUT1.

OPR ERR Operator Error (also called CHECK FAIL). Illuminates when the AGC
detects a data entry error. Driven by bit 7 of OUT1.

KEY REL Key Release. Illuminated by the AGC when it needs to use the display,
but th e opera tor has ta ken co ntrol of it. The AG C cau ses this la m p to
flash to signal the operator to release control of the display by hitting
the KEY REL button. Driven by bit 5 (KEY RELS) of OUT1.

PROG Program Alarm. Illuminates when the AGC encoun ters an error
condition. Driven by bit 8 of OUT1.

STBY Standby. Illuminates when the AG C is in the STANDB Y mod e.

PARITY ALARM Illum inates wh en a p arity error is dete cted d uring the m em ory cycle in
the MEM m odule.
KBD (Keyboard)
The keyboard is an 18-pushbutton unit that
gen erates an d latch es a 5-b it code. Th e code is
given in “Keyboard and Display System Program
for AGC (Program Sun rise)”, A. I. Green and J.
J. Rocchio, E-1574, MIT Instrumentation
Laboratory, Cambridge, MA, 1964.

The keyboard codes and logic for generating the


5-bit signal is reproduced to the right. The “Key
Name” column identifies the name of the
keyb oard k ey; “A ” throug h “E” are the 5 lo gic
signals for the 5-bit code, where “A” is the MSB.
The 18 switches in the
keyboard feed into the
combinational logic encoder
wh ich ge nerate s the 5 -bit
signal. The output of the
enco der feeds into a 5 -bit
latch.

The keys are debounced by


generating a “keypress”
sign al wh enev er a key is
pushed. The keypress signal
feeds through a “D” flip-flop
clocked at around 100Hz.
This sam ples the keyp ress
signal every 10 mSec and
latches the sample. The 10
mSec interval exceeds the
contact bounce time of the
keyboa rd switches.

To give the combinational logic time to settle before the keycode is latched, the output of the
keypress D flip-flop is fed into an RC monstable. Latching occurs on the trailing edge of the
one-shot pulse.

The keyboard codes are fed into “input register” IN0, which is really just a buffer that gates
the codes on to the read bu s when the proper read sign al is asserted. The original design also
map s the keypress strobe which generates the keyboard interrupt (KEYRUPT) onto bit 6 of

the register, but I skipped this since there doesn’t seem to be any practical reason for doing
it an d th e CO LOSS US fligh t softw are d oesn ’t seem to loo k at th e field .

The STANDBY ALLOWED switch (CTL module) maps to be 14.


KBD INPUTS:

I/F signal fu ll n am e state definition

CLK:
CLK2 CLOCK 2 data transfer occurs on falling edge

CPM:
GENRST GENERAL RESET 0=reset KBD register

KBD OUTPUTS:

I/F signal fu ll n am e state definition

KBD:
KB_01 KEYBOARD BUS 01 Keyboard codes
...
KB_05 KEYBOARD BUS 05

INT:
KB_STR KEY STROBE 1=key pressed strobe; to KEYRU PT. Key
data is valid on the negative edge of
KB_STR. Data is latched until the next
keypress.
INP (Input Registers)
INP INPUTS:

I/F signal fu ll n am e state definition

CPM:
RA4 READ IN0 0=output IN0 register to read bus
RA5 READ IN1 0=output IN1 register to read bus
RA6 READ IN2 0=output IN2 register to read bus
RA7 READ IN3 0=output IN3 register to read bus

KBD:
KB_01 KEYBOARD BUS 01
...
KB_05 KEYBOARD BUS 05

NSA STANDBY ALLOW ED 0=standb y allowed

INP OUTPUTS:

I/F signal fu ll n am e state definition

RBUS:
RB_01 READ BUS 01
...
RB_14 READ BUS 14
RB_15 READ BUS 15 US (overflow) bit for read/write bus
RB_16 READ BUS 16 SG (sign) bit for read/write bus

BUSY READ BUS BUSY 0=output enabled to read bus


OUT (Output Registers)
OUT INPUTS:

I/F signal fu ll n am e state definition


CLK:
CLK2 CLOCK 2 data transfer occurs on falling edge

CPM:
RA11 READ OUT1 0=OUT1 to read bus
RA12 READ OUT2 0=OUT2 to read bus
RA13 READ OUT3 0=OUT3 to read bus
RA14 READ OUT4 0=OUT4 to read bus

WA11 WRITE OUT1 0=load OU T1 from


write bus
WA12 WRITE OUT2 0=load OU T2 from
write bus
WA13 WRITE OUT3 0=load OU T3 from
write bus
WA14 WRITE OUT4 0=load OU T4 from
write bus

GENRST GENERAL RESET 0=clear DSKY, OUT1,


and OUT2.

WBUS:
WB_01 WRITE BUS 01
...
WB_14 WRITE BUS 14
WB_15 WRITE BUS 15 US (overflow) bit for
write bus
WB_16 WRITE BUS 16 SG (sign ) bit for write
bus

OUT OUTPUTS:

I/F signal fu ll n am e state definition

DSP:
OT0_01 OUT0 REG 01 OU T0 reg ister outp ut to
DSKY
...
OT0_16 OUT0 REG 16

OT1_01 OUT1 REG 01 COM P panel indicator; 1=on


OT1_03 OUT1 REG 03 UPTL panel indicator; 1=on
OT1_05 OUT1 REG 05 KEY RELS p anel indicator; 1=on
OT1_07 OUT1 REG 07 CHECK FAIL panel indicator 1=on
OT1_08 OUT1 REG 08 STBY pan el indicator 1=on
OT1_09 OUT1 REG 09 PROG ALM pan el indicator 1=on
RBUS:
RB_01 READ BUS 01
...
RB_14 READ BUS 14
RB_15 READ BUS 15 US (overflow) bit for read/write bus
RB_16 READ BUS 16 SG (sign) bit for read/write bus

BUSY READ BUS BUSY 0=output enabled to read bus


DSP (Display)
The 7 -segm ent D SK Y disp lay is driv en b y outp ut registe r 0 (OU T0). Ea ch 16 -bit w rite to
OU T0 w rites d ata to a p air of 7 -seg m ent dig its.

Four fields in OUT0 are involved: The relay word (RLYWD ; bits 12-15) field selects the pair of
digits; the DSPH field (bits 6-10) contains the 5-bit numerical code for the left digit in the
pair, an d DP SL (b its 1-5) has th e code for the righ t digit. Th e 1-b it DPS C (bit 1 1) field
controls verb/noun flash (enables 1 Hz blinking of the VERB and NOUN digits) and the plus
and minus signs to the left of the three 5-digit “registers” on the DSKY display.

Bits 15-12 Bit 11 Bits 10-6 Bits 5-1


R LY W D DSPC DSPH DSPL
1011 MD1 MD2
1010 FLASH VD1 VD2
1001 ND1 ND2
1000 UPACT R1D1
0111 +R1S R1D2 R1D3
0110 -R1S R1D4 R1D5
0101 +R2S R2D1 R2D2
0100 -R2S R2D3 R2D4
0011 R2D5 R3D1
0010 +R3S R3D2 R3D3
0001 -R3S R3D4 R3D5

Each 7-segment digit on the display has a name (VD1, VD2, etc). The
digits are physically arran ged like this:

MD1 MD2 : major mode (PROG)


VD1 VD2 : VERB ND1 ND2 : NOUN

R1S R1D1 R1D2 R1D3 R1D4 R1D5 : regi ster 1


R2S R2D1 R2D2 R2D3 R2D4 R2D5 : regi ster 2
R3S R3D1 R3D2 R3D3 R3D4 R3D5 : regi ster 3

In m y assem bled unit, th e disp lays w ere in gro ups of 3, so som e digits
were not needed and left unwired.

The 5-bit codes that illuminate each digit in the display are given below; these are the codes
sent to the DPSH and DPSL fields in OUT0.

In my implementation, I translate them into 4-bit binary-coded decimal representation (BCD)


and feed them into a 74LS4 7 7-segm ent decoder. The mapping of the AGC digit to my
74LS47 d ecoder code is also given. The AGC digit codes are very peculiar; I suspect they
were chosen for easy decoding in to the 7-segm ent displays.

Dig it AGC 74LS47


Blank 00000 1111
0 10101 0000
1 00011 0001
2 11001 0010
3 11011 0011
4 01111 0100
5 11110 0101
6 11100 0110
7 10011 0111
8 11101 1000
9 11111 1001

My initial block diagram for the DSP logic is shown here. Two combinational logic code
converters changes the 5-bit AGC code (DS PH, DPSL) into 4-bit BCD. The converted codes
are la tche d in to 4- bit reg isters b y w rite pu lses d ecod ed b y the rela y w ord (RLY W D) d ecod er.
Single b it latches hold the flash an d sign bit code s transm itted by DS PC (bit 11 of OUT0 ).

Altho ugh I show separa te deco ders for each digit, I actua lly m ultiple xed th e disp lay to
minimize the hardware. In this way, I only needed a pair of 74LS47 decoders; one for DSPH
and the other for DSPL.
Some back-of-the-envelope bits and
pieces of the logic design are also
shown here. One “diagram” shows the
verb/n oun flash log ic. Note th e sim ple
Karnaugh map (a graphical method for
reducing boolean expressions), and a
bit of bubble-pushing (a graphical
tec hn ique for a pp lying De Morg an ’s
Theorem to transform logic functions
betwee n AN D an d OR ).

The other “diagram” shows the logic for


the +/- signs on registers 1, 2, and 3. The
displays I used in my replica cannot
display a + sign, so I modified the AGC
logic slightly: decimal data is represented
by a leftmost decimal point. Negative
decimal numbers have a - sign and
decim al point; po sitive deci m al n um bers
just ha ve the decim al poin t.
The d igit disp lay po rtion of the DS KY u ses green com m on-a nod e LED displa ys grou ped in
three’s. Like most com ponen ts, these were purcha sed from JA MEC O. Here’s the pinouts:
A pan el of discrete indicator LEDs are m apped against bits in ou tput register 1 (OU T1).
DSP INPUTS:

I/F signal fu ll n am e state definition

CLK:
CLK2 CLOCK 2 data transfer occurs on falling edge

PAR:
PARALM PARITY ALARM 1= parity alarm

INP:
OT0_01 OUT0 REG 01 OUT0 register output to DSKY
...
OT0_15 OUT0 REG 15

OT1_01 OUT1 REG 01 COM P panel indicator; 1=on


OT1_03 OUT1 REG 03 UP TL p anel indica tor;
1=on
OT1_05 OUT1 REG 05 KEY RELS p anel
indicator; 1=on
OT1_07 OUT1 REG 07 CHECK FAIL panel
indicator 1=on
OT1_08 OUT1 REG 08 STBY pan el indicator
1=on
OT1_09 OUT1 REG 09 PROG ALM pan el
indicator 1=on

CPM:
WA10 WRITE OUT0 0=write into OUT0
(DSKY) from write bus

GENRST GENERAL RESET 0=clear the DSKY.

DSP OUTPUTS:

I/F signal fu ll n am e state definition

none.
Fabrication
The IO modu le is (2) 13"x5" circuit boards, and 1 DSKY panel containing a display driver
board, a 7-segment display board, a discrete LED indicator board, and a keyboard.

Module Rack

The m odu le fram ewo rk is desig ned to


resemble a relay rack, but scaled to fit the
circuit board dimensions. It is constructed
out of 1"x 2" pin e and spray- pain ted sem i-
gloss gray.

Circuit boards are mounted to the rack by 2


ph illips screw s at either end . Nylon s pacers
(1/4") are used as standoffs to hold the
boa rd ed ges abo ve th e rack . The boa rds a re
mounted so the chips are in the back and
the pins are wiring are visible from the
front.

Power is distributed by 2 heavy aluminum


bus bars moun ted vertically, one per side,
on the back of the module. Machine screws
are mounted through the bus bars at
evenly-spaced intervals to provide
connection points for the boards.

Solid copper wire (24 gauge) connects the


boa rds to the b us b ars. R ing term ina ls are
used on the bus bar side of the connection. On the circuit board size, the wires are soldered
directly to the supply rails.

Materials were purchased from Home Depot, ACE Hardware, and Radio Shack.

Circuit Boards

The circuit boards are 13"x5" general purpose prototyping boards, epoxy glass with double-
side plated throu gh pa ds on 0.1" cen ters (JAMEC O 21 477C L).

ICs are mounted in level 3 machine tooled wire-wrap sockets: 8, 14,


16, 20, 24, and 28 pin (JAMEC O). Each socket has the pin-out labeled
wit h a w ire-w rap sock et ID m arke r, wh ich slips o nto the socke t befo re
wra pping (JAM ECO). T he p art num ber is writt en o nto the ID m arke r.

Sockets are arranged in 4 horizontal rows on each board, with about


10 sockets per row.

Power is distributed on the back-side of each board by bare 24-gauge


solid copper wire supply rails soldered at equal intervals to Klipwrap terminals: 3-prong
terminals with a square tail for wire-wrapping (JAMECO 341 63CL). A +5V rail runs above
each row of sockets and a ground rail runs below. Each rail connects directly to the aluminum
m odu le po we r bus using a ring tail co nn ector.
On the p in sid e of th e bo ard, all con nec tion s are m ade w ith 3 0 A W G K yna r wire -w rap wire
(JAMECO ). Red wire is used for direct connections to the +5V supp ly rail. Black wire is used
for direct connections to ground. White wire is used for everything else.

Power connections from the supply rails to each ICs are double-wrapped. Bypassing
capa citors (.1 uf d isc ) are sold ered ac ross the su pply rails at the Klipw rap term inals; abou t 1
capacitor for every 2 IC packag es.

All connections were stripped and hand-wrapped using a Radio Shack hand-wrap tool. As
each connection was made, the corresponding line on the schematic was marked with a
colored h igh ligh ter.

DIP resistor networks (JAMECO) plugged into 20-pin wire-wrap sockets were used as current
limiting resistors for the panel ind icators.
IO Circuit Board A

The A board contains the module interface buffers, input and output registers, and the
latches that hold the BCD codes for the 7-segm ent displays.
IO Circuit Board B

The B board conta ins key boa rd an d disp lay log ic. The 4 0-pin IDE con necto rs that in terface to
the other modules are visible at the bottom. The 5 red LEDs show the keyboard code latched
into the K BD outp ut reg ister.
IO Device Driver Board C

The C b oard con tain s driv er tran sistors an d th eir associa ted re sistors. Th e tran sistors are
pla stic m ediu m -po we r com plem entary silicon : NP N tra nsis tors are TIP1 02 , PNP transistors
are TIP107. Viewed from the front of the TO-220 case, the base (1) is to the left, collector
(2) in th e m idd le, an d em itter (3 ) to th e righ t. The m etal tab (4) is the collec tor.
An empty space at the top of the IO module rack was filled with a plexiglass panel listing
verb and noun codes:
Parts (ICs)
IC’s, sockets, PCB’s, resistors, capacitors, wire-wrap wire were purchased from JAMECO. IDE
wire-wrap sockets were from DigiKey. W ire ties, wire-wrap tools, and copper wire were from
Ra dio Sh ack. IDE rib bon cab les w ere purch ased from an o nlin e com pu ter su pp lier.

74LS00 (13) U27, U27B, U27C, U15C, U15B, U15, U14C, U14B, U14, U29D, U29C,
U29B, U34B
74LS02 (3) U25,U25,U33C
74LS04 (27) U40E, U40D, U38D, U38C, U40F, U40C, U39E, U39D, U39C, U39B,
U41D, U40B, U37F, U39F, U12, U12B, U12C, U12D, U12E, U11F, U11E,
U11D, U11C, U11B, U11, U39A, U37
74LS06 (41) U26F, U26E, U26D, U26C, U26B, U26, U20, U18D, U18C, U18B, U18,
U20C, U17E, U17D, U20B, U16F, U16E, U9, U9B, U9C, U9D, U9E, U9F,
U10, U10B, U10C, U10D, U7, U7B, U7C, U7D, U7E, U7F, U8, U20,
U19F, U19E, U19D, U19C, U19B, U19
74LS08 (1) U28
74LS10 (1) U4
74LS20 (5) U2, U3, U5, U31, U32
74LS27 (2) U35C, U36
74LS47 (4) U46, U47, U48, U45
74LS74 (1) U1
74LS86 (1) U24C
74LS112 (5) U6A, U23B, U22B, U21B, U30
74LS138 (9) U50, U67, U68, U69, U70, U73, U74, U75, U76
74LS148 (4) U65, U66, U71, U72
74LS154 (1) U51
74LS161A (1) U49
74LS244 (10) U101, U100, U52, U53, U77, U78, U81, U82, U83, U84
74LS273 (3) U79, U80, U44
74LS374 (11) U54, U55, U56, U57, U58, U59, U60, U61, U62, U63, U64
GREENCA (21) DISP1, DISP2, DISP3, DISP4,DISP5, DISP6, DISP7, DISP8, DISP9,
DISP10, DISP11, DISP12, DISP13, DISP14, DISP15, DISP16, DISP17,
DISP18, DISP19, DISP20, DISP21
555 (3) U85, U42, U43
NPN3 (35) Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q15, Q16, Q17, Q18, Q19, Q20, Q21,
Q32, Q33, Q34, Q35, Q36, Q37, Q38, Q39, Q40, Q41, Q42, Q43, Q44,
Q45, Q8, Q9, Q10, Q11, Q12, Q13, Q14
PNP3 (22) Q22, Q23, Q24, Q25, Q26, Q27, Q28, Q29, Q30, Q31, Q46, Q47, Q48,
Q49, Q50, Q51, Q52, Q53, Q54, Q55, Q56, Q57

Power Budget
qty mA (ea) m A (tot)
74LS00 13 2.4 31 .2
74LS02 3 2.4 7.2
74LS04 27 3.6 97 .2
74LS06 41 3.6 14 7.6
74LS08 1 4.4 4.4
74LS10 1 1.8 1.8
74LS20 5 1.2 6.0
74LS27 2 3.4 6.8
74LS47 4 7.0 28 .0
74LS74 1 4.0 4.0
74LS86 1 6.1 6.1
74LS112 5 4.0 20 .0
74LS138 9 6.3 56 .7
74LS148 4 12 .0 48 .0
74LS154 1 6.2 6.2
74LS161 1 19 .0 19 .0
74LS244 10 32 .0 32 0.0
74LS273 3 17 .0 51 .0
74LS374 11 27 .0 29 7.0
555 3 3.0 9.0
GREENCA 21 14 0.0 29 40 .0
LED 25 20 .0 50 0.0
----
4.6 Amps total
4.1 Am ps (excludin g single LED s)
Block I
Apollo Guidance Computer (AGC)
How to build one in your basement

Part 6: Assembler

John Pultorak
December, 2004
Abstract
This report describes my successful project to build a working reproduction of the 1964
prototype for the Block I Apollo Guidance Computer. The AGC is the flight computer for the
Ap ollo m oon lan din gs, and is the wo rld’s first in tegra ted c ircuit com pu ter.

I built it in my ba sem ent. It took me 4 yea rs.

If you like, you can build one too. It will take you less time, and yours will be better than
mine.

I docum ented m y project in 9 separate .pdf files:

Part 1 O v ervie w : Introdu ces the p roject.

Part 2 CTL Module: Design and construction of the control module.

Part 3 PROC M odule: Design and construction of the processing (CPU) modu le.

Part 4 MEM Module: Design and construction of the mem ory module.

Part 5 IO Module: Design and construction of the display/keyboard (DSKY) m odule.

Part 6 Assem bler: A cross-a ssem bler for AG C softw are dev elopm ent.

Part 7 C+ + S imu lator: A low-level simulator that runs assemb led AGC code.

Part 8 Flight Software: My translation of portions of the COLOSSUS 249 flight


software.

Part 9 Test & Che ckou t: A suite of test programs in AG C assembly langu age.
Overview
This A GC cross-asse m bler, code d in C ++ , produ ces AG C ob ject code in M otorola S -form at.
The code can be burned into EPROM for use by the hardware AGC, or executed by the C++
AG C sim ula tor.

Operation
The assembler reads an AGC source code text file (having a .asm extension). It generates
an assembly listing text file (.lst extension) and an two object code text files (_H.hex and
_L.hex suffixes). The object code files are readable by the AGC simulator or an EPROM
prog ram m er.

Syntax
Sou rce code files are text files con tainin g m ultiple lines of sou rce code . Each lin e is
terminated by a newline character. Source code files can be produced by any editor, as long
as it doesn't insert any hidden characters or formatting information.

Each line of source code consists of one of the following:


a) A blank line.
b) A com me nt (com me nts m ust begin with a sem icolon (;)).
c) A line of assembler code.

The assembler ignores blank lines and anything that occurs after a semicolon on any given
line.

A line of assem bler code consists of the following comp onents:


1) A label (this is optiona l).
2) An op code or assembler directive.
3) An op erand (also op tional).
4) A com me nt (optional; com me nts m ust start with a sem icolon).

The comp onents, if present, mu st appear in the order given. Each component is separated
from th e nex t by on e or m ore wh ite spac es or tabs . The on ly cons traint is th at the la bel, if
present, must start in the 1st colum n of the line. If no label is present, the op code or
assembler directive must not start in the 1st column, but may appear in any subsequent
column.

The operand may consist of one of the following:


a) An asterisk (the assembler substitutes in the current value of the location
cou nter.
b) A symbolic name (the assemble substitutes in the value during the second
pass of the assembly.
c) A num eric constant. Octal contants must be preceeded by a '%'; hexadecimal
consta nts are p receede d by a '$'; anyth ing els e is treated as a de cim al.
d) An expression consisting of one or more of the above, separated by the
operators: +, -, @ (for multiplication), or / (for division). Unary plus or minus
is also allowed.
Exam ples:
*+2 Location counter plus 2.
LABEL +% 10/2 LABEL plus octal number 10 divided by decimal number 2.
-5 Negative decimal 5.
Assembler Directives
The following directives are supported:

ORG Set the location counter to the operand following ORG.

EQU Set the label to the operand value.

DS Define a word of storage; set the value to the operand value.

CADR Define a word of storage; set the value to the operand value (assumed
to be a 14-b it address; this is an alias for CAD R).

ADRES Define a word of storage; set the value to the operand value (operand
is treated as a 12-bit ad dress).

INCL Inline include a file; the operand contains the filenam e.

Addressing
The location counter and symbol table always contain the full 14-bit address. AGC
instructions require a 12-bit address as an operand. Addressing is implemented in 1K banks
(bits 10 throu gh 1). B its 12 and 11 select banks 0 (era sab le m em ory), 1 (fixed -fixed ), or 2
(fixed-fixed). These banks are directly addressable in 12-bits. Banks above 2 are called
'fixed-switchable' and are addressed using the BANK register: bits 12 and 11 of the operand
are set to '1' and the bank register provides the high-order 4 bits (bits 14 through 11) of the
address. The lower 10 bits of the operand provide the address within the bank.

Errata
a) The assembler ignores the last line of the source (.asm) file. If the last line of your
source file contains code, you must add an additional blank line to the end of your
source file to ensure that your last line of code is assembled.

b) The symbol table could be sorted before the second pass. The linear search through
the sym bol table could be replaced by a mo re efficient m ethod. (Bu t, assembly is so
fast, it doesn’t m atter).

c) The assembler directives and syntax don't match those of the original block I AGC.

d) A macro definition capability might be handy.

e) Gen erates em pty .lst an d .obj files if the so urce cod e file does n ot exist.

f) Incorrectly a ssign s a zero va lue to la bels th at equ ate to a forw ard referen ced lab el.
i.e.:
LABEL1 EQU LABEL2 ; assem bler in correc tly sets LA BEL 1 to z ero
LABEL2 EQU *
Assembler listing
The assembler produces a list file (.lst) that shows the assembled source code. The format
of the file w as des igne d to m im ic (som ewh at) the forma t of the origin al assem bler. You will
find a fragment of the original assembler listing reproduced in part 8.

List files from my assem bler ha ve this form at:


Object code
The object code is output to text files in Motorola S-Record format (S2F) suitable for
im m edia te us e by EPR OM prog ram m ers, an d also rea dable b y the C+ + sim ula tor.

There ’s lots of docum enta tion on S-R ecord form ats on the inte rnet.

The files are pretty big. If you open one up, you’ll discover they contain many short records
that look like these:

This is a fragment of the source code for the lower 8 bits of the TECO1 test and checkout
program (described in pa rt 9).

The assembler contains C++ code for generating these files; the C++ simu lator has code for
rea din g th em .
Assembler v1.6
/*
****************************************************************
*
* Cross Assembler for Block I Apollo Guidance Computer (AGC4)
* THIS VERSION IS MODIFIED TO OUTPUT IN S-RECORD FORMAT SUITABLE FOR
* LOADING IN EPROM (s2f format).
* Author: John Pultorak
* 6/1/2002
*
*****************************************************************

Versions:
1.0 - First version of the AGC assembler.
1.1 - Added ability to handle simple arithmetic expressions for the operand.
1.2 - Changed label fields to 14 char. Printed symbol table in 3 columns.
Corrected wrong implementation of OVSK.
1.3 - Added support for bank addressing.
1.4 - Added capability to inline code with nested includes.
1.5 - Added CADR, ADRES assembler directives. Swapped addresses for
TIME1 and TIME2 for compatibility with Block II PINBALL routines.
1.6 - Fixed the parser so it doesn't mangle comments anymore. Added
FCADR and ECADR to the assembler directives. Added a check for
assembled code that overwrites already used locations.
1.6 - EPROM Assembler outputs to low and high EPROM files.

Operation:
The assembler reads an AGC source code file (having a .asm extension).
It generates an assembly listing text file (.lst extension) and an
object code text file (.obj extension). The object code file is readable
by the AGC simulator.

Syntax:
Source code files are text files containing multiple lines of source code.
Each line is terminated by a newline character. Source code files can be
produced by any editor, as long as it doesn't insert any hidden characters
or formatting information.

Each line of assembly code consists of one of the following:


a) a blank line;
b) a comment (comments must begin with a semicolon (;));
c) or a line of assembler code.

The assembler ignores blank lines and anything that occurs after a semicolon
on any given line.

A line of assembler code consists of the following components:


1) a label (this is optional);
2) an op code or assembler directive;
3) an operand (also optional);
4) a comment (optional; comments must start with a semicolon)

The components, if present, must appear in the order given. Each component is
separated from the next by one or more white spaces or tabs. The only constraint
is that the label, if present, must start in the 1st column of the line. If no
label is present, the op code or assembler directive must not start in the 1st
column, but may appear in any subsequent column.

The operand may consist of one of the following:


a) an asterisk (the assembler substitutes in the current value of the
location counter;
b) a symbolic name (the assemble substitutes in the value during the
second pass of the assembly;
c) a numeric constant. Octal contants must be preceeded by a '%';
hexadecimal constants are preceeded by a '$'; anything else
is treated as a decimal.
d) an expression consisting of one or more of the above, separated by
the operators: +, -, @ (for multiplication), or / (for division).
Unary plus or minus is also allowed.
examples:
*+2 means location counter plus 2
LABEL+%10/2
-5
Assembler Directives:
The following directives are supported:
ORG - set the location counter to the operand following ORG.
EQU - set the label to the operand value.
DS - define a word of storage; set the value to the operand value.
CADR - define a word of storage; set the value to the operand value
(assumed to be a 14-bit address; this is an alias for CADR).
ADRES - define a word of storage; set the value to the operand value
(operand is treated as a 12-bit address).
INCL - inline include a file; the operand contains the filename.

Addressing:
The location counter and symbol table always contain the full 14-bit address.
AGC instructions require a 12-bit address as an operand. Addressing is implemented
in 1K banks (bits 10 through 1). Bits 12 and 11 select banks 0 (erasable memory),
1 (fixed-fixed), or 2 (fixed-fixed). These banks are directly addressable in
12-bits. Banks above 2 are called 'fixed-switchable' and are addressed using
the BANK register: bits 12 and 11 of the operand are set to '1' and the bank
register provides the high-order 4 bits (bits 14 through 11) of the address.
The lower 10 bits of the operand provide the address within the bank.

Errata:
The assembler ignores the last line of the source (.asm) file. If the last
line of your source file contains code, you must add an additional blank
line to the end of your source file to ensure that your last line of code
is assembled.

The symbol table should be sorted before the second pass. The linear
search through the symbol table should be replaced by a more efficient
method.

The assembler directives and syntax don't match those of the original block
I AGC.

A macro definition capability would be handy.

Generates empty .lst and .obj files if the source code file does not exist.

Incorrectly assigns a zero value to labels that equate to a forward referenced


label. i.e.:
LABEL1 EQU LABEL2 ; assembler incorrectly sets LABEL1
to zero
LABEL2 EQU *

The assembler generates object code for eraseable memory addresses.


*/

#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <iostream.h>
#include <stdio.h>

// Create a flag for each memory location in a 14-bit address space


// These are initialized to false, and then set as the code is assembled.
// An attempt to assemble code on top of a previously used location is
// flagged as an error.
const unsigned agcMemSize = 037777+1; // # of cells in a 14-bit address range
bool memoryUsed [agcMemSize];

//***********************************************************************
// MODIFIED FROM THE ORIGINAL ASSEMBLER HERE.
// This array represents the fixed memory PROM (14-bit address)
int EPROM_H [agcMemSize];
int EPROM_L [agcMemSize];
//***********************************************************************

unsigned pass = 0;
unsigned locCntr = 0; // address in 14-bit format
unsigned errorCount = 0;

FILE* fpList = 0; // output (assembly listing)


//***********************************************************************
// MODIFIED FROM THE ORIGINAL ASSEMBLER HERE.
FILE* fpObj_H = 0; // output MSB's (object code in S-RECORD format)
FILE* fpObj_L = 0; // output LSB's (object code in S-RECORD format)
//***********************************************************************

struct ocode
{
char* name;
unsigned code;
bool isOpCode; // true=it is an op code
bool addrOpnd; // true=convert operand to 12-bit address format
unsigned len; // words
};

ocode allcodes[] =
{
// Block I op codes.
{ "TC", 0000000, true, true, 1 },
{ "CCS", 0010000, true, true, 1 },
{ "INDEX", 0020000, true, true, 1 },
{ "XCH", 0030000, true, true, 1 },
{ "CS", 0040000, true, true, 1 },
{ "TS", 0050000, true, true, 1 },
{ "AD", 0060000, true, true, 1 },
{ "MASK", 0070000, true, true, 1 },
{ "MP", 0040000, true, true, 1 },
{ "DV", 0050000, true, true, 1 },
{ "SU", 0060000, true, true, 1 },

// Implied address codes (R393: 3-12)


{ "RESUME", 0020025, true, false, 1 }, // INDEX 25
{ "EXTEND", 0025777, true, false, 1 }, // INDEX 5777
{ "INHINT", 0020017, true, false, 1 }, // INDEX 17
{ "RELINT", 0020016, true, false, 1 }, // INDEX 16
{ "XAQ", 0000000, true, false, 1 }, // TC A
{ "RETURN", 0000001, true, false, 1 }, // TC Q
{ "NOOP", 0030000, true, false, 1 }, // XCH A
{ "COM", 0040000, true, false, 1 }, // CS A
{ "TCAA", 0050002, true, false, 1 }, // TS Z
{ "OVSK", 0050000, true, false, 1 }, // TS A
{ "DOUBLE", 0060000, true, false, 1 }, // AD A
{ "SQUARE", 0040000, true, false, 1 }, // MP A

// For "clarity" (R393: 3-12)


{ "TCR", 0000000, true, true, 1 }, // same as TC; subroutine call with return
{ "CAF", 0030000, true, true, 1 }, // same as XCH; address to fixed acts like
Clear and Add
{ "OVIND", 0050000, true, true, 1 }, // same as TS

// Assembler directives
{ "DS", 0, false, false, 1 }, // define storage; reserves 1 word of memory
{ "CADR", 0, false, false, 1 }, // define 14-bit addr; reserves 1 word of memory
{ "FCADR", 0, false, false, 1 }, // define 14-bit addr; reserves 1 word of memory
{ "ECADR", 0, false, false, 1 }, // define 14-bit addr; reserves 1 word of memory
{ "ADRES", 0, false, true, 1 }, // define 12-bit addr; reserves 1 word of memory
{ "ORG", 0, false, false, 0 }, // origin; sets location counter to operand value
{ "EQU", 0, false, false, 0 }, // equate; assigns a value to a label
{ "INCL", 0, false, false, 0 }, // include; inline include source code
{ "", 0, false, false, 99 } // end-of-list flag
};

void parse(char* buf, char* labl, char* opcd, char* opnd, char* cmnt)
{
// strip off newline char.
buf[strlen(buf) - 1] = '\0';

// replace any horizontal tabs with spaces


for(unsigned i=0; i<strlen(buf); i++)
{
if(buf[i] == ';') break; // don't edit comments
if(buf[i] == '\t') buf[i] = ' ';
}

strcpy(labl,"");
strcpy(opcd,"");
strcpy(opnd,"");
strcpy(cmnt,"");

char* sp = buf;
char* s = 0;

enum {_labl=0, _opcd, _opnd, _cmnt } mode = _labl;

if(buf[0] == ' ') mode = _opcd;


do
{
s = strtok(sp, " "); sp = 0;
if(s)
{
if(s[0] == ';')
{
// Copy the remainder of the comment verbatim.
strcat(cmnt, s); strcat(cmnt, " ");
s += strlen(s) + 1; strcat(cmnt, s);
return;

}
switch(mode)
{
case _labl: strcat(labl, s); mode = _opcd; break;
case _opcd: strcat(opcd, s); mode = _opnd; break;
case _opnd: strcat(opnd, s); mode = _cmnt; break;
}
}
} while(s);
}

struct symbl
{
char name[20];
unsigned val;
};

symbl symTab[5000];
unsigned nSym = 0;

// Pre-defined symbols corresponding to architectural


// conventions in the block I AGC4.
symbl constSymTab[] =
{
{ "A", 00 },
{ "Q", 01 },
{ "Z", 02 },
{ "LP", 03 },
{ "IN0", 04 },
{ "IN1", 05 },
{ "IN2", 06 },
{ "IN3", 07 },
{ "OUT0", 010 },
{ "OUT1", 011 },
{ "OUT2", 012 },
{ "OUT3", 013 },
{ "OUT4", 014 },
{ "BANK", 015 },
{ "CYR", 020 },
{ "SR", 021 },
{ "CYL", 022 },
{ "SL", 023 },
{ "ZRUPT", 024 },
{ "BRUPT", 025 },
{ "ARUPT", 026 },
{ "QRUPT", 027 },
{ "OVCTR", 034 },
{ "TIME2", 035 },
{ "TIME1", 036 },
{ "TIME3", 037 },
{ "TIME4", 040 },
{ "GOPROG", 02000 },
{ "T3RUPT", 02004 },
{ "ERRUPT", 02010 },
{ "DSRUPT", 02014 },
{ "KEYRUPT", 02020 },
{ "UPRUPT", 02024 },
{ "EXTENDER", 05777 },
{ "BANK0", 000057 }, // erasable memory, just above the counter assignments
{ "BANK1", 002000 }, // fixed-fixed
{ "BANK2", 004000 }, // fixed-fixed
{ "BANK3", 006000 }, // start of fixed-switchable
{ "BANK4", 010000 },
{ "BANK5", 012000 },
{ "BANK6", 014000 },
{ "BANK7", 016000 },
{ "BANK10", 020000 },
{ "BANK11", 022000 },
{ "BANK12", 024000 },
{ "BANK13", 026000 },
{ "BANK14", 030000 },
{ "", 0 }
};

void add(char* labl, unsigned value)


{
// Check whether symbol is already defined.
unsigned i;
for(i=0; i<nSym; i++)
{
if(strcmp(symTab[i].name, labl)==0)
{
fprintf(fpList,"*** ERROR: %s redefined.\n", symTab[i].name);
errorCount++;
return;
}
}

// Add new symbol to symbol table


strcpy(symTab[nSym].name, labl);
symTab[nSym].val = value;
nSym++;
}

// Return the int value of the operand string. The string is


// assumed to be a simple value (not an expression)
int _getopnd(char* opnd)
{
if(strlen(opnd)==0)
return 0;
else if(opnd[0] == '$') // hex number
return strtol(opnd+1, 0, 16);
else if(opnd[0] == '%') // octal number
return strtol(opnd+1, 0, 8);
else if(isdigit(opnd[0])) // decimal number
return strtol(opnd, 0, 10);
else if(opnd[0] == '*')
return locCntr;
else // must be label; look up value
{
unsigned i;
for(i=0; i<nSym; i++)
{
if(strcmp(symTab[i].name, opnd)==0)
return symTab[i].val;
}

// Not there, so check whether symbol is an


// assembler-defined constant. If so, copy it to
// the user-defined symbols.
for(i=0; strcmp(constSymTab[i].name,"") != 0; i++)
{
if(strcmp(constSymTab[i].name, opnd)==0)
{
strcpy(symTab[nSym].name, opnd);
symTab[nSym].val = constSymTab[i].val;
nSym++;
return constSymTab[i].val;
}
}

if(pass == 1)
{
fprintf(fpList,"*** ERROR: %s undefined.\n", opnd);
errorCount++;
}
}
return 0;
}

// returns pointer to new position in istr


char* getToken(char* istr, char* ostr)
{
*ostr = '\0';

// bump past any whitespace


while(*istr == ' ') istr++;

if(*istr == '\0') return istr;

bool keepGoing = true;


do
{
*ostr = *istr;
if(*ostr == '+' || *ostr == '-' || *ostr == '@' || *ostr == '/')
keepGoing = false;
ostr++; istr++;
}
while(keepGoing && *istr != '\0' && *istr != '+' && *istr != '-'
&& *istr != '@' && *istr != '/');

*ostr = '\0';
return istr;
}

int _eval(char* sp, int tot)


{
if(*sp == '\0') return tot;

char op[20];
sp = getToken(sp, op);

char vstr[20];
int val = 0;
sp = getToken(sp,vstr);
if(*vstr =='-') // unary minus
{
sp = getToken(sp, vstr);
val = -(_getopnd(vstr));
}
else
val = _getopnd(vstr);

switch(*op)
{
case '+': tot += val; break;
case '-': tot -= val; break;
case '@': tot *= val; break;
case '/': tot /= val; break;
}

return _eval(sp,tot);
}

int eval(char* sp)


{
char op[20];
getToken(sp, op);
char sp1[80];
if(*op != '+' && *op != '-')
strcpy(sp1,"+");
else
strcpy(sp1,"");
strcat(sp1, sp);
return _eval(sp1, 0);
}

// Return the value of the operand string. The string may


// be a simple token or an expression consisting of multiple
// tokens and operators. Evaluation occurs from left to right;
// no parenthesis allowed. Unary minus is allowed and correctly
// evaluated. Valid operators are +, -, @, and /. The @ operator
// is multiplication (the traditional * operator already is used
// to refer to the location counter.
unsigned getopnd(char* opnd)
{
//return _getopnd(opnd); // the old call did not allow for expressions

unsigned retval = 0;
if(strcmp(opnd,"-0") == 0 || strcmp(opnd,"-%0") == 0 || strcmp(opnd,"-$0") == 0)
retval = 077777; // -0
else
{ // return the int value of the operand
int opndVal = eval(opnd);

// now, convert the number into 16-bit signed AGC format


if(opndVal < 0)
{
// convert negative values into AGC 16-bit 1's C form.
opndVal = 077777 + opndVal;
if(opndVal < 0)
{
fprintf(fpList,"*** ERROR: %s underflowed.\n", opnd);
errorCount++;
opndVal = 0;
}
}
else if(opndVal > 077777)
{
fprintf(fpList,"*** ERROR: %s overflowed.\n", opnd);
errorCount++;
opndVal = 0;
}
retval = (unsigned) opndVal;
}
return retval;
}

bool isDefined(char* opcd)


{
for(int j=0; allcodes[j].len != 99; j++)
{
if(strcmp(allcodes[j].name, opcd) == 0)
{
return true;
}
}
return false;
}

unsigned getopcode(char* opcd)


{
for(int j=0; allcodes[j].len != 99; j++)
{
if(strcmp(allcodes[j].name, opcd) == 0)
{
return allcodes[j].code;
}
}
fprintf(fpList,"*** ERROR: %s undefined.\n", opcd);
errorCount++;

return 0;
}

bool has12bitAddr(char* opcd)


{
for(int j=0; allcodes[j].len != 99; j++)
{
if(strcmp(allcodes[j].name, opcd) == 0)
{
return allcodes[j].addrOpnd;
}
}
return false;
}

bool isOpCode(char* opcd)


{
for(int j=0; allcodes[j].len != 99; j++)
{
if(strcmp(allcodes[j].name, opcd) == 0)
{
return allcodes[j].isOpCode;
}
}
return false;
}

unsigned getoplen(char* opcd)


{
for(int j=0; allcodes[j].len != 99; j++)
{
if(strcmp(allcodes[j].name, opcd) == 0)
{
return allcodes[j].len;
}
}
return 0;
}

void updateLocCntr(char* opcd, char* opnd)


{
unsigned size = 0;
for(int i=0; allcodes[i].len != 99; i++)
{
if(strcmp(allcodes[i].name, opcd) == 0)
{
size = allcodes[i].len; break;
}
}
locCntr += size;

if(strcmp(opcd,"ORG") == 0)
{
locCntr = getopnd(opnd);
}
}

unsigned genOddParity(unsigned r)
{
//check the lower 15 bits of 'r' and return the odd parity
unsigned evenParity =
(1&(r>>0)) ^ (1&(r>>1)) ^ (1&(r>>2)) ^ (1&(r>>3)) ^
(1&(r>>4)) ^ (1&(r>>5)) ^ (1&(r>>6)) ^ (1&(r>>7)) ^
(1&(r>>8)) ^ (1&(r>>9)) ^ (1&(r>>10)) ^ (1&(r>>11)) ^
(1&(r>>12)) ^ (1&(r>>13)) ^ (1&(r>>14));
return ~evenParity & 1; // odd parity
}

// Read the source file and build the symbol table.


void readSourceForPass1(char* fn)
{
char buf[256];
char labl[100]; // label
char opcd[100]; // op code
char opnd[100]; // operand
char cmnt[100]; // comment

// Open the source code file.


FILE* fp = fopen(fn, "r");
if(!fp)
{
perror("fopen failed for source file");
return;
}

while(fgets(buf, 256, fp))


{
parse(buf, labl, opcd, opnd, cmnt);

if(strcmp(opcd,"INCL")==0)
readSourceForPass1(opnd);

if(strlen(labl)>0)
{
if(strcmp(opcd,"EQU")==0)
add(labl, getopnd(opnd));
else
add(labl, locCntr);
}
updateLocCntr(opcd, opnd);
}
fclose(fp);
}

// Read the source file and symbol table and build


// the object code
void readSourceForPass2(char* fn)
{
char buf[256];
char labl[100]; // label
char opcd[100]; // op code
char opnd[100]; // operand
char cmnt[100]; // comment

// Open the source code file.


FILE* fp = fopen(fn, "r");
if(!fp)
{
perror("fopen failed for source file");
return;
}

while(fgets(buf,256,fp))
{
parse(buf, labl, opcd, opnd, cmnt);

if(strcmp(opcd,"INCL")==0)
{
// Include directive (INCL).
fprintf(fpList, " %-14s %-8s %-14s %s\n",
labl, opcd, opnd, cmnt);
readSourceForPass2(opnd);
}
else if(strcmp(opcd,"")==0)
{
// Comment.
fprintf(fpList, " %s\n", cmnt);
}
else if(getoplen(opcd) == 0)
{
// Must be ORG or EQU assembler directive.
fprintf(fpList, " %-14s %-8s %-14s %s\n",
labl, opcd, opnd, cmnt);

if(!isDefined(opcd))
{
fprintf(fpList,"*** ERROR: %s undefined.\n", opcd);
errorCount++;
}
}
else
{
// Since we got this far, we know the assembly line contains a
// valid 'opcd' that reserves some storage space. It must be an
// instruction or a DS.

// Location counter (locCntr) contains 14-bit address; symbol table


// also stores 14-bit addresses. If the operand is an address above
// bank 3 (05777), it is not directly addressable and needs to be
// converted into a 12-bit bank address. In bank addressing, bits
// 12,11 are set and bits 10-1 contain the address inside the bank.
// (The programmer must set the bank register to select the correct
// bank.)

// Generate a string containing the address (for the list file).


// If the address is erasable, or fixed-fixed, show the 12-bit
// address.
// If the address is fixed-switchable, show the bank number,
// followed by the 10-bit bank address.
unsigned locCntrBank = (036000 & locCntr) >> 10;
char locCntrString[20];
if(locCntrBank <= 3)
sprintf(locCntrString, " %04o", locCntr);
else
sprintf(locCntrString, "%2o,%04o", locCntrBank, 01777 & locCntr);

// Generate the data to be stored at that address. Convert to


// 12-bit address format, if necessary.
unsigned operValue = getopnd(opnd);
unsigned operBank = (036000 & operValue) >> 10;
if(has12bitAddr(opcd))
{
// Convert operand from a 14-bit address to a 12-bit
// address.
// First, find bank (bits 14-11). If the bank is <= 03, no
// conversion is necessary.
if(operBank > 03)
{
// Bank is not directly addressable, so get 10 bit
// bank address and set fixed-switchable flag bits
// 12 and 11.
operValue = (01777 & operValue) | 06000;
}
}
unsigned data = getopcode(opcd) + operValue;
data |= genOddParity(data) << 15;

// Generate a string containing the data info for the list file.
char dataString[20];
if(isOpCode(opcd))
sprintf(dataString, "%01o %2o,%04o %1o",
(getopcode(opcd) & 070000) >> 12, operBank, operValue,
genOddParity(data));
else
sprintf(dataString, " %05o %1o", operValue, genOddParity(data));

if(memoryUsed[locCntr])
{
fprintf(fpList,"*** ERROR: %06o address already in use.\n",
locCntr);
errorCount++;
}
memoryUsed[locCntr] = true;

fprintf(fpList, "%05o %7s %11s %-14s %-8s %-14s %s\n",


locCntr, locCntrString, dataString, labl, opcd, opnd, cmnt);

//***********************************************************************
// MODIFIED FROM THE ORIGINAL ASSEMBLER HERE.
// Insert the assembled code into an array, indexed by the address.
// This has the effect of sorting the data by address, so we can walk
// through the addresses and output the code to EPROM later.
unsigned dataLow = 0x00ff & data;
unsigned dataHigh = (0xff00 & data) >> 8;

if(locCntr >=1024) // FIXED MEMORY only; not ERASEABLE


{
// fixed memory
EPROM_H [locCntr] = dataHigh;
EPROM_L [locCntr] = dataLow;
}
//***********************************************************************

updateLocCntr(opcd, opnd);
}
fclose(fp);
}

//***********************************************************************
// MODIFIED FROM THE ORIGINAL ASSEMBLER HERE.

void writeEPROM(FILE* fpObj, int EPROM[])


{
// Write an EPROM file using Motorola's S-Record format (s2f).

// Some parameters that control file format. You can change maxBytes
// without affecting anything else. 'addressBytes' is determined by
// the choosen S-Record format.
const int maxBytes = 20; // set limit on record length
const int addressBytes = 3; // 16-bit address range
const int sumCheckBytes = 1;

const int maxdata = maxBytes - addressBytes - sumCheckBytes;

int i=0; // current EPROM address


int sumCheck = 0;
while (i < agcMemSize)
{
// get dataByteCount; the number of bytes of EPROM data per record.
int dataByteCount = maxdata;
if(i + dataByteCount >= agcMemSize)
{
dataByteCount = agcMemSize - i;
}
// write record header (*** 2 byte address assumed ***)
int totalByteCount = dataByteCount + addressBytes + sumCheckBytes;
fprintf(fpObj, "S2%02X%06X", totalByteCount, i);
sumCheck = totalByteCount & 0xff;
sumCheck = (sumCheck + ((i & 0xff0000) >> 16)) % 256;
sumCheck = (sumCheck + ((i & 0x00ff00) >> 8)) % 256;
sumCheck = (sumCheck + ((i & 0x0000ff) )) % 256;

// write data bytes into record


for(int j=0; j<dataByteCount; j++)
{
fprintf(fpObj, "%02X", EPROM [i+j]);
sumCheck = (sumCheck + EPROM [i+j]) % 256;

}
// terminate record by adding the checksum and a newline.
fprintf(fpObj, "%02X\n", (~sumCheck) & 0xff);

i += dataByteCount;
}
// write an end-of-file record here
i=0; // set address zero for last record
sumCheck = 0x04; // byte count
sumCheck = (sumCheck + ((i & 0xff0000) >> 16)) % 256;
sumCheck = (sumCheck + ((i & 0x00ff00) >> 8)) % 256;
sumCheck = (sumCheck + ((i & 0x0000ff) )) % 256;
fprintf(fpObj, "S804%06X%02X", i, (~sumCheck) & 0xff);

//***********************************************************************

void main(int argc, char* argv[])


{
cout << "AGC Block I assembler" << endl;

// The assembler reads an assembly source code file


// with a .asm extension; i.e.: myProg.asm
// It writes an assembly listing text file with
// a .lst extension (myProg.lst) and an object code
// text file with a .obj extension (myProg.obj)
#ifdef NOTDEF
// use this to enter the source file using command line
if(argc != 2)
{
cout << "*** ERROR: source file name not specified." << endl;
exit(-1);
}

fp = fopen(argv[1], "r");
#endif

char sourcefile[80];
cout << "Enter source file: ";
cin >> sourcefile;

// Valid source files have a .asm extension; strip the


// extension off so we can use the prefix for the list
// and object files.
char prefix[80];
strcpy(prefix, sourcefile);

char* p = prefix;
while(*p != '\0') { p++; if(*p == '.') break; }
if(strcmp(p,".asm") != 0)
{
cerr << "*** ERROR: Source file not *.asm" << endl;
exit(-1);
}
*p = '\0';

// Open a text file for the assembly listing. The filename


// will have a .lst extension.
char listfile[80];
sprintf(listfile, "%s.lst", prefix);
fpList = fopen(listfile, "w");
if(!fpList)
{
perror("fopen failed for assembly list file");
exit(-1);
}

//***********************************************************************
// MODIFIED FROM THE ORIGINAL ASSEMBLER HERE.
// Open a two text files for the object code. The filenames
// will have a .hex extension
char objfile[80];

sprintf(objfile, "%s_H.hex", prefix);


fpObj_H = fopen(objfile, "w");
if(!fpObj_H)
{
perror("fopen failed for object file");
exit(-1);
}

sprintf(objfile, "%s_L.hex", prefix);


fpObj_L = fopen(objfile, "w");
if(!fpObj_L)
{
perror("fopen failed for object file");
exit(-1);
}

fprintf(fpList,"Block I Apollo Guidance Computer (AGC4) assembler version 1.6 for


EPROM\n\n");

// INITIALIZE EPROM
for(int k=0; k< agcMemSize; k++)
{
EPROM_H [k] = 0;
EPROM_L [k] = 0;
}
//***********************************************************************

fprintf(fpList,"First pass: generate symbol table.\n");


readSourceForPass1(sourcefile);

locCntr = 0;
pass++;

// Clear the memory use flags; these are used to catch


// any overwriting of already assembled code.
memset(memoryUsed, false, sizeof(bool) * agcMemSize);

fprintf(fpList,"Second pass: generate object code.\n\n");


readSourceForPass2(sourcefile);

//***********************************************************************
// MODIFIED FROM THE ORIGINAL ASSEMBLER HERE.
// Write the EPROM data to file
writeEPROM(fpObj_H, EPROM_H);
writeEPROM(fpObj_L, EPROM_L);

fclose(fpObj_H);
fclose(fpObj_L);
//***********************************************************************

fprintf(fpList,"\nAssembly complete. Errors = %d\n", errorCount);

fprintf(fpList,"\nSymbol table:\n");

unsigned j=0;
for(unsigned i=0; i<nSym; i++)
{
fprintf(fpList,"%-14s %06o ", symTab[i].name, symTab[i].val);
j = (j+1) % 3;
if(j==0) fprintf(fpList,"\n");
}
fclose(fpList);
}
Block I
Apollo Guidance Computer (AGC)
How to build one in your basement

Part 7: C++ Simulator

John Pultorak
December, 2004
Abstract
This report describes my successful project to build a working reproduction of the 1964
prototype for the Block I Apollo Guidance Computer. The AGC is the flight computer for the
Ap ollo m oon lan din gs, and is the wo rld’s first in tegra ted c ircuit com pu ter.

I built it in my ba sem ent. It took me 4 yea rs.

If you like, you can build one too. It will take you less time, and yours will be better than
mine.

I docum ented m y project in 9 separate .pdf files:

Part 1 O v ervie w : Introdu ces the p roject.

Part 2 CTL Module: Design and construction of the control module.

Part 3 PROC M odule: Design and construction of the processing (CPU) modu le.

Part 4 MEM Module: Design and construction of the mem ory module.

Part 5 IO Module: Design and construction of the display/keyboard (DSKY) m odule.

Part 6 Assem bler: A cross-a ssem bler for AG C softw are dev elopm ent.

Part 7 C+ + S imu lator: A low-level simulator that runs assemb led AGC code.

Part 8 Flight Software: My translation of portions of the COLOSSUS 249 flight


software.

Part 9 Test & Che ckou t: A suite of test programs in AG C assembly langu age.
Overview
This documen t describes my AG C Block I C++ sim ulator. I developed it almost entirely from
detaile d inform ation in this d ocum ent:

A. Hopk ins, R. Alonso, and H . Blair-Smith, "Logical Description for the Apollo Guidan ce
Computer (AGC4)", R-393, MIT Instrumentation Laboratory, Cambridge, MA, Mar. 1963.

My simulator reproduces not only the AGC instruction set and user-accessible registers, but
all of the registers, all microinstructions, time-pulse generator states, read, write and
me mo ry busses, and con trol pulses (logic signals) for all AGC subsystem s.

The simulator is a tool I used to capture AGC design from the R-39 3 docum ent. When I got
it working well enough to run my test and checkout software suite (described in part 9) and
flight software (described in part 8), I knew I understood the AGC well enough to build one.

The sim ulato r head er and source co de files be cam e requ irem ents th at gu ided my AG C log ic
design (d escribed in parts 2-5 ).

Running the simulator

The simulator is run by keyboard comm ands. The output is a scrolling, formatted text
display; the compiler obligingly provides a little DOS window for viewing the output. It looks
like this (most of
the nu m bers are
in octal):

The to p line is
the revision
number of the
sim ula tor. I
went through
lots of versions.

The second line


sh ow s th e T im e
Pulse Generator
state (TP11) and
some of the
important scaler
outpu ts.

The nex t line shows th e current state of some sm all registers in the SEQ su bsystem , which
is part of the AGC control module. STA an d STB stage registers which select instruction
sub sequ ences . BR1 and 2 are th e bran ch regis ters. SNI is “sele ct next in struction ”, a 1-bit
register that does exactly that. CI is the “carry-in” bit for the ALU. The loop counter is used
for iterating throu gh arithm etic instructions.

The n ext line , starting w ith RP CELL , show s im portan t registers ass ociated with interrup ts
and the priority counters. The tail-end of the line shows the current instruction (in the SQ
register), which is an INDEX instruction. The subsequence is NDX0.
The ne xt lin e (C P) sh ow s curre ntly asse rted c ontrol pu lses (l ogic signals). ST1 a nd WE are
being asserted.

The left side of the next 4 lines shows the state of registers associated with memory (S, G,
P, P2, and C ADR ), the ALU (B , X, Y, U), and the read b us (RB U) and write bus (W BU).

The right side of those 4 lines shows control inputs for running, stepping, and clocking the
sim ula tor.

The bottom part of the display shows AGC m emory. The 2-digit num bers on the left show
mem ory addresses from 00-56 . Each mem ory location has a nam e; it’s shown to the right of
the address. Immediately to the right of that is the contents of that location.

Add resses 00 -17 a re m app ed to A GC registers, an d are n ot really p art of the A GC eraseab le
memory. Addresses 00-03 are the AGC central registers, followed by input and output
registers.

Addresses 16 and 17 are not storage locations, but a means for enabling and disabling
interrupts.

The eraseab le mem ory starts at address 20. Add resses 20-23 a re the editing registers.
Writing to these causes the data in the registers to be shifted or rotated.

Add resses 24-27 a re used for saving the cen tral registers (00-03) wh en an in terrupt occurs.

Add resses 34-56 a re priority counter locations. The AG C will increm ent or decrem ent these
based on + pr - logic signa ls to the priority counter cells.

The lower right of the simulator is the DSKY: the display/keyboard user interface for the
astron auts. Th e sim ulato r is runn ing th e CO LOS SU S 24 9 fligh t softwa re load, a nd is
curren tly execu ting m ajor m ode 0 (P00 ), verb 16 , noun 36, w hich is a m onitor p rogram to
continuously display the command module elapsed time clock. The clock, displayed in R1,
R2, and R3, shows 0 hours, 1 minute, and 54.23 seconds. It updates about once a second
but, of course, you can’t see that here.

Compiler

The simulator was compiled with Microsoft Visual C++ 6.0 Standard Edition.
Commands
Here’s the comp lete list of com ma nds the sim ulator know s. The keyboa rd key you h it is:
{Q}, and the name of the command is: <QUIT>.

Simulator commands
{q} <QUIT> Exits the sim ula tor.
{l} <LOAD> The comm and is a lower case “L”, not a “1". Load fixed
m em ory w ith objec t cod e pro du ced by th e ass em bler.
The object code files are in Motorola S-Record format
(compatible with EPROM programmers). The command
will ask for a filenam e.
{m} <MENU> Intended to be a useful menu of simulator command,
but I ne ver got a roun d to it.

Hardware reset commands


{p} <POW ER UP RESET> Asserts the PURST control signal. This is a power-up
reset sign al tha t is supp osed to be au tom atically
generated when the AGC initially powers on.
{h} <RESET> Asserts th e GEN RS T con trol signa l.

Clock controls
{F1} <CLK> Single-step the AGC clock. Only works when MCLK <F2>
has been selected.
{F2} <MCLK> Asserts the MCLK control signal. Disables the free-
running 1MHz clock. When MCLK is selected, you can
single-step the clock by by pressing <F1>.
{F4} <FCLK> Asserts the FCLK control signal. Causes the simu lator
clock to free-run at 1MHz. This is the normal operational
mod e.
Time pulse generator (TPG) controls
{r} <RUN> Toggles b etween the “run” (1) an d “step” (0) m odes.
“Run ” mak es the AG C free-run (the norm al mo de).
“Step” single-steps the AGC, either by instruction or by
instruction subsequence.
{s} <STEP> Steps the AGC to the next instruction or instruction
sequence when <R> is toggled to the step mode.
{n} <INST> Toggles whether the AGC steps by instruction (1) or
instruction subsequence (0). Each instruction contains
one or more subsequences. Each subsequence is 12
steps or timing pulses long.

Debugger commands
{e} <EXAM INE> Examines the contents of memory. The comman d asks
for a sta rting add ress a nd then disp lays the m em ory
data at that ad dress and follow ing locations.
{y} <WATCH> Halts the AGC wh en any instruction changes a watched
m em ory lo catio n. Th e com m and as ks for a m em ory
address (CADR) to watch.
{b} <BREAK POINT> Toggles a breakpoint on/off. When the breakpoint is on,
it halts the AGC when instruction execution hits that
address.
{d} <DISPLAY> Displays or refreshes the standard AGC register display.
{f} <DEBUG> Displays the currently executing AGC source code. You
can single step with this display and watch the AGC
move through the source code. Very useful for
debugging. A “>” arrow shows the next instruction to be
executed in the listing.

Scaler controls
{z} <F17> Manu ally generates the <F17 > scaler pulse. Useful for
testing when the scaler has been toggled to off <C>, or
when you’re single-stepping the AGC.
{x} <F13> Manu ally generates the <F13 > scaler pulse. Useful for
testing when the scaler has been toggled to off <C>, or
when you’re single-stepping the AGC.
{c} <TOGGLE SCALER> Toggle the scaler on/off. When the scaler is off, the F13
and F17 signals are not automatically generated.

Priority counter controls


{[} <-CNTR> Ma nua lly assert a m inus inpu t to a prority co unte r cell.
The com m and w ill ask for the cell n um ber.
{]} <+CNTR> Ma nua lly assert a p lus inp ut to a p riority coun ter cell.
The com m and w ill ask for the cell n um ber.
Interrupt controls
{i} <INTERRUPT> Generates an AGC interrupt. The comm and will ask you
for an interrupt num ber (1-5).

Other AGC controls


{a} <STANDBY ALLOWED> The standby allowed switch lets the AGC software put
the AGC in a standby m ode.
{;} <CLEAR PARITY ALARM> Clears the parity alarm. The alarm is generated when an
error oc curs (odd pa rity) in m em ory.

DSKY controls
{/} <VERB> The VERB key on the DSKY display.
{*} <NOUN> The NOUN key on the DSKY display.
{-} <MINUS> The MINUS key on the DSKY display.
{+} <PLUS> The PLUS key on the DSKY display.
{.} <CLEAR> The CLEAR key on the DSKY display.
{j} <ENTER> The ENTER key on the DSKY display.
{g} <KEY R EL> The KEY RELEASE key on the DSKY display.
Simulator demonstration
Here’s the sim ulato r, dem onstra ting so m e CO LOS SU S 24 9 fligh t softwa re functio ns. Th is is
the same scenario I ran in Part 1 using my hardware AGC.

Initialization

At startup, the simulator loads the microinstructions from the EPROM tables. These are the
sam e tables I eventually use d to program the hardw are AGC EPRO Ms.

Reading EPR OM: CP M1_8 .hex


Reading EPR OM: CP M9_1 6.hex
Reading EPR OM: CP M17_ 24.hex
Reading EPR OM: CP M25_ 32.hex
Reading EPR OM: CP M33_ 40.hex
Reading EPR OM: CP M41_ 48.hex
Reading EPR OM: CP M49_ 56.hex

The simulator is now initialized


and read y for comm ands.

<LOAD>

The simulator asks, and I enter the name of object files containing the COLOSSUS flight
software.

<POWER UP RESET> <RUN>


<FCLK>

I tell the sim ula tor to start


running, and enable the free-
runn ing clo ck. The AG C starts
running in real-time with the 1MHz
clock. The DSK Y shows m ajor
mo de 00 (P0 0).
Display elapsed time from
the CM clock

<VERB> <0> < 6> <NOUN> <3>


<6> <ENTER>

Test display lights

<VERB> <3> <5> <ENTER>

All DSKY lamps and display


segm ents illum inate for 5 sec;
after 5 sec, the DSKY lamps
extinguish.

Load component 1 for


dataset at octal address
50 with octal 123

<VERB> <2> < 1> <NOUN> <0>


<1> <ENTER>

Verb/noun display flashes: waiting


for address. Flashing is indicated
by the asterisk to the right of the
NOUN display.
<5> <0> <ENTER>

Verb/no un disp lay flash continu es:


waiting for data.

<1> <2> <3> <ENTER>

Octal word from R1 is loaded at


address 50.

Start a monitor program


to continuously display
elapsed time from the CM
clock

<VERB> <1> < 6> <NOUN> <3>


<6> <ENTER>
Display component 1 of
dataset at octal address
50

<VERB> <0> < 1>

The key rel light flashes be cause


the CM clock monitor program has
been suspended. This is indicated
by an asterisk in the KR display
above the DSKY.

<NOUN> <0> <1> <ENTER>

Verb/noun display flashes: waiting


for address.

<5> <0> <ENTER>

Octa l word from a ddres s 50 is


displayed in R1.
Increment the address

<NOUN> <1> <5> <ENTER>

Octa l word from a ddres s 51 is


displayed in R1, address in R3.

<ENTER>

Octa l word from a ddres s 52 is


displayed in R1, address in R3.

Resume the CM clock


monitor program

<KEY R EL>

Verb 16, noun 36 reappears, along


with the clock display. Notice that
the K R ligh t (asterisk) g oes ou t.
Terminate the CM clock
monitor program

<VERB> <3> <4> <ENTER>

Change major mode to


P00

<VERB> <3> <7> <ENTER>

Verb/no un disp lay flashes:


waiting for major mode.

<0> <0> <ENTER>


The 20 -or-so
subsystems in the AGC
are represented by C++
classes. There are some
additional classes for
registers and other
things.

I wanted a simulator
architec ture I could
develop quickly that
wou ld easily and directly
m ap to a hard wa re logic
design. I went through
16 versions of the
sim ula tor; th ey’re
discussed at the top of
the A GC Ma in.cpp file
wh ich contains,
unsurprisingly, the
ma in().

If you want to run the


simulator, you can
compile it from the
source code given here. To run it, you’ll also need the assembler (discussed in part 6), some
AGC software (parts 8 and 9), and the EPROM tables in Motorola S-Record format. The C++
code to generate these tables is given at the end of part 2.

Here it is, warts a nd all...


Main (AGCMain.cpp)

/****************************************************************************
* AGC4 (Apollo Guidance Computer) BLOCK I Simulator
*
* AUTHOR: John Pultorak
* DATE: 07/29/02
* FILE: AGCmain.cpp
*
* VERSIONS:
* 1.0 - initial version.
* 1.1 - fixed minor bugs; passed automated test and checkout programs:
* teco1.asm, teco2.asm, and teco3.asm to test basic instructions,
* extended instructions, and editing registers.
* 1.2 - decomposed architecture into subsystems; fixed minor bug in DSKY
* keyboard logic (not tested in current teco*.asm suite).
* Implemented scaler pulses F17, F13, F10. Tied scaler output to
* involuntary counters and interrupts. Implemented counter overflow
* logic and tied it to interrupts and other counters. Added simple
* set/clear breakpoint. Fixed a bug in bank addressing.
* 1.3 - fixed bugs in the DSKY. Added 14-bit effective address (CADR) to the
* simulator display output. Inhibited interrupts when the operator
* single-steps the AGC.
* 1.4 - performance enhancements. Recoded the control pulse execution code
* for better simulator performance. Also changed the main loop so it
* polls the keyboard and system clock less often for better performance.
* 1.5 - reversed the addresses of TIME1 and TIME2 so TIME2 occurs first.
* This is the way its done in Block II so that a common routine (READLO)
* can be used to read the double word for AGC time.
* 1.6 - added indicators for 'CHECK FAIL' and 'KEY RELS'. Mapped them to OUT1,
* bits 5 and 7. Added a function to display the current location in
* the source code list file using the current CADR.
* 1.7 - increased length of 'examine' function display. Any changes in DSKY now
* force the simulator to update the display immediately. Added a 'watch'
* function that looks for changes in a memory location and halts the
* AGC. Added the 'UPTL', 'COMP', and "PROG ALM" lights to the DSKY.
* 1.8 - started reorganizing the simulator in preparation for H/W logic design.
* Eliminated slow (1Hz) clock capability. Removed BUS REQUEST feature.
* Eliminated SWRST switch.
* 1.9 - eliminated the inclusive 'OR' of the output for all registers onto the
* R/W bus. The real AGC OR'ed all register output onto the bus; normally
* only one register was enabled at a time, but for some functions several
* were simultaneously enabled to take advantage of the 'OR' function (i.e.:
* for the MASK instruction). The updated logic will use tristate outputs
* to the bus except for the few places where the 'OR' function is actually
* needed. Moved the parity bit out of the G register into a 1-bit G15
* register. This was done for convenience because the parity bit in G
* is set independently from the rest of the register.
* 1.10 - moved the G15 parity register from MBF to the PAR subsystem. Merged SBFWG
* and SBEWG pulses into a single SBWG pulse. Deleted the CLG pulse for MBF
* (not needed). Separated the ALU read pulses from all others so they can
* be executed last to implement the ALU inclusive OR functions. Implemented
* separate read and write busses, linked through the ALU. Implemented test
* parity (TP) signal in PAR; added parity alarm (PALM) FF to latch PARITY
* ALARM indicator in PAR.
* 1.11 - consolidated address testing signals and moved them to ADR. Moved memory
* read/write functions from MBF to MEM. Merged EMM and FMM subsystems into
* MEM. Fixed a bad logic bug in writeMemory() that was causing the load of
* the fixed memory to overwrite array boundaries and clobber the CPM table.
* Added a memory bus (MEM_DATA_BUS, MEM_PARITY_BUS).
* 1.12 - reduced the number of involuntary counters (CTR) from 20 to 8. Eliminated
* the SHINC subsequence. Changed the (CTR) sequence and priority registers into
* a single synchronization register clocked by WPCTR. Eliminated the fifth
* interrupt (UPRUPT; INT). Eliminated (OUT) the signal to read from output
* register 0 (the DSKY register), since it was not used and did not provide
* any useful function, anyway. Deleted register OUT0 (OUT) which shadowed
* the addressed DSKY register and did not provide any useful function.
* Eliminated the unused logic that sets the parity bit in OUT2 for downlink
* telemetry.
* 1.13 - reorganized the CPM control pulses into CPM-A, CPM-B, and CPM-C groups.
* Added the SDV1, SMP1, and SRSM3 control pulses to CPM-A to indicate when
* those subsequences are active; these signals are input to CPM-C. Moved the
* ISD function into CPM-A. Fixed a minor bug causing subsequence RSM3 to be
* displayed as RSM0. Added GENRST to clear most registers during STBY.
* 1.14 - Moved CLISQ to TP1 to fix a problem in the hardware AGC. CLISQ was clearing
* SNI on CLK2 at TP12, but the TPG was advancing on CLK1 which occurs after
* CLK2, so the TPG state machine was not seeing SNI and was not moving to
* the correct state. In this software simulation, everything advances on
* the same pulse, so it wasn't a problem to clear SNI on TP12. Added a
* switch to enable/disable the scaler.
* 1.15 - Reenabled interrupts during stepping (by removing MON::RUN) signals from
* CPM-A and CPM-C logic). Interrupts can be prevented by disabling the scaler.
* Fixed a problem with INHINT1; it is supposed to prevent an interrupt
* between instructions if there's an overflow. It was supposed to be cleared
* on TP12 after SNI (after a new instruction), but was being cleared on TP12
* after every subsequence.
* 1.16 - Changed CPM-A to load and use EPROM tables for the control pulse matrix. The
* EPROM tables are negative logic (0=asserted), but this simulator expects
* positive logic, so each word is bit-flipped when the EPROM tables load
* during simulator initialization.
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo Guidance
* Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh Blair-Smith, R-393,
* MIT Instrumentation Laboratory, 1963.
*
* PORTABILITY:
* Compiled with Microsoft Visual C++ 6.0 standard edition. Should be fairly
* portable, except for some Microsoft-specific I/O and timer calls in this file.
*
* NOTE: set tabs to 4 spaces to keep columns formatted correctly.
*
*****************************************************************************
*/

#include <conio.h>

#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>

#include "reg.h"

#include "TPG.h"
#include "MON.h"
#include "SCL.h"
#include "SEQ.h"
#include "INP.h"
#include "OUT.h"
#include "BUS.h"
#include "DSP.h"
#include "ADR.h"
#include "PAR.h"
#include "MBF.h"
#include "MEM.h"
#include "CTR.h"
#include "INT.h"
#include "KBD.h"
#include "CRG.h"
#include "ALU.h"
#include "CPM.h"
#include "ISD.h"
#include "CLK.h"

extern bool dskyChanged;

//-----------------------------------------------------------------------
// CONTROL LOGIC

void genAGCStates()
{
// 1) Decode the current instruction subsequence (glbl_subseq).
// SEQ::glbl_subseq = CPM::instructionSubsequenceDecoder();

// 2) Build a list of control pulses for this state.


CPM::controlPulseMatrix();
// 3) Execute the control pulses for this state. In the real AGC, these occur
// simultaneously. Since we can't achieve that here, we break it down into the
// following steps:
// Most operations involve data transfers--usually reading data from
// a register onto a bus and then writing that data into another register. To
// approximate this, we first iterate through all registers to perform
// the 'read' operation--this transfers data from register to bus.
// Then we again iterate through the registers to do 'write' operations,
// which move data from the bus back into the register.

BUS::glbl_READ_BUS = 0; // clear bus; necessary because words are logical


// OR'ed onto the bus.
MEM::MEM_DATA_BUS = 0; // clear data lines: memory bits 15-1
MEM::MEM_PARITY_BUS = 0; // parity line: memory bit 16

// Now start executing the pulses:

// First, read register outputs onto the bus or anywhere else.


int i;
for(i=0; i<MAXPULSES && SEQ::glbl_cp[i] != NO_PULSE; i++)
{
CLK::doexecR(SEQ::glbl_cp[i]);
}

// Next, execute ALU read pulses. See comments in ALU .C file


ALU::glbl_BUS = 0;
for(i=0; i<MAXPULSES && SEQ::glbl_cp[i] != NO_PULSE; i++)
{
CLK::doexecR_ALU(SEQ::glbl_cp[i]);
}
BUS::glbl_WRITE_BUS = BUS::glbl_READ_BUS; // in case nothing is logically OR'ed below;
for(i=0; i<MAXPULSES && SEQ::glbl_cp[i] != NO_PULSE; i++)
{
CLK::doexecR_ALU_OR(SEQ::glbl_cp[i]);
}

// Now, write the bus and any other signals into the register inputs.

for(i=0; i<MAXPULSES && SEQ::glbl_cp[i] != NO_PULSE; i++)


{
CLK::doexecW(SEQ::glbl_cp[i]);
}

// Always execute these pulses.


SCL::doexecWP_SCL();
SCL::doexecWP_F17();
SCL::doexecWP_F13();
SCL::doexecWP_F10();
TPG::doexecWP_TPG();
}

//-----------------------------------------------------------------------
// SIMULATION LOGIC

// contains prefix for source filename; i.e.: the portion


// of the filename before .obj or .lst
char filename[80];

char* getCommand(char* prompt)


{
static char s[80];
char* sp = s;

cout << prompt; cout.flush();

char key;
while((key = _getch()) != 13)
{
if(isprint(key))
{
cout << key; cout.flush();
*sp = key; sp++;
}
else if(key == 8 && sp != s)
{
cout << key << " " << key; cout.flush();
sp--;
}
}
*sp = '\0';
return s;
}

bool breakpointEnab = false;


unsigned breakpoint = 0;
void toggleBreakpoint()
{
if(!breakpointEnab)
{
char b[80];
strcpy(b, getCommand("Set breakpoint: -- enter 14-bit CADR (octal): "));
cout << endl;

breakpoint = strtol(b,0,8);
breakpointEnab = true;
}
else
{
cout << "Clearing breakpoint." << endl;
breakpointEnab = false;
}
}

bool watchEnab = false;


unsigned watchAddr = 0;
unsigned oldWatchValue = 0;
void toggleWatch()
{
if(!watchEnab)
{
char b[80];
strcpy(b, getCommand("Set watch: -- enter 14-bit CADR (octal): "));
cout << endl;

watchAddr = strtol(b,0,8);
watchEnab = true;
oldWatchValue = MEM::readMemory(watchAddr);

char buf[100];
sprintf(buf, "%06o: %06o", watchAddr, oldWatchValue);
cout << buf << endl;
}
else
{
cout << "Clearing watch." << endl;
watchEnab = false;
}
}

void incrCntr()
{
char cntrname[80];
strcpy(cntrname, getCommand("Increment counter: -- enter pcell (0-19): "));
cout << endl;

int pc = atoi(cntrname);
CTR::pcUp[pc] = 1;
}

void decrCntr()
{
char cntrname[80];
strcpy(cntrname, getCommand("Decrement counter: -- enter pcell (0-19): "));
cout << endl;

int pc = atoi(cntrname);
CTR::pcDn[pc] = 1;
}
void interrupt()
{
char iname[80];
strcpy(iname, getCommand("Interrupt: -- enter priority (1-5): "));
cout << endl;

int i = atoi(iname) - 1;
INT::rupt[i] = 1;
}

#ifdef NOTDEF
// Load AGC memory from the specified file object file
void loadMemory()
{
strcpy(filename, getCommand("Load Memory -- enter filename: "));
cout << endl;

// Add the .obj extension.


char fname[80];
strcpy(fname, filename);
strcat(fname, ".obj");

FILE* fp = fopen(fname, "r");


if(!fp)
{
perror("fopen failed:");
cout << "*** ERROR: Can't load memory for file: " << fname << endl;
return;
}
unsigned addr;
unsigned data;
while(fscanf(fp, "%o %o", &addr, &data) != EOF)
{
MEM::writeMemory(addr, data);
}
fclose(fp);
cout << "Memory loaded." << endl;
}
#endif

static int loadBuf[0xffff+1]; // tempory buffer for assembling H,L memory data

void loadEPROM(char* fileName, bool highBytes)


{
cout << "Reading EPROM: " << fileName << endl;

// Open the EPROM file.


FILE* ifp = fopen(fileName, "r");
if(!ifp)
{
perror("fopen failed for source file");
exit(-1);
}

const int addressBytes = 3; // 24-bit address range


const int sumCheckBytes = 1;

char buf[4096]; // buffer holds a single S-Record


while(fgets(buf,4096,ifp))
{
// process a record
if(buf[0] != 'S')
{
cout << "Error reading start of EPROM record for: " << fileName << endl;
exit(-1);
}

char tmp[256];

strncpy(tmp, &buf[2], 2); tmp[2] = '\0';


int totalByteCount = strtol(tmp, 0, 16);
int mySumCheck = totalByteCount & 0xff;
strncpy(tmp, &buf[4], 6); tmp[addressBytes*2] = '\0';
int address = strtol(tmp, 0, 16);
mySumCheck = (mySumCheck + ((address & 0xff0000) >> 16)) % 256;
mySumCheck = (mySumCheck + ((address & 0x00ff00) >> 8)) % 256;
mySumCheck = (mySumCheck + ((address & 0x0000ff) )) % 256;

//cout << hex << totalByteCount << ", " << address << dec << endl;

int dataBytes = totalByteCount - addressBytes - sumCheckBytes;

int i = (addressBytes+2)*2; // index to 1st databyte char.


for(int j=0; j<dataBytes; j++)
{
// get a data byte
strncpy(tmp, &buf[i], 2); tmp[2] = '\0';
int data = strtol(tmp, 0, 16);
//cout << hex << data << dec << endl;
mySumCheck = (mySumCheck + data) % 256;

if(highBytes)
{
loadBuf[address] = loadBuf[address] | ((data << 8) & 0xff00);
}
else
{
loadBuf[address] = loadBuf[address] | (data & 0xff);
}
address++;

i+=2; // bump to next databyte char


}
strncpy(tmp, &buf[i], 2); tmp[2] = '\0';
int sumCheck = strtol(tmp, 0, 16);

if(sumCheck != ((~mySumCheck) & 0xff))


{
cout << "sumCheck failed; file: " << fileName
<< ", address: " << hex << address
<< ", sumCheck: " << sumCheck << ", mySumCheck: " << mySumCheck
<< dec << endl;
exit(-1);
}

}
fclose(ifp);
cout << "Memory loaded." << endl;
}

// Load AGC memory from the specified EPROM files


void loadMemory()
{
strcpy(filename, getCommand("Load Memory -- enter filename: "));
cout << endl;

char fname[80];

// Add the _H.hex extension.


strcpy(fname, filename);
strcat(fname, "_H.hex");

loadEPROM(fname, true);

// Add the _L.hex extension.


strcpy(fname, filename);
strcat(fname, "_L.hex");

loadEPROM(fname, false);

//*******************************************************************
// EPROM is now in loadBuf; move it to AGC memory.
// AGC fixed memory only uses NUMFBANK banks.
for(int address=1024; address < 1024*(NUMFBANK+1); address++)
{
// Don't load address region 0-1023; that region is allocated
// to eraseable memory.
//cout << "loading CADR=" << hex << address << endl;
MEM::writeMemory(address, loadBuf[address]);
}
//*******************************************************************
}

// Write the entire contents of fixed and


// eraseable memory to the specified file.
// Does not write the registers
void saveMemory(char* filename)
{
FILE* fp = fopen(filename, "w");
if(!fp)
{
perror("*** ERROR: fopen failed:");
exit(-1);
}
char buf[100];
for(unsigned addr=020; addr<=031777; addr++)
{
sprintf(buf, "%06o %06o\n", addr, MEM::readMemory(addr));
fputs(buf, fp);
}
fclose(fp);
}

void examineMemory()
{
char theAddress[20];
strcpy(theAddress, getCommand("Examine Memory -- enter address (octal): "));
cout << endl;

unsigned address = strtol(theAddress, 0, 8);

char buf[100];
for(unsigned i=address; i<address+23; i++)
{
sprintf(buf, "%06o: %06o", i, MEM::readMemory(i));
cout << buf << endl;
}
}

// Returns true if time (s) elapsed since last time it returned true; does not block
// search for "Time Management"
bool checkElapsedTime(time_t s)
{
if(!s) return true;

static clock_t start = clock();


clock_t finish = clock();

double duration = (double)(finish - start) / CLOCKS_PER_SEC;


if(duration >= s)
{
start = finish;
return true;
}
return false;
}

// Blocks until time (s) has elapsed.


void delay(time_t s)
{
if(!s) return;

clock_t start = clock();


clock_t finish = 0;
double duration = 0;

do
{
finish = clock();
}
while((duration = (double)(finish - start) / CLOCKS_PER_SEC) < s);
}

void updateAGCDisplay()
{
static bool displayTimeout = false;
static int clockCounter = 0;

if(checkElapsedTime(2)) displayTimeout = true;


if(MON::FCLK)
{
if(MON::RUN)
{
// update every 2 seconds at the start of a new instruction
if(displayTimeout || dskyChanged)
{
clockCounter++;
if(
(TPG::register_SG.read() == TP12 &&
SEQ::register_SNI.read() == 1) ||
(TPG::register_SG.read() == STBY) ||
clockCounter > 500 ||
dskyChanged)
{
MON::displayAGC();
displayTimeout = false;
clockCounter = 0;
dskyChanged = false;
}
}
}
else
{
static bool displayOnce = false;
if(TPG::register_SG.read() == WAIT)
{
if(displayOnce == false)
{
MON::displayAGC();
displayOnce = true;
clockCounter = 0;
}
}
else
{
displayOnce = false;
}
}
}
else
MON::displayAGC(); // When the clock is manual or slow, always update.
}

void showMenu()
{
cout << "AGC4 EMULATOR MENU:" << endl;
cout << " 'r' = RUN: toggle RUN/HALT switch upward to the RUN position." << endl;
}

const int startCol = 0; // columns are numbered 0-n


const int colLen = 5; // number of chars in column

const int maxLines = 23; // # of total lines to display


const int noffset = 10; // # of lines prior to, and including, selected line

const int maxLineLen = 79;

void showSourceCode()
{
// Add the .lst extension.
char fname[80];
strcpy(fname, filename);
strcat(fname, ".lst");

// Open the file containing the source code listing.


FILE* fp = fopen(fname, "r");
if(!fp)
{
perror("fopen failed:");
cout << "*** ERROR: Can't load source list file: " << fname << endl;
return;
}
cout << endl;

// Get the address of the source code line to display.


// The address we want is the current effective address is the
// S and bank registers.
char CADR[colLen+1];
sprintf(CADR, "%05o", ADR::getEffectiveAddress());

int op = 0; // offset index


long foffset[noffset];
for(int i=0; i<noffset; i++) foffset[i]=0;

bool foundit = false;


int lineCount = 0;

char s[256];
char valString[20];
char out[256];

while(!feof(fp))
{
if(!foundit)
{
foffset[op] = ftell(fp);
op = (op + 1) % noffset;
}

// Read a line of the source code list file.


if(fgets(s, 256, fp))
{
// Get the address (CADR) from the line.
strncpy(valString, s+startCol, colLen);
valString[colLen]='\0';

// 'foundit' is true after we have found the desired line.


if(foundit)
{
if(strcmp(valString,CADR) == 0)
cout << ">";
else
cout << " ";

// truncate line so it fits in 80 col display


strncpy(out, s, maxLineLen);
out[maxLineLen] = '\0';
cout << out;

lineCount++;
if(lineCount >= maxLines)
break;
}
else
{
if(strcmp(valString, CADR) == 0)
{
// Reposition the file pointer back several lines so
// we can see the code that preceeds the desired
// line, too.
foundit = true;
fseek(fp, foffset[op], 0);
}
}
}
}
fclose(fp);
}

void main(int argc, char* argv[])


{
CPM::readEPROM( "CPM1_8.hex", CPM::EPROM1_8);
CPM::readEPROM( "CPM9_16.hex", CPM::EPROM9_16);
CPM::readEPROM("CPM17_24.hex", CPM::EPROM17_24);
CPM::readEPROM("CPM25_32.hex", CPM::EPROM25_32);
CPM::readEPROM("CPM33_40.hex", CPM::EPROM33_40);
CPM::readEPROM("CPM41_48.hex", CPM::EPROM41_48);
CPM::readEPROM("CPM49_56.hex", CPM::EPROM49_56);

bool singleClock = false;

genAGCStates();
MON::displayAGC();

while(1)
{
// NOTE: assumes that the display is always pointing to the start of
// a new line at the top of this loop!

// Clock the AGC, but between clocks, poll the keyboard


// for front-panel input by the user. This uses a Microsoft function;
// substitute some other non-blocking function to access the keyboard
// if you're porting this to a different platform.
cout << "> "; cout.flush(); // display prompt

while( !_kbhit() )
{
if(MON::FCLK || singleClock)
{
// This is a performance enhancement. If the AGC is running,
// don't check the keyboard or simulator display every
// simulation cycle, because that slows the simulator
// down too much.
int genStateCntr = 100;
do {
CLK::clkAGC();
singleClock = false;

genAGCStates();
genStateCntr--;

// Needs more work. It doesn't always stop at the


// right location and sometimes stops at the
// instruction afterwards, too.
if(breakpointEnab &&
breakpoint == ADR::getEffectiveAddress())
{
MON::RUN = 0;
}

// Halt right after instr that changes a watched


// memory location.
if(watchEnab)
{
unsigned newWatchValue = MEM::readMemory(watchAddr);
if(newWatchValue != oldWatchValue)
{
MON::RUN = 0;
}
oldWatchValue = newWatchValue;
}

} while (MON::FCLK && MON::RUN && genStateCntr > 0);

updateAGCDisplay();

}
// for convenience, clear the single step switch on TP1; in the
// hardware AGC, this happens when the switch is released
if(MON::STEP && TPG::register_SG.read() == TP1) MON::STEP = 0;
}
char key = _getch();

// Keyboard controls for front-panel:


switch(key)
{
// AGC controls
// simulator controls

case 'q': cout << "QUIT..." << endl; exit(0);


case 'm': showMenu(); break;

case 'd':
genAGCStates();
MON::displayAGC();
break; // update display

case 'l': loadMemory(); break;


case 'e': examineMemory(); break;

case 'f':
showSourceCode();
break;

case ']':
incrCntr();
//genAGCStates();
//displayAGC(EVERY_CYCLE);
break;

case '[':
decrCntr();
//genAGCStates();
//displayAGC(EVERY_CYCLE);
break;

case 'i':
interrupt();
//genAGCStates();
//displayAGC(EVERY_CYCLE);
break;

case 'z':
//SCL::F17 = (SCL::F17 + 1) % 2;
genAGCStates();
MON::displayAGC();
break;

case 'x':
//SCL::F13 = (SCL::F13 + 1) % 2;
genAGCStates();
MON::displayAGC();
break;

case 'c':
MON::SCL_ENAB = (MON::SCL_ENAB + 1) % 2;
genAGCStates();
MON::displayAGC();
break;

case 'r':
MON::RUN = (MON::RUN + 1) % 2;
genAGCStates();
if(!MON::FCLK) MON::displayAGC();
break;

case 's':
MON::STEP = (MON::STEP + 1) % 2;
genAGCStates();
if(!MON::FCLK) MON::displayAGC();
break;

case 'a':
MON::SA = (MON::SA + 1) % 2;
genAGCStates();
MON::displayAGC();
break;

case 'n':
MON::INST = (MON::INST + 1) % 2;
genAGCStates();
MON::displayAGC();
break;

case 'p':
MON::PURST = (MON::PURST + 1) % 2;
genAGCStates();
MON::displayAGC();
break;

case 'b':
toggleBreakpoint();
break;

case 'y':
toggleWatch();
break;

case ';':
// Clear ALARM indicators
PAR::CLR_PALM(); // Asynchronously clear PARITY FAIL
MON::displayAGC();
break;

// DSKY:
case '0': KBD::keypress(KEYIN_0); break;
case '1': KBD::keypress(KEYIN_1); break;
case '2': KBD::keypress(KEYIN_2); break;
case '3': KBD::keypress(KEYIN_3); break;
case '4': KBD::keypress(KEYIN_4); break;
case '5': KBD::keypress(KEYIN_5); break;
case '6': KBD::keypress(KEYIN_6); break;
case '7': KBD::keypress(KEYIN_7); break;
case '8': KBD::keypress(KEYIN_8); break;
case '9': KBD::keypress(KEYIN_9); break;
case '+': KBD::keypress(KEYIN_PLUS); break;
case '-': KBD::keypress(KEYIN_MINUS); break;
case '.': KBD::keypress(KEYIN_CLEAR); break;
case '/': KBD::keypress(KEYIN_VERB); break;
case '*': KBD::keypress(KEYIN_NOUN); break;
case 'g': KBD::keypress(KEYIN_KEY_RELEASE); break;
case 'h': KBD::keypress(KEYIN_ERROR_RESET); break;
case 'j': KBD::keypress(KEYIN_ENTER); break;

case '\0': // must be a function key


key = _getch();
switch(key)
{
case 0x3b: // F1: single clock pulse (when system clock off)
singleClock = true; break;
case 0x3c: // F2: manual clock (FCLK=0)
MON::FCLK = 0; genAGCStates(); MON::displayAGC(); break;
case 0x3e: // F4: fast clock (FCLK=1)
MON::FCLK = 1; genAGCStates(); MON::displayAGC(); break;
default: cout << "function key: " << key << "="
<< hex << (int) key << dec << endl;
}
break;

//default: cout << "??" << endl;


default: cout << key << "=" << hex << (int) key << dec << endl;
}
}
}
ADR (ADR.h)

/****************************************************************************
* ADR - MEMORY ADDRESS subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: ADR.h
*
* VERSIONS:
*
* DESCRIPTION:
* Memory address for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef ADR_H
#define ADR_H

enum specialRegister { // octal addresses of special registers


// Flip-Flop registers
A_ADDR =00,
Q_ADDR =01,
Z_ADDR =02,
LP_ADDR =03,
IN0_ADDR =04,
IN1_ADDR =05,
IN2_ADDR =06,
IN3_ADDR =07,
OUT0_ADDR =010,
OUT1_ADDR =011,
OUT2_ADDR =012,
OUT3_ADDR =013,
OUT4_ADDR =014,
BANK_ADDR =015,

// No bits in these registers


RELINT_ADDR =016,
INHINT_ADDR =017,

// In eraseable memory
CYR_ADDR =020,
SR_ADDR =021,
CYL_ADDR =022,
SL_ADDR =023,
ZRUPT_ADDR =024,
BRUPT_ADDR =025,
ARUPT_ADDR =026,
QRUPT_ADDR =027,
};

class regS : public reg


{
public:
regS() : reg(12, "%04o") { }
};

class regBNK : public reg


{
public:
regBNK() : reg(4, "%02o") { }
};

class ADR
{
friend class MON;

friend class MEM;

friend class CLK;


friend class CPM;

public:
static void execWP_WS();
static void execRP_RBK();
static void execWP_WBK();

static bool GTR_17(); // for MBF, CPM


static bool GTR_27(); // for PAR
static bool EQU_16(); // for CPM
static bool EQU_17(); // for CPM
static bool EQU_25(); // for SEQ
static bool GTR_1777(); // for CPM

static unsigned getEffectiveAddress();

private:
static regS register_S; // address register
static regBNK register_BNK; // bank register

static unsigned bankDecoder();

static unsigned conv_WBK[];


};

#endif
ADR (ADR.cpp)

/****************************************************************************
* ADR - MEMORY ADDRESS subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: ADR.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "reg.h"
#include "ADR.h"
#include "SEQ.h"
#include "BUS.h"

regS ADR::register_S; // address register


regBNK ADR::register_BNK; // bank register

// transfer bits 14-11 from the bus into the 4-bit bank register
unsigned ADR::conv_WBK[] =
{ BX, BX, BX, BX, BX, BX, BX, BX, BX, BX, BX, BX, B14, B13, B12, B11 };

void ADR::execWP_WS()
{
register_S.write(BUS::glbl_WRITE_BUS);
}

void ADR::execRP_RBK()
{
BUS::glbl_READ_BUS = register_BNK.read() << 10;
}

void ADR::execWP_WBK()
{
register_BNK.writeShift(BUS::glbl_WRITE_BUS, ADR::conv_WBK);
}

bool ADR::GTR_27()
{
return (register_S.read() > 027);
}

bool ADR::GTR_17()
{
// check: address is not a central register
return (register_S.read() > 017);
}

bool ADR::EQU_25()
{
return (register_S.read() == 025);
}

bool ADR::EQU_17()
{
// check: instruction is INHINT (INDEX 017)
return (register_S.read() == 017);
}

bool ADR::EQU_16()
{
// check: instruction is RELINT (INDEX 016))
return (register_S.read() == 016);
}

bool ADR::GTR_1777()
{
// check: address is fixed memory
return (register_S.read() > 01777);
}

unsigned ADR::bankDecoder()
{
// Memory is organized into 13 banks of 1K words each. The banks are numbered
// 0-12. Bank 0 is erasable memory; banks 1-12 are fixed (rope) memory. The 10
// lower bits in the S register address memory inside a bank. The 2 upper bits
// in the S register select the bank. If the 2 upper bits are both 1, the 4-bit
// bank register is used to select the bank.
// 12 11 Bank
// 0 0 0 erasable memory
// 0 1 1 fixed-fixed 1 memory
// 1 0 2 fixed-fixed 2 memory
// 1 1 3-12 fixed-switchable memory (bank register selects bank)
unsigned bank = ADR::register_S.readField(12,11);
if(bank == 3)
{
// fixed-switchable
if(register_BNK.read() <= 03) // defaults to 6000 - 7777
return 03;
else
return register_BNK.read(); // 10000 - 31777
}
else
return bank; // erasable or fixed-fixed
}

unsigned ADR::getEffectiveAddress()
{
// Return the 14-bit address selected by lower 10 bits of the S register (1K)
// and the bank decoder (which selects the 1K bank)
unsigned lowAddress = ADR::register_S.readField(10,1);

if(ADR::bankDecoder() == 0)
return lowAddress;

unsigned highAddress = ADR::bankDecoder() << 10;


return highAddress | lowAddress;
}
ALU (ALU.h)

/****************************************************************************
* ALU - ARITHMETIC UNIT subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: ALU.h
*
* VERSIONS:
*
* DESCRIPTION:
* Arithmetic Unit for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef ALU_H
#define ALU_H

#include "reg.h"

class regB : public reg


{
public:
regB() : reg(16, "%06o") { }
};

class regCI : public reg


{
public:
regCI() : reg(1, "%01o") { }
};

class regX : public reg


{
public:
regX() : reg(16, "%06o") { }
};

class regY : public reg


{
public:
regY() : reg(16, "%06o") { }
};

class regU : public reg


{
public:
regU() : reg(16, "%06o") { }
virtual unsigned read();
};

class ALU
{
public:
static unsigned glbl_BUS; // mixes the RC and RU together for MASK

// In the hardware AGC, all read pulses are enabled simultaneously


// by CLK1. This simulator has to do the pulses one-at-a-time, so
// they are executed in the following sequence to mimic the hardware:
//
// 1) all read pulses involving subsystems other than ALU are executed,
// These read pulses output to the glbl_READ_BUS. Only 0 or 1
// of these pulses should be active at any time (never 2 or more),
//
// 2) next, the read pulses for the ALU are executed. The ALU is treated
// differently because it is the only subsystem where several read
// pulses can be active simultaneously. In the original AGC, these
// pulses 'inclusive OR' their output to the glbl_READ_BUS, so the
// simulator has be implemented to execute all read pulses other than
// the ALU reads first, so the ALU will have the bus data it needs
// in order to do the inclusive OR.
// In the recreated AGC hardware design, the ALU is also the subsystem
// that links the glbl_READ_BUS to the glbl_WRITE_BUS.
//
// The recreated ALU hardware design checks whether anything is being
// written to the glbl_READ_BUS by the other subsystems. If not, it
// outputs zeroes to the glbl_READ_BUS for input to the inclusive OR
// operation.
// It then transfers data on the glbl_READ_BUS to the glbl_WRITE_BUS
// using an inclusive OR with data generated by other ALU read pulses.
// The AGC sequencer uses this operation to set certain data lines.
//
// 3) finally, all write pulses are executed.

static void execRP_ALU_RB();


static void execRP_ALU_RC();
static void execRP_ALU_RU();

static void execRP_ALU_OR_RB14();


static void execRP_ALU_OR_R1();
static void execRP_ALU_OR_R1C();
static void execRP_ALU_OR_R2();
static void execRP_ALU_OR_R22();
static void execRP_ALU_OR_R24();
static void execRP_ALU_OR_R2000();
static void execRP_ALU_OR_RSB();

static void execWP_GENRST();


static void execWP_WB();

static void execWP_CI();


static void execWP_WY();

static void execWP_WX();


static void execWP_WYx();

static regB register_B; // next instruction


static regCI register_CI; // ALU carry-in flip flop
static regX register_X; // ALU X register
static regY register_Y; // ALU Y register
static regU register_U; // ALU sum
};

#endif
ALU (ALU.cpp)

/****************************************************************************
* ALU - ARITHMETIC UNIT subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: ALU.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "ALU.h"
#include "SEQ.h"
#include "BUS.h"

regB ALU::register_B; // next instruction


regCI ALU::register_CI; // ALU carry-in flip flop
regX ALU::register_X; // ALU X register
regY ALU::register_Y; // ALU Y register
regU ALU::register_U; // ALU sum

unsigned ALU::glbl_BUS = 0;

//************************************************************

void ALU::execRP_ALU_RB()
{
BUS::glbl_READ_BUS = register_B.read();
}

// Performs an inclusive OR or register U and register C;


// in the MASK instruction, the RC and RU control pulses
// are activated simultaneously. This causes both to be
// gated onto the AGC bus which performs the logical OR.
void ALU::execRP_ALU_RC()
{
ALU::glbl_BUS |= register_B.outmask() & (~register_B.read());
BUS::glbl_READ_BUS = ALU::glbl_BUS;
}

// Performs an inclusive OR or register U and register C;


// in the MASK instruction, the RC and RU control pulses
// are activated simultaneously. This causes both to be
// gated onto the AGC bus which performs the logical OR.
void ALU::execRP_ALU_RU()
{
ALU::glbl_BUS |= register_U.read();
BUS::glbl_READ_BUS = ALU::glbl_BUS;
}

//************************************************************

//************************************************************
// This is the interface between the read and write busses

void ALU::execRP_ALU_OR_RB14()
{
BUS::glbl_WRITE_BUS |= 0020000 | BUS::glbl_READ_BUS;
}

void ALU::execRP_ALU_OR_R1()
{
BUS::glbl_WRITE_BUS |= 0000001 | BUS::glbl_READ_BUS;
}

void ALU::execRP_ALU_OR_R1C()
{
BUS::glbl_WRITE_BUS |= 0177776 | BUS::glbl_READ_BUS;
}

void ALU::execRP_ALU_OR_R2()
{
BUS::glbl_WRITE_BUS |= 0000002 | BUS::glbl_READ_BUS;
}

void ALU::execRP_ALU_OR_RSB()
{
BUS::glbl_WRITE_BUS |= 0100000 | BUS::glbl_READ_BUS;
}

void ALU::execRP_ALU_OR_R22()
{
BUS::glbl_WRITE_BUS |= 0000022 | BUS::glbl_READ_BUS;
}

void ALU::execRP_ALU_OR_R24()
{
BUS::glbl_WRITE_BUS |= 0000024 | BUS::glbl_READ_BUS;
}

void ALU::execRP_ALU_OR_R2000()
{
BUS::glbl_WRITE_BUS |= 0002000 | BUS::glbl_READ_BUS; // TC GOPROG instruction
}

//************************************************************

void ALU::execWP_GENRST()
{
}

void ALU::execWP_CI()
{
register_CI.writeField(1,1,1);
}

void ALU::execWP_WX()
{
register_X.write(BUS::glbl_WRITE_BUS);
}

void ALU::execWP_WB()
{
register_B.write(BUS::glbl_WRITE_BUS);
}

void ALU::execWP_WYx()
{
register_Y.write(BUS::glbl_WRITE_BUS);
}

void ALU::execWP_WY()
{
if(!SEQ::isAsserted(CI)) register_CI.writeField(1,1,0);
register_X.write(0);
register_Y.write(BUS::glbl_WRITE_BUS);
}
unsigned regU::read()
{
unsigned carry =
(outmask()+1) & (ALU::register_X.read() + ALU::register_Y.read()); // end-around
carry
if(carry || ALU::register_CI.read())
carry = 1;
else
carry = 0;
return outmask() & (ALU::register_X.read() + ALU::register_Y.read() + carry);
}
BUS (BUS.h)
/****************************************************************************
* BUS - READ/WRITE BUS subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: BUS.h
*
* VERSIONS:
*
* DESCRIPTION:
* RW Bus for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef BUS_H
#define BUS_H

// BUS LINE DESIGNATIONS


// Specify the assignment of bus lines to the inputs of a register (for a 'write'
// operation into a register). Each 'conv_' array specifies the inputs into a
// single register. The index into the array corresponds to the bit position in
// the register, where the first parameter (index=0) is bit 16 of the register (msb)
// and the last parameter (index=15) is register bit 1 (lsb). The value of
// the parameter identifies the bus line assigned to that register bit. 'BX'
// means 'don't care'; i.e.: leave that register bit alone.

enum { D0=17, // force bit to zero


SGM=15, // sign bit in memory
SG=16, // sign (S2; one's compliment)
US=15, // uncorrected sign (S1; overflow), except in register G
B14=14, B13=13, B12=12, B11=11, B10=10, B9=9, B8=8,
B7=7, B6=6, B5=5, B4=4, B3=3, B2=2, B1=1,
BX=0 // ignore
};

enum ovfState { NO_OVF, POS_OVF, NEG_OVF };

class BUS
{
public:
static unsigned glbl_READ_BUS; // read/write bus for xfer between central regs
static unsigned glbl_WRITE_BUS; // read/write bus for xfer between central regs

friend class INT;


friend class CTR;

private:
static ovfState testOverflow(unsigned bus);
};

#endif
BUS (BUS.cpp)
/****************************************************************************
* BUS - READ/WRITE BUS subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: BUS.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "BUS.h"

unsigned BUS::glbl_READ_BUS = 0;
unsigned BUS::glbl_WRITE_BUS = 0;

ovfState BUS::testOverflow(unsigned bus)


{
if((bus & 0100000) && !(bus & 0040000))
return NEG_OVF; // negative overflow
else if(!(bus & 0100000) && (bus & 0040000))
return POS_OVF; // positive overflow
else
return NO_OVF;
}
CLK (CLK.h)
/****************************************************************************
* CLK - CLOCK subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: CLK.h
*
* VERSIONS:
*
* DESCRIPTION:
* Clock for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef CLK_H
#define CLK_H

#include "reg.h"

// define pointer-to-function type


typedef void (*EXECTYPE)();

class CLK
{
public:
static void doexecR(int pulse);
static void doexecR_ALU(int pulse);
static void doexecR_ALU_OR(int pulse);
static void doexecW(int pulse);

static void clkAGC();

static reg* registerList[];

};

#endif
CLK (CLK.cpp)
/****************************************************************************
* CLK - CLOCK subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: CLK.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "CLK.h"
#include "INP.h"
#include "OUT.h"
#include "MBF.h"
#include "ADR.h"
#include "SEQ.h"
#include "ALU.h"
#include "CRG.h"
#include "CTR.h"
#include "INT.h"
#include "PAR.h"
#include "TPG.h"
#include "SCL.h"
#include "MEM.h"

// A container for all registers. This is kept so we can iterate through


// all registers to execute the control pulses. For simulation purposes
// only; this has no counterpart in the hardware AGC.
reg* CLK::registerList[] = // registers are in no particular sequence
{
&INP::register_IN0, &INP::register_IN1, &INP::register_IN2, &INP::register_IN3,
&OUT::register_OUT1, &OUT::register_OUT2,
&OUT::register_OUT3, &OUT::register_OUT4,
&MBF::register_G, &PAR::register_G15, &ADR::register_S, &ADR::register_BNK,
&SEQ::register_SQ, &ALU::register_B,
&CRG::register_Q, &CRG::register_Z, &CRG::register_LP, &CRG::register_A, &ALU::register_X,
&ALU::register_Y, &ALU::register_U,
&SEQ::register_STA, &SEQ::register_STB, &SEQ::register_SNI,
&SEQ::register_LOOPCTR, &ALU::register_CI, &SEQ::register_BR1, &SEQ::register_BR2,
&CTR::register_UpCELL, &CTR::register_DnCELL,
&INT::register_RPCELL, &INT::register_INHINT1, &INT::register_INHINT,
&PAR::register_P, &PAR::register_P2, &PAR::register_PALM,
&TPG::register_SG,
&SCL::register_SCL,
&SCL::register_F17, &SCL::register_F13, &SCL::register_F10,
0 // zero is end-of-list flag
};

void CLK::clkAGC()
{
// Now that all the inputs are set up, clock the registers so the outputs
// can change state in accordance with the inputs.
for(int i=0; registerList[i]; i++)
{
registerList[i]->clk();
}
}

void execR_NOPULSE() { }
void execR_RA0() { CRG::execRP_RA0(); }
void execR_RA1() { CRG::execRP_RA1(); }
void execR_RA2() { CRG::execRP_RA2(); }
void execR_RA3() { CRG::execRP_RA3(); }
void execR_RA4() { INP::execRP_RA4(); }
void execR_RA5() { INP::execRP_RA5(); }
void execR_RA6() { INP::execRP_RA6(); }
void execR_RA7() { INP::execRP_RA7(); }
void execR_RA11() { OUT::execRP_RA11(); }
void execR_RA12() { OUT::execRP_RA12(); }
void execR_RA13() { OUT::execRP_RA13(); }
void execR_RA14() { OUT::execRP_RA14(); }
void execR_RA() { CRG::execRP_RA(); }
void execR_RBK() { ADR::execRP_RBK(); }
void execR_RG() { MBF::execRP_RG(); }
void execR_RLP() { CRG::execRP_RLP(); }
void execR_RQ() { CRG::execRP_RQ(); }
void execR_RRPA() { INT::execRP_RRPA(); }
void execR_RSCT() { CTR::execRP_RSCT(); }
void execR_RZ() { CRG::execRP_RZ(); }
void execR_SBWG() { MEM::execRP_SBWG(); }
void execR_WE() { MBF::execRP_WE(); PAR::execRP_WE(); }

void execR_ALU_RB() { ALU::execRP_ALU_RB(); }


void execR_ALU_RC() { ALU::execRP_ALU_RC(); }
void execR_ALU_RU() { ALU::execRP_ALU_RU(); }

void execR_ALU_OR_RSB() { ALU::execRP_ALU_OR_RSB(); }


void execR_ALU_OR_R1() { ALU::execRP_ALU_OR_R1(); }
void execR_ALU_OR_R1C() { ALU::execRP_ALU_OR_R1C(); }
void execR_ALU_OR_R2() { ALU::execRP_ALU_OR_R2(); }
void execR_ALU_OR_R22() { ALU::execRP_ALU_OR_R22(); }
void execR_ALU_OR_R24() { ALU::execRP_ALU_OR_R24(); }
void execR_ALU_OR_R2000() { ALU::execRP_ALU_OR_R2000(); }
void execR_ALU_OR_RB14() { ALU::execRP_ALU_OR_RB14(); }

EXECTYPE execR[] =
{
execR_NOPULSE, // NO_PULSE,
execR_NOPULSE, // CI, // Carry in
execR_NOPULSE, // CLG, // Clear G
execR_NOPULSE, // CLCTR, // Clear loop counter**
execR_NOPULSE, // CTR, // Loop counter
execR_NOPULSE, // GP, // Generate Parity
execR_NOPULSE, // KRPT, // Knock down Rupt priority
execR_NOPULSE, // NISQ, // New instruction to the SQ register
execR_RA, // RA, // Read A
execR_NOPULSE, // RB, // Read B
execR_NOPULSE, // RB14, // Read bit 14
execR_NOPULSE, // RC, // Read C
execR_RG, // RG, // Read G
execR_RLP, // RLP, // Read LP
execR_NOPULSE, // RP2, // Read parity 2
execR_RQ, // RQ, // Read Q
execR_RRPA, // RRPA, // Read RUPT address
execR_NOPULSE, // RSB, // Read sign bit
execR_RSCT, // RSCT, // Read selected counter address
execR_NOPULSE, // RU, // Read sum
execR_RZ, // RZ, // Read Z
execR_NOPULSE, // R1, // Read 1
execR_NOPULSE, // R1C, // Read 1 complimented
execR_NOPULSE, // R2, // Read 2
execR_NOPULSE, // R22, // Read 22
execR_NOPULSE, // R24, // Read 24
execR_NOPULSE, // ST1, // Stage 1
execR_NOPULSE, // ST2, // Stage 2
execR_NOPULSE, // TMZ, // Test for minus zero
execR_NOPULSE, // TOV, // Test for overflow
execR_NOPULSE, // TP, // Test parity
execR_NOPULSE, // TRSM, // Test for resume
execR_NOPULSE, // TSGN, // Test sign
execR_NOPULSE, // TSGN2, // Test sign 2
execR_NOPULSE, // WA, // Write A
execR_NOPULSE, // WALP, // Write A and LP
execR_NOPULSE, // WB, // Write B
execR_NOPULSE, // WGx, // Write G (do not reset)
execR_NOPULSE, // WLP, // Write LP
execR_NOPULSE, // WOVC, // Write overflow counter
execR_NOPULSE, // WOVI, // Write overflow RUPT inhibit
execR_NOPULSE, // WOVR, // Write overflow
execR_NOPULSE, // WP, // Write P
execR_NOPULSE, // WPx, // Write P (do not reset)
execR_NOPULSE, // WP2, // Write P2
execR_NOPULSE, // WQ, // Write Q
execR_NOPULSE, // WS, // Write S
execR_NOPULSE, // WX, // Write X
execR_NOPULSE, // WY, // Write Y
execR_NOPULSE, // WYx, // Write Y (do not reset)
execR_NOPULSE, // WZ, // Write Z

execR_NOPULSE, // RSC, // Read special and central


execR_NOPULSE, // WSC, // Write special and central
execR_NOPULSE, // WG, // Write G

execR_NOPULSE, // SDV1, // Subsequence DV1 is active


execR_NOPULSE, // SMP1, // Subsequence MP1 is active
execR_NOPULSE, // SRSM3, // Subsequence RSM3 is active

execR_RA0, // RA0, // Read register at address 0 (A)


execR_RA1, // RA1, // Read register at address 1 (Q)
execR_RA2, // RA2, // Read register at address 2 (Z)
execR_RA3, // RA3, // Read register at address 3 (LP)
execR_RA4, // RA4, // Read register at address 4
execR_RA5, // RA5, // Read register at address 5
execR_RA6, // RA6, // Read register at address 6
execR_RA7, // RA7, // Read register at address 7
execR_NOPULSE, // RA10, // Read register at address 10 (octal)
execR_RA11, // RA11, // Read register at address 11 (octal)
execR_RA12, // RA12, // Read register at address 12 (octal)
execR_RA13, // RA13, // Read register at address 13 (octal)
execR_RA14, // RA14, // Read register at address 14 (octal)
execR_RBK, // RBK, // Read BNK
execR_NOPULSE, // WA0, // Write register at address 0 (A)
execR_NOPULSE, // WA1, // Write register at address 1 (Q)
execR_NOPULSE, // WA2, // Write register at address 2 (Z)
execR_NOPULSE, // WA3, // Write register at address 3 (LP)
execR_NOPULSE, // WA10, // Write register at address 10 (octal)
execR_NOPULSE, // WA11, // Write register at address 11 (octal)
execR_NOPULSE, // WA12, // Write register at address 12 (octal)
execR_NOPULSE, // WA13, // Write register at address 13 (octal)
execR_NOPULSE, // WA14, // Write register at address 14 (octal)
execR_NOPULSE, // WBK, // Write BNK
execR_NOPULSE, // WGn, // Write G (normal gates)**
execR_NOPULSE, // W20, // Write into CYR
execR_NOPULSE, // W21, // Write into SR
execR_NOPULSE, // W22, // Write into CYL
execR_NOPULSE, // W23 // Write into SL

execR_NOPULSE, // GENRST,// General Reset**


execR_NOPULSE, // CLINH, // Clear INHINT**
execR_NOPULSE, // CLINH1,// Clear INHINT1**
execR_NOPULSE, // CLSTA, // Clear state counter A (STA)**
execR_NOPULSE, // CLSTB, // Clear state counter B (STB)**
execR_NOPULSE, // CLISQ, // Clear SNI**
execR_NOPULSE, // CLRP, // Clear RPCELL**
execR_NOPULSE, // INH, // Set INHINT**
execR_NOPULSE, // RPT, // Read RUPT opcode **
execR_SBWG, // SBWG, // Write G from memory
execR_NOPULSE, // SETSTB,// Set the ST1 bit of STB
execR_WE, // WE, // Write E-MEM from G
execR_NOPULSE, // WPCTR, // Write PCTR (latch priority counter sequence)**
execR_NOPULSE, // WSQ, // Write SQ
execR_NOPULSE, // WSTB, // Write stage counter B (STB)**
execR_NOPULSE, // R2000, // Read 2000 **

}; // 99

void CLK::doexecR(int pulse) { execR[pulse](); }

EXECTYPE execR_ALU[] =
{
execR_NOPULSE, // NO_PULSE,
execR_NOPULSE, // CI, // Carry in
execR_NOPULSE, // CLG, // Clear G
execR_NOPULSE, // CLCTR, // Clear loop counter**
execR_NOPULSE, // CTR, // Loop counter
execR_NOPULSE, // GP, // Generate Parity
execR_NOPULSE, // KRPT, // Knock down Rupt priority
execR_NOPULSE, // NISQ, // New instruction to the SQ register
execR_NOPULSE, // RA, // Read A
execR_ALU_RB, // RB, // Read B
execR_NOPULSE, // RB14, // Read bit 14
execR_ALU_RC, // RC, // Read C
execR_NOPULSE, // RG, // Read G
execR_NOPULSE, // RLP, // Read LP
execR_NOPULSE, // RP2, // Read parity 2
execR_NOPULSE, // RQ, // Read Q
execR_NOPULSE, // RRPA, // Read RUPT address
execR_NOPULSE, // RSB, // Read sign bit
execR_NOPULSE, // RSCT, // Read selected counter address
execR_ALU_RU, // RU, // Read sum
execR_NOPULSE, // RZ, // Read Z
execR_NOPULSE, // R1, // Read 1
execR_NOPULSE, // R1C, // Read 1 complimented
execR_NOPULSE, // R2, // Read 2
execR_NOPULSE, // R22, // Read 22
execR_NOPULSE, // R24, // Read 24
execR_NOPULSE, // ST1, // Stage 1
execR_NOPULSE, // ST2, // Stage 2
execR_NOPULSE, // TMZ, // Test for minus zero
execR_NOPULSE, // TOV, // Test for overflow
execR_NOPULSE, // TP, // Test parity
execR_NOPULSE, // TRSM, // Test for resume
execR_NOPULSE, // TSGN, // Test sign
execR_NOPULSE, // TSGN2, // Test sign 2
execR_NOPULSE, // WA, // Write A
execR_NOPULSE, // WALP, // Write A and LP
execR_NOPULSE, // WB, // Write B
execR_NOPULSE, // WGx, // Write G (do not reset)
execR_NOPULSE, // WLP, // Write LP
execR_NOPULSE, // WOVC, // Write overflow counter
execR_NOPULSE, // WOVI, // Write overflow RUPT inhibit
execR_NOPULSE, // WOVR, // Write overflow
execR_NOPULSE, // WP, // Write P
execR_NOPULSE, // WPx, // Write P (do not reset)
execR_NOPULSE, // WP2, // Write P2
execR_NOPULSE, // WQ, // Write Q
execR_NOPULSE, // WS, // Write S
execR_NOPULSE, // WX, // Write X
execR_NOPULSE, // WY, // Write Y
execR_NOPULSE, // WYx, // Write Y (do not reset)
execR_NOPULSE, // WZ, // Write Z

execR_NOPULSE, // RSC, // Read special and central


execR_NOPULSE, // WSC, // Write special and central
execR_NOPULSE, // WG, // Write G

execR_NOPULSE, // SDV1, // Subsequence DV1 is active


execR_NOPULSE, // SMP1, // Subsequence MP1 is active
execR_NOPULSE, // SRSM3, // Subsequence RSM3 is active

execR_NOPULSE, // RA0, // Read register at address 0 (A)


execR_NOPULSE, // RA1, // Read register at address 1 (Q)
execR_NOPULSE, // RA2, // Read register at address 2 (Z)
execR_NOPULSE, // RA3, // Read register at address 3 (LP)
execR_NOPULSE, // RA4, // Read register at address 4
execR_NOPULSE, // RA5, // Read register at address 5
execR_NOPULSE, // RA6, // Read register at address 6
execR_NOPULSE, // RA7, // Read register at address 7
execR_NOPULSE, // RA10, // Read register at address 10 (octal)
execR_NOPULSE, // RA11, // Read register at address 11 (octal)
execR_NOPULSE, // RA12, // Read register at address 12 (octal)
execR_NOPULSE, // RA13, // Read register at address 13 (octal)
execR_NOPULSE, // RA14, // Read register at address 14 (octal)
execR_NOPULSE, // RBK, // Read BNK
execR_NOPULSE, // WA0, // Write register at address 0 (A)
execR_NOPULSE, // WA1, // Write register at address 1 (Q)
execR_NOPULSE, // WA2, // Write register at address 2 (Z)
execR_NOPULSE, // WA3, // Write register at address 3 (LP)
execR_NOPULSE, // WA10, // Write register at address 10 (octal)
execR_NOPULSE, // WA11, // Write register at address 11 (octal)
execR_NOPULSE, // WA12, // Write register at address 12 (octal)
execR_NOPULSE, // WA13, // Write register at address 13 (octal)
execR_NOPULSE, // WA14, // Write register at address 14 (octal)
execR_NOPULSE, // WBK, // Write BNK
execR_NOPULSE, // WGn, // Write G (normal gates)**
execR_NOPULSE, // W20, // Write into CYR
execR_NOPULSE, // W21, // Write into SR
execR_NOPULSE, // W22, // Write into CYL
execR_NOPULSE, // W23 // Write into SL

execR_NOPULSE, // GENRST,// General Reset**


execR_NOPULSE, // CLINH, // Clear INHINT**
execR_NOPULSE, // CLINH1,// Clear INHINT1**
execR_NOPULSE, // CLSTA, // Clear state counter A (STA)**
execR_NOPULSE, // CLSTB, // Clear state counter B (STB)**
execR_NOPULSE, // CLISQ, // Clear SNI**
execR_NOPULSE, // CLRP, // Clear RPCELL**
execR_NOPULSE, // INH, // Set INHINT**
execR_NOPULSE, // RPT, // Read RUPT opcode **
execR_NOPULSE, // SBWG, // Write G from memory
execR_NOPULSE, // SETSTB,// Set the ST1 bit of STB
execR_NOPULSE, // WE, // Write E-MEM from G
execR_NOPULSE, // WPCTR, // Write PCTR (latch priority counter sequence)**
execR_NOPULSE, // WSQ, // Write SQ
execR_NOPULSE, // WSTB, // Write stage counter B (STB)**
execR_NOPULSE, // R2000, // Read 2000 **

};

void CLK::doexecR_ALU(int pulse) { execR_ALU[pulse](); }

EXECTYPE execR_ALU_OR[] =
{
execR_NOPULSE, // NO_PULSE,
execR_NOPULSE, // CI, // Carry in
execR_NOPULSE, // CLG, // Clear G
execR_NOPULSE, // CLCTR, // Clear loop counter**
execR_NOPULSE, // CTR, // Loop counter
execR_NOPULSE, // GP, // Generate Parity
execR_NOPULSE, // KRPT, // Knock down Rupt priority
execR_NOPULSE, // NISQ, // New instruction to the SQ register
execR_NOPULSE, // RA, // Read A
execR_NOPULSE, // RB, // Read B
execR_ALU_OR_RB14, // RB14, // Read bit 14
execR_NOPULSE, // RC, // Read C
execR_NOPULSE, // RG, // Read G
execR_NOPULSE, // RLP, // Read LP
execR_NOPULSE, // RP2, // Read parity 2
execR_NOPULSE, // RQ, // Read Q
execR_NOPULSE, // RRPA, // Read RUPT address
execR_ALU_OR_RSB, // RSB, // Read sign bit
execR_NOPULSE, // RSCT, // Read selected counter address
execR_NOPULSE, // RU, // Read sum
execR_NOPULSE, // RZ, // Read Z
execR_ALU_OR_R1, // R1, // Read 1
execR_ALU_OR_R1C, // R1C, // Read 1 complimented
execR_ALU_OR_R2, // R2, // Read 2
execR_ALU_OR_R22, // R22, // Read 22
execR_ALU_OR_R24, // R24, // Read 24
execR_NOPULSE, // ST1, // Stage 1
execR_NOPULSE, // ST2, // Stage 2
execR_NOPULSE, // TMZ, // Test for minus zero
execR_NOPULSE, // TOV, // Test for overflow
execR_NOPULSE, // TP, // Test parity
execR_NOPULSE, // TRSM, // Test for resume
execR_NOPULSE, // TSGN, // Test sign
execR_NOPULSE, // TSGN2, // Test sign 2
execR_NOPULSE, // WA, // Write A
execR_NOPULSE, // WALP, // Write A and LP
execR_NOPULSE, // WB, // Write B
execR_NOPULSE, // WGx, // Write G (do not reset)
execR_NOPULSE, // WLP, // Write LP
execR_NOPULSE, // WOVC, // Write overflow counter
execR_NOPULSE, // WOVI, // Write overflow RUPT inhibit
execR_NOPULSE, // WOVR, // Write overflow
execR_NOPULSE, // WP, // Write P
execR_NOPULSE, // WPx, // Write P (do not reset)
execR_NOPULSE, // WP2, // Write P2
execR_NOPULSE, // WQ, // Write Q
execR_NOPULSE, // WS, // Write S
execR_NOPULSE, // WX, // Write X
execR_NOPULSE, // WY, // Write Y
execR_NOPULSE, // WYx, // Write Y (do not reset)
execR_NOPULSE, // WZ, // Write Z

execR_NOPULSE, // RSC, // Read special and central


execR_NOPULSE, // WSC, // Write special and central
execR_NOPULSE, // WG, // Write G

execR_NOPULSE, // SDV1, // Subsequence DV1 is active


execR_NOPULSE, // SMP1, // Subsequence MP1 is active
execR_NOPULSE, // SRSM3, // Subsequence RSM3 is active

execR_NOPULSE, // RA0, // Read register at address 0 (A)


execR_NOPULSE, // RA1, // Read register at address 1 (Q)
execR_NOPULSE, // RA2, // Read register at address 2 (Z)
execR_NOPULSE, // RA3, // Read register at address 3 (LP)
execR_NOPULSE, // RA4, // Read register at address 4
execR_NOPULSE, // RA5, // Read register at address 5
execR_NOPULSE, // RA6, // Read register at address 6
execR_NOPULSE, // RA7, // Read register at address 7
execR_NOPULSE, // RA10, // Read register at address 10 (octal)
execR_NOPULSE, // RA11, // Read register at address 11 (octal)
execR_NOPULSE, // RA12, // Read register at address 12 (octal)
execR_NOPULSE, // RA13, // Read register at address 13 (octal)
execR_NOPULSE, // RA14, // Read register at address 14 (octal)
execR_NOPULSE, // RBK, // Read BNK
execR_NOPULSE, // WA0, // Write register at address 0 (A)
execR_NOPULSE, // WA1, // Write register at address 1 (Q)
execR_NOPULSE, // WA2, // Write register at address 2 (Z)
execR_NOPULSE, // WA3, // Write register at address 3 (LP)
execR_NOPULSE, // WA10, // Write register at address 10 (octal)
execR_NOPULSE, // WA11, // Write register at address 11 (octal)
execR_NOPULSE, // WA12, // Write register at address 12 (octal)
execR_NOPULSE, // WA13, // Write register at address 13 (octal)
execR_NOPULSE, // WA14, // Write register at address 14 (octal)
execR_NOPULSE, // WBK, // Write BNK
execR_NOPULSE, // WGn, // Write G (normal gates)**
execR_NOPULSE, // W20, // Write into CYR
execR_NOPULSE, // W21, // Write into SR
execR_NOPULSE, // W22, // Write into CYL
execR_NOPULSE, // W23 // Write into SL

execR_NOPULSE, // GENRST,// General Reset**


execR_NOPULSE, // CLINH, // Clear INHINT**
execR_NOPULSE, // CLINH1,// Clear INHINT1**
execR_NOPULSE, // CLSTA, // Clear state counter A (STA)**
execR_NOPULSE, // CLSTB, // Clear state counter B (STB)**
execR_NOPULSE, // CLISQ, // Clear SNI**
execR_NOPULSE, // CLRP, // Clear RPCELL**
execR_NOPULSE, // INH, // Set INHINT**
execR_NOPULSE, // RPT, // Read RUPT opcode **
execR_NOPULSE, // SBWG, // Write G from memory
execR_NOPULSE, // SETSTB,// Set the ST1 bit of STB
execR_NOPULSE, // WE, // Write E-MEM from G
execR_NOPULSE, // WPCTR, // Write PCTR (latch priority counter sequence)**
execR_NOPULSE, // WSQ, // Write SQ
execR_NOPULSE, // WSTB, // Write stage counter B (STB)**
execR_ALU_OR_R2000, // R2000, // Read 2000 **

};

void CLK::doexecR_ALU_OR(int pulse) { execR_ALU_OR[pulse](); }


void execW_NOPULSE() { }
void execW_CI() { ALU::execWP_CI(); }
void execW_CLG() { PAR::execWP_CLG(); }
void execW_CLINH() { INT::execWP_CLINH(); }
void execW_CLINH1() { INT::execWP_CLINH1(); }
void execW_CLISQ() { SEQ::execWP_CLISQ(); }
void execW_CLCTR() { SEQ::execWP_CLCTR(); }
void execW_CLRP() { INT::execWP_CLRP(); }
void execW_CLSTA() { SEQ::execWP_CLSTA(); }
void execW_CLSTB() { SEQ::execWP_CLSTB(); }
void execW_CTR() { SEQ::execWP_CTR(); }

void execW_GENRST() { SEQ::execWP_GENRST();


MBF::execWP_GENRST();
CRG::execWP_GENRST();
PAR::execWP_GENRST();
ALU::execWP_GENRST();
CTR::execWP_GENRST();
INT::execWP_GENRST();
OUT::execWP_GENRST(); }

void execW_GP() { PAR::execWP_GP(); }


void execW_INH() { INT::execWP_INH(); }
void execW_KRPT() { INT::execWP_KRPT(); }
void execW_NISQ() { SEQ::execWP_NISQ(); }
void execW_RPT() { INT::execWP_RPT(); }
void execW_RP2() { PAR::execWP_RP2(); }
void execW_SBWG() { MBF::execWP_SBWG(); PAR::execWP_SBWG(); }
void execW_SETSTB() { SEQ::execWP_SETSTB(); }
void execW_ST1() { SEQ::execWP_ST1(); }
void execW_ST2() { SEQ::execWP_ST2(); }
void execW_TMZ() { SEQ::execWP_TMZ(); }
void execW_TOV() { SEQ::execWP_TOV(); }
void execW_TP() { PAR::execWP_TP(); }
void execW_TRSM() { SEQ::execWP_TRSM(); }
void execW_TSGN() { SEQ::execWP_TSGN(); }
void execW_TSGN2() { SEQ::execWP_TSGN2(); }
void execW_WA0() { CRG::execWP_WA0(); }
void execW_WA1() { CRG::execWP_WA1(); }
void execW_WA2() { CRG::execWP_WA2(); }
void execW_WA3() { CRG::execWP_WA3(); }
void execW_WA10() { OUT::execWP_WA10(); }
void execW_WA11() { OUT::execWP_WA11(); }
void execW_WA12() { OUT::execWP_WA12(); }
void execW_WA13() { OUT::execWP_WA13(); }
void execW_WA14() { OUT::execWP_WA14(); }
void execW_WA() { CRG::execWP_WA(); }
void execW_WALP() { CRG::execWP_WALP(); }
void execW_WB() { ALU::execWP_WB(); }
void execW_WBK() { ADR::execWP_WBK(); }
void execW_WE() { MEM::execWP_WE(); }
void execW_WGn() { MBF::execWP_WGn(); }
void execW_WGx() { MBF::execWP_WGx(); PAR::execWP_WGx(); }
void execW_WLP() { CRG::execWP_WLP(); }
void execW_WOVC() { CTR::execWP_WOVC(); }
void execW_WOVI() { INT::execWP_WOVI(); }
void execW_WOVR() { CTR::execWP_WOVR(); }
void execW_WP() { PAR::execWP_WP(); }
void execW_WPx() { PAR::execWP_WPx(); }
void execW_WP2() { PAR::execWP_WP2(); }
void execW_WPCTR() { CTR::execWP_WPCTR(); }
void execW_WQ() { CRG::execWP_WQ(); }
void execW_WS() { ADR::execWP_WS(); }
void execW_WSQ() { SEQ::execWP_WSQ(); }
void execW_WSTB() { SEQ::execWP_WSTB(); }
void execW_WX() { ALU::execWP_WX(); }
void execW_WY() { ALU::execWP_WY(); }
void execW_WYx() { ALU::execWP_WYx(); }
void execW_WZ() { CRG::execWP_WZ(); }
void execW_W20() { MBF::execWP_W20(); }
void execW_W21() { MBF::execWP_W21(); }
void execW_W22() { MBF::execWP_W22(); }
void execW_W23() { MBF::execWP_W23(); }
EXECTYPE execW[] =
{
execW_NOPULSE, // NO_PULSE,
execW_CI, // CI, // Carry in
execW_CLG, // CLG, // Clear G
execW_CLCTR, // CLCTR, // Clear loop counter**
execW_CTR, // CTR, // Loop counter
execW_GP, // GP, // Generate Parity
execW_KRPT, // KRPT, // Knock down Rupt priority
execW_NISQ, // NISQ, // New instruction to the SQ register
execW_NOPULSE, // RA, // Read A
execW_NOPULSE, // RB, // Read B
execW_NOPULSE, // RB14, // Read bit 14
execW_NOPULSE, // RC, // Read C
execW_NOPULSE, // RG, // Read G
execW_NOPULSE, // RLP, // Read LP
execW_RP2, // RP2, // Read parity 2
execW_NOPULSE, // RQ, // Read Q
execW_NOPULSE, // RRPA, // Read RUPT address
execW_NOPULSE, // RSB, // Read sign bit
execW_NOPULSE, // RSCT, // Read selected counter address
execW_NOPULSE, // RU, // Read sum
execW_NOPULSE, // RZ, // Read Z
execW_NOPULSE, // R1, // Read 1
execW_NOPULSE, // R1C, // Read 1 complimented
execW_NOPULSE, // R2, // Read 2
execW_NOPULSE, // R22, // Read 22
execW_NOPULSE, // R24, // Read 24
execW_ST1, // ST1, // Stage 1
execW_ST2, // ST2, // Stage 2
execW_TMZ, // TMZ, // Test for minus zero
execW_TOV, // TOV, // Test for overflow
execW_TP, // TP, // Test parity
execW_TRSM, // TRSM, // Test for resume
execW_TSGN, // TSGN, // Test sign
execW_TSGN2, // TSGN2, // Test sign 2
execW_WA, // WA, // Write A
execW_WALP, // WALP, // Write A and LP
execW_WB, // WB, // Write B
execW_WGx, // WGx, // Write G (do not reset)
execW_WLP, // WLP, // Write LP
execW_WOVC, // WOVC, // Write overflow counter
execW_WOVI, // WOVI, // Write overflow RUPT inhibit
execW_WOVR, // WOVR, // Write overflow
execW_WP, // WP, // Write P
execW_WPx, // WPx, // Write P (do not reset)
execW_WP2, // WP2, // Write P2
execW_WQ, // WQ, // Write Q
execW_WS, // WS, // Write S
execW_WX, // WX, // Write X
execW_WY, // WY, // Write Y
execW_WYx, // WYx, // Write Y (do not reset)
execW_WZ, // WZ, // Write Z

execW_NOPULSE, // RSC, // Read special and central


execW_NOPULSE, // WSC, // Write special and central
execW_NOPULSE, // WG, // Write G

execR_NOPULSE, // SDV1, // Subsequence DV1 is active


execR_NOPULSE, // SMP1, // Subsequence MP1 is active
execR_NOPULSE, // SRSM3, // Subsequence RSM3 is active

execW_NOPULSE, // RA0, // Read register at address 0 (A)


execW_NOPULSE, // RA1, // Read register at address 1 (Q)
execW_NOPULSE, // RA2, // Read register at address 2 (Z)
execW_NOPULSE, // RA3, // Read register at address 3 (LP)
execW_NOPULSE, // RA4, // Read register at address 4
execW_NOPULSE, // RA5, // Read register at address 5
execW_NOPULSE, // RA6, // Read register at address 6
execW_NOPULSE, // RA7, // Read register at address 7
execW_NOPULSE, // RA10, // Read register at address 10 (octal)
execW_NOPULSE, // RA11, // Read register at address 11 (octal)
execW_NOPULSE, // RA12, // Read register at address 12 (octal)
execW_NOPULSE, // RA13, // Read register at address 13 (octal)
execW_NOPULSE, // RA14, // Read register at address 14 (octal)
execW_NOPULSE, // RBK, // Read BNK
execW_WA0, // WA0, // Write register at address 0 (A)
execW_WA1, // WA1, // Write register at address 1 (Q)
execW_WA2, // WA2, // Write register at address 2 (Z)
execW_WA3, // WA3, // Write register at address 3 (LP)
execW_WA10, // WA10, // Write register at address 10 (octal)
execW_WA11, // WA11, // Write register at address 11 (octal)
execW_WA12, // WA12, // Write register at address 12 (octal)
execW_WA13, // WA13, // Write register at address 13 (octal)
execW_WA14, // WA14, // Write register at address 14 (octal)
execW_WBK, // WBK, // Write BNK
execW_WGn, // WGn, // Write G (normal gates)**
execW_W20, // W20, // Write into CYR
execW_W21, // W21, // Write into SR
execW_W22, // W22, // Write into CYL
execW_W23, // W23 // Write into SL

execW_GENRST, // GENRST,// General Reset**


execW_CLINH, // CLINH, // Clear INHINT**
execW_CLINH1, // CLINH1,// Clear INHINT1**
execW_CLSTA, // CLSTA, // Clear state counter A (STA)**
execW_CLSTB, // CLSTB, // Clear state counter B (STB)**
execW_CLISQ, // CLISQ, // Clear SNI**
execW_CLRP, // CLRP, // Clear RPCELL**
execW_INH, // INH, // Set INHINT**
execW_RPT, // RPT, // Read RUPT opcode **
execW_SBWG, // SBWG, // Write G from memory
execW_SETSTB, // SETSTB,// Set the ST1 bit of STB
execW_WE, // WE, // Write E-MEM from G
execW_WPCTR, // WPCTR, // Write PCTR (latch priority counter sequence)**
execW_WSQ, // WSQ, // Write SQ
execW_WSTB, // WSTB, // Write stage counter B (STB)**
execW_NOPULSE, // R2000, // Read 2000 **

}; // 99

void CLK::doexecW(int pulse) { execW[pulse](); }


CPM (CPM.h)
/****************************************************************************
* CPM - CONTROL PULSE MATRIX subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: CPM.h
*
* VERSIONS:
*
* DESCRIPTION:
* Control Pulse Matrix for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef CPM_H
#define CPM_H

#include "TPG.h"
#include "SEQ.h"

class CPM
{
public:

static subseq instructionSubsequenceDecoder(


int counter_subseq, int SQ_field, int STB_field);

static char* subseqString[];

static void controlPulseMatrix();

static void readEPROM(char* fileName, int* eprom);

static int EPROM1_8 [0x3fff+1];


static int EPROM9_16 [0x3fff+1];
static int EPROM17_24[0x3fff+1];
static int EPROM25_32[0x3fff+1];
static int EPROM33_40[0x3fff+1];
static int EPROM41_48[0x3fff+1];
static int EPROM49_56[0x3fff+1];

private:
// Clear the list of currently asserted control pulses.
static void clearControlPulses();

// Assert the set of control pulses by adding them to the list of currently
// active control signals.
static void assert(cpType* pulse);

// Assert a control pulse by adding it to the list of currently asserted


// control pulses.
static void assert(cpType pulse);

static void get_CPM_A(int CPM_A_address);

static void getControlPulses_EPROM(int address);

static void checkEPROM(int inval, int lowbit);


};

#endif
CPM (CPM.cpp)
/****************************************************************************
* CPM - CONTROL PULSE MATRIX subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: CPM.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "CPM.h"
#include "SEQ.h"
#include "MON.h"
#include "CTR.h"
#include "INT.h"
#include "ADR.h"

#include <stdlib.h>

char* CPM::subseqString[] =
{
"TC0",
"CCS0",
"CCS1",
"NDX0",
"NDX1",
"RSM3",
"XCH0",
"CS0",
"TS0",
"AD0",
"MASK0",
"MP0",
"MP1",
"MP3",
"DV0",
"DV1",
"SU0",
"RUPT1",
"RUPT3",
"STD2",
"PINC0",
"MINC0",
"SHINC0",
"NO_SEQ"
};

subseq CPM::instructionSubsequenceDecoder(
int counter_subseq, int SQ_field, int STB_field)
{
// Combinational logic decodes instruction and the stage count
// to get the instruction subsequence.
static subseq decode[16][4] = {
{ TC0, RUPT1, STD2, RUPT3 }, // 00
{ CCS0, CCS1, NO_SEQ, NO_SEQ }, // 01
{ NDX0, NDX1, NO_SEQ, RSM3 }, // 02
{ XCH0, NO_SEQ, STD2, NO_SEQ }, // 03

{ NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 04


{ NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 05
{ NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 06
{ NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 07
{ NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 10

{ MP0, MP1, NO_SEQ, MP3 }, // 11


{ DV0, DV1, STD2, NO_SEQ }, // 12
{ SU0, NO_SEQ, STD2, NO_SEQ }, // 13

{ CS0, NO_SEQ, STD2, NO_SEQ }, // 14


{ TS0, NO_SEQ, STD2, NO_SEQ }, // 15
{ AD0, NO_SEQ, STD2, NO_SEQ }, // 16
{ MASK0, NO_SEQ, STD2, NO_SEQ } // 17

};

if(counter_subseq == PINCSEL)
return PINC0;
else if(counter_subseq == MINCSEL)
return MINC0;
else
return decode[SQ_field][STB_field];
}

void CPM::clearControlPulses()
{
for(unsigned i=0; i<MAXPULSES; i++)
SEQ::glbl_cp[i] = NO_PULSE;
}

void CPM::assert(cpType* pulse)


{
int j=0;
for(unsigned i=0; i<MAXPULSES && j<MAX_IPULSES && pulse[j] != NO_PULSE; i++)
{
if(SEQ::glbl_cp[i] == NO_PULSE)
{
SEQ::glbl_cp[i] = pulse[j];
j++;
}
}
}

void CPM::assert(cpType pulse)


{
for(unsigned i=0; i<MAXPULSES; i++)
{
if(SEQ::glbl_cp[i] == NO_PULSE)
{
SEQ::glbl_cp[i] = pulse;
break;
}
}
}

int CPM::EPROM1_8 [];


int CPM::EPROM9_16 [];
int CPM::EPROM17_24[];
int CPM::EPROM25_32[];
int CPM::EPROM33_40[];
int CPM::EPROM41_48[];
int CPM::EPROM49_56[];

void CPM::readEPROM(char* fileName, int* eprom)


{
cout << "Reading EPROM: " << fileName << endl;

// Open the EPROM file.


FILE* ifp = fopen(fileName, "r");
if(!ifp)
{
perror("fopen failed for source file");
exit(-1);
}

const int addressBytes = 3; // 24-bit address range


const int sumCheckBytes = 1;

char buf[4096];
while(fgets(buf,4096,ifp))
{
// process a record
if(buf[0] != 'S')
{
cout << "Error reading start of EPROM record for: " << fileName << endl;
exit(-1);
}

char tmp[256];

strncpy(tmp, &buf[2], 2); tmp[2] = '\0';


int totalByteCount = strtol(tmp, 0, 16);
int mySumCheck = totalByteCount & 0xff;

strncpy(tmp, &buf[4], 6); tmp[addressBytes*2] = '\0';


int address = strtol(tmp, 0, 16);
mySumCheck = (mySumCheck + ((address & 0xff0000) >> 16)) % 256;
mySumCheck = (mySumCheck + ((address & 0x00ff00) >> 8)) % 256;
mySumCheck = (mySumCheck + ((address & 0x0000ff) )) % 256;

//cout << hex << totalByteCount << ", " << address << dec << endl;

int dataBytes = totalByteCount - addressBytes - sumCheckBytes;


int i = (addressBytes+2)*2; // index to 1st databyte char.
for(int j=0; j<dataBytes; j++)
{
// get a data byte
strncpy(tmp, &buf[i], 2); tmp[2] = '\0';
int data = strtol(tmp, 0, 16);
//cout << hex << data << dec << endl;
mySumCheck = (mySumCheck + data) % 256;

// The H/W AGC needs negative logic in the EPROMS (0=asserted)


// but this simulator needs positive logic, so we bit flip the word.
//eprom[address] = data;
eprom[address] = ((~data) & 0xff);
address++;

i+=2; // bump to next databyte char


}
strncpy(tmp, &buf[i], 2); tmp[2] = '\0';
int sumCheck = strtol(tmp, 0, 16);

if(sumCheck != ((~mySumCheck) & 0xff))


{
cout << "sumCheck failed; file: " << fileName << ", address: " << hex <<
address
<< ", sumCheck: " << sumCheck << ", mySumCheck: " << mySumCheck <<
dec << endl;
exit(-1);
}

}
fclose(ifp);
}

void CPM::checkEPROM(int inval, int lowbit)


{
for(int mask=0x1; inval && mask !=0x100; mask=mask<<1)
{
if(inval & mask)
assert((cpType) lowbit);
lowbit++;
}
}

// perform the CPM-A EPROM function using the EPROM files


void CPM::getControlPulses_EPROM(int address)
{
checkEPROM(EPROM1_8 [address], 1);
checkEPROM(EPROM9_16 [address], 9);
checkEPROM(EPROM17_24[address], 17);
checkEPROM(EPROM25_32[address], 25);
checkEPROM(EPROM33_40[address], 33);
checkEPROM(EPROM41_48[address], 41);
checkEPROM(EPROM49_56[address], 49);
}

void CPM::get_CPM_A(int address)


{
// Use the EPROM tables to get the CPM-A control pulses documented
// in R-393.
getControlPulses_EPROM(address);

// Now add some additional control pulses implied, but not documented
// in R-393.
if(SEQ::register_LOOPCTR.read() == 6)
{
assert(ST2); // STA <- 2
assert(CLCTR); // CTR <- 0
}

//*****************************************************************
// Now that the EPROM tables are used for CPM-A, this function is only
// used to display the instruction subsequence in MON.
SEQ::glbl_subseq = CPM::instructionSubsequenceDecoder(
CTR::getSubseq(), SEQ::register_SQ.read(), SEQ::register_STB.read());
//*****************************************************************

// These were in CPM-C, where the rest of the control signal assertions
// related to their use still are, but were moved here because WB and RB
// are part of the R-393 sequence tables. Check CPM-C to see how these
// assertions fit in (the former use is commented out there).
switch(TPG::register_SG.read())
{

case PWRON:
assert(WB); // TC GOPROG copied to B (see CPM-C for related assertions)
break;

case TP12:
if(SEQ::register_SNI.read() == 1)
{
if(!INT::IRQ())
{
// Normal instruction
assert(RB); // SQ <- B (see CPM-C for related assertions)
}
}
break;

default: ;
}
}

void CPM::controlPulseMatrix()
{
// Combination logic decodes time pulse, subsequence, branch register, and
// "select next instruction" latch to get control pulses associated with
// those states.

// Get rid of any old control pulses.


clearControlPulses();

//*******************************************************************************
// SUBSYSTEM A

int SB2_field = 0;
int SB1_field = 0;

switch(CTR::getSubseq())
{
case PINCSEL:
SB2_field = 0;
SB1_field = 1;
break;
case MINCSEL:
SB2_field = 1;
SB1_field = 0;
break;
default:
SB2_field = 0;
SB1_field = 0;
};

int CPM_A_address = 0;
CPM_A_address =
(SB2_field << 13) |
(SB1_field << 12) |
(SEQ::register_SQ.read() << 8) |
(SEQ::register_STB.read() << 6) |
(TPG::register_SG.read() << 2) |
(SEQ::register_BR1.read() << 1) |
SEQ::register_BR2.read();

// Construct address into CPM-A control pulse ROM:


// Address bits (bit 1 is LSB)
// 1: register BR2
// 2: register BR1
// 3-6: register SG (4)
// 7,8: register STB (2)
// 9-12: register SQ (4)
// 13: STB_01 (from CTR: selects PINC, MINC, or none)
// 14: STB_02 (from CTR: selects PINC, MINC, or none)
get_CPM_A(CPM_A_address);

//*******************************************************************************

//*******************************************************************************
// SUBSYSTEM B

// NOTE: WG, RSC, WSC are generated by SUBSYSTEM A. Those 3 signals are only used
// by SUBSYSTEM B; not anywhere else.

// CONSIDER MOVING TO ADR **********************8

if(SEQ::isAsserted(WG))
{
switch(ADR::register_S.read())
{
case 020: assert(W20); break;
case 021: assert(W21); break;
case 022: assert(W22); break;
case 023: assert(W23); break;
default: if(ADR::GTR_17()) assert(WGn); // not a central register
}
}
if(SEQ::isAsserted(RSC))
{
switch(ADR::register_S.read())
{
case 00: assert(RA0); break;
case 01: assert(RA1); break;
case 02: assert(RA2); break;
case 03: assert(RA3); break;
case 04: assert(RA4); break;
case 05: assert(RA5); break;
case 06: assert(RA6); break;
case 07: assert(RA7); break;
case 010: assert(RA10); break;
case 011: assert(RA11); break;
case 012: assert(RA12); break;
case 013: assert(RA13); break;
case 014: assert(RA14); break;
case 015: assert(RBK); break;
default: break; // 016, 017
}
}
if(SEQ::isAsserted(WSC))
switch(ADR::register_S.read())
{
case 00: assert(WA0); break;
case 01: assert(WA1); break;
case 02: assert(WA2); break;
case 03: assert(WA3); break;
case 010: assert(WA10); break;
case 011: assert(WA11); break;
case 012: assert(WA12); break;
case 013: assert(WA13); break;
case 014: assert(WA14); break;
case 015: assert(WBK); break;
default: break; // 016, 017
}
//*******************************************************************************

//*******************************************************************************
// SUBSYSTEM C

switch(TPG::register_SG.read())
{

case STBY:
assert(GENRST);
// inhibit all alarms
// init "SQ" complex
// clear branch registers
// stage registers are not cleared; should they be?

// zeroes are already gated onto bus when no read pulses are asserted.
// to zero synchronous-clocked registers, assert write pulses here.
// Level-triggered registers are zeroed by GENRST anded with CLK2.
break;

case PWRON:
assert(R2000);
//assert(WB); // TC GOPROG copied to B (implemented in CPM-A)
break;

case TP1:
// Moved this from TP12 to TP1 because CLISQ was getting cleared in the
// hardware AGC before TPG was clocked; therefore TPG was not seeing the
// SNI indication.
assert(CLISQ); // SNI <- 0

case TP5:
// EMEM must be available in G register by TP6
if( ADR::GTR_17() && // not a central register
!ADR::GTR_1777() && // not fixed memory
!SEQ::isAsserted(SDV1) && // not a loop counter subseq
!SEQ::isAsserted(SMP1))
{
assert(SBWG);
}
if( ADR::EQU_17() ) assert (INH); // INHINT (INDEX 017)
if( ADR::EQU_16() ) assert (CLINH); // RELINT (INDEX 016)
break;

case TP6:
// FMEM must be available in G register by TP7
if( ADR::GTR_1777() && // not eraseable
memory
!SEQ::isAsserted(SDV1) && // not a loop counter subseq
!SEQ::isAsserted(SMP1))
{
assert(SBWG);
}
break;

case TP11:
// G register written to memory beginning at TP11; Memory updates are in
// G by TP10 for all normal and extracode instructions, but the PINC, MINC,
// and SHINC sequences write to G in TP10 because they need to update the
// parity bit.
if( ADR::GTR_17() && // not a central register
!ADR::GTR_1777() && // not fixed memory
!SEQ::isAsserted(SDV1) && // not a loop counter subseq
!SEQ::isAsserted(SMP1))
{
assert(WE);
}
// Additional interrupts are inhibited during servicing of an interrupt;
// Remove the inhibition when RESUME is executed (INDEX 025)
if(SEQ::isAsserted(SRSM3)) assert(CLRP);
break;

case TP12:
// DISABLE INPUT CHANGE TO PRIORITY COUNTER (reenable after TP1)
// Check the priority counters; service any waiting inputs on the next
// memory cycle.
assert(WPCTR);
if(SEQ::register_SNI.read() == 1) // if SNI is set, get next instruction
{
if(INT::IRQ()) // if interrupt requested (see CPM-A for similar assertion)
{
// Interrupt: SQ <- 0 (the default RW bus state)
assert(RPT); // latch interrupt vector
assert(SETSTB); // STB <- 1
}
else
{
// Normal instruction
//assert(RB); // SQ <- B (implemented in CPM-A)
assert(CLSTB); // STB <- 0
}
assert(WSQ);
assert(CLSTA); // STA <- 0

// Remove inhibition of interrupts (if they were) AFTER the next


instruction
assert(CLINH1); // INHINT1 <- 0
}
else if(CTR::getSubseq() == NOPSEL) // if previous sequence was not a counter
{
// get next sequence for same instruction.
assert(WSTB); // STB <- STA
assert(CLSTA); // STA <- 0

}
//assert(CLISQ); // SNI <- 0 (moved to TP1)

break;

default: ;
}
//*******************************************************************************
}
CRG (CRG.h)
/****************************************************************************
* CRG - ADDRESSABLE CENTRAL REGISTER subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: CRG.h
*
* VERSIONS:
*
* DESCRIPTION:
* Addressable Central Registers for the Block 1 Apollo Guidance Computer
* prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef CRG_H
#define CRG_H

#include "reg.h"

class regQ : public reg


{
public:
regQ() : reg(16, "%06o") { }
};

class regZ : public reg


{
public:
regZ() : reg(16, "%06o") { }
};

class regLP : public reg


{
public:
regLP() : reg(16, "%06o") { }
};

class regA : public reg


{
public:
regA() : reg(16, "%06o") { }
};

class CRG
{
public:
static void execWP_GENRST();

static void execRP_RQ();


static void execRP_RA1();
static void execWP_WQ();
static void execWP_WA1();
static void execRP_RZ();
static void execRP_RA2();
static void execWP_WZ();
static void execWP_WA2();
static void execRP_RLP();
static void execRP_RA3();
static void execRP_RA();
static void execRP_RA0();
static void execWP_WA();
static void execWP_WA0();
static void execWP_WALP();
static void execWP_WLP();
static void execWP_WA3();

static regQ register_Q; // return address


static regZ register_Z; // program counter
static regLP register_LP; // lower accumulator
static regA register_A; // accumulator

static unsigned conv_WALP_LP[];


static unsigned conv_WALP_A[];
static unsigned conv_WLP[];
};

#endif
CRG (CRG.cpp)

/****************************************************************************
* CRG - ADDRESSABLE CENTRAL REGISTER subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: CRG.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "CRG.h"
#include "SEQ.h"
#include "BUS.h"

regQ CRG::register_Q; // return address


regZ CRG::register_Z; // program counter
regLP CRG::register_LP; // lower accumulator
regA CRG::register_A; // accumulator

// BUS LINE ASSIGNMENTS


// Specify the assignment of bus lines to the inputs of a register (for a 'write'
// operation into a register). Each 'conv_' array specifies the inputs into a
// single register. The index into the array corresponds to the bit position in
// the register, where the first parameter (index=0) is bit 16 of the register (msb)
// and the last parameter (index=15) is register bit 1 (lsb). The value of
// the parameter identifies the bus line assigned to that register bit. 'BX'
// means 'don't care'; i.e.: leave that register bit alone.

unsigned CRG::conv_WALP_LP[] =
{ BX, BX, B1, BX, BX, BX, BX, BX, BX, BX, BX, BX, BX, BX, BX, BX };

unsigned CRG::conv_WALP_A[] =
{ SG, SG, US, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2 };

unsigned CRG::conv_WLP[] =
{ B1, B1, D0, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2 };

void CRG::execWP_GENRST()
{
register_Q.write(0);
register_Z.write(0);
register_LP.write(0);
register_A.write(0);
}

void CRG::execRP_RQ()
{
BUS::glbl_READ_BUS = register_Q.read();
}
void CRG::execRP_RA1()
{
BUS::glbl_READ_BUS = register_Q.read();
}

void CRG::execWP_WQ()
{
register_Q.write(BUS::glbl_WRITE_BUS);
}

void CRG::execWP_WA1()
{
register_Q.write(BUS::glbl_WRITE_BUS);
}

void CRG::execRP_RZ()
{
BUS::glbl_READ_BUS = register_Z.read();
}

void CRG::execRP_RA2()
{
BUS::glbl_READ_BUS = register_Z.read();
}

void CRG::execWP_WZ()
{
register_Z.write(BUS::glbl_WRITE_BUS);
}

void CRG::execWP_WA2()
{
register_Z.write(BUS::glbl_WRITE_BUS);
}

void CRG::execRP_RLP()
{
BUS::glbl_READ_BUS = register_LP.read();
}

void CRG::execRP_RA3()
{
BUS::glbl_READ_BUS = register_LP.read();
}

void CRG::execWP_WALP()
{
register_LP.writeShift(BUS::glbl_WRITE_BUS, CRG::conv_WALP_LP);
register_A.writeShift(BUS::glbl_WRITE_BUS, CRG::conv_WALP_A);
}
void CRG::execWP_WLP()
{
register_LP.writeShift(BUS::glbl_WRITE_BUS, CRG::conv_WLP);
}

void CRG::execWP_WA3()
{
register_LP.writeShift(BUS::glbl_WRITE_BUS, CRG::conv_WLP);
}

void CRG::execRP_RA()
{
BUS::glbl_READ_BUS = register_A.read();
}

void CRG::execRP_RA0()
{
BUS::glbl_READ_BUS = register_A.read();
}

void CRG::execWP_WA()
{
register_A.write(BUS::glbl_WRITE_BUS);
}

void CRG::execWP_WA0()
{
register_A.write(BUS::glbl_WRITE_BUS);
}
CTR (CTR.h)
/****************************************************************************
* CTR - INVOLUNTARY PRIORITY COUNTER subsystem
*
* AUTHOR: John Pultorak
* DATE: 10/25/02
* FILE: CTR.h
*
* VERSIONS:
*
* DESCRIPTION:
* Involuntary Counters for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef CTR_H
#define CTR_H

#include "reg.h"

enum ctrNumber { // indexes for priority cells


OVCTR =0,

TIME2 =1, // Block II puts TIME2 first


TIME1 =2,
TIME3 =3,
TIME4 =4,

};

enum ctrAddr { // octal addresses of counters


// Note: In Block 1, TIME1 preceeds TIME2. In Block II,
// this is reversed: TIME2 preceeds TIME1. This reversal
// was done so that the most significant time word occurs
// at the lower address in the 2 word AGC clock. Therefore,
// a common AGC software routine can be used to read the
// time.
OVCTR_ADDR =0034,

TIME2_ADDR =0035, // Block II puts TIME2 first


TIME1_ADDR =0036,
TIME3_ADDR =0037,
TIME4_ADDR =0040,

SPARE1_ADDR =0041,

SPARE2_ADDR =0042,
SPARE3_ADDR =0043
};

enum pCntrType {
NOPSEL =0, // NO COUNTER
PINCSEL =1, // PINC
MINCSEL =2 // MINC
};

class regUpCELL : public reg


{
public:
// Bit synchronize the counter inputs.
regUpCELL() : reg(8, "%03o") { }

};

class regDnCELL : public reg


{
public:
// Bit synchronize the counter inputs.
regDnCELL() : reg(8, "%03o") { }

};

class CTR
{
public:
static void execWP_GENRST();
static void execWP_WPCTR();
static void execRP_RSCT();
static void execWP_WOVR();
static void execWP_WOVC();

static unsigned getSubseq();

static unsigned pcUp[];


static unsigned pcDn[];

static regUpCELL register_UpCELL; // latches the selected priority counter cell (0-7)
static regDnCELL register_DnCELL; // latches the selected priority counter cell (0-7)

private:
static void resetAllpc();
};

#endif
CTR (CTR.cpp)
/****************************************************************************
* CTR - INVOLUNTARY PRIORITY COUNTER subsystem
*
* AUTHOR: John Pultorak
* DATE: 10/25/02
* FILE: CTR.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "CTR.h"
#include "INT.h"
#include "BUS.h"
#include "SEQ.h"

regUpCELL CTR::register_UpCELL; // latches the selected priority counter cell (0-7 (decimal))
regDnCELL CTR::register_DnCELL; // latches the selected priority counter cell (0-7 (decimal))

unsigned CTR::pcUp[8];
unsigned CTR::pcDn[8];

// PRIORITY COUNTERS

// ****************************************************
// The interrupt priorities are stored in RPCELL as 1-5, but
// the priority counter priorities are stored as 0-7; this
// inconsistency should be fixed, probably. Also, the method
// of address determination for the priority counters needs work

void CTR::resetAllpc()
{
for(int i=0; i<8; i++) { pcUp[i]=0; pcDn[i]=0; }
}

// priority encoder; outputs 0-7; 0=highest priority (OVCTR), 1=TIME2, 2=TIME1, etc
static bool newPriority = true; // a simulator performance optimization; not in the hardware AGC
unsigned getPriority()
{
// simulator optimization; don't recompute priority if the priority inputs haven't
changed
static unsigned priority = 7; // default (lowest priority)
if(!newPriority) return priority;

priority = 7; // default (lowest priority)


for(int i=0; i<8; i++)
{
if(CTR::register_UpCELL.readField(i+1,i+1) |
CTR::register_DnCELL.readField(i+1,i+1))
{
priority = i;
break;
}
}
newPriority = false;
return priority;
}

unsigned CTR::getSubseq()
{
unsigned pc = getPriority();

unsigned upCell = CTR::register_UpCELL.readField(pc+1,pc+1);


unsigned dnCell = CTR::register_DnCELL.readField(pc+1,pc+1);
if(upCell == 1 && dnCell == 0)
return PINCSEL;
else if(upCell == 0 && dnCell == 1)
return MINCSEL;
else
return NOPSEL;
}

void CTR::execWP_GENRST()
{
register_UpCELL.write(0);
register_DnCELL.write(0);

resetAllpc();
}

void CTR::execWP_WPCTR()
{
// transfer cell data into up and down synch registers

for(int i=0; i<8; i++)


{
register_UpCELL.writeField(i+1,i+1,pcUp[i]);
register_DnCELL.writeField(i+1,i+1,pcDn[i]);
}
newPriority=true; // a simulator performance optimization; not in hardware AGC
}

// Selected counter address is requested at TP1.


// Counter address is latched at TP12

void CTR::execRP_RSCT()
{
BUS::glbl_READ_BUS = 034 + getPriority();
}

void CTR::execWP_WOVR()
{
unsigned pc = getPriority();
if(register_UpCELL.readField(pc+1,pc+1))
{
pcUp[pc]=0;
}
if(register_DnCELL.readField(pc+1,pc+1))
{
pcDn[pc]=0;
}

// generate various actions in response to counter overflows:


switch(BUS::testOverflow(BUS::glbl_WRITE_BUS))
{
case POS_OVF: // positive overflow
switch(getPriority()) // get the counter
{
case TIME1: CTR::pcUp[TIME2]=1; break; // overflow from TIME1 increments
TIME2
case TIME3: INT::rupt[T3RUPT]=1; break; // overflow from TIME3 triggers
T3RUPT
case TIME4: INT::rupt[DSRUPT]=1; break; // overflow from TIME4 triggers
DSRUPT
}
break;
case NEG_OVF: break; // no actions for negative counter overflow
}
}

void CTR::execWP_WOVC()
{
switch(BUS::testOverflow(BUS::glbl_WRITE_BUS))
{
case POS_OVF: CTR::pcUp[OVCTR]=1; break; // incr OVCTR (034)
case NEG_OVF: CTR::pcDn[OVCTR]=1; break; // decr OVCTR (034)
}

// register_PCELL: Overflow from the selected counter appears


// on the bus when WOVR or WOVC is asserted;
// it could be used to trigger an interrupt
// or routed to increment another counter
DSP (DSP.h)
/****************************************************************************
* DSP - DSKY DISPLAY subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: DSP.h
*
* VERSIONS:
*
* DESCRIPTION:
* DSKY Display for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef DSP_H
#define DSP_H

class DSP
{
public:
// DSKY display

// major mode display


static char MD1;
static char MD2;

// verb display
static char VD1;
static char VD2;

// noun display
static char ND1;
static char ND2;

// R1
static char R1S;
static char R1D1;
static char R1D2;
static char R1D3;
static char R1D4;
static char R1D5;

// R2
static char R2S;
static char R2D1;
static char R2D2;
static char R2D3;
static char R2D4;
static char R2D5;

// R3
static char R3S;
static char R3D1;
static char R3D2;
static char R3D3;
static char R3D4;
static char R3D5;

// These flags control the sign; if both bits are 0 or 1, there is no sign.
// Otherwise, the sign is set by the selected bit.
static unsigned R1SP;
static unsigned R1SM;
static unsigned R2SP;
static unsigned R2SM;
static unsigned R3SP;
static unsigned R3SM;
// verb/noun flash
static unsigned flash;

static void clearOut0();

static char signConv(unsigned p, unsigned m);

static char outConv(unsigned in);

static void decodeRelayWord(unsigned in);


};

#endif
DSP (DSP.cpp)
/****************************************************************************
* DSP - DSKY DISPLAY subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: DSP.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "DSP.h"
#include <string.h>
#include <iostream.h>
#include <stdio.h>

bool dskyChanged = false; // true when DSKY display changes

// major mode display


char DSP::MD1=0;
char DSP::MD2=0;

// verb display
char DSP::VD1=0;
char DSP::VD2=0;

// noun display
char DSP::ND1=0;
char DSP::ND2=0;

// R1
char DSP::R1S=0;
char DSP::R1D1=0;
char DSP::R1D2=0;
char DSP::R1D3=0;
char DSP::R1D4=0;
char DSP::R1D5=0;

// R2
char DSP::R2S=0;
char DSP::R2D1=0;
char DSP::R2D2=0;
char DSP::R2D3=0;
char DSP::R2D4=0;
char DSP::R2D5=0;

// R3
char DSP::R3S=0;
char DSP::R3D1=0;
char DSP::R3D2=0;
char DSP::R3D3=0;
char DSP::R3D4=0;
char DSP::R3D5=0;

// These flags control the sign; if both bits are 0 or 1, there is no sign.
// Otherwise, the sign is set by the selected bit.
unsigned DSP::R1SP=0;
unsigned DSP::R1SM=0;
unsigned DSP::R2SP=0;
unsigned DSP::R2SM=0;
unsigned DSP::R3SP=0;
unsigned DSP::R3SM=0;

// flag controls 1 Hz flash of verb and noun display


unsigned DSP::flash = 0; // 0=flash off, 1=flash on

void DSP::clearOut0()
{
MD1 = MD2 = ' '; // major mode display
VD1 = VD2 = ' '; // verb display
ND1 = ND2 = ' '; // noun display
R1S = R1D1 = R1D2 = R1D3 = R1D4 = R1D5 = ' '; // R1
R2S = R2D1 = R2D2 = R2D3 = R2D4 = R2D5 = ' '; // R2
R3S = R3D1 = R3D2 = R3D3 = R3D4 = R3D5 = ' '; // R3

R1SP = R1SM = 0;
R2SP = R2SM = 0;
R3SP = R3SM = 0;
}

char DSP::signConv(unsigned p, unsigned m)


{
if(p && !m)
return '+';
else if(m && !p)
return '-';
else
return ' ';
}

char DSP::outConv(unsigned in)


{
switch(in)
{
case 000: return ' ';
case 025: return '0';
case 003: return '1';
case 031: return '2';
case 033: return '3';
case 017: return '4';
case 036: return '5';
case 034: return '6';
case 023: return '7';
case 035: return '8';
case 037: return '9';
}
return ' '; // error
}

void DSP::decodeRelayWord(unsigned in)


{
unsigned charSelect = (in & 074000) >> 11; // get bits 15-12
unsigned b11 = (in & 02000) >> 10; // get bit 11
unsigned bHigh = (in & 01740) >> 5; // get bits 10-6
unsigned bLow = in & 037;

//******************************
#ifdef NOTDEF
char buf[80];
sprintf(buf, "bits15-12: %02o, Bit11: %01o, bits10-6: %02o, bits5-1: %02o",
charSelect, b11, bHigh, bLow);
cout << buf << endl;
#endif
dskyChanged = true;
//******************************

switch(charSelect)
{
case 013: MD1 = outConv(bHigh); MD2 = outConv(bLow); break;
case 012: VD1 = outConv(bHigh); VD2 = outConv(bLow); flash = b11; break;
case 011: ND1 = outConv(bHigh); ND2 = outConv(bLow); break;
case 010: R1D1 = outConv(bLow); break;
// UPACT not implemented

case 007: R1SP = b11; R1S = signConv(R1SP, R1SM);


R1D2 = outConv(bHigh);
R1D3 = outConv(bLow); break;

case 006: R1SM = b11; R1S = signConv(R1SP, R1SM);


R1D4 = outConv(bHigh);
R1D5 = outConv(bLow); break;

case 005: R2SP = b11; R2S = signConv(R2SP, R2SM);


R2D1 = outConv(bHigh);
R2D2 = outConv(bLow); break;
case 004: R2SM = b11; R2S = signConv(R2SP, R2SM);
R2D3 = outConv(bHigh);
R2D4 = outConv(bLow); break;

case 003: R2D5 = outConv(bHigh);


R3D1 = outConv(bLow); break;

case 002: R3SP = b11; R3S = signConv(R3SP, R3SM);


R3D2 = outConv(bHigh);
R3D3 = outConv(bLow); break;

case 001: R3SM = b11; R3S = signConv(R3SP, R3SM);


R3D4 = outConv(bHigh);
R3D5 = outConv(bLow); break;
}
}
INP (INP.h)
/****************************************************************************
* INP - INPUT REGISTER subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: INP.h
*
* VERSIONS:
*
* DESCRIPTION:
* Input Registers for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef INP_H
#define INP_H

#include "reg.h"

class regIn0 : public reg


{
public:
regIn0() : reg(16, "%06o") { }
};

class regIn1 : public reg


{
public:
regIn1() : reg(16, "%06o") { }
};

class regIn2 : public reg


{
public:
regIn2() : reg(16, "%06o") { }
};

class regIn3 : public reg


{
public:
regIn3() : reg(16, "%06o") { }
};

class INP
{
public:
static void execRP_RA4();
static void execRP_RA5();
static void execRP_RA6();
static void execRP_RA7();
static regIn0 register_IN0; // input register 0
static regIn1 register_IN1; // input register 1
static regIn2 register_IN2; // input register 2
static regIn3 register_IN3; // input register 3
};

#endif
INP (INP.cpp)
/****************************************************************************
* INP - INPUT REGISTER subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: INP.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "INP.h"
#include "SEQ.h"
#include "KBD.h"
#include "MON.h"
#include "BUS.h"

regIn0 INP::register_IN0; // input register 0


regIn1 INP::register_IN1; // input register 1
regIn2 INP::register_IN2; // input register 2
regIn3 INP::register_IN3; // input register 3

void INP::execRP_RA4()
{
// Sample the state of the inputs at the moment the
// read pulse is asserted. In the H/W implementation,
// register 0 is a buffer, not a latch.
register_IN0.writeField(5,1,KBD::kbd);
register_IN0.writeField(6,6,0); // actually should be keypressed strobe
register_IN0.writeField(14,14,MON::SA);
register_IN0.clk();
BUS::glbl_READ_BUS = register_IN0.read();
}

void INP::execRP_RA5()
{
BUS::glbl_READ_BUS = register_IN1.read();
}

void INP::execRP_RA6()
{
BUS::glbl_READ_BUS = register_IN2.read();
}

void INP::execRP_RA7()
{
BUS::glbl_READ_BUS = register_IN3.read();
}
INT (INT.h)
/****************************************************************************
* INT - PRIORITY INTERRUPT subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: INT.h
*
* VERSIONS:
*
* DESCRIPTION:
* Priority Interrupts for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef INT_H
#define INT_H

#include "reg.h"

enum ruptAddress {
// Addresses for service routines of vectored interrupts
T3RUPT_ADDR =02004, // option 1: overflow of TIME 3
ERRUPT_ADDR =02010, // option 2: error signal
DSRUPT_ADDR =02014, // option 3: telemetry end pulse or TIME 4 overflow
KEYRUPT_ADDR =02020, // option 4: activity from MARK, keyboard, or tape reader
};

enum ruptNumber {
// Option number (selects rupt priority cell)
// NOTE: the priority cells (rupt[]) are indexed 0-4, but stored in the
// RPCELL register as 1-5; (0 in RPCELL means no interrupt)
T3RUPT =0, // option 1: overflow of TIME 3
ERRUPT =1, // option 2: error signal
DSRUPT =2, // option 3: telemetry end pulse or TIME 4 overflow
KEYRUPT =3, // option 4: activity from MARK, keyboard, or tape reader
};

class regRPCELL : public reg


{
public:
regRPCELL() : reg(5, "%02o") { }
};
// also inhibits additional interrupts while an interrupt is being processed

class regINHINT1 : public reg


{
public:
regINHINT1() : reg(1, "%01o") { }
};

class regINHINT : public reg


{
public:
regINHINT() : reg(1, "%01o") { }
};

class INT
{
public:
friend class CLK;
friend class MON;

static void execRP_RRPA();


static void execWP_GENRST();
static void execWP_RPT();
static void execWP_KRPT();
static void execWP_CLRP();
static void execWP_WOVI();
static void execWP_CLINH1();
static void execWP_INH();
static void execWP_CLINH();

static bool IRQ(); // returns true if an interrupt is requested

static unsigned rupt[];

private:
static void resetAllRupt();
static unsigned getPriorityRupt();

static regRPCELL register_RPCELL; // latches the selected priority interrupt vector (1-5)
static regINHINT1 register_INHINT1; // inhibits interrupts for 1 instruction (on WOVI)
static regINHINT register_INHINT; // inhibits interrupts on INHINT, reenables on RELINT
};

#endif
INT (INT.cpp)
/****************************************************************************
* INT - PRIORITY INTERRUPT subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: INT.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "INT.h"
#include "SEQ.h"
#include "BUS.h"

regRPCELL INT::register_RPCELL; // latches the selected priority interrupt vector (1-5)


regINHINT1 INT::register_INHINT1; // inhibits interrupts for 1 instruction (on WOVI)
regINHINT INT::register_INHINT; // inhibits interrupts on INHINT, reenables on RELINT

// NOTE: the priority cells (rupt[]) are indexed 0-4, but stored in the
// RPCELL register as 1-5; (0 in RPCELL means no interrupt)
unsigned INT::rupt[5];

bool INT::IRQ()
{
if( INT::getPriorityRupt() // if interrupt
requested
&& INT::register_RPCELL.read() == 0 // and interrupt not currently being
serviced
&& INT::register_INHINT1.read() == 0 // and interrupt not inhibited for 1
instruction
&& INT::register_INHINT.read() == 0) // and interrupts enabled (RELINT)
{
return true;
}
return false;
}

void INT::resetAllRupt()
{
for(int i=0; i<5; i++) { rupt[i]=0; }
}

// interrupt vector; outputs 1-5 (decimal) == vector; 0 == no interrupt


unsigned INT::getPriorityRupt()
{
for(int i=0; i<5; i++) { if(rupt[i]) return i+1; }
return 0;
}

void INT::execRP_RRPA()
{
BUS::glbl_READ_BUS = 02000 + (register_RPCELL.read() << 2);
}

// latches the selected priority interrupt vector (1-5)


// also inhibits additional interrupts while an interrupt is being processed

void INT::execWP_GENRST()
{
register_RPCELL.write(0);
register_INHINT.write(1);
resetAllRupt();
}

void INT::execWP_RPT()
{
register_RPCELL.write(INT::getPriorityRupt());
}

void INT::execWP_KRPT()
{
INT::rupt[register_RPCELL.read()-1] = 0;
}

void INT::execWP_CLRP()
{
register_RPCELL.write(0);
}

// INHINT1: inhibits interrupts for 1 instruction (on WOVI)

void INT::execWP_WOVI()
{
if(BUS::testOverflow(BUS::glbl_WRITE_BUS) != NO_OVF)
register_INHINT1.write(1);
}

void INT::execWP_CLINH1()
{
register_INHINT1.write(0);
}

// INHINT: inhibits interrupts on INHINT, reenables on RELINT

void INT::execWP_INH()
{
register_INHINT.write(1);
}

void INT::execWP_CLINH()
{
register_INHINT.write(0);
}
ISD (ISD.h)
/****************************************************************************
* ISD - INSTRUCTION SUBSEQUENCE DECODER subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: ISD.h
*
* VERSIONS:
*
* DESCRIPTION:
* Instruction Subsequence Decoder for the Block 1 Apollo Guidance Computer
* prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef ISD_H
#define ISD_H

#include "SEQ.h"
#include "CTR.h"

// INSTRUCTION SUBSEQUENCE DECODER

#ifdef NOTDEF
class ISD
{
public:
static subseq instructionSubsequenceDecoder();

static char* ISD::subseqString[];


};
#endif

#endif
ISD (ISD.cpp)
/****************************************************************************
* ISD - INSTRUCTION SUBSEQUENCE DECODER subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: ISD.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "ISD.h"

#ifdef NOTDEF
char* ISD::subseqString[] =
{
"TC0",
"CCS0",
"CCS1",
"NDX0",
"NDX1",
"RSM3",
"XCH0",
"CS0",
"TS0",
"AD0",
"MASK0",
"MP0",
"MP1",
"MP3",
"DV0",
"DV1",
"SU0",
"RUPT1",
"RUPT3",
"STD2",
"PINC0",
"MINC0",
"SHINC0",
"NO_SEQ"
};

subseq ISD::instructionSubsequenceDecoder()
{
// Combinational logic decodes instruction and the stage count
// to get the instruction subsequence.
static subseq decode[16][4] = {
{ TC0, RUPT1, STD2, RUPT3 }, // 00
{ CCS0, CCS1, NO_SEQ, NO_SEQ }, // 01
{ NDX0, NDX1, NO_SEQ, RSM3 }, // 02
{ XCH0, NO_SEQ, STD2, NO_SEQ }, // 03

{ NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 04


{ NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 05
{ NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 06
{ NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 07
{ NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 10

{ MP0, MP1, NO_SEQ, MP3 }, // 11


{ DV0, DV1, STD2, NO_SEQ }, // 12
{ SU0, NO_SEQ, STD2, NO_SEQ }, // 13

{ CS0, NO_SEQ, STD2, NO_SEQ }, // 14


{ TS0, NO_SEQ, STD2, NO_SEQ }, // 15
{ AD0, NO_SEQ, STD2, NO_SEQ }, // 16
{ MASK0, NO_SEQ, STD2, NO_SEQ } // 17

};

switch(CTR::getSubseq())
{
case PINCSEL: return PINC0;
case MINCSEL: return MINC0;
default: return decode[SEQ::register_SQ.read()][SEQ::register_STB.read()];
}
}

#endif
KBD (KBD.h)
/****************************************************************************
* KBD - DSKY KEYBOARD subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: KBD.h
*
* VERSIONS:
*
* DESCRIPTION:
* DSKY Keyboard for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef KBD_H
#define KBD_H

enum keyInType {
// DSKY keyboard input codes: Taken from E-1574, Appendix 1
// These codes enter the computer through bits 1-5 of IN0.
// The MSB is in bit 5; LSB in bit 1. Key entry generates KEYRUPT.
KEYIN_NONE =0, // no key depressed**
KEYIN_0 =020,
KEYIN_1 =001,
KEYIN_2 =002,
KEYIN_3 =003,
KEYIN_4 =004,
KEYIN_5 =005,
KEYIN_6 =006,
KEYIN_7 =007,
KEYIN_8 =010,
KEYIN_9 =011,
KEYIN_VERB =021,
KEYIN_ERROR_RESET =022,
KEYIN_KEY_RELEASE =031,
KEYIN_PLUS =032,
KEYIN_MINUS =033,
KEYIN_ENTER =034,
KEYIN_CLEAR =036,
KEYIN_NOUN =037,
};

class KBD
{
public:
static keyInType kbd; // latches the last key entry from the DSKY
static void keypress(keyInType c);
};

#endif
KBD (KBD.cpp)
/****************************************************************************
* KBD - DSKY KEYBOARD subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: KBD.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "KBD.h"
#include "INT.h"

// DSKY keyboard
keyInType KBD::kbd=KEYIN_NONE; // latches the last key entry from the DSKY

void KBD::keypress(keyInType c)
{
// latch the keycode
kbd = c;
// generate KEYRUPT interrupt
INT::rupt[KEYRUPT] = 1;
}
MBF (MBF.h)
/****************************************************************************
* MBF - MEMORY BUFFER REGISTER subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: MBF.h
*
* VERSIONS:
*
* DESCRIPTION:
* Memory Buffer Register for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef MBF_H
#define MBF_H

#include "reg.h"

class regG : public reg


{
public:
// all memory bits except bit 15 (parity)
// bit 15 is not used, so ignore it.
regG() : reg(16, "%06o") { }
};

class MBF
{
public:
static void execWP_GENRST();

static void execRP_RG();


static void execRP_WE();

static void execWP_WGn();


static void execWP_WGx();
static void execWP_W20();
static void execWP_W21();
static void execWP_W22();
static void execWP_W23();
static void execWP_SBWG();

// Bit 15 (parity) is kept in a separate register in PAR


// because it is independently loaded.
static regG register_G; // memory buffer register (except for bit 15)

static unsigned conv_RG[];


static unsigned conv_WGn[];
static unsigned conv_W20[];
static unsigned conv_W21[];
static unsigned conv_W22[];
static unsigned conv_W23[];
static unsigned conv_SBWG[];
static unsigned conv_WE[];

};

#endif
MBF (MBF.cpp)
/****************************************************************************
* MBF - MEMORY BUFFER REGISTER subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: MBF.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "MBF.h"
#include "SEQ.h"
#include "ADR.h"
#include "BUS.h"
#include "PAR.h"
#include "MEM.h"

// The actual bit 15 of register_G is not used.


regG MBF::register_G; // memory buffer register (except bit 15: parity)

unsigned MBF::conv_RG[] =
{ SG, SG, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2, B1 };

unsigned MBF::conv_SBWG[] =
{ SGM, BX, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2, B1 };

unsigned MBF::conv_WE[] =
{ BX, SG, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2, B1 };

unsigned MBF::conv_W20[] =
{ B1, BX, SG, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2 };

unsigned MBF::conv_W21[] =
{ SG, BX, SG, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2 };

unsigned MBF::conv_W22[] =
{ B14, BX, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2, B1, SG };

unsigned MBF::conv_W23[] =
{ SG, BX, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2, B1, SG };

void MBF::execWP_GENRST()
{
register_G.write(0);
}

void MBF::execRP_RG()
{
if(ADR::GTR_17())
{
BUS::glbl_READ_BUS = register_G.shiftData(0, register_G.read(), MBF::conv_RG);
}
}

void MBF::execRP_WE()
{
// Write G into memory; shift the sign to bit 15; parity is written from the
// PAR subsystem
MEM::MEM_DATA_BUS = (register_G.shiftData(0, MBF::register_G.read(), MBF::conv_WE));
}
void MBF::execWP_WGn()
{
register_G.write(BUS::glbl_WRITE_BUS);
}

void MBF::execWP_WGx()
{
// This is only used in PINC, MINC, and SHINC. Does not clear G
// register; writes (ORs) into G from RWBus and writes into parity
// from 1-15 generator. The sequence calls CLG in a previous TP to
// reset G to zero, so the OR operation can be safely eliminated
// from my implementation of the design.
register_G.write(BUS::glbl_WRITE_BUS);
}

void MBF::execWP_W20()
{
register_G.writeShift(BUS::glbl_WRITE_BUS, MBF::conv_W20);
}

void MBF::execWP_W21()
{
register_G.writeShift(BUS::glbl_WRITE_BUS, MBF::conv_W21);
}

void MBF::execWP_W22()
{
register_G.writeShift(BUS::glbl_WRITE_BUS, MBF::conv_W22);
}

void MBF::execWP_W23()
{
register_G.writeShift(BUS::glbl_WRITE_BUS, MBF::conv_W23);
}

void MBF::execWP_SBWG()
{
register_G.writeShift(MEM::MEM_DATA_BUS, MBF::conv_SBWG);

}
MEM (MEM.h)
/****************************************************************************
* MEM - ERASEABLE/FIXED MEMORY subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/26/02
* FILE: MEM.h
*
* VERSIONS:
*
* DESCRIPTION:
* Eraseable & Fixed Memory for the Block 1 Apollo Guidance Computer
* prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef MEM_H
#define MEM_H

#include "reg.h"

#define NUMFBANK 12 // number of 1024 word fixed memory banks

class regEMEM : public reg


{
public:
regEMEM() : reg(16, "%06o") { }
regEMEM& operator= (const unsigned& r) { write(r); return *this; }
};

class regFMEM : public reg


{
public:
regFMEM() : reg(16, "%06o") { }
regFMEM& operator= (const unsigned& r) { write(r); return *this; }
};

class MEM
{
public:
static void execWP_WE();
static void execRP_SBWG();

static regEMEM register_EMEM[]; // erasable memory


static regFMEM register_FMEM[]; // fixed memory

static unsigned MEM_DATA_BUS; // data lines: memory bits 15-1


static unsigned MEM_PARITY_BUS; // parity line: memory bit 16

static unsigned readMemory();


static void writeMemory(unsigned data);

// The following functions are used in the simulator,


// but are implemented in the AGC design.
static unsigned readMemory(unsigned address);
static void writeMemory(unsigned address, unsigned data);

};

#endif
MEM (MEM.cpp)
/****************************************************************************
* MEM - ERASEABLE/FIXED MEMORY subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/26/02
* FILE: MEM.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "MEM.h"
#include "ADR.h"
#include "stdlib.h"

regEMEM MEM::register_EMEM[1024]; // erasable memory


regFMEM MEM::register_FMEM[1024*(NUMFBANK+1)]; // fixed memory (lowest 1024 words ignored)

unsigned MEM::MEM_DATA_BUS = 0; // data lines: memory bits 15-1


unsigned MEM::MEM_PARITY_BUS = 0; // parity line: memory bit 16

void MEM::execWP_WE()
{
// Write into memory; parity bit in bit 16
writeMemory( (MEM_PARITY_BUS << 15) | MEM_DATA_BUS );
}

void MEM::execRP_SBWG()
{
MEM_DATA_BUS = readMemory() & 0077777; // everything except parity
MEM_PARITY_BUS = (readMemory() & 0100000) >> 15; // parity bit only
}

unsigned MEM::readMemory()
{
// Return memory value addressed by lower 10 bits of the S register (1K) and the
// bank decoder (which selects the 1K bank)
unsigned lowAddress = ADR::register_S.readField(10,1);

if(ADR::bankDecoder() == 0)
return MEM::register_EMEM[lowAddress].read();

unsigned highAddress = ADR::bankDecoder() << 10;


return MEM::register_FMEM[highAddress | lowAddress].read();
}

void MEM::writeMemory(unsigned data)


{
// Write into erasable memory addressed by lower 10 bits of the S register (1K)
// and the bank decoder (which selects the 1K bank)
unsigned lowAddress = ADR::register_S.readField(10,1);
if(ADR::bankDecoder() == 0)
{
MEM::register_EMEM[lowAddress].write(data);
MEM::register_EMEM[lowAddress].clk(); // not a synchronous FF, so execute
immediately *************
}
}

unsigned MEM::readMemory(unsigned address)


{
// Address is 14 bits. This function is used by the simulator for examining
// memory; it is not part of the AGC design.
unsigned lowAddress = address & 01777;
unsigned bank = (address & 036000) >> 10;

if(bank == 0)
return MEM::register_EMEM[lowAddress].read();
unsigned highAddress = bank << 10;
return MEM::register_FMEM[highAddress | lowAddress].read();
}

void MEM::writeMemory(unsigned address, unsigned data)


{
// Address is 14 bits. This function is used by the simulator for depositing into
// memory; it is not part of the AGC design. This function is also used to
// initialize fixed memory.
//************************************************************
// This function could also write the parity into memory
//************************************************************
unsigned lowAddress = address & 01777;
unsigned bank = (address & 036000) >> 10;

if(bank == 0)
{
if(lowAddress > 1024)
{
cout << "Error: Eraseable address=" << lowAddress << endl;
exit(0);
}
MEM::register_EMEM[lowAddress].write(data);
MEM::register_EMEM[lowAddress].clk(); // execute immediately
}
else
{
unsigned highAddress = bank << 10;
if((highAddress | lowAddress) >= 1024*(NUMFBANK+1))
{
cout << "Error: Fixed address=" << (highAddress | lowAddress) << endl;
exit(0);
}

MEM::register_FMEM[highAddress | lowAddress].write(data);
MEM::register_FMEM[highAddress | lowAddress].clk(); // execute immediately
}
}
MON (MON.h)
/****************************************************************************
* MON - AGC MONITOR subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: MON.h
*
* VERSIONS:
*
* DESCRIPTION:
* AGC Monitor for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef MON_H
#define MON_H

class MON
{
public:
static void displayAGC();

static char* MON::clkTypestring[];

static unsigned PURST; // power up reset


static unsigned RUN; // run/halt switch
static unsigned STEP; // single step switch
static unsigned INST; // instruction/sequence step select switch
static unsigned FCLK; // clock mode (0=single (manual) clock, 1=continuous clock)

static unsigned SA; // "standby allowed" SW;


// 0=NO (full power), 1=YES (low power)

static unsigned SCL_ENAB; // "scaler enabled" SW; 0=NO (scaler halted), 1=YES
(scaler running)
};

#endif
MON (MON.cpp)
/****************************************************************************
* MON - AGC MONITOR subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: MON.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "MON.h"

#include "TPG.h"
#include "MON.h"
#include "SCL.h"
#include "SEQ.h"
#include "INP.h"
#include "OUT.h"
#include "BUS.h"
#include "DSP.h"
#include "ADR.h"
#include "PAR.h"
#include "MBF.h"
#include "MEM.h"
#include "CTR.h"
#include "INT.h"
#include "KBD.h"
#include "CRG.h"
#include "ALU.h"
#include "CPM.h"
#include "ISD.h"
#include "CLK.h"

unsigned MON::PURST=1; // power up reset; initially high at startup


unsigned MON::RUN=0; // run/halt switch
unsigned MON::STEP=0; // single step switch
unsigned MON::INST=1; // instruction/sequence step select switch
unsigned MON::FCLK=0; // clock mode

unsigned MON::SA=0; // "standby allowed" SW; 0=NO (full power), 1=YES (low power)

unsigned MON::SCL_ENAB=1; // "scaler enabled" SW; 0=NO (scaler halted), 1=YES (scaler
running)

void MON::displayAGC()
{
char buf[100];
cout << "AGC4 SIMULATOR 1.16 -------------------------------" << endl;
sprintf(buf," TP: %-5s F17:%1d F13:%1d F10:%1d SCL:%06o",
TPG::tpTypestring[TPG::register_SG.read()],
SCL::register_F17.read(), SCL::register_F13.read(), SCL::register_F10.read(),
SCL::register_SCL.read());
cout << buf << endl;

sprintf(buf, " STA:%01o STB:%01o BR1:%01o BR2:%01o SNI:%01o CI:%01o


LOOPCTR:%01o",
SEQ::register_STA.read(), SEQ::register_STB.read(),
SEQ::register_BR1.read(), SEQ::register_BR2.read(),
SEQ::register_SNI.read(), ALU::register_CI.read(), SEQ::register_LOOPCTR.read());
cout << buf << endl;

sprintf(buf, " RPCELL:%05o INH1:%01o INH:%01o UpCELL:%03o DnCELL:%03o SQ:%02o %-6s


%-6s",
INT::register_RPCELL.read(), INT::register_INHINT1.read(),
INT::register_INHINT.read(),
CTR::register_UpCELL.read(), CTR::register_DnCELL.read(),
SEQ::register_SQ.read(), SEQ::instructionString[SEQ::register_SQ.read()],
CPM::subseqString[SEQ::glbl_subseq]);
cout << buf << endl;

sprintf(buf, " CP:%s", SEQ::getControlPulses());


cout << buf << endl;
// For the G register, bit 15 comes from register G15; the other bits (16, 14-1)
come
// from register G.
sprintf(buf, " S: %04o G:%06o P:%06o (r)RUN :%1d (p)PURST:%1d
(F2,F4)FCLK:%1d",
ADR::register_S.read(),
(MBF::register_G.read() & 0137777) | (PAR::register_G15.read() << 14),
PAR::register_P.read(),
MON::RUN, MON::PURST, MON::FCLK);
cout << buf << endl;

sprintf(buf, " RBU:%06o WBU:%06o P2:%01o (s)STEP:%1d",


BUS::glbl_READ_BUS & 0177777, BUS::glbl_WRITE_BUS & 0177777,
PAR::register_P2.read(), MON::STEP);
cout << buf << endl;

char parityAlm = ' ';


if(PAR::register_PALM.read()) parityAlm = '*';

sprintf(buf, " B:%06o CADR:%06o (n)INST:%1d PALM:[%c]",


ALU::register_B.read(), ADR::getEffectiveAddress(), MON::INST, parityAlm);
cout << buf << endl;

sprintf(buf, " X:%06o Y:%06o U:%06o (a)SA :%1d",


ALU::register_X.read(), ALU::register_Y.read(), ALU::register_U.read(), MON::SA);
cout << buf << endl;

cout << endl;


sprintf(buf, "00 A:%06o 15 BANK:%02o 36 TIME1:%06o 53 OPT Y:%06o",
CRG::register_A.read(), ADR::register_BNK.read(), MEM::readMemory(036),
MEM::readMemory(053));
cout << buf << endl;
sprintf(buf, "01 Q:%06o 16 RELINT:%6s 37 TIME3:%06o 54 TRKR X:%06o",
CRG::register_Q.read(),"", MEM::readMemory(037), MEM::readMemory(054));
cout << buf << endl;
sprintf(buf, "02 Z:%06o 17 INHINT:%6s 40 TIME4:%06o 55 TRKR Y:%06o",
CRG::register_Z.read(),"", MEM::readMemory(040), MEM::readMemory(055));
cout << buf << endl;
sprintf(buf, "03 LP:%06o 20 CYR:%06o 41 UPLINK:%06o 56 TRKR Z:%06o",
CRG::register_LP.read(), MEM::readMemory(020), MEM::readMemory(041),
MEM::readMemory(056));
cout << buf << endl;

sprintf(buf, "04 IN0:%06o 21 SR:%06o 42 OUTCR1:%06o",


INP::register_IN0.read(), MEM::readMemory(021), MEM::readMemory(042));
cout << buf << endl;

char progAlm = ' ';


if(OUT::register_OUT1.read() & 0400) progAlm = '*';

char compFail = ' '; // also called 'check fail' and 'oper err'
if(OUT::register_OUT1.read() & 0100) compFail = '*';

char keyRels = ' ';


if(OUT::register_OUT1.read() & 020) keyRels = '*';

char upTl = ' ';


if(OUT::register_OUT1.read() & 004) upTl = '*';

char comp = ' '; // also called comp acty


if(OUT::register_OUT1.read() & 001) comp = '*';

sprintf(buf, "05 IN1:%06o 22 CYL:%06o 43 OUTCR2:%06o CF:[%c%c]:KR [%c]:PA",


INP::register_IN1.read(), MEM::readMemory(022), MEM::readMemory(043),
compFail, keyRels, progAlm);
cout << buf << endl;

sprintf(buf, "06 IN2:%06o 23 SL:%06o 44 PIPA X:%06o",


INP::register_IN2.read(), MEM::readMemory(023), MEM::readMemory(044));
cout << buf << endl;

sprintf(buf, "07 IN3:%06o 24 ZRUPT:%06o 45 PIPA Y:%06o A:[%c%c] M:[%c%c]",


INP::register_IN3.read(), MEM::readMemory(024), MEM::readMemory(045),
upTl, comp, DSP::MD1, DSP::MD2);

cout << buf << endl;


char fc = ' '; if(DSP::flash) fc = '*';
sprintf(buf, "10 OUT0: 25 BRUPT:%06o 46 PIPA Z:%06o V:[%c%c] N:[%c%c] %c",
MEM::readMemory(025), MEM::readMemory(046),
DSP::VD1, DSP::VD2, DSP::ND1, DSP::ND2, fc);
cout << buf << endl;
sprintf(buf, "11 OUT1:%06o 26 ARUPT:%06o 47 CDU X:%06o R1:[ %c%c%c%c%c%c ]",
OUT::register_OUT1.read(), MEM::readMemory(026), MEM::readMemory(047),
DSP::R1S, DSP::R1D1, DSP::R1D2, DSP::R1D3, DSP::R1D4, DSP::R1D5);
cout << buf << endl;
sprintf(buf, "12 OUT2:%06o 27 QRUPT:%06o 50 CDU Y:%06o R2:[ %c%c%c%c%c%c ]",
OUT::register_OUT2.read(), MEM::readMemory(027), MEM::readMemory(050),
DSP::R2S, DSP::R2D1, DSP::R2D2, DSP::R2D3, DSP::R2D4, DSP::R2D5);
cout << buf << endl;
sprintf(buf, "13 OUT3:%06o 34 OVCTR:%06o 51 CDU Z:%06o R3:[ %c%c%c%c%c%c ]",
OUT::register_OUT3.read(), MEM::readMemory(034), MEM::readMemory(051),
DSP::R3S, DSP::R3D1, DSP::R3D2, DSP::R3D3, DSP::R3D4, DSP::R3D5);
cout << buf << endl;
sprintf(buf, "14 OUT4:%06o 35 TIME2:%06o 52 OPT X:%06o",
OUT::register_OUT4.read(), MEM::readMemory(035), MEM::readMemory(052));
cout << buf << endl;
}
OUT (OUT.h)
/****************************************************************************
* OUT - OUTPUT REGISTER subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: OUT.h
*
* VERSIONS:
*
* DESCRIPTION:
* Output Registers for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef OUT_H
#define OUT_H

#include "reg.h"

class regOut1 : public reg


{
public: regOut1() : reg(16, "%06o") { }
};

class regOut2 : public reg


{
public: regOut2() : reg(16, "%06o") { }

};

class regOut3 : public reg


{
public: regOut3() : reg(16, "%06o") { }
};

class regOut4 : public reg


{
public: regOut4() : reg(16, "%06o") { }
};

class OUT
{
public:
static void execWP_GENRST();
static void execWP_WA10();
static void execRP_RA11();
static void execWP_WA11();
static void execRP_RA12();
static void execWP_WA12();
static void execRP_RA13();
static void execWP_WA13();
static void execRP_RA14();
static void execWP_WA14();

static regOut1 register_OUT1; // output register 1


static regOut2 register_OUT2; // output register 2
static regOut3 register_OUT3; // output register 3
static regOut4 register_OUT4; // output register 4
};

#endif
OUT (OUT.cpp)
/****************************************************************************
* OUT - OUTPUT REGISTER subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: OUT.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "OUT.h"
#include "SEQ.h"
#include "BUS.h"
#include "DSP.h"
#include "ADR.h"
#include "PAR.h"
#include <stdlib.h>

regOut1 OUT::register_OUT1; // output register 1


regOut2 OUT::register_OUT2; // output register 2
regOut3 OUT::register_OUT3; // output register 3
regOut4 OUT::register_OUT4; // output register 4

// Writing to OUT0 loads the selected DSKY display register.

void OUT::execWP_GENRST()
{
DSP::clearOut0();

register_OUT1.write(0);
register_OUT2.write(0);
}

void OUT::execWP_WA10()
{
DSP::decodeRelayWord(BUS::glbl_WRITE_BUS);
}

void OUT::execRP_RA11()
{
BUS::glbl_READ_BUS = register_OUT1.read();
}

void OUT::execWP_WA11()
{
register_OUT1.write(BUS::glbl_WRITE_BUS);
}

void OUT::execRP_RA12()
{
BUS::glbl_READ_BUS = register_OUT2.read();
}

void OUT::execWP_WA12()
{
register_OUT2.write(BUS::glbl_WRITE_BUS);
}
void OUT::execRP_RA13()
{
BUS::glbl_READ_BUS = register_OUT3.read();
}

void OUT::execWP_WA13()
{
register_OUT3.write(BUS::glbl_WRITE_BUS);
}

void OUT::execRP_RA14()
{
BUS::glbl_READ_BUS = register_OUT4.read();
}

void OUT::execWP_WA14()
{
register_OUT4.write(BUS::glbl_WRITE_BUS);
}
PAR (PAR.h)
/****************************************************************************
* PAR - PARITY GENERATION AND TEST subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: PAR.h
*
* VERSIONS:
*
* DESCRIPTION:
* Parity Generation and Test for the Block 1 Apollo Guidance Computer
* prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef PAR_H
#define PAR_H

#include "reg.h"

class regG15 : public reg


{
public:
// memory buffer register bit 15 (parity) only
regG15() : reg(1, "%01o") { }
};

class regP : public reg


{
public: regP() : reg(16, "%06o") { }
};

class regP2 : public reg


{
public:
regP2() : reg(1, "%01o") { }
};

class regPALM : public reg


{
public:
// parity alarm FF (set on TP)
regPALM() : reg(1, "%01o") { }
};

class PAR
{
public:
static void execRP_WE();

static void execWP_WP();


static void execWP_WPx();
static void execWP_WP2();
static void execWP_RP2();
static void execWP_GP();
static void execWP_SBWG();
static void execWP_WGx();
static void execWP_CLG();

static void execWP_GENRST();


static void execWP_TP();

static void CLR_PALM(); // asynchronous clear for PARITY ALARM

// memory buffer register bit 15; the rest of the


// memory buffer register is defined in MBF
static regG15 register_G15;

static regP2 register_P2;


static regP register_P;

static regPALM register_PALM;

static unsigned gen1_15Parity(unsigned r);


static unsigned genP_15Parity(unsigned r);

static unsigned conv_WP[];


};

#endif
PAR (PAR.cpp)
/****************************************************************************
* PAR - PARITY GENERATION AND TEST subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: PAR.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "PAR.h"
#include "SEQ.h"
#include "BUS.h"
#include "MBF.h"
#include "ADR.h"
#include "MEM.h"

regP PAR::register_P;
regP2 PAR::register_P2;
regG15 PAR::register_G15; // memory buffer register bit 15
regPALM PAR::register_PALM; // PARITY ALARM FF

unsigned PAR::conv_WP[] =
{
BX, SG, B14, B13, B12, B11, B10, B9, B8, B7, B6, B5, B4, B3, B2, B1
};

void PAR::execRP_WE()
{
// Write parity into memory.
MEM::MEM_PARITY_BUS = PAR::register_G15.read();

// IMPLEMENTATION NOTE: It has been empirically determined that the following


// control signals are mutually exclusive (there is never more than one of these
// generated at any time):
// GP, WGX, RP2, SBWG, CLG

// NOTE: WP clears register_P before writing into it. Strictly speaking, WPx isn't
// supposed to clear the register (should OR into the register), but in the counter
// sequences where WPx is used, register_P is always cleared in the previous TP by
// asserting WP with default zeroes on the write bus.

void PAR::execWP_WP()
{
// set all bits except parity bit
register_P.writeShift(BUS::glbl_WRITE_BUS, PAR::conv_WP);
// now set parity bit; in the actual AGC, this is
// a single operation.
if(SEQ::isAsserted(RG))
register_P.writeField(16, 16, register_G15.read());
else
register_P.writeField(16, 16, 0); // clear parity bit
}

void PAR::execWP_WPx()
{
// set all bits except parity bit
register_P.writeShift(BUS::glbl_WRITE_BUS, PAR::conv_WP);
// now set parity bit; in the actual AGC, this is
// a single operation.
if(SEQ::isAsserted(RG))
register_P.writeField(16, 16, register_G15.read());
else
register_P.writeField(16, 16, 0); // clear parity bit
}

void PAR::execWP_WP2()
{
register_P2.write(gen1_15Parity(register_P.read()));
}

void PAR::execWP_RP2()
{
register_G15.write(register_P2.read());
}

void PAR::execWP_GP()
{
register_G15.write(gen1_15Parity(register_P.read()));
}

void PAR::execWP_SBWG()
{
register_G15.write(MEM::MEM_PARITY_BUS); // load memory bit 16 (parity) into G15
}

void PAR::execWP_WGx()
{
// This is only used in PINC, MINC, and SHINC. Does not clear G
// register; writes (ORs) into G from RWBus and writes into parity
// from 1-15 generator. All done in one operation, although I show
// it in two steps here. The sequence calls CLG in a previous TP.
register_G15.write(PAR::gen1_15Parity(register_P.read()));
}

void PAR::execWP_CLG()
{
register_G15.write(0);
}

void PAR::execWP_GENRST()
{
register_PALM.write(0);
}

void PAR::execWP_TP()
{
if(ADR::GTR_27() && genP_15Parity(register_P.read()))
register_PALM.write(genP_15Parity(register_P.read()));
}

void PAR::CLR_PALM()
{
// asynchronous clear for PARITY ALARM (from MON)
register_PALM.clear();
}

unsigned PAR::gen1_15Parity(unsigned r)
{
//check the lower 15 bits of 'r' and return the odd parity;
//bit 16 is ignored.
unsigned evenParity =
(1&(r>>0)) ^ (1&(r>>1)) ^ (1&(r>>2)) ^ (1&(r>>3)) ^
(1&(r>>4)) ^ (1&(r>>5)) ^ (1&(r>>6)) ^ (1&(r>>7)) ^
(1&(r>>8)) ^ (1&(r>>9)) ^ (1&(r>>10)) ^ (1&(r>>11)) ^
(1&(r>>12)) ^ (1&(r>>13)) ^ (1&(r>>14));
return ~evenParity & 1; // odd parity
}

unsigned PAR::genP_15Parity(unsigned r)
{
//check all 16 bits of 'r' and return the odd parity
unsigned evenParity =
(1&(r>>0)) ^ (1&(r>>1)) ^ (1&(r>>2)) ^ (1&(r>>3)) ^
(1&(r>>4)) ^ (1&(r>>5)) ^ (1&(r>>6)) ^ (1&(r>>7)) ^
(1&(r>>8)) ^ (1&(r>>9)) ^ (1&(r>>10)) ^ (1&(r>>11)) ^
(1&(r>>12)) ^ (1&(r>>13)) ^ (1&(r>>14)) ^ (1&(r>>15));
return ~evenParity & 1; // odd parity
}
Registers (reg.h)

#ifndef reg_H
#define reg_H

#include <iostream.h>
#include <string.h>
#include <stdio.h>

class reg
{
public:
virtual unsigned read() { return mask & slaveVal; }
virtual void write(unsigned v) { load = true; masterVal = mask & v; }

// asynchronous clear
void clear() { slaveVal = 0; }

// load is set when a register is written into.


void clk() { if(load) slaveVal = masterVal; load = false; }

unsigned readField(unsigned msb, unsigned lsb); // bitfield numbered n - 1


void writeField(unsigned msb, unsigned lsb, unsigned v); // bitfield numbered n - 1

// Write a 16-bit word (in) into the register. Transpose the bits according to
// the specification (ib).
void writeShift(unsigned in, unsigned* ib);

// Return a shifted 16-bit word. Transpose the 'in' bits according to


// the specification 'ib'. 'Or' the result to out and return the value.
unsigned shiftData(unsigned out, unsigned in, unsigned* ib);

unsigned outmask() { return mask; }

protected:
reg(unsigned s, char* fs)
: size(s), mask(0), masterVal(0), slaveVal(0), fmtString(fs), load(false)
{ mask = buildMask(size);}

static unsigned buildMask(unsigned s);

friend ostream& operator << (ostream& os, const reg& r)


{ char buf[32]; sprintf(buf, r.fmtString, r.slaveVal); os << buf; return os; }

private:
unsigned size; // bits
unsigned masterVal;
unsigned slaveVal;
unsigned mask;
char* fmtString;
bool load;

reg(); // prevent instantiation of default constructor


};

#endif
Registers (reg.cpp)

#include "reg.h"
#include <math.h>
#include "BUS.h"

unsigned reg::buildMask(unsigned s)
{
unsigned msk = 0;
for(unsigned i=0; i<s; i++)
{
msk = (msk << 1) | 1;
}
return msk;
}

unsigned reg::readField(unsigned msb, unsigned lsb)


{
return (slaveVal >> (lsb-1)) & buildMask((msb-lsb)+1);
}

void reg::writeField(unsigned msb, unsigned lsb, unsigned v)


{
load = true;
unsigned fmask = buildMask((msb-lsb)+1) << (lsb-1);
v = (v << (lsb-1)) & fmask;
masterVal = (masterVal &(~fmask)) | v;
}

void reg::writeShift(unsigned in, unsigned* ib)


{
load = true;
unsigned out = masterVal;

// iterate through each bit of the output word, copying in bits from the input
// word and transposing bit position according to the specification (ib)
for(unsigned i=0; i<16; i++)
{
if(ib[i] == BX) continue; // BX is 'don't care', so leave it alone

// zero the output bit at 'ob', where ob specifies a bit


// position (numbered 16-1, where 1 is lsb)
unsigned ob = 16-i;
unsigned obmask = 1 << (ob - 1); // create mask for output bit
out &= ~obmask;

if(ib[i] == D0) continue; // D0 is 'force the bit to zero'

// copy input bit ib[i] to output bit 'ob', where ib and ob


// specify bit positions (numbered 16-1, where 1 is lsb)
unsigned ibmask = 1 << (ib[i] - 1); // create mask for input bit
unsigned inbit = in & ibmask;

int shift = ib[i]-ob;


if(shift<0)
inbit = inbit << abs(shift);
else if(shift > 0)
inbit = inbit >> shift;
out |= inbit;
}
masterVal = out;
}

unsigned reg::shiftData(unsigned out, unsigned in, unsigned* ib)


{
// iterate through each bit of the output word, copying in bits from the input
// word and transposing bit position according to the specification (ib)
for(unsigned i=0; i<16; i++)
{
if(ib[i] == BX) continue; // BX is 'don't care', so leave it alone

// zero the output bit at 'ob', where ob specifies a bit


// position (numbered 16-1, where 1 is lsb)
unsigned ob = 16-i;
unsigned obmask = 1 << (ob - 1); // create mask for output bit
out &= ~obmask;

if(ib[i] == D0) continue; // D0 is 'force the bit to zero'

// copy input bit ib[i] to output bit 'ob', where ib and ob


// specify bit positions (numbered 16-1, where 1 is lsb)
unsigned ibmask = 1 << (ib[i] - 1); // create mask for input bit
unsigned inbit = in & ibmask;

int shift = ib[i]-ob;


if(shift<0)
inbit = inbit << abs(shift);
else if(shift > 0)
inbit = inbit >> shift;
out |= inbit;
}
return out;
}
SCL (SCL.h)
/****************************************************************************
* SCL - SCALER subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: SCL.h
*
* VERSIONS:
*
* DESCRIPTION:
* Scaler for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef SCL_H
#define SCL_H

#include "reg.h"

class regF17 : public reg


{
public:
regF17() : reg(2, "%01o") { }
};

class regF13 : public reg


{
public:
regF13() : reg(2, "%01o") { }
};

class regF10 : public reg


{
public:
regF10() : reg(2, "%01o") { }
};

class regSCL : public reg


{
public:
regSCL() : reg(17, "%06o") { }
};

class SCL
{
public:
static void doexecWP_SCL();
static void doexecWP_F17();
static void doexecWP_F13();
static void doexecWP_F10();

static regSCL register_SCL;

// Normally outputs '0'; outputs '1' for one


// clock pulse at the indicated frequency.
static unsigned F17x(); // 0.78125 Hz scaler output
static unsigned F13x(); // 12.5 Hz scaler output
static unsigned F10x(); // 100 Hz scaler output

static regF17 register_F17;


static regF13 register_F13;
static regF10 register_F10;
};
#endif
SCL (SCL.cpp)
/****************************************************************************
* SCL - SCALER subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: SCL.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "SCL.h"
#include "CTR.h"
#include "MON.h"

regSCL SCL::register_SCL;
regF17 SCL::register_F17;
regF13 SCL::register_F13;
regF10 SCL::register_F10;

enum oneShotType { // **inferred; not defined in orignal R393 AGC4 spec.


WAIT_FOR_TRIGGER=0,
OUTPUT_PULSE=1, // LSB (bit 1) is the output bit for the one-shot
WAIT_FOR_RESET=2
};

void SCL::doexecWP_F17()
{
int bit = SCL::register_SCL.readField(17,17);
switch(register_F17.read())
{
case WAIT_FOR_TRIGGER: if(bit==1) register_F17.write(OUTPUT_PULSE); break;
case OUTPUT_PULSE: register_F17.write(WAIT_FOR_RESET); break;
case WAIT_FOR_RESET: if(bit==0) register_F17.write(WAIT_FOR_TRIGGER); break;
default: ;
}
}

void SCL::doexecWP_F13()
{
int bit = SCL::register_SCL.readField(13,13);
switch(register_F13.read())
{
case WAIT_FOR_TRIGGER: if(bit==1) register_F13.write(OUTPUT_PULSE); break;
case OUTPUT_PULSE: register_F13.write(WAIT_FOR_RESET); break;
case WAIT_FOR_RESET: if(bit==0) register_F13.write(WAIT_FOR_TRIGGER); break;
default: ;
}
}

void SCL::doexecWP_F10()
{
int bit = SCL::register_SCL.readField(10,10);
switch(register_F10.read())
{
case WAIT_FOR_TRIGGER: if(bit==1) register_F10.write(OUTPUT_PULSE); break;
case OUTPUT_PULSE: register_F10.write(WAIT_FOR_RESET);
CTR::pcUp[TIME1] = 1;
CTR::pcUp[TIME3] = 1;
CTR::pcUp[TIME4] = 1;
break;
case WAIT_FOR_RESET: if(bit==0) register_F10.write(WAIT_FOR_TRIGGER); break;
default: ;
}
}

unsigned SCL::F17x()
{
return register_F17.readField(1,1);
}

unsigned SCL::F13x()
{
return register_F13.readField(1,1);
}

unsigned SCL::F10x()
{
return register_F10.readField(1,1);
}

void SCL::doexecWP_SCL()
{
if(MON::SCL_ENAB) // if the scaler is enabled
{
//write((read() + 1) % outmask());
register_SCL.write((register_SCL.read() + 1));
}
}
SEQ (SEQ.h)
/****************************************************************************
* SEQ - SEQUENCE GENERATOR subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: SEQ.h
*
* VERSIONS:
*
* DESCRIPTION:
* Sequence Generator for the Block 1 Apollo Guidance Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef SEQ_H
#define SEQ_H

#include "reg.h"

#define MAXPULSES 15
#define MAX_IPULSES 5 // no more than 5 instruction-generated pulses active at any time

enum cpType { // **inferred; not defined in orignal R393 AGC4 spec.


NO_PULSE=0,

// OUTPUTS FROM SUBSYSTEM A


CI =1, // Carry in
CLG =2, // Clear G
CLCTR =3, // Clear loop counter
CTR =4, // Loop counter
GP =5, // Generate Parity
KRPT =6, // Knock down Rupt priority
NISQ =7, // New instruction to the SQ register
RA =8, // Read A
RB =9, // Read B
RB14 =10, // Read bit 14
RC =11, // Read C
RG =12, // Read G
RLP =13, // Read LP
RP2 =14, // Read parity 2
RQ =15, // Read Q
RRPA =16, // Read RUPT address
RSB =17, // Read sign bit
RSCT =18, // Read selected counter address
RU =19, // Read sum
RZ =20, // Read Z
R1 =21, // Read 1
R1C =22, // Read 1 complimented
R2 =23, // Read 2
R22 =24, // Read 22
R24 =25, // Read 24
ST1 =26, // Stage 1
ST2 =27, // Stage 2
TMZ =28, // Test for minus zero
TOV =29, // Test for overflow
TP =30, // Test parity
TRSM =31, // Test for resume
TSGN =32, // Test sign
TSGN2 =33, // Test sign 2
WA =34, // Write A
WALP =35, // Write A and LP
WB =36, // Write B
WGx =37, // Write G (do not reset)
WLP =38, // Write LP
WOVC =39, // Write overflow counter
WOVI =40, // Write overflow RUPT inhibit
WOVR =41, // Write overflow
WP =42, // Write P
WPx =43, // Write P (do not reset)
WP2 =44, // Write P2
WQ =45, // Write Q
WS =46, // Write S
WX =47, // Write X
WY =48, // Write Y
WYx =49, // Write Y (do not reset)
WZ =50, // Write Z

// OUTPUTS FROM SUBSYSTEM A; USED AS INPUTS TO SUBSYSTEM B ONLY;


// NOT USED OUTSIDE CPM
RSC =51, // Read special and central (output to B only, not outside CPM)
WSC =52, // Write special and central (output to B only, not outside CPM)
WG =53, // Write G (output to B only, not outside CPM)

// OUTPUTS FROM SUBSYSTEM A; USED AS INPUTS TO SUBSYSTEM C ONLY;


// NOT USED OUTSIDE CPM
SDV1 =54, // Subsequence DV1 is currently active
SMP1 =55, // Subsequence MP1 is currently active
SRSM3 =56, // Subsequence RSM3 is currently active

// EXTERNAL OUTPUTS FROM SUBSYSTEM B


//
RA0 =57, // Read register at address 0 (A)
RA1 =58, // Read register at address 1 (Q)
RA2 =59, // Read register at address 2 (Z)
RA3 =60, // Read register at address 3 (LP)
RA4 =61, // Read register at address 4
RA5 =62, // Read register at address 5
RA6 =63, // Read register at address 6
RA7 =64, // Read register at address 7
RA10 =65, // Read register at address 10 (octal)
RA11 =66, // Read register at address 11 (octal)
RA12 =67, // Read register at address 12 (octal)
RA13 =68, // Read register at address 13 (octal)
RA14 =69, // Read register at address 14 (octal)
RBK =70, // Read BNK
WA0 =71, // Write register at address 0 (A)
WA1 =72, // Write register at address 1 (Q)
WA2 =73, // Write register at address 2 (Z)
WA3 =74, // Write register at address 3 (LP)
WA10 =75, // Write register at address 10 (octal)
WA11 =76, // Write register at address 11 (octal)
WA12 =77, // Write register at address 12 (octal)
WA13 =78, // Write register at address 13 (octal)
WA14 =79, // Write register at address 14 (octal)
WBK =80, // Write BNK
WGn =81, // Write G (normal gates)**
W20 =82, // Write into CYR
W21 =83, // Write into SR
W22 =84, // Write into CYL
W23 =85, // Write into SL

// THESE ARE THE LEFTOVERS -- THEY'RE PROBABLY USED IN SUBSYSTEM C


//
GENRST =86, // General Reset**
CLINH =87, // Clear INHINT**
CLINH1 =88, // Clear INHINT1**
CLSTA =89, // Clear state counter A (STA)**
CLSTB =90, // Clear state counter B (STB)**
CLISQ =91, // Clear SNI**
CLRP =92, // Clear RPCELL**
INH =93, // Set INHINT**
RPT =94, // Read RUPT opcode **
SBWG =95, // Write G from memory
SETSTB =96, // Set the ST1 bit of STB
WE =97, // Write E-MEM from G
WPCTR =98, // Write PCTR (latch priority counter sequence)**
WSQ =99, // Write SQ
WSTB =100, // Write stage counter B (STB)**
R2000 =101, // Read 2000 **
};

// INSTRUCTIONS

// Op Codes, as they appear in the SQ register.


enum instruction {
// The code in the SQ register is the same as the op code for these
// four instructions.
TC =00, // 00 TC K Transfer Control 1 MCT
CCS =01, // 01 CCS K Count, Compare, and Skip 2 MCT
INDEX =02, // 02 INDEX K 2 MCT
XCH =03, // 03 XCH K Exchange 2 MCT

// The SQ register code is the op code + 010 (octal). This happens because all
// of these instructions have bit 15 set (the sign (SG) bit) while in memory. When the
// instruction is copied from memory to the memory buffer register (G) to register
// B, the SG bit moves from bit 15 to bit 16 and the sign is copied back into bit
// 15 (US). Therefore, the CS op code (04) becomes (14), and so on.
CS =014, // 04 CS K Clear and Subtract 2 MCT
TS =015, // 05 TS K Transfer to Storage 2 MCT
AD =016, // 06 AD K Add 2 or 3 MCT
MASK =017, // 07 MASK K Bitwise AND 2 MCT

// These are extended instructions. They are accessed by executing an INDEX 5777
// before each instruction. By convention, address 5777 contains 47777. The INDEX
// instruction adds 47777 to the extended instruction to form the SQ op code. For
// example, the INDEX adds 4 to the 4 op code for MP to produce the 11 (octal; the
// addition generates an end-around-carry). SQ register code (the 7777 part is a
// negative zero).
MP =011, // 04 MP K Multiply 10 MCT
DV =012, // 05 DV K Divide 18 MCT
SU =013, // 06 SU K Subtract 4 or 5 MCT
};

enum subseq {
TC0 =0,
CCS0 =1,
CCS1 =2,
NDX0 =3,
NDX1 =4,
RSM3 =5,
XCH0 =6,
CS0 =7,
TS0 =8,
AD0 =9,
MASK0 =10,
MP0 =11,
MP1 =12,
MP3 =13,
DV0 =14,
DV1 =15,
SU0 =16,
RUPT1 =17,
RUPT3 =18,
STD2 =19,
PINC0 =20,
MINC0 =21,
SHINC0 =22,
NO_SEQ =23
};

enum scType { // identifies subsequence for a given instruction


SUB0=0, // ST2=0, ST1=0
SUB1=1, // ST2=0, ST1=1
SUB2=2, // ST2=1, ST1=0
SUB3=3 // ST2=1, ST1=1
};

enum brType {
BR00 =0, // BR1=0, BR2=0
BR01 =1, // BR1=0, BR2=1
BR10 =2, // BR1=1, BR2=0
BR11 =3, // BR1=1, BR2=1
NO_BR =4 // NO BRANCH
};

const int GOPROG =02000; // bottom address of fixed memory

class regSQ : public reg


{
public:
regSQ() : reg(4, "%02o") { }
};

class regSTA : public reg


{
public:
regSTA() : reg(2, "%01o") { }
};

class regSTB : public reg


{
public:
regSTB() : reg(2, "%01o") { }
};

class regBR1 : public reg


{
public:
regBR1() : reg(1, "%01o") { }
};

class regBR2 : public reg


{
public:
regBR2() : reg(1, "%01o") { }
};

class regCTR : public reg


{
public:
regCTR() : reg(3, "%01o") { }
};

class regSNI : public reg


{
public: regSNI() : reg(1, "%01o") { }
};

class SEQ
{
public:
static void execWP_GENRST();
static void execWP_WSQ();
static void execWP_NISQ();
static void execWP_CLISQ();
static void execWP_ST1();
static void execWP_ST2();
static void execWP_TRSM();
static void execWP_CLSTA();
static void execWP_WSTB();
static void execWP_CLSTB();
static void execWP_SETSTB();
static void execWP_TSGN();
static void execWP_TOV();
static void execWP_TMZ();
static void execWP_TSGN2();
static void execWP_CTR();
static void execWP_CLCTR();

static regSNI register_SNI; // select next intruction flag


static cpType glbl_cp[MAXPULSES]; // current set of asserted control pulses
(MAXPULSES)

static char* cpTypeString[];


// Test the currently asserted control pulses; return true if the specified
// control pulse is active.
static bool isAsserted(cpType pulse);

// Return a string containing the names of all asserted control pulses.


static char* getControlPulses();

static subseq glbl_subseq; // currently decoded instruction subsequence

static regSQ register_SQ; // instruction register


static regSTA register_STA; // stage counter A
static regSTB register_STB; // stage counter B
static regBR1 register_BR1; // branch register1
static regBR2 register_BR2; // branch register2
static regCTR register_LOOPCTR; // loop counter

static char* instructionString[];


};

#endif
SEQ (SEQ.cpp)
/****************************************************************************
* SEQ - SEQUENCE GENERATOR subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: SEQ.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "SEQ.h"
#include "ADR.h"
#include "BUS.h"

regSNI SEQ::register_SNI; // select next intruction flag


cpType SEQ::glbl_cp[]; // current set of asserted control pulses (MAXPULSES)

regSQ SEQ::register_SQ; // instruction register


regSTA SEQ::register_STA; // stage counter A
regSTB SEQ::register_STB; // stage counter B
regBR1 SEQ::register_BR1; // branch register1
regBR2 SEQ::register_BR2; // branch register2
regCTR SEQ::register_LOOPCTR; // loop counter
subseq SEQ::glbl_subseq; // currently decoded instruction subsequence

char* SEQ::instructionString[] =
{
"TC",
"CCS",
"INDEX",
"XCH",
"***",
"***",
"***",
"***",
"***",
"MP",
"DV",
"SU",
"CS",
"TS",
"AD",
"MASK"
};

char* SEQ::cpTypeString[] =
{
"NO_PULSE",

// OUTPUTS FROM SUBSYSTEM A


"CI", "CLG", "CLCTR", "CTR", "GP", "KRPT", "NISQ", "RA", "RB",
"RB14", "RC", "RG", "RLP", "RP2", "RQ", "RRPA", "RSB", "RSCT",
"RU", "RZ", "R1", "R1C", "R2", "R22", "R24", "ST1", "ST2", "TMZ",
"TOV", "TP", "TRSM", "TSGN", "TSGN2", "WA", "WALP", "WB", "WGx",
"WLP", "WOVC", "WOVI", "WOVR", "WP", "WPx", "WP2", "WQ", "WS",
"WX", "WY", "WYx", "WZ",

// OUTPUTS FROM SUBSYSTEM A; USED AS INPUTS TO SUBSYSTEM B ONLY;


// NOT USED OUTSIDE CPM
//
"RSC", "WSC", "WG",

// OUTPUTS FROM SUBSYSTEM A; USED AS INPUTS TO SUBSYSTEM C ONLY;


// NOT USED OUTSIDE CPM
//
"SDV1", "SMP1", "SRSM3",

// EXTERNAL OUTPUTS FROM SUBSYSTEM B


//
"RA0", "RA1", "RA2", "RA3", "RA4", "RA5", "RA6", "RA7", "RA10", "RA11",
"RA12", "RA13", "RA14", "RBK", "WA0", "WA1", "WA2", "WA3", "WA10",
"WA11", "WA12", "WA13", "WA14", "WBK", "WGn", "W20", "W21", "W22", "W23",

// THESE ARE THE LEFTOVERS -- THEY'RE PROBABLY USED IN SUBSYSTEM C


//
"GENRST", "CLINH", "CLINH1", "CLSTA", "CLSTB", "CLISQ", "CLRP", "INH",
"RPT", "SBWG", "SETSTB", "WE", "WPCTR", "WSQ", "WSTB", "R2000"
};

void SEQ::execWP_GENRST()
{
register_SQ.write(0);
register_BR1.write(0);
register_BR2.write(0);
register_SNI.write(0);
register_LOOPCTR.write(0);
register_STA.write(0);
register_STB.write(0);
}

void SEQ::execWP_WSQ()
{
register_SQ.write(BUS::glbl_WRITE_BUS >> 12);
}

void SEQ::execWP_NISQ()
{
register_SNI.writeField(1,1,1); // change to write(1)??
}

void SEQ::execWP_CLISQ()
{
register_SNI.writeField(1,1,0); // change to write(0)??
}

bool SEQ::isAsserted(cpType pulse)


{
for(unsigned i=0; i<MAXPULSES; i++)
if(glbl_cp[i] == pulse) return true;
return false;
}

char* SEQ::getControlPulses()
{
static char buf[MAXPULSES*6];
strcpy(buf,"");

for(unsigned i=0; i<MAXPULSES && glbl_cp[i] != NO_PULSE; i++)


{
strcat(buf, cpTypeString[glbl_cp[i]]);
strcat(buf," ");
}
//if(strcmp(buf,"") == 0) strcat(buf,"NONE");
return buf;
}

void SEQ::execWP_ST1()
{
register_STA.writeField(1,1,1);
}
void SEQ::execWP_ST2()
{
register_STA.writeField(2,2,1);
}

void SEQ::execWP_TRSM()
{
if(ADR::EQU_25())
register_STA.writeField(2,2,1);
}

void SEQ::execWP_CLSTA()
{
register_STA.writeField(2,1,0);
}

void SEQ::execWP_WSTB()
{
register_STB.write(SEQ::register_STA.read());
}

void SEQ::execWP_CLSTB()
{
register_STB.writeField(2,1,0);
}

void SEQ::execWP_SETSTB()
{
register_STB.writeField(2,1,1);
}

void SEQ::execWP_TSGN()
{
// Set Branch 1 FF
// if sign bit is '1' (negative sign)
if(BUS::glbl_WRITE_BUS & 0100000)
register_BR1.write(1);
else
register_BR1.write(0);

void SEQ::execWP_TOV()
{
// Set Branch 1 FF
// if negative overflow (sign==1; overflow==0)
if((BUS::glbl_WRITE_BUS & 0140000) == 0100000)
register_BR1.write(1);
else
register_BR1.write(0);

// Set Branch 2 FF
// if positive overflow (sign==0; oveflow==1)
if((BUS::glbl_WRITE_BUS & 0140000) == 0040000)
register_BR2.write(1);
else
register_BR2.write(0);
}

void SEQ::execWP_TSGN2()
{
// Set Branch 2 FF
// if sign bit is '1' (negative sign)
if(BUS::glbl_WRITE_BUS & 0100000)
register_BR2.write(1);
else
register_BR2.write(0);

void SEQ::execWP_TMZ()
{
// Set Branch 2 FF
// if minus zero
if(BUS::glbl_WRITE_BUS == 0177777)
register_BR2.write(1);
else
register_BR2.write(0);
}

void SEQ::execWP_CTR()
{
register_LOOPCTR.write(register_LOOPCTR.read()+1);
}

void SEQ::execWP_CLCTR()
{
register_LOOPCTR.write(0);
}
TPG (TPG.h)
/****************************************************************************
* TPG - TIME PULSE GENERATOR subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: TPG.h
*
* VERSIONS:
*
* DESCRIPTION:
* Time Pulse Generator and Start/Stop Logic for the Block 1 Apollo Guidance
* Computer prototype (AGC4).
*
* SOURCES:
* Mostly based on information from "Logical Description for the Apollo
* Guidance Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh
* Blair-Smith, R-393, MIT Instrumentation Laboratory, 1963.
*
* NOTES:
*
*****************************************************************************
*/
#ifndef TPG_H
#define TPG_H

#include "reg.h"

// Start/Stop Logic and Time Pulse Generator Subsystem

enum tpType {
STBY =0,
PWRON =1,

TP1 =2, // TIME PULSE 1: start of memory cycle time (MCT)


TP2 =3,
TP3 =4,
TP4 =5,
TP5 =6,
TP6 =7, // EMEM is available in G register by TP6
TP7 =8, // FMEM is available in G register by TP7
TP8 =9,
TP9 =10,
TP10 =11, // G register written to memory beginning at TP10
TP11 =12, // TIME PULSE 11: end of memory cycle time (MCT)
TP12 =13, // select new subsequence/select new instruction

SRLSE =14, // step switch release


WAIT =15
};

class regSG : public reg


{
public: regSG() : reg(4, "%02o") { }

};

class TPG
{
public:
static void doexecWP_TPG();

static regSG register_SG;

static char* tpTypestring[];


};

#endif
TPG (TPG.cpp)
/****************************************************************************
* TPG - TIME PULSE GENERATOR subsystem
*
* AUTHOR: John Pultorak
* DATE: 9/22/01
* FILE: TPG.cpp
*
* NOTES: see header file.
*
*****************************************************************************
*/
#include "TPG.h"
#include "MON.h"
#include "SCL.h"
#include "SEQ.h"
#include "OUT.h"

char* TPG::tpTypestring[] = // must correspond to tpType enumerated type


{
"STBY", "PWRON", "TP1", "TP2", "TP3", "TP4", "TP5", "TP6", "TP7", "TP8",
"TP9", "TP10", "TP11", "TP12", "SRLSE", "WAIT"
};

regSG TPG::register_SG; // static member

void TPG::doexecWP_TPG()
{
unsigned mystate = register_SG.read();
if(MON::PURST)
mystate = STBY;
else
switch(mystate)
{
case STBY: if(!MON::PURST && ((!MON::FCLK) || SCL::F17x())) mystate = PWRON; break;
case PWRON: if(((!MON::FCLK) || SCL::F13x())) mystate = TP1; break;

case TP1: mystate = TP2; break;


case TP2: mystate = TP3; break;
case TP3: mystate = TP4; break;
case TP4: mystate = TP5; break;
case TP5: mystate = TP6; break;
case TP6: mystate = TP7; break;
case TP7: mystate = TP8; break;
case TP8: mystate = TP9; break;
case TP9: mystate = TP10; break;
case TP10: mystate = TP11; break;
case TP11: mystate = TP12; break;
case TP12:
if(SEQ::register_SNI.read() && OUT::register_OUT1.readField(8,8) && MON::SA)
mystate = STBY;
// the next transition to TP1 is incompletely decoded; it works because
// the transition to STBY has already been tested.
else if((MON::RUN) || (!SEQ::register_SNI.read() && MON::INST))
mystate = TP1;
else
mystate = SRLSE;
break;
case SRLSE: if(!MON::STEP) mystate = WAIT; break;
case WAIT:
if(MON::STEP || MON::RUN)
mystate = TP1;
break;
default: break;
}
register_SG.write(mystate);
}
Block I
Apollo Guidance Computer (AGC)
How to build one in your basement

Part 8: Flight Software

John Pultorak
December, 2004
Abstract
This report describes my successful project to build a working reproduction of the 1964
prototype for the Block I Apollo Guidance Computer. The AGC is the flight computer for the
Ap ollo m oon lan din gs, and is the wo rld’s first in tegra ted c ircuit com pu ter.

I built it in my ba sem ent. It took me 4 yea rs.

If you like, you can build one too. It will take you less time, and yours will be better than
mine.

I docum ented m y project in 9 separate .pdf files:

Part 1 O v ervie w : Introdu ces the p roject.

Part 2 CTL Module: Design and construction of the control module.

Part 3 PROC M odule: Design and construction of the processing (CPU) modu le.

Part 4 MEM Module: Design and construction of the mem ory module.

Part 5 IO Module: Design and construction of the display/keyboard (DSKY) m odule.

Part 6 Assem bler: A cross-a ssem bler for AG C softw are dev elopm ent.

Part 7 C+ + S imu lator: A low-level simulator that runs assemb led AGC code.

Part 8 Flight Software: My translation of portions of the COLOSSUS 249 flight


software.

Part 9 Test & Che ckou t: A suite of test programs in AG C assembly langu age.
Overview
I wan ted ge nuin e Block I flight softwa re for my AG C, bu t could n’t find an y. I eventu ally
woun d up recreating Block I software from Block II code listings that were available. Major
portions of Block II were originally coded as Block I anyway, so the conversion back was not
too difficult. A bou t 95% of the instru ctions w ere alread y Block I, so I just had to transla te
the rem aining 5 % into their Block I equivalen ts.

I dow nloa ded a partia l listing of th e CO LOS SU S 24 9 fligh t softwa re for the Blo ck II Apollo
Com man d Modu le AGC from a M.I.T website in late 2001. The listing (at that time)
com prised first half
of the flight
software load. The
second half was
missing.

The part that was


present contained
erase able m em ory
declarations, and
PINBALL, the AGC
user interface. The
missing portion
contained (among
other things) the
EXEC and
WA ITLIST pieces of
the operating
system, bank
register calling
routin es, and ma th
libraries.

The down loaded


doc um ent wa s a .pd f file con tain ing 300 or so fuzzy dig ital im ages of assem bler listing. I
printed it, and then retyped the erasable memory and PINBALL portions into a text file,
m arkin g off ea ch reentered line in th e orig ina l listin g w ith a hig hlig hter.

I coded my own versions of the EXEC, WAITLIST, BANKC ALL, and other missing routines
used by PINBALL. The R-393 d ocumen t from M .I.T. provided some guidance.

Over a 6 month period, I was able to get all regular verbs and generic normal nouns
working. These are listed near the top of my assembler listing.

I add ed a dd ition al com m ents to portion s of cod e tak en from CO LOSS US . The pag e nu m bers
in my comments refer to pages in the COLOSSUS 249 assembler listing.
Original COLOSSUS 249 Assembler Code
For comparison purposes, here’s a page of the original AGC assem bly code, downloaded
from th e M.I.T. w ebsite. Th is is a tiny p ortion of th e PINB ALL co de. I m arked each line w ith
a yellow highlighter as I reentered it into a text file.
My COLOSSUS Assembler Code
Here’s the exact same portion of code, assembled for my Block I AGC. If you compare the
two listings, you’ll see that I defined some different assembler directives for allocating
storage (DS % instea d of OC T) an d tha t my code is sittin g in a different ba nk (5 vs. 40) a t a
different offset than the original Block II code.
Scenarios
Here’s how some AGC verbs and nouns are used to do commonplace operations. The
overview (part 1) and simulator (part 7) docum ents contain actual examples of some of
these operations in the simu lated and hardw are AGC s.

Display elapsed time from the AGC clock:


<VERB> <0> <6> <NOUN> <3> <6> <ENTER>

Start a monitor program to continuously display the AGC clock:


<VERB> <1> <6> <NOUN> <3> <6> <ENTER>

Terminate a monitor program:


<VERB> <3> <4> <ENTER>

Test DSJT display lights:


<VERB> <3> <5> <ENTER>
All DSKY lamps and display segments illuminate for 5 sec. after 5 sec, the DSKY
lamps extinguish.

Load component 1 for dataset at octal address 50 with octal 123:


<VERB> <2> <1> <NOUN> <0> <1> <ENTER>
Verb/no un disp lay flashes: wa iting for address.
<5> <0> <ENTER>
Verb/noun display flash continues: waiting for data.
<1> <2> <3> <ENTER>
Octal word from R1 is loaded at address 50.

Display component 1 of dataset at octal address 50:


<VERB> <0> <1> <NOUN> <0> <1> <ENTER>
Verb/no un disp lay flashes: wa iting for address.
<5> <0> <ENTER>
Octal word from address 50 is displayed in R1.

Display component 1 of dataset incrementing from 50:


<VERB> <0> <1> <NOUN> <0> <1> <ENTER>
Verb/no un disp lay flashes: wa iting for address.
<5> <0> <ENTER>
Octal word from address 50 is displayed in R1.
<NOUN> <1> <5> <ENTER>
Octal word from address 51 is displayed in R1, address in R3.
<ENTER>
Octal word from address 52 is displayed in R1, address in R3.

Load 3 component dataset at octal address 50 with octal values: 123, 456, 701:
<VERB> <2> <5> <NOUN> <0> <1> <ENTER>
Verb/no un disp lay flashes: wa iting for address.
<5> <0> <ENTER>
Verb/noun display flash continues: waiting for data.
<1> <2> <3> <ENTER>
<4> <5> <6> <ENTER>
<7> <0> <1> <ENTER>
Octal wo rd from R1 is loaded at add ress 50; Octal w ord from R2 is loaded at add ress
51, Octal word from R3 is loaded at address 52.

Display 3 component dataset beginning at address 50:


<VERB> <0> <5> <NOUN> <0> <1> <ENTER>
Verb/no un disp lay flashes: wa iting for address.
<5> <0> <ENTER>
Octa l word from a ddres s 50 is d isplay ed in R 1; O ctal w ord from add ress 51 is
displayed in R2; Octal word from address 52 is displayed in R3.

Change major mode to P00:


<VERB> <3> <7> <ENTER>
Verb/noun display flashes: waiting for major mode
<0> <0> <ENTER>
VERBS and NOUNS
COLOSSUS REGULAR VERBS (00-39 decimal)

This is a dap ted from the A pollo 2 04 a cciden t report po sted on mu ltiple
web sites by Richard F. Drushel. The information has been changed as
necessary to be consistent with usage in COLOSSUS 249.
Verb | |
Code | Description | Remarks
| |
01 | Display octal comp 1 in R1 | Performs octal display of data on
| | REGISTER 1.
| |
02 | Display octal comp 2 in R2 | Performs octal display of data on
| | REGISTER 1.
| |
03 | Display octal comp 3 in R3 | Performs octal display of data on
| | REGISTER 1.
| |
04 | Display octal comp 1,2 | Performs octal display of data on
| in R1,R2 | REGISTER 1 and REGISTER 2
| |
05 | Display octal comp 1,2,3 | Performs octal display of data on
| in R1,R2,R3 | REGISTER 1, REGISTER 2, and REGISTER 3.
| |
06 | Display decimal in R1 or | Performs decimal display of data on
| R1,R2 or R1,R2,R3 | appropriate registers. The scale
| | factors, types of scale factor
| | routines, and component information
| | are stored within the machine for each
| | noun which it is required to display
| | in decimal.
| |
07 | Display DP decimal in R1,R2 | Performs a double precision decimal
| | display of data on REGISTER 1 and
| | REGISTER 2. It does no scale
| | factoring. It merely performs a 10-
| | character, fractional decimal
| | conversion of two consecutive, erasable
| | registers, using REGISTER 1 and
| | REGISTER 2. The sign is placed in the
| | REGISTER 1 sign position with the
| | REGISTER 2 sign position remaining
| | blank. It cannot be used with mixed
| | nouns. Its intended use is primarily
| | with "machine address to be specified"
| | nouns.
| |
08 | (Spare) |
| |
09 | (Spare) |
| |
10 | (Spare) |
| |
11 | Monitor octal comp 1 in R1 | Performs octal display of updated data
| | every 1/2 second on REGISTER 1.
| |
12 | Monitor octal comp 2 in R2 | Performs octal display of updated data
| | every 1/2 second on REGISTER 1.
| |
13 | Monitor octal comp 3 in R3 | Performs octal display of updated data
| | every 1/2 second on REGISTER 1.
| |
14 | Monitor octal comp 1,2 | Performs octal display of updated data
| in R1,R2 | every 1/2 second on REGISTER 1 and
| | REGISTER 2.
| |
15 | Monitor octal comp 1,2,3 | Performs octal display of updated data
| in R1,R2,R3 | every 1/2 second on REGISTER 1,
| | REGISTER 2, and REGISTER 3.
| |
16 | Monitor decimal in R1 or | Performs decimal display of updated
| R1,R2, or R1,R2,R3 | data every 1/2 second on appropriate
| | registers.
| |
17 | Monitor DP decimal in R1,R2 | Performs double precision display of
| | decimal data on REGISTER 1 and
| | REGISTER 2. No scale factoring is
| | performed. Provides 10-character,
| | fractional decimal conversion of two
| | consecutive erasable registers. The
| | sign is placed in the sign-bit
| | position of REGISTER 1. REGISTER 2
| | sign bit is blank.
| |
18 | (Spare) |
| |
19 | (Spare) |
| |
20 | (Spare) |
| |
21 | Load component 1 into R1 | Performs data loading. Octal
| | quantities are unsigned. Decimal
| | quantities are preceded by + or -
| | sign. Data is displayed on REGISTER
| | 1.
| |
22 | Load component 2 into R2 | Performs data loading. Octal
| | quantities are unsigned. Decimal
| | quantities are preceded by + or -
| | sign. Data is displayed on REGISTER
| | 2.
| |
23 | Load component 3 into R3 | Performs data loading. Octal
| | quantities are unsigned. Decimal
| | quantities are preceded by + or -
| | sign. Data is displayed on REGISTER
| | 3.
| |
24 | Load component 1,2 into | Performs data loading. Octal
| R1,R2 | quantities are unsigned. Decimal
| | quantities are preceded by + or -
| | sign. Data is displayed on REGISTER
| | 1 and REGISTER 2.
| |
25 | Load component 1,2,3 into | Performs data loading. Octal
| R1,R2,R3 | quantities are unsigned. Decimal
| | quantities are preceded by + or -
| | sign. Data is displayed on REGISTER
| | 1, REGISTER 2, and REGISTER 3.
| |
26 | (Spare) |
| |
27 | Display fixed memory | This verb is included to permit
| | displaying the contents of fixed
| | memory in any bank. Its intended use
| | is for checking program ropes and the
| | BANK positions of program ropes.
| |
28 | (Spare) |
| |
29 | (Spare) |
| |
30 | Request EXECUTIVE | Enters request to executive routine
| (Used only during ground | for any machine address with priority
| checkout.) | involved. This verb assumes that the
| | desired priority has been loaded into
| | bits 10-14 of the prio/delay register
| | (noun 26). This verb is used with the
| | noun, "machine address to be
| | specified". The complete address of
| | the desired location is then keyed in.
| | (Refer to "Machine address to be
| | specified" in paragraph on Verb/Noun
| | Formats.)
| |
31 | Request WAITLIST | Enters request to "waitlist routine"
| (Used only during ground | for any machine address with delay
| checkout.) | involved. This verb assumes that the
| | desired number of 10-millisecond units
| | of delay has been loaded into the low
| | order bits of the prio/delay register
| | (noun 26). This verb is used with the
| | "machine address to be specified" noun.
| | The complete address of the desired
| | location is then keyed in. (Refer to
| | "Machine address to be specified" in
| | paragraph on Verb/Noun Formats.)
| |
32 | Recycle |
| |
33 | Proceed (without data) | Informs routine requesting data that
| | the operator chooses not to load
| | fresh data, but wishes the routine to
| | continue as best it can with old data.
| | Final decision for what action should
| | be taken is left to the requesting
| | routine.
| |
34 | Terminate | Informs routine requesting data to be
| | loaded that the operator chooses not
| | to load fresh data and wishes the
| | routine to terminate. Final decision
| | for what action should be taken is
| | left to the requesting routine. If
| | monitor is on, it is turned off.
| |
35 | Test lights |
| |
36 | Request fresh start | Initializes the program control
| | software and the keyboard and display
| | system program.
| |
37 | Change program (major mode) | Change to new major mode. (Refer to
| | "Change major mode" in paragraph on
| | Verb/Noun Formats.)
| |
;--------------------------------------------------------------------------

COLOSSUS EXTENDED VERBS (40-99 decimal)

Not im plem ented. U se of th ese v erbs trigg ers th e 'check fail ' indic ator.

COLOSSUS NORMAL NOUNS (00-39 decimal)

This is a dap ted from the A pollo 2 04 a cciden t report po sted on mu ltiple
web sites by Richard F. Drushel. The information has been changed as
necessary to be consistent with usage in COLOSSUS 249.

Noun | |
Code | Description | Scale/Units
| |
01 | Specify machine address (frac) | .XXXXX FRAC
| | .XXXXX FRAC
| | .XXXXX FRAC
| |
02 | Specify machine address (whole) | XXXXX INTEGER
| | XXXXX INTEGER
| | XXXXX INTEGER
| |
03 | Specify machine address (degree) | XXX.XX DEG
| | XXX.XX DEG
| | XXX.XX DEG
| |
04 | (Spare) |
| |
05 | (Spare) |
| |
06 | (Spare) |
| |
07 | (Spare) |
| |
08 | (Spare) |
| |
09 | Alarm codes | OCT
| | OCT
| | OCT
| |
10 | (Spare) |
| |
11 | (Spare) |
| |
12 | (Spare) |
| |
13 | (Spare) |
| |
14 | (Spare) |
| |
15 | Increment address | OCT
| |
| |
16 | (Spare) |
| |
17 | (Spare) |
| |
18 | (Spare) |
| |
19 | (Spare) |
| |
20 | (Spare) |
| |
21 | (Spare) |
| |
22 | (Spare) |
| |
23 | (Spare) |
| |
24 | (Spare) |
| |
25 | (Spare) |
| |
26 | Prio/delay, address | OCT (prio/delay)
| | OCT (14-bit CADR)
| | (not used)
| |
27 | (Spare) |
| |
28 | (Spare) |
| |
29 | (Spare) |
| |
30 | (Spare) |
| |
31 | (Spare) |
| |
32 | (Spare) |
| |
33 | (Spare) |
| |
34 | (Spare) |
| |
35 | (Spare) |
| |
36 | Time of CMC clock: |
| REGISTER 1 | 00XXX. hours
| REGISTER 2 | 000XX. minutes
| REGISTER 3 | 0XX.XX seconds
| |
37 | (Spare) |
| |
38 | (Spare) |
| |
39 | (Spare) |
| |
;--------------------------------------------------------------------------

COLOSSUS MIXED NOUNS (40-99 decimal)

Not implemented.
Flight software assembler listing
B l o c k I A p o l l o Guidance Computer (AGC4) asse mbler version 1.6 for EPROM

F i r s t p a s s : g enerate symbol table.


S e c o n d p a s s : generate object code.

;================= =========================================================
; AGC (file:agc.as m)
;
; Version: 1.0
; Author: John P ultorak
; Date: 6/7/20 02
;
; PURPOSE:
; AGC Block I demo nstration. Includes most of the AGC operating system:
; WAITLIST, EXEC, PINBALL (DSKY routines), NOUN tables, VERB tables,
; bank intercommun ication routines, the KEY, T3, and T4 interrupt handlers,
; and some dual pr ecision (DP) math routines.
;
; The interpreter is not currently implemented.
;
; Where available, the source is from the Apollo 8 command module computer (CMC )
; load (called COL OSSUS). In cases where COLOSSUS source is not available,
; functionally equ ivalent code was constructed using COLOSSUS calling and retur n
; parameters and a ccording to specifications in the technical reports given bel o w .
;
; OPERATION:
; TBD.
;
; ERRATA:
; - Adapted for th e AGC4R assembler. The assembler directives and syntax
; differ somewhat from the original AGC assembler.
; - some of the or iginal source was missing from the COLOSSUS listing and
; had to be revers e engineered. Those portions probably differ somewhat
; from the origina l code in implementation, but should be functionally
; identical.
; - because the CO LOSSUS source is for a block II AGC, but the AGC
; implemented here is block I, about 5% of COLOSSUS had to be translated
; to equivalent bl ock I code.
;
; SOURCES:
; Information on t he Block I architecture: instruction set, instruction
; sequences, regis ters, register transfers, control pulses, memory and
; memory addressin g, I/O assignments, interrupts, and involuntary counters
; was obtained fro m:
;
; A. Hopkins, R. Alonso, and H. Blair-Smith, "Logical Description
; for the Apollo Guidance Computer (AGC4)", R-393,
; MIT Instrumentation Laboratory, Cambridge, MA, Mar. 1963.
;
; Supplementary AG C hardware information was obtained from:
;
; R. Alonso, J. H. Laning, Jr. and H. Blair-Smith, "Preliminary
; MOD 3C Programmer's Manual", E-1077, MIT Instrumentation
; Laboratory, Cambridge, MA, Nov. 1961.
;
; B. I. Savage and A. Drake, "AGC4 Basic Training Manual, Volume I",
; E-2052, MIT Instrumentation Laboratory, Cambridge,
; MA, Jan. 1967.
;
; E. C. Hall, "MIT's Role in Project Apollo, Volume III, Computer
; Subsystem", R-700, MIT Charles Stark Draper Laboratory,
; Cambridge, MA, Aug. 1972.
;
; A. Hopkins, "Guidance Computer Design, Part VI", source unknown.
;
; E, C. Hall, "Journey to the Moon: The History of the Apollo
; Guidance Computer", AIAA, Reston VA, 1996.
;
; AGC software inf ormation was obtained from:
;
; AGC Block II COLOSSUS rev 249 assembly listing, Oct 28, 1968. (A
; listing of the 1st 50% of the build. It encludes the entire
; eraseable memory, restart initialization, T4RUPT, and the
; entire set of DSKY routines. About 5% of instructions
; had to be converted from Block II to Block I).
;
; A. I. Green and J. J. Rocchio, "Keyboard and Display System Program
; for AGC (Program Sunrise)", E-1574, MIT Instrumentation
; Laboratory, Cambridge, MA, Aug. 1964. Contains detailed
; flowcharts and design materials for the DSKY software.
;
; A. Hopkins, R. Alonso, and H. Blair-Smith, "Logical Description
; for the Apollo Guidance Computer (AGC4)", R-393,
; MIT Instrumentation Laboratory, Cambridge, MA, Mar. 1963.
; Contains the software interfaces for EXEC and WAITLIST, and
; portions of the dual precision (DP) math library.
;
;================= =========================================================

INC L doc.asm
;================= =========================================================
; AGC documentatio n (file:doc.asm)
;
; Version: 1.0
; Author: John P ultorak
; Date: 06/01/ 2002
;
; PURPOSE:
; Documents AGC op s source code.
;================= =========================================================

;----------------- ---------------------------------------------------------
; DSKY OPERATION ( examples)
;
; verb/noun (V/N) flash: When the verb and noun indicators flash
; at 1Hz, the DSKY is waiting for keyboard input.
;
;
; Display elapsed time from the AGC clock:
; <VERB> <0> <6> <NOUN> <3> <6> <ENTER>
;
; Test display lig hts
; a) <VERB> <3> <5> <ENTER>
; b) all DSKY lamps and display segments illuminate for 5 sec.
; c) after 5 sec, the DSKY lamps extinguish
;
; Load component 1 for dataset at octal address 50 with octal 123
; a) <VERB> <2> <1> <NOUN> <0> <1> <ENTER>
; b) verb/noun display flashes; waiting for address
; c) <5> <0> <ENTER>
; d) verb/noun display flash continues; waiting for data
; e) <1> <2> <3> <ENTER>
; f) octal word from R1 is loaded at address 50,
;
; Display componen t 1 of dataset at octal address 50:
; a) <VERB> <0> <1> <NOUN> <0> <1> <ENTER>
; b) verb/noun display flashes; waiting for address
; c) <5> <0> <ENTER>
; d) octal word from address 50 is displayed in R1
;
; Load 3 component dataset at octal address 50 with octal values
; 123,456,701
; a) <VERB> <2> <5> <NOUN> <0> <1> <ENTER>
; b) verb/noun display flashes; waiting for address
; c) <5> <0> <ENTER>
; d) verb/noun display flash continues; waiting for data
; e) <1> <2> <3> <ENTER>
; f) <4> <5> <6> <ENTER>
; g) <7> <0> <1> <ENTER>
; h) octal word from R1 is loaded at address 50,
; octal word from R2 is loaded at address 51,
; octal word from R3 is loaded at address 52
;
; Display 3 compon ent dataset beginning at address 50:
; a) <VERB> <0> <5> <NOUN> <0> <1> <ENTER>
; b) verb/noun display flashes; waiting for address
; c) <5> <0> <ENTER>
; d) octal word from address 50 is displayed in R1,
; octal word from address 51 is displayed in R2,
; octal word from address 52 is displayed in R3
;
;----------------- ---------------------------------------------------------

;----------------- ---------------------------------------------------------
; COLOSSUS REGULAR VERBS (00-39 decimal)
;
; This is adapted from the Apollo 204 accident report posted on multiple
; web sites by Ric hard F. Drushel. The information has been changed as
; necessary to be consistent with usage in COLOSSUS.
;
;
; Verb | |
; Code | Description | Remarks
; | |
; 01 | Display octal comp 1 in R1 | Performs octal display of data on
; | | REGISTER 1.
; | |
; 02 | Display octal comp 2 in R2 | Performs octal display of data on
; | | REGISTER 1.
; | |
; 03 | Display octal comp 3 in R3 | Performs octal display of data on
; | | REGISTER 1.
; | |
; 04 | Display octal comp 1,2 | Performs octal display of data on
; | in R1,R2 | REGISTER 1 and REGISTER 2
; | |
; 05 | Display octal comp 1,2,3 | Performs octal display of data on
; | in R1,R2 ,R3 | REGISTER 1, REGISTER 2, and REGISTER 3 .
; | |
; 06 | Display decimal in R1 or | Performs decimal display of data on
; | R1,R2 or R1,R2,R3 | appropriate registers. The scale
; | | factors, types of scale factor
; | | routines, and component information
; | | are stored within the machine for eac h
; | | noun which it is required to display
; | | in decimal.
; | |
; 07 | Display DP decimal in R1,R2 | Performs a double precision decimal
; | | display of data on REGISTER 1 and
; | | REGISTER 2. It does no scale
; | | factoring. It merely performs a 10-
; | | character, fractional decimal
; | | conversion of two consecutive, erasab l e
; | | registers, using REGISTER 1 and
; | | REGISTER 2. The sign is placed in th e
; | | REGISTER 1 sign position with the
; | | REGISTER 2 sign position remaining
; | | blank. It cannot be used with mixed
; | | nouns. Its intended use is primarily
; | | with "machine address to be specified "
; | | nouns.
; | |
; 08 | (Spare) |
; | |
; 09 | (Spare) |
; | |
; 10 | (Spare) |
; | |
; 11 | Monitor octal comp 1 in R1 | Performs octal display of updated dat a
; | | every 1/2 second on REGISTER 1.
; | |
; 12 | Monitor octal comp 2 in R2 | Performs octal display of updated dat a
; | | every 1/2 second on REGISTER 1.
; | |
; 13 | Monitor octal comp 3 in R3 | Performs octal display of updated dat a
; | | every 1/2 second on REGISTER 1.
; | |
; 14 | Monitor octal comp 1,2 | Performs octal display of updated dat a
; | in R1,R2 | every 1/2 second on REGISTER 1 and
; | | REGISTER 2.
; | |
; 15 | Monitor octal comp 1,2,3 | Performs octal display of updated dat a
; | in R1,R2 ,R3 | every 1/2 second on REGISTER 1,
; | | REGISTER 2, and REGISTER 3.
; | |
; 16 | Monitor decimal in R1 or | Performs decimal display of updated
; | R1,R2, o r R1,R2,R3 | data every 1/2 second on appropriate
; | | registers.
; | |
; 17 | Monitor DP decimal in R1,R2 | Performs double precision display of
; | | decimal data on REGISTER 1 and
; | | REGISTER 2. No scale factoring is
; | | performed. Provides 10-character,
; | | fractional decimal conversion of two
; | | consecutive erasable registers. The
; | | sign is placed in the sign-bit
; | | position of REGISTER 1. REGISTER 2
; | | sign bit is blank.
; | |
; 18 | (Spare) |
; | |
; 19 | (Spare) |
; | |
; 20 | (Spare) |
; | |
; 21 | Load com ponent 1 into R1 | Performs data loading. Octal
; | | quantities are unsigned. Decimal
; | | quantities are preceded by + or -
; | | sign. Data is displayed on REGISTER
; | | 1.
; | |
; 22 | Load com ponent 2 into R2 | Performs data loading. Octal
; | | quantities are unsigned. Decimal
; | | quantities are preceded by + or -
; | | sign. Data is displayed on REGISTER
; | | 2.
; | |
; 23 | Load com ponent 3 into R3 | Performs data loading. Octal
; | | quantities are unsigned. Decimal
; | | quantities are preceded by + or -
; | | sign. Data is displayed on REGISTER
; | | 3.
; | |
; 24 | Load com ponent 1,2 into | Performs data loading. Octal
; | R1,R2 | quantities are unsigned. Decimal
; | | quantities are preceded by + or -
; | | sign. Data is displayed on REGISTER
; | | 1 and REGISTER 2.
; | |
; 25 | Load com ponent 1,2,3 into | Performs data loading. Octal
; | R1,R2,R3 | quantities are unsigned. Decimal
; | | quantities are preceded by + or -
; | | sign. Data is displayed on REGISTER
; | | 1, REGISTER 2, and REGISTER 3.
; | |
; 26 | (Spare) |
; | |
; 27 | Display fixed memory | This verb is included to permit
; | | displaying the contents of fixed
; | | memory in any bank. Its intended use
; | | is for checking program ropes and the
; | | BANK positions of program ropes.
; | |
; 28 | (Spare) |
; | |
; 29 | (Spare) |
; | |
; 30 | Request EXECUTIVE | Enters request to executive routine
; | (Used on ly during ground | for any machine address with priority
; | checkout .) | involved. This verb assumes that the
; | | desired priority has been loaded into
; | | bits 10-14 of the prio/delay register
; | | (noun 26). This verb is used with th e
; | | noun, "machine address to be
; | | specified". The complete address of
; | | the desired location is then keyed in .
; | | (Refer to "Machine address to be
; | | specified" in paragraph on Verb/Noun
; | | Formats.)
; | |
; 31 | Request WAITLIST | Enters request to "waitlist routine"
; | (Used on ly during ground | for any machine address with delay
; | checkout .) | involved. This verb assumes that the
; | | desired number of 10-millisecond unit s
; | | of delay has been loaded into the low
; | | order bits of the prio/delay register
; | | (noun 26). This verb is used with th e
; | | "machine address to be specified" nou n .
; | | The complete address of the desired
; | | location is then keyed in. (Refer to
; | | "Machine address to be specified" in
; | | paragraph on Verb/Noun Formats.)
; | |
; 32 | Recycle |
; | |
; 33 | Proceed (without data) | Informs routine requesting data that
; | | the operator chooses not to load
; | | fresh data, but wishes the routine to
; | | continue as best it can with old data .
; | | Final decision for what action should
; | | be taken is left to the requesting
; | | routine.
; | |
; 34 | Terminat e | Informs routine requesting data to be
; | | loaded that the operator chooses not
; | | to load fresh data and wishes the
; | | routine to terminate. Final decision
; | | for what action should be taken is
; | | left to the requesting routine. If
; | | monitor is on, it is turned off.
; | |
; 35 | Test lig hts |
; | |
; 36 | Request fresh start | Initializes the program control
; | | software and the keyboard and display
; | | system program.
; | |
; 37 | Change p rogram (major mode) | Change to new major mode. (Refer to
; | | "Change major mode" in paragraph on
; | | Verb/Noun Formats.)
; | |
;----------------- ---------------------------------------------------------
;----------------- ---------------------------------------------------------
; COLOSSUS EXTENDE D VERBS (40-99 decimal)
;
; Not implemented. Use of these verbs triggers the 'check fail' indicator.
;----------------- ---------------------------------------------------------

;----------------- ---------------------------------------------------------
; COLOSSUS NORMAL NOUNS (00-39 decimal)
;
; This is adapted from the Apollo 204 accident report posted on multiple
; web sites by Ric hard F. Drushel. The information has been changed as
; necessary to be consistent with usage in COLOSSUS.
;
;
; Noun | |
; Code | Description | Scale/Units
; | |
; 01 | Specify machine address (frac) | .XXXXX FRAC
; | | .XXXXX FRAC
; | | .XXXXX FRAC
; | |
; 02 | Specify machine address (whole) | XXXXX INTEGER
; | | XXXXX INTEGER
; | | XXXXX INTEGER
; | |
; 03 | Specify machine address (degree) | XXX.XX DEG
; | | XXX.XX DEG
; | | XXX.XX DEG
; | |
; 04 | (Spare) |
; | |
; 05 | (Spare) |
; | |
; 06 | (Spare) |
; | |
; 07 | (Spare) |
; | |
; 08 | (Spare) |
; | |
; 09 | Alarm co des | OCT
; | | OCT
; | | OCT
; | |
; 10 | (Spare) |
; | |
; 11 | (Spare) |
; | |
; 12 | (Spare) |
; | |
; 13 | (Spare) |
; | |
; 14 | (Spare) |
; | |
; 15 | Incremen t address | OCT
; | |
; | |
; 16 | (Spare) |
; | |
; 17 | (Spare) |
; | |
; 18 | (Spare) |
; | |
; 19 | (Spare) |
; | |
; 20 | (Spare) |
; | |
; 21 | (Spare) |
; | |
; 22 | (Spare) |
; | |
; 23 | (Spare) |
; | |
; 24 | (Spare) |
; | |
; 25 | (Spare) |
; | |
; 26 | Prio/del ay, address | OCT (prio/delay)
; | | OCT (14-bit CADR)
; | | (not used)
; | |
; 27 | (Spare) |
; | |
; 28 | (Spare) |
; | |
; 29 | (Spare) |
; | |
; 30 | (Spare) |
; | |
; 31 | (Spare) |
; | |
; 32 | (Spare) |
; | |
; 33 | (Spare) |
; | |
; 34 | (Spare) |
; | |
; 35 | (Spare) |
; | |
; 36 | Time of CMC clock: |
; | REGIST ER 1 | 00XXX. hours
; | REGIST ER 2 | 000XX. minutes
; | REGIST ER 3 | 0XX.XX seconds
; | |
; 37 | (Spare) |
; | |
; 38 | (Spare) |
; | |
; 39 | (Spare) |
; | |
;----------------- ---------------------------------------------------------

;----------------- ---------------------------------------------------------
; COLOSSUS MIXED N OUNS (40-99 decimal)
;
; Not implemented.
;----------------- ---------------------------------------------------------

;----------------- ---------------------------------------------------------
; AGC ADDRESS ASSI GNMENTS
;
; Central Register s
;
; 000000 A accumulator
; 000001 Q subroutine return address
; 000002 Z program counter
; 000003 LP lower product register
;
; Input Registers
;
; 000004 IN0
; 000005 IN1
; 000006 IN2
; 000007 IN3
;
; Output Registers
;
; 000010 OUT0
; 000011 OUT1
; 000012 OUT2
; 000013 OUT3
; 000014 OUT4
;
; Memory Bank Sele ct
;
; 000015 BANK
;
; Interrupt Contro l
;
; 000016 RELINT re-enable interrupts
; 000017 INHINT inhibit interrupts
;
; Editing Register s
;
; 000020 CYR cycle right
; 000021 SR shift rRight
; 000022 CYL cycle left
; 000023 SL shift left
;
; Interrupt Storag e Area
;
; 000024 ZRUPT save program counter (Z)
; 000025 BRUPT save B register
; 000026 ARUPT save accumulator (A)
; 000027 QRUPT save Q register
;
; 000030 - 000033 NOT USED
;
; Involuntary Coun ters
;
; 000034 OVCTR arithmetic overflow counter
; 000035 TIME2 AGC clock (high)
; 000036 TIME1 AGC clock (low)
; 000037 TIME3 WAITLIST (T3) timer
; 000040 TIME4 DISPLAY (T4) timer
;
; Involuntary Coun ters -- currently unused
;
; 000041 - 000056 NOT USED
;
; Eraseable Memory
;
; 000057 - 001777
;
; Start of fixed m emory
;
; 002000 GOPROG AGC (re)start vector
;
; 002004 T3RUPT interrupt vector for TIME3 (T3RUPT)
; 020010 ERRUPT interrupt vector
; 020014 DSRUPT interrupt vector for DSRUPT (T4RUPT)
; 020020 KEYRUPT interrupt vector for keyboard
; 020024 UPRUPT interrupt vector for uplink
;----------------- ---------------------------------------------------------

;----------------- ---------------------------------------------------------
; AGC TABLES (name , file, description)
;
; Keyboard/display
; CHARIN2 bank40_1.asm keyboard character table
; INRELTAB bank40_1.asm DSKY register/display table map
; DSPTAB dsky_e.asm display table for DSKY
;
; Verbs:
; VERBTAB bank41_1.asm regular verb routines (00-39)
;
; Nouns:
; NNADTAB bank42_3.asm noun address table (00-99)
; NNTYPTAB bank42_3.asm noun type table (00-99)
; SFINTAB bank42_3.asm noun input scale factor select
; SFOUTAB bank42_3.asm nout output scale factor select
; IDADDTAB bank42_3.asm mixed noun address table (40-99)
; RUTMXTAB bank42_3.asm mixed noun scale factor routine (40-9 9 )
;
; Noun scale facto r routines:
; SFOUTABR bank41_1.asm scale factor output routines
; SFINTABR bank41_2.asm scale factor input routines
;
; Major Modes:
; FCADRMM bank04_1.asm entry points for MM jobs
; EPREMM1 bank04_1,asm priorities for MM jobs
;----------------- ---------------------------------------------------------

; ERASEABLE MEMORY DECLARATIONS

ORG BANK0 ; immediately following counters


INC L waitlist_e.asm ; WAITLIST variables
;================= =========================================================
; WAITLIST (file:w aitlist_e.asm)
;
; Version: 1.0
; Author: John P ultorak
; Date: 11/15/ 2001
;
; PURPOSE:
; Eraseable memory variables and structures for the WAITLIST. See the
; WAITLIST source code file for more information.
;================= =========================================================

MAXTASK EQU 7 ; max number of tasks


MAXVAL EQU %037777 ; largest pos 15-bit int (+16383 dec)
MAXDELAY EQU 12000 ; 120 seconds (in .01 sec ticks)
MAXTIMEOUT EQU MAXVAL-MAXDELAY+1 ; TIME3 setting for MAXDELAY

; task delta t: nu mber of 10 mSec ticks until timeout.


; i.e.: 0=timeou t, 1=10mS until timeout, 2=20mS until timeout...
; maximum time d elay is 120 (decimal) seconds.
;
; If a task record is empty (unused), the address is always set to
; zero and the tim e is set to MAXDELAY.

; task record stru cture


TSKTIME EQU 0 ; offset to task delta time
TSKADDR EQU 1 ; offset to 14-bit task address

TRECSZ EQU 2 ; size of task record (words)

; Array of all tas k records


WL_taskList EQU *
00057 0057 00000 1 DS 0 ; record 0
00060 0060 00000 1 DS 0

00061 0061 00000 1 DS 0 ; record 1


00062 0062 00000 1 DS 0

00063 0063 00000 1 DS 0 ; record 2


00064 0064 00000 1 DS 0

00065 0065 00000 1 DS 0 ; record 3


00066 0066 00000 1 DS 0

00067 0067 00000 1 DS 0 ; record 4


00070 0070 00000 1 DS 0

00071 0071 00000 1 DS 0 ; record 5


00072 0072 00000 1 DS 0

00073 0073 00000 1 DS 0 ; record 6


00074 0074 00000 1 DS 0

00075 0075 00000 1 WL_IN_saveQ DS 0 ; return address


00076 0076 00000 1 WL_IN_taskPtr DS 0 ; points to task rec in list
00077 0077 00000 1 WL_IN_loopCnt DS 0 ; loop counter

00100 0100 00000 1 WL_AT_saveQ DS 0 ; return address


00101 0101 00000 1 WL_AT_taskPtr DS 0 ; points to task rec in list
00102 0102 00000 1 WL_AT_newTime DS 0 ; time to be inserted
00103 0103 00000 1 WL_AT_timeLeft DS 0 ; time remaining until timeout
00104 0104 00000 1 WL_AT_loopCnt DS 0 ; loop counter

00105 0105 00000 1 WL_T3_saveQ DS 0 ; return address


00106 0106 00000 1 WL_T3_oldBank DS 0 ; current bank

00107 0107 00000 1 WL_ST_saveQ DS 0 ; return address


00110 0110 00000 1 WL_ST_taskPtr DS 0 ; points to task rec in list
00111 0111 00000 1 WL_ST_newTime DS 0 ; time-out time
00112 0112 00000 1 WL_ST_loopCnt DS 0 ; loop counter

00113 0113 00000 1 WL_RT_saveQ DS 0 ; return address


00114 0114 00000 1 WL_RT_runAddr DS 0 ; address of task to run

00115 0115 00000 1 WL_RM_saveQ DS 0 ; return address


00116 0116 00000 1 WL_RM_taskPtr DS 0 ; points to task rec in list
00117 0117 00000 1 WL_RM_taskPtr2 DS 0 ; points to task rec behind taskPtr
00120 0120 00000 1 WL_RM_loopCnt DS 0 ; loop counter
00121 0121 00000 1 WL_RM_retval DS 0 ; tmp store for return value

00122 0122 00000 1 WL_IS_newTime DS 0 ; INPUT: time to be inserted


00123 0123 00000 1 WL_IS_newAddr DS 0 ; INPUT: address to be inserted
00124 0124 00000 1 WL_IS_saveQ DS 0 ; return address
00125 0125 00000 1 WL_IS_taskPtr DS 0 ; points to task rec in list
00126 0126 00000 1 WL_IS_taskPtr2 DS 0 ; points to task rec ahead of taskPtr
00127 0127 00000 1 WL_IS_loopCnt DS 0 ; loop counter

INC L exec_e.asm ; EXEC variables


;================= =========================================================
; EXEC (file:exec_ e.asm)
;
; Version: 1.0
; Author: John P ultorak
; Date: 04/26/ 2002
;
; PURPOSE:
; Eraseable memory variables and structures for the EXEX. See the EXEC
; source code file for more information.
;
; The COLOSSUS ver sion of this is on p. 70.
;
; ERRATA: The curr ent version of the EXEC does not set the BANKSET parameter.
; Instead, it stor es the 14-bit CADR in LOC. Also, the JOBPRIOBASE field
; has been added.
;================= =========================================================
MAXJOBS EQU 7 ; max number jobs (not incl current job)

JRECSZ EQU 13 ; size of job record (words)

; (COLOSSUS, p. 70 )
; dynamically allo cated core sets for EXEC jobs (8 sets)

; record for curre nt (running) job


; Job priority: 0= no job, 1=lowest priority job, 2=...

EX_currentJob EQU *

MPAC EQU * ; multi-purpose accumulator


00130 0130 00000 1 DS 0
00131 0131 00000 1 DS 0
00132 0132 00000 1 DS 0
00133 0133 00000 1 DS 0
00134 0134 00000 1 DS 0
00135 0135 00000 1 DS 0
00136 0136 00000 1 DS 0

00137 0137 00000 1 MODE DS 0 ; +1 for TP, +0 for DP, or -1 for vector
00140 0140 00000 1 LOC DS 0 ; location associated with job
00141 0141 00000 1 BANKSET DS 0 ; usually contains bank setting
00142 0142 00000 1 PUSHLOC DS 0 ; word of packed interpretive parameters
00143 0143 00000 1 PRIORITY DS 0 ; priority of present job and work area
00144 0144 00000 1 JOBPRIOBASE DS 0 ; nominal job priority

; records for addi tional jobs waiting to run

JREC0 EQU *
ORG JREC0+JRECSZ

JREC1 EQU *
ORG JREC1+JRECSZ

JREC2 EQU *
ORG JREC2+JRECSZ

JREC3 EQU *
ORG JREC3+JRECSZ

JREC4 EQU *
ORG JREC4+JRECSZ

JREC5 EQU *
ORG JREC5+JRECSZ

JREC6 EQU *
ORG JREC6+JRECSZ

; sorted list of j obs to run. The list is sorted by job priority


; with the highest priority job at the top of the list. Each
; entry on the lis t is a word index to a job record; the indexes are
; relative to 'EX_ currentJob', but the current job is not on the
; list.

EX_jobList EQU *
ORG EX_jobList+MAXJOBS

LOCCTR EQU EX_jobList ; index to next job record

CHGJOB EQU 1 ; change jobs at next opportunity


KEEPJOB EQU 0 ; keep the same job
00307 0307 00000 1 newJob DS 0 ; change flag (set to CHGJOB or KEEPJOB)

00310 0310 00000 1 EX_JW_saveQ DS 0 ; return address


00311 0311 00000 1 EX_JW_loopCnt DS 0 ; loop counter
00312 0312 00000 1 EX_JW_CADR DS 0 ; address of job to wake
00313 0313 00000 1 EX_JW_foundit DS 0 ; 0=job not found, 1=found
00314 0314 00000 1 EX_JW_jobPtr DS 0 ; points to job rec in list
00315 0315 00000 1 EX_JW_jobPtr2 DS 0 ; points to job rec ahead of jobPtr
00316 0316 00000 1 EX_JW_fndIndx DS 0 ; index to awoken record

00317 0317 00000 1 EX_AJ_saveQ DS 0 ; return address


00320 0320 00000 1 EX_AJ_loopCnt DS 0 ; loop counter
00321 0321 00000 1 EX_AJ_jobPrio DS 0 ; priority of new job
00322 0322 00000 1 EX_AJ_jobPtr DS 0 ; initialized to EX_jobList at startup
00323 0323 00000 1 EX_AJ_field DS 0 ; index to field from start of record
00324 0324 00000 1 EX_AJ_findx DS 0 ; total index to field
00325 0325 00000 1 EX_IN_saveQ DS 0 ; return address
00326 0326 00000 1 EX_IN_loopCnt DS 0 ; loop counter
00327 0327 00000 1 EX_IN_jobPtr DS 0 ; points to job rec in list
00330 0330 00000 1 EX_IN_recIndex DS 0 ; record index init counter
00331 0331 00000 1 EX_IN_field DS 0 ; index to field from start of record
00332 0332 00000 1 EX_IN_findx DS 0 ; total index to field

00333 0333 00000 1 EX_MN_runAddr DS 0 ; address of job to run


00334 0334 00000 1 EX_MN_field DS 0 ; index to field from start of record
00335 0335 00000 1 EX_MN_findx DS 0 ; total index to field

00336 0336 00000 1 EX_RM_saveQ DS 0 ; return address


00337 0337 00000 1 EX_RM_jobPtr DS 0 ; points to job rec in list
00340 0340 00000 1 EX_RM_jobPtr2 DS 0 ; points to job rec behind jobPtr
00341 0341 00000 1 EX_RM_savePtr DS 0 ; tmp store for index taken off list
00342 0342 00000 1 EX_RM_loopCnt DS 0 ; loop counter
00343 0343 00000 1 EX_RM_retval DS 0 ; tmp store for return value
00344 0344 00000 1 EX_RM_field DS 0 ; index to field from start of record
00345 0345 00000 1 EX_RM_findx DS 0 ; total index to field

00346 0346 00000 1 EX_IS_newPrio DS 0 ; INPUT: priority to be inserted


00347 0347 00000 1 EX_IS_newPrioB DS 0 ; INPUT: nominal priority to be inserted
00350 0350 00000 1 EX_IS_newLoc DS 0 ; INPUT: address to be inserted
00351 0351 00000 1 EX_IS_saveQ DS 0 ; return address
00352 0352 00000 1 EX_IS_jobPtr DS 0 ; points to job rec in list
00353 0353 00000 1 EX_IS_jobPtr2 DS 0 ; points to job rec ahead of jobPtr
00354 0354 00000 1 EX_IS_loopCnt DS 0 ; loop counter
INC L dsky_e.asm ; DSKY variables
;================= =========================================================
; DSKY (file:dsky_ e.asm)
;
; Version: 1.0
; Author: John P ultorak
; Date: 12/14/ 2001
;
; PURPOSE:
; Eraseable memory variables and structures for the DSKY. See the EXEC
; source code file for more information.
;
; Adapted from the AGC Block II COLOSSUS rev 249 assembly listing,
; Oct 28, 1968.
;================= =========================================================

00355 0355 00000 1 FLAGWRD5 DS 0

; GENERAL ERASABLE ASSIGNMENTS


; (COLOSSUS, p. 66 )

; interrupt tempor ary storage pool


; (ITEMP1 through RUPTREG4)

00356 0356 00000 1 ITEMP1 DS 0


WAITEXIT EQU ITEMP1
EXECTEM1 EQU ITEMP1

00357 0357 00000 1 ITEMP2 DS 0


WAITBANK EQU ITEMP2
EXECTEM2 EQU ITEMP2

00360 0360 00000 1 ITEMP3 DS 0


RUPTSTOR EQU ITEMP3
WAITADR EQU ITEMP3
NEWPRIO EQU ITEMP3

00361 0361 00000 1 ITEMP4 DS 0


;LOCCTR EQU ITEMP4 ; moved to EXEC
WAITTEMP EQU ITEMP4

00362 0362 00000 1 ITEMP5 DS 0


NEWLOC EQU ITEMP5

00363 0363 00000 1 ITEMP6 DS 0


NEWLOCP1 EQU ITEMP6 ; DP address

00364 0364 00000 1 NEWJOB DS 0 ; COLOSSUS: must be at loc 68 due to wir i n g


00365 0365 00000 1 RUPTREG1 DS 0
00366 0366 00000 1 RUPTREG2 DS 0
00367 0367 00000 1 RUPTREG3 DS 0
00370 0370 00000 1 RUPTREG4 DS 0
KEYTEMP1 EQU RUPTREG4
DSRUPTEM EQU RUPTREG4

; FLAGWORD reserva tions


STATE EQU * ; 12 words
00371 0371 00000 1 DS 0
00372 0372 00000 1 DS 0
00373 0373 00000 1 DS 0
00374 0374 00000 1 DS 0
00375 0375 00000 1 DS 0
00376 0376 00000 1 DS 0
00377 0377 00000 1 DS 0
00400 0400 00000 1 DS 0
00401 0401 00000 1 DS 0
00402 0402 00000 1 DS 0
00403 0403 00000 1 DS 0
00404 0404 00000 1 DS 0

FLAGFILL EQU * ; space for future flags


00405 0405 00000 1 DS 0
00406 0406 00000 1 DS 0
00407 0407 00000 1 DS 0
00410 0410 00000 1 DS 0

; pad load for DAP s


; (COLOSSUS, p. 67 )

EMDOT EQU FLAGFILL

; exit for VB3

STATEXIT EQU FLAGFILL+2

; EXEC temporaries which may be used between CCS NEWJOBS.


; (INTB15P through RUPTMXM)

00411 0411 00000 1 INTB15P DS 0 ; reflects 15th bit of indexable address e s


DSEXIT EQU INTB15P ; return for DSPIN
EXITEM EQU INTB15P ; return for scale factor routine select
BLANKRET EQU INTB15P ; return for 2BLANK

00412 0412 00000 1 INTBIT15 DS 0 ; similar to above


WRDRET EQU INTBIT15 ; return for 5BLANK
WDRET EQU INTBIT15 ; return for DSPWD
DECRET EQU INTBIT15 ; return for PUTCOM (dec load)
_2122REG EQU INTBIT15 ; temp for CHARIN

; The registers be tween ADDRWD and PRIORITY must stay in the following order
; for interpretive trace.

00413 0413 00000 1 ADDRWD DS 0 ; 12 bit interpretive operand subaddress


00414 0414 00000 1 POLISH DS 0 ; holds CADR made from POLISH address
UPDATRET EQU POLISH ; return for UPDATNN, UPDATVB
CHAR EQU POLISH ; temp for CHARIN
ERCNT EQU POLISH ; counter for error light reset
DECOUNT EQU POLISH ; counter for scaling and display (dec)

00415 0415 00000 1 FIXLOC DS 0 ; work area address


00416 0416 00000 1 OVFIND DS 0 ; set non-zero on overflow

VBUF EQU * ; temporary storage used for vectors


00417 0417 00000 1 DS 0
00420 0420 00000 1 DS 0
00421 0421 00000 1 DS 0
00422 0422 00000 1 DS 0
00423 0423 00000 1 DS 0
00424 0424 00000 1 DS 0
SGNON EQU VBUF ; temp for +,- on
NOUNTEM EQU VBUF ; counter for MIXNOUN fetch
DISTEM EQU VBUF ; counter for octal display verbs
DECTEM EQU VBUF ; counter for fetch (dec display verbs)

SGNOFF EQU VBUF+1 ; temp for +,- off


NVTEMP EQU VBUF+1 ; temp for NVSUB
SFTEMP1 EQU VBUF+1 ; storage for SF const hi part(=SFTEMP2- 1 )
HITEMIN EQU VBUF+1 ; temp for load of hrs, min, sec
; must = LOWTEMIN- 1

CODE EQU VBUF+2 ; for DSPIN


SFTEMP2 EQU VBUF+2 ; storage for SF const low part(=SFTEMP1 + 1 )
LOWTEMIN EQU VBUF+2 ; temp for load of hrs, min, sec
; must = HITEMIN+1

; (COLOSSUS, p. 68 )

MIXTEMP EQU VBUF+3 ; for MIXNOUN data


SIGNRET EQU VBUF+3 ; return for +,- on

; Also, MIXTEMP+1 = VBUF+4, MIXTEMP+2 = VBUF+5

BUF EQU * ; temporary scalar storage


00425 0425 00000 1 DS 0
00426 0426 00000 1 DS 0
00427 0427 00000 1 DS 0

00430 0430 00000 1 BUF2 DS 0


00431 0431 00000 1 DS 0

INDEXLOC EQU BUF ; contains address of specified index


SWWORD EQU BUF ; address of switch word
SWBIT EQU BUF+1 ; switch bit within switch word
00432 0432 00000 1 MPTEMP DS 0 ; temporary used in multiply and shift
DMPNTEMP EQU MPTEMP ; DMPSUB temporary
00433 0433 00000 1 DOTINC DS 0 ; component increment for DOT subroutine
DVSIGN EQU DOTINC ; determines sign of DDV result
ESCAPE EQU DOTINC ; used in arcsin/arccos
ENTRET EQU DOTINC ; exit from enter

00434 0434 00000 1 DOTRET DS 0 ; return from DOT subroutine


DVNORMCT EQU DOTRET ; dividend normalization count in DDV
ESCAPE2 EQU DOTRET ; alternate arcsin/arccos switch
WDCNT EQU DOTRET ; char counter for DSPWD
INREL EQU DOTRET ; input buffer selector (X,Y,Z REG)

00435 0435 00000 1 MATINC DS 0 ; vector increment in MXV and VXM


MAXDVSW EQU MATINC ; +0 if DP quotient is near one - else - 1
POLYCNT EQU MATINC ; polynomial loop counter
DSPMMTEM EQU MATINC ; DSPCOUNT save for DSPMM
MIXBR EQU MATINC ; indicator for mixed or normal noun

00436 0436 00000 1 TEM1 DS 0 ; EXEC temp


POLYRET EQU TEM1
DSREL EQU TEM1 ; rel address for DSPIN

00437 0437 00000 1 TEM2 DS 0 ; EXEC temp


DSMAG EQU TEM2 ; magnitude store for DSPIN
IDADDTEM EQU TEM2 ; mixnoun indirect address store

00440 0440 00000 1 TEM3 DS 0 ; EXEC temp


COUNT EQU TEM3 ; for DSPIN

00441 0441 00000 1 TEM4 DS 0 ; EXEC temp


LSTPTR EQU TEM4 ; list pointer for GRABUSY
RELRET EQU TEM4 ; return for RELDSP
FREERET EQU TEM4 ; return for FREEDSP
DSPWDRET EQU TEM4 ; return for DSPSIGN
SEPSCRET EQU TEM4 ; return for SEPSEC
SEPMNRET EQU TEM4 ; return for SEPMIN

00442 0442 00000 1 TEM5 DS 0 ; EXEC temp


NOUNADD EQU TEM5 ; temp storage for noun address

; (COLOSSUS, p. 69 )

00443 0443 00000 1 NNADTEM DS 0 ; temp for noun address table entry
00444 0444 00000 1 NNTYPTEM DS 0 ; temp for noun type table entry
00445 0445 00000 1 IDAD1TEM DS 0 ; temp for indir address table entry (MI X N N )
; must - IDAD2TEM- 1, = IDAD3TEM-2
00446 0446 00000 1 IDAD2TEM DS 0 ; temp for indir address table entry (MI X N N )
; must - IDAD2TEM+ 1, = IDAD3TEM-1
00447 0447 00000 1 IDAD3TEM DS 0 ; temp for indir address table entry (MI X N N )
; must - IDAD1TEM+ 2, = IDAD2TEM+1
00450 0450 00000 1 RUTMXTEM DS 0 ; temp for SF rout table entry (MIXNN on l y )

; AX*SR*T storage

DEXDEX EQU TEM2 ; B(1) tmp


DEX1 EQU TEM3 ; B(1) tmp
DEX2 EQU TEM4 ; B(1) tmp
RTNSAVER EQU TEM5 ; B(1) tmp
TERM1TMP EQU BUF2 ; B(2) tmp

; (COLOSSUS, p. 70 ) Note: the eraseable memory for the EXEC.


; Moved to the EXE C area

; (COLOSSUS, p. 72 )
; unswitched for d isplay interface routines

00451 0451 00000 1


RESTREG DS 0 ; B(1) prm for display starts
00452 0452 00000 1
NVWORD DS 0
00453 0453 00000 1
MARXNV DS 0
00454 0454 00000 1
NVSAVE DS 0
; (retain the orde r of CADRFLSH to FAILREG+2 for downlink purposes)
00455 0455 00000 1 CADRFLSH DS 0 ; B(1) tmp
00456 0456 00000 1 CADRMARK DS 0 ; B(1) tmp
00457 0457 00000 1 TEMPFLSH DS 0 ; B(1) tmp

00460 0460 00000 1 FAILREG DS 0 ; B(3) prm 3 alarm-abort user=S 2CADR


00461 0461 00000 1 DS 0
00462 0462 00000 1 DS 0

; (COLOSSUS, p. 73 )
; verb 37 storage

00463 0463 00000 1 MINDEX DS 0 ; B(1) tmp index for major mode
00464 0464 00000 1 MMNUMBER DS 0 ; B(1) tmp major mode requested via V37

; pinball interrup t storage

00465 0465 00000 1 DSPCNT DS 0 ; B(1) prm DSPOUT counter

; pinball executiv e action

00466 0466 00000 1 DSPCOUNT DS 0 ; display position indicator


00467 0467 00000 1 DECBRNCH DS 0 ; Bits2,1: octal=0, +dec=1, -dec=2
; Bit5=R1 (dec), B it4=R2 (dec), Bit3=R3 (dec)
00470 0470 00000 1 VERBREG DS 0 ; verb code
00471 0471 00000 1 NOUNREG DS 0 ; noun code
00472 0472 00000 1 XREG DS 0 ; R1 input buffer
00473 0473 00000 1 YREG DS 0 ; R2 input buffer
00474 0474 00000 1 ZREG DS 0 ; R3 input buffer
00475 0475 00000 1 XREGLP DS 0 ; low part of XREG (for ded conv only)
00476 0476 00000 1 YREGLP DS 0 ; low part of YREG (for ded conv only)
HITEMOUT EQU YREGLP ; temp for display of HRS, MIN, SEC
; must equal LOT EMOUT-1
00477 0477 00000 1 ZREGLP DS 0 ; low part of ZREG (for ded conv only)
LOTEMOUT EQU ZREGLP ; temp for display of HRS, MIN, SEC
; must equal HIT EMOUT+1
; (COLOSSUS, p. 74 )

00500 0500 00000 1 MODREG DS 0 ; mode code


00501 0501 00000 1 DSPLOCK DS 0 ; keyboard/subroutine call interlock
00502 0502 00000 1 REQRET DS 0 ; return register for load
00503 0503 00000 1 LOADSTAT DS 0 ; status indicator for LOADTST
00504 0504 00000 1 CLPASS DS 0 ; pass indicator clear
00505 0505 00000 1 NOUT DS 0 ; activity counter for DSPTAB
00506 0506 00000 1 NOUNCADR DS 0 ; machine CADR for noun
00507 0507 00000 1 MONSAVE DS 0 ; N/V code for monitor (= MONSAVE1 - 1)
00510 0510 00000 1 MONSAVE1 DS 0 ; NOUNCADR for monitor (MATBS) = MONSAVE + 1
00511 0511 00000 1 MONSAVE2 DS 0 ; NVMONOPT options

; The 11 register table for the display panel (COLOSSUS, p.74, p.306)
; comment key = RELADD: RELAYWD BIT11 BITS10-6 BITS5-1

DSPTAB EQU *
00512 0512 00000 1 DS 0 ; 0: 0001 -R3 R3D4( 1) R3D5( 0)
00513 0513 00000 1 DS 0 ; 1: 0010 +R3 R3D2( 3) R3D3( 2)
00514 0514 00000 1 DS 0 ; 2: 0011 --- R2D5( 5) R3D1( 4)
00515 0515 00000 1 DS 0 ; 3: 0100 -R2 R2D3( 7) R2D4( 6)
00516 0516 00000 1 DS 0 ; 4: 0101 +R2 R2D1(11) R2D2(10)
00517 0517 00000 1 DS 0 ; 5: 0110 -R1 R1D4(13) R1D5(12)
00520 0520 00000 1 DS 0 ; 6: 0111 +R1 R1D2(15) R1D3(14)
00521 0521 00000 1 DS 0 ; 7: 1000 --- -------- R1D1(16)
00522 0522 00000 1 DS 0 ; 8: 1001 --- ND1 (21) ND2 (20)
00523 0523 00000 1 DS 0 ; 9: 1010 --- VD1 (23) VD2 (22)
00524 0524 00000 1 DS 0 ; 10:1011 --- MD1 (25) MD1 (24)
00525 0525 00000 1 DS 0 ; 11: C/S lights

00526 0526 00000 1 NVQTEM DS 0 ; NVSUB storage for calling address


; must = NVBNKTEM- 1
00527 0527 00000 1 NVBNKTEM DS 0 ; NVSUB storage for calling bank
; must = NVQTEM+1
00530 0530 00000 1 VERBSAVE DS 0 ; needed for recycle
00531 0531 00000 1 CADRSTOR DS 0 ; ENDIDLE storage
00532 0532 00000 1 DSPLIST DS 0 ; waiting reg for DSP syst internal use
00533 0533 00000 1 EXTVRACT DS 0 ; extended verb activity interlock

00534 0534 00000 1 DSPTEM1 DS 0 ; buffer storage area 1 (mostly for time )
00535 0535 00000 1 DS 0
00536 0536 00000 1 DS 0

00537 0537 00000 1 DSPTEM2 DS 0 ; buffer storage area 2 (mostly for deg)
00540 0540 00000 1 DS 0
00541 0541 00000 1 DS 0

DSPTEMX EQU DSPTEM2 ; B(2) S-S display buffer for external v e r b s


NORMTEM1 EQU DSPTEM1 ; B(3) DSP normal display registers

; display for exte nded verbs

OPTIONX EQU DSPTEMX ; B(2) extended verb option code N12(VB2 )


; temp store for m ajor mode change

00542 0542 00000 1 MMTEMP DS 0

; T4RUPT Erasable

00543 0543 00000 1 DSRUPTSW DS 0 ; (COLOSSUS, p. 78)


00544 0544 00000 1 T4RET DS 0 ; added, not part of COLOSSUS
00545 0545 00000 1 DSPOUTRET DS 0 ; added, not part of COLOSSUS
00546 0546 00000 1 DK_IN_saveQ DS 0 ; return for T4RUPT init

; Replacement for Block II LXCH instruction (not part of COLOSSUS)

00547 0547 00000 1 LXCH_LPRET DS 0 ; LP return address


00550 0550 00000 1 LXCH_A DS 0 ; save A

; vars for KEYPROG

00551 0551 00000 1 KP_MPAC DS 0

; Vars for DPTEST (not part of COLOSSUS)

00552 0552 00000 1 DPTEST_A DS 0


00553 0553 00000 1 DPTEST_Q DS 0

; Vars for REQDATX , REQDATY, REQDATZ (not part of COLOSSUS)

00554 0554 00000 1 REQ_Q DS 0

; Vars for SETNCAD R (not part of COLOSSUS)

00555 0555 00000 1 SETNCADR_Q DS 0

; Vars for ALLDC_O C (not part of COLOSSUS)

00556 0556 00000 1 ALLDC_OC_Q DS 0

; Vars for SFRUTMI X (not part of COLOSSUS)

00557 0557 00000 1 SFRUTMIX_L DS 0

; Vars for SFCONUM (not part of COLOSSUS)

00560 0560 00000 1 SFCONUM_L DS 0

; vars for BLANKSU B (not part of COLOSSUS)

00561 0561 00000 1 BLANKSUB_Q DS 0

; Vars for GTSFOUT , GTSFIN (not part of COLOSSUS)

00562 0562 00000 1 GTSF_RET DS 0

; Vars for FIXRANG E (not part of COLOSSUS)

00563 0563 00000 1 FR_RETQ DS 0

; Vars for NVSUB ( not part of COLOSSUS)

00564 0564 00000 1 NVSUB_L DS 0


00565 0565 00000 1 NVSUB_A DS 0

; Vars for ENDIDLE (not part of COLOSSUS)

00566 0566 00000 1 ENDIDLE_L DS 0

; Vars for NVSUBUS Y (not part of COLOSSUS)

00567 0567 00000 1 NBSUBSY1_L DS 0

; Vars for FLASHON /FLASHOFF (not part of COLOSSUS)

00570 0570 00000 1 FLASHRET DS 0

; vars for PASTEVB (not part of COLOSSUS)

00571 0571 00000 1 PASTE_TMP DS 0


; vars for NEWMODE A (not part of COLOSSUS)

00572 0572 00000 1 NEWMODEA_Q DS 0

; Vars for MATH LI B (not part of COLOSSUS)

00573 0573 00000 1 SHORTMP_A DS 0


00574 0574 00000 1 SHORTMP_OVFL DS 0
00575 0575 00000 1 SHORTMP_OVFH DS 0
00576 0576 00000 1 ADDRWD1 DS 0
00577 0577 00000 1 MATH_Q DS 0
00600 0600 00000 1 PRSHRTMP_Q DS 0

; KEYRUPT Eraseabl e

00601 0601 00000 1 KEYRET DS 0 ; added, not part of COLOSSUS

00602 0602 00000 1 SAVEQ DS 0 ; temp for return addr

; Bank intercommun ication

00603 0603 00000 1 BJBANK DS 0


00604 0604 00000 1 BJRET DS 0

00605 0605 00000 1 PJBANK DS 0


00606 0606 00000 1 PJRET DS 0
00607 0607 00000 1 PJA DS 0

00610 0610 00000 1 BCBANK DS 0


00611 0611 00000 1 BCRET DS 0
00612 0612 00000 1 BCA DS 0

00613 0613 00000 1 MBCBANK DS 0


00614 0614 00000 1 MBCRET DS 0
00615 0615 00000 1 MBCA DS 0

00616 0616 00000 1 DCBANK DS 0


00617 0617 00000 1 DCRET DS 0

; FIXED MEMORY DEC LARATIONS

ORG EXTENDER
05777 5777 47777 0 DS %47777 ; needed for EXTEND

;----------------- ---------------------------------------------------------
; RESTART/INTERRUP T ENTRY POINTS
;----------------- ---------------------------------------------------------

; Program (re)star t
ORG GOPROG
02000 2000 0 1,2126 0 TC goMAIN ; AGC (re)start begins here!

; Interrupt vector s
ORG T3RUPT
02004 2004 5 0,0026 0 TS ARUPT ; TIME3 interrupt vector
02005 2005 3 0,0001 0 XCH Q
02006 2006 5 0,0027 1 TS QRUPT
02007 2007 0 1,2034 1 TC goT3

ORG ERRUPT
02010 2010 5 0,0026 0 TS ARUPT
02011 2011 3 0,0001 0 XCH Q
02012 2012 5 0,0027 1 TS QRUPT
02013 2013 0 1,2036 0 TC goER

ORG DSRUPT ; T4RUPT for DSKY display


02014 2014 5 0,0026 0 TS ARUPT
02015 2015 3 0,0001 0 XCH Q
02016 2016 5 0,0027 1 TS QRUPT
02017 2017 0 1,2037 1 TC goDS

ORG KEYRUPT ; DSKY keyboard interrupt vector


02020 2020 5 0,0026 0 TS ARUPT
02021 2021 3 0,0001 0 XCH Q
02022 2022 5 0,0027 1 TS QRUPT
02023 2023 0 1,2041 0 TC goKEY

ORG UPRUPT
02024 2024 5 0,0026 0 TS ARUPT
02025 2025 3 0,0001 0 XCH Q
02026 2026 5 0,0027 1 TS QRUPT
02027 2027 0 1,2043 1 TC goUP
; restore Q and A registers and resume

endRUPT EQU *
02030 2030 3 0,0027 1 XCH QRUPT ; restore Q
02031 2031 5 0,0001 0 TS Q
02032 2032 3 0,0026 0 XCH ARUPT ; restore A
02033 2033 2 0,0000 1 RES UME ; resume normal program execution

;----------------- ---------------------------------------------------------
; RUPT (INTERRUPT) SERVICE ROUTINES
;
; Upon entry, regi sters will contain these values:
; - ZRUPT: Prior c ontents of program counter (Z register).
; - BRUPT: Prior c ontents of B register.
; - ARUPT: Prior c ontents of accumulator (A register).
; - QRUPT: Prior c ontents of Q register.
;
; When the service routine is finished, jump to endRUPT to restore the A
; and Q registers. Call RESUME to restore Z and B, which causes a return
; to normal (non-i nterrupt) execution. Interrupts are disabled upon entry
; to the service r outine; they are reenabled following RESUME.
;----------------- ---------------------------------------------------------

goT3 EQU *
02034 2034 0 1,2347 0 TCR WL_TIME3task ; handle T3RUPT for WAITLIST
02035 2035 0 1,2030 0 TC endRUPT

goER EQU *
02036 2036 0 1,2030 0 TC endRUPT

goDS EQU *
02037 2037 0 2,4047 0 TCR T4PROG ; handle T4RUPT for DSKY display
02040 2040 0 1,2030 0 TC endRUPT

goKEY EQU *
02041 2041 0 2,4132 0 TCR KEYPROG ; handle keyrupt for keyboard entry
02042 2042 0 1,2030 0 TC endRUPT

goUP EQU *
02043 2043 0 1,2030 0 TC endRUPT

;----------------- ---------------------------------------------------------
; FIXED MEMORY CON STANTS
;----------------- ---------------------------------------------------------

02044 2044 00200 0 ofbit DS %200 ; OUT1, bit 8 initiates standby

02045 2045 77777 0 NEG0 DS -0


02046 2046 77776 1 NEG1 DS -1
02047 2047 77775 1 NEG2 DS -2

02050 2050 00000 1 ZERO DS 0


02051 2051 00001 0 ONE DS 1
02052 2052 00002 0 TWO DS 2
02053 2053 00003 1 THREE DS 3
02054 2054 00004 0 FOUR DS 4
02055 2055 00005 1 FIVE DS 5
02056 2056 00006 1 SIX DS 6
02057 2057 00007 0 SEVEN DS 7
02060 2060 00012 1 TEN DS 10
02061 2061 00013 0 ELEVEN DS 11

; must be in rever se order. Pinball treats this as a table


; and indexes thru it.

02062 2062 40000 0 BIT15 DS %40000


02063 2063 20000 0 BIT14 DS %20000
02064 2064 10000 0 BIT13 DS %10000
02065 2065 04000 0 BIT12 DS %04000
02066 2066 02000 0 BIT11 DS %02000
02067 2067 01000 0 BIT10 DS %01000
02070 2070 00400 0 BIT9 DS %00400
02071 2071 00200 0 BIT8 DS %00200
02072 2072 00100 0 BIT7 DS %00100
02073 2073 00040 0 BIT6 DS %00040
02074 2074 00020 0 BIT5 DS %00020
02075 2075 00010 0 BIT4 DS %00010
02076 2076 00004 0 BIT3 DS %00004
02077 2077 00002 0 BIT2 DS %00002
02100 2100 00001 0 BIT1 DS %00001

02101 2101 00177 0 LOW7 DS %00177

02102 2102 06000 1 bankAddr DS %6000 ; fixed-switchable addr range starts her e
02103 2103 01777 1 lowAddr DS %1777 ; mask for 10-bit address
02104 2104 01400 1 OCT1400 DS %1400
02105 2105 00013 0 NOUTCON DS 11

02106 2106 37777 1 POSMAX DS %37777

;----------------- --------------------------------------------------------
; CLRMEM - INITIAL IZE ERASEABLE MEMORY
;
; Uses QRUPT and A RUPT as scratchpad. This is OK, because interrupts
; are disabled any way. All eraseable memory above the AGC clock (TIME1,
; TIME2) is cleare d. The AGC clock is not cleared because this might
; be a restart or a startup from standby mode.
;----------------- --------------------------------------------------------

CLRMEM EQU *
02107 2107 3 0,0001 0 XCH Q
02110 2110 5 0,0027 1 TS QRUPT ; save return address

02111 2111 3 1,2123 0 XCH CLRMEM_WC ; init count of words to clear


02112 2112 5 0,0026 0 TS ARUPT

CLRMEM_CHK EQU *
02113 2113 1 0,0026 1 CCS ARUPT
02114 2114 0 1,2116 0 TC CLRMEM_WORD
02115 2115 0 0,0027 1 TC QRUPT ; return

CLRMEM_WORD EQU *
02116 2116 5 0,0026 0 TS ARUPT
02117 2117 3 1,2050 0 CAF CLRMEM_VAL
02120 2120 2 0,0026 1 IND EX ARUPT
02121 2121 5 0,0037 0 TS CLRMEM_BADDR ; clear a word
02122 2122 0 1,2113 0 TC CLRMEM_CHK ; done?

CLRMEM_VAL EQU ZERO ; set memory to this value


CLRMEM_BADDR EQU TIME3 ; base address to clear
02123 2123 01741 1 CLRMEM_WC DS %1777-TIME3+1 ; clear everything >= TIME3

;----------------- --------------------------------------------------------
; FRESH START
;
; AGC starts execu ting here, following power-up, or restart.
;----------------- --------------------------------------------------------

02124 2124 10000 0 V37BANK DS %10000 ; BANK (4) containg PREMM1, FCADRMM1
02125 2125 37600 0 SAMASK DS %37600 ; mask to zero lower 7 bits

goMAIN EQU *
SLAP1 EQU goMAIN ; entry for V36 (fresh start request)

02126 2126 2 0,0000 0 INH INT

; First, check for standby operation. Loosely based on the standby


; algorithm in R-3 93. Probably should flash the 'computer activity'
; light as well.

02127 2127 3 1,2071 0 CAF BIT8 ; add 2 to 7th power to AGC clock
02130 2130 6 0,0036 1 AD TIME1
02131 2131 5 0,0036 1 TS TIME1

02132 2132 3 1,2050 0 CAF ZERO ; skipped on ovf and C(A) set to 1
02133 2133 6 0,0035 1 AD TIME2 ; bump TIME2 with overflow, if any
02134 2134 5 0,0035 1 TS TIME2

02135 2135 3 1,2125 0 CAF SAMASK ; zero the LSBs of TIME1


02136 2136 7 0,0036 0 MAS K TIME1
02137 2137 5 0,0036 1 TS TIME1

02140 2140 3 1,2044 0 XCH ofbit ; enable standby operation


02141 2141 5 0,0011 1 TS OUT1

02142 2142 0 1,2107 0 TC CLRMEM ; clear everything but the AGC clock

; set fresh start major mode to P00 (AGC CMC idle)

02143 2143 3 1,2124 1 CAF V37BANK


02144 2144 5 0,0015 0 TS BANK ; bank for major mode tables

02145 2145 3 4,6046 0 CAF NOV37MM ; assumes BANK is set (above)


02146 2146 5 0,0463 0 TS MINDEX ; index to P00

goMMchange EQU *
02147 2147 2 0,0000 0 INH INT ; inhibit interrupts

; Initialize WAITL IST and EXEC eraseable memory. Initialize DSKY eraseable
; memory (but don' t initialize BANK or MINDEX; they are used to start the
; main job for thi s major mode.

02150 2150 0 1,3252 1 TCR EX_initEX ; initialize EXEC


02151 2151 0 1,2204 0 TCR WL_initWL ; initialize WAITLIST
02152 2152 0 2,4007 1 TCR DK_initDK ; initialize DSKY

; Start the major mode job. This is modified from COLOSSUS because block I
; doesn't have E-b ank and my SPVAC interface is a little different from the
; original. The re ferences to PREMM1 and FCADRMM1 assume that the BANK is
; set to the one c ontaining those tables.

V37XEQ EQU *
02153 2153 2 0,0000 0 INH INT
02154 2154 2 0,0463 1 IND EX MINDEX
02155 2155 3 4,6037 0 CAF PREMM1
02156 2156 5 0,0542 1 TS MMTEMP

02157 2157 7 2,4666 1 MAS K HI5 ; obtain priority bits 15-11


02160 2160 0 2,4640 1 TC RIGHT5
02161 2161 0 2,4640 1 TC RIGHT5 ; shift right to bits 5-1
02162 2162 5 0,0360 1 TS NEWPRIO ; store PRIO for SPVAC

02163 2163 2 0,0463 1 IND EX MINDEX


02164 2164 3 4,6030 1 CAF FCADRMM1

02165 2165 0 1,3075 0 TC SPVAC ; job CADR in C(A), job prio in NEWPRIO

V37XEQC EQU *
02166 2166 3 1,2050 0 CAF ZERO ; was CA MMTEMP in Block II
02167 2167 6 0,0542 1 AD MMTEMP ; upon return from FINDVAC, place the
02170 2170 7 1,2101 1 MAS K LOW7 ; new MM in MODREG (the low 7 bits of
02171 2171 0 2,5036 1 TC NEWMODEA ; PHSERDT1)

02172 2172 0 2,5003 1 TC RELDSP ; release display

; Start the EXEC.

02173 2173 0 1,2656 0 TC EX_exec ; never returns

;----------------- --------------------------------------------------------
; AGC LIBRARIES
;
; System services in fixed-fixed memory.
;----------------- --------------------------------------------------------

INC L waitlist_f.asm ; WAITLIST, incl. T3RUPT handler


;================= =========================================================
; WAITLIST (file:w aitlist_f.asm)
;
; Version: 1.0
; Author: John P ultorak
; Date: 11/15/ 2001
;
; PURPOSE:
; Constants and so urce code for WAITLIST.
;
; Non-preemptive i nterrupt timer routines, originally implemented by J. H.
; Laning, Jr. for AGC3 and later adapted for AGC4. Briefly discussed in
; R-393, which giv es some of the software interfaces into the WAITLIST.
; This is my own r ecreation, and the internals may differ from the original.
;
; A task is schedu led for execution by calling 'WAITLIST' and
; furnishing the t ime-out time and starting address.
; L XCH TASK_TIMEOUT ; in 10 mSec ticks
; L+1 TC WAITLIST
; L+2 DS TASK_ADDRESS ; 14-bit address
; L+3 ... execution resumes here
;
; TASK_TIMEOUT = a positive integer from 1 - MAXDELAY that specifies the delay
; in 10 mSec ti cks. Maximum delay is 12000 (2 minutes).
; TASK_ADDRESS = s tarting address of the task (14-bit address)
;
; WAITLIST can be called from from an interrupt, or from normal execution.
; It is the only p ublic function of the waitlist.
;
; **** WARNING *** * If WAITLIST is not called from an interrupt, be sure to
; inhibit interrup ts before calling it to protect the integrity of the list.
;
; Tasks execute wh en TIME3 overflows and generates an interrupt (T3RUPT).
; The task execute s during the interrupt. Tasks terminate themselves by
; jumping to TASKO VER.
; TC TASKOVER
;
; Because tasks ex ecute during an interrupt, they should be fairly short.
; Tasks can initia te longer operations by scheduling a 'job' using EXEC.
;================= =========================================================

02174 2174 00002 0 WL_taskRecSize DS TRECSZ ; size of a task record (words)


02175 2175 00057 0 WL_tskLstStart DS WL_taskList ; starting address for task list
02176 2176 00073 0 WL_tskLstEnd DS MAXTASK-1@TRECSZ+WL_taskList
02177 2177 00006 1 WL_numTasks DS MAXTASK-1 ; init loop counter for all tasks
02200 2200 00005 1 WL_numTasks1 DS MAXTASK-2 ; init loop counter for all tasks - 1

02201 2201 37777 1 WL_maxVal DS MAXVAL


02202 2202 27340 0 WL_maxDelay DS MAXDELAY
02203 2203 10440 0 WL_maxTimeOut DS MAXTIMEOUT

;----------------- ---------------------------------------------------------
; WL_initWL - INIT IALIZE WAITLIST
;
; Subroutine initi alizes the eraseable memory segment for WAITLIST.
; Necessary in cas e the AGC is restarted.
;
; Note: the valid range for TIME3 is 10440 to 37777 (which spans
; 12000 (base 10 ) ticks, which corresponds to 120 seconds)
; positive overf low occurs at 40000, which triggers T3RUPT.
; TIME3 values o f 0 to 10437 are illegal; these values occur
; after timeout when the counter overflows. TIME3 values in this
; range indicate that timeout has occurred and that T3RUPT is
; presently occu ring, or is pending.
;----------------- ---------------------------------------------------------

WL_initWL EQU *
02204 2204 3 0,0001 0 XCH Q
02205 2205 5 0,0075 0 TS WL_IN_saveQ ; save return address

02206 2206 3 1,2203 1 CAF WL_maxTimeOut


02207 2207 5 0,0037 0 TS TIME3

; Iterate through task list and initialize all records to NIL

02210 2210 3 1,2175 0 CAF WL_tskLstStart ; init pointer to start of list


02211 2211 5 0,0076 0 TS WL_IN_taskPtr

02212 2212 3 1,2177 1 CAF WL_numTasks ; loop for number of tasks


WL_IN_loop EQU *
02213 2213 5 0,0077 1 TS WL_IN_loopCnt

02214 2214 3 1,2202 0 CAF WL_maxDelay


02215 2215 2 0,0076 1 IND EX WL_IN_taskPtr
02216 2216 5 0,0000 1 TS TSKTIME

02217 2217 3 1,2050 0 CAF ZERO


02220 2220 2 0,0076 1 IND EX WL_IN_taskPtr
02221 2221 5 0,0001 0 TS TSKADDR

02222 2222 3 0,0076 0 XCH WL_IN_taskPtr ; bump task pointer back 1 record
02223 2223 6 1,2174 1 AD WL_taskRecSize
02224 2224 5 0,0076 0 TS WL_IN_taskPtr

02225 2225 1 0,0077 0 CCS WL_IN_loopCnt ; done checking task list?


02226 2226 0 1,2213 0 TC WL_IN_loop ; not yet

02227 2227 3 0,0075 0 XCH WL_IN_saveQ


02230 2230 5 0,0001 0 TS Q ; restore return address
02231 2231 0 0,0000 0 RET URN

;----------------- ---------------------------------------------------------
; WAITLIST - ADD T ASK TO WAITLIST
;
; Subroutine adds a task to WL_taskList. The following conditions are
; true upon entry.
; 1) The task list is sorted so the next task scheduled for execution
; is at the fro nt of the list.
; 2) If no tasks a re currently scheduled, the task record at the front
; of the list w ill be NIL.
; 3) Unused (NIL) records in the task list have their time fields set to
; MAXDELAY and their address fields set to zero.
; 4) If any tasks are on the waitlist, the time field in that task's
; record will c ontain the remaining time AFTER the next timeout. The
; task schedule d for execution at timeout will have a time remaining
; of zero.
; Any other tas ks that will execute at that time will also have a time of
; zero. Tasks t hat will execute some time in the future AFTER timeout
; will have non zero times; these times indicate the additional time
; needed after the next timeout.
;
; This is the only 'public' function. It can be called from a job or from
; a task or other interrupt. It disables interrupts to maintain the integrity
; of the taskList.
;----------------- ---------------------------------------------------------

WAITLIST EQU *
02232 2232 5 0,0102 1 TS WL_AT_newTime ; save task time
02233 2233 3 0,0001 0 XCH Q
02234 2234 5 0,0100 0 TS WL_AT_saveQ ; save return address-1

02235 2235 3 1,2050 0 CAF ZERO


02236 2236 2 1,2176 1 IND EX WL_tskLstEnd
02237 2237 6 0,0001 0 AD TSKADDR
02240 2240 1 0,0000 0 CCS A ; list full?
02241 2241 0 1,2343 1 TC WL_AT_done ; >0 yes, so give up

; Calculate time r emaining until currently scheduled time-out.

02242 2242 3 1,2050 0 CAF ZERO


02243 2243 6 0,0037 0 AD TIME3 ; get time
02244 2244 5 0,0103 0 TS WL_AT_timeLeft ; save it, temporarily

; Did TIME3 recent ly overflow? If so, we are inside T3RUPT, or T3RUPT


; is pending. TIME 3 values from 0 - 10437 are not legal, so they
; indicate that an overflow has occurred.

02245 2245 4 1,2203 0 CS WL_maxTimeOut


02246 2246 6 0,0103 0 AD WL_AT_timeLeft
02247 2247 1 0,0000 0 CCS A ; TIME3 recently overflowed?
02250 2250 0 1,2264 0 TC WL_AT_noOvf ; >0 no
02251 2251 0 1,2264 0 TC WL_AT_noOvf ; +0 no
02252 2252 0 1,2254 0 TC *+2 ; <0 yes
02253 2253 0 1,2264 0 TC WL_AT_noOvf ; -0 no

; TIME3 already ti med-out, so we must be inside T3RUPT, or T3RUPT


; is pending. Just add the new task to the list. No time correction
; is necessary; th e epoch is NOW.

02254 2254 3 1,2050 0 CAF ZERO


02255 2255 6 0,0102 1 AD WL_AT_newTime
02256 2256 5 0,0122 0 TS WL_IS_newTime ; set time field in new task record

02257 2257 2 0,0100 1 IND EX WL_AT_saveQ ; indirectly address WL_AT_saveQ


02260 2260 3 0,0000 1 CAF 0
02261 2261 5 0,0123 1 TS WL_IS_newAddr ; set addr field in new task record

02262 2262 0 1,2473 0 TCR WL_insert ; add new task to task list
02263 2263 0 1,2343 1 TC WL_AT_done

; TIME3 has not ti med out yet. Calculate time remaining until timeout
; (timeout occurs when TIME3 overflows)

WL_AT_noOvf EQU *
02264 2264 4 0,0103 1 CS WL_AT_timeLeft ; get -TIME3
02265 2265 6 1,2201 0 AD WL_maxVal
02266 2266 6 1,2051 1 AD ONE
02267 2267 5 0,0103 0 TS WL_AT_timeLeft ; time left = -TIME3 + %37777 + 1

; Compare that tim e against the timeout for the new task.

WL_AT_chkOrder EQU *
02270 2270 4 0,0102 0 CS WL_AT_newTime
02271 2271 6 0,0103 0 AD WL_AT_timeLeft
02272 2272 1 0,0000 0 CCS A ; compare new task to current
02273 2273 0 1,2306 0 TC WL_AT_mkFirst ; >0 (make new task 1st)
02274 2274 0 1,2276 0 TC *+2 ; +0
02275 2275 0 1,2276 0 TC *+1 ; <0

; The new task doe s not need to run before the current time-out, so
; just add it to t he list. Subtract the remaining time interval from the
; new task's time, so the new task will have the same epoch as the other
; tasks on the lis t.

02276 2276 4 0,0103 1 CS WL_AT_timeLeft


02277 2277 6 0,0102 1 AD WL_AT_newTime ; make epoch correction
02300 2300 5 0,0122 0 TS WL_IS_newTime ; set time field in new task record

02301 2301 2 0,0100 1 IND EX WL_AT_saveQ ; indirectly address WL_AT_saveQ


02302 2302 3 0,0000 1 CAF 0
02303 2303 5 0,0123 1 TS WL_IS_newAddr ; set addr field in new task record

02304 2304 0 1,2473 0 TCR WL_insert ; add new task to task list
02305 2305 0 1,2343 1 TC WL_AT_done

; The new task nee ds to run prior to the current time-out. Add the time
; remaining to all tasks currently on the list to change their epoch
; to NOW.

WL_AT_mkFirst EQU *
02306 2306 3 1,2175 0 CAF WL_tskLstStart ; set pointer to front of list
02307 2307 5 0,0101 1 TS WL_AT_taskPtr

02310 2310 3 1,2177 1 CAF WL_numTasks ; loop for number of tasks


WL_AT_loop EQU *
02311 2311 5 0,0104 1 TS WL_AT_loopCnt

02312 2312 3 1,2050 0 CAF ZERO


02313 2313 2 0,0101 0 IND EX WL_AT_taskPtr
02314 2314 6 0,0001 0 AD TSKADDR
02315 2315 1 0,0000 0 CCS A ; end of list?
02316 2316 0 1,2320 1 TC *+2 ; >0 no, so keep going
02317 2317 0 1,2333 0 TC WL_AT_schTsk ; +0 yes, add the new task

02320 2320 3 1,2050 0 CAF ZERO


02321 2321 2 0,0101 0 IND EX WL_AT_taskPtr
02322 2322 6 0,0000 1 AD TSKTIME
02323 2323 6 0,0103 0 AD WL_AT_timeLeft ; time-out = time-out + timeLeft
02324 2324 2 0,0101 0 IND EX WL_AT_taskPtr
02325 2325 5 0,0000 1 TS TSKTIME

02326 2326 3 0,0101 1 XCH WL_AT_taskPtr ; bump task pointer back 1 record
02327 2327 6 1,2174 1 AD WL_taskRecSize
02330 2330 5 0,0101 1 TS WL_AT_taskPtr

02331 2331 1 0,0104 0 CCS WL_AT_loopCnt ; done fixing the times?


02332 2332 0 1,2311 0 TC WL_AT_loop ; not yet

; Now that the tas ks all share the same epoch, add the new task to the
; list and call th e scheduler to schedule the next task.

WL_AT_schTsk EQU *
02333 2333 3 1,2050 0 CAF ZERO
02334 2334 6 0,0102 1 AD WL_AT_newTime
02335 2335 5 0,0122 0 TS WL_IS_newTime ; set time field in new task record

02336 2336 2 0,0100 1 IND EX WL_AT_saveQ ; indirectly address WL_AT_saveQ


02337 2337 3 0,0000 1 CAF 0
02340 2340 5 0,0123 1 TS WL_IS_newAddr ; set addr field in new task record

02341 2341 0 1,2473 0 TCR WL_insert ; add new task to task list

02342 2342 0 1,2417 1 TCR WL_schedTask ; schedule the next task

WL_AT_done EQU *
02343 2343 3 0,0100 0 XCH WL_AT_saveQ
02344 2344 6 1,2051 1 AD ONE
02345 2345 5 0,0001 0 TS Q ; restore return address
02346 2346 0 0,0000 0 RET URN

;----------------- ---------------------------------------------------------
; WL_TIME3task - T 3 TIMEOUT
;
; Perform WAITLIST activities when TIME3 times-out. Called by the
; T3 interrupt han dler.
;----------------- ---------------------------------------------------------

WL_TIME3task EQU *
02347 2347 3 0,0001 0 XCH Q
02350 2350 5 0,0105 0 TS WL_T3_saveQ ; save return address
02351 2351 3 0,0015 0 XCH BANK
02352 2352 5 0,0106 0 TS WL_T3_oldBank ; save current bank

; Execute all time d-out tasks.

02353 2353 0 1,2362 1 TCR WL_runTasks

; Set up TIME3 to overflow at the next task's time-out.


; Adjust the time- outs for all remaining tasks.

02354 2354 0 1,2417 1 TCR WL_schedTask

02355 2355 3 0,0106 0 XCH WL_T3_oldBank


02356 2356 5 0,0015 0 TS BANK ; restore previous bank
02357 2357 3 0,0105 0 XCH WL_T3_saveQ
02360 2360 5 0,0001 0 TS Q ; restore return address
02361 2361 0 0,0000 0 RET URN

;----------------- ---------------------------------------------------------
; WL_runTasks - RU N TIMED-OUT TASK(S)
;
; Runs all tasks t imed-out on WL_taskList. Tasks are removed
; from the list be fore they are run.
;----------------- ---------------------------------------------------------

WL_runTasks EQU *
02362 2362 3 0,0001 0 XCH Q
02363 2363 5 0,0113 1 TS WL_RT_saveQ ; save return address
; loop, checking t he task on the front of the list. If it is
; timed out, remov e it from the list and run it.

WL_RT_loop EQU *
02364 2364 3 1,2050 0 CAF ZERO
02365 2365 2 1,2175 1 IND EX WL_tskLstStart
02366 2366 6 0,0000 1 AD TSKTIME
02367 2367 1 0,0000 0 CCS A ; task timed out?
02370 2370 0 1,2414 1 TC WL_RT_done ; >0 no, so we are done
02371 2371 0 1,2373 1 TC *+2 ; +0
02372 2372 0 1,2373 1 TC *+1 ; <0

; This task has ti med out, so run it.

02373 2373 0 1,2565 0 TCR WL_remove ; remove task from list


02374 2374 5 0,0114 0 TS WL_RT_runAddr ; save 14-bit address of task to run

; The task address is always 14-bit, so check whether the address falls
; within erasable or fixed-fixed memory. If so, use it as-is; otherwise,
; set the bank reg ister and change the address to 12-bit.

02375 2375 4 0,0000 0 COM ; -(14bitAddr)+%6000


02376 2376 6 1,2102 0 AD bankAddr
02377 2377 1 0,0000 0 CCS A ; task is bank addressed?
02400 2400 0 1,2411 1 TC WL_RT_runIt ; >0 no, just run it, as is
02401 2401 0 1,2403 1 TC *+2 ; +0 yes
02402 2402 0 1,2403 1 TC *+1 ; <0 yes

02403 2403 3 1,2050 0 CAF ZERO


02404 2404 6 0,0114 0 AD WL_RT_runAddr
02405 2405 5 0,0015 0 TS BANK ; set the bank

02406 2406 7 1,2103 0 MAS K lowAddr ; get lowest 10-bits of address


02407 2407 6 1,2102 0 AD bankAddr ; set bits 11,12 for fixed-switchable
02410 2410 5 0,0114 0 TS WL_RT_runAddr

WL_RT_runIt EQU *
02411 2411 2 0,0114 1 IND EX WL_RT_runAddr ; apply indirect address to next instr.
02412 2412 0 0,0000 1 TC 0 ; run the task

TASKOVER EQU * ; task returns here


02413 2413 0 1,2364 1 TC WL_RT_loop ; check next task on list

WL_RT_done EQU *
02414 2414 3 0,0113 1 XCH WL_RT_saveQ
02415 2415 5 0,0001 0 TS Q ; restore return address
02416 2416 0 0,0000 0 RET URN

;----------------- ---------------------------------------------------------
; WL_schedTask - S CHEDULE NEXT TASK
;
; Schedule task on the front of list for the next time-out. Adjust the
; time-out for all other tasks on the list, so they contain the remaining
; time after the n ext timeout.
;----------------- ---------------------------------------------------------

WL_schedTask EQU *
02417 2417 3 0,0001 0 XCH Q
02420 2420 5 0,0107 1 TS WL_ST_saveQ ; save return address

02421 2421 3 1,2050 0 CAF ZERO


02422 2422 2 1,2175 1 IND EX WL_tskLstStart
02423 2423 6 0,0001 0 AD TSKADDR
02424 2424 1 0,0000 0 CCS A ; task scheduled?
02425 2425 0 1,2427 1 TC *+2 ; >0 yes
02426 2426 0 1,2466 1 TC WL_ST_noTask ; +0 no, so we are done

02427 2427 3 1,2050 0 CAF ZERO


02430 2430 2 1,2175 1 IND EX WL_tskLstStart
02431 2431 6 0,0000 1 AD TSKTIME
02432 2432 5 0,0111 0 TS WL_ST_newTime ; save the new task's time-out

; Iterate through all tasks on the list. Subtract the time-out time
; from each task. (The 1st task on the list will now have a time-out
; of zero)

02433 2433 3 1,2175 0 CAF WL_tskLstStart ; set pointer to front of list


02434 2434 5 0,0110 1 TS WL_ST_taskPtr

02435 2435 3 1,2177 1 CAF WL_numTasks ; loop for number of tasks


WL_ST_loop EQU *
02436 2436 5 0,0112 0 TS WL_ST_loopCnt

02437 2437 3 1,2050 0 CAF ZERO


02440 2440 2 0,0110 0 IND EX WL_ST_taskPtr
02441 2441 6 0,0001 0 AD TSKADDR
02442 2442 1 0,0000 0 CCS A ; end of list?
02443 2443 0 1,2445 0 TC *+2 ; >0 no, so keep going
02444 2444 0 1,2461 0 TC WL_ST_setT3 ; +0 yes, set TIME3

02445 2445 3 1,2050 0 CAF ZERO


02446 2446 2 0,0110 0 IND EX WL_ST_taskPtr
02447 2447 6 0,0000 1 AD TSKTIME
02450 2450 2 0,0000 1 EXT END
02451 2451 6 0,0111 0 SU WL_ST_newTime ; time-out = time-out - newtime
02452 2452 2 0,0110 0 IND EX WL_ST_taskPtr
02453 2453 5 0,0000 1 TS TSKTIME

02454 2454 3 0,0110 1 XCH WL_ST_taskPtr ; bump task pointer back 1 record
02455 2455 6 1,2174 1 AD WL_taskRecSize
02456 2456 5 0,0110 1 TS WL_ST_taskPtr

02457 2457 1 0,0112 1 CCS WL_ST_loopCnt ; done fixing the times?


02460 2460 0 1,2436 1 TC WL_ST_loop ; not yet

; Set TIME3 to ove rflow at the time-out of the task on the front
; of the list: TIM E3 = %37777 - WL_ST_newTime + 1

WL_ST_setT3 EQU *
02461 2461 4 0,0111 1 CS WL_ST_newTime
02462 2462 6 1,2201 0 AD WL_maxVal
02463 2463 6 1,2051 1 AD ONE
02464 2464 5 0,0037 0 TS TIME3 ; overflow at new time-out time
02465 2465 0 1,2470 0 TC WL_ST_done

WL_ST_noTask EQU *
02466 2466 3 1,2203 1 CAF WL_maxTimeOut
02467 2467 5 0,0037 0 TS TIME3 ; nothing scheduled, reset the clock

WL_ST_done EQU *
02470 2470 3 0,0107 1 XCH WL_ST_saveQ
02471 2471 5 0,0001 0 TS Q ; restore return address
02472 2472 0 0,0000 0 RET URN

;----------------- ---------------------------------------------------------
; WL_insert - INSE RT TASK INTO SORTED LIST
;
; Insert a task re cord into the sorted list. Use 'WL_IS_newTime' and
; 'WL_IS_newAddr' to set the fields of record to be inserted.
; Performs an inse rtion sort, with the records sorted by time.
; Lowest times are at the front of the list. If several records
; have the same ti me, the records inserted first will appear first
; in the list. NIL records have a time of NOTASK and a address
; of positive zero .
;----------------- ---------------------------------------------------------

WL_insert EQU *
02473 2473 3 0,0001 0 XCH Q
02474 2474 5 0,0124 0 TS WL_IS_saveQ ; save return address

02475 2475 3 1,2176 0 CAF WL_tskLstEnd ; set pointer to back of list


02476 2476 5 0,0125 1 TS WL_IS_taskPtr

02477 2477 2 0,0000 1 EXT END


02500 2500 6 1,2174 1 SU WL_taskRecSize ; set pointer to rec in front of it
02501 2501 5 0,0126 1 TS WL_IS_taskPtr2

02502 2502 3 1,2050 0 CAF ZERO


02503 2503 2 0,0125 0 IND EX WL_IS_taskPtr
02504 2504 6 0,0001 0 AD TSKADDR
02505 2505 1 0,0000 0 CCS A ; list full?
02506 2506 0 1,2562 1 TC WL_IS_done ; >0 yes

; Work from the ba ck of the list to the front, pushing each record
; to the back unti l the insertion point is found.

02507 2507 3 1,2200 1 CAF WL_numTasks1 ; loop for number of tasks minus 1
WL_IS_loop EQU *
02510 2510 5 0,0127 0 TS WL_IS_loopCnt

02511 2511 3 1,2050 0 CAF ZERO


02512 2512 2 0,0126 0 IND EX WL_IS_taskPtr2
02513 2513 6 0,0001 0 AD TSKADDR
02514 2514 1 0,0000 0 CCS A ; previous record is NIL?
02515 2515 0 1,2517 0 TC *+2 ; no, so check it
02516 2516 0 1,2541 0 TC WL_IS_bumpPtr ; yes, so skip to next record

; Is this the inse rtion point?

02517 2517 4 0,0122 1 CS WL_IS_newTime


02520 2520 2 0,0126 0 IND EX WL_IS_taskPtr2
02521 2521 6 0,0000 1 AD TSKTIME
02522 2522 1 0,0000 0 CCS A ; found insertion point?
02523 2523 0 1,2527 0 TC *+4 ; >0 no, keep checking
02524 2524 0 1,2552 1 TC WL_IS_insRec ; +0 yes
02525 2525 0 1,2552 1 TC WL_IS_insRec ; <0 yes
02526 2526 0 1,2552 1 TC WL_IS_insRec ; -0 yes

; No, bump the rec ord toward the back of the list.

02527 2527 3 1,2050 0 CAF ZERO


02530 2530 2 0,0126 0 IND EX WL_IS_taskPtr2
02531 2531 6 0,0000 1 AD TSKTIME
02532 2532 2 0,0125 0 IND EX WL_IS_taskPtr
02533 2533 5 0,0000 1 TS TSKTIME ; copy time field

02534 2534 3 1,2050 0 CAF ZERO


02535 2535 2 0,0126 0 IND EX WL_IS_taskPtr2
02536 2536 6 0,0001 0 AD TSKADDR
02537 2537 2 0,0125 0 IND EX WL_IS_taskPtr
02540 2540 5 0,0001 0 TS TSKADDR ; copy address field

WL_IS_bumpPtr EQU *
02541 2541 3 0,0125 1 XCH WL_IS_taskPtr ; bump task pointer forward 1 record
02542 2542 2 0,0000 1 EXT END
02543 2543 6 1,2174 1 SU WL_taskRecSize
02544 2544 5 0,0125 1 TS WL_IS_taskPtr

02545 2545 2 0,0000 1 EXT END


02546 2546 6 1,2174 1 SU WL_taskRecSize ; set pointer to record in front of it
02547 2547 5 0,0126 1 TS WL_IS_taskPtr2

02550 2550 1 0,0127 1 CCS WL_IS_loopCnt ; done bumping tasks backward?


02551 2551 0 1,2510 1 TC WL_IS_loop ; not yet

; Insert new recor d.

WL_IS_insRec EQU *
02552 2552 3 1,2050 0 CAF ZERO
02553 2553 6 0,0122 0 AD WL_IS_newTime
02554 2554 2 0,0125 0 IND EX WL_IS_taskPtr
02555 2555 5 0,0000 1 TS TSKTIME ; set time field

02556 2556 3 1,2050 0 CAF ZERO


02557 2557 6 0,0123 1 AD WL_IS_newAddr
02560 2560 2 0,0125 0 IND EX WL_IS_taskPtr
02561 2561 5 0,0001 0 TS TSKADDR ; set address field

WL_IS_done EQU *
02562 2562 3 0,0124 0 XCH WL_IS_saveQ
02563 2563 5 0,0001 0 TS Q ; restore return address
02564 2564 0 0,0000 0 RET URN

;----------------- ---------------------------------------------------------
; WL_remove - REMO VE TASK FROM FRONT OF LIST
;
; Returns the addr ess of the task in register A. If the list is
; empty, it return s zero in A. If a task is removed from the list,
; the remaining ta sks are moved up to the front.
;----------------- ---------------------------------------------------------

WL_remove EQU *
02565 2565 3 0,0001 0 XCH Q
02566 2566 5 0,0115 1 TS WL_RM_saveQ ; save return address

02567 2567 3 1,2175 0 CAF WL_tskLstStart ; set pointer to front of list


02570 2570 5 0,0116 1 TS WL_RM_taskPtr

02571 2571 6 1,2174 1 AD WL_taskRecSize ; set pointer to next rec behind it


02572 2572 5 0,0117 0 TS WL_RM_taskPtr2

; Save the address of record at the front of the list.

02573 2573 3 1,2050 0 CAF ZERO


02574 2574 2 0,0116 0 IND EX WL_RM_taskPtr
02575 2575 6 0,0001 0 AD TSKADDR
02576 2576 5 0,0121 0 TS WL_RM_retval ; get address of 1st task

02577 2577 1 0,0000 0 CCS A ; list empty?


02600 2600 0 1,2602 1 TC *+2 ; >0, no
02601 2601 0 1,2636 0 TC WL_RM_done ; +0, yes, so exit

; Loop through the remaining records in the task list and


; bubble them up t o the front.

02602 2602 3 1,2200 1 CAF WL_numTasks1 ; loop for number of tasks minus 1
WL_RM_loop EQU *
02603 2603 5 0,0120 1 TS WL_RM_loopCnt
02604 2604 3 1,2050 0 CAF ZERO
02605 2605 2 0,0117 1 IND EX WL_RM_taskPtr2
02606 2606 6 0,0000 1 AD TSKTIME
02607 2607 2 0,0116 0 IND EX WL_RM_taskPtr
02610 2610 5 0,0000 1 TS TSKTIME ; copy time field

02611 2611 3 1,2050 0 CAF ZERO


02612 2612 2 0,0117 1 IND EX WL_RM_taskPtr2
02613 2613 6 0,0001 0 AD TSKADDR
02614 2614 2 0,0116 0 IND EX WL_RM_taskPtr
02615 2615 5 0,0001 0 TS TSKADDR ; copy address field

02616 2616 1 0,0000 0 CCS A ; remainder of list empty?


02617 2617 0 1,2621 0 TC *+2 ; >0, no
02620 2620 0 1,2636 0 TC WL_RM_done ; +0, yes, so exit

02621 2621 3 0,0116 1 XCH WL_RM_taskPtr ; bump task pointer back 1 record
02622 2622 6 1,2174 1 AD WL_taskRecSize
02623 2623 5 0,0116 1 TS WL_RM_taskPtr

02624 2624 6 1,2174 1 AD WL_taskRecSize ; set pointer to record behind it


02625 2625 5 0,0117 0 TS WL_RM_taskPtr2

02626 2626 1 0,0120 0 CCS WL_RM_loopCnt ; done bumping tasks upward?


02627 2627 0 1,2603 0 TC WL_RM_loop ; not yet

; Since we removed a record, the last record on the list


; should be NIL.

02630 2630 3 1,2202 0 CAF WL_maxDelay


02631 2631 2 0,0116 0 IND EX WL_RM_taskPtr
02632 2632 5 0,0000 1 TS TSKTIME ; set time field to NIL

02633 2633 3 1,2050 0 CAF ZERO


02634 2634 2 0,0116 0 IND EX WL_RM_taskPtr
02635 2635 5 0,0001 0 TS TSKADDR ; set address field to NIL

WL_RM_done EQU *
02636 2636 3 0,0115 1 XCH WL_RM_saveQ
02637 2637 5 0,0001 0 TS Q ; restore return address
02640 2640 3 0,0121 0 XCH WL_RM_retval ; return task address in A
02641 2641 0 0,0000 0 RET URN
INC L exec_f.asm ; EXEC
;================= =========================================================
; EXEC (file:exec_ f.asm)
;
; Version: 1.0
; Author: John P ultorak
; Date: 04/26/ 2002
;
; PURPOSE:
; Constants and so urce code for EXEC.
;
; Non-preemptive m ultitasking routines, originally implemented by J. H.
; Laning, Jr. for AGC3 and later adapted for AGC4. Briefly discussed in
; R-393, which giv es some of the software interfaces into the
; multitasking. Th is is my own recreation, and it only includes the job
; scheduling. The original EXEC also includes memory management for the
; eraseable memory ; this is not reproduced here.
;
; Overview: schedu led elements are called 'jobs'. Up to 7 jobs can be
; concurrently sch eduled. An 8th 'dummy' job is always scheduled. Each
; job has an assig ned priority (1-n, where 1 is the lowest priority).
; The highest prio rity job always executes. When that job terminates,
; the next highest priority job is selected for execution. If several
; jobs have the sa me priority, they are executed round-robin.
;
; A job is schedul ed for execution by calling 'NOVAC' and
; furnishing the j ob priority and starting address.
; L XCH JOB_PRIORITY
; L+1 TC NOVAC
; L+2 DS JOB_ADDRESS
; L+3 ... execution resumes here
;
; JOB_PRIORITY = a positive integer from %3 - %37776 where a higher number
; indicates hig her priority. Priorities below 3 are reserved for
; internal EXEC use: 0=no job, 1=sleeping job, 2=dummy job.
; Priority %377 77 is also reserved for woken jobs.
; JOB_ADDRESS = st arting address of the job.
;
; **** WARNING *** * If NOVAC is not being called from an interrupt, be sure to
; inhibit interrup ts before calling it to protect the integrity of the list.
;
; When a new job i s added, the new job's record (core set) is
; initialized with a copy of the current job's record (MPAC and other
; parameters), exc ept for the new job priority and address, which are
; set by the 'add job' routine. Therefore, data can be stored into
; MPAC prior to st arting a new job as a method of passing data into
; the new job.
;
; Jobs terminate t hemselves by jumping to ENDOFJOB. This removes them
; from the EXEC sc heduler:
; TC ENDOFJOB
;
; Jobs can suspend themselves (yield to a higher priority job) by
; executing the fo llowing sequence. If there is no other job of
; higher priority, executing of the yielded job resumes at L+2
; L CCS newJob
; L+1 TC CHANG1
; L+2 ... execution resumes here
;
; If there is no o ther job of equal or higher priority, the branch is
; not taken.
;
; Jobs can put the mselves to sleep by calling JOBSLEEP. The address
; where execution of the sleeping job should resume must be in register
; A before calling JOBSLEEP. The job will remain sleeping until JOBWAKE
; is called:
;
; L CAF WAKECADR
; L+1 TC JOBSLEEP
; (does not return from JOBSLEEP)
;
; Sleeping jobs ar e awakened by calling JOBWAKE. The address where
; execution of the sleeping job should resume must be in register A.
; JOBWAKE returns to the address after the call and execution continues
; for the calling job. The job that was sleeping will now be the next
; job to execute.
;
; L CAF WAKECADR
; L+1 TC JOBWAKE
; L+2 ... execution continues here
;
;================= =========================================================

02642 2642 37777 1 EX_WAKE_PRIO DS %37777 ; waking job priority (highest)


02643 2643 00002 0 EX_DUMMY_PRIO DS %00002 ; dummy job priority (lowest runnable)
02644 2644 00001 0 EX_SLEEP_PRIO DS %00001 ; sleeping job; must be < dummy

02645 2645 00130 0 EX_jobCurStart DS EX_currentJob ; starting address for current job

02646 2646 00015 0 EX_jobRecSize DS JRECSZ ; size of a job record (words)


02647 2647 00300 1 EX_jobLstStart DS EX_jobList ; starting address for jobList
02650 2650 00307 0 EX_jobLstEnd DS MAXJOBS+EX_jobList
02651 2651 00306 1 EX_jobLstEnd1 DS MAXJOBS-1+EX_jobList
02652 2652 00006 1 EX_numJobs DS MAXJOBS-1 ; init loop counter for all jobs
02653 2653 00005 1 EX_numJobs1 DS MAXJOBS-2 ; init loop counter for all jobs - 1

; enumerated types for setting change flag:


02654 2654 00001 0 EX_changeJob DS CHGJOB ; change job
02655 2655 00000 1 EX_keepJob DS KEEPJOB ; keep job

;----------------- ---------------------------------------------------------
; EX_exec -- EXEC SCHEDULER
;
; Executes the hig hest priority job. Enables interrupts while the job is
; running. Once ca lled, this function never returns.
;----------------- ---------------------------------------------------------

EX_exec EQU * ; entry point

; Add a dummy job (lowest priority) that never terminates.

02656 2656 3 1,2643 1 CAF EX_DUMMY_PRIO ; job priority


02657 2657 0 1,3162 1 TC NOVAC
02660 2660 03510 0 DS dumJob ; 14 bit job address
02661 2661 2 0,0000 0 INH INT ; inhibit RUPTs enab by addJob

; Get the next job to run.

EX_MN_findJob EQU *
02662 2662 0 1,3410 1 TCR EX_remove

; compare priority of current job to priority of next waiting job.


; If next job has same priority as current job, set the newJob
; flag so they wil l be scheduled round-robin.

02663 2663 4 0,0143 0 CS PRIORITY ; get priority of current job

02664 2664 2 1,2647 1 IND EX EX_jobLstStart


02665 2665 2 0,0000 0 IND EX 0
02666 2666 6 0,0143 1 AD PRIORITY ; compare with priority of next job
02667 2667 1 0,0000 0 CCS A ; next job has equal priority?
02670 2670 0 1,2677 0 TC EX_MN_setFlg ; >0 (error!)
02671 2671 0 1,2677 0 TC EX_MN_setFlg ; +0 yes, set flag
02672 2672 0 1,2674 0 TC *+2 ; <0 no, clear flag
02673 2673 0 1,2677 0 TC EX_MN_setFlg ; -0 yes, set flag

02674 2674 3 1,2655 0 CAF EX_keepJob ; clear change flag


02675 2675 5 0,0307 0 TS newJob
02676 2676 0 1,2701 0 TC EX_MN_runJob

EX_MN_setFlg EQU *
02677 2677 3 1,2654 1 CAF EX_changeJob ; set change flag
02700 2700 5 0,0307 0 TS newJob

; Start the job. I nterrupts are reenabled before 'EX_curJobPtr' is


; referenced, but the interrupts can only call 'NOVAC' which does
; not change 'EX_c urJobPtr'.

; The job address is always 14-bit, so check whether the address falls
; within erasable or fixed-fixed memory. If so, use it as-is; otherwise,
; set the bank reg ister and change the address to 12-bit.

EX_MN_runJob EQU *
02701 2701 3 1,2050 0 CAF ZERO
02702 2702 6 0,0140 1 AD LOC
02703 2703 5 0,0333 1 TS EX_MN_runAddr ; save job's 14 bit address

02704 2704 4 0,0000 0 COM


02705 2705 6 1,2102 0 AD bankAddr ; -(14bitAddr)+%6000
02706 2706 1 0,0000 0 CCS A ; job is bank addressed?
02707 2707 0 1,2720 0 TC EX_MN_runIt ; >0 no, just run it, as is
02710 2710 0 1,2712 1 TC *+2 ; +0 yes
02711 2711 0 1,2712 1 TC *+1 ; <0 yes

02712 2712 3 1,2050 0 CAF ZERO


02713 2713 6 0,0333 1 AD EX_MN_runAddr
02714 2714 5 0,0015 0 TS BANK ; set the bank

02715 2715 7 1,2103 0 MAS K lowAddr ; get lowest 10-bits of address


02716 2716 6 1,2102 0 AD bankAddr ; set bits 11,12 for fixed-switchable
02717 2717 5 0,0333 1 TS EX_MN_runAddr

EX_MN_runIt EQU *
02720 2720 2 0,0000 1 REL INT ; enable interrupts
02721 2721 2 0,0333 0 IND EX EX_MN_runAddr ; apply indirect address to next instr.
02722 2722 0 0,0000 1 TC 0 ; run the job

; Job is terminate d. Delete the job record.

ENDOFJOB EQU *
02723 2723 2 0,0000 0 INH INT ; inhibit interrupts
02724 2724 0 1,2662 1 TC EX_MN_findJob ; get next job

; job is sleeping. Keep the job record, but drop the priority so it
; is below the pri ority of the dummy job. This will keep the job
; from running unt il JOBWAKE is called. The address where it should
; resume running w hen awoken is in register A.

JOBSLEEP EQU *
02725 2725 2 0,0000 0 INH INT ; inhibit interrupts
02726 2726 5 0,0140 1 TS LOC ; save restart address

02727 2727 3 1,2644 0 CAF EX_SLEEP_PRIO


02730 2730 5 0,0143 1 TS PRIORITY ; set sleeping priority
02731 2731 5 0,0346 0 TS EX_IS_newPrio

02732 2732 0 1,2757 0 TC EX_MN_mvRec ; finish up

; Job is suspended . Keep the job record, but update the address, so
; execution will r esume at the point after suspension.

CHANG1 EQU *
02733 2733 2 0,0000 0 INH INT ; inhibit interrupts
02734 2734 3 0,0001 0 XCH Q
02735 2735 5 0,0333 1 TS EX_MN_runAddr ; save job's 12 bit restart address

02736 2736 4 0,0000 0 COM


02737 2737 6 1,2102 0 AD bankAddr ; -(12bitAddr)+%6000
02740 2740 1 0,0000 0 CCS A ; job is bank addressed?
02741 2741 0 1,2750 1 TC EX_MN_notBank ; >0 no, just save it, as is
02742 2742 0 1,2744 1 TC *+2 ; +0 yes
02743 2743 0 1,2744 1 TC *+1 ; <0 yes

02744 2744 4 1,2102 1 CS bankAddr ; 12bitAddr - %6000


02745 2745 6 0,0333 1 AD EX_MN_runAddr
02746 2746 6 0,0015 0 AD BANK ; make it a 14-bit address
02747 2747 0 1,2752 0 TC EX_MN_saveIt

EX_MN_notBank EQU *
02750 2750 3 1,2050 0 CAF ZERO
02751 2751 6 0,0333 1 AD EX_MN_runAddr ; get restart address

EX_MN_saveIt EQU *
02752 2752 5 0,0140 1 TS LOC ; save job's new starting address

02753 2753 3 1,2050 0 CAF ZERO


02754 2754 6 0,0144 0 AD JOBPRIOBASE
02755 2755 5 0,0143 1 TS PRIORITY
02756 2756 5 0,0346 0 TS EX_IS_newPrio ; restore job priority to nominal value

; given the priori ty, find the insertion point in the list. Copy
; the current job into the list at the correct insertion point.

EX_MN_mvRec EQU *
02757 2757 0 1,3332 0 TCR EX_findIns
02760 2760 5 0,0352 0 TS EX_IS_jobPtr ; save address of insertion point

; copy all fields in current record to list

02761 2761 3 1,2646 1 XCH EX_jobRecSize


02762 2762 5 0,0334 0 TS EX_MN_field

EX_MN_loop3 EQU *
02763 2763 1 0,0334 1 CCS EX_MN_field ; done?
02764 2764 0 1,2766 1 TC *+2 ; not yet
02765 2765 0 1,3002 0 TC EX_MN_done3 ; yes
02766 2766 5 0,0334 0 TS EX_MN_field

; copy this field to list

02767 2767 3 1,2050 0 CAF ZERO


02770 2770 2 0,0352 1 IND EX EX_IS_jobPtr
02771 2771 6 0,0000 1 AD 0 ; get index to record in list
02772 2772 6 0,0334 0 AD EX_MN_field ; add field displacement
02773 2773 5 0,0335 1 TS EX_MN_findx ; save index to field in list

02774 2774 3 1,2050 0 CAF ZERO


02775 2775 2 0,0334 1 IND EX EX_MN_field
02776 2776 6 0,0130 0 AD EX_currentJob ; get field from current job
02777 2777 2 0,0335 0 IND EX EX_MN_findx
03000 3000 5 0,0130 0 TS EX_currentJob ; copy field to list

03001 3001 0 1,2763 1 TC EX_MN_loop3

EX_MN_done3 EQU *
03002 3002 0 1,2662 1 TC EX_MN_findJob ; get next job

;----------------- ---------------------------------------------------------
; JOBWAKE - wake u p the job identified by address in register A
;
; Search jobList f or a job with address matching the address in A.
; If found, bump t he priority up to the highest level, so the job
; will be the next to run.
;
; This is a 'publi c' function. It assumes that interrupts are already
; disabled before it is called. Disabling interrupts during JOBWAKE
; is necessary to preserve the integrity of the joblist.
;----------------- ---------------------------------------------------------

JOBWAKE EQU *
03003 3003 5 0,0312 1 TS EX_JW_CADR ; save job address
03004 3004 3 0,0001 0 XCH Q
03005 3005 5 0,0310 0 TS EX_JW_saveQ ; save return address

; Search the jobli st for the job to wake (job address matches
; EX_JW_CADR).

03006 3006 3 1,2050 0 CAF ZERO


03007 3007 5 0,0313 0 TS EX_JW_foundit ; clear 'found it' flag

03010 3010 3 1,2651 1 CAF EX_jobLstEnd1 ; set pointer to back of list


03011 3011 5 0,0314 1 TS EX_JW_jobPtr

03012 3012 6 1,2046 1 AD NEG1 ; set pointer to rec in front of it


03013 3013 5 0,0315 0 TS EX_JW_jobPtr2

03014 3014 3 1,2653 0 CAF EX_numJobs1 ; loop for number of jobs minus 1
EX_JW_loop EQU *
03015 3015 5 0,0311 1 TS EX_JW_loopCnt

; if foundit=0, jo b has not been found yet. Keep searching toward


; the front of t he list.
; if foundit=1, th e job has been found and removed from the list.
; push all jobs in front of the removed job one step to the back
; to fill in the gap and to make room at the front of the list
; for the awoken job.

03016 3016 1 0,0313 1 CCS EX_JW_foundit ; already found job to wake?


03017 3017 0 1,3035 1 TC EX_JW_moveRec ; >0, yes

; Is this the job?

03020 3020 4 0,0312 0 CS EX_JW_CADR


03021 3021 2 0,0314 0 IND EX EX_JW_jobPtr
03022 3022 2 0,0000 0 IND EX 0
03023 3023 6 0,0140 1 AD LOC
03024 3024 1 0,0000 0 CCS A ; found job to wake?
03025 3025 0 1,3041 1 TC EX_JW_bumpPtr ; >0, no
03026 3026 0 1,3030 1 TC *+2 ; +0, yes
03027 3027 0 1,3041 1 TC EX_JW_bumpPtr ; <0, no

; found the job to wake.

03030 3030 3 1,2051 1 CAF ONE


03031 3031 5 0,0313 0 TS EX_JW_foundit ; set 'found it' flag

; save record inde x for awoken job

03032 3032 2 0,0314 0 IND EX EX_JW_jobPtr


03033 3033 3 0,0000 1 XCH 0
03034 3034 5 0,0316 0 TS EX_JW_fndIndx ; index for awoken job

; bump prior recor d back

EX_JW_moveRec EQU *
03035 3035 2 0,0315 1 IND EX EX_JW_jobPtr2
03036 3036 3 0,0000 1 XCH 0
03037 3037 2 0,0314 0 IND EX EX_JW_jobPtr
03040 3040 3 0,0000 1 XCH 0

EX_JW_bumpPtr EQU *
03041 3041 3 0,0314 1 XCH EX_JW_jobPtr ; bump job pointer forward 1 record
03042 3042 6 1,2046 1 AD NEG1
03043 3043 5 0,0314 1 TS EX_JW_jobPtr

03044 3044 6 1,2046 1 AD NEG1 ; set pointer to record in front of it


03045 3045 5 0,0315 0 TS EX_JW_jobPtr2

03046 3046 1 0,0311 0 CCS EX_JW_loopCnt ; done bumping jobs backward?


03047 3047 0 1,3015 0 TC EX_JW_loop ; not yet

03050 3050 1 0,0313 1 CCS EX_JW_foundit ; found job to wake?


03051 3051 0 1,3053 1 TC *+2 ; >0, yes
03052 3052 0 1,3056 1 TC EX_JW_done ; no

03053 3053 3 0,0316 0 XCH EX_JW_fndIndx ; put awoken job on front of list
03054 3054 2 1,2647 1 IND EX EX_jobLstStart
03055 3055 5 0,0000 1 TS 0

EX_JW_done EQU *

; Is the awoken jo b at the front of the list?


; (If it was alrea dy there before we started searching, 'foundIt'
; will be false (0 ) so we need to make this test).

03056 3056 4 0,0312 0 CS EX_JW_CADR


03057 3057 2 1,2647 1 IND EX EX_jobLstStart
03060 3060 2 0,0000 0 IND EX 0
03061 3061 6 0,0140 1 AD LOC
03062 3062 1 0,0000 0 CCS A ; woken job at front of list?
03063 3063 0 1,3074 1 TC EX_JW_return ; >0, no
03064 3064 0 1,3066 1 TC *+2 ; +0, yes
03065 3065 0 1,3074 1 TC EX_JW_return ; <0, no

; set awoken prior ity and change job flag

03066 3066 3 1,2642 0 CAF EX_WAKE_PRIO


03067 3067 2 1,2647 1 IND EX EX_jobLstStart
03070 3070 2 0,0000 0 IND EX 0
03071 3071 5 0,0143 1 TS PRIORITY ; set waking priority

03072 3072 3 1,2654 1 CAF EX_changeJob ; set the change flag


03073 3073 5 0,0307 0 TS newJob

EX_JW_return EQU *
03074 3074 0 0,0310 0 TC EX_JW_saveQ ; return
;----------------- ---------------------------------------------------------
; SPVAC - ADD A JO B TO THE JOBLIST
;
; Similar to NOVAC , but used by VERB 37. The job CADR is in register A.
; The job priority is in NEWPRIO. Return to the address in Q.
;
; NOVAC differs fr om SPVAC, because NOVAC has the job CADR at the address
; in Q, and return s to Q+1. Also, in NOVAC the job priority is in A.
;
; This is a 'publi c' function. It can be called from a job
; or from an inter rupt.
;----------------- ---------------------------------------------------------

SPVAC EQU *
03075 3075 5 0,0350 1 TS EX_IS_newLoc ; store new job address
03076 3076 3 0,0001 0 XCH Q
03077 3077 5 0,0317 1 TS EX_AJ_saveQ ; save return address

; add new job to e nd of list

03100 3100 3 1,2050 0 CAF ZERO


03101 3101 6 0,0360 1 AD NEWPRIO
03102 3102 5 0,0346 0 TS EX_IS_newPrio
03103 3103 5 0,0347 1 TS EX_IS_newPrioB ; store new job priority

03104 3104 0 1,3332 0 TCR EX_findIns ; find insertion point in list


03105 3105 5 0,0352 0 TS EX_IS_jobPtr ; save address of insertion point

; Initialize relev ant fields in new job. The remaining fields


; should already b e zeroed.

; Initialize field s for new job record. New job inherits copy of
; MPAC from curren t job, so copy all fields in current job to new
; job in list

03106 3106 3 1,2646 1 XCH EX_jobRecSize


03107 3107 5 0,0323 0 TS EX_AJ_field

EX_SP_loop1 EQU *
03110 3110 1 0,0323 1 CCS EX_AJ_field ; done?
03111 3111 0 1,3113 1 TC *+2 ; not yet
03112 3112 0 1,3127 0 TC EX_SP_done1 ; yes
03113 3113 5 0,0323 0 TS EX_AJ_field

; copy this field to list

03114 3114 3 1,2050 0 CAF ZERO


03115 3115 2 0,0352 1 IND EX EX_IS_jobPtr
03116 3116 6 0,0000 1 AD 0 ; get index to record in list
03117 3117 6 0,0323 0 AD EX_AJ_field ; add field displacement
03120 3120 5 0,0324 1 TS EX_AJ_findx ; save index to field in list

03121 3121 3 1,2050 0 CAF ZERO


03122 3122 2 0,0323 1 IND EX EX_AJ_field
03123 3123 6 0,0130 0 AD EX_currentJob ; get field from current job
03124 3124 2 0,0324 0 IND EX EX_AJ_findx
03125 3125 5 0,0130 0 TS EX_currentJob ; copy field to list

03126 3126 0 1,3110 1 TC EX_SP_loop1

; now, overwrite f ields in the record with the priority


; and location uni que to this job.

EX_SP_done1 EQU *
03127 3127 3 1,2050 0 CAF ZERO
03130 3130 6 0,0346 0 AD EX_IS_newPrio
03131 3131 2 0,0352 1 IND EX EX_IS_jobPtr
03132 3132 2 0,0000 0 IND EX 0
03133 3133 5 0,0143 1 TS PRIORITY ; set priority field

03134 3134 3 1,2050 0 CAF ZERO


03135 3135 6 0,0347 1 AD EX_IS_newPrioB
03136 3136 2 0,0352 1 IND EX EX_IS_jobPtr
03137 3137 2 0,0000 0 IND EX 0
03140 3140 5 0,0144 0 TS JOBPRIOBASE ; set nominal priority field

03141 3141 3 1,2050 0 CAF ZERO


03142 3142 6 0,0350 1 AD EX_IS_newLoc
03143 3143 2 0,0352 1 IND EX EX_IS_jobPtr
03144 3144 2 0,0000 0 IND EX 0
03145 3145 5 0,0140 1 TS LOC ; set address field

; Set changeflag i f priority of new job >= priority of current job


EX_SP_testFlg EQU *
03146 3146 4 0,0143 0 CS PRIORITY ; get -priority of current job

03147 3147 6 0,0321 1 AD EX_AJ_jobPrio ; add positive priority of new job


03150 3150 1 0,0000 0 CCS A ; new job is highest priority?
03151 3151 0 1,3154 1 TC *+3 ; >0, yes
03152 3152 0 1,3154 1 TC *+2 ; +0, yes
03153 3153 0 1,3156 0 TC EX_SP_done2 ; <0, no, current job is higher priority

03154 3154 3 1,2654 1 CAF EX_changeJob ; set the change flag


03155 3155 5 0,0307 0 TS newJob

EX_SP_done2 EQU *
03156 3156 3 0,0317 1 XCH EX_AJ_saveQ
03157 3157 5 0,0001 0 TS Q
03160 3160 0 0,0000 0 RET URN

;----------------- ---------------------------------------------------------
; FINDVAC - not im plemented
;
;----------------- ---------------------------------------------------------

03161 3161 0 0,0001 0 FINDVAC TC Q ; just return

;----------------- ---------------------------------------------------------
; NOVAC - ADD A JO B TO THE JOBLIST
;
; Search jobList f or an empty slot. If found, put the new job in the
; empty slot. If t he new job has the same, or higher, priority than the
; current job, set the change flag to 'CHGJOB' (change jobs at the next
; opportunity).
;
; This is a 'publi c' function. It can be called from a job
; or from an inter rupt.
;----------------- ---------------------------------------------------------

NOVAC EQU *
03162 3162 5 0,0321 1 TS EX_AJ_jobPrio ; save job priority
03163 3163 3 0,0001 0 XCH Q
03164 3164 5 0,0317 1 TS EX_AJ_saveQ ; save return address-1

; add new job to e nd of list

03165 3165 3 1,2050 0 CAF ZERO


03166 3166 6 0,0321 1 AD EX_AJ_jobPrio
03167 3167 5 0,0346 0 TS EX_IS_newPrio
03170 3170 5 0,0347 1 TS EX_IS_newPrioB ; store new job priority

03171 3171 2 0,0317 0 IND EX EX_AJ_saveQ ; indirectly address addJobQ


03172 3172 3 0,0000 1 CAF 0
03173 3173 5 0,0350 1 TS EX_IS_newLoc ; store new job address

03174 3174 0 1,3332 0 TCR EX_findIns ; find insertion point in list


03175 3175 5 0,0352 0 TS EX_IS_jobPtr ; save address of insertion point

; Initialize relev ant fields in new job. The remaining fields


; should already b e zeroed.

; Initialize field s for new job record. New job inherits copy of
; MPAC from curren t job, so copy all fields in current job to new
; job in list

03176 3176 3 1,2646 1 XCH EX_jobRecSize


03177 3177 5 0,0323 0 TS EX_AJ_field

EX_AJ_loop1 EQU *
03200 3200 1 0,0323 1 CCS EX_AJ_field ; done?
03201 3201 0 1,3203 0 TC *+2 ; not yet
03202 3202 0 1,3217 0 TC EX_AJ_done1 ; yes
03203 3203 5 0,0323 0 TS EX_AJ_field

; copy this field to list

03204 3204 3 1,2050 0 CAF ZERO


03205 3205 2 0,0352 1 IND EX EX_IS_jobPtr
03206 3206 6 0,0000 1 AD 0 ; get index to record in list
03207 3207 6 0,0323 0 AD EX_AJ_field ; add field displacement
03210 3210 5 0,0324 1 TS EX_AJ_findx ; save index to field in list

03211 3211 3 1,2050 0 CAF ZERO


03212 3212 2 0,0323 1 IND EX EX_AJ_field
03213 3213 6 0,0130 0 AD EX_currentJob ; get field from current job
03214 3214 2 0,0324 0 IND EX EX_AJ_findx
03215 3215 5 0,0130 0 TS EX_currentJob ; copy field to list

03216 3216 0 1,3200 0 TC EX_AJ_loop1

; now, overwrite f ields in the record with the priority


; and location uni que to this job.

EX_AJ_done1 EQU *
03217 3217 3 1,2050 0 CAF ZERO
03220 3220 6 0,0346 0 AD EX_IS_newPrio
03221 3221 2 0,0352 1 IND EX EX_IS_jobPtr
03222 3222 2 0,0000 0 IND EX 0
03223 3223 5 0,0143 1 TS PRIORITY ; set priority field

03224 3224 3 1,2050 0 CAF ZERO


03225 3225 6 0,0347 1 AD EX_IS_newPrioB
03226 3226 2 0,0352 1 IND EX EX_IS_jobPtr
03227 3227 2 0,0000 0 IND EX 0
03230 3230 5 0,0144 0 TS JOBPRIOBASE ; set nominal priority field

03231 3231 3 1,2050 0 CAF ZERO


03232 3232 6 0,0350 1 AD EX_IS_newLoc
03233 3233 2 0,0352 1 IND EX EX_IS_jobPtr
03234 3234 2 0,0000 0 IND EX 0
03235 3235 5 0,0140 1 TS LOC ; set address field

; Set changeflag i f priority of new job >= priority of current job

EX_AJ_testFlg EQU *
03236 3236 4 0,0143 0 CS PRIORITY ; get -priority of current job

03237 3237 6 0,0321 1 AD EX_AJ_jobPrio ; add positive priority of new job


03240 3240 1 0,0000 0 CCS A ; new job is highest priority?
03241 3241 0 1,3244 0 TC *+3 ; >0, yes
03242 3242 0 1,3244 0 TC *+2 ; +0, yes
03243 3243 0 1,3246 1 TC EX_AJ_done2 ; <0, no, current job is higher priority

03244 3244 3 1,2654 1 CAF EX_changeJob ; set the change flag


03245 3245 5 0,0307 0 TS newJob

EX_AJ_done2 EQU *
03246 3246 3 0,0317 1 XCH EX_AJ_saveQ
03247 3247 6 1,2051 1 AD ONE
03250 3250 5 0,0001 0 TS Q
03251 3251 0 0,0000 0 RET URN

;----------------- ---------------------------------------------------------
; EX_initEX - INIT IALIZE EXEC
;
; Initialize the e raseable memory segment for EXEC. Necessary in
; case the AGC is restarted.
;----------------- ---------------------------------------------------------

EX_initEX EQU *
03252 3252 3 0,0001 0 XCH Q
03253 3253 5 0,0325 0 TS EX_IN_saveQ ; save return address

03254 3254 3 1,2655 0 CAF EX_keepJob ; clear change flag


03255 3255 5 0,0307 0 TS newJob

03256 3256 3 1,2050 0 CAF ZERO


03257 3257 5 0,0143 1 TS PRIORITY ; set current job record to NIL

; Iterate through jobList, initialize each element on the list so it


; points to its ow n job record.

03260 3260 3 1,2647 0 CAF EX_jobLstStart ; init pointer to start of list


03261 3261 5 0,0327 1 TS EX_IN_jobPtr

03262 3262 3 1,2050 0 CAF ZERO


03263 3263 6 1,2646 1 AD EX_jobRecSize
03264 3264 5 0,0330 1 TS EX_IN_recIndex

03265 3265 3 1,2652 1 CAF EX_numJobs ; loop for number of jobs


EX_IN_loop1 EQU *
03266 3266 5 0,0326 0 TS EX_IN_loopCnt

03267 3267 3 0,0330 1 XCH EX_IN_recIndex


03270 3270 2 0,0327 0 IND EX EX_IN_jobPtr
03271 3271 5 0,0000 1 TS 0 ; initialize record index
03272 3272 6 1,2646 1 AD EX_jobRecSize
03273 3273 5 0,0330 1 TS EX_IN_recIndex ; bump index to next record

03274 3274 3 0,0327 1 XCH EX_IN_jobPtr ; bump job pointer back 1 record
03275 3275 6 1,2051 1 AD ONE
03276 3276 5 0,0327 1 TS EX_IN_jobPtr

03277 3277 1 0,0326 1 CCS EX_IN_loopCnt ; done clearing jobList?


03300 3300 0 1,3266 0 TC EX_IN_loop1 ; not yet

; Iterate through job records, initialize each field to zero.

03301 3301 3 1,2647 0 CAF EX_jobLstStart ; init pointer to start of list


03302 3302 5 0,0327 1 TS EX_IN_jobPtr

03303 3303 3 1,2652 1 CAF EX_numJobs ; loop for number of jobs


EX_IN_loop2 EQU *
03304 3304 5 0,0326 0 TS EX_IN_loopCnt

; loop for number of fields in each record

03305 3305 3 1,2646 1 XCH EX_jobRecSize


03306 3306 5 0,0331 0 TS EX_IN_field

EX_IN_loop3 EQU *
03307 3307 1 0,0331 1 CCS EX_IN_field ; done?
03310 3310 0 1,3312 1 TC *+2 ; not yet
03311 3311 0 1,3324 1 TC EX_IN_done ; yes
03312 3312 5 0,0331 0 TS EX_IN_field

; set the field to zero

03313 3313 3 1,2050 0 CAF ZERO


03314 3314 2 0,0327 0 IND EX EX_IN_jobPtr
03315 3315 6 0,0000 1 AD 0 ; get index to record
03316 3316 6 0,0331 0 AD EX_IN_field ; add field displacement
03317 3317 5 0,0332 0 TS EX_IN_findx ; save index to field
03320 3320 3 1,2050 0 CAF ZERO
03321 3321 2 0,0332 1 IND EX EX_IN_findx
03322 3322 5 0,0130 0 TS EX_currentJob ; clear field

03323 3323 0 1,3307 0 TC EX_IN_loop3

; done clearing al l fields in record, so do next record

EX_IN_done EQU *
03324 3324 3 0,0327 1 XCH EX_IN_jobPtr ; bump job pointer back 1 record
03325 3325 6 1,2051 1 AD ONE
03326 3326 5 0,0327 1 TS EX_IN_jobPtr

03327 3327 1 0,0326 1 CCS EX_IN_loopCnt ; done clearing jobList?


03330 3330 0 1,3304 0 TC EX_IN_loop2 ; not yet

03331 3331 0 0,0325 0 TC EX_IN_saveQ ; return

;----------------- ---------------------------------------------------------
; EX_findIns - FIN D INSERTION POINT INTO SORTED LIST
;
; Insert a job rec ord into the sorted list. Use 'EX_IS_newPrio',
; EX_IS_newPrioB a nd 'EX_IS_newLoc' to set the fields of record to
; be inserted.
; Performs an inse rtion sort, with the records sorted by priority.
; Highest priority is at the front of the list. If several records
; have the same pr iority, the records inserted first will appear first
; in the list. NIL records have a priority of zero.
;----------------- ---------------------------------------------------------

EX_findIns EQU *
03332 3332 3 0,0001 0 XCH Q
03333 3333 5 0,0351 0 TS EX_IS_saveQ ; save return address

03334 3334 3 1,2651 1 CAF EX_jobLstEnd1 ; set pointer to back of list


03335 3335 5 0,0352 0 TS EX_IS_jobPtr

03336 3336 6 1,2046 1 AD NEG1 ; set pointer to rec in front of it


03337 3337 5 0,0353 1 TS EX_IS_jobPtr2

03340 3340 3 1,2050 0 CAF ZERO


03341 3341 2 0,0352 1 IND EX EX_IS_jobPtr
03342 3342 2 0,0000 0 IND EX 0
03343 3343 6 0,0143 1 AD PRIORITY ; check last record on list

03344 3344 1 0,0000 0 CCS A ; list full?


03345 3345 0 1,3405 0 TC EX_FI_done ; >0 yes

; Work from the ba ck of the list to the front, pushing each record
; to the back unti l the insertion point is found.

03346 3346 3 1,2653 0 CAF EX_numJobs1 ; loop for number of jobs minus 1
EX_FI_loop EQU *
03347 3347 5 0,0354 0 TS EX_IS_loopCnt

03350 3350 3 1,2050 0 CAF ZERO


03351 3351 2 0,0353 0 IND EX EX_IS_jobPtr2
03352 3352 2 0,0000 0 IND EX 0
03353 3353 6 0,0143 1 AD PRIORITY
03354 3354 1 0,0000 0 CCS A ; previous record is NIL?
03355 3355 0 1,3357 0 TC *+2 ; no, so check it
03356 3356 0 1,3376 0 TC EX_FI_bumpPtr ; yes, so skip to next record

; Is this the inse rtion point?

03357 3357 4 0,0346 1 CS EX_IS_newPrio


03360 3360 2 0,0353 0 IND EX EX_IS_jobPtr2
03361 3361 2 0,0000 0 IND EX 0
03362 3362 6 0,0143 1 AD PRIORITY
03363 3363 1 0,0000 0 CCS A ; found insertion point?
03364 3364 0 1,3405 0 TC EX_FI_insRec ; >0 yes
03365 3365 0 1,3405 0 TC EX_FI_insRec ; +0 yes
03366 3366 0 1,3370 0 TC *+2 ; <0 no, keep checking
03367 3367 0 1,3405 0 TC EX_FI_insRec ; -0 yes

; No, bump the rec ord toward the back of the list.

03370 3370 2 0,0353 0 IND EX EX_IS_jobPtr2


03371 3371 3 0,0000 1 XCH 0
03372 3372 2 0,0352 1 IND EX EX_IS_jobPtr
03373 3373 3 0,0000 1 XCH 0
03374 3374 2 0,0353 0 IND EX EX_IS_jobPtr2
03375 3375 3 0,0000 1 XCH 0

EX_FI_bumpPtr EQU *
03376 3376 3 0,0352 0 XCH EX_IS_jobPtr ; bump job pointer forward 1 record
03377 3377 6 1,2046 1 AD NEG1
03400 3400 5 0,0352 0 TS EX_IS_jobPtr

03401 3401 6 1,2046 1 AD NEG1 ; set pointer to record in front of it


03402 3402 5 0,0353 1 TS EX_IS_jobPtr2

03403 3403 1 0,0354 1 CCS EX_IS_loopCnt ; done bumping jobs backward?


03404 3404 0 1,3347 1 TC EX_FI_loop ; not yet

; New record shoul d be inserted at EX_IS_jobPtr.

EX_FI_insRec EQU *

EX_FI_done EQU *
03405 3405 3 1,2050 0 CAF ZERO
03406 3406 6 0,0352 0 AD EX_IS_jobPtr ; get insertion spot in list
03407 3407 0 0,0351 0 TC EX_IS_saveQ ; return

;----------------- ---------------------------------------------------------
; EX_remove - REMO VE JOB FROM FRONT OF LIST
;
; Remove job from front of list and copy it to the current job. Bubble
; any remaining jo bs toward the front of the list.
;----------------- ---------------------------------------------------------

EX_remove EQU *
03410 3410 3 0,0001 0 XCH Q
03411 3411 5 0,0336 1 TS EX_RM_saveQ ; save return address

03412 3412 3 1,2647 0 CAF EX_jobLstStart ; set pointer to front of list


03413 3413 5 0,0337 0 TS EX_RM_jobPtr

03414 3414 6 1,2051 1 AD ONE ; set pointer to next rec behind it


03415 3415 5 0,0340 0 TS EX_RM_jobPtr2

; Dequeue the reco rd at the top of the list (the next job to run).
; Make it the curr ent job by copying it to the current job record.

03416 3416 3 1,2646 1 XCH EX_jobRecSize


03417 3417 5 0,0344 1 TS EX_RM_field

EX_RM_loop1 EQU *
03420 3420 1 0,0344 0 CCS EX_RM_field ; done?
03421 3421 0 1,3423 1 TC *+2 ; not yet
03422 3422 0 1,3437 1 TC EX_RM_done1 ; yes
03423 3423 5 0,0344 1 TS EX_RM_field

; copy field from list to current job

03424 3424 3 1,2050 0 CAF ZERO


03425 3425 2 0,0337 1 IND EX EX_RM_jobPtr
03426 3426 6 0,0000 1 AD 0 ; get index to record
03427 3427 6 0,0344 1 AD EX_RM_field ; add field displacement
03430 3430 5 0,0345 0 TS EX_RM_findx ; save index to field
03431 3431 3 1,2050 0 CAF ZERO
03432 3432 2 0,0345 1 IND EX EX_RM_findx
03433 3433 6 0,0130 0 AD EX_currentJob ; get field
03434 3434 2 0,0344 0 IND EX EX_RM_field
03435 3435 5 0,0130 0 TS EX_currentJob ; move to current job

03436 3436 0 1,3420 1 TC EX_RM_loop1

; done copying rec ord for current job. Restore the current job to
; its default prio rity, in case it was previously elevated.

EX_RM_done1 EQU *
03437 3437 3 1,2050 0 CAF ZERO
03440 3440 6 0,0144 0 AD JOBPRIOBASE
03441 3441 5 0,0143 1 TS PRIORITY

03442 3442 2 0,0337 1 IND EX EX_RM_jobPtr


03443 3443 3 0,0000 1 XCH 0
03444 3444 5 0,0341 1 TS EX_RM_savePtr ; so we can move it to the end later

; Loop through the remaining records in the job list and


; bubble them up t o the front.

03445 3445 3 1,2653 0 CAF EX_numJobs1 ; loop for number of jobs minus 1
EX_RM_loop2 EQU *
03446 3446 5 0,0342 1 TS EX_RM_loopCnt

03447 3447 2 0,0340 1 IND EX EX_RM_jobPtr2


03450 3450 3 0,0000 1 XCH 0
03451 3451 2 0,0337 1 IND EX EX_RM_jobPtr
03452 3452 5 0,0000 1 TS 0

03453 3453 1 0,0000 0 CCS A ; remainder of list empty?


03454 3454 0 1,3456 0 TC *+2 ; >0, no
03455 3455 0 1,3465 0 TC EX_RM_done2 ; +0, yes, so exit

03456 3456 3 0,0337 0 XCH EX_RM_jobPtr ; bump job pointer back 1 record
03457 3457 6 1,2051 1 AD ONE
03460 3460 5 0,0337 0 TS EX_RM_jobPtr

03461 3461 6 1,2051 1 AD ONE ; set pointer to record behind it


03462 3462 5 0,0340 0 TS EX_RM_jobPtr2

03463 3463 1 0,0342 0 CCS EX_RM_loopCnt ; done bumping jobs upward?


03464 3464 0 1,3446 1 TC EX_RM_loop2 ; not yet

; Since we removed a record, the last record on the list


; should be NIL.

EX_RM_done2 EQU *
03465 3465 3 0,0341 1 XCH EX_RM_savePtr
03466 3466 2 0,0337 1 IND EX EX_RM_jobPtr ; move the index for the top record
03467 3467 5 0,0000 1 TS 0 ; to the bottom of the list

; set all fields i n NIL record to zero

03470 3470 3 1,2646 1 XCH EX_jobRecSize


03471 3471 5 0,0344 1 TS EX_RM_field

EX_RM_loop3 EQU *
03472 3472 1 0,0344 0 CCS EX_RM_field ; done?
03473 3473 0 1,3475 1 TC *+2 ; not yet
03474 3474 0 1,3507 0 TC EX_RM_done3 ; yes
03475 3475 5 0,0344 1 TS EX_RM_field

; set this field t o zero

03476 3476 3 1,2050 0 CAF ZERO


03477 3477 2 0,0337 1 IND EX EX_RM_jobPtr
03500 3500 6 0,0000 1 AD 0 ; get index to record
03501 3501 6 0,0344 1 AD EX_RM_field ; add field displacement
03502 3502 5 0,0345 0 TS EX_RM_findx ; save index to field
03503 3503 3 1,2050 0 CAF ZERO
03504 3504 2 0,0345 1 IND EX EX_RM_findx
03505 3505 5 0,0130 0 TS EX_currentJob ; clear field

03506 3506 0 1,3472 0 TC EX_RM_loop3

EX_RM_done3 EQU *
03507 3507 0 0,0336 1 TC EX_RM_saveQ ; return

;----------------- ---------------------------------------------------------
; DUMMY JOB - runs at the lowest priority and never terminates. Ensures
; that there is al ways at least one job executing. Sleeping jobs are
; given a lower pr iority than the dummy job.
;
; The dummy job co ntrols the computer activity light on the DSKY. When
; the dummy job is running, the light is off. When the dummy job is
; preempted by a h igher priority job, the light is on.
;
; I couldn't find good information on the computer activity light
; in COLOSSUS, so this is my best guess concerning its operation. It
; seems consistent witht the MPEG video of the Apollo 11 DSKY.
;----------------- ---------------------------------------------------------

; entering dummy j ob -- turn off computer activity light

dumJob EQU *
03510 3510 3 1,2050 0 CAF ZERO
03511 3511 6 0,0011 1 AD DSALMOUT
03512 3512 7 1,3525 1 MAS K NOTACTLT
03513 3513 5 0,0011 1 TS DSALMOUT ; turn bit1 off

; runtime loop for dummy job

dumJob1 EQU *
03514 3514 1 0,0307 1 CCS newJob ; check for context switch
03515 3515 0 1,3517 1 TC dumJob2 ; yes
03516 3516 0 1,3514 1 TC dumJob1 ; no

; exiting dummy jo b -- turn on computer activity light

dumJob2 EQU *
03517 3517 4 0,0011 0 CS DSALMOUT ; inclusive OR bit 1 with 1 using
03520 3520 7 1,3525 1 MAS K NOTACTLT ; Demorgan's theorem
03521 3521 4 0,0000 0 COM
03522 3522 5 0,0011 1 TS DSALMOUT

03523 3523 0 1,2733 1 TC CHANG1 ; exit to run higher priority job


03524 3524 0 1,3510 0 TC dumJob ; job done, return here, light off again

03525 3525 77776 1 NOTACTLT DS %77776 ; 1's compliment of bit1 (comp activity l i g h t )
INC L bank_f.asm ; bank intercommunication routines
;================= =========================================================
; BANK INTERCOMMUN ICATION (file:bank_f.asm)
;
; Version: 1.0
; Author: John P ultorak
; Date: 01/19/ 2002
;
; PURPOSE:
; Contains bank in tercommunication routines.
; The source is mi ssing from my (incomplete) listing of COLOSSUS. The
; implementation h ere is inferred from the usage in the COLOSSUS pinball
; routines. Some o f these routines could probably be combined or optimized
; away if I unders tood the pinball software architecture a little better.
;================= =========================================================

;----------------- ---------------------------------------------------------
; DXCHJUMP
; Do a bank jump t o the CADR in register A. After the bank jump, the return
; CADR is in regis ter A. Contents of register Q are destroyed.
; This is my attem pt to implement the block I equivalent for
; DCA MY2CADR
; DXCH Z
;... which is used in some places in COLOSSUS to implement bank jumps. In that
; implementation, MY2CADR has the lower portion of the address in MYCADR and
; the bank portion in MY2CADR+1. DCA loads the lower address into A and the
; bank address int o L. DXCH loads the lower address into Z and the bank portion
; into BB (both ba nk register), thereby doing a bank call. After the call,
; the lower return address is in A and the return bank is in L.
;----------------- ---------------------------------------------------------

DXCHJUMP EQU *
03526 3526 5 0,0576 0 TS ADDRWD1 ; save 14-bit destination address

03527 3527 3 0,0001 0 XCH Q


03530 3530 5 0,0617 1 TS DCRET ; save old return address

03531 3531 3 0,0015 0 XCH BANK


03532 3532 5 0,0616 0 TS DCBANK ; save old bank

; put the 12-bit d estination address in ADDRWD1

03533 3533 4 0,0576 1 CS ADDRWD1 ; -(14bitAddr)+%6000


03534 3534 6 1,2102 0 AD bankAddr
03535 3535 1 0,0000 0 CCS A ; CADR is bank addressed?
03536 3536 0 1,3547 1 TC DODXCHCALL ; >0 no, just run it, as is
03537 3537 0 1,3541 1 TC *+2 ; +0 yes
03540 3540 0 1,3541 1 TC *+1 ; <0 yes

03541 3541 3 1,2050 0 CAF ZERO


03542 3542 6 0,0576 0 AD ADDRWD1
03543 3543 5 0,0015 0 TS BANK ; set the bank

03544 3544 7 1,2103 0 MAS K lowAddr ; get lowest 10-bits of address


03545 3545 6 1,2102 0 AD bankAddr ; set bits 11,12 for fixed-switchable
03546 3546 5 0,0576 0 TS ADDRWD1 ; save 12-bit destination address

; put the 14-bit r eturn CADR into A.

DODXCHCALL EQU *
03547 3547 4 0,0617 0 CS DCRET ; get 12-bit return address
03550 3550 6 1,2102 0 AD bankAddr ; -(12bitAddr)+%6000
03551 3551 1 0,0000 0 CCS A ; return address is bank addressed?
03552 3552 0 1,3561 0 TC DC_NOTBANK ; >0 no, just use it, as is
03553 3553 0 1,3555 1 TC *+2 ; +0 yes
03554 3554 0 1,3555 1 TC *+1 ; <0 yes

03555 3555 4 1,2102 1 CS bankAddr ; 12bitAddr - %6000


03556 3556 6 0,0617 1 AD DCRET
03557 3557 6 0,0616 0 AD DCBANK ; put return CADR in A
03560 3560 0 1,3563 1 TC *+3

DC_NOTBANK EQU *
03561 3561 3 1,2050 0 CAF ZERO
03562 3562 6 0,0617 1 AD DCRET ; put return CADR in A

03563 3563 2 0,0576 1 IND EX ADDRWD1 ; apply indirect address to next instr.
03564 3564 0 0,0000 1 TC 0 ; make the jump

;----------------- ---------------------------------------------------------
; BANKCALL
; Do a bank jump t o the location referenced by the 14-bit address referenced
; in Q. Does not a ffect register A (but assumes A does not contain an
; overflow). Funct ionally identical to POSTJUMP.
; Usage:
; TC BANKCALL ; bank jump to CADR
; DS MYCADR ; the 14-bit address
; returns here if MYCADR calls TC Q.
;
; Inferred from th e AGC Block II COLOSSUS rev 249 assembly listing,
; Oct 28, 1968.
;----------------- ---------------------------------------------------------

BANKCALL EQU *
03565 3565 5 0,0612 1 TS BCA ; save A

03566 3566 2 0,0001 1 IND EX Q ; load the CADR into A


03567 3567 3 0,0000 1 CAF 0
03570 3570 5 0,0576 0 TS ADDRWD1 ; save 14-bit destination address

03571 3571 3 0,0001 0 XCH Q


03572 3572 5 0,0611 1 TS BCRET ; save old return address-1

03573 3573 3 0,0015 0 XCH BANK


03574 3574 5 0,0610 0 TS BCBANK ; save old bank

03575 3575 4 0,0576 1 CS ADDRWD1 ; -(14bitAddr)+%6000


03576 3576 6 1,2102 0 AD bankAddr
03577 3577 1 0,0000 0 CCS A ; CADR is bank addressed?
03600 3600 0 1,3611 1 TC DOBANKCALL ; >0 no, just run it, as is
03601 3601 0 1,3603 1 TC *+2 ; +0 yes
03602 3602 0 1,3603 1 TC *+1 ; <0 yes

03603 3603 3 1,2050 0 CAF ZERO


03604 3604 6 0,0576 0 AD ADDRWD1
03605 3605 5 0,0015 0 TS BANK ; set the bank

03606 3606 7 1,2103 0 MAS K lowAddr ; get lowest 10-bits of address


03607 3607 6 1,2102 0 AD bankAddr ; set bits 11,12 for fixed-switchable
03610 3610 5 0,0576 0 TS ADDRWD1

DOBANKCALL EQU *
03611 3611 3 0,0612 1 XCH BCA ; restore A
03612 3612 2 0,0576 1 IND EX ADDRWD1 ; apply indirect address to next instr.
03613 3613 0 0,0000 1 TC 0 ; make the jump

; Jump returns her e; restore the old bank and return

03614 3614 5 0,0612 1 TS BCA ; save A


03615 3615 3 0,0610 0 XCH BCBANK
03616 3616 5 0,0015 0 TS BANK
03617 3617 3 0,0611 1 XCH BCRET
03620 3620 6 1,2051 1 AD ONE ; skip CADR
03621 3621 5 0,0001 0 TS Q

03622 3622 3 0,0612 1 XCH BCA ; restore A


03623 3623 0 0,0000 0 RET URN

;----------------- ---------------------------------------------------------
; MYBANKCALL
; Functionally ide ntical to BANKCALL. Used for converting the FLASHON/FLASHOFF
; COLOSSUS block I I code to block I. In Block II, the V/N flash is controlled b y
; setting a bit in an I/O channel. In Block I, a bit in the display table must
; be set using _11 DSPIN. Because _11DSPIN is in fixed/switchable memory, but is
; called from fixe d/fixed, a bank call function is needed. The original BANKCAL L
; could not be use d because it is not reentrant and I dont understand its usage
; in COLOSSUS well enough to be certain that FLASHON/FLASHOFF isn't already
; being called som ewhere through BANKCALL.
;----------------- ---------------------------------------------------------

MYBANKCALL EQU *
03624 3624 5 0,0615 0 TS MBCA ; save A

03625 3625 2 0,0001 1 IND EX Q ; load the CADR into A


03626 3626 3 0,0000 1 CAF 0
03627 3627 5 0,0576 0 TS ADDRWD1 ; save 14-bit destination address

03630 3630 3 0,0001 0 XCH Q


03631 3631 6 1,2051 1 AD ONE ; skip CADR
03632 3632 5 0,0614 1 TS MBCRET ; save old return address

03633 3633 3 0,0015 0 XCH BANK


03634 3634 5 0,0613 0 TS MBCBANK ; save old bank

03635 3635 3 1,2050 0 CAF ZERO


03636 3636 6 0,0576 0 AD ADDRWD1
03637 3637 5 0,0015 0 TS BANK ; set the bank

03640 3640 7 1,2103 0 MAS K lowAddr ; get lowest 10-bits of address


03641 3641 6 1,2102 0 AD bankAddr ; set bits 11,12 for fixed-switchable
03642 3642 5 0,0576 0 TS ADDRWD1

03643 3643 3 0,0615 0 XCH MBCA ; restore A


03644 3644 2 0,0576 1 IND EX ADDRWD1 ; apply indirect address to next instr.
03645 3645 0 0,0000 1 TC 0 ; make the jump

; Jump returns her e; restore the old bank and return

03646 3646 5 0,0615 0 TS MBCA ; save A


03647 3647 3 0,0613 0 XCH MBCBANK
03650 3650 5 0,0015 0 TS BANK

03651 3651 3 0,0615 0 XCH MBCA ; restore A


03652 3652 0 0,0614 1 TC MBCRET

;----------------- ---------------------------------------------------------
; POSTJUMP
; Do a bank jump t o the location referenced by the 14-bit address referenced
; in Q. Does not affect register A (but assumes A does not contain an
; overflow). Funct ionally identical to BANKCALL
; Usage:
; TC POSTJUMP ; bank jump to CADR
; DS MYCADR ; the 14-bit address
; returns here if MYCADR calls TC Q.
;
; Inferred from th e AGC Block II COLOSSUS rev 249 assembly listing,
; Oct 28, 1968.
;----------------- ---------------------------------------------------------

POSTJUMP EQU *
03653 3653 5 0,0607 0 TS PJA ; save A

03654 3654 2 0,0001 1 IND EX Q ; load the CADR into A


03655 3655 3 0,0000 1 CAF 0
03656 3656 5 0,0576 0 TS ADDRWD1 ; save 14-bit destination address

03657 3657 3 0,0001 0 XCH Q


03660 3660 5 0,0606 1 TS PJRET ; save old return address-1

03661 3661 3 0,0015 0 XCH BANK


03662 3662 5 0,0605 1 TS PJBANK ; save old bank

03663 3663 4 0,0576 1 CS ADDRWD1 ; -(14bitAddr)+%6000


03664 3664 6 1,2102 0 AD bankAddr
03665 3665 1 0,0000 0 CCS A ; CADR is bank addressed?
03666 3666 0 1,3677 1 TC DOPOSTJUMP ; >0 no, just run it, as is
03667 3667 0 1,3671 1 TC *+2 ; +0 yes
03670 3670 0 1,3671 1 TC *+1 ; <0 yes

03671 3671 3 1,2050 0 CAF ZERO


03672 3672 6 0,0576 0 AD ADDRWD1
03673 3673 5 0,0015 0 TS BANK ; set the bank

03674 3674 7 1,2103 0 MAS K lowAddr ; get lowest 10-bits of address


03675 3675 6 1,2102 0 AD bankAddr ; set bits 11,12 for fixed-switchable
03676 3676 5 0,0576 0 TS ADDRWD1

DOPOSTJUMP EQU *
03677 3677 3 0,0607 0 XCH PJA ; restore A
03700 3700 2 0,0576 1 IND EX ADDRWD1 ; apply indirect address to next instr.
03701 3701 0 0,0000 1 TC 0 ; make the jump

; Jump returns her e; restore the old bank and return

03702 3702 5 0,0607 0 TS PJA ; save A

03703 3703 3 0,0605 1 XCH PJBANK


03704 3704 5 0,0015 0 TS BANK

03705 3705 3 0,0606 1 XCH PJRET


03706 3706 6 1,2051 1 AD ONE ; skip CADR
03707 3707 5 0,0001 0 TS Q

03710 3710 3 0,0607 0 XCH PJA ; restore A


03711 3711 0 0,0000 0 RET URN

;----------------- ---------------------------------------------------------
; BANKJUMP
; Do a bank jump t o the location referenced by the 14-bit address in A.
; Usage:
; CADRSTOR DS MYCADR
;
; CAF CADRSTOR ; load the 14-bit address
; TC BANKJUMP ; bank jump to CADR
; returns here if MYCADR calls TC Q
;
; Inferred from th e AGC Block II COLOSSUS rev 249 assembly listing,
; Oct 28, 1968.
;----------------- ---------------------------------------------------------

BANKJUMP EQU *
03712 3712 5 0,0576 0 TS ADDRWD1 ; save 14-bit destination address

03713 3713 3 0,0001 0 XCH Q


03714 3714 5 0,0604 0 TS BJRET ; save old return address

03715 3715 3 0,0015 0 XCH BANK


03716 3716 5 0,0603 1 TS BJBANK ; save old bank

03717 3717 4 0,0576 1 CS ADDRWD1 ; -(14bitAddr)+%6000


03720 3720 6 1,2102 0 AD bankAddr
03721 3721 1 0,0000 0 CCS A ; CADR is bank addressed?
03722 3722 0 1,3733 0 TC DOBANKJUMP ; >0 no, just run it, as is
03723 3723 0 1,3725 1 TC *+2 ; +0 yes
03724 3724 0 1,3725 1 TC *+1 ; <0 yes

03725 3725 3 1,2050 0 CAF ZERO


03726 3726 6 0,0576 0 AD ADDRWD1
03727 3727 5 0,0015 0 TS BANK ; set the bank

03730 3730 7 1,2103 0 MAS K lowAddr ; get lowest 10-bits of address


03731 3731 6 1,2102 0 AD bankAddr ; set bits 11,12 for fixed-switchable
03732 3732 5 0,0576 0 TS ADDRWD1

DOBANKJUMP EQU *
03733 3733 2 0,0576 1 IND EX ADDRWD1 ; apply indirect address to next instr.
03734 3734 0 0,0000 1 TC 0 ; make the jump

; Jump returns her e; restore the old bank and return

03735 3735 3 0,0603 1 XCH BJBANK


03736 3736 5 0,0015 0 TS BANK

03737 3737 3 0,0604 0 XCH BJRET


03740 3740 5 0,0001 0 TS Q
03741 3741 0 0,0000 0 RET URN

;----------------- ---------------------------------------------------------
; DATACALL
; Retrieve memory contents at location referenced by the 14-bit address in A.
; Usage:
; CADRSTOR DS MYCADR
;
; CAF CADRSTOR ; load the 14-bit address
; TC DATACALL ; return data in A
;
; Inferred from th e AGC Block II COLOSSUS rev 249 assembly listing,
; Oct 28, 1968.
;----------------- ---------------------------------------------------------

DATACALL EQU *
03742 3742 5 0,0576 0 TS ADDRWD1 ; save 14-bit address

03743 3743 3 0,0001 0 XCH Q


03744 3744 5 0,0604 0 TS BJRET ; save old return address

03745 3745 3 0,0015 0 XCH BANK


03746 3746 5 0,0603 1 TS BJBANK ; save old bank

03747 3747 4 0,0576 1 CS ADDRWD1 ; -(14bitAddr)+%6000


03750 3750 6 1,2102 0 AD bankAddr
03751 3751 1 0,0000 0 CCS A ; CADR is bank addressed?
03752 3752 0 1,3763 0 TC DODATACALL ; >0 no, just use it, as is
03753 3753 0 1,3755 0 TC *+2 ; +0 yes
03754 3754 0 1,3755 0 TC *+1 ; <0 yes

03755 3755 3 1,2050 0 CAF ZERO


03756 3756 6 0,0576 0 AD ADDRWD1
03757 3757 5 0,0015 0 TS BANK ; set the bank

03760 3760 7 1,2103 0 MAS K lowAddr ; get lowest 10-bits of address


03761 3761 6 1,2102 0 AD bankAddr ; set bits 11,12 for fixed-switchable
03762 3762 5 0,0576 0 TS ADDRWD1

DODATACALL EQU *
03763 3763 3 1,2050 0 CAF ZERO
03764 3764 2 0,0576 1 IND EX ADDRWD1 ; apply indirect address to next instr.
03765 3765 6 0,0000 1 AD 0 ; load the word

03766 3766 3 0,0603 1 XCH BJBANK ; restore the old bank


03767 3767 5 0,0015 0 TS BANK

03770 3770 3 0,0603 1 XCH BJBANK ; get the word


03771 3771 0 0,0604 0 TC BJRET ; return

INC L T4rupt_f.asm ; T4RUPT handler


;================= =========================================================
; T4RUPT (file:T4r upt_f.asm)
;
; Version: 1.0
; Author: John P ultorak
; Date: 01/09/ 2002
;