| /* 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_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 */ |
| uint8 xack = 0; /* XACK signal */ |
| uint32 int_req = 0; /* Interrupt request */ |
| |
| int32 PCX; /* External view of PC */ |
| int32 PC; |
| UNIT *uptr; |
| uint32 port; //port used in any IN/OUT |
| |
| /* 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 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, uint8); |
| uint16 port; |
| uint8 devnum; |
| }; |
| |
| /* 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 = { |
| "I8080", //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 */ |
| 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 IR, OP, DAR, reason, adr; |
| |
| PC = saved_PC & WORD_R; /* load local PC */ |
| reason = 0; |
| |
| uptr = i8080_dev.units; |
| 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? */ |
| push_word(PC); /* do an RST 7 */ |
| PC = 0x0038; |
| int_req &= ~INT_R; |
| } |
| } |
| } /* end interrupt */ |
| |
| if (sim_brk_summ && |
| sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ |
| reason = STOP_IBKPT; /* stop simulation */ |
| break; |
| } |
| |
| sim_interval--; /* countdown clock */ |
| PCX = PC; |
| |
| 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; |
| } |
| |
| // 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); |
| 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); |
| putreg((OP >> 3) & 0x07, DAR); |
| goto loop_end; |
| } |
| |
| if ((OP & 0xC7) == 0x05) { /* DCR */ |
| DAR = getreg((OP >> 3) & 0x07); |
| DAR--; |
| setinc(DAR); |
| putreg((OP >> 3) & 0x07, DAR); |
| goto loop_end; |
| } |
| |
| if ((OP & 0xCF) == 0x03) { /* INX */ |
| DAR = getpair((OP >> 4) & 0x03); |
| DAR++; |
| putpair((OP >> 4) & 0x03, DAR); |
| goto loop_end; |
| } |
| |
| if ((OP & 0xCF) == 0x0B) { /* DCX */ |
| DAR = getpair((OP >> 4) & 0x03); |
| DAR--; |
| 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 |
| 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; |
| break; |
| |
| case 0x0F: /* RRC */ |
| COND_SET_FLAG(A & 0x01, CF); |
| A = (A >> 1) & 0xFF; |
| if (GET_FLAG(CF)) |
| A |= 0x80; |
| break; |
| |
| case 0x17: /* RAL */ |
| DAR = GET_FLAG(CF); |
| COND_SET_FLAG(A & 0x80, CF); |
| A = (A << 1) & 0xFF; |
| if (DAR) |
| A |= 0x01; |
| break; |
| |
| case 0x1F: /* RAR */ |
| DAR = GET_FLAG(CF); |
| COND_SET_FLAG(A & 0x01, CF); |
| A = (A >> 1) & 0xFF; |
| if (DAR) |
| A |= 0x80; |
| break; |
| |
| case 0x2F: /* CMA */ |
| A = ~A; |
| 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 */ |
| DAR = fetch_byte(1); |
| port = DAR; |
| A = dev_table[DAR].routine(0, 0); |
| break; |
| |
| case 0xD3: /* OUT */ |
| DAR = fetch_byte(1); |
| port = DAR; |
| 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; |
| 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 */ |
| 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; |
| sim_brk_types = sim_brk_dflt = SWMASK ('E'); |
| sim_printf(" 8080: Reset\n"); |
| 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, CONST char *cptr, CONST 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 - 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]; |
| |
| 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 */ |