Cleaned up conditional jump handling

This commit is contained in:
Jeff Parsons 2015-05-04 19:26:06 -07:00 committed by jeffpar
commit 2784934491
10 changed files with 135 additions and 50 deletions

View file

@ -8,16 +8,16 @@
"ram": [
{ "id": "pc386.ramLow",
"name": "",
"addr": 0x00000,
"size": 0xa0000,
"addr": 0,
"size": 655360,
"test": false
}
],
"rom": [
{ "id": "pc386.romTests",
"name": "",
"addr": 0xf0000,
"size": 0x10000,
"addr": 983296,
"size": 65280,
"file": "/tests/pc/80386/tests.json",
"notify": ""
}

View file

@ -100,8 +100,8 @@ A BackTrack index is encoded as a 32-bit value with three parts:
This represents a total of 31 bits, with bit 31 reserved.
For example, look at one of the last things a ROM does during boot: load a disk sector into RAM. It will be up to the
disk controller (or DMA controller if one is used) to create a BackTrack object representing the sector that was read,
For example, look at one of the last things a ROM does during boot: loading a disk sector into RAM. It will be up to the
disk controller (or DMA controller, if used) to create a BackTrack object representing the sector that was read,
adding that object to the global BackTrack object array, and then associating the corresponding BackTrack index with
the first byte of RAM where the sector was loaded. Subsequent bytes of RAM containing the rest of the sector will refer
to the same BackTrack object, using BackTrack indexes containing offsets 1-511.

View file

@ -1603,7 +1603,7 @@ if (DEBUGGER) {
/*
* When we dump the EXT word, we mask off the LIMIT1619 and BASE2431 bits, because those have already
* been incorporated into the limit and base properties of the segment register; all we care about here
* are whether EXT contains any of the AVAIL (0x10), BIG (0x40) or GRANULARITY (0x80) bits.
* are whether EXT contains any of the AVAIL (0x10), BIG (0x40) or LIMITPAGES (0x80) bits.
*/
this.println(sDump + " dpl=" + str.toHexByte(seg.dpl) + " type=" + str.toHexByte(seg.type >> 8) + " (" + sType + ")" + " ext=" + str.toHexWord(seg.ext & ~(X86.DESC.EXT.LIMIT1619 | X86.DESC.EXT.BASE2431)));
};

View file

@ -103,10 +103,10 @@ var X86 = {
MASK: 0xfff8 // table index
},
DESC: { // Descriptor Table Entry
LIMIT: {
LIMIT: { // LIMIT bits 0-15
OFFSET: 0x0
},
BASE: {
BASE: { // BASE bits 0-15
OFFSET: 0x2
},
ACC: { // bit definitions for the access word (offset 0x4)
@ -166,7 +166,7 @@ var X86 = {
* is 0xffffffff instead of 0xffff.
*/
BIG: 0x0040, // clear if default operand/address size is 16-bit, set if 32-bit
GRANULARITY: 0x0080, // clear if limit is bytes, set if limit is 4Kb pages
LIMITPAGES: 0x0080, // clear if limit granularity is bytes, set if limit granularity is 4Kb pages
BASE2431: 0xff00
},
INVALID: 0 // use X86.DESC.INVALID for invalid DESC values

View file

@ -3197,18 +3197,6 @@ X86CPU.prototype.getIPDisp = function()
return w;
};
/**
* getIPDispWord()
*
* @this {X86CPU}
* @return {number} sign-extended value from the word at the current IP; IP advanced by 2 or 4
*/
X86CPU.prototype.getIPDispWord = function()
{
var w = this.getIPWord();
return (this.dataSize == 2? ((w << 16) >> 16) : w);
};
/**
* getSIBAddr(mod)
*

View file

@ -342,6 +342,23 @@ X86.opMOVcr = function MOVcr()
}
};
/*
* NOTE: The following 16 new conditional jumps actually rely on the OPERAND override setting
* for determining whether a signed 16-bit or 32-bit displacement will be fetched, even though
* the ADDRESS override might seem more intuitive. Think of them as instructions that are loading
* a new operand into IP/EIP.
*
* Also, in 16-bit code, even though a signed rel16 value would seem to imply a range of -32768
* to +32767, any location within a 64Kb code segment outside that range can be reached by choosing
* a displacement in the opposite direction, causing the 16-bit value in EIP to underflow or overflow;
* any underflow or overflow doesn't matter, because only the low 16 bits of EIP are used when a
* 16-bit OPERAND size is in effect.
*
* In fact, for 16-bit jumps, it's simpler to always think of rel16 as an UNSIGNED value added to
* the current EIP, where the result is then truncated to a 16-bit value. This is why we don't have
* to sign-extend rel16 before adding it to the current EIP.
*/
/**
* opJOw()
*
@ -351,7 +368,7 @@ X86.opMOVcr = function MOVcr()
*/
X86.opJOw = function JOw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (this.getOF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -369,7 +386,7 @@ X86.opJOw = function JOw()
*/
X86.opJNOw = function JNOw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (!this.getOF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -387,7 +404,7 @@ X86.opJNOw = function JNOw()
*/
X86.opJCw = function JCw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (this.getCF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -405,7 +422,7 @@ X86.opJCw = function JCw()
*/
X86.opJNCw = function JNCw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (!this.getCF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -423,7 +440,7 @@ X86.opJNCw = function JNCw()
*/
X86.opJZw = function JZw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (this.getZF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -441,7 +458,7 @@ X86.opJZw = function JZw()
*/
X86.opJNZw = function JNZw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (!this.getZF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -459,7 +476,7 @@ X86.opJNZw = function JNZw()
*/
X86.opJBEw = function JBEw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (this.getCF() || this.getZF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -477,7 +494,7 @@ X86.opJBEw = function JBEw()
*/
X86.opJNBEw = function JNBEw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (!this.getCF() && !this.getZF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -495,7 +512,7 @@ X86.opJNBEw = function JNBEw()
*/
X86.opJSw = function JSw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (this.getSF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -513,7 +530,7 @@ X86.opJSw = function JSw()
*/
X86.opJNSw = function JNSw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (!this.getSF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -531,7 +548,7 @@ X86.opJNSw = function JNSw()
*/
X86.opJPw = function JPw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (this.getPF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -549,7 +566,7 @@ X86.opJPw = function JPw()
*/
X86.opJNPw = function JNPw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (!this.getPF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -567,7 +584,7 @@ X86.opJNPw = function JNPw()
*/
X86.opJLw = function JLw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (!this.getSF() != !this.getOF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -585,7 +602,7 @@ X86.opJLw = function JLw()
*/
X86.opJNLw = function JNLw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (!this.getSF() == !this.getOF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -603,7 +620,7 @@ X86.opJNLw = function JNLw()
*/
X86.opJLEw = function JLEw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (this.getZF() || !this.getSF() != !this.getOF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -621,7 +638,7 @@ X86.opJLEw = function JLEw()
*/
X86.opJNLEw = function JNLEw()
{
var disp = this.getIPDispWord();
var disp = this.getIPWord();
if (!this.getZF() && !this.getSF() == !this.getOF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
@ -1301,7 +1318,7 @@ X86.aOps0F[0x06] = X86.opCLTS;
/*
* On all processors (except the 8086/8088, of course), X86.OPCODE.UD2 (0x0F,0x0B), aka "UD2", is an
* instruction guaranteed to raise a #UD (Invalid Opcode) exception (INT 0x06) on all future x86 processors.
* instruction guaranteed to raise a #UD (Invalid Opcode) exception (INT 0x06) on all post-8086 processors.
*/
X86.aOps0F[0x0B] = X86.opInvalid;

View file

@ -3497,6 +3497,10 @@ X86.opESC = function ESC()
/**
* op=0xE0 (LOOPNZ disp)
*
* NOTE: All the instructions in this group (LOOPNZ, LOOPZ, LOOP, and JCXZ) actually
* rely on the ADDRESS override setting for determining whether CX or ECX will be used,
* even though it seems counter-intuitive; ditto for the REP prefix.
*
* @this {X86CPU}
*/
X86.opLOOPNZ = function LOOPNZ()
@ -3513,6 +3517,10 @@ X86.opLOOPNZ = function LOOPNZ()
/**
* op=0xE1 (LOOPZ disp)
*
* NOTE: All the instructions in this group (LOOPNZ, LOOPZ, LOOP, and JCXZ) actually
* rely on the ADDRESS override setting for determining whether CX or ECX will be used,
* even though it seems counter-intuitive; ditto for the REP prefix.
*
* @this {X86CPU}
*/
X86.opLOOPZ = function LOOPZ()
@ -3529,6 +3537,10 @@ X86.opLOOPZ = function LOOPZ()
/**
* op=0xE2 (LOOP disp)
*
* NOTE: All the instructions in this group (LOOPNZ, LOOPZ, LOOP, and JCXZ) actually
* rely on the ADDRESS override setting for determining whether CX or ECX will be used,
* even though it seems counter-intuitive; ditto for the REP prefix.
*
* @this {X86CPU}
*/
X86.opLOOP = function LOOP()
@ -3543,7 +3555,11 @@ X86.opLOOP = function LOOP()
};
/**
* op=0xE3 (JCXZ disp)
* op=0xE3 (JCXZ/JECXZ disp)
*
* NOTE: All the instructions in this group (LOOPNZ, LOOPZ, LOOP, and JCXZ) actually
* rely on the ADDRESS override setting for determining whether CX or ECX will be used,
* even though it seems counter-intuitive; ditto for the REP prefix.
*
* @this {X86CPU}
*/

View file

@ -588,7 +588,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
if (I386 && cpu.model >= X86.MODEL_80386) {
base |= (ext & X86.DESC.EXT.BASE2431) << 16;
limit |= (ext & X86.DESC.EXT.LIMIT1619) << 16;
if (ext & X86.DESC.EXT.GRANULARITY) limit = (limit << 12) | 0xfff;
if (ext & X86.DESC.EXT.LIMITPAGES) limit = (limit << 12) | 0xfff;
}
while (true) {

View file

@ -1,7 +1,7 @@
all: tests.json
tests.rom: tests.nasm
nasm -f bin tests.nasm -l tests.lst -o tests.rom
tests.com: tests.nasm
nasm -f bin tests.nasm -l tests.lst -o tests.com
tests.json: tests.rom
node ../../../modules/filedump/bin/filedump --file=tests.rom --output=tests.json --overwrite
tests.json: tests.com
node ../../../modules/filedump/bin/filedump --file=tests.com --output=tests.json --overwrite

View file

@ -1,21 +1,85 @@
cpu 386
org 0x0000
;
; This file is designed to run as a ROM replacement, but it has a .COM extension because it's
; also designed to run as a COM file under DOS (hence the "org 0x100").
;
org 0x100
section .text
bits 16
DPL0 equ 0
DPL1 equ 1
DPL2 equ 2
DPL3 equ 3
ACC_TYPE_SEG equ 0x1000
ACC_TYPE_CODE equ 0x0800
ACC_TYPE_READABLE equ 0x0200
ACC_TYPE_WRITABLE equ 0x0200
ACC_TYPE_CODE_READABLE equ 0x1a00
ACC_TYPE_DATA_WRITABLE equ 0x1200
EXT_BIG equ 0x0040
SEG_CODE_REAL equ 0xf000
SEG_CODE_PROT equ 0x0008
SEG_DATA_PROT equ 0x0010
CR0_MSW_PE equ 0x0001
;
; descDT defines a descriptor, given a base (%1), limit (%2), type (%3), dpl (%4), and bigness (%5)
;
%macro defDesc 1-5 0,0,0,0
dw (%2 & 0x0000ffff)
dw (%1 & 0x0000ffff)
dw ((%1 & 0x00ff0000) >> 16) | %3 | (%4 << 13)
dw ((%2 & 0x000f0000) >> 16) | %5 | ((%1 & 0xff000000) >> 16)
%endmacro
start: mov eax,0x44332211
mov ecx,0x88776655
mul ecx
div ecx
jnz near goProt
call dword 0xf000:start
times 32768 nop
times 0xfff0-($-$$) nop
romGDT: defDesc 0
defDesc 0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,0
defDesc 0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE,0
goProt: lgdt [cs:romGDT]
mov eax,cr0
or eax,CR0_MSW_PE
mov cr0,eax
jmp dword SEG_CODE_PROT:inProt
inProt: mov ax,SEG_DATA_PROT
mov ds,ax
;
; Do some protected-mode tests...
;
goReal: mov eax,cr0
and eax,~CR0_MSW_PE
mov cr0,eax
jmp dword SEG_CODE_REAL:inReal
inReal: or eax,1
jnz start
;
; Fill the remaining space with NOPs until we get to target offset 0xFFF0.
;
; Note that we subtract 0x100 from the target offset because we're ORG'ed at 0x100.
;
times 0xfff0-0x100-($-$$) nop
bits 16
jmp 0xf000:start
jmp SEG_CODE_REAL:start
db 0x20
db '04/04/15'