/* i8080.c: Intel 8080/8085 CPU simulator

   Copyright (c) 1997-2005, Charles E. Owen

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   CHARLES E. OWEN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

   Except as contained in this notice, the name of Charles E. Owen shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Charles E. Owen.

   This software was modified by Bill Beech, Nov 2010, to allow emulation of Intel
   iSBC Single Board Computers.

   Copyright (c) 2011, William A. Beech

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

   Except as contained in this notice, the name of William A. Beech shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from William A. Beech.

   cpu          8080 CPU

   08 Oct 02    RMS     Tied off spurious compiler warnings
   23 Nov 10    WAB     Modified for iSBC emulation
   04 Dec 12    WAB     Added 8080 interrupts
   14 Dec 12    WAB     Added 8085 interrupts

   The register state for the 8080 CPU is:

   A<0:7>               Accumulator
   BC<0:15>             BC Register Pair
   DE<0:15>             DE Register Pair
   HL<0:15>             HL Register Pair 
   PSW<0:7>             Program Status Word (Flags)
   PC<0:15>             Program counter
   SP<0:15>             Stack Pointer

   The 8080 is an 8-bit CPU, which uses 16-bit registers to address
   up to 64KB of memory.

   The 78 basic instructions come in 1, 2, and 3-byte flavors.

   This routine is the instruction decode routine for the 8080.
   It is called from the simulator control program to execute
   instructions in simulated memory, starting at the simulated PC.
   It runs until 'reason' is set non-zero.

   General notes:

   1. Reasons to stop.  The simulator can be stopped by:

        HALT instruction
        I/O error in I/O simulator
        Invalid OP code (if ITRAP is set on CPU)

   2. Interrupts.
      There are 8 possible levels of interrupt, and in effect they
      do a hardware CALL instruction to one of 8 possible low
      memory addresses.

   3. Non-existent memory.  On the 8080, reads to non-existent memory
      return 0FFh, and writes are ignored.  In the simulator, the
      largest possible memory is instantiated and initialized to zero.
      Thus, only writes need be checked against actual memory size.

   4. Adding I/O devices.  These modules must be modified:
      i8080.c - add I/O service routines to dev_table
      isys80XX_sys.c - add pointer to data structures in sim_devices
      system_defs.h - to define devices and addresses assigned to devices

    ?? ??? 11 - Original file.
    16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size.
    20 Dec 12 - Modified for basic interrupt function.
    03 Mar 13 - Added trace function.
    04 Mar 13 - Modified all instructions to truncate the affected register
                at the end of the routine.
    17 Mar 13 - Modified to enable/disable trace based on start and stop
                addresses.
*/

#include "system_defs.h"

#define UNIT_V_OPSTOP   (UNIT_V_UF)     /* Stop on Invalid OP? */
#define UNIT_OPSTOP     (1 << UNIT_V_OPSTOP)
#define UNIT_V_MSTOP    (UNIT_V_UF+1)   /* Stop on Invalid memory? */
#define UNIT_MSTOP      (1 << UNIT_V_MSTOP)
#define UNIT_V_8085     (UNIT_V_UF+2)   /* 8080/8085 switch */
#define UNIT_8085       (1 << UNIT_V_8085)
#define UNIT_V_XACK     (UNIT_V_UF+3)   /*  XACK switch */
#define UNIT_XACK       (1 << UNIT_V_XACK)

/* Flag values to set proper positions in PSW */
#define CF      0x01
#define PF      0x04
#define AF      0x10
#define ZF      0x40
#define SF      0x80

/* Macros to handle the flags in the PSW 
    8080 has bit #1 always set.  This is (not well) documented  behavior. */
#define PSW_ALWAYS_ON       (0x02)        /* for 8080 */
#define PSW_MSK (CF|PF|AF|ZF|SF)
#define TOGGLE_FLAG(FLAG)   (PSW ^= FLAG)
#define SET_FLAG(FLAG)      (PSW |= FLAG)
#define CLR_FLAG(FLAG)      (PSW &= ~FLAG)
#define GET_FLAG(FLAG)      (PSW & FLAG)
#define COND_SET_FLAG(COND,FLAG) \
    if (COND) SET_FLAG(FLAG); else CLR_FLAG(FLAG)

#define SET_XACK(VAL)       (xack = VAL)
#define GET_XACK(FLAG)      (xack &= FLAG)

/* values for IM bits */
#define ITRAP   0x100
#define SID     0x80
#define SOD     0x80
#define SDE     0x40
#define R75     0x10
#define IE      0x08
#define MSE     0x08
#define M75     0x04
#define M65     0x02
#define M55     0x01

/* register masks */
#define BYTE_R  0xFF
#define WORD_R  0xFFFF

#define i8080_NAME    "Intel i8080/85 Processor Chip"

#define HIST_MIN        64
#define HIST_MAX        (1u << 18)
#define HIST_ILNT       3                               /* max inst length */

typedef struct {
    uint16              pc;
    uint16              sp;
    uint8               psw;
    uint8               a;
    uint8               b;
    uint8               c;
    uint8               d;
    uint8               e;
    uint8               h;
    uint8               l;
    t_value             inst[HIST_ILNT];
    } InstHistory;

/* storage for the rest of the registers */
uint32 PSW = 0;                         /* program status word */
uint32 A = 0;                           /* accumulator */
uint32 BC = 0;                          /* BC register pair */
uint32 DE = 0;                          /* DE register pair */
uint32 HL = 0;                          /* HL register pair */
uint32 SP = 0;                          /* Stack pointer */
uint32 saved_PC = 0;                    /* program counter */
uint32 IM = 0;                          /* Interrupt Mask Register */
uint8  xack = 0;                        /* XACK signal */
uint32 int_req = 0;                     /* Interrupt request */
uint8 INTA = 0;                         // interrupt acknowledge
uint16 PCX;                             /* External view of PC */
uint16 PCY;                             /* Internal view of PC */
uint16 PC;
UNIT *uptr;
uint16 port;                            //port used in any IN/OUT
uint16 addr;                            //addr used for operand fetch
uint32 IR;
uint16 devnum = 0;
uint8 cpu_onetime = 0;

int32 hst_p = 0;                        /* history pointer */
int32 hst_lnt = 0;                      /* history length */
InstHistory *hst = NULL;                /* instruction history */

static const char* i8080_desc(DEVICE *dptr) {
    return i8080_NAME;
}

/* function prototypes */

void    set_cpuint(int32 int_num);
void    dumpregs(void);
int32   fetch_byte(int32 flag);
int32   fetch_word(void);
uint16  pop_word(void);
void    push_word(uint16 val);
void    setarith(int32 reg);
void    setlogical(int32 reg);
void    setinc(int32 reg);
int32   getreg(int32 reg);
void    putreg(int32 reg, int32 val);
int32   getpair(int32 reg);
int32   getpush(int32 reg);
void    putpush(int32 reg, int32 data);
void    putpair(int32 reg, int32 val);
void    parity(int32 reg);
int32   cond(int32 con);
t_stat  cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat  cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat  i8080_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
t_stat  i8080_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
t_stat  i8080_reset (DEVICE *dptr);

/* external function prototypes */

extern t_stat i8080_reset (DEVICE *dptr);
extern uint8 get_mbyte(uint16 addr);
extern uint16 get_mword(uint16 addr);
extern void put_mbyte(uint16 addr, uint8 val);
extern void put_mword(uint16 addr, uint16 val);
extern int32 sim_int_char;
extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */

struct idev {
    uint8 (*routine)(t_bool io, uint8 data, uint8 devnum); 
    uint16 port;
    uint16 devnum;
    uint8 dummy;
};

/* This is the I/O configuration table.  There are 256 possible
device addresses, if a device is plugged to a port it's routine
address is here, 'nulldev' means no device is available
*/

extern struct idev dev_table[];

/* CPU data structures
   i8080_dev      CPU device descriptor
   i8080_unit     CPU unit descriptor
   i8080_reg      CPU register list
   i8080_mod      CPU modifiers list
*/

UNIT i8080_unit = { UDATA (NULL, 0, 65535) }; /* default 8080 */

REG i8080_reg[] = {
    { HRDATA (PC, saved_PC, 16) },      /* must be first for sim_PC */
    { HRDATA (PSW, PSW, 8) },
    { HRDATA (A, A, 8) },
    { HRDATA (BC, BC, 16) },
    { HRDATA (DE, DE, 16) },
    { HRDATA (HL, HL, 16) },
    { HRDATA (SP, SP, 16) },
    { HRDATA (IM, IM, 8) },
    { HRDATA (XACK, xack, 8) },
    { HRDATA (INTR, int_req, 32) },
    { HRDATA (WRU, sim_int_char, 8) },
    { NULL }
};

MTAB i8080_mod[] = {
    { UNIT_OPSTOP, UNIT_OPSTOP, "ITRAP", "ITRAP", NULL },
    { UNIT_OPSTOP, 0, "NOITRAP", "NOITRAP", NULL },
    { UNIT_MSTOP, UNIT_MSTOP, "MTRAP", "MTRAP", NULL },
    { UNIT_MSTOP, 0, "NOMTRAP", "NOMTRAP", NULL },
    { UNIT_8085, 0, "8080", "8080", NULL },
    { UNIT_8085, UNIT_8085, "8085", "8085", NULL },
    { UNIT_XACK, 0, "NOXACK", "NOXACK", NULL },
    { UNIT_XACK, UNIT_XACK, "XACK", "XACK", NULL },
    { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP|MTAB_NC, 0, "HISTORY", "HISTORY=n",
      &cpu_set_hist, &cpu_show_hist, NULL, "Enable/Display instruction history" },
    { 0 }
};

DEBTAB i8080_debug[] = {
    { "ALL", DEBUG_all },
    { "FLOW", DEBUG_flow },
    { "READ", DEBUG_read },
    { "WRITE", DEBUG_write },
    { "XACK", DEBUG_xack },
    { NULL }
};

DEVICE i8080_dev = {
    "CPU",                            //name
    &i8080_unit,                        //units
    i8080_reg,                          //registers
    i8080_mod,                          //modifiers
    1,                                  //numunits
    16,                                 //aradix 
    16,                                 //awidth 
    1,                                  //aincr 
    16,                                 //dradix 
    8,                                  //dwidth
    &i8080_ex,                          //examine 
    &i8080_dep,                         //deposit 
    &i8080_reset,                       //reset
    NULL,                               //boot
    NULL,                               //attach 
    NULL,                               //detach
    NULL,                               //context
    DEV_DEBUG,                          //flags 
    0,                                  //dctrl 
    i8080_debug,                        //debflags
    NULL,                               //msize
    NULL,                               //lname
    NULL,                               //help routine
    NULL,                               //attach help routine
    NULL,                               //help context
    &i8080_desc                         //device description
};

/* tables for the disassembler */
const char *opcode[] = {                      
"NOP", "LXI B,", "STAX B", "INX B",             /* 0x00 */
"INR B", "DCR B", "MVI B,", "RLC",
"???", "DAD B", "LDAX B", "DCX B",
"INR C", "DCR C", "MVI C,", "RRC",
"???", "LXI D,", "STAX D", "INX D",             /* 0x10 */
"INR D", "DCR D", "MVI D,", "RAL",
"???", "DAD D", "LDAX D", "DCX D",
"INR E", "DCR E", "MVI E,", "RAR",
"RIM", "LXI H,", "SHLD ", "INX H",              /* 0x20 */
"INR H", "DCR H", "MVI H,", "DAA",
"???", "DAD H", "LHLD ", "DCX H",
"INR L", "DCR L", "MVI L", "CMA",
"SIM", "LXI SP,", "STA ", "INX SP",             /* 0x30 */
"INR M", "DCR M", "MVI M,", "STC",
"???", "DAD SP", "LDA ", "DCX SP",
"INR A", "DCR A", "MVI A,", "CMC",
"MOV B,B", "MOV B,C", "MOV B,D", "MOV B,E",     /* 0x40 */
"MOV B,H", "MOV B,L", "MOV B,M", "MOV B,A",
"MOV C,B", "MOV C,C", "MOV C,D", "MOV C,E",
"MOV C,H", "MOV C,L", "MOV C,M", "MOV C,A",
"MOV D,B", "MOV D,C", "MOV D,D", "MOV D,E",     /* 0x50 */
"MOV D,H", "MOV D,L", "MOV D,M", "MOV D,A",
"MOV E,B", "MOV E,C", "MOV E,D", "MOV E,E",
"MOV E,H", "MOV E,L", "MOV E,M", "MOV E,A",
"MOV H,B", "MOV H,C", "MOV H,D", "MOV H,E",     /* 0x60 */
"MOV H,H", "MOV H,L", "MOV H,M", "MOV H,A",
"MOV L,B", "MOV L,C", "MOV L,D", "MOV L,E",
"MOV L,H", "MOV L,L", "MOV L,M", "MOV L,A",
"MOV M,B", "MOV M,C", "MOV M,D", "MOV M,E",     /* 0x70 */
"MOV M,H", "MOV M,L", "HLT", "MOV M,A",
"MOV A,B", "MOV A,C", "MOV A,D", "MOV A,E",
"MOV A,H", "MOV A,L", "MOV A,M", "MOV A,A",
"ADD B", "ADD C", "ADD D", "ADD E",             /* 0x80 */
"ADD H", "ADD L", "ADD M", "ADD A",
"ADC B", "ADC C", "ADC D", "ADC E",
"ADC H", "ADC L", "ADC M", "ADC A",
"SUB B", "SUB C", "SUB D", "SUB E",             /* 0x90 */
"SUB H", "SUB L", "SUB M", "SUB A",
"SBB B", "SBB C", "SBB D", "SBB E",
"SBB H", "SBB L", "SBB M", "SBB A",
"ANA B", "ANA C", "ANA D", "ANA E",             /* 0xA0 */
"ANA H", "ANA L", "ANA M", "ANA A",
"XRA B", "XRA C", "XRA D", "XRA E",
"XRA H", "XRA L", "XRA M", "XRA A",
"ORA B", "ORA C", "ORA D", "ORA E",             /* 0xB0 */
"ORA H", "ORA L", "ORA M", "ORA A",
"CMP B", "CMP C", "CMP D", "CMP E",
"CMP H", "CMP L", "CMP M", "CMP A",
"RNZ", "POP B", "JNZ ", "JMP ",                 /* 0xC0 */
"CNZ ", "PUSH B", "ADI ", "RST 0",
"RZ", "RET", "JZ ", "???",
"CZ ", "CALL ", "ACI ", "RST 1",
"RNC", "POP D", "JNC ", "OUT ",                 /* 0xD0 */
"CNC ", "PUSH D", "SUI ", "RST 2",
"RC", "???", "JC ", "IN ",
"CC ", "???", "SBI ", "RST 3",
"RPO", "POP H", "JPO ", "XTHL",                 /* 0xE0 */
"CPO ", "PUSH H", "ANI ", "RST 4",
"RPE", "PCHL", "JPE ", "XCHG",
"CPE ", "???", "XRI ", "RST 5",
"RP", "POP PSW", "JP ", "DI",                   /* 0xF0 */
"CP ", "PUSH PSW", "ORI ", "RST 6",
"RM", "SPHL", "JM ", "EI",
"CM ", "???", "CPI ", "RST 7",
};

int32 oplen[256] = {
1,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1,
0,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1,
1,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1,
1,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,3,3,3,1,2,1,1,1,3,0,3,3,2,1,
1,1,3,2,3,1,2,1,1,0,3,2,3,0,2,1,
1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1,
1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1 
};

void set_cpuint(int32 int_num)
{
    int_req |= int_num;
}


/* instruction simulator */
int32 sim_instr(void)
{
    extern int32 sim_interval;
    uint32 OP, DAR, reason, adr;
    int i;
    InstHistory *hst_ent = NULL;

    PC = saved_PC & WORD_R;             /* load local PC */
    reason = 0;

    uptr = i8080_dev.units;

    if (cpu_onetime++ == 0) {
        if ((uptr->flags & UNIT_8085))
            sim_printf("    CPU = 8085\n");
        else
            sim_printf("    CPU = 8080\n");
    }
    
    /* Main instruction fetch/decode loop */

    while (reason == 0) {               /* loop until halted */

        if (i8080_dev.dctrl & DEBUG_reg) {
            dumpregs();
            sim_printf("\n");
        }

        if (sim_interval <= 0) {        /* check clock queue */
            if ((reason = sim_process_event()))
                break;
        }

        if (int_req > 0) {              /* interrupt? */
            if (uptr->flags & UNIT_8085) { /* 8085 */
                if (int_req & ITRAP) {  /* int */
                    push_word(PC);
                    PC = 0x0024;
                    int_req &= ~ITRAP;
                } else if (IM & IE) {
                    if (int_req & I75 && IM & M75) { /* int 7.5 */
                        push_word(PC);
                        PC = 0x003C;
                        int_req &= ~I75;
                    } else if (int_req & I65 && IM & M65) { /* int 6.5 */
                        push_word(PC);
                        PC = 0x0034;
                        int_req &= ~I65;
                    } else if (int_req & I55 && IM & M55) { /* int 5.5 */
                        push_word(PC);
                        PC = 0x002C;
                        int_req &= ~I55;
                    } else if (int_req & INT_R) { /* intr */
                        push_word(PC);  /* do an RST 7 */
                        PC = 0x0038;
                        int_req &= ~INT_R;
                    }
                } 
            } else {                    /* 8080 */
                if (IM & IE) {          /* enabled? */
                    INTA = 1;
                    push_word(PC);      /* do an RST 2 */
                    PC = 0x0038;
                    int_req = 0;
//                    sim_printf("8080 Interrupt\n");
                }
            }
        }                               /* end interrupt */

        if (sim_brk_summ &&
            sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */
//            dumpregs();
            reason = STOP_IBKPT;        /* stop simulation */
            break;
        }

        if (hst_lnt) {                  /* record history? */
            hst_ent = &hst[hst_p];
            hst_ent->pc = PC;
            hst_ent->sp = SP;
            hst_ent->psw = PSW;
            hst_ent->a = A;
            hst_ent->b = (BC >> 8) & 0xFF;
            hst_ent->c = (BC & 0xFF);
            hst_ent->d = (DE >> 8) & 0xFF;
            hst_ent->e = (DE & 0xFF);
            hst_ent->h = (HL >> 8) & 0xFF;
            hst_ent->l = (HL & 0xFF);
            for (i = 0; i < HIST_ILNT; i++)
                hst_ent->inst[i] = (t_value)get_mbyte (PC + i);
            hst_p = (hst_p + 1);
            if (hst_p >= hst_lnt)
                hst_p = 0;
        }

        sim_interval--;                 /* countdown clock */
        PCY = PCX = PC;
        
        IR = OP = fetch_byte(0);        /* instruction fetch */

        if (uptr->flags & UNIT_XACK) {
            if (GET_XACK(1) == 0) {         // no XACK for instruction fetch
                reason = STOP_XACK;
                sim_printf("\nFailed XACK for Instruction Fetch from %04X", PCX);
                continue;
             }
         }

        // first instruction decode
        if (OP == 0x76) {               /* HLT Instruction*/
            reason = STOP_HALT;
            PC--;
            continue;
        }

        /* Handle below all operations which refer to registers or
          register pairs.  After that, a large switch statement
          takes care of all other opcodes */

        if ((OP & 0xC0) == 0x40) {      /* MOV */
            DAR = getreg(OP & 0x07);
            putreg((OP >> 3) & 0x07, DAR);
            goto loop_end;
        }

        if ((OP & 0xC7) == 0x06) {      /* MVI */
            putreg((OP >> 3) & 0x07, fetch_byte(1));
            goto loop_end;
        }

        if ((OP & 0xCF) == 0x01) {      /* LXI */
            DAR = fetch_word();
            putpair((OP >> 4) & 0x03, DAR);
            goto loop_end;
        }

        if ((OP & 0xEF) == 0x0A) {      /* LDAX */
            DAR = getpair((OP >> 4) & 0x03);
            putreg(7, get_mbyte(DAR));
            goto loop_end;
        }

        if ((OP & 0xEF) == 0x02) {      /* STAX */
            DAR = getpair((OP >> 4) & 0x03);
            put_mbyte(DAR, getreg(7));
            goto loop_end;
        }

        if ((OP & 0xF8) == 0xB8) {      /* CMP */
            DAR = A;
            DAR -= getreg(OP & 0x07);
            setarith(DAR);
            A &= BYTE_R;                //required ***
            goto loop_end;
        }

        if ((OP & 0xC7) == 0xC2) {      /* JMP <condition> */
            adr = fetch_word();
            if (cond((OP >> 3) & 0x07))
                PC = adr;
            goto loop_end;
        }

        if ((OP & 0xC7) == 0xC4) {      /* CALL <condition> */
            adr = fetch_word();
            if (cond((OP >> 3) & 0x07)) {
                push_word(PC);
                PC = adr;
            }
            goto loop_end;
        }

        if ((OP & 0xC7) == 0xC0) {      /* RET <condition> */
            if (cond((OP >> 3) & 0x07)) {
                PC = pop_word();
            }
            goto loop_end;
        }

        if ((OP & 0xC7) == 0xC7) {      /* RST */
            push_word(PC);
            PC = OP & 0x38;
            goto loop_end;
        }

        if ((OP & 0xCF) == 0xC5) {      /* PUSH */
            DAR = getpush((OP >> 4) & 0x03);
            push_word(DAR);
            goto loop_end;
        }

        if ((OP & 0xCF) == 0xC1) {      /* POP */
            DAR = pop_word();
            putpush((OP >> 4) & 0x03, DAR);
            goto loop_end;
        }

        if ((OP & 0xF8) == 0x80) {      /* ADD */
            A += getreg(OP & 0x07);
            setarith(A);
            A &= BYTE_R;                //required
            goto loop_end;
        }

        if ((OP & 0xF8) == 0x88) {      /* ADC */
            A += getreg(OP & 0x07);
            if (GET_FLAG(CF))
                A++;
            setarith(A);
            A &= BYTE_R;                //required
            goto loop_end;
        }

        if ((OP & 0xF8) == 0x90) {      /* SUB */
            A -= getreg(OP & 0x07);
            setarith(A);
            A &= BYTE_R;                //required
            goto loop_end;
        }

        if ((OP & 0xF8) == 0x98) {      /* SBB */
            A -= getreg(OP & 0x07);
            if (GET_FLAG(CF))
                A--;
            setarith(A);
            A &= BYTE_R;                //required
            goto loop_end;
        }

        if ((OP & 0xC7) == 0x04) {      /* INR */
            DAR = getreg((OP >> 3) & 0x07);
            DAR++;
            setinc(DAR);
            DAR &= BYTE_R;              //required
            putreg((OP >> 3) & 0x07, DAR);
            goto loop_end;
        }

        if ((OP & 0xC7) == 0x05) {      /* DCR */
            DAR = getreg((OP >> 3) & 0x07);
            DAR--;
            setinc(DAR);
            DAR &= BYTE_R;              //required
            putreg((OP >> 3) & 0x07, DAR);
            goto loop_end;
        }

        if ((OP & 0xCF) == 0x03) {      /* INX */
            DAR = getpair((OP >> 4) & 0x03);
            DAR++;
            DAR &= WORD_R;              //required
            putpair((OP >> 4) & 0x03, DAR);
            goto loop_end;
        }

        if ((OP & 0xCF) == 0x0B) {      /* DCX */
            DAR = getpair((OP >> 4) & 0x03);
            DAR--;
            DAR &= WORD_R;              //required
            putpair((OP >> 4) & 0x03, DAR);
            goto loop_end;
        }

        if ((OP & 0xCF) == 0x09) {      /* DAD */
            HL += getpair((OP >> 4) & 0x03);
            COND_SET_FLAG(HL & 0x10000, CF);
            HL &= WORD_R;               //required
            goto loop_end;
        }

        if ((OP & 0xF8) == 0xA0) {      /* ANA */
            A &= getreg(OP & 0x07);
            setlogical(A);
            goto loop_end;
        }

        if ((OP & 0xF8) == 0xA8) {      /* XRA */
            A ^= getreg(OP & 0x07);
            setlogical(A);
            goto loop_end;
        }

        if ((OP & 0xF8) == 0xB0) {      /* ORA */
            A |= getreg(OP & 0x07);
            setlogical(A);
            goto loop_end;
        }

        /* The Big Instruction Decode Switch */

        switch (IR) {

        /* 8085 instructions only */
        case 0x20:                  /* RIM */
            if (i8080_unit.flags & UNIT_8085) { /* 8085 */
                A = IM;
            } else {                /* 8080 */
                reason = STOP_OPCODE;
                PC--;
            }
            break;

        case 0x30:                  /* SIM */
            if (i8080_unit.flags & UNIT_8085) { /* 8085 */
                if (A & MSE) {
                    IM &= 0xF8;
                    IM |= A & 0x07;
                }
                if (A & I75) {      /* reset RST 7.5 FF */
                }
            } else {                /* 8080 */
                reason = STOP_OPCODE;
                PC--;
            }
            break;

    /* Logical instructions */

        case 0xFE:                  /* CPI */
            DAR = A;
            DAR -= fetch_byte(1);
            setarith(DAR);
            break;

        case 0xE6:                  /* ANI */
            A &= fetch_byte(1);
            setlogical(A);
            break;

        case 0xEE:                  /* XRI */
            A ^= fetch_byte(1);
            setlogical(A);
            break;

        case 0xF6:                  /* ORI */
            A |= fetch_byte(1);
            setlogical(A);
            break;

        /* Jump instructions */

        case 0xC3:                  /* JMP */
            PC = fetch_word();
            break;

        case 0xE9:                  /* PCHL */
            PC = HL;
            break;

        case 0xCD:                  /* CALL */
            adr = fetch_word();
            push_word(PC);
            PC = adr;
            break;

        case 0xC9:                  /* RET */
            PC = pop_word();
            break;

        /* Data Transfer Group */

        case 0x32:                  /* STA */
            DAR = fetch_word();
            put_mbyte(DAR, A);
            break;

        case 0x3A:                  /* LDA */
            DAR = fetch_word();
            A = get_mbyte(DAR);
            break;

        case 0x22:                  /* SHLD */
            DAR = fetch_word();
            put_mword(DAR, HL);
            break;

        case 0x2A:                  /* LHLD */
            DAR = fetch_word();
            HL = get_mword(DAR);
            break;

        case 0xEB:                  /* XCHG */
            DAR = HL;
            HL = DE;
            HL &= WORD_R;           //required
            DE = DAR;
            break;

        /* Arithmetic Group */

        case 0xC6:                  /* ADI */
            A += fetch_byte(1);
            setarith(A);
            A &= BYTE_R;            //required
            break;

        case 0xCE:                  /* ACI */
            A += fetch_byte(1);
            if (GET_FLAG(CF))
                A++;
            setarith(A);
            A &= BYTE_R;            //required
            break;

        case 0xD6:                  /* SUI */
            A -= fetch_byte(1);
            setarith(A);
            A &= BYTE_R;            //required
            break;

        case 0xDE:                  /* SBI */
            A -= fetch_byte(1);
            if (GET_FLAG(CF))
                A--;
            A &= BYTE_R;            //required
            break;

        case 0x27:                  /* DAA */
            DAR = A & 0x0F;
            if (DAR > 9 || GET_FLAG(AF)) {
                DAR += 6;
                A &= 0xF0;
                A |= DAR & 0x0F;
                COND_SET_FLAG(DAR & 0x10, AF);
            }
            DAR = (A >> 4) & 0x0F;
            if (DAR > 9 || GET_FLAG(AF)) {
                DAR += 6;
                if (GET_FLAG(AF)) DAR++;
                A &= 0x0F;
                A |= (DAR << 4);
            }
            COND_SET_FLAG(DAR & 0x10, CF);
            COND_SET_FLAG(A & 0x80, SF);
            COND_SET_FLAG((A & 0xFF) == 0, ZF);
            A &= BYTE_R;            //required
            parity(A);
            break;

        case 0x07:                  /* RLC */
            COND_SET_FLAG(A & 0x80, CF);
            A = A << 1;
            if (GET_FLAG(CF))
                A |= 0x01;
            A &= BYTE_R;            //required
            break;

        case 0x0F:                  /* RRC */
            COND_SET_FLAG(A & 0x01, CF);
            A = A >> 1;
            if (GET_FLAG(CF))
                A |= 0x80;
            A &= BYTE_R;            //required
            break;

        case 0x17:                  /* RAL */
            DAR = GET_FLAG(CF);
            COND_SET_FLAG(A & 0x80, CF);
            A = A << 1;
            if (DAR)
                A |= 0x01;
            A &= BYTE_R;            //required
            break;

        case 0x1F:                  /* RAR */
            DAR = GET_FLAG(CF);
            COND_SET_FLAG(A & 0x01, CF);
            A = A >> 1;
            if (DAR)
                A |= 0x80;
            A &= BYTE_R;            //required
            break;

        case 0x2F:                  /* CMA */
            A = ~A;
            A &= BYTE_R;            //required
            break;

        case 0x3F:                  /* CMC */
            TOGGLE_FLAG(CF);
            break;

        case 0x37:                  /* STC */
            SET_FLAG(CF);
            break;

        /* Stack, I/O & Machine Control Group */

        case 0x00:                  /* NOP */
            break;

        case 0xE3:                  /* XTHL */
            DAR = pop_word();
            push_word(HL);
            HL = DAR;
            break;

        case 0xF9:                  /* SPHL */
            SP = HL;
            break;

        case 0xFB:                  /* EI */
            IM |= IE;
            break;

        case 0xF3:                  /* DI */
            IM &= ~IE;
            break;

        case 0xDB:                  /* IN */
            SET_XACK(1);            /* good I/O address */
            port = fetch_byte(1);
            A = dev_table[port].routine(0, 0, dev_table[port].devnum & BYTEMASK);
            break;

        case 0xD3:                  /* OUT */
            SET_XACK(1);            /* good I/O address */
            port = fetch_byte(1);
            dev_table[port].routine(1, A, dev_table[port].devnum & BYTEMASK);
            break;

        default:                    /* undefined opcode */ 
            if (i8080_unit.flags & UNIT_OPSTOP) {
                reason = STOP_OPCODE;
                PC--;
            }
            break;
        }
loop_end:

        if (uptr->flags & UNIT_XACK) {
            if (GET_XACK(1) == 0) {     // no XACK for operand fetch
//                reason = STOP_XACK;
                if (OP == 0xD3 || OP == 0xDB) {
                        sim_printf("\nFailed XACK for Port %02X Fetch from %04X", port, PCX);
                } else {
                        sim_printf("\nFailed XACK for Operand %04X Fetch from %04X", addr, PCX);
                continue;
                }
            }
        }
    }

/* Simulation halted */

    saved_PC = PC;
    return reason;
}

/* dump the registers */
void dumpregs(void)
{
    sim_printf("  PC=%04X A=%02X BC=%04X DE=%04X HL=%04X SP=%04X IM=%02X XACK=%d",
        PCY, A, BC, DE, HL, SP, IM, xack);
    sim_printf(" IR=%02X addr=%04X", IR, addr);
    sim_printf(" CF=%d ZF=%d AF=%d SF=%d PF=%d\n", 
    GET_FLAG(CF) ? 1 : 0,
    GET_FLAG(ZF) ? 1 : 0,
    GET_FLAG(AF) ? 1 : 0,
    GET_FLAG(SF) ? 1 : 0,
    GET_FLAG(PF) ? 1 : 0);
}

/* fetch an instruction or byte */
int32 fetch_byte(int32 flag)
{
    uint32 val;

    val = get_mbyte(PC) & 0xFF;         /* fetch byte */
    PC = (PC + 1) & ADDRMASK;           /* increment PC */
    addr = val & BYTEMASK;
    return val;
}

/* fetch a word */
int32 fetch_word(void)
{
    uint16 val;

    val = get_mbyte(PC) & BYTE_R;       /* fetch low byte */
    val |= get_mbyte(PC + 1) << 8;      /* fetch high byte */
//    if (i8080_dev.dctrl & DEBUG_asm || uptr->flags & UNIT_TRACE)   /* display source code */
//        sim_printf("0%04XH", val);
    PC = (PC + 2) & ADDRMASK;           /* increment PC */
    val &= WORD_R;
    addr = val;
    return val;
}

/* push a word to the stack */
void push_word(uint16 val)
{
    SP--;
    put_mbyte(SP, (val >> 8));
    SP--;
    put_mbyte(SP, val & 0xFF);
}

/* pop a word from the stack */
uint16 pop_word(void)
{
    register uint16 res;

    res = get_mbyte(SP);
    SP++;
    res |= get_mbyte(SP) << 8;
    SP++;
    return res;
}

/* Test an 8080 flag condition and return 1 if true, 0 if false */
int32 cond(int32 con)
{
    switch (con) {
    case 0:                         /* NZ */
        if (GET_FLAG(ZF) == 0) return 1;
        break;
    case 1:                         /* Z */
        if (GET_FLAG(ZF)) return 1;
        break;
    case 2:                         /* NC */
        if (GET_FLAG(CF) == 0) return 1;
        break;
    case 3:                         /* C */
        if (GET_FLAG(CF)) return 1;
        break;
    case 4:                         /* PO */
        if (GET_FLAG(PF) == 0) return 1;
        break;
    case 5:                         /* PE */
        if (GET_FLAG(PF)) return 1;
        break;
    case 6:                         /* P */
        if (GET_FLAG(SF) == 0) return 1;
        break;
    case 7:                         /* M */
        if (GET_FLAG(SF)) return 1;
        break;
    default:
        break;
    }
    return 0;
}

/* Set the <C>arry, <S>ign, <Z>ero and <P>arity flags following
   an arithmetic operation on 'reg'.
*/

void setarith(int32 reg)
{
    COND_SET_FLAG(reg & 0x100, CF);
    COND_SET_FLAG(reg & 0x80, SF);
    COND_SET_FLAG((reg & BYTE_R) == 0, ZF);
    CLR_FLAG(AF);
    parity(reg);
}

/* Set the <C>arry, <S>ign, <Z>ero amd <P>arity flags following
   a logical (bitwise) operation on 'reg'.
*/

void setlogical(int32 reg)
{
    CLR_FLAG(CF);
    COND_SET_FLAG(reg & 0x80, SF);
    COND_SET_FLAG((reg & BYTE_R) == 0, ZF);
    CLR_FLAG(AF);
    parity(reg);
}

/* Set the Parity (P) flag based on parity of 'reg', i.e., number
   of bits on even: P=0200000, else P=0
*/

void parity(int32 reg)
{
    int32 bc = 0;

    if (reg & 0x01) bc++;
    if (reg & 0x02) bc++;
    if (reg & 0x04) bc++;
    if (reg & 0x08) bc++;
    if (reg & 0x10) bc++;
    if (reg & 0x20) bc++;
    if (reg & 0x40) bc++;
    if (reg & 0x80) bc++;
    if (bc & 0x01)
        CLR_FLAG(PF);
    else
        SET_FLAG(PF);
}

/* Set the <S>ign, <Z>ero and <P>arity flags following
   an INR/DCR operation on 'reg'.
*/

void setinc(int32 reg)
{
    COND_SET_FLAG(reg & 0x80, SF);
    COND_SET_FLAG((reg & BYTE_R) == 0, ZF);
    parity(reg);
}

/* Get an 8080 register and return it */
int32 getreg(int32 reg)
{
    switch (reg) {
    case 0:                         /* reg B */
        return ((BC >>8) & BYTE_R);
    case 1:                         /* reg C */
        return (BC & BYTE_R);
    case 2:                         /* reg D */
        return ((DE >>8) & BYTE_R);
    case 3:                         /* reg E */
        return (DE & BYTE_R);
    case 4:                         /* reg H */
        return ((HL >>8) & BYTE_R);
    case 5:                         /* reg L */
        return (HL & BYTE_R);
    case 6:                         /* reg M */
        return (get_mbyte(HL));
    case 7:                         /* reg A */
        return (A);
    default:
        break;
    }
    return 0;
}

/* Put a value into an 8-bit 8080 register from memory */
void putreg(int32 reg, int32 val)
{
    switch (reg) {
    case 0:                         /* reg B */
        BC = BC & BYTE_R;
        BC = BC | (val <<8);
        break;
    case 1:                         /* reg C */
        BC = BC & 0xFF00;
        BC = BC | val;
        break;
    case 2:                         /* reg D */
        DE = DE & BYTE_R;
        DE = DE | (val <<8);
        break;
    case 3:                         /* reg E */
        DE = DE & 0xFF00;
        DE = DE | val;
        break;
    case 4:                         /* reg H */
        HL = HL & BYTE_R;
        HL = HL | (val <<8);
        break;
    case 5:                         /* reg L */
        HL = HL & 0xFF00;
        HL = HL | val;
        break;
    case 6:                         /* reg M */
        put_mbyte(HL, val);
        break;
    case 7:                         /* reg A */
        A = val & BYTE_R;
    default:
        break;
    }
}

/* Return the value of a selected register pair */
int32 getpair(int32 reg)
{
    switch (reg) {
    case 0:                         /* reg BC */
        return (BC);
    case 1:                         /* reg DE */
        return (DE);
    case 2:                         /* reg HL */
        return (HL);
    case 3:                         /* reg SP */
        return (SP);
    default:
        break;
    }
    return 0;
}

/* Return the value of a selected register pair, in PUSH
   format where 3 means A & flags, not SP */
int32 getpush(int32 reg)
{
    int32 stat;

    switch (reg) {
    case 0:                         /* reg BC */
        return (BC);
    case 1:                         /* reg DE */
        return (DE);
    case 2:                         /* reg HL */
        return (HL);
    case 3:                         /* reg (A << 8) | PSW */
        stat = A << 8 | PSW;
        return (stat);
    default:
        break;
    }
    return 0;
}


/* Place data into the indicated register pair, in PUSH
   format where 3 means A & flags, not SP */
void putpush(int32 reg, int32 data)
{
    switch (reg) {
    case 0:                         /* reg BC */
        BC = data;
        break;
    case 1:                         /* reg DE */
        DE = data;
        break;
    case 2:                         /* reg HL */
        HL = data;
        break;
    case 3:                         /* reg (A << 8) | PSW */
        A = (data >> 8) & BYTE_R;
        PSW = data & BYTE_R;
        break;
    default:
        break;
    }
}


/* Put a value into an 8080 register pair */
void putpair(int32 reg, int32 val)
{
    switch (reg) {
    case 0:                         /* reg BC */
        BC = val;
        break;
    case 1:                         /* reg DE */
        DE = val;
        break;
    case 2:                         /* reg HL */
        HL = val;
        break;
    case 3:                         /* reg SP */
        SP = val;
        break;
    default:
        break;
    }
}

/* Reset routine */

t_stat i8080_reset(DEVICE *dptr)
{
    PSW = PSW_ALWAYS_ON;
    CLR_FLAG(CF);
    CLR_FLAG(ZF);
    saved_PC = 0;
    int_req = 0;
    IM = 0;
    INTA = 0;
    sim_brk_types = sim_brk_dflt = SWMASK ('E');
    return SCPE_OK;
}

/* This is the dumper/loader. This command uses the -h to signify a
    hex dump/load vice a binary one.  If no address is given to load, it
    takes the address from the hex record or the current PC for binary.
*/

#define HLEN    16

int32 sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag)
{
    int32 i, addr = 0, addr0 = 0, cnt = 0, cnt0 = 0, start = 0x10000;
    int32 addr1 = 0, end = 0, byte, chk, rtype, flag0 = 1;
    char buf[128], data[128], *p;

    printf("sim_load cptr=%s fnam=%s flag=%d\n", cptr, fnam, flag);
    cnt = sscanf(cptr, " %04X %04X", &start, &end);
    addr=start;
    printf("cnt=%d start=%05X end=%05X\n", cnt, start, end);
    if (flag == 0) {                    //load
        if (sim_switches & SWMASK ('H')) { //hex
            if (cnt > 1)                //2 arguments - error
                return SCPE_ARG;
            cnt = 0;
            while (fgets(buf, sizeof(buf)-1, fileref)) {
                sscanf(buf, " :%02x%04x%02x%s", &cnt, &addr, &rtype, data);
                if (rtype == 0) {
                    chk = 0;
                    chk -= HLEN;
                    chk -= addr & BYTEMASK;
                    chk -= addr >> 8;
                    p = (char *) data;
                    for (i=0; i<=cnt; i++) {
                        sscanf (p, "%2x", &byte);
                        p += 2;
                        put_mbyte(addr + i, byte);
                        chk -= byte; chk &= BYTEMASK;
                        cnt++;
                    }
                    sscanf (p, "%2x", &byte);
                    if (chk == 0)
                        printf("+");
                    else
                        printf("-");
                } else 
                    return SCPE_ARG;
            }
        } else {                        //binary
            cnt = 0;
            addr1 = addr;
            while ((i = getc (fileref)) != EOF) {
                put_mbyte(addr, i);
                addr++; cnt++;
            }
        }
        printf ("%d Bytes loaded at %04X\n", cnt, addr1);
        return (SCPE_OK);
    } else {                            //dump
        if (cnt != 2)                   //must be 2 arguments
            return SCPE_ARG;
        cnt = 0;
        addr0 = addr;
        if (sim_switches & SWMASK ('H')) { //hex
            while((addr + HLEN) < end) { //full records
                fprintf(fileref,":%02X%04X00", HLEN, addr);
                chk = 0;
                chk -= HLEN;
                chk -= addr & BYTEMASK;
                chk -= addr >> 8;
                for (i=0; i<HLEN; i++) {
                    byte = get_mbyte(addr + i);
                    fprintf(fileref, "%02X", byte & BYTEMASK);
                    chk -= byte; chk &= BYTEMASK;
                    cnt++;
                }
                fprintf(fileref,"%02X\n", chk & BYTEMASK);
                addr += HLEN;
            }
            if(addr < end) { //last record
                fprintf(fileref, ":%02X%04X00", end - addr, addr);
                chk = 0;
                chk -= end - addr;
                chk -= addr & BYTEMASK;
                chk -= addr >> 8;
                for (i=0; i<=(end - addr); i++) {
                    byte = get_mbyte(addr + i);
                    fprintf(fileref, "%02X", byte & BYTEMASK);
                    chk -= byte; chk &= BYTEMASK;
                    cnt++;
                }
                fprintf(fileref, "%02X\n", chk);
                addr = end;
            }
            fprintf(fileref,":00000001FF\n"); //EOF record
        } else {                        //binary
            while (addr <= end) {
                i = get_mbyte(addr);
                putc(i, fileref);
                addr++; cnt++;
            }
        }
        printf ("%d Bytes dumped from %04X\n", cnt, addr0);
    }
    return (SCPE_OK);
}

/* Symbolic output - working
   Inputs:
        *of     =       output stream
        addr    =       current PC
        *val    =       pointer to values
        *uptr   =       pointer to unit
        sw      =       switches
   Outputs:
        status  =       error code
*/

t_stat fprint_sym(FILE *of, t_addr addr, t_value *val,
    UNIT *uptr, int32 sw)
{
    int32 cflag, c1, c2, inst, adr;

    cflag = (uptr == NULL) || (uptr == &i8080_unit);
    c1 = (val[0] >> 8) & 0x7F;
    c2 = val[0] & 0x7F;
    if (sw & SWMASK ('A')) {
        fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2);
        return SCPE_OK;
    }
    if (sw & SWMASK ('C')) {
        fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1);
        fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2);
        return SCPE_OK;
    }
    if (!(sw & SWMASK ('M'))) return SCPE_ARG;
    inst = val[0];
    fprintf (of, "%s", opcode[inst]);
    if (oplen[inst] == 2) {
//        if (strchr(opcode[inst], ' ') != NULL)
//            fprintf (of, ",");
//        else fprintf (of, " ");
        fprintf (of, "%02X", val[1]);
    }
    if (oplen[inst] == 3) {
        adr = val[1] & 0xFF;
        adr |= (val[2] << 8) & 0xff00;
//        if (strchr(opcode[inst], ' ') != NULL)
//            fprintf (of, ",");
//        else fprintf (of, " ");
        fprintf (of, "%04X", adr);
    }
    return -(oplen[inst] - 1);
}

/* Symbolic input
   Inputs:
        *cptr   =       pointer to input string
        addr    =       current PC
        *uptr   =       pointer to unit
        *val    =       pointer to output values
        sw      =       switches
   Outputs:
        status  =       error status
*/

t_stat parse_sym(CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
{
    int32 cflag, i = 0, j, r;
    char gbuf[CBUFSIZE];

    memset (gbuf, 0, sizeof (gbuf));
    cflag = (uptr == NULL) || (uptr == &i8080_unit);
    while (isspace (*cptr)) cptr++;                         /* absorb spaces */
    if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */
        if (cptr[0] == 0) return SCPE_ARG;                  /* must have 1 char */
        val[0] = (uint32) cptr[0];
        return SCPE_OK;
    }
    if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */
        if (cptr[0] == 0) return SCPE_ARG;                  /* must have 1 char */
        val[0] = ((uint32) cptr[0] << 8) + (uint32) cptr[1];
        return SCPE_OK;
    }

/* An instruction: get opcode (all characters until null, comma,
   or numeric (including spaces).
*/

    while (i < sizeof (gbuf) - 4) {
        if (*cptr == ',' || *cptr == '\0' ||
             isdigit(*cptr))
                break;
        gbuf[i] = toupper(*cptr);
        cptr++;
        i++;
    }

/* Allow for RST which has numeric as part of opcode */

    if (toupper(gbuf[0]) == 'R' &&
        toupper(gbuf[1]) == 'S' &&
        toupper(gbuf[2]) == 'T') {
        gbuf[i] = toupper(*cptr);
        cptr++;
        i++;
    }

/* Allow for 'MOV' which is only opcode that has comma in it. */

    if (toupper(gbuf[0]) == 'M' &&
        toupper(gbuf[1]) == 'O' &&
        toupper(gbuf[2]) == 'V') {
        gbuf[i] = toupper(*cptr);
        cptr++;
        i++;
        gbuf[i] = toupper(*cptr);
        cptr++;
        i++;
    }

/* kill trailing spaces if any */
    gbuf[i] = '\0';
    for (j = i - 1; gbuf[j] == ' '; j--) {
        gbuf[j] = '\0';
    }

/* find opcode in table */
    for (j = 0; j < 256; j++) {
        if (strcmp(gbuf, opcode[j]) == 0)
            break;
    }
    if (j > 255)                                            /* not found */
        return SCPE_ARG;

    val[0] = j;                                             /* store opcode */
    if (oplen[j] < 2)                                       /* if 1-byter we are done */
        return SCPE_OK;
    if (*cptr == ',') cptr++;
    cptr = get_glyph(cptr, gbuf, 0);                        /* get address */
    sscanf(gbuf, "%o", &r);
    if (oplen[j] == 2) {
        val[1] = r & 0xFF;
        return (-1);
    }
    val[1] = r & 0xFF;
    val[2] = (r >> 8) & 0xFF;
    return (-2);
}

/* Set history */

t_stat cpu_set_hist(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
    int i, lnt;
    t_stat r;

    if (cptr == NULL) {
        for (i = 0; i < hst_lnt; i++)
            hst[i].pc = 0;
        hst_p = 0;
        return SCPE_OK;
        }
    lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r);
    if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN)))
        return SCPE_ARG;
    hst_p = 0;
    if (hst_lnt) {
        free (hst);
        hst_lnt = 0;
        hst = NULL;
        }
    if (lnt) {
        hst = (InstHistory *) calloc (lnt, sizeof (InstHistory));
        if (hst == NULL)
            return SCPE_MEM;
        hst_lnt = lnt;
        }
    return SCPE_OK;
}

/* Show history */

t_stat cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
    int k, di, lnt, ir;
    const char *cptr = (const char *) desc;
    t_stat r;
    InstHistory *h;

    if (hst_lnt == 0)                       /* enabled? */
        return SCPE_NOFNC;
    if (cptr) {
        lnt = (int32) get_uint (cptr, 10, hst_lnt, &r);
        if ((r != SCPE_OK) || (lnt == 0))
            return SCPE_ARG;
        }
    else lnt = hst_lnt;
    di = hst_p - lnt;                       /* work forward */
    if (di < 0)
        di = di + hst_lnt;
    fprintf (st, "PC   SP   CC A  B  C  D  E  F  H  L  Instruction\n\n");
    for (k = 0; k < lnt; k++) {             /* print specified */
        h = &hst[(di++) % hst_lnt];         /* entry pointer */
        ir = h->inst[0];
        fprintf (st, "%04X %04X %02X ", h->pc , h->sp, h->psw);
        fprintf (st, "%02X %02X %02X %02X %02X %02X %02X ", 
            h->a, h->b, h->c, h->d, h->e, h->h, h->l);
        if ((fprint_sym (st, h->pc, h->inst, &i8080_unit, SWMASK ('M'))) > 0)
            fprintf (st, "(undefined) %02X", h->inst[0]);
        fputc ('\n', st);                               /* end line */
        }
    return SCPE_OK;
}

/* Memory examine */

t_stat i8080_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
    if (addr >= MEMSIZE) 
        return SCPE_NXM;
    if (vptr != NULL) 
        *vptr = get_mbyte(addr);
    return SCPE_OK;
}

/* Memory deposit */

t_stat i8080_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
    if (addr >= MEMSIZE) 
        return SCPE_NXM;
    put_mbyte(addr, val);
    return SCPE_OK;
}

/* end of i8080.c */
