From 7b8636e9cb303ff9841094270254d8d636f4c3f5 Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Tue, 9 Jul 2024 23:12:10 -0400 Subject: [PATCH] add some various bits to hopefully assemble something useful --- .gitignore | 3 + SForth.dmp | 66 +++------ alvm | 1 + dis.sh | 2 + onward/onward.s | 383 ++++++++++++++++++++++++++++++++++++++++++++++++ test.c | 7 + 6 files changed, 418 insertions(+), 44 deletions(-) create mode 100644 .gitignore create mode 160000 alvm create mode 100755 dis.sh create mode 100644 onward/onward.s create mode 100644 test.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a86a8a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +SForth +*.o +a.out diff --git a/SForth.dmp b/SForth.dmp index e7c6ec5..03e5bfe 100644 --- a/SForth.dmp +++ b/SForth.dmp @@ -1,55 +1,33 @@ # David Smith 2022 david.a.c.v.smith@gmail.com http://dacvs.neocities.org/ # This is the 1st of the 2 files that define SmithForth, a subroutine-threaded Forth for x86-64. -# Understanding this file may be easier if first you watch my video series on hand-made -# Linux x86 executables (featuring the ELF header and general-purpose x86 instructions): -# https://www.youtube.com/playlist?list=PLZCIHSjpQ12woLj0sjsnqDH8yVuXwTy3p +# ELF FILE HEADER -# ============= ELF FILE HEADER -# -# Linux will run a computing job given the name of an executable file. An executable file -# contains machine code for the processor and information for the operating system about the -# layout of the file and the dimensions of the job. Working without the usual development -# tools, we write this information by hand. - -7F 45 4C 46 # e_ident[EI_MAG]: ELF magic number - 02 # e_ident[EI_CLASS]: 1: 32-bit, 2: 64-bit - 01 # e_ident[EI_DATA]: 1: little-endian, 2: big-endian - 01 # e_ident[EI_VERSION]: ELF header version; must be 1 - 00 # e_ident[EI_OSABI]: Target OS ABI; should be 0 -00 # e_ident[EI_ABIVERSION]: ABI version; 0 is ok for Linux - 00 00 00 00 00 00 00 # e_ident[EI_PAD]: unused, should be 0 -02 00 # e_type: object file type; 2: executable - 3E 00 # e_machine: instruction set architecture; 3: x86, 3E: amd64 - 01 00 00 00 # e_version: ELF identification version; must be 1 -78 00 40 00 00 00 00 00 # e_entry: memory address of entry point (where process starts) -40 00 00 00 00 00 00 00 # e_phoff: file offset where program headers begin (34: 32-bit, 40: 64) -00 00 00 00 00 00 00 00 # e_shoff: file offset where section headers begin -00 00 00 00 # e_flags: 0 for x86 - 40 00 # e_ehsize: size of this header (34: 32-bit, 40: 64-bit) - 38 00 # e_phentsize: size of each program header (20: 32-bit, 38: 64-bit) -01 00 # e_phnum: number of program headers - 40 00 # e_shentsize: size of each section header (28: 32-bit, 40: 64-bit) - 00 00 # e_shnum: number of section headers - 00 00 # e_shstrndx: index of section header containing section names +7F 45 4C 46 02 01 01 00 +00 00 00 00 00 00 00 00 +02 00 3E 00 01 00 00 00 +78 00 40 00 00 00 00 00 # e_entry: 0x00400078 (virtual memory address of entry point) +40 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 40 00 38 00 +01 00 40 00 00 00 00 00 # ============= ELF PROGRAM HEADER -01 00 00 00 # p_type: segment type; 1: loadable - 07 00 00 00 # p_flags: segment-dependent flags (1: X, 2: W, 4: R) -00 00 00 00 00 00 00 00 # p_offset: file offset where segment begins -00 00 40 00 00 00 00 00 # p_vaddr: virtual address of segment in memory (amd64: 00400000) -00 00 00 00 00 00 00 00 # p_paddr: physical address of segment, unspecified by 386 supplement -02 1E 01 00 00 00 00 00 ##### p_filesz: size in bytes of the segment in the file image (see make.sh) -00 00 C0 7F 00 00 00 00 # p_memsz: (>= filesz) size in bytes of the segment in memory -00 10 00 00 00 00 00 00 # p_align: 1000 for x86 +01 00 00 00 07 00 00 00 +00 00 00 00 00 00 00 00 +00 00 40 00 00 00 00 00 # p_vaddr: 0x00400000 (virtual address of segment in memory) +00 00 00 00 00 00 00 00 +02 1E 01 00 00 00 00 00 # p_filesz: 0x00011E02 (size in bytes of the segment in the file image) +00 00 C0 7F 00 00 00 00 # p_memsz: 0x7FC00000 ((>= filesz) size in bytes of the segment in memory) +00 10 00 00 00 00 00 00 # ============= 64-BIT EXTENSIONS # # SmithForth has 64-bit Forth cells (i.e., integers) and uses the instruction set # x86-64 (a.k.a. amd64). Changes from x86 to x86-64 are explained in Intel's # manual (and in AMD's). See for instance Vol 1 Sec 3.2.1, Vol 2 Sec 2.2.1, and the -# instruction set reference, Vol 2 Chs 3, 4, 5. There are many subtle details. +# instruction set reference, Vol 2 Chs 3, 4, 5. There are many subtle details. # # In a nutshell, general-purpose registers are widened from 32 bits to 64. The old eight # 32-bit general-purpose registers EAX, ECX, ..., EDI are still available as operands. @@ -372,7 +350,7 @@ C3 # return ret C3 99 01 5D ################ ] ( -- ) rbracket ====================================================== 6A 01 # push 1(Compiling) push imm8 6A ib -8F 04 25 20 00 00 10 # pop [STATE] pop r/m64 8F /0 00 000 100 00 100 101 +8F 04 25 20 00 00 10 # pop [STATE] pop r/m64 8F /0 00 000 100 00 100 101 C3 # return ret C3 99 81 5C ################ \ ( "ccc" -- ) backslash IMMEDIATE ================================ @@ -434,7 +412,7 @@ C3 # return ret C3 48 39 C8 # cmp rax, rcx cmp r/m64, r64 REX.W 39 /r 11 001 000 74 01 #+jump _lengthEq if == je rel8 74 cb C3 # return ret C3 -# _lengthEq: # +# _lengthEq: # 48 8B FD # rdi = rbp mov r64, r/m64 REX.W 8B /r 11 111 101 F3 A6 # strings equal ? repe cmps m8, m8 F3 A6 C3 # return ret C3 @@ -447,7 +425,7 @@ E8 03 00 00 00 #+call (FIND) call rel32 E8 cd C3 # return ret C3 # # # # # # # # # # # # # (FIND) 50 57 # push rax, rdi push r64 50+rd -99 78 # Call xt= +99 78 # Call xt= 5F 58 # pop rdi, rax pop r64 58+rd 74 04 #+jump _end if == je rel8 74 cb 48 8B 5B 08 # rbx = [rbx+8] mov r64, r/m64 REX.W 8B /r 01 011 011 @@ -457,9 +435,9 @@ C3 # return ret C3 99 03 4E 75 6D ########## Num ( rbp=addr rax=u -- n ) ============================================ 49 83 EF 08 # r15 -= 8 sub r/m64, imm8 REX.W 83 /5 ib 11 101 111 49 83 27 00 # [r15] = 0 and r/m64, imm8 REX.W 83 /4 ib 00 100 111 -48 89 C1 # rcx = rax mov r/m64, r64 REX.W 89 /r 11 000 001 +48 89 C1 # rcx = rax mov r/m64, r64 REX.W 89 /r 11 000 001 48 8B F5 # rsi = rbp mov r64, r/m64 REX.W 8B /r 11 110 101 -# _beg: # +# _beg: # E8 03 00 00 00 #+call (Num) call rel32 E8 cd E2 F9 #-jump beg if --rcx loop rel8 E2 cb C3 # return ret C3 diff --git a/alvm b/alvm new file mode 160000 index 0000000..c692c16 --- /dev/null +++ b/alvm @@ -0,0 +1 @@ +Subproject commit c692c1683c13196f540a0315c15d11825d0417d1 diff --git a/dis.sh b/dis.sh new file mode 100755 index 0000000..a65e908 --- /dev/null +++ b/dis.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cc -g -c -o test.o test.c -O1 -fno-omit-frame-pointer && objdump -D test.o | less diff --git a/onward/onward.s b/onward/onward.s new file mode 100644 index 0000000..cf29911 --- /dev/null +++ b/onward/onward.s @@ -0,0 +1,383 @@ +//--------------------------------------------------------------------- +// +//--------------------------------------------------------------------- + +.set RETURN_STACK_SIZE, 8192 +.set BUFFER_SIZE, 4096 + +.set F_IMMED, 0x80 +.set F_HIDDEN, 0x20 +.set F_LENMASK, 0x1f +.set link, 0 + + +//--------------------------------------------------------------------- +// Register Usage +//--------------------------------------------------------------------- +// x86_64 Parameter Registers: +// rdi, rsi, rdx, rcx, r8, r9 +// +// x86_64 Preserved Registers: +// rbx, rsp, rbp, r12, r13, r14, r15 + +.set PC, %rsi /* Program Counter */ +.set ASP, %rsp /* Argument Stack Pointer*/ +.set RSP, %rbp /* Return Stack Pointer */ +.set OA1, %rax /* Operator Argument Register 1 */ +.set OA2, %rbx /* Operator Argument Register 2 */ + + +//--------------------------------------------------------------------- +// Buffer Allocations +//--------------------------------------------------------------------- + + .bss + .align 4096 + .size return_stack, 8192 +return_stack: + .zero 8192 +return_stack_top: // Initial top of return stack. + + +//--------------------------------------------------------------------- +// Assembler Macros +//--------------------------------------------------------------------- + +// Execute next instruction +.macro NEXT + lodsq + jmpq *(OA1) +.endm + +.macro PUSHRSP reg + leaq -8(%rbp),%rbp /* push reg on to return stack */ + movq \reg,(%rbp) +.endm + +.macro POPRSP reg + movq (%rbp),\reg /* pop top of return stack to reg */ + leaq 8(%rbp),%rbp +.endm + +.macro defcode name, namelen, flags=0, label + .data + .p2align 4 + .global name_\label +name_\label : + .quad link + .set link, name_\label + .byte \flags+\namelen + .ascii "\name" + .p2align 4 + .global \label +\label : + .quad code_\label + .text + .p2align 4, 0x90 + .global code_\label +code_\label : +.endm + +.macro defword name, namelen, flags=0, label + .data + .p2align 4 + .global name_\label +name_\label : + .quad link + .set link,name_\label + .byte \flags+\namelen + .ascii "\name" + .p2align 4 + .globl \label +\label : + .quad DOCOL +.endm + +.macro defconst name, namelen, flags=0, label, value +defcode \name,\namelen,\flags,\label + pushq $\value + NEXT +.endm + +//--------------------------------------------------------------------- +// Inner Interpreter Word +//--------------------------------------------------------------------- + +// Main Interpreter Definition + .text + .globl DOCOL + .p2align 4, 0x90 +DOCOL: + PUSHRSP PC + addq OA1, 8 + mov PC, OA1 + NEXT + + +//--------------------------------------------------------------------- +// Core Interpreter Words +//--------------------------------------------------------------------- + +defword "quit",4,,quit + .quad rz, rsp_store /* R0 RSP!, clear the return stack */ + .quad interpret /* interpret the next word */ + .quad branch,-16 /* and loop (indefinitely) */ + +defcode "interpret",9,,interpret + NEXT + +defword ":",1,,COLON + .quad word + .quad create + .quad lit, DOCOL, comma + .quad latest, fetch, hidden + .quad rbrack + .quad return + +defword ";",1,F_IMMED,SEMICOLON + .quad lit, return, comma + .quad latest, fetch, hidden + .quad lbrack + .quad return + +defcode "return",6,,return + POPRSP PC + NEXT + +defcode "word",4,,word + NEXT + +defcode "create",6,,create + NEXT + +defcode "lit",3,,lit + lodsq + pushq OA1 + NEXT + +defcode ",",1,,comma + NEXT + +defcode "latest",6,,latest + NEXT + +defcode "@",1,,fetch + NEXT + +defcode "hidden",6,,hidden + NEXT + +defcode "[",1,,lbrack + NEXT + +defcode "]",1,,rbrack + NEXT + + +//--------------------------------------------------------------------- +// Branching Words +//--------------------------------------------------------------------- + +defcode "branch",6,,branch + addq (%rsi), %rsi /* add the offset to the instruction pointer */ + NEXT + +defcode "0branch",7,,zbranch + popq %rax + test %rax,%rax /* top of stack is zero? */ + jz code_branch /* if so, jump back to the branch function above */ + lodsq /* otherwise we need to skip the offset */ + NEXT + + +//--------------------------------------------------------------------- +// Return Stack Words +//--------------------------------------------------------------------- + +defconst "R0",2,,rz,return_stack_top + +defcode ">R",2,,to_r + popq %rax /* pop parameter stack into %eax */ + PUSHRSP %rax /* push it on to the return stack */ + NEXT + +defcode "R>",2,,from_r + POPRSP %rax /* pop return stack on to %eax */ + pushq %rax /* and push on to parameter stack */ + NEXT + +defcode "RSP@",4,,rsp_fetch + pushq %rbp + NEXT + +defcode "RSP!",4,,rsp_store + popq %rbp + NEXT + +defcode "RDROP",5,,rdrop + addq $8, %rbp + NEXT + +//--------------------------------------------------------------------- +// Builtin Primitives +//--------------------------------------------------------------------- +// Legend: +// i Integer value +// f Fixed point value +// F Floating point value +// s String value +// v Any value + +// (i - ) Exit the application with status code +defcode "halt",4,,halt + popq %rdi + call _exit + +// (v - )Duplicates the top value on the stack +defcode "drop",4,,drop + popq OA1 + NEXT + +// (v1 v2 - v2 v1) Swap the top two values on the stack +defcode "swap",4,,swap + popq OA1 + popq OA2 + pushq OA2 + pushq OA1 + NEXT + +// (v - v v) Duplicate the top value on the stack +defcode "dup",3,,dup + movq (ASP), OA1 + pushq OA1 + NEXT + +// (v1 v2 - v1 v2 v1) Duplicate second item on the stack +defcode "over",4,,over + movq 8(ASP), OA1 + pushq OA1 + NEXT + +// defcode rot +// defcode -rot +// defcode 2drop +// defcode 2dup +// defcode 2swap +// defcode ?dup + +// defcode 1+ +// defcode 1- +// defcode 4+ +// defcode 4- +// defcode 8+ +// defcode 8- + +// defcode + +// defcode - +// defcode * +// defcode / +// defcode % + +// defcode = +// defcode != +// defcode < +// defcode > +// defcode <= +// defcode >= + +// defcode 0= +// defcode 0!= +// defcode 0< +// defcode 0> +// defcode 0<= +// defcode 0>= + +// defcode and +// defcode or +// defcode xor +// defcode not + + +// defcode ret + + +// defcode ! +// defcode @ +// defcode +! +// defcode -! +// defcode b! +// defcode b@ +// defcode b@b! +// defcode bmove + + +// defcode >r +// defcode r> +// defcode rsp@ +// defcode rsp! +// defcode rdrop + +// defcode asp@ +// defcode asp! + +// defcode getc +// defcode putc +// defcode word +// defcode number +// defcode find +// defcode >cfa +// defcode >dfa +// defcode create +// defcode , +// defcode [ +// defcode ] +// defcode : +// defcode ; +// defcode immediate +// defcode hidden +// defcode hide +// defcode ' + +// defcode branch +// defcode 0branch +// defcode litstring +// defcode tell +// defcode quit +// defcode interpret + +// defcode char +// defcode execute +// defcode syscall // or EFI call + +// Floating point operations +// defcode +. +// defcode -. +// defcode *. +// defcode /. + +//--------------------------------------------------------------------- +// Main Routine +//--------------------------------------------------------------------- + .text + .global _main + .global main + .p2align 4, 0x90 +_main: +main: + cld + mov $return_stack_top, RSP + mov $cold_start, PC + NEXT + +// pushq OA1 +// pushq %rbp +// leaq return_stack_top(%rip), RSP +// leaq cold_start(%rip), PC +// NEXT + +// First instructions to execute by the interpreter + .data + .global cold_start + .p2align 4 +cold_start: + .quad quit diff --git a/test.c b/test.c new file mode 100644 index 0000000..6ca36b6 --- /dev/null +++ b/test.c @@ -0,0 +1,7 @@ +int bar = 1; +int foo = 1; + +int add1(int a) +{ + return a + foo; +} -- 2.52.0