| /* 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 */ |