5117 lines
116 KiB
Plaintext
5117 lines
116 KiB
Plaintext
BITS 16
|
|
org 0x7C00
|
|
cli
|
|
xor ax, ax
|
|
mov ds, ax
|
|
mov es, ax
|
|
mov ss, ax
|
|
mov sp, 0x8000
|
|
sti
|
|
mov ax, 3
|
|
int 0x10
|
|
mov bp, loading
|
|
mov ax, 0x1301
|
|
mov bx, 7
|
|
mov cx, 12
|
|
mov dx, 0x0102
|
|
int 0x10
|
|
mov bl, 2
|
|
mov ah, 2
|
|
mov dx, 0x0201
|
|
int 0x10
|
|
mov ah, 9
|
|
mov al, '.'
|
|
mov cx, 14
|
|
int 0x10
|
|
mov di, 0x0050
|
|
mov ax, 19
|
|
mov cx, 14
|
|
call read
|
|
mov di, 0x0210
|
|
mov ax, 1
|
|
mov cx, 9
|
|
call read
|
|
mov dx, 224
|
|
mov bx, 0x0500
|
|
search:
|
|
cld
|
|
mov si, filename
|
|
mov cx, 11
|
|
mov di, bx
|
|
repe cmpsb
|
|
je found
|
|
add bx, 32
|
|
dec dx
|
|
jz error
|
|
jmp search
|
|
read:
|
|
pusha
|
|
mov bl, 18
|
|
div bl
|
|
mov cl, ah
|
|
inc cl
|
|
xor ah, ah
|
|
mov bl, 2
|
|
div bl
|
|
mov ch, al
|
|
mov dh, ah
|
|
mov ax, cx
|
|
mov cx, 10
|
|
.retry:
|
|
push es
|
|
push cx
|
|
mov cx, ax
|
|
push cx
|
|
xor ax, ax
|
|
push dx
|
|
int 0x13
|
|
jc .failed
|
|
pop dx
|
|
pop cx
|
|
xor bx, bx
|
|
mov es, di
|
|
mov ax, 0x0201
|
|
int 0x13
|
|
jnc .success
|
|
.failed:
|
|
pop dx
|
|
pop ax
|
|
pop cx
|
|
pop es
|
|
loop .retry
|
|
mov bp, failure
|
|
mov ax, 0x1301
|
|
mov bx, 4
|
|
mov cx, 11
|
|
mov dx, 0x0401
|
|
int 0x10
|
|
mov ah, 0
|
|
int 0x16
|
|
int 0x19
|
|
.success:
|
|
pop cx
|
|
pop es
|
|
popa
|
|
add di, 32
|
|
inc ax
|
|
loop read
|
|
ret
|
|
found:
|
|
mov bp, [bx+26]
|
|
mov di, 0x0800
|
|
.block:
|
|
xor cx, cx
|
|
mov cl, 1
|
|
mov si, bp
|
|
.contig:
|
|
mov ax, 3
|
|
mul si
|
|
shr ax, 1
|
|
mov bx, ax
|
|
mov ax, word [(0x2100+bx)]
|
|
jc .odd
|
|
and ax, 0x0FFF
|
|
jmp .next
|
|
.odd:
|
|
shr ax, 4
|
|
.next:
|
|
inc si
|
|
cmp ax, si
|
|
jne .pass
|
|
inc cl
|
|
adc ch, 0
|
|
jmp .contig
|
|
.pass:
|
|
xchg bp, ax
|
|
dec ax
|
|
dec ax
|
|
mov dx, 1
|
|
mul dx
|
|
add ax, 33
|
|
call read
|
|
cmp bp, 0x0FF8
|
|
jb .block
|
|
|
|
.386
|
|
model flat
|
|
|
|
extrn strlen:proc
|
|
extrn isalnum:proc
|
|
extrn isxdigit:proc
|
|
|
|
.code
|
|
|
|
; Short URL's are at most 12 characters long, and contain just
|
|
; alphanumeric characters. This validation has to be done, to
|
|
; evade any attempts of path traversal exploit.
|
|
|
|
; Input: ecx
|
|
; Output: esi
|
|
; Registers preserved.
|
|
ValidateShortUrl proc near
|
|
; Push eax - we don't want to trash it after strlen() returns.
|
|
push eax
|
|
; As we don't want to alter ecx, preserve ebx - as it'll be a pointer we'll modify.
|
|
push ebx
|
|
; ebx = ecx; initialize the pointer
|
|
mov ebx, ecx
|
|
; eax = length of string pointed by ecx.
|
|
push ecx
|
|
call strlen
|
|
; Compare the length to 12 - if it's longer than 12, return a truthy value.
|
|
cmp eax, 12
|
|
ja .skip
|
|
.loop:
|
|
; Move a byte pointed by ebx to esi and sign-extend it.
|
|
movsx esi, BYTE PTR [ebx]
|
|
; Check if esi is alphanumeric.
|
|
push esi
|
|
call isalnum
|
|
; Clean up after isalnum
|
|
add esp, 4
|
|
; When isalnum returns 0 to eax, terminate the loop.
|
|
test eax, eax
|
|
je .skip
|
|
; Increment the pointer to point next character.
|
|
inc ebx
|
|
jmp .loop
|
|
.skip:
|
|
; Usual cleanup.
|
|
pop ecx
|
|
pop ebx
|
|
pop eax
|
|
ret
|
|
ValidateShortUrl endp
|
|
|
|
; Adresses of various stuff on the stack.
|
|
; Will contain each character of a string.
|
|
character = 13
|
|
|
|
; First byte of %XX syntax.
|
|
b = 14
|
|
|
|
; Second byte of %XX syntax.
|
|
a = 15
|
|
|
|
; The same, but aligned to +12B stack frame
|
|
b12frame = 26
|
|
a12frame = 27
|
|
|
|
; Will decode from edx to ecx
|
|
DecodeURL proc near
|
|
; As always, we don't want to overwrite edx and ecx,
|
|
; So make a copy of these registers and make use of another register.
|
|
; ecx (destination) => esi
|
|
push esi
|
|
mov esi, ecx
|
|
; edx (source) => ebx
|
|
push ebx
|
|
mov ebx, edx
|
|
; Reserve 20 bytes for a stack frame.
|
|
sub esp, 20
|
|
.char_loop:
|
|
; Recall a byte from source, put it in al
|
|
mov al, BYTE PTR [ebx]
|
|
; Move it over to it's place in memory.
|
|
mov BYTE PTR [esp+character], al
|
|
; Quit if we already hit zero.
|
|
test al, al
|
|
je .quit
|
|
; Increment destination pointer now; will take care of it later.
|
|
; The code branches a couple of times and there are at least three
|
|
; ways it can loop; so one inc there will save space later on.
|
|
inc esi
|
|
; Did we hit %? If so, then we're about to parse a hex code.
|
|
cmp BYTE PTR [esp+character], '%'
|
|
jne .no_hexcode
|
|
; Recall first byte, store it in dl.
|
|
mov dl, BYTE PTR [ebx+1]
|
|
test dl, dl
|
|
; We implement a lax parser; if the client sends us incorrect urlencoded
|
|
; data we're going to deal with it anyway. This handles the case when % is
|
|
; at the end of the string.
|
|
je .invalid_percent
|
|
; Recall second byte, store it in cl.
|
|
mov cl, BYTE PTR [ebx+2]
|
|
test cl, cl
|
|
; Store b
|
|
mov BYTE PTR [esp+b], cl
|
|
je .invalid_percent
|
|
; We enter a new stack frame; therefore A adress is morphed and we'll write
|
|
; it to the memory only now.
|
|
sub esp, 12
|
|
; Sign-extend dl to eax, to push it later for isxdigit.
|
|
movsx eax, dl
|
|
mov BYTE PTR [esp+a12frame], dl
|
|
; Invoke
|
|
push eax
|
|
call isxdigit
|
|
; Leave the stack frame.
|
|
add esp, 16
|
|
; isxdigit returned zero? If so, we spotted an incorrect %XX code.
|
|
test eax, eax
|
|
je .invalid_percent
|
|
; The same code, but for B.
|
|
sub esp, 12
|
|
movsx eax, BYTE PTR [esp+b12frame]
|
|
push eax
|
|
call isxdigit
|
|
add esp, 16
|
|
test eax, eax
|
|
je .invalid_percent
|
|
; After all this; recall a and b values back.
|
|
mov dl, BYTE PTR [esp+a]
|
|
mov cl, BYTE PTR [esp+b]
|
|
; a lesser or equal to 'a'?
|
|
cmp dl, 'a'
|
|
; yep, move on.
|
|
jle .skip_acase
|
|
; Subtract 'a' - 'A' => 32.
|
|
sub edx, 32
|
|
; a is still greater than 'A' (65), therefore we jump there.
|
|
jmp .gtA
|
|
; subtract '0'
|
|
.skip_acase:
|
|
lea eax, [edx-'0']
|
|
cmp dl, 64
|
|
jle .check_b
|
|
.gtA:
|
|
; Subtract 'A' - 10, that is, 55.
|
|
lea eax, [edx-55]
|
|
; The same procedure for this case; we just
|
|
; decode two hex digits.
|
|
.check_b:
|
|
cmp cl, 'a'
|
|
jle .maybe_gt
|
|
sub ecx, 32
|
|
jmp .gtA2
|
|
.maybe_gt:
|
|
lea edx, [ecx-'0']
|
|
cmp cl, 64
|
|
jle .make_number
|
|
.gtA2:
|
|
lea edx, [ecx-55]
|
|
.make_number:
|
|
; Make a number out of these; 16 * a + b; instead of multiplying
|
|
; we'll utilize leftshift.
|
|
sal eax, 4
|
|
add eax, edx
|
|
; add 3 to source - we read 3 characters.
|
|
add ebx, 3
|
|
; we incremented esi, therefore we reference [esi - 1]; store al there.
|
|
mov BYTE PTR [esi-1], al
|
|
; loop again
|
|
jmp .char_loop
|
|
.no_hexcode:
|
|
; No %XX, maybe we hit '+'?
|
|
cmp BYTE PTR [esp+character], '+'
|
|
; Nope; just copy over the character.
|
|
jne .invalid_percent
|
|
; Yep, replace it with a space.
|
|
mov BYTE PTR [esi-1], ' '
|
|
; Increment the source pointer
|
|
inc ebx
|
|
; Loop again.
|
|
jmp .char_loop
|
|
.invalid_percent:
|
|
; Recall the character
|
|
mov al, BYTE PTR [esp+character]
|
|
; Increment the source pointer.
|
|
inc ebx
|
|
; Store the character back.
|
|
mov BYTE PTR [esi-1], al
|
|
; Loop again.
|
|
jmp .char_loop
|
|
.quit:
|
|
; Glue in the null-terminator.
|
|
mov BYTE PTR [esi], 0
|
|
; And finally, leave the stackframe.
|
|
add esp, 20
|
|
pop ebx
|
|
pop esi
|
|
ret
|
|
DecodeURL endp
|
|
|
|
|
|
format PE GUI 4.0
|
|
|
|
entry _start
|
|
|
|
include 'win32ax.inc'
|
|
|
|
; Node in memory:
|
|
|
|
; ESI ESI+4 ESI+8
|
|
; v v v
|
|
; +--------------------------+
|
|
; | left | right | type |
|
|
; +--------------------------+
|
|
|
|
node.left equ 0
|
|
node.right equ 4
|
|
node.type equ 8
|
|
|
|
; SKI nodes
|
|
TYPE_S equ 0
|
|
TYPE_K equ 1
|
|
TYPE_I equ 2
|
|
|
|
; alloc_node node holding two other nodes.
|
|
TYPE_BI equ 3
|
|
|
|
section '.text' code readable executable writeable
|
|
; Program entry point.
|
|
; Create the dialog, hook up the dialog procedure,
|
|
; enter an event loop.
|
|
proc _start
|
|
; Create a heap, store it's handle in asmh.
|
|
invoke HeapCreate, 0, 0, 0
|
|
mov DWORD [asmh], eax
|
|
; Get the handle of the current module
|
|
invoke GetModuleHandleA, 0
|
|
; ... and use it to create a dialog box.
|
|
; 1 here is the resource identifier for the form.
|
|
invoke CreateDialogParamA, eax, 1, 0, DialogProc, 0
|
|
; store the dialog handle in hDlg.
|
|
mov DWORD [hDlg], eax
|
|
; show the dialog.
|
|
invoke ShowWindow, eax, SW_SHOW
|
|
; window message loop.
|
|
.message_loop:
|
|
; fetch the next message to msg.
|
|
invoke GetMessage, msg, 0, 0, 0
|
|
; GetMessage returns 0 => quit
|
|
test eax, eax
|
|
je .quit
|
|
; if the return value != -1
|
|
inc eax
|
|
jne .isdlg
|
|
; return value == -1 => an error occured.
|
|
; ExitProcess(1)
|
|
push 1
|
|
jmp .die
|
|
.isdlg:
|
|
; is it a dialog message?
|
|
invoke IsDialogMessageA, hDlg, msg
|
|
; nope, ignore.
|
|
test eax, eax
|
|
jne .message_loop
|
|
; Otherwise, translate and dispatch it.
|
|
invoke TranslateMessage, msg
|
|
invoke DispatchMessage, msg
|
|
jmp .message_loop
|
|
.quit:
|
|
; ExitProcess(0)
|
|
push 0
|
|
.die:
|
|
call [ExitProcess]
|
|
endp
|
|
|
|
; Dialog procedure - handling incoming messages.
|
|
proc DialogProc
|
|
; Stack frame construction.
|
|
push ebp
|
|
mov ebp, esp
|
|
sub esp, 16
|
|
mov edx, DWORD [ebp+12]
|
|
mov eax, DWORD [ebp+8]
|
|
mov ecx, DWORD [ebp+16]
|
|
; handle WM_CLOSE
|
|
cmp edx, WM_CLOSE
|
|
je .close_handler
|
|
; handle WM_COMMAND
|
|
cmp edx, WM_COMMAND
|
|
je .command_handler
|
|
; don't handle everything not being WM_DESTROY
|
|
; (return FALSE)
|
|
cmp edx, WM_DESTROY
|
|
jne .no_op
|
|
; ... so we're handling WM_DESTROY here.
|
|
invoke PostQuitMessage, 0
|
|
jmp .c_exit
|
|
.close_handler:
|
|
; WM_CLOSE => pass around the WM_DESTROY message.
|
|
invoke DestroyWindow, eax
|
|
.c_exit:
|
|
; common WM_DESTROY and WM_CLOSE fallthrough.
|
|
; return TRUE.
|
|
xor ebx, ebx
|
|
inc ebx
|
|
; the only way out is to
|
|
jmp .die
|
|
.command_handler:
|
|
; 2 is the '&Quit' button ID.
|
|
; If anything other has been pressed, branch.
|
|
cmp cx, 2
|
|
jne .not_quit
|
|
; Quit button pressed -> die
|
|
invoke DestroyWindow, eax
|
|
.no_op:
|
|
; a RETURN FALSE stub for lacking handlers for
|
|
; WM_COMMAND cases and unknown message ID's.
|
|
xor ebx, ebx
|
|
jmp .die
|
|
.not_quit:
|
|
; '&Quit' wasn't pressed, so maybe it was '&Evaluate'?
|
|
; return FALSE if LOWORD(ecx) != 1
|
|
xor ebx, ebx
|
|
dec cx
|
|
jne .die
|
|
; '&Evaluate' pressed, handle that.
|
|
; Get the handle to the 3rd dialog item => the expression input
|
|
invoke GetDlgItem, eax, 3
|
|
; stuff it in wnd
|
|
mov DWORD [wnd], eax
|
|
; get the text length to allocate approperiate amount of space on the stack
|
|
invoke GetWindowTextLengthA, eax
|
|
; Save the esp
|
|
mov ecx, esp
|
|
; Reserve space for the null terminator.
|
|
; Basically, we're constructing a buffer on the stack
|
|
lea edx, [eax+1]
|
|
add eax, 17
|
|
and eax, 0xFFFFFFF0
|
|
sub ecx, eax
|
|
mov esp, ecx
|
|
; While we're at it, null-terminate it.
|
|
mov BYTE [esp], 0
|
|
; Read the control data, put it in the buffer.
|
|
mov DWORD [ebp-12], ecx
|
|
invoke GetWindowTextA, DWORD [wnd], ecx, edx
|
|
mov ecx, DWORD [ebp-12]
|
|
; Evaluate it.
|
|
call eval
|
|
; Reset the control text.
|
|
invoke SetWindowText, DWORD [wnd], eax
|
|
.die:
|
|
; Pop off the VLA
|
|
lea esp, [ebp-8]
|
|
; Set the correct return value.
|
|
mov eax, ebx
|
|
; Balance the stack
|
|
pop ebx
|
|
pop esi
|
|
pop ebp
|
|
ret 16
|
|
endp
|
|
|
|
; Calculate the size of the tree, stringified.
|
|
; Takes the tree in eax.
|
|
proc str_size
|
|
; Preserve and clear eax, make a copy of the
|
|
; pointer in ebx.
|
|
push esi ebx
|
|
xor esi, esi
|
|
mov ebx, eax
|
|
.loop:
|
|
; if node.type == TYPE_BI, then it has two children
|
|
cmp DWORD [ebx+node.type], TYPE_BI
|
|
jne .quit
|
|
; Apparently it does.
|
|
; left-recurse to get the lhs size
|
|
mov eax, DWORD [ebx+node.left]
|
|
call str_size
|
|
; eax contains the lhs size, so everything left
|
|
; is the rhs size. loop on the right node.
|
|
mov ebx, DWORD [ebx+node.right]
|
|
; add two bytes for '(' and ')'
|
|
lea esi, [esi+eax+2]
|
|
jmp .loop
|
|
.quit:
|
|
; The node doesn't have two children - return 1
|
|
; (a single byte for either S, K or I)
|
|
lea eax, [esi+1]
|
|
pop ebx esi
|
|
ret
|
|
endp
|
|
|
|
; Stringify the tree.
|
|
; Take it in eax. The buffer is static and
|
|
; it's the callers' duty to allocate it.
|
|
proc stringify
|
|
; copy the node pointer to ebx
|
|
push ebx
|
|
mov ebx, eax
|
|
; first, take the node type.
|
|
mov edx, DWORD [eax+node.type]
|
|
; because no matter where we branch the buffer will be used,
|
|
; preload it.
|
|
mov eax, DWORD [buf]
|
|
; increment the current buffer pointer stored in the variable
|
|
; and hold own instance, which points to one byte before
|
|
inc eax
|
|
mov DWORD [buf], eax
|
|
dec eax
|
|
; has two children?
|
|
cmp edx, TYPE_BI
|
|
jne .combinator
|
|
; alloc_node tree starts with '('
|
|
mov BYTE [eax], '('
|
|
; Recurse on the lhs and rhs
|
|
mov eax, DWORD [ebx+node.left]
|
|
call stringify
|
|
mov eax, DWORD [ebx+node.right]
|
|
call stringify
|
|
; increment pointer, store the ')'.
|
|
mov eax, DWORD [buf]
|
|
mov BYTE [eax], ')'
|
|
inc eax
|
|
mov DWORD [buf], eax
|
|
dec eax
|
|
jmp .stop
|
|
.combinator:
|
|
; stringify the combinator.
|
|
; use the lookup string for that.
|
|
mov dl, BYTE [ski+edx]
|
|
; store back the letter.
|
|
mov BYTE [eax], dl
|
|
; the pointer is already incremented, so we fall thru to return
|
|
.stop:
|
|
pop ebx
|
|
ret
|
|
endp
|
|
|
|
; a wrapper over HeapFree, which frees the pointer in eax.
|
|
; XXX: inline?
|
|
proc free
|
|
invoke HeapFree, DWORD [asmh], 0, eax
|
|
ret
|
|
endp
|
|
|
|
; free a tree recursively
|
|
proc free_tree
|
|
; preserve ebx, make a copy of eax
|
|
push ebx
|
|
mov ebx, eax
|
|
; has children?
|
|
cmp DWORD [eax+node.type], TYPE_BI
|
|
jne .no_children
|
|
; recurse over children.
|
|
mov eax, DWORD [eax+node.left]
|
|
call free_tree
|
|
mov eax, DWORD [ebx+node.right]
|
|
call free_tree
|
|
.no_children:
|
|
; take the copy, restore ebx, free the parent.
|
|
mov eax, ebx
|
|
pop ebx
|
|
jmp free
|
|
endp
|
|
|
|
; Allocate a new tree node.
|
|
; takes new nodes' type in eax.
|
|
proc alloc_node
|
|
; preserve eax, because it will get trashed by HeapAlloc
|
|
push ebx
|
|
mov ebx, eax
|
|
; Call HeapAlloc, alloc 4 (left) + 4 (right) + 4 (type) B.
|
|
; Zero the memory so we don't have to set left and right to NULL.
|
|
invoke HeapAlloc, DWORD [asmh], HEAP_ZERO_MEMORY, 4 + 4 + 4
|
|
; Set the type.
|
|
mov DWORD [eax+node.type], ebx
|
|
pop ebx
|
|
ret
|
|
endp
|
|
|
|
; read a node from the input buffer, and return it in eax.
|
|
proc read_node
|
|
; preserve ebx
|
|
push ebx
|
|
; load the code pointer
|
|
mov eax, DWORD [code]
|
|
; increment it, store back
|
|
inc eax
|
|
mov DWORD [code], eax
|
|
dec eax
|
|
; load a byte
|
|
mov al, BYTE [eax]
|
|
; if al>'K' then al may be 'S'
|
|
cmp al, 'K'
|
|
je .read_k
|
|
jg .maybe_s
|
|
; reading a tree
|
|
cmp al, '('
|
|
je .read_bitree
|
|
; if it's not 'I', spew out an error.
|
|
cmp al, 'I'
|
|
jne .parse_error
|
|
; build an i-node
|
|
push TYPE_I
|
|
pop eax
|
|
jmp .build_node
|
|
.maybe_s:
|
|
; if it's not 'S', spew out an error.
|
|
cmp al, 'S'
|
|
jne .parse_error
|
|
; otherwise, clear eax (load TYPE_S)
|
|
; and build a new node.
|
|
xor eax, eax
|
|
jmp .build_node
|
|
.read_bitree:
|
|
; load the approperiate type and allocate a node
|
|
push TYPE_BI
|
|
pop eax
|
|
call alloc_node
|
|
mov ebx, eax
|
|
; read the left node
|
|
call read_node
|
|
mov DWORD [ebx+node.left], eax
|
|
; eax = 0 => return NULL to broadcast an error.
|
|
test eax, eax
|
|
je .nullify
|
|
; read the right node
|
|
call read_node
|
|
mov DWORD [ebx+node.right], eax
|
|
test eax, eax
|
|
je .nullify
|
|
; no errors - increment the code pointer to skip the trailing `)`.
|
|
inc DWORD [code]
|
|
jmp .die
|
|
.read_k:
|
|
; set eax to 1 (loading TYPE_K)
|
|
; and fall thru to construction of a new node.
|
|
xor eax, eax
|
|
inc eax
|
|
.build_node:
|
|
pop ebx
|
|
jmp alloc_node
|
|
.parse_error:
|
|
; in case of a parse error, display a message and fall thru to returning NULL.
|
|
invoke MessageBoxA, 0, msge, 0, MB_OK
|
|
.nullify:
|
|
xor ebx, ebx
|
|
.die:
|
|
; set the return value and quit
|
|
mov eax, ebx
|
|
pop ebx
|
|
ret
|
|
endp
|
|
|
|
; duplicate a tree in eax.
|
|
proc dup_tree
|
|
push esi ebx
|
|
mov ebx, eax
|
|
; Make a new node with this node's type.
|
|
mov eax, DWORD [eax+node.type]
|
|
call alloc_node
|
|
; if type != TYPE_BI then return that instance.
|
|
cmp DWORD [ebx+node.type], TYPE_BI
|
|
jne .shallow
|
|
; else, clone recursively. copy the original
|
|
; ptr, because it will get overwritten
|
|
mov esi, eax
|
|
; clone the left node
|
|
mov eax, DWORD [ebx+node.left]
|
|
call dup_tree
|
|
mov DWORD [esi+node.left], eax
|
|
; clone the right node
|
|
mov eax, DWORD [ebx+node.right]
|
|
call dup_tree
|
|
mov DWORD [esi+node.right], eax
|
|
; restore eax
|
|
mov eax, esi
|
|
.shallow:
|
|
pop ebx esi
|
|
ret
|
|
endp
|
|
|
|
proc eval_step
|
|
push edi esi ebx
|
|
mov ebx, eax
|
|
; has one child? if node.left == NULL
|
|
mov eax, DWORD [eax+node.left]
|
|
test eax, eax
|
|
je .no_left
|
|
; if the first child's type is I
|
|
cmp DWORD [eax+node.type], TYPE_I
|
|
jne .not_inode
|
|
; got identity, so take the right node.
|
|
mov esi, DWORD [ebx+node.right]
|
|
; free this node and the left node.
|
|
jmp .clean
|
|
.not_inode:
|
|
; it's not I. eax is now orig->left
|
|
; if orig->left->left->type == K
|
|
mov edx, DWORD [eax+node.left]
|
|
; wait, maybe there is no left node
|
|
test edx, edx
|
|
je .no_left
|
|
; check the type.
|
|
cmp DWORD [edx+node.type], TYPE_K
|
|
; branch if it's not K either.
|
|
jne .not_knode
|
|
; free orig->right and orig->left->left
|
|
; keep and return orig->left->right
|
|
mov esi, DWORD [eax+node.right]
|
|
mov eax, DWORD [ebx+node.right]
|
|
call free_tree
|
|
mov eax, DWORD [ebx+node.left]
|
|
mov eax, DWORD [eax+node.left]
|
|
; fallthru to free the orig->left->left node
|
|
.clean:
|
|
call free_tree
|
|
mov eax, ebx
|
|
call free
|
|
.yield_saved:
|
|
mov ebx, esi
|
|
jmp .done
|
|
.not_knode:
|
|
; if it's not a K or I node, then for sure it's either
|
|
; a node we have to evaluate recursively _or_ a S node.
|
|
; check for existence of X = orig->left->left->left
|
|
mov edx, DWORD [edx]
|
|
test edx, edx
|
|
je .no_left
|
|
; X->type != TYPE_S?
|
|
cmp DWORD [edx+node.type], TYPE_S
|
|
jne .no_left
|
|
; ok, so definitely it's a S node.
|
|
; to get ((Sx)y)z = ((xz)(yz)), first build the outer binode.
|
|
push TYPE_BI
|
|
pop eax
|
|
call alloc_node
|
|
; OK, save it in esi
|
|
mov esi, eax
|
|
; build two another binodes, and put them as the left and right
|
|
; node of this tree.
|
|
push 3
|
|
pop eax
|
|
call alloc_node
|
|
mov DWORD [esi+node.left], eax
|
|
push 3
|
|
pop eax
|
|
call alloc_node
|
|
mov DWORD [esi+node.right], eax
|
|
; now the magic happens. do the following:
|
|
; (esi + node.left)->left = dup(orig->left->left->right)
|
|
; (esi + node.left)->right = dup(orig->right)
|
|
; (esi + node.right)->left = dup(orig->left->right)
|
|
; (esi + node.right)->right = dup(orig->right)
|
|
; I'm not sure if this many dup calls are required, but they
|
|
; help to shave off some space and trouble needed to free the
|
|
; correct elements of the trees. we're not really aiming for
|
|
; performance here, so it's alright.
|
|
mov edi, DWORD [esi+node.left]
|
|
mov eax, DWORD [ebx+node.left]
|
|
mov eax, DWORD [eax+node.left]
|
|
mov eax, DWORD [eax+node.right]
|
|
call dup_tree
|
|
mov DWORD [edi+node.left], eax
|
|
mov eax, DWORD [ebx+node.right]
|
|
mov edi, DWORD [esi+node.left]
|
|
call dup_tree
|
|
mov DWORD [edi+node.right], eax
|
|
mov eax, DWORD [ebx+node.left]
|
|
mov edi, DWORD [esi+node.right]
|
|
mov eax, DWORD [eax+node.right]
|
|
call dup_tree
|
|
mov DWORD [edi+node.left], eax
|
|
mov eax, DWORD [ebx+node.right]
|
|
mov edi, DWORD [esi+node.right]
|
|
call dup_tree
|
|
mov DWORD [edi+node.right], eax
|
|
; free the original tree
|
|
mov eax, ebx
|
|
call free_tree
|
|
jmp .yield_saved
|
|
.no_left:
|
|
; maybe it's a binode, which we just need to evaluate
|
|
; deeper to get some observable result?
|
|
cmp DWORD [ebx+node.type], TYPE_BI
|
|
jne .done
|
|
; recurse twice. first set the left node, then the right node.
|
|
call eval_step
|
|
mov DWORD [ebx+node.left], eax
|
|
mov eax, DWORD [ebx+node.right]
|
|
call eval_step
|
|
mov DWORD [ebx+node.right], eax
|
|
.done:
|
|
mov eax, ebx
|
|
pop ebx esi edi
|
|
ret
|
|
endp
|
|
|
|
; the evaluation wrapper called by the DialogProc.
|
|
; takes the input buffer in ecx.
|
|
eval:
|
|
push esi ebx
|
|
mov ebx, ecx
|
|
; store the input in the code buffer.
|
|
mov DWORD [code], ecx
|
|
; read the expression.
|
|
call read_node
|
|
; if read_node returns null, then an error occured
|
|
test eax, eax
|
|
je .read_fail
|
|
; call the evaluation procedure.
|
|
call eval_step
|
|
mov esi, eax
|
|
; find out the size of the buffer, stringified.
|
|
call str_size
|
|
; allocate it a byte of extra space.
|
|
inc eax
|
|
invoke HeapAlloc, DWORD [asmh], 0, eax
|
|
; initialize the output buffer.
|
|
mov DWORD [buf], eax
|
|
; save the output copy to ourselves to later return it.
|
|
mov ebx, eax
|
|
; take back the saved buffer, stringify the input into it
|
|
mov eax, esi
|
|
call stringify
|
|
; NULL terminate
|
|
mov eax, DWORD [buf]
|
|
mov BYTE [eax], 0
|
|
; free the original tree
|
|
mov eax, esi
|
|
call free_tree
|
|
.read_fail:
|
|
; in any case, return the value we've got.
|
|
mov eax, ebx
|
|
pop ebx esi
|
|
ret
|
|
|
|
wnd: dd 0
|
|
msg MSG
|
|
hDlg: dd 0
|
|
asmh: dd 0
|
|
buf: dd 0
|
|
code: dd 0
|
|
ski: db 'SKI', 0
|
|
msge: db '?', 0
|
|
|
|
section '.rsrc' resource data readable
|
|
directory RT_DIALOG, dialogs
|
|
resource dialogs, 1, LANG_ENGLISH+SUBLANG_DEFAULT, demo
|
|
dialog demo,'SKI calculus',70,70,330,20,WS_CAPTION+WS_POPUP+WS_SYSMENU+DS_MODALFRAME
|
|
dialogitem 'STATIC', '&Code: ', 4, 4, 5, 21, 9, WS_VISIBLE+WS_CHILD+WS_GROUP
|
|
dialogitem 'BUTTON', '&Quit', 2, 269, 4, 50, 11, BS_PUSHBUTTON+WS_CHILD+WS_VISIBLE+WS_GROUP
|
|
dialogitem 'BUTTON', '&Evaluate', 1, 218, 4, 50, 11, BS_DEFPUSHBUTTON+WS_CHILD+WS_VISIBLE+WS_GROUP
|
|
dialogitem 'EDIT', '', 3, 28, 3, 187, 14, ES_LEFT+WS_CHILD+WS_VISIBLE+WS_BORDER+WS_GROUP+ES_AUTOHSCROLL
|
|
enddialog
|
|
|
|
section '.idata' import data readable writable
|
|
library kernel32, 'KERNEL32.DLL', \
|
|
user32, 'USER32.DLL'
|
|
|
|
include 'api\kernel32.inc'
|
|
include 'api\user32.inc'
|
|
|
|
|
|
|
|
; flat assembler core
|
|
; Copyright (c) 1999-2022, Tomasz Grysztar.
|
|
; All rights reserved.
|
|
|
|
assembler:
|
|
xor eax,eax
|
|
mov [stub_size],eax
|
|
mov [current_pass],ax
|
|
mov [resolver_flags],eax
|
|
mov [number_of_sections],eax
|
|
mov [actual_fixups_size],eax
|
|
assembler_loop:
|
|
mov eax,[labels_list]
|
|
mov [tagged_blocks],eax
|
|
mov eax,[additional_memory]
|
|
mov [free_additional_memory],eax
|
|
mov eax,[additional_memory_end]
|
|
mov [structures_buffer],eax
|
|
mov esi,[source_start]
|
|
mov edi,[code_start]
|
|
xor eax,eax
|
|
mov dword [adjustment],eax
|
|
mov dword [adjustment+4],eax
|
|
mov [addressing_space],eax
|
|
mov [error_line],eax
|
|
mov [counter],eax
|
|
mov [format_flags],eax
|
|
mov [number_of_relocations],eax
|
|
mov [undefined_data_end],eax
|
|
mov [file_extension],eax
|
|
mov [next_pass_needed],al
|
|
mov [output_format],al
|
|
mov [adjustment_sign],al
|
|
mov [evex_mode],al
|
|
mov [code_type],16
|
|
call init_addressing_space
|
|
pass_loop:
|
|
call assemble_line
|
|
jnc pass_loop
|
|
mov eax,[additional_memory_end]
|
|
cmp eax,[structures_buffer]
|
|
je pass_done
|
|
sub eax,18h
|
|
mov eax,[eax+4]
|
|
mov [current_line],eax
|
|
jmp missing_end_directive
|
|
pass_done:
|
|
call close_pass
|
|
mov eax,[labels_list]
|
|
check_symbols:
|
|
cmp eax,[memory_end]
|
|
jae symbols_checked
|
|
test byte [eax+8],8
|
|
jz symbol_defined_ok
|
|
mov cx,[current_pass]
|
|
cmp cx,[eax+18]
|
|
jne symbol_defined_ok
|
|
test byte [eax+8],1
|
|
jz symbol_defined_ok
|
|
sub cx,[eax+16]
|
|
cmp cx,1
|
|
jne symbol_defined_ok
|
|
and byte [eax+8],not 1
|
|
or [next_pass_needed],-1
|
|
symbol_defined_ok:
|
|
test byte [eax+8],10h
|
|
jz use_prediction_ok
|
|
mov cx,[current_pass]
|
|
and byte [eax+8],not 10h
|
|
test byte [eax+8],20h
|
|
jnz check_use_prediction
|
|
cmp cx,[eax+18]
|
|
jne use_prediction_ok
|
|
test byte [eax+8],8
|
|
jz use_prediction_ok
|
|
jmp use_misprediction
|
|
check_use_prediction:
|
|
test byte [eax+8],8
|
|
jz use_misprediction
|
|
cmp cx,[eax+18]
|
|
je use_prediction_ok
|
|
use_misprediction:
|
|
or [next_pass_needed],-1
|
|
use_prediction_ok:
|
|
test byte [eax+8],40h
|
|
jz check_next_symbol
|
|
and byte [eax+8],not 40h
|
|
test byte [eax+8],4
|
|
jnz define_misprediction
|
|
mov cx,[current_pass]
|
|
test byte [eax+8],80h
|
|
jnz check_define_prediction
|
|
cmp cx,[eax+16]
|
|
jne check_next_symbol
|
|
test byte [eax+8],1
|
|
jz check_next_symbol
|
|
jmp define_misprediction
|
|
check_define_prediction:
|
|
test byte [eax+8],1
|
|
jz define_misprediction
|
|
cmp cx,[eax+16]
|
|
je check_next_symbol
|
|
define_misprediction:
|
|
or [next_pass_needed],-1
|
|
check_next_symbol:
|
|
add eax,LABEL_STRUCTURE_SIZE
|
|
jmp check_symbols
|
|
symbols_checked:
|
|
cmp [next_pass_needed],0
|
|
jne next_pass
|
|
mov eax,[error_line]
|
|
or eax,eax
|
|
jz assemble_ok
|
|
mov [current_line],eax
|
|
cmp [error],undefined_symbol
|
|
jne error_confirmed
|
|
mov eax,[error_info]
|
|
or eax,eax
|
|
jz error_confirmed
|
|
test byte [eax+8],1
|
|
jnz next_pass
|
|
error_confirmed:
|
|
call error_handler
|
|
error_handler:
|
|
mov eax,[error]
|
|
sub eax,error_handler
|
|
add [esp],eax
|
|
ret
|
|
next_pass:
|
|
inc [current_pass]
|
|
mov ax,[current_pass]
|
|
cmp ax,[passes_limit]
|
|
je code_cannot_be_generated
|
|
jmp assembler_loop
|
|
assemble_ok:
|
|
ret
|
|
|
|
create_addressing_space:
|
|
mov ebx,[addressing_space]
|
|
test ebx,ebx
|
|
jz init_addressing_space
|
|
test byte [ebx+0Ah],1
|
|
jnz illegal_instruction
|
|
mov eax,edi
|
|
sub eax,[ebx+18h]
|
|
mov [ebx+1Ch],eax
|
|
init_addressing_space:
|
|
mov ebx,[tagged_blocks]
|
|
mov dword [ebx-4],10h
|
|
mov dword [ebx-8],24h
|
|
sub ebx,8+24h
|
|
cmp ebx,edi
|
|
jbe out_of_memory
|
|
mov [tagged_blocks],ebx
|
|
mov [addressing_space],ebx
|
|
xor eax,eax
|
|
mov [ebx],edi
|
|
mov [ebx+4],eax
|
|
mov [ebx+8],eax
|
|
mov [ebx+10h],eax
|
|
mov [ebx+14h],eax
|
|
mov [ebx+18h],edi
|
|
mov [ebx+1Ch],eax
|
|
mov [ebx+20h],eax
|
|
ret
|
|
|
|
assemble_line:
|
|
mov eax,[tagged_blocks]
|
|
sub eax,100h
|
|
cmp edi,eax
|
|
ja out_of_memory
|
|
lods byte [esi]
|
|
cmp al,1
|
|
je assemble_instruction
|
|
jb source_end
|
|
cmp al,3
|
|
jb define_label
|
|
je define_constant
|
|
cmp al,4
|
|
je label_addressing_space
|
|
cmp al,0Fh
|
|
je new_line
|
|
cmp al,13h
|
|
je code_type_setting
|
|
cmp al,10h
|
|
jne illegal_instruction
|
|
lods byte [esi]
|
|
jmp segment_prefix
|
|
code_type_setting:
|
|
lods byte [esi]
|
|
mov [code_type],al
|
|
jmp instruction_assembled
|
|
new_line:
|
|
lods dword [esi]
|
|
mov [current_line],eax
|
|
and [prefix_flags],0
|
|
cmp [symbols_file],0
|
|
je continue_line
|
|
cmp [next_pass_needed],0
|
|
jne continue_line
|
|
mov ebx,[tagged_blocks]
|
|
mov dword [ebx-4],1
|
|
mov dword [ebx-8],14h
|
|
sub ebx,8+14h
|
|
cmp ebx,edi
|
|
jbe out_of_memory
|
|
mov [tagged_blocks],ebx
|
|
mov [ebx],eax
|
|
mov [ebx+4],edi
|
|
mov eax,[addressing_space]
|
|
mov [ebx+8],eax
|
|
mov al,[code_type]
|
|
mov [ebx+10h],al
|
|
continue_line:
|
|
cmp byte [esi],0Fh
|
|
je line_assembled
|
|
jmp assemble_line
|
|
define_label:
|
|
lods dword [esi]
|
|
cmp eax,0Fh
|
|
jb invalid_use_of_symbol
|
|
je reserved_word_used_as_symbol
|
|
mov ebx,eax
|
|
lods byte [esi]
|
|
mov [label_size],al
|
|
call make_label
|
|
jmp continue_line
|
|
make_label:
|
|
mov eax,edi
|
|
xor edx,edx
|
|
xor cl,cl
|
|
mov ebp,[addressing_space]
|
|
sub eax,[ds:ebp]
|
|
sbb edx,[ds:ebp+4]
|
|
sbb cl,[ds:ebp+8]
|
|
jp label_value_ok
|
|
call recoverable_overflow
|
|
label_value_ok:
|
|
mov [address_sign],cl
|
|
test byte [ds:ebp+0Ah],1
|
|
jnz make_virtual_label
|
|
or byte [ebx+9],1
|
|
xchg eax,[ebx]
|
|
xchg edx,[ebx+4]
|
|
mov ch,[ebx+9]
|
|
shr ch,1
|
|
and ch,1
|
|
neg ch
|
|
sub eax,[ebx]
|
|
sbb edx,[ebx+4]
|
|
sbb ch,cl
|
|
mov dword [adjustment],eax
|
|
mov dword [adjustment+4],edx
|
|
mov [adjustment_sign],ch
|
|
or al,ch
|
|
or eax,edx
|
|
setnz ah
|
|
jmp finish_label
|
|
make_virtual_label:
|
|
and byte [ebx+9],not 1
|
|
cmp eax,[ebx]
|
|
mov [ebx],eax
|
|
setne ah
|
|
cmp edx,[ebx+4]
|
|
mov [ebx+4],edx
|
|
setne al
|
|
or ah,al
|
|
finish_label:
|
|
mov ebp,[addressing_space]
|
|
mov ch,[ds:ebp+9]
|
|
mov cl,[label_size]
|
|
mov edx,[ds:ebp+14h]
|
|
mov ebp,[ds:ebp+10h]
|
|
finish_label_symbol:
|
|
mov al,[address_sign]
|
|
xor al,[ebx+9]
|
|
and al,10b
|
|
or ah,al
|
|
xor [ebx+9],al
|
|
cmp cl,[ebx+10]
|
|
mov [ebx+10],cl
|
|
setne al
|
|
or ah,al
|
|
cmp ch,[ebx+11]
|
|
mov [ebx+11],ch
|
|
setne al
|
|
or ah,al
|
|
cmp ebp,[ebx+12]
|
|
mov [ebx+12],ebp
|
|
setne al
|
|
or ah,al
|
|
or ch,ch
|
|
jz label_symbol_ok
|
|
cmp edx,[ebx+20]
|
|
mov [ebx+20],edx
|
|
setne al
|
|
or ah,al
|
|
label_symbol_ok:
|
|
mov cx,[current_pass]
|
|
xchg [ebx+16],cx
|
|
mov edx,[current_line]
|
|
mov [ebx+28],edx
|
|
and byte [ebx+8],not 2
|
|
test byte [ebx+8],1
|
|
jz new_label
|
|
cmp cx,[ebx+16]
|
|
je symbol_already_defined
|
|
btr dword [ebx+8],10
|
|
jc requalified_label
|
|
inc cx
|
|
sub cx,[ebx+16]
|
|
setnz al
|
|
or ah,al
|
|
jz label_made
|
|
test byte [ebx+8],8
|
|
jz label_made
|
|
mov cx,[current_pass]
|
|
cmp cx,[ebx+18]
|
|
jne label_made
|
|
requalified_label:
|
|
or [next_pass_needed],-1
|
|
pop [error_line]
|
|
ret
|
|
no_end_directive:
|
|
mov eax,[error_line]
|
|
mov [current_line],eax
|
|
jmp missing_end_directive
|
|
skip_repeat:
|
|
call find_end_repeat
|
|
jmp find_end_directive
|
|
skip_while:
|
|
call find_end_while
|
|
jmp find_end_directive
|
|
skip_if:
|
|
call skip_if_block
|
|
jmp find_end_directive
|
|
skip_if_block:
|
|
call find_else
|
|
jc if_block_skipped
|
|
cmp byte [esi],1
|
|
jne skip_after_else
|
|
cmp word [esi+1],if_directive-instruction_handler
|
|
jne skip_after_else
|
|
add esi,4
|
|
jmp skip_if_block
|
|
skip_after_else:
|
|
call find_end_if
|
|
if_block_skipped:
|
|
ret
|
|
end_directive:
|
|
lods byte [esi]
|
|
cmp al,1
|
|
jne invalid_argument
|
|
lods word [esi]
|
|
inc esi
|
|
cmp ax,virtual_directive-instruction_handler
|
|
je end_virtual
|
|
cmp ax,repeat_directive-instruction_handler
|
|
je end_repeat
|
|
cmp ax,while_directive-instruction_handler
|
|
je end_while
|
|
cmp ax,if_directive-instruction_handler
|
|
je end_if
|
|
cmp ax,data_directive-instruction_handler
|
|
je end_data
|
|
jmp invalid_argument
|
|
break_directive:
|
|
mov ebx,[structures_buffer]
|
|
mov al,[esi]
|
|
or al,al
|
|
jz find_breakable_structure
|
|
cmp al,0Fh
|
|
jne extra_characters_on_line
|
|
find_breakable_structure:
|
|
cmp ebx,[additional_memory_end]
|
|
je unexpected_instruction
|
|
mov ax,[ebx]
|
|
cmp ax,repeat_directive-instruction_handler
|
|
je break_repeat
|
|
cmp ax,while_directive-instruction_handler
|
|
je break_while
|
|
cmp ax,if_directive-instruction_handler
|
|
je break_if
|
|
add ebx,18h
|
|
jmp find_breakable_structure
|
|
break_if:
|
|
push [current_line]
|
|
mov eax,[ebx+4]
|
|
mov [current_line],eax
|
|
call remove_structure_data
|
|
call skip_if_block
|
|
pop [current_line]
|
|
mov ebx,[structures_buffer]
|
|
jmp find_breakable_structure
|
|
break_repeat:
|
|
push ebx
|
|
call find_end_repeat
|
|
pop ebx
|
|
jmp stop_repeat
|
|
break_while:
|
|
push ebx
|
|
jmp stop_while
|
|
|
|
define_data:
|
|
cmp edi,[tagged_blocks]
|
|
jae out_of_memory
|
|
cmp byte [esi],'('
|
|
jne simple_data_value
|
|
mov ebx,esi
|
|
inc esi
|
|
call skip_expression
|
|
xchg esi,ebx
|
|
cmp byte [ebx],81h
|
|
jne simple_data_value
|
|
inc esi
|
|
call get_count_value
|
|
inc esi
|
|
or eax,eax
|
|
jz duplicate_zero_times
|
|
cmp byte [esi],91h
|
|
jne duplicate_single_data_value
|
|
inc esi
|
|
duplicate_data:
|
|
push eax esi
|
|
duplicated_values:
|
|
cmp edi,[tagged_blocks]
|
|
jae out_of_memory
|
|
clc
|
|
call near dword [esp+8]
|
|
lods byte [esi]
|
|
cmp al,','
|
|
je duplicated_values
|
|
cmp al,92h
|
|
jne invalid_argument
|
|
pop ebx eax
|
|
dec eax
|
|
jz data_defined
|
|
mov esi,ebx
|
|
jmp duplicate_data
|
|
duplicate_single_data_value:
|
|
cmp edi,[tagged_blocks]
|
|
jae out_of_memory
|
|
push eax esi
|
|
clc
|
|
call near dword [esp+8]
|
|
pop ebx eax
|
|
dec eax
|
|
jz data_defined
|
|
mov esi,ebx
|
|
jmp duplicate_single_data_value
|
|
duplicate_zero_times:
|
|
cmp byte [esi],91h
|
|
jne skip_single_data_value
|
|
inc esi
|
|
skip_data_value:
|
|
call skip_symbol
|
|
jc invalid_argument
|
|
cmp byte [esi],92h
|
|
jne skip_data_value
|
|
inc esi
|
|
jmp data_defined
|
|
skip_single_data_value:
|
|
call skip_symbol
|
|
jmp data_defined
|
|
simple_data_value:
|
|
cmp edi,[tagged_blocks]
|
|
jae out_of_memory
|
|
clc
|
|
call near dword [esp]
|
|
data_defined:
|
|
lods byte [esi]
|
|
cmp al,','
|
|
je define_data
|
|
dec esi
|
|
stc
|
|
ret
|
|
data_bytes:
|
|
call define_data
|
|
jc instruction_assembled
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
je get_byte
|
|
cmp al,'?'
|
|
jne invalid_argument
|
|
mov eax,edi
|
|
mov byte [edi],0
|
|
inc edi
|
|
jmp undefined_data
|
|
get_byte:
|
|
cmp byte [esi],0
|
|
je get_string
|
|
call get_byte_value
|
|
stos byte [edi]
|
|
ret
|
|
get_string:
|
|
inc esi
|
|
lods dword [esi]
|
|
mov ecx,eax
|
|
lea eax,[edi+ecx]
|
|
cmp eax,[tagged_blocks]
|
|
ja out_of_memory
|
|
rep movs byte [edi],[esi]
|
|
inc esi
|
|
ret
|
|
undefined_data:
|
|
mov ebp,[addressing_space]
|
|
test byte [ds:ebp+0Ah],1
|
|
jz mark_undefined_data
|
|
ret
|
|
mark_undefined_data:
|
|
cmp eax,[undefined_data_end]
|
|
je undefined_data_ok
|
|
mov [undefined_data_start],eax
|
|
undefined_data_ok:
|
|
mov [undefined_data_end],edi
|
|
ret
|
|
data_unicode:
|
|
or [base_code],-1
|
|
jmp define_words
|
|
data_words:
|
|
mov [base_code],0
|
|
define_words:
|
|
call define_data
|
|
jc instruction_assembled
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
je get_word
|
|
cmp al,'?'
|
|
jne invalid_argument
|
|
mov eax,edi
|
|
and word [edi],0
|
|
scas word [edi]
|
|
jmp undefined_data
|
|
ret
|
|
get_word:
|
|
cmp [base_code],0
|
|
je word_data_value
|
|
cmp byte [esi],0
|
|
je word_string
|
|
word_data_value:
|
|
call get_word_value
|
|
call mark_relocation
|
|
stos word [edi]
|
|
ret
|
|
word_string:
|
|
inc esi
|
|
lods dword [esi]
|
|
mov ecx,eax
|
|
jecxz word_string_ok
|
|
lea eax,[edi+ecx*2]
|
|
cmp eax,[tagged_blocks]
|
|
ja out_of_memory
|
|
xor ah,ah
|
|
copy_word_string:
|
|
lods byte [esi]
|
|
stos word [edi]
|
|
loop copy_word_string
|
|
word_string_ok:
|
|
inc esi
|
|
ret
|
|
data_dwords:
|
|
call define_data
|
|
jc instruction_assembled
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
je get_dword
|
|
cmp al,'?'
|
|
jne invalid_argument
|
|
mov eax,edi
|
|
and dword [edi],0
|
|
scas dword [edi]
|
|
jmp undefined_data
|
|
get_dword:
|
|
push esi
|
|
call get_dword_value
|
|
pop ebx
|
|
cmp byte [esi],':'
|
|
je complex_dword
|
|
call mark_relocation
|
|
stos dword [edi]
|
|
ret
|
|
complex_dword:
|
|
mov esi,ebx
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
call get_word_value
|
|
push eax
|
|
inc esi
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
jne invalid_operand
|
|
mov al,[value_type]
|
|
push eax
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
call get_word_value
|
|
call mark_relocation
|
|
stos word [edi]
|
|
pop eax
|
|
mov [value_type],al
|
|
pop eax
|
|
call mark_relocation
|
|
stos word [edi]
|
|
ret
|
|
data_pwords:
|
|
call define_data
|
|
jc instruction_assembled
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
je get_pword
|
|
cmp al,'?'
|
|
jne invalid_argument
|
|
mov eax,edi
|
|
and dword [edi],0
|
|
scas dword [edi]
|
|
and word [edi],0
|
|
scas word [edi]
|
|
jmp undefined_data
|
|
get_pword:
|
|
push esi
|
|
call get_pword_value
|
|
pop ebx
|
|
cmp byte [esi],':'
|
|
je complex_pword
|
|
call mark_relocation
|
|
stos dword [edi]
|
|
mov ax,dx
|
|
stos word [edi]
|
|
ret
|
|
complex_pword:
|
|
mov esi,ebx
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
call get_word_value
|
|
push eax
|
|
inc esi
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
jne invalid_operand
|
|
mov al,[value_type]
|
|
push eax
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
call get_dword_value
|
|
call mark_relocation
|
|
stos dword [edi]
|
|
pop eax
|
|
mov [value_type],al
|
|
pop eax
|
|
call mark_relocation
|
|
stos word [edi]
|
|
ret
|
|
data_qwords:
|
|
call define_data
|
|
jc instruction_assembled
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
je get_qword
|
|
cmp al,'?'
|
|
jne invalid_argument
|
|
mov eax,edi
|
|
and dword [edi],0
|
|
scas dword [edi]
|
|
and dword [edi],0
|
|
scas dword [edi]
|
|
jmp undefined_data
|
|
get_qword:
|
|
call get_qword_value
|
|
call mark_relocation
|
|
stos dword [edi]
|
|
mov eax,edx
|
|
stos dword [edi]
|
|
ret
|
|
data_twords:
|
|
call define_data
|
|
jc instruction_assembled
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
je get_tword
|
|
cmp al,'?'
|
|
jne invalid_argument
|
|
mov eax,edi
|
|
and dword [edi],0
|
|
scas dword [edi]
|
|
and dword [edi],0
|
|
scas dword [edi]
|
|
and word [edi],0
|
|
scas word [edi]
|
|
jmp undefined_data
|
|
get_tword:
|
|
cmp byte [esi],'.'
|
|
jne complex_tword
|
|
inc esi
|
|
cmp word [esi+8],8000h
|
|
je fp_zero_tword
|
|
mov eax,[esi]
|
|
stos dword [edi]
|
|
mov eax,[esi+4]
|
|
stos dword [edi]
|
|
mov ax,[esi+8]
|
|
add ax,3FFFh
|
|
jo value_out_of_range
|
|
cmp ax,7FFFh
|
|
jge value_out_of_range
|
|
cmp ax,0
|
|
jg tword_exp_ok
|
|
mov cx,ax
|
|
neg cx
|
|
inc cx
|
|
cmp cx,64
|
|
jae value_out_of_range
|
|
cmp cx,32
|
|
ja large_shift
|
|
mov eax,[esi]
|
|
mov edx,[esi+4]
|
|
mov ebx,edx
|
|
shr edx,cl
|
|
shrd eax,ebx,cl
|
|
jmp tword_mantissa_shift_done
|
|
large_shift:
|
|
sub cx,32
|
|
xor edx,edx
|
|
mov eax,[esi+4]
|
|
shr eax,cl
|
|
tword_mantissa_shift_done:
|
|
jnc store_shifted_mantissa
|
|
add eax,1
|
|
adc edx,0
|
|
store_shifted_mantissa:
|
|
mov [edi-8],eax
|
|
mov [edi-4],edx
|
|
xor ax,ax
|
|
test edx,1 shl 31
|
|
jz tword_exp_ok
|
|
inc ax
|
|
tword_exp_ok:
|
|
mov bl,[esi+11]
|
|
shl bx,15
|
|
or ax,bx
|
|
stos word [edi]
|
|
add esi,13
|
|
ret
|
|
fp_zero_tword:
|
|
xor eax,eax
|
|
stos dword [edi]
|
|
stos dword [edi]
|
|
mov al,[esi+11]
|
|
shl ax,15
|
|
stos word [edi]
|
|
add esi,13
|
|
ret
|
|
complex_tword:
|
|
call get_word_value
|
|
push eax
|
|
cmp byte [esi],':'
|
|
jne invalid_operand
|
|
inc esi
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
jne invalid_operand
|
|
mov al,[value_type]
|
|
push eax
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
call get_qword_value
|
|
call mark_relocation
|
|
stos dword [edi]
|
|
mov eax,edx
|
|
stos dword [edi]
|
|
pop eax
|
|
mov [value_type],al
|
|
pop eax
|
|
call mark_relocation
|
|
stos word [edi]
|
|
ret
|
|
data_file:
|
|
lods word [esi]
|
|
cmp ax,'('
|
|
jne invalid_argument
|
|
add esi,4
|
|
call open_binary_file
|
|
mov eax,[esi-4]
|
|
lea esi,[esi+eax+1]
|
|
mov al,2
|
|
xor edx,edx
|
|
call lseek
|
|
push eax
|
|
xor edx,edx
|
|
cmp byte [esi],':'
|
|
jne position_ok
|
|
inc esi
|
|
cmp byte [esi],'('
|
|
jne invalid_argument
|
|
inc esi
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
push ebx
|
|
call get_count_value
|
|
pop ebx
|
|
mov edx,eax
|
|
sub [esp],edx
|
|
jc value_out_of_range
|
|
position_ok:
|
|
cmp byte [esi],','
|
|
jne size_ok
|
|
inc esi
|
|
cmp byte [esi],'('
|
|
jne invalid_argument
|
|
inc esi
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
push ebx edx
|
|
call get_count_value
|
|
pop edx ebx
|
|
cmp eax,[esp]
|
|
ja value_out_of_range
|
|
mov [esp],eax
|
|
size_ok:
|
|
xor al,al
|
|
call lseek
|
|
pop ecx
|
|
mov edx,edi
|
|
add edi,ecx
|
|
jc out_of_memory
|
|
cmp edi,[tagged_blocks]
|
|
ja out_of_memory
|
|
call read
|
|
jc error_reading_file
|
|
call close
|
|
lods byte [esi]
|
|
cmp al,','
|
|
je data_file
|
|
dec esi
|
|
jmp instruction_assembled
|
|
open_binary_file:
|
|
push esi
|
|
push edi
|
|
mov eax,[current_line]
|
|
find_current_source_path:
|
|
mov esi,[eax]
|
|
test byte [eax+7],80h
|
|
jz get_current_path
|
|
mov eax,[eax+8]
|
|
jmp find_current_source_path
|
|
get_current_path:
|
|
lodsb
|
|
stosb
|
|
or al,al
|
|
jnz get_current_path
|
|
cut_current_path:
|
|
cmp edi,[esp]
|
|
je current_path_ok
|
|
cmp byte [edi-1],'\'
|
|
je current_path_ok
|
|
cmp byte [edi-1],'/'
|
|
je current_path_ok
|
|
dec edi
|
|
jmp cut_current_path
|
|
current_path_ok:
|
|
mov esi,[esp+4]
|
|
call expand_path
|
|
pop edx
|
|
mov esi,edx
|
|
call open
|
|
jnc file_opened
|
|
mov edx,[include_paths]
|
|
search_in_include_paths:
|
|
push edx esi
|
|
mov edi,esi
|
|
mov esi,[esp+4]
|
|
call get_include_directory
|
|
mov [esp+4],esi
|
|
mov esi,[esp+8]
|
|
call expand_path
|
|
pop edx
|
|
mov esi,edx
|
|
call open
|
|
pop edx
|
|
jnc file_opened
|
|
cmp byte [edx],0
|
|
jne search_in_include_paths
|
|
mov edi,esi
|
|
mov esi,[esp]
|
|
push edi
|
|
call expand_path
|
|
pop edx
|
|
mov esi,edx
|
|
call open
|
|
jc file_not_found
|
|
file_opened:
|
|
mov edi,esi
|
|
pop esi
|
|
ret
|
|
reserve_bytes:
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
jne invalid_argument
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
call get_count_value
|
|
mov ecx,eax
|
|
mov edx,ecx
|
|
add edx,edi
|
|
jc out_of_memory
|
|
cmp edx,[tagged_blocks]
|
|
ja out_of_memory
|
|
push edi
|
|
cmp [next_pass_needed],0
|
|
je zero_bytes
|
|
add edi,ecx
|
|
jmp reserved_data
|
|
zero_bytes:
|
|
xor eax,eax
|
|
shr ecx,1
|
|
jnc bytes_stosb_ok
|
|
stos byte [edi]
|
|
bytes_stosb_ok:
|
|
shr ecx,1
|
|
jnc bytes_stosw_ok
|
|
stos word [edi]
|
|
bytes_stosw_ok:
|
|
rep stos dword [edi]
|
|
reserved_data:
|
|
pop eax
|
|
call undefined_data
|
|
jmp instruction_assembled
|
|
reserve_words:
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
jne invalid_argument
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
call get_count_value
|
|
mov ecx,eax
|
|
mov edx,ecx
|
|
shl edx,1
|
|
jc out_of_memory
|
|
add edx,edi
|
|
jc out_of_memory
|
|
cmp edx,[tagged_blocks]
|
|
ja out_of_memory
|
|
push edi
|
|
cmp [next_pass_needed],0
|
|
je zero_words
|
|
lea edi,[edi+ecx*2]
|
|
jmp reserved_data
|
|
zero_words:
|
|
xor eax,eax
|
|
shr ecx,1
|
|
jnc words_stosw_ok
|
|
stos word [edi]
|
|
words_stosw_ok:
|
|
rep stos dword [edi]
|
|
jmp reserved_data
|
|
reserve_dwords:
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
jne invalid_argument
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
call get_count_value
|
|
mov ecx,eax
|
|
mov edx,ecx
|
|
shl edx,1
|
|
jc out_of_memory
|
|
shl edx,1
|
|
jc out_of_memory
|
|
add edx,edi
|
|
jc out_of_memory
|
|
cmp edx,[tagged_blocks]
|
|
ja out_of_memory
|
|
push edi
|
|
cmp [next_pass_needed],0
|
|
je zero_dwords
|
|
lea edi,[edi+ecx*4]
|
|
jmp reserved_data
|
|
zero_dwords:
|
|
xor eax,eax
|
|
rep stos dword [edi]
|
|
jmp reserved_data
|
|
reserve_pwords:
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
jne invalid_argument
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
call get_count_value
|
|
mov ecx,eax
|
|
shl ecx,1
|
|
jc out_of_memory
|
|
add ecx,eax
|
|
mov edx,ecx
|
|
shl edx,1
|
|
jc out_of_memory
|
|
add edx,edi
|
|
jc out_of_memory
|
|
cmp edx,[tagged_blocks]
|
|
ja out_of_memory
|
|
push edi
|
|
cmp [next_pass_needed],0
|
|
je zero_words
|
|
lea edi,[edi+ecx*2]
|
|
jmp reserved_data
|
|
reserve_qwords:
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
jne invalid_argument
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
call get_count_value
|
|
mov ecx,eax
|
|
shl ecx,1
|
|
jc out_of_memory
|
|
mov edx,ecx
|
|
shl edx,1
|
|
jc out_of_memory
|
|
shl edx,1
|
|
jc out_of_memory
|
|
add edx,edi
|
|
jc out_of_memory
|
|
cmp edx,[tagged_blocks]
|
|
ja out_of_memory
|
|
push edi
|
|
cmp [next_pass_needed],0
|
|
je zero_dwords
|
|
lea edi,[edi+ecx*4]
|
|
jmp reserved_data
|
|
reserve_twords:
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
jne invalid_argument
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
call get_count_value
|
|
mov ecx,eax
|
|
shl ecx,2
|
|
jc out_of_memory
|
|
add ecx,eax
|
|
mov edx,ecx
|
|
shl edx,1
|
|
jc out_of_memory
|
|
add edx,edi
|
|
jc out_of_memory
|
|
cmp edx,[tagged_blocks]
|
|
ja out_of_memory
|
|
push edi
|
|
cmp [next_pass_needed],0
|
|
je zero_words
|
|
lea edi,[edi+ecx*2]
|
|
jmp reserved_data
|
|
align_directive:
|
|
lods byte [esi]
|
|
cmp al,'('
|
|
jne invalid_argument
|
|
cmp byte [esi],'.'
|
|
je invalid_value
|
|
call get_count_value
|
|
mov edx,eax
|
|
dec edx
|
|
test eax,edx
|
|
jnz invalid_align_value
|
|
or eax,eax
|
|
jz invalid_align_value
|
|
cmp eax,1
|
|
je instruction_assembled
|
|
mov ecx,edi
|
|
mov ebp,[addressing_space]
|
|
sub ecx,[ds:ebp]
|
|
cmp dword [ds:ebp+10h],0
|
|
jne section_not_aligned_enough
|
|
cmp byte [ds:ebp+9],0
|
|
je make_alignment
|
|
cmp [output_format],3
|
|
je pe_alignment
|
|
cmp [output_format],5
|
|
jne object_alignment
|
|
test [format_flags],1
|
|
jnz pe_alignment
|
|
object_alignment:
|
|
mov ebx,[ds:ebp+14h]
|
|
cmp byte [ebx],0
|
|
jne section_not_aligned_enough
|
|
cmp eax,[ebx+10h]
|
|
jbe make_alignment
|
|
jmp section_not_aligned_enough
|
|
pe_alignment:
|
|
cmp eax,1000h
|
|
ja section_not_aligned_enough
|
|
make_alignment:
|
|
dec eax
|
|
and ecx,eax
|
|
jz instruction_assembled
|
|
neg ecx
|
|
add ecx,eax
|
|
inc ecx
|
|
mov edx,ecx
|
|
add edx,edi
|
|
jc out_of_memory
|
|
cmp edx,[tagged_blocks]
|
|
ja out_of_memory
|
|
push edi
|
|
cmp [next_pass_needed],0
|
|
je nops
|
|
add edi,ecx
|
|
jmp reserved_data
|
|
invalid_align_value:
|
|
cmp [error_line],0
|
|
jne instruction_assembled
|
|
mov eax,[current_line]
|
|
mov [error_line],eax
|
|
mov [error],invalid_value
|
|
jmp instruction_assembled
|
|
nops:
|
|
mov eax,90909090h
|
|
shr ecx,1
|
|
jnc nops_stosb_ok
|
|
stos byte [edi]
|
|
nops_stosb_ok:
|
|
shr ecx,1
|
|
jnc nops_stosw_ok
|
|
stos word [edi]
|
|
nops_stosw_ok:
|
|
rep stos dword [edi]
|
|
jmp reserved_data
|
|
err_directive:
|
|
mov al,[esi]
|
|
cmp al,0Fh
|
|
je invoked_error
|
|
or al,al
|
|
jz invoked_error
|
|
jmp extra_characters_on_line
|
|
assert_directive:
|
|
call calculate_logical_expression
|
|
or al,al
|
|
jnz instruction_assembled
|
|
cmp [error_line],0
|
|
jne instruction_assembled
|
|
mov eax,[current_line]
|
|
mov [error_line],eax
|
|
mov [error],assertion_failed
|
|
jmp instruction_assembled
|
|
|
|
hex_digit_skip:
|
|
dec esi
|
|
jmp get_hex_digit
|
|
get_oct_number:
|
|
xor bl,bl
|
|
get_oct_digit:
|
|
cmp esi,[number_start]
|
|
jb number_ok
|
|
movzx eax,byte [esi]
|
|
cmp al,27h
|
|
je oct_digit_skip
|
|
cmp al,'_'
|
|
je oct_digit_skip
|
|
sub al,30h
|
|
cmp al,7
|
|
ja bad_number
|
|
oct_digit_ok:
|
|
xor edx,edx
|
|
mov cl,bl
|
|
dec esi
|
|
cmp bl,63
|
|
ja oct_out_of_range
|
|
jne oct_range_ok
|
|
cmp al,1
|
|
ja oct_out_of_range
|
|
oct_range_ok:
|
|
add bl,3
|
|
cmp cl,30
|
|
je oct_digit_wrap
|
|
ja oct_digit_high
|
|
shl eax,cl
|
|
or dword [edi],eax
|
|
jmp get_oct_digit
|
|
oct_digit_wrap:
|
|
shl eax,cl
|
|
adc dword [edi+4],0
|
|
or dword [edi],eax
|
|
jmp get_oct_digit
|
|
oct_digit_high:
|
|
sub cl,32
|
|
shl eax,cl
|
|
or dword [edi+4],eax
|
|
jmp get_oct_digit
|
|
oct_digit_skip:
|
|
dec esi
|
|
jmp get_oct_digit
|
|
oct_out_of_range:
|
|
or al,al
|
|
jz get_oct_digit
|
|
or ebp,-1
|
|
jmp get_oct_digit
|
|
hex_number_ok:
|
|
dec esi
|
|
pascal_hex_ok:
|
|
cmp esi,[number_start]
|
|
jne bad_number
|
|
number_ok:
|
|
pop esi
|
|
number_done:
|
|
clc
|
|
ret
|
|
get_text_number:
|
|
lods dword [esi]
|
|
mov edx,eax
|
|
xor bl,bl
|
|
mov dword [edi],0
|
|
mov dword [edi+4],0
|
|
get_text_character:
|
|
sub edx,1
|
|
jc number_done
|
|
movzx eax,byte [esi]
|
|
inc esi
|
|
mov cl,bl
|
|
cmp bl,64
|
|
je text_out_of_range
|
|
add bl,8
|
|
cmp cl,32
|
|
jae text_character_high
|
|
shl eax,cl
|
|
or dword [edi],eax
|
|
jmp get_text_character
|
|
text_character_high:
|
|
sub cl,32
|
|
shl eax,cl
|
|
or dword [edi+4],eax
|
|
jmp get_text_character
|
|
text_out_of_range:
|
|
or ebp,-1
|
|
jmp get_text_character
|
|
cmp al,'~'
|
|
je separator
|
|
cmp al,'>'
|
|
je greater
|
|
cmp al,'<'
|
|
je less
|
|
cmp al,')'
|
|
je close_parenthesis
|
|
or al,al
|
|
jz contents_parsed
|
|
cmp al,'['
|
|
je address_argument
|
|
cmp al,']'
|
|
je separator
|
|
cmp al,'{'
|
|
je open_decorator
|
|
cmp al,'}'
|
|
je close_decorator
|
|
cmp al,'#'
|
|
je unallowed_character
|
|
cmp al,'`'
|
|
je unallowed_character
|
|
cmp al,3Bh
|
|
je foreign_argument
|
|
cmp [decorator_symbols_allowed],0
|
|
je not_a_separator
|
|
cmp al,'-'
|
|
je separator
|
|
not_a_separator:
|
|
dec esi
|
|
cmp al,1Ah
|
|
jne expression_argument
|
|
push edi
|
|
mov edi,directive_operators
|
|
call get_operator
|
|
or al,al
|
|
jnz operator_argument
|
|
inc esi
|
|
movzx ecx,byte [esi]
|
|
inc esi
|
|
call get_symbol
|
|
jnc symbol_argument
|
|
cmp ecx,1
|
|
jne check_argument
|
|
cmp byte [esi],'?'
|
|
jne check_argument
|
|
pop edi
|
|
movs byte [edi],[esi]
|
|
jmp argument_parsed
|
|
foreign_argument:
|
|
dec esi
|
|
call skip_foreign_line
|
|
jmp contents_parsed
|
|
symbol_argument:
|
|
pop edi
|
|
stos word [edi]
|
|
cmp byte [esi],'+'
|
|
jne argument_parsed
|
|
and ax,0F0FFh
|
|
cmp ax,6010h
|
|
jne argument_parsed
|
|
movs byte [edi],[esi]
|
|
jmp argument_parsed
|
|
operator_argument:
|
|
pop edi
|
|
cmp al,85h
|
|
je ptr_argument
|
|
stos byte [edi]
|
|
cmp al,8Ch
|
|
je forced_expression
|
|
cmp al,81h
|
|
je forced_parenthesis
|
|
cmp al,80h
|
|
je parse_at_operator
|
|
cmp al,82h
|
|
je parse_from_operator
|
|
cmp al,89h
|
|
je parse_label_operator
|
|
cmp al,0F8h
|
|
je forced_expression
|
|
jmp argument_parsed
|
|
instruction_separator:
|
|
stos byte [edi]
|
|
allow_embedded_instruction:
|
|
cmp byte [esi],1Ah
|
|
jne parse_argument
|
|
push edi
|
|
inc esi
|
|
movzx ecx,byte [esi]
|
|
inc esi
|
|
call get_instruction
|
|
jnc embedded_instruction
|
|
call get_data_directive
|
|
jnc embedded_instruction
|
|
pop edi
|
|
sub esi,2
|
|
jmp parse_argument
|
|
embedded_instruction:
|
|
pop edi
|
|
mov dl,al
|
|
mov al,1
|
|
stos byte [edi]
|
|
mov ax,bx
|
|
stos word [edi]
|
|
mov al,dl
|
|
stos byte [edi]
|
|
jmp parse_instruction_arguments
|
|
parse_times_directive:
|
|
mov al,'('
|
|
stos byte [edi]
|
|
call convert_expression
|
|
mov al,')'
|
|
stos byte [edi]
|
|
cmp byte [esi],':'
|
|
jne allow_embedded_instruction
|
|
movs byte [edi],[esi]
|
|
jmp allow_embedded_instruction
|
|
parse_segment_directive:
|
|
or [formatter_symbols_allowed],-1
|
|
parse_label_directive:
|
|
cmp byte [esi],1Ah
|
|
jne argument_parsed
|
|
push esi
|
|
inc esi
|
|
movzx ecx,byte [esi]
|
|
inc esi
|
|
call identify_label
|
|
pop ebx
|
|
cmp eax,0Fh
|
|
je non_label_identified
|
|
mov byte [edi],2
|
|
inc edi
|
|
stos dword [edi]
|
|
xor al,al
|
|
stos byte [edi]
|
|
jmp argument_parsed
|
|
non_label_identified:
|
|
mov esi,ebx
|
|
jmp argument_parsed
|
|
parse_load_directive:
|
|
cmp byte [esi],1Ah
|
|
jne argument_parsed
|
|
push esi
|
|
inc esi
|
|
movzx ecx,byte [esi]
|
|
inc esi
|
|
call get_label_id
|
|
pop ebx
|
|
cmp eax,0Fh
|
|
je non_label_identified
|
|
mov byte [edi],2
|
|
inc edi
|
|
stos dword [edi]
|
|
xor al,al
|
|
stos byte [edi]
|
|
jmp argument_parsed
|
|
parse_public_directive:
|
|
cmp byte [esi],1Ah
|
|
jne parse_argument
|
|
inc esi
|
|
push esi
|
|
movzx ecx,byte [esi]
|
|
inc esi
|
|
push esi ecx
|
|
push edi
|
|
or [formatter_symbols_allowed],-1
|
|
call get_symbol
|
|
mov [formatter_symbols_allowed],0
|
|
pop edi
|
|
jc parse_public_label
|
|
cmp al,1Dh
|
|
jne parse_public_label
|
|
add esp,12
|
|
stos word [edi]
|
|
jmp parse_public_directive
|
|
parse_public_label:
|
|
pop ecx esi
|
|
mov al,2
|
|
stos byte [edi]
|
|
call get_label_id
|
|
stos dword [edi]
|
|
mov ax,8600h
|
|
stos word [edi]
|
|
pop ebx
|
|
push ebx esi edi
|
|
mov edi,directive_operators
|
|
call get_operator
|
|
pop edi edx ebx
|
|
cmp al,86h
|
|
je argument_parsed
|
|
mov esi,edx
|
|
xchg esi,ebx
|
|
movzx ecx,byte [esi]
|
|
inc esi
|
|
mov ax,'('
|
|
stos word [edi]
|
|
mov eax,ecx
|
|
stos dword [edi]
|
|
rep movs byte [edi],[esi]
|
|
xor al,al
|
|
stos byte [edi]
|
|
xchg esi,ebx
|
|
jmp argument_parsed
|
|
parse_extrn_directive:
|
|
cmp byte [esi],22h
|
|
je parse_quoted_extrn
|
|
cmp byte [esi],1Ah
|
|
jne parse_argument
|
|
push esi
|
|
movzx ecx,byte [esi+1]
|
|
add esi,2
|
|
mov ax,'('
|
|
stos word [edi]
|
|
mov eax,ecx
|
|
stos dword [edi]
|
|
rep movs byte [edi],[esi]
|
|
mov ax,8600h
|
|
stos word [edi]
|
|
pop esi
|
|
parse_label_operator:
|
|
cmp byte [esi],1Ah
|
|
jne argument_parsed
|
|
inc esi
|
|
movzx ecx,byte [esi]
|
|
inc esi
|
|
mov al,2
|
|
stos byte [edi]
|
|
call get_label_id
|
|
stos dword [edi]
|
|
xor al,al
|
|
stos byte [edi]
|
|
jmp argument_parsed
|
|
parse_from_operator:
|
|
cmp byte [esi],22h
|
|
je argument_parsed
|
|
parse_at_operator:
|
|
cmp byte [esi],':'
|
|
je argument_parsed
|
|
jmp forced_multipart_expression
|
|
parse_quoted_extrn:
|
|
inc esi
|
|
mov ax,'('
|
|
stos word [edi]
|
|
lods dword [esi]
|
|
mov ecx,eax
|
|
stos dword [edi]
|
|
rep movs byte [edi],[esi]
|
|
xor al,al
|
|
stos byte [edi]
|
|
push esi edi
|
|
mov edi,directive_operators
|
|
call get_operator
|
|
mov edx,esi
|
|
pop edi esi
|
|
cmp al,86h
|
|
jne argument_parsed
|
|
stos byte [edi]
|
|
mov esi,edx
|
|
jmp parse_label_operator
|
|
ptr_argument:
|
|
call parse_address
|
|
jmp address_parsed
|
|
check_argument:
|
|
push esi ecx
|
|
sub esi,2
|
|
mov edi,single_operand_operators
|
|
call get_operator
|
|
pop ecx esi
|
|
or al,al
|
|
jnz not_instruction
|
|
call get_instruction
|
|
jnc embedded_instruction
|
|
call get_data_directive
|
|
jnc embedded_instruction
|
|
not_instruction:
|
|
pop edi
|
|
sub esi,2
|
|
expression_argument:
|
|
cmp byte [esi],22h
|
|
jne not_string
|
|
mov eax,[esi+1]
|
|
lea ebx,[esi+5+eax]
|
|
push ebx ecx esi edi
|
|
call parse_expression
|
|
pop eax edx ecx ebx
|
|
cmp esi,ebx
|
|
jne expression_argument_parsed
|
|
mov edi,eax
|
|
mov esi,edx
|
|
string_argument:
|
|
inc esi
|
|
mov ax,'('
|
|
stos word [edi]
|
|
lods dword [esi]
|
|
mov ecx,eax
|
|
stos dword [edi]
|
|
shr ecx,1
|
|
jnc string_movsb_ok
|
|
movs byte [edi],[esi]
|
|
string_movsb_ok:
|
|
shr ecx,1
|
|
jnc string_movsw_ok
|
|
movs word [edi],[esi]
|
|
string_movsw_ok:
|
|
rep movs dword [edi],[esi]
|
|
xor al,al
|
|
stos byte [edi]
|
|
jmp expression_argument_parsed
|
|
parse_expression:
|
|
mov al,'('
|
|
stos byte [edi]
|
|
call convert_expression
|
|
mov al,')'
|
|
stos byte [edi]
|
|
ret
|
|
not_string:
|
|
cmp byte [esi],'('
|
|
jne expression
|
|
mov eax,esp
|
|
sub eax,[stack_limit]
|
|
cmp eax,100h
|
|
jb stack_overflow
|
|
push esi edi
|
|
inc esi
|
|
mov al,91h
|
|
stos byte [edi]
|
|
inc [parenthesis_stack]
|
|
jmp parse_argument
|
|
expression_comparator:
|
|
stos byte [edi]
|
|
jmp forced_expression
|
|
greater:
|
|
cmp byte [esi],'='
|
|
jne separator
|
|
inc esi
|
|
mov al,0F2h
|
|
jmp separator
|
|
less:
|
|
cmp byte [edi-1],0F6h
|
|
je separator
|
|
cmp byte [esi],'>'
|
|
je not_equal
|
|
cmp byte [esi],'='
|
|
jne separator
|
|
inc esi
|
|
mov al,0F3h
|
|
jmp separator
|
|
not_equal:
|
|
inc esi
|
|
mov al,0F1h
|
|
jmp expression_comparator
|
|
expression:
|
|
call parse_expression
|
|
jmp expression_argument_parsed
|
|
forced_expression:
|
|
xor al,al
|
|
xchg al,[formatter_symbols_allowed]
|
|
push eax
|
|
call parse_expression
|
|
forced_expression_parsed:
|
|
pop eax
|
|
mov [formatter_symbols_allowed],al
|
|
jmp argument_parsed
|
|
forced_multipart_expression:
|
|
xor al,al
|
|
xchg al,[formatter_symbols_allowed]
|
|
push eax
|
|
call parse_expression
|
|
cmp byte [esi],':'
|
|
jne forced_expression_parsed
|
|
movs byte [edi],[esi]
|
|
call parse_expression
|
|
jmp forced_expression_parsed
|
|
address_argument:
|
|
call parse_address
|
|
lods byte [esi]
|
|
cmp al,']'
|
|
je address_parsed
|
|
cmp al,','
|
|
je divided_address
|
|
dec esi
|
|
mov al,')'
|
|
stos byte [edi]
|
|
jmp argument_parsed
|
|
divided_address:
|
|
mov ax,'),'
|
|
stos word [edi]
|
|
jmp expression
|
|
address_parsed:
|
|
mov al,']'
|
|
stos byte [edi]
|
|
jmp argument_parsed
|
|
parse_address:
|
|
mov al,'['
|
|
stos byte [edi]
|
|
cmp word [esi],021Ah
|
|
jne convert_address
|
|
push esi
|
|
add esi,4
|
|
lea ebx,[esi+1]
|
|
cmp byte [esi],':'
|
|
pop esi
|
|
jne convert_address
|
|
add esi,2
|
|
mov ecx,2
|
|
push ebx edi
|
|
call get_symbol
|
|
pop edi esi
|
|
jc unknown_segment_prefix
|
|
cmp al,10h
|
|
jne unknown_segment_prefix
|
|
mov al,ah
|
|
and ah,11110000b
|
|
cmp ah,30h
|
|
jne unknown_segment_prefix
|
|
add al,30h
|
|
stos byte [edi]
|
|
jmp convert_address
|
|
unknown_segment_prefix:
|
|
sub esi,5
|
|
convert_address:
|
|
push edi
|
|
mov edi,address_sizes
|
|
call get_operator
|
|
pop edi
|
|
or al,al
|
|
jz convert_expression
|
|
add al,70h
|
|
stos byte [edi]
|
|
jmp convert_expression
|
|
forced_parenthesis:
|
|
cmp byte [esi],'('
|
|
jne argument_parsed
|
|
inc esi
|
|
mov al,91h
|
|
jmp separator
|
|
unallowed_character:
|
|
mov al,0FFh
|
|
jmp separator
|
|
open_decorator:
|
|
inc [decorator_symbols_allowed]
|
|
jmp separator
|
|
close_decorator:
|
|
dec [decorator_symbols_allowed]
|
|
jmp separator
|
|
close_parenthesis:
|
|
mov al,92h
|
|
separator:
|
|
stos byte [edi]
|
|
argument_parsed:
|
|
cmp [parenthesis_stack],0
|
|
je parse_argument
|
|
dec [parenthesis_stack]
|
|
add esp,8
|
|
jmp argument_parsed
|
|
expression_argument_parsed:
|
|
cmp [parenthesis_stack],0
|
|
je parse_argument
|
|
cmp byte [esi],')'
|
|
jne argument_parsed
|
|
dec [parenthesis_stack]
|
|
pop edi esi
|
|
jmp expression
|
|
contents_parsed:
|
|
cmp [parenthesis_stack],0
|
|
je contents_ok
|
|
dec [parenthesis_stack]
|
|
add esp,8
|
|
jmp contents_parsed
|
|
contents_ok:
|
|
ret
|
|
|
|
mov al,2
|
|
xor edx,edx
|
|
call lseek
|
|
mov edx,[esi+8]
|
|
sub eax,edx
|
|
jz line_data_displayed
|
|
push eax
|
|
xor al,al
|
|
call lseek
|
|
mov ecx,[esp]
|
|
mov edx,[additional_memory]
|
|
lea eax,[edx+ecx]
|
|
cmp eax,[additional_memory_end]
|
|
ja out_of_memory
|
|
call read
|
|
call close
|
|
pop ecx
|
|
mov esi,[additional_memory]
|
|
get_line_data:
|
|
mov al,[esi]
|
|
cmp al,0Ah
|
|
je display_line_data
|
|
cmp al,0Dh
|
|
je display_line_data
|
|
cmp al,1Ah
|
|
je display_line_data
|
|
or al,al
|
|
jz display_line_data
|
|
inc esi
|
|
loop get_line_data
|
|
display_line_data:
|
|
mov ecx,esi
|
|
mov esi,[additional_memory]
|
|
sub ecx,esi
|
|
call display_block
|
|
line_data_displayed:
|
|
mov esi,cr_lf
|
|
call display_string
|
|
pop ebx
|
|
or ebx,ebx
|
|
jnz display_error_line
|
|
cmp [preprocessing_done],0
|
|
je display_error_message
|
|
mov esi,preprocessed_instruction_prefix
|
|
call display_string
|
|
mov esi,[current_line]
|
|
add esi,16
|
|
mov edi,[additional_memory]
|
|
xor dl,dl
|
|
convert_instruction:
|
|
lodsb
|
|
cmp al,1Ah
|
|
je copy_symbol
|
|
cmp al,22h
|
|
je copy_symbol
|
|
cmp al,3Bh
|
|
je instruction_converted
|
|
stosb
|
|
or al,al
|
|
jz instruction_converted
|
|
xor dl,dl
|
|
jmp convert_instruction
|
|
copy_symbol:
|
|
or dl,dl
|
|
jz space_ok
|
|
mov byte [edi],20h
|
|
inc edi
|
|
space_ok:
|
|
cmp al,22h
|
|
je quoted
|
|
lodsb
|
|
movzx ecx,al
|
|
rep movsb
|
|
or dl,-1
|
|
jmp convert_instruction
|
|
quoted:
|
|
mov al,27h
|
|
stosb
|
|
lodsd
|
|
mov ecx,eax
|
|
jecxz quoted_copied
|
|
copy_quoted:
|
|
lodsb
|
|
stosb
|
|
cmp al,27h
|
|
jne quote_ok
|
|
stosb
|
|
quote_ok:
|
|
loop copy_quoted
|
|
quoted_copied:
|
|
mov al,27h
|
|
stosb
|
|
or dl,-1
|
|
jmp convert_instruction
|
|
instruction_converted:
|
|
xor al,al
|
|
stosb
|
|
mov esi,[additional_memory]
|
|
call display_string
|
|
mov esi,cr_lf
|
|
call display_string
|
|
display_error_message:
|
|
mov esi,error_prefix
|
|
call display_string
|
|
pop esi
|
|
call display_string
|
|
mov esi,error_suffix
|
|
call display_string
|
|
mov al,2
|
|
jmp exit_program
|
|
|
|
make_timestamp:
|
|
push buffer
|
|
call [GetSystemTime]
|
|
movzx ecx,word [buffer]
|
|
mov eax,ecx
|
|
sub eax,1970
|
|
mov ebx,365
|
|
mul ebx
|
|
mov ebp,eax
|
|
mov eax,ecx
|
|
sub eax,1969
|
|
shr eax,2
|
|
add ebp,eax
|
|
mov eax,ecx
|
|
sub eax,1901
|
|
mov ebx,100
|
|
div ebx
|
|
sub ebp,eax
|
|
mov eax,ecx
|
|
xor edx,edx
|
|
sub eax,1601
|
|
mov ebx,400
|
|
div ebx
|
|
add ebp,eax
|
|
movzx ecx,word [buffer+2]
|
|
mov eax,ecx
|
|
dec eax
|
|
mov ebx,30
|
|
mul ebx
|
|
add ebp,eax
|
|
cmp ecx,8
|
|
jbe months_correction
|
|
mov eax,ecx
|
|
sub eax,7
|
|
shr eax,1
|
|
add ebp,eax
|
|
mov ecx,8
|
|
months_correction:
|
|
mov eax,ecx
|
|
shr eax,1
|
|
add ebp,eax
|
|
cmp ecx,2
|
|
jbe day_correction_ok
|
|
sub ebp,2
|
|
movzx ecx,word [buffer]
|
|
test ecx,11b
|
|
jnz day_correction_ok
|
|
xor edx,edx
|
|
mov eax,ecx
|
|
mov ebx,100
|
|
div ebx
|
|
or edx,edx
|
|
jnz day_correction
|
|
mov eax,ecx
|
|
mov ebx,400
|
|
div ebx
|
|
or edx,edx
|
|
jnz day_correction_ok
|
|
day_correction:
|
|
inc ebp
|
|
day_correction_ok:
|
|
movzx eax,word [buffer+6]
|
|
dec eax
|
|
add eax,ebp
|
|
mov ebx,24
|
|
mul ebx
|
|
movzx ecx,word [buffer+8]
|
|
add eax,ecx
|
|
mov ebx,60
|
|
mul ebx
|
|
movzx ecx,word [buffer+10]
|
|
add eax,ecx
|
|
mov ebx,60
|
|
mul ebx
|
|
movzx ecx,word [buffer+12]
|
|
add eax,ecx
|
|
adc edx,0
|
|
ret
|
|
|
|
error_prefix db 'error: ',0
|
|
error_suffix db '.'
|
|
cr_lf db 0Dh,0Ah,0
|
|
line_number_start db ' [',0
|
|
line_data_start db ':',0Dh,0Ah,0
|
|
preprocessed_instruction_prefix db 'processed: ',0
|
|
|
|
|
|
; flat assembler interface for Linux
|
|
; Copyright (c) 1999-2022, Tomasz Grysztar.
|
|
; All rights reserved.
|
|
|
|
format ELF executable 3
|
|
entry start
|
|
|
|
segment readable executable
|
|
|
|
start:
|
|
|
|
mov [con_handle],1
|
|
mov esi,_logo
|
|
call display_string
|
|
|
|
mov [command_line],esp
|
|
mov ecx,[esp]
|
|
lea ebx,[esp+4+ecx*4+4]
|
|
mov [environment],ebx
|
|
call get_params
|
|
jc information
|
|
|
|
call init_memory
|
|
|
|
mov esi,_memory_prefix
|
|
call display_string
|
|
mov eax,[memory_end]
|
|
sub eax,[memory_start]
|
|
add eax,[additional_memory_end]
|
|
sub eax,[additional_memory]
|
|
shr eax,10
|
|
call display_number
|
|
mov esi,_memory_suffix
|
|
call display_string
|
|
|
|
mov eax,78
|
|
mov ebx,buffer
|
|
xor ecx,ecx
|
|
int 0x80
|
|
mov eax,dword [buffer]
|
|
mov ecx,1000
|
|
mul ecx
|
|
mov ebx,eax
|
|
mov eax,dword [buffer+4]
|
|
div ecx
|
|
add eax,ebx
|
|
mov [start_time],eax
|
|
|
|
and [preprocessing_done],0
|
|
call preprocessor
|
|
or [preprocessing_done],-1
|
|
call parser
|
|
call assembler
|
|
call formatter
|
|
|
|
call display_user_messages
|
|
movzx eax,[current_pass]
|
|
inc eax
|
|
call display_number
|
|
mov esi,_passes_suffix
|
|
call display_string
|
|
mov eax,78
|
|
mov ebx,buffer
|
|
xor ecx,ecx
|
|
int 0x80
|
|
mov eax,dword [buffer]
|
|
mov ecx,1000
|
|
mul ecx
|
|
mov ebx,eax
|
|
mov eax,dword [buffer+4]
|
|
div ecx
|
|
add eax,ebx
|
|
sub eax,[start_time]
|
|
jnc time_ok
|
|
add eax,3600000
|
|
time_ok:
|
|
xor edx,edx
|
|
mov ebx,100
|
|
div ebx
|
|
or eax,eax
|
|
jz display_bytes_count
|
|
xor edx,edx
|
|
mov ebx,10
|
|
div ebx
|
|
push edx
|
|
call display_number
|
|
mov dl,'.'
|
|
call display_character
|
|
pop eax
|
|
call display_number
|
|
mov esi,_seconds_suffix
|
|
call display_string
|
|
display_bytes_count:
|
|
mov eax,[written_size]
|
|
call display_number
|
|
mov esi,_bytes_suffix
|
|
call display_string
|
|
xor al,al
|
|
jmp exit_program
|
|
|
|
information:
|
|
mov esi,_usage
|
|
call display_string
|
|
mov al,1
|
|
jmp exit_program
|
|
|
|
get_params:
|
|
mov ebx,[command_line]
|
|
mov [input_file],0
|
|
mov [output_file],0
|
|
mov [symbols_file],0
|
|
mov [memory_setting],0
|
|
mov [passes_limit],100
|
|
mov ecx,[ebx]
|
|
add ebx,8
|
|
dec ecx
|
|
jz bad_params
|
|
mov [definitions_pointer],predefinitions
|
|
get_param:
|
|
mov esi,[ebx]
|
|
mov al,[esi]
|
|
cmp al,'-'
|
|
je option_param
|
|
cmp [input_file],0
|
|
jne get_output_file
|
|
mov [input_file],esi
|
|
jmp next_param
|
|
get_output_file:
|
|
cmp [output_file],0
|
|
jne bad_params
|
|
mov [output_file],esi
|
|
jmp next_param
|
|
option_param:
|
|
inc esi
|
|
lodsb
|
|
cmp al,'m'
|
|
je memory_option
|
|
cmp al,'M'
|
|
je memory_option
|
|
cmp al,'p'
|
|
je passes_option
|
|
cmp al,'P'
|
|
je passes_option
|
|
cmp al,'d'
|
|
je definition_option
|
|
cmp al,'D'
|
|
je definition_option
|
|
cmp al,'s'
|
|
je symbols_option
|
|
cmp al,'S'
|
|
je symbols_option
|
|
bad_params:
|
|
stc
|
|
ret
|
|
memory_option:
|
|
cmp byte [esi],0
|
|
jne get_memory_setting
|
|
dec ecx
|
|
jz bad_params
|
|
add ebx,4
|
|
mov esi,[ebx]
|
|
get_memory_setting:
|
|
call get_option_value
|
|
or edx,edx
|
|
jz bad_params
|
|
cmp edx,1 shl (32-10)
|
|
jae bad_params
|
|
mov [memory_setting],edx
|
|
jmp next_param
|
|
passes_option:
|
|
cmp byte [esi],0
|
|
jne get_passes_setting
|
|
dec ecx
|
|
jz bad_params
|
|
add ebx,4
|
|
mov esi,[ebx]
|
|
get_passes_setting:
|
|
call get_option_value
|
|
or edx,edx
|
|
jz bad_params
|
|
cmp edx,10000h
|
|
ja bad_params
|
|
mov [passes_limit],dx
|
|
next_param:
|
|
add ebx,4
|
|
dec ecx
|
|
jnz get_param
|
|
cmp [input_file],0
|
|
je bad_params
|
|
mov eax,[definitions_pointer]
|
|
mov byte [eax],0
|
|
mov [initial_definitions],predefinitions
|
|
clc
|
|
ret
|
|
definition_option:
|
|
cmp byte [esi],0
|
|
jne get_definition
|
|
dec ecx
|
|
jz bad_params
|
|
add ebx,4
|
|
mov esi,[ebx]
|
|
get_definition:
|
|
push edi
|
|
mov edi,[definitions_pointer]
|
|
call convert_definition_option
|
|
mov [definitions_pointer],edi
|
|
pop edi
|
|
jc bad_params
|
|
jmp next_param
|
|
symbols_option:
|
|
cmp byte [esi],0
|
|
jne get_symbols_setting
|
|
dec ecx
|
|
jz bad_params
|
|
add ebx,4
|
|
mov esi,[ebx]
|
|
get_symbols_setting:
|
|
mov [symbols_file],esi
|
|
jmp next_param
|
|
get_option_value:
|
|
xor eax,eax
|
|
mov edx,eax
|
|
get_option_digit:
|
|
lodsb
|
|
cmp al,20h
|
|
je option_value_ok
|
|
or al,al
|
|
jz option_value_ok
|
|
sub al,30h
|
|
jc invalid_option_value
|
|
cmp al,9
|
|
ja invalid_option_value
|
|
imul edx,10
|
|
jo invalid_option_value
|
|
add edx,eax
|
|
jc invalid_option_value
|
|
jmp get_option_digit
|
|
option_value_ok:
|
|
dec esi
|
|
clc
|
|
ret
|
|
invalid_option_value:
|
|
stc
|
|
ret
|
|
convert_definition_option:
|
|
mov edx,edi
|
|
cmp edi,predefinitions+1000h
|
|
jae bad_definition_option
|
|
xor al,al
|
|
stosb
|
|
copy_definition_name:
|
|
lodsb
|
|
cmp al,'='
|
|
je copy_definition_value
|
|
cmp al,20h
|
|
je bad_definition_option
|
|
or al,al
|
|
jz bad_definition_option
|
|
cmp edi,predefinitions+1000h
|
|
jae bad_definition_option
|
|
stosb
|
|
inc byte [edx]
|
|
jnz copy_definition_name
|
|
bad_definition_option:
|
|
stc
|
|
ret
|
|
copy_definition_value:
|
|
lodsb
|
|
cmp al,20h
|
|
je definition_value_end
|
|
or al,al
|
|
jz definition_value_end
|
|
cmp edi,predefinitions+1000h
|
|
jae bad_definition_option
|
|
stosb
|
|
jmp copy_definition_value
|
|
definition_value_end:
|
|
dec esi
|
|
cmp edi,predefinitions+1000h
|
|
jae bad_definition_option
|
|
xor al,al
|
|
stosb
|
|
clc
|
|
ret
|
|
|
|
include 'system.inc'
|
|
|
|
include '..\version.inc'
|
|
|
|
_copyright db 'Copyright (c) 1999-2022, Tomasz Grysztar',0xA,0
|
|
|
|
_logo db 'flat assembler version ',VERSION_STRING,0
|
|
_usage db 0xA
|
|
db 'usage: fasm <source> [output]',0xA
|
|
db 'optional settings:',0xA
|
|
db ' -m <limit> set the limit in kilobytes for the available memory',0xA
|
|
db ' -p <limit> set the maximum allowed number of passes',0xA
|
|
db ' -d <name>=<value> define symbolic variable',0xA
|
|
db ' -s <file> dump symbolic information for debugging',0xA
|
|
db 0
|
|
_memory_prefix db ' (',0
|
|
_memory_suffix db ' kilobytes memory)',0xA,0
|
|
_passes_suffix db ' passes, ',0
|
|
_seconds_suffix db ' seconds, ',0
|
|
_bytes_suffix db ' bytes.',0xA,0
|
|
|
|
include '..\errors.inc'
|
|
include '..\symbdump.inc'
|
|
include '..\preproce.inc'
|
|
include '..\parser.inc'
|
|
include '..\exprpars.inc'
|
|
include '..\assemble.inc'
|
|
include '..\exprcalc.inc'
|
|
include '..\formats.inc'
|
|
include '..\x86_64.inc'
|
|
include '..\avx.inc'
|
|
|
|
include '..\tables.inc'
|
|
include '..\messages.inc'
|
|
|
|
segment readable writeable
|
|
|
|
align 4
|
|
|
|
include '..\variable.inc'
|
|
|
|
command_line dd ?
|
|
memory_setting dd ?
|
|
definitions_pointer dd ?
|
|
environment dd ?
|
|
timestamp dq ?
|
|
start_time dd ?
|
|
con_handle dd ?
|
|
displayed_count dd ?
|
|
last_displayed db ?
|
|
character db ?
|
|
preprocessing_done db ?
|
|
|
|
predefinitions rb 1000h
|
|
buffer rb 1000h
|
|
|
|
push dword [BackgroundColour]
|
|
call _CreateSolidBrush@4 ; Create a brush for the window backgound
|
|
mov dword [BackgroundBrush], EAX
|
|
|
|
mov dword [wc.cbSize], 48 ; [EBP - 80]
|
|
mov dword [wc.style], CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW ; [EBP - 76]
|
|
mov dword [wc.lpfnWndProc], WndProc ; [EBP - 72]
|
|
mov dword [wc.cbClsExtra], NULL ; [EBP - 68]
|
|
mov dword [wc.cbWndExtra], NULL ; [EBP - 64]
|
|
mov EAX, dword [hInstance]
|
|
mov dword [wc.hInstance], EAX ; [EBP - 60]
|
|
|
|
push LR_SHARED
|
|
push NULL
|
|
push NULL
|
|
push IMAGE_ICON
|
|
push IDI_APPLICATION
|
|
push NULL
|
|
call _LoadImageA@24 ; Large program icon
|
|
mov dword [wc.hIcon], EAX ; [EBP - 56]
|
|
|
|
push LR_SHARED
|
|
push NULL
|
|
push NULL
|
|
push IMAGE_CURSOR
|
|
push IDC_ARROW
|
|
push NULL
|
|
call _LoadImageA@24 ; Cursor
|
|
mov dword [wc.hCursor], EAX ; [EBP - 52]
|
|
|
|
mov EAX, dword [BackgroundBrush]
|
|
mov dword [wc.hbrBackground], EAX ; [EBP - 48]
|
|
mov dword [wc.lpszMenuName], NULL ; [EBP - 44]
|
|
mov dword [wc.lpszClassName], ClassName ; [EBP - 40]
|
|
|
|
push LR_SHARED
|
|
push NULL
|
|
push NULL
|
|
push IMAGE_ICON
|
|
push IDI_APPLICATION
|
|
push NULL
|
|
call _LoadImageA@24 ; Small program icon
|
|
mov dword [wc.hIconSm], EAX ; [EBP - 36]
|
|
|
|
lea EAX, [wc] ; [EBP - 80]
|
|
push EAX
|
|
call _RegisterClassExA@4
|
|
|
|
push SM_CXFULLSCREEN
|
|
call _GetSystemMetrics@4 ; Get the current screen width
|
|
mov dword [Screen.Width], EAX ; [EBP - 104]
|
|
|
|
push SM_CYFULLSCREEN
|
|
call _GetSystemMetrics@4 ; Get the current screen height
|
|
mov dword [Screen.Height], EAX ; [EBP - 100]
|
|
|
|
mov dword [ClientArea.left], 0 ; [EBP - 96]
|
|
mov dword [ClientArea.top], 0 ; [EBP - 92]
|
|
mov dword [ClientArea.right], WindowWidth ; [EBP - 88]
|
|
mov dword [ClientArea.bottom], WindowHeight ; [EBP - 84]
|
|
|
|
push WS_EX_COMPOSITED ; Extended style
|
|
push NULL
|
|
push WS_OVERLAPPEDWINDOW ; Style
|
|
lea EAX, [ClientArea] ; [EBP - 96]
|
|
push EAX
|
|
call _AdjustWindowRectEx@16 ; Get window size for the desired client size
|
|
; Size is returned in ClientArea
|
|
mov EAX, dword [ClientArea.top] ; [EBP - 92]
|
|
sub dword [ClientArea.bottom], EAX ; New Height = ClientArea.bottom - ClientArea.top
|
|
|
|
mov EAX, dword [ClientArea.left] ; [EBP - 96]
|
|
sub dword [ClientArea.right], EAX ; New Width = ClientArea.right - ClientArea.left
|
|
|
|
push NULL
|
|
push dword [hInstance]
|
|
push NULL
|
|
push NULL
|
|
|
|
push dword [ClientArea.bottom] ; Height. [EBP - 84]
|
|
push dword [ClientArea.right] ; Width. [EBP - 88]
|
|
|
|
xor ECX, ECX
|
|
mov EAX, dword [Screen.Height] ; [EBP - 100]
|
|
sub EAX, dword [ClientArea.bottom] ; Corrected window height. [EBP - 84]
|
|
cmovs EAX, ECX ; Clamp to 0 (top) if negative
|
|
shr EAX, 1 ; EAX = (Screen.Height - window height) / 2
|
|
push EAX ; Y position, now centred
|
|
|
|
mov EAX, dword [Screen.Width] ; [EBP - 104]
|
|
sub EAX, dword [ClientArea.right] ; Corrected window width. [EBP - 88]
|
|
cmovs EAX, ECX ; Clamp to 0 (left) if negative
|
|
shr EAX, 1 ; EAX = (Screen.Width - window width) / 2
|
|
push EAX ; X position, now centred
|
|
|
|
push WS_OVERLAPPEDWINDOW
|
|
push WindowName
|
|
push ClassName
|
|
push WS_EX_COMPOSITED
|
|
call _CreateWindowExA@48
|
|
mov dword [hWnd], EAX ; [EBP - 4]
|
|
|
|
push SW_SHOWNORMAL
|
|
push dword [hWnd] ; [EBP - 4]
|
|
call _ShowWindow@8
|
|
|
|
push dword [hWnd] ; [EBP - 4]
|
|
call _UpdateWindow@4
|
|
|
|
.MessageLoop:
|
|
push NULL
|
|
push NULL
|
|
push NULL
|
|
lea EAX, [msg] ; [EBP - 32]
|
|
push EAX
|
|
call _GetMessageA@16
|
|
cmp EAX, 0
|
|
je .Done
|
|
|
|
lea EAX, [msg] ; [EBP - 32]
|
|
push EAX
|
|
push dword [hWnd] ; [EBP - 4]
|
|
call _IsDialogMessageA@8 ; For keyboard strokes
|
|
cmp EAX, 0
|
|
jne .MessageLoop ; Skip TranslateMessage and DispatchMessage
|
|
|
|
lea EAX, [msg] ; [EBP - 32]
|
|
push EAX
|
|
call _TranslateMessage@4
|
|
|
|
lea EAX, [msg] ; [EBP - 32]
|
|
push EAX
|
|
call _DispatchMessageA@4
|
|
jmp .MessageLoop
|
|
|
|
.Done:
|
|
xor EAX, EAX
|
|
mov ESP, EBP ; Remove the stack frame
|
|
pop EBP
|
|
ret
|
|
|
|
WndProc:
|
|
push EBP ; Set up a stack frame
|
|
mov EBP, ESP
|
|
sub ESP, 68 ; 68 bytes for local variables
|
|
|
|
%define hWnd EBP + 8 ; Location of the 4 passed parameters from
|
|
%define uMsg EBP + 12 ; the calling function
|
|
%define wParam EBP + 16 ; We can now access these parameters by name
|
|
%define lParam EBP + 20
|
|
|
|
%define ps EBP - 68 ; PAINTSTRUCT structure. 64 bytes
|
|
%define ps.hdc EBP - 68
|
|
%define ps.fErase EBP - 64
|
|
%define ps.rcPaint.left EBP - 60
|
|
%define ps.rcPaint.top EBP - 56
|
|
%define ps.rcPaint.right EBP - 52
|
|
%define ps.rcPaint.bottom EBP - 48
|
|
%define ps.Restore EBP - 44
|
|
%define ps.fIncUpdate EBP - 40
|
|
%define ps.rgbReserved EBP - 36
|
|
|
|
%define hdc EBP - 4
|
|
|
|
cmp dword [uMsg], WM_CLOSE ; [EBP + 12]
|
|
je WMCLOSE
|
|
|
|
cmp dword [uMsg], WM_COMMAND ; [EBP + 12]
|
|
je WMCOMMAND
|
|
|
|
cmp dword [uMsg], WM_CREATE ; [EBP + 12]
|
|
je WMCREATE
|
|
|
|
cmp dword [uMsg], WM_CTLCOLOREDIT ; [EBP + 12]
|
|
je WMCTLCOLOREDIT
|
|
|
|
cmp dword [uMsg], WM_CTLCOLORSTATIC ; [EBP + 12]
|
|
je WMCTLCOLORSTATIC
|
|
|
|
cmp dword [uMsg], WM_DESTROY ; [EBP + 12]
|
|
je WMDESTROY
|
|
|
|
cmp dword [uMsg], WM_PAINT ; [EBP + 12]
|
|
je WMPAINT
|
|
|
|
DefaultMessage:
|
|
push dword [lParam] ; [EBP + 20]
|
|
push dword [wParam] ; [EBP + 16]
|
|
push dword [uMsg] ; [EBP + 12]
|
|
push dword [hWnd] ; [EBP + 8]
|
|
call _DefWindowProcA@16
|
|
jmp Return
|
|
|
|
WMCLOSE:
|
|
push MB_YESNO | MB_DEFBUTTON2
|
|
push WindowName
|
|
push ExitText
|
|
push dword [hWnd] ; [EBP + 8]
|
|
call _MessageBoxA@16
|
|
|
|
cmp EAX, IDNO
|
|
je Return.WM_Processed
|
|
|
|
push dword [hWnd] ; [EBP + 8]
|
|
call _DestroyWindow@4 ; Send a WM_DESTROY message
|
|
jmp Return.WM_Processed
|
|
|
|
WMCOMMAND:
|
|
mov EAX, dword [wParam] ; EAX = ID. [EBP + 16]
|
|
|
|
cmp AX, Static1ID
|
|
je .Static1
|
|
|
|
cmp AX, Static2ID
|
|
je .Static2
|
|
|
|
jmp Return.WM_Processed
|
|
|
|
.Static1:
|
|
mov EAX, dword [Static1Colour]
|
|
mov EBX, dword [Static1ColourA]
|
|
mov dword [Static1Colour], EBX
|
|
mov dword [Static1ColourA], EAX ; Swap colours
|
|
|
|
push TRUE
|
|
push NULL
|
|
push dword [lParam] ; Static1 handle. [EBP + 20]
|
|
call _InvalidateRect@12 ; Redraw control
|
|
jmp Return.WM_Processed
|
|
|
|
.Static2:
|
|
mov EAX, dword [Static2Colour]
|
|
mov EBX, dword [Static2ColourA]
|
|
mov dword [Static2Colour], EBX
|
|
mov dword [Static2ColourA], EAX ; Swap colours
|
|
|
|
push TRUE
|
|
push NULL
|
|
push dword [lParam] ; Static2 handle. [EBP + 20]
|
|
call _InvalidateRect@12 ; Redraw control
|
|
jmp Return.WM_Processed
|
|
|
|
WMCREATE:
|
|
push NULL
|
|
push dword [hInstance]
|
|
push Static1ID
|
|
push dword [hWnd] ; [EBP + 8]
|
|
push 20 ; Height
|
|
push 400 ; Width
|
|
push 10 ; Y
|
|
push 120 ; X
|
|
push WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTER
|
|
push Text1 ; Default text
|
|
push StaticClass
|
|
push NULL
|
|
call _CreateWindowExA@48
|
|
mov dword [Static1], EAX
|
|
|
|
push NULL
|
|
push dword [hInstance]
|
|
push Static2ID
|
|
push dword [hWnd] ; [EBP + 8]
|
|
push 20 ; Height
|
|
push 400 ; Width
|
|
push 40 ; Y
|
|
push 120 ; X
|
|
push WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTER
|
|
push Text2 ; Default text
|
|
push StaticClass
|
|
push NULL
|
|
call _CreateWindowExA@48
|
|
mov dword [Static2], EAX
|
|
|
|
push NULL
|
|
push dword [hInstance]
|
|
push Edit1ID
|
|
push dword [hWnd] ; [EBP + 8]
|
|
push 20 ; Height
|
|
push 400 ; Width
|
|
push 70 ; Y
|
|
push 120 ; X
|
|
push WS_CHILD | WS_VISIBLE | ES_CENTER | WS_TABSTOP | ES_AUTOHSCROLL
|
|
push Text1 ; Default text
|
|
push EditClass
|
|
push NULL
|
|
call _CreateWindowExA@48
|
|
mov dword [Edit1], EAX
|
|
|
|
push NULL
|
|
push dword [hInstance]
|
|
push Edit2ID
|
|
push dword [hWnd] ; [EBP + 8]
|
|
push 20 ; Height
|
|
push 400 ; Width
|
|
push 100 ; Y
|
|
push 120 ; X
|
|
push WS_CHILD | WS_VISIBLE | ES_CENTER | WS_TABSTOP | ES_AUTOHSCROLL
|
|
push Text2 ; Default text
|
|
push EditClass
|
|
push NULL
|
|
call _CreateWindowExA@48
|
|
mov dword [Edit2], EAX
|
|
|
|
lea EAX, [SegoeUI]
|
|
push EAX
|
|
push DEFAULT_PITCH
|
|
push PROOF_QUALITY
|
|
push CLIP_DEFAULT_PRECIS
|
|
push OUT_DEFAULT_PRECIS
|
|
push ANSI_CHARSET
|
|
push NULL
|
|
push NULL
|
|
push NULL
|
|
push 400 ; Weight
|
|
push NULL
|
|
push NULL
|
|
push NULL
|
|
push 20 ; Size
|
|
call _CreateFontA@56
|
|
mov dword [Font], EAX
|
|
|
|
push FALSE
|
|
push dword [Font]
|
|
push WM_SETFONT
|
|
push dword [Static1]
|
|
call _SendMessageA@16 ; Set Static1 font
|
|
|
|
push FALSE
|
|
push dword [Font]
|
|
push WM_SETFONT
|
|
push dword [Static2]
|
|
call _SendMessageA@16 ; Set Static2 font
|
|
|
|
push FALSE
|
|
push dword [Font]
|
|
push WM_SETFONT
|
|
push dword [Edit1]
|
|
call _SendMessageA@16 ; Set Edit1 font
|
|
|
|
push FALSE
|
|
push dword [Font]
|
|
push WM_SETFONT
|
|
push dword [Edit2]
|
|
call _SendMessageA@16 ; Set Edit2 font
|
|
|
|
jmp Return.WM_Processed
|
|
|
|
WMCTLCOLOREDIT: ; For colouring edit controls
|
|
push dword [lParam] ; [EBP + 20]
|
|
call _GetDlgCtrlID@4 ; EAX = ID
|
|
|
|
cmp EAX, Edit1ID
|
|
je .Edit1
|
|
|
|
cmp EAX, Edit2ID
|
|
je .Edit2
|
|
|
|
.Default:
|
|
push NULL_BRUSH
|
|
call _GetStockObject@4 ; Return a brush
|
|
jmp Return
|
|
|
|
.Edit1:
|
|
push dword [Edit1TextColour]
|
|
push dword [wParam] ; [EBP + 16]
|
|
call _SetTextColor@8
|
|
|
|
push OPAQUE
|
|
push dword [wParam] ; [EBP + 16]
|
|
call _SetBkMode@8
|
|
|
|
push dword [Edit1BackColour]
|
|
push dword [wParam] ; [EBP + 16]
|
|
call _SetBkColor@8
|
|
|
|
push NULL_BRUSH
|
|
call _GetStockObject@4 ; Return a brush
|
|
jmp Return
|
|
|
|
.Edit2:
|
|
push dword [Edit2TextColour]
|
|
push dword [wParam] ; [EBP + 16]
|
|
call _SetTextColor@8
|
|
|
|
push OPAQUE
|
|
push dword [wParam] ; [EBP + 16]
|
|
call _SetBkMode@8
|
|
|
|
push dword [Edit2BackColour]
|
|
push dword [wParam] ; [EBP + 16]
|
|
call _SetBkColor@8
|
|
|
|
push NULL_BRUSH
|
|
call _GetStockObject@4 ; Return a brush
|
|
jmp Return
|
|
|
|
WMCTLCOLORSTATIC: ; For colouring static controls
|
|
push dword [lParam] ; [EBP + 20]
|
|
call _GetDlgCtrlID@4 ; EAX = ID
|
|
|
|
cmp EAX, Static1ID
|
|
je .Static1
|
|
|
|
cmp EAX, Static2ID
|
|
je .Static2
|
|
|
|
.Default:
|
|
push NULL_BRUSH
|
|
call _GetStockObject@4 ; Return a brush
|
|
jmp Return
|
|
|
|
.Static1:
|
|
push dword [Static1Colour]
|
|
push dword [wParam] ; [EBP + 16]
|
|
call _SetTextColor@8
|
|
|
|
push OPAQUE
|
|
push dword [wParam] ; [EBP + 16]
|
|
call _SetBkMode@8
|
|
|
|
push 0604060h
|
|
push dword [wParam] ; [EBP + 16]
|
|
call _SetBkColor@8
|
|
|
|
push NULL_BRUSH
|
|
call _GetStockObject@4 ; Return a brush
|
|
jmp Return
|
|
|
|
.Static2:
|
|
push dword [Static2Colour]
|
|
push dword [wParam] ; [EBP + 16]
|
|
call _SetTextColor@8
|
|
|
|
push OPAQUE
|
|
push dword [wParam] ; [EBP + 16]
|
|
call _SetBkMode@8
|
|
|
|
push 0005000h
|
|
push dword [wParam] ; [EBP + 16]
|
|
call _SetBkColor@8
|
|
|
|
push GRAY_BRUSH
|
|
call _GetStockObject@4 ; Return a brush
|
|
jmp Return
|
|
|
|
WMDESTROY:
|
|
push dword [BackgroundBrush]
|
|
call _DeleteObject@4
|
|
|
|
push dword [Font]
|
|
call _DeleteObject@4
|
|
|
|
push NULL
|
|
call _PostQuitMessage@4
|
|
jmp Return.WM_Processed
|
|
|
|
WMPAINT:
|
|
lea EAX, [ps] ; Starting address of PAINTSTRUCT. [EBP - 68]
|
|
push EAX
|
|
push dword [hWnd] ; [EBP + 8]
|
|
call _BeginPaint@8
|
|
mov dword [hdc], EAX
|
|
|
|
push BLACKNESS ; Operation
|
|
push 0 ; Source Y
|
|
push 0 ; Source X
|
|
push dword [hdc] ; Source device context
|
|
push 20 ; Height
|
|
push 400 ; Width
|
|
push 130 ; Destination Y
|
|
push 120 ; Destination X
|
|
push dword [hdc] ; Destination device context
|
|
call _BitBlt@36 ; Blit a black rectangle
|
|
|
|
lea EAX, [ps] ; [EBP - 68]
|
|
push EAX
|
|
push dword [hWnd] ; [EBP + 8]
|
|
call _EndPaint@8
|
|
|
|
Return.WM_Processed:
|
|
xor EAX, EAX ; WM_ has been processed, return 0
|
|
|
|
Return:
|
|
mov ESP, EBP ; Remove the stack frame
|
|
pop EBP
|
|
ret 16 ; Pop 4 parameters off the stack and return
|
|
|
|
.586
|
|
.MODEL flat, C
|
|
|
|
printf PROTO, pString : PTR BYTE, args : VARARG
|
|
scanf PROTO, pFormat : PTR BYTE, args : VARARG
|
|
|
|
.data
|
|
|
|
prime_numbers dword 2, 3, 5, 7, 11, 13, 17, 19, 23, 29
|
|
number dword 0
|
|
|
|
string byte "Please enter number: ",0
|
|
printf_string byte "%d", 0dh, 0ah, 0
|
|
scanf_string byte "%d", 0
|
|
|
|
.code
|
|
asmMain PROC
|
|
|
|
INVOKE printf, ADDR string
|
|
invoke scanf, addr scanf_string, addr number
|
|
|
|
mov esi, offset prime_numbers
|
|
mov ecx, lengthof prime_numbers
|
|
L1 :
|
|
mov eax, number
|
|
mov edx, 0
|
|
mov ebx, [esi]
|
|
div ebx
|
|
cmp edx, 0
|
|
je not_prime_number
|
|
add esi, 4
|
|
loop L1
|
|
|
|
prime_number :
|
|
mov eax, 1
|
|
INVOKE printf, ADDR printf_string, eax
|
|
jmp L2
|
|
|
|
not_prime_number :
|
|
mov eax, 0
|
|
INVOKE printf, ADDR printf_string, eax
|
|
|
|
L2 :
|
|
|
|
ret
|
|
asmMain ENDP
|
|
END
|
|
|
|
TITLE CHKDSK - MS-DOS Disk consistancy checker
|
|
|
|
; CHKDSK Version 2.30
|
|
; Verifies and repairs MS-DOS disk directory.
|
|
|
|
|
|
; To build CHKDSK you need three modules:
|
|
; CHKDSK CHKPROC CHKMES
|
|
; They should be linked the that order as well.
|
|
|
|
|
|
; REVISION HISTORY
|
|
|
|
;REV 1.1
|
|
; 05/21/82 Added rev number
|
|
|
|
;REV 1.5
|
|
; Mod by NANCYP to report on extents
|
|
; Mod by AARONR to report volume ID
|
|
|
|
;REV 2.0
|
|
; Total rewrite for directories
|
|
|
|
;REV 2.1
|
|
; Added ^C and INT 24H handlers
|
|
|
|
;REV 2.2
|
|
; INTERNATIONAL support
|
|
|
|
;REV 2.3
|
|
; Split into two modules to allow assembly on a PC
|
|
; CHKDSK and CHKPROC
|
|
|
|
FALSE EQU 0
|
|
TRUE EQU NOT FALSE
|
|
|
|
DRVCHAR EQU ":"
|
|
|
|
;The following defines the ranges of DOS version numbers for which this CHKDSK
|
|
; is good
|
|
|
|
DOSVER_LOW EQU 0136H ;1.54 in hex
|
|
DOSVER_HIGH EQU 020BH ;2.11 in hex
|
|
|
|
|
|
INCLUDE DOSSYM.ASM
|
|
|
|
FCB EQU 5CH
|
|
|
|
;Drive parameter block from DOS header
|
|
|
|
SUBTTL Segments used in load order
|
|
|
|
CODE SEGMENT PUBLIC
|
|
CODE ENDS
|
|
|
|
CONST SEGMENT PUBLIC BYTE
|
|
CONST ENDS
|
|
|
|
DATA SEGMENT PUBLIC WORD
|
|
DATA ENDS
|
|
|
|
DG GROUP CODE,CONST,DATA
|
|
|
|
SUBTTL Initialized Data
|
|
PAGE
|
|
CONST SEGMENT PUBLIC BYTE
|
|
|
|
PUBLIC HECODE,SWITCHAR,NOISY,DOFIX,CONBUF,ORPHCNT,ORPHSIZ,DOFIX
|
|
PUBLIC HIDCNT,HIDSIZ,DIRCNT,DIRSIZ,FILCNT,FILSIZ,BADSIZ,LCLUS
|
|
PUBLIC DOTENT,HAVFIX,SECONDPASS,NUL,ALLFILE,PARSTR,ERRSUB,LCLUS
|
|
PUBLIC DIRTYFAT,BADSIZ,DDOTENT,CROSSCNT,ORPHFCB,ORPHEXT,ALLDRV
|
|
PUBLIC FRAGMENT,USERDIR,DIRBUF,USERDIR,FIXMFLG,DOTMES,DIRCHAR
|
|
|
|
EXTRN IDMES1:BYTE,IDPOST:BYTE,VNAME:BYTE,MONTAB:BYTE
|
|
EXTRN TCHAR:BYTE,BADREAD_PRE:BYTE,BADREAD_POST:BYTE
|
|
EXTRN CRLF:BYTE,BADVER:BYTE,BADSUBDIR:BYTE,CENTRY:BYTE
|
|
EXTRN BADDRV:BYTE,BADCD:BYTE,BADRDMES:BYTE,OPNERR:BYTE
|
|
EXTRN CONTAINS:BYTE,EXTENTS:BYTE,NOEXTENTS:BYTE
|
|
EXTRN BADDRVM:BYTE,BADDRVM2:BYTE,BADIDBYT:BYTE
|
|
|
|
|
|
DIRBUF LABEL BYTE ;Entry buffer for searches
|
|
VOLID DB -1,0,0,0,0,0,8 ;Volume ID FCB
|
|
VOLNAM DB 0,"???????????"
|
|
DB 25 DUP(0)
|
|
|
|
ALLFILE DB -1,0,0,0,0,0,1EH ;Extended FCB
|
|
ALLDRV DB 0,"???????????"
|
|
DB 25 DUP (?)
|
|
|
|
ORPHFCB DB 0,"FILE0000"
|
|
ORPHEXT DB "CHK"
|
|
DB 25 DUP (?)
|
|
|
|
|
|
;Non-message data
|
|
|
|
SWITCHAR DB "-"
|
|
ROOTSTR LABEL BYTE
|
|
DIRCHAR DB "/"
|
|
NUL DB 0
|
|
PARSTR DB "..",0
|
|
DOTMES DB ".",0
|
|
DOTENT DB ". "
|
|
DDOTENT DB ".. "
|
|
HECODE DB ?
|
|
FIXMFLG DB 0 ;Flag for printing fixmes
|
|
ERRSUB DW 0 ;Flag for bad subdir error
|
|
FRAGMENT DB 0 ;Flag for extent processing
|
|
DIRTYFAT DB 0 ;Dirty flag for FAT
|
|
DIRCNT DW 0 ;# directories
|
|
DIRSIZ DW 0 ;# alloc units in directories
|
|
FILCNT DW 0 ;# reg files
|
|
FILSIZ DW 0 ;# alloc units in reg files
|
|
HIDCNT DW 0 ;# hidden files
|
|
HIDSIZ DW 0 ;# alloc units in hidden files
|
|
BADSIZ DW 0 ;# alloc units in bad sectors
|
|
ORPHCNT DW 0 ;# orphan files made
|
|
ORPHSIZ DW 0 ;# alloc units in orphan files
|
|
LCLUS DW 0 ;# alloc units in lost clusters
|
|
DISPFLG DB 0 ;used by number routines
|
|
CROSSCNT DW 0 ;# crosslinked files (first pass)
|
|
SECONDPASS DB 0 ;Pass flag
|
|
HAVFIX DB 0 ;non zero if any fixes
|
|
DOFIX DB 0 ;flag for F switch
|
|
NOISY DB 0 ;flag for V switch
|
|
USERDIR DB "/",0 ;Users current dir for drive
|
|
DB (DIRSTRLEN-1) DUP (?)
|
|
CONBUF DB 15,0 ;Input buffer
|
|
DB 15 DUP (?)
|
|
|
|
CONST ENDS
|
|
|
|
SUBTTL Un-initialized Data
|
|
PAGE
|
|
DATA SEGMENT PUBLIC WORD
|
|
|
|
PUBLIC ZEROTRUNC,NAMBUF,MCLUS,THISDPB,STACKLIM,ERRCNT
|
|
PUBLIC SRFCBPT,ISCROSS,CSIZE,DSIZE,SSIZE,FAT,FATMAP
|
|
PUBLIC HARDCH,CONTCH,USERDEV,SECBUF,DOTSNOGOOD
|
|
|
|
HARDCH DD ? ;Pointer to real INT 24 handler
|
|
CONTCH DD ? ;Pointer to real INT 23 handler
|
|
THISDPB DD ? ;Pointer to drive DPB
|
|
USERDEV DB ? ;Users current device
|
|
CSIZE DB ? ;Sectors per cluster
|
|
SSIZE DW ? ;bytes per sector
|
|
DSIZE DW ? ;# alloc units on disk
|
|
MCLUS DW ? ;DSIZE + 1
|
|
NAMBUF DB 14 DUP (?) ;Buffer
|
|
DOTSNOGOOD DB ? ;. or .. error flag
|
|
ZEROTRUNC DB ? ;Trimming flag
|
|
ISCROSS DB ? ;Crosslink flag
|
|
OLDCLUS DW ?
|
|
SRFCBPT DW ?
|
|
FATMAP DW OFFSET DG:FAT ;Offset of FATMAP table
|
|
SECBUF DW ? ;Offset of sector buffer
|
|
ERRCNT DB ? ;Used by FATread and write
|
|
STACKLIM DW ? ;Stack growth limit
|
|
|
|
INTERNATVARS internat_block <>
|
|
DB (internat_block_max - ($ - INTERNATVARS)) DUP (?)
|
|
|
|
FAT LABEL WORD
|
|
DATA ENDS
|
|
|
|
|
|
SUBTTL Start of CHKDSK
|
|
|
|
CODE SEGMENT PUBLIC
|
|
ASSUME CS:DG,DS:DG,ES:DG,SS:DG
|
|
|
|
PUBLIC SUBERRP,DOTCOMBMES,FIGREC,FCB_TO_ASCZ,PRTCHR,EPRINT
|
|
PUBLIC PRINT,DOCRLF,DISP16BITS,DISP32BITS,DISPCLUS,CHECKFILES
|
|
|
|
EXTRN RDSKERR:NEAR,SETSWITCH:NEAR,PROMPTYN:NEAR,REPORT:NEAR
|
|
EXTRN PRINTCURRDIRERR:NEAR,PRINTTHISEL2:NEAR,CHECKERR:NEAR
|
|
EXTRN INT_23:NEAR,INT_24:NEAR,FINDCHAIN:NEAR,DONE:NEAR,AMDONE:NEAR
|
|
EXTRN FATAL:NEAR,DIRPROC:NEAR,CHKMAP:NEAR,CHKCROSS:NEAR,UNPACK:NEAR
|
|
|
|
ORG 100H
|
|
|
|
CHKDSK:
|
|
JMP SHORT CHSTRT
|
|
|
|
HEADER DB "Ver 2.30"
|
|
|
|
CHSTRT:
|
|
|
|
;Code to print header.
|
|
; PUSH AX
|
|
; MOV DX,OFFSET DG:HEADER
|
|
; CALL PRINT
|
|
; POP AX
|
|
|
|
PUSH AX ;Save DRIVE validity info
|
|
MOV AH,GET_VERSION
|
|
INT 21H
|
|
XCHG AH,AL ;Turn it around to AH.AL
|
|
CMP AX,DOSVER_LOW
|
|
JB GOTBADDOS
|
|
CMP AX,DOSVER_HIGH
|
|
JBE OKDOS
|
|
GOTBADDOS:
|
|
MOV DX,OFFSET DG:BADVER
|
|
JMP CERROR
|
|
|
|
OKDOS:
|
|
POP AX ;Get back drive info
|
|
MOV BX,0FFF0H
|
|
MOV DX,SP
|
|
CMP DX,BX
|
|
JAE STACKOK ;Lots of stack
|
|
MOV DX,DS:[2] ;High break
|
|
MOV CX,CS
|
|
SUB DX,CX
|
|
CMP DX,0FFFH
|
|
JAE SETSTACK ;Lots to grab
|
|
MOV CX,4 ;Suck up more stack (blast command)
|
|
SHL DX,CL
|
|
MOV BX,DX
|
|
SETSTACK:
|
|
CLI
|
|
MOV SP,BX
|
|
STI
|
|
STACKOK:
|
|
PUSH AX
|
|
MOV AH,DISK_RESET ;Flush everything, and invalidate
|
|
INT 21H
|
|
POP AX
|
|
CMP AL,0FFH ;Illegal drive specifier?
|
|
JNZ FILECHK ;No -- check for filename
|
|
|
|
DRVERR:
|
|
MOV DX,OFFSET DG:BADDRV
|
|
CERROR:
|
|
PUSH CS ;Make sure DS is OK
|
|
POP DS
|
|
CALL PRINT ;Print error message
|
|
INT 20H
|
|
|
|
CERROR2:
|
|
PUSH DX
|
|
CALL DONE ;Reset users disk
|
|
POP DX
|
|
JMP SHORT CERROR
|
|
|
|
FILECHK:
|
|
MOV AX,(CHAR_OPER SHL 8)
|
|
INT 21H
|
|
MOV [SWITCHAR],DL
|
|
CMP DL,"/"
|
|
JNZ SLASHOK
|
|
MOV [DIRCHAR],"\"
|
|
MOV [USERDIR],"\"
|
|
SLASHOK:
|
|
CMP DS:(BYTE PTR FCB+1)," " ;Filename specified?
|
|
JZ DRVCHK ;No -- get the correct drive
|
|
MOV AL,[SWITCHAR]
|
|
CMP DS:(BYTE PTR FCB+1),AL ;Filename specified?
|
|
JZ DRVCHK ;No -- get the correct drive
|
|
MOV BYTE PTR [FRAGMENT],1 ;Set flag to perform fragment
|
|
;check on specified files
|
|
DRVCHK:
|
|
CALL SETSWITCH ;Look for switches
|
|
MOV AH,GET_DEFAULT_DRIVE ;Get current drive
|
|
INT 21H
|
|
MOV [USERDEV],AL ;Save for later
|
|
MOV AH,AL
|
|
INC AH ;A = 1
|
|
MOV BH,DS:(BYTE PTR FCB) ;See if drive specified
|
|
OR BH,BH
|
|
JZ SETDSK
|
|
MOV AL,BH
|
|
MOV AH,AL
|
|
DEC AL ;A = 0
|
|
SETDSK:
|
|
MOV [ALLDRV],AH ;Target drive
|
|
MOV [VOLNAM],AH ;A = 1
|
|
MOV [ORPHFCB],AH ;A = 1
|
|
ADD [BADDRVM],AL ;A = 0
|
|
ADD [BADDRVM2],AL ;A = 0
|
|
MOV DL,AH ;A = 1
|
|
MOV AH,GET_DPB ;Get the DPB
|
|
INT 21H
|
|
ASSUME DS:NOTHING
|
|
CMP AL,-1
|
|
JNZ DRVISOK ;Bad drive (should always be ok)
|
|
MOV DX,OFFSET DG:BADDRV
|
|
CERROR2J: JMP CERROR2
|
|
|
|
DRVISOK:
|
|
DEC DL ;A = 0
|
|
MOV AH,SET_DEFAULT_DRIVE ;Set Target
|
|
INT 21H
|
|
CMP [BX.dpb_current_dir],0
|
|
JZ CURRISROOT ;Save users current dir for target
|
|
MOV SI,BX
|
|
ADD SI,dpb_dir_text
|
|
MOV DI,OFFSET DG:USERDIR + 1
|
|
SETDIRLP:
|
|
LODSB
|
|
STOSB
|
|
OR AL,AL
|
|
JZ CURRISROOT
|
|
JMP SHORT SETDIRLP
|
|
CURRISROOT:
|
|
MOV WORD PTR [THISDPB+2],DS
|
|
PUSH CS
|
|
POP DS
|
|
ASSUME DS:DG
|
|
MOV WORD PTR [THISDPB],BX
|
|
MOV AX,(GET_INTERRUPT_VECTOR SHL 8) OR 23H
|
|
INT 21H
|
|
MOV WORD PTR [CONTCH],BX
|
|
MOV WORD PTR [CONTCH+2],ES
|
|
MOV AX,(SET_INTERRUPT_VECTOR SHL 8) OR 23H
|
|
MOV DX,OFFSET DG:INT_23
|
|
INT 21H
|
|
MOV AX,(GET_INTERRUPT_VECTOR SHL 8) OR 24H
|
|
INT 21H
|
|
MOV WORD PTR [HARDCH],BX
|
|
MOV WORD PTR [HARDCH+2],ES
|
|
MOV AX,(SET_INTERRUPT_VECTOR SHL 8) OR 24H
|
|
MOV DX,OFFSET DG:INT_24
|
|
INT 21H
|
|
PUSH CS
|
|
POP ES
|
|
MOV DX,OFFSET DG:ROOTSTR
|
|
MOV AH,CHDIR ;Start at root
|
|
INT 21H
|
|
MOV DX,OFFSET DG:BADCD
|
|
JC CERROR2J ;Couldn't get there
|
|
MOV DX,OFFSET DG:FAT ;Scratch space
|
|
MOV AH,SET_DMA
|
|
INT 21H
|
|
MOV DX,OFFSET DG:VOLID ;Look for VOL ID
|
|
MOV AH,DIR_SEARCH_FIRST
|
|
INT 21H
|
|
CMP AL,-1
|
|
JZ NOTVOLID
|
|
CALL PRINTID ;Have a VOL ID
|
|
NOTVOLID:
|
|
LDS BX,[THISDPB]
|
|
ASSUME DS:NOTHING
|
|
MOV AX,[BX.dpb_sector_size]
|
|
MOV [SSIZE],AX ;Sector size in bytes
|
|
MOV AL,[BX.dpb_cluster_mask]
|
|
INC AL
|
|
MOV [CSIZE],AL ;Sectros per cluster
|
|
MOV AX,[BX.dpb_max_cluster]
|
|
MOV [MCLUS],AX ;Bound for FAT searching
|
|
DEC AX
|
|
MOV [DSIZE],AX ;Total data clusters on disk
|
|
MOV AL,[BX.dpb_FAT_size] ;Sectors for one fat
|
|
XOR AH,AH
|
|
MOV CX,AX
|
|
MUL [SSIZE] ;Bytes for FAT
|
|
ADD [FATMAP],AX ;Allocate FAT space
|
|
MOV AX,[FATMAP]
|
|
ADD AX,[MCLUS]
|
|
ADD AX,2 ;Insurance
|
|
MOV [SECBUF],AX ;Allocate FATMAP space
|
|
ADD AX,[SSIZE]
|
|
ADD AX,20 ;Insurance
|
|
MOV [STACKLIM],AX ;Limit on recursion
|
|
MOV DI,CX
|
|
MOV CL,[BX.dpb_FAT_count] ;Number of FATs
|
|
MOV DX,[BX.dpb_first_FAT] ;First sector of FAT
|
|
PUSH CS
|
|
POP DS
|
|
ASSUME DS:DG
|
|
MOV BX,OFFSET DG:FAT
|
|
MOV AL,[ALLDRV]
|
|
DEC AL
|
|
MOV AH,'1'
|
|
RDLOOP:
|
|
XCHG CX,DI
|
|
PUSH DX
|
|
PUSH CX
|
|
PUSH DI
|
|
PUSH AX
|
|
INT 25H ;Read in the FAT
|
|
MOV [HECODE],AL
|
|
POP AX ;Flags
|
|
JNC RDOK
|
|
MOV DX,OFFSET DG:BADREAD_PRE ;Barfed
|
|
CALL PRINT
|
|
POP AX
|
|
PUSH AX
|
|
MOV DL,AH
|
|
CALL PRTCHR
|
|
MOV DX,OFFSET DG:BADREAD_POST
|
|
CALL PRINT
|
|
POP AX
|
|
POP CX
|
|
POP DI
|
|
POP DX
|
|
INC AH
|
|
ADD DX,DI
|
|
LOOP RDLOOP ;Try next FAT
|
|
CALL RDSKERR
|
|
JNZ NORETRY1
|
|
JMP NOTVOLID
|
|
NORETRY1:
|
|
MOV BX,OFFSET DG:BADRDMES
|
|
JMP FATAL ;Couldn't read any FAT, BARF
|
|
|
|
RDOK:
|
|
POP AX ;Clean up
|
|
POP AX
|
|
POP AX
|
|
POP AX
|
|
MOV SI,OFFSET DG:FAT
|
|
LODSB ;Check FAT ID byte
|
|
CMP AL,0F8H
|
|
JAE IDOK
|
|
MOV DX,OFFSET DG:BADIDBYT ;FAT ID bad
|
|
CALL PROMPTYN ;Ask user
|
|
JZ IDOK
|
|
JMP ALLDONE ;User said stop
|
|
IDOK:
|
|
MOV DI,[FATMAP]
|
|
MOV CX,[MCLUS]
|
|
INC CX
|
|
XOR AL,AL
|
|
REP STOSB ;Initialize FATMAP to all free
|
|
MOV DX,OFFSET DG:DIRBUF ;FOR ALL SEARCHING
|
|
MOV AH,SET_DMA
|
|
INT 21H
|
|
XOR AX,AX
|
|
PUSH AX ;I am root
|
|
PUSH AX ;Parent is root
|
|
CALL DIRPROC
|
|
CALL CHKMAP ;Look for badsectors, orphans
|
|
CALL CHKCROSS ;Check for second pass
|
|
CALL DOCRLF
|
|
CALL REPORT
|
|
|
|
ALLDONE:
|
|
CALL AMDONE
|
|
INT 20H ;Fini
|
|
|
|
|
|
ASSUME DS:DG
|
|
|
|
SUBTTL Check for extents in specified files
|
|
PAGE
|
|
CHECKFILES:
|
|
;Search the directory for the files specified on the command line
|
|
;and report the number of fragmented allocation units found in
|
|
;each one.
|
|
CALL DOCRLF
|
|
MOV AH,SET_DMA
|
|
MOV DX,[FATMAP] ;Use the first free space available
|
|
MOV BP,DX
|
|
ADD BP,27 ;cluster in the directory entry
|
|
INT 21H
|
|
MOV AH,DIR_SEARCH_FIRST ;Look for the first file
|
|
FRAGCHK:
|
|
MOV DX,FCB
|
|
INT 21H
|
|
OR AL,AL ;Did we find it?
|
|
JNZ MSGCHK ;No -- we're done
|
|
XOR AX,AX ;Initialize the fragment counter
|
|
MOV SI,[BP] ;Get the first cluster
|
|
CALL UNPACK
|
|
CMP DI,0FF8H ;End-of-file?
|
|
JAE NXTCHK ;Yes -- go report the results
|
|
INC SI
|
|
CMP SI,DI
|
|
JZ EACHCLUS
|
|
INC AX
|
|
EACHCLUS:
|
|
MOV [OLDCLUS],DI ;Save the last cluster found
|
|
MOV SI,DI ;Get the next cluster
|
|
CALL UNPACK
|
|
INC [OLDCLUS] ;Bump the old cluster
|
|
CMP DI,[OLDCLUS] ;Are they the same?
|
|
JNZ LASTCLUS ;No -- check for end-of-file
|
|
JMP SHORT EACHCLUS ;Continue processing
|
|
LASTCLUS:
|
|
CMP DI,0FF8H ;End-of-file?
|
|
JAE NXTCHK ;Yes -- go report the results
|
|
INC AX ;No -- found a fragement
|
|
JMP SHORT EACHCLUS ;Continue processing
|
|
|
|
NXTCHK:
|
|
OR AX,AX
|
|
JZ GETNXT
|
|
MOV [FRAGMENT],2 ;Signal that we output at least one file
|
|
PUSH AX ;Save count of fragments
|
|
MOV SI,[FATMAP]
|
|
INC SI
|
|
CALL PRINTTHISEL2
|
|
CALL DOCRLF
|
|
MOV DX,OFFSET DG:CONTAINS ;Print message
|
|
CALL PRINT
|
|
POP SI ;Number of fragments found
|
|
INC SI ;Number non-contig blocks
|
|
XOR DI,DI
|
|
MOV BX,OFFSET DG:EXTENTS
|
|
PUSH BP
|
|
CALL DISP16BITS
|
|
POP BP
|
|
GETNXT:
|
|
MOV AH,DIR_SEARCH_NEXT ;Look for the next file
|
|
JMP FRAGCHK
|
|
|
|
MSGCHK:
|
|
CMP AH,DIR_SEARCH_FIRST
|
|
JNZ FILSPOK
|
|
MOV SI,FCB + 1 ;File not found error
|
|
CALL PRINTTHISEL2
|
|
CALL DOCRLF
|
|
MOV DX,OFFSET DG:OPNERR
|
|
CALL PRINT ;Bad file spec
|
|
RET
|
|
FILSPOK:
|
|
CMP BYTE PTR [FRAGMENT],2
|
|
JZ CDONE
|
|
MOV DX,OFFSET DG:NOEXTENTS
|
|
CALL PRINT
|
|
CDONE:
|
|
RET
|
|
|
|
|
|
FIGREC:
|
|
;Convert cluster number in BX to sector # AH of cluster in DX
|
|
LDS DI,[THISDPB]
|
|
ASSUME DS:NOTHING
|
|
MOV CL,[DI.dpb_cluster_shift]
|
|
MOV DX,BX
|
|
DEC DX
|
|
DEC DX
|
|
SHL DX,CL
|
|
OR DL,AH
|
|
ADD DX,[DI.dpb_first_sector]
|
|
PUSH CS
|
|
POP DS
|
|
ASSUME DS:DG
|
|
RET
|
|
|
|
|
|
SUBTTL PRINTID - Print Volume ID info
|
|
PAGE
|
|
PRINTID:
|
|
ASSUME DS:DG
|
|
MOV DX,OFFSET DG:INTERNATVARS
|
|
MOV AX,INTERNATIONAL SHL 8
|
|
INT 21H
|
|
MOV [DISPFLG],1 ;Don't sub spaces for leading zeros
|
|
MOV SI,OFFSET DG:FAT + 8
|
|
MOV DI,OFFSET DG:VNAME
|
|
MOV CX,11
|
|
REP MOVSB
|
|
MOV DX,OFFSET DG:IDMES1
|
|
CALL PRINT ;Print ID message
|
|
ADD SI,13
|
|
LODSW ;Get date
|
|
PUSH SI
|
|
MOV DX,AX
|
|
MOV AX,[INTERNATVARS.Date_tim_format]
|
|
OR AX,AX
|
|
JZ USPDAT
|
|
DEC AX
|
|
JZ EUPDAT
|
|
CALL P_YR
|
|
CALL P_DSEP
|
|
CALL P_MON
|
|
CALL P_DSEP
|
|
MOV CX,1000H ;Do not supress leading zeroes
|
|
CALL P_DAY
|
|
JMP P_TIME
|
|
|
|
USPDAT:
|
|
CALL P_MONTH_NAM
|
|
MOV CX,1110H ;Supress at most 1 leading 0
|
|
CALL P_DAY
|
|
PUSH DX
|
|
MOV DL,','
|
|
CALL PRTCHR
|
|
MOV DL,' '
|
|
CALL PRTCHR
|
|
POP DX
|
|
PYA:
|
|
CALL P_YR
|
|
JMP P_TIME
|
|
|
|
EUPDAT:
|
|
MOV CX,1110H ;Supress at most 1 leading 0
|
|
CALL P_DAY
|
|
PUSH DX
|
|
MOV DL,' '
|
|
CALL PRTCHR
|
|
POP DX
|
|
CALL P_MONTH_NAM
|
|
JMP PYA
|
|
|
|
P_DSEP:
|
|
PUSH DX
|
|
MOV DL,[INTERNATVARS.Date_sep]
|
|
CALL PRTCHR
|
|
POP DX
|
|
RET
|
|
|
|
P_MONTH_NAM:
|
|
MOV AX,DX
|
|
PUSH DX
|
|
MOV CL,5
|
|
SHR AX,CL
|
|
AND AX,0FH ;Month in AX
|
|
DEC AX ;Make 0 indexed
|
|
MOV CX,AX
|
|
SHL AX,1
|
|
ADD AX,CX ;Mult by 3 chars/mo
|
|
MOV SI,OFFSET DG:MONTAB
|
|
ADD SI,AX
|
|
LODSB
|
|
MOV DL,AL
|
|
CALL PRTCHR
|
|
LODSB
|
|
MOV DL,AL
|
|
CALL PRTCHR
|
|
LODSB
|
|
MOV DL,AL
|
|
CALL PRTCHR
|
|
MOV DL,' '
|
|
CALL PRTCHR
|
|
POP DX
|
|
RET
|
|
|
|
P_MON:
|
|
MOV SI,DX
|
|
PUSH DX
|
|
MOV CL,5
|
|
SHR SI,CL
|
|
AND SI,0FH ;Month in SI
|
|
CALL CONVERT
|
|
MOV DL,AL
|
|
MOV CX,1000H ;Do not supress leading 0
|
|
CALL OUTBYTE ;Print month
|
|
POP DX
|
|
RET
|
|
|
|
P_DAY:
|
|
MOV SI,DX
|
|
PUSH DX
|
|
PUSH CX
|
|
AND SI,01FH ;SI has day
|
|
CALL CONVERT
|
|
POP CX
|
|
MOV DL,AL
|
|
CALL OUTBYTE ;Print day
|
|
POP DX
|
|
RET
|
|
|
|
P_YR:
|
|
MOV SI,DX
|
|
PUSH DX
|
|
MOV CL,9
|
|
SHR SI,CL
|
|
AND SI,07FH ;SI has raw year
|
|
ADD SI,1980 ;Real year
|
|
CALL CONVERT
|
|
MOV CX,1000H ;Do not supress leading zeros
|
|
CALL OUTWORD ;Print year
|
|
POP DX
|
|
RET
|
|
|
|
P_TIME:
|
|
MOV DL,' '
|
|
CALL PRTCHR
|
|
POP SI
|
|
ADD SI,-4
|
|
LODSW ;Get time
|
|
MOV DI,AX
|
|
MOV SI,DI
|
|
MOV CL,11
|
|
SHR SI,CL
|
|
AND SI,01FH ;SI has hour
|
|
CMP [INTERNATVARS.Time_24],0
|
|
JNZ ISOK2 ;24 hour time?
|
|
CMP SI,12
|
|
JB ISOK ;Is AM
|
|
MOV [TCHAR],'p'
|
|
JZ ISOK ;Is 12-1p
|
|
SUB SI,12 ;Is PM
|
|
ISOK:
|
|
OR SI,SI
|
|
JNZ ISOK2
|
|
MOV SI,12 ;0 is 12a
|
|
ISOK2:
|
|
CALL CONVERT
|
|
MOV CX,1110H ;Supress at most 1 leading 0
|
|
MOV DL,AL
|
|
CALL OUTBYTE ;Print hour
|
|
MOV DL,BYTE PTR [INTERNATVARS.Time_sep]
|
|
CALL PRTCHR
|
|
MOV SI,DI
|
|
MOV CL,5
|
|
SHR SI,CL
|
|
AND SI,03FH ;SI has minute
|
|
CALL CONVERT
|
|
MOV CX,1000H ;Do not supress leading zeroes
|
|
MOV DL,AL
|
|
CALL OUTBYTE ;Print minute
|
|
MOV DL,[TCHAR]
|
|
CMP [INTERNATVARS.Time_24],0
|
|
JNZ NOAP ;24 hour time, no a or p
|
|
CALL PRTCHR ;Print a or p
|
|
NOAP:
|
|
MOV DX,OFFSET DG:IDPOST
|
|
CALL PRINT
|
|
MOV [DISPFLG],0
|
|
RET
|
|
|
|
CONVERT:
|
|
MOV CX,16
|
|
XOR AX,AX
|
|
CNVLOOP:
|
|
SHL SI,1
|
|
CALL CONVWRD
|
|
CLC
|
|
LOOP CNVLOOP
|
|
RET
|
|
|
|
SUBTTL Misc Routines - Mostly I/O
|
|
PAGE
|
|
CONVWRD:
|
|
ADC AL,AL
|
|
DAA
|
|
XCHG AL,AH
|
|
ADC AL,AL
|
|
DAA
|
|
XCHG AL,AH
|
|
RET1: RET
|
|
|
|
UNSCALE:
|
|
SHR CX,1
|
|
JC RET1
|
|
SHL SI,1
|
|
RCL DI,1
|
|
JMP SHORT UNSCALE
|
|
|
|
DISP16BITS:
|
|
MOV BYTE PTR DISPFLG,1
|
|
JMP SHORT DISP32BITS
|
|
|
|
DISPCLUS:
|
|
MUL [SSIZE]
|
|
MOV CL,[CSIZE]
|
|
XOR CH,CH
|
|
MOV SI,AX
|
|
MOV DI,DX
|
|
CALL UNSCALE
|
|
|
|
DISP32BITS:
|
|
PUSH BP
|
|
PUSH BX
|
|
XOR AX,AX
|
|
MOV BX,AX
|
|
MOV BP,AX
|
|
MOV CX,32
|
|
CONVLP:
|
|
SHL SI,1
|
|
RCL DI,1
|
|
XCHG AX,BP
|
|
CALL CONVWRD
|
|
XCHG AX,BP
|
|
XCHG AX,BX
|
|
CALL CONVWRD
|
|
XCHG AX,BX
|
|
ADC AL,0
|
|
LOOP CONVLP
|
|
; Conversion complete
|
|
MOV CX,1310H ;Print 3-digit number with 2 leading blanks
|
|
CMP BYTE PTR DISPFLG,0
|
|
JNZ FOURDIG
|
|
MOV CX,1810H ;Print 8-digit number with 2 leading blanks
|
|
XCHG DX,AX
|
|
CALL DIGIT
|
|
XCHG AX,BX
|
|
CALL OUTWORD
|
|
FOURDIG:
|
|
MOV AX,BP
|
|
CALL OUTWORD
|
|
MOV BYTE PTR DISPFLG,0
|
|
POP DX
|
|
CALL PRINT
|
|
POP BP
|
|
RET
|
|
|
|
OUTWORD:
|
|
PUSH AX
|
|
MOV DL,AH
|
|
CALL OUTBYTE
|
|
POP DX
|
|
OUTBYTE:
|
|
MOV DH,DL
|
|
SHR DL,1
|
|
SHR DL,1
|
|
SHR DL,1
|
|
SHR DL,1
|
|
CALL DIGIT
|
|
MOV DL,DH
|
|
DIGIT:
|
|
AND DL,0FH
|
|
JZ BLANKZER
|
|
MOV CL,0
|
|
BLANKZER:
|
|
DEC CH
|
|
AND CL,CH
|
|
OR DL,30H
|
|
SUB DL,CL
|
|
CMP BYTE PTR DISPFLG,0
|
|
JZ PRTCHR
|
|
CMP DL,30H
|
|
JL RET2
|
|
PRTCHR:
|
|
MOV AH,STD_CON_OUTPUT
|
|
INT 21H
|
|
RET2: RET
|
|
|
|
PRINTCNT:
|
|
LODSB
|
|
MOV DL,AL
|
|
INT 21H
|
|
LOOP PRINTCNT
|
|
RET
|
|
|
|
EPRINT:
|
|
CALL CHECKERR
|
|
JNZ RET$1
|
|
JMP SHORT PRINT
|
|
|
|
DOCRLF:
|
|
MOV DX,OFFSET DG:CRLF
|
|
PRINT:
|
|
MOV AH,STD_CON_STRING_OUTPUT
|
|
INT 21H
|
|
RET$1: RET
|
|
|
|
DOTCOMBMES:
|
|
CMP [NOISY],0
|
|
JZ SUBERRP
|
|
PUSH DX
|
|
CALL PRINTCURRDIRERR
|
|
MOV DX,OFFSET DG:CENTRY
|
|
CALL EPRINT
|
|
POP DX
|
|
CALL EPRINT
|
|
CALL DOCRLF
|
|
RET
|
|
|
|
SUBERRP:
|
|
MOV AL,1
|
|
XCHG AL,[ERRSUB]
|
|
CMP AL,0
|
|
JNZ RET32
|
|
MOV SI,OFFSET DG:NUL
|
|
CALL PRINTCURRDIRERR
|
|
MOV DX,OFFSET DG:BADSUBDIR
|
|
CALL EPRINT
|
|
RET32: RET
|
|
|
|
|
|
FCB_TO_ASCZ: ;Convert DS:SI to ASCIIZ ES:DI
|
|
MOV CX,8
|
|
MAINNAME:
|
|
LODSB
|
|
CMP AL,' '
|
|
JZ SKIPSPC
|
|
STOSB
|
|
SKIPSPC:
|
|
LOOP MAINNAME
|
|
LODSB
|
|
CMP AL,' '
|
|
JZ GOTNAME
|
|
MOV AH,AL
|
|
MOV AL,'.'
|
|
STOSB
|
|
XCHG AL,AH
|
|
STOSB
|
|
MOV CL,2
|
|
EXTNAME:
|
|
LODSB
|
|
CMP AL,' '
|
|
JZ GOTNAME
|
|
STOSB
|
|
LOOP EXTNAME
|
|
|
|
GOTNAME:
|
|
XOR AL,AL
|
|
STOSB
|
|
RET
|
|
|
|
CODE ENDS
|
|
END CHKDSK
|
|
|
|
|
|
;
|
|
; BASIC-DOS Driver/Application Interface Entry Points
|
|
;
|
|
; @author Jeff Parsons <Jeff@pcjs.org>
|
|
; @copyright (c) 2020-2021 Jeff Parsons
|
|
; @license MIT <https://basicdos.com/LICENSE.txt>
|
|
;
|
|
; This file is part of PCjs, a computer emulation software project at pcjs.org
|
|
;
|
|
include macros.inc
|
|
include 8086.inc
|
|
include bios.inc
|
|
include dos.inc
|
|
include dosapi.inc
|
|
|
|
DOS segment word public 'CODE'
|
|
|
|
EXTWORD <FUNCTBL>
|
|
EXTABS <FUNCTBL_SIZE,UTILTBL_SIZE>
|
|
EXTWORD <scb_active>
|
|
EXTBYTE <scb_locked,int_level>
|
|
EXTNEAR <msc_readctrlc,msc_sigerr>
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_dverr (INT 00h)
|
|
;
|
|
; If a "divide exception" occurs, this default handler reports it and then
|
|
; aborts the current program.
|
|
;
|
|
DEFPROC dos_dverr,DOSFAR
|
|
IFDEF MAXDEBUG
|
|
DBGBRK
|
|
ENDIF
|
|
push ax
|
|
IFNDEF DEBUG
|
|
PRINTF <"Division error",13,10>
|
|
ELSE
|
|
;
|
|
; Print the 32-bit return address on the stack, and since it's already on
|
|
; the stack, we don't have to push it, which means PRINTF won't try to pop it
|
|
; either. However, since we had to push AX (the only register that PRINTF
|
|
; modifies), we must include a special PRINTF formatter (%U) that skips one
|
|
; 16-bit value on the stack.
|
|
;
|
|
PRINTF <"Division error @%U%08lx",13,10>
|
|
ENDIF
|
|
call msc_sigerr
|
|
pop ax
|
|
IFDEF DEBUG
|
|
iret
|
|
ELSE
|
|
mov ah,EXTYPE_DVERR
|
|
jmp dos_abort
|
|
ENDIF
|
|
ENDPROC dos_dverr
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_sstep (INT 01h)
|
|
;
|
|
; If a trace interrupt (or an explicit INT 10h) occurs, and no debugger
|
|
; is currently running, we catch it here and ignore it.
|
|
;
|
|
DEFPROC dos_sstep,DOSFAR
|
|
iret
|
|
ENDPROC dos_sstep
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_brkpt (INT 03h)
|
|
;
|
|
; If a breakpoint interrupt occurs, and no debugger is currently running,
|
|
; we catch it here and ignore it.
|
|
;
|
|
DEFPROC dos_brkpt,DOSFAR
|
|
iret
|
|
ENDPROC dos_brkpt
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_oferr (INT 04h)
|
|
;
|
|
; If an "overflow exception" occurs, this default handler reports it and
|
|
; signals the error.
|
|
;
|
|
DEFPROC dos_oferr,DOSFAR
|
|
IFDEF MAXDEBUG
|
|
DBGBRK
|
|
ENDIF
|
|
push ax
|
|
IFNDEF DEBUG
|
|
PRINTF <"Overflow error",13,10>
|
|
ELSE
|
|
;
|
|
; Print the 32-bit return address on the stack, and since it's already on
|
|
; the stack, we don't have to push it, which means PRINTF won't try to pop it
|
|
; either. However, since we had to push AX (the only register that PRINTF
|
|
; modifies), we must include a special PRINTF formatter (%U) that skips one
|
|
; 16-bit value on the stack.
|
|
;
|
|
PRINTF <"Overflow error @%U%08lx",13,10>
|
|
ENDIF
|
|
call msc_sigerr
|
|
pop ax
|
|
IFDEF DEBUG
|
|
iret
|
|
ELSE
|
|
mov ah,EXTYPE_OVERR
|
|
jmp dos_abort
|
|
ENDIF
|
|
ENDPROC dos_oferr
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_opchk (INT 06h)
|
|
;
|
|
; This interrupt is used by DEBUG builds to perform "operation checks",
|
|
; based on the byte that follows the INT 06h instruction; eg:
|
|
;
|
|
; CCh: breakpoint
|
|
; F9h: assertion failure
|
|
; FBh: 32-bit multiply check
|
|
; FCh: 32-bit division check
|
|
;
|
|
; If the 8086 emulation environment isn't set up to intercept INT 06h and
|
|
; perform these checks, this handler ensures the checks are harmless.
|
|
;
|
|
DEFPROC dos_opchk,DOSFAR
|
|
IFDEF DEBUG
|
|
push bp
|
|
mov bp,sp
|
|
push ax
|
|
push si
|
|
push ds
|
|
lds si,dword ptr [bp+2] ; DS:SI = CS:IP from stack
|
|
cld
|
|
lodsb
|
|
mov [bp+2],si ; update CS:IP to skip OPCHECK byte
|
|
cmp al,OP_ASSERT ; OP_ASSERT?
|
|
jnz oc9 ; no
|
|
sub si,3 ; display the address of the INT 06h
|
|
; PRINTF <"Assertion failure @%08lx",13,10>,si,ds
|
|
DBGBRK
|
|
oc9: pop ds
|
|
pop si
|
|
pop ax
|
|
pop bp
|
|
ENDIF
|
|
;
|
|
; Even if you mistakenly run a DEBUG binary on a non-DEBUG system (which
|
|
; means all that's here is this IRET), any operation check should still be
|
|
; innocuous (but that's neither guaranteed nor recommended).
|
|
;
|
|
iret
|
|
ENDPROC dos_opchk
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_term (INT 20h)
|
|
;
|
|
; NOTE: In PC DOS, this interrupt, as well as INT 21h AH=00h, apparently
|
|
; requires the call to be made from the segment containing the PSP (CS == PSP).
|
|
; We do not. Also, the underlying function here (DOS_PSP_TERM) sets a default
|
|
; exit code (we use zero), whereas DOS_PSP_RETURN (4Ch) allows any exit code
|
|
; to be returned.
|
|
;
|
|
DEFPROC dos_term,DOSFAR
|
|
mov ah,DOS_PSP_TERM
|
|
int 21h
|
|
iret
|
|
ENDPROC dos_term
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_restart
|
|
;
|
|
; Default CTRLC response handler; if carry is set, call DOS_PSP_RETURN with
|
|
; (arbitrary) exit code -1.
|
|
;
|
|
; Inputs:
|
|
; Carry determines whether we exit the process or restart the DOS call
|
|
;
|
|
; Outputs:
|
|
; None
|
|
;
|
|
DEFPROC dos_restart,DOSFAR
|
|
jnc dos_func
|
|
mov ah,EXTYPE_CTRLC
|
|
DEFLBL dos_abort,near
|
|
mov al,0FFh ; AL = exit code
|
|
xchg dx,ax ; DL = exit code, DH = exit type
|
|
DOSUTIL TERM
|
|
ASSERT NEVER ; assert that we never get here
|
|
ENDPROC dos_restart
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_func (INT 21h)
|
|
;
|
|
; Inputs:
|
|
; Varies
|
|
;
|
|
; Outputs:
|
|
; Varies
|
|
;
|
|
DEFPROC dos_func,DOSFAR
|
|
cld ; we assume CLD everywhere
|
|
sub sp,size WS_TEMP
|
|
push ax ; order of pushes must match REG_FRAME
|
|
push bx
|
|
push cx
|
|
push dx
|
|
DEFLBL dos_enter,near
|
|
push ds
|
|
push si
|
|
push es
|
|
push di
|
|
push bp
|
|
mov bp,sp
|
|
|
|
dc0: IF REG_CHECK ; in DEBUG builds, use CALL to push
|
|
call dos_check ; a marker ("dos_check") onto the stack
|
|
ENDIF ; which REG_CHECK checks will verify
|
|
DEFLBL dos_check,near
|
|
;
|
|
; While we assign DS and ES to the DOS segment on DOS function entry, we
|
|
; do NOT assume they will still be set that way when the FUNCTBL call returns.
|
|
;
|
|
mov bx,cs
|
|
mov ds,bx
|
|
ASSUME DS:DOS
|
|
mov es,bx
|
|
ASSUME ES:DOS
|
|
|
|
mov bx,[scb_active]
|
|
ASSERT STRUCT,[bx],SCB
|
|
inc [bx].SCB_INDOS
|
|
;
|
|
; Utility functions don't automatically re-enable interrupts, clear carry,
|
|
; or check for CTRLC, since some of them are called from interrupt handlers.
|
|
;
|
|
cmp ah,80h ; utility function?
|
|
jb dc1 ; no
|
|
sub ah,80h
|
|
cmp ah,UTILTBL_SIZE ; utility function within range?
|
|
jae dos_leave ; no
|
|
mov bl,ah
|
|
add bl,FUNCTBL_SIZE ; the utility function table
|
|
jmp short dc2 ; follows the DOS function table
|
|
|
|
dc1: sti
|
|
and [bp].REG_FL,NOT FL_CARRY
|
|
cmp ah,FUNCTBL_SIZE
|
|
cmc
|
|
jb dc3
|
|
|
|
IFDEF MAXDEBUG
|
|
push ax
|
|
mov al,ah
|
|
;
|
|
; %P is a special formatter that prints the caller's REG_CS:REG_IP-2 in hex;
|
|
; "#010" ensures it's printed with "0x" and 8 digits with leading zeroes.
|
|
;
|
|
DPRINTF 'd',<"%#010P: DOS function %02bxh\r\n">,ax
|
|
pop ax
|
|
ENDIF ; MAXDEBUG
|
|
;
|
|
; If CTRLC checking is enabled for all (non-utility) functions and a CTRLC
|
|
; was detected (two conditions that we check with a single compare), signal it.
|
|
;
|
|
cmp word ptr [bx].SCB_CTRLC_ALL,0101h
|
|
je dc4 ; signal CTRLC
|
|
mov bl,ah
|
|
dc2: mov bh,0 ; BX = function #
|
|
add bx,bx ; convert function # to word offset
|
|
;
|
|
; For convenience, general-purpose registers AX, CX, DX, SI, DI, and SS
|
|
; contain their original values.
|
|
;
|
|
call FUNCTBL[bx]
|
|
ASSUME DS:NOTHING, ES:NOTHING
|
|
;
|
|
; We'd just as soon IRET to the caller (which also restores their D flag),
|
|
; so we now update FL_CARRY on the stack (which we already cleared on entry).
|
|
;
|
|
dc3: adc [bp].REG_FL,0
|
|
|
|
DEFLBL dos_leave,near
|
|
IF REG_CHECK ; in DEBUG builds, check the "marker"
|
|
pop bx ; that we pushed on entry
|
|
ASSERT Z,<cmp bx,offset dos_check>
|
|
ENDIF
|
|
;
|
|
; Whenever the session's INDOS count returns to zero, check for a pending
|
|
; SCSTAT_ABORT; if set AND we're not in the middle of a hardware interrupt,
|
|
; clear the ABORT condition and simulate a DOSUTIL TERM session abort.
|
|
;
|
|
mov bx,[scb_active]
|
|
ASSERT STRUCT,cs:[bx],SCB
|
|
dec cs:[bx].SCB_INDOS
|
|
ASSERT GE
|
|
jnz dos_leave2
|
|
test cs:[bx].SCB_STATUS,SCSTAT_ABORT
|
|
jz dos_leave2
|
|
cmp word ptr [scb_locked],-1; do NOT abort if session or driver
|
|
jne dos_leave2 ; lock levels are >= 0
|
|
and cs:[bx].SCB_STATUS,NOT SCSTAT_ABORT
|
|
;
|
|
; WARNING: This simulation of DOSUTIL TERM takes a shortcut by not updating
|
|
; REG_AH or REG_DX in REG_FRAME, but neither utl_term nor psp_termcode rely
|
|
; on REG_FRAME for their inputs, so while this is not completely kosher, we'll
|
|
; be fine. The same is true for the termination code in int_leave.
|
|
;
|
|
mov dx,(EXTYPE_ABORT SHL 8) OR 0FFh
|
|
mov ah,DOS_UTL_TERM + 80h
|
|
jmp dc0
|
|
|
|
dc4: jmp msc_readctrlc
|
|
|
|
DEFLBL dos_leave2,near
|
|
pop bp
|
|
pop di
|
|
pop es
|
|
ASSUME ES:NOTHING
|
|
pop si
|
|
pop ds
|
|
ASSUME DS:NOTHING
|
|
pop dx
|
|
pop cx
|
|
pop bx
|
|
pop ax
|
|
add sp,size WS_TEMP
|
|
iret
|
|
ENDPROC dos_func
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_exit (INT 22h handler)
|
|
;
|
|
DEFPROC dos_exit,DOSFAR
|
|
ASSERT NEVER ; assert that we never get here
|
|
jmp near ptr dos_term
|
|
ENDPROC dos_exit
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_ctrlc (INT 23h handler)
|
|
;
|
|
DEFPROC dos_ctrlc,DOSFAR
|
|
push ax
|
|
mov ah,DOS_DSK_RESET
|
|
int 21h
|
|
pop ax
|
|
stc ; set carry to indicate termination
|
|
ret
|
|
ENDPROC dos_ctrlc
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_error (INT 24h handler)
|
|
;
|
|
; Outputs:
|
|
; AL = 0: ignore error
|
|
; AL = 1: retry operation
|
|
; AL = 2: abort program via INT 23h
|
|
; AL = 3: fail system call in progress
|
|
;
|
|
DEFPROC dos_error,DOSFAR
|
|
mov al,CRERR_ABORT ; default to 2 (abort via INT 23h)
|
|
iret
|
|
ENDPROC dos_error
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_default (currently used for INT 28h and INT 2Ah-2Fh)
|
|
;
|
|
DEFPROC dos_default,DOSFAR
|
|
iret
|
|
ENDPROC dos_default
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; disk_read (INT 25h)
|
|
;
|
|
; TODO
|
|
;
|
|
DEFPROC disk_read,DOSFAR
|
|
iret
|
|
ENDPROC disk_read
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; disk_write (INT 26h)
|
|
;
|
|
; TODO
|
|
;
|
|
DEFPROC disk_write,DOSFAR
|
|
iret
|
|
ENDPROC disk_write
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_tsr (INT 27h)
|
|
;
|
|
; TODO
|
|
;
|
|
DEFPROC dos_tsr,DOSFAR
|
|
iret
|
|
ENDPROC dos_tsr
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_call5 (INT 30h)
|
|
;
|
|
; We typically arrive here via NEAR CALL 0005h to FAR CALL to FAR JMP in
|
|
; vector 30h. We should be able to transform that into an INT 21h by "simply"
|
|
; moving the NEAR CALL return address into the FAR CALL return address, then
|
|
; replacing the NEAR CALL return address with the current flags, and finally
|
|
; moving the DOS function # from CL to AH.
|
|
;
|
|
; Not being familiar with the CALL 0005h interface, whether that's actually
|
|
; sufficient remains to be seen.
|
|
;
|
|
DEFPROC dos_call5,DOSFAR
|
|
push bp
|
|
mov bp,sp
|
|
mov ax,[bp+6]
|
|
mov [bp+2],ax
|
|
pushf ; since we didn't arrive here via INT,
|
|
pop [bp+6] ; these flags should have interrupts on
|
|
pop bp
|
|
mov ah,cl
|
|
jmp near ptr dos_func
|
|
ENDPROC dos_call5
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; dos_util (INT 32h)
|
|
;
|
|
; We could jump straight to dos_func after adjusting the function number,
|
|
; but if a breakpoint has been set on dos_func, we'd rather not have dos_util
|
|
; calls triggering it as well; hence the redundant CLD and jmp + 1.
|
|
;
|
|
DEFPROC dos_util,DOSFAR
|
|
cld
|
|
add ah,80h
|
|
jmp near ptr dos_func + 1 ; avoid the same entry point as INT 21h
|
|
ENDPROC dos_util
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; int_enter
|
|
;
|
|
; DDINT_ENTER is "revectored" here by sysinit.
|
|
;
|
|
; Inputs:
|
|
; None
|
|
;
|
|
; Outputs:
|
|
; Carry clear (DOS interrupt processing enabled)
|
|
;
|
|
DEFPROC int_enter,DOSFAR
|
|
inc [int_level]
|
|
clc
|
|
ret
|
|
ENDPROC int_enter
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; int_leave
|
|
;
|
|
; DDINT_LEAVE is "revectored" here by sysinit.
|
|
;
|
|
; Inputs:
|
|
; Carry set to reschedule, assuming int_level has dropped below zero
|
|
;
|
|
; Outputs:
|
|
; None
|
|
;
|
|
DEFPROC int_leave,DOSFAR
|
|
cli
|
|
dec [int_level]
|
|
jge ddl9
|
|
jnc ddl9
|
|
;
|
|
; Enter DOS to perform a reschedule.
|
|
;
|
|
; However, we first take a peek at the current SCB's INDOS count and
|
|
; ABORT flag; if the count is zero and the flag is set, force termination.
|
|
;
|
|
cld
|
|
sub sp,size WS_TEMP
|
|
push ax
|
|
push bx
|
|
push cx
|
|
push dx
|
|
mov ah,DOS_UTL_YIELD + 80h
|
|
mov bx,cs:[scb_active]
|
|
cmp cs:[bx].SCB_INDOS,0
|
|
jne ddl8
|
|
test cs:[bx].SCB_STATUS,SCSTAT_ABORT
|
|
jz ddl8
|
|
and cs:[bx].SCB_STATUS,NOT SCSTAT_ABORT
|
|
mov dx,(EXTYPE_ABORT SHL 8) OR 0FFh
|
|
mov ah,DOS_UTL_TERM + 80h
|
|
ddl8: jmp dos_enter
|
|
ddl9: iret
|
|
ENDPROC int_leave
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;
|
|
; func_none (handler for unimplemented DOS functions)
|
|
;
|
|
; Inputs:
|
|
; Varies
|
|
;
|
|
; Outputs:
|
|
; REG_AX = ERR_INVALID, carry set
|
|
;
|
|
DEFPROC func_none,DOS
|
|
IFDEF DEBUG
|
|
mov al,ah
|
|
;
|
|
; %P is a special formatter that prints the caller's REG_CS:REG_IP-2 in hex;
|
|
; "#010" ensures it's printed with "0x" and 8 digits with leading zeroes.
|
|
;
|
|
DPRINTF 'd',<"%#010P: unsupported DOS function %02bxh\r\n">,ax
|
|
ENDIF ; DEBUG
|
|
|
|
mov [bp].REG_AX,ERR_INVALID
|
|
stc
|
|
ret
|
|
ENDPROC func_none
|
|
|
|
DOS ends
|
|
|
|
end |