| /* 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: | |
| <systen name>_io.c add I/O service routines to dev_table | |
| <system name>_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 <ctype.h> | |
| #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 <C>arry, <S>ign, <Z>ero and <P>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 <C>arry, <S>ign, <Z>ero amd <P>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 <S>ign, <Z>ero amd <P>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); | |
| } | |