| /* | |
| * Dos/PC Emulator | |
| * Copyright (C) 1991 Jim Hudgens | |
| * | |
| * | |
| * The file is part of GDE. | |
| * | |
| * GDE is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; either version 1, or (at your option) | |
| * any later version. | |
| * | |
| * GDE is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with GDE; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include "altairz80_defs.h" | |
| #include "i86.h" | |
| extern uint32 GetBYTEExtended(register uint32 Addr); | |
| extern void PutBYTEExtended(register uint32 Addr, const register uint32 Value); | |
| extern int32 AX_S; /* AX register (8086) */ | |
| extern int32 BX_S; /* BX register (8086) */ | |
| extern int32 CX_S; /* CX register (8086) */ | |
| extern int32 DX_S; /* DX register (8086) */ | |
| extern int32 CS_S; /* CS register (8086) */ | |
| extern int32 DS_S; /* DS register (8086) */ | |
| extern int32 ES_S; /* ES register (8086) */ | |
| extern int32 SS_S; /* SS register (8086) */ | |
| extern int32 DI_S; /* DI register (8086) */ | |
| extern int32 SI_S; /* SI register (8086) */ | |
| extern int32 BP_S; /* BP register (8086) */ | |
| extern int32 SPX_S; /* SP register (8086) */ | |
| extern int32 IP_S; /* IP register (8086) */ | |
| extern int32 FLAGS_S; /* flags register (8086) */ | |
| extern int32 PCX_S; /* PC register (8086), 20 bit */ | |
| extern uint32 PCX; /* external view of PC */ | |
| extern UNIT cpu_unit; | |
| #if !UNIX_PLATFORM | |
| extern void pollForCPUStop(void); | |
| #endif | |
| void i86_intr_raise(PC_ENV *m,uint8 intrnum); | |
| void cpu8086reset(void); | |
| t_stat sim_instr_8086(void); | |
| void cpu8086_intr(uint8 intrnum); | |
| /* $Log: $ | |
| * Revision 0.05 1992/04/12 23:16:42 hudgens | |
| * Many changes. Added support for the QUICK_FETCH option, | |
| * so that memory accesses are faster. Now compiles with gcc -Wall | |
| * and gcc -traditional and Sun cc. | |
| * | |
| * Revision 0.04 1991/07/30 01:59:56 hudgens | |
| * added copyright. | |
| * | |
| * Revision 0.03 1991/06/03 01:02:09 hudgens | |
| * fixed minor problems due to unsigned to signed short integer | |
| * promotions. | |
| * | |
| * Revision 0.02 1991/03/31 01:29:39 hudgens | |
| * Fixed segment handling (overrides, default segment accessed) in | |
| * routines decode_rmXX_address and the {fetch,store}_data_{byte,word}. | |
| * | |
| * Revision 0.01 1991/03/30 21:59:49 hudgens | |
| * Initial checkin. | |
| * | |
| * | |
| */ | |
| /* this file includes subroutines which do: | |
| stuff involving decoding instruction formats. | |
| stuff involving accessess of immediate data via IP. | |
| etc. | |
| */ | |
| static void i86_intr_handle(PC_ENV *m) | |
| { uint16 tmp; | |
| uint8 intno; | |
| if (intr & INTR_SYNCH) /* raised by something */ | |
| { | |
| intno = m->intno; | |
| { | |
| tmp = m->R_FLG; | |
| push_word(m, tmp); | |
| CLEAR_FLAG(m, F_IF); | |
| CLEAR_FLAG(m, F_TF); | |
| /* [JCE] If we're interrupting between a segment override (or REP override) | |
| * and the following instruction, decrease IP to get back to the prefix */ | |
| if (m->sysmode & (SYSMODE_SEGMASK | SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) | |
| { | |
| --m->R_IP; | |
| } | |
| /* [JCE] CS and IP were the wrong way round... */ | |
| push_word(m, m->R_CS); | |
| push_word(m, m->R_IP); | |
| tmp = mem_access_word(m, intno * 4); | |
| m->R_IP = tmp; | |
| tmp = mem_access_word(m, intno * 4 + 2); | |
| m->R_CS = tmp; | |
| } | |
| intr &= ~INTR_SYNCH; /* [JCE] Dealt with, reset flag */ | |
| } | |
| /* The interrupt code can't pick up the segment override status. */ | |
| DECODE_CLEAR_SEGOVR(m); | |
| } | |
| void i86_intr_raise(PC_ENV *m,uint8 intrnum) | |
| { | |
| m->intno = intrnum; | |
| intr |= INTR_SYNCH; | |
| } | |
| static PC_ENV cpu8086; | |
| void cpu8086_intr(uint8 intrnum) | |
| { | |
| i86_intr_raise(&cpu8086, intrnum); | |
| } | |
| static void setViewRegisters(void) { | |
| FLAGS_S = cpu8086.R_FLG; | |
| AX_S = cpu8086.R_AX; | |
| BX_S = cpu8086.R_BX; | |
| CX_S = cpu8086.R_CX; | |
| DX_S = cpu8086.R_DX; | |
| SPX_S = cpu8086.R_SP; | |
| BP_S = cpu8086.R_BP; | |
| SI_S = cpu8086.R_SI; | |
| DI_S = cpu8086.R_DI; | |
| ES_S = cpu8086.R_ES; | |
| CS_S = cpu8086.R_CS; | |
| SS_S = cpu8086.R_SS; | |
| DS_S = cpu8086.R_DS; | |
| IP_S = cpu8086.R_IP; | |
| } | |
| static void setCPURegisters(void) { | |
| cpu8086.R_FLG = FLAGS_S; | |
| cpu8086.R_AX = AX_S; | |
| cpu8086.R_BX = BX_S; | |
| cpu8086.R_CX = CX_S; | |
| cpu8086.R_DX = DX_S; | |
| cpu8086.R_SP = SPX_S; | |
| cpu8086.R_BP = BP_S; | |
| cpu8086.R_SI = SI_S; | |
| cpu8086.R_DI = DI_S; | |
| cpu8086.R_ES = ES_S; | |
| cpu8086.R_CS = CS_S; | |
| cpu8086.R_SS = SS_S; | |
| cpu8086.R_DS = DS_S; | |
| cpu8086.R_IP = IP_S; | |
| } | |
| void cpu8086reset(void) { | |
| cpu8086.R_AX = 0x1961; | |
| if ((cpu8086.R_AH != 0x19) || (cpu8086.R_AL != 0x61)) { | |
| sim_printf("Fatal endian error - make sure to compile with '#define LOWFIRST %i'\n", 1 - LOWFIRST); | |
| exit(1); | |
| } | |
| /* 16 bit registers */ | |
| cpu8086.R_AX = 0; | |
| cpu8086.R_BX = 0; | |
| cpu8086.R_CX = 0; | |
| cpu8086.R_DX = 0; | |
| /* special registers */ | |
| cpu8086.R_SP = 0; | |
| cpu8086.R_BP = 0; | |
| cpu8086.R_SI = 0; | |
| cpu8086.R_DI = 0; | |
| cpu8086.R_IP = 0; | |
| cpu8086.R_FLG = F_ALWAYS_ON; | |
| /* segment registers */ | |
| cpu8086.R_CS = 0; | |
| cpu8086.R_DS = 0; | |
| cpu8086.R_SS = 0; | |
| cpu8086.R_ES = 0; | |
| setViewRegisters(); | |
| } | |
| static uint32 getFullPC(void) { | |
| return cpu8086.R_IP + (cpu8086.R_CS << 4); | |
| } | |
| extern int32 switch_cpu_now; /* hharte */ | |
| t_stat sim_instr_8086(void) { | |
| t_stat reason = SCPE_OK; | |
| uint8 op1; | |
| int32 newIP; | |
| setCPURegisters(); | |
| intr = 0; | |
| newIP = PCX_S - 16 * CS_S; | |
| switch_cpu_now = TRUE; /* hharte */ | |
| if ((0 <= newIP) && (newIP <= 0xffff)) | |
| cpu8086.R_IP = newIP; | |
| else { | |
| if (CS_S != ((PCX_S & 0xf0000) >> 4)) { | |
| cpu8086.R_CS = (PCX_S & 0xf0000) >> 4; | |
| if (cpu_unit.flags & UNIT_CPU_VERBOSE) | |
| sim_printf("CPU: " ADDRESS_FORMAT " Segment register CS set to %04x" NLP, PCX, cpu8086.R_CS); | |
| } | |
| cpu8086.R_IP = PCX_S & 0xffff; | |
| } | |
| while (switch_cpu_now == TRUE) { /* loop until halted */ | |
| if (sim_interval <= 0) { /* check clock queue */ | |
| #if !UNIX_PLATFORM | |
| /* poll on platforms without reliable signalling but not too often */ | |
| pollForCPUStop(); /* following sim_process_event will check for stop */ | |
| #endif | |
| if ( (reason = sim_process_event()) ) | |
| break; | |
| } | |
| if (sim_brk_summ && sim_brk_test(getFullPC(), SWMASK('E'))) { /* breakpoint? */ | |
| reason = STOP_IBKPT; /* stop simulation */ | |
| break; | |
| } | |
| PCX = getFullPC(); | |
| op1 = GetBYTEExtended((((uint32)cpu8086.R_CS<<4) + cpu8086.R_IP) & 0xFFFFF); | |
| if (sim_brk_summ && sim_brk_test(op1, (1u << SIM_BKPT_V_SPC) | SWMASK('I'))) { /* instruction breakpoint? */ | |
| reason = STOP_IBKPT; /* stop simulation */ | |
| break; | |
| } | |
| sim_interval--; | |
| cpu8086.R_IP++; | |
| (*(i86_optab[op1]))(&cpu8086); | |
| if (intr & INTR_HALTED) { | |
| reason = STOP_HALT; | |
| intr &= ~INTR_HALTED; | |
| break; | |
| } | |
| if (intr & INTR_ILLEGAL_OPCODE) { | |
| intr &= ~INTR_ILLEGAL_OPCODE; | |
| if (cpu_unit.flags & UNIT_CPU_OPSTOP) { | |
| reason = STOP_OPCODE; | |
| break; | |
| } | |
| } | |
| if (((intr & INTR_SYNCH) && (cpu8086.intno == 0 || cpu8086.intno == 2)) || | |
| (ACCESS_FLAG(&cpu8086, F_IF))) { | |
| /* [JCE] Reversed the sense of this ACCESS_FLAG; it's set for interrupts | |
| enabled, not interrupts blocked i.e. either not blockable (intr 0 or 2) | |
| or the IF flag not set so interrupts not blocked */ | |
| /* hharte: if a segment override exists, then treat that as "atomic" and do not handle | |
| * an interrupt until the override is cleared. | |
| * Not sure if this is the way an 8086 really works, need to find out for sure. | |
| * Also, what about the REPE prefix? | |
| */ | |
| if ((cpu8086.sysmode & SYSMODE_SEGMASK) == 0) { | |
| i86_intr_handle(&cpu8086); | |
| } | |
| } | |
| } | |
| /* It we stopped processing instructions because of a switch to the other | |
| * CPU, then fixup the reason code. | |
| */ | |
| if (switch_cpu_now == FALSE) { | |
| reason = SCPE_OK; | |
| PCX += 2; | |
| PCX_S = PCX; | |
| } else { | |
| PCX_S = (reason == STOP_HALT) | (reason == STOP_OPCODE) ? PCX : getFullPC(); | |
| } | |
| setViewRegisters(); | |
| return reason; | |
| } | |
| void halt_sys(PC_ENV *m) | |
| { | |
| intr |= INTR_HALTED; | |
| } | |
| /* once the instruction is fetched, an optional byte follows which | |
| has 3 fields encoded in it. This routine fetches the byte | |
| and breaks into the three fields. | |
| This has been changed, in an attempt to reduce the amount of | |
| executed code for this frequently executed subroutine. If this | |
| works, then it may pay to somehow inline it. | |
| */ | |
| #ifdef NOTDEF | |
| /* this code generated the following table */ | |
| main() | |
| { int i; | |
| sim_printf("\n\nstruct modrm{ uint8 mod,rh,rl;} modrmtab[] = {\n"); | |
| for (i=0; i<256; i++) | |
| { | |
| sim_printf("{%d,%d,%d}, ",((i&0xc0)>>6),((i&0x38)>>3),(i&0x07)); | |
| if (i%4==3) | |
| sim_printf("/* %d to %d */\n",i&0xfc,i); | |
| } | |
| sim_printf("};\n\n"); | |
| } | |
| #endif | |
| struct modrm { uint16 mod, rh, rl; }; | |
| static struct modrm modrmtab[] = { | |
| {0,0,0}, {0,0,1}, {0,0,2}, {0,0,3}, /* 0 to 3 */ | |
| {0,0,4}, {0,0,5}, {0,0,6}, {0,0,7}, /* 4 to 7 */ | |
| {0,1,0}, {0,1,1}, {0,1,2}, {0,1,3}, /* 8 to 11 */ | |
| {0,1,4}, {0,1,5}, {0,1,6}, {0,1,7}, /* 12 to 15 */ | |
| {0,2,0}, {0,2,1}, {0,2,2}, {0,2,3}, /* 16 to 19 */ | |
| {0,2,4}, {0,2,5}, {0,2,6}, {0,2,7}, /* 20 to 23 */ | |
| {0,3,0}, {0,3,1}, {0,3,2}, {0,3,3}, /* 24 to 27 */ | |
| {0,3,4}, {0,3,5}, {0,3,6}, {0,3,7}, /* 28 to 31 */ | |
| {0,4,0}, {0,4,1}, {0,4,2}, {0,4,3}, /* 32 to 35 */ | |
| {0,4,4}, {0,4,5}, {0,4,6}, {0,4,7}, /* 36 to 39 */ | |
| {0,5,0}, {0,5,1}, {0,5,2}, {0,5,3}, /* 40 to 43 */ | |
| {0,5,4}, {0,5,5}, {0,5,6}, {0,5,7}, /* 44 to 47 */ | |
| {0,6,0}, {0,6,1}, {0,6,2}, {0,6,3}, /* 48 to 51 */ | |
| {0,6,4}, {0,6,5}, {0,6,6}, {0,6,7}, /* 52 to 55 */ | |
| {0,7,0}, {0,7,1}, {0,7,2}, {0,7,3}, /* 56 to 59 */ | |
| {0,7,4}, {0,7,5}, {0,7,6}, {0,7,7}, /* 60 to 63 */ | |
| {1,0,0}, {1,0,1}, {1,0,2}, {1,0,3}, /* 64 to 67 */ | |
| {1,0,4}, {1,0,5}, {1,0,6}, {1,0,7}, /* 68 to 71 */ | |
| {1,1,0}, {1,1,1}, {1,1,2}, {1,1,3}, /* 72 to 75 */ | |
| {1,1,4}, {1,1,5}, {1,1,6}, {1,1,7}, /* 76 to 79 */ | |
| {1,2,0}, {1,2,1}, {1,2,2}, {1,2,3}, /* 80 to 83 */ | |
| {1,2,4}, {1,2,5}, {1,2,6}, {1,2,7}, /* 84 to 87 */ | |
| {1,3,0}, {1,3,1}, {1,3,2}, {1,3,3}, /* 88 to 91 */ | |
| {1,3,4}, {1,3,5}, {1,3,6}, {1,3,7}, /* 92 to 95 */ | |
| {1,4,0}, {1,4,1}, {1,4,2}, {1,4,3}, /* 96 to 99 */ | |
| {1,4,4}, {1,4,5}, {1,4,6}, {1,4,7}, /* 100 to 103 */ | |
| {1,5,0}, {1,5,1}, {1,5,2}, {1,5,3}, /* 104 to 107 */ | |
| {1,5,4}, {1,5,5}, {1,5,6}, {1,5,7}, /* 108 to 111 */ | |
| {1,6,0}, {1,6,1}, {1,6,2}, {1,6,3}, /* 112 to 115 */ | |
| {1,6,4}, {1,6,5}, {1,6,6}, {1,6,7}, /* 116 to 119 */ | |
| {1,7,0}, {1,7,1}, {1,7,2}, {1,7,3}, /* 120 to 123 */ | |
| {1,7,4}, {1,7,5}, {1,7,6}, {1,7,7}, /* 124 to 127 */ | |
| {2,0,0}, {2,0,1}, {2,0,2}, {2,0,3}, /* 128 to 131 */ | |
| {2,0,4}, {2,0,5}, {2,0,6}, {2,0,7}, /* 132 to 135 */ | |
| {2,1,0}, {2,1,1}, {2,1,2}, {2,1,3}, /* 136 to 139 */ | |
| {2,1,4}, {2,1,5}, {2,1,6}, {2,1,7}, /* 140 to 143 */ | |
| {2,2,0}, {2,2,1}, {2,2,2}, {2,2,3}, /* 144 to 147 */ | |
| {2,2,4}, {2,2,5}, {2,2,6}, {2,2,7}, /* 148 to 151 */ | |
| {2,3,0}, {2,3,1}, {2,3,2}, {2,3,3}, /* 152 to 155 */ | |
| {2,3,4}, {2,3,5}, {2,3,6}, {2,3,7}, /* 156 to 159 */ | |
| {2,4,0}, {2,4,1}, {2,4,2}, {2,4,3}, /* 160 to 163 */ | |
| {2,4,4}, {2,4,5}, {2,4,6}, {2,4,7}, /* 164 to 167 */ | |
| {2,5,0}, {2,5,1}, {2,5,2}, {2,5,3}, /* 168 to 171 */ | |
| {2,5,4}, {2,5,5}, {2,5,6}, {2,5,7}, /* 172 to 175 */ | |
| {2,6,0}, {2,6,1}, {2,6,2}, {2,6,3}, /* 176 to 179 */ | |
| {2,6,4}, {2,6,5}, {2,6,6}, {2,6,7}, /* 180 to 183 */ | |
| {2,7,0}, {2,7,1}, {2,7,2}, {2,7,3}, /* 184 to 187 */ | |
| {2,7,4}, {2,7,5}, {2,7,6}, {2,7,7}, /* 188 to 191 */ | |
| {3,0,0}, {3,0,1}, {3,0,2}, {3,0,3}, /* 192 to 195 */ | |
| {3,0,4}, {3,0,5}, {3,0,6}, {3,0,7}, /* 196 to 199 */ | |
| {3,1,0}, {3,1,1}, {3,1,2}, {3,1,3}, /* 200 to 203 */ | |
| {3,1,4}, {3,1,5}, {3,1,6}, {3,1,7}, /* 204 to 207 */ | |
| {3,2,0}, {3,2,1}, {3,2,2}, {3,2,3}, /* 208 to 211 */ | |
| {3,2,4}, {3,2,5}, {3,2,6}, {3,2,7}, /* 212 to 215 */ | |
| {3,3,0}, {3,3,1}, {3,3,2}, {3,3,3}, /* 216 to 219 */ | |
| {3,3,4}, {3,3,5}, {3,3,6}, {3,3,7}, /* 220 to 223 */ | |
| {3,4,0}, {3,4,1}, {3,4,2}, {3,4,3}, /* 224 to 227 */ | |
| {3,4,4}, {3,4,5}, {3,4,6}, {3,4,7}, /* 228 to 231 */ | |
| {3,5,0}, {3,5,1}, {3,5,2}, {3,5,3}, /* 232 to 235 */ | |
| {3,5,4}, {3,5,5}, {3,5,6}, {3,5,7}, /* 236 to 239 */ | |
| {3,6,0}, {3,6,1}, {3,6,2}, {3,6,3}, /* 240 to 243 */ | |
| {3,6,4}, {3,6,5}, {3,6,6}, {3,6,7}, /* 244 to 247 */ | |
| {3,7,0}, {3,7,1}, {3,7,2}, {3,7,3}, /* 248 to 251 */ | |
| {3,7,4}, {3,7,5}, {3,7,6}, {3,7,7}, /* 252 to 255 */ | |
| }; | |
| void fetch_decode_modrm(PC_ENV *m, uint16 *mod, uint16 *regh, uint16 *regl) | |
| { uint8 fetched; | |
| register struct modrm *p; | |
| /* do the fetch in real mode. Shift the CS segment register | |
| over by 4 bits, and add in the IP register. Index into | |
| the system memory. | |
| */ | |
| /* [JCE] Wrap at 1Mb (the A20 gate) */ | |
| fetched = GetBYTEExtended(((m->R_CS << 4) + (m->R_IP++)) & 0xFFFFF); | |
| #ifdef NOTDEF | |
| *mod = ((fetched&0xc0)>>6); | |
| *regh= ((fetched&0x38)>>3); | |
| *regl= (fetched&0x7); | |
| #else | |
| p = modrmtab + fetched; | |
| *mod = p->mod; | |
| *regh= p->rh; | |
| *regl= p->rl; | |
| #endif | |
| } | |
| /* | |
| return a pointer to the register given by the R/RM field of | |
| the modrm byte, for byte operands. | |
| Also enables the decoding of instructions. | |
| */ | |
| uint8 *decode_rm_byte_register(PC_ENV *m, int reg) | |
| { | |
| switch(reg) | |
| { | |
| case 0: | |
| return &m->R_AL; | |
| break; | |
| case 1: | |
| return &m->R_CL; | |
| break; | |
| case 2: | |
| return &m->R_DL; | |
| break; | |
| case 3: | |
| return &m->R_BL; | |
| break; | |
| case 4: | |
| return &m->R_AH; | |
| break; | |
| case 5: | |
| return &m->R_CH; | |
| break; | |
| case 6: | |
| return &m->R_DH; | |
| break; | |
| case 7: | |
| return &m->R_BH; | |
| break; | |
| } | |
| halt_sys(m); | |
| return NULL; /* NOT REACHED OR REACHED ON ERROR */ | |
| } | |
| /* | |
| return a pointer to the register given by the R/RM field of | |
| the modrm byte, for word operands. | |
| Also enables the decoding of instructions. | |
| */ | |
| uint16 *decode_rm_word_register(PC_ENV *m, int reg) | |
| { | |
| switch(reg) | |
| { | |
| case 0: | |
| return &m->R_AX; | |
| break; | |
| case 1: | |
| return &m->R_CX; | |
| break; | |
| case 2: | |
| return &m->R_DX; | |
| break; | |
| case 3: | |
| return &m->R_BX; | |
| break; | |
| case 4: | |
| return &m->R_SP; | |
| break; | |
| case 5: | |
| return &m->R_BP; | |
| break; | |
| case 6: | |
| return &m->R_SI; | |
| break; | |
| case 7: | |
| return &m->R_DI; | |
| break; | |
| } | |
| halt_sys(m); | |
| return NULL; /* NOTREACHED OR REACHED ON ERROR*/ | |
| } | |
| /* | |
| return a pointer to the register given by the R/RM field of | |
| the modrm byte, for word operands, modified from above | |
| for the weirdo special case of segreg operands. | |
| Also enables the decoding of instructions. | |
| */ | |
| uint16 *decode_rm_seg_register(PC_ENV *m, int reg) | |
| { | |
| switch(reg) | |
| { | |
| case 0: | |
| return &m->R_ES; | |
| break; | |
| case 1: | |
| return &m->R_CS; | |
| break; | |
| case 2: | |
| return &m->R_SS; | |
| break; | |
| case 3: | |
| return &m->R_DS; | |
| break; | |
| case 4: | |
| case 5: | |
| case 6: | |
| case 7: | |
| break; | |
| } | |
| halt_sys(m); | |
| return NULL; /* NOT REACHED OR REACHED ON ERROR */ | |
| } | |
| /* once the instruction is fetched, an optional byte follows which | |
| has 3 fields encoded in it. This routine fetches the byte | |
| and breaks into the three fields. | |
| */ | |
| uint8 fetch_byte_imm(PC_ENV *m) | |
| { | |
| uint8 fetched; | |
| /* do the fetch in real mode. Shift the CS segment register | |
| over by 4 bits, and add in the IP register. Index into | |
| the system memory. | |
| */ | |
| /* [JCE] Wrap at 1Mb (the A20 gate) */ | |
| fetched = GetBYTEExtended((((uint32)m->R_CS << 4) + (m->R_IP++)) & 0xFFFFF); | |
| return fetched; | |
| } | |
| uint16 fetch_word_imm(PC_ENV *m) | |
| { | |
| uint16 fetched; | |
| /* do the fetch in real mode. Shift the CS segment register | |
| over by 4 bits, and add in the IP register. Index into | |
| the system PC_ENVory. | |
| */ | |
| /* [JCE] Wrap at 1Mb (the A20 gate) */ | |
| fetched = GetBYTEExtended((((uint32)m->R_CS << 4) + (m->R_IP++)) & 0xFFFFF); | |
| fetched |= (GetBYTEExtended((((uint32)m->R_CS << 4) + (m->R_IP++)) & 0xFFFFF) << 8); | |
| return fetched; | |
| } | |
| /* | |
| return the offset given by mod=00 addressing. | |
| Also enables the decoding of instructions. | |
| */ | |
| uint16 decode_rm00_address(PC_ENV *m, int rm) | |
| { | |
| uint16 offset; | |
| /* note the code which specifies the corresponding segment (ds vs ss) | |
| below in the case of [BP+..]. The assumption here is that at the | |
| point that this subroutine is called, the bit corresponding to | |
| SYSMODE_SEG_DS_SS will be zero. After every instruction | |
| except the segment override instructions, this bit (as well | |
| as any bits indicating segment overrides) will be clear. So | |
| if a SS access is needed, set this bit. Otherwise, DS access | |
| occurs (unless any of the segment override bits are set). | |
| */ | |
| switch(rm) | |
| { | |
| case 0: | |
| return (int16)m->R_BX + (int16)m->R_SI; | |
| break; | |
| case 1: | |
| return (int16)m->R_BX + (int16)m->R_DI; | |
| break; | |
| case 2: | |
| m->sysmode |= SYSMODE_SEG_DS_SS; | |
| return (int16)m->R_BP + (int16)m->R_SI; | |
| break; | |
| case 3: | |
| m->sysmode |= SYSMODE_SEG_DS_SS; | |
| return (int16)m->R_BP + (int16)m->R_DI; | |
| break; | |
| case 4: | |
| return m->R_SI; | |
| break; | |
| case 5: | |
| return m->R_DI; | |
| break; | |
| case 6: | |
| offset = (int16)fetch_word_imm(m); | |
| return offset; | |
| break; | |
| case 7: | |
| return m->R_BX; | |
| } | |
| halt_sys(m); | |
| return 0; | |
| } | |
| /* | |
| return the offset given by mod=01 addressing. | |
| Also enables the decoding of instructions. | |
| */ | |
| uint16 decode_rm01_address(PC_ENV *m, int rm) | |
| { | |
| int8 displacement; | |
| /* note comment on decode_rm00_address above */ | |
| displacement = (int8)fetch_byte_imm(m); /* !!!! Check this */ | |
| switch(rm) | |
| { | |
| case 0: | |
| return (int16)m->R_BX + (int16)m->R_SI + displacement; | |
| break; | |
| case 1: | |
| return (int16)m->R_BX + (int16)m->R_DI + displacement; | |
| break; | |
| case 2: | |
| m->sysmode |= SYSMODE_SEG_DS_SS; | |
| return (int16)m->R_BP + (int16)m->R_SI + displacement; | |
| break; | |
| case 3: | |
| m->sysmode |= SYSMODE_SEG_DS_SS; | |
| return (int16)m->R_BP + (int16)m->R_DI + displacement; | |
| break; | |
| case 4: | |
| return (int16)m->R_SI + displacement; | |
| break; | |
| case 5: | |
| return (int16)m->R_DI + displacement; | |
| break; | |
| case 6: | |
| m->sysmode |= SYSMODE_SEG_DS_SS; | |
| return (int16)m->R_BP + displacement; | |
| break; | |
| case 7: | |
| return (int16)m->R_BX + displacement; | |
| break; | |
| } | |
| halt_sys(m); | |
| return 0; /* SHOULD NOT HAPPEN */ | |
| } | |
| /* | |
| return the offset given by mod=01 addressing. | |
| Also enables the decoding of instructions. | |
| */ | |
| uint16 decode_rm10_address(PC_ENV *m, int rm) | |
| { | |
| int16 displacement; | |
| /* note comment on decode_rm00_address above */ | |
| displacement = (int16)fetch_word_imm(m); | |
| switch(rm) | |
| { | |
| case 0: | |
| return (int16)m->R_BX + (int16)m->R_SI + displacement; | |
| break; | |
| case 1: | |
| return (int16)m->R_BX + (int16)m->R_DI + displacement; | |
| break; | |
| case 2: | |
| m->sysmode |= SYSMODE_SEG_DS_SS; | |
| return (int16)m->R_BP + (int16)m->R_SI + displacement; | |
| break; | |
| case 3: | |
| m->sysmode |= SYSMODE_SEG_DS_SS; | |
| return (int16)m->R_BP + (int16)m->R_DI + displacement; | |
| break; | |
| case 4: | |
| return (int16)m->R_SI + displacement; | |
| break; | |
| case 5: | |
| return (int16)m->R_DI + displacement; | |
| break; | |
| case 6: | |
| m->sysmode |= SYSMODE_SEG_DS_SS; | |
| return (int16)m->R_BP + displacement; | |
| break; | |
| case 7: | |
| return (int16)m->R_BX + displacement; | |
| break; | |
| } | |
| halt_sys(m); | |
| return 0; | |
| /*NOTREACHED */ | |
| } | |
| /* fetch a byte of data, given an offset, the current register set, | |
| and a descriptor for memory. | |
| */ | |
| uint8 fetch_data_byte(PC_ENV *m, uint16 offset) | |
| { | |
| register uint8 value; | |
| /* this code originally completely broken, and never showed | |
| up since the DS segments === SS segment in all test cases. | |
| It had been originally assumed, that all access to data would | |
| involve the DS register unless there was a segment override. | |
| Not so. Address modes such as -3[BP] or 10[BP+SI] all | |
| refer to addresses relative to the SS. So, at the minimum, | |
| all decodings of addressing modes would have to set/clear | |
| a bit describing whether the access is relative to DS or SS. | |
| That is the function of the cpu-state-varible m->sysmode. | |
| There are several potential states: | |
| repe prefix seen (handled elsewhere) | |
| repne prefix seen (ditto) | |
| cs segment override | |
| ds segment override | |
| es segment override | |
| ss segment override | |
| ds/ss select (in absense of override) | |
| Each of the above 7 items are handled with a bit in the sysmode | |
| field. | |
| The latter 5 can be implemented as a simple state machine: | |
| */ | |
| switch(m->sysmode & SYSMODE_SEGMASK) | |
| { | |
| case 0: | |
| /* default case: use ds register */ | |
| value = GetBYTEExtended(((uint32)m->R_DS<<4) + offset); | |
| break; | |
| case SYSMODE_SEG_DS_SS: | |
| /* non-overridden, use ss register */ | |
| /* [JCE] Wrap at 1Mb (the A20 gate) */ | |
| value = GetBYTEExtended((((uint32)m->R_SS << 4) + offset) & 0xFFFFF); | |
| break; | |
| case SYSMODE_SEGOVR_CS: | |
| /* ds overridden */ | |
| case SYSMODE_SEGOVR_CS|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use cs register */ | |
| /* [JCE] Wrap at 1Mb (the A20 gate) */ | |
| value = GetBYTEExtended((((uint32)m->R_CS << 4) + offset) & 0xFFFFF); | |
| break; | |
| case SYSMODE_SEGOVR_DS: | |
| /* ds overridden --- shouldn't happen, but hey. */ | |
| case SYSMODE_SEGOVR_DS|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use ds register */ | |
| /* [JCE] Wrap at 1Mb (the A20 gate) */ | |
| value = GetBYTEExtended((((uint32)m->R_DS << 4) + offset) & 0xFFFFF); | |
| break; | |
| case SYSMODE_SEGOVR_ES: | |
| /* ds overridden */ | |
| case SYSMODE_SEGOVR_ES|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use es register */ | |
| /* [JCE] Wrap at 1Mb (the A20 gate) */ | |
| value = GetBYTEExtended((((uint32)m->R_ES << 4) + offset) & 0xFFFFF); | |
| break; | |
| case SYSMODE_SEGOVR_SS: | |
| /* ds overridden */ | |
| case SYSMODE_SEGOVR_SS|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use ss register === should not happen */ | |
| /* [JCE] Wrap at 1Mb (the A20 gate) */ | |
| value = GetBYTEExtended((((uint32)m->R_SS << 4) + offset) & 0xFFFFF); | |
| break; | |
| default: | |
| sim_printf("error: should not happen: multiple overrides. " NLP); | |
| value = 0; | |
| halt_sys(m); | |
| } | |
| return value; | |
| } | |
| /* fetch a byte of data, given an offset, the current register set, | |
| and a descriptor for memory. | |
| */ | |
| uint8 fetch_data_byte_abs(PC_ENV *m, uint16 segment, uint16 offset) | |
| { | |
| register uint8 value; | |
| uint32 addr; | |
| /* note, cannot change this, since we do not know the ID of the segment. */ | |
| /* [JCE] Simulate wrap at top of memory (the A20 gate) */ | |
| /* addr = (segment << 4) + offset; */ | |
| addr = ((segment << 4) + offset) & 0xFFFFF; | |
| value = GetBYTEExtended(addr); | |
| return value; | |
| } | |
| /* fetch a byte of data, given an offset, the current register set, | |
| and a descriptor for memory. | |
| */ | |
| uint16 fetch_data_word(PC_ENV *m, uint16 offset) | |
| { | |
| uint16 value; | |
| /* See note above in fetch_data_byte. */ | |
| switch(m->sysmode & SYSMODE_SEGMASK) | |
| { | |
| case 0: | |
| /* default case: use ds register */ | |
| /* [JCE] Wrap at 1Mb (the A20 gate) */ | |
| value = GetBYTEExtended((((uint32)m->R_DS << 4) + offset) & 0xFFFFF) | |
| | (GetBYTEExtended((((uint32)m->R_DS << 4) + | |
| (uint16)(offset + 1)) & 0xFFFFF) << 8); | |
| break; | |
| case SYSMODE_SEG_DS_SS: | |
| /* non-overridden, use ss register */ | |
| /* [JCE] Wrap at 1Mb (the A20 gate) */ | |
| value = GetBYTEExtended((((uint32)m->R_SS << 4) + offset) & 0xFFFFF) | |
| | (GetBYTEExtended((((uint32)m->R_SS << 4) | |
| + (uint16)(offset + 1)) & 0xFFFFF) << 8); | |
| break; | |
| case SYSMODE_SEGOVR_CS: | |
| /* ds overridden */ | |
| case SYSMODE_SEGOVR_CS|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use cs register */ | |
| /* [JCE] Wrap at 1Mb (the A20 gate) */ | |
| value = GetBYTEExtended((((uint32)m->R_CS << 4) + offset) & 0xFFFFF) | |
| | (GetBYTEExtended((((uint32)m->R_CS << 4) | |
| + (uint16)(offset + 1)) & 0xFFFFF) << 8); | |
| break; | |
| case SYSMODE_SEGOVR_DS: | |
| /* ds overridden --- shouldn't happen, but hey. */ | |
| case SYSMODE_SEGOVR_DS|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use ds register */ | |
| /* [JCE] Wrap at 1Mb (the A20 gate) */ | |
| value = GetBYTEExtended((((uint32)m->R_DS << 4) + offset) & 0xFFFFF) | |
| | (GetBYTEExtended((((uint32)m->R_DS << 4) | |
| + (uint16)(offset + 1)) & 0xFFFFF) << 8); | |
| break; | |
| case SYSMODE_SEGOVR_ES: | |
| /* ds overridden */ | |
| case SYSMODE_SEGOVR_ES|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use es register */ | |
| value = GetBYTEExtended((((uint32)m->R_ES << 4) + offset) & 0xFFFFF) | |
| | (GetBYTEExtended((((uint32)m->R_ES << 4) + | |
| (uint16)(offset + 1)) & 0xFFFFF) << 8); | |
| break; | |
| case SYSMODE_SEGOVR_SS: | |
| /* ds overridden */ | |
| case SYSMODE_SEGOVR_SS|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use ss register === should not happen */ | |
| value = GetBYTEExtended((((uint32)m->R_SS << 4) + offset) & 0xFFFFF) | |
| | (GetBYTEExtended((((uint32)m->R_SS << 4) | |
| + (uint16)(offset + 1)) & 0xFFFFF) << 8); | |
| break; | |
| default: | |
| sim_printf("error: should not happen: multiple overrides. " NLP); | |
| value = 0; | |
| halt_sys(m); | |
| } | |
| return value; | |
| } | |
| /* fetch a byte of data, given an offset, the current register set, | |
| and a descriptor for memory. | |
| */ | |
| uint16 fetch_data_word_abs(PC_ENV *m, uint16 segment, uint16 offset) | |
| { | |
| uint16 value; | |
| uint32 addr; | |
| /* [JCE] Simulate wrap at top of memory (the A20 gate) */ | |
| /* addr = (segment << 4) + offset; */ | |
| addr = ((segment << 4) + offset) & 0xFFFFF; | |
| value = GetBYTEExtended(addr) | (GetBYTEExtended(addr + 1) << 8); | |
| return value; | |
| } | |
| /* Store a byte of data, given an offset, the current register set, | |
| and a descriptor for memory. | |
| */ | |
| void store_data_byte(PC_ENV *m, uint16 offset, uint8 val) | |
| { | |
| /* See note above in fetch_data_byte. */ | |
| uint32 addr; | |
| register uint16 segment; | |
| switch(m->sysmode & SYSMODE_SEGMASK) | |
| { | |
| case 0: | |
| /* default case: use ds register */ | |
| segment = m->R_DS; | |
| break; | |
| case SYSMODE_SEG_DS_SS: | |
| /* non-overridden, use ss register */ | |
| segment = m->R_SS; | |
| break; | |
| case SYSMODE_SEGOVR_CS: | |
| /* ds overridden */ | |
| case SYSMODE_SEGOVR_CS|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use cs register */ | |
| segment = m->R_CS; | |
| break; | |
| case SYSMODE_SEGOVR_DS: | |
| /* ds overridden --- shouldn't happen, but hey. */ | |
| case SYSMODE_SEGOVR_DS|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use ds register */ | |
| segment = m->R_DS; | |
| break; | |
| case SYSMODE_SEGOVR_ES: | |
| /* ds overridden */ | |
| case SYSMODE_SEGOVR_ES|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use es register */ | |
| segment = m->R_ES; | |
| break; | |
| case SYSMODE_SEGOVR_SS: | |
| /* ds overridden */ | |
| case SYSMODE_SEGOVR_SS|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use ss register === should not happen */ | |
| segment = m->R_SS; | |
| break; | |
| default: | |
| sim_printf("error: should not happen: multiple overrides. " NLP); | |
| segment = 0; | |
| halt_sys(m); | |
| } | |
| /* [JCE] Simulate wrap at top of memory (the A20 gate) */ | |
| /* addr = (segment << 4) + offset; */ | |
| addr = (((uint32)segment << 4) + offset) & 0xFFFFF; | |
| PutBYTEExtended(addr, val); | |
| } | |
| void store_data_byte_abs(PC_ENV *m, uint16 segment, uint16 offset, uint8 val) | |
| { | |
| register uint32 addr; | |
| /* [JCE] Simulate wrap at top of memory (the A20 gate) */ | |
| /* addr = (segment << 4) + offset; */ | |
| addr = (((uint32)segment << 4) + offset) & 0xFFFFF; | |
| PutBYTEExtended(addr, val); | |
| } | |
| /* Store a word of data, given an offset, the current register set, | |
| and a descriptor for memory. | |
| */ | |
| void store_data_word(PC_ENV *m, uint16 offset, uint16 val) | |
| { | |
| register uint32 addr; | |
| register uint16 segment; | |
| /* See note above in fetch_data_byte. */ | |
| switch(m->sysmode & SYSMODE_SEGMASK) | |
| { | |
| case 0: | |
| /* default case: use ds register */ | |
| segment = m->R_DS; | |
| break; | |
| case SYSMODE_SEG_DS_SS: | |
| /* non-overridden, use ss register */ | |
| segment = m->R_SS; | |
| break; | |
| case SYSMODE_SEGOVR_CS: | |
| /* ds overridden */ | |
| case SYSMODE_SEGOVR_CS|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use cs register */ | |
| segment = m->R_CS; | |
| break; | |
| case SYSMODE_SEGOVR_DS: | |
| /* ds overridden --- shouldn't happen, but hey. */ | |
| case SYSMODE_SEGOVR_DS|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use ds register */ | |
| segment = m->R_DS; | |
| break; | |
| case SYSMODE_SEGOVR_ES: | |
| /* ds overridden */ | |
| case SYSMODE_SEGOVR_ES|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use es register */ | |
| segment = m->R_ES; | |
| break; | |
| case SYSMODE_SEGOVR_SS: | |
| /* ds overridden */ | |
| case SYSMODE_SEGOVR_SS|SYSMODE_SEG_DS_SS: | |
| /* ss overridden, use ss register === should not happen */ | |
| segment = m->R_SS; | |
| break; | |
| default: | |
| sim_printf("error: should not happen: multiple overrides." NLP); | |
| segment = 0; | |
| halt_sys(m); | |
| } | |
| /* [JCE] Simulate wrap at top of memory (the A20 gate) */ | |
| /* addr = (segment << 4) + offset; */ | |
| addr = (((uint32)segment << 4) + offset) & 0xFFFFF; | |
| PutBYTEExtended(addr, val & 0xff); | |
| PutBYTEExtended(addr + 1, val >> 8); | |
| } | |
| void store_data_word_abs(PC_ENV *m, uint16 segment, uint16 offset, uint16 val) | |
| { | |
| register uint32 addr; | |
| /* [JCE] Wrap at top of memory */ | |
| addr = ((segment << 4) + offset) & 0xFFFFF; | |
| PutBYTEExtended(addr, val & 0xff); | |
| PutBYTEExtended(addr + 1, val >> 8); | |
| } |