stage0/Linux Bootstrap/x86/M0_x86.M1

977 lines
32 KiB
Plaintext

;; 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/>.
;; 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
DEFINE ADDI8_AL 04
DEFINE ADDI8_EAX 83C0
DEFINE ADDI8_EBX 83C3
DEFINE ADDI8_ECX 83C1
DEFINE ADDI8_EDX 83C2
DEFINE ADD_EAX_to_EBX 01C3
DEFINE ADD_ECX_to_EAX 01C8
DEFINE ANDI8_EAX 83E0
DEFINE CALL32 E8
DEFINE CMPI8_AL 3C
DEFINE CMPI8_EAX 83F8
DEFINE CMPI8_EBX 83FB
DEFINE CMPI8_ECX 83F9
DEFINE CMPI8_EDI 83FF
DEFINE CMPI8_EDX 83FA
DEFINE CMP_EAX_EBX 39D8
DEFINE CMP_EAX_ECX 39C8
DEFINE CMP_EBX_ECX 39D9
DEFINE COPY_EAX_to_EBP 89C5
DEFINE COPY_EAX_to_EBX 89C3
DEFINE COPY_EAX_to_ECX 89C1
DEFINE COPY_EAX_to_EDI 89C7
DEFINE COPY_EAX_to_EDX 89C2
DEFINE COPY_EBP_to_EAX 89E8
DEFINE COPY_EBP_to_EBX 89EB
DEFINE COPY_EBX_to_EAX 89D8
DEFINE COPY_EBX_to_ECX 89D9
DEFINE COPY_EBX_to_EDI 89DF
DEFINE COPY_EBX_to_EDX 89DA
DEFINE COPY_ECX_to_EAX 89C8
DEFINE COPY_ECX_to_EBX 89CB
DEFINE COPY_EDI_to_EAX 89F8
DEFINE COPY_EDI_to_EBX 89FB
DEFINE COPY_EDX_to_EAX 89D0
DEFINE COPY_EDX_to_EBP 89D5
DEFINE IMULI8_EAX 6BC0
DEFINE INT_80 CD80
DEFINE JBE32 0F86
DEFINE JE32 0F84
DEFINE JG32 0F8F
DEFINE JL32 0F8C
DEFINE JMP32 E9
DEFINE JNE32 0F85
DEFINE LEA32_ECX_from_esp 8D0C24
DEFINE LOAD32_Absolute32_ebx 8B1D
DEFINE LOAD32_EAX_from_EAX 8B00
DEFINE LOAD32_EAX_from_EAX_Immediate8 8B40
DEFINE LOAD32_EAX_from_EBX_Immediate8 8B43
DEFINE LOAD32_EAX_from_ECX 8B01
DEFINE LOAD32_EAX_from_ECX_Immediate8 8B41
DEFINE LOAD32_EAX_from_EDX_Immediate8 8B42
DEFINE LOAD32_EBX_from_EAX_Immediate8 8B58
DEFINE LOAD32_EBX_from_EBX 8B1B
DEFINE LOAD32_EBX_from_ECX_Immediate8 8B59
DEFINE LOAD32_ECX_from_EAX_Immediate8 8B48
DEFINE LOAD32_ECX_from_EBX 8B0B
DEFINE LOAD32_ECX_from_ECX 8B09
DEFINE LOAD32_EDX_from_EDX 8B12
DEFINE LOAD8_al_from_EBX 8A03
DEFINE LOAD8_al_from_EBX_indexed_ECX 8A040B
DEFINE LOAD8_al_from_ECX 8A01
DEFINE LOAD8_al_from_EDX 8A02
DEFINE LOAD8_bl_from_EAX 8A18
DEFINE LOAD8_bl_from_EDX 8A1A
DEFINE LOAD8_cl_from_EAX 8A08
DEFINE LOAD8_cl_from_EBX 8A0B
DEFINE LOAD8_cl_from_EBX_Immediate8 8A4B
DEFINE LOADI32_EAX B8
DEFINE LOADI32_EBX BB
DEFINE LOADI32_ECX B9
DEFINE LOADI32_EDI BF
DEFINE LOADI32_EDX BA
DEFINE MOVZX_al 0FB6C0
DEFINE MOVZX_bl 0FB6DB
DEFINE MOVZX_cl 0FB6C9
DEFINE NULL 00000000
DEFINE POP_EAX 58
DEFINE POP_EBX 5B
DEFINE POP_ECX 59
DEFINE POP_EDI 5F
DEFINE POP_EDX 5A
DEFINE POP_FLAGS 9D
DEFINE PUSH_EAX 50
DEFINE PUSH_EBX 53
DEFINE PUSH_ECX 51
DEFINE PUSH_EDI 57
DEFINE PUSH_EDX 52
DEFINE PUSH_FLAGS 9C
DEFINE RET C3
DEFINE SHLI8_EAX C1E0
DEFINE SHRI8_EAX C1E8
DEFINE STORE32_Absolute32_eax A3
DEFINE STORE32_EAX_into_Address_EBX 8903
DEFINE STORE32_EAX_into_Address_ECX 8901
DEFINE STORE32_EAX_into_Address_ECX_Immediate8 8941
DEFINE STORE32_EAX_into_Address_EDX_Immediate8 8942
DEFINE STORE32_EBP_into_Address_EDX 892A
DEFINE STORE32_EBX_into_Address_EAX_Immediate8 8958
DEFINE STORE32_EBX_into_Address_ECX_Immediate8 8959
DEFINE STORE32_ECX_into_Address_EDX_Immediate8 894A
DEFINE STORE8_al_into_Address_EBX 8803
DEFINE STORE8_cl_into_Address_EDX 880A
DEFINE SUBI8_ECX 83E9
DEFINE XCHG_EAX_EBX 93
:_start
POP_EAX ;·Get·the·number·of·arguments
POP_EBX ;·Get·the·program·name
POP_EBX ;·Get·the·actual·input name
LOADI32_ECX %0 ;·prepare·read_only
LOADI32_EAX %5 ;·the·syscall·number·for·open()
INT_80 ; Now open that damn file
STORE32_Absolute32_eax &Input ; Preserve the file pointer we were given
POP_EBX ;·Get·the·actual·output name
LOADI32_ECX %577 ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
LOADI32_EDX %384 ; Prepare file as RWX for owner only (600 in octal)
LOADI32_EAX %5 ; the·syscall·number·for·open()
INT_80 ; Now open that damn file
CMPI8_EAX !0 ; Check for missing output
JG32 %_start_out ; Have real input
LOADI32_EAX %1 ; Use stdout
:_start_out
STORE32_Absolute32_eax &Output ; Preserve the file pointer we were given
LOADI32_EAX %45 ; the Syscall # for SYS_BRK
LOADI32_EBX %0 ; Get current brk
INT_80 ; Let the kernel do the work
COPY_EAX_to_EDI ; Set our malloc pointer
CALL32 %Tokenize_Line ; Get all lines
COPY_EBP_to_EAX ; prepare for Reverse_List
CALL32 %Reverse_List ; Correct order
COPY_EAX_to_EBP ; Update HEAD
CALL32 %Identify_Macros ; Find the DEFINEs
CALL32 %Line_Macro ; Apply the DEFINEs
CALL32 %Process_String ; Handle strings
CALL32 %Eval_Immediates ; Handle Numbers
CALL32 %Preserve_Other ; Collect the remaining
CALL32 %Print_Hex ; Output our results
:Done
; program completed Successfully
LOADI32_EBX %0 ; All is well
LOADI32_EAX %1 ; put the exit syscall number in eax
INT_80 ; 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
CALL32 %fgetc ; Read a char
CMPI8_EAX !-4 ; Check for EOF
JE32 %done ; File is collected
MOVZX_al ; We have to zero extend it to use it
COPY_EAX_to_ECX ; Protect C
LOADI32_EBX &comments ; Get pointer to "#;"
CALL32 %In_Set ; Check for comments
CMPI8_EAX !1 ; If comments
JE32 %Purge_LineComment ; try again
COPY_ECX_to_EAX ; put C in place for check
LOADI32_EBX &terminators ; Get pointer to "\n\t "
CALL32 %In_Set ; Check for terminators
CMPI8_EAX !1 ; If terminator
JE32 %restart ; try again
LOADI32_EAX %32 ; Malloc the struct P
CALL32 %malloc ; Get pointer to P
COPY_EAX_to_EDX ; Protect P
STORE32_EBP_into_Address_EDX ; P->NEXT = HEAD
COPY_EDX_to_EBP ; HEAD = P
COPY_ECX_to_EAX ; put C in place for check
LOADI32_EBX &string_char ; Get pointer to "\"'"
CALL32 %In_Set ; Check for string chars
CMPI8_EAX !1 ; If string char
JE32 %Store_String ; Get string
CALL32 %Store_Atom ; Get whole token
JMP32 %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
LOADI32_EAX %-4 ; Put EOF in eax
PUSH_EAX ; Assume bad (If nothing read, value will remain EOF)
LEA32_ECX_from_esp ; Get stack address
LOAD32_Absolute32_ebx &Input ; Where are we reading from
LOADI32_EAX %3 ; the syscall number for read
LOADI32_EDX %1 ; set the size of chars we want
INT_80 ; 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
COPY_EDI_to_EBX ; Using the current pointer
ADD_EAX_to_EBX ; Request the number of desired bytes
LOADI32_EAX %45 ; the Syscall # for SYS_BRK
INT_80 ; call the Kernel
COPY_EDI_to_EAX ; Return pointer
COPY_EBX_to_EDI ; 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
CALL32 %fgetc ; Get a char
MOVZX_al ; Zero extend
CMPI8_EAX !10 ; While not LF
JNE32 %Purge_LineComment ; Keep reading
JMP32 %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
LOADI32_EAX %2 ; Using TYPE STRING
STORE32_EAX_into_Address_EDX_Immediate8 !8 ; HEAD->TYPE = STRING
LOADI32_EAX %256 ; Malloc the string
CALL32 %malloc ; Get pointer to P
STORE32_EAX_into_Address_EDX_Immediate8 !16 ; HEAD->TEXT = STRING
COPY_ECX_to_EBX ; Protect terminator
COPY_EAX_to_EDX ; Protect string pointer
:Store_String_Loop
STORE8_cl_into_Address_EDX ; write byte
CALL32 %fgetc ; read next char
MOVZX_al ; Zero extend it
COPY_EAX_to_ECX ; Update C
ADDI8_EDX !1 ; STRING = STRING + 1
CMP_EBX_ECX ; See if we hit terminator
JNE32 %Store_String_Loop ; Otherwise keep looping
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
COPY_EDX_to_EAX ; return HEAD
JMP32 %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
LOADI32_EAX %256 ; Malloc the string
CALL32 %malloc ; Get pointer to P
STORE32_EAX_into_Address_EDX_Immediate8 !16 ; HEAD->TEXT = STRING
LOADI32_EBX &terminators ; Get pointer to "\n\t "
COPY_EAX_to_EDX ; Protect string pointer
:Store_Atom_loop
STORE8_cl_into_Address_EDX ; write byte
CALL32 %fgetc ; read next char
MOVZX_al ; Zero extend it
COPY_EAX_to_ECX ; Update C
ADDI8_EDX !1 ; STRING = STRING + 1
CALL32 %In_Set ; Check for terminators
CMPI8_EAX !0 ; Check for "\n\t "
JE32 %Store_Atom_loop ; Loop otherwise
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
COPY_EDX_to_EAX ; 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
LOAD8_cl_from_EBX ; Read char
MOVZX_cl ; Zero extend it
CMP_EAX_ECX ; See if they match
JE32 %In_Set_True ; return true
CMPI8_ECX !0 ; Check for NULL
JE32 %In_Set_False ; return false
ADDI8_EBX !1 ; s = s + 1
JMP32 %In_Set_loop ; Keep looping
:In_Set_True
LOADI32_EAX %1 ; Set True
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RET
:In_Set_False
LOADI32_EAX %0 ; Set FALSE
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RET
;; Char sets
:terminators
"
"
:comments
";#"
:string_char
'22 27 00'
;; Reverse_List function
;; Receives List in EAX
;; Returns the list reversed in EAX
:Reverse_List
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EAX_to_EBX ; Set HEAD
LOADI32_EAX %0 ; ROOT = NULL
:Reverse_List_Loop
CMPI8_EBX !0 ; WHILE HEAD != NULL
JE32 %Reverse_List_Done ; Stop otherwise
LOAD32_ECX_from_EBX ; NEXT = HEAD->NEXT
STORE32_EAX_into_Address_EBX ; HEAD->NEXT = ROOT
COPY_EBX_to_EAX ; ROOT = HEAD
COPY_ECX_to_EBX ; HEAD = NEXT
JMP32 %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
LOADI32_EBX &DEFINE_str ; Setup define string
COPY_EAX_to_ECX ; I = HEAD
:Identify_Macros_Loop
LOAD32_EAX_from_ECX_Immediate8 !16 ; I->TEXT
CALL32 %match ; IF "DEFINE" == I->TEXT
CMPI8_EAX !0 ; Check if match
JNE32 %Identify_Macros_Next ; Skip the work
;; Deal with MACRO
LOADI32_EAX %1 ; Using MACRO
STORE32_EAX_into_Address_ECX_Immediate8 !8 ; I->TYPE = MACRO
LOAD32_EAX_from_ECX ; I->NEXT
LOAD32_EAX_from_EAX_Immediate8 !16 ; I->NEXT->TEXT
STORE32_EAX_into_Address_ECX_Immediate8 !16 ; I->TEXT = I->NEXT->TEXT
LOAD32_EAX_from_ECX ; I->NEXT
LOAD32_EAX_from_EAX ; I->NEXT->NEXT
LOAD32_EAX_from_EAX_Immediate8 !16 ; I->NEXT->NEXT->TEXT
STORE32_EAX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = I->NEXT->NEXT->TEXT
LOAD32_EAX_from_ECX ; I->NEXT
LOAD32_EAX_from_EAX ; I->NEXT->NEXT
LOAD32_EAX_from_EAX ; I->NEXT->NEXT->NEXT
STORE32_EAX_into_Address_ECX ; I->NEXT = I->NEXT->NEXT->NEXT
:Identify_Macros_Next
LOAD32_ECX_from_ECX ; I = I->NEXT
CMPI8_ECX !0 ; Check for NULL
JNE32 %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
"DEFINE"
;; 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
COPY_EAX_to_ECX ; S1 in place
COPY_EBX_to_EDX ; S2 in place
:match_Loop
LOAD8_al_from_ECX ; S1[0]
MOVZX_al ; Make it useful
LOAD8_bl_from_EDX ; S2[0]
MOVZX_bl ; Make it useful
CMP_EAX_EBX ; See if they match
JNE32 %match_False ; If not
ADDI8_ECX !1 ; S1 = S1 + 1
ADDI8_EDX !1 ; S2 = S2 + 1
CMPI8_EAX !0 ; If reached end of string
JE32 %match_Done ; Perfect match
JMP32 %match_Loop ; Otherwise keep looping
:match_False
LOADI32_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
LOAD32_EBX_from_EAX_Immediate8 !8 ; I->TYPE
CMPI8_EBX !1 ; IF MACRO == I->TYPE
JNE32 %Line_Macro_Next ; Otherwise move on
;; Is a macro apply
LOAD32_EBX_from_EAX_Immediate8 !16 ; I->TEXT
LOAD32_ECX_from_EAX_Immediate8 !24 ; I->EXPRESSION
LOAD32_EAX_from_EAX ; I->NEXT
CALL32 %Set_Expression ; Apply it
JMP32 %Line_Macro_Loop ; Move on to next
:Line_Macro_Next
LOAD32_EAX_from_EAX ; I->NEXT
CMPI8_EAX !0 ; Check for NULL
JNE32 %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
COPY_EAX_to_EDX ; Set I
:Set_Expression_Loop
LOAD32_EAX_from_EDX_Immediate8 !8 ; I->TYPE
CMPI8_EAX !1 ; IF MACRO == I->TYPE
JE32 %Set_Expression_Next ; Ignore and move on
LOAD32_EAX_from_EDX_Immediate8 !16 ; I->TEXT
CALL32 %match ; Check for match
CMPI8_EAX !0 ; If match
JNE32 %Set_Expression_Next ; Otherwise next
;; We have a non-macro match
STORE32_ECX_into_Address_EDX_Immediate8 !24 ; I->EXPRESSION = EXP
:Set_Expression_Next
LOAD32_EDX_from_EDX ; I = I->NEXT
CMPI8_EDX !0 ; IF NULL == I
JNE32 %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
COPY_EAX_to_ECX ; I = HEAD
:Process_String_loop
LOAD32_EAX_from_ECX_Immediate8 !8 ; I->TYPE
CMPI8_EAX !2 ; IF STRING == I->TYPE
JNE32 %Process_String_Next ; Skip to next
LOAD32_EBX_from_ECX_Immediate8 !16 ; I->TEXT
LOAD8_al_from_EBX ; I->TEXT[0]
MOVZX_al ; make it useful
CMPI8_EAX !39 ; IF '\'' == I->TEXT[0]
JNE32 %Process_String_Raw ; Deal with '\"'
;; Deal with '\''
ADDI8_EBX !1 ; I->TEXT + 1
STORE32_EBX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = I->TEXT + 1
JMP32 %Process_String_Next ; Move on to next
:Process_String_Raw
COPY_EBX_to_EAX ; Get length of I->TEXT
CALL32 %string_length ; Do it
SHRI8_EAX !2 ; LENGTH = LENGTH >> 2
ADDI8_EAX !1 ; LENGTH = LENGTH + 1
SHLI8_EAX !3 ; LENGTH = LENGTH << 3
CALL32 %malloc ; Get string
COPY_EBX_to_EDX ; S = I->TEXT
ADDI8_EDX !1 ; S = S + 1
STORE32_EAX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = hexify
COPY_EAX_to_EBX ; Put hexify buffer in ebx
:Process_String_Raw_Loop
LOAD8_al_from_EDX ; Read 1 chars
MOVZX_al ; Make it useful
ADDI8_EDX !1 ; S = S + 1
CMPI8_AL !0 ; Check for NULL
PUSH_FLAGS ; Protect condition
CALL32 %hex8 ; write them all
POP_FLAGS ; restore condition
JNE32 %Process_String_Raw_Loop ; Keep looping
:Process_String_Next
LOAD32_ECX_from_ECX ; I = I->NEXT
CMPI8_ECX !0 ; IF NULL == I
JNE32 %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
COPY_EAX_to_EBX ; Set S
LOADI32_ECX %0 ; INDEX = 0
:string_length_loop
LOAD8_al_from_EBX_indexed_ECX ; S[0]
MOVZX_al ; make it useful
CMPI8_EAX !0 ; IF NULL == S[0]
JE32 %string_length_done ; Stop
ADDI8_ECX !1 ; INDEX = INDEX + 1
JMP32 %string_length_loop ; Keep going
:string_length_done
COPY_ECX_to_EAX ; 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
COPY_EAX_to_EDX ; I = HEAD
:Eval_Immediates_Loop
;; Check for MACRO
LOAD32_EAX_from_EDX_Immediate8 !8 ; I->TYPE
CMPI8_EAX !1 ; IF MACRO == I-TYPE
JE32 %Eval_Immediates_Next ; Skip to next
;; Check for NULL EXPRESSION
LOAD32_EAX_from_EDX_Immediate8 !24 ; I->EXPRESSION
CMPI8_EAX !0 ; IF NULL == I->EXPRESSION
JNE32 %Eval_Immediates_Next ; Skip to next
;; Check if number
LOAD32_EAX_from_EDX_Immediate8 !16 ; I->TEXT
LOAD8_bl_from_EAX ; I->TEXT[0]
MOVZX_bl ; Extend to use
ADDI8_EAX !1 ; I->TEXT + 1
LOAD8_cl_from_EAX ; I->TEXT[1]
MOVZX_cl ; Extend to use
CALL32 %numerate_string ; Convert string to INT
CMPI8_EAX !0 ; IF 0 == numerate_number(I->TEXT + 1)
JNE32 %Eval_Immediates_value ; Has a value
;; Last chance for Immediate
CMPI8_ECX !48 ; If '0' == I->TEXT[1]
JNE32 %Eval_Immediates_Next ; Skip to next
:Eval_Immediates_value
CALL32 %express_number ; Convert value to hex string
STORE32_EAX_into_Address_EDX_Immediate8 !24 ; I->EXPRESSION = express_number(value, I-TEXT[0])
:Eval_Immediates_Next
LOAD32_EDX_from_EDX ; I = I->NEXT
CMPI8_EDX !0 ; IF NULL == I
JNE32 %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
COPY_EAX_to_EBX ; put S in correct place
LOADI32_EAX %0 ; Initialize to Zero
:numerate_string_loop
LOAD8_cl_from_EBX_Immediate8 !1 ; S[1]
MOVZX_cl ; make it useful
CMPI8_ECX !120 ; IF 'x' == S[1]
JE32 %numerate_hex ; Deal with hex input
;; Assume decimal input
LOADI32_ECX %0 ; Assume no negation
LOAD8_cl_from_EBX ; S[0]
MOVZX_cl ; make it useful
CMPI8_ECX !45 ; IF '-' == S[0]
JNE32 %numerate_decimal ; Skip negation
LOADI32_EDI %1 ; Set FLAG
ADDI8_EBX !1 ; S = S + 1
:numerate_decimal
LOAD8_cl_from_EBX ; S[0]
MOVZX_cl ; make it useful
CMPI8_ECX !0 ; IF NULL == S[0]
JE32 %numerate_decimal_done ; We are done
IMULI8_EAX !10 ; VALUE = VALUE * 10
SUBI8_ECX !48 ; CH = CH - '0'
CMPI8_ECX !9 ; Check for illegal
JG32 %numerate_string_fail ; If CH > '9'
CMPI8_ECX !0 ; Check for illegal
JL32 %numerate_string_fail ; IF CH < 0
ADD_ECX_to_EAX ; VALUE = VALUE + CH
ADDI8_EBX !1 ; S = S + 1
JMP32 %numerate_decimal ; Keep looping
:numerate_decimal_done
CMPI8_EDI !1 ; Check if need to negate
JNE32 %numerate_string_done ; Nope
IMULI8_EAX !-1 ; VALUE = VALUE * -1
JMP32 %numerate_string_done ; Done
:numerate_hex
ADDI8_EBX !2 ; S = S + 2
:numerate_hex_loop
LOAD8_cl_from_EBX ; S[0]
MOVZX_cl ; make it useful
CMPI8_ECX !0 ; IF NULL == S[0]
JE32 %numerate_string_done ; We are done
SHLI8_EAX !4 ; VALUE = VALUE << 4
SUBI8_ECX !48 ; CH = CH - '0'
CMPI8_ECX !10 ; IF 10 >= CH
JL32 %numerate_hex_digit ; NO
SUBI8_ECX !7 ; Push A-F into range
:numerate_hex_digit
CMPI8_ECX !15 ; Check for illegal
JG32 %numerate_string_fail ; If CH > 'F'
CMPI8_ECX !0 ; Check for illegal
JL32 %numerate_string_fail ; IF CH < 0
ADD_ECX_to_EAX ; VALUE = VALUE + CH
ADDI8_EBX !1 ; S = S + 1
JMP32 %numerate_hex_loop ; Keep looping
:numerate_string_fail
LOADI32_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
COPY_EBX_to_ECX ; Put CH in right place
COPY_EAX_to_EBX ; Protect VALUE
CMPI8_ECX !37 ; IF '%' == CH
JNE32 %express_number2 ; Otherwise try @
LOADI32_EAX %9 ; We need 3bytes
CALL32 %malloc ; Get S pointer
XCHG_EAX_EBX ; Put S and VALUE in place
PUSH_EBX ; Protect S
CALL32 %hex32l ; Store 32bits
JMP32 %express_number_done ; done
:express_number2
CMPI8_ECX !64 ; IF '@' == CH
JNE32 %express_number1 ; Othrewise try !
LOADI32_EAX %5 ; We need 3bytes
CALL32 %malloc ; Get S pointer
XCHG_EAX_EBX ; Put S and VALUE in place
PUSH_EBX ; Protect S
CALL32 %hex16l ; Store 16bits
JMP32 %express_number_done ; done
:express_number1
LOADI32_EAX %3 ; We need 3bytes
CALL32 %malloc ; Get S pointer
XCHG_EAX_EBX ; Put S and VALUE in place
PUSH_EBX ; Protect S
CALL32 %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
CALL32 %hex32l ; Store it
POP_EAX ; do top 32
SHRI8_EAX !32 ; do bottom 32 first
:hex32l
PUSH_EAX ; Protect top 16
CALL32 %hex16l ; Store it
POP_EAX ; do top 16
SHRI8_EAX !16 ; do bottom 16 first
:hex16l
PUSH_EAX ; Protect top byte
CALL32 %hex8 ; Store it
POP_EAX ; do high byte
SHRI8_EAX !8 ; do bottom byte first
:hex8
PUSH_EAX ; Protect bottom nibble
SHRI8_EAX !4 ; do high nibble first
CALL32 %hex4 ; Store it
POP_EAX ; do low nibble
:hex4
ANDI8_EAX !0xF ; isolate nibble
ADDI8_AL !48 ; convert to ascii
CMPI8_AL !57 ; valid digit?
JBE32 %hex1 ; yes
ADDI8_AL !7 ; use alpha range
:hex1
STORE8_al_into_Address_EBX ; store result
ADDI8_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
LOAD32_EBX_from_EAX_Immediate8 !24 ; I->EXPRESSION
CMPI8_EBX !0 ; IF NULL == I->EXPRESSION
JNE32 %Preserve_Other_Next ; Otherwise next
;; Needs preserving
LOAD32_EBX_from_EAX_Immediate8 !16 ; I->TEXT
STORE32_EBX_into_Address_EAX_Immediate8 !24 ; I->EXPRESSION = I->TEXT
:Preserve_Other_Next
LOAD32_EAX_from_EAX ; I = I->NEXT
CMPI8_EAX !0 ; IF NULL == I
JNE32 %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
COPY_EBP_to_EBX ; I = Head
:Print_Hex_Loop
LOAD32_EAX_from_EBX_Immediate8 !8 ; I->TYPE
CMPI8_EAX !1 ; IF MACRO == I->TYPE
JE32 %Print_Hex_Next ; Skip
LOAD32_EAX_from_EBX_Immediate8 !24 ; Using EXPRESSION
CALL32 %File_Print ; Print it
LOADI32_EAX %10 ; NEWLINE
CALL32 %fputc ; Append it
:Print_Hex_Next
LOAD32_EBX_from_EBX ; Iterate to next Token
CMPI8_EBX !0 ; Check for NULL
JNE32 %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
COPY_EAX_to_EBX ; Protect S
CMPI8_EAX !0 ; Protect against nulls
JE32 %File_Print_Done ; Simply don't try to print them
:File_Print_Loop
LOAD8_al_from_EBX ; Read byte
MOVZX_al ; zero extend
CMPI8_EAX !0 ; Check for NULL
JE32 %File_Print_Done ; Stop at NULL
CALL32 %fputc ; write it
ADDI8_EBX !1 ; S = S + 1
JMP32 %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
LEA32_ECX_from_esp ; Get stack address
LOAD32_Absolute32_ebx &Output ; Write to target file
LOADI32_EAX %4 ; the syscall number for write
LOADI32_EDX %1 ; set the size of chars we want
INT_80 ; call the Kernel
POP_EAX ; Restore stack
POP_EBX ; Restore EBX
POP_ECX ; Restore ECX
POP_EDX ; Restore EDX
RET
:Output
NULL
:Input
NULL
:ELF_end