langsmoke/ref_x86

5117 lines
116 KiB
Plaintext
Raw Normal View History

2023-11-12 13:49:57 +00:00
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