Started work on instruction testing

This commit is contained in:
Jeff Parsons 2015-10-14 18:05:21 -07:00
commit 0cc582405d
3 changed files with 236 additions and 137 deletions

View file

@ -297,7 +297,7 @@ function doCommand(sCmd)
default: default:
if (sCmd) { if (sCmd) {
try { try {
if (dbg && !dbg.doCommand(sCmd, true)) { if (dbg && !dbg.doCommands(sCmd, true)) {
sCmd = '(' + sCmd + ')'; sCmd = '(' + sCmd + ')';
result = eval(sCmd); // jshint ignore:line result = eval(sCmd); // jshint ignore:line
} }

View file

@ -233,18 +233,15 @@ function Debugger(parmsDbg)
* $('dw 0:0') * $('dw 0:0')
* $('h') * $('h')
* ... * ...
*
* WARNING: doCommand() expects the same conditions that parseCommand() imposes; ie, a trim and
* lower-case command string.
*/ */
var dbg = this; var dbg = this;
if (window) { if (window) {
if (window['$'] === undefined) { if (window['$'] === undefined) {
window['$'] = function(s) { return dbg.doCommand(s); }; window['$'] = function(s) { return dbg.doCommands(s); };
} }
} else { } else {
if (global['$'] === undefined) { if (global['$'] === undefined) {
global['$'] = function(s) { return dbg.doCommand(s); }; global['$'] = function(s) { return dbg.doCommands(s); };
} }
} }
@ -1866,37 +1863,36 @@ if (DEBUGGER) {
* control.focus(); * control.focus();
*/ */
control.onkeydown = function onKeyDownDebugInput(event) { control.onkeydown = function onKeyDownDebugInput(event) {
var sInput; var sCmds;
if (event.keyCode == Keyboard.KEYCODE.CR) { if (event.keyCode == Keyboard.KEYCODE.CR) {
sInput = control.value; sCmds = control.value;
control.value = ""; control.value = "";
var a = dbg.parseCommand(sInput, true); dbg.doCommands(sCmds, true);
for (var s in a) dbg.doCommand(a[s]);
} }
else if (event.keyCode == Keyboard.KEYCODE.ESC) { else if (event.keyCode == Keyboard.KEYCODE.ESC) {
control.value = sInput = ""; control.value = sCmds = "";
} }
else { else {
if (event.keyCode == Keyboard.KEYCODE.UP) { if (event.keyCode == Keyboard.KEYCODE.UP) {
if (dbg.iPrevCmd < dbg.aPrevCmds.length - 1) { if (dbg.iPrevCmd < dbg.aPrevCmds.length - 1) {
sInput = dbg.aPrevCmds[++dbg.iPrevCmd]; sCmds = dbg.aPrevCmds[++dbg.iPrevCmd];
} }
} }
else if (event.keyCode == Keyboard.KEYCODE.DOWN) { else if (event.keyCode == Keyboard.KEYCODE.DOWN) {
if (dbg.iPrevCmd > 0) { if (dbg.iPrevCmd > 0) {
sInput = dbg.aPrevCmds[--dbg.iPrevCmd]; sCmds = dbg.aPrevCmds[--dbg.iPrevCmd];
} else { } else {
sInput = ""; sCmds = "";
dbg.iPrevCmd = -1; dbg.iPrevCmd = -1;
} }
} }
if (sInput != null) { if (sCmds != null) {
var cch = sInput.length; var cch = sCmds.length;
control.value = sInput; control.value = sCmds;
control.setSelectionRange(cch, cch); control.setSelectionRange(cch, cch);
} }
} }
if (sInput != null && event.preventDefault) event.preventDefault(); if (sCmds != null && event.preventDefault) event.preventDefault();
}; };
return true; return true;
@ -1907,10 +1903,9 @@ if (DEBUGGER) {
500, 100, 500, 100,
function onClickDebugEnter(fRepeat) { function onClickDebugEnter(fRepeat) {
if (dbg.controlDebug) { if (dbg.controlDebug) {
var sInput = dbg.controlDebug.value; var sCmds = dbg.controlDebug.value;
dbg.controlDebug.value = ""; dbg.controlDebug.value = "";
var a = dbg.parseCommand(sInput, true); dbg.doCommands(sCmds, true);
for (var s in a) dbg.doCommand(a[s]);
return true; return true;
} }
if (DEBUG) dbg.log("no debugger input buffer"); if (DEBUG) dbg.log("no debugger input buffer");
@ -3613,9 +3608,9 @@ if (DEBUGGER) {
this.println("Type ? for help with PCjs Debugger commands"); this.println("Type ? for help with PCjs Debugger commands");
this.updateStatus(); this.updateStatus();
if (this.sInitCommands) { if (this.sInitCommands) {
var a = this.parseCommand(this.sInitCommands); var sCmds = this.sInitCommands;
this.sInitCommands = null; this.sInitCommands = null;
for (var s in a) this.doCommand(a[s]); this.doCommands(sCmds);
} }
}; };
@ -7727,6 +7722,23 @@ if (DEBUGGER) {
return result; return result;
}; };
/**
* doCommands(sCmds, fSave)
*
* @this {Debugger}
* @param {string} sCmds
* @param {boolean} [fSave]
* @return {boolean} true if all commands processed, false if not
*/
Debugger.prototype.doCommands = function(sCmds, fSave)
{
var a = this.parseCommand(sCmds, fSave);
for (var s in a) {
if (!this.doCommand(a[s])) return false;
}
return true;
};
/** /**
* Debugger.init() * Debugger.init()
* *

View file

@ -79,7 +79,7 @@ SSEG_PROT32 equ 0x0028
; ;
%assign selDesc 0 %assign selDesc 0
%macro defDesc 1-5 none,0,0,0,0 %macro defDesc 1-5 0,0,0,0
%assign %1 selDesc %assign %1 selDesc
dw (%3 & 0x0000ffff) dw (%3 & 0x0000ffff)
dw (%2 & 0x0000ffff) dw (%2 & 0x0000ffff)
@ -95,7 +95,7 @@ SSEG_PROT32 equ 0x0028
; ;
; The "setDesc" macro creates a descriptor, given a name (%1), base (%2), limit (%3), type (%4), and ext (%5) ; The "setDesc" macro creates a descriptor, given a name (%1), base (%2), limit (%3), type (%4), and ext (%5)
; ;
%macro setDesc 1-5 none,0,0,0,0 %macro setDesc 1-5 0,0,0,0
%assign %1 selDesc %assign %1 selDesc
set ebx,%2 set ebx,%2
set ecx,%3 set ecx,%3
@ -115,7 +115,7 @@ start: nop
mul ecx mul ecx
div ecx div ecx
cmp eax,ebx cmp eax,ebx
jne near exitErr ; apparently we have to tell NASM "near" because this is a forward reference jne near error ; apparently we have to tell NASM v0.98.40 "near" for all long forward references
xor dx,dx xor dx,dx
mov ds,dx ; DS -> 0x0000 mov ds,dx ; DS -> 0x0000
@ -124,7 +124,7 @@ start: nop
; ;
mov eax,ds mov eax,ds
test eax,eax test eax,eax
jnz near exitErr jnz near error
jmp initGDT jmp initGDT
times 32768 nop ; lots of NOPs to force a 16-bit conditional jump times 32768 nop ; lots of NOPs to force a 16-bit conditional jump
@ -155,10 +155,7 @@ storeDesc:
addrGDT:dw myGDTEnd - myGDT - 1 ; 16-bit limit of myGDT addrGDT:dw myGDTEnd - myGDT - 1 ; 16-bit limit of myGDT
dw myGDT, 0x000f ; 32-bit base address of myGDT dw myGDT, 0x000f ; 32-bit base address of myGDT
; myGDT: defDesc NULL ; the first descriptor in any descriptor table is always a dud (it corresponds to the null selector)
; TODO: Why do I need to provide a 2nd parameter for "defDesc NULL"? Is this a NASM 0.98.x bug?
;
myGDT: defDesc NULL,0 ; the first descriptor in any descriptor table is always a dud (it corresponds to the null selector)
defDesc CSEG_PROT16,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_NONE defDesc CSEG_PROT16,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_NONE
defDesc CSEG_PROT32,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_BIG defDesc CSEG_PROT32,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_BIG
defDesc DSEG_PROT16,0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_NONE defDesc DSEG_PROT16,0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_NONE
@ -235,10 +232,6 @@ exitErrDOS:
errDOSMem: errDOSMem:
db "Insufficient memory",CR,LF,'$' db "Insufficient memory",CR,LF,'$'
exitErr:nop
int3
jmp exitErr
allocPages: allocPages:
mov bx,0x2000 ; 8K paragraphs == 128K bytes mov bx,0x2000 ; 8K paragraphs == 128K bytes
mov ah,DOS_ALLOC mov ah,DOS_ALLOC
@ -354,37 +347,28 @@ toProt32:
cmp eax,[0x0000] cmp eax,[0x0000]
jne near error jne near error
mov [0x0000],edx ; restore the DWORD at 0x0000:0x0000 from EDX mov [0x0000],edx ; restore the DWORD at 0x0000:0x0000 from EDX
jmp testROM
;
; The next few tests currently work only when running as a ROM image; they rely not only on
; the contents of the last two bytes at the top of the first 1Mb, but also on their location,
; because if the processor improperly reads beyond those bytes, a fault should occur.
;
testROM:
; ;
; Test moving a byte to a 32-bit register with sign-extension ; Test moving a byte to a 32-bit register with sign-extension
; ;
movsx eax,byte [0xfffff] movsx eax,byte [cs:0xffff]
cmp eax,0xffffff80 cmp eax,0xffffff80
jne near error jne near error
; ;
; Test moving a word to a 32-bit register with sign-extension ; Test moving a word to a 32-bit register with sign-extension
; ;
movsx eax,word [0xffffe] movsx eax,word [cs:0xfffe]
cmp eax,0xffff80fc cmp eax,0xffff80fc
jne near error jne near error
; ;
; Test moving a byte to a 32-bit register with zero-extension ; Test moving a byte to a 32-bit register with zero-extension
; ;
movzx eax,byte [0xfffff] movzx eax,byte [cs:0xffff]
cmp eax,0x00000080 cmp eax,0x00000080
jne near error jne near error
; ;
; Test moving a word to a 32-bit register with zero-extension ; Test moving a word to a 32-bit register with zero-extension
; ;
movzx eax,word [0xffffe] movzx eax,word [cs:0xfffe]
cmp eax,0x000080fc cmp eax,0x000080fc
jne near error jne near error
; ;
@ -393,25 +377,53 @@ testROM:
mov esp,0x40000 mov esp,0x40000
mov edx,[esp] ; save word at scratch address 0x40000 mov edx,[esp] ; save word at scratch address 0x40000
add esp,4 add esp,4
push -128 ; NASM refuses to use opcode 0x6A ("PUSH imm8") push byte -128 ; NASM refuses to use opcode 0x6A ("PUSH imm8") unless we specify "byte"
pop ebx ; verify EBX == 0xFFFFFF80 pop ebx ; verify EBX == 0xFFFFFF80
cmp ebx,0xFFFFFF80
jne near error
and ebx,0xff ; verify EBX == 0x00000080 and ebx,0xff ; verify EBX == 0x00000080
cmp ebx,0x00000080
jne near error
movsx bx,bl ; verify EBX == 0x0000FF80 movsx bx,bl ; verify EBX == 0x0000FF80
cmp ebx,0x0000FF80
jne near error
movsx ebx,bx ; verify EBX == 0xFFFFFF80 movsx ebx,bx ; verify EBX == 0xFFFFFF80
cmp ebx,0xFFFFFF80
jne near error
movzx bx,bl ; verify EBX == 0xFFFF0080 movzx bx,bl ; verify EBX == 0xFFFF0080
cmp ebx,0xFFFF0080
jne near error
movzx ebx,bl ; verify EBX == 0x00000080 movzx ebx,bl ; verify EBX == 0x00000080
cmp ebx,0x00000080
jne near error
not ebx ; verify EBX == 0xFFFFFF7F not ebx ; verify EBX == 0xFFFFFF7F
cmp ebx,0xFFFFFF7F
jne near error
movsx bx,bl ; verify EBX == 0xFFFF007F movsx bx,bl ; verify EBX == 0xFFFF007F
cmp ebx,0xFFFF007F
jne near error
movsx ebx,bl ; verify EBX == 0x0000007F movsx ebx,bl ; verify EBX == 0x0000007F
cmp ebx,0x0000007F
jne near error
not ebx ; verify EBX == 0xFFFFFF80 not ebx ; verify EBX == 0xFFFFFF80
cmp ebx,0xFFFFFF80
jne near error
movzx ebx,bx ; verify EBX == 0x0000FF80 movzx ebx,bx ; verify EBX == 0x0000FF80
cmp ebx,0x0000FF80
jne near error
movzx bx,bl ; verify EBX == 0x00000080 movzx bx,bl ; verify EBX == 0x00000080
cmp ebx,0x00000080
jne near error
movsx bx,bl movsx bx,bl
neg bx neg bx
neg bx neg bx
cmp ebx,0x0000FF80
jne near error
movsx ebx,bx movsx ebx,bx
neg ebx neg ebx
neg ebx neg ebx
cmp ebx,0xFFFFFF80
jne near error
; ;
; Test assorted 32-bit addressing modes ; Test assorted 32-bit addressing modes
; ;
@ -423,35 +435,109 @@ testROM:
mov ecx,0x40000 ; now access that scratch address using various addressing modes mov ecx,0x40000 ; now access that scratch address using various addressing modes
cmp [ecx],eax cmp [ecx],eax
jne error jne near error
add ecx,64 add ecx,64
cmp [ecx-64],eax cmp [ecx-64],eax
jne error jne near error
sub ecx,64 sub ecx,64
shr ecx,1 shr ecx,1
cmp [ecx+0x20000],eax cmp [ecx+0x20000],eax
jne error jne near error
cmp [ecx+ecx],eax cmp [ecx+ecx],eax
jne error jne near error
shr ecx,1 shr ecx,1
cmp [ecx+ecx*2+0x10000],eax cmp [ecx+ecx*2+0x10000],eax
jne error jne near error
cmp [ecx*4],eax cmp [ecx*4],eax
jne error jne near error
mov ebp,ecx mov ebp,ecx
cmp [ebp+ecx*2+0x10000],eax cmp [ebp+ecx*2+0x10000],eax
je error ; since SS != DS, this better be a mismatch je near error ; since SS != DS, this better be a mismatch
mov [0x40000],edx ; restore word at scratch address 0x40000 mov [0x40000],edx ; restore word at scratch address 0x40000
;
; Now run a series of unverified opcode tests (verification will happen later, by comparing the output of the tests)
;
mov esi,tableOps ; ESI -> tableOps entry
testOps:
movzx ecx,byte [cs:esi] ; ECX == length of instruction sequence
jecxz doneOps ; zero means we've reached the end of the table
movzx ebx,byte [cs:esi+1] ; EBX == TYPE
shl ebx,5 ; EBX == type * 32
movzx edx,byte [cs:esi+2] ; EDX == SIZE
lea ebx,[cs:typeValues+ebx+edx*8] ; EBX -> values for type
add esi,3 ; ESI -> instruction sequence to test
push ecx
mov ecx,[cs:ebx] ; ECX == count of values for dst
mov ebx,[cs:ebx+4] ; EBX -> values for dst
mov ebp,ecx ; EBP == count of values for src
mov edi,ebx ; EDI -> values for src
testDst:
push ebp
push edi
testSrc:
mov eax,[cs:ebx] ; EAX == dst
mov edx,[cs:edi] ; EDX == src
call esi
add edi,4 ; EDI -> next src
dec ebp ; decrement src count
jnz testSrc
pop edi ; ESI -> restored values for src
pop ebp ; EBP == restored count of values for src
add ebx,4 ; EBX -> next dst
loop testDst
pop ecx
add esi,ecx ; ESI -> next tableOps entry
jmp testOps
doneOps:
jmp doneProt jmp doneProt
error: nop TYPE_ARITH equ 0
SIZE_BYTE equ 0
SIZE_SHORT equ 1
SIZE_LONG equ 2
%macro defOp 4
%ifidn %2,al
%assign size SIZE_BYTE
%elifidn %2,ax
%assign size SIZE_SHORT
%else
%assign size SIZE_LONG
%endif
db %%end-%%beg,%4,size
%%beg: %1 %2,%3
ret
%%end:
%endmacro
tableOps:
defOp add,al,dl,TYPE_ARITH
defOp add,ax,dx,TYPE_ARITH
defOp add,eax,edx,TYPE_ARITH
db 0
typeValues:
dd 9,arithValues,18,arithValues,27,arithValues,0,0
arithValues:
dd 0x00,0x01,0x02,0x7E,0x7F,0x80,0x81,0xFE,0xFF
dd 0x0000,0x0001,0x0002,0x7FFE,0x7FFF,0x8000,0x8001,0xFFFE,0xFFFF
dd 0x00000000,0x00000001,0x00000002,0x7FFFFFFE,0x7FFFFFFF,0x80000000,0x80000001,0xFFFFFFFE,0xFFFFFFFF
error: int3
jmp error
doneProt: doneProt:
mov ax,DSEG_PROT16 mov ax,DSEG_PROT16
@ -468,7 +554,7 @@ toProt16:
%endif %endif
goReal: mov eax,cr0 goReal: mov eax,cr0
and eax,~(CR0_MSW_PE | CR0_PG) and eax,~(CR0_MSW_PE | CR0_PG) & 0xffffffff
mov cr0,eax mov cr0,eax
jmpReal: jmpReal:
jmp CSEG_REAL:toReal jmp CSEG_REAL:toReal
@ -483,6 +569,7 @@ toReal:
cmp ax,CSEG_REAL ; is CS equal to 0xf000? cmp ax,CSEG_REAL ; is CS equal to 0xf000?
je near jmpStart ; yes, so loop around, only because we have nowhere else to go je near jmpStart ; yes, so loop around, only because we have nowhere else to go
int INT_DOSEXIT ; no, so assume we're running under DOS and exit int INT_DOSEXIT ; no, so assume we're running under DOS and exit
; ;
; Fill the remaining space with NOPs until we get to target offset 0xFFF0. ; 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. ; Note that we subtract 0x100 from the target offset because we're ORG'ed at 0x100.