blob: 100e7f967817eb5e33c4b10089a825041b2c2c0a [file] [log] [blame] [raw]
/* 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);
}