* Allows setting the FCB address used to send filenames between the host and guest operating systems. * Increment the SIMH device version to SIMH005.
955 lines
32 KiB
C
955 lines
32 KiB
C
/*
|
|
* 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"
|
|
|
|
/* GLOBAL */
|
|
volatile int intr;
|
|
|
|
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;
|
|
|
|
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);
|
|
}
|
|
|
|
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\n", 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 ( (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. \n");
|
|
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. \n");
|
|
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. \n");
|
|
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.\n");
|
|
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);
|
|
}
|