1232 lines
39 KiB
C
1232 lines
39 KiB
C
/* 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);
|
|
}
|
|
|