|  | /* 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: | 
|  |  | 
|  | altair_cpu.c    add I/O service routines to dev_table | 
|  | altair_sys.c    add pointer to data structures in sim_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_8085     (UNIT_V_UF+1)   /* 8080/8085 switch */ | 
|  | #define UNIT_8085       (1 << UNIT_V_8085) | 
|  | #define UNIT_V_TRACE    (UNIT_V_UF+2)   /*  Trace switch */ | 
|  | #define UNIT_TRACE      (1 << UNIT_V_TRACE) | 
|  |  | 
|  | /* 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 | 
|  |  | 
|  | /* 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 */ | 
|  | uint32 xack = 0;                        /* XACK signal */ | 
|  | uint32 int_req = 0;                     /* Interrupt request */ | 
|  |  | 
|  | int32 PCX;                              /* External view of PC */ | 
|  | int32 PC; | 
|  | UNIT *uptr; | 
|  |  | 
|  | /* 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  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 int32 get_mbyte(int32 addr); | 
|  | extern int32 get_mword(int32 addr); | 
|  | extern void put_mbyte(int32 addr, int32 val); | 
|  | extern void put_mword(int32 addr, int32 val); | 
|  | extern int32 sim_int_char; | 
|  | extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ | 
|  |  | 
|  |  | 
|  | struct idev { | 
|  | int32 (*routine)(); | 
|  | }; | 
|  |  | 
|  | /* 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_8085, 0, "8080", "8080", NULL }, | 
|  | { UNIT_8085, UNIT_8085, "8085", "8085", NULL }, | 
|  | { UNIT_OPSTOP, 0, "ITRAP", "ITRAP", NULL }, | 
|  | { UNIT_OPSTOP, UNIT_OPSTOP, "NOITRAP", "NOITRAP", NULL }, | 
|  | { UNIT_TRACE, 0, "NOTRACE", "NOTRACE", NULL }, | 
|  | { UNIT_TRACE, UNIT_TRACE, "TRACE", "TRACE", NULL }, | 
|  | { 0 } | 
|  | }; | 
|  |  | 
|  | DEBTAB i8080_debug[] = { | 
|  | { "ALL", DEBUG_all }, | 
|  | { "FLOW", DEBUG_flow }, | 
|  | { "READ", DEBUG_read }, | 
|  | { "WRITE", DEBUG_write }, | 
|  | { "LEV1", DEBUG_level1 }, | 
|  | { "LEV2", DEBUG_level2 }, | 
|  | { "REG", DEBUG_reg }, | 
|  | { "ASM", DEBUG_asm }, | 
|  | { 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,                       //reset | 
|  | NULL,                               //boot | 
|  | NULL,                               //attach | 
|  | NULL,                               //detach | 
|  | NULL,                               //ctxt | 
|  | DEV_DEBUG,                          //flags | 
|  | 0,                                  //dctrl | 
|  | i8080_debug,                        //debflags | 
|  | NULL,                               //msize | 
|  | NULL                                //lname | 
|  | }; | 
|  |  | 
|  | /* tables for the disassembler */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | //FILE    *fpd; | 
|  |  | 
|  | /* instruction simulator */ | 
|  | int32 sim_instr (void) | 
|  | { | 
|  | extern int32 sim_interval; | 
|  | uint32 IR, OP, DAR, reason, adr; | 
|  |  | 
|  | PC = saved_PC & WORD_R;             /* load local PC */ | 
|  | reason = 0; | 
|  |  | 
|  | uptr = i8080_dev.units; | 
|  | if (i8080_dev.dctrl & DEBUG_flow) { | 
|  | 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 (PC == 0x1000) {             /* turn on debugging */ | 
|  | //            i8080_dev.dctrl = DEBUG_asm + DEBUG_reg; | 
|  | //            reason = STOP_HALT; | 
|  | //        } | 
|  | if (i8080_dev.dctrl & DEBUG_reg) { | 
|  | dumpregs(); | 
|  | sim_printf("\n"); | 
|  | } | 
|  |  | 
|  | if (sim_interval <= 0) {        /* check clock queue */ | 
|  | if ((reason = sim_process_event())) | 
|  | break; | 
|  | } | 
|  | sim_interval--;                 /* countdown clock */ | 
|  |  | 
|  | if (int_req > 0) {              /* interrupt? */ | 
|  | //            sim_printf("\ni8080: int_req=%04X IM=%04X", int_req, IM); | 
|  | 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? */ | 
|  | push_word(PC);      /* do an RST 7 */ | 
|  | PC = 0x0038; | 
|  | int_req &= ~INT_R; | 
|  | //                    sim_printf("\ni8080: int_req=%04X", int_req); | 
|  | } | 
|  | } | 
|  | }                               /* end interrupt */ | 
|  |  | 
|  | if (sim_brk_summ && | 
|  | sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ | 
|  | reason = STOP_IBKPT;        /* stop simulation */ | 
|  | break; | 
|  | } | 
|  |  | 
|  | PCX = PC; | 
|  |  | 
|  | //        fprintf(fpd, "%04X\n", PC); | 
|  | //        fflush(fpd); | 
|  |  | 
|  | if (uptr->flags & UNIT_TRACE) { | 
|  | dumpregs(); | 
|  | sim_printf("\n"); | 
|  | } | 
|  | IR = OP = fetch_byte(0);        /* instruction fetch */ | 
|  |  | 
|  | if (GET_XACK(1) == 0) {         /* no XACK for instruction fetch */ | 
|  | reason = STOP_XACK; | 
|  | sim_printf("Stopped for XACK-1 PC=%04X\n", --PC); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | 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 & 0xFF; | 
|  | DAR -= getreg(OP & 0x07); | 
|  | setarith(DAR); | 
|  | DAR &= BYTE_R; | 
|  | 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; | 
|  | goto loop_end; | 
|  | } | 
|  |  | 
|  | if ((OP & 0xF8) == 0x88) {      /* ADC */ | 
|  | A += getreg(OP & 0x07); | 
|  | if (GET_FLAG(CF)) | 
|  | A++; | 
|  | setarith(A); | 
|  | A &= BYTE_R; | 
|  | goto loop_end; | 
|  | } | 
|  |  | 
|  | if ((OP & 0xF8) == 0x90) {      /* SUB */ | 
|  | A -= getreg(OP & 0x07); | 
|  | setarith(A); | 
|  | A &= BYTE_R; | 
|  | goto loop_end; | 
|  | } | 
|  |  | 
|  | if ((OP & 0xF8) == 0x98) {      /* SBB */ | 
|  | A -= getreg(OP & 0x07); | 
|  | if (GET_FLAG(CF)) | 
|  | A--; | 
|  | setarith(A); | 
|  | A &= BYTE_R; | 
|  | goto loop_end; | 
|  | } | 
|  |  | 
|  | if ((OP & 0xC7) == 0x04) {      /* INR */ | 
|  | DAR = getreg((OP >> 3) & 0x07); | 
|  | DAR++; | 
|  | setinc(DAR); | 
|  | //            DAR &= BYTE_R; | 
|  | putreg((OP >> 3) & 0x07, DAR); | 
|  | goto loop_end; | 
|  | } | 
|  |  | 
|  | if ((OP & 0xC7) == 0x05) {      /* DCR */ | 
|  | DAR = getreg((OP >> 3) & 0x07); | 
|  | DAR--; | 
|  | setinc(DAR); | 
|  | //            DAR &= BYTE_R; | 
|  | putreg((OP >> 3) & 0x07, DAR); | 
|  | goto loop_end; | 
|  | } | 
|  |  | 
|  | if ((OP & 0xCF) == 0x03) {      /* INX */ | 
|  | DAR = getpair((OP >> 4) & 0x03); | 
|  | DAR++; | 
|  | //            DAR &= WORD_R; | 
|  | putpair((OP >> 4) & 0x03, DAR); | 
|  | goto loop_end; | 
|  | } | 
|  |  | 
|  | if ((OP & 0xCF) == 0x0B) {      /* DCX */ | 
|  | DAR = getpair((OP >> 4) & 0x03); | 
|  | DAR--; | 
|  | //            DAR &= WORD_R; | 
|  | 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; | 
|  | 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); | 
|  | A &= BYTE_R; | 
|  | 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); | 
|  | //                DAR &= BYTE_R; | 
|  | setarith(DAR); | 
|  | break; | 
|  |  | 
|  | case 0xE6:                  /* ANI */ | 
|  | A &= fetch_byte(1); | 
|  | setlogical(A); | 
|  | break; | 
|  |  | 
|  | case 0xEE:                  /* XRI */ | 
|  | A ^= fetch_byte(1); | 
|  | //                DAR &= BYTE_R; | 
|  | 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(); | 
|  | DAR &= WORD_R; | 
|  | put_mbyte(DAR, A); | 
|  | break; | 
|  |  | 
|  | case 0x3A:                  /* LDA */ | 
|  | DAR = fetch_word(); | 
|  | DAR &= WORD_R; | 
|  | A = get_mbyte(DAR); | 
|  | break; | 
|  |  | 
|  | case 0x22:                  /* SHLD */ | 
|  | DAR = fetch_word(); | 
|  | DAR &= WORD_R; | 
|  | put_mword(DAR, HL); | 
|  | break; | 
|  |  | 
|  | case 0x2A:                  /* LHLD */ | 
|  | DAR = fetch_word(); | 
|  | DAR &= WORD_R; | 
|  | HL = get_mword(DAR); | 
|  | break; | 
|  |  | 
|  | case 0xEB:                  /* XCHG */ | 
|  | DAR = HL; | 
|  | HL = DE; | 
|  | HL &= WORD_R; | 
|  | DE = DAR; | 
|  | DE &= WORD_R; | 
|  | break; | 
|  |  | 
|  | /* Arithmetic Group */ | 
|  |  | 
|  | case 0xC6:                  /* ADI */ | 
|  | A += fetch_byte(1); | 
|  | setarith(A); | 
|  | A &= BYTE_R; | 
|  | break; | 
|  |  | 
|  | case 0xCE:                  /* ACI */ | 
|  | A += fetch_byte(1); | 
|  | if (GET_FLAG(CF)) | 
|  | A++; | 
|  | setarith(A); | 
|  | A &= BYTE_R; | 
|  | break; | 
|  |  | 
|  | case 0xD6:                  /* SUI */ | 
|  | A -= fetch_byte(1); | 
|  | setarith(A); | 
|  | A &= BYTE_R; | 
|  | break; | 
|  |  | 
|  | case 0xDE:                  /* SBI */ | 
|  | A -= fetch_byte(1); | 
|  | if (GET_FLAG(CF)) | 
|  | A--; | 
|  | setarith(A); | 
|  | A &= BYTE_R; | 
|  | 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); | 
|  | parity(A); | 
|  | break; | 
|  |  | 
|  | case 0x07:                  /* RLC */ | 
|  | COND_SET_FLAG(A & 0x80, CF); | 
|  | A = (A << 1) & 0xFF; | 
|  | if (GET_FLAG(CF)) | 
|  | A |= 0x01; | 
|  | A &= BYTE_R; | 
|  | break; | 
|  |  | 
|  | case 0x0F:                  /* RRC */ | 
|  | COND_SET_FLAG(A & 0x01, CF); | 
|  | A = (A >> 1) & 0xFF; | 
|  | if (GET_FLAG(CF)) | 
|  | A |= 0x80; | 
|  | A &= BYTE_R; | 
|  | break; | 
|  |  | 
|  | case 0x17:                  /* RAL */ | 
|  | DAR = GET_FLAG(CF); | 
|  | COND_SET_FLAG(A & 0x80, CF); | 
|  | A = (A << 1) & 0xFF; | 
|  | if (DAR) | 
|  | A |= 0x01; | 
|  | A &= BYTE_R; | 
|  | break; | 
|  |  | 
|  | case 0x1F:                  /* RAR */ | 
|  | DAR = GET_FLAG(CF); | 
|  | COND_SET_FLAG(A & 0x01, CF); | 
|  | A = (A >> 1) & 0xFF; | 
|  | if (DAR) | 
|  | A |= 0x80; | 
|  | A &= BYTE_R; | 
|  | break; | 
|  |  | 
|  | case 0x2F:                  /* CMA */ | 
|  | A = ~A; | 
|  | A &= BYTE_R; | 
|  | 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; | 
|  | HL &= WORD_R; | 
|  | break; | 
|  |  | 
|  | case 0xF9:                  /* SPHL */ | 
|  | SP = HL; | 
|  | break; | 
|  |  | 
|  | case 0xFB:                  /* EI */ | 
|  | IM |= IE; | 
|  | //                sim_printf("\nEI: pc=%04X", PC - 1); | 
|  | break; | 
|  |  | 
|  | case 0xF3:                  /* DI */ | 
|  | IM &= ~IE; | 
|  | //                sim_printf("\nDI: pc=%04X", PC - 1); | 
|  | break; | 
|  |  | 
|  | case 0xDB:                  /* IN */ | 
|  | DAR = fetch_byte(1); | 
|  | A = dev_table[DAR].routine(0, 0); | 
|  | A &= BYTE_R; | 
|  | break; | 
|  |  | 
|  | case 0xD3:                  /* OUT */ | 
|  | DAR = fetch_byte(1); | 
|  | dev_table[DAR].routine(1, A); | 
|  | break; | 
|  |  | 
|  | default:                    /* undefined opcode */ | 
|  | if (i8080_unit.flags & UNIT_OPSTOP) { | 
|  | reason = STOP_OPCODE; | 
|  | PC--; | 
|  | } | 
|  | break; | 
|  | } | 
|  | loop_end: | 
|  | if (GET_XACK(1) == 0) {         /* no XACK for instruction fetch */ | 
|  | reason = STOP_XACK; | 
|  | sim_printf("Stopped for XACK-2 PC=%04X\n", --PC); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | /* Simulation halted */ | 
|  |  | 
|  | saved_PC = PC; | 
|  | //    fclose(fpd); | 
|  | return reason; | 
|  | } | 
|  |  | 
|  | /* dump the registers */ | 
|  | void dumpregs(void) | 
|  | { | 
|  | sim_printf("  A=%02X BC=%04X DE=%04X HL=%04X SP=%04X IM=%02X XACK=%d\n", | 
|  | A, BC, DE, HL, SP, IM, xack); | 
|  | 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 */ | 
|  | if (i8080_dev.dctrl & DEBUG_asm || uptr->flags & UNIT_TRACE) {  /* display source code */ | 
|  | switch (flag) { | 
|  | case 0:                     /* opcode fetch */ | 
|  | sim_printf("OP=%02X        %04X %s", val,  PC, opcode[val]); | 
|  | break; | 
|  | case 1:                     /* byte operand fetch */ | 
|  | sim_printf("0%02XH", val); | 
|  | break; | 
|  | } | 
|  | } | 
|  | PC = (PC + 1) & ADDRMASK;           /* increment PC */ | 
|  | val &= BYTE_R; | 
|  | 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; | 
|  | 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 amd <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 */ | 
|  | //           sim_printf("reg=%04X BC=%04X ret=%04X\n", | 
|  | //               reg, BC, (BC >>8) & 0xff); | 
|  | 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 */ | 
|  | //            sim_printf("reg=%04X val=%04X\n", reg, val); | 
|  | BC = BC & BYTE_R; | 
|  | //            sim_printf("BC&0x00ff=%04X val<<8=%04X\n", BC, val<<8); | 
|  | 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; | 
|  | sim_brk_types = sim_brk_dflt = SWMASK ('E'); | 
|  | sim_printf("   8080: Reset\n"); | 
|  | //    fpd = fopen("trace.txt", "w"); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* This is the binary loader.  The input file is considered to be | 
|  | a string of literal bytes with no special format. The load | 
|  | starts at the current value of the PC. | 
|  | */ | 
|  |  | 
|  | int32 sim_load (FILE *fileref, char *cptr, char *fnam, int flag) | 
|  | { | 
|  | int32 i, addr = 0, cnt = 0; | 
|  |  | 
|  | if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; | 
|  | addr = saved_PC; | 
|  | while ((i = getc (fileref)) != EOF) { | 
|  | put_mbyte(addr, i); | 
|  | addr++; | 
|  | cnt++; | 
|  | }                                   /* end while */ | 
|  | sim_printf ("%d Bytes loaded.\n", cnt); | 
|  | 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 == &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 (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) | 
|  | { | 
|  | int32 cflag, i = 0, j, r; | 
|  | char gbuf[CBUFSIZE]; | 
|  |  | 
|  | 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 (1) { | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* end of i8080.c */ |