Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 101 additions & 2 deletions src/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ static int8_t CF, PF, ZF, TF, IF, DF;
/* All the word flags may be either none-zero (true) or zero (false) */
static unsigned AF, OF, SF;

/*
* 80286 system registers:
* Descriptor table registers are loadable (LGDT/LIDT) and readable (SGDT/SIDT).
* Machine status word is readable (SMSW) so CPU-detection code works.
* Since we only emulate real mode none of these affect addressing.
*/
static uint32_t gdt_base, idt_base;
static uint16_t gdt_limit, idt_limit;
static uint16_t msw;

/* CPU speed: number of instructions to execute each millisecond */
static unsigned ins_per_ms;

Expand Down Expand Up @@ -267,6 +277,12 @@ void init_cpu(void)

segment_override = NoSeg;

// 80286 system registers, reset state
gdt_base = idt_base = 0;
gdt_limit = 0xFFFF;
idt_limit = 0x03FF;
msw = 0xFFF0; // real mode PE clear

// Read CPU speed vars
ins_per_ms = 0;
num_ins_exec = 0;
Expand Down Expand Up @@ -2315,6 +2331,89 @@ NORETURN static void i_halt(void)
exit(0);
}

// 80286 LMSW: load machine status word.
// Setting PE means switching to protected mode.
static void do_lmsw(uint16_t val)
{
if((val & 1) && !(msw & 1))
print_error("error, program tried to switch to 80286 protected mode "
"(LMSW with PE=1), which is not supported\n");
msw = (msw & 0xFFF0) | (val & 0x000F);
}

// 80286 CLTS: clear the Task-Switched flag in the machine status word.
static void i_clts(void)
{
msw = msw & ~0x0008u;
}

// 80286 group 7 (0F 01): SGDT, SIDT, LGDT, LIDT, SMSW and LMSW.
static void i_0f_01(void)
{
unsigned ModRM = FETCH_B();
switch(ModRM & 0x38)
{
case 0x20: // SMSW r/m16
GetModRMRMW(ModRM);
SetModRMRMW(ModRM, msw);
break;
case 0x30: // LMSW r/m16
do_lmsw(GetModRMRMW(ModRM));
break;
case 0x00: // SGDT m
case 0x08: // SIDT m
case 0x10: // LGDT m
case 0x18: // LIDT m
if(ModRM >= 0xC0)
{
i_undefined(); // register operand illegal for these
break;
}
{
uint32_t addr = GetModRMAddress(ModRM);
switch(ModRM & 0x38)
{
case 0x00: // SGDT: 16-bit + 24-bit base, high bytes are FF
SetMemAbsW(addr + 0, gdt_limit);
SetMemAbsW(addr + 2, gdt_base);
SetMemAbsB(addr + 4, gdt_base >> 16);
SetMemAbsB(addr + 5, 0xFF);
break;
case 0x08: // SIDT
SetMemAbsW(addr + 0, idt_limit);
SetMemAbsW(addr + 2, idt_base);
SetMemAbsB(addr + 4, idt_base >> 16);
SetMemAbsB(addr + 5, 0xFF);
break;
case 0x10: // LGDT
gdt_limit = GetMemAbsW(addr);
gdt_base = GetMemAbsW(addr + 2) | ((uint32_t)GetMemAbsB(addr + 4) << 16);
break;
case 0x18: // LIDT
idt_limit = GetMemAbsW(addr);
idt_base = GetMemAbsW(addr + 2) | ((uint32_t)GetMemAbsB(addr + 4) << 16);
break;
}
}
break;
default: // 0x28 and 0x38 are undefined
i_undefined();
break;
}
}

// 80286 two-byte 0F-prefix opcodes
static void i_0fpre(void)
{
uint8_t op = FETCH_B();
switch(op)
{
case 0x01: i_0f_01(); break;
case 0x06: i_clts(); break;
default: i_undefined(); break;
}
}

static void debug_instruction(void)
{
unsigned nip = (cpuGetIP() + 0xFFFF) & 0xFFFF; // subtract 1!
Expand Down Expand Up @@ -2351,7 +2450,7 @@ static void do_instruction(uint8_t code)
case 0x0C: OP_ald8(OR);
case 0x0D: OP_axd16(OR);
case 0x0e: PushWord(sregs[CS]); break;
case 0x0f: i_undefined(); break;
case 0x0f: i_0fpre(); break; /* 286 */
case 0x10: OP_br8(ADC);
case 0x11: OP_wr16(ADC);
case 0x12: OP_r8b(ADC);
Expand Down Expand Up @@ -2435,7 +2534,7 @@ static void do_instruction(uint8_t code)
case 0x60: i_pusha(); break; /* 186 */
case 0x61: i_popa(); break; /* 186 */
case 0x62: i_bound(); break; /* 186 */
case 0x63: i_undefined(); break;
case 0x63: i_undefined(); break; /* 286 */
case 0x64: i_undefined(); break;
case 0x65: i_undefined(); break;
case 0x66: i_undefined(); break;
Expand Down
3 changes: 1 addition & 2 deletions src/cpu.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/*
* This is based on code by David Hedley, from pcemu.
*
* Most of the CPU emulation was rewritten and code was extended to support
* 80186 and some 81280 instructions.
* 80186 and 80286 instructions.
*/

#pragma once
Expand Down
40 changes: 38 additions & 2 deletions src/dis.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ static const char *table_fe[] = {"INC", "DEC", "ILL", "ILL", "ILL", "ILL", "ILL"
static const char *table_ff[] = {"INC", "DEC", "CALL", "CALL",
"JMP", "JMP", "PUSH", "ILL"};
static const char *table_8x[] = {"ADD", "OR", "ADC", "SBB", "AND", "SUB", "XOR", "CMP"};
static const char *table_0f00[] = {"SLDT", "STR", "LLDT", "LTR",
"VERR", "VERW", "DB", "DB"};
static const char *table_0f01[] = {"SGDT", "SIDT", "LGDT", "LIDT",
"SMSW", "DB", "LMSW", "DB"};

#define BREG byte_reg[(ModRM & 0x38) >> 3]
#define WREG word_reg[(ModRM & 0x38) >> 3]
Expand Down Expand Up @@ -472,6 +476,38 @@ static const char *decode_ff(const uint8_t *ip, int seg_over)
return decode_w(ip, table_ff[m], seg_over);
}

static const char *decode_0f_w(const uint8_t *ip, const char *ins, int seg_over)
{
unsigned ModRM = ip[2];
fillbytes(ip, 3 + get_mem_len(ModRM));
sprintf(IPOS, "%-7s %s", ins, get_mem(ModRM, ip + 2, word_reg, "", seg_over));
return buf;
}

static const char *decode_0f_r16w(const uint8_t *ip, const char *ins, int seg_over)
{
unsigned ModRM = ip[2];
fillbytes(ip, 3 + get_mem_len(ModRM));
sprintf(IPOS, "%-7s %s,%s", ins, word_reg[(ModRM & 0x38) >> 3],
get_mem(ModRM, ip + 2, word_reg, "", seg_over));
return buf;
}

static const char *decode_0f(const uint8_t *ip, int seg_over)
{
unsigned m = (ip[2] & 0x38) >> 3;
switch(ip[1])
{
case 0x00: return decode_0f_w(ip, table_0f00[m], seg_over);
case 0x01: return decode_0f_w(ip, table_0f01[m], seg_over);
case 0x02: return decode_0f_r16w(ip, "LAR", seg_over);
case 0x03: return decode_0f_r16w(ip, "LSL", seg_over);
case 0x05: fillbytes(ip, 2); sprintf(IPOS, "LOADALL"); return buf;
case 0x06: fillbytes(ip, 2); sprintf(IPOS, "CLTS"); return buf;
default: return decode_databyte(ip, "DB");
}
}

static const char *show_io(const uint8_t *ip, const char *ins, const char *regs)
{
fillbytes(ip, 1);
Expand Down Expand Up @@ -581,7 +617,7 @@ const char *disa(const uint8_t *ip, uint16_t reg_ip, int segment_override)
case 0x0c: return decode_ald8(ip, "OR");
case 0x0d: return decode_axd16(ip, "OR");
case 0x0e: return decode_pushpopseg(ip, "PUSH");
case 0x0f: return decode_databyte(ip, "DB");
case 0x0f: return decode_0f(ip, segment_override);
case 0x10: return decode_br8(ip, "ADC", segment_override);
case 0x11: return decode_wr16(ip, "ADC", segment_override);
case 0x12: return decode_r8b(ip, "ADC", segment_override);
Expand Down Expand Up @@ -665,7 +701,7 @@ const char *disa(const uint8_t *ip, uint16_t reg_ip, int segment_override)
case 0x60: return show(ip, "PUSHA");
case 0x61: return show(ip, "POPA");
case 0x62: return decode_w(ip, "BOUND", segment_override);
case 0x63: return decode_databyte(ip, "DB");
case 0x63: return decode_wr16(ip, "ARPL", segment_override);
case 0x64: return decode_databyte(ip, "DB");
case 0x65: return decode_databyte(ip, "DB");
case 0x66: return decode_databyte(ip, "DB");
Expand Down