stage0/Linux Bootstrap/x86/hex2_x86.S

595 lines
15 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, EDX, ECX, EBX => Temps
;; EDI => IP
;; EBP => MALLOC
;; ESI => HEAD
;; Struct format: (size 24)
;; NEXT => 0
;; TARGET => 8
;; NAME => 16
; Where the ELF Header is going to hit
; Simply jump to _start
; Our main function
_start:
mov ebx, 0 ; Get current pointer
call malloc ; Get current HEAP
mov ebx, eax ; Using current
mov ebp, eax ; Setup MALLOC
add ebx, 8192000 ; Create space for temp
call malloc ; Give ourselves 8192000 bytes to work with
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 edx, 0 ; Really sure
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
call ClearScratch ; Zero scratch
mov eax, -1 ; Our flag for byte processing
mov [Flag], eax ; Set
mov eax, 0 ; temp storage for the sum
mov [High], eax ; Set
mov edi, 0x8048000 ; Our starting IP
mov esi, 0 ; HEAD = NULL
call First_pass ; Process it
; rewind input file
mov ebx, [Input] ; Using our input file
mov ecx, 0 ; Offset Zero
mov edx, 0 ; Whence Zero
mov eax, 19 ; lseek
push esi ; Protect HEAD
int 0x80
pop esi ; Restore HEAD
mov eax, -1 ; Our flag for byte processing
mov [Flag], eax ; Set
mov eax, 0 ; temp storage for the sum
mov [High], eax ; Set
mov edi, 0x8048000 ; Our starting IP
call Second_pass ; Process it
jmp Done
First_pass:
call Read_byte
; Deal with EOF
cmp eax, -4
je First_pass_done
; Check for :
cmp eax, 0x3a
jne First_pass_0
; Deal with label
jmp StoreLabel
First_pass_0:
; Check for !
cmp eax, 0x21
je First_pass_pointer
; Check for @
cmp eax, 0x40
je First_pass_pointer
; Check for $
cmp eax, 0x24
je First_pass_pointer
; Check for %
cmp eax, 0x25
je First_pass_pointer
; Check for &
cmp eax, 0x26
je First_pass_pointer
; Deal with everything else
call hex ; Process our char
; Deal with EOF
cmp eax, -4
je First_pass_done
; deal with -1 values
cmp eax, 0
jl First_pass
; deal with toggle
mov eax, [Flag]
cmp eax, 0
je First_pass_1
add edi, 1 ; Increment IP
First_pass_1:
not eax
mov [Flag], eax
jmp First_pass
Update_Pointer:
; Check for !
cmp eax, 0x21
je Update_Pointer_1
; Check for @
cmp eax, 0x40
je Update_Pointer_2
; Check for $
cmp eax, 0x24
je Update_Pointer_2
; Check for %
cmp eax, 0x25
je Update_Pointer_4
; Check for &
cmp eax, 0x26
je Update_Pointer_4
;; deal with bad input
call fail
Update_Pointer_4:
add edi, 2 ; Increment IP
Update_Pointer_2:
add edi, 1 ; Increment IP
Update_Pointer_1:
add edi, 1 ; Increment IP
ret
First_pass_pointer:
; Deal with Pointer to label
call Update_Pointer ; Increment IP
mov ebx, table ; Using scratch
call consume_token ; Read token
call ClearScratch ; Throw away token
cmp eax, 0x3E ; check for '>'
jne First_pass ; Loop again
;; Deal with %label>label case
mov ebx, table ; Write to scratch
call consume_token ; get token
call ClearScratch ; Clean up after ourselves
jmp First_pass ; Loop again
First_pass_done:
RET
hex:
; deal with EOF
cmp eax, -4
je EOF
; deal with line comments starting with ;
cmp eax, 0x23
je ascii_comment
; deal with line comments starting with ;
cmp eax, 0x3b
je ascii_comment
; deal all ascii less than 0
cmp eax, 0x30
jl ascii_other
; deal with 0-9
cmp eax, 0x3a
jl ascii_num
; deal with all ascii less than A
cmp eax, 0x41
jl ascii_other
; deal with A-F
cmp eax, 0x47
jl ascii_high
;deal with all ascii less than a
cmp eax, 0x61
jl ascii_other
;deal with a-f
cmp eax, 0x67
jl ascii_low
; The rest that remains needs to be ignored
jmp ascii_other
Second_pass:
call Read_byte
; Deal with EOF
cmp eax, -4
je Second_pass_done
; Simply drop the label
cmp eax, 0x3a
jne Second_pass_0
mov ebx, table ; Using scratch
call consume_token ; Read token
call ClearScratch ; Throw away token
jmp Second_pass
Second_pass_0:
; Deal with % pointer
cmp eax, 0x25
je StorePointer_rel4
; Deal with @ pointer
cmp eax, 0x40
je StorePointer_rel2
; Deal with ! pointer
cmp eax, 0x21
je StorePointer_rel1
; Deal with & pointer
cmp eax, 0x26
je StorePointer_abs4
; Deal with $ pointer
cmp eax, 0x24
je StorePointer_abs2
Second_pass_1:
; Deal with everything else
call hex ; Process our char
; Deal with EOF
cmp eax, -4
je Second_pass_done
; deal with -1 values
cmp eax, 0
jl Second_pass
; deal with toggle
mov ebx, [Flag]
cmp ebx, 0
je print
; process first byte of pair
shl eax, 4
mov [High], eax
mov eax, 0
mov [Flag], eax
jmp Second_pass
Second_pass_done:
ret
EOF:
ret
ascii_num:
sub eax, 0x30
ret
ascii_low:
sub eax, 0x57
ret
ascii_high:
sub eax, 0x37
ret
ascii_other:
mov eax, -1
ret
ascii_comment:
call Read_byte
cmp eax, 0xd
je ascii_comment_cr
cmp eax, 0xa
jne ascii_comment
ascii_comment_cr:
mov eax, -1
ret
; process second byte of pair
print:
; update the sum and store in output
add eax, [High]
mov [table], al
; flip the toggle
mov eax, [Flag]
not eax
mov [Flag], eax
; Print our first Hex
mov edx, 1 ; set the size of chars we want
call print_chars
add edi, 1 ; Increment IP
jmp Second_pass
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
;; 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 a value in EBX
malloc:
mov eax, 45 ; the Syscall # for SYS_BRK
push esi ; Protect esi
push edi ; Protect edi
int 0x80 ; call the Kernel
pop edi ; Restore edi
pop esi ; Restore esi
ret
Read_byte:
; Attempt to read 1 byte from STDIN
push esi ; Protect esi
push edi ; Protect edi
push ebx ; Protect ebx
push ecx ; Protect ecx
mov edx, 1 ; set the size of chars we want
mov ecx, write ; Where to put it
mov ebx, [Input] ; Where are we reading from
mov eax, 3 ; the syscall number for read
int 0x80 ; call the Kernel
pop ecx ; Restore ecx
pop ebx ; Restore ebx
pop edi ; Restore edi
pop esi ; Restore esi
test eax, eax ; check what we got
je Read_byte_1 ; Got EOF call it done
; load byte
mov al, [write] ; load char
movzx eax, al ; We have to zero extend it to use it
ret
; Deal with EOF
Read_byte_1:
mov eax, -4 ; Put EOF in eax
ret
print_chars:
push esi ; Protect esi
push edi ; Protect edi
push ebx ; Protect ebx
push ecx ; Protect ecx
mov ecx, table ; What we are writing
mov ebx, [Output] ; Write to target file
mov eax, 4 ; the syscall number for write
int 0x80 ; call the Kernel
pop ecx ; Restore ecx
pop ebx ; Restore ebx
pop edi ; Restore edi
pop esi ; Restore esi
ret
;; Receives pointer in EBX
;; Writes out char and updates EBX
consume_token:
call Read_byte ; Consume_token
; Check for \t
cmp eax, 0x09
je consume_token_done
; Check for \n
cmp eax, 0x0A
je consume_token_done
; Check for ' '
cmp eax, 0x20
je consume_token_done
; Check for '>'
cmp eax, 0x3E
je consume_token_done
;; Looks like we are still reading token
mov [ebx], al ; Store char
add ebx, 1 ; Point to next spot
jmp consume_token ; loop until done
consume_token_done:
mov ecx, 0 ; Padd with nulls
mov [ebx], ecx
add ebx, 4
ret
StoreLabel:
mov eax, ebp ; ENTRY
add ebp, 24 ; CALLOC
mov [eax+8], edi ; ENTRY->TARGET = IP
mov [eax], esi ; ENTRY->NEXT = JUMP_TABLE
mov esi, eax ; JUMP_TABLE = ENTRY
mov [esi+16], ebp ; ENTRY->NAME = TOKEN
mov ebx, ebp ; Write Starting after struct
call consume_token ; Collect whole string
mov ebp, ebx ; Update HEAP
jmp First_pass
GetTarget:
push ebx ; protect ebx
push ecx ; protect ecx
push edx ; protect edx
push esi ; protect JUMP_TABLE
mov ecx, table ; Reset scratch
mov edx, [esi+16] ; I->NAME
GetTarget_loop:
mov al, [ecx] ; I->NAME[0]
mov bl, [edx] ; scratch[0]
movzx ebx, bl ; Zero extend
movzx eax, al ; Zero extend
cmp al, bl ; IF TOKEN == I->NAME
jne GetTarget_miss ; Oops
add ecx, 1
add edx, 1
cmp al, 0
jne GetTarget_loop ; Loop until
jmp GetTarget_done ; Match
;; Miss
GetTarget_miss:
mov esi, [esi] ; I = I->NEXT
cmp esi, 0 ; IF NULL == I
je fail ; Abort hard
mov edx, [esi+16] ; I->NAME
mov ecx, table ; Reset scratch
jmp GetTarget_loop
GetTarget_done:
mov eax, [esi+8] ; Get address
pop esi ; Restore JUMP_TABLE
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
ClearScratch:
push eax ; Protect against changes
push ebx ; And overwrites
push ecx ; While we work
mov ebx, table ; Where our table is
mov al, 0 ; Using null
ClearScratch_loop:
mov ecx, [ebx] ; Get current value
mov [ebx], al ; Because we want null
add ebx, 1 ; Increment
cmp ecx, 0 ; Check if we hit null
jne ClearScratch_loop ; Keep looping
pop ecx ; Restore
pop ebx ; Damage
pop eax ; Entirely
ret
StorePointer:
call Update_Pointer ; Increment IP
mov ebx, table ; Write to scratch
call consume_token ; get token
push eax ; Protect base_sep_p
mov eax, table ; Pointer to scratch
call GetTarget ; Get address of pointer
call ClearScratch ; Clean up after ourselves
mov edx, edi ; base = IP
pop ebx ; Restore base_sep_p
cmp ebx, 0x3E ; If base_sep_p == '>'
jne StorePointer_done ; If not
;; Deal with %label>label case
push eax ; We need to preserve main target
mov ebx, table ; Write to scratch
call consume_token ; get token
mov eax, table ; Pointer to scratch
call GetTarget ; Get address of pointer
call ClearScratch ; Clean up after ourselves
mov edx, eax ; Use our new base
pop eax ; Restore main target
StorePointer_done:
ret
StorePointer_rel4:
call StorePointer ; Do Common
sub eax, edx ; target - ip
mov [table], eax ; put value in output
mov edx, 4 ; set the size of chars we want
call print_chars
call ClearScratch ; Clean up after ourselves
jmp Second_pass
StorePointer_rel2:
call StorePointer ; Do Common
sub eax, edx ; target - ip
mov [table], eax ; put value in output
mov edx, 2 ; set the size of chars we want
call print_chars
call ClearScratch ; Clean up after ourselves
jmp Second_pass
StorePointer_rel1:
call StorePointer ; Do Common
sub eax, edx ; target - ip
mov [table], eax ; put value in output
mov edx, 1 ; set the size of chars we want
call print_chars
call ClearScratch ; Clean up after ourselves
jmp Second_pass
StorePointer_abs4:
call StorePointer ; Do Common
mov [table], eax ; put value in output
mov edx, 4 ; set the size of chars we want
call print_chars
call ClearScratch ; Clean up after ourselves
jmp Second_pass
StorePointer_abs2:
call StorePointer ; Do Common
mov [table], eax ; put value in output
mov edx, 2 ; set the size of chars we want
call print_chars
call ClearScratch ; Clean up after ourselves
jmp Second_pass
fail:
; Some shit went wrong
mov ebx, 1 ; All is wrong
mov eax, 1 ; put the exit syscall number in eax
int 0x80 ; Call it a good day
section .data
ELF_end:
High:
dd 0
Flag:
dd 0
Input:
dd 0
Output:
dd 0
write:
dd 0
table:
dd 66