Project standard source code has tabs converted to spaces and CRLF line endings. Other text files have CRLF line endings.
1630 lines
50 KiB
C
1630 lines
50 KiB
C
/* i8080.c: Intel 8080/8085 CPU simulator
|
|
|
|
Copyright (c) 1997-2005, Charles E. Owen
|
|
|
|
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
|
|
CHARLES E. OWEN 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 Charles E. Owen shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Charles E. Owen.
|
|
|
|
This software was modified by Bill Beech, Nov 2010, to allow emulation of Intel
|
|
iSBC Single Board Computers.
|
|
|
|
Copyright (c) 2011, William A. Beech
|
|
|
|
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
|
|
WILLIAM A. BEECH 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 William A. Beech shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from William A. Beech.
|
|
|
|
cpu 8080 CPU
|
|
|
|
08 Oct 02 RMS Tied off spurious compiler warnings
|
|
23 Nov 10 WAB Modified for iSBC emulation
|
|
04 Dec 12 WAB Added 8080 interrupts
|
|
14 Dec 12 WAB Added 8085 interrupts
|
|
|
|
The register state for the 8080 CPU is:
|
|
|
|
A<0:7> Accumulator
|
|
BC<0:15> BC Register Pair
|
|
DE<0:15> DE Register Pair
|
|
HL<0:15> HL Register Pair
|
|
PSW<0:7> Program Status Word (Flags)
|
|
PC<0:15> Program counter
|
|
SP<0:15> Stack Pointer
|
|
|
|
The 8080 is an 8-bit CPU, which uses 16-bit registers to address
|
|
up to 64KB of memory.
|
|
|
|
The 78 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.
|
|
There are 8 possible levels of interrupt, and in effect they
|
|
do a hardware CALL instruction to one of 8 possible low
|
|
memory addresses.
|
|
|
|
3. Non-existent memory. On the 8080, reads to non-existent memory
|
|
return 0FFh, and writes are ignored. In the simulator, the
|
|
largest possible memory is instantiated and initialized to zero.
|
|
Thus, only writes need be checked against actual memory size.
|
|
|
|
4. Adding I/O devices. These modules must be modified:
|
|
i8080.c - add I/O service routines to dev_table
|
|
isys80XX_sys.c - add pointer to data structures in sim_devices
|
|
system_defs.h - to define devices and addresses assigned to devices
|
|
|
|
?? ??? 11 - Original file.
|
|
16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size.
|
|
20 Dec 12 - Modified for basic interrupt function.
|
|
03 Mar 13 - Added trace function.
|
|
04 Mar 13 - Modified all instructions to truncate the affected register
|
|
at the end of the routine.
|
|
17 Mar 13 - Modified to enable/disable trace based on start and stop
|
|
addresses.
|
|
*/
|
|
|
|
#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_MSTOP (UNIT_V_UF+1) /* Stop on Invalid memory? */
|
|
#define UNIT_MSTOP (1 << UNIT_V_MSTOP)
|
|
#define UNIT_V_8085 (UNIT_V_UF+2) /* 8080/8085 switch */
|
|
#define UNIT_8085 (1 << UNIT_V_8085)
|
|
#define UNIT_V_XACK (UNIT_V_UF+3) /* XACK switch */
|
|
#define UNIT_XACK (1 << UNIT_V_XACK)
|
|
|
|
/* Flag values to set proper positions in PSW */
|
|
#define CF 0x01
|
|
#define PF 0x04
|
|
#define AF 0x10
|
|
#define ZF 0x40
|
|
#define SF 0x80
|
|
|
|
/* Macros to handle the flags in the PSW
|
|
8080 has bit #1 always set. This is (not well) documented behavior. */
|
|
#define PSW_ALWAYS_ON (0x02) /* for 8080 */
|
|
#define PSW_MSK (CF|PF|AF|ZF|SF)
|
|
#define TOGGLE_FLAG(FLAG) (PSW ^= FLAG)
|
|
#define SET_FLAG(FLAG) (PSW |= FLAG)
|
|
#define CLR_FLAG(FLAG) (PSW &= ~FLAG)
|
|
#define GET_FLAG(FLAG) (PSW & FLAG)
|
|
#define COND_SET_FLAG(COND,FLAG) \
|
|
if (COND) SET_FLAG(FLAG); else CLR_FLAG(FLAG)
|
|
|
|
#define SET_XACK(VAL) (xack = VAL)
|
|
#define GET_XACK(FLAG) (xack &= FLAG)
|
|
|
|
/* values for IM bits */
|
|
#define ITRAP 0x100
|
|
#define SID 0x80
|
|
#define SOD 0x80
|
|
#define SDE 0x40
|
|
#define R75 0x10
|
|
#define IE 0x08
|
|
#define MSE 0x08
|
|
#define M75 0x04
|
|
#define M65 0x02
|
|
#define M55 0x01
|
|
|
|
/* register masks */
|
|
#define BYTE_R 0xFF
|
|
#define WORD_R 0xFFFF
|
|
|
|
#define i8080_NAME "Intel i8080/85 Processor Chip"
|
|
|
|
#define HIST_MIN 64
|
|
#define HIST_MAX (1u << 18)
|
|
#define HIST_ILNT 3 /* max inst length */
|
|
|
|
typedef struct {
|
|
uint16 pc;
|
|
uint16 sp;
|
|
uint8 psw;
|
|
uint8 a;
|
|
uint8 b;
|
|
uint8 c;
|
|
uint8 d;
|
|
uint8 e;
|
|
uint8 h;
|
|
uint8 l;
|
|
t_value inst[HIST_ILNT];
|
|
} InstHistory;
|
|
|
|
/* storage for the rest of the registers */
|
|
uint32 PSW = 0; /* program status word */
|
|
uint32 A = 0; /* accumulator */
|
|
uint32 BC = 0; /* BC register pair */
|
|
uint32 DE = 0; /* DE register pair */
|
|
uint32 HL = 0; /* HL register pair */
|
|
uint32 SP = 0; /* Stack pointer */
|
|
uint32 saved_PC = 0; /* program counter */
|
|
uint32 IM = 0; /* Interrupt Mask Register */
|
|
uint8 xack = 0; /* XACK signal */
|
|
uint32 int_req = 0; /* Interrupt request */
|
|
uint8 INTA = 0; // interrupt acknowledge
|
|
uint16 PCX; /* External view of PC */
|
|
uint16 PCY; /* Internal view of PC */
|
|
uint16 PC;
|
|
UNIT *uptr;
|
|
uint16 port; //port used in any IN/OUT
|
|
uint16 addr; //addr used for operand fetch
|
|
uint32 IR;
|
|
uint16 devnum = 0;
|
|
uint8 cpu_onetime = 0;
|
|
|
|
int32 hst_p = 0; /* history pointer */
|
|
int32 hst_lnt = 0; /* history length */
|
|
InstHistory *hst = NULL; /* instruction history */
|
|
|
|
static const char* i8080_desc(DEVICE *dptr) {
|
|
return i8080_NAME;
|
|
}
|
|
|
|
/* function prototypes */
|
|
|
|
void set_cpuint(int32 int_num);
|
|
void dumpregs(void);
|
|
int32 fetch_byte(int32 flag);
|
|
int32 fetch_word(void);
|
|
uint16 pop_word(void);
|
|
void push_word(uint16 val);
|
|
void setarith(int32 reg);
|
|
void setlogical(int32 reg);
|
|
void setinc(int32 reg);
|
|
int32 getreg(int32 reg);
|
|
void putreg(int32 reg, int32 val);
|
|
int32 getpair(int32 reg);
|
|
int32 getpush(int32 reg);
|
|
void putpush(int32 reg, int32 data);
|
|
void putpair(int32 reg, int32 val);
|
|
void parity(int32 reg);
|
|
int32 cond(int32 con);
|
|
t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
t_stat i8080_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
|
|
t_stat i8080_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
|
|
t_stat i8080_reset (DEVICE *dptr);
|
|
|
|
/* external function prototypes */
|
|
|
|
extern t_stat i8080_reset (DEVICE *dptr);
|
|
extern uint8 get_mbyte(uint16 addr);
|
|
extern uint16 get_mword(uint16 addr);
|
|
extern void put_mbyte(uint16 addr, uint8 val);
|
|
extern void put_mword(uint16 addr, uint16 val);
|
|
extern int32 sim_int_char;
|
|
extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */
|
|
|
|
struct idev {
|
|
uint8 (*routine)(t_bool io, uint8 data, uint8 devnum);
|
|
uint16 port;
|
|
uint16 devnum;
|
|
uint8 dummy;
|
|
};
|
|
|
|
/* This is the I/O configuration table. There are 256 possible
|
|
device addresses, if a device is plugged to a port it's routine
|
|
address is here, 'nulldev' means no device is available
|
|
*/
|
|
|
|
extern struct idev dev_table[];
|
|
|
|
/* CPU data structures
|
|
i8080_dev CPU device descriptor
|
|
i8080_unit CPU unit descriptor
|
|
i8080_reg CPU register list
|
|
i8080_mod CPU modifiers list
|
|
*/
|
|
|
|
UNIT i8080_unit = { UDATA (NULL, 0, 65535) }; /* default 8080 */
|
|
|
|
REG i8080_reg[] = {
|
|
{ HRDATA (PC, saved_PC, 16) }, /* must be first for sim_PC */
|
|
{ HRDATA (PSW, PSW, 8) },
|
|
{ HRDATA (A, A, 8) },
|
|
{ HRDATA (BC, BC, 16) },
|
|
{ HRDATA (DE, DE, 16) },
|
|
{ HRDATA (HL, HL, 16) },
|
|
{ HRDATA (SP, SP, 16) },
|
|
{ HRDATA (IM, IM, 8) },
|
|
{ HRDATA (XACK, xack, 8) },
|
|
{ HRDATA (INTR, int_req, 32) },
|
|
{ HRDATA (WRU, sim_int_char, 8) },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB i8080_mod[] = {
|
|
{ UNIT_OPSTOP, UNIT_OPSTOP, "ITRAP", "ITRAP", NULL },
|
|
{ UNIT_OPSTOP, 0, "NOITRAP", "NOITRAP", NULL },
|
|
{ UNIT_MSTOP, UNIT_MSTOP, "MTRAP", "MTRAP", NULL },
|
|
{ UNIT_MSTOP, 0, "NOMTRAP", "NOMTRAP", NULL },
|
|
{ UNIT_8085, 0, "8080", "8080", NULL },
|
|
{ UNIT_8085, UNIT_8085, "8085", "8085", NULL },
|
|
{ UNIT_XACK, 0, "NOXACK", "NOXACK", NULL },
|
|
{ UNIT_XACK, UNIT_XACK, "XACK", "XACK", NULL },
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP|MTAB_NC, 0, "HISTORY", "HISTORY=n",
|
|
&cpu_set_hist, &cpu_show_hist, NULL, "Enable/Display instruction history" },
|
|
{ 0 }
|
|
};
|
|
|
|
DEBTAB i8080_debug[] = {
|
|
{ "ALL", DEBUG_all },
|
|
{ "FLOW", DEBUG_flow },
|
|
{ "READ", DEBUG_read },
|
|
{ "WRITE", DEBUG_write },
|
|
{ "XACK", DEBUG_xack },
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE i8080_dev = {
|
|
"CPU", //name
|
|
&i8080_unit, //units
|
|
i8080_reg, //registers
|
|
i8080_mod, //modifiers
|
|
1, //numunits
|
|
16, //aradix
|
|
16, //awidth
|
|
1, //aincr
|
|
16, //dradix
|
|
8, //dwidth
|
|
&i8080_ex, //examine
|
|
&i8080_dep, //deposit
|
|
&i8080_reset, //reset
|
|
NULL, //boot
|
|
NULL, //attach
|
|
NULL, //detach
|
|
NULL, //context
|
|
DEV_DEBUG, //flags
|
|
0, //dctrl
|
|
i8080_debug, //debflags
|
|
NULL, //msize
|
|
NULL, //lname
|
|
NULL, //help routine
|
|
NULL, //attach help routine
|
|
NULL, //help context
|
|
&i8080_desc //device description
|
|
};
|
|
|
|
/* tables for the disassembler */
|
|
const char *opcode[] = {
|
|
"NOP", "LXI B,", "STAX B", "INX B", /* 0x00 */
|
|
"INR B", "DCR B", "MVI B,", "RLC",
|
|
"???", "DAD B", "LDAX B", "DCX B",
|
|
"INR C", "DCR C", "MVI C,", "RRC",
|
|
"???", "LXI D,", "STAX D", "INX D", /* 0x10 */
|
|
"INR D", "DCR D", "MVI D,", "RAL",
|
|
"???", "DAD D", "LDAX D", "DCX D",
|
|
"INR E", "DCR E", "MVI E,", "RAR",
|
|
"RIM", "LXI H,", "SHLD ", "INX H", /* 0x20 */
|
|
"INR H", "DCR H", "MVI H,", "DAA",
|
|
"???", "DAD H", "LHLD ", "DCX H",
|
|
"INR L", "DCR L", "MVI L", "CMA",
|
|
"SIM", "LXI SP,", "STA ", "INX SP", /* 0x30 */
|
|
"INR M", "DCR M", "MVI M,", "STC",
|
|
"???", "DAD SP", "LDA ", "DCX SP",
|
|
"INR A", "DCR A", "MVI A,", "CMC",
|
|
"MOV B,B", "MOV B,C", "MOV B,D", "MOV B,E", /* 0x40 */
|
|
"MOV B,H", "MOV B,L", "MOV B,M", "MOV B,A",
|
|
"MOV C,B", "MOV C,C", "MOV C,D", "MOV C,E",
|
|
"MOV C,H", "MOV C,L", "MOV C,M", "MOV C,A",
|
|
"MOV D,B", "MOV D,C", "MOV D,D", "MOV D,E", /* 0x50 */
|
|
"MOV D,H", "MOV D,L", "MOV D,M", "MOV D,A",
|
|
"MOV E,B", "MOV E,C", "MOV E,D", "MOV E,E",
|
|
"MOV E,H", "MOV E,L", "MOV E,M", "MOV E,A",
|
|
"MOV H,B", "MOV H,C", "MOV H,D", "MOV H,E", /* 0x60 */
|
|
"MOV H,H", "MOV H,L", "MOV H,M", "MOV H,A",
|
|
"MOV L,B", "MOV L,C", "MOV L,D", "MOV L,E",
|
|
"MOV L,H", "MOV L,L", "MOV L,M", "MOV L,A",
|
|
"MOV M,B", "MOV M,C", "MOV M,D", "MOV M,E", /* 0x70 */
|
|
"MOV M,H", "MOV M,L", "HLT", "MOV M,A",
|
|
"MOV A,B", "MOV A,C", "MOV A,D", "MOV A,E",
|
|
"MOV A,H", "MOV A,L", "MOV A,M", "MOV A,A",
|
|
"ADD B", "ADD C", "ADD D", "ADD E", /* 0x80 */
|
|
"ADD H", "ADD L", "ADD M", "ADD A",
|
|
"ADC B", "ADC C", "ADC D", "ADC E",
|
|
"ADC H", "ADC L", "ADC M", "ADC A",
|
|
"SUB B", "SUB C", "SUB D", "SUB E", /* 0x90 */
|
|
"SUB H", "SUB L", "SUB M", "SUB A",
|
|
"SBB B", "SBB C", "SBB D", "SBB E",
|
|
"SBB H", "SBB L", "SBB M", "SBB A",
|
|
"ANA B", "ANA C", "ANA D", "ANA E", /* 0xA0 */
|
|
"ANA H", "ANA L", "ANA M", "ANA A",
|
|
"XRA B", "XRA C", "XRA D", "XRA E",
|
|
"XRA H", "XRA L", "XRA M", "XRA A",
|
|
"ORA B", "ORA C", "ORA D", "ORA E", /* 0xB0 */
|
|
"ORA H", "ORA L", "ORA M", "ORA A",
|
|
"CMP B", "CMP C", "CMP D", "CMP E",
|
|
"CMP H", "CMP L", "CMP M", "CMP A",
|
|
"RNZ", "POP B", "JNZ ", "JMP ", /* 0xC0 */
|
|
"CNZ ", "PUSH B", "ADI ", "RST 0",
|
|
"RZ", "RET", "JZ ", "???",
|
|
"CZ ", "CALL ", "ACI ", "RST 1",
|
|
"RNC", "POP D", "JNC ", "OUT ", /* 0xD0 */
|
|
"CNC ", "PUSH D", "SUI ", "RST 2",
|
|
"RC", "???", "JC ", "IN ",
|
|
"CC ", "???", "SBI ", "RST 3",
|
|
"RPO", "POP H", "JPO ", "XTHL", /* 0xE0 */
|
|
"CPO ", "PUSH H", "ANI ", "RST 4",
|
|
"RPE", "PCHL", "JPE ", "XCHG",
|
|
"CPE ", "???", "XRI ", "RST 5",
|
|
"RP", "POP PSW", "JP ", "DI", /* 0xF0 */
|
|
"CP ", "PUSH PSW", "ORI ", "RST 6",
|
|
"RM", "SPHL", "JM ", "EI",
|
|
"CM ", "???", "CPI ", "RST 7",
|
|
};
|
|
|
|
int32 oplen[256] = {
|
|
1,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1,
|
|
0,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1,
|
|
1,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1,
|
|
1,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,3,3,3,1,2,1,1,1,3,0,3,3,2,1,
|
|
1,1,3,2,3,1,2,1,1,0,3,2,3,0,2,1,
|
|
1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1,
|
|
1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1
|
|
};
|
|
|
|
void set_cpuint(int32 int_num)
|
|
{
|
|
int_req |= int_num;
|
|
}
|
|
|
|
|
|
/* instruction simulator */
|
|
int32 sim_instr(void)
|
|
{
|
|
extern int32 sim_interval;
|
|
uint32 OP, DAR, reason, adr;
|
|
int i;
|
|
InstHistory *hst_ent = NULL;
|
|
|
|
PC = saved_PC & WORD_R; /* load local PC */
|
|
reason = 0;
|
|
|
|
uptr = i8080_dev.units;
|
|
|
|
if (cpu_onetime++ == 0) {
|
|
if ((uptr->flags & UNIT_8085))
|
|
sim_printf(" CPU = 8085\n");
|
|
else
|
|
sim_printf(" CPU = 8080\n");
|
|
}
|
|
|
|
/* Main instruction fetch/decode loop */
|
|
|
|
while (reason == 0) { /* loop until halted */
|
|
|
|
if (i8080_dev.dctrl & DEBUG_reg) {
|
|
dumpregs();
|
|
sim_printf("\n");
|
|
}
|
|
|
|
if (sim_interval <= 0) { /* check clock queue */
|
|
if ((reason = sim_process_event()))
|
|
break;
|
|
}
|
|
|
|
if (int_req > 0) { /* interrupt? */
|
|
if (uptr->flags & UNIT_8085) { /* 8085 */
|
|
if (int_req & ITRAP) { /* int */
|
|
push_word(PC);
|
|
PC = 0x0024;
|
|
int_req &= ~ITRAP;
|
|
} else if (IM & IE) {
|
|
if (int_req & I75 && IM & M75) { /* int 7.5 */
|
|
push_word(PC);
|
|
PC = 0x003C;
|
|
int_req &= ~I75;
|
|
} else if (int_req & I65 && IM & M65) { /* int 6.5 */
|
|
push_word(PC);
|
|
PC = 0x0034;
|
|
int_req &= ~I65;
|
|
} else if (int_req & I55 && IM & M55) { /* int 5.5 */
|
|
push_word(PC);
|
|
PC = 0x002C;
|
|
int_req &= ~I55;
|
|
} else if (int_req & INT_R) { /* intr */
|
|
push_word(PC); /* do an RST 7 */
|
|
PC = 0x0038;
|
|
int_req &= ~INT_R;
|
|
}
|
|
}
|
|
} else { /* 8080 */
|
|
if (IM & IE) { /* enabled? */
|
|
INTA = 1;
|
|
push_word(PC); /* do an RST 2 */
|
|
PC = 0x0038;
|
|
int_req = 0;
|
|
// sim_printf("8080 Interrupt\n");
|
|
}
|
|
}
|
|
} /* end interrupt */
|
|
|
|
if (sim_brk_summ &&
|
|
sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */
|
|
// dumpregs();
|
|
reason = STOP_IBKPT; /* stop simulation */
|
|
break;
|
|
}
|
|
|
|
if (hst_lnt) { /* record history? */
|
|
hst_ent = &hst[hst_p];
|
|
hst_ent->pc = PC;
|
|
hst_ent->sp = SP;
|
|
hst_ent->psw = PSW;
|
|
hst_ent->a = A;
|
|
hst_ent->b = (BC >> 8) & 0xFF;
|
|
hst_ent->c = (BC & 0xFF);
|
|
hst_ent->d = (DE >> 8) & 0xFF;
|
|
hst_ent->e = (DE & 0xFF);
|
|
hst_ent->h = (HL >> 8) & 0xFF;
|
|
hst_ent->l = (HL & 0xFF);
|
|
for (i = 0; i < HIST_ILNT; i++)
|
|
hst_ent->inst[i] = (t_value)get_mbyte (PC + i);
|
|
hst_p = (hst_p + 1);
|
|
if (hst_p >= hst_lnt)
|
|
hst_p = 0;
|
|
}
|
|
|
|
sim_interval--; /* countdown clock */
|
|
PCY = PCX = PC;
|
|
|
|
IR = OP = fetch_byte(0); /* instruction fetch */
|
|
|
|
if (uptr->flags & UNIT_XACK) {
|
|
if (GET_XACK(1) == 0) { // no XACK for instruction fetch
|
|
reason = STOP_XACK;
|
|
sim_printf("\nFailed XACK for Instruction Fetch from %04X", PCX);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// first instruction decode
|
|
if (OP == 0x76) { /* HLT Instruction*/
|
|
reason = STOP_HALT;
|
|
PC--;
|
|
continue;
|
|
}
|
|
|
|
/* Handle below all operations which refer to registers or
|
|
register pairs. After that, a large switch statement
|
|
takes care of all other opcodes */
|
|
|
|
if ((OP & 0xC0) == 0x40) { /* MOV */
|
|
DAR = getreg(OP & 0x07);
|
|
putreg((OP >> 3) & 0x07, DAR);
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xC7) == 0x06) { /* MVI */
|
|
putreg((OP >> 3) & 0x07, fetch_byte(1));
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xCF) == 0x01) { /* LXI */
|
|
DAR = fetch_word();
|
|
putpair((OP >> 4) & 0x03, DAR);
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xEF) == 0x0A) { /* LDAX */
|
|
DAR = getpair((OP >> 4) & 0x03);
|
|
putreg(7, get_mbyte(DAR));
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xEF) == 0x02) { /* STAX */
|
|
DAR = getpair((OP >> 4) & 0x03);
|
|
put_mbyte(DAR, getreg(7));
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xF8) == 0xB8) { /* CMP */
|
|
DAR = A;
|
|
DAR -= getreg(OP & 0x07);
|
|
setarith(DAR);
|
|
A &= BYTE_R; //required ***
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xC7) == 0xC2) { /* JMP <condition> */
|
|
adr = fetch_word();
|
|
if (cond((OP >> 3) & 0x07))
|
|
PC = adr;
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xC7) == 0xC4) { /* CALL <condition> */
|
|
adr = fetch_word();
|
|
if (cond((OP >> 3) & 0x07)) {
|
|
push_word(PC);
|
|
PC = adr;
|
|
}
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xC7) == 0xC0) { /* RET <condition> */
|
|
if (cond((OP >> 3) & 0x07)) {
|
|
PC = pop_word();
|
|
}
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xC7) == 0xC7) { /* RST */
|
|
push_word(PC);
|
|
PC = OP & 0x38;
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xCF) == 0xC5) { /* PUSH */
|
|
DAR = getpush((OP >> 4) & 0x03);
|
|
push_word(DAR);
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xCF) == 0xC1) { /* POP */
|
|
DAR = pop_word();
|
|
putpush((OP >> 4) & 0x03, DAR);
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xF8) == 0x80) { /* ADD */
|
|
A += getreg(OP & 0x07);
|
|
setarith(A);
|
|
A &= BYTE_R; //required
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xF8) == 0x88) { /* ADC */
|
|
A += getreg(OP & 0x07);
|
|
if (GET_FLAG(CF))
|
|
A++;
|
|
setarith(A);
|
|
A &= BYTE_R; //required
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xF8) == 0x90) { /* SUB */
|
|
A -= getreg(OP & 0x07);
|
|
setarith(A);
|
|
A &= BYTE_R; //required
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xF8) == 0x98) { /* SBB */
|
|
A -= getreg(OP & 0x07);
|
|
if (GET_FLAG(CF))
|
|
A--;
|
|
setarith(A);
|
|
A &= BYTE_R; //required
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xC7) == 0x04) { /* INR */
|
|
DAR = getreg((OP >> 3) & 0x07);
|
|
DAR++;
|
|
setinc(DAR);
|
|
DAR &= BYTE_R; //required
|
|
putreg((OP >> 3) & 0x07, DAR);
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xC7) == 0x05) { /* DCR */
|
|
DAR = getreg((OP >> 3) & 0x07);
|
|
DAR--;
|
|
setinc(DAR);
|
|
DAR &= BYTE_R; //required
|
|
putreg((OP >> 3) & 0x07, DAR);
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xCF) == 0x03) { /* INX */
|
|
DAR = getpair((OP >> 4) & 0x03);
|
|
DAR++;
|
|
DAR &= WORD_R; //required
|
|
putpair((OP >> 4) & 0x03, DAR);
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xCF) == 0x0B) { /* DCX */
|
|
DAR = getpair((OP >> 4) & 0x03);
|
|
DAR--;
|
|
DAR &= WORD_R; //required
|
|
putpair((OP >> 4) & 0x03, DAR);
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xCF) == 0x09) { /* DAD */
|
|
HL += getpair((OP >> 4) & 0x03);
|
|
COND_SET_FLAG(HL & 0x10000, CF);
|
|
HL &= WORD_R; //required
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xF8) == 0xA0) { /* ANA */
|
|
A &= getreg(OP & 0x07);
|
|
setlogical(A);
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xF8) == 0xA8) { /* XRA */
|
|
A ^= getreg(OP & 0x07);
|
|
setlogical(A);
|
|
goto loop_end;
|
|
}
|
|
|
|
if ((OP & 0xF8) == 0xB0) { /* ORA */
|
|
A |= getreg(OP & 0x07);
|
|
setlogical(A);
|
|
goto loop_end;
|
|
}
|
|
|
|
/* The Big Instruction Decode Switch */
|
|
|
|
switch (IR) {
|
|
|
|
/* 8085 instructions only */
|
|
case 0x20: /* RIM */
|
|
if (i8080_unit.flags & UNIT_8085) { /* 8085 */
|
|
A = IM;
|
|
} else { /* 8080 */
|
|
reason = STOP_OPCODE;
|
|
PC--;
|
|
}
|
|
break;
|
|
|
|
case 0x30: /* SIM */
|
|
if (i8080_unit.flags & UNIT_8085) { /* 8085 */
|
|
if (A & MSE) {
|
|
IM &= 0xF8;
|
|
IM |= A & 0x07;
|
|
}
|
|
if (A & I75) { /* reset RST 7.5 FF */
|
|
}
|
|
} else { /* 8080 */
|
|
reason = STOP_OPCODE;
|
|
PC--;
|
|
}
|
|
break;
|
|
|
|
/* Logical instructions */
|
|
|
|
case 0xFE: /* CPI */
|
|
DAR = A;
|
|
DAR -= fetch_byte(1);
|
|
setarith(DAR);
|
|
break;
|
|
|
|
case 0xE6: /* ANI */
|
|
A &= fetch_byte(1);
|
|
setlogical(A);
|
|
break;
|
|
|
|
case 0xEE: /* XRI */
|
|
A ^= fetch_byte(1);
|
|
setlogical(A);
|
|
break;
|
|
|
|
case 0xF6: /* ORI */
|
|
A |= fetch_byte(1);
|
|
setlogical(A);
|
|
break;
|
|
|
|
/* Jump instructions */
|
|
|
|
case 0xC3: /* JMP */
|
|
PC = fetch_word();
|
|
break;
|
|
|
|
case 0xE9: /* PCHL */
|
|
PC = HL;
|
|
break;
|
|
|
|
case 0xCD: /* CALL */
|
|
adr = fetch_word();
|
|
push_word(PC);
|
|
PC = adr;
|
|
break;
|
|
|
|
case 0xC9: /* RET */
|
|
PC = pop_word();
|
|
break;
|
|
|
|
/* Data Transfer Group */
|
|
|
|
case 0x32: /* STA */
|
|
DAR = fetch_word();
|
|
put_mbyte(DAR, A);
|
|
break;
|
|
|
|
case 0x3A: /* LDA */
|
|
DAR = fetch_word();
|
|
A = get_mbyte(DAR);
|
|
break;
|
|
|
|
case 0x22: /* SHLD */
|
|
DAR = fetch_word();
|
|
put_mword(DAR, HL);
|
|
break;
|
|
|
|
case 0x2A: /* LHLD */
|
|
DAR = fetch_word();
|
|
HL = get_mword(DAR);
|
|
break;
|
|
|
|
case 0xEB: /* XCHG */
|
|
DAR = HL;
|
|
HL = DE;
|
|
HL &= WORD_R; //required
|
|
DE = DAR;
|
|
break;
|
|
|
|
/* Arithmetic Group */
|
|
|
|
case 0xC6: /* ADI */
|
|
A += fetch_byte(1);
|
|
setarith(A);
|
|
A &= BYTE_R; //required
|
|
break;
|
|
|
|
case 0xCE: /* ACI */
|
|
A += fetch_byte(1);
|
|
if (GET_FLAG(CF))
|
|
A++;
|
|
setarith(A);
|
|
A &= BYTE_R; //required
|
|
break;
|
|
|
|
case 0xD6: /* SUI */
|
|
A -= fetch_byte(1);
|
|
setarith(A);
|
|
A &= BYTE_R; //required
|
|
break;
|
|
|
|
case 0xDE: /* SBI */
|
|
A -= fetch_byte(1);
|
|
if (GET_FLAG(CF))
|
|
A--;
|
|
A &= BYTE_R; //required
|
|
break;
|
|
|
|
case 0x27: /* DAA */
|
|
DAR = A & 0x0F;
|
|
if (DAR > 9 || GET_FLAG(AF)) {
|
|
DAR += 6;
|
|
A &= 0xF0;
|
|
A |= DAR & 0x0F;
|
|
COND_SET_FLAG(DAR & 0x10, AF);
|
|
}
|
|
DAR = (A >> 4) & 0x0F;
|
|
if (DAR > 9 || GET_FLAG(AF)) {
|
|
DAR += 6;
|
|
if (GET_FLAG(AF)) DAR++;
|
|
A &= 0x0F;
|
|
A |= (DAR << 4);
|
|
}
|
|
COND_SET_FLAG(DAR & 0x10, CF);
|
|
COND_SET_FLAG(A & 0x80, SF);
|
|
COND_SET_FLAG((A & 0xFF) == 0, ZF);
|
|
A &= BYTE_R; //required
|
|
parity(A);
|
|
break;
|
|
|
|
case 0x07: /* RLC */
|
|
COND_SET_FLAG(A & 0x80, CF);
|
|
A = A << 1;
|
|
if (GET_FLAG(CF))
|
|
A |= 0x01;
|
|
A &= BYTE_R; //required
|
|
break;
|
|
|
|
case 0x0F: /* RRC */
|
|
COND_SET_FLAG(A & 0x01, CF);
|
|
A = A >> 1;
|
|
if (GET_FLAG(CF))
|
|
A |= 0x80;
|
|
A &= BYTE_R; //required
|
|
break;
|
|
|
|
case 0x17: /* RAL */
|
|
DAR = GET_FLAG(CF);
|
|
COND_SET_FLAG(A & 0x80, CF);
|
|
A = A << 1;
|
|
if (DAR)
|
|
A |= 0x01;
|
|
A &= BYTE_R; //required
|
|
break;
|
|
|
|
case 0x1F: /* RAR */
|
|
DAR = GET_FLAG(CF);
|
|
COND_SET_FLAG(A & 0x01, CF);
|
|
A = A >> 1;
|
|
if (DAR)
|
|
A |= 0x80;
|
|
A &= BYTE_R; //required
|
|
break;
|
|
|
|
case 0x2F: /* CMA */
|
|
A = ~A;
|
|
A &= BYTE_R; //required
|
|
break;
|
|
|
|
case 0x3F: /* CMC */
|
|
TOGGLE_FLAG(CF);
|
|
break;
|
|
|
|
case 0x37: /* STC */
|
|
SET_FLAG(CF);
|
|
break;
|
|
|
|
/* Stack, I/O & Machine Control Group */
|
|
|
|
case 0x00: /* NOP */
|
|
break;
|
|
|
|
case 0xE3: /* XTHL */
|
|
DAR = pop_word();
|
|
push_word(HL);
|
|
HL = DAR;
|
|
break;
|
|
|
|
case 0xF9: /* SPHL */
|
|
SP = HL;
|
|
break;
|
|
|
|
case 0xFB: /* EI */
|
|
IM |= IE;
|
|
break;
|
|
|
|
case 0xF3: /* DI */
|
|
IM &= ~IE;
|
|
break;
|
|
|
|
case 0xDB: /* IN */
|
|
SET_XACK(1); /* good I/O address */
|
|
port = fetch_byte(1);
|
|
A = dev_table[port].routine(0, 0, dev_table[port].devnum & BYTEMASK);
|
|
break;
|
|
|
|
case 0xD3: /* OUT */
|
|
SET_XACK(1); /* good I/O address */
|
|
port = fetch_byte(1);
|
|
dev_table[port].routine(1, A, dev_table[port].devnum & BYTEMASK);
|
|
break;
|
|
|
|
default: /* undefined opcode */
|
|
if (i8080_unit.flags & UNIT_OPSTOP) {
|
|
reason = STOP_OPCODE;
|
|
PC--;
|
|
}
|
|
break;
|
|
}
|
|
loop_end:
|
|
|
|
if (uptr->flags & UNIT_XACK) {
|
|
if (GET_XACK(1) == 0) { // no XACK for operand fetch
|
|
// reason = STOP_XACK;
|
|
if (OP == 0xD3 || OP == 0xDB) {
|
|
sim_printf("\nFailed XACK for Port %02X Fetch from %04X", port, PCX);
|
|
} else {
|
|
sim_printf("\nFailed XACK for Operand %04X Fetch from %04X", addr, PCX);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Simulation halted */
|
|
|
|
saved_PC = PC;
|
|
return reason;
|
|
}
|
|
|
|
/* dump the registers */
|
|
void dumpregs(void)
|
|
{
|
|
sim_printf(" PC=%04X A=%02X BC=%04X DE=%04X HL=%04X SP=%04X IM=%02X XACK=%d",
|
|
PCY, A, BC, DE, HL, SP, IM, xack);
|
|
sim_printf(" IR=%02X addr=%04X", IR, addr);
|
|
sim_printf(" CF=%d ZF=%d AF=%d SF=%d PF=%d\n",
|
|
GET_FLAG(CF) ? 1 : 0,
|
|
GET_FLAG(ZF) ? 1 : 0,
|
|
GET_FLAG(AF) ? 1 : 0,
|
|
GET_FLAG(SF) ? 1 : 0,
|
|
GET_FLAG(PF) ? 1 : 0);
|
|
}
|
|
|
|
/* fetch an instruction or byte */
|
|
int32 fetch_byte(int32 flag)
|
|
{
|
|
uint32 val;
|
|
|
|
val = get_mbyte(PC) & 0xFF; /* fetch byte */
|
|
PC = (PC + 1) & ADDRMASK; /* increment PC */
|
|
addr = val & BYTEMASK;
|
|
return val;
|
|
}
|
|
|
|
/* fetch a word */
|
|
int32 fetch_word(void)
|
|
{
|
|
uint16 val;
|
|
|
|
val = get_mbyte(PC) & BYTE_R; /* fetch low byte */
|
|
val |= get_mbyte(PC + 1) << 8; /* fetch high byte */
|
|
// if (i8080_dev.dctrl & DEBUG_asm || uptr->flags & UNIT_TRACE) /* display source code */
|
|
// sim_printf("0%04XH", val);
|
|
PC = (PC + 2) & ADDRMASK; /* increment PC */
|
|
val &= WORD_R;
|
|
addr = val;
|
|
return val;
|
|
}
|
|
|
|
/* push a word to the stack */
|
|
void push_word(uint16 val)
|
|
{
|
|
SP--;
|
|
put_mbyte(SP, (val >> 8));
|
|
SP--;
|
|
put_mbyte(SP, val & 0xFF);
|
|
}
|
|
|
|
/* pop a word from the stack */
|
|
uint16 pop_word(void)
|
|
{
|
|
register uint16 res;
|
|
|
|
res = get_mbyte(SP);
|
|
SP++;
|
|
res |= get_mbyte(SP) << 8;
|
|
SP++;
|
|
return res;
|
|
}
|
|
|
|
/* Test an 8080 flag condition and return 1 if true, 0 if false */
|
|
int32 cond(int32 con)
|
|
{
|
|
switch (con) {
|
|
case 0: /* NZ */
|
|
if (GET_FLAG(ZF) == 0) return 1;
|
|
break;
|
|
case 1: /* Z */
|
|
if (GET_FLAG(ZF)) return 1;
|
|
break;
|
|
case 2: /* NC */
|
|
if (GET_FLAG(CF) == 0) return 1;
|
|
break;
|
|
case 3: /* C */
|
|
if (GET_FLAG(CF)) return 1;
|
|
break;
|
|
case 4: /* PO */
|
|
if (GET_FLAG(PF) == 0) return 1;
|
|
break;
|
|
case 5: /* PE */
|
|
if (GET_FLAG(PF)) return 1;
|
|
break;
|
|
case 6: /* P */
|
|
if (GET_FLAG(SF) == 0) return 1;
|
|
break;
|
|
case 7: /* M */
|
|
if (GET_FLAG(SF)) 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)
|
|
{
|
|
COND_SET_FLAG(reg & 0x100, CF);
|
|
COND_SET_FLAG(reg & 0x80, SF);
|
|
COND_SET_FLAG((reg & BYTE_R) == 0, ZF);
|
|
CLR_FLAG(AF);
|
|
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)
|
|
{
|
|
CLR_FLAG(CF);
|
|
COND_SET_FLAG(reg & 0x80, SF);
|
|
COND_SET_FLAG((reg & BYTE_R) == 0, ZF);
|
|
CLR_FLAG(AF);
|
|
parity(reg);
|
|
}
|
|
|
|
/* Set the Parity (P) flag based on parity of 'reg', i.e., number
|
|
of bits on even: P=0200000, 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 & 0x01)
|
|
CLR_FLAG(PF);
|
|
else
|
|
SET_FLAG(PF);
|
|
}
|
|
|
|
/* Set the <S>ign, <Z>ero and <P>arity flags following
|
|
an INR/DCR operation on 'reg'.
|
|
*/
|
|
|
|
void setinc(int32 reg)
|
|
{
|
|
COND_SET_FLAG(reg & 0x80, SF);
|
|
COND_SET_FLAG((reg & BYTE_R) == 0, ZF);
|
|
parity(reg);
|
|
}
|
|
|
|
/* Get an 8080 register and return it */
|
|
int32 getreg(int32 reg)
|
|
{
|
|
switch (reg) {
|
|
case 0: /* reg B */
|
|
return ((BC >>8) & BYTE_R);
|
|
case 1: /* reg C */
|
|
return (BC & BYTE_R);
|
|
case 2: /* reg D */
|
|
return ((DE >>8) & BYTE_R);
|
|
case 3: /* reg E */
|
|
return (DE & BYTE_R);
|
|
case 4: /* reg H */
|
|
return ((HL >>8) & BYTE_R);
|
|
case 5: /* reg L */
|
|
return (HL & BYTE_R);
|
|
case 6: /* reg M */
|
|
return (get_mbyte(HL));
|
|
case 7: /* reg A */
|
|
return (A);
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Put a value into an 8-bit 8080 register from memory */
|
|
void putreg(int32 reg, int32 val)
|
|
{
|
|
switch (reg) {
|
|
case 0: /* reg B */
|
|
BC = BC & BYTE_R;
|
|
BC = BC | (val <<8);
|
|
break;
|
|
case 1: /* reg C */
|
|
BC = BC & 0xFF00;
|
|
BC = BC | val;
|
|
break;
|
|
case 2: /* reg D */
|
|
DE = DE & BYTE_R;
|
|
DE = DE | (val <<8);
|
|
break;
|
|
case 3: /* reg E */
|
|
DE = DE & 0xFF00;
|
|
DE = DE | val;
|
|
break;
|
|
case 4: /* reg H */
|
|
HL = HL & BYTE_R;
|
|
HL = HL | (val <<8);
|
|
break;
|
|
case 5: /* reg L */
|
|
HL = HL & 0xFF00;
|
|
HL = HL | val;
|
|
break;
|
|
case 6: /* reg M */
|
|
put_mbyte(HL, val);
|
|
break;
|
|
case 7: /* reg A */
|
|
A = val & BYTE_R;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Return the value of a selected register pair */
|
|
int32 getpair(int32 reg)
|
|
{
|
|
switch (reg) {
|
|
case 0: /* reg BC */
|
|
return (BC);
|
|
case 1: /* reg DE */
|
|
return (DE);
|
|
case 2: /* reg HL */
|
|
return (HL);
|
|
case 3: /* reg SP */
|
|
return (SP);
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Return the value of a selected register pair, in PUSH
|
|
format where 3 means A & flags, not SP */
|
|
int32 getpush(int32 reg)
|
|
{
|
|
int32 stat;
|
|
|
|
switch (reg) {
|
|
case 0: /* reg BC */
|
|
return (BC);
|
|
case 1: /* reg DE */
|
|
return (DE);
|
|
case 2: /* reg HL */
|
|
return (HL);
|
|
case 3: /* reg (A << 8) | PSW */
|
|
stat = A << 8 | PSW;
|
|
return (stat);
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Place data into the indicated register pair, in PUSH
|
|
format where 3 means A & flags, not SP */
|
|
void putpush(int32 reg, int32 data)
|
|
{
|
|
switch (reg) {
|
|
case 0: /* reg BC */
|
|
BC = data;
|
|
break;
|
|
case 1: /* reg DE */
|
|
DE = data;
|
|
break;
|
|
case 2: /* reg HL */
|
|
HL = data;
|
|
break;
|
|
case 3: /* reg (A << 8) | PSW */
|
|
A = (data >> 8) & BYTE_R;
|
|
PSW = data & BYTE_R;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Put a value into an 8080 register pair */
|
|
void putpair(int32 reg, int32 val)
|
|
{
|
|
switch (reg) {
|
|
case 0: /* reg BC */
|
|
BC = val;
|
|
break;
|
|
case 1: /* reg DE */
|
|
DE = val;
|
|
break;
|
|
case 2: /* reg HL */
|
|
HL = val;
|
|
break;
|
|
case 3: /* reg SP */
|
|
SP = val;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Reset routine */
|
|
|
|
t_stat i8080_reset(DEVICE *dptr)
|
|
{
|
|
PSW = PSW_ALWAYS_ON;
|
|
CLR_FLAG(CF);
|
|
CLR_FLAG(ZF);
|
|
saved_PC = 0;
|
|
int_req = 0;
|
|
IM = 0;
|
|
INTA = 0;
|
|
sim_brk_types = sim_brk_dflt = SWMASK ('E');
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* This is the dumper/loader. This command uses the -h to signify a
|
|
hex dump/load vice a binary one. If no address is given to load, it
|
|
takes the address from the hex record or the current PC for binary.
|
|
*/
|
|
|
|
#define HLEN 16
|
|
|
|
int32 sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag)
|
|
{
|
|
int32 i, addr = 0, addr0 = 0, cnt = 0, cnt0 = 0, start = 0x10000;
|
|
int32 addr1 = 0, end = 0, byte, chk, rtype, flag0 = 1;
|
|
char buf[128], data[128], *p;
|
|
|
|
printf("sim_load cptr=%s fnam=%s flag=%d\n", cptr, fnam, flag);
|
|
cnt = sscanf(cptr, " %04X %04X", &start, &end);
|
|
addr=start;
|
|
printf("cnt=%d start=%05X end=%05X\n", cnt, start, end);
|
|
if (flag == 0) { //load
|
|
if (sim_switches & SWMASK ('H')) { //hex
|
|
if (cnt > 1) //2 arguments - error
|
|
return SCPE_ARG;
|
|
cnt = 0;
|
|
while (fgets(buf, sizeof(buf)-1, fileref)) {
|
|
sscanf(buf, " :%02x%04x%02x%s", &cnt, &addr, &rtype, data);
|
|
if (rtype == 0) {
|
|
chk = 0;
|
|
chk -= HLEN;
|
|
chk -= addr & BYTEMASK;
|
|
chk -= addr >> 8;
|
|
p = (char *) data;
|
|
for (i=0; i<=cnt; i++) {
|
|
sscanf (p, "%2x", &byte);
|
|
p += 2;
|
|
put_mbyte(addr + i, byte);
|
|
chk -= byte; chk &= BYTEMASK;
|
|
cnt++;
|
|
}
|
|
sscanf (p, "%2x", &byte);
|
|
if (chk == 0)
|
|
printf("+");
|
|
else
|
|
printf("-");
|
|
} else
|
|
return SCPE_ARG;
|
|
}
|
|
} else { //binary
|
|
cnt = 0;
|
|
addr1 = addr;
|
|
while ((i = getc (fileref)) != EOF) {
|
|
put_mbyte(addr, i);
|
|
addr++; cnt++;
|
|
}
|
|
}
|
|
printf ("%d Bytes loaded at %04X\n", cnt, addr1);
|
|
return (SCPE_OK);
|
|
} else { //dump
|
|
if (cnt != 2) //must be 2 arguments
|
|
return SCPE_ARG;
|
|
cnt = 0;
|
|
addr0 = addr;
|
|
if (sim_switches & SWMASK ('H')) { //hex
|
|
while((addr + HLEN) < end) { //full records
|
|
fprintf(fileref,":%02X%04X00", HLEN, addr);
|
|
chk = 0;
|
|
chk -= HLEN;
|
|
chk -= addr & BYTEMASK;
|
|
chk -= addr >> 8;
|
|
for (i=0; i<HLEN; i++) {
|
|
byte = get_mbyte(addr + i);
|
|
fprintf(fileref, "%02X", byte & BYTEMASK);
|
|
chk -= byte; chk &= BYTEMASK;
|
|
cnt++;
|
|
}
|
|
fprintf(fileref,"%02X\n", chk & BYTEMASK);
|
|
addr += HLEN;
|
|
}
|
|
if(addr < end) { //last record
|
|
fprintf(fileref, ":%02X%04X00", end - addr, addr);
|
|
chk = 0;
|
|
chk -= end - addr;
|
|
chk -= addr & BYTEMASK;
|
|
chk -= addr >> 8;
|
|
for (i=0; i<=(end - addr); i++) {
|
|
byte = get_mbyte(addr + i);
|
|
fprintf(fileref, "%02X", byte & BYTEMASK);
|
|
chk -= byte; chk &= BYTEMASK;
|
|
cnt++;
|
|
}
|
|
fprintf(fileref, "%02X\n", chk);
|
|
addr = end;
|
|
}
|
|
fprintf(fileref,":00000001FF\n"); //EOF record
|
|
} else { //binary
|
|
while (addr <= end) {
|
|
i = get_mbyte(addr);
|
|
putc(i, fileref);
|
|
addr++; cnt++;
|
|
}
|
|
}
|
|
printf ("%d Bytes dumped from %04X\n", cnt, addr0);
|
|
}
|
|
return (SCPE_OK);
|
|
}
|
|
|
|
/* Symbolic output - working
|
|
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 == &i8080_unit);
|
|
c1 = (val[0] >> 8) & 0x7F;
|
|
c2 = val[0] & 0x7F;
|
|
if (sw & SWMASK ('A')) {
|
|
fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2);
|
|
return SCPE_OK;
|
|
}
|
|
if (sw & SWMASK ('C')) {
|
|
fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1);
|
|
fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2);
|
|
return SCPE_OK;
|
|
}
|
|
if (!(sw & SWMASK ('M'))) return SCPE_ARG;
|
|
inst = val[0];
|
|
fprintf (of, "%s", opcode[inst]);
|
|
if (oplen[inst] == 2) {
|
|
// if (strchr(opcode[inst], ' ') != NULL)
|
|
// fprintf (of, ",");
|
|
// else fprintf (of, " ");
|
|
fprintf (of, "%02X", 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, "%04X", 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];
|
|
|
|
memset (gbuf, 0, sizeof (gbuf));
|
|
cflag = (uptr == NULL) || (uptr == &i8080_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' ||
|
|
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++;
|
|
}
|
|
|
|
/* Allow for 'MOV' which is only opcode that has comma in it. */
|
|
|
|
if (toupper(gbuf[0]) == 'M' &&
|
|
toupper(gbuf[1]) == 'O' &&
|
|
toupper(gbuf[2]) == 'V') {
|
|
gbuf[i] = toupper(*cptr);
|
|
cptr++;
|
|
i++;
|
|
gbuf[i] = toupper(*cptr);
|
|
cptr++;
|
|
i++;
|
|
}
|
|
|
|
/* kill trailing spaces if any */
|
|
gbuf[i] = '\0';
|
|
for (j = i - 1; gbuf[j] == ' '; j--) {
|
|
gbuf[j] = '\0';
|
|
}
|
|
|
|
/* find opcode in table */
|
|
for (j = 0; j < 256; j++) {
|
|
if (strcmp(gbuf, opcode[j]) == 0)
|
|
break;
|
|
}
|
|
if (j > 255) /* not found */
|
|
return SCPE_ARG;
|
|
|
|
val[0] = j; /* store opcode */
|
|
if (oplen[j] < 2) /* if 1-byter we are done */
|
|
return SCPE_OK;
|
|
if (*cptr == ',') cptr++;
|
|
cptr = get_glyph(cptr, gbuf, 0); /* get address */
|
|
sscanf(gbuf, "%o", &r);
|
|
if (oplen[j] == 2) {
|
|
val[1] = r & 0xFF;
|
|
return (-1);
|
|
}
|
|
val[1] = r & 0xFF;
|
|
val[2] = (r >> 8) & 0xFF;
|
|
return (-2);
|
|
}
|
|
|
|
/* Set history */
|
|
|
|
t_stat cpu_set_hist(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
int i, lnt;
|
|
t_stat r;
|
|
|
|
if (cptr == NULL) {
|
|
for (i = 0; i < hst_lnt; i++)
|
|
hst[i].pc = 0;
|
|
hst_p = 0;
|
|
return SCPE_OK;
|
|
}
|
|
lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r);
|
|
if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN)))
|
|
return SCPE_ARG;
|
|
hst_p = 0;
|
|
if (hst_lnt) {
|
|
free (hst);
|
|
hst_lnt = 0;
|
|
hst = NULL;
|
|
}
|
|
if (lnt) {
|
|
hst = (InstHistory *) calloc (lnt, sizeof (InstHistory));
|
|
if (hst == NULL)
|
|
return SCPE_MEM;
|
|
hst_lnt = lnt;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Show history */
|
|
|
|
t_stat cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
int k, di, lnt, ir;
|
|
const char *cptr = (const char *) desc;
|
|
t_stat r;
|
|
InstHistory *h;
|
|
|
|
if (hst_lnt == 0) /* enabled? */
|
|
return SCPE_NOFNC;
|
|
if (cptr) {
|
|
lnt = (int32) get_uint (cptr, 10, hst_lnt, &r);
|
|
if ((r != SCPE_OK) || (lnt == 0))
|
|
return SCPE_ARG;
|
|
}
|
|
else lnt = hst_lnt;
|
|
di = hst_p - lnt; /* work forward */
|
|
if (di < 0)
|
|
di = di + hst_lnt;
|
|
fprintf (st, "PC SP CC A B C D E F H L Instruction\n\n");
|
|
for (k = 0; k < lnt; k++) { /* print specified */
|
|
h = &hst[(di++) % hst_lnt]; /* entry pointer */
|
|
ir = h->inst[0];
|
|
fprintf (st, "%04X %04X %02X ", h->pc , h->sp, h->psw);
|
|
fprintf (st, "%02X %02X %02X %02X %02X %02X %02X ",
|
|
h->a, h->b, h->c, h->d, h->e, h->h, h->l);
|
|
if ((fprint_sym (st, h->pc, h->inst, &i8080_unit, SWMASK ('M'))) > 0)
|
|
fprintf (st, "(undefined) %02X", h->inst[0]);
|
|
fputc ('\n', st); /* end line */
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Memory examine */
|
|
|
|
t_stat i8080_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
if (addr >= MEMSIZE)
|
|
return SCPE_NXM;
|
|
if (vptr != NULL)
|
|
*vptr = get_mbyte(addr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Memory deposit */
|
|
|
|
t_stat i8080_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
if (addr >= MEMSIZE)
|
|
return SCPE_NXM;
|
|
put_mbyte(addr, val);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* end of i8080.c */
|