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 [output]',0xA db 'optional settings:',0xA db ' -m set the limit in kilobytes for the available memory',0xA db ' -p set the maximum allowed number of passes',0xA db ' -d = define symbolic variable',0xA db ' -s 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 ; @copyright (c) 2020-2021 Jeff Parsons ; @license MIT ; ; 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 EXTABS EXTWORD EXTBYTE EXTNEAR ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; 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, 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