/* i8008.c: Intel 8008 CPU simulator. Copyright (c) 2017, Hans-Ake Lund Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL HANS-AKE LUND BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Hans-Ake Lund shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Hans-Ake Lund. cpu 8008 CPU The register state for the 8008 CPU is: A<0:7> Accumulator B<0:7> B Register C<0:7> C Register D<0:7> D Register E<0:7> E Register HL<0:15> HL Register Pair CF Carry flag ZF Zero flag SF Sign bit PF Parity bit PC<0:13> Program Counter SP<0:2> Stack Pointer into return stack with 7 levels (the SP register is not available for 8008 programs) The 8008 is an 8-bit CPU, which uses 14 bits of 16-bit registers to address up to 16KB of memory. The basic instructions come in 1, 2, and 3-byte flavors. This routine is the instruction decode routine for the 8080. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction I/O error in I/O simulator Invalid OP code (if ITRAP is set on CPU) 2. Interrupts. Hardware external to the CPU supplies an instruction this could be an RST instruction making a call to one of 8 possibe memory addresses. 3. Non-existent memory. On the 8008, reads to non-existent memory return 0377, and writes are ignored. In the simulator, the largest possible memory is instantiated and initialized to 0377. Thus, only writes need be checked against actual memory size. 4. Adding I/O devices. These modules must be modified: _io.c add I/O service routines to dev_table _sys.c add pointer to data structures in sim_devices CPU documentation: http://www.classiccmp.org/8008/8008UM.pdf 04-Sep-17 HAL Working version of CPU simulator for SCELBI computer 12-Sep-17 HAL Modules restructured in "Intel-Systems" directory */ #include #include "system_defs.h" #define UNIT_V_OPSTOP (UNIT_V_UF) /* Stop on Invalid OP? */ #define UNIT_OPSTOP (1 << UNIT_V_OPSTOP) #define UNIT_V_CHIP (UNIT_V_UF+1) /* 8008 */ #define UNIT_CHIP (1 << UNIT_V_CHIP) #define UNIT_V_MSIZE (UNIT_V_UF+2) /* Memory Size */ #define UNIT_MSIZE (1 << UNIT_V_MSIZE) unsigned char Mem[MAXMEMSIZE]; /* Memory */ unsigned int Smem[8]; /* Stack memory with 7 levels (TODO: and program counter) */ int32 Areg = 0; /* accumulator */ int32 Breg = 0; /* B register */ int32 Creg = 0; /* C register */ int32 Dreg = 0; /* D register */ int32 Ereg = 0; /* E register */ int32 HLreg = 0; /* HL register pair */ int32 SPreg = 0; /* Stack pointer 3 bits */ int32 Cflag = 0; /* Carry flag */ int32 Zflag = 0; /* Zero flag */ int32 Sflag = 0; /* Sign flag */ int32 Pflag = 0; /* Parity flag */ int32 saved_PCreg = 0; /* Program Counter */ int32 INTEflag = 0; /* Interrupt Enable */ int32 int_req = 0; /* Interrupt Request */ int32 PCXreg; /* External view of PC */ /* Function prototypes */ t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); t_stat cpu_reset (DEVICE *dptr); t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw); t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw); void setarith(int32 reg); void setlogical(int32 reg); void setinc(int32 reg); int32 getreg(int32 reg); void putreg(int32 reg, int32 val); void parity(int32 reg); int32 cond(int32 con); extern struct idev dev_table[32]; /* 8008 CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; REG cpu_reg[] = { { ORDATA (PC, saved_PCreg, 16) }, { ORDATA (A, Areg, 8) }, { ORDATA (B, Breg, 8) }, { ORDATA (C, Creg, 8) }, { ORDATA (D, Dreg, 8) }, { ORDATA (E, Ereg, 8) }, { ORDATA (HL, HLreg, 16) }, { ORDATA (SP, SPreg, 16) }, { FLDATA (CF, Cflag, 16) }, { FLDATA (ZF, Zflag, 16) }, { FLDATA (SF, Sflag, 16) }, { FLDATA (PF, Pflag, 16) }, { FLDATA (INTE, INTEflag, 16) }, { ORDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_CHIP, 0, "8008", "8008", NULL }, { UNIT_OPSTOP, UNIT_OPSTOP, "ITRAP", "ITRAP", NULL }, { UNIT_OPSTOP, 0, "NOITRAP", "NOITRAP", NULL }, { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, { 0 } }; DEVICE cpu_dev = { "I8008", &cpu_unit, cpu_reg, cpu_mod, // name, units, registers, modifiers 1, 8, 16, 1, // numunits, aradix, awidth, aincr 8, 8, // dradix, dwidth &cpu_ex, &cpu_dep, &cpu_reset, // examine, deposit, reset NULL, NULL, NULL // boot, attach, detach }; /* Intel 8008 opcodes */ static const char *opcode[] = { "HLT", "HLT", "RLC", "RFC", /* 000-003 */ "ADI", "RST0", "LAI", "RET", /* 004-007 */ "INB", "DCB", "RRC", "RFZ", /* 010-013 */ "ACI", "RST1", "LBI", "RET", /* 014-017 */ "INC", "DCC", "RAL", "RFS", /* 020-023 */ "SUI", "RST2", "LCI", "RET", /* 024-027 */ "IND", "DCD", "RAR", "RFP", /* 030-033 */ "SBI", "RST3", "LDI", "RET", /* 034-037 */ "INE", "DCE", "???", "RTC", /* 040-043 */ "NDI", "RST4", "LEI", "RET", /* 044-047 */ "ICH", "DCH", "???", "RTZ", /* 050-053 */ "XRI", "RST5", "LHI", "RET", /* 054-057 */ "INL", "DCL", "???", "RTS", /* 060-063 */ "ORI", "RST6", "LLI", "RET", /* 064-067 */ "???", "???", "???", "RTP", /* 070-073 */ "CPI", "RST7", "LMI", "RET", /* 074-077 */ "JFC", "INP", "CFC", "INP", /* 100-103 */ "JMP", "INP", "CAL", "INP", /* 104-107 */ "JFZ", "INP", "CFZ", "INP", /* 110-113 */ "JMP", "INP", "CAL", "INP", /* 114-117 */ "JFS", "OUT", "CFS", "OUT", /* 120-123 */ "JMP", "OUT", "CAL", "OUT", /* 124-127 */ "JFP", "OUT", "CFP", "OUT", /* 130-133 */ "JMP", "OUT", "CAL", "OUT", /* 134-137 */ "JTC", "OUT", "CTC", "OUT", /* 140-143 */ "JMP", "OUT", "CAL", "OUT", /* 144-147 */ "JTZ", "OUT", "CTZ", "OUT", /* 150-153 */ "JMP", "OUT", "CAL", "OUT", /* 154-157 */ "JTS", "OUT", "CTS", "OUT", /* 160-163 */ "JMP", "OUT", "CAL", "OUT", /* 164-167 */ "JTP", "OUT", "CTP", "OUT", /* 170-173 */ "JMP", "OUT", "CAL", "OUT", /* 174-177 */ "ADA", "ADB", "ADC", "ADD", /* 200-203 */ "ADE", "ADH", "ADL", "ADM", /* 204-207 */ "ACA", "ACB", "ACC", "ACD", /* 210-213 */ "ACE", "ACH", "ACL", "ACM", /* 214-217 */ "SUA", "SUB", "SUC", "SUD", /* 220-223 */ "SUE", "SUH", "SUL", "SUM", /* 224-227 */ "SBA", "SBB", "SBC", "SBD", /* 230-233 */ "SBE", "SBH", "SBL", "SBM", /* 234-237 */ "NDA", "NDB", "NDC", "NDD", /* 240-243 */ "NDE", "NDH", "NDL", "NDM", /* 244-247 */ "XRA", "XRB", "XRC", "XRD", /* 250-253 */ "XRE", "XRH", "XRL", "XRM", /* 254-257 */ "ORA", "ORB", "ORC", "ORD", /* 260-263 */ "ORE", "ORH", "ORL", "ORM", /* 264-267 */ "CPA", "CPB", "CPC", "CPD", /* 270-273 */ "CPE", "CPH", "CPL", "CPM", /* 274-277 */ "LAA", "LAB", "LAC", "LAD", /* 300-303 */ "LAE", "LAH", "LAL", "LAM", /* 304-307 */ "LBA", "LBB", "LBC", "LBD", /* 310-313 */ "LBE", "LBH", "LBL", "LBM", /* 314-317 */ "LCA", "LCB", "LCC", "LCD", /* 320-323 */ "LCE", "LCH", "LCL", "LCM", /* 324-327 */ "LDA", "LDB", "LDC", "LDD", /* 330-333 */ "LDE", "LDH", "LDL", "LDM", /* 334-337 */ "LEA", "LEB", "LEC", "LED", /* 340-343 */ "LEE", "LEH", "LEL", "LEM", /* 344-347 */ "LHA", "LHB", "LHC", "LHD", /* 350-353 */ "LHE", "LHH", "LHL", "LHM", /* 354-357 */ "LLA", "LLB", "LLC", "LLD", /* 360-363 */ "LLE", "LLH", "LLL", "LLM", /* 364-367 */ "LMA", "LMB", "LMC", "LMD", /* 370-373 */ "LME", "LMH", "LML", "HLT" /* 374-377 */ }; /* Intel 8008 opcode lengths */ int32 oplen[256] = { 1,1,1,1,2,1,2,1, /* 000 - 007 */ 1,1,1,1,2,1,2,1, /* 010 - 017 */ 1,1,1,1,2,1,2,1, /* 020 - 027 */ 1,1,1,1,2,1,2,1, /* 030 - 037 */ 1,1,0,1,2,1,2,1, /* 040 - 047 */ 1,1,0,1,2,1,2,1, /* 050 - 057 */ 1,1,0,1,2,1,2,1, /* 060 - 067 */ 0,0,0,1,2,1,2,1, /* 070 - 077 */ 3,1,3,1,3,1,3,1, /* 100 - 107 */ 3,1,3,1,3,1,3,1, /* 110 - 117 */ 3,1,3,1,3,1,3,1, /* 120 - 127 */ 3,1,3,1,3,1,3,1, /* 130 - 137 */ 3,1,3,1,3,1,3,1, /* 140 - 147 */ 3,1,3,1,3,1,3,1, /* 150 - 157 */ 3,1,3,1,3,1,3,1, /* 160 - 167 */ 3,1,3,1,3,1,3,1, /* 170 - 177 */ 1,1,1,1,1,1,1,1, /* 200 - 207 */ 1,1,1,1,1,1,1,1, /* 210 - 217 */ 1,1,1,1,1,1,1,1, /* 220 - 227 */ 1,1,1,1,1,1,1,1, /* 230 - 237 */ 1,1,1,1,1,1,1,1, /* 240 - 247 */ 1,1,1,1,1,1,1,1, /* 250 - 257 */ 1,1,1,1,1,1,1,1, /* 260 - 267 */ 1,1,1,1,1,1,1,1, /* 270 - 277 */ 1,1,1,1,1,1,1,1, /* 300 - 307 */ 1,1,1,1,1,1,1,1, /* 310 - 317 */ 1,1,1,1,1,1,1,1, /* 320 - 327 */ 1,1,1,1,1,1,1,1, /* 330 - 337 */ 1,1,1,1,1,1,1,1, /* 340 - 347 */ 1,1,1,1,1,1,1,1, /* 350 - 357 */ 1,1,1,1,1,1,1,1, /* 360 - 367 */ 1,1,1,1,1,1,1,1 /* 370 - 377 */ }; /* Decode instructions */ t_stat sim_instr (void) { int32 PC, IR, OP, DAR, reason, hi, lo, carry, states; /* states (Machine States) are recorded for each instruction but not used yet */ PC = saved_PCreg & ADDRMASK; /* load local PC */ Cflag = Cflag & 0200000; reason = 0; /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ if (sim_interval <= 0) { /* check clock queue */ if ((reason = sim_process_event ())) break; } if (int_req > 0) { /* interrupt? */ /* 8008 interrupts not implemented yet. */ } /* end interrupt */ if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } PCXreg = PC; IR = OP = Mem[PC]; /* fetch instruction */ PC = (PC + 1) & ADDRMASK; /* increment PC */ sim_interval--; if ((OP == 0377) || ((OP & 0376) == 0)) { /* HLT Instructions */ reason = STOP_HALT; PC--; states = 4; continue; } /* Handle below all operations which refer to registers, also handle jump, call, return and i/o. After that, a large switch statement takes care of all other opcodes. The original mnemonics for 8008 published 1972 are used. For the instructions: "s" source register, "d" destination register. Octal notation is used in most cases just like in the original documentation. */ if ((OP & 0307) == 0307) { /* LdM */ if (HLreg & 0xC000) { sim_printf("LdM addr > 16K: %o", HLreg); PC--; reason = SCPE_STOP; continue; } DAR = Mem[HLreg]; DAR = DAR & 0377; putreg((OP >> 3) & 07, DAR); states = 8; continue; } if ((OP & 0370) == 0370) { /* LMs */ if (HLreg & 0xC000) { sim_printf("LMs addr > 16K: %o", HLreg); PC--; reason = SCPE_STOP; continue; } DAR = getreg(OP & 07); DAR = DAR & 0377; Mem[HLreg] = DAR; states = 7; continue; } if ((OP & 0300) == 0300) { /* Lds */ DAR = getreg(OP & 07); DAR = DAR & 0377; putreg((OP >> 3) & 07, DAR); states = 5; continue; } if (OP == 0076) { /* LMI */ if (HLreg & 0xc000) { sim_printf("LMI addr > 16K: %o", HLreg); PC--; reason = SCPE_STOP; continue; } DAR = Mem[PC]; PC++; Mem[HLreg] = DAR; states = 9; continue; } if ((OP & 0307) == 0006) { /* LdI */ putreg((OP >> 3) & 07, Mem[PC]); PC++; states = 8; continue; } if ((OP & 0307) == 0000) { /* INd */ DAR = getreg((OP >> 3) & 07); DAR++; setinc(DAR); DAR = DAR & 0377; putreg((OP >> 3) & 07, DAR); states = 5; continue; } if ((OP & 0307) == 0001) { /* DCd */ DAR = getreg((OP >> 3) & 07); DAR--; setinc(DAR); DAR = DAR & 0377; putreg((OP >> 3) & 07, DAR); states = 5; continue; } if (OP == 0207) { /* ADM */ if (HLreg & 0xC000) { sim_printf("LDM addr > 16K: %o", HLreg); PC--; reason = SCPE_STOP; continue; } Areg += Mem[HLreg]; setarith(Areg); Areg = Areg & 0377; states = 8; continue; } if ((OP & 0370) == 0200) { /* ADs */ Areg += getreg(OP & 07); setarith(Areg); Areg = Areg & 0377; states = 5; continue; } if (OP == 0217) { /* ACM */ if (HLreg & 0xC000) { sim_printf("ACM addr > 16K: %o", HLreg); PC--; reason = SCPE_STOP; continue; } carry = 0; if (Cflag) carry = 1; Areg += Mem[HLreg]; Areg += carry; setarith(Areg); Areg = Areg & 0377; states = 8; continue; } if ((OP & 0370) == 0210) { /* ACs */ carry = 0; if (Cflag) carry = 1; Areg += getreg(OP & 07); Areg += carry; setarith(Areg); Areg = Areg & 0377; states = 5; continue; } if (OP == 0227) { /* SUM */ if (HLreg & 0xC000) { sim_printf("SUM addr > 16K: %o", HLreg); PC--; reason = SCPE_STOP; continue; } Areg -= Mem[HLreg]; setarith(Areg); Areg = Areg & 0377; states = 8; continue; } if ((OP & 0370) == 0220) { /* SUs */ Areg -= getreg(OP & 07); setarith(Areg); Areg = Areg & 0377; states = 5; continue; } if (OP == 0237) { /* SBM */ if (HLreg & 0xC000) { sim_printf("SBM addr > 16K: %o", HLreg); PC--; reason = SCPE_STOP; continue; } carry = 0; if (Cflag) carry = 1; Areg -= (Mem[HLreg] + carry); setarith(Areg); Areg = Areg & 0377; states = 8; continue; } if ((OP & 0370) == 0230) { /* SBs */ carry = 0; if (Cflag) carry = 1; Areg -= (getreg(OP & 07)) + carry ; setarith(Areg); Areg = Areg & 0377; states = 5; continue; } if (OP == 0247) { /* NDM */ if (HLreg & 0xC000) { sim_printf("NDM addr > 16K: %o", HLreg); PC--; reason = SCPE_STOP; continue; } Areg &= Mem[HLreg]; setlogical(Areg); Areg = Areg & 0377; states = 8; continue; } if ((OP & 0370) == 0240) { /* NDs */ Areg &= getreg(OP & 07); setlogical(Areg); Areg = Areg & 0377; states = 5; continue; } if (OP == 0257) { /* XRM */ if (HLreg & 0xC000) { sim_printf("XRM addr > 16K: %o", HLreg); PC--; reason = SCPE_STOP; continue; } Areg ^= Mem[HLreg]; setlogical(Areg); Areg &= 0377; states = 8; continue; } if ((OP & 0370) == 0250) { /* XRs */ Areg ^= getreg(OP & 07); setlogical(Areg); Areg &= 0377; continue; } if (OP == 0267) { /* ORM */ if (HLreg & 0xC000) { sim_printf("ORM addr > 16K: %o", HLreg); PC--; reason = SCPE_STOP; continue; } Areg |= Mem[HLreg]; setlogical(Areg); Areg &= 0377; states = 8; continue; } if ((OP & 0370) == 0260) { /* ORs */ Areg |= getreg(OP & 07); setlogical(Areg); Areg &= 0377; states = 5; continue; } if (OP == 0277) { /* CPM */ if (HLreg & 0xC000) { sim_printf("CPM addr > 16K: %o", HLreg); PC--; reason = SCPE_STOP; continue; } DAR = Areg & 0377; DAR -= Mem[HLreg]; setarith(DAR); states = 8; continue; } if ((OP & 0370) == 0270) { /* CPs */ DAR = Areg & 0377; DAR -= getreg(OP & 07); setarith(DAR); states = 5; continue; } if ((OP & 0307) == 0104) { /* JMP */ lo = Mem[PC]; PC++; hi = Mem[PC]; PC++; PC = ((hi << 8) + lo) & 0x3fff; states = 11; continue; } if ((OP & 0347) == 0100) { /* JFc */ if (cond((OP >> 3) & 03) == 0) { lo = Mem[PC]; PC++; hi = Mem[PC]; PC++; PC = ((hi << 8) + lo) & 0x3fff; states = 11; } else { PC += 2; states = 9; } continue; } if ((OP & 0347) == 0140) { /* JTc */ if (cond((OP >> 3) & 03) == 1) { lo = Mem[PC]; PC++; hi = Mem[PC]; PC++; PC = ((hi << 8) + lo) & 0x3fff; states = 11; } else { PC += 2; states = 9; } continue; } if ((OP & 0307) == 0106) { /* CAL */ lo = Mem[PC]; PC++; hi = Mem[PC]; PC++; Smem[SPreg] = PC & 0x3fff; SPreg++; SPreg = SPreg & 07; PC = ((hi << 8) + lo) & 0x3fff; states = 11; continue; } if ((OP & 0347) == 0102) { /* CFc */ if (cond((OP >> 3) & 03) == 0) { lo = Mem[PC]; PC++; hi = Mem[PC]; PC++; Smem[SPreg] = PC & 0x3fff; SPreg++; SPreg = SPreg & 07; PC = ((hi << 8) + lo) & 0x3fff; states = 11; } else { PC += 2; states = 9; } continue; } if ((OP & 0347) == 0142) { /* CTc */ if (cond((OP >> 3) & 03) == 1) { lo = Mem[PC]; PC++; hi = Mem[PC]; PC++; Smem[SPreg] = PC & 0x3fff; SPreg++; SPreg = SPreg & 07; PC = ((hi << 8) + lo) & 0x3fff; states = 11; } else { PC += 2; states = 9; } continue; } if ((OP & 0307) == 0007) { /* RET */ SPreg--; SPreg = SPreg & 07; PC = Smem[SPreg]; states = 5; continue; } if ((OP & 0347) == 0003) { /* RFc */ if (cond((OP >> 3) & 03) == 0) { SPreg--; SPreg = SPreg & 07; PC = Smem[SPreg]; states = 5; } else { states = 3; } continue; } if ((OP & 0347) == 0043) { /* RTc */ if (cond((OP >> 3) & 03) == 1) { SPreg--; SPreg = SPreg & 07; PC = Smem[SPreg]; states = 5; } else { states = 3; } continue; } if ((OP & 0307) == 0005) { /* RST */ Smem[SPreg] = PC & 0x3fff; SPreg++; SPreg = SPreg & 07; PC = OP & 0070; states = 5; continue; } if ((OP & 0301) == 0101) { /* INP/OUT */ DAR = (OP & 0076) >> 1; if (DAR < 8) /* INP */{ Areg = dev_table[DAR].routine(0, 0); states = 8; } else /* OUT */{ dev_table[DAR].routine(1, Areg); states = 6; } continue; } /* The Instruction Decode Switch */ switch (IR) { /* Arithmetic Group */ case 0004: { /* ADI */ Areg += Mem[PC]; PC++; setarith(Areg); Areg = Areg & 0377; states = 8; break; } case 0014: { /* ACI */ carry = 0; if (Cflag) carry = 1; Areg += Mem[PC]; Areg += carry; PC++; setarith(Areg); Areg = Areg & 0377; states = 8; break; } case 0024: { /* SUI */ Areg -= Mem[PC]; PC++; setarith(Areg); Areg = Areg & 0377; states = 8; break; } case 0034: { /* SBI */ carry = 0; if (Cflag) carry = 1; Areg -= (Mem[PC] + carry); PC++; setarith(Areg); Areg = Areg & 0377; states = 8; break; } /* Logical instructions */ case 0044: { /* NDI */ Areg &= Mem[PC]; PC++; setlogical(Areg); Areg &= 0377; states = 8; break; } case 0054: { /* XRI */ Areg ^= Mem[PC]; PC++; setlogical(Areg); Areg &= 0377; states = 8; break; } case 0064: { /* ORI */ Areg |= Mem[PC]; PC++; setlogical(Areg); Areg &= 0377; states = 8; break; } case 0074: { /* CPI */ DAR = Areg & 0377; DAR -= Mem[PC]; PC++; setarith(DAR); states = 8; break; } case 0002: { /* RLC */ if (Areg & 0x80) Cflag = 0200000; else Cflag = 0; Areg = (Areg << 1) & 0377; if (Cflag) Areg |= 01; states = 5; break; } case 0012: { /* RRC */ if (Areg & 0x01) Cflag = 0200000; else Cflag = 0; Areg = (Areg >> 1) & 0377; if (Cflag) Areg |= 0x80; states = 5; break; } case 0022: { /* RAL */ DAR = Cflag; if (Areg & 0x80) Cflag = 0200000; else Cflag = 0; Areg = (Areg << 1) & 0377; if (DAR) Areg |= 0x01; else Areg &= 0xFE; states = 5; break; } case 0032: { /* RAR */ DAR = Cflag; if (Areg & 0x01) Cflag = 0200000; else Cflag = 0; Areg = (Areg >> 1) & 0377; if (DAR) Areg |= 0x80; else Areg &= 0x7F; states = 5; break; } default: { if (cpu_unit.flags & UNIT_OPSTOP) { reason = STOP_OPCODE; PC--; } break; } } } /* Simulation halted */ saved_PCreg = PC; return reason; } /* Test an 8008 flag condition and return 1 if true, 0 if false */ int32 cond(int32 con) { switch (con) { case 0: /* carry */ if (Cflag != 0) return (1); break; case 1: /* zero */ if (Zflag != 0) return (1); break; case 2: /* sign */ if (Sflag != 0) return (1); break; case 3: /* parity */ if (Pflag != 0) return (1); break; default: break; } return (0); } /* Set the arry, ign, ero and

arity flags following an arithmetic operation on 'reg'. */ void setarith(int32 reg) { if (reg & 0x100) Cflag = 0200000; else Cflag = 0; if (reg & 0x80) Sflag = 0200000; else Sflag = 0; if ((reg & 0xff) == 0) Zflag = 0200000; else Zflag = 0; parity(reg); } /* Set the arry, ign, ero amd

arity flags following a logical (bitwise) operation on 'reg'. */ void setlogical(int32 reg) { Cflag = 0; if (reg & 0x80) Sflag = 0200000; else Sflag = 0; if ((reg & 0xff) == 0) Zflag = 0200000; else Zflag = 0; parity(reg); } /* Set the Parity (P) flag based on parity of 'reg', i.e., number of bits on even: P=1, else P=0 */ void parity(int32 reg) { int32 bc = 0; if (reg & 0x01) bc++; if (reg & 0x02) bc++; if (reg & 0x04) bc++; if (reg & 0x08) bc++; if (reg & 0x10) bc++; if (reg & 0x20) bc++; if (reg & 0x40) bc++; if (reg & 0x80) bc++; if (bc & 1) /* odd number of bits */ Pflag = 0; else Pflag = 0200000; } /* Set the ign, ero amd

arity flags following an INR/DCR operation on 'reg'. */ void setinc(int32 reg) { if (reg & 0x80) Sflag = 0200000; else Sflag = 0; if ((reg & 0xff) == 0) Zflag = 0200000; else Zflag = 0; parity(reg); } /* Get an 8008 register and return it */ int32 getreg(int32 reg) { switch (reg) { case 0: return (Areg & 0377); case 1: return (Breg & 0377); case 2: return (Creg & 0377); case 3: return (Dreg & 0377); case 4: return (Ereg & 0377); case 5: return ((HLreg >> 8) & 0377); case 6: return (HLreg & 0377); default: break; } return 0; } /* Put a value into an 8008 register */ void putreg(int32 reg, int32 val) { switch (reg) { case 0: Areg = val & 0377; break; case 1: Breg = val & 0377; break; case 2: Creg = val & 0377; break; case 3: Dreg = val & 0377; break; case 4: Ereg = val & 0377; break; case 5: HLreg = HLreg & 0x00ff; HLreg = HLreg | ((val <<8) & 0xff00); break; case 6: HLreg = HLreg & 0xff00; HLreg = HLreg | (val & 0x00ff); break; default: break; } } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { Cflag = 0; Zflag = 0; saved_PCreg = 0; int_req = 0; sim_brk_types = sim_brk_dflt = SWMASK ('E'); return SCPE_OK; } /* Memory examine */ t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { if (addr >= MEMSIZE) return SCPE_NXM; if (vptr != NULL) *vptr = Mem[addr] & 0377; return SCPE_OK; } /* Memory deposit */ t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { if (addr >= MEMSIZE) return SCPE_NXM; Mem[addr] = val & 0377; return SCPE_OK; } /* Set memory size */ t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { int32 mc = 0; uint32 i; if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) return SCPE_ARG; for (i = val; i < MEMSIZE; i++) mc = mc | Mem[i]; if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) return SCPE_OK; MEMSIZE = val; for (i = MEMSIZE; i < MAXMEMSIZE; i++) Mem[i] = 0377; return SCPE_OK; } /* Symbolic output Inputs: *of = output stream addr = current PC *val = pointer to values *uptr = pointer to unit sw = switches Outputs: status = error code */ t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 cflag, c1, c2, inst, adr; cflag = (uptr == NULL) || (uptr == &cpu_unit); c1 = (val[0] >> 8) & 0177; c2 = val[0] & 0177; if (sw & SWMASK ('A')) { fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); return SCPE_OK; } if (sw & SWMASK ('C')) { fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); return SCPE_OK; } if (!(sw & SWMASK ('M'))) return SCPE_ARG; inst = val[0]; fprintf (of, "%s", opcode[inst]); /* Handle INP/OUT op codes */ if ((inst & 0301) == 0101) { fprintf (of, " %o", (inst & 076) >> 1); } if (oplen[inst] == 2) { if (strchr(opcode[inst], ' ') != NULL) fprintf (of, ","); else fprintf (of, " "); fprintf (of, "%o", val[1]); } if (oplen[inst] == 3) { adr = val[1] & 0xFF; adr |= (val[2] << 8) & 0xff00; if (strchr(opcode[inst], ' ') != NULL) fprintf (of, ","); else fprintf (of, " "); fprintf (of, "%o", adr); } return -(oplen[inst] - 1); } /* Symbolic input Inputs: *cptr = pointer to input string addr = current PC *uptr = pointer to unit *val = pointer to output values sw = switches Outputs: status = error status */ t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) { int32 cflag, i = 0, j, r; char gbuf[CBUFSIZE]; int32 opcode_inp = 0; int32 opcode_out = 0; memset (gbuf, 0, sizeof (gbuf)); cflag = (uptr == NULL) || (uptr == &cpu_unit); while (isspace (*cptr)) cptr++; /* absorb spaces */ if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ val[0] = (uint32) cptr[0]; return SCPE_OK; } if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ val[0] = ((uint32) cptr[0] << 8) + (uint32) cptr[1]; return SCPE_OK; } /* An instruction: get opcode (all characters until null, comma, or numeric (including spaces). */ while (i < sizeof (gbuf) - 4) { if (*cptr == ',' || *cptr == '\0' || sim_isdigit(*cptr)) break; gbuf[i] = toupper(*cptr); cptr++; i++; } /* Allow for RST which has numeric as part of opcode */ if (toupper(gbuf[0]) == 'R' && toupper(gbuf[1]) == 'S' && toupper(gbuf[2]) == 'T') { gbuf[i] = toupper(*cptr); cptr++; i++; } /* Handle INP and OUT opcodes */ if (toupper(gbuf[0]) == 'I' && toupper(gbuf[1]) == 'N' && toupper(gbuf[2]) == 'P') { opcode_inp = 1; } if (toupper(gbuf[0]) == 'O' && toupper(gbuf[1]) == 'U' && toupper(gbuf[2]) == 'T') { opcode_out = 1; } /* kill trailing spaces if any */ gbuf[i] = '\0'; sim_trim_endspc (gbuf); /* kill trailing spaces if any */ gbuf[i] = '\0'; sim_trim_endspc (gbuf); /* find opcode in table */ for (j = 0; j < 256; j++) { if (strcmp(gbuf, opcode[j]) == 0) break; } if (j > 255) /* not found */ return sim_messagef (SCPE_ARG, "No such opcode: %s\n", gbuf); val[0] = j; /* store opcode */ if ((oplen[j] < 2) && (opcode_inp == 0) && (opcode_out == 0)) /* if 1-byter */ return SCPE_OK; /* or not INP/OUT we are done */ if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, 0); /* get address */ sscanf(gbuf, "%o", &r); if (opcode_inp) { if (r <= 7) { val[0] |= r << 1; return SCPE_OK; } else { return SCPE_ARG; } } if (opcode_out) { if ((8 <= r) && (r <= 31)) { val[0] |= r << 1; return SCPE_OK; } else { return SCPE_ARG; } } if (oplen[j] == 2) { val[1] = r & 0xFF; return (-1); } val[1] = r & 0xFF; val[2] = (r >> 8) & 0xFF; return (-2); }