873 lines
29 KiB
ArmAsm
873 lines
29 KiB
ArmAsm
;; 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 <http://www.gnu.org/licenses/>.
|
|
|
|
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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; Recieves 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
|
|
;; recieves 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
|