### Copyright (C) 2016 Jeremiah Orians ### Copyright (C) 2017 Jan Nieuwenhuizen ### 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 . DEFINE ADDI8_to_R12 4983C4 DEFINE ADDI8_to_R13 4983C5 DEFINE ADDI8_to_RBX 4883C3 DEFINE ADDI8_to_RSI 4883C6 DEFINE ADDI8_to_RDI 4883C7 DEFINE ADDI32_to_RDI 4881C7 DEFINE ADD_R14_to_RAX 4C01F0 DEFINE CALLI32 E8 DEFINE CMP_AL_to_BL 38D8 DEFINE CMP_RAX_Immediate8 4883F8 DEFINE CMP_RBX_Immediate8 4883FB DEFINE CMP_RCX_Immediate8 4883F9 DEFINE CMP_R15_Immediate8 4983FF DEFINE COPY_R9_to_RDI 4C89CF DEFINE COPY_R10_to_RDI 4C89D7 DEFINE COPY_R11_to_RCX 4C89D9 DEFINE COPY_R12_to_RAX 4C89E0 DEFINE COPY_R12_to_RBX 4C89E3 DEFINE COPY_R13_to_RDX 4C89EA DEFINE COPY_RBX_to_R12 4989DC DEFINE COPY_RAX_to_R9 4989C1 DEFINE COPY_RAX_to_R10 4989C2 DEFINE COPY_RAX_to_R11 4989C3 DEFINE COPY_RAX_to_R12 4989C4 DEFINE COPY_RAX_to_R14 4989C6 DEFINE COPY_RAX_to_RDX 4889C2 DEFINE COPY_RAX_to_RDI 4889C7 DEFINE JE32 0F84 DEFINE JG32 0F8F DEFINE JL32 0F8C DEFINE JMP32 E9 DEFINE JNE32 0F85 DEFINE STORE32_from_RAX_into_ABS32 48890425 DEFINE LOAD8_AL_from_Address_RSI 8A06 DEFINE LOAD8_BL_from_Address_RDI 8A1F DEFINE LOAD8_AL_from_Absolute32 8A0425 DEFINE LOAD32_into_RSI_from_Address_RCX_Immediate8 488B71 DEFINE LOAD32_into_RAX_from_Address_RCX_Immediate8 488B41 DEFINE LOAD32_into_RCX_from_Address_RCX 488B09 DEFINE LOAD32_into_RCX_from_Address_RBX 488B0B DEFINE LOADI32_RAX 48C7C0 DEFINE LOADI32_RBX BB DEFINE LOADI32_RCX B9 DEFINE LOADI32_RDI BF DEFINE LOADI32_RDX BA DEFINE LOADI32_RSI BE DEFINE LOADI32_R11 41BB DEFINE LOADI32_R13 41BD DEFINE LOADI32_R14 41BE DEFINE LOADI32_R15 49C7C7 DEFINE NOT_R15 49F7D7 DEFINE NULL 00000000 DEFINE POP_RAX 58 DEFINE POP_RBX 5B DEFINE POP_RCX 59 DEFINE POP_RDI 5F DEFINE POP_R11 415B DEFINE PUSH_R11 4153 DEFINE PUSH_RAX 50 DEFINE PUSH_RBX 53 DEFINE PUSH_RCX 51 DEFINE RET C3 DEFINE SUB_RDX_from_RAX 4829D0 DEFINE SHL_R14_Immediate8 49C1E6 DEFINE STORE8_AL_into_Absolute32 880425 DEFINE STORE8_al_into_Address_RBX 8803 DEFINE STORE32_RCX_into_Address_RBX 48890B DEFINE STORE32_R11_into_Address_RAX 4C8918 DEFINE STORE32_R13_into_Address_RAX_Immediate8 4C8968 DEFINE STORE32_R12_into_Address_R11_Immediate8 4D8963 DEFINE SUBI8_RAX 83E8 DEFINE SYSCALL 0F05 DEFINE TEST_RAX_RAX 4885C0 DEFINE ZERO_EXTEND_AL 480FB6C0 DEFINE ZERO_EXTEND_BL 480FB6DB # Where the ELF Header is going to hit # Simply jump to _start # Our main function ;; Register usage: ;; RAX, RDX, RSI, RDI => Temps ;; R15 => Flag ;; R14 => High bits ;; R13 => IP ;; R12 => MALLOC ;; R11 => HEAD ;; Struct format: (size 24) ;; NEXT => 0 ;; TARGET => 8 ;; NAME => 16 :_start LOADI32_RDI %0 ; Get current pointer CALLI32 %malloc ; Get current HEAP COPY_RAX_to_RDI ; Using current COPY_RAX_to_R12 ; Setup MALLOC ADDI32_to_RDI %8192000 ; Create space for temp CALLI32 %malloc ; Give ourselves 8192000 bytes to work with POP_RAX ;·Get·the·number·of·arguments POP_RDI ;·Get·the·program·name POP_RDI ;·Get·the·actual·input name LOADI32_RSI %0 ;·prepare·read_only LOADI32_RAX %2 ;·the·syscall·number·for·open() SYSCALL ; Now open that damn file COPY_RAX_to_R9 ; Preserve the file pointer we were given POP_RDI ;·Get·the·actual·output name LOADI32_RSI %577 ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC LOADI32_RDX %448 ; Prepare file as RWX for owner only (700 in octal) LOADI32_RAX %2 ;·the·syscall·number·for·open() SYSCALL ; Now open that damn file CMP_RAX_Immediate8 !0 ; Check for missing output JG32 %_start_out ; Have real input LOADI32_RAX %1 ; Use stdout :_start_out COPY_RAX_to_R10 ; Preserve the file pointer we were given CALLI32 %ClearScratch ; Zero scratch LOADI32_R15 %-1 ; Our flag for byte processing LOADI32_R14 %0 ; temp storage for the sum LOADI32_R13 %0x00600000 ; Our starting IP LOADI32_R11 %0 ; HEAD = NULL CALLI32 %First_pass ; Process it ; rewind input file COPY_R9_to_RDI ; Using our input file LOADI32_RSI %0 ; Offset Zero LOADI32_RDX %0 ; Whence Zero LOADI32_RAX %8 ; lseek PUSH_R11 ; Protect HEAD SYSCALL POP_R11 ; Restore HEAD LOADI32_R15 %-1 ; Our flag for byte processing LOADI32_R14 %0 ; temp storage for the sum LOADI32_R13 %0x00600000 ; Our starting IP CALLI32 %Second_pass ; Process it JMP32 %Done :First_pass CALLI32 %Read_byte ; Deal with EOF CMP_RAX_Immediate8 !-4 JE32 %First_pass_done ; Check for : CMP_RAX_Immediate8 !0x3A JNE32 %First_pass_0 ; Deal with label JMP32 %StoreLabel :First_pass_0 ; Check for ! CMP_RAX_Immediate8 !0x21 JE32 %First_pass_pointer ; Check for @ CMP_RAX_Immediate8 !0x40 JE32 %First_pass_pointer ; Check for $ CMP_RAX_Immediate8 !0x24 JE32 %First_pass_pointer ; Check for % CMP_RAX_Immediate8 !0x25 JE32 %First_pass_pointer ; Check for & CMP_RAX_Immediate8 !0x26 JE32 %First_pass_pointer ; Deal with everything else CALLI32 %hex ; Process our char ; Deal with EOF CMP_RAX_Immediate8 !-4 JE32 %First_pass_done ; deal with -1 values CMP_RAX_Immediate8 !0 JL32 %First_pass ; deal with toggle CMP_R15_Immediate8 !0 JE32 %First_pass_1 ADDI8_to_R13 !1 ; Increment IP :First_pass_1 NOT_R15 JMP32 %First_pass :Update_Pointer ; Check for ! CMP_RAX_Immediate8 !0x21 JE32 %Update_Pointer_1 ; Check for @ CMP_RAX_Immediate8 !0x40 JE32 %Update_Pointer_2 ; Check for $ CMP_RAX_Immediate8 !0x24 JE32 %Update_Pointer_2 ; Check for % CMP_RAX_Immediate8 !0x25 JE32 %Update_Pointer_4 ; Check for & CMP_RAX_Immediate8 !0x26 JE32 %Update_Pointer_4 ;; deal with bad input CALLI32 %fail :Update_Pointer_4 ADDI8_to_R13 !2 ; Increment IP :Update_Pointer_2 ADDI8_to_R13 !1 ; Increment IP :Update_Pointer_1 ADDI8_to_R13 !1 ; Increment IP RET :First_pass_pointer ; Deal with Pointer to label CALLI32 %Update_Pointer ; Increment IP LOADI32_RBX &table ; Using scratch CALLI32 %consume_token ; Read token CALLI32 %ClearScratch ; Throw away token CMP_RAX_Immediate8 !0x3E ; check for '>' JNE32 %First_pass ; Loop again ;; Deal with %label>label case LOADI32_RBX &table ; Write to scratch CALLI32 %consume_token ; get token CALLI32 %ClearScratch ; Clean up after ourselves JMP32 %First_pass ; Loop again :First_pass_done RET :hex ; deal with EOF CMP_RAX_Immediate8 !-4 JE32 %EOF ; deal with line comments starting with # CMP_RAX_Immediate8 !0x23 JE32 %ascii_comment ; deal with line comments starting with ; CMP_RAX_Immediate8 !0x3B JE32 %ascii_comment ; deal all ascii less than 0 CMP_RAX_Immediate8 !0x30 JL32 %ascii_other ; deal with 0-9 CMP_RAX_Immediate8 !0x3A JL32 %ascii_num ; deal with all ascii less than A CMP_RAX_Immediate8 !0x41 JL32 %ascii_other ; deal with A-F CMP_RAX_Immediate8 !0x47 JL32 %ascii_high ;deal with all ascii less than a CMP_RAX_Immediate8 !0x61 JL32 %ascii_other ;deal with a-f CMP_RAX_Immediate8 !0x67 JL32 %ascii_low ; The rest that remains needs to be ignored JMP32 %ascii_other :Second_pass CALLI32 %Read_byte ; Deal with EOF CMP_RAX_Immediate8 !-4 JE32 %Second_pass_done ; Simply drop the label CMP_RAX_Immediate8 !0x3A JNE32 %Second_pass_0 LOADI32_RBX &table ; Using scratch CALLI32 %consume_token ; Read token CALLI32 %ClearScratch ; Throw away token JMP32 %Second_pass :Second_pass_0 ; Deal with % pointer CMP_RAX_Immediate8 !0x25 JE32 %StorePointer_rel4 ; Deal with @ pointer CMP_RAX_Immediate8 !0x40 JE32 %StorePointer_rel2 ; Deal with ! pointer CMP_RAX_Immediate8 !0x21 JE32 %StorePointer_rel1 ; Deal with & pointer CMP_RAX_Immediate8 !0x26 JE32 %StorePointer_abs4 ; Deal with $ pointer CMP_RAX_Immediate8 !0x24 JE32 %StorePointer_abs2 :Second_pass_1 ; Deal with everything else CALLI32 %hex ; Process our char ; Deal with EOF CMP_RAX_Immediate8 !-4 JE32 %Second_pass_done ; deal with -1 values CMP_RAX_Immediate8 !0 JL32 %Second_pass ; deal with toggle CMP_R15_Immediate8 !0 JE32 %print ; process first byte of pair COPY_RAX_to_R14 LOADI32_R15 %0 JMP32 %Second_pass :Second_pass_done RET :EOF RET :ascii_num SUBI8_RAX !0x30 RET :ascii_low SUBI8_RAX !0x57 RET :ascii_high SUBI8_RAX !0x37 RET :ascii_other LOADI32_RAX %-1 RET :ascii_comment CALLI32 %Read_byte CMP_RAX_Immediate8 !0x0D JE32 %ascii_comment_cr CMP_RAX_Immediate8 !0x0A JNE32 %ascii_comment :ascii_comment_cr LOADI32_RAX %-1 RET ; process second byte of pair :print ; update the sum and store in output SHL_R14_Immediate8 !4 ADD_R14_to_RAX STORE8_AL_into_Absolute32 &table ; flip the toggle NOT_R15 ; Print our first Hex LOADI32_RDX %1 ; set the size of chars we want CALLI32 %print_chars ADDI8_to_R13 !1 ; Increment IP JMP32 %Second_pass :Done ; program completed Successfully LOADI32_RDI %0 ; All is well LOADI32_RAX %0x3C ; put the exit syscall number in eax SYSCALL ; 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 RDI :malloc LOADI32_RAX %12 ; the Syscall # for SYS_BRK PUSH_R11 ; Protect r11 SYSCALL ; call the Kernel POP_R11 ; Restore r11 RET :Read_byte ; Attempt to read 1 byte from STDIN LOADI32_RDX %1 ; set the size of chars we want LOADI32_RSI &write ; Where to put it COPY_R9_to_RDI ; Where are we reading from LOADI32_RAX %0 ; the syscall number for read PUSH_R11 ; Protect r11 SYSCALL ; call the Kernel POP_R11 ; Restore r11 TEST_RAX_RAX ; check what we got JE32 %Read_byte_1 ; Got EOF call it done ; load byte LOAD8_AL_from_Absolute32 &write ; load char ZERO_EXTEND_AL ; We have to zero extend it to use it RET ; Deal with EOF :Read_byte_1 LOADI32_RAX %-4 ; Put EOF in rax RET :print_chars LOADI32_RSI &table ; What we are writing COPY_R10_to_RDI ; Write to target file LOADI32_RAX %1 ; the syscall number for write PUSH_R11 ; Protect HEAD SYSCALL ; call the Kernel POP_R11 ; Restore HEAD RET ;; Receives pointer in RBX ;; Writes out char and updates RBX :consume_token CALLI32 %Read_byte ; Consume_token ; Check for \t CMP_RAX_Immediate8 !0x09 JE32 %consume_token_done ; Check for \n CMP_RAX_Immediate8 !0x0A JE32 %consume_token_done ; Check for ' ' CMP_RAX_Immediate8 !0x20 JE32 %consume_token_done ; Check for '>' CMP_RAX_Immediate8 !0x3E JE32 %consume_token_done ;; Looks like we are still reading token STORE8_al_into_Address_RBX ; Store char ADDI8_to_RBX !1 ; Point to next spot JMP32 %consume_token ; loop until done :consume_token_done LOADI32_RCX %0 ; Padd with nulls STORE32_RCX_into_Address_RBX ADDI8_to_RBX !8 RET :StoreLabel COPY_R12_to_RAX ; ENTRY ADDI8_to_R12 !24 ; CALLOC STORE32_R13_into_Address_RAX_Immediate8 !8 ; ENTRY->TARGET = IP STORE32_R11_into_Address_RAX ; ENTRY->NEXT = JUMP_TABLE COPY_RAX_to_R11 ; JUMP_TABLE = ENTRY STORE32_R12_into_Address_R11_Immediate8 !16 ; ENTRY->NAME = TOKEN COPY_R12_to_RBX ; Write Starting after struct CALLI32 %consume_token ; Collect whole string COPY_RBX_to_R12 ; Update HEAP JMP32 %First_pass :GetTarget LOADI32_RDI &table ; Reset scratch COPY_R11_to_RCX ; Grab JUMP_TABLE LOAD32_into_RSI_from_Address_RCX_Immediate8 !16 ; I->NAME :GetTarget_loop LOAD8_AL_from_Address_RSI ; I->NAME[0] LOAD8_BL_from_Address_RDI ; scratch[0] ZERO_EXTEND_BL ; Zero extend ZERO_EXTEND_AL ; Zero extend CMP_AL_to_BL ; IF TOKEN == I->NAME JNE32 %GetTarget_miss ; Oops ADDI8_to_RSI !1 ADDI8_to_RDI !1 CMP_RAX_Immediate8 !0 JNE32 %GetTarget_loop ; Loop until JMP32 %GetTarget_done ; Match ;; Miss :GetTarget_miss LOAD32_into_RCX_from_Address_RCX ; I = I->NEXT CMP_RCX_Immediate8 !0 ; IF NULL == I JE32 %fail ; Abort hard LOAD32_into_RSI_from_Address_RCX_Immediate8 !16 ; I->NAME LOADI32_RDI &table ; Reset scratch JMP32 %GetTarget_loop :GetTarget_done LOAD32_into_RAX_from_Address_RCX_Immediate8 !8 ; Get address RET :ClearScratch PUSH_RAX ; Protect against changes PUSH_RBX ; And overwrites PUSH_RCX ; While we work LOADI32_RBX &table ; Where our table is LOADI32_RAX %0 ; Using null :ClearScratch_loop LOAD32_into_RCX_from_Address_RBX ; Get current value STORE8_al_into_Address_RBX ; Because we want null ADDI8_to_RBX !1 ; Increment CMP_RCX_Immediate8 !0 ; Check if we hit null JNE32 %ClearScratch_loop ; Keep looping POP_RCX ; Don't Forget to POP_RBX ; Restore Damage POP_RAX ; Entirely RET :StorePointer CALLI32 %Update_Pointer ; Increment IP LOADI32_RBX &table ; Write to scratch CALLI32 %consume_token ; get token PUSH_RAX ; Protect base_sep_p LOADI32_RAX &table ; Pointer to scratch CALLI32 %GetTarget ; Get address of pointer CALLI32 %ClearScratch ; Clean up after ourselves COPY_R13_to_RDX ; base = IP POP_RBX ; Restore base_sep_p CMP_RBX_Immediate8 !0x3E ; If base_sep_p == '>' JNE32 %StorePointer_done ; If not ;; Deal with %label>label case PUSH_RAX ; We need to preserve main target LOADI32_RBX &table ; Write to scratch CALLI32 %consume_token ; get token LOADI32_RAX &table ; Pointer to scratch CALLI32 %GetTarget ; Get address of pointer CALLI32 %ClearScratch ; Clean up after ourselves COPY_RAX_to_RDX ; Use our new base POP_RAX ; Restore main target :StorePointer_done RET :StorePointer_rel4 CALLI32 %StorePointer ; Do Common SUB_RDX_from_RAX ; target - ip STORE32_from_RAX_into_ABS32 &table ; put value in output LOADI32_RDX %4 ; set the size of chars we want CALLI32 %print_chars CALLI32 %ClearScratch ; Clean up after ourselves JMP32 %Second_pass :StorePointer_rel2 CALLI32 %StorePointer ; Do Common SUB_RDX_from_RAX ; target - ip STORE32_from_RAX_into_ABS32 &table ; put value in output LOADI32_RDX %2 ; set the size of chars we want CALLI32 %print_chars CALLI32 %ClearScratch ; Clean up after ourselves JMP32 %Second_pass :StorePointer_rel1 CALLI32 %StorePointer ; Do Common SUB_RDX_from_RAX ; target - ip STORE32_from_RAX_into_ABS32 &table ; put value in output LOADI32_RDX %1 ; set the size of chars we want CALLI32 %print_chars CALLI32 %ClearScratch ; Clean up after ourselves JMP32 %Second_pass :StorePointer_abs4 CALLI32 %StorePointer ; Do Common STORE32_from_RAX_into_ABS32 &table ; put value in output LOADI32_RDX %4 ; set the size of chars we want CALLI32 %print_chars CALLI32 %ClearScratch ; Clean up after ourselves JMP32 %Second_pass :StorePointer_abs2 CALLI32 %StorePointer ; Do Common STORE32_from_RAX_into_ABS32 &table ; put value in output LOADI32_RDX %2 ; set the size of chars we want CALLI32 %print_chars CALLI32 %ClearScratch ; Clean up after ourselves JMP32 %Second_pass :fail ; Some shit went wrong LOADI32_RDI %1 ; All is wrong LOADI32_RAX %0x3C ; put the exit syscall number in eax SYSCALL ; Call it a good day :write NULL NULL :table :ELF_end