Initial commit (a clone of the jsmachines project as of v1.15.3)
This commit is contained in:
commit
a5e3e6a59d
714 changed files with 130602 additions and 0 deletions
628
tests/pc/cpu/cpuid.asm
Normal file
628
tests/pc/cpu/cpuid.asm
Normal file
|
|
@ -0,0 +1,628 @@
|
|||
; From https://groups.google.com/forum/#!searchin/comp.sys.ibm.pc/single-step$20interrupt/comp.sys.ibm.pc/irWPIdzmCHQ/SyqEtq9mqCEJ
|
||||
|
||||
title CPUID - Determine CPU & NDP Type
|
||||
page 58,122
|
||||
name CPUID
|
||||
|
||||
;
|
||||
; CPUID uniquely identifies each NEC & Intel CPU & NDP.
|
||||
;
|
||||
; Notes on program structure:
|
||||
;
|
||||
; This program uses four segments, two classes, and one group.
|
||||
; It demonstrates a useful technique for programmers who generate
|
||||
; .COM programs. In particular, it shows how to use segment
|
||||
; classes to re-order segments, and how to eliminate the linker's
|
||||
; warning message about the absence of a stack segment.
|
||||
;
|
||||
; The correspondence between segments and classes is as follows:
|
||||
;
|
||||
; Segment Class
|
||||
; ------- -----
|
||||
; STACK prog
|
||||
; DATA data
|
||||
; MDATA data
|
||||
; CODE prog
|
||||
;
|
||||
; The segments apprear in the above order in the program source
|
||||
; to avoid forward references in the CODE segment to labels in
|
||||
; the DATA/MDATA segments. However, because the STACK segment
|
||||
; appears first in the file, it and all segments in the same
|
||||
; class are made contiguous by the linker. Thus they precede
|
||||
; the DATA/MDATA segments in the resulting .COM file because
|
||||
; the latter are in a different class. In this manner, although
|
||||
; DATA and MDATA precede CODE in the source file, their order
|
||||
; is swapped in the .COM file. That way there is no need for
|
||||
; an initial skip over the data areas to get to the CODE
|
||||
; segment. As a side benefit, declaring a STACK segment (as
|
||||
; the first segment in the source) also eliminates the linker's
|
||||
; warning about that segment missing. Finally, all segments
|
||||
; are declared to be in the same group so the linker can properly
|
||||
; resolve offsets.
|
||||
;
|
||||
; Note that if you re-assemble the code for any reason, it is
|
||||
; important to use an assembler later than the IBM version 1.0.
|
||||
; That version has a number of bugs including an annoying habit
|
||||
; of alphabetizing segment names in the .OBJ file. If you use
|
||||
; IBM MASM 2.0, be sure to specify /S to order the segments
|
||||
; properly.
|
||||
;
|
||||
; If the program reports results at variance with your knowledge
|
||||
; of the system, please contact the author.
|
||||
;
|
||||
; Environments tested in:
|
||||
;
|
||||
; CPU Speed
|
||||
; System in MHz CPU NDP
|
||||
; ------ --------- --- ---
|
||||
; IBM PC AT 6 Intel 80286 Intel 80287
|
||||
; IBM PC AT 9 Intel 80286 Intel 80287
|
||||
; IBM PC AT 6 Intel 80286 none
|
||||
; IBM PC AT 8.5 Intel 80286 none
|
||||
; IBM PC 4.77 Intel 8088 Intel 8087-3
|
||||
; IBM PC 4.77 Intel 8088* Intel 8087-3
|
||||
; IBM PC XT 4.77 Intel 8088 none
|
||||
; IBM PC XT 4.77 Intel 8088 Intel 8087-3
|
||||
; IBM PC Portable 4.77 NEC V20 none
|
||||
; COMPAQ 4.77 Intel 8088 none
|
||||
; COMPAQ 4.77 NEC V20 none
|
||||
; AT&T PC 6300 8 Intel 8086 Intel 8087-2
|
||||
; AT&T PC 6300 8 NEC V30 Intel 8087-2
|
||||
; Tandy 2000 8 Intel 80186 none
|
||||
;
|
||||
; * = faulty CPU
|
||||
;
|
||||
; Program structure:
|
||||
;
|
||||
; Group PGROUP:
|
||||
; Stack segment STACK, byte-aligned, stack, class 'prog'
|
||||
; Program segment CODE, byte-aligned, public, class 'prog'
|
||||
; Data segment DATA, byte-aligned, public, class 'data'
|
||||
; Data segment MDATA, byte-aligned, public, class 'data'
|
||||
;
|
||||
; Assembly requirements:
|
||||
;
|
||||
; Use MASM 1.25 or later.
|
||||
; With IBM's MASM 2.0 only, use /S to avoid alphabetizing the
|
||||
; segment names.
|
||||
; Use /r option to generate real NDP code.
|
||||
;
|
||||
; MASM CPUID/r; to convert .ASM to .OBJ
|
||||
; LINK CPUID; to convert .OBJ to .EXE
|
||||
; EXE2BIN CPUID CPUID.COM to convert .EXE to .COM
|
||||
; ERASE CPUID.EXE to avoid executing .EXE
|
||||
;
|
||||
; Note that the linker doesn't warn about a missing stack segment.
|
||||
;
|
||||
; Author:
|
||||
;
|
||||
; Original code by: Bob Smith May 1985
|
||||
; Qualitas, Inc.
|
||||
; 8314 Thoreau Dr.
|
||||
; Bethesda, MD 20817
|
||||
;
|
||||
; Arthur Zachai suggested the technique to distinguish within the
|
||||
; 808x and 8018x families by exploiting the difference in the
|
||||
; length of their pre-fetch instruction queues.
|
||||
;
|
||||
; Published in PC Tech Journal - April 1986 - Vol 4 No 4
|
||||
|
||||
subttl Structures, Records, Equates, & Macros
|
||||
page
|
||||
ARG_STR struc
|
||||
dw ? ; caller's bp
|
||||
ARG_OFF dw ? ; caller's offset
|
||||
ARG_SEG dw ? ; segment
|
||||
ARG_FLG dw ? ; flags
|
||||
ARG_STR ends
|
||||
|
||||
; Record to define bits in the CPU's & NDP's flags' registers
|
||||
|
||||
CPUFLAGS record RO:1,NT:1,IOPL:2,OF:1,DF:1,IF:1,TF:1,SF:1,ZF:1,R1:1,AF:1,R2:1,PF:1,R3:1,CF:1
|
||||
|
||||
NDPFLAGS record R4:3,IC:1,RC:2,PC:2,IEM:1,R5:1,PM:1,UM:1,OM:1,ZM:1,DM:1,IM:1
|
||||
|
||||
; FLG_PIQL Pre-fetch instruction queue length, 0 => 4-byte
|
||||
; 1 => 6-byte
|
||||
; FLG_08 Intel 808x
|
||||
; FLG_NEC NEC V20 or V30
|
||||
; FLG_18 Intel 8018x
|
||||
; FLG_28 Intel 8028x
|
||||
; FLG_87 Intel 8087
|
||||
; FLG_287 Intel 80287
|
||||
;
|
||||
; FLG_CERR Faulty CPU
|
||||
; FLG_NERR Faulty NDP switch setting
|
||||
|
||||
FLG record RSVD:9,FLG_NERR:1,FLG_CERR:1,FLG_NDP:2,FLG_CPU:3
|
||||
|
||||
; CPU-related flags
|
||||
|
||||
FLG_PIQL equ 001b shl FLG_CPU
|
||||
FLG_08 equ 000b shl FLG_CPU
|
||||
FLG_NEC equ 010b shl FLG_CPU
|
||||
FLG_18 equ 100b shl FLG_CPU
|
||||
FLG_28 equ 110b shl FLG_CPU
|
||||
|
||||
FLG_8088 equ FLG_08
|
||||
FLG_8086 equ FLG_08 or FLG_PIQL
|
||||
FLG_V20 equ FLG_NEC
|
||||
FLG_v30 equ FLG_NEC or FLG_PIQL
|
||||
FLG_80188 equ FLG_18
|
||||
FLG_80186 equ FLG_18 or FLG_PIQL
|
||||
FLG_80286 equ FLG_28 or FLG_PIQL
|
||||
|
||||
; NDP-related flags
|
||||
|
||||
; 00b shl FLG_NDP Not Present
|
||||
FLG_87 equ 01b shl FLG_NDP
|
||||
FLG_287 equ 10b shl FLG_NDP
|
||||
BEL equ 07h
|
||||
LF equ 0ah
|
||||
CR equ 0dh
|
||||
EOS equ '$'
|
||||
|
||||
POPFF macro
|
||||
local L1,L2
|
||||
jmp short L2 ; skip over IRET
|
||||
L1:
|
||||
iret ; pop the cs & ip pushed below along
|
||||
; with the flags, our original purpose
|
||||
L2:
|
||||
push cs ; prepare for IRET by pushing cs
|
||||
call L1 ; push ip, jump to IRET
|
||||
endm ; POPFF macro
|
||||
|
||||
TAB macro TYP
|
||||
push bx ; save for a moment
|
||||
and bx,mask FLG_&TYP ; isolate flags
|
||||
mov cl,FLG_&TYP ; shift amount
|
||||
shr bx,cl ; shift to low-order
|
||||
shl bx,1 ; times two to index table of words
|
||||
mov dx,TYP&MSG_TAB[bx] ; ds:dx => descriptive message
|
||||
pop bx ; restore
|
||||
mov ah,09h ; function code to display string
|
||||
int 21h ; request dos service
|
||||
endm ; TAB macro
|
||||
page
|
||||
INT_VEC segment at 0 ; start INT_VEC segment
|
||||
dd ? ; pointer to INT 00h
|
||||
INT01_OFF dw ? ; pointer to INT 01h
|
||||
INT01_SEG dw ?
|
||||
INT_VEC ends ; end INT_VEC segment
|
||||
|
||||
PGROUP group STACK,CODE,DATA,MDATA
|
||||
|
||||
; The following segment both positions class 'prog' segments lower in
|
||||
; memory than others so the first byte of the resulting .COM file is
|
||||
; in the CODE segment, as well as satisfies the LINKer's need to have
|
||||
; a stack segment.
|
||||
|
||||
STACK segment byte stack 'prog' ; start STACK segment
|
||||
STACK ends ; end STACK segment
|
||||
|
||||
I11_REC record I11_PRN:2,I11_RSV1:2,I11_COM:3,I11_RSV2:1,I11_DISK:2,I11_VID:2,I11_RSV3:2,I11_NDP:1,I11_IPL:1
|
||||
|
||||
DATA segment byte public 'data' ; start DATA segment
|
||||
assume ds:PGROUP
|
||||
|
||||
OLDINT01_VEC label dword ; save area for original INT 01h handler
|
||||
OLDINT01_OFF dw ?
|
||||
OLDINT01_SEG dw ?
|
||||
|
||||
NDP_CW label word ; save area for NDP control word
|
||||
db ?
|
||||
NDP_CW_HI db 0 ; high byte of control word
|
||||
NDP_ENV dw 7 dup(?) ; save area for NDP environment
|
||||
|
||||
DATA ends
|
||||
subttl Message Data Area
|
||||
page
|
||||
MDATA segment byte public 'data' ; start MDATA segment
|
||||
assume ds:PGROUP
|
||||
|
||||
MSG_START db 'CPUID -- Version 1.0'
|
||||
db CR,LF,CR,LF,EOS
|
||||
MSG_8088 db 'CPU is an Intel 8088.'
|
||||
db CR,LF,EOS
|
||||
MSG_8086 db 'CPU is an Intel 8086.'
|
||||
db CR,LF,EOS
|
||||
MSG_V20 db 'CPU is an NEC V20.'
|
||||
db CR,LF,EOS
|
||||
MSG_V30 db 'CPU is an NEC V30.'
|
||||
db CR,LF,EOS
|
||||
MSG_80188 db 'CPU is an Intel 80188.'
|
||||
db CR,LF,EOS
|
||||
MSG_80186 db 'CPU is an Intel 80186.'
|
||||
db CR,LF,EOS
|
||||
MSG_UNK db 'CPU is a maverick -- 80288??.'
|
||||
db CR,LF,EOS
|
||||
MSG_80286 db 'CPU is an Intel 80286.'
|
||||
db CR,LF,EOS
|
||||
|
||||
CPUMSG_TAB label word
|
||||
dw PGROUP:MSG_8088 ; 000 = Intel 8088
|
||||
dw PGROUP:MSG_8086 ; 001 = Intel 8086
|
||||
dw PGROUP:MSG_V20 ; 010 = NEC V20
|
||||
dw PGROUP:MSG_V30 ; 011 = NEC V30
|
||||
dw PGROUP:MSG_80188 ; 100 = Intel 80188
|
||||
dw PGROUP:MSG_80186 ; 101 = Intel 80186
|
||||
dw PGROUP:MSG_UNK ; 110 = ?
|
||||
dw PGROUP:MSG_80286 ; 111 = Intel 80286
|
||||
|
||||
NDPMSG_TAB label word
|
||||
dw PGROUP:MSG_NDPX ; 00 = No NDP
|
||||
dw PGROUP:MSG_8087 ; 01 = Intel 8087
|
||||
dw PGROUP:MSG_80287 ; 10 = Intel 80287
|
||||
|
||||
MSG_NDPX db 'NDP is not present.'
|
||||
db CR,LF,EOS
|
||||
MSG_8087 db 'NDP is an Intel 8087.'
|
||||
db CR,LF,EOS
|
||||
MSG_80287 db 'NDP is an Intel 80287.'
|
||||
db CR,LF,EOS
|
||||
|
||||
CERRMSG_TAB label word
|
||||
dw PGROUP:MSG_CPUOK ; 0 = CPU healthy
|
||||
dw PGROUP:MSG_CPUBAD ; 1 = CPU faulty
|
||||
|
||||
MSG_CPUOK db 'CPU appears to be healthy.'
|
||||
db CR,LF,EOS
|
||||
MSG_CPUBAD label byte
|
||||
db BEL,'*** CPU incorrectly allows interrupts '
|
||||
db 'after a change to SS ***',CR,LF
|
||||
db 'It should be replaced with a more recent '
|
||||
db 'version as it could crash the',CR,LF
|
||||
db 'system at seemingly random times.',CR,LF,EOS
|
||||
|
||||
NERRMSG_TAB label word
|
||||
dw PGROUP:MSG_NDPSWOK ; 0 = NDP switch set correctly
|
||||
dw PGROUP:MSG_NDPSWERR ; 1 = NDP switch set incorrectly
|
||||
|
||||
MSG_NDPSWOK db EOS ; no message
|
||||
MSG_NDPSWERR label byte
|
||||
db '*** Although there is an NDP installed '
|
||||
db 'on this sytem, the corresponding',CR,LF
|
||||
db 'system board switch is not properly set. '
|
||||
db 'To correct this, flip switch 2 of',CR,LF
|
||||
db 'switch block 1 on the system board.',CR,LF,EOS
|
||||
|
||||
MDATA ends ; end MDATA segment
|
||||
subttl Main Routine
|
||||
page
|
||||
CODE segment byte public 'prog' ; start CODE segment
|
||||
assume cs:PGROUP,ds:PGROUP,es:PGROUP
|
||||
org 100h ; skip over PSP
|
||||
|
||||
INITIAL proc near
|
||||
mov dx,offset ds:MSG_START ; starting message
|
||||
mov ah,09h ; function code to display string
|
||||
int 21h ; request DOS service
|
||||
|
||||
call CPUID ; check the CPU's identity
|
||||
|
||||
TAB CPU ; display CPU results
|
||||
TAB NDP ; display NDP results
|
||||
TAB CERR ; display CPU ERR results
|
||||
TAB NERR ; display NDP ERR results
|
||||
|
||||
ret ; return to DOS
|
||||
INITIAL endp ; end INITIAL procedure
|
||||
subttl CPUID Procedure
|
||||
page
|
||||
CPUID proc near ; start CPUID procedure
|
||||
assume cs:PGROUP,ds:PGROUP,es:PGROUP
|
||||
|
||||
; This procedure determines the type of CPU and NDP (if any) in use.
|
||||
;
|
||||
; The possibilities include:
|
||||
;
|
||||
; Intel 8086
|
||||
; Intel 8088
|
||||
; NEC V20
|
||||
; NEC V30
|
||||
; Intel 80186
|
||||
; Intel 80188
|
||||
; Intel 80286
|
||||
; Intel 8087
|
||||
; Intel 80287
|
||||
;
|
||||
; Also checked is whether or not the CPU allows interrupts after
|
||||
; changing the SS register segment. If the CPU does, it is faulty
|
||||
; and should be replaced.
|
||||
;
|
||||
; Further, if an NDP is installed, non-AT machines should have a
|
||||
; system board switch set. Such a discrepancy is reported.
|
||||
;
|
||||
; On exit, BX contains flag settings (as defined in FLG record) which
|
||||
; the caller can check. For example, to test for an Intel 80286, use
|
||||
;
|
||||
; and bx,mask FLAG_CPU
|
||||
; cmp bx,FLG_80286
|
||||
; je ITSA286
|
||||
|
||||
irp XX,<ax,cx,di,ds,es> ; save registers
|
||||
push XX
|
||||
endm
|
||||
|
||||
; test for 80286 -- this CPU executes PUSH SP by first storing SP on
|
||||
; stack, then decrementing it. earlier CPU's decrement, THEN store.
|
||||
|
||||
mov bx,FLG_28 ; assume it's a 286
|
||||
push sp ; only 286 pushes pre-push SP
|
||||
pop ax ; get it back
|
||||
cmp ax,sp ; check for same
|
||||
je CHECK_PIQL ; they are, so it's a 286
|
||||
|
||||
; test for 80186/80188 -- 18xx and 286 CPU's mask shift/rotate
|
||||
; operations mod 32; earlier CPUs use all 8 bits of CL.
|
||||
|
||||
mov bx,FLG_18 ; assume it's an 8018x
|
||||
mov cl,32+1 ; 18x masks shift counts mod 32
|
||||
; note we can't use just 32 in CL
|
||||
mov al,0ffh ; start with all bits set
|
||||
|
||||
shl al,cl ; shift one position if 18x
|
||||
jnz CHECK_PIQL ; some bits still on,
|
||||
; so its a 18x, check PIQL
|
||||
|
||||
; test for V20
|
||||
|
||||
mov bx,FLG_NEC ; assume it's an NEC V-series CPU
|
||||
call CHECK_NEC ; see if it's an NEC chip
|
||||
jcxz CHECK_PIQL ; good guess, check PIQL
|
||||
|
||||
mov bx,FLG_08 ; it's an 808x
|
||||
subttl Check Length of Pre-Fetch Instruction Queue
|
||||
page
|
||||
; Check the length of the pre-fetch instruction queue (PIQ).
|
||||
;
|
||||
; xxxx6 CPUs have a PIQ length of 6 bytes,
|
||||
; xxxx8 CPUs have a PIQ length of 4 bytes
|
||||
;
|
||||
; Self-modifying code is used to distinguish the two PIQ lengths.
|
||||
|
||||
CHECK_PIQL:
|
||||
call PIQL_SUB ; handle via subroutine
|
||||
jcxz CHECK_ERR ; if CX is 0, INC was not executed,
|
||||
; hence PIQ length is 4
|
||||
or bx,FLG_PIQL ; PIQ length is 6
|
||||
subttl Check for Allowing Interrupts After POP SS
|
||||
page
|
||||
; Test for faulty chip (allows interrupts after change to SS register)
|
||||
|
||||
CHECK_ERR:
|
||||
xor ax,ax ; prepare to address
|
||||
; interrupt vector segment
|
||||
mov ds,ax ; DS points to segment 0
|
||||
assume ds:INT_VEC ; tell the assembler
|
||||
|
||||
cli ; nobody move while we swap
|
||||
|
||||
mov ax,offset cs:INT01 ; point to our own handler
|
||||
xchg ax,INT01_OFF ; get and swap offset
|
||||
mov OLDINT01_OFF,ax ; save to restore later
|
||||
|
||||
mov ax,cs ; our handler's segment
|
||||
xchg ax,INT01_SEG ; get and swap segment
|
||||
mov OLDINT01_SEG,ax ; save to restore later
|
||||
|
||||
; note we continue with interrupts disabled to avoid
|
||||
; an external interrupt occuring during this test
|
||||
|
||||
mov cx,1 ; initialize a register
|
||||
push ss ; save ss to store back into itself
|
||||
pushf ; move flags
|
||||
pop ax ; ... into ax
|
||||
or ax,mask TF ; set trap flag
|
||||
push ax ; place onto stack
|
||||
POPFF ; ... and then into effect
|
||||
; some CPUs effect the trap flag
|
||||
; immediately, some
|
||||
; wait one instruction
|
||||
nop ; allow interrupt to take effect
|
||||
|
||||
POST_NOP:
|
||||
pop ss ; change the stack segment register
|
||||
; (to itself)
|
||||
dec cx ; normal cpu's execute this instruction
|
||||
; before recognizing the single-step
|
||||
; interrupt
|
||||
hlt ; we never get here
|
||||
|
||||
INT01:
|
||||
|
||||
; Note: IF=TF=0
|
||||
; If we're stopped at or before POST_NOP, continue on
|
||||
|
||||
push bp ; prepare to address the stack
|
||||
mov bp,sp ; hello, Mr. stack
|
||||
|
||||
cmp [bp].ARG_OFF,offset cs:POST_NOP ; check offset
|
||||
pop bp ; restore
|
||||
ja INTO1_DONE ; we're done
|
||||
|
||||
iret ; return to caller
|
||||
|
||||
INTO1_DONE:
|
||||
|
||||
; restore old INT 01h handler
|
||||
|
||||
les ax,OLDINT01_VEC ; ES:AX ==> old INT 01h handler
|
||||
assume es:nothing ; tell the assembler
|
||||
mov INT01_OFF,ax ; restore offset
|
||||
mov INT01_SEG,es ; ... and segment
|
||||
sti ; allow interrupts again (IF=1)
|
||||
|
||||
add sp,3*2 ; strip ip, cs, and flags from stack
|
||||
|
||||
push cs ; setup ds for code below
|
||||
pop ds
|
||||
assume ds:PGROUP ; tell the assembler
|
||||
|
||||
jcxz CHECK_NDP ; if cx is 0, the dec cx was executed,
|
||||
; and the cpu is ok
|
||||
or bx,mask FLG_CERR ; it's a faulty chip
|
||||
subttl Check For Numeric Data Processor
|
||||
page
|
||||
; Test for a Numeric Data Processor -- Intel 8087 or 80287. The
|
||||
; technique used is passive -- it leaves the NDP in the same state in
|
||||
; which it is found.
|
||||
|
||||
CHECK_NDP:
|
||||
cli ; protect FNSTENV
|
||||
fnstenv NDP_ENV ; if NDP present, save
|
||||
; current environment,
|
||||
; otherwise, this instruction
|
||||
; is ignored
|
||||
mov cx,50/7 ; cycle this many times
|
||||
loop $ ; wait for result to be stored
|
||||
sti ; allow interrupts
|
||||
fninit ; initialize processor to known state
|
||||
jmp short $+2 ; wait for initialization
|
||||
|
||||
fnstcw NDP_CW ; save control word
|
||||
jmp short $+2 ; wait for result to be stored
|
||||
jmp short $+2
|
||||
cmp NDP_CW_HI,03h ; check for NDP initial control word
|
||||
jne CPUID_EXIT ; no NDP installed
|
||||
int 11h ; get equipment flags into ax
|
||||
test ax,mask I11_NDP ; check NDP-installed bit
|
||||
jnz CHECK_NDP1 ; it's correctly set
|
||||
or bx,mask FLG_NERR ; mark as in error
|
||||
CHECK_NDP1:
|
||||
and NDP_CW,not mask IEM ; enable interrupts
|
||||
; (IEM=0, 8087 only)
|
||||
fldcw NDP_CW ; reload control word
|
||||
fdisi ; disable interrupts (IEM=1) on 8087,
|
||||
; ignored by 80287
|
||||
fstcw NDP_CW ; save control word
|
||||
fldenv NDP_ENV ; restore original NDP environment
|
||||
; no need to wait
|
||||
; for environment to be loaded
|
||||
test NDP_CW,mask IEM ; check interrupt enable mask
|
||||
; (8087 only)
|
||||
jnz CPUID_8087 ; it changed, hence NDP is an 8087
|
||||
or bx,FLG_287 ; NDP is an 80287
|
||||
jmp short CPUID_EXIT ; exit with falgs in BX
|
||||
CPUID_8087:
|
||||
or bx,FLG_87 ; NDP is an 8087
|
||||
CPUID_EXIT:
|
||||
irp XX,<es,ds,di,cx,ax> ; restore registers
|
||||
pop XX
|
||||
endm
|
||||
assume ds:nothing,es:nothing
|
||||
ret ; return to caller
|
||||
CPUID endp ; end CPUID procedure
|
||||
subttl Check For NEC V20/V30
|
||||
page
|
||||
CHECK_NEC proc near
|
||||
|
||||
; The NEC V20/V30 are very compatible with the Intel 8086/8088.
|
||||
; The only point of "incompatibility" is that they do not contain
|
||||
; a bug found in the Intel CPU's. Specifically, the NEC CPU's
|
||||
; correctly restart an interrupted multi-prefix string instruction
|
||||
; at the start of the instruction. The Intel CPU's incorrectly
|
||||
; restart in the middle of the instruction. This routine tests
|
||||
; for that situation by executing such an instruction for a
|
||||
; sufficiently long period of time for a timer interrupt to occur.
|
||||
; If at the end of the instruction, CX is zero, it must be an NEC
|
||||
; CPU; if not, it's an Intel CPU.
|
||||
;
|
||||
; Note that we're counting on the timer interrupt to do its thing
|
||||
; every 18.2 times per second.
|
||||
;
|
||||
; Here's a worst case analysis: An Intel 8088/8086 executes 65535
|
||||
; iterations of LODSB ES[SI] in 2+9+13*65535 = 851,966 clock ticks.
|
||||
; If the Intel 8088/8086 is running at 10 MHz, each clock tick is
|
||||
; 100 nanoseconds, hence the entire operation takes 85 milliseconds.
|
||||
; If the timer is running at normal speed, it interrupts the CPU every
|
||||
; 55ms and so should interrupt the repeated string instruction at least
|
||||
; once.
|
||||
|
||||
mov cx,0ffffh ; move a lot of data
|
||||
sti ; ensure timer enabled
|
||||
|
||||
; execute multi-prefix instruction. note that the value of ES as
|
||||
; well as the direction flag setting is irrelevant.
|
||||
|
||||
push ax ; save registers
|
||||
push si
|
||||
rep lods byte ptr es:[si]
|
||||
pop si ; restore
|
||||
pop ax
|
||||
|
||||
; on exit: if cx is zero, it's an NEC CPU, otherwise it's an Intel CPU
|
||||
|
||||
ret ; return to caller
|
||||
CHECK_NEC endp
|
||||
subttl Pre-Fetch Instruction Queue Subroutine
|
||||
page
|
||||
PIQL_SUB proc near
|
||||
|
||||
; This subroutine discerns the length of the CPU's pre-fetch
|
||||
; instruction queue (PIQ).
|
||||
;
|
||||
; The technique used is to first ensure that the PIQ is full, then
|
||||
; change an instruction which should be in a 6-byte PIQ but not in a
|
||||
; 4-byte PIQ. Then, if the original instruction is executed, the PIQ
|
||||
; is 6-bytes long; if the new instruction is executed, PIQ length is 4.
|
||||
;
|
||||
; We ensure the PIQ is full be executing an instruction which takes
|
||||
; long enough so that the Bus Interface Unit (BIU) can fill the PIQ
|
||||
; while the instruction is executing.
|
||||
;
|
||||
; Specifically, for all byt the last STOSB, we're simple marking time
|
||||
; waiting for the BIU to fill the PIQ. The last STOSB actually changes
|
||||
; the instruction. By that time, the orignial instruction should be in
|
||||
; a six-byte PIQ byt not a four-byte PIQ.
|
||||
|
||||
assume cs:PGROUP,es:PGROUP
|
||||
@REP equ 3 ; repeat the store this many times
|
||||
std ; store backwards
|
||||
mov di,offset es:LAB_INC+@REP-1 ; change the instructions
|
||||
; at ES:DI
|
||||
; and preceding
|
||||
mov al,ds:LAB_STI ; change to a sti
|
||||
mov cx,@REP ; give the BIU time
|
||||
; to pre-fetch instructions
|
||||
cli ; ensure interrupts are disabled,
|
||||
; otherwise a timer tick
|
||||
; could change the PIQ filling
|
||||
rep stosb ; change the instruction
|
||||
; during execution of this instruction
|
||||
; the BIU is refilling the PIQ. The
|
||||
; current instruction is no longer
|
||||
; in the PIQ.
|
||||
; Note at end, CX is 0.
|
||||
|
||||
; The PIQ begins filling here
|
||||
|
||||
cld ; restore direction flag
|
||||
nop ; PIQ fillers
|
||||
nop
|
||||
nop
|
||||
|
||||
; The following instruction is beyond a four-byte-PIQ CPU's reach,
|
||||
; but within that of a six-byte-PIQ CPU.
|
||||
|
||||
LAB_INC label byte
|
||||
inc cx ; executed only if PIQ length is 6
|
||||
|
||||
LAB_STI label byte
|
||||
rept @REP-1
|
||||
sti ; restore interrupts
|
||||
endm
|
||||
ret ; return to caller
|
||||
assume ds:nothing,es:nothing
|
||||
PIQL_SUB endp ; end PIQL_SUB procedure
|
||||
|
||||
CODE ends ; end code segment
|
||||
|
||||
if1
|
||||
%OUT Pass 1 Complete
|
||||
else
|
||||
%OUT Pass 2 Complete
|
||||
endif
|
||||
|
||||
end INITIAL ; end CPUID module
|
||||
58
tests/pc/exec/exec.asm
Normal file
58
tests/pc/exec/exec.asm
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
.model tiny
|
||||
.code
|
||||
|
||||
ExecBlk Struc
|
||||
Psp dw ?
|
||||
Cmdline dw ?
|
||||
CmdSeg dw ?
|
||||
FCB1 dw ?
|
||||
FCB1seg dw ?
|
||||
FCB2 dw ?
|
||||
FCB2seg dw ?
|
||||
ExecBlk ends
|
||||
|
||||
org 100h
|
||||
|
||||
main proc
|
||||
lea bx,pspblk
|
||||
mov sp,bx
|
||||
call resize
|
||||
|
||||
mov execb.psp,0 ; copy our environment
|
||||
mov execb.cmdline,80h ; pass command line as is
|
||||
mov execb.FCB1,5ch
|
||||
mov execb.FCB2,6ch
|
||||
mov execb.cmdseg,cs
|
||||
mov execb.FCB1seg,cs
|
||||
mov execb.FCB2seg,cs
|
||||
lea dx,execf
|
||||
push ds
|
||||
pop es
|
||||
lea bx,execb
|
||||
mov ax,4b00h
|
||||
int 21h
|
||||
merr:
|
||||
mov ax,4c00h
|
||||
int 21h
|
||||
main endp
|
||||
resize proc
|
||||
add bx,15
|
||||
mov cl,4
|
||||
shr bx,cl
|
||||
mov ax,cs
|
||||
add bx,ax
|
||||
mov ah,4ah
|
||||
int 21h
|
||||
jc merr
|
||||
ret
|
||||
resize endp
|
||||
|
||||
|
||||
execf db "123.EXE",0 ; ASCIIZ pathname of file to be
|
||||
execd
|
||||
execb ExecBlk <>
|
||||
_stack dw 1000h dup(?)
|
||||
|
||||
pspblk label byte
|
||||
|
||||
end main
|
||||
1
tests/pc/trace/.gitignore
vendored
Normal file
1
tests/pc/trace/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/trace.lst
|
||||
16
tests/pc/trace/makefile
Normal file
16
tests/pc/trace/makefile
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#
|
||||
# To load trace.com and trace.txt onto a virtual disk image that PCjs can access,
|
||||
# add an "automount" setting to your PCjs <fdc> configuration, like so:
|
||||
#
|
||||
# <fdc id="fdcNEC" automount='{B:{name:"Trace Tests",path:"/tests/pc/trace/trace.com;trace.txt"}}'/>
|
||||
#
|
||||
# Alternatively, if you want to run the tests in another virtual PC environment (eg, VMware Fusion),
|
||||
# you can create a .ISO image on OS X, like so:
|
||||
#
|
||||
# hdiutil makehybrid -o ~/Sites/jsmachines/disks/pc/test/trace.iso ~/Sites/jsmachines/tests/pc/trace -iso -joliet
|
||||
#
|
||||
|
||||
all: trace.com
|
||||
|
||||
trace.com: trace.asm
|
||||
nasm -f bin trace.asm -l trace.lst -o trace.com
|
||||
684
tests/pc/trace/trace.asm
Normal file
684
tests/pc/trace/trace.asm
Normal file
|
|
@ -0,0 +1,684 @@
|
|||
;
|
||||
; trace.asm
|
||||
;
|
||||
; Purpose: take an instruction log, as recorded by the Debugger's traceLog() function, and
|
||||
; "play" the instructions back on another machine DOS-compatible 8086 machine, verifying that:
|
||||
;
|
||||
; 1) results match the recorded results
|
||||
; 2) any "modified" flags match the recorded flags
|
||||
; 3) any "unmodified" flags remain unmodified
|
||||
;
|
||||
; The format of an instruction log entry is a series of lines (ASCII characters terminated by an LF),
|
||||
; where each line looks like:
|
||||
;
|
||||
; F000:EEFF SHL(0480,0002,F006) 1200,F006
|
||||
;
|
||||
; ie, address, space, instruction, parenthesis, dst operand, comma, src operand, comma,
|
||||
; input flags, parenthesis, space, result, comma, and output flags.
|
||||
;
|
||||
; WARNING: For the shift and rotate tests to pass on a real x86 CPU, we either have to distinguish
|
||||
; between single-bit shifts and multi-bit shifts (because the latter leaves PS_OF in an "undefined"
|
||||
; state), or we have to ignore PS_OF altogether. For now, I'm specifying PS_ALL_BUT_OF for those
|
||||
; instructions, even though we'll be missing OVERFLOW validation for all single-bit shifts and rotates.
|
||||
;
|
||||
CPU 8086
|
||||
;
|
||||
; Bit masks for all the arithmetic flags we care about
|
||||
;
|
||||
PS_NONE equ 0x0000
|
||||
PS_CF equ 0x0001 ; bit 0: Carry flag
|
||||
PS_PF equ 0x0004 ; bit 2: Parity flag
|
||||
PS_AF equ 0x0010 ; bit 4: Auxiliary Carry flag (aka Arithmetic flag)
|
||||
PS_ZF equ 0x0040 ; bit 6: Zero flag
|
||||
PS_SF equ 0x0080 ; bit 7: Sign flag
|
||||
PS_OF equ 0x0800 ; bit 11: Overflow flag
|
||||
PS_ALL equ PS_CF | PS_PF | PS_AF | PS_ZF | PS_SF | PS_OF
|
||||
PS_ALL_BUT_AF equ PS_CF | PS_PF | PS_ZF | PS_SF | PS_OF
|
||||
PS_ALL_BUT_OF equ PS_CF | PS_PF | PS_AF | PS_ZF | PS_SF
|
||||
|
||||
%macro openF 1
|
||||
section .data
|
||||
%%name: db %1,0
|
||||
section .text
|
||||
mov dx,%%name
|
||||
mov ax,0x3D00
|
||||
int 0x21
|
||||
%endmacro
|
||||
|
||||
%macro readF 3
|
||||
%ifnidni %1,bx
|
||||
mov bx,%1
|
||||
%endif
|
||||
%ifnidni %2,dx
|
||||
mov dx,%2
|
||||
%endif
|
||||
%ifnidni %3,cx
|
||||
mov cx,%3
|
||||
%endif
|
||||
mov ah,0x3F
|
||||
int 0x21
|
||||
%endmacro
|
||||
|
||||
%macro print 1
|
||||
%ifidni %1,line
|
||||
push ax
|
||||
push cx
|
||||
push dx
|
||||
push si
|
||||
mov dx,si
|
||||
dec cx
|
||||
add si,cx
|
||||
mov byte [si],'$'
|
||||
mov ah,0x09
|
||||
int 0x21
|
||||
mov dx,strCRLF
|
||||
mov ah,0x09
|
||||
int 0x21
|
||||
pop si
|
||||
pop dx
|
||||
pop cx
|
||||
pop ax
|
||||
%else
|
||||
%ifstr %1
|
||||
section .data
|
||||
%%str: db %1,'$'
|
||||
section .text
|
||||
push dx
|
||||
mov dx,%%str
|
||||
%elifnidni %1,dx
|
||||
push dx
|
||||
mov dx,%1
|
||||
%endif
|
||||
push ax
|
||||
mov ah,0x09
|
||||
int 0x21
|
||||
pop ax
|
||||
%ifnidni %1,dx
|
||||
pop dx
|
||||
%endif
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro exit 0-2
|
||||
%ifstr %1
|
||||
section .data
|
||||
%%msg: db %1,'$'
|
||||
section .text
|
||||
%ifidni %2,oncarry
|
||||
jnc %%ok
|
||||
%endif
|
||||
mov dx,%%msg
|
||||
mov ah,0x09
|
||||
int 0x21
|
||||
%endif
|
||||
int 0x20
|
||||
%%ok:
|
||||
%endmacro
|
||||
|
||||
%macro break 0
|
||||
;
|
||||
; "int3" generates the 1-byte breakpoint instruction; "int 3" generates a 2-byte software interrupt
|
||||
;
|
||||
int3
|
||||
%endmacro
|
||||
|
||||
org 0x100
|
||||
section .text
|
||||
|
||||
openF "TRACE.TXT"
|
||||
exit "unable to open file",oncarry
|
||||
|
||||
mov di,file_buffer
|
||||
readF ax,di,file_buffer_len
|
||||
|
||||
m1: exit "unable to read file",oncarry
|
||||
|
||||
test ax,ax ; AX contains how many bytes were actually read
|
||||
jnz m2
|
||||
exit "processing complete"
|
||||
|
||||
m2: cld
|
||||
mov si,file_buffer
|
||||
add di,ax
|
||||
;
|
||||
; At this point, DS:SI is the current line pointer, and DI is the end-of-buffer
|
||||
; position. getLine() will update CX to the length of the current line (including
|
||||
; the terminating LF).
|
||||
;
|
||||
m3: call getLine
|
||||
jnc m4
|
||||
;
|
||||
; Oops, carry is set, so we're missing part or all of the next line. Move the
|
||||
; partial line to the top of the file buffer and then fill the rest of the buffer.
|
||||
;
|
||||
mov di,file_buffer
|
||||
rep movsb
|
||||
mov cx,file_buffer_end
|
||||
sub cx,di
|
||||
readF bx,di,cx
|
||||
jmp m1
|
||||
;
|
||||
; OK, we now have a complete line at DS:SI, guaranteed to end with an LF, with a
|
||||
; length of CX (although CX will soon be overwritten by calls to getHex).
|
||||
;
|
||||
m4:
|
||||
; print line
|
||||
|
||||
mov ah,' '
|
||||
call skipTo
|
||||
m4err: exit "missing space",oncarry
|
||||
inc si
|
||||
push di
|
||||
mov di,ins_name
|
||||
m5: lodsb
|
||||
cmp al,'('
|
||||
je m6
|
||||
stosb
|
||||
jmp m5
|
||||
m6: mov al,'$'
|
||||
stosb
|
||||
pop di
|
||||
|
||||
print ins_name
|
||||
print strColon
|
||||
|
||||
call getHex
|
||||
mov [dst_operand],ax
|
||||
mov [dst_operand+2],dx
|
||||
mov [dst_size],cx
|
||||
call printHex
|
||||
print strComma
|
||||
|
||||
inc si
|
||||
call getHex
|
||||
mov [src_operand],ax
|
||||
mov [src_operand+2],dx
|
||||
mov [src_size],cx
|
||||
call printHex
|
||||
|
||||
inc si
|
||||
call getHex
|
||||
and ax,PS_ALL
|
||||
mov [operand_flags],ax
|
||||
jcxz m6b
|
||||
print strComma
|
||||
call printHex
|
||||
|
||||
m6b: mov ah,' '
|
||||
call skipTo
|
||||
jc m4err
|
||||
inc si
|
||||
call getHex
|
||||
mov [result_operand],ax
|
||||
mov [result_operand+2],dx
|
||||
mov [result_size],cx
|
||||
print strEquals
|
||||
call printHex
|
||||
print strComma
|
||||
|
||||
inc si
|
||||
call getHex
|
||||
and ax,PS_ALL
|
||||
mov [result_flags],ax
|
||||
call printHex
|
||||
print strCRLF
|
||||
;
|
||||
; Now that we know operand sizes, it's time to look up the instruction function
|
||||
;
|
||||
push si
|
||||
mov si,ins_table
|
||||
push di
|
||||
m7a: mov di,ins_name
|
||||
m7b: lodsb
|
||||
test al,al
|
||||
jz m8
|
||||
mov ah,[es:di]
|
||||
inc di
|
||||
cmp al,ah
|
||||
je m7b
|
||||
m7c: lodsb
|
||||
test al,al
|
||||
jnz m7c
|
||||
add si,8 ; after then name, each ins_table entry is 4 words long
|
||||
cmp byte [si],0
|
||||
jne m7a
|
||||
print "missing function: "
|
||||
print ins_name
|
||||
exit
|
||||
|
||||
m8: mov ax,[si]
|
||||
mov cx,[result_size]
|
||||
mov dx,compare8
|
||||
cmp cl,4
|
||||
jb m8a
|
||||
mov ax,[si+2]
|
||||
mov dx,compare16
|
||||
cmp cl,8
|
||||
jb m8a
|
||||
mov ax,[si+4]
|
||||
mov dx,compare32
|
||||
m8a: test ax,ax
|
||||
jnz m8b
|
||||
print "missing "
|
||||
print ins_name
|
||||
xchg ax,cx
|
||||
call printHex
|
||||
exit
|
||||
m8b: mov [ins_function],ax
|
||||
mov [ins_compare],dx
|
||||
mov ax,[si+6]
|
||||
mov [relevant_flags],ax
|
||||
;
|
||||
; Let's call the instruction function now, loading the PS_ALL flags with the same values
|
||||
; that the emulator recorded (operand_flags).
|
||||
;
|
||||
pushf
|
||||
pop cx ; CX == current flags
|
||||
mov ax,PS_ALL
|
||||
not ax
|
||||
and cx,ax ; CX == current flags, with PS_ALL flags cleared
|
||||
or cx,[operand_flags]
|
||||
push cx ; CX == current flags, with PS_ALL flags from operand_flags included
|
||||
popf
|
||||
call [ins_function]
|
||||
pushf
|
||||
pop cx
|
||||
call [ins_compare]
|
||||
;
|
||||
; If we're still here, the instruction passed, so restore the line pointer and move to the next line
|
||||
;
|
||||
pop di
|
||||
pop si
|
||||
;
|
||||
; When we finished reading the current line, DS:SI should have been left pointing at the terminating LF;
|
||||
; however, if we used "print" to display it, that LF would have replaced with a '$'. In any case, we don't
|
||||
; really need to call skipTo, if we know we're at the end of the current line.
|
||||
;
|
||||
; mov ah,0x0A
|
||||
; call skipTo
|
||||
inc si ; step over the LF (or '$', in case we printed the line before processing it)
|
||||
jmp m3
|
||||
|
||||
compare8:
|
||||
mov ah,[result_operand]
|
||||
cmp al,ah
|
||||
jne c8err
|
||||
jmp compareFlags
|
||||
c8err: print "byte mismatch:"
|
||||
print strActual
|
||||
mov cx,2
|
||||
call printHex
|
||||
print strRecorded
|
||||
mov al,ah
|
||||
call printHex
|
||||
print strCRLF
|
||||
exit
|
||||
|
||||
compare16:
|
||||
mov dx,[result_operand]
|
||||
cmp ax,dx
|
||||
jne c16err
|
||||
jmp compareFlags
|
||||
c16err: print "word mismatch:"
|
||||
print strActual
|
||||
mov cx,4
|
||||
call printHex
|
||||
print strRecorded
|
||||
xchg ax,dx
|
||||
call printHex
|
||||
print strCRLF
|
||||
exit
|
||||
|
||||
compare32:
|
||||
cmp ax,[result_operand]
|
||||
jne c32err
|
||||
cmp dx,[result_operand+2]
|
||||
je compareFlags
|
||||
c32err: print "dword mismatch:"
|
||||
print strActual
|
||||
mov cx,8
|
||||
call printHex
|
||||
print strRecorded
|
||||
mov ax,[result_operand]
|
||||
mov dx,[result_operand+2]
|
||||
call printHex
|
||||
print strCRLF
|
||||
exit
|
||||
|
||||
compareFlags:
|
||||
mov dx,[result_flags]
|
||||
and cx,[relevant_flags]
|
||||
and dx,[relevant_flags]
|
||||
cmp cx,dx
|
||||
je cfret
|
||||
print "flag mismatch:"
|
||||
print strActual
|
||||
xchg ax,cx
|
||||
mov cx,4
|
||||
call printHex
|
||||
print strRecorded
|
||||
xchg ax,dx
|
||||
call printHex
|
||||
print strCRLF
|
||||
exit
|
||||
cfret: ret
|
||||
|
||||
testROL8:
|
||||
mov al,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
rol al,cl
|
||||
ret
|
||||
|
||||
testROL16:
|
||||
mov ax,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
rol ax,cl
|
||||
ret
|
||||
|
||||
testROR8:
|
||||
mov al,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
ror al,cl
|
||||
ret
|
||||
|
||||
testROR16:
|
||||
mov ax,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
ror ax,cl
|
||||
ret
|
||||
|
||||
testRCL8:
|
||||
mov al,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
rcl al,cl
|
||||
ret
|
||||
|
||||
testRCL16:
|
||||
mov ax,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
rcl ax,cl
|
||||
ret
|
||||
|
||||
testRCR8:
|
||||
mov al,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
rcr al,cl
|
||||
ret
|
||||
|
||||
testRCR16:
|
||||
mov ax,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
rcr ax,cl
|
||||
ret
|
||||
|
||||
testSHL8:
|
||||
mov al,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
shl al,cl
|
||||
ret
|
||||
|
||||
testSHL16:
|
||||
mov ax,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
shl ax,cl
|
||||
ret
|
||||
|
||||
testMUL16:
|
||||
mov al,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
mul cl
|
||||
ret
|
||||
|
||||
testMUL32:
|
||||
mov ax,[dst_operand]
|
||||
mov cx,[src_operand]
|
||||
mul cx
|
||||
ret
|
||||
|
||||
testIMUL16:
|
||||
mov al,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
imul cl
|
||||
ret
|
||||
|
||||
testIMUL32:
|
||||
mov ax,[dst_operand]
|
||||
mov cx,[src_operand]
|
||||
imul cx
|
||||
ret
|
||||
|
||||
testDIV16:
|
||||
mov ax,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
div cl
|
||||
ret
|
||||
|
||||
testDIV32:
|
||||
mov ax,[dst_operand]
|
||||
mov dx,[dst_operand+2]
|
||||
mov cx,[src_operand]
|
||||
div cx
|
||||
ret
|
||||
|
||||
testIDIV16:
|
||||
mov ax,[dst_operand]
|
||||
mov cl,[src_operand]
|
||||
idiv cl
|
||||
ret
|
||||
|
||||
testIDIV32:
|
||||
mov ax,[dst_operand]
|
||||
mov dx,[dst_operand+2]
|
||||
mov cx,[src_operand]
|
||||
idiv cx
|
||||
ret
|
||||
|
||||
;
|
||||
; getHex: get value of hex string
|
||||
;
|
||||
; Inputs
|
||||
; DS:SI -> hex string
|
||||
;
|
||||
; Outputs
|
||||
; CX == number of characters
|
||||
; DX:AX == corresponding value
|
||||
; DS:SI -> next non-hex character
|
||||
;
|
||||
; Uses
|
||||
; AX, CX, DX, SI, Flags
|
||||
;
|
||||
; Notes
|
||||
; Supports upper-case alpha chars only, with no prefixes (eg, "0x") or suffixes (eg, "h");
|
||||
; if there are more than 8 hex characters, the value will represent only the last 8 characters.
|
||||
;
|
||||
getHex:
|
||||
push bx
|
||||
sub bx,bx ; BX holds the low 16 bits
|
||||
sub dx,dx ; DX holds the high 16 bits
|
||||
sub cx,cx ; CX holds the character count
|
||||
gh1: lodsb
|
||||
cmp al,'0'
|
||||
jb gh9
|
||||
cmp al,'9'
|
||||
ja gh3
|
||||
sub al,'0'
|
||||
gh2: shl bx,1
|
||||
rcl dx,1
|
||||
shl bx,1
|
||||
rcl dx,1
|
||||
shl bx,1
|
||||
rcl dx,1
|
||||
shl bx,1
|
||||
rcl dx,1
|
||||
or bl,al
|
||||
inc cx
|
||||
jmp gh1
|
||||
gh3: cmp al,'A'
|
||||
jb gh9
|
||||
cmp al,'F'
|
||||
ja gh9
|
||||
sub al,'A'-10
|
||||
jmp gh2
|
||||
gh9: dec si
|
||||
xchg ax,bx ; DX:AX now holds the final 32-bit result
|
||||
pop bx
|
||||
ret
|
||||
|
||||
;
|
||||
; printHex: print value in hex
|
||||
;
|
||||
; Inputs
|
||||
; DX:AX == value
|
||||
; CX == # of characters
|
||||
;
|
||||
; Outputs
|
||||
; None
|
||||
;
|
||||
; Uses
|
||||
; Flags
|
||||
;
|
||||
printHex:
|
||||
push ax
|
||||
push bx
|
||||
push cx
|
||||
push dx
|
||||
push di
|
||||
mov bx,ax ; DX:BX now holds the value to print
|
||||
mov di,hex_buffer_end - 1
|
||||
mov al,'$'
|
||||
std
|
||||
stosb
|
||||
ph1: jcxz ph3
|
||||
mov al,bl
|
||||
and al,0x0F
|
||||
add al,'0'
|
||||
cmp al,'9'
|
||||
jbe ph2
|
||||
add al,'A'-'0'-10
|
||||
ph2: stosb
|
||||
dec cx
|
||||
shr dx,1
|
||||
rcr bx,1
|
||||
shr dx,1
|
||||
rcr bx,1
|
||||
shr dx,1
|
||||
rcr bx,1
|
||||
shr dx,1
|
||||
rcr bx,1
|
||||
jmp ph1
|
||||
ph3: cld
|
||||
inc di
|
||||
print di
|
||||
pop di
|
||||
pop dx
|
||||
pop cx
|
||||
pop bx
|
||||
pop ax
|
||||
ret
|
||||
|
||||
;
|
||||
; getLine: find the length of the current line
|
||||
;
|
||||
; Inputs
|
||||
; DS:SI -> start of line
|
||||
; DS:DI -> first byte past end of line buffer
|
||||
;
|
||||
; Outputs
|
||||
; CX == length of line, including the terminating LF (or partial length)
|
||||
; Carry clear if line complete, carry set if line incomplete (SI reached DI)
|
||||
;
|
||||
; Uses
|
||||
; AL, CX, Flags
|
||||
;
|
||||
getLine:
|
||||
push si
|
||||
sub cx,cx
|
||||
gl1: cmp si,di
|
||||
jb gl2
|
||||
stc
|
||||
jmp gl9
|
||||
gl2: lodsb
|
||||
inc cx
|
||||
cmp al,0x0A
|
||||
jne gl1
|
||||
gl9: pop si
|
||||
ret
|
||||
|
||||
;
|
||||
; skipTo: skip to the character in AH
|
||||
;
|
||||
; Inputs
|
||||
; AH == specified character
|
||||
; DS:SI -> LF-terminated line
|
||||
;
|
||||
; Outputs
|
||||
; DS:SI -> specified character if carry clear, or LF if carry set
|
||||
;
|
||||
; Uses
|
||||
; AL, SI, Flags
|
||||
;
|
||||
skipTo:
|
||||
lodsb
|
||||
cmp al,ah
|
||||
je st9
|
||||
cmp al,0x0A
|
||||
jne skipTo
|
||||
stc
|
||||
st9: dec si
|
||||
ret
|
||||
|
||||
;
|
||||
; The following is "const" (read-only) data...
|
||||
;
|
||||
section .data
|
||||
ins_table db "ROL",0
|
||||
dw testROL8, testROL16, 0, PS_ALL_BUT_OF
|
||||
db "ROR",0
|
||||
dw testROR8, testROR16, 0, PS_ALL_BUT_OF
|
||||
db "RCL",0
|
||||
dw testRCL8, testRCL16, 0, PS_ALL_BUT_OF
|
||||
db "RCR",0
|
||||
dw testRCR8, testRCR16, 0, PS_ALL_BUT_OF
|
||||
db "SHL",0
|
||||
dw testSHL8, testSHL16, 0, PS_ALL_BUT_AF
|
||||
db "MUL",0
|
||||
dw 0, testMUL16, testMUL32, PS_CF | PS_OF
|
||||
db "IMUL",0
|
||||
dw 0, testIMUL16, testIMUL32, PS_CF | PS_OF
|
||||
db "DIV",0
|
||||
dw 0, testDIV16, testDIV32, PS_NONE
|
||||
db "IMUL",0
|
||||
dw 0, testIDIV16, testIDIV32, PS_NONE
|
||||
db 0 ; end of instruction table
|
||||
|
||||
strCRLF db 0x0D,0x0A,'$'
|
||||
strColon db ":$"
|
||||
strEquals db "=$"
|
||||
strComma db ",$"
|
||||
strActual db " actual=$"
|
||||
strRecorded db " recorded=$"
|
||||
|
||||
;
|
||||
; We end with all the unitialized data (ie, data that doesn't need to be stored in the binary)
|
||||
;
|
||||
section .bss
|
||||
ins_name resb 6
|
||||
ins_function resw 1
|
||||
ins_compare resw 1
|
||||
dst_operand resw 2
|
||||
dst_size resw 1
|
||||
src_operand resw 2
|
||||
src_size resw 1
|
||||
operand_flags resw 1
|
||||
relevant_flags resw 1
|
||||
result_operand resw 2
|
||||
result_size resw 1
|
||||
result_flags resw 1
|
||||
|
||||
hex_buffer resb 9
|
||||
hex_buffer_end equ $
|
||||
hex_buffer_len equ hex_buffer_end - hex_buffer
|
||||
|
||||
file_buffer resb 0x1000
|
||||
file_buffer_end equ $
|
||||
file_buffer_len equ file_buffer_end - file_buffer
|
||||
BIN
tests/pc/trace/trace.com
Normal file
BIN
tests/pc/trace/trace.com
Normal file
Binary file not shown.
1353
tests/pc/trace/trace.txt
Normal file
1353
tests/pc/trace/trace.txt
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue