diff --git a/code/asm/hello_arenas.asm b/code/asm/hello_arenas.asm new file mode 100644 index 0000000..8b18851 --- /dev/null +++ b/code/asm/hello_arenas.asm @@ -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 +%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: +%macro def_Slice 1 + struc Slice_ %+ %1 + .ptr: resq 1 + .len: resq 1 + endstruc +%endmacro + +def_Slice Byte + +; Usage: stack_slice %1: , %2 , %3 +%macro stack_slice 2 + call_frame_alloc %1 %+ _size +%endmacro + +; Usage: slice_assert %1: Slice_ { .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 (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 +; Meant to only be used with call-table structs: _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: +%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: , %2: +%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 diff --git a/scripts/build_hello_arenas.ps1 b/scripts/build_hello_arenas.ps1 new file mode 100644 index 0000000..1f1739d --- /dev/null +++ b/scripts/build_hello_arenas.ps1 @@ -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