Compare commits

..

31 Commits

Author SHA1 Message Date
Ed_
172be7bfd8 update ignores 2025-07-19 19:41:15 -04:00
Ed_
af05912d8b prep hello_arenas.asm 2025-06-22 16:32:11 -04:00
Ed_
f8ac822458 more cleanup 2025-06-22 16:16:16 -04:00
Ed_
682401c731 cleanup 2025-06-22 16:13:05 -04:00
Ed_
a3e56b9394 corrections (was not actually done) 2025-06-22 15:56:19 -04:00
Ed_
62d2773e6f finished hello_files.asm 2025-06-22 00:33:32 -04:00
Ed_
f35b738da2 realizing a convention... 2025-06-21 23:01:07 -04:00
Ed_
5ea4b5426c adjustments 2025-06-21 19:10:01 -04:00
Ed_
bcbdd2c0a2 major refactor (I'm starting to understand things 2025-06-21 18:51:49 -04:00
Ed_
9bc1e59bdb almost done... stack errors to deal with now... 2025-06-21 17:28:04 -04:00
Ed_
39b4be9d7a progress on hello_files 2025-06-21 14:19:50 -04:00
Ed_
c8f513f386 progress on hello_files.asm (fails on CreateFileA) 2025-06-21 02:57:41 -04:00
Ed_
0b2ba408c7 progress on hello_files.asm 2025-06-21 02:28:41 -04:00
Ed_
c1b8f935e0 progress on hello_files.asm 2025-06-21 01:52:23 -04:00
Ed_
a56fc1a454 progress on hello_files.asm 2025-06-20 22:50:54 -04:00
Ed_
ec884ba88e progress on hello_files 2025-06-20 11:38:29 -04:00
Ed_
86b964b6c5 im not good at asm 2025-06-17 02:54:01 -04:00
Ed_
bc0d81f9bc working on hello_files.asm 2025-06-16 10:27:15 -04:00
Ed_
4654fda9f7 hello_slices 2025-06-16 02:13:37 -04:00
Ed_
b1b401784a switching to nasm, yasm is dead from what I can tell, not caring about perf diff anymore. 2025-06-15 18:37:40 -04:00
Ed_
032e91a81f add toolchain notes 2025-06-15 16:54:10 -04:00
Ed_
e49258c8c2 some setup 2025-06-15 16:51:54 -04:00
Ed_
06212960ce removing C code, opting to mess around in odin. 2025-06-15 16:32:43 -04:00
Ed_
a54f4b0b57 working on this repo again... 2025-06-15 16:23:46 -04:00
Ed_
80c830b59d Starting to build a codebase for messing with assembly generation compatible with yasm 2024-11-29 14:31:38 -05:00
Ed_
531daa38c1 gutting fasm (doesn't have full-feature debug symbols) 2024-11-25 08:01:24 -05:00
Ed_
da70d0adf9 prepareing for next thing 2024-11-25 01:01:29 -05:00
Ed_
03b272d91e Working hello world with fasm2 2024-11-25 00:05:27 -05:00
Ed_
dceb330571 added fasm2 as well 2024-11-25 00:04:53 -05:00
Ed_
8a974e9c89 editor config 2024-11-24 23:13:33 -05:00
Ed_
dbfd94ea40 add flat assembler toolchain 2024-11-24 23:13:28 -05:00
28 changed files with 31736 additions and 0 deletions

30
.editorconfig Normal file
View File

@@ -0,0 +1,30 @@
root = true
[*.asm]
indent_style = tab
indent_size = 2
[*.refactor]
indent_style = space
indent_size = 4
[*.md]
indent_style = space
indent_size = 4
[*.{h, c, hpp, cpp}]
indent_style = tab
indent_size = 2
[*.{ps1, psm1}]
indent_style = tab
indent_size = 4
[*.odin]
indent_style = tab
indent_size = 2
charset = utf-8
[*.{natvis, natstepfilter}]
indent_style = tab
indent_size = 4

4
.gitignore vendored
View File

@@ -0,0 +1,4 @@
build/**
.vscode/settings.json
code/asm/hello_files.proj
asm_dip

37
.vscode/c_cpp_propertiees.json vendored Normal file
View File

@@ -0,0 +1,37 @@
{
"configurations": [
{
"name": "Win32 msvc",
"includePath": [
"${workspaceFolder}/source/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"INTELLISENSE_DIRECTIVES"
],
"windowsSdkVersion": "10.0.19041.0",
"compilerPath": "C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.29.30133/bin/HostX64/x64/cl.exe",
"intelliSenseMode": "msvc-x64",
"compileCommands": "${workspaceFolder}/project/build/compile_commands.json"
},
{
"name": "Win32 clang",
"includePath": [
"${workspaceFolder}/source/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"INTELLISENSE_DIRECTIVES"
],
"windowsSdkVersion": "10.0.19041.0",
"compilerPath": "C:/Users/Ed/scoop/apps/llvm/current/bin/clang++.exe",
"intelliSenseMode": "windows-clang-x64",
"compileCommands": "${workspaceFolder}/project/build/compile_commands.json"
}
],
"version": 4
}

3
code/asm.odin Normal file
View File

@@ -0,0 +1,3 @@
package asmduff // ASM Duffle

178
code/asm/asm.user Normal file
View File

@@ -0,0 +1,178 @@
// raddbg 0.9.18 user file
window:
{
size: 1536.000000 864.000000
panels:
{
0.25:
{
0.25:
{
watch:
{
expression: ""
selected
}
watch: expression: "query:types"
}
0.25:
{
watch:
{
expression: "query:threads"
selected
}
watch: expression: "query:targets"
watch: expression: "query:breakpoints"
watch: expression: "query:watch_pins"
}
0.25:
{
disasm:
{
expression: ""
selected
}
text: expression: "query:output"
}
0.25:
{
watch:
{
expression: "query:call_stack"
selected
}
watch: expression: "query:modules"
}
}
0.75:
{
getting_started: selected
selected
}
}
split_x
pos: 26 26
monitor: "\\\\.\\DISPLAY1"
}
keybindings:
{
{ kill_all f5 shift }
{ step_into_inst f11 alt }
{ step_over_inst f10 alt }
{ step_out f11 shift }
{ halt x ctrl shift }
{ halt pause }
{ run f5 }
{ restart f5 ctrl shift }
{ step_into f11 }
{ step_over f10 }
{ run_to_line f10 ctrl }
{ set_next_statement f10 ctrl shift }
{ inc_window_font_size equal alt }
{ dec_window_font_size minus alt }
{ window n ctrl shift }
{ toggle_fullscreen return ctrl }
{ new_panel_right p ctrl }
{ new_panel_down minus ctrl }
{ rotate_panel_columns 2 ctrl }
{ next_panel comma ctrl }
{ prev_panel comma ctrl shift }
{ focus_panel_right right ctrl alt }
{ focus_panel_left left ctrl alt }
{ focus_panel_up up ctrl alt }
{ focus_panel_down down ctrl alt }
{ undo z ctrl }
{ redo y ctrl }
{ go_back left alt }
{ go_forward right alt }
{ close_panel p ctrl shift alt }
{ next_tab page_down ctrl }
{ prev_tab page_up ctrl }
{ next_tab tab ctrl }
{ prev_tab tab ctrl shift }
{ move_tab_right page_down ctrl shift }
{ move_tab_left page_up ctrl shift }
{ close_tab w ctrl }
{ tab_bar_top up ctrl shift alt }
{ tab_bar_bottom down ctrl shift alt }
{ open_tab t ctrl }
{ open o ctrl }
{ switch i ctrl }
{ switch_to_partner_file o alt }
{ open_user n ctrl shift alt }
{ open_project n ctrl alt }
{ open_user o ctrl shift alt }
{ open_project o ctrl alt }
{ save_user s ctrl shift alt }
{ save_project s ctrl shift }
{ edit f2 }
{ accept return }
{ accept space }
{ cancel esc }
{ move_left left }
{ move_right right }
{ move_up up }
{ move_down down }
{ move_left_select left shift }
{ move_right_select right shift }
{ move_up_select up shift }
{ move_down_select down shift }
{ move_left_chunk left ctrl }
{ move_right_chunk right ctrl }
{ move_up_chunk up ctrl }
{ move_down_chunk down ctrl }
{ move_up_page page_up }
{ move_down_page page_down }
{ move_up_whole home ctrl }
{ move_down_whole end ctrl }
{ move_left_chunk_select left ctrl shift }
{ move_right_chunk_select right ctrl shift }
{ move_up_chunk_select up ctrl shift }
{ move_down_chunk_select down ctrl shift }
{ move_up_page_select page_up shift }
{ move_down_page_select page_down shift }
{ move_up_whole_select home ctrl shift }
{ move_down_whole_select end ctrl shift }
{ move_up_reorder up alt }
{ move_down_reorder down alt }
{ move_home home }
{ move_end end }
{ move_home_select home shift }
{ move_end_select end shift }
{ select_all a ctrl }
{ delete_single delete }
{ delete_chunk delete ctrl }
{ backspace_single backspace }
{ backspace_chunk backspace ctrl }
{ copy c ctrl }
{ copy insert ctrl }
{ cut x ctrl }
{ paste v ctrl }
{ paste insert shift }
{ insert_text null }
{ move_next tab }
{ move_prev tab shift }
{ goto_line g ctrl }
{ goto_address g alt }
{ search f ctrl }
{ search_backwards r ctrl }
{ find_next f3 }
{ find_prev f3 ctrl }
{ find_selected_thread f4 }
{ goto_name j ctrl }
{ goto_name_at_cursor f12 }
{ toggle_watch_expr_at_cursor w alt }
{ toggle_watch_expr_at_mouse d ctrl }
{ toggle_watch_pin f9 ctrl }
{ toggle_breakpoint f9 }
{ add_address_breakpoint f9 shift }
{ add_function_breakpoint f9 ctrl shift }
{ attach f6 shift }
{ open_palette f1 }
{ open_palette p ctrl shift }
{ log_marker m ctrl shift alt }
{ toggle_dev_menu d ctrl shift alt }
}
current_path: "C:/users/ed/appdata/roaming/raddbg"

712
code/asm/hello_arenas.asm Normal file
View File

@@ -0,0 +1,712 @@
; Hello Files!
BITS 64 ; Explicitly specify 64-bit mode
DEFAULT REL ; Use RIP-relative addressing by default
%define BUILD_DEBUG 1
;region DSL
%define marg
%define rcounter_32 ecx
%define rdata_32 edx
%define r8_32 r8d
%define r9_32 r9d
%define raccumulator rax
%define rbase rbx
%define rcounter rcx
%define rdata rdx
%define rdst_id rdi
%define rsrc_id rsi
%define rstack_ptr rsp
%define rstack_base_ptr rbp
%define false 0
%define true 1
%define true_overflow 2
;endregion DSL
;region Registers
; Wipes all 64-bit general-purpose registers except for the stack pointer (RSP).
; Zeroing RSP would corrupt the stack and crash the program.
; Zeroing RBP will break the stack frame chain used by debuggers.
%macro wipe_gprs 0
xor rax, rax
xor rbx, rbx
xor rcx, rcx
xor rdx, rdx
xor rsi, rsi
xor rdi, rdi
; xor rbp, rbp
xor r8, r8
xor r9, r9
xor r10, r10
xor r11, r11
xor r12, r12
xor r13, r13
xor r14, r14
xor r15, r15
%endmacro
; Resets the Floating-Point Unit (FPU), which also clears all MMX registers
; (MM0-MM7) and FPU stack registers (ST0-ST7).
%macro wipe_fpu_mmxs 0
finit
%endmacro
; Wipes the 128-bit XMM registers. Requires a CPU with at least SSE.
%macro wipe_xmms 0
vxorps xmm0, xmm0, xmm0
vxorps xmm1, xmm1, xmm1
vxorps xmm2, xmm2, xmm2
vxorps xmm3, xmm3, xmm3
vxorps xmm4, xmm4, xmm4
vxorps xmm5, xmm5, xmm5
vxorps xmm6, xmm6, xmm6
vxorps xmm7, xmm7, xmm7
vxorps xmm8, xmm8, xmm8
vxorps xmm9, xmm9, xmm9
vxorps xmm10, xmm10, xmm10
vxorps xmm11, xmm11, xmm11
vxorps xmm12, xmm12, xmm12
vxorps xmm13, xmm13, xmm13
vxorps xmm14, xmm14, xmm14
vxorps xmm15, xmm15, xmm15
%endmacro
; =============================================================================
; AVX Registers (YMM0-YMM15)
; =============================================================================
; Wipes the 256-bit YMM registers. Requires a CPU with AVX support.
; This also wipes the lower 128 bits (the XMM registers), so you don't
; need to call WIPE_XMM_REGS if you call this one.
%macro wipe_ymms 0
vzeroupper ; Clears upper 128 bits of all YMM registers
vxorps ymm0, ymm0, ymm0 ; Clears the full YMM0 (including lower XMM0)
vxorps ymm1, ymm1, ymm1
vxorps ymm2, ymm2, ymm2
vxorps ymm3, ymm3, ymm3
vxorps ymm4, ymm4, ymm4
vxorps ymm5, ymm5, ymm5
vxorps ymm6, ymm6, ymm6
vxorps ymm7, ymm7, ymm7
vxorps ymm8, ymm8, ymm8
vxorps ymm9, ymm9, ymm9
vxorps ymm10, ymm10, ymm10
vxorps ymm11, ymm11, ymm11
vxorps ymm12, ymm12, ymm12
vxorps ymm13, ymm13, ymm13
vxorps ymm14, ymm14, ymm14
vxorps ymm15, ymm15, ymm15
%endmacro
; =============================================================================
; AVX-512 Registers (ZMM0-ZMM31 and K0-K7)
; =============================================================================
; Wipes the 512-bit ZMM registers and the 8 mask registers (k0-k7).
; Requires a CPU with AVX-512F support. This is the most comprehensive
; vector register wipe and makes WIPE_XMM_REGS and WIPE_YMM_REGS redundant.
%macro wipe_avx512s 0
; Wipe Mask Registers (k0-k7)
kxorb k0, k0, k0
kxorb k1, k1, k1
kxorb k2, k2, k2
kxorb k3, k3, k3
kxorb k4, k4, k4
kxorb k5, k5, k5
kxorb k6, k6, k6
kxorb k7, k7, k7
; Wipe ZMM registers (zmm0-zmm31)
vpxord zmm0, zmm0, zmm0
vpxord zmm1, zmm1, zmm1
vpxord zmm2, zmm2, zmm2
vpxord zmm3, zmm3, zmm3
vpxord zmm4, zmm4, zmm4
vpxord zmm5, zmm5, zmm5
vpxord zmm6, zmm6, zmm6
vpxord zmm7, zmm7, zmm7
vpxord zmm8, zmm8, zmm8
vpxord zmm9, zmm9, zmm9
vpxord zmm10, zmm10, zmm10
vpxord zmm11, zmm11, zmm11
vpxord zmm12, zmm12, zmm12
vpxord zmm13, zmm13, zmm13
vpxord zmm14, zmm14, zmm14
vpxord zmm15, zmm15, zmm15
vpxord zmm16, zmm16, zmm16
vpxord zmm17, zmm17, zmm17
vpxord zmm18, zmm18, zmm18
vpxord zmm19, zmm19, zmm19
vpxord zmm20, zmm20, zmm20
vpxord zmm21, zmm21, zmm21
vpxord zmm22, zmm22, zmm22
vpxord zmm23, zmm23, zmm23
vpxord zmm24, zmm24, zmm24
vpxord zmm25, zmm25, zmm25
vpxord zmm26, zmm26, zmm26
vpxord zmm27, zmm27, zmm27
vpxord zmm28, zmm28, zmm28
vpxord zmm29, zmm29, zmm29
vpxord zmm30, zmm30, zmm30
vpxord zmm31, zmm31, zmm31
%endmacro
;endregion Registers
;region Debug
%define debug_trap 3
%ifidn BUILD_DEBUG, 1
%macro assert_cmp 3
cmp %2, %3
%1 %%.passed
int debug_trap
%%.passed:
%endmacro
%macro assert_not_null 1
cmp %1, nullptr
jnz %%.passed
int debug_trap
%%.passed: ; macro-unique-prefix (%%) .passed is the label name
%endmacro
%define dbg_wipe_gprs wipe_gprs
%define dbg_wipe_fpu_mmxs wipe_fpu_mmxs
%define dbg_wipe_xmms wipe_xmms
%define dbg_wipe_ymms wipe_ymms
%define dbg_wipe_avx512s wipe_avx512s
%else
%macro assert_cmp 3
%cmp %2, %3
%endmacro
%macro assert_not_null 1
%endmacro
%macro slice_assert 1
%endmacro
%define dbg_wipe_gprs
%define dbg_wipe_fpu_mmxs
%define dbg_wipe_xmms
%define dbg_wipe_ymms
%define dbg_wipe_avx512s
%endif ; BUILD_DEBUG
;endregion Debug
;region Math
; returns: raccumulator = U64
; Usage
%macro min_S64 2
mov raccumulator, %1
cmp raccumulator, %2
cmovg raccumulator, %2 ; ConditionalMoveIfGreater
%endmacro min_S64
;endregion Math
;region Memory
%define nullptr 0
%define kilo 1024
; Usage: def_array <name: %1> <size: %2>
%macro def_farray 2+
struc %1
.ptr: resb %2
endstruc
%endmacro
def_farray Mem_128k, 128 * kilo
;region memory_copy
; dst = rdst_id = [Byte]
; src = rsrc_id = [Byte]
; rcounter = U64
section .text
memory_copy:
cld
rep movsb ; REPEAT MoveStringByte
; 1. Copies the byte from [RSI] to [RDI].
; 2. Increments RSI and RDI (because of CLD).
; 3. Decrements RCX.
; 4. Repeats until RCX is 0.
ret
;endregion memory_copy
struc Slice
.ptr: resq 1
.len: resq 1
endstruc
; Usage: def_Slice %1: <slice_label>
%macro def_Slice 1
struc Slice_ %+ %1
.ptr: resq 1
.len: resq 1
endstruc
%endmacro
def_Slice Byte
; Usage: stack_slice %1: <type>, %2 <slice id>, %3 <stack_offset>
%macro stack_slice 2
call_frame_alloc %1 %+ _size
%endmacro
; Usage: slice_assert %1: Slice_<type> { .ptr = Slice.ptr, .len = Slice.len }
%macro slice_assert 1
%ifidn BUILD_DEBUG, 1
cmp qword [%1 + Slice.ptr], nullptr
jnz %%.ptr_passed
int debug_trap
%%.ptr_passed: ; macro-unique-prefix (%%) .passed is the label name
cmp qword [%1 + Slice.len], 0
jg %%.len_passed
int debug_trap
%%.len_passed:
%endif
%endmacro
; Usage slice_assert %1: ptr, %2: len
%macro slice_assert 2
%ifidn BUILD_DEBUG, 1
cmp %1, nullptr
jnz %%.ptr_passed
int debug_trap
%%.ptr_passed:
cmp %2, 0
jg %%.len_passed
int debug_trap
%%.len_passed:
%endif
%endmacro
; Usage: cf (call-frame begin)
; Establishes a new stack frame, saving the caller's frame.
; This is a standard function prologue.
%macro cf 0
push rstack_base_ptr ; Save the caller's frame pointer (RBP)
mov rstack_base_ptr, rstack_ptr ; Set our new frame pointer to the current stack position
%endmacro
; Usage: cf_alloc <size_or_symbol> (call-frame allocate)
%macro cf_alloc 1
sub rstack_ptr, %1 ; Allocate space by subtracting from the stack pointer (RSP)
%endmacro
; Usage: cf_commit (call-frame commit)
; Finalizes the stack frame by ensuring it is correctly aligned for a function call.
%macro cf_commit 0
; The Windows x64 ABI requires the stack pointer (RSP) to be 16-byte
; aligned immediately before a CALL instruction. This command ensures alignment
; by clearing the last 4 bits of RSP, rounding it down to the nearest multiple of 16.
and rstack_ptr, ~15
%endmacro
; Usage: cf_end (call-frame end)
; Tears down the stack frame, deallocating all memory and restoring the caller's frame.
%macro cf_end 0
mov rstack_ptr, rstack_base_ptr ; Deallocate the entire frame at once by resetting RSP to the saved RBP
pop rstack_base_ptr ; Restore the caller's frame pointer
%endmacro
; Usage: cf_ctbl <ctbl struc>
; Meant to only be used with call-table structs: <Symbol>_ctbl naming convention
%macro cf_ctbl 1
push rstack_base_ptr
mov rstack_base_ptr, rstack_ptr
sub rstack_ptr, %1 %+ _ctbl_size
and rstack_ptr, ~15
%endmacro
; Usage stac_alloc %1: <stack_offset>
%macro stack_push 1
push rstack_base_ptr
mov rstack_base_ptr, rstack_ptr
sub rstack_ptr, %1
%endmacro
%macro stack_pop 0
mov rstack_ptr, rstack_base_ptr
pop rstack_base_ptr
%endmacro
;endregion Memory
;region Allocator Interface
; TODO(Ed): Make it!
;endregion Allocator Interface
;region OS
; TODO(Ed): Make it!
;endregion OS
;region FArena
; TODO(Ed): Make it!
;endregion FArena
;region VArena
; TODO(Ed): Make it!
;endregion VArena
;region Arena
; TODO(Ed): Make it!
;endregion Arena
;region Strings
struc Str8
.ptr: resq 1
.len: resq 1
endstruc
def_Slice Slice_Str8
; Usage: lit %1: <slice_label>, %2: <utf-8 literal>
%macro lit 2
%%str_data: db %2
%%str_len: equ $ - %%str_data
%1:
istruc Str8
at Str8.ptr, dq %%str_data
at Str8.len, dq %%str_len
iend
%endmacro
section .data
lit path_hello_files_asm, `./code/asm/hello_files.asm`
;endregion Strings
;region String Ops
;region str8_to_cstr_capped
%push proc_scope
; result: rcounter = [UTF8]
; content: Str8 = { .ptr = rdata, .len = r8 }
; mem: r9 = [Slice_Byte]
%define result rcounter
%define content_ptr rdata
%define content_len r8
%define mem r9
section .text
str8_to_cstr_capped:
push raccumulator
push rdst_id
push rsrc_id
sub rstack_ptr, 8
; U64 raccumulator = min(content.len, mem.len - 1);
mov rsrc_id, qword [mem + Slice_Byte.len]
sub rsrc_id, 1
min_S64 content_len, rsrc_id ; raccumulator has result
; memory_copy(mem.ptr, content.ptr, copy_len);
mov rdst_id, qword [mem + Slice_Byte.ptr]
mov rsrc_id, content_ptr
mov rcounter, raccumulator
call memory_copy
; mem.ptr[copy_len] = '\0';
mov rdst_id, qword [mem + Slice_Byte.ptr]
mov byte [rdst_id + raccumulator], 0
; return cast(char*, mem.ptr);
mov result, qword [mem + Str8.ptr]
add rstack_ptr, 8
pop rsrc_id
pop rdst_id
pop raccumulator
ret
%pop proc_scope
;endregion str8_to_cstr_capped
;endregion String Ops
;region WinAPI
%define MS_INVALID_HANDLE_VALUE -1
%define MS_FILE_ATTRIBUTE_NORMAL 0x00000080
%define MS_FILE_SHARE_READ 0x00000001
%define MS_GENERIC_READ 0x80000000
%define MS_OPEN_EXISTING 3
%define MS_STD_OUTPUT_HANDLE -11
%define wapi_shadow_space 32
; kernel32.lib
; Process API
extern CloseHandle
extern ExitProcess
extern GetLastError
; File API
extern CreateFileA
extern GetFileSizeEx
extern ReadFile
extern WriteFileA
; Console IO
extern GetStdHandle
extern WriteConsoleA
struc wapi_ctbl
.shadow: resq 4 ; 32 bytes for RCX, RDX, R8, R9
endstruc
; rcx: hObject
struc CloseHandle_ctbl
.shadow: resq 4
endstruc
; rcx: uExitCode
struc ExitProcess_ctbl
.shadow: resq 4
endstruc
; no args
struc GetLastError_ctbl
.shadow: resq 4
endstruc
; rcx: lpFileName
; rdx: dwDesiredAccess
; r8: dwShareMode
; r9: lpSecurityAttributes
; s1: dwCreationDisposition
; s2: dwFlagsAndAttributes
; s3: hTemplateFile
; NOTE: Even though the first two are DWORDs, on the stack they each
; occupy a full 8-byte slot in the x64 ABI.
struc CreateFileA_ctbl
.shadow: resq 4
.dwCreationDisposition: resq 1
.dwFlagsAndAttributes: resq 1
.hTemplateFile: resq 1
endstruc
; rcx: hFile
; rdx: lpFileSize
struc GetFileSizeEx_ctbl
.shadow: resq 4
endstruc
; rcx: hFile
; rdx: lpBuffer
; r8: nNumberOfBytesToRead
; r9: lpNumberOfBytesRead
; s1: lpOverlapped
struc ReadFile_ctbl
.shadow: resq 4
.lpOverlapped: resq 1
endstruc
; rcx: hFile
; rdx: lpBuffer
; r8: nNumberOfBytesToWrite
; r9: lpNumberOfBytesWritten
; s1: lpOverlapped
struc WriteFileA_ctbl
.shadow: resq 4
.lpOverlapped: resq 1
endstruc
struc FileOpInfo
.content: resb Slice_Byte_size
endstruc
; rcx: nStdHandle
struc GetStdHandle_ctbl
.shadow: resq 4
endstruc
; rcx: hConsoleOutput
; rdx: lpBuffer
; r8: nNumberOfCharsToWrite
; r9: lpNumberOfCharsWritten
; s1: lpReserved
struc WriteConsoleA_ctbl
.shadow: resq 4
.lpReserved: resq 1
.lpNumberOfCharsWritten: resq 1
endstruc
section .data
std_out_hndl dq 0
;endregion WinAPI
;region file_read_contents
%push proc_scope
; Reg allocation:
; result: rcounter = [FileOpInfo]
; path: Slice_Str8 = { .ptr = rdata, .len = r8 }
; backing: r9 = [Slice_Byte]
%define result rcounter
%define path_ptr rdata
%define path_len r8
%define backing r9
section .text
file_read_contents:
; validation
assert_not_null result
slice_assert backing
slice_assert path_ptr, path_len
; save registers
push rbase ; id_file
push r12 ; result
push r13 ; backing
push r14 ; file_size
mov r12, result
mov r13, backing
%define result r12
%define backing r13
; rcounter = str8_to_cstr_capped(path, slice_fmem(scratch));
; We're using backing to store the cstr temporarily until ReadFile.
call str8_to_cstr_capped ; (rdata, r8, r9)
; path_cstr = rcounter; path_len has will be discarded in the CreateFileA call
%define path_cstr rcounter
cf_ctbl CreateFileA ; call-frame CreateFileA {
; rcounter = path_cstr
mov rdata_32, MS_GENERIC_READ ; dwDesiredAccess = MS_GENERIC_READ
mov r8_32, MS_FILE_SHARE_READ ; dwShareMode = MS_FILE_SHARE_READ
xor r9, r9 ; lpSecurityAttributes = nullptr
mov qword [rstack_ptr + CreateFileA_ctbl.dwCreationDisposition], MS_OPEN_EXISTING ; stack.ptr[.dwCreationDisposition] = MS_OPEN_EXISTING
mov qword [rstack_ptr + CreateFileA_ctbl.dwFlagsAndAttributes ], MS_FILE_ATTRIBUTE_NORMAL ; stack.ptr[.dwFlagsAndAttributes ] = MS_FILE_ATTRIBUTE_NORMAL
mov qword [rstack_ptr + CreateFileA_ctbl.hTemplateFile ], nullptr ; stack.ptr[.hTemplateFile ] = nullptr
call CreateFileA ; CreateFileA <- rcounter, rdata, r8, r9, stack
cf_end ; }
; B32 open_failed = raccumulator == MS_INVALID_HANDLE_VALUE
; if (open_failed) goto %%.error_exit
assert_cmp jne, raccumulator, MS_INVALID_HANDLE_VALUE
je .error_exit
mov rbase, raccumulator ; rbase = id_file
%define id_file rbase
cf_ctbl GetFileSizeEx ; call-frame GetFileSizeEx {
mov rcounter, id_file ; rcounter = id_file
lea rdata, [result + FileOpInfo.content + Slice_Byte.len] ; lpFileSize = result.content.len
call GetFileSizeEx ; GetFileSizeEx <- rcounter, rdata, stack
cf_end ; }
; B32 not_enough_backing = result.content.len > backing.len
; if (not_enough_backing) goto .error_close_handle
mov r8, [backing + Slice_Byte.len] ; r8 = backing.len
mov r9, [result + FileOpInfo.content + Slice_Byte.len] ; r9 = result.content.len
assert_cmp jle, r9, r8 ; r9 <= r8
jg .error_close_handle ; if (flagged greater) goto .error_close_handle
; MS_BOOL get_size_failed = ! raccumulator
; if (get_size_failed) goto .error_exit
assert_cmp jne, raccumulator, false ; raccumulator != false
je .error_close_handle ; if (flagged equal) goto .error_close_handle
%define file_size r14d
mov r14d, r9d
cf_ctbl ReadFile ; call-frame ReadFile {
mov rcounter, id_file ; hfile: rcounter = rbase
mov rdata, [backing + Slice_Byte.ptr ] ; lpBuffer: rdata = backing.ptr
mov r8_32, file_size ; nNumberOfBytesToRead: r8_32 = file_size
lea r9, [result + FileOpInfo.content + Slice_Byte.len] ; lpNumberOfBytesRead: r9 = & result.content.len
mov qword [rstack_ptr + ReadFile_ctbl.lpOverlapped], 0 ; lpOverlapped: nullptr
call ReadFile ; ReadFile <- rcounter, rata, r8, r9, stack
cf_end ; }
; B32 read_failed = ! read_result
; if (read_failed) goto .error_exit
assert_cmp jnz, raccumulator, false
je .error_exit
; read_failed |= amount_read != result.content.len
; if (read_failed) goto .error_exit
mov r9, qword [result + FileOpInfo.content + Slice_Byte.len]
assert_cmp je, file_size, r9d
jne .error_close_handle
; CloseHandle(id_file)
cf_ctbl CloseHandle ; call-frame CloseHandle {
mov rcounter, id_file ; rcounter = id_file (rbase)
call CloseHandle ; CloseHandle <- rcounter, stack
cf_end ; }
; reslt.content.ptr = raccumulator
mov raccumulator, [backing + Slice_Byte.ptr] ; raccumulator = backing.ptr
mov [result + FileOpInfo.content + Slice_Byte.ptr], raccumulator ; result.content.ptr = raccumulator
jmp .cleanup ; goto .cleanup
.error_close_handle:
cf_ctbl CloseHandle ; call-frame CloseHandle {
mov rcounter, rbase ; rcounter = id_file (rbase)
call CloseHandle ; CloseHandle <- rcounter, stack
cf_end ; }
.error_exit:
; result = {}
mov qword [result + FileOpInfo.content + Slice_Byte.ptr], 0
mov qword [result + FileOpInfo.content + Slice_Byte.len], 0
.cleanup:
pop r14 ; file_size
pop backing
pop result
pop id_file
; restore registers
ret
section .bss
; local_persist raw_scratch : [64 * kilo]byte
file_read_contents.raw_scratch: resb 64 * kilo
file_read_contents.path_cstr: resq 1
section .data
; local_persist scratch = fmem_slice(raw_scratch)
file_read_contents.scratch:
istruc Slice_Byte
at Slice_Byte.ptr, dq file_read_contents.raw_scratch
at Slice_Byte.len, dq 64 * kilo
iend
%pop proc_scope
;endregion file_read_contents
section .text
global main
%push proc_scope
main:
xor rcx, rcx
cf_ctbl GetStdHandle ; call-frame GetStdHandle {
mov rcounter_32, MS_STD_OUTPUT_HANDLE ; rcounter.32 = MS_STD_OUTPUT_HANDLE
call GetStdHandle ; GetStdHandle <- rcounter, stack
cf_end ; }
mov [std_out_hndl], raccumulator ; std_out_hndl = raccumulator
; TODO(Ed): Setup an arena allocator for backing!
; TODO(ED): Setup allocator info for an arena
%push calling
cf ; call-frame file_read_contents {
; Need to pass the allocator info
; %define backing rsp + Slice_Byte_size
cf_commit
mov qword [backing + Slice_Byte.ptr], read_mem ; local_backing.ptr = read_mem.ptr
mov qword [backing + Slice_Byte.len], Mem_128k_size ; local_backing.len = Mem_128k_size
lea rcounter, file ; rcounter = file.ptr
mov rdata, [path_hello_files_asm + Str8.ptr] ; rdata = path_hello_files.ptr
mov r8, [path_hello_files_asm + Str8.len] ; r8 = path_hello_files.len
lea r9, [backing] ; r9 = & local_backing
call file_read_contents ; read_file_contents <- rcounter, rdata, r8, r9, stack
cf_end ; }
%pop calling
cf_ctbl ExitProcess ; call-frame ExitProcess {
xor ecx, ecx ; ecx = 0
call ExitProcess ; ExitProcess <- rcx, stack
cf_end ; }
ret
%pop proc_scope
section .bss
read_mem: resb Mem_128k_size ; internal global read_mem: Mem_128k
file: resb FileOpInfo_size ; internal global file: FileOpInfo

688
code/asm/hello_files.asm Normal file
View File

@@ -0,0 +1,688 @@
; Hello Files!
BITS 64 ; Explicitly specify 64-bit mode
DEFAULT REL ; Use RIP-relative addressing by default
%define BUILD_DEBUG 1
;region DSL
%define marg
%define rcounter_32 ecx
%define rdata_32 edx
%define r8_32 r8d
%define r9_32 r9d
%define raccumulator rax
%define rbase rbx
%define rcounter rcx
%define rdata rdx
%define rdst_id rdi
%define rsrc_id rsi
%define rstack_ptr rsp
%define rstack_base_ptr rbp
%define false 0
%define true 1
%define true_overflow 2
;endregion DSL
;region Registers
; Wipes all 64-bit general-purpose registers except for the stack pointer (RSP).
; Zeroing RSP would corrupt the stack and crash the program.
; Zeroing RBP will break the stack frame chain used by debuggers.
%macro wipe_gprs 0
xor rax, rax
xor rbx, rbx
xor rcx, rcx
xor rdx, rdx
xor rsi, rsi
xor rdi, rdi
; xor rbp, rbp
xor r8, r8
xor r9, r9
xor r10, r10
xor r11, r11
xor r12, r12
xor r13, r13
xor r14, r14
xor r15, r15
%endmacro
; Resets the Floating-Point Unit (FPU), which also clears all MMX registers
; (MM0-MM7) and FPU stack registers (ST0-ST7).
%macro wipe_fpu_mmxs 0
finit
%endmacro
; Wipes the 128-bit XMM registers. Requires a CPU with at least SSE.
%macro wipe_xmms 0
vxorps xmm0, xmm0, xmm0
vxorps xmm1, xmm1, xmm1
vxorps xmm2, xmm2, xmm2
vxorps xmm3, xmm3, xmm3
vxorps xmm4, xmm4, xmm4
vxorps xmm5, xmm5, xmm5
vxorps xmm6, xmm6, xmm6
vxorps xmm7, xmm7, xmm7
vxorps xmm8, xmm8, xmm8
vxorps xmm9, xmm9, xmm9
vxorps xmm10, xmm10, xmm10
vxorps xmm11, xmm11, xmm11
vxorps xmm12, xmm12, xmm12
vxorps xmm13, xmm13, xmm13
vxorps xmm14, xmm14, xmm14
vxorps xmm15, xmm15, xmm15
%endmacro
; =============================================================================
; AVX Registers (YMM0-YMM15)
; =============================================================================
; Wipes the 256-bit YMM registers. Requires a CPU with AVX support.
; This also wipes the lower 128 bits (the XMM registers), so you don't
; need to call WIPE_XMM_REGS if you call this one.
%macro wipe_ymms 0
vzeroupper ; Clears upper 128 bits of all YMM registers
vxorps ymm0, ymm0, ymm0 ; Clears the full YMM0 (including lower XMM0)
vxorps ymm1, ymm1, ymm1
vxorps ymm2, ymm2, ymm2
vxorps ymm3, ymm3, ymm3
vxorps ymm4, ymm4, ymm4
vxorps ymm5, ymm5, ymm5
vxorps ymm6, ymm6, ymm6
vxorps ymm7, ymm7, ymm7
vxorps ymm8, ymm8, ymm8
vxorps ymm9, ymm9, ymm9
vxorps ymm10, ymm10, ymm10
vxorps ymm11, ymm11, ymm11
vxorps ymm12, ymm12, ymm12
vxorps ymm13, ymm13, ymm13
vxorps ymm14, ymm14, ymm14
vxorps ymm15, ymm15, ymm15
%endmacro
; =============================================================================
; AVX-512 Registers (ZMM0-ZMM31 and K0-K7)
; =============================================================================
; Wipes the 512-bit ZMM registers and the 8 mask registers (k0-k7).
; Requires a CPU with AVX-512F support. This is the most comprehensive
; vector register wipe and makes WIPE_XMM_REGS and WIPE_YMM_REGS redundant.
%macro wipe_avx512s 0
; Wipe Mask Registers (k0-k7)
kxorb k0, k0, k0
kxorb k1, k1, k1
kxorb k2, k2, k2
kxorb k3, k3, k3
kxorb k4, k4, k4
kxorb k5, k5, k5
kxorb k6, k6, k6
kxorb k7, k7, k7
; Wipe ZMM registers (zmm0-zmm31)
vpxord zmm0, zmm0, zmm0
vpxord zmm1, zmm1, zmm1
vpxord zmm2, zmm2, zmm2
vpxord zmm3, zmm3, zmm3
vpxord zmm4, zmm4, zmm4
vpxord zmm5, zmm5, zmm5
vpxord zmm6, zmm6, zmm6
vpxord zmm7, zmm7, zmm7
vpxord zmm8, zmm8, zmm8
vpxord zmm9, zmm9, zmm9
vpxord zmm10, zmm10, zmm10
vpxord zmm11, zmm11, zmm11
vpxord zmm12, zmm12, zmm12
vpxord zmm13, zmm13, zmm13
vpxord zmm14, zmm14, zmm14
vpxord zmm15, zmm15, zmm15
vpxord zmm16, zmm16, zmm16
vpxord zmm17, zmm17, zmm17
vpxord zmm18, zmm18, zmm18
vpxord zmm19, zmm19, zmm19
vpxord zmm20, zmm20, zmm20
vpxord zmm21, zmm21, zmm21
vpxord zmm22, zmm22, zmm22
vpxord zmm23, zmm23, zmm23
vpxord zmm24, zmm24, zmm24
vpxord zmm25, zmm25, zmm25
vpxord zmm26, zmm26, zmm26
vpxord zmm27, zmm27, zmm27
vpxord zmm28, zmm28, zmm28
vpxord zmm29, zmm29, zmm29
vpxord zmm30, zmm30, zmm30
vpxord zmm31, zmm31, zmm31
%endmacro
;endregion Registers
;region Debug
%define debug_trap 3
%ifidn BUILD_DEBUG, 1
%macro assert_cmp 3
cmp %2, %3
%1 %%.passed
int debug_trap
%%.passed:
%endmacro
%macro assert_not_null 1
cmp %1, nullptr
jnz %%.passed
int debug_trap
%%.passed: ; macro-unique-prefix (%%) .passed is the label name
%endmacro
%define dbg_wipe_gprs wipe_gprs
%define dbg_wipe_fpu_mmxs wipe_fpu_mmxs
%define dbg_wipe_xmms wipe_xmms
%define dbg_wipe_ymms wipe_ymms
%define dbg_wipe_avx512s wipe_avx512s
%else
%macro assert_cmp 3
%cmp %2, %3
%endmacro
%macro assert_not_null 1
%endmacro
%macro slice_assert 1
%endmacro
%define dbg_wipe_gprs
%define dbg_wipe_fpu_mmxs
%define dbg_wipe_xmms
%define dbg_wipe_ymms
%define dbg_wipe_avx512s
%endif ; BUILD_DEBUG
;endregion Debug
;region Memory
%define nullptr 0
%define kilo 1024
; Usage: def_array <name: %1> <size: %2>
%macro def_farray 2+
struc %1
.ptr: resb %2
endstruc
%endmacro
def_farray Mem_128k, 128 * kilo
;region memory_copy
; dst = rdst_id = [Byte]
; src = rsrc_id = [Byte]
; rcounter = U64
section .text
memory_copy:
cld
rep movsb ; REPEAT MoveStringByte
; 1. Copies the byte from [RSI] to [RDI].
; 2. Increments RSI and RDI (because of CLD).
; 3. Decrements RCX.
; 4. Repeats until RCX is 0.
ret
;endregion memory_copy
struc Slice
.ptr: resq 1
.len: resq 1
endstruc
; Usage: def_Slice %1: <slice_label>
%macro def_Slice 1
struc Slice_ %+ %1
.ptr: resq 1
.len: resq 1
endstruc
%endmacro
def_Slice Byte
; Usage: stack_slice %1: <type>, %2 <slice id>, %3 <stack_offset>
%macro stack_slice 2
call_frame_alloc %1 %+ _size
%endmacro
; Usage: slice_assert %1: Slice_<type> { .ptr = Slice.ptr, .len = Slice.len }
%macro slice_assert 1
%ifidn BUILD_DEBUG, 1
cmp qword [%1 + Slice.ptr], nullptr
jnz %%.ptr_passed
int debug_trap
%%.ptr_passed: ; macro-unique-prefix (%%) .passed is the label name
cmp qword [%1 + Slice.len], 0
jg %%.len_passed
int debug_trap
%%.len_passed:
%endif
%endmacro
; Usage slice_assert %1: ptr, %2: len
%macro slice_assert 2
%ifidn BUILD_DEBUG, 1
cmp %1, nullptr
jnz %%.ptr_passed
int debug_trap
%%.ptr_passed:
cmp %2, 0
jg %%.len_passed
int debug_trap
%%.len_passed:
%endif
%endmacro
; Usage: cf (call-frame begin)
; Establishes a new stack frame, saving the caller's frame.
; This is a standard function prologue.
%macro cf 0
push rstack_base_ptr ; Save the caller's frame pointer (RBP)
mov rstack_base_ptr, rstack_ptr ; Set our new frame pointer to the current stack position
%endmacro
; Usage: cf_alloc <size_or_symbol> (call-frame allocate)
%macro cf_alloc 1
sub rstack_ptr, %1 ; Allocate space by subtracting from the stack pointer (RSP)
%endmacro
; Usage: cf_commit (call-frame commit)
; Finalizes the stack frame by ensuring it is correctly aligned for a function call.
%macro cf_commit 0
; The Windows x64 ABI requires the stack pointer (RSP) to be 16-byte
; aligned immediately before a CALL instruction. This command ensures alignment
; by clearing the last 4 bits of RSP, rounding it down to the nearest multiple of 16.
and rstack_ptr, ~15
%endmacro
; Usage: cf_end (call-frame end)
; Tears down the stack frame, deallocating all memory and restoring the caller's frame.
%macro cf_end 0
mov rstack_ptr, rstack_base_ptr ; Deallocate the entire frame at once by resetting RSP to the saved RBP
pop rstack_base_ptr ; Restore the caller's frame pointer
%endmacro
; Usage: cf_ctbl <ctbl struc>
; Meant to only be used with call-table structs: <Symbol>_ctbl naming convention
%macro cf_ctbl 1
push rstack_base_ptr
mov rstack_base_ptr, rstack_ptr
sub rstack_ptr, %1 %+ _ctbl_size
and rstack_ptr, ~15
%endmacro
; Usage stac_alloc %1: <stack_offset>
%macro stack_push 1
push rstack_base_ptr
mov rstack_base_ptr, rstack_ptr
sub rstack_ptr, %1
%endmacro
%macro stack_pop 0
mov rstack_ptr, rstack_base_ptr
pop rstack_base_ptr
%endmacro
;endregion Memory
;region Math
; returns: raccumulator = U64
; Usage
%macro min_S64 2
mov raccumulator, %1
cmp raccumulator, %2
cmovg raccumulator, %2 ; ConditionalMoveIfGreater
%endmacro min_S64
;endregion Math
;region Strings
struc Str8
.ptr: resq 1
.len: resq 1
endstruc
def_Slice Slice_Str8
; Usage: lit %1: <slice_label>, %2: <utf-8 literal>
%macro lit 2
%%str_data: db %2
%%str_len: equ $ - %%str_data
%1:
istruc Str8
at Str8.ptr, dq %%str_data
at Str8.len, dq %%str_len
iend
%endmacro
section .data
lit path_hello_files_asm, `./code/asm/hello_files.asm`
;endregion Strings
;region String Ops
;region str8_to_cstr_capped
%push proc_scope
; result: rcounter = [UTF8]
; content: Str8 = { .ptr = rdata, .len = r8 }
; mem: r9 = [Slice_Byte]
%define result rcounter
%define content_ptr rdata
%define content_len r8
%define mem r9
section .text
str8_to_cstr_capped:
push raccumulator
push rdst_id
push rsrc_id
sub rstack_ptr, 8
; U64 raccumulator = min(content.len, mem.len - 1);
mov rsrc_id, qword [mem + Slice_Byte.len]
sub rsrc_id, 1
min_S64 content_len, rsrc_id ; raccumulator has result
; memory_copy(mem.ptr, content.ptr, copy_len);
mov rdst_id, qword [mem + Slice_Byte.ptr]
mov rsrc_id, content_ptr
mov rcounter, raccumulator
call memory_copy
; mem.ptr[copy_len] = '\0';
mov rdst_id, qword [mem + Slice_Byte.ptr]
mov byte [rdst_id + raccumulator], 0
; return cast(char*, mem.ptr);
mov result, qword [mem + Str8.ptr]
add rstack_ptr, 8
pop rsrc_id
pop rdst_id
pop raccumulator
ret
%pop proc_scope
;endregion str8_to_cstr_capped
;endregion String Ops
;region WinAPI
%define MS_INVALID_HANDLE_VALUE -1
%define MS_FILE_ATTRIBUTE_NORMAL 0x00000080
%define MS_FILE_SHARE_READ 0x00000001
%define MS_GENERIC_READ 0x80000000
%define MS_OPEN_EXISTING 3
%define MS_STD_OUTPUT_HANDLE -11
%define wapi_shadow_space 32
; kernel32.lib
; Process API
extern CloseHandle
extern ExitProcess
extern GetLastError
; File API
extern CreateFileA
extern GetFileSizeEx
extern ReadFile
extern WriteFileA
; Console IO
extern GetStdHandle
extern WriteConsoleA
struc wapi_ctbl
.shadow: resq 4 ; 32 bytes for RCX, RDX, R8, R9
endstruc
; rcx: hObject
struc CloseHandle_ctbl
.shadow: resq 4
endstruc
; rcx: uExitCode
struc ExitProcess_ctbl
.shadow: resq 4
endstruc
; no args
struc GetLastError_ctbl
.shadow: resq 4
endstruc
; rcx: lpFileName
; rdx: dwDesiredAccess
; r8: dwShareMode
; r9: lpSecurityAttributes
; s1: dwCreationDisposition
; s2: dwFlagsAndAttributes
; s3: hTemplateFile
; NOTE: Even though the first two are DWORDs, on the stack they each
; occupy a full 8-byte slot in the x64 ABI.
struc CreateFileA_ctbl
.shadow: resq 4
.dwCreationDisposition: resq 1
.dwFlagsAndAttributes: resq 1
.hTemplateFile: resq 1
endstruc
; rcx: hFile
; rdx: lpFileSize
struc GetFileSizeEx_ctbl
.shadow: resq 4
endstruc
; rcx: hFile
; rdx: lpBuffer
; r8: nNumberOfBytesToRead
; r9: lpNumberOfBytesRead
; s1: lpOverlapped
struc ReadFile_ctbl
.shadow: resq 4
.lpOverlapped: resq 1
endstruc
; rcx: hFile
; rdx: lpBuffer
; r8: nNumberOfBytesToWrite
; r9: lpNumberOfBytesWritten
; s1: lpOverlapped
struc WriteFileA_ctbl
.shadow: resq 4
.lpOverlapped: resq 1
endstruc
struc FileOpInfo
.content: resb Slice_Byte_size
endstruc
; rcx: nStdHandle
struc GetStdHandle_ctbl
.shadow: resq 4
endstruc
; rcx: hConsoleOutput
; rdx: lpBuffer
; r8: nNumberOfCharsToWrite
; r9: lpNumberOfCharsWritten
; s1: lpReserved
struc WriteConsoleA_ctbl
.shadow: resq 4
.lpReserved: resq 1
.lpNumberOfCharsWritten: resq 1
endstruc
section .data
std_out_hndl dq 0
;endregion WinAPI
;region file_read_contents
%push proc_scope
; Reg allocation:
; result: rcounter = [FileOpInfo]
; path: Slice_Str8 = { .ptr = rdata, .len = r8 }
; backing: r9 = [Slice_Byte]
%define result rcounter
%define path_ptr rdata
%define path_len r8
%define backing r9
section .text
file_read_contents:
; validation
assert_not_null result
slice_assert backing
slice_assert path_ptr, path_len
; save registers
push rbase ; id_file
push r12 ; result
push r13 ; backing
push r14 ; file_size
mov r12, result
mov r13, backing
%define result r12
%define backing r13
; rcounter = str8_to_cstr_capped(path, slice_fmem(scratch));
; We're using backing to store the cstr temporarily until ReadFile.
call str8_to_cstr_capped ; (rdata, r8, r9)
; path_cstr = rcounter; path_len has will be discarded in the CreateFileA call
%define path_cstr rcounter
cf_ctbl CreateFileA ; call-frame CreateFileA {
; rcounter = path_cstr
mov rdata_32, MS_GENERIC_READ ; dwDesiredAccess = MS_GENERIC_READ
mov r8_32, MS_FILE_SHARE_READ ; dwShareMode = MS_FILE_SHARE_READ
xor r9, r9 ; lpSecurityAttributes = nullptr
mov qword [rstack_ptr + CreateFileA_ctbl.dwCreationDisposition], MS_OPEN_EXISTING ; stack.ptr[.dwCreationDisposition] = MS_OPEN_EXISTING
mov qword [rstack_ptr + CreateFileA_ctbl.dwFlagsAndAttributes ], MS_FILE_ATTRIBUTE_NORMAL ; stack.ptr[.dwFlagsAndAttributes ] = MS_FILE_ATTRIBUTE_NORMAL
mov qword [rstack_ptr + CreateFileA_ctbl.hTemplateFile ], nullptr ; stack.ptr[.hTemplateFile ] = nullptr
call CreateFileA ; CreateFileA <- rcounter, rdata, r8, r9, stack
cf_end ; }
; B32 open_failed = raccumulator == MS_INVALID_HANDLE_VALUE
; if (open_failed) goto %%.error_exit
assert_cmp jne, raccumulator, MS_INVALID_HANDLE_VALUE
je .error_exit
mov rbase, raccumulator ; rbase = id_file
%define id_file rbase
cf_ctbl GetFileSizeEx ; call-frame GetFileSizeEx {
mov rcounter, id_file ; rcounter = id_file
lea rdata, [result + FileOpInfo.content + Slice_Byte.len] ; lpFileSize = result.content.len
call GetFileSizeEx ; GetFileSizeEx <- rcounter, rdata, stack
cf_end ; }
; B32 not_enough_backing = result.content.len > backing.len
; if (not_enough_backing) goto .error_close_handle
mov r8, [backing + Slice_Byte.len] ; r8 = backing.len
mov r9, [result + FileOpInfo.content + Slice_Byte.len] ; r9 = result.content.len
assert_cmp jle, r9, r8 ; r9 <= r8
jg .error_close_handle ; if (flagged greater) goto .error_close_handle
; MS_BOOL get_size_failed = ! raccumulator
; if (get_size_failed) goto .error_exit
assert_cmp jne, raccumulator, false ; raccumulator != false
je .error_close_handle ; if (flagged equal) goto .error_close_handle
%define file_size r14d
mov r14d, r9d
cf_ctbl ReadFile ; call-frame ReadFile {
mov rcounter, id_file ; hfile: rcounter = rbase
mov rdata, [backing + Slice_Byte.ptr ] ; lpBuffer: rdata = backing.ptr
mov r8_32, file_size ; nNumberOfBytesToRead: r8_32 = file_size
lea r9, [result + FileOpInfo.content + Slice_Byte.len] ; lpNumberOfBytesRead: r9 = & result.content.len
mov qword [rstack_ptr + ReadFile_ctbl.lpOverlapped], 0 ; lpOverlapped: nullptr
call ReadFile ; ReadFile <- rcounter, rata, r8, r9, stack
cf_end ; }
; B32 read_failed = ! read_result
; if (read_failed) goto .error_exit
assert_cmp jnz, raccumulator, false
je .error_exit
; read_failed |= amount_read != result.content.len
; if (read_failed) goto .error_exit
mov r9, qword [result + FileOpInfo.content + Slice_Byte.len]
assert_cmp je, file_size, r9d
jne .error_close_handle
; CloseHandle(id_file)
cf_ctbl CloseHandle ; call-frame CloseHandle {
mov rcounter, id_file ; rcounter = id_file (rbase)
call CloseHandle ; CloseHandle <- rcounter, stack
cf_end ; }
; reslt.content.ptr = raccumulator
mov raccumulator, [backing + Slice_Byte.ptr] ; raccumulator = backing.ptr
mov [result + FileOpInfo.content + Slice_Byte.ptr], raccumulator ; result.content.ptr = raccumulator
jmp .cleanup ; goto .cleanup
.error_close_handle:
cf_ctbl CloseHandle ; call-frame CloseHandle {
mov rcounter, rbase ; rcounter = id_file (rbase)
call CloseHandle ; CloseHandle <- rcounter, stack
cf_end ; }
.error_exit:
; result = {}
mov qword [result + FileOpInfo.content + Slice_Byte.ptr], 0
mov qword [result + FileOpInfo.content + Slice_Byte.len], 0
.cleanup:
pop r14 ; file_size
pop backing
pop result
pop id_file
; restore registers
ret
section .bss
; local_persist raw_scratch : [64 * kilo]byte
file_read_contents.raw_scratch: resb 64 * kilo
file_read_contents.path_cstr: resq 1
section .data
; local_persist scratch = fmem_slice(raw_scratch)
file_read_contents.scratch:
istruc Slice_Byte
at Slice_Byte.ptr, dq file_read_contents.raw_scratch
at Slice_Byte.len, dq 64 * kilo
iend
%pop proc_scope
;endregion file_read_contents
section .text
global main
%push proc_scope
main:
xor rcx, rcx
cf_ctbl GetStdHandle ; call-frame GetStdHandle {
mov rcounter_32, MS_STD_OUTPUT_HANDLE ; rcounter.32 = MS_STD_OUTPUT_HANDLE
call GetStdHandle ; GetStdHandle <- rcounter, stack
cf_end ; }
mov [std_out_hndl], raccumulator ; std_out_hndl = raccumulator
%push calling
cf ; call-frame file_read_contents {
cf_alloc Slice_Byte_size ; stack local_backing : Slice_byte
cf_commit
%define local_backing rsp + Slice_Byte_size
mov qword [local_backing + Slice_Byte.ptr], read_mem ; local_backing.ptr = read_mem.ptr
mov qword [local_backing + Slice_Byte.len], Mem_128k_size ; local_backing.len = Mem_128k_size
lea rcounter, file ; rcounter = file.ptr
mov rdata, [path_hello_files_asm + Str8.ptr] ; rdata = path_hello_files.ptr
mov r8, [path_hello_files_asm + Str8.len] ; r8 = path_hello_files.len
lea r9, [local_backing] ; r9 = & local_backing
call file_read_contents ; read_file_contents <- rcounter, rdata, r8, r9, stack
cf_end ; }
%pop calling
cf_ctbl WriteConsoleA ; call-frame WriteConsoleA {
mov rcounter, [std_out_hndl] ; rcounter = std_out_hndl
mov rdata, [file + FileOpInfo.content + Slice_Byte.ptr] ; rdata = file.content.ptr
mov r8_32, [file + FileOpInfo.content + Slice_Byte.len] ; r8 = file.content.len
lea r9, [rstack_ptr + WriteConsoleA_ctbl.lpNumberOfCharsWritten] ; r9 = & stack.ptr[WriteFileA.ctbl.lpNumberOfCharsWritten]
mov qword [rstack_ptr + WriteConsoleA_ctbl.lpReserved], nullptr ; stack.ptr[.ctbl.lpRserved] = nullptr
call WriteConsoleA ; WriteConsoleA <- rcounter, rdata, r9, stack
cf_end
cf_ctbl ExitProcess ; call-frame ExitProcess {
xor ecx, ecx ; ecx = 0
call ExitProcess ; ExitProcess <- rcx, stack
cf_end ; }
ret
%pop proc_scope
section .bss
read_mem: resb Mem_128k_size ; internal global read_mem: Mem_128k
file: resb FileOpInfo_size ; internal global file: FileOpInfo

43
code/asm/hello_nasm.asm Normal file
View File

@@ -0,0 +1,43 @@
; hello.asm - Hello World with debug symbols for YASM
BITS 64 ; Explicitly specify 64-bit mode
DEFAULT REL ; Use RIP-relative addressing by default
extern ExitProcess ; Import Windows API functions
extern GetStdHandle
extern WriteConsoleA
; Data section
section .data
message db "Hello, NASM!", 13, 10, 0 ; String with CRLF and null terminator
message_len equ $ - message ; Calculate string length
; Code section
section .text
global main ; Export main symbol for linker
main:
; Function prologue
push rbp
mov rbp, rsp
sub rsp, 32 ; Shadow space for Windows API calls
; Get stdout handle
mov ecx, -11 ; STD_OUTPUT_HANDLE
call GetStdHandle
mov rbx, rax ; Save handle for WriteConsole
; Write message
mov rcx, rbx ; Console handle
lea rdx, [message] ; Message buffer
mov r8d, message_len ; Message length
lea r9, [rsp+28] ; Written chars (unused)
mov qword [rsp+20], 0 ; Reserved (must be 0)
call WriteConsoleA
; Exit program
xor ecx, ecx ; Exit code 0
call ExitProcess
; Function epilogue (not reached due to ExitProcess)
leave
ret

81
code/asm/hello_slices.asm Normal file
View File

@@ -0,0 +1,81 @@
; Hello Slices!
%define marg
%define rcounter_32 ecx
%define reg8_32 r8d
%define raccumulator rax
%define rcounter rcx
%define rdata rdx
%define rstack_ptr rsp
%define rstack_base_ptr rbp
%define reg9 r9
%define MS_STD_OUTPUT_HANDLE 11
struc Slice_Byte
.ptr: resq 1
.len: resq 1
endstruc
struc Slice_Str8
.ptr: resq 1
.len: resq 1
endstruc
; Usage: lit %1: <slice_label> %2: <utf-8 literal>
%macro lit 2
lit_ %+ %1: db %2
lit_ %+ %1 %+ _len: equ $ - (lit_ %+ %1)
%endmacro
BITS 64 ; Explicitly specify 64-bit mode
DEFAULT REL ; Use RIP-relative addressing by default
; kernel32.lib
extern GetStdHandle
extern WriteConsoleA
section .data
std_out_hndl dq 0
section .lits progbits noexec nowrite
lit hello_msg, `Hello Slices\n`
section .text
global main
%define wapi_shadow_width 48
%macro wapi_shadow_space 0
push rstack_base_ptr
mov rstack_base_ptr, rstack_ptr
sub rstack_ptr, wapi_shadow_width
%endmacro
%define wapi_arg4_offset 28
%define wapi_arg5_offset 32
%define wapi_write_console_written_chars reg9
%macro wapi_write_console 2
mov rcounter,[%1] ; Console Handle
lea rdata, [%2] ; Slice_Str8.Ptr
mov reg8_32, %2 %+ _len ; Slice_Str8.Len
lea reg9, [rstack_ptr + wapi_arg4_offset] ; Written chars
mov qword [rstack_ptr + wapi_arg5_offset], 0 ; Reserved (must be 0)
call WriteConsoleA
%endmacro
main:
wapi_shadow_space
; Setup stdout handle
mov rcounter_32, -MS_STD_OUTPUT_HANDLE
call GetStdHandle
mov [std_out_hndl], raccumulator
wapi_write_console std_out_hndl, lit_hello_msg
leave
ret

1
code/asm/readme.md Normal file
View File

@@ -0,0 +1 @@
# Handwritten assembly

23
code/host.odin Normal file
View File

@@ -0,0 +1,23 @@
package asmduff
section_data :: proc()
{
}
section_text :: proc()
{
}
section_externs :: proc()
{
}
main :: proc()
{
}

3
code/pkg_mappings.odin Normal file
View File

@@ -0,0 +1,3 @@
package asmduff
// import

120
scripts/build.odin Normal file
View File

@@ -0,0 +1,120 @@
package build
//region Script Grime
import mem "core:mem"
import vmem "core:mem/virtual"
import os "core:os/os2"
import "core:fmt"
import _strings "core:strings"
StrGen :: _strings.Builder
strgen_init :: _strings.builder_init
strgen_join :: _strings.join
join_path :: #force_inline proc(elems : ..string) -> string { res, _ := os.join_path(transmute([]string)elems, context.allocator); return transmute(string)res }
get_working_dir :: #force_inline proc() -> string { res, _ := os.get_working_directory(context.allocator); return transmute(string)res }
join_str :: #force_inline proc(elems : ..string) -> string {
gen : StrGen; strgen_init(& gen, context.allocator)
res, _ := strgen_join(elems, "")
return res
}
// For a beakdown of any flag, type <odin_compiler> <command> -help
command_build :: "build"
command_check :: "check"
command_query :: "query"
command_report :: "report"
command_run :: "run"
flag_build_mode :: "-build-mode:"
flag_build_mode_dll :: "-build-mode:dll"
flag_collection :: "-collection:"
flag_file :: "-file"
flag_debug :: "-debug"
flag_define :: "-define:"
flag_default_allocator_nil :: "-default-to-nil-allocator"
flag_disable_assert :: "-disable-assert"
flag_dynamic_map_calls :: "-dynamic-map-calls"
flag_extra_assembler_flags :: "-extra_assembler-flags:"
flag_extra_linker_flags :: "-extra-linker-flags:"
flag_ignore_unknown_attributes :: "-ignore-unknown-attributes"
flag_keep_temp_files :: "-keep-temp-files"
flag_max_error_count :: "-max-error-count:"
flag_micro_architecture_native :: "-microarch:native"
flag_no_bounds_check :: "-no-bounds-check"
flag_no_crt :: "-no-crt"
flag_no_entrypoint :: "-no-entry-point"
flag_no_thread_local :: "-no-thread-local"
flag_no_thread_checker :: "-no-threaded-checker"
flag_output_path :: "-out="
flag_optimization_level :: "-opt:"
flag_optimize_none :: "-o:none"
flag_optimize_minimal :: "-o:minimal"
flag_optimize_size :: "-o:size"
flag_optimize_speed :: "-o:speed"
falg_optimize_aggressive :: "-o:aggressive"
flag_pdb_name :: "-pdb-name:"
flag_sanitize_address :: "-sanitize:address"
flag_sanitize_memory :: "-sanitize:memory"
flag_sanitize_thread :: "-sanitize:thread"
flag_subsystem :: "-subsystem:"
flag_show_debug_messages :: "-show-debug-messages"
flag_show_timings :: "-show-timings"
flag_show_more_timings :: "-show-more-timings"
flag_show_system_calls :: "-show-system-calls"
flag_target :: "-target:"
flag_thread_count :: "-thread-count:"
flag_use_lld :: "-linker:lld"
flag_use_rad_link :: "-linker:radlink"
flag_use_separate_modules :: "-use-separate-modules"
flag_vet_all :: "-vet"
flag_vet_unused_entities :: "-vet-unused"
flag_vet_semicolon :: "-vet-semicolon"
flag_vet_shadow_vars :: "-vet-shadowing"
flag_vet_using_stmt :: "-vet-using-stmt"
flag_microarch_zen5 :: "--microarch:znver5"
flag_msvc_link_disable_dynamic_base :: "/DYNAMICBASE:NO"
flag_msvc_link_base_address :: "/BASE:"
flag_msvc_link_fixed_base_address :: "/FIXED"
flag_msvc_link_stack_size :: "/STACK"
flag_msvc_link_debug :: "/DEBUG"
msvc_link_default_base_address :: 0x180000000
//endregion Script Grime
build :: proc(working_dir : string, args : []string) -> (stdout : string, stderr : string) {
fmt.println("Building:", args)
res, errs : []byte; _, res, errs, _ = os.process_exec({ working_dir = working_dir, command = args}, context.allocator)
return transmute(string)res, transmute(string)errs;
}
main :: proc() {
varena : vmem.Arena; _ = vmem.arena_init_growing(& varena, mem.Megabyte * 64 ); context.allocator = vmem.arena_allocator(& varena)
exe_odin :: "odin.exe"
path_root := get_working_dir()
path_build := join_path(path_root, "build")
path_code := join_path(path_root, "code")
file_source := join_path(path_code, "host.odin")
file_exe := join_path(path_build, "host.exe")
res, errs := build(path_build, {
exe_odin,
command_build,
file_source,
flag_file,
join_str(flag_output_path, file_exe),
flag_optimize_none,
flag_default_allocator_nil,
flag_debug,
flag_microarch_zen5,
flag_no_thread_checker,
flag_show_timings,
flag_use_rad_link,
join_str(flag_subsystem, "console"),
})
fmt.println(res)
fmt.println(errs)
}

41
scripts/build_duff.ps1 Normal file
View File

@@ -0,0 +1,41 @@
$path_root = split-path -Path $PSScriptRoot -Parent
$path_build = join-path $path_root 'build'
$path_code = join-path $path_root 'code'
$path_source = join-path $PSScriptRoot 'build.odin'
$exe = join-path $path_build 'build_win32.exe'
if ((test-path $path_build) -eq $false) {
new-item -itemtype directory -path $path_build
}
$odin = 'odin.exe'
$command_build = 'build'
$flag_debug = '-debug'
$flag_file = '-file'
$flag_dyn_map_calls = '-dynamic-map-calls'
$flag_no_bounds_check = '-no-bounds-check'
$flag_no_threaded_checker = '-no-threaded-checker'
$flag_no_type_assert = '-no-type-assert'
$flag_optimize_none = '-o:none'
$flag_output_path = '-out='
$flag_default_allocator_nil = '-default-to-nil-allocator'
push-location $path_root
$build_args = @()
$build_args += $command_build
$build_args += $path_source
$build_args += $flag_file
# $build_args += $flag_debug
$build_args += $flag_optimize_none
$build_args += $flag_no_bounds_check
$build_args += $flag_no_threaded_checker
$build_args += $flag_no_type_assert
$build_args += $flag_dyn_map_calls
$build_args += $flag_default_allocator_nil
$build_args += $flag_output_path + $exe
# & $odin $build_args
& $exe
pop-location

View File

@@ -0,0 +1,120 @@
$ps1_devshell = join-path $PSScriptRoot 'helpers/devshell.ps1'
. $ps1_devshell -arch amd64
$path_root = split-path -Path $PSScriptRoot -Parent
$path_build = join-path $path_root 'build'
$path_code = join-path $path_root 'code'
$path_asm = join-path $path_code 'asm'
$path_toolchain = join-path $path_root 'toolchain'
$path_rad = join-path $path_toolchain 'rad'
if ((test-path $path_build) -eq $false) {
new-item -itemtype directory -path $path_build
}
$unit_name = 'hello_arenas'
$src = join-path $path_asm "$unit_name.asm"
$unit = join-path $path_build "$unit_name.unit.asm"
$listing = join-path $path_build "$unit_name.asm.list"
$link_obj = join-path $path_build "$unit_name.o"
$map = join-path $path_build "$unit_name.map"
$pdb = join-path $path_build "$unit_name.pdb"
$rdi = join-path $path_build "$unit_name.rdi"
$rdi_listing = join-path $path_build "$unit_name.rdi.list"
$exe = join-path $path_build "$unit_name.exe"
$nasm = 'nasm'
$link = 'link.exe'
$radbin = join-path $path_rad 'radbin.exe'
$radlink = join-path $path_rad 'radlink.exe'
push-location $path_root
$f_assemble_only = '-a'
$f_bin_fmt_coff = '-f coff'
$f_bin_fmt_win64 = '-f win64'
$f_debug = '-g'
$f_debug_fmt_win64 = '-g cv8'
$f_dmacro = '-Dmacro='
$f_Ipath = '-Ipath '
$f_listing = '-l'
$f_listing_plus = '-L+'
$f_preprocess_only = '-E'
$f_optimize_none = '-O0'
$f_optimize_min = '-O1'
$f_optimize_multi = '-Ox'
$f_optimize_multi_disp = '-Ov'
$f_outfile = '-o '
$f_warnings_as_errors = '-Werror'
# $nargs = @(
# $src,
# $f_preprocess_only,
# $f_optimize_none,
# ($f_outfile + $unit)
# )
# write-host 'Preprocessing'
# $nargs | ForEach-Object { Write-Host $_ }
# & $nasm $nargs
$nargs = @(
$src,
$f_optimize_none,
$f_bin_fmt_win64,
$f_debug_fmt_win64,
$f_listing_plus,
($f_listing + $listing),
($f_outfile + $link_obj)
)
write-host 'Assembling'
$nargs | ForEach-Object { Write-Host "`t$_" }
& $nasm $nargs
$lib_kernel32 = 'kernel32.lib'
$lib_msvcrt = 'msvcrt.lib'
$link_nologo = '/NOLOGO'
$link_debug = '/DEBUG:'
$link_entrypoint = '/ENTRY:'
$link_mapfile = '/MAP:'
$link_no_incremental = '/INCREMENTAL:NO'
$link_large_address_aware = '/LARGEADDRESSAWARE:NO'
$link_listing = '/LIST'
$link_outfile = '/OUT:'
$link_win_machine_64 = '/MACHINE:X64'
$link_win_pdb = '/PDB:'
$link_win_subsystem_console = '/SUBSYSTEM:CONSOLE'
$link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS'
$rad_debug = '/RAD_DEBUG'
$rad_debug_name = '/RAD_DEBUG_NAME:'
$rad_large_pages = '/RAD_LARGE_PAGES:'
$nargs = @(
# $rad_debug,
$link_nologo,
($link_debug + 'FULL'),
($link_mapfile + $map),
($link_win_pdb + $pdb),
# $link_listing,
$link_no_incremental,
$link_large_address_aware,
$link_win_machine_64,
$link_win_subsystem_console,
$lib_kernel32,
# $lib_msvcrt,
($link_entrypoint + 'main'),
($link_outfile + $exe),
$link_obj
)
write-host 'Linking'
$nargs | ForEach-Object { Write-Host "`t$_" }
& $link $nargs
pop-location
$rbin_out = '--out:'
$rbin_dump = '--dump'
write-host 'Dumping RDI'
$nargs = @($pdb, ($rbin_out + $rdi))
& $radbin $nargs
$nargs = @($rbin_dump, $rdi)
$dump = & $radbin $nargs
$dump > $rdi_listing

View File

@@ -0,0 +1,120 @@
$ps1_devshell = join-path $PSScriptRoot 'helpers/devshell.ps1'
. $ps1_devshell -arch amd64
$path_root = split-path -Path $PSScriptRoot -Parent
$path_build = join-path $path_root 'build'
$path_code = join-path $path_root 'code'
$path_asm = join-path $path_code 'asm'
$path_toolchain = join-path $path_root 'toolchain'
$path_rad = join-path $path_toolchain 'rad'
if ((test-path $path_build) -eq $false) {
new-item -itemtype directory -path $path_build
}
$unit_name = 'hello_files'
$src = join-path $path_asm "$unit_name.asm"
$unit = join-path $path_build "$unit_name.unit.asm"
$listing = join-path $path_build "$unit_name.asm.list"
$link_obj = join-path $path_build "$unit_name.o"
$map = join-path $path_build "$unit_name.map"
$pdb = join-path $path_build "$unit_name.pdb"
$rdi = join-path $path_build "$unit_name.rdi"
$rdi_listing = join-path $path_build "$unit_name.rdi.list"
$exe = join-path $path_build "$unit_name.exe"
$nasm = 'nasm'
$link = 'link.exe'
$radbin = join-path $path_rad 'radbin.exe'
$radlink = join-path $path_rad 'radlink.exe'
push-location $path_root
$f_assemble_only = '-a'
$f_bin_fmt_coff = '-f coff'
$f_bin_fmt_win64 = '-f win64'
$f_debug = '-g'
$f_debug_fmt_win64 = '-g cv8'
$f_dmacro = '-Dmacro='
$f_Ipath = '-Ipath '
$f_listing = '-l'
$f_listing_plus = '-L+'
$f_preprocess_only = '-E'
$f_optimize_none = '-O0'
$f_optimize_min = '-O1'
$f_optimize_multi = '-Ox'
$f_optimize_multi_disp = '-Ov'
$f_outfile = '-o '
$f_warnings_as_errors = '-Werror'
# $nargs = @(
# $src,
# $f_preprocess_only,
# $f_optimize_none,
# ($f_outfile + $unit)
# )
# write-host 'Preprocessing'
# $nargs | ForEach-Object { Write-Host $_ }
# & $nasm $nargs
$nargs = @(
$src,
$f_optimize_none,
$f_bin_fmt_win64,
$f_debug_fmt_win64,
$f_listing_plus,
($f_listing + $listing),
($f_outfile + $link_obj)
)
write-host 'Assembling'
$nargs | ForEach-Object { Write-Host "`t$_" }
& $nasm $nargs
$lib_kernel32 = 'kernel32.lib'
$lib_msvcrt = 'msvcrt.lib'
$link_nologo = '/NOLOGO'
$link_debug = '/DEBUG:'
$link_entrypoint = '/ENTRY:'
$link_mapfile = '/MAP:'
$link_no_incremental = '/INCREMENTAL:NO'
$link_large_address_aware = '/LARGEADDRESSAWARE:NO'
$link_listing = '/LIST'
$link_outfile = '/OUT:'
$link_win_machine_64 = '/MACHINE:X64'
$link_win_pdb = '/PDB:'
$link_win_subsystem_console = '/SUBSYSTEM:CONSOLE'
$link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS'
$rad_debug = '/RAD_DEBUG'
$rad_debug_name = '/RAD_DEBUG_NAME:'
$rad_large_pages = '/RAD_LARGE_PAGES:'
$nargs = @(
# $rad_debug,
$link_nologo,
($link_debug + 'FULL'),
($link_mapfile + $map),
($link_win_pdb + $pdb),
# $link_listing,
$link_no_incremental,
$link_large_address_aware,
$link_win_machine_64,
$link_win_subsystem_console,
$lib_kernel32,
# $lib_msvcrt,
($link_entrypoint + 'main'),
($link_outfile + $exe),
$link_obj
)
write-host 'Linking'
$nargs | ForEach-Object { Write-Host "`t$_" }
& $link $nargs
pop-location
$rbin_out = '--out:'
$rbin_dump = '--dump'
write-host 'Dumping RDI'
$nargs = @($pdb, ($rbin_out + $rdi))
& $radbin $nargs
$nargs = @($rbin_dump, $rdi)
$dump = & $radbin $nargs
$dump > $rdi_listing

View File

@@ -0,0 +1,103 @@
$ps1_devshell = join-path $PSScriptRoot 'helpers/devshell.ps1'
. $ps1_devshell -arch amd64
$path_root = split-path -Path $PSScriptRoot -Parent
$path_build = join-path $path_root 'build'
$path_code = join-path $path_root 'code'
$path_asm = join-path $path_code 'asm'
$path_toolchain = join-path $path_root 'toolchain'
$path_rad = join-path $path_toolchain 'rad'
if ((test-path $path_build) -eq $false) {
new-item -itemtype directory -path $path_build
}
$unit_name = 'hello_nasm'
$unit = join-path $path_asm "$unit_name.asm"
$listing = join-path $path_build "$unit_name.list"
$link_obj = join-path $path_build "$unit_name.o"
$map = join-path $path_build "$unit_name.map"
$pdb = join-path $path_build "$unit_name.pdb"
$rdi = join-path $path_build "$unit_name.rdi"
$exe = join-path $path_build "$unit_name.exe"
$nasm = 'nasm'
$link = 'link.exe'
$radbin = join-path $path_rad 'radbin.exe'
$radlink = join-path $path_rad 'radlink.exe'
push-location $path_root
$f_assemble_only = '-a'
$f_bin_fmt_coff = '-f coff'
$f_bin_fmt_win64 = '-f win64'
$f_debug = '-g'
$f_debug_fmt_win64 = '-g cv8'
$f_dmacro = '-Dmacro='
$f_Ipath = '-Ipath '
$f_listing = '-l'
$f_listing_plus = '-L+'
$f_preprocess_only = '-E'
$f_optimize_none = '-O0'
$f_optimize_min = '-O1'
$f_optimize_multi = '-Ox'
$f_optimize_multi_disp = '-Ov'
$f_outfile = '-o '
$f_warnings_as_errors = '-Werror'
write-host 'Preprocessing'
$args = @(
$unit,
$f_preprocess_only,
$f_optimize_none,
($f_listing + $listing),
$f_listing_plus
)
& $nasm $args
write-host 'Assembling'
$args = @(
$unit,
$f_preprocess_only,
$f_optimize_none,
$f_bin_fmt_win64,
$f_debug_fmt_win64,
($f_outfile + $link_obj)
)
& $nasm $args
$lib_kernel32 = 'kernel32.lib'
$lib_msvcrt = 'msvcrt.lib'
$link_nologo = '/NOLOGO'
$link_debug = '/DEBUG:'
$link_entrypoint = '/ENTRY:'
$link_mapfile = '/MAP:'
$link_library = '/'
$link_outfile = '/OUT:'
$link_win_machine_64 = '/MACHINE:X64'
$link_win_subsystem_console = '/SUBSYSTEM:CONSOLE'
$link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS'
$rad_debug = '/RAD_DEBUG'
$rad_debug_name = '/RAD_DEBUG_NAME:'
$rad_large_pages = '/RAD_LARGE_PAGES:'
$args = @(
# $rad_debug,
$link_nologo,
($link_debug + 'FULL'),
($link_mapfile + $map)
$link_win_machine_64,
$link_win_subsystem_console,
$lib_kernel32,
# $lib_msvcrt,
($link_entrypoint + 'main'),
($link_outfile + $exe),
$link_obj
)
write-host 'Linking'
& $link $args
pop-location
$rbin_out = '--out:'
$rbin_dump = '--dump '
# $radbin

View File

@@ -0,0 +1,76 @@
$ps1_devshell = join-path $PSScriptRoot 'helpers/devshell.ps1'
. $ps1_devshell -arch amd64
$path_root = split-path -Path $PSScriptRoot -Parent
$path_build = join-path $path_root 'build'
$path_code = join-path $path_root 'code'
$path_asm = join-path $path_code 'asm'
$path_toolchain = join-path $path_root 'toolchain'
$path_radlink = join-path $path_toolchain 'radlink'
if ((test-path $path_build) -eq $false) {
new-item -itemtype directory -path $path_build
}
$hello_nasm = join-path $path_asm 'hello_slices.asm'
$listing = join-path $path_build 'hello_slices.list'
$link_obj = join-path $path_build 'hello_slices.o'
$exe = join-path $path_build 'hello_slices.exe'
$nasm = 'nasm'
$radlink = join-path $path_radlink 'radlink.exe'
push-location $path_root
$f_assemble_only = '-a'
$f_bin_fmt_coff = '-f coff'
$f_bin_fmt_win64 = '-f win64'
$f_debug = '-g'
$f_debug_fmt_win64 = '-g cv8'
$f_dmacro = '-Dmacro='
$f_Ipath = '-Ipath '
$f_listing = '-l'
$f_preprocess_only = '-E'
$f_optimize_none = '-O0'
$f_optimize_min = '-O1'
$f_optimize_multi = '-Ox'
$f_optimize_multi_disp = '-Ov'
$f_outfile = '-o '
$f_warnings_as_errors = '-Werror'
$args = @(
$hello_nasm,
$f_optimize_none,
# $f_preprocess_only,
$f_bin_fmt_win64,
$f_debug_fmt_win64,
($f_listing + $listing),
($f_outfile + $link_obj)
)
& $nasm $args
$lib_kernel32 = 'kernel32.lib'
$lib_msvcrt = 'msvcrt.lib'
$link = 'link.exe'
$link_debug = '/DEBUG:'
$link_entrypoint = '/ENTRY:'
$link_library = '/'
$link_outfile = '/OUT:'
$link_win_machine_64 = '/MACHINE:X64'
$link_win_subsystem_console = '/SUBSYSTEM:CONSOLE'
$link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS'
$rad_debug = '/RAD_DEBUG'
$rad_debug_name = '/RAD_DEBUG_NAME:'
$rad_large_pages = '/RAD_LARGE_PAGES:'
$args = @(
$rad_debug,
# ($link_debug + 'FULL'),
$link_win_machine_64,
$link_win_subsystem_console,
$lib_kernel32,
# $lib_msvcrt,
($link_entrypoint + 'main'),
($link_outfile + $exe),
$link_obj
)
& $radlink $args
pop-location

View File

@@ -0,0 +1,28 @@
if ($env:VCINSTALLDIR) {
return
}
$ErrorActionPreference = "Stop"
# Use vswhere to find the latest Visual Studio installation
$vswhere_out = & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath
if ($null -eq $vswhere_out) {
Write-Host "ERROR: Visual Studio installation not found"
exit 1
}
# Find Launch-VsDevShell.ps1 in the Visual Studio installation
$vs_path = $vswhere_out
$vs_devshell = Join-Path $vs_path "\Common7\Tools\Launch-VsDevShell.ps1"
if ( -not (Test-Path $vs_devshell) ) {
Write-Host "ERROR: Launch-VsDevShell.ps1 not found in Visual Studio installation"
Write-Host Tested path: $vs_devshell
exit 1
}
# Launch the Visual Studio Developer Shell
Push-Location
write-host @args
& $vs_devshell @args
Pop-Location

View File

@@ -0,0 +1,82 @@
# This is meant to be used with build.ps1, and is not a standalone script.
function check-FileForChanges
{
param(
[Parameter(Mandatory=$true)]
[string]$path_file
)
if (-not (Test-Path $path_file -PathType Leaf)) {
Write-Error "The provided path is not a valid file: $path_file"
return $false
}
$file_name = Split-Path $path_file -Leaf
$path_csv = Join-Path $path_build ($file_name + "_file_hash.csv")
$csv_file_hash = $null
if (Test-Path $path_csv) {
$csv_file_hash = Import-Csv $path_csv | Select-Object -ExpandProperty value
}
$current_hash_info = Get-FileHash -Path $path_file -Algorithm MD5
$current_file_hash = $current_hash_info.Hash
# Save the current hash to the CSV
[PSCustomObject]@{
name = $path_file
value = $current_file_hash
} | Export-Csv $path_csv -NoTypeInformation
if ($csv_file_hash -and $csv_file_hash -eq $current_file_hash) {
return $false
} else {
return $true
}
}
# Check to see if the module has changed files since the last build
function check-ModuleForChanges
{
param( [string]$path_module, [array]$excludes )
$module_name = split-path $path_module -leaf
$path_csv = Join-Path $path_build ("module_" + $module_name + "_hashes.csv")
$csv_file_hashes = $null
if ( test-path $path_csv ) {
$csv_file_hashes = @{}
import-csv $path_csv | foreach-object {
$csv_file_hashes[ $_.name ] = $_.value
}
}
$file_hashes = @{}
get-childitem -path $path_module -file -Exclude $excludes -Recurse | foreach-object {
$id = $_.fullname
$hash_info = get-filehash -path $id -Algorithm MD5
$file_hashes[ $id ] = $hash_info.Hash
}
$file_hashes.GetEnumerator() | foreach-object { [PSCustomObject]$_ } |
export-csv $path_csv -NoTypeInformation
if ( -not $csv_file_hashes ) { return $true }
if ( $csv_file_hashes.Count -ne $file_hashes.Count ) { return $true }
foreach ( $key in $csv_file_hashes.Keys ) {
if ( $csv_file_hashes[ $key ] -ne $file_hashes[ $key ] ) {
return $true
}
}
return $false
}
function mark-ModuleDirty {
param( [string]$path_module )
$module_name = split-path $path_module -leaf
$path_csv = Join-Path $path_build ("module_" + $module_name + "_hashes.csv")
remove-item -Force -Path $path_csv
}

20
scripts/helpers/ini.ps1 Normal file
View File

@@ -0,0 +1,20 @@
# This is meant to be used with build.ps1, and is not a standalone script.
function Get-IniContent { param([ string]$filePath )
$ini = @{}
$currentSection = $null
switch -regex -file $filePath
{
"^\[(.+)\]$" {
$currentSection = $matches[1].Trim()
$ini[$currentSection] = @{}
}
"^(.+?)\s*=\s*(.*)" {
$key, $value = $matches[1].Trim(), $matches[2].Trim()
if ($null -ne $currentSection) {
$ini[$currentSection][$key] = $value
}
}
}
return $ini
}

67
scripts/helpers/misc.ps1 Normal file
View File

@@ -0,0 +1,67 @@
function clone-gitrepo { param( [string] $path, [string] $url )
if (test-path $path) {
# git -C $path pull
}
else {
Write-Host "Cloning $url ..."
git clone $url $path
}
}
function Update-GitRepo
{
param( [string] $path, [string] $url, [string] $build_command )
if ( $build_command -eq $null ) {
write-host "Attempted to call Update-GitRepo without build_command specified"
return
}
$repo_name = $url.Split('/')[-1].Replace('.git', '')
$last_built_commit = join-path $path_build "last_built_commit_$repo_name.txt"
if ( -not(test-path -Path $path))
{
write-host "Cloining repo from $url to $path"
git clone $url $path
write-host "Building $url"
push-location $path
& "$build_command"
pop-location
git -C $path rev-parse HEAD | out-file $last_built_commit
$script:binaries_dirty = $true
write-host
return
}
git -C $path fetch
$latest_commit_hash = git -C $path rev-parse '@{u}'
$last_built_hash = if (Test-Path $last_built_commit) { Get-Content $last_built_commit } else { "" }
if ( $latest_commit_hash -eq $last_built_hash ) {
write-host
return
}
write-host "Build out of date for: $path, updating"
write-host 'Pulling...'
git -C $path pull
write-host "Building $url"
push-location $path
& $build_command
pop-location
$latest_commit_hash | out-file $last_built_commit
$script:binaries_dirty = $true
write-host
}
function verify-path { param( $path )
if (test-path $path) {return}
new-item -ItemType Directory -Path $path
return
}

18
scripts/update_deps.ps1 Normal file
View File

@@ -0,0 +1,18 @@
$misc = join-path $PSScriptRoot 'helpers/misc.ps1'
. $misc
$path_root = git rev-parse --show-toplevel
$path_build = join-path $path_root 'build'
$path_scripts = join-path $path_root 'scripts'
$path_source = join-path $path_root 'source'
$path_toolchain = join-path $path_root 'toolchain'
# Note: No longer using nasm
if ($false) {
$url_yasm = 'https://github.com/yasm/yasm.git'
$path_yasm = join-path $path_toolchain 'yasm'
$path_libyasm = join-path $path_yasm 'libyasm'
clone-gitrepo $path_yasm $url_yasm
}

29125
toolchain/gencpp/gencpp_c11.h Normal file

File diff suppressed because it is too large Load Diff

BIN
toolchain/rad/radbin.exe Normal file

Binary file not shown.

BIN
toolchain/rad/radlink.exe Normal file

Binary file not shown.

BIN
toolchain/rad/radlink.pdb Normal file

Binary file not shown.

13
toolchain/readme.md Normal file
View File

@@ -0,0 +1,13 @@
# Toolchain
## Gencpp C11
Repo: https://github.com/Ed94/gencpp.git
Commit: 685bba36d5ed64b48908c950fbbb7f1dbb297120
## NASM
Version: 2.16.03
Installed Via: scoop
## RAD Linker