;; Copyright (C) 2017 Jeremiah Orians ;; This file is part of stage0. ;; ;; stage0 is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; ;; stage0 is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with stage0. If not, see . section .text global _start ;; Register usage: ;; EAX, ECX, EBX => Temps ;; EDI => MALLOC ;; EBP => HEAD ;; [Output] => Output_file ;; [Input] => Input_file ;; Struct format: (size 32) ;; NEXT => 0 ;; TYPE => 8 ;; TEXT => 16 ;; EXPRESSION => 24 ;; Types ;; None => 0 ;; MACRO => 1 ;; STRING => 2 ; Where the ELF Header is going to hit ; Simply jump to _start ; Our main function _start: pop eax ;·Get·the·number·of·arguments pop ebx ;·Get·the·program·name pop ebx ;·Get·the·actual·input name mov ecx, 0 ;·prepare·read_only mov eax, 5 ;·the·syscall·number·for·open() int 0x80 ; Now open that damn file mov [Input], eax ; Preserve the file pointer we were given pop ebx ;·Get·the·actual·output name mov ecx, 577 ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC mov edx, 384 ; Prepare file as RW for owner only (600 in octal) mov eax, 5 ;·the·syscall·number·for·open() int 0x80 ; Now open that damn file cmp eax, 0 ; Check for missing output jg _start_out ; Have real input mov eax, 1 ; Use stdout _start_out: mov [Output], eax ; Preserve the file pointer we were given mov eax, 45 ; the Syscall # for SYS_BRK mov ebx, 0 ; Get current brk int 0x80 ; Let the kernel do the work mov edi, eax ; Set our malloc pointer call Tokenize_Line ; Get all lines mov eax, ebp ; prepare for Reverse_List call Reverse_List ; Correct order mov ebp, eax ; Update HEAD call Identify_Macros ; Find the DEFINEs call Line_Macro ; Apply the DEFINEs call Process_String ; Handle strings call Eval_Immediates ; Handle Numbers call Preserve_Other ; Collect the remaining call Print_Hex ; Output our results Done: ; program completed Successfully mov ebx, 0 ; All is well mov eax, 1 ; put the exit syscall number in eax int 0x80 ; Call it a good day ;; Tokenize_Line Function ;; Using input file [Input] and Head EBP ;; Creates a linked list of structs ;; Uses EBX for in_set strings, ECX for Int C and EDX for Struct Token* p Tokenize_Line: push ebx ; Protect EBX push ecx ; Protect ECX push edx ; Protect EDX restart: call fgetc ; Read a char cmp eax, -4 ; Check for EOF je done ; File is collected movzx eax, al ; We have to zero extend it to use it mov ecx, eax ; Protect C mov ebx, comments ; Get pointer to "#;" call In_Set ; Check for comments cmp eax, 1 ; If comments je Purge_LineComment ; try again mov eax, ecx ; put C in place for check mov ebx, terminators ; Get pointer to "\n\t " call In_Set ; Check for terminators cmp eax, 1 ; If terminator je restart ; try again mov eax, 32 ; Malloc the struct P call malloc ; Get pointer to P mov edx, eax ; Protect P mov [edx], ebp ; P->NEXT = HEAD mov ebp, edx ; HEAD = P mov eax, ecx ; put C in place for check mov ebx, string_char ; Get pointer to "\"'" call In_Set ; Check for string chars cmp eax, 1 ; If string char je Store_String ; Get string call Store_Atom ; Get whole token jmp restart done: pop edx ; Restore EDX pop ecx ; Restore ECX pop ebx ; Restore EBX ret ;; fgetc function ;; Receives FILE* in [Input] ;; Returns -4 (EOF) or char in EAX fgetc: push edx ; Protect EDX push ecx ; Protect ECX push ebx ; Protect EBX mov eax, -4 ; Put EOF in eax push eax ; Assume bad (If nothing read, value will remain EOF) lea ecx, [esp] ; Get stack address mov ebx, [Input] ; Where are we reading from mov eax, 3 ; the syscall number for read mov edx, 1 ; set the size of chars we want int 0x80 ; call the Kernel pop eax ; Get either char or EOF pop ebx ; Restore EBX pop ecx ; Restore ECX pop edx ; Restore EDX ret ;; Malloc isn't actually required if the program being built fits in the initial memory ;; However, it doesn't take much to add it. ;; Requires EDI to be initialized and EAX to have the number of desired bytes malloc: push edx ; Protect EDX push ecx ; Protect ECX push ebx ; Protect EBX mov ebx, edi ; Using the current pointer add ebx, eax ; Request the number of desired bytes mov eax, 45 ; the Syscall # for SYS_BRK int 0x80 ; call the Kernel mov eax, edi ; Return pointer mov edi, ebx ; Update pointer pop ebx ; Restore EBX pop ecx ; Restore ECX pop edx ; Restore EDX ret ;; Purge_LineComment function ;; Reads chars until LF and jumps to restart Purge_LineComment: call fgetc ; Get a char movzx eax, al ; Zero extend cmp eax, 10 ; While not LF jne Purge_LineComment ; Keep reading jmp restart ;; Store_String Function ;; Receives C in ECX, HEAD in EDX and Input file in [Output] ;; Uses EBX for terminator, ECX for C and EDX for string Store_String: push ebx ; Protect EBX push ecx ; Protect ECX push edx ; Protect EDX mov eax, 2 ; Using TYPE STRING mov [edx+8], eax ; HEAD->TYPE = STRING mov eax, 256 ; Malloc the string call malloc ; Get pointer to P mov [edx+16], eax ; HEAD->TEXT = STRING mov ebx, ecx ; Protect terminator mov edx, eax ; Protect string pointer Store_String_Loop: mov [edx], cl ; write byte call fgetc ; read next char movzx eax, al ; Zero extend it mov ecx, eax ; Update C add edx, 1 ; STRING = STRING + 1 cmp ecx, ebx ; See if we hit terminator jne Store_String_Loop ; Otherwise keep looping pop edx ; Restore EDX pop ecx ; Restore ECX pop ebx ; Restore EBX mov eax, edx ; return HEAD jmp restart ;; Store_Atom Function ;; Receives C in ECX, HEAD in EDX and Input file in [Input] ;; Uses EBX for in_set strings, ECX for C and EDX for string Store_Atom: push ebx ; Protect EBX push ecx ; Protect ECX push edx ; Protect EDX mov eax, 256 ; Malloc the string call malloc ; Get pointer to P mov [edx+16], eax ; HEAD->TEXT = STRING mov ebx, terminators ; Get pointer to "\n\t " mov edx, eax ; Protect string pointer Store_Atom_loop: mov [edx], cl ; write byte call fgetc ; read next char movzx eax, al ; Zero extend it mov ecx, eax ; Update C add edx, 1 ; STRING = STRING + 1 call In_Set ; Check for terminators cmp eax, 0 ; Check for "\n\t " je Store_Atom_loop ; Loop otherwise pop edx ; Restore EDX pop ecx ; Restore ECX pop ebx ; Restore EBX mov eax, edx ; return HEAD ret ;; In_Set function ;; Receives Char C in EAX and CHAR* in EBX ;; Returns 1 if true, zero if false in EAX In_Set: push ebx ; Protect EBX push ecx ; Protect ECX In_Set_loop: mov cl, [ebx] ; Read char movzx ecx, cl ; Zero extend it cmp eax, ecx ; See if they match je In_Set_True ; return true cmp ecx, 0 ; Check for NULL je In_Set_False ; return false add ebx, 1 ; s = s + 1 jmp In_Set_loop ; Keep looping In_Set_True: mov eax, 1 ; Set True pop ecx ; Restore ECX pop ebx ; Restore EBX ret In_Set_False: mov eax, 0 ; Set FALSE pop ecx ; Restore ECX pop ebx ; Restore EBX ret ;; Char sets terminators: db 10, 9, 32, 0 comments: db 35, 59, 0 string_char: db 34, 39, 0 ;; Reverse_List function ;; Receives List in EAX ;; Returns the list reversed in EAX Reverse_List: push ebx ; Protect EBX push ecx ; Protect ECX mov ebx, eax ; Set HEAD mov eax, 0 ; ROOT = NULL Reverse_List_Loop: cmp ebx, 0 ; WHILE HEAD != NULL je Reverse_List_Done ; Stop otherwise mov ecx, [ebx] ; NEXT = HEAD->NEXT mov [ebx], eax ; HEAD->NEXT = ROOT mov eax, ebx ; ROOT = HEAD mov ebx, ecx ; HEAD = NEXT jmp Reverse_List_Loop ; Keep Going Reverse_List_Done: pop ecx ; Restore ECX pop ebx ; Restore EBX ret ;; Identify_Macros function ;; Receives List in EAX ;; Updates the list in place; does not modify registers ;; Uses EBX for DEFINE, ECX for I Identify_Macros: push eax ; Protect EAX push ebx ; Protect EBX push ecx ; Protect ECX push edx ; Protect EDX mov ebx, DEFINE_str ; Setup define string mov ecx, eax ; I = HEAD Identify_Macros_Loop: mov eax, [ecx+16] ; I->TEXT call match ; IF "DEFINE" == I->TEXT cmp eax, 0 ; Check if match jne Identify_Macros_Next ; Skip the work ;; Deal with MACRO mov eax, 1 ; Using MACRO mov [ecx+8], eax ; I->TYPE = MACRO mov eax, [ecx] ; I->NEXT mov eax, [eax+16] ; I->NEXT->TEXT mov [ecx+16], eax ; I->TEXT = I->NEXT->TEXT mov eax, [ecx] ; I->NEXT mov eax, [eax] ; I->NEXT->NEXT mov eax, [eax+16] ; I->NEXT->NEXT->TEXT mov [ecx+24], eax ; I->EXPRESSION = I->NEXT->NEXT->TEXT mov eax, [ecx] ; I->NEXT mov eax, [eax] ; I->NEXT->NEXT mov eax, [eax] ; I->NEXT->NEXT->NEXT mov [ecx], eax ; I->NEXT = I->NEXT->NEXT->NEXT Identify_Macros_Next: mov ecx, [ecx] ; I = I->NEXT cmp ecx, 0 ; Check for NULL jne Identify_Macros_Loop ; Keep looping otherwise pop edx ; Restore EDX pop ecx ; Restore ECX pop ebx ; Restore EBX pop eax ; Restore EAX ret DEFINE_str: db 68, 69, 70, 73, 78, 69, 0 ;; match function ;; Receives CHAR* in EAX and CHAR* in EBX ;; Returns 0 (TRUE) or 1 (FALSE) in EAX match: push ebx ; Protect EBX push ecx ; Protect ECX push edx ; Protect EDX mov ecx, eax ; S1 in place mov edx, ebx ; S2 in place match_Loop: mov al, [ecx] ; S1[0] movzx eax, al ; Make it useful mov bl, [edx] ; S2[0] movzx ebx, bl ; Make it useful cmp eax, ebx ; See if they match jne match_False ; If not add ecx, 1 ; S1 = S1 + 1 add edx, 1 ; S2 = S2 + 1 cmp eax, 0 ; If reached end of string je match_Done ; Perfect match jmp match_Loop ; Otherwise keep looping match_False: mov eax, 1 ; Return false match_Done: pop edx ; Restore EDX pop ecx ; Restore ECX pop ebx ; Restore EBX ret ;; Line_Macro function ;; Receives List in EAX ;; Updates the list in place; does not modify registers ;; Uses EAX for I, EBX for I->TEXT, ECX for I->EXPRESSION Line_Macro: push eax ; Protect EAX push ebx ; Protect EBX push ecx ; Protect ECX push edx ; Protect EDX Line_Macro_Loop: mov ebx, [eax+8] ; I->TYPE cmp ebx, 1 ; IF MACRO == I->TYPE jne Line_Macro_Next ; Otherwise move on ;; Is a macro apply mov ebx, [eax+16] ; I->TEXT mov ecx, [eax+24] ; I->EXPRESSION mov eax, [eax] ; I->NEXT call Set_Expression ; Apply it jmp Line_Macro_Loop ; Move on to next Line_Macro_Next: mov eax, [eax] ; I->NEXT cmp eax, 0 ; Check for NULL jne Line_Macro_Loop ; Keep going pop edx ; Restore EDX pop ecx ; Restore ECX pop ebx ; Restore EBX pop eax ; Restore EAX ret ;; Set_Expression function ;; Receives List in EAX, CHAR* in EBX and CHAR* in ECX ;; Updates the list in place; does not modify registers ;; Uses EBX for C, ECX for EXP and EDX for I Set_Expression: push eax ; Protect EAX push ebx ; Protect EBX push ecx ; Protect ECX push edx ; Protect EDX mov edx, eax ; Set I Set_Expression_Loop: mov eax, [edx+8] ; I->TYPE cmp eax, 1 ; IF MACRO == I->TYPE je Set_Expression_Next ; Ignore and move on mov eax, [edx+16] ; I->TEXT call match ; Check for match cmp eax, 0 ; If match jne Set_Expression_Next ; Otherwise next ;; We have a non-macro match mov [edx+24], ecx ; I->EXPRESSION = EXP Set_Expression_Next: mov edx, [edx] ; I = I->NEXT cmp edx, 0 ; IF NULL == I jne Set_Expression_Loop ; Otherwise keep looping pop edx ; Restore EDX pop ecx ; Restore ECX pop ebx ; Restore EBX pop eax ; Restore EAX ret ;; Process_String function ;; Receives List in EAX ;; Update the list in place; does not modify registers ;; Uses EBX for I->TEXT, ECX for I and EDX for S Process_String: push eax ; Protect EAX push ebx ; Protect EBX push ecx ; Protect ECX push edx ; Protect EDX mov ecx, eax ; I = HEAD Process_String_loop: mov eax, [ecx+8] ; I->TYPE cmp eax, 2 ; IF STRING == I->TYPE jne Process_String_Next ; Skip to next mov ebx, [ecx+16] ; I->TEXT mov al, [ebx] ; I->TEXT[0] movzx eax, al ; make it useful cmp eax, 39 ; IF '\'' == I->TEXT[0] jne Process_String_Raw ; Deal with '"' ;; Deal with '\'' add ebx, 1 ; I->TEXT + 1 mov [ecx+24], ebx ; I->EXPRESSION = I->TEXT + 1 jmp Process_String_Next ; Move on to next Process_String_Raw: mov eax, ebx ; Get length of I->TEXT call string_length ; Do it shr eax, 2 ; LENGTH = LENGTH >> 2 add eax, 1 ; LENGTH = LENGTH + 1 shl eax, 3 ; LENGTH = LENGTH << 3 call malloc ; Get string mov edx, ebx ; S = I->TEXT add edx, 1 ; S = S + 1 mov [ecx+24], eax ; I->EXPRESSION = hexify mov ebx, eax ; Put hexify buffer in ebx Process_String_Raw_Loop: mov al, [edx] ; Read 1 chars movzx eax, al ; Make it useful add edx, 1 ; S = S + 1 cmp al, 0 ; Check for NULL pushf ; Protect condition call hex8 ; write them all popf ; restore condition jne Process_String_Raw_Loop ; Keep looping Process_String_Next: mov ecx, [ecx] ; I = I->NEXT cmp ecx, 0 ; IF NULL == I jne Process_String_loop ; Otherwise keep looping pop edx ; Restore EDX pop ecx ; Restore ECX pop ebx ; Restore EBX pop eax ; Restore EAX ret ;; string_length function ;; Receives CHAR* in EAX ;; Returns INT in EAX ;; Uses EAX for CH, EBX for S and ECX for INDEX string_length: push ebx ; Protect EBX push ecx ; Protect ECX mov ebx, eax ; Set S mov ecx, 0 ; INDEX = 0 string_length_loop: mov al, [ebx+ecx] ; S[0] movzx eax, al ; make it useful cmp eax, 0 ; IF NULL == S[0] je string_length_done ; Stop add ecx, 1 ; INDEX = INDEX + 1 jmp string_length_loop ; Keep going string_length_done: mov eax, ecx ; RETURN INDEX pop ecx ; Restore ECX pop ebx ; Restore EBX ret ;; Eval_Immediates function ;; Receives List in EAX ;; Updates the list in place; does not modify registers ;; Uses EBX for I->TEXT[0], ECX for I->TEXT[1] and EDX for I Eval_Immediates: push eax ; Protect EAX push ebx ; Protect EBX push ecx ; Protect ECX push edx ; Protect EDX mov edx, eax ; I = HEAD Eval_Immediates_Loop: ;; Check for MACRO mov eax, [edx+8] ; I->TYPE cmp eax, 1 ; IF MACRO == I-TYPE je Eval_Immediates_Next ; Skip to next ;; Check for NULL EXPRESSION mov eax, [edx+24] ; I->EXPRESSION cmp eax, 0 ; IF NULL == I->EXPRESSION jne Eval_Immediates_Next ; Skip to next ;; Check if number mov eax, [edx+16] ; I->TEXT mov bl, [eax] ; I->TEXT[0] movzx ebx, bl ; Extend to use add eax, 1 ; I->TEXT + 1 mov cl, [eax] ; I->TEXT[1] movzx ecx, cl ; Extend to use call numerate_string ; Convert string to INT cmp eax, 0 ; IF 0 == numerate_number(I->TEXT + 1) jne Eval_Immediates_value ; Has a value ;; Last chance for Immediate cmp ecx, 48 ; If '0' == I->TEXT[1] jne Eval_Immediates_Next ; Skip to next Eval_Immediates_value: call express_number ; Convert value to hex string mov [edx+24], eax ; I->EXPRESSION = express_number(value, I-TEXT[0]) Eval_Immediates_Next: mov edx, [edx] ; I = I->NEXT cmp edx, 0 ; IF NULL == I jne Eval_Immediates_Loop ; Otherwise keep looping pop edx ; Restore EDX pop ecx ; Restore ECX pop ebx ; Restore EBX pop eax ; Restore EAX ret ;; numerate_string function ;; Receives CHAR* in EAX ;; Returns value of CHAR* in EAX ;; Uses EAX for VALUE, EBX for S, ECX for CH and EDI for NEGATIVE? numerate_string: push ebx ; Protect EBX push ecx ; Protect ECX push edx ; Protect EDX push edi ; Protect EDI mov ebx, eax ; put S in correct place mov eax, 0 ; Initialize to Zero numerate_string_loop: mov cl, [ebx+1] ; S[1] movzx ecx, cl ; make it useful cmp ecx, 120 ; IF 'x' == S[1] je numerate_hex ; Deal with hex input ;; Assume decimal input mov ecx, 0 ; Assume no negation mov cl, [ebx] ; S[0] movzx ecx, cl ; make it useful cmp ecx, 45 ; IF '-' == S[0] jne numerate_decimal ; Skip negation mov edi, 1 ; Set FLAG add ebx, 1 ; S = S + 1 numerate_decimal: mov cl, [ebx] ; S[0] movzx ecx, cl ; make it useful cmp ecx, 0 ; IF NULL == S[0] je numerate_decimal_done ; We are done imul eax, 10 ; VALUE = VALUE * 10 sub ecx, 48 ; CH = CH - '0' cmp ecx, 9 ; Check for illegal jg numerate_string_fail ; If CH > '9' cmp ecx, 0 ; Check for illegal jl numerate_string_fail ; IF CH < 0 add eax, ecx ; VALUE = VALUE + CH add ebx, 1 ; S = S + 1 jmp numerate_decimal ; Keep looping numerate_decimal_done: cmp edi, 1 ; Check if need to negate jne numerate_string_done ; Nope imul eax, -1 ; VALUE = VALUE * -1 jmp numerate_string_done ; Done numerate_hex: add ebx, 2 ; S = S + 2 numerate_hex_loop: mov cl, [ebx] ; S[0] movzx ecx, cl ; make it useful cmp ecx, 0 ; IF NULL == S[0] je numerate_string_done ; We are done shl eax, 4 ; VALUE = VALUE << 4 sub ecx, 48 ; CH = CH - '0' cmp ecx, 10 ; IF 10 >= CH jl numerate_hex_digit ; NO sub ecx, 7 ; Push A-F into range numerate_hex_digit: cmp ecx, 15 ; Check for illegal jg numerate_string_fail ; If CH > 'F' cmp ecx, 0 ; Check for illegal jl numerate_string_fail ; IF CH < 0 add eax, ecx ; VALUE = VALUE + CH add ebx, 1 ; S = S + 1 jmp numerate_hex_loop ; Keep looping numerate_string_fail: mov eax, 0 ; return ZERO numerate_string_done: pop edi ; Restore EDI pop edx ; Restore EDX pop ecx ; Restore ECX pop ebx ; Restore EBX ret ;; express_number function ;; Receives INT in EAX and CHAR in EBX ;; Allocates a string and expresses the value in hex ;; Returns string in EAX ;; Uses EAX for VALUE, EBX for S and ECX for CH express_number: push ebx ; Protect EBX push ecx ; Protect ECX push edx ; Protect EDX mov ecx, ebx ; Put CH in right place mov ebx, eax ; Protect VALUE cmp ecx, 37 ; IF '%' == CH jne express_number2 ; Otherwise try @ mov eax, 9 ; We need 3bytes call malloc ; Get S pointer xchg eax, ebx ; Put S and VALUE in place push ebx ; Protect S call hex32l ; Store 32bits jmp express_number_done ; done express_number2: cmp ecx, 64 ; IF '@' == CH jne express_number1 ; Othrewise try ! mov eax, 5 ; We need 3bytes call malloc ; Get S pointer xchg eax, ebx ; Put S and VALUE in place push ebx ; Protect S call hex16l ; Store 16bits jmp express_number_done ; done express_number1: mov eax, 3 ; We need 3bytes call malloc ; Get S pointer xchg eax, ebx ; Put S and VALUE in place push ebx ; Protect S call hex8 ; Store 8bit express_number_done: pop eax ; Restore S pop edx ; Restore EDX pop ecx ; Restore ECX pop ebx ; Restore EBX ret ;; HEX to ascii routine ;; Receives INT in EAX and CHAR* in EBX ;; Stores ascii of INT in CHAR* ;; Returns only modifying EAX hex64l: push eax ; Protect top 32 call hex32l ; Store it pop eax ; do top 32 shr eax, 32 ; do bottom 32 first hex32l: push eax ; Protect top 16 call hex16l ; Store it pop eax ; do top 16 shr eax, 16 ; do bottom 16 first hex16l: push eax ; Protect top byte call hex8 ; Store it pop eax ; do high byte shr eax, 8 ; do bottom byte first hex8: push eax ; Protect bottom nibble shr eax, 4 ; do high nibble first call hex4 ; Store it pop eax ; do low nibble hex4: and eax, 0xf ; isolate nibble add al,'0' ; convert to ascii cmp al,'9' ; valid digit? jbe hex1 ; yes add al,7 ; use alpha range hex1: mov [ebx], al ; store result add ebx, 1 ; next position ret ;; Preserve_Other function ;; Receives List in EAX ;; Updates the list in place; does not modify registers ;; Uses EAX for I, EBX for I->TEXT Preserve_Other: push eax ; Protect EAX push ebx ; Protect EBX push ecx ; Protect ECX push edx ; Protect EDX Preserve_Other_Loop: mov ebx, [eax+24] ; I->EXPRESSION cmp ebx, 0 ; IF NULL == I->EXPRESSION jne Preserve_Other_Next ; Otherwise next ;; Needs preserving mov ebx, [eax+16] ; I->TEXT mov [eax+24], ebx ; I->EXPRESSION = I->TEXT Preserve_Other_Next: mov eax, [eax] ; I = I->NEXT cmp eax, 0 ; IF NULL == I jne Preserve_Other_Loop ; Otherwise keep looping pop edx ; Restore EDX pop ecx ; Restore ECX pop ebx ; Restore EBX pop eax ; Restore EAX ret ;; Print_Hex function ;; Receives list in EAX ;; walks the list and prints the I->EXPRESSION for all nodes followed by newline ;; Uses EBX for I Print_Hex: push ebx ; Protect EBX push ecx ; Protect ECX mov ebx, ebp ; I = Head Print_Hex_Loop: mov eax, [ebx+8] ; I->TYPE cmp eax, 1 ; IF MACRO == I->TYPE je Print_Hex_Next ; Skip mov eax, [ebx + 24] ; Using EXPRESSION call File_Print ; Print it mov eax, 10 ; NEWLINE call fputc ; Append it Print_Hex_Next: mov ebx, [ebx] ; Iterate to next Token cmp ebx, 0 ; Check for NULL jne Print_Hex_Loop ; Otherwise keep looping pop ecx ; Restore ECX pop ebx ; Restore EBX ret ;; File_Print function ;; Receives CHAR* in EAX ;; calls fputc for every non-null char File_Print: push ebx ; Protect EBX push ecx ; Protect ECX mov ebx, eax ; Protect S cmp eax, 0 ; Protect against nulls je File_Print_Done ; Simply don't try to print them File_Print_Loop: mov al, [ebx] ; Read byte movzx eax, al ; zero extend cmp eax, 0 ; Check for NULL je File_Print_Done ; Stop at NULL call fputc ; write it add ebx, 1 ; S = S + 1 jmp File_Print_Loop ; Keep going File_Print_Done: pop ecx ; Restore ECX pop ebx ; Restore EBX ret ;; fputc function ;; receives CHAR in EAX and FILE* in [Output] ;; writes char and returns fputc: push edx ; Protect EDX push ecx ; protect ECX push ebx ; protect EBX push eax ; We are writing eax lea ecx, [esp] ; Get stack address mov ebx, [Output] ; Write to target file mov eax, 4 ; the syscall number for write mov edx, 1 ; set the size of chars we want int 0x80 ; call the Kernel pop eax ; Restore stack pop ebx ; Restore EBX pop ecx ; Restore ECX pop edx ; Restore EDX ret section .data Input: dd 0 Output: dd 0