Monday, November 9, 2020

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...