CARDIAC Simulation using PC/370 - Part 4
CARDIAC Simulation - Part 1 - Introduction to CARDIAC
CARDIAC Simulation - Part 2 - Primary Logic/Architecture
CARDIAC Simulation - Part 3 - Memory Design / Management
CARDIAC Simulation - Part 4 - CARDIAC Instruction Set Processing
Here's the fourth & final installment of my CARDIAC Simulator project. The program TDCPU.ALC took about a day in total - design/development/testing. It took much longer to formulate this blog post - complete with the 3D stickman & computer hardware - than the actual PC/370 programming.
Above: CARDIAC Simulator Animation - Program Flow in Action.
The animation above demonstrates the program flow along with the cell & internal register updates (i.e. program counter/accumulator) along with display fields for the instruction, reader & output. The asterisk (*) tracks the program counter which, for the purposes of my simulator, I have always defaulted to cell 10. This leaves the first column (under 00) for program variables.
CARDIAC Instruction Set
Opcode Mnemonic Operation
====== ======== ===================================================
0 INP Read a card into memory
1 CLA Clear accumulator and add from memory (load)
2 ADD Add from memory to accumulator
3 TAC Test accumulator and jump if negative
4 SFT Shift accumulator
5 OUT Write memory location to output card
6 STO Store accumulator to memory
7 SUB Subtract memory from accumulator
8 JMP Jump and save PC
9 HRS Halt and reset
====== ======== ===================================================
CARDIAC Operation Table
The <F1> STEP key searches for the proper operational routine for the current Opcode & branches to it. Upon return it loops back, displays the screen & accepts the next keyboard input.
*---------------------------------------------------------------------*
* CARDIAC OPERATIONS/ROUTINES *
*---------------------------------------------------------------------*
OPTAB DS 0F OPERATION TABLE
DC C'0',C'INP',A(@INP) 0-INP-READ INPUT
DC C'1',C'CLA',A(@CLA) 1-CLA-CLEAR ACC FROM MEM
DC C'2',C'ADD',A(@ADD) 2-ADD-TO ACC FROM MEM
DC C'3',C'TAC',A(@TAC) 3-TAC-TEST ACC/JMP NEG
DC C'4',C'SFT',A(@SFT) 4-SFT-SHIFT ACC L/R
DC C'5',C'OUT',A(@OUT) 5-OUT-WRITE MEM TO OUT
DC C'6',C'STO',A(@STO) 6-STO-STORE ACC TO MEM
DC C'7',C'SUB',A(@SUB) 7-SUB-MEM FROM ACC
DC C'8',C'JMP',A(@JMP) 8-JMP-JUMP/SAVE PC99
DC C'9',C'HRS',A(@HRS) 9-HRS-HALT/RESET
DC X'FF' END OF TABLE
*---------------------------------------------------------------------*
Now for the individual instruction routines. Each component performs a very simple function - mostly reading or writing the cell values or updating the program counter & accumulator.
0: INP - Read Card into Memory Location
The INP instruction reads the next value contained in the input card deck. In this case the deck is the DTAB table that contains our data values. As a data record is read the value is placed in the cell specified by the operand. The DTAB pointer (DPTR) is also incremented (for the next read) & the value is presented on the screen in the SCRRDR variable.
***********************************************************************
* 0:INP - READ CARD INTO MEMORY LOCATION *
***********************************************************************
DC F'0' RETURN ADDRESS SAVE AREA
@INP EQU *
ST 6,*-4 SAVE RETURN ADDRESS
*
L 8,DPTR LOAD DECK POINTER
CLI 0(8),X'FF' END OF TABLE??
BE @INPX YES - EXIT ROUTING
*
LA 5,62 LOAD SCRRDR COUNT
LA 6,SCRRDR+61 LOAD SCRRDR OFFSET
@INPRDR EQU *
MVC 4(1,6),0(6) BUMP READER ENTRIES
SH 6,=H'1' DECREMENT SCRRDR PTR
BCT 5,@INPRDR LOOP UNTIL COMPLETE
*
MVC SCRRDR(3),0(8) MOVE DECK ENTRY
C 8,=A(DTAB) FIRST DECK ENTRY?
BE *+8 YES - BYPASS COMMA
MVI SCRRDR+3,C',' MOVE COMMA DELIMITER
*
LA 7,MEMTAB LOAD MEMORY ADDRESS
PACK DUBB,XOPER MOVE TARGET OPERAND
CVB 3,DUBB CONVERT TO BINARY
MH 3,=H'8' MULTIPLY BY LENGTH
AR 7,3 ADJUST MEMORY PTR
MVC 0(3,7),0(8) MOVE DECK ENTRY TO CELL
*
LA 8,6(,8) BUMP DECK POINTER
ST 8,DPTR STORE DECK POINTER
@INPX EQU *
L 6,@INP-4 RESTORE LINK REGISTER
BR 6 BRANCH ON LINK REGISTER
***********************************************************************
1: CLA - Clear Accumulator & Add From Memory
The CLA instruction clears the accumulator & effectively replaces it with the value contained in the cell specified by the operand. Since there is only one accumulator it's value may need to be stored, cleared & reloaded on multiple occasions. That is the nature of this simple CPU.
***********************************************************************
* 1:CLA - CLEAR ACCUMULATOR & ADD FROM MEMORY *
***********************************************************************
DC F'0' RETURN ADDRESS SAVE AREA
@CLA EQU *
ST 6,*-4 SAVE RETURN ADDRESS
*
LA 7,MEMTAB LOAD MEMORY ADDRESS
PACK DUBB,XOPER MOVE TARGET OPERAND
CVB 3,DUBB CONVERT TO BINARY
MH 3,=H'8' MULTIPLY BY LENGTH
AR 7,3 ADJUST MEMORY PTR
*
ZAP XACC,=P'0' CLEAR ACCUM PACKED
MVC XACCU(4),=CL4'0000' CLEAR ACCUM CHAR
CLI 0(7),C'-' NEGATIVE SIGN?
BE @CLANEG YES - HANDLE NEGATIVE
@CLAPOS EQU *
MVC XACCU+1(3),0(7) MOVE CELL DATA
PACK XACC,XACCU PACK ACCUMULATOR
B @CLAX EXIT ROUTINE PLEASE
@CLANEG EQU *
MVC XACCU+2(2),1(7) MOVE CELL DATA
PACK XACC,XACCU PACK ACCUMULATOR
NI XACC+2,X'F0' TURN OFF SIGN BITS
OI XACC+2,X'0D' SET NEGATIVE SIGN
MVI XACCU,C'-' SET NEGATIVE CHAR
@CLAX EQU *
L 6,@CLA-4 RESTORE LINK REGISTER
BR 6 BRANCH ON LINK REGISTER
***********************************************************************
2: ADD - Add From Memory to Accumulator
The ADD instruction adds the value contained in the cell specified by the operand to the accumulator.
***********************************************************************
* 2:ADD - ADD FROM MEMORY TO ACCUMULATOR *
***********************************************************************
DC F'0' RETURN ADDRESS SAVE AREA
@ADD EQU *
ST 6,*-4 SAVE RETURN ADDRESS
*
LA 7,MEMTAB LOAD MEMORY ADDRESS
PACK DUBB,XOPER MOVE TARGET OPERAND
CVB 3,DUBB CONVERT TO BINARY
MH 3,=H'8' MULTIPLY BY LENGTH
AR 7,3 ADJUST MEMORY PTR
*
CLI 0(7),C'-' NEGATIVE SIGN?
BE @ADDNEG YES - HANDLE NEGATIVE
@ADDPOS EQU *
PACK WORK,0(3,7) PACK CELL VALUE
B @ADDACC ADD TO ACCUMULATOR
@ADDNEG EQU *
PACK WORK,1(2,7) PACK CELL VALUE
NI WORK+1,X'F0' TURN OFF SIGN BITS
OI WORK+1,X'0D' SET NEGATIVE SIGN
@ADDACC EQU *
AP XACC,WORK ADD TO ACCUMULATOR
*
UNPK XACCU,XACC UNPACK ACCUMULATOR
OI XACCU+3,X'F0' TURN ON ZONE BITS
CP XACC,=P'0' ACCUMULATOR NEGATIVE?
BNL @ADDX NO - EXIT ROUTINE
MVI XACCU,C'-' SET NEGATIVE CHAR
@ADDX EQU *
L 6,@ADD-4 RESTORE LINK REGISTER
BR 6 BRANCH ON LINK REGISTER
***********************************************************************
3: TAC - Test Accumulator - Jump If Negative
The TAC instruction performs a jump if the accumulator is negative. The program counter is set to the value of the instruction operand. Unlike the JMP instruction a return address is not formatted & placed in cell 99. This is helpful when reading input & ending with a -1 value.
***********************************************************************
* 3:TAC - TEST ACCUMULATOR - JUMP IF NEGATIVE *
***********************************************************************
DC F'0' RETURN ADDRESS SAVE AREA
@TAC EQU *
ST 6,*-4 SAVE RETURN ADDRESS
*
CP XACC,=P'0' ACCUMULATOR NEGATIVE?
BNL @TACX NO - EXIT ROUTINE
*
ZAP ZPC,XPC SAVE PRIOR INSTRUCTION
PACK XPC,XOPER SET JUMP ADDRESS
UNPK XPCU,XPC UNPACK PROGRAM COUNTER
OI XPCU+2,X'F0' TURN ON ZONE BITS
@TACX EQU *
L 6,@TAC-4 RESTORE LINK REGISTER
BR 6 BRANCH ON LINK REGISTER
***********************************************************************
4: SFT - Shift Accumulator (Left/Right)
The SFT instruction is seldom used with very little, if any, examples in the CARDIAC documentation. The 2-digit operand is split in two and represents both a shift to the left & a shift to the right. Since we are working with 3-digit integers any shift value > 3 (in either direction) simply sets the field to zero. It's difficult to have a sensible shift with so few integers but here it is anyway. Bytes are moved in a simplistic fashion within a loop for each shift value. No need to over engineer on this one.
***********************************************************************
* 4:SFT - SHIFT ACCUMULATOR (LEFT/RIGHT) *
***********************************************************************
DC F'0' RETURN ADDRESS SAVE AREA
@SFT EQU *
ST 6,*-4 SAVE RETURN ADDRESS
*
MVI WSIGN,C' ' INIT NEGATIVE SIGN
CP XACC,=P'0' ACCUMULATOR NEGATIVE?
BNL *+8 NO - CONTINUE PLEASE
MVI WSIGN,C'-' YES - SET NEGATIVE SIGN
*
PACK DUBB,XOPER(1) LOAD LEFT SHIFT VALUE
CP DUBB,=P'0' LEFT SHIFT = ZERO?
BE @SFTR YES - BYPASS LEFT SHIFT
CP DUBB,=P'4' SHIFT LESS THAN 4??
BL *+20 YES - PERFORM LEFT SHIFT
MVC XACCU,=CL4'0000' CLEAR ACCUMULATOR CHAR
ZAP XACC,=P'0' CLEAR ACCUMULATOR PACK
B @SFTX EXIT ROUTINE PLEASE
*
CLI XACCU,C'-' NEGATIVE SIGN?
BNE *+8 NO - CONTINUE PLEASE
MVI XACCU,C'0' YES - SET TO ZERO
*
CVB 4,DUBB CONVERT SHIFT TO BINARY
@SFTLM EQU *
MVC XACCU(1),XACCU+1 SHIFT LEFT PART 1
MVC XACCU+1(1),XACCU+2 SHIFT LEFT PART 2
MVC XACCU+2(1),XACCU+3 SHIFT LEFT PART 3
MVI XACCU+3,C'0' SHIFT LEFT PART 4
BCT 4,@SFTLM LOOP UNTIL COMPLETE
@SFTR EQU *
PACK DUBB,XOPER+1(1) LOAD RIGHT SHIFT VALUE
CP DUBB,=P'0' RIGHT SHIFT = ZERO?
BE @SFTACC YES - BYPASS RIGHT SHIFT
CP DUBB,=P'4' SHIFT LESS THAN 4??
BL *+20 YES - PERFORM RIGHT SHIFT
MVC XACCU,=CL4'0000' CLEAR ACCUMULATOR CHAR
ZAP XACC,=P'0' CLEAR ACCUMULATOR PACK
B @SFTX EXIT ROUTINE PLEASE
*
CLI XACCU,C'-' NEGATIVE SIGN?
BNE *+8 NO - CONTINUE PLEASE
MVI XACCU,C'0' YES - SET TO ZERO
*
CVB 4,DUBB CONVERT SHIFT TO BINARY
@SFTRM EQU *
MVC XACCU+3(1),XACCU+2 SHIFT RIGHT PART 1
MVC XACCU+2(1),XACCU+1 SHIFT RIGHT PART 2
MVC XACCU+1(1),XACCU SHIFT RIGHT PART 3
MVI XACCU,C'0' SHIFT RIGHT PART 4
BCT 4,@SFTRM LOOP UNTIL COMPLETE
@SFTACC EQU *
PACK XACC,XACCU PACK ACCUMULATOR
CLI WSIGN,C'-' ACCUMULATOR NEGATIVE?
BNE @SFTX NO - EXIT ROUTINE PLEASE
*
* PRESERVE NEGATIVE SIGN (DUE TO SPACE LIMITATIONS)
*
MVI XACCU,C'0' SET HIGH ORDER TO ZERO
PACK XACC,XACCU PACK ACCUMULATOR
NI XACC+2,X'F0' TURN OFF SIGN BITS
OI XACC+2,X'0D' SET NEGATIVE SIGN
MVI XACCU,C'-' SET NEGATIVE CHAR
@SFTX EQU *
L 6,@SFT-4 RESTORE LINK REGISTER
BR 6 BRANCH ON LINK REGISTER
***********************************************************************
5: OUT - Write Memory Location to Output
The OUT instruction writes the cell value specified by the operand to the "theoretical" printing device. In this case I have a screen variable called SCROUT that displays the output. The output values push to the right as they are written.
***********************************************************************
* 5:OUT - WRITE MEMORY LOCATION TO OUTPUT *
***********************************************************************
DC F'0' RETURN ADDRESS SAVE AREA
@OUT EQU *
ST 6,*-4 SAVE RETURN ADDRESS
*
LA 5,62 LOAD SCROUT COUNT
LA 6,SCROUT+61 LOAD SCROUT OFFSET
@OUTBMP EQU *
MVC 4(1,6),0(6) BUMP READER ENTRIES
SH 6,=H'1' DECREMENT SCRRDR PTR
BCT 5,@OUTBMP LOOP UNTIL COMPLETE
*
LA 7,MEMTAB LOAD MEMORY ADDRESS
PACK DUBB,XOPER MOVE TARGET OPERAND
CVB 3,DUBB CONVERT TO BINARY
MH 3,=H'8' MULTIPLY BY LENGTH
AR 7,3 ADJUST MEMORY PTR
MVC SCROUT(3),0(7) PRINT CELL DATA
*
CLI SCROUT+4,C' ' FIRST DECK ENTRY?
BE *+8 YES - BYPASS COMMA
MVI SCROUT+3,C',' MOVE COMMA DELIMITER
@OUTX EQU *
L 6,@OUT-4 RESTORE LINK REGISTER
BR 6 BRANCH ON LINK REGISTER
***********************************************************************
6: STO - Store Accumulator to Memory Location
The STO instruction places the current value of the accumulator in the cell specified by the operand.
***********************************************************************
* 6:STO - STORE ACCUMULATOR TO MEMORY LOCATION *
***********************************************************************
DC F'0' RETURN ADDRESS SAVE AREA
@STO EQU *
ST 6,*-4 SAVE RETURN ADDRESS
*
LA 7,MEMTAB LOAD MEMORY ADDRESS
PACK DUBB,XOPER MOVE TARGET OPERAND
CVB 3,DUBB CONVERT TO BINARY
MH 3,=H'8' MULTIPLY BY LENGTH
AR 7,3 ADJUST MEMORY PTR
MVC 0(3,7),XACCU+1 MOVE ACCUMULATOR TO CELL
CLI XACCU,C'-' ACCUMULATOR NEGATIVE?
BNE @STOX NO - EXIT ROUTINE
MVI 0(7),C'-' YES - SET NEGATIVE SIGN
@STOX EQU *
L 6,@STO-4 RESTORE LINK REGISTER
BR 6 BRANCH ON LINK REGISTER
***********************************************************************
7: SUB - Subtract Memory From Accumulator
The SUB instruction subtracts the value contained in the cell specified by the operand from the accumulator.
***********************************************************************
* 7:SUB - SUBTRACT MEMORY FROM ACCUMULATOR *
***********************************************************************
DC F'0' RETURN ADDRESS SAVE AREA
@SUB EQU *
ST 6,*-4 SAVE RETURN ADDRESS
*
LA 7,MEMTAB LOAD MEMORY ADDRESS
PACK DUBB,XOPER MOVE TARGET OPERAND
CVB 3,DUBB CONVERT TO BINARY
MH 3,=H'8' MULTIPLY BY LENGTH
AR 7,3 ADJUST MEMORY PTR
*
CLI 0(7),C'-' NEGATIVE SIGN?
BE @SUBNEG YES - HANDLE NEGATIVE
@SUBPOS EQU *
PACK WORK,0(3,7) PACK CELL VALUE
B @SUBACC SUB FROM ACCUMULATOR
@SUBNEG EQU *
PACK WORK,1(2,7) PACK CELL VALUE
NI WORK+1,X'F0' TURN OFF SIGN BITS
OI WORK+1,X'0D' SET NEGATIVE SIGN
@SUBACC EQU *
SP XACC,WORK SUB FROM ACCUMULATOR
*
UNPK XACCU,XACC UNPACK ACCUMULATOR
OI XACCU+3,X'F0' TURN ON ZONE BITS
CP XACC,=P'0' ACCUMULATOR NEGATIVE?
BNL @SUBX NO - EXIT ROUTINE
MVI XACCU,C'-' SET NEGATIVE CHAR
@SUBX EQU *
L 6,@SUB-4 RESTORE LINK REGISTER
BR 6 BRANCH ON LINK REGISTER
***********************************************************************
8: JMP - Jump & Save Program Counter
The JMP instruction performs two functions. First it takes the address of the next sequential instruction & stores it as a JMP instruction in cell 99. This means it has the prefix of "8" followed by the 2-digit cell address. This is how subroutines can be constructed. Secondly, the instruction then transfers control to the address specified by the operand - this is accomplished simply by updating the program counter.
***********************************************************************
* 8:JMP - JUMP & SAVE PROGRAM COUNTER *
***********************************************************************
DC F'0' RETURN ADDRESS SAVE AREA
@JMP EQU *
ST 6,*-4 SAVE RETURN ADDRESS
*
L 7,MEM99 LOAD CELL 99 ADDRESS
MVI 0(7),C'8' STORE JUMP OPCODE
MVC 1(2,7),XPCU+1 STORE PROGRAM COUNTER
*
ZAP ZPC,XPC SAVE PRIOR INSTRUCTION
PACK XPC,XOPER SET JUMP ADDRESS
UNPK XPCU,XPC UNPACK PROGRAM COUNTER
OI XPCU+2,X'F0' TURN ON ZONE BITS
@JMPX EQU *
L 6,@JMP-4 RESTORE LINK REGISTER
BR 6 BRANCH ON LINK REGISTER
***********************************************************************
9: HRS - Halt & Reset
This stops the program & sets the program counter to the operand values of the instruction. The typical HRS code is "900" which sets the program counter to 00. In my version of the simulator I deactivate the <F1> STEP key after the halt if the 00 operand is used. At that point the user can only use <ESC> EXIT or <F4> RESET to start over.
***********************************************************************
* 9:HRS - HALT & RESET *
***********************************************************************
DC F'0' RETURN ADDRESS SAVE AREA
@HRS EQU *
ST 6,*-4 SAVE RETURN ADDRESS
*
ZAP ZPC,XPC SAVE PRIOR INSTRUCTION
PACK XPC,XOPER SET JUMP ADDRESS
UNPK XPCU,XPC UNPACK PROGRAM COUNTER
OI XPCU+2,X'F0' TURN ON ZONE BITS
@HRSX EQU *
L 6,@HRS-4 RESTORE LINK REGISTER
BR 6 BRANCH ON LINK REGISTER
***********************************************************************
The CARDIAC Simulator and this blog topic are now complete!
That's all there is to say about that...