Compare commits
22 Commits
2a5b3461e3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| d01cf53ff8 | |||
| 5817060a8b | |||
| bc2da4df4c | |||
| 3e9d6980df | |||
| b91869a49e | |||
| 662f5afd9b | |||
| 1f9a9bdc21 | |||
| f8ac671e3a | |||
| 7dfd1b1927 | |||
| a4658b1951 | |||
| 9db292f616 | |||
| 56cc90c57b | |||
| fb286c7f11 | |||
| 16b8483312 | |||
| d888968ce0 | |||
| dd83866f2b | |||
| 35fe0b899a | |||
| 39ec4213d6 | |||
| bd3dc5f2bc | |||
| 00ad047d9a | |||
| 6f0715b0ca | |||
| 20be5d0892 |
+3
-1
@@ -1 +1,3 @@
|
||||
*/build
|
||||
Build
|
||||
part2/*.f64
|
||||
part2/*.json
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
@echo off
|
||||
|
||||
REM Setup ==========================================================================================
|
||||
set script_dir_backslash=%~dp0
|
||||
set script_dir=%script_dir_backslash:~0,-1%
|
||||
set build_dir=%script_dir%\Build
|
||||
|
||||
set part1_dir=%script_dir%\part1
|
||||
set part1_build_dir=%build_dir%\part1
|
||||
if not exist %part1_build_dir% mkdir %part1_build_dir%
|
||||
|
||||
set part2_dir=%script_dir%\part2
|
||||
set part2_build_dir=%build_dir%\part2
|
||||
if not exist %part2_build_dir% mkdir %part2_build_dir%
|
||||
goto :part2
|
||||
|
||||
REM Part 1 =========================================================================================
|
||||
REM Build ==========================================================================================
|
||||
pushd %part1_build_dir%
|
||||
cl %part1_dir%\sim8086.c /W4 /WX /Z7 /nologo || exit /b 1
|
||||
popd
|
||||
|
||||
REM Tests ==========================================================================================
|
||||
set listing_0037=listing_0037_single_register_mov
|
||||
set part1_build_dir_listing_0037=%part1_build_dir%\%listing_0037%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0037% %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0037% > %part1_build_dir_listing_0037%_disassembled.asm
|
||||
nasm %part1_build_dir_listing_0037%_disassembled.asm
|
||||
fc /B %part1_build_dir_listing_0037% %part1_build_dir_listing_0037%_disassembled || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0038=listing_0038_many_register_mov
|
||||
set part1_build_dir_listing_0038=%part1_build_dir%\%listing_0038%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0038% %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0038% > %part1_build_dir_listing_0038%_disassembled.asm
|
||||
nasm %part1_build_dir_listing_0038%_disassembled.asm
|
||||
fc /B %part1_build_dir_listing_0038% %part1_build_dir_listing_0038%_disassembled || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0039=listing_0039_more_movs
|
||||
set part1_build_dir_listing_0039=%part1_build_dir%\%listing_0039%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0039% %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0039% > %part1_build_dir_listing_0039%_disassembled.asm
|
||||
nasm %part1_build_dir_listing_0039%_disassembled.asm
|
||||
fc /B %part1_build_dir_listing_0039% %part1_build_dir_listing_0039%_disassembled || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0040=listing_0040_challenge_movs
|
||||
set part1_build_dir_listing_0040=%part1_build_dir%\%listing_0040%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0040% %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0040% > %part1_build_dir_listing_0040%_disassembled.asm
|
||||
nasm %part1_build_dir_listing_0040%_disassembled.asm
|
||||
fc /B %part1_build_dir_listing_0040% %part1_build_dir_listing_0040%_disassembled || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0041=listing_0041_add_sub_cmp_jnz
|
||||
set part1_build_dir_listing_0041=%part1_build_dir%\%listing_0041%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0041% %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0041% > %part1_build_dir_listing_0041%_disassembled.asm
|
||||
nasm %part1_build_dir_listing_0041%_disassembled.asm
|
||||
fc /B %part1_build_dir_listing_0041% %part1_build_dir_listing_0041%_disassembled || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0042=listing_0042_completionist_decode
|
||||
set part1_build_dir_listing_0042=%part1_build_dir%\%listing_0042%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0042% %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0042% > %part1_build_dir_listing_0042%_disassembled.asm
|
||||
nasm %part1_build_dir_listing_0042%_disassembled.asm
|
||||
fc /B %part1_build_dir_listing_0042% %part1_build_dir_listing_0042%_disassembled || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0043=listing_0043_immediate_movs
|
||||
set part1_build_dir_listing_0043=%part1_build_dir%\%listing_0043%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0043% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0043%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0043% > %part1_build_dir_listing_0043%_disassembled.asm
|
||||
%part1_build_dir%\sim8086.exe --exec %part1_build_dir_listing_0043% > %part1_build_dir_listing_0043%_disassembled.txt
|
||||
|
||||
nasm %part1_build_dir_listing_0043%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0043% %part1_build_dir_listing_0043%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0043%.txt %part1_build_dir_listing_0043%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0044=listing_0044_register_movs
|
||||
set part1_build_dir_listing_0044=%part1_build_dir%\%listing_0044%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0044% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0044%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe --exec %part1_build_dir_listing_0044% > %part1_build_dir_listing_0044%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0044% > %part1_build_dir_listing_0044%_disassembled.asm
|
||||
|
||||
nasm %part1_build_dir_listing_0044%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0044% %part1_build_dir_listing_0044%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0044%.txt %part1_build_dir_listing_0044%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0045=listing_0045_challenge_register_movs
|
||||
set part1_build_dir_listing_0045=%part1_build_dir%\%listing_0045%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0045% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0045%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe --exec %part1_build_dir_listing_0045% > %part1_build_dir_listing_0045%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0045% > %part1_build_dir_listing_0045%_disassembled.asm
|
||||
|
||||
nasm %part1_build_dir_listing_0045%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0045% %part1_build_dir_listing_0045%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0045%.txt %part1_build_dir_listing_0045%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0046=listing_0046_add_sub_cmp
|
||||
set part1_build_dir_listing_0046=%part1_build_dir%\%listing_0046%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0046% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0046%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe --exec %part1_build_dir_listing_0046% > %part1_build_dir_listing_0046%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0046% > %part1_build_dir_listing_0046%_disassembled.asm
|
||||
|
||||
nasm %part1_build_dir_listing_0046%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0046% %part1_build_dir_listing_0046%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0046%.txt %part1_build_dir_listing_0046%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0047=listing_0047_challenge_flags
|
||||
set part1_build_dir_listing_0047=%part1_build_dir%\%listing_0047%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0047% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0047%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe --exec %part1_build_dir_listing_0047% > %part1_build_dir_listing_0047%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0047% > %part1_build_dir_listing_0047%_disassembled.asm
|
||||
|
||||
nasm %part1_build_dir_listing_0047%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0047% %part1_build_dir_listing_0047%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0047%.txt %part1_build_dir_listing_0047%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0048=listing_0048_ip_register
|
||||
set part1_build_dir_listing_0048=%part1_build_dir%\%listing_0048%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0048% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0048%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe --exec --log-instruction-ptr %part1_build_dir_listing_0048% > %part1_build_dir_listing_0048%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0048% > %part1_build_dir_listing_0048%_disassembled.asm
|
||||
|
||||
nasm %part1_build_dir_listing_0048%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0048% %part1_build_dir_listing_0048%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0048%.txt %part1_build_dir_listing_0048%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0049=listing_0049_conditional_jumps
|
||||
set part1_build_dir_listing_0049=%part1_build_dir%\%listing_0049%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0049% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0049%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe --exec --log-instruction-ptr %part1_build_dir_listing_0049% > %part1_build_dir_listing_0049%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0049% > %part1_build_dir_listing_0049%_disassembled.asm
|
||||
|
||||
nasm %part1_build_dir_listing_0049%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0049% %part1_build_dir_listing_0049%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0049%.txt %part1_build_dir_listing_0049%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0050=listing_0050_challenge_jumps
|
||||
set part1_build_dir_listing_0050=%part1_build_dir%\%listing_0050%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0050% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0050%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe --exec --log-instruction-ptr %part1_build_dir_listing_0050% > %part1_build_dir_listing_0050%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0050% > %part1_build_dir_listing_0050%_disassembled.asm
|
||||
|
||||
nasm %part1_build_dir_listing_0050%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0050% %part1_build_dir_listing_0050%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0050%.txt %part1_build_dir_listing_0050%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0051=listing_0051_memory_mov
|
||||
set part1_build_dir_listing_0051=%part1_build_dir%\%listing_0051%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0051% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0051%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe --exec --log-instruction-ptr %part1_build_dir_listing_0051% > %part1_build_dir_listing_0051%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0051% > %part1_build_dir_listing_0051%_disassembled.asm
|
||||
|
||||
nasm %part1_build_dir_listing_0051%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0051% %part1_build_dir_listing_0051%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0051%.txt %part1_build_dir_listing_0051%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0052=listing_0052_memory_add_loop
|
||||
set part1_build_dir_listing_0052=%part1_build_dir%\%listing_0052%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0052% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0052%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe --exec --log-instruction-ptr %part1_build_dir_listing_0052% > %part1_build_dir_listing_0052%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0052% > %part1_build_dir_listing_0052%_disassembled.asm
|
||||
|
||||
nasm %part1_build_dir_listing_0052%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0052% %part1_build_dir_listing_0052%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0052%.txt %part1_build_dir_listing_0052%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0053=listing_0053_add_loop_challenge
|
||||
set part1_build_dir_listing_0053=%part1_build_dir%\%listing_0053%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0053% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0053%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
%part1_build_dir%\sim8086.exe --exec --log-instruction-ptr %part1_build_dir_listing_0053% > %part1_build_dir_listing_0053%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0053% > %part1_build_dir_listing_0053%_disassembled.asm
|
||||
|
||||
nasm %part1_build_dir_listing_0053%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0053% %part1_build_dir_listing_0053%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0053%.txt %part1_build_dir_listing_0053%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0054=listing_0054_draw_rectangle
|
||||
set part1_build_dir_listing_0054=%part1_build_dir%\%listing_0054%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0054% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0054%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
pushd %part1_build_dir%
|
||||
%part1_build_dir%\sim8086.exe --exec --log-instruction-ptr --dump %part1_build_dir_listing_0054% > %part1_build_dir_listing_0054%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0054% > %part1_build_dir_listing_0054%_disassembled.asm
|
||||
popd
|
||||
|
||||
nasm %part1_build_dir_listing_0054%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0054% %part1_build_dir_listing_0054%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0054%.txt %part1_build_dir_listing_0054%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0055=listing_0055_challenge_rectangle
|
||||
set part1_build_dir_listing_0055=%part1_build_dir%\%listing_0055%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0055% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0055%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
pushd %part1_build_dir%
|
||||
%part1_build_dir%\sim8086.exe --exec --log-instruction-ptr --dump %part1_build_dir_listing_0055% > %part1_build_dir_listing_0055%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0055% > %part1_build_dir_listing_0055%_disassembled.asm
|
||||
popd
|
||||
|
||||
nasm %part1_build_dir_listing_0055%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0055% %part1_build_dir_listing_0055%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0055%.txt %part1_build_dir_listing_0055%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0056=listing_0056_estimating_cycles
|
||||
set part1_build_dir_listing_0056=%part1_build_dir%\%listing_0056%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0056% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0056%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
pushd %part1_build_dir%
|
||||
%part1_build_dir%\sim8086.exe --exec --log-instruction-ptr --log-cycle-counts 8086 %part1_build_dir_listing_0056% > %part1_build_dir_listing_0056%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0056% > %part1_build_dir_listing_0056%_disassembled.asm
|
||||
%part1_build_dir%\sim8086.exe --exec --log-instruction-ptr --log-cycle-counts 8088 %part1_build_dir_listing_0056% >> %part1_build_dir_listing_0056%_disassembled.txt
|
||||
popd
|
||||
|
||||
nasm %part1_build_dir_listing_0056%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0056% %part1_build_dir_listing_0056%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0056%.txt %part1_build_dir_listing_0056%_disassembled.txt || exit /b 1
|
||||
|
||||
REM ================================================================================================
|
||||
set listing_0057=listing_0057_challenge_cycles
|
||||
set part1_build_dir_listing_0057=%part1_build_dir%\%listing_0057%
|
||||
|
||||
copy /Y %part1_dir%\%listing_0057% %part1_build_dir% 1>NUL
|
||||
copy /Y %part1_dir%\%listing_0057%.txt %part1_build_dir% 1>NUL
|
||||
|
||||
pushd %part1_build_dir%
|
||||
%part1_build_dir%\sim8086.exe --exec --log-instruction-ptr --log-cycle-counts 8086 %part1_build_dir_listing_0057% > %part1_build_dir_listing_0057%_disassembled.txt
|
||||
%part1_build_dir%\sim8086.exe %part1_build_dir_listing_0057% > %part1_build_dir_listing_0057%_disassembled.asm
|
||||
%part1_build_dir%\sim8086.exe --exec --log-instruction-ptr --log-cycle-counts 8088 %part1_build_dir_listing_0057% >> %part1_build_dir_listing_0057%_disassembled.txt
|
||||
popd
|
||||
|
||||
nasm %part1_build_dir_listing_0057%_disassembled.asm
|
||||
|
||||
fc /B %part1_build_dir_listing_0057% %part1_build_dir_listing_0057%_disassembled || exit /b 1
|
||||
fc /N %part1_build_dir_listing_0057%.txt %part1_build_dir_listing_0057%_disassembled.txt || exit /b 1
|
||||
|
||||
REM Part 2 =========================================================================================
|
||||
REM Build ==========================================================================================
|
||||
:part2
|
||||
pushd %part2_build_dir%
|
||||
cl %part2_dir%\haversine_generator.c /W4 /WX /Z7 /nologo /Fe:haversine_generator_debug || exit /b 1
|
||||
cl %part2_dir%\haversine_generator.c /W4 /WX /Z7 /nologo /O2 /Fe:haversine_generator_release || exit /b 1
|
||||
|
||||
cl %part2_dir%\haversine_generator.c /DHAV_PROFILER /W4 /WX /Z7 /nologo /Fe:haversine_generator_profiled_debug || exit /b 1
|
||||
cl %part2_dir%\haversine_generator.c /DHAV_PROFILER /W4 /WX /Z7 /nologo /O2 /Fe:haversine_generator_profiled_release || exit /b 1
|
||||
|
||||
cl %part2_dir%\haversine.c /W4 /WX /Z7 /nologo /Fe:haversine_debug || exit /b 1
|
||||
cl %part2_dir%\haversine.c /W4 /WX /Z7 /nologo /O2 /Fe:haversine_release || exit /b 1
|
||||
|
||||
cl %part2_dir%\haversine.c /DHAV_PROFILER /W4 /WX /Z7 /nologo /Fe:haversine_profiled_debug || exit /b 1
|
||||
cl %part2_dir%\haversine.c /DHAV_PROFILER /W4 /WX /Z7 /nologo /O2 /Fe:haversine_profiled_release || exit /b 1
|
||||
|
||||
cl %part2_dir%\listing_0071_os_timer_main.cpp /W4 /WX /Z7 /O2 /nologo /Fe:listing_0071_os_timer_main_release || exit /b 1
|
||||
cl %part2_dir%\listing_0072_cpu_timer_main.cpp /W4 /WX /Z7 /O2 /nologo /Fe:listing_0072_cpu_timer_main_release || exit /b 1
|
||||
cl %part2_dir%\listing_0073_cpu_timer_guessfreq_main.cpp /W4 /WX /Z7 /O2 /nologo /Fe:listing_0073_cpu_timer_guessfreq_release || exit /b 1
|
||||
popd
|
||||
@@ -1,94 +0,0 @@
|
||||
@echo off
|
||||
|
||||
REM Setup
|
||||
REM ===========================================================================
|
||||
set script_dir_backslash=%~dp0
|
||||
set script_dir=%script_dir_backslash:~0,-1%
|
||||
set build_dir=%script_dir%\build
|
||||
if not exist %build_dir% mkdir %build_dir%
|
||||
|
||||
REM Build
|
||||
REM ===========================================================================
|
||||
pushd %build_dir%
|
||||
cl %script_dir%\sim8086.c /W4 /WX /Z7 /nologo || exit /b 1
|
||||
popd
|
||||
|
||||
REM Tests
|
||||
REM ===========================================================================
|
||||
set listing_0037=listing_0037_single_register_mov
|
||||
copy /Y %script_dir%\%listing_0037% %build_dir% 1>NUL
|
||||
set build_dir_listing_0037=%build_dir%\%listing_0037%
|
||||
%build_dir%\sim8086.exe %build_dir_listing_0037% > %build_dir_listing_0037%_disassembled.asm
|
||||
nasm %build_dir_listing_0037%_disassembled.asm
|
||||
fc /B %build_dir_listing_0037% %build_dir_listing_0037%_disassembled || exit /b 1
|
||||
|
||||
set listing_0038=listing_0038_many_register_mov
|
||||
copy /Y %script_dir%\%listing_0038% %build_dir% 1>NUL
|
||||
set build_dir_listing_0038=%build_dir%\%listing_0038%
|
||||
%build_dir%\sim8086.exe %build_dir_listing_0038% > %build_dir_listing_0038%_disassembled.asm
|
||||
nasm %build_dir_listing_0038%_disassembled.asm
|
||||
fc /B %build_dir_listing_0038% %build_dir_listing_0038%_disassembled || exit /b 1
|
||||
|
||||
set listing_0039=listing_0039_more_movs
|
||||
copy /Y %script_dir%\%listing_0039% %build_dir% 1>NUL
|
||||
set build_dir_listing_0039=%build_dir%\%listing_0039%
|
||||
%build_dir%\sim8086.exe %build_dir_listing_0039% > %build_dir_listing_0039%_disassembled.asm
|
||||
nasm %build_dir_listing_0039%_disassembled.asm
|
||||
fc /B %build_dir_listing_0039% %build_dir_listing_0039%_disassembled || exit /b 1
|
||||
|
||||
set listing_0040=listing_0040_challenge_movs
|
||||
copy /Y %script_dir%\%listing_0040% %build_dir% 1>NUL
|
||||
set build_dir_listing_0040=%build_dir%\%listing_0040%
|
||||
%build_dir%\sim8086.exe %build_dir_listing_0040% > %build_dir_listing_0040%_disassembled.asm
|
||||
nasm %build_dir_listing_0040%_disassembled.asm
|
||||
fc /B %build_dir_listing_0040% %build_dir_listing_0040%_disassembled || exit /b 1
|
||||
|
||||
set listing_0041=listing_0041_add_sub_cmp_jnz
|
||||
copy /Y %script_dir%\%listing_0041% %build_dir% 1>NUL
|
||||
set build_dir_listing_0041=%build_dir%\%listing_0041%
|
||||
%build_dir%\sim8086.exe %build_dir_listing_0041% > %build_dir_listing_0041%_disassembled.asm
|
||||
nasm %build_dir_listing_0041%_disassembled.asm
|
||||
fc /B %build_dir_listing_0041% %build_dir_listing_0041%_disassembled || exit /b 1
|
||||
|
||||
set listing_0042=listing_0042_completionist_decode
|
||||
copy /Y %script_dir%\%listing_0042% %build_dir% 1>NUL
|
||||
set build_dir_listing_0042=%build_dir%\%listing_0042%
|
||||
%build_dir%\sim8086.exe %build_dir_listing_0042% > %build_dir_listing_0042%_disassembled.asm
|
||||
nasm %build_dir_listing_0042%_disassembled.asm
|
||||
fc /B %build_dir_listing_0042% %build_dir_listing_0042%_disassembled || exit /b 1
|
||||
|
||||
set listing_0043=listing_0043_immediate_movs
|
||||
copy /Y %script_dir%\%listing_0043% %build_dir% 1>NUL
|
||||
copy /Y %script_dir%\%listing_0043%.txt %build_dir% 1>NUL
|
||||
set build_dir_listing_0043=%build_dir%\%listing_0043%
|
||||
%build_dir%\sim8086.exe --exec %build_dir_listing_0043% > %build_dir_listing_0043%_disassembled.txt
|
||||
fc /N %build_dir_listing_0043%.txt %build_dir_listing_0043%_disassembled.txt || exit /b 1
|
||||
|
||||
set listing_0044=listing_0044_register_movs
|
||||
copy /Y %script_dir%\%listing_0044% %build_dir% 1>NUL
|
||||
copy /Y %script_dir%\%listing_0044%.txt %build_dir% 1>NUL
|
||||
set build_dir_listing_0044=%build_dir%\%listing_0044%
|
||||
%build_dir%\sim8086.exe --exec %build_dir_listing_0044% > %build_dir_listing_0044%_disassembled.txt
|
||||
fc /N %build_dir_listing_0044%.txt %build_dir_listing_0044%_disassembled.txt || exit /b 1
|
||||
|
||||
set listing_0045=listing_0045_challenge_register_movs
|
||||
copy /Y %script_dir%\%listing_0045% %build_dir% 1>NUL
|
||||
copy /Y %script_dir%\%listing_0045%.txt %build_dir% 1>NUL
|
||||
set build_dir_listing_0045=%build_dir%\%listing_0045%
|
||||
%build_dir%\sim8086.exe --exec %build_dir_listing_0045% > %build_dir_listing_0045%_disassembled.txt
|
||||
fc /N %build_dir_listing_0045%.txt %build_dir_listing_0045%_disassembled.txt || exit /b 1
|
||||
set listing_0045=listing_0045_challenge_register_movs
|
||||
|
||||
set listing_0046=listing_0046_add_sub_cmp
|
||||
copy /Y %script_dir%\%listing_0046% %build_dir% 1>NUL
|
||||
copy /Y %script_dir%\%listing_0046%.txt %build_dir% 1>NUL
|
||||
set build_dir_listing_0046=%build_dir%\%listing_0046%
|
||||
%build_dir%\sim8086.exe --exec %build_dir_listing_0046% > %build_dir_listing_0046%_disassembled.txt
|
||||
fc /N %build_dir_listing_0046%.txt %build_dir_listing_0046%_disassembled.txt || exit /b 1
|
||||
|
||||
set listing_0047=listing_0047_challenge_flags
|
||||
copy /Y %script_dir%\%listing_0047% %build_dir% 1>NUL
|
||||
copy /Y %script_dir%\%listing_0047%.txt %build_dir% 1>NUL
|
||||
set build_dir_listing_0047=%build_dir%\%listing_0047%
|
||||
%build_dir%\sim8086.exe --exec %build_dir_listing_0047% > %build_dir_listing_0047%_disassembled.txt
|
||||
fc /N %build_dir_listing_0047%.txt %build_dir_listing_0047%_disassembled.txt || exit /b 1
|
||||
Binary file not shown.
@@ -0,0 +1,23 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Please see https://computerenhance.com for further information
|
||||
;
|
||||
; ========================================================================
|
||||
|
||||
; ========================================================================
|
||||
; LISTING 48
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov cx, 200
|
||||
mov bx, cx
|
||||
add cx, 1000
|
||||
mov bx, 2000
|
||||
sub cx, bx
|
||||
@@ -0,0 +1,13 @@
|
||||
--- test\listing_0048_ip_register execution ---
|
||||
mov cx, 200 ; cx:0x0->0xc8 ip:0x0->0x3
|
||||
mov bx, cx ; bx:0x0->0xc8 ip:0x3->0x5
|
||||
add cx, 1000 ; cx:0xc8->0x4b0 ip:0x5->0x9 flags:->A
|
||||
mov bx, 2000 ; bx:0xc8->0x7d0 ip:0x9->0xc
|
||||
sub cx, bx ; cx:0x4b0->0xfce0 ip:0xc->0xe flags:A->CS
|
||||
|
||||
Final registers:
|
||||
bx: 0x07d0 (2000)
|
||||
cx: 0xfce0 (64736)
|
||||
ip: 0x000e (14)
|
||||
flags: CS
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Please see https://computerenhance.com for further information
|
||||
;
|
||||
; ========================================================================
|
||||
|
||||
; ========================================================================
|
||||
; LISTING 49
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov cx, 3
|
||||
mov bx, 1000
|
||||
loop_start:
|
||||
add bx, 10
|
||||
sub cx, 1
|
||||
jnz loop_start
|
||||
@@ -0,0 +1,18 @@
|
||||
--- test\listing_0049_conditional_jumps execution ---
|
||||
mov cx, 3 ; cx:0x0->0x3 ip:0x0->0x3
|
||||
mov bx, 1000 ; bx:0x0->0x3e8 ip:0x3->0x6
|
||||
add bx, 10 ; bx:0x3e8->0x3f2 ip:0x6->0x9 flags:->A
|
||||
sub cx, 1 ; cx:0x3->0x2 ip:0x9->0xc flags:A->
|
||||
jne $-6 ; ip:0xc->0x6
|
||||
add bx, 10 ; bx:0x3f2->0x3fc ip:0x6->0x9 flags:->P
|
||||
sub cx, 1 ; cx:0x2->0x1 ip:0x9->0xc flags:P->
|
||||
jne $-6 ; ip:0xc->0x6
|
||||
add bx, 10 ; bx:0x3fc->0x406 ip:0x6->0x9 flags:->PA
|
||||
sub cx, 1 ; cx:0x1->0x0 ip:0x9->0xc flags:PA->PZ
|
||||
jne $-6 ; ip:0xc->0xe
|
||||
|
||||
Final registers:
|
||||
bx: 0x0406 (1030)
|
||||
ip: 0x000e (14)
|
||||
flags: PZ
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,38 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Please see https://computerenhance.com for further information
|
||||
;
|
||||
; ========================================================================
|
||||
|
||||
; ========================================================================
|
||||
; LISTING 50
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov ax, 10
|
||||
mov bx, 10
|
||||
mov cx, 10
|
||||
|
||||
label_0:
|
||||
cmp bx, cx
|
||||
je label_1
|
||||
|
||||
add ax, 1
|
||||
jp label_2
|
||||
|
||||
label_1:
|
||||
sub bx, 5
|
||||
jb label_3
|
||||
|
||||
label_2:
|
||||
sub cx, 2
|
||||
|
||||
label_3:
|
||||
loopnz label_0
|
||||
@@ -0,0 +1,38 @@
|
||||
--- test\listing_0050_challenge_jumps execution ---
|
||||
mov ax, 10 ; ax:0x0->0xa ip:0x0->0x3
|
||||
mov bx, 10 ; bx:0x0->0xa ip:0x3->0x6
|
||||
mov cx, 10 ; cx:0x0->0xa ip:0x6->0x9
|
||||
cmp bx, cx ; ip:0x9->0xb flags:->PZ
|
||||
je $+7 ; ip:0xb->0x12
|
||||
sub bx, 5 ; bx:0xa->0x5 ip:0x12->0x15 flags:PZ->P
|
||||
jb $+5 ; ip:0x15->0x17
|
||||
sub cx, 2 ; cx:0xa->0x8 ip:0x17->0x1a flags:P->
|
||||
loopnz $-17 ; cx:0x8->0x7 ip:0x1a->0x9
|
||||
cmp bx, cx ; ip:0x9->0xb flags:->CAS
|
||||
je $+7 ; ip:0xb->0xd
|
||||
add ax, 1 ; ax:0xa->0xb ip:0xd->0x10 flags:CAS->
|
||||
jp $+7 ; ip:0x10->0x12
|
||||
sub bx, 5 ; bx:0x5->0x0 ip:0x12->0x15 flags:->PZ
|
||||
jb $+5 ; ip:0x15->0x17
|
||||
sub cx, 2 ; cx:0x7->0x5 ip:0x17->0x1a flags:PZ->P
|
||||
loopnz $-17 ; cx:0x5->0x4 ip:0x1a->0x9
|
||||
cmp bx, cx ; ip:0x9->0xb flags:P->CPAS
|
||||
je $+7 ; ip:0xb->0xd
|
||||
add ax, 1 ; ax:0xb->0xc ip:0xd->0x10 flags:CPAS->P
|
||||
jp $+7 ; ip:0x10->0x17
|
||||
sub cx, 2 ; cx:0x4->0x2 ip:0x17->0x1a flags:P->
|
||||
loopnz $-17 ; cx:0x2->0x1 ip:0x1a->0x9
|
||||
cmp bx, cx ; ip:0x9->0xb flags:->CPAS
|
||||
je $+7 ; ip:0xb->0xd
|
||||
add ax, 1 ; ax:0xc->0xd ip:0xd->0x10 flags:CPAS->
|
||||
jp $+7 ; ip:0x10->0x12
|
||||
sub bx, 5 ; bx:0x0->0xfffb ip:0x12->0x15 flags:->CAS
|
||||
jb $+5 ; ip:0x15->0x1a
|
||||
loopnz $-17 ; cx:0x1->0x0 ip:0x1a->0x1c
|
||||
|
||||
Final registers:
|
||||
ax: 0x000d (13)
|
||||
bx: 0xfffb (65531)
|
||||
ip: 0x001c (28)
|
||||
flags: CAS
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Please see https://computerenhance.com for further information
|
||||
;
|
||||
; ========================================================================
|
||||
|
||||
; ========================================================================
|
||||
; LISTING 51
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov word [1000], 1
|
||||
mov word [1002], 2
|
||||
mov word [1004], 3
|
||||
mov word [1006], 4
|
||||
|
||||
mov bx, 1000
|
||||
mov word [bx + 4], 10
|
||||
|
||||
mov bx, word [1000]
|
||||
mov cx, word [1002]
|
||||
mov dx, word [1004]
|
||||
mov bp, word [1006]
|
||||
@@ -0,0 +1,19 @@
|
||||
--- test\listing_0051_memory_mov execution ---
|
||||
mov word [+1000], 1 ; ip:0x0->0x6
|
||||
mov word [+1002], 2 ; ip:0x6->0xc
|
||||
mov word [+1004], 3 ; ip:0xc->0x12
|
||||
mov word [+1006], 4 ; ip:0x12->0x18
|
||||
mov bx, 1000 ; bx:0x0->0x3e8 ip:0x18->0x1b
|
||||
mov word [bx+4], 10 ; ip:0x1b->0x20
|
||||
mov bx, [+1000] ; bx:0x3e8->0x1 ip:0x20->0x24
|
||||
mov cx, [+1002] ; cx:0x0->0x2 ip:0x24->0x28
|
||||
mov dx, [+1004] ; dx:0x0->0xa ip:0x28->0x2c
|
||||
mov bp, [+1006] ; bp:0x0->0x4 ip:0x2c->0x30
|
||||
|
||||
Final registers:
|
||||
bx: 0x0001 (1)
|
||||
cx: 0x0002 (2)
|
||||
dx: 0x000a (10)
|
||||
bp: 0x0004 (4)
|
||||
ip: 0x0030 (48)
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,36 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Please see https://computerenhance.com for further information
|
||||
;
|
||||
; ========================================================================
|
||||
|
||||
; ========================================================================
|
||||
; LISTING 52
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov dx, 6
|
||||
mov bp, 1000
|
||||
|
||||
mov si, 0
|
||||
init_loop_start:
|
||||
mov word [bp + si], si
|
||||
add si, 2
|
||||
cmp si, dx
|
||||
jnz init_loop_start
|
||||
|
||||
mov bx, 0
|
||||
mov si, 0
|
||||
add_loop_start:
|
||||
mov cx, word [bp + si]
|
||||
add bx, cx
|
||||
add si, 2
|
||||
cmp si, dx
|
||||
jnz add_loop_start
|
||||
@@ -0,0 +1,43 @@
|
||||
--- test\listing_0052_memory_add_loop execution ---
|
||||
mov dx, 6 ; dx:0x0->0x6 ip:0x0->0x3
|
||||
mov bp, 1000 ; bp:0x0->0x3e8 ip:0x3->0x6
|
||||
mov si, 0 ; ip:0x6->0x9
|
||||
mov word [bp+si], si ; ip:0x9->0xb
|
||||
add si, 2 ; si:0x0->0x2 ip:0xb->0xe
|
||||
cmp si, dx ; ip:0xe->0x10 flags:->CPAS
|
||||
jne $-7 ; ip:0x10->0x9
|
||||
mov word [bp+si], si ; ip:0x9->0xb
|
||||
add si, 2 ; si:0x2->0x4 ip:0xb->0xe flags:CPAS->
|
||||
cmp si, dx ; ip:0xe->0x10 flags:->CAS
|
||||
jne $-7 ; ip:0x10->0x9
|
||||
mov word [bp+si], si ; ip:0x9->0xb
|
||||
add si, 2 ; si:0x4->0x6 ip:0xb->0xe flags:CAS->P
|
||||
cmp si, dx ; ip:0xe->0x10 flags:P->PZ
|
||||
jne $-7 ; ip:0x10->0x12
|
||||
mov bx, 0 ; ip:0x12->0x15
|
||||
mov si, 0 ; si:0x6->0x0 ip:0x15->0x18
|
||||
mov cx, [bp+si] ; ip:0x18->0x1a
|
||||
add bx, cx ; ip:0x1a->0x1c
|
||||
add si, 2 ; si:0x0->0x2 ip:0x1c->0x1f flags:PZ->
|
||||
cmp si, dx ; ip:0x1f->0x21 flags:->CPAS
|
||||
jne $-9 ; ip:0x21->0x18
|
||||
mov cx, [bp+si] ; cx:0x0->0x2 ip:0x18->0x1a
|
||||
add bx, cx ; bx:0x0->0x2 ip:0x1a->0x1c flags:CPAS->
|
||||
add si, 2 ; si:0x2->0x4 ip:0x1c->0x1f
|
||||
cmp si, dx ; ip:0x1f->0x21 flags:->CAS
|
||||
jne $-9 ; ip:0x21->0x18
|
||||
mov cx, [bp+si] ; cx:0x2->0x4 ip:0x18->0x1a
|
||||
add bx, cx ; bx:0x2->0x6 ip:0x1a->0x1c flags:CAS->P
|
||||
add si, 2 ; si:0x4->0x6 ip:0x1c->0x1f
|
||||
cmp si, dx ; ip:0x1f->0x21 flags:P->PZ
|
||||
jne $-9 ; ip:0x21->0x23
|
||||
|
||||
Final registers:
|
||||
bx: 0x0006 (6)
|
||||
cx: 0x0004 (4)
|
||||
dx: 0x0006 (6)
|
||||
bp: 0x03e8 (1000)
|
||||
si: 0x0006 (6)
|
||||
ip: 0x0023 (35)
|
||||
flags: PZ
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,35 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Please see https://computerenhance.com for further information
|
||||
;
|
||||
; ========================================================================
|
||||
|
||||
; ========================================================================
|
||||
; LISTING 53
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov dx, 6
|
||||
mov bp, 1000
|
||||
|
||||
mov si, 0
|
||||
init_loop_start:
|
||||
mov word [bp + si], si
|
||||
add si, 2
|
||||
cmp si, dx
|
||||
jnz init_loop_start
|
||||
|
||||
mov bx, 0
|
||||
mov si, dx
|
||||
sub bp, 2
|
||||
add_loop_start:
|
||||
add bx, word [bp + si]
|
||||
sub si, 2
|
||||
jnz add_loop_start
|
||||
@@ -0,0 +1,36 @@
|
||||
--- test\listing_0053_add_loop_challenge execution ---
|
||||
mov dx, 6 ; dx:0x0->0x6 ip:0x0->0x3
|
||||
mov bp, 1000 ; bp:0x0->0x3e8 ip:0x3->0x6
|
||||
mov si, 0 ; ip:0x6->0x9
|
||||
mov word [bp+si], si ; ip:0x9->0xb
|
||||
add si, 2 ; si:0x0->0x2 ip:0xb->0xe
|
||||
cmp si, dx ; ip:0xe->0x10 flags:->CPAS
|
||||
jne $-7 ; ip:0x10->0x9
|
||||
mov word [bp+si], si ; ip:0x9->0xb
|
||||
add si, 2 ; si:0x2->0x4 ip:0xb->0xe flags:CPAS->
|
||||
cmp si, dx ; ip:0xe->0x10 flags:->CAS
|
||||
jne $-7 ; ip:0x10->0x9
|
||||
mov word [bp+si], si ; ip:0x9->0xb
|
||||
add si, 2 ; si:0x4->0x6 ip:0xb->0xe flags:CAS->P
|
||||
cmp si, dx ; ip:0xe->0x10 flags:P->PZ
|
||||
jne $-7 ; ip:0x10->0x12
|
||||
mov bx, 0 ; ip:0x12->0x15
|
||||
mov si, dx ; ip:0x15->0x17
|
||||
sub bp, 2 ; bp:0x3e8->0x3e6 ip:0x17->0x1a flags:PZ->
|
||||
add bx, [bp+si] ; bx:0x0->0x4 ip:0x1a->0x1c
|
||||
sub si, 2 ; si:0x6->0x4 ip:0x1c->0x1f
|
||||
jne $-5 ; ip:0x1f->0x1a
|
||||
add bx, [bp+si] ; bx:0x4->0x6 ip:0x1a->0x1c flags:->P
|
||||
sub si, 2 ; si:0x4->0x2 ip:0x1c->0x1f flags:P->
|
||||
jne $-5 ; ip:0x1f->0x1a
|
||||
add bx, [bp+si] ; ip:0x1a->0x1c flags:->P
|
||||
sub si, 2 ; si:0x2->0x0 ip:0x1c->0x1f flags:P->PZ
|
||||
jne $-5 ; ip:0x1f->0x21
|
||||
|
||||
Final registers:
|
||||
bx: 0x0006 (6)
|
||||
dx: 0x0006 (6)
|
||||
bp: 0x03e6 (998)
|
||||
ip: 0x0021 (33)
|
||||
flags: PZ
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,43 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Please see https://computerenhance.com for further information
|
||||
;
|
||||
; ========================================================================
|
||||
|
||||
; ========================================================================
|
||||
; LISTING 54
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
; Start image after one row, to avoid overwriting our code!
|
||||
mov bp, 64*4
|
||||
|
||||
mov dx, 0
|
||||
y_loop_start:
|
||||
|
||||
mov cx, 0
|
||||
x_loop_start:
|
||||
; Fill pixel
|
||||
mov word [bp + 0], cx ; Red
|
||||
mov word [bp + 2], dx ; Blue
|
||||
mov byte [bp + 3], 255 ; Alpha
|
||||
|
||||
; Advance pixel location
|
||||
add bp, 4
|
||||
|
||||
; Advance X coordinate and loop
|
||||
add cx, 1
|
||||
cmp cx, 64
|
||||
jnz x_loop_start
|
||||
|
||||
; Advance Y coordinate and loop
|
||||
add dx, 1
|
||||
cmp dx, 64
|
||||
jnz y_loop_start
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,53 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Please see https://computerenhance.com for further information
|
||||
;
|
||||
; ========================================================================
|
||||
|
||||
; ========================================================================
|
||||
; LISTING 55
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
; Start image after one row, to avoid overwriting our code!
|
||||
mov bp, 64*4
|
||||
|
||||
; Draw the solid rectangle red/blue/alpha
|
||||
mov dx, 64
|
||||
y_loop_start:
|
||||
|
||||
mov cx, 64
|
||||
x_loop_start:
|
||||
mov byte [bp + 0], cl ; Red
|
||||
mov byte [bp + 1], 0 ; Green
|
||||
mov byte [bp + 2], dl ; Blue
|
||||
mov byte [bp + 3], 255 ; Alpha
|
||||
add bp, 4
|
||||
|
||||
loop x_loop_start
|
||||
|
||||
sub dx, 1
|
||||
jnz y_loop_start
|
||||
|
||||
; Add the line rectangle green
|
||||
mov bp, 64*4 + 4*64 + 4
|
||||
mov bx, bp
|
||||
mov cx, 62
|
||||
outline_loop_start:
|
||||
|
||||
mov byte [bp + 1], 255 ; Top line
|
||||
mov byte [bp + 61*64*4 + 1], 255 ; Bottom line
|
||||
mov byte [bx + 1], 255 ; Left line
|
||||
mov byte [bx + 61*4 + 1], 255 ; Right line
|
||||
|
||||
add bp, 4
|
||||
add bx, 4*64
|
||||
|
||||
loop outline_loop_start
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Please see https://computerenhance.com for further information
|
||||
;
|
||||
; ========================================================================
|
||||
|
||||
; ========================================================================
|
||||
; LISTING 56
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov bx, 1000
|
||||
mov bp, 2000
|
||||
mov si, 3000
|
||||
mov di, 4000
|
||||
|
||||
mov cx, bx
|
||||
mov dx, 12
|
||||
|
||||
mov dx, [1000]
|
||||
|
||||
mov cx, [bx]
|
||||
mov cx, [bp]
|
||||
mov [si], cx
|
||||
mov [di], cx
|
||||
|
||||
mov cx, [bx + 1000]
|
||||
mov cx, [bp + 1000]
|
||||
mov [si + 1000], cx
|
||||
mov [di + 1000], cx
|
||||
|
||||
add cx, dx
|
||||
add [di + 1000], cx
|
||||
add dx, 50
|
||||
@@ -0,0 +1,72 @@
|
||||
**************
|
||||
**** 8086 ****
|
||||
**************
|
||||
|
||||
WARNING: Clocks reported by this utility are strictly from the 8086 manual.
|
||||
They will be inaccurate, both because the manual clocks are estimates, and because
|
||||
some of the entries in the manual look highly suspicious and are probably typos.
|
||||
|
||||
--- test\listing_0056_estimating_cycles execution ---
|
||||
mov bx, 1000 ; Clocks: +4 = 4 | bx:0x0->0x3e8 ip:0x0->0x3
|
||||
mov bp, 2000 ; Clocks: +4 = 8 | bp:0x0->0x7d0 ip:0x3->0x6
|
||||
mov si, 3000 ; Clocks: +4 = 12 | si:0x0->0xbb8 ip:0x6->0x9
|
||||
mov di, 4000 ; Clocks: +4 = 16 | di:0x0->0xfa0 ip:0x9->0xc
|
||||
mov cx, bx ; Clocks: +2 = 18 | cx:0x0->0x3e8 ip:0xc->0xe
|
||||
mov dx, 12 ; Clocks: +4 = 22 | dx:0x0->0xc ip:0xe->0x11
|
||||
mov dx, [+1000] ; Clocks: +14 = 36 (8 + 6ea) | dx:0xc->0x0 ip:0x11->0x15
|
||||
mov cx, [bx] ; Clocks: +13 = 49 (8 + 5ea) | cx:0x3e8->0x0 ip:0x15->0x17
|
||||
mov cx, [bp] ; Clocks: +13 = 62 (8 + 5ea) | ip:0x17->0x1a
|
||||
mov word [si], cx ; Clocks: +14 = 76 (9 + 5ea) | ip:0x1a->0x1c
|
||||
mov word [di], cx ; Clocks: +14 = 90 (9 + 5ea) | ip:0x1c->0x1e
|
||||
mov cx, [bx+1000] ; Clocks: +17 = 107 (8 + 9ea) | ip:0x1e->0x22
|
||||
mov cx, [bp+1000] ; Clocks: +17 = 124 (8 + 9ea) | ip:0x22->0x26
|
||||
mov word [si+1000], cx ; Clocks: +18 = 142 (9 + 9ea) | ip:0x26->0x2a
|
||||
mov word [di+1000], cx ; Clocks: +18 = 160 (9 + 9ea) | ip:0x2a->0x2e
|
||||
add cx, dx ; Clocks: +3 = 163 | ip:0x2e->0x30 flags:->PZ
|
||||
add word [di+1000], cx ; Clocks: +25 = 188 (16 + 9ea) | ip:0x30->0x34
|
||||
add dx, 50 ; Clocks: +4 = 192 | dx:0x0->0x32 ip:0x34->0x37 flags:PZ->
|
||||
|
||||
Final registers:
|
||||
bx: 0x03e8 (1000)
|
||||
dx: 0x0032 (50)
|
||||
bp: 0x07d0 (2000)
|
||||
si: 0x0bb8 (3000)
|
||||
di: 0x0fa0 (4000)
|
||||
ip: 0x0037 (55)
|
||||
|
||||
**************
|
||||
**** 8088 ****
|
||||
**************
|
||||
|
||||
WARNING: Clocks reported by this utility are strictly from the 8086 manual.
|
||||
They will be inaccurate, both because the manual clocks are estimates, and because
|
||||
some of the entries in the manual look highly suspicious and are probably typos.
|
||||
|
||||
--- test\listing_0056_estimating_cycles execution ---
|
||||
mov bx, 1000 ; Clocks: +4 = 4 | bx:0x0->0x3e8 ip:0x0->0x3
|
||||
mov bp, 2000 ; Clocks: +4 = 8 | bp:0x0->0x7d0 ip:0x3->0x6
|
||||
mov si, 3000 ; Clocks: +4 = 12 | si:0x0->0xbb8 ip:0x6->0x9
|
||||
mov di, 4000 ; Clocks: +4 = 16 | di:0x0->0xfa0 ip:0x9->0xc
|
||||
mov cx, bx ; Clocks: +2 = 18 | cx:0x0->0x3e8 ip:0xc->0xe
|
||||
mov dx, 12 ; Clocks: +4 = 22 | dx:0x0->0xc ip:0xe->0x11
|
||||
mov dx, [+1000] ; Clocks: +18 = 40 (8 + 6ea + 4p) | dx:0xc->0x0 ip:0x11->0x15
|
||||
mov cx, [bx] ; Clocks: +17 = 57 (8 + 5ea + 4p) | cx:0x3e8->0x0 ip:0x15->0x17
|
||||
mov cx, [bp] ; Clocks: +17 = 74 (8 + 5ea + 4p) | ip:0x17->0x1a
|
||||
mov word [si], cx ; Clocks: +18 = 92 (9 + 5ea + 4p) | ip:0x1a->0x1c
|
||||
mov word [di], cx ; Clocks: +18 = 110 (9 + 5ea + 4p) | ip:0x1c->0x1e
|
||||
mov cx, [bx+1000] ; Clocks: +21 = 131 (8 + 9ea + 4p) | ip:0x1e->0x22
|
||||
mov cx, [bp+1000] ; Clocks: +21 = 152 (8 + 9ea + 4p) | ip:0x22->0x26
|
||||
mov word [si+1000], cx ; Clocks: +22 = 174 (9 + 9ea + 4p) | ip:0x26->0x2a
|
||||
mov word [di+1000], cx ; Clocks: +22 = 196 (9 + 9ea + 4p) | ip:0x2a->0x2e
|
||||
add cx, dx ; Clocks: +3 = 199 | ip:0x2e->0x30 flags:->PZ
|
||||
add word [di+1000], cx ; Clocks: +33 = 232 (16 + 9ea + 8p) | ip:0x30->0x34
|
||||
add dx, 50 ; Clocks: +4 = 236 | dx:0x0->0x32 ip:0x34->0x37 flags:PZ->
|
||||
|
||||
Final registers:
|
||||
bx: 0x03e8 (1000)
|
||||
dx: 0x0032 (50)
|
||||
bp: 0x07d0 (2000)
|
||||
si: 0x0bb8 (3000)
|
||||
di: 0x0fa0 (4000)
|
||||
ip: 0x0037 (55)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
»иЅРѕёї ‹‰‹
|
||||
‰ ‹‹и‰€и‹Љи‰‰и’иѓL’й•зѓK
|
||||
@@ -0,0 +1,42 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Please see https://computerenhance.com for further information
|
||||
;
|
||||
; ========================================================================
|
||||
|
||||
; ========================================================================
|
||||
; LISTING 57
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov bx, 1000
|
||||
mov bp, 2000
|
||||
mov si, 3000
|
||||
mov di, 4000
|
||||
|
||||
mov cx, [bp + di]
|
||||
mov [bx + si], cx
|
||||
|
||||
mov cx, [bp + si]
|
||||
mov [bx + di], cx
|
||||
|
||||
mov cx, [bp + di + 1000]
|
||||
mov [bx + si + 1000], cx
|
||||
|
||||
mov cx, [bp + si + 1000]
|
||||
mov [bx + di + 1000], cx
|
||||
|
||||
add dx, [bp + si + 1000]
|
||||
|
||||
add word [bp + si], 76
|
||||
|
||||
add dx, [bp + si + 1001]
|
||||
add [di + 999], dx
|
||||
add word [bp + si], 75
|
||||
@@ -0,0 +1,70 @@
|
||||
**************
|
||||
**** 8086 ****
|
||||
**************
|
||||
|
||||
WARNING: Clocks reported by this utility are strictly from the 8086 manual.
|
||||
They will be inaccurate, both because the manual clocks are estimates, and because
|
||||
some of the entries in the manual look highly suspicious and are probably typos.
|
||||
|
||||
--- test\listing_0057_challenge_cycles execution ---
|
||||
mov bx, 1000 ; Clocks: +4 = 4 | bx:0x0->0x3e8 ip:0x0->0x3
|
||||
mov bp, 2000 ; Clocks: +4 = 8 | bp:0x0->0x7d0 ip:0x3->0x6
|
||||
mov si, 3000 ; Clocks: +4 = 12 | si:0x0->0xbb8 ip:0x6->0x9
|
||||
mov di, 4000 ; Clocks: +4 = 16 | di:0x0->0xfa0 ip:0x9->0xc
|
||||
mov cx, [bp+di] ; Clocks: +15 = 31 (8 + 7ea) | ip:0xc->0xe
|
||||
mov word [bx+si], cx ; Clocks: +16 = 47 (9 + 7ea) | ip:0xe->0x10
|
||||
mov cx, [bp+si] ; Clocks: +16 = 63 (8 + 8ea) | ip:0x10->0x12
|
||||
mov word [bx+di], cx ; Clocks: +17 = 80 (9 + 8ea) | ip:0x12->0x14
|
||||
mov cx, [bp+di+1000] ; Clocks: +19 = 99 (8 + 11ea) | ip:0x14->0x18
|
||||
mov word [bx+si+1000], cx ; Clocks: +20 = 119 (9 + 11ea) | ip:0x18->0x1c
|
||||
mov cx, [bp+si+1000] ; Clocks: +20 = 139 (8 + 12ea) | ip:0x1c->0x20
|
||||
mov word [bx+di+1000], cx ; Clocks: +21 = 160 (9 + 12ea) | ip:0x20->0x24
|
||||
add dx, [bp+si+1000] ; Clocks: +21 = 181 (9 + 12ea) | ip:0x24->0x28 flags:->PZ
|
||||
add word [bp+si], 76 ; Clocks: +25 = 206 (17 + 8ea) | ip:0x28->0x2b flags:PZ->
|
||||
add dx, [bp+si+1001] ; Clocks: +25 = 231 (9 + 12ea + 4p) | ip:0x2b->0x2f flags:->PZ
|
||||
add word [di+999], dx ; Clocks: +33 = 264 (16 + 9ea + 8p) | ip:0x2f->0x33 flags:PZ->P
|
||||
add word [bp+si], 75 ; Clocks: +25 = 289 (17 + 8ea) | ip:0x33->0x36 flags:P->A
|
||||
|
||||
Final registers:
|
||||
bx: 0x03e8 (1000)
|
||||
bp: 0x07d0 (2000)
|
||||
si: 0x0bb8 (3000)
|
||||
di: 0x0fa0 (4000)
|
||||
ip: 0x0036 (54)
|
||||
flags: A
|
||||
|
||||
**************
|
||||
**** 8088 ****
|
||||
**************
|
||||
|
||||
WARNING: Clocks reported by this utility are strictly from the 8086 manual.
|
||||
They will be inaccurate, both because the manual clocks are estimates, and because
|
||||
some of the entries in the manual look highly suspicious and are probably typos.
|
||||
|
||||
--- test\listing_0057_challenge_cycles execution ---
|
||||
mov bx, 1000 ; Clocks: +4 = 4 | bx:0x0->0x3e8 ip:0x0->0x3
|
||||
mov bp, 2000 ; Clocks: +4 = 8 | bp:0x0->0x7d0 ip:0x3->0x6
|
||||
mov si, 3000 ; Clocks: +4 = 12 | si:0x0->0xbb8 ip:0x6->0x9
|
||||
mov di, 4000 ; Clocks: +4 = 16 | di:0x0->0xfa0 ip:0x9->0xc
|
||||
mov cx, [bp+di] ; Clocks: +19 = 35 (8 + 7ea + 4p) | ip:0xc->0xe
|
||||
mov word [bx+si], cx ; Clocks: +20 = 55 (9 + 7ea + 4p) | ip:0xe->0x10
|
||||
mov cx, [bp+si] ; Clocks: +20 = 75 (8 + 8ea + 4p) | ip:0x10->0x12
|
||||
mov word [bx+di], cx ; Clocks: +21 = 96 (9 + 8ea + 4p) | ip:0x12->0x14
|
||||
mov cx, [bp+di+1000] ; Clocks: +23 = 119 (8 + 11ea + 4p) | ip:0x14->0x18
|
||||
mov word [bx+si+1000], cx ; Clocks: +24 = 143 (9 + 11ea + 4p) | ip:0x18->0x1c
|
||||
mov cx, [bp+si+1000] ; Clocks: +24 = 167 (8 + 12ea + 4p) | ip:0x1c->0x20
|
||||
mov word [bx+di+1000], cx ; Clocks: +25 = 192 (9 + 12ea + 4p) | ip:0x20->0x24
|
||||
add dx, [bp+si+1000] ; Clocks: +25 = 217 (9 + 12ea + 4p) | ip:0x24->0x28 flags:->PZ
|
||||
add word [bp+si], 76 ; Clocks: +33 = 250 (17 + 8ea + 8p) | ip:0x28->0x2b flags:PZ->
|
||||
add dx, [bp+si+1001] ; Clocks: +25 = 275 (9 + 12ea + 4p) | ip:0x2b->0x2f flags:->PZ
|
||||
add word [di+999], dx ; Clocks: +33 = 308 (16 + 9ea + 8p) | ip:0x2f->0x33 flags:PZ->P
|
||||
add word [bp+si], 75 ; Clocks: +33 = 341 (17 + 8ea + 8p) | ip:0x33->0x36 flags:P->A
|
||||
|
||||
Final registers:
|
||||
bx: 0x03e8 (1000)
|
||||
bp: 0x07d0 (2000)
|
||||
si: 0x0bb8 (3000)
|
||||
di: 0x0fa0 (4000)
|
||||
ip: 0x0036 (54)
|
||||
flags: A
|
||||
|
||||
+556
-194
@@ -13,46 +13,6 @@
|
||||
|
||||
#include "sim8086_stdlib.c"
|
||||
|
||||
typedef enum S86_RegisterByte {
|
||||
S86_RegisterByte_Lo,
|
||||
S86_RegisterByte_Hi,
|
||||
S86_RegisterByte_Count,
|
||||
S86_RegisterByte_Nil,
|
||||
} S86_RegisterByte;
|
||||
|
||||
typedef union S86_Register16 {
|
||||
uint16_t word;
|
||||
uint8_t bytes[S86_RegisterByte_Count];
|
||||
} S86_Register16;
|
||||
|
||||
typedef struct S86_RegisterFileFlags {
|
||||
bool carry;
|
||||
bool zero;
|
||||
bool sign;
|
||||
bool overflow;
|
||||
bool parity;
|
||||
bool auxiliary_carry;
|
||||
} S86_RegisterFileFlags;
|
||||
|
||||
typedef struct S86_RegisterFile {
|
||||
S86_RegisterFileFlags flags;
|
||||
|
||||
S86_Register16 ax;
|
||||
S86_Register16 bx;
|
||||
S86_Register16 cx;
|
||||
S86_Register16 dx;
|
||||
|
||||
S86_Register16 sp;
|
||||
S86_Register16 bp;
|
||||
S86_Register16 si;
|
||||
S86_Register16 di;
|
||||
|
||||
S86_Register16 es;
|
||||
S86_Register16 cs;
|
||||
S86_Register16 ss;
|
||||
S86_Register16 ds;
|
||||
} S86_RegisterFile;
|
||||
|
||||
bool S86_RegisterFileFlagsEq(S86_RegisterFileFlags lhs, S86_RegisterFileFlags rhs)
|
||||
{
|
||||
bool result = lhs.carry == rhs.carry &&
|
||||
@@ -236,6 +196,49 @@ S86_Str8 S86_MnemonicOpStr8(S86_MnemonicOp type)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool S86_MnemonicOpIsAccumulator(S86_MnemonicOp type)
|
||||
{
|
||||
bool result = type == S86_MnemonicOp_AX ||
|
||||
type == S86_MnemonicOp_AL ||
|
||||
type == S86_MnemonicOp_AH;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool S86_MnemonicOpIsRegister(S86_MnemonicOp type)
|
||||
{
|
||||
bool result = (type >= S86_MnemonicOp_AL && type <= S86_MnemonicOp_DI) ||
|
||||
(type >= S86_MnemonicOp_ES && type <= S86_MnemonicOp_DS);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool S86_MnemonicOpIsEffectiveAddress(S86_MnemonicOp type)
|
||||
{
|
||||
bool result = (type >= S86_MnemonicOp_BX_SI && type <= S86_MnemonicOp_BP_DI) ||
|
||||
(type == S86_MnemonicOp_DirectAddress);
|
||||
return result;
|
||||
}
|
||||
|
||||
S86_Str8 S86_RegisterFileRegArrayStr8(S86_RegisterFileRegArray type)
|
||||
{
|
||||
S86_Str8 result = {0};
|
||||
switch (type) {
|
||||
case S86_RegisterFileRegArray_AX: result = S86_MnemonicOpStr8(S86_MnemonicOp_AX); break;
|
||||
case S86_RegisterFileRegArray_BX: result = S86_MnemonicOpStr8(S86_MnemonicOp_BX); break;
|
||||
case S86_RegisterFileRegArray_CX: result = S86_MnemonicOpStr8(S86_MnemonicOp_CX); break;
|
||||
case S86_RegisterFileRegArray_DX: result = S86_MnemonicOpStr8(S86_MnemonicOp_DX); break;
|
||||
case S86_RegisterFileRegArray_SP: result = S86_MnemonicOpStr8(S86_MnemonicOp_SP); break;
|
||||
case S86_RegisterFileRegArray_BP: result = S86_MnemonicOpStr8(S86_MnemonicOp_BP); break;
|
||||
case S86_RegisterFileRegArray_SI: result = S86_MnemonicOpStr8(S86_MnemonicOp_SI); break;
|
||||
case S86_RegisterFileRegArray_DI: result = S86_MnemonicOpStr8(S86_MnemonicOp_DI); break;
|
||||
case S86_RegisterFileRegArray_ES: result = S86_MnemonicOpStr8(S86_MnemonicOp_ES); break;
|
||||
case S86_RegisterFileRegArray_CS: result = S86_MnemonicOpStr8(S86_MnemonicOp_DS); break;
|
||||
case S86_RegisterFileRegArray_SS: result = S86_MnemonicOpStr8(S86_MnemonicOp_SS); break;
|
||||
case S86_RegisterFileRegArray_DS: result = S86_MnemonicOpStr8(S86_MnemonicOp_DS); break;
|
||||
case S86_RegisterFileRegArray_Count: break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void S86_PrintOpcodeMnemonicOp(S86_Opcode opcode, bool src)
|
||||
{
|
||||
// TODO: It sucks to have these enums that specify source or dest because
|
||||
@@ -247,9 +250,6 @@ void S86_PrintOpcodeMnemonicOp(S86_Opcode opcode, bool src)
|
||||
// and then you can have one code path that just checks the flags on each op
|
||||
S86_MnemonicOp mnemonic_op = src ? opcode.src : opcode.dest;
|
||||
|
||||
bool wide_prefix = ( src && opcode.wide_prefix == S86_WidePrefix_Src) ||
|
||||
(!src && opcode.wide_prefix == S86_WidePrefix_Dest);
|
||||
|
||||
bool effective_addr = ( src && opcode.effective_addr == S86_EffectiveAddress_Src) ||
|
||||
(!src && opcode.effective_addr == S86_EffectiveAddress_Dest);
|
||||
|
||||
@@ -261,8 +261,8 @@ void S86_PrintOpcodeMnemonicOp(S86_Opcode opcode, bool src)
|
||||
else
|
||||
S86_PrintFmt(" ");
|
||||
|
||||
if (wide_prefix)
|
||||
S86_PrintFmt("%s ", opcode.wide ? "word" : "byte");
|
||||
if (!src && opcode.word_byte_prefix != S86_WordBytePrefix_None)
|
||||
S86_PrintFmt("%s ", opcode.word_byte_prefix == S86_WordBytePrefix_Word ? "word" : "byte");
|
||||
|
||||
if (effective_addr && opcode.seg_reg_prefix != S86_MnemonicOp_Invalid) {
|
||||
S86_Str8 prefix = S86_MnemonicOpStr8(opcode.seg_reg_prefix);
|
||||
@@ -274,12 +274,14 @@ void S86_PrintOpcodeMnemonicOp(S86_Opcode opcode, bool src)
|
||||
|
||||
if (mnemonic_op == S86_MnemonicOp_DirectAddress) {
|
||||
S86_PrintFmt("%s%d",
|
||||
opcode.displacement >= 0 ? "" : "-",
|
||||
opcode.displacement >= 0 ? "+" : "-",
|
||||
opcode.displacement >= 0 ? opcode.displacement : -opcode.displacement);
|
||||
} else if (mnemonic_op == S86_MnemonicOp_Jump) {
|
||||
S86_PrintFmt("$+2%c%d",
|
||||
opcode.displacement > 0 ? '+' : '-',
|
||||
opcode.displacement > 0 ? opcode.displacement : -opcode.displacement);
|
||||
// NOTE: Account for the opcode itself which is 2 bytes, e.g. we can print $+2-8 or just $-6
|
||||
int32_t displacement = opcode.displacement + 2;
|
||||
S86_PrintFmt("$%c%d",
|
||||
displacement > 0 ? '+' : '-',
|
||||
displacement > 0 ? displacement : -displacement);
|
||||
} else if (mnemonic_op == S86_MnemonicOp_Immediate) {
|
||||
if (opcode.immediate_is_8bit) {
|
||||
S86_PrintFmt("%d", (int8_t)opcode.immediate);
|
||||
@@ -299,8 +301,8 @@ void S86_PrintOpcodeMnemonicOp(S86_Opcode opcode, bool src)
|
||||
}
|
||||
|
||||
if (effective_addr && opcode.displacement) {
|
||||
S86_PrintFmt("%c%d",
|
||||
opcode.displacement >= 0 ? '+' : '-',
|
||||
S86_PrintFmt("%s%d",
|
||||
opcode.displacement >= 0 ? "+" : "-",
|
||||
opcode.displacement >= 0 ? opcode.displacement : -opcode.displacement);
|
||||
}
|
||||
}
|
||||
@@ -370,8 +372,12 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it,
|
||||
S86_OpDecode const *decode_table,
|
||||
uint16_t decode_table_size,
|
||||
bool *lock_prefix,
|
||||
S86_MnemonicOp *seg_reg)
|
||||
S86_MnemonicOp *seg_reg,
|
||||
bool cycle_count_8088)
|
||||
{
|
||||
(void)cycle_count_8088;
|
||||
|
||||
size_t buffer_start_index = buffer_it->index;
|
||||
char op_code_bytes[2] = {0};
|
||||
size_t op_code_size = 0;
|
||||
op_code_bytes[op_code_size++] = S86_BufferIteratorNextByte(buffer_it);
|
||||
@@ -417,10 +423,12 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it,
|
||||
S86_ASSERT(op_code_size > 0 && op_code_size <= S86_ARRAY_UCOUNT(op_code_bytes));
|
||||
S86_ASSERT(op_decode_type != S86_OpDecodeType_Count && "Unknown instruction");
|
||||
|
||||
S86_Opcode result = {0};
|
||||
result.mnemonic = op_decode->mnemonic;
|
||||
result.lock_prefix = *lock_prefix;
|
||||
result.seg_reg_prefix = *seg_reg;
|
||||
S86_Opcode result = {0};
|
||||
result.type = op_decode_type;
|
||||
result.mnemonic = op_decode->mnemonic;
|
||||
result.lock_prefix = *lock_prefix;
|
||||
result.seg_reg_prefix = *seg_reg;
|
||||
|
||||
S86_ASSERT(*seg_reg == S86_MnemonicOp_Invalid || (*seg_reg >= S86_MnemonicOp_ES && *seg_reg <= S86_MnemonicOp_DS));
|
||||
switch (op_decode_type) {
|
||||
// NOTE: Instruction Pattern => [0b0000'0000W | 0bAA00'0CCC | DISP-LO | DISP-HI]
|
||||
@@ -464,8 +472,6 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it,
|
||||
}
|
||||
|
||||
S86_DecodeEffectiveAddr(&result, buffer_it, rm, mod, w);
|
||||
result.wide_prefix = S86_WidePrefix_Dest;
|
||||
|
||||
// NOTE: Bit shifts use 'v' to indicate if shift distance should
|
||||
// come from cl register otherwise bitshift by 1
|
||||
if (op_decode_type >= S86_OpDecodeType_SHL_SAL && op_decode_type <= S86_OpDecodeType_RCR) {
|
||||
@@ -658,10 +664,6 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it,
|
||||
}
|
||||
|
||||
result.src = S86_MnemonicOp_Immediate;
|
||||
if (op_decode_type == S86_OpDecodeType_MOVImmediateToRegOrMem)
|
||||
result.wide_prefix = S86_WidePrefix_Src;
|
||||
else if (result.effective_addr_loads_mem)
|
||||
result.wide_prefix = S86_WidePrefix_Dest;
|
||||
} break;
|
||||
|
||||
// NOTE: Instruction Pattern => [0b0000'W00W | DATA-LO | DATA-HI]
|
||||
@@ -855,52 +857,111 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it,
|
||||
if (op_decode_type != S86_OpDecodeType_SEGMENT)
|
||||
*seg_reg = S86_MnemonicOp_Invalid;
|
||||
|
||||
if (result.effective_addr == S86_EffectiveAddress_Dest && result.effective_addr_loads_mem) {
|
||||
result.word_byte_prefix = (result.wide || result.src >= S86_MnemonicOp_AX && result.src <= S86_MnemonicOp_BX)
|
||||
? S86_WordBytePrefix_Word
|
||||
: S86_WordBytePrefix_Byte;
|
||||
}
|
||||
|
||||
|
||||
size_t buffer_end_index = buffer_it->index;
|
||||
result.byte_size = S86_CAST(uint8_t)(buffer_end_index - buffer_start_index);
|
||||
S86_ASSERT(result.immediate < S86_CAST(uint16_t)-1);
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct S86_MnemonicOpToRegisterFileMap {
|
||||
S86_MnemonicOp mnemonic_op; ///< Register/op that the mnemonic is using
|
||||
S86_MnemonicOp mnemonic_op_reg16; ///< 16 bit register for the mnemonic op
|
||||
S86_Register16 *reg; ///< Pointer to the register memory this mnemonic op is using
|
||||
S86_RegisterByte byte; ///< The 'byte' that the mnemonic operates on (hi, lo or nil e.g. word)
|
||||
} S86_MnemonicOpToRegisterFileMap;
|
||||
|
||||
#define PRINT_USAGE S86_PrintLn(S86_STR8("usage: sim8086.exe [--exec] <binary asm file>"))
|
||||
char const CLI_ARG_EXEC[] = "--exec";
|
||||
char const CLI_ARG_LOG_INSTRUCTION_PTR[] = "--log-instruction-ptr";
|
||||
char const CLI_ARG_LOG_CYCLE_COUNTS[] = "--log-cycle-counts";
|
||||
char const CLI_ARG_DUMP[] = "--dump";
|
||||
#define PRINT_USAGE \
|
||||
S86_PrintLnFmt("USAGE: sim8086.exe [%.*s] [%.*s] [%.*s] [%.*s <8086|8088>] <binary asm file>", \
|
||||
S86_ARRAY_UCOUNT(CLI_ARG_EXEC) - 1, \
|
||||
CLI_ARG_EXEC, \
|
||||
S86_ARRAY_UCOUNT(CLI_ARG_LOG_INSTRUCTION_PTR) - 1, \
|
||||
CLI_ARG_LOG_INSTRUCTION_PTR, \
|
||||
S86_ARRAY_UCOUNT(CLI_ARG_LOG_CYCLE_COUNTS) - 1, \
|
||||
CLI_ARG_LOG_CYCLE_COUNTS, \
|
||||
S86_ARRAY_UCOUNT(CLI_ARG_DUMP) - 1, \
|
||||
CLI_ARG_DUMP)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// NOTE: Argument handling
|
||||
// =========================================================================
|
||||
if (argc != 2 && argc != 3) {
|
||||
if (argc < 2) {
|
||||
PRINT_USAGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool exec_mode = false;
|
||||
char const *file_path = NULL;
|
||||
if (argc == 3) {
|
||||
S86_Str8 exec_mode_str8 = {argv[1], strlen(argv[1])};
|
||||
file_path = argv[2];
|
||||
if (!S86_Str8_Equals(exec_mode_str8, S86_STR8("--exec"))) {
|
||||
PRINT_USAGE;
|
||||
return -1;
|
||||
S86_Str8 CLI_ARG_EXEC_STR8 = (S86_Str8){(char *)CLI_ARG_EXEC, S86_ARRAY_UCOUNT(CLI_ARG_EXEC) - 1};
|
||||
S86_Str8 CLI_ARG_LOG_INSTRUCTION_PTR_STR8 = (S86_Str8){(char *)CLI_ARG_LOG_INSTRUCTION_PTR, S86_ARRAY_UCOUNT(CLI_ARG_LOG_INSTRUCTION_PTR) - 1};
|
||||
S86_Str8 CLI_ARG_LOG_CYCLE_COUNTS_STR8 = (S86_Str8){(char *)CLI_ARG_LOG_CYCLE_COUNTS, S86_ARRAY_UCOUNT(CLI_ARG_LOG_CYCLE_COUNTS) - 1};
|
||||
S86_Str8 CLI_ARG_DUMP_STR8 = (S86_Str8){(char *)CLI_ARG_DUMP, S86_ARRAY_UCOUNT(CLI_ARG_DUMP) - 1};
|
||||
|
||||
typedef enum CycleCount {
|
||||
CycleCount_None,
|
||||
CycleCount_8086,
|
||||
CycleCount_8088,
|
||||
} CycleCount;
|
||||
|
||||
CycleCount log_cycle_counts = CycleCount_None;
|
||||
bool exec_mode = false;
|
||||
bool log_instruction_ptr = false;
|
||||
bool dump = false;
|
||||
S86_Str8 file_path = {0};
|
||||
for (int arg_index = 1; arg_index < argc; arg_index++) {
|
||||
|
||||
char const *arg_cstring = argv[arg_index];
|
||||
S86_Str8 arg_str8 = (S86_Str8){(char *)arg_cstring, strlen(arg_cstring)};
|
||||
S86_Str8 next_arg_str8 = S86_STR8("");
|
||||
if ((arg_index + 1) < argc)
|
||||
next_arg_str8 = (S86_Str8){(char *)argv[arg_index + 1], strlen(argv[arg_index + 1])};
|
||||
|
||||
if (S86_Str8_Equals(arg_str8, CLI_ARG_EXEC_STR8)) {
|
||||
exec_mode = true;
|
||||
} else if (S86_Str8_Equals(arg_str8, CLI_ARG_LOG_INSTRUCTION_PTR_STR8)) {
|
||||
log_instruction_ptr = true;
|
||||
} else if (S86_Str8_Equals(arg_str8, CLI_ARG_LOG_CYCLE_COUNTS_STR8)) {
|
||||
if (S86_Str8_Equals(next_arg_str8, S86_STR8("8086"))) {
|
||||
log_cycle_counts = CycleCount_8086;
|
||||
} else if (S86_Str8_Equals(next_arg_str8, S86_STR8("8088"))) {
|
||||
log_cycle_counts = CycleCount_8088;
|
||||
} else {
|
||||
S86_PrintLnFmt("ERROR: Only '8086' or '8088' is accepted after the cycle count argument [arg=\"%.*s\"]", S86_STR8_FMT(next_arg_str8));
|
||||
PRINT_USAGE;
|
||||
return -1;
|
||||
}
|
||||
arg_index++;
|
||||
} else if (S86_Str8_Equals(arg_str8, CLI_ARG_DUMP_STR8)) {
|
||||
dump = true;
|
||||
} else {
|
||||
if (file_path.size) {
|
||||
S86_PrintLnFmt("ERROR: Only 1 ASM binary file is supported per invocation [file=\"%.*s\"]", S86_STR8_FMT(file_path));
|
||||
PRINT_USAGE;
|
||||
return -1;
|
||||
}
|
||||
file_path = arg_str8;
|
||||
}
|
||||
exec_mode = true;
|
||||
} else {
|
||||
file_path = argv[1];
|
||||
}
|
||||
|
||||
char const *file_name = file_path;
|
||||
size_t file_path_size = strlen(file_path);
|
||||
for (size_t index = file_path_size - 1; index < file_path_size; index--) {
|
||||
if (file_path[index] == '\\' || file_path[index] == '/') {
|
||||
file_name = file_path + index + 1;
|
||||
char const *file_name = file_path.data;
|
||||
for (size_t index = file_path.size - 1; index < file_path.size; index--) {
|
||||
if (file_path.data[index] == '\\' || file_path.data[index] == '/') {
|
||||
file_name = file_path.data + index + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
S86_Buffer buffer = S86_FileRead(file_path);
|
||||
S86_Buffer buffer = S86_FileRead(file_path.data);
|
||||
if (!S86_BufferIsValid(buffer)) {
|
||||
S86_PrintLnFmt("File read failed [path=\"%s\"]", argv[1], buffer.size);
|
||||
S86_PrintLnFmt("ERROR: Failed to read file [path=\"%.*s\"]", S86_STR8_FMT(file_path));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1198,51 +1259,76 @@ int main(int argc, char **argv)
|
||||
|
||||
// NOTE: Decode assembly
|
||||
// =========================================================================
|
||||
if (exec_mode)
|
||||
if (exec_mode) {
|
||||
if (log_cycle_counts != CycleCount_None) { // NOTE: Print disclaimer + header
|
||||
S86_PrintLn(S86_STR8("**************"));
|
||||
S86_PrintLnFmt("**** %s ****", log_cycle_counts == CycleCount_8086 ? "8086" : "8088");
|
||||
S86_PrintLn(S86_STR8("**************"));
|
||||
S86_PrintLn(S86_STR8(""));
|
||||
S86_PrintLn(S86_STR8("WARNING: Clocks reported by this utility are strictly from the 8086 manual."));
|
||||
S86_PrintLn(S86_STR8("They will be inaccurate, both because the manual clocks are estimates, and because"));
|
||||
S86_PrintLn(S86_STR8("some of the entries in the manual look highly suspicious and are probably typos."));
|
||||
S86_PrintLn(S86_STR8(""));
|
||||
}
|
||||
S86_PrintLnFmt("--- test\\%s execution ---", file_name);
|
||||
else
|
||||
} else {
|
||||
S86_PrintLn(S86_STR8("bits 16"));
|
||||
}
|
||||
|
||||
uint32_t const S86_MEMORY_SIZE = 1024 * 1024;
|
||||
S86_RegisterFile register_file = {0};
|
||||
S86_BufferIterator buffer_it = S86_BufferIteratorInit(buffer);
|
||||
S86_MnemonicOp seg_reg = {0};
|
||||
bool lock_prefix = false;
|
||||
uint8_t *memory = VirtualAlloc(0, S86_MEMORY_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
memcpy(memory, buffer.data, buffer.size);
|
||||
|
||||
S86_MnemonicOpToRegisterFileMap mnemonic_op_to_register_file_map[] = {
|
||||
{.mnemonic_op = S86_MnemonicOp_AX, .mnemonic_op_reg16 = S86_MnemonicOp_AX, .reg = ®ister_file.ax, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_AL, .mnemonic_op_reg16 = S86_MnemonicOp_AX, .reg = ®ister_file.ax, .byte = S86_RegisterByte_Lo},
|
||||
{.mnemonic_op = S86_MnemonicOp_AH, .mnemonic_op_reg16 = S86_MnemonicOp_AX, .reg = ®ister_file.ax, .byte = S86_RegisterByte_Hi},
|
||||
{.mnemonic_op = S86_MnemonicOp_AX, .reg = ®ister_file.reg.file.ax, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_AL, .reg = ®ister_file.reg.file.ax, .byte = S86_RegisterByte_Lo},
|
||||
{.mnemonic_op = S86_MnemonicOp_AH, .reg = ®ister_file.reg.file.ax, .byte = S86_RegisterByte_Hi},
|
||||
|
||||
{.mnemonic_op = S86_MnemonicOp_CX, .mnemonic_op_reg16 = S86_MnemonicOp_CX, .reg = ®ister_file.cx, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_CL, .mnemonic_op_reg16 = S86_MnemonicOp_CX, .reg = ®ister_file.cx, .byte = S86_RegisterByte_Lo},
|
||||
{.mnemonic_op = S86_MnemonicOp_CH, .mnemonic_op_reg16 = S86_MnemonicOp_CX, .reg = ®ister_file.cx, .byte = S86_RegisterByte_Lo},
|
||||
{.mnemonic_op = S86_MnemonicOp_CX, .reg = ®ister_file.reg.file.cx, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_CL, .reg = ®ister_file.reg.file.cx, .byte = S86_RegisterByte_Lo},
|
||||
{.mnemonic_op = S86_MnemonicOp_CH, .reg = ®ister_file.reg.file.cx, .byte = S86_RegisterByte_Lo},
|
||||
|
||||
{.mnemonic_op = S86_MnemonicOp_DX, .mnemonic_op_reg16 = S86_MnemonicOp_DX, .reg = ®ister_file.dx, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_DL, .mnemonic_op_reg16 = S86_MnemonicOp_DX, .reg = ®ister_file.dx, .byte = S86_RegisterByte_Lo},
|
||||
{.mnemonic_op = S86_MnemonicOp_DH, .mnemonic_op_reg16 = S86_MnemonicOp_DX, .reg = ®ister_file.dx, .byte = S86_RegisterByte_Hi},
|
||||
{.mnemonic_op = S86_MnemonicOp_DX, .reg = ®ister_file.reg.file.dx, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_DL, .reg = ®ister_file.reg.file.dx, .byte = S86_RegisterByte_Lo},
|
||||
{.mnemonic_op = S86_MnemonicOp_DH, .reg = ®ister_file.reg.file.dx, .byte = S86_RegisterByte_Hi},
|
||||
|
||||
{.mnemonic_op = S86_MnemonicOp_BX, .mnemonic_op_reg16 = S86_MnemonicOp_BX, .reg = ®ister_file.bx, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_BL, .mnemonic_op_reg16 = S86_MnemonicOp_BX, .reg = ®ister_file.bx, .byte = S86_RegisterByte_Lo},
|
||||
{.mnemonic_op = S86_MnemonicOp_BH, .mnemonic_op_reg16 = S86_MnemonicOp_BX, .reg = ®ister_file.bx, .byte = S86_RegisterByte_Hi},
|
||||
{.mnemonic_op = S86_MnemonicOp_BX, .reg = ®ister_file.reg.file.bx, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_BL, .reg = ®ister_file.reg.file.bx, .byte = S86_RegisterByte_Lo},
|
||||
{.mnemonic_op = S86_MnemonicOp_BH, .reg = ®ister_file.reg.file.bx, .byte = S86_RegisterByte_Hi},
|
||||
|
||||
{.mnemonic_op = S86_MnemonicOp_SP, .mnemonic_op_reg16 = S86_MnemonicOp_SP, .reg = ®ister_file.sp, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_BP, .mnemonic_op_reg16 = S86_MnemonicOp_BP, .reg = ®ister_file.bp, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_SI, .mnemonic_op_reg16 = S86_MnemonicOp_SI, .reg = ®ister_file.si, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_DI, .mnemonic_op_reg16 = S86_MnemonicOp_DI, .reg = ®ister_file.di, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_SP, .reg = ®ister_file.reg.file.sp, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_BP, .reg = ®ister_file.reg.file.bp, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_SI, .reg = ®ister_file.reg.file.si, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_DI, .reg = ®ister_file.reg.file.di, .byte = S86_RegisterByte_Nil},
|
||||
|
||||
{.mnemonic_op = S86_MnemonicOp_ES, .mnemonic_op_reg16 = S86_MnemonicOp_ES, .reg = ®ister_file.es, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_CS, .mnemonic_op_reg16 = S86_MnemonicOp_CS, .reg = ®ister_file.cs, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_SS, .mnemonic_op_reg16 = S86_MnemonicOp_SS, .reg = ®ister_file.ss, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_DS, .mnemonic_op_reg16 = S86_MnemonicOp_DS, .reg = ®ister_file.ds, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_ES, .reg = ®ister_file.reg.file.es, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_CS, .reg = ®ister_file.reg.file.cs, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_SS, .reg = ®ister_file.reg.file.ss, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_DS, .reg = ®ister_file.reg.file.ds, .byte = S86_RegisterByte_Nil},
|
||||
|
||||
{.mnemonic_op = S86_MnemonicOp_BX_SI, .reg = ®ister_file.reg.file.bx, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_BX_DI, .reg = ®ister_file.reg.file.bx, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_BP_SI, .reg = ®ister_file.reg.file.bp, .byte = S86_RegisterByte_Nil},
|
||||
{.mnemonic_op = S86_MnemonicOp_BP_DI, .reg = ®ister_file.reg.file.bp, .byte = S86_RegisterByte_Nil},
|
||||
};
|
||||
|
||||
while (S86_BufferIteratorHasMoreBytes(buffer_it)) {
|
||||
S86_Opcode opcode = S86_DecodeOpcode(&buffer_it,
|
||||
DECODE_TABLE,
|
||||
S86_ARRAY_UCOUNT(DECODE_TABLE),
|
||||
&lock_prefix,
|
||||
&seg_reg);
|
||||
// NOTE: Execute the assembly
|
||||
// =========================================================================
|
||||
S86_Buffer instruction_buffer = {0};
|
||||
instruction_buffer.data = (char *)&memory[register_file.instruction_ptr];
|
||||
instruction_buffer.size = buffer.size;
|
||||
S86_BufferIterator instruction_it = S86_BufferIteratorInit(instruction_buffer);
|
||||
uint32_t clocks_counter = 0;
|
||||
|
||||
bool lock_prefix = false;
|
||||
S86_MnemonicOp seg_reg = S86_CAST(S86_MnemonicOp)0;
|
||||
for (uint16_t prev_ip = 0; register_file.instruction_ptr < buffer.size; prev_ip = register_file.instruction_ptr) {
|
||||
instruction_it.index = register_file.instruction_ptr;
|
||||
S86_Opcode opcode = S86_DecodeOpcode(&instruction_it, DECODE_TABLE, S86_ARRAY_UCOUNT(DECODE_TABLE), &lock_prefix, &seg_reg, log_cycle_counts == CycleCount_8088);
|
||||
S86_PrintOpcode(opcode);
|
||||
|
||||
register_file.instruction_ptr += opcode.byte_size;
|
||||
if (opcode.mnemonic == S86_Mnemonic_LOCK || opcode.mnemonic == S86_Mnemonic_SEGMENT)
|
||||
continue;
|
||||
|
||||
@@ -1251,7 +1337,13 @@ int main(int argc, char **argv)
|
||||
continue;
|
||||
}
|
||||
|
||||
S86_RegisterFileFlags prev_flags = register_file.flags;
|
||||
// NOTE: Simulate instruction ==============================================================
|
||||
bool cycle_count_8088 = log_cycle_counts == CycleCount_8088;
|
||||
uint32_t base_clocks = 0;
|
||||
uint32_t effective_address_clocks = 0;
|
||||
uint32_t transfer_penalty_clocks = 0;
|
||||
|
||||
S86_RegisterFile prev_register_file = register_file;
|
||||
switch (opcode.mnemonic) {
|
||||
case S86_Mnemonic_PUSH: /*FALLTHRU*/
|
||||
case S86_Mnemonic_POP: /*FALLTHRU*/
|
||||
@@ -1299,15 +1391,11 @@ int main(int argc, char **argv)
|
||||
case S86_Mnemonic_CALL: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JMP: /*FALLTHRU*/
|
||||
case S86_Mnemonic_RET: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JE_JZ: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JL_JNGE: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JLE_JNG: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JB_JNAE: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JBE_JNA: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JP_JPE: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JO: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JS: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JNE_JNZ: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JNL_JGE: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JNLE_JG: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JNB_JAE: /*FALLTHRU*/
|
||||
@@ -1315,9 +1403,7 @@ int main(int argc, char **argv)
|
||||
case S86_Mnemonic_JNP_JO: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JNO: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JNS: /*FALLTHRU*/
|
||||
case S86_Mnemonic_LOOP: /*FALLTHRU*/
|
||||
case S86_Mnemonic_LOOPZ_LOOPE: /*FALLTHRU*/
|
||||
case S86_Mnemonic_LOOPNZ_LOOPNE: /*FALLTHRU*/
|
||||
case S86_Mnemonic_JCXZ: /*FALLTHRU*/
|
||||
case S86_Mnemonic_INT: /*FALLTHRU*/
|
||||
case S86_Mnemonic_INT3: /*FALLTHRU*/
|
||||
@@ -1336,24 +1422,18 @@ int main(int argc, char **argv)
|
||||
case S86_Mnemonic_SEGMENT: break;
|
||||
|
||||
case S86_Mnemonic_MOV: {
|
||||
S86_MnemonicOpToRegisterFileMap const *dest_map = NULL;
|
||||
for (size_t index = 0; !dest_map && index < S86_ARRAY_UCOUNT(mnemonic_op_to_register_file_map); index++) {
|
||||
S86_MnemonicOpToRegisterFileMap const *item = mnemonic_op_to_register_file_map + index;
|
||||
if (item->mnemonic_op == opcode.dest)
|
||||
dest_map = item;
|
||||
}
|
||||
S86_ASSERT(dest_map);
|
||||
|
||||
uint16_t prev_dest = dest_map->reg->word;
|
||||
bool byte_op = opcode.dest >= S86_MnemonicOp_AL && opcode.dest <= S86_MnemonicOp_BH;
|
||||
uint16_t src = 0;
|
||||
bool byte_op = opcode.dest >= S86_MnemonicOp_AL && opcode.dest <= S86_MnemonicOp_BH;
|
||||
if (opcode.src == S86_MnemonicOp_Immediate) {
|
||||
if (byte_op) {
|
||||
S86_ASSERT(opcode.immediate < S86_CAST(uint8_t)-1);
|
||||
dest_map->reg->bytes[dest_map->byte] = S86_CAST(uint8_t)opcode.immediate;
|
||||
src = S86_CAST(uint8_t)opcode.immediate;
|
||||
} else {
|
||||
S86_ASSERT(opcode.immediate < S86_CAST(uint16_t)-1);
|
||||
dest_map->reg->word = S86_CAST(uint16_t)opcode.immediate;
|
||||
src = S86_CAST(uint16_t)opcode.immediate;
|
||||
}
|
||||
} else if (opcode.src == S86_MnemonicOp_DirectAddress) {
|
||||
S86_ASSERT(opcode.displacement >= 0);
|
||||
src = memory[opcode.displacement];
|
||||
} else {
|
||||
S86_MnemonicOpToRegisterFileMap const *src_map = NULL;
|
||||
for (size_t index = 0; !src_map && index < S86_ARRAY_UCOUNT(mnemonic_op_to_register_file_map); index++) {
|
||||
@@ -1362,14 +1442,119 @@ int main(int argc, char **argv)
|
||||
src_map = item;
|
||||
}
|
||||
|
||||
if (byte_op)
|
||||
dest_map->reg->bytes[dest_map->byte] = src_map->reg->bytes[src_map->byte];
|
||||
else
|
||||
dest_map->reg->word = src_map->reg->word;
|
||||
if ((src_map->mnemonic_op >= S86_MnemonicOp_BX_SI &&
|
||||
src_map->mnemonic_op <= S86_MnemonicOp_BP_DI) || opcode.effective_addr == S86_EffectiveAddress_Src) {
|
||||
uint16_t address = 0;
|
||||
if (src_map->mnemonic_op == S86_MnemonicOp_BX_SI) {
|
||||
address = src_map->reg->word + register_file.reg.file.si.word;
|
||||
} else if (src_map->mnemonic_op == S86_MnemonicOp_BX_DI) {
|
||||
address = src_map->reg->word + register_file.reg.file.di.word;
|
||||
} else if (src_map->mnemonic_op == S86_MnemonicOp_BP_SI) {
|
||||
address = src_map->reg->word + register_file.reg.file.si.word;
|
||||
} else if (src_map->mnemonic_op == S86_MnemonicOp_BP_DI) {
|
||||
address = src_map->reg->word + register_file.reg.file.di.word;
|
||||
} else if (opcode.effective_addr == S86_EffectiveAddress_Src) {
|
||||
address = src_map->reg->word;
|
||||
} else {
|
||||
S86_ASSERT(!"Invalid code path");
|
||||
}
|
||||
src = *(uint16_t *)&memory[address];
|
||||
} else {
|
||||
src = byte_op ? src_map->reg->bytes[src_map->byte] : src_map->reg->word;
|
||||
}
|
||||
}
|
||||
|
||||
S86_Str8 dest_reg16 = S86_MnemonicOpStr8(dest_map->mnemonic_op_reg16);
|
||||
S86_PrintFmt(" ; %.*s:0x%x->0x%x ", S86_STR8_FMT(dest_reg16), prev_dest, dest_map->reg->word);
|
||||
uint8_t *dest_lo = NULL;
|
||||
uint8_t *dest_hi = NULL;
|
||||
if (opcode.dest == S86_MnemonicOp_DirectAddress) {
|
||||
// NOTE: The 8086 doesn't support load to store directly
|
||||
// memory to memory afaict
|
||||
S86_ASSERT(opcode.dest != opcode.src);
|
||||
S86_ASSERT(opcode.displacement >= 0);
|
||||
|
||||
dest_lo = memory + opcode.displacement;
|
||||
dest_hi = byte_op ? NULL : memory + (opcode.displacement + 1);
|
||||
} else {
|
||||
S86_MnemonicOpToRegisterFileMap const *dest_map = NULL;
|
||||
for (size_t index = 0; !dest_map && index < S86_ARRAY_UCOUNT(mnemonic_op_to_register_file_map); index++) {
|
||||
S86_MnemonicOpToRegisterFileMap const *item = mnemonic_op_to_register_file_map + index;
|
||||
if (item->mnemonic_op == opcode.dest)
|
||||
dest_map = item;
|
||||
}
|
||||
S86_ASSERT(dest_map);
|
||||
|
||||
// NOTE: Effective address means we're store/load from memory
|
||||
// The opcode value is the address.
|
||||
if (opcode.effective_addr == S86_EffectiveAddress_Dest && opcode.effective_addr_loads_mem) {
|
||||
uint16_t address = dest_map->reg->word + S86_CAST(uint16_t)opcode.displacement;
|
||||
if (dest_map->mnemonic_op == S86_MnemonicOp_BX_SI) {
|
||||
address = dest_map->reg->word + register_file.reg.file.si.word;
|
||||
} else if (dest_map->mnemonic_op == S86_MnemonicOp_BX_DI) {
|
||||
address = dest_map->reg->word + register_file.reg.file.di.word;
|
||||
} else if (dest_map->mnemonic_op == S86_MnemonicOp_BP_SI) {
|
||||
address = dest_map->reg->word + register_file.reg.file.si.word;
|
||||
} else if (dest_map->mnemonic_op == S86_MnemonicOp_BP_DI) {
|
||||
address = dest_map->reg->word + register_file.reg.file.di.word;
|
||||
}
|
||||
dest_lo = memory + address;
|
||||
dest_hi = byte_op ? NULL : memory + (address + 1);
|
||||
|
||||
} else {
|
||||
if (byte_op) {
|
||||
dest_lo = &dest_map->reg->bytes[dest_map->byte];
|
||||
} else {
|
||||
dest_lo = &dest_map->reg->bytes[0];
|
||||
dest_hi = &dest_map->reg->bytes[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dest_lo)
|
||||
*dest_lo = S86_CAST(uint8_t)(src >> 0);
|
||||
if (dest_hi)
|
||||
*dest_hi = S86_CAST(uint8_t)(src >> 8);
|
||||
|
||||
if (S86_MnemonicOpIsRegister(opcode.dest) && opcode.src == S86_MnemonicOp_Immediate && !opcode.effective_addr_loads_mem) {
|
||||
base_clocks = 4;
|
||||
} else if (S86_MnemonicOpIsRegister(opcode.dest) && S86_MnemonicOpIsRegister(opcode.src) && !opcode.effective_addr_loads_mem) {
|
||||
base_clocks = 2;
|
||||
} else if (S86_MnemonicOpIsRegister(opcode.dest) && opcode.src == S86_MnemonicOp_DirectAddress && opcode.effective_addr_loads_mem && opcode.effective_addr == S86_EffectiveAddress_Src) {
|
||||
base_clocks = 8;
|
||||
effective_address_clocks = 6;
|
||||
if (cycle_count_8088 && opcode.wide) {
|
||||
transfer_penalty_clocks = 4;
|
||||
}
|
||||
} else if (S86_MnemonicOpIsRegister(opcode.dest) && S86_MnemonicOpIsRegister(opcode.src) && opcode.effective_addr_loads_mem && opcode.effective_addr == S86_EffectiveAddress_Src) {
|
||||
base_clocks = 8;
|
||||
effective_address_clocks = opcode.displacement ? 9 : 5;
|
||||
if (cycle_count_8088 && opcode.wide) {
|
||||
transfer_penalty_clocks = 4;
|
||||
}
|
||||
} else if (S86_MnemonicOpIsRegister(opcode.dest) && S86_MnemonicOpIsRegister(opcode.src) && opcode.effective_addr_loads_mem && opcode.effective_addr == S86_EffectiveAddress_Dest) {
|
||||
base_clocks = 9;
|
||||
effective_address_clocks = opcode.displacement ? 9 : 5;
|
||||
if (cycle_count_8088 && opcode.wide) {
|
||||
transfer_penalty_clocks = 4;
|
||||
}
|
||||
} else if (S86_MnemonicOpIsRegister(opcode.dest) && S86_MnemonicOpIsEffectiveAddress(opcode.src) && opcode.effective_addr_loads_mem && opcode.effective_addr == S86_EffectiveAddress_Src) {
|
||||
base_clocks = 8;
|
||||
if (cycle_count_8088 && opcode.wide)
|
||||
transfer_penalty_clocks = 4;
|
||||
if (opcode.src == S86_MnemonicOp_BP_DI || opcode.src == S86_MnemonicOp_BX_SI) {
|
||||
effective_address_clocks = opcode.displacement ? 11 : 7;
|
||||
} else if (opcode.src == S86_MnemonicOp_BP_SI || opcode.src == S86_MnemonicOp_BX_DI) {
|
||||
effective_address_clocks = opcode.displacement ? 12 : 8;
|
||||
}
|
||||
} else if (S86_MnemonicOpIsEffectiveAddress(opcode.dest) && S86_MnemonicOpIsRegister(opcode.src) && opcode.effective_addr_loads_mem && opcode.effective_addr == S86_EffectiveAddress_Dest) {
|
||||
base_clocks = 9;
|
||||
if (cycle_count_8088 && opcode.wide)
|
||||
transfer_penalty_clocks = 4;
|
||||
if (opcode.dest == S86_MnemonicOp_BP_DI || opcode.dest == S86_MnemonicOp_BX_SI) {
|
||||
effective_address_clocks = opcode.displacement ? 11 : 7;
|
||||
} else if (opcode.dest == S86_MnemonicOp_BP_SI || opcode.dest == S86_MnemonicOp_BX_DI) {
|
||||
effective_address_clocks = opcode.displacement ? 12 : 8;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case S86_Mnemonic_ADD: /*FALLTHRU*/
|
||||
@@ -1383,12 +1568,11 @@ int main(int argc, char **argv)
|
||||
}
|
||||
S86_ASSERT(dest_map);
|
||||
|
||||
bool subtract = opcode.mnemonic != S86_Mnemonic_ADD;
|
||||
S86_Register16 dest = *dest_map->reg;
|
||||
S86_Register16 prev_dest = dest;
|
||||
bool byte_op = opcode.dest >= S86_MnemonicOp_AL && opcode.dest <= S86_MnemonicOp_BH;
|
||||
bool subtract = opcode.mnemonic != S86_Mnemonic_ADD;
|
||||
bool byte_op = opcode.dest >= S86_MnemonicOp_AL && opcode.dest <= S86_MnemonicOp_BH;
|
||||
|
||||
uint16_t src = 0;
|
||||
uint16_t src = 0;
|
||||
uint16_t src_address = 0;
|
||||
if (opcode.src == S86_MnemonicOp_Immediate) {
|
||||
if (byte_op) {
|
||||
S86_ASSERT(opcode.immediate < S86_CAST(uint8_t)-1);
|
||||
@@ -1397,6 +1581,9 @@ int main(int argc, char **argv)
|
||||
S86_ASSERT(opcode.immediate < S86_CAST(uint16_t)-1);
|
||||
src = S86_CAST(uint16_t)opcode.immediate;
|
||||
}
|
||||
} else if (opcode.src == S86_MnemonicOp_DirectAddress) {
|
||||
S86_ASSERT(opcode.displacement >= 0);
|
||||
src = memory[opcode.displacement];
|
||||
} else {
|
||||
S86_MnemonicOpToRegisterFileMap const *src_map = NULL;
|
||||
for (size_t index = 0; !src_map && index < S86_ARRAY_UCOUNT(mnemonic_op_to_register_file_map); index++) {
|
||||
@@ -1404,19 +1591,76 @@ int main(int argc, char **argv)
|
||||
if (item->mnemonic_op == opcode.src)
|
||||
src_map = item;
|
||||
}
|
||||
src = byte_op ? src_map->reg->bytes[src_map->byte] : src_map->reg->word;
|
||||
|
||||
if ((src_map->mnemonic_op >= S86_MnemonicOp_BX_SI &&
|
||||
src_map->mnemonic_op <= S86_MnemonicOp_BP_DI) || (opcode.effective_addr == S86_EffectiveAddress_Src && opcode.effective_addr_loads_mem)) {
|
||||
if (src_map->mnemonic_op == S86_MnemonicOp_BX_SI) {
|
||||
src_address = S86_CAST(uint16_t)(src_map->reg->word + register_file.reg.file.si.word + opcode.displacement);
|
||||
} else if (src_map->mnemonic_op == S86_MnemonicOp_BX_DI) {
|
||||
src_address = S86_CAST(uint16_t)(src_map->reg->word + register_file.reg.file.di.word + opcode.displacement);
|
||||
} else if (src_map->mnemonic_op == S86_MnemonicOp_BP_SI) {
|
||||
src_address = S86_CAST(uint16_t)(src_map->reg->word + register_file.reg.file.si.word + opcode.displacement);
|
||||
} else if (src_map->mnemonic_op == S86_MnemonicOp_BP_DI) {
|
||||
src_address = S86_CAST(uint16_t)(src_map->reg->word + register_file.reg.file.di.word + opcode.displacement);
|
||||
} else if (opcode.effective_addr == S86_EffectiveAddress_Src) {
|
||||
src_address = S86_CAST(uint16_t)(src_map->reg->word + opcode.displacement);
|
||||
} else {
|
||||
S86_ASSERT(!"Invalid code path");
|
||||
}
|
||||
src = *(uint16_t *)&memory[src_address];
|
||||
} else {
|
||||
src = byte_op ? src_map->reg->bytes[src_map->byte] : src_map->reg->word;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Overflow if the sign masks were initially the same,
|
||||
// but, after the operation the sign masked changed.
|
||||
uint8_t const sign_mask8 = 0b1000'0000;
|
||||
uint16_t const sign_mask16 = 0b1000'0000'0000'0000;
|
||||
|
||||
// NOTE: Effective address means we're store/load from memory
|
||||
// The opcode value is the address.
|
||||
S86_Register16 dummy_register = {0};
|
||||
uint8_t *dest_lo = NULL;
|
||||
uint8_t *dest_hi = NULL;
|
||||
if (opcode.effective_addr == S86_EffectiveAddress_Dest && opcode.effective_addr_loads_mem) {
|
||||
uint16_t address = dest_map->reg->word;
|
||||
if (dest_map->mnemonic_op == S86_MnemonicOp_BX_SI) {
|
||||
address = S86_CAST(uint16_t)(dest_map->reg->word + register_file.reg.file.si.word + opcode.displacement);
|
||||
} else if (dest_map->mnemonic_op == S86_MnemonicOp_BX_DI) {
|
||||
address = S86_CAST(uint16_t)(dest_map->reg->word + register_file.reg.file.di.word + opcode.displacement);
|
||||
} else if (dest_map->mnemonic_op == S86_MnemonicOp_BP_SI) {
|
||||
address = S86_CAST(uint16_t)(dest_map->reg->word + register_file.reg.file.si.word + opcode.displacement);
|
||||
} else if (dest_map->mnemonic_op == S86_MnemonicOp_BP_DI) {
|
||||
address = S86_CAST(uint16_t)(dest_map->reg->word + register_file.reg.file.di.word + opcode.displacement);
|
||||
} else if (opcode.effective_addr == S86_EffectiveAddress_Dest) {
|
||||
address = S86_CAST(uint16_t)(dest_map->reg->word + opcode.displacement);
|
||||
}
|
||||
dest_lo = memory + address;
|
||||
dest_hi = byte_op ? NULL : memory + (address + 1);
|
||||
|
||||
} else {
|
||||
if (byte_op) {
|
||||
dest_lo = &dest_map->reg->bytes[dest_map->byte];
|
||||
} else {
|
||||
dest_lo = &dest_map->reg->bytes[0];
|
||||
dest_hi = &dest_map->reg->bytes[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode.mnemonic == S86_Mnemonic_CMP) {
|
||||
dummy_register.bytes[0] = *dest_lo;
|
||||
dummy_register.bytes[1] = *dest_hi;
|
||||
dest_lo = &dummy_register.bytes[0];
|
||||
dest_hi = &dummy_register.bytes[1];
|
||||
}
|
||||
|
||||
if (byte_op) {
|
||||
uint8_t src_u8 = S86_CAST(uint8_t)src;
|
||||
if (subtract)
|
||||
src_u8 = ~src_u8 + 1;
|
||||
|
||||
uint8_t dest_u8 = dest.bytes[dest_map->byte];
|
||||
uint8_t dest_u8 = *dest_lo;
|
||||
uint8_t new_dest_u8 = dest_u8 + src_u8;
|
||||
|
||||
// NOTE: Overflow check
|
||||
@@ -1439,66 +1683,174 @@ int main(int argc, char **argv)
|
||||
register_file.flags.sign = new_dest_u8 & 0b1000'0000;
|
||||
|
||||
// NOTE: Update the register
|
||||
dest.bytes[dest_map->byte] = new_dest_u8;
|
||||
*dest_lo = new_dest_u8;
|
||||
} else {
|
||||
if (subtract)
|
||||
src = ~src + 1;
|
||||
|
||||
uint16_t dest_word = *(uint16_t *)dest_lo;
|
||||
S86_Register16 new_dest = {0};
|
||||
new_dest.word = dest.word + src;
|
||||
new_dest.word = dest_word + src;
|
||||
|
||||
// NOTE: Overflow check
|
||||
bool initially_matching_sign_masks = (dest.word & sign_mask16) == (src & sign_mask16);
|
||||
bool sign_masks_changed = (dest.word & sign_mask16) != (new_dest.word & sign_mask16);
|
||||
bool initially_matching_sign_masks = (dest_word & sign_mask16) == (src & sign_mask16);
|
||||
bool sign_masks_changed = (dest_word & sign_mask16) != (new_dest.word & sign_mask16);
|
||||
register_file.flags.overflow = initially_matching_sign_masks && sign_masks_changed;
|
||||
|
||||
// NOTE: Auxiliary carry check
|
||||
uint8_t dest_lo_nibble_lo = dest.bytes[S86_RegisterByte_Lo] & 0b0000'1111 >> 0;
|
||||
uint8_t dest_lo_nibble_hi = dest.bytes[S86_RegisterByte_Lo] & 0b1111'0000 >> 4;
|
||||
uint8_t dest_lo_nibble_lo = *dest_lo & 0b0000'1111 >> 0;
|
||||
uint8_t dest_lo_nibble_hi = *dest_lo & 0b1111'0000 >> 4;
|
||||
uint8_t new_dest_lo_nibble_lo = new_dest.bytes[S86_RegisterByte_Lo] & 0b0000'1111 >> 0;
|
||||
uint8_t new_dest_lo_nibble_hi = new_dest.bytes[S86_RegisterByte_Lo] & 0b1111'0000 >> 4;
|
||||
register_file.flags.auxiliary_carry = subtract ? new_dest_lo_nibble_hi > dest_lo_nibble_hi
|
||||
: new_dest_lo_nibble_lo < dest_lo_nibble_lo;
|
||||
|
||||
// NOTE: Carry check
|
||||
register_file.flags.carry = subtract ? new_dest.word > dest.word : new_dest.word < dest.word;
|
||||
register_file.flags.carry = subtract ? new_dest.word > dest_word : new_dest.word < dest_word;
|
||||
|
||||
// NOTE: Sign check
|
||||
register_file.flags.sign = new_dest.word & 0b1000'0000'0000'0000;
|
||||
|
||||
// NOTE: Update the register
|
||||
dest.word = new_dest.word;
|
||||
*dest_lo = new_dest.bytes[0];
|
||||
*dest_hi = new_dest.bytes[1];
|
||||
}
|
||||
|
||||
int lo_bit_count = _mm_popcnt_u32(S86_CAST(uint32_t)dest.bytes[S86_RegisterByte_Lo]);
|
||||
int lo_bit_count = _mm_popcnt_u32(S86_CAST(uint32_t)*dest_lo);
|
||||
register_file.flags.parity = lo_bit_count % 2 == 0;
|
||||
register_file.flags.zero = false;
|
||||
if (opcode.mnemonic == S86_Mnemonic_ADD || opcode.mnemonic == S86_Mnemonic_SUB)
|
||||
register_file.flags.zero = byte_op ? dest.bytes[dest_map->byte] == 0 : dest.word == 0;
|
||||
register_file.flags.zero = byte_op ? *dest_lo == 0 : *(uint16_t*)dest_lo == 0;
|
||||
|
||||
if (opcode.mnemonic == S86_Mnemonic_CMP) {
|
||||
S86_PrintFmt(" ; ");
|
||||
} else {
|
||||
S86_Str8 dest_reg16 = S86_MnemonicOpStr8(dest_map->mnemonic_op_reg16);
|
||||
S86_PrintFmt(" ; %.*s:0x%x->0x%x ", S86_STR8_FMT(dest_reg16), prev_dest.word, dest.word);
|
||||
*dest_map->reg = dest;
|
||||
if (S86_MnemonicOpIsRegister(opcode.dest) && S86_MnemonicOpIsRegister(opcode.src) && opcode.effective_addr == S86_EffectiveAddress_None) {
|
||||
base_clocks = 3;
|
||||
} else if (S86_MnemonicOpIsRegister(opcode.dest) && S86_MnemonicOpIsRegister(opcode.src) && opcode.effective_addr == S86_EffectiveAddress_Dest) {
|
||||
base_clocks = 16;
|
||||
effective_address_clocks = opcode.displacement ? 9 : 5;
|
||||
if (cycle_count_8088) {
|
||||
if (opcode.wide) {
|
||||
transfer_penalty_clocks = 8;
|
||||
}
|
||||
} else {
|
||||
if ((uintptr_t)dest_lo & 1) {
|
||||
transfer_penalty_clocks = 8;
|
||||
}
|
||||
}
|
||||
} else if (S86_MnemonicOpIsRegister(opcode.dest) && opcode.src == S86_MnemonicOp_Immediate) {
|
||||
base_clocks = 4;
|
||||
} else if (S86_MnemonicOpIsEffectiveAddress(opcode.dest) && opcode.src == S86_MnemonicOp_Immediate && opcode.effective_addr_loads_mem) {
|
||||
base_clocks = 17;
|
||||
if (cycle_count_8088 && opcode.wide)
|
||||
transfer_penalty_clocks = 8;
|
||||
if (opcode.dest == S86_MnemonicOp_BP_DI || opcode.dest == S86_MnemonicOp_BX_SI) {
|
||||
effective_address_clocks = opcode.displacement ? 11 : 7;
|
||||
} else if (opcode.dest == S86_MnemonicOp_BP_SI || opcode.dest == S86_MnemonicOp_BX_DI) {
|
||||
effective_address_clocks = opcode.displacement ? 12 : 8;
|
||||
}
|
||||
} else if (S86_MnemonicOpIsRegister(opcode.dest) && S86_MnemonicOpIsEffectiveAddress(opcode.src) && opcode.effective_addr_loads_mem && opcode.effective_addr == S86_EffectiveAddress_Src) {
|
||||
base_clocks = 9;
|
||||
if (cycle_count_8088) {
|
||||
if (opcode.wide) {
|
||||
transfer_penalty_clocks = 4;
|
||||
}
|
||||
} else {
|
||||
if (src_address & 1) {
|
||||
transfer_penalty_clocks = 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode.src == S86_MnemonicOp_BP_DI || opcode.src == S86_MnemonicOp_BX_SI) {
|
||||
effective_address_clocks = opcode.displacement ? 11 : 7;
|
||||
} else if (opcode.src == S86_MnemonicOp_BP_SI || opcode.src == S86_MnemonicOp_BX_DI) {
|
||||
effective_address_clocks = opcode.displacement ? 12 : 8;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case S86_Mnemonic_JNE_JNZ: {
|
||||
if (!register_file.flags.zero)
|
||||
register_file.instruction_ptr += S86_CAST(int16_t)opcode.displacement;
|
||||
} break;
|
||||
|
||||
case S86_Mnemonic_JE_JZ: {
|
||||
if (register_file.flags.zero)
|
||||
register_file.instruction_ptr += S86_CAST(int16_t)opcode.displacement;
|
||||
} break;
|
||||
|
||||
case S86_Mnemonic_JP_JPE: {
|
||||
if (register_file.flags.parity)
|
||||
register_file.instruction_ptr += S86_CAST(int16_t)opcode.displacement;
|
||||
} break;
|
||||
|
||||
case S86_Mnemonic_JB_JNAE: {
|
||||
if (register_file.flags.carry)
|
||||
register_file.instruction_ptr += S86_CAST(int16_t)opcode.displacement;
|
||||
} break;
|
||||
|
||||
case S86_Mnemonic_LOOP: {
|
||||
register_file.reg.file.cx.word -= 1;
|
||||
if (register_file.reg.file.cx.word != 0)
|
||||
register_file.instruction_ptr += S86_CAST(int16_t)opcode.displacement;
|
||||
} break;
|
||||
|
||||
case S86_Mnemonic_LOOPNZ_LOOPNE: {
|
||||
register_file.reg.file.cx.word -= 1;
|
||||
if (register_file.reg.file.cx.word != 0 && !register_file.flags.zero)
|
||||
register_file.instruction_ptr += S86_CAST(int16_t)opcode.displacement;
|
||||
} break;
|
||||
}
|
||||
|
||||
if (!S86_RegisterFileFlagsEq(register_file.flags, prev_flags)) {
|
||||
clocks_counter += base_clocks + effective_address_clocks + transfer_penalty_clocks;
|
||||
|
||||
// NOTE: Printing ==========================================================================
|
||||
S86_PrintFmt(" ; ");
|
||||
|
||||
// NOTE: Clocks
|
||||
if (log_cycle_counts) {
|
||||
S86_PrintFmt("Clocks: +%u = %u", base_clocks + effective_address_clocks + transfer_penalty_clocks, clocks_counter);
|
||||
if (effective_address_clocks || transfer_penalty_clocks) {
|
||||
S86_PrintFmt(" (%u", base_clocks);
|
||||
if (effective_address_clocks)
|
||||
S86_PrintFmt(" + %uea", effective_address_clocks);
|
||||
if (transfer_penalty_clocks)
|
||||
S86_PrintFmt(" + %up", transfer_penalty_clocks);
|
||||
S86_PrintFmt(")");
|
||||
}
|
||||
S86_PrintFmt(" | ");
|
||||
}
|
||||
|
||||
// NOTE: Registers
|
||||
for (size_t index = 0; index < S86_RegisterFileRegArray_Count; index++) {
|
||||
if (register_file.reg.array[index].word != prev_register_file.reg.array[index].word) {
|
||||
S86_Str8 label = S86_RegisterFileRegArrayStr8(index);
|
||||
S86_PrintFmt("%.*s:0x%x->0x%x ",
|
||||
S86_STR8_FMT(label),
|
||||
prev_register_file.reg.array[index].word,
|
||||
register_file.reg.array[index].word);
|
||||
|
||||
// NOTE: In 8086, instructions can only change one register at a
|
||||
// time. Once we find the first delta in the register file, we
|
||||
// exit.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Instruction Pointer
|
||||
if (log_instruction_ptr)
|
||||
S86_PrintFmt("ip:0x%x->0x%x ", prev_ip, register_file.instruction_ptr);
|
||||
|
||||
// NOTE: Flags
|
||||
if (!S86_RegisterFileFlagsEq(register_file.flags, prev_register_file.flags)) {
|
||||
S86_PrintFmt("flags:");
|
||||
if (prev_flags.carry)
|
||||
if (prev_register_file.flags.carry)
|
||||
S86_PrintFmt("C");
|
||||
if (prev_flags.parity)
|
||||
if (prev_register_file.flags.parity)
|
||||
S86_PrintFmt("P");
|
||||
if (prev_flags.auxiliary_carry)
|
||||
if (prev_register_file.flags.auxiliary_carry)
|
||||
S86_PrintFmt("A");
|
||||
if (prev_flags.zero)
|
||||
if (prev_register_file.flags.zero)
|
||||
S86_PrintFmt("Z");
|
||||
if (prev_flags.sign)
|
||||
if (prev_register_file.flags.sign)
|
||||
S86_PrintFmt("S");
|
||||
if (prev_flags.overflow)
|
||||
if (prev_register_file.flags.overflow)
|
||||
S86_PrintFmt("O");
|
||||
|
||||
S86_PrintFmt("->");
|
||||
@@ -1516,33 +1868,37 @@ int main(int argc, char **argv)
|
||||
S86_PrintFmt("O");
|
||||
S86_PrintFmt(" ");
|
||||
}
|
||||
|
||||
S86_Print(S86_STR8("\n"));
|
||||
}
|
||||
|
||||
if (exec_mode) {
|
||||
S86_PrintLn(S86_STR8("\nFinal registers:"));
|
||||
if (register_file.ax.word)
|
||||
S86_PrintLnFmt(" ax: 0x%04x (%u)", register_file.ax.word, register_file.ax.word);
|
||||
if (register_file.bx.word)
|
||||
S86_PrintLnFmt(" bx: 0x%04x (%u)", register_file.bx.word, register_file.bx.word);
|
||||
if (register_file.cx.word)
|
||||
S86_PrintLnFmt(" cx: 0x%04x (%u)", register_file.cx.word, register_file.cx.word);
|
||||
if (register_file.dx.word)
|
||||
S86_PrintLnFmt(" dx: 0x%04x (%u)", register_file.dx.word, register_file.dx.word);
|
||||
if (register_file.sp.word)
|
||||
S86_PrintLnFmt(" sp: 0x%04x (%u)", register_file.sp.word, register_file.sp.word);
|
||||
if (register_file.bp.word)
|
||||
S86_PrintLnFmt(" bp: 0x%04x (%u)", register_file.bp.word, register_file.bp.word);
|
||||
if (register_file.si.word)
|
||||
S86_PrintLnFmt(" si: 0x%04x (%u)", register_file.si.word, register_file.si.word);
|
||||
if (register_file.di.word)
|
||||
S86_PrintLnFmt(" di: 0x%04x (%u)", register_file.di.word, register_file.di.word);
|
||||
if (register_file.es.word)
|
||||
S86_PrintLnFmt(" es: 0x%04x (%u)", register_file.es, register_file.es);
|
||||
if (register_file.ss.word)
|
||||
S86_PrintLnFmt(" ss: 0x%04x (%u)", register_file.ss, register_file.ss);
|
||||
if (register_file.ds.word)
|
||||
S86_PrintLnFmt(" ds: 0x%04x (%u)", register_file.ds, register_file.ds);
|
||||
if (register_file.reg.file.ax.word)
|
||||
S86_PrintLnFmt(" ax: 0x%04x (%u)", register_file.reg.file.ax.word, register_file.reg.file.ax.word);
|
||||
if (register_file.reg.file.bx.word)
|
||||
S86_PrintLnFmt(" bx: 0x%04x (%u)", register_file.reg.file.bx.word, register_file.reg.file.bx.word);
|
||||
if (register_file.reg.file.cx.word)
|
||||
S86_PrintLnFmt(" cx: 0x%04x (%u)", register_file.reg.file.cx.word, register_file.reg.file.cx.word);
|
||||
if (register_file.reg.file.dx.word)
|
||||
S86_PrintLnFmt(" dx: 0x%04x (%u)", register_file.reg.file.dx.word, register_file.reg.file.dx.word);
|
||||
if (register_file.reg.file.sp.word)
|
||||
S86_PrintLnFmt(" sp: 0x%04x (%u)", register_file.reg.file.sp.word, register_file.reg.file.sp.word);
|
||||
if (register_file.reg.file.bp.word)
|
||||
S86_PrintLnFmt(" bp: 0x%04x (%u)", register_file.reg.file.bp.word, register_file.reg.file.bp.word);
|
||||
if (register_file.reg.file.si.word)
|
||||
S86_PrintLnFmt(" si: 0x%04x (%u)", register_file.reg.file.si.word, register_file.reg.file.si.word);
|
||||
if (register_file.reg.file.di.word)
|
||||
S86_PrintLnFmt(" di: 0x%04x (%u)", register_file.reg.file.di.word, register_file.reg.file.di.word);
|
||||
if (register_file.reg.file.es.word)
|
||||
S86_PrintLnFmt(" es: 0x%04x (%u)", register_file.reg.file.es.word, register_file.reg.file.es.word);
|
||||
if (register_file.reg.file.ss.word)
|
||||
S86_PrintLnFmt(" ss: 0x%04x (%u)", register_file.reg.file.ss.word, register_file.reg.file.ss.word);
|
||||
if (register_file.reg.file.ds.word)
|
||||
S86_PrintLnFmt(" ds: 0x%04x (%u)", register_file.reg.file.ds.word, register_file.reg.file.ds.word);
|
||||
|
||||
if (log_instruction_ptr)
|
||||
S86_PrintLnFmt(" ip: 0x%04x (%u)", register_file.instruction_ptr, register_file.instruction_ptr);
|
||||
|
||||
S86_RegisterFileFlags nil_flags = {0};
|
||||
if (!S86_RegisterFileFlagsEq(register_file.flags, nil_flags)) {
|
||||
@@ -1563,4 +1919,10 @@ int main(int argc, char **argv)
|
||||
}
|
||||
S86_Print(S86_STR8("\n"));
|
||||
}
|
||||
|
||||
if (dump) {
|
||||
char buf[1024];
|
||||
snprintf(buf, sizeof(buf), "%s_mem_dump.data", file_name);
|
||||
S86_FileWrite(buf, memory, S86_MEMORY_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
+87
-13
@@ -307,6 +307,7 @@ typedef enum S86_MnemonicOp {
|
||||
|
||||
S86_MnemonicOp_DirectInterSegment,
|
||||
S86_MnemonicOp_Jump,
|
||||
S86_MnemonicOp_Count,
|
||||
} S86_MnemonicOp;
|
||||
|
||||
typedef enum S86_EffectiveAddress {
|
||||
@@ -315,32 +316,105 @@ typedef enum S86_EffectiveAddress {
|
||||
S86_EffectiveAddress_Dest,
|
||||
} S86_EffectiveAddress;
|
||||
|
||||
typedef enum S86_WidePrefix {
|
||||
S86_WidePrefix_None,
|
||||
S86_WidePrefix_Src,
|
||||
S86_WidePrefix_Dest,
|
||||
} S86_WidePrefix;
|
||||
typedef enum S86_WordBytePrefix {
|
||||
S86_WordBytePrefix_None,
|
||||
S86_WordBytePrefix_Byte,
|
||||
S86_WordBytePrefix_Word,
|
||||
} S86_WordBytePrefix;
|
||||
|
||||
typedef struct S86_Opcode {
|
||||
S86_OpDecodeType type;
|
||||
uint8_t byte_size; ///< Number of bytes used to encode this opcode
|
||||
S86_Mnemonic mnemonic; ///< Mnemonic type
|
||||
S86_EffectiveAddress effective_addr; ///< Src/dest op is an effective address calculation
|
||||
bool effective_addr_loads_mem; ///< Effective address uses '[]' notation to load address memory
|
||||
bool lock_prefix; ///< Prefix the opcode with "lock" instruction
|
||||
bool rep_prefix; ///< Prefix the opcode with "rep" instruction
|
||||
bool wide; ///< Opcode has the 'w' flag set
|
||||
S86_WidePrefix wide_prefix; ///< Mnemonic src/dest op requires a 'word' or 'byte' prefix (e.g. ambiguous immediate size)
|
||||
S86_WordBytePrefix word_byte_prefix; ///< Opcode has the 'word' or 'byte' prefix
|
||||
S86_MnemonicOp src; ///< Source op for the mnemonic
|
||||
S86_MnemonicOp dest; ///< Destination op for the mnemonic
|
||||
int32_t displacement; ///< Opcode has displacement/data/offset
|
||||
int32_t immediate; ///< Immediate value when src/dest op is an immediate
|
||||
bool immediate_is_8bit; ///< Immediate was 8bit and sign extended
|
||||
S86_MnemonicOp seg_reg_prefix; ///< Segment register that should prefix the upcoming instruction
|
||||
uint32_t base_clocks; ///< Number of cycles required to complete this operation
|
||||
uint32_t effective_address_clocks; ///< Number of cycles required to complete this operation
|
||||
uint32_t transfer_penalty_clocks; ///< Number of extra cycles required to complete a word transfer
|
||||
} S86_Opcode;
|
||||
|
||||
S86_Str8 S86_MnemonicStr8 (S86_Mnemonic type);
|
||||
S86_MnemonicOp S86_MnemonicOpFromWReg (bool w, uint8_t reg);
|
||||
S86_MnemonicOp S86_MnemonicOpFromSR (uint8_t sr);
|
||||
S86_Str8 S86_MnemonicOpStr8 (S86_MnemonicOp type);
|
||||
void S86_PrintOpcodeMnemonicOp(S86_Opcode opcode, bool src);
|
||||
void S86_PrintOpcode (S86_Opcode opcode);
|
||||
void S86_DecodeEffectiveAddr (S86_Opcode *opcode, S86_BufferIterator *it, uint8_t rm, uint8_t mod, uint8_t w);
|
||||
typedef enum S86_RegisterByte {
|
||||
S86_RegisterByte_Lo,
|
||||
S86_RegisterByte_Hi,
|
||||
S86_RegisterByte_Count,
|
||||
S86_RegisterByte_Nil,
|
||||
} S86_RegisterByte;
|
||||
|
||||
typedef union S86_Register16 {
|
||||
uint16_t word;
|
||||
uint8_t bytes[S86_RegisterByte_Count];
|
||||
} S86_Register16;
|
||||
|
||||
typedef struct S86_RegisterFileFlags {
|
||||
bool carry;
|
||||
bool zero;
|
||||
bool sign;
|
||||
bool overflow;
|
||||
bool parity;
|
||||
bool auxiliary_carry;
|
||||
} S86_RegisterFileFlags;
|
||||
|
||||
typedef enum S86_RegisterFileRegArray {
|
||||
S86_RegisterFileRegArray_AX,
|
||||
S86_RegisterFileRegArray_BX,
|
||||
S86_RegisterFileRegArray_CX,
|
||||
S86_RegisterFileRegArray_DX,
|
||||
|
||||
S86_RegisterFileRegArray_SP,
|
||||
S86_RegisterFileRegArray_BP,
|
||||
S86_RegisterFileRegArray_SI,
|
||||
S86_RegisterFileRegArray_DI,
|
||||
|
||||
S86_RegisterFileRegArray_ES,
|
||||
S86_RegisterFileRegArray_CS,
|
||||
S86_RegisterFileRegArray_SS,
|
||||
S86_RegisterFileRegArray_DS,
|
||||
S86_RegisterFileRegArray_Count,
|
||||
} S86_RegisterFileRegArray;
|
||||
|
||||
typedef struct S86_RegisterFile {
|
||||
S86_RegisterFileFlags flags;
|
||||
uint16_t instruction_ptr;
|
||||
|
||||
union {
|
||||
struct {
|
||||
S86_Register16 ax;
|
||||
S86_Register16 bx;
|
||||
S86_Register16 cx;
|
||||
S86_Register16 dx;
|
||||
|
||||
S86_Register16 sp;
|
||||
S86_Register16 bp;
|
||||
S86_Register16 si;
|
||||
S86_Register16 di;
|
||||
|
||||
S86_Register16 es;
|
||||
S86_Register16 cs;
|
||||
S86_Register16 ss;
|
||||
S86_Register16 ds;
|
||||
} file;
|
||||
S86_Register16 array[S86_RegisterFileRegArray_Count];
|
||||
} reg;
|
||||
} S86_RegisterFile;
|
||||
|
||||
bool S86_RegisterFileFlagsEq (S86_RegisterFileFlags lhs, S86_RegisterFileFlags rhs);
|
||||
S86_Str8 S86_MnemonicStr8 (S86_Mnemonic type);
|
||||
S86_MnemonicOp S86_MnemonicOpFromWReg (bool w, uint8_t reg);
|
||||
S86_MnemonicOp S86_MnemonicOpFromSR (uint8_t sr);
|
||||
S86_Str8 S86_MnemonicOpStr8 (S86_MnemonicOp type);
|
||||
bool S86_MnemonicOpIsAccumulator (S86_MnemonicOp type);
|
||||
bool S86_MnemonicOpIsRegister (S86_MnemonicOp type);
|
||||
S86_Str8 S86_RegisterFileRegArrayStr8(S86_RegisterFileRegArray type);
|
||||
void S86_PrintOpcodeMnemonicOp (S86_Opcode opcode, bool src);
|
||||
void S86_PrintOpcode (S86_Opcode opcode);
|
||||
void S86_DecodeEffectiveAddr (S86_Opcode *opcode, S86_BufferIterator *it, uint8_t rm, uint8_t mod, uint8_t w);
|
||||
|
||||
@@ -111,6 +111,42 @@ void S86_FileFree(S86_Buffer buffer)
|
||||
VirtualFree(buffer.data, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
bool S86_FileWrite(char const *file_path, void const *buffer, size_t buffer_size)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// NOTE: Open file
|
||||
// =========================================================================
|
||||
HANDLE file_handle = CreateFile(
|
||||
/*LPCSTR lpFileName*/ file_path,
|
||||
/*DWORD dwDesiredAccess*/ GENERIC_WRITE,
|
||||
/*DWORD dwShareMode*/ 0,
|
||||
/*LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ NULL,
|
||||
/*DWORD dwCreationDisposition*/ CREATE_ALWAYS,
|
||||
/*DWORD dwFlagsAndAttributes*/ 0,
|
||||
/*HANDLE hTemplateFile*/ NULL
|
||||
);
|
||||
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
return result;
|
||||
|
||||
// NOTE: Write file to disk
|
||||
// =========================================================================
|
||||
DWORD bytes_written = 0;
|
||||
BOOL write_file_result = WriteFile(
|
||||
/*HANDLE hFile*/ file_handle,
|
||||
/*LPVOID lpBuffer*/ buffer,
|
||||
/*DWORD nNumberOfBytesToWrite*/ S86_CAST(DWORD)buffer_size,
|
||||
/*LPDWORD lpNumberOfBytesWrite*/ &bytes_written,
|
||||
/*LPOVERLAPPED lpOverlapped*/ NULL
|
||||
);
|
||||
|
||||
S86_ASSERT(bytes_written == buffer_size);
|
||||
result = write_file_result && bytes_written == buffer_size;
|
||||
CloseHandle(file_handle);
|
||||
return result;
|
||||
};
|
||||
|
||||
void S86_Print(S86_Str8 string)
|
||||
{
|
||||
if (s86_globals.stdout_handle == NULL) {
|
||||
|
||||
@@ -53,6 +53,7 @@ uint8_t S86_BufferIteratorNextByte(S86_BufferIterator *it);
|
||||
// ============================================================================
|
||||
S86_Buffer S86_FileRead(char const *file_path);
|
||||
void S86_FileFree(S86_Buffer buffer);
|
||||
bool S86_FileWrite(char const *file_path, void const *buffer, size_t buffer_size);
|
||||
|
||||
// NOTE: Print
|
||||
// ============================================================================
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <Windows.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "haversine_stdlib.h"
|
||||
#include "listing_0065_haversine_formula.cpp"
|
||||
#include "listing_0074_platform_metrics.cpp"
|
||||
#include "haversine_stdlib.c"
|
||||
|
||||
typedef struct Str8FindResult {
|
||||
bool found;
|
||||
HAV_Str8 match;
|
||||
HAV_Str8 match_to_end_of_buffer;
|
||||
} Str8FindResult;
|
||||
|
||||
static Str8FindResult FindFirstCharThatLooksLikeANumber(HAV_Str8 buffer)
|
||||
{
|
||||
Str8FindResult result = {0};
|
||||
for (size_t index = 0; !result.found && index < buffer.size; index++) {
|
||||
if (HAV_CharIsDigit(buffer.data[index]) || buffer.data[index] == '+' || buffer.data[index] == '-') {
|
||||
result.found = true;
|
||||
result.match = (HAV_Str8){.data = buffer.data + index, .size = 1};
|
||||
result.match_to_end_of_buffer = (HAV_Str8){.data = result.match.data, .size = buffer.size - index};
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static f64 StringToF64(HAV_Str8 value)
|
||||
{
|
||||
f64 result = 0.f;
|
||||
Str8FindResult find_result = FindFirstCharThatLooksLikeANumber(value);
|
||||
if (!find_result.found)
|
||||
return result;
|
||||
|
||||
bool negative = false;
|
||||
HAV_Str8 real_number_string = find_result.match_to_end_of_buffer;
|
||||
if (find_result.match.data[0] == '+' || find_result.match.data[0] == '-') {
|
||||
negative = find_result.match.data[0] == '-';
|
||||
real_number_string.data++;
|
||||
real_number_string.size--;
|
||||
}
|
||||
|
||||
HAV_Str8BinarySplitResult number_split = HAV_Str8_BinarySplit(real_number_string, HAV_STR8("."));
|
||||
HAV_Str8 integer_part = number_split.lhs;
|
||||
HAV_Str8 decimal_part = number_split.rhs;
|
||||
|
||||
uint64_t integer_part_as_u64 = 0;
|
||||
for (size_t index = 0; index < integer_part.size; index++) {
|
||||
integer_part_as_u64 *= 10;
|
||||
integer_part_as_u64 += integer_part.data[index] - '0';
|
||||
}
|
||||
|
||||
uint64_t decimal_part_as_u64 = 0;
|
||||
uint64_t decimal_magnitude = 1;
|
||||
for (size_t index = 0; index < decimal_part.size; index++) {
|
||||
decimal_part_as_u64 *= 10;
|
||||
decimal_part_as_u64 += decimal_part.data[index] - '0';
|
||||
decimal_magnitude *= 10;
|
||||
}
|
||||
|
||||
typedef union FU64 {
|
||||
f64 f64_value;
|
||||
uint64_t u64_value;
|
||||
} FU64;
|
||||
|
||||
FU64 fu64 = {0};
|
||||
fu64.f64_value = HAV_CAST(f64)integer_part_as_u64 + (HAV_CAST(f64)decimal_part_as_u64 / decimal_magnitude);
|
||||
if (negative) {
|
||||
fu64.u64_value |= (1ULL << 63);
|
||||
}
|
||||
return fu64.f64_value;
|
||||
}
|
||||
|
||||
#define PRINT_USAGE HAV_PrintLnFmt("Usage: %s [haversine_input.json] [answers.f64]", argv[0])
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// NOTE: Arg Parsing
|
||||
// =========================================================================
|
||||
if (argc != 2 && argc != 3) {
|
||||
PRINT_USAGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_profiler.begin_tsc = ReadCPUTimer();
|
||||
HAV_Str8 arg_json = {argv[1], strlen(argv[1])};
|
||||
HAV_Str8 arg_answers = {0};
|
||||
if (argc == 3)
|
||||
arg_answers = (HAV_Str8){.data = argv[2], .size = strlen(argv[2])};
|
||||
|
||||
HAV_Buffer json_buffer = HAV_FileRead(arg_json.data);
|
||||
if (!HAV_BufferIsValid(json_buffer))
|
||||
return 0;
|
||||
|
||||
HAV_ProfilerZone prof_parse_and_sum_zone = HAV_Profiler_BeginZone("Parse&Hav Sum");
|
||||
f64 haversine_sum = 0;
|
||||
size_t pair_count = 0;
|
||||
HAV_Str8 json_it = (HAV_Str8){.data = json_buffer.data, .size = json_buffer.size};
|
||||
for (;; pair_count++) {
|
||||
f64 x0 = 0.f, y0 = 0.f, x1 = 0.f, y1 = 0.f;
|
||||
HAV_ProfilerZone prof_json_parse_zone = HAV_Profiler_BeginZoneBandwidth("Parse", json_it.size);
|
||||
HAV_Str8BinarySplitResult x0_key = HAV_Str8_BinarySplit(json_it, HAV_STR8("x0"));
|
||||
if (x0_key.rhs.size) {
|
||||
Str8FindResult x0_find_value = FindFirstCharThatLooksLikeANumber(x0_key.rhs);
|
||||
HAV_Str8BinarySplitResult x0_value = HAV_Str8_BinarySplit(x0_find_value.match_to_end_of_buffer, HAV_STR8(","));
|
||||
|
||||
HAV_Str8BinarySplitResult y0_key = HAV_Str8_BinarySplit(x0_value.rhs, HAV_STR8("y0"));
|
||||
Str8FindResult y0_find_value = FindFirstCharThatLooksLikeANumber(y0_key.rhs);
|
||||
HAV_Str8BinarySplitResult y0_value = HAV_Str8_BinarySplit(y0_find_value.match_to_end_of_buffer, HAV_STR8(","));
|
||||
|
||||
HAV_Str8BinarySplitResult x1_key = HAV_Str8_BinarySplit(y0_value.rhs, HAV_STR8("x1"));
|
||||
Str8FindResult x1_find_value = FindFirstCharThatLooksLikeANumber(x1_key.rhs);
|
||||
HAV_Str8BinarySplitResult x1_value = HAV_Str8_BinarySplit(x1_find_value.match_to_end_of_buffer, HAV_STR8(","));
|
||||
|
||||
HAV_Str8BinarySplitResult y1_key = HAV_Str8_BinarySplit(x1_value.rhs, HAV_STR8("y1"));
|
||||
Str8FindResult y1_find_value = FindFirstCharThatLooksLikeANumber(y1_key.rhs);
|
||||
HAV_Str8BinarySplitResult y1_value = HAV_Str8_BinarySplit(y1_find_value.match_to_end_of_buffer, HAV_STR8("}"));
|
||||
|
||||
x0 = StringToF64(x0_value.lhs);
|
||||
y0 = StringToF64(y0_value.lhs);
|
||||
x1 = StringToF64(x1_value.lhs);
|
||||
y1 = StringToF64(y1_value.lhs);
|
||||
|
||||
json_it = y1_value.rhs;
|
||||
}
|
||||
#if 0
|
||||
HAV_PrintLnFmt("{x0: %.*s (%f), y0: %.*s (%f), x1 %.*s (%f), y1: %.*s (%f)}",
|
||||
HAV_STR8_FMT(x0_value.lhs), x0,
|
||||
HAV_STR8_FMT(y0_value.lhs), y0,
|
||||
HAV_STR8_FMT(x1_value.lhs), x1,
|
||||
HAV_STR8_FMT(y1_value.lhs), y1);
|
||||
#endif
|
||||
|
||||
HAV_Profiler_EndZone(prof_json_parse_zone);
|
||||
if (!x0_key.rhs.size)
|
||||
break;
|
||||
|
||||
HAV_ProfilerZone prof_haversine_sum_zone = HAV_Profiler_BeginZoneBandwidth("Hav Sum", sizeof(x0) + sizeof(y0) + sizeof(x1) + sizeof(y1));
|
||||
f64 haversine_dist = ReferenceHaversine(x0, y0, x1, y1, /*EarthRadius*/ 6372.8);
|
||||
haversine_sum += haversine_dist;
|
||||
HAV_Profiler_EndZone(prof_haversine_sum_zone);
|
||||
}
|
||||
HAV_Profiler_EndZone(prof_parse_and_sum_zone);
|
||||
|
||||
haversine_sum /= pair_count;
|
||||
size_t input_size = json_buffer.size;
|
||||
HAV_PrintLnFmt("Input size: %zu", input_size);
|
||||
HAV_PrintLnFmt("Pair count: %zu", pair_count);
|
||||
HAV_PrintLnFmt("Haversine sum: %f", haversine_sum);
|
||||
|
||||
if (arg_answers.size) {
|
||||
HAV_Buffer answers_buffer = HAV_FileRead(arg_answers.data);
|
||||
if (HAV_BufferIsValid(answers_buffer)) {
|
||||
HAV_ASSERT(answers_buffer.size == (pair_count * sizeof(f64)) + /*Reference Sum*/ sizeof(f64));
|
||||
|
||||
f64 reference_haversine_sum = 0;
|
||||
memcpy(&reference_haversine_sum, answers_buffer.data + (pair_count * sizeof(f64)), sizeof(reference_haversine_sum));
|
||||
|
||||
f64 difference = reference_haversine_sum - haversine_sum;
|
||||
HAV_PrintLn(HAV_STR8("\nValidation: "));
|
||||
HAV_PrintLnFmt("Reference sum: %f", reference_haversine_sum);
|
||||
HAV_PrintLnFmt("Difference: %f", difference);
|
||||
}
|
||||
}
|
||||
|
||||
g_profiler.end_tsc = ReadCPUTimer();
|
||||
HAV_Profiler_Dump();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <Windows.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "haversine_stdlib.h"
|
||||
#include "listing_0074_platform_metrics.cpp"
|
||||
#include "listing_0065_haversine_formula.cpp"
|
||||
#include "haversine_stdlib.c"
|
||||
|
||||
#define PRINT_USAGE HAV_PrintLnFmt("Usage: %s [uniform/cluster] [random seed] [number of coordinate pairs to generate]", argv[0])
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// NOTE: Unit Tests
|
||||
// =========================================================================
|
||||
{
|
||||
{
|
||||
HAV_Str8ToU64Result result = HAV_Str8_ToU64(HAV_STR8("00"));
|
||||
HAV_ASSERT(result.success);
|
||||
HAV_ASSERT(result.value == 0);
|
||||
}
|
||||
|
||||
{
|
||||
HAV_Str8ToU64Result result = HAV_Str8_ToU64(HAV_STR8("+100"));
|
||||
HAV_ASSERT(!result.success);
|
||||
}
|
||||
|
||||
{
|
||||
HAV_Str8ToU64Result result = HAV_Str8_ToU64(HAV_STR8("100,0"));
|
||||
HAV_ASSERT(!result.success);
|
||||
}
|
||||
|
||||
{
|
||||
HAV_Str8ToU64Result result = HAV_Str8_ToU64(HAV_STR8("100a"));
|
||||
HAV_ASSERT(!result.success);
|
||||
}
|
||||
|
||||
{
|
||||
HAV_Str8ToU64Result result = HAV_Str8_ToU64(HAV_STR8("3147"));
|
||||
HAV_ASSERT(result.success);
|
||||
HAV_ASSERT(result.value == 3147);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Arg Parsing
|
||||
// =========================================================================
|
||||
if (argc != 4) {
|
||||
PRINT_USAGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
HAV_Str8 arg_uniform_cluster = {argv[1], strlen(argv[1])};
|
||||
HAV_Str8 arg_random_seed = {argv[2], strlen(argv[2])};
|
||||
HAV_Str8 arg_number_of_coordinate_pairs_to_generate = {argv[3], strlen(argv[3])};
|
||||
|
||||
typedef enum PointGenerator {
|
||||
PointGenerator_Invalid,
|
||||
PointGenerator_Uniform,
|
||||
PointGenerator_Cluster,
|
||||
} PointGenerator;
|
||||
|
||||
HAV_Str8ToU64Result random_seed_u64_result = HAV_Str8_ToU64(arg_random_seed);
|
||||
HAV_Str8ToU64Result number_of_coordinate_pairs_to_generate_u64_result = HAV_Str8_ToU64(arg_number_of_coordinate_pairs_to_generate);
|
||||
PointGenerator point_generator = PointGenerator_Invalid;
|
||||
|
||||
if (HAV_Str8_Equals(arg_uniform_cluster, HAV_STR8("uniform"))) {
|
||||
point_generator = PointGenerator_Uniform;
|
||||
} else if (HAV_Str8_Equals(arg_uniform_cluster, HAV_STR8("cluster"))) {
|
||||
point_generator = PointGenerator_Cluster;
|
||||
} else {
|
||||
PRINT_USAGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!random_seed_u64_result.success) {
|
||||
HAV_PrintLnFmt("Random seed was not a valid U64 value [seed=%.*s]", HAV_STR8_FMT(arg_random_seed));
|
||||
PRINT_USAGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!number_of_coordinate_pairs_to_generate_u64_result.success) {
|
||||
HAV_PrintLnFmt("Number of coordinate pairs to generate was not a valid U64 value [seed=%.*s]", HAV_STR8_FMT(arg_number_of_coordinate_pairs_to_generate));
|
||||
PRINT_USAGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t const MAX_COORD_PAIRS = 100'000'000;
|
||||
if (number_of_coordinate_pairs_to_generate_u64_result.value > MAX_COORD_PAIRS) {
|
||||
HAV_PrintLnFmt("Maximum number of coordinate pairs exceeded, exiting to avoid accidental large files [requested=%zu, max=%zu]",
|
||||
number_of_coordinate_pairs_to_generate_u64_result.value,
|
||||
MAX_COORD_PAIRS);
|
||||
PRINT_USAGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// NOTE: Generator
|
||||
// =========================================================================
|
||||
uint64_t point_count = number_of_coordinate_pairs_to_generate_u64_result.value;
|
||||
uint64_t random_seed = random_seed_u64_result.value;
|
||||
uint64_t rng_state = random_seed;
|
||||
|
||||
char json_file_name[1024];
|
||||
char answers_file_name[1024];
|
||||
snprintf(json_file_name, sizeof(json_file_name), "haversine_%zu_%s_%zu.json", point_count, point_generator == PointGenerator_Cluster ? "cluster" : "uniform", random_seed);
|
||||
snprintf(answers_file_name, sizeof(json_file_name), "haversine_%zu_%s_%zu.f64", point_count, point_generator == PointGenerator_Cluster ? "cluster" : "uniform", random_seed);
|
||||
|
||||
HANDLE haversine_json_file_handle = CreateFile(
|
||||
/*LPCSTR lpFileName*/ json_file_name,
|
||||
/*DWORD dwDesiredAccess*/ GENERIC_WRITE,
|
||||
/*DWORD dwShareMode*/ 0,
|
||||
/*LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ NULL,
|
||||
/*DWORD dwCreationDisposition*/ CREATE_ALWAYS,
|
||||
/*DWORD dwFlagsAndAttributes*/ 0,
|
||||
/*HANDLE hTemplateFile*/ NULL
|
||||
);
|
||||
|
||||
HANDLE haversine_f64_file_handle = CreateFile(
|
||||
/*LPCSTR lpFileName*/ answers_file_name,
|
||||
/*DWORD dwDesiredAccess*/ GENERIC_WRITE,
|
||||
/*DWORD dwShareMode*/ 0,
|
||||
/*LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ NULL,
|
||||
/*DWORD dwCreationDisposition*/ CREATE_ALWAYS,
|
||||
/*DWORD dwFlagsAndAttributes*/ 0,
|
||||
/*HANDLE hTemplateFile*/ NULL
|
||||
);
|
||||
|
||||
f64 const LAT_LON_MIN = -180.0;
|
||||
f64 const LAT_LON_MAX = 180.0;
|
||||
|
||||
uint64_t const MAX_CLUSTERS = 64;
|
||||
uint64_t const points_per_cluster = point_count / MAX_CLUSTERS;
|
||||
|
||||
char tmp_buffer[128];
|
||||
f64 expected_sum = 0;
|
||||
HAV_PrintHandle(haversine_json_file_handle, HAV_STR8("{\"pairs\":[\n"));
|
||||
|
||||
f64 const sum_coefficient = 1.0 / point_count;
|
||||
f64 point_centre = 0;
|
||||
f64 point_min_offset = LAT_LON_MIN;
|
||||
f64 point_max_offset = LAT_LON_MAX;
|
||||
for (int index = 0; index < point_count; index++) {
|
||||
if (point_generator == PointGenerator_Cluster && (index % points_per_cluster) == 0) {
|
||||
point_centre = HAV_PCG32_PieF64(&rng_state, LAT_LON_MIN, LAT_LON_MAX);
|
||||
point_min_offset = -HAV_PCG32_PieF64(&rng_state, 0, 45);
|
||||
point_max_offset = -point_min_offset;
|
||||
}
|
||||
|
||||
f64 x0 = point_centre + HAV_PCG32_PieF64(&rng_state, point_min_offset, point_max_offset);
|
||||
f64 y0 = point_centre + HAV_PCG32_PieF64(&rng_state, point_min_offset, point_max_offset);
|
||||
f64 x1 = point_centre + HAV_PCG32_PieF64(&rng_state, point_min_offset, point_max_offset);
|
||||
f64 y1 = point_centre + HAV_PCG32_PieF64(&rng_state, point_min_offset, point_max_offset);
|
||||
|
||||
f64 haversine_dist = ReferenceHaversine(x0, y0, x1, y1, /*EarthRadius*/ 6372.8);
|
||||
HAV_PrintHandle(haversine_f64_file_handle, (HAV_Str8){.data = (char *)&haversine_dist, .size = sizeof(haversine_dist)});
|
||||
expected_sum += (sum_coefficient * haversine_dist);
|
||||
|
||||
size_t json_line_size = snprintf(tmp_buffer, sizeof(tmp_buffer), " {\"x0\": %f, \"y0\": %f, \"x1\": %f, \"y1\": %f}%s\n", x0, y0, x1, y1, (index == (point_count - 1) ? "" : ","));
|
||||
HAV_ASSERT(json_line_size < sizeof(tmp_buffer));
|
||||
HAV_PrintHandle(haversine_json_file_handle, (HAV_Str8){.data = tmp_buffer, .size = json_line_size});
|
||||
}
|
||||
HAV_PrintHandle(haversine_json_file_handle, HAV_STR8("]}\n"));
|
||||
HAV_PrintHandle(haversine_f64_file_handle, (HAV_Str8){.data = (char *)&expected_sum, .size = sizeof(expected_sum)});
|
||||
|
||||
CloseHandle(haversine_json_file_handle);
|
||||
CloseHandle(haversine_f64_file_handle);
|
||||
|
||||
HAV_PrintLnFmt("Method: %s", (point_generator == PointGenerator_Uniform ? "uniform" : "cluster"));
|
||||
HAV_PrintLnFmt("Seed: %zu", random_seed);
|
||||
HAV_PrintLnFmt("Pair Count: %zu", point_count);
|
||||
HAV_PrintLnFmt("Expected Sum: %f", expected_sum);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
// NOTE: Implementation
|
||||
// ============================================================================
|
||||
bool HAV_Str8_Equals(HAV_Str8 lhs, HAV_Str8 rhs)
|
||||
{
|
||||
bool result = lhs.size == rhs.size && memcmp(lhs.data, rhs.data, lhs.size) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
HAV_Str8ToU64Result HAV_Str8_ToU64(HAV_Str8 string)
|
||||
{
|
||||
HAV_Str8ToU64Result result = {0};
|
||||
|
||||
size_t ch_index = 0;
|
||||
while (ch_index < string.size && HAV_CharIsWhiteSpace(string.data[ch_index]))
|
||||
ch_index++;
|
||||
|
||||
for (; ch_index < string.size; ch_index++) {
|
||||
char ch = string.data[ch_index];
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
result.value = (result.value * 10) + (ch - '0');
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
HAV_Str8BinarySplitResult HAV_Str8_BinarySplit(HAV_Str8 buffer, HAV_Str8 find)
|
||||
{
|
||||
HAV_Str8BinarySplitResult result = {0};
|
||||
result.lhs = buffer;
|
||||
for (size_t index = 0; (index + find.size) <= buffer.size; index++) {
|
||||
HAV_Str8 check = {buffer.data + index, find.size};
|
||||
if (HAV_Str8_Equals(find, check)) {
|
||||
result.lhs.size = index;
|
||||
result.rhs.data = check.data + find.size;
|
||||
result.rhs.size = buffer.size - (index + find.size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HAV_CharIsWhiteSpace(char ch)
|
||||
{
|
||||
bool result = ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HAV_CharIsDigit(char ch)
|
||||
{
|
||||
bool result = ch >= '0' && ch <= '9';
|
||||
return result;
|
||||
}
|
||||
|
||||
void HAV_Profiler_Dump()
|
||||
{
|
||||
u64 total_elapsed_tsc = g_profiler.end_tsc - g_profiler.begin_tsc;
|
||||
u64 cpu_frequency = EstimateCPUTimerFreq();
|
||||
if (cpu_frequency)
|
||||
printf("\nTotal time: %0.4fms (CPU freq %llu)\n", 1000.0 * (f64)total_elapsed_tsc / (f64)cpu_frequency, cpu_frequency);
|
||||
|
||||
for (uint32_t index = 1; index < HAV_ARRAY_UCOUNT(g_profiler.anchors); index++) {
|
||||
HAV_ProfilerAnchor const *anchor = g_profiler.anchors + index;
|
||||
if (!anchor->elapsed_tsc_inclusive)
|
||||
break;
|
||||
|
||||
f64 percent = total_elapsed_tsc ? (f64)anchor->elapsed_tsc_exclusive / (f64)total_elapsed_tsc * 100.0 : 100.0;
|
||||
printf(" %.*s[%zu]: %llu (%.2f%%", HAV_STR8_FMT(anchor->label), anchor->hits, anchor->elapsed_tsc_exclusive, percent);
|
||||
if (anchor->elapsed_tsc_inclusive != anchor->elapsed_tsc_exclusive) {
|
||||
f64 percent_w_children = total_elapsed_tsc ? ((f64)anchor->elapsed_tsc_inclusive / (f64)total_elapsed_tsc * 100.0) : 100.0;
|
||||
printf(", %.2f%% w/children", percent_w_children);
|
||||
}
|
||||
printf(")");
|
||||
|
||||
if (anchor->byte_count) {
|
||||
f64 megabytes_processed = anchor->byte_count / (1024.f * 1024.f);
|
||||
f64 elapsed_s = anchor->elapsed_tsc_inclusive / HAV_CAST(f64)cpu_frequency;
|
||||
f64 bytes_per_s = anchor->byte_count / elapsed_s;
|
||||
f64 gigabytes_bandwidth = bytes_per_s / (1024.f * 1024.f * 1024.f);
|
||||
printf(" %.3fmb at %.2fgb/s", megabytes_processed, gigabytes_bandwidth);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
HAV_ProfilerZone HAV_Profiler_BeginZone_(HAV_Str8 label, uint32_t index, u64 byte_count)
|
||||
{
|
||||
HAV_ProfilerZone result = {0};
|
||||
#if defined(HAV_PROFILER)
|
||||
result.index = index;
|
||||
result.label = label;
|
||||
result.tsc = ReadCPUTimer();
|
||||
result.elapsed_tsc_inclusive = g_profiler.anchors[index].elapsed_tsc_inclusive;
|
||||
result.byte_count = byte_count;
|
||||
result.parent_index = g_profiler.parent_index;
|
||||
g_profiler.parent_index = index;
|
||||
#else
|
||||
(void)label; (void)index; (void)byte_count;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
void HAV_Profiler_EndZone(HAV_ProfilerZone zone)
|
||||
{
|
||||
#if defined(HAV_PROFILER)
|
||||
u64 elapsed_tsc = ReadCPUTimer() - zone.tsc;
|
||||
HAV_ProfilerAnchor* anchor = g_profiler.anchors + zone.index;
|
||||
HAV_ProfilerAnchor* parent = g_profiler.anchors + zone.parent_index;
|
||||
|
||||
anchor->elapsed_tsc_exclusive += elapsed_tsc;
|
||||
anchor->elapsed_tsc_inclusive = zone.elapsed_tsc_inclusive + elapsed_tsc;
|
||||
anchor->label = zone.label;
|
||||
anchor->byte_count += zone.byte_count;
|
||||
anchor->hits++;
|
||||
parent->elapsed_tsc_exclusive -= elapsed_tsc;
|
||||
g_profiler.parent_index = zone.parent_index;
|
||||
#else
|
||||
(void)zone;
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4146) // warning C4146: unary minus operator applied to unsigned type, result still unsigned
|
||||
uint32_t HAV_PCG32_Pie (uint64_t *state)
|
||||
{
|
||||
uint64_t old = *state ^ 0xc90fdaa2adf85459ULL;
|
||||
*state = *state * 6364136223846793005ULL + 0xc90fdaa2adf85459ULL;
|
||||
uint32_t xorshifted = (uint32_t)(((old >> 18u) ^ old) >> 27u);
|
||||
uint32_t rot = old >> 59u;
|
||||
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
f64 HAV_PCG32_PieF64(uint64_t *state, f64 min, f64 max)
|
||||
{
|
||||
uint32_t u32_value = HAV_PCG32_Pie(state);
|
||||
f64 t01 = HAV_CAST(f64)u32_value / HAV_CAST(f64)HAV_CAST(uint32_t)-1;
|
||||
f64 result = min + (max - min) * t01;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HAV_BufferIsValid(HAV_Buffer buffer)
|
||||
{
|
||||
bool result = buffer.data && buffer.size;
|
||||
return result;
|
||||
}
|
||||
|
||||
HAV_BufferIterator HAV_BufferIteratorInit(HAV_Buffer buffer)
|
||||
{
|
||||
HAV_BufferIterator result = {0};
|
||||
result.buffer = buffer;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HAV_BufferIteratorHasMoreBytes(HAV_BufferIterator it)
|
||||
{
|
||||
bool result = HAV_BufferIsValid(it.buffer) && it.index < it.buffer.size;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t HAV_BufferIteratorPeekByte(HAV_BufferIterator *it)
|
||||
{
|
||||
HAV_ASSERT(it);
|
||||
HAV_ASSERT(HAV_BufferIsValid(it->buffer));
|
||||
HAV_ASSERT(it->index < it->buffer.size);
|
||||
uint8_t result = it->buffer.data[it->index];
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t HAV_BufferIteratorNextByte(HAV_BufferIterator *it)
|
||||
{
|
||||
uint8_t result = HAV_BufferIteratorPeekByte(it);
|
||||
it->index++;
|
||||
return result;
|
||||
}
|
||||
|
||||
HAV_Buffer HAV_FileRead(char const *file_path)
|
||||
{
|
||||
HAV_Buffer result = {0};
|
||||
|
||||
// NOTE: Determine file size
|
||||
// =========================================================================
|
||||
WIN32_FILE_ATTRIBUTE_DATA file_attrib_data = {0};
|
||||
if (GetFileAttributesEx(file_path, GetFileExInfoStandard, &file_attrib_data) == 0)
|
||||
return result;
|
||||
|
||||
// NOTE: Open file
|
||||
// =========================================================================
|
||||
HANDLE file_handle = CreateFile(
|
||||
/*LPCSTR lpFileName*/ file_path,
|
||||
/*DWORD dwDesiredAccess*/ GENERIC_READ,
|
||||
/*DWORD dwShareMode*/ 0,
|
||||
/*LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ NULL,
|
||||
/*DWORD dwCreationDisposition*/ OPEN_EXISTING,
|
||||
/*DWORD dwFlagsAndAttributes*/ 0,
|
||||
/*HANDLE hTemplateFile*/ NULL
|
||||
);
|
||||
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
return result;
|
||||
|
||||
// NOTE: Allocate buffer
|
||||
// =========================================================================
|
||||
uint64_t file_size = (uint64_t)file_attrib_data.nFileSizeHigh << 32 | (uint64_t)file_attrib_data.nFileSizeLow << 0;
|
||||
HAV_ASSERT(file_size < (DWORD)-1);
|
||||
char *buffer = VirtualAlloc(
|
||||
/*LPVOID lpAddress*/ NULL,
|
||||
/*SIZE_T dwSize*/ file_size,
|
||||
/*DWORD flAllocationType*/ MEM_COMMIT | MEM_RESERVE,
|
||||
/*DWORD flProtect*/ PAGE_READWRITE
|
||||
);
|
||||
|
||||
if (!buffer)
|
||||
goto end;
|
||||
|
||||
// NOTE: Read file to buffer
|
||||
// =========================================================================
|
||||
DWORD bytes_read = 0;
|
||||
HAV_ProfilerZone prof_file_read_zone = HAV_Profiler_BeginZoneBandwidth("File Read", file_size);
|
||||
BOOL read_file_result = ReadFile(
|
||||
/*HANDLE hFile*/ file_handle,
|
||||
/*LPVOID lpBuffer*/ buffer,
|
||||
/*DWORD nNumberOfBytesToRead*/ HAV_CAST(DWORD)file_size,
|
||||
/*LPDWORD lpNumberOfBytesRead*/ &bytes_read,
|
||||
/*LPOVERLAPPED lpOverlapped*/ NULL
|
||||
);
|
||||
HAV_Profiler_EndZone(prof_file_read_zone);
|
||||
|
||||
// NOTE: Handle read result
|
||||
// =========================================================================
|
||||
if (read_file_result == 0) {
|
||||
VirtualFree(buffer, 0, MEM_RELEASE);
|
||||
} else {
|
||||
result.data = buffer;
|
||||
result.size = file_size;
|
||||
}
|
||||
|
||||
end:
|
||||
CloseHandle(file_handle);
|
||||
return result;
|
||||
};
|
||||
|
||||
void HAV_FileFree(HAV_Buffer buffer)
|
||||
{
|
||||
if (HAV_BufferIsValid(buffer))
|
||||
VirtualFree(buffer.data, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
bool HAV_FileWrite(char const *file_path, void const *buffer, size_t buffer_size)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// NOTE: Open file
|
||||
// =========================================================================
|
||||
HANDLE file_handle = CreateFile(
|
||||
/*LPCSTR lpFileName*/ file_path,
|
||||
/*DWORD dwDesiredAccess*/ GENERIC_WRITE,
|
||||
/*DWORD dwShareMode*/ 0,
|
||||
/*LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ NULL,
|
||||
/*DWORD dwCreationDisposition*/ CREATE_ALWAYS,
|
||||
/*DWORD dwFlagsAndAttributes*/ 0,
|
||||
/*HANDLE hTemplateFile*/ NULL
|
||||
);
|
||||
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
return result;
|
||||
|
||||
// NOTE: Write file to disk
|
||||
// =========================================================================
|
||||
DWORD bytes_written = 0;
|
||||
BOOL write_file_result = WriteFile(
|
||||
/*HANDLE hFile*/ file_handle,
|
||||
/*LPVOID lpBuffer*/ buffer,
|
||||
/*DWORD nNumberOfBytesToWrite*/ HAV_CAST(DWORD)buffer_size,
|
||||
/*LPDWORD lpNumberOfBytesWrite*/ &bytes_written,
|
||||
/*LPOVERLAPPED lpOverlapped*/ NULL
|
||||
);
|
||||
|
||||
HAV_ASSERT(bytes_written == buffer_size);
|
||||
result = write_file_result && bytes_written == buffer_size;
|
||||
CloseHandle(file_handle);
|
||||
return result;
|
||||
};
|
||||
|
||||
void HAV_PrintHandle(void *handle, HAV_Str8 string)
|
||||
{
|
||||
DWORD bytes_written = 0;
|
||||
WriteFile(handle, string.data, HAV_CAST(DWORD)string.size, &bytes_written, NULL);
|
||||
(void)bytes_written;
|
||||
}
|
||||
|
||||
void HAV_Print(HAV_Str8 string)
|
||||
{
|
||||
if (pap_globals.stdout_handle == NULL) {
|
||||
pap_globals.stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD mode = 0;
|
||||
BOOL get_console_mode_result = GetConsoleMode(
|
||||
/*HANDLE hConsoleHandle*/ pap_globals.stdout_handle,
|
||||
/*LPDWORD lpMode*/ &mode
|
||||
);
|
||||
pap_globals.write_to_console = get_console_mode_result != 0;
|
||||
}
|
||||
|
||||
|
||||
HAV_ASSERT(string.size < HAV_CAST(DWORD)-1);
|
||||
if (pap_globals.write_to_console) {
|
||||
DWORD chars_written = 0;
|
||||
WriteConsoleA(pap_globals.stdout_handle, string.data, (DWORD)string.size, &chars_written, NULL);
|
||||
} else {
|
||||
HAV_PrintHandle(pap_globals.stdout_handle, string);
|
||||
}
|
||||
}
|
||||
|
||||
void HAV_PrintFmt(char const *fmt, ...)
|
||||
{
|
||||
va_list args, args_copy;
|
||||
va_start(args, fmt);
|
||||
|
||||
va_copy(args_copy, args);
|
||||
int string_size = vsnprintf(NULL, 0, fmt, args_copy);
|
||||
va_end(args_copy);
|
||||
|
||||
char buffer[8192];
|
||||
HAV_ASSERT(string_size >= 0 && string_size < HAV_ARRAY_UCOUNT(buffer));
|
||||
if (string_size) {
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
HAV_Str8 string = {.data = buffer, .size = string_size};
|
||||
HAV_Print(string);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void HAV_PrintLn(HAV_Str8 string)
|
||||
{
|
||||
HAV_Print(string);
|
||||
HAV_Print(HAV_STR8("\n"));
|
||||
}
|
||||
|
||||
void HAV_PrintLnFmt(char const *fmt, ...)
|
||||
{
|
||||
va_list args, args_copy;
|
||||
va_start(args, fmt);
|
||||
|
||||
va_copy(args_copy, args);
|
||||
int string_size = vsnprintf(NULL, 0, fmt, args_copy);
|
||||
va_end(args_copy);
|
||||
|
||||
char buffer[8192];
|
||||
HAV_ASSERT(string_size >= 0 && string_size < HAV_ARRAY_UCOUNT(buffer));
|
||||
if (string_size) {
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
HAV_Str8 string = {.data = buffer, .size = string_size};
|
||||
HAV_PrintLn(string);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
#include <stdint.h>
|
||||
|
||||
// NOTE: Macros
|
||||
// ============================================================================
|
||||
#define HAV_STRINGIFY2(token) #token
|
||||
#define HAV_STRINGIFY(token) HAV_STRINGIFY2(token)
|
||||
|
||||
#if defined(NDEBUG)
|
||||
#define HAV_ASSERT(expr)
|
||||
#else
|
||||
#define HAV_ASSERT(expr) \
|
||||
if (!(expr)) { \
|
||||
HAV_PrintLnFmt("Assertion triggered [file=\"" __FILE__ ":" HAV_STRINGIFY(__LINE__) "\", expr=\"" #expr "\"]"); \
|
||||
__debugbreak(); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define HAV_ARRAY_UCOUNT(array) sizeof((array)) / sizeof((array)[0])
|
||||
#define HAV_CAST(Type) (Type)
|
||||
#define HAV_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define HAV_MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
typedef uint64_t u64;
|
||||
|
||||
// NOTE: Globals
|
||||
// ============================================================================
|
||||
typedef struct HAV_Globals {
|
||||
HANDLE stdout_handle;
|
||||
bool write_to_console;
|
||||
} HAV_Globals;
|
||||
|
||||
HAV_Globals pap_globals;
|
||||
|
||||
// NOTE: Strings
|
||||
// ============================================================================
|
||||
typedef struct HAV_Str8 {
|
||||
char *data;
|
||||
size_t size;
|
||||
} HAV_Str8;
|
||||
|
||||
typedef struct HAV_Str8ToU64Result {
|
||||
bool success;
|
||||
uint64_t value;
|
||||
} HAV_Str8ToU64Result;
|
||||
|
||||
typedef struct HAV_Str8BinarySplitResult {
|
||||
HAV_Str8 lhs;
|
||||
HAV_Str8 rhs;
|
||||
} HAV_Str8BinarySplitResult;
|
||||
|
||||
#define HAV_STR8(string) (HAV_Str8){.data = (string), .size = HAV_ARRAY_UCOUNT(string) - 1 }
|
||||
#define HAV_STR8_FMT(string) (int)((string).size), (string).data
|
||||
|
||||
bool HAV_Str8_Equals(HAV_Str8 lhs, HAV_Str8 rhs);
|
||||
HAV_Str8ToU64Result HAV_Str8_ToU64(HAV_Str8 string);
|
||||
HAV_Str8BinarySplitResult HAV_Str8_BinarySplit(HAV_Str8 buffer, HAV_Str8 find);
|
||||
|
||||
bool HAV_CharIsWhiteSpace(char ch);
|
||||
bool HAV_CharIsDigit(char ch);
|
||||
|
||||
// NOTE: Profiler
|
||||
// ============================================================================
|
||||
typedef struct HAV_ProfilerAnchor {
|
||||
HAV_Str8 label;
|
||||
u64 elapsed_tsc_exclusive; // Does not include children
|
||||
u64 elapsed_tsc_inclusive; // Includes children
|
||||
u64 byte_count;
|
||||
u64 hits;
|
||||
} HAV_ProfilerAnchor;
|
||||
|
||||
typedef struct HAV_Profiler {
|
||||
HAV_ProfilerAnchor anchors[4096];
|
||||
u64 begin_tsc;
|
||||
u64 end_tsc;
|
||||
u64 parent_index;
|
||||
} HAV_Profiler;
|
||||
|
||||
typedef struct HAV_ProfilerZone {
|
||||
u64 parent_index;
|
||||
uint32_t index;
|
||||
HAV_Str8 label;
|
||||
u64 elapsed_tsc_inclusive;
|
||||
u64 tsc;
|
||||
u64 byte_count;
|
||||
} HAV_ProfilerZone;
|
||||
|
||||
static HAV_Profiler g_profiler;
|
||||
|
||||
#define HAV_Profiler_BeginZone(label) HAV_Profiler_BeginZone_(HAV_STR8(label), __COUNTER__ + 1, 0)
|
||||
#define HAV_Profiler_BeginZoneBandwidth(label, byte_count) HAV_Profiler_BeginZone_(HAV_STR8(label), __COUNTER__ + 1, byte_count)
|
||||
|
||||
static void HAV_Profiler_Dump();
|
||||
static HAV_ProfilerZone HAV_Profiler_BeginZone_(HAV_Str8 label, uint32_t index, u64 byte_count);
|
||||
static void HAV_Profiler_EndZone(HAV_ProfilerZone zone);
|
||||
|
||||
// NOTE: PCG32
|
||||
// ============================================================================
|
||||
// NOTE: PCG RNG from Demetri Spanos: https://github.com/demetri/scribbles
|
||||
// pcg32_pie, based on the minimal C version from O'Neill at pcg-random.org;
|
||||
// I've made a few (subjective) UX improvements for beginner use
|
||||
//
|
||||
// I'm not allowing the user to pick the stream/increment constant at all,
|
||||
// since there is almost never a reason for doing this in common applications.
|
||||
// This means that the prng state is reduced to a single uint64_t which also
|
||||
// means we can avoid having a state struct at all. The (fixed) stream constant
|
||||
// uses the leading hex digits of pi and e multipled by 2^30 (c90fdaa2 and
|
||||
// adf85459).
|
||||
//
|
||||
// I have also added an XOR with the same digits on the output path prior
|
||||
// to xorshift mixing. This prevents the "surprising" result that the
|
||||
// first "random 32-bit number" from a (very common) 0 seed is 0.
|
||||
//
|
||||
// use:
|
||||
// uint64_t state = 12345; // whatever you like can go here
|
||||
// uint32_t some_random_32_bits = pcg32_pie(&state);
|
||||
// uint32_t more_random_32_bits = pcg32_pie(&state);
|
||||
uint32_t HAV_PCG32_Pie (uint64_t *state);
|
||||
f64 HAV_PCG32_PieF64(uint64_t *state, f64 min, f64 max);
|
||||
|
||||
// NOTE: Buffer
|
||||
// ============================================================================
|
||||
typedef struct HAV_Buffer {
|
||||
char *data;
|
||||
size_t size;
|
||||
} HAV_Buffer;
|
||||
|
||||
typedef struct HAV_BufferIterator {
|
||||
HAV_Buffer buffer;
|
||||
size_t index;
|
||||
} HAV_BufferIterator;
|
||||
|
||||
bool HAV_BufferIsValid (HAV_Buffer buffer);
|
||||
HAV_BufferIterator HAV_BufferIteratorInit (HAV_Buffer buffer);
|
||||
bool HAV_BufferIteratorHasMoreBytes(HAV_BufferIterator it);
|
||||
uint8_t HAV_BufferIteratorNextByte (HAV_BufferIterator *it);
|
||||
|
||||
// NOTE: File
|
||||
// ============================================================================
|
||||
HAV_Buffer HAV_FileRead (char const *file_path);
|
||||
void HAV_FileFree (HAV_Buffer buffer);
|
||||
bool HAV_FileWrite(char const *file_path, void const *buffer, size_t buffer_size);
|
||||
|
||||
// NOTE: Print
|
||||
// ============================================================================
|
||||
void HAV_PrintHandle(void *handle, HAV_Str8 string);
|
||||
void HAV_PrintLn (HAV_Str8 string);
|
||||
void HAV_PrintLnFmt (char const *fmt, ...);
|
||||
@@ -0,0 +1,53 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 65
|
||||
======================================================================== */
|
||||
|
||||
static f64 Square(f64 A)
|
||||
{
|
||||
f64 Result = (A*A);
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 RadiansFromDegrees(f64 Degrees)
|
||||
{
|
||||
f64 Result = 0.01745329251994329577 * Degrees;
|
||||
return Result;
|
||||
}
|
||||
|
||||
// NOTE(casey): EarthRadius is generally expected to be 6372.8
|
||||
static f64 ReferenceHaversine(f64 X0, f64 Y0, f64 X1, f64 Y1, f64 EarthRadius)
|
||||
{
|
||||
/* NOTE(casey): This is not meant to be a "good" way to calculate the Haversine distance.
|
||||
Instead, it attempts to follow, as closely as possible, the formula used in the real-world
|
||||
question on which these homework exercises are loosely based.
|
||||
*/
|
||||
|
||||
f64 lat1 = Y0;
|
||||
f64 lat2 = Y1;
|
||||
f64 lon1 = X0;
|
||||
f64 lon2 = X1;
|
||||
|
||||
f64 dLat = RadiansFromDegrees(lat2 - lat1);
|
||||
f64 dLon = RadiansFromDegrees(lon2 - lon1);
|
||||
lat1 = RadiansFromDegrees(lat1);
|
||||
lat2 = RadiansFromDegrees(lat2);
|
||||
|
||||
f64 a = Square(sin(dLat/2.0)) + cos(lat1)*cos(lat2)*Square(sin(dLon/2));
|
||||
f64 c = 2.0*asin(sqrt(a));
|
||||
|
||||
f64 Result = EarthRadius * c;
|
||||
|
||||
return Result;
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 66
|
||||
======================================================================== */
|
||||
|
||||
/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot
|
||||
call fopen(). If we replace fopen() with fopen_s() to avoid the warning,
|
||||
then the code doesn't compile on Linux anymore, since fopen_s() does not
|
||||
exist there.
|
||||
|
||||
What exactly the CRT maintainers were thinking when they made this choice,
|
||||
I have no idea. */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef double f64;
|
||||
#define U64Max UINT64_MAX
|
||||
|
||||
#include "listing_0065_haversine_formula.cpp"
|
||||
|
||||
struct random_series
|
||||
{
|
||||
u64 A, B, C, D;
|
||||
};
|
||||
|
||||
static u64 RotateLeft(u64 V, int Shift)
|
||||
{
|
||||
u64 Result = ((V << Shift) | (V >> (64-Shift)));
|
||||
return Result;
|
||||
}
|
||||
|
||||
static u64 RandomU64(random_series *Series)
|
||||
{
|
||||
u64 A = Series->A;
|
||||
u64 B = Series->B;
|
||||
u64 C = Series->C;
|
||||
u64 D = Series->D;
|
||||
|
||||
u64 E = A - RotateLeft(B, 27);
|
||||
|
||||
A = (B ^ RotateLeft(C, 17));
|
||||
B = (C + D);
|
||||
C = (D + E);
|
||||
D = (E + A);
|
||||
|
||||
Series->A = A;
|
||||
Series->B = B;
|
||||
Series->C = C;
|
||||
Series->D = D;
|
||||
|
||||
return D;
|
||||
}
|
||||
|
||||
static random_series Seed(u64 Value)
|
||||
{
|
||||
random_series Series = {};
|
||||
|
||||
// NOTE(casey): This is the seed pattern for JSF generators, as per the original post
|
||||
Series.A = 0xf1ea5eed;
|
||||
Series.B = Value;
|
||||
Series.C = Value;
|
||||
Series.D = Value;
|
||||
|
||||
u32 Count = 20;
|
||||
while(Count--)
|
||||
{
|
||||
RandomU64(&Series);
|
||||
}
|
||||
|
||||
return Series;
|
||||
}
|
||||
|
||||
static f64 RandomInRange(random_series *Series, f64 Min, f64 Max)
|
||||
{
|
||||
f64 t = (f64)RandomU64(Series) / (f64)U64Max;
|
||||
f64 Result = (1.0 - t)*Min + t*Max;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static FILE *Open(long long unsigned PairCount, char *Label, char *Extension)
|
||||
{
|
||||
char Temp[256];
|
||||
sprintf(Temp, "data_%llu_%s.%s", PairCount, Label, Extension);
|
||||
FILE *Result = fopen(Temp, "wb");
|
||||
if(!Result)
|
||||
{
|
||||
fprintf(stderr, "Unable to open \"%s\" for writing.\n", Temp);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 RandomDegree(random_series *Series, f64 Center, f64 Radius, f64 MaxAllowed)
|
||||
{
|
||||
f64 MinVal = Center - Radius;
|
||||
if(MinVal < -MaxAllowed)
|
||||
{
|
||||
MinVal = -MaxAllowed;
|
||||
}
|
||||
|
||||
f64 MaxVal = Center + Radius;
|
||||
if(MaxVal > MaxAllowed)
|
||||
{
|
||||
MaxVal = MaxAllowed;
|
||||
}
|
||||
|
||||
f64 Result = RandomInRange(Series, MinVal, MaxVal);
|
||||
return Result;
|
||||
}
|
||||
|
||||
int main(int ArgCount, char **Args)
|
||||
{
|
||||
if(ArgCount == 4)
|
||||
{
|
||||
u64 ClusterCountLeft = U64Max;
|
||||
f64 MaxAllowedX = 180;
|
||||
f64 MaxAllowedY = 90;
|
||||
|
||||
f64 XCenter = 0;
|
||||
f64 YCenter = 0;
|
||||
f64 XRadius = MaxAllowedX;
|
||||
f64 YRadius = MaxAllowedY;
|
||||
|
||||
char *MethodName = Args[1];
|
||||
if(strcmp(MethodName, "cluster") == 0)
|
||||
{
|
||||
ClusterCountLeft = 0;
|
||||
}
|
||||
else if(strcmp(MethodName, "uniform") != 0)
|
||||
{
|
||||
MethodName = "uniform";
|
||||
fprintf(stderr, "WARNING: Unrecognized method name. Using 'uniform'.\n");
|
||||
}
|
||||
|
||||
u64 SeedValue = atoll(Args[2]);
|
||||
random_series Series = Seed(SeedValue);
|
||||
|
||||
u64 MaxPairCount = (1ULL << 34);
|
||||
u64 PairCount = atoll(Args[3]);
|
||||
if(PairCount < MaxPairCount)
|
||||
{
|
||||
u64 ClusterCountMax = 1 + (PairCount / 64);
|
||||
|
||||
FILE *FlexJSON = Open(PairCount, "flex", "json");
|
||||
FILE *HaverAnswers = Open(PairCount, "haveranswer", "f64");
|
||||
if(FlexJSON && HaverAnswers)
|
||||
{
|
||||
fprintf(FlexJSON, "{\"pairs\":[\n");
|
||||
f64 Sum = 0;
|
||||
f64 SumCoef = 1.0 / (f64)PairCount;
|
||||
for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex)
|
||||
{
|
||||
if(ClusterCountLeft-- == 0)
|
||||
{
|
||||
ClusterCountLeft = ClusterCountMax;
|
||||
XCenter = RandomInRange(&Series, -MaxAllowedX, MaxAllowedX);
|
||||
YCenter = RandomInRange(&Series, -MaxAllowedY, MaxAllowedY);
|
||||
XRadius = RandomInRange(&Series, 0, MaxAllowedX);
|
||||
YRadius = RandomInRange(&Series, 0, MaxAllowedY);
|
||||
}
|
||||
|
||||
f64 X0 = RandomDegree(&Series, XCenter, XRadius, MaxAllowedX);
|
||||
f64 Y0 = RandomDegree(&Series, YCenter, YRadius, MaxAllowedY);
|
||||
f64 X1 = RandomDegree(&Series, XCenter, XRadius, MaxAllowedX);
|
||||
f64 Y1 = RandomDegree(&Series, YCenter, YRadius, MaxAllowedY);
|
||||
|
||||
f64 EarthRadius = 6372.8;
|
||||
f64 HaversineDistance = ReferenceHaversine(X0, Y0, X1, Y1, EarthRadius);
|
||||
|
||||
Sum += SumCoef*HaversineDistance;
|
||||
|
||||
char *JSONSep = (PairIndex == (PairCount - 1)) ? "\n" : ",\n";
|
||||
fprintf(FlexJSON, " {\"x0\":%.16f, \"y0\":%.16f, \"x1\":%.16f, \"y1\":%.16f}%s", X0, Y0, X1, Y1, JSONSep);
|
||||
|
||||
fwrite(&HaversineDistance, sizeof(HaversineDistance), 1, HaverAnswers);
|
||||
}
|
||||
fprintf(FlexJSON, "]}\n");
|
||||
fwrite(&Sum, sizeof(Sum), 1, HaverAnswers);
|
||||
|
||||
fprintf(stdout, "Method: %s\n", MethodName);
|
||||
fprintf(stdout, "Random seed: %llu\n", SeedValue);
|
||||
fprintf(stdout, "Pair count: %llu\n", PairCount);
|
||||
fprintf(stdout, "Expected sum: %.16f\n", Sum);
|
||||
}
|
||||
|
||||
if(FlexJSON) fclose(FlexJSON);
|
||||
if(HaverAnswers) fclose(HaverAnswers);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "To avoid accidentally generating massive files, number of pairs must be less than %llu.\n", MaxPairCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [uniform/cluster] [random seed] [number of coordinate pairs to generate]\n", Args[0]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 67
|
||||
======================================================================== */
|
||||
|
||||
/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot
|
||||
call fopen(). If we replace fopen() with fopen_s() to avoid the warning,
|
||||
then the code doesn't compile on Linux anymore, since fopen_s() does not
|
||||
exist there.
|
||||
|
||||
What exactly the CRT maintainers were thinking when they made this choice,
|
||||
I have no idea. */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int32_t b32;
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
struct haversine_pair
|
||||
{
|
||||
f64 X0, Y0;
|
||||
f64 X1, Y1;
|
||||
};
|
||||
|
||||
#include "listing_0065_haversine_formula.cpp"
|
||||
#include "listing_0068_buffer.cpp"
|
||||
#include "listing_0069_lookup_json_parser.cpp"
|
||||
|
||||
static buffer ReadEntireFile(char *FileName)
|
||||
{
|
||||
buffer Result = {};
|
||||
|
||||
FILE *File = fopen(FileName, "rb");
|
||||
if(File)
|
||||
{
|
||||
#if _WIN32
|
||||
struct __stat64 Stat;
|
||||
_stat64(FileName, &Stat);
|
||||
#else
|
||||
struct stat Stat;
|
||||
stat(FileName, &Stat);
|
||||
#endif
|
||||
|
||||
Result = AllocateBuffer(Stat.st_size);
|
||||
if(Result.Data)
|
||||
{
|
||||
if(fread(Result.Data, Result.Count, 1, File) != 1)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName);
|
||||
FreeBuffer(&Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs)
|
||||
{
|
||||
f64 Sum = 0;
|
||||
|
||||
f64 SumCoef = 1 / (f64)PairCount;
|
||||
for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex)
|
||||
{
|
||||
haversine_pair Pair = Pairs[PairIndex];
|
||||
f64 EarthRadius = 6372.8;
|
||||
f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius);
|
||||
Sum += SumCoef*Dist;
|
||||
}
|
||||
|
||||
return Sum;
|
||||
}
|
||||
|
||||
int main(int ArgCount, char **Args)
|
||||
{
|
||||
int Result = 1;
|
||||
|
||||
if((ArgCount == 2) || (ArgCount == 3))
|
||||
{
|
||||
buffer InputJSON = ReadEntireFile(Args[1]);
|
||||
|
||||
u32 MinimumJSONPairEncoding = 6*4;
|
||||
u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding;
|
||||
if(MaxPairCount)
|
||||
{
|
||||
buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair));
|
||||
if(ParsedValues.Count)
|
||||
{
|
||||
haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data;
|
||||
u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs);
|
||||
f64 Sum = SumHaversineDistances(PairCount, Pairs);
|
||||
|
||||
fprintf(stdout, "Input size: %llu\n", InputJSON.Count);
|
||||
fprintf(stdout, "Pair count: %llu\n", PairCount);
|
||||
fprintf(stdout, "Haversine sum: %.16f\n", Sum);
|
||||
|
||||
if(ArgCount == 3)
|
||||
{
|
||||
buffer AnswersF64 = ReadEntireFile(Args[2]);
|
||||
if(AnswersF64.Count >= sizeof(f64))
|
||||
{
|
||||
f64 *AnswerValues = (f64 *)AnswersF64.Data;
|
||||
|
||||
fprintf(stdout, "\nValidation:\n");
|
||||
|
||||
u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64);
|
||||
if(PairCount != RefAnswerCount)
|
||||
{
|
||||
fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount);
|
||||
}
|
||||
|
||||
f64 RefSum = AnswerValues[RefAnswerCount];
|
||||
fprintf(stdout, "Reference sum: %.16f\n", RefSum);
|
||||
fprintf(stdout, "Difference: %.16f\n", Sum - RefSum);
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FreeBuffer(&ParsedValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Malformed input JSON\n");
|
||||
}
|
||||
|
||||
FreeBuffer(&InputJSON);
|
||||
|
||||
Result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]);
|
||||
fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 68
|
||||
======================================================================== */
|
||||
|
||||
struct buffer
|
||||
{
|
||||
size_t Count;
|
||||
u8 *Data;
|
||||
};
|
||||
|
||||
#define CONSTANT_STRING(String) {sizeof(String) - 1, (u8 *)(String)}
|
||||
|
||||
static b32 IsInBounds(buffer Source, u64 At)
|
||||
{
|
||||
b32 Result = (At < Source.Count);
|
||||
return Result;
|
||||
}
|
||||
|
||||
static b32 AreEqual(buffer A, buffer B)
|
||||
{
|
||||
if(A.Count != B.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for(u64 Index = 0; Index < A.Count; ++Index)
|
||||
{
|
||||
if(A.Data[Index] != B.Data[Index])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static buffer AllocateBuffer(size_t Count)
|
||||
{
|
||||
buffer Result = {};
|
||||
Result.Data = (u8 *)malloc(Count);
|
||||
if(Result.Data)
|
||||
{
|
||||
Result.Count = Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to allocate %llu bytes.\n", Count);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void FreeBuffer(buffer *Buffer)
|
||||
{
|
||||
if(Buffer->Data)
|
||||
{
|
||||
free(Buffer->Data);
|
||||
}
|
||||
*Buffer = {};
|
||||
}
|
||||
@@ -0,0 +1,508 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 69
|
||||
======================================================================== */
|
||||
|
||||
enum json_token_type
|
||||
{
|
||||
Token_end_of_stream,
|
||||
Token_error,
|
||||
|
||||
Token_open_brace,
|
||||
Token_open_bracket,
|
||||
Token_close_brace,
|
||||
Token_close_bracket,
|
||||
Token_comma,
|
||||
Token_colon,
|
||||
Token_semi_colon,
|
||||
Token_string_literal,
|
||||
Token_number,
|
||||
Token_true,
|
||||
Token_false,
|
||||
Token_null,
|
||||
|
||||
Token_count,
|
||||
};
|
||||
|
||||
struct json_token
|
||||
{
|
||||
json_token_type Type;
|
||||
buffer Value;
|
||||
};
|
||||
|
||||
struct json_element
|
||||
{
|
||||
buffer Label;
|
||||
buffer Value;
|
||||
json_element *FirstSubElement;
|
||||
|
||||
json_element *NextSibling;
|
||||
};
|
||||
|
||||
struct json_parser
|
||||
{
|
||||
buffer Source;
|
||||
u64 At;
|
||||
b32 HadError;
|
||||
};
|
||||
|
||||
static b32 IsJSONDigit(buffer Source, u64 At)
|
||||
{
|
||||
b32 Result = false;
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Val = Source.Data[At];
|
||||
Result = ((Val >= '0') && (Val <= '9'));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static b32 IsJSONWhitespace(buffer Source, u64 At)
|
||||
{
|
||||
b32 Result = false;
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Val = Source.Data[At];
|
||||
Result = ((Val == ' ') || (Val == '\t') || (Val == '\n') || (Val == '\r'));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static b32 IsParsing(json_parser *Parser)
|
||||
{
|
||||
b32 Result = !Parser->HadError && IsInBounds(Parser->Source, Parser->At);
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void Error(json_parser *Parser, json_token Token, char const *Message)
|
||||
{
|
||||
Parser->HadError = true;
|
||||
fprintf(stderr, "ERROR: \"%.*s\" - %s\n", (u32)Token.Value.Count, (char *)Token.Value.Data, Message);
|
||||
}
|
||||
|
||||
static void ParseKeyword(buffer Source, u64 *At, buffer KeywordRemaining, json_token_type Type, json_token *Result)
|
||||
{
|
||||
if((Source.Count - *At) >= KeywordRemaining.Count)
|
||||
{
|
||||
buffer Check = Source;
|
||||
Check.Data += *At;
|
||||
Check.Count = KeywordRemaining.Count;
|
||||
if(AreEqual(Check, KeywordRemaining))
|
||||
{
|
||||
Result->Type = Type;
|
||||
Result->Value.Count += KeywordRemaining.Count;
|
||||
*At += KeywordRemaining.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static json_token GetJSONToken(json_parser *Parser)
|
||||
{
|
||||
json_token Result = {};
|
||||
|
||||
buffer Source = Parser->Source;
|
||||
u64 At = Parser->At;
|
||||
|
||||
while(IsJSONWhitespace(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
Result.Type = Token_error;
|
||||
Result.Value.Count = 1;
|
||||
Result.Value.Data = Source.Data + At;
|
||||
u8 Val = Source.Data[At++];
|
||||
switch(Val)
|
||||
{
|
||||
case '{': {Result.Type = Token_open_brace;} break;
|
||||
case '[': {Result.Type = Token_open_bracket;} break;
|
||||
case '}': {Result.Type = Token_close_brace;} break;
|
||||
case ']': {Result.Type = Token_close_bracket;} break;
|
||||
case ',': {Result.Type = Token_comma;} break;
|
||||
case ':': {Result.Type = Token_colon;} break;
|
||||
case ';': {Result.Type = Token_semi_colon;} break;
|
||||
|
||||
case 'f':
|
||||
{
|
||||
ParseKeyword(Source, &At, CONSTANT_STRING("alse"), Token_false, &Result);
|
||||
} break;
|
||||
|
||||
case 'n':
|
||||
{
|
||||
ParseKeyword(Source, &At, CONSTANT_STRING("ull"), Token_null, &Result);
|
||||
} break;
|
||||
|
||||
case 't':
|
||||
{
|
||||
ParseKeyword(Source, &At, CONSTANT_STRING("rue"), Token_true, &Result);
|
||||
} break;
|
||||
|
||||
case '"':
|
||||
{
|
||||
Result.Type = Token_string_literal;
|
||||
|
||||
u64 StringStart = At;
|
||||
|
||||
while(IsInBounds(Source, At) && (Source.Data[At] != '"'))
|
||||
{
|
||||
if(IsInBounds(Source, (At + 1)) &&
|
||||
(Source.Data[At] == '\\') &&
|
||||
(Source.Data[At + 1] == '"'))
|
||||
{
|
||||
// NOTE(casey): Skip escaped quotation marks
|
||||
++At;
|
||||
}
|
||||
|
||||
++At;
|
||||
}
|
||||
|
||||
Result.Value.Data = Source.Data + StringStart;
|
||||
Result.Value.Count = At - StringStart;
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
} break;
|
||||
|
||||
case '-':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
{
|
||||
u64 Start = At - 1;
|
||||
Result.Type = Token_number;
|
||||
|
||||
// NOTE(casey): Move past a leading negative sign if one exists
|
||||
if((Val == '-') && IsInBounds(Source, At))
|
||||
{
|
||||
Val = Source.Data[At++];
|
||||
}
|
||||
|
||||
// NOTE(casey): If the leading digit wasn't 0, parse any digits before the decimal point
|
||||
if(Val != '0')
|
||||
{
|
||||
while(IsJSONDigit(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(casey): If there is a decimal point, parse any digits after the decimal point
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '.'))
|
||||
{
|
||||
++At;
|
||||
while(IsJSONDigit(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(casey): If it's in scientific notation, parse any digits after the "e"
|
||||
if(IsInBounds(Source, At) && ((Source.Data[At] == 'e') || (Source.Data[At] == 'E')))
|
||||
{
|
||||
++At;
|
||||
|
||||
if(IsInBounds(Source, At) && ((Source.Data[At] == '+') || (Source.Data[At] == '-')))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
|
||||
while(IsJSONDigit(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
}
|
||||
|
||||
Result.Value.Count = At - Start;
|
||||
} break;
|
||||
|
||||
default:
|
||||
{
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
Parser->At = At;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static json_element *ParseJSONList(json_parser *Parser, json_token StartingToken, json_token_type EndType, b32 HasLabels);
|
||||
static json_element *ParseJSONElement(json_parser *Parser, buffer Label, json_token Value)
|
||||
{
|
||||
b32 Valid = true;
|
||||
|
||||
json_element *SubElement = 0;
|
||||
if(Value.Type == Token_open_bracket)
|
||||
{
|
||||
SubElement = ParseJSONList(Parser, Value, Token_close_bracket, false);
|
||||
}
|
||||
else if(Value.Type == Token_open_brace)
|
||||
{
|
||||
SubElement = ParseJSONList(Parser, Value, Token_close_brace, true);
|
||||
}
|
||||
else if((Value.Type == Token_string_literal) ||
|
||||
(Value.Type == Token_true) ||
|
||||
(Value.Type == Token_false) ||
|
||||
(Value.Type == Token_null) ||
|
||||
(Value.Type == Token_number))
|
||||
{
|
||||
// NOTE(casey): Nothing to do here, since there is no additional data
|
||||
}
|
||||
else
|
||||
{
|
||||
Valid = false;
|
||||
}
|
||||
|
||||
json_element *Result = 0;
|
||||
|
||||
if(Valid)
|
||||
{
|
||||
Result = (json_element *)malloc(sizeof(json_element));
|
||||
Result->Label = Label;
|
||||
Result->Value = Value.Value;
|
||||
Result->FirstSubElement = SubElement;
|
||||
Result->NextSibling = 0;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static json_element *ParseJSONList(json_parser *Parser, json_token StartingToken, json_token_type EndType, b32 HasLabels)
|
||||
{
|
||||
json_element *FirstElement = {};
|
||||
json_element *LastElement = {};
|
||||
|
||||
while(IsParsing(Parser))
|
||||
{
|
||||
buffer Label = {};
|
||||
json_token Value = GetJSONToken(Parser);
|
||||
if(HasLabels)
|
||||
{
|
||||
if(Value.Type == Token_string_literal)
|
||||
{
|
||||
Label = Value.Value;
|
||||
|
||||
json_token Colon = GetJSONToken(Parser);
|
||||
if(Colon.Type == Token_colon)
|
||||
{
|
||||
Value = GetJSONToken(Parser);
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(Parser, Colon, "Expected colon after field name");
|
||||
}
|
||||
}
|
||||
else if(Value.Type != EndType)
|
||||
{
|
||||
Error(Parser, Value, "Unexpected token in JSON");
|
||||
}
|
||||
}
|
||||
|
||||
json_element *Element = ParseJSONElement(Parser, Label, Value);
|
||||
if(Element)
|
||||
{
|
||||
LastElement = (LastElement ? LastElement->NextSibling : FirstElement) = Element;
|
||||
}
|
||||
else if(Value.Type == EndType)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(Parser, Value, "Unexpected token in JSON");
|
||||
}
|
||||
|
||||
json_token Comma = GetJSONToken(Parser);
|
||||
if(Comma.Type == EndType)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if(Comma.Type != Token_comma)
|
||||
{
|
||||
Error(Parser, Comma, "Unexpected token in JSON");
|
||||
}
|
||||
}
|
||||
|
||||
return FirstElement;
|
||||
}
|
||||
|
||||
static json_element *ParseJSON(buffer InputJSON)
|
||||
{
|
||||
json_parser Parser = {};
|
||||
Parser.Source = InputJSON;
|
||||
|
||||
json_element *Result = ParseJSONElement(&Parser, {}, GetJSONToken(&Parser));
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void FreeJSON(json_element *Element)
|
||||
{
|
||||
while(Element)
|
||||
{
|
||||
json_element *FreeElement = Element;
|
||||
Element = Element->NextSibling;
|
||||
|
||||
FreeJSON(FreeElement->FirstSubElement);
|
||||
free(FreeElement);
|
||||
}
|
||||
}
|
||||
|
||||
static json_element *LookupElement(json_element *Object, buffer ElementName)
|
||||
{
|
||||
json_element *Result = 0;
|
||||
|
||||
if(Object)
|
||||
{
|
||||
for(json_element *Search = Object->FirstSubElement; Search; Search = Search->NextSibling)
|
||||
{
|
||||
if(AreEqual(Search->Label, ElementName))
|
||||
{
|
||||
Result = Search;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 ConvertJSONSign(buffer Source, u64 *AtResult)
|
||||
{
|
||||
u64 At = *AtResult;
|
||||
|
||||
f64 Result = 1.0;
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '-'))
|
||||
{
|
||||
Result = -1.0;
|
||||
++At;
|
||||
}
|
||||
|
||||
*AtResult = At;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 ConvertJSONNumber(buffer Source, u64 *AtResult)
|
||||
{
|
||||
u64 At = *AtResult;
|
||||
|
||||
f64 Result = 0.0;
|
||||
while(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Char = Source.Data[At] - (u8)'0';
|
||||
if(Char < 10)
|
||||
{
|
||||
Result = 10.0*Result + (f64)Char;
|
||||
++At;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*AtResult = At;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 ConvertElementToF64(json_element *Object, buffer ElementName)
|
||||
{
|
||||
f64 Result = 0.0;
|
||||
|
||||
json_element *Element = LookupElement(Object, ElementName);
|
||||
if(Element)
|
||||
{
|
||||
buffer Source = Element->Value;
|
||||
u64 At = 0;
|
||||
|
||||
f64 Sign = ConvertJSONSign(Source, &At);
|
||||
f64 Number = ConvertJSONNumber(Source, &At);
|
||||
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '.'))
|
||||
{
|
||||
++At;
|
||||
f64 C = 1.0 / 10.0;
|
||||
while(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Char = Source.Data[At] - (u8)'0';
|
||||
if(Char < 10)
|
||||
{
|
||||
Number = Number + C*(f64)Char;
|
||||
C *= 1.0 / 10.0;
|
||||
++At;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(IsInBounds(Source, At) && ((Source.Data[At] == 'e') || (Source.Data[At] == 'E')))
|
||||
{
|
||||
++At;
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '+'))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
|
||||
f64 ExponentSign = ConvertJSONSign(Source, &At);
|
||||
f64 Exponent = ExponentSign*ConvertJSONNumber(Source, &At);
|
||||
Number *= pow(10.0, Exponent);
|
||||
}
|
||||
|
||||
Result = Sign*Number;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static u64 ParseHaversinePairs(buffer InputJSON, u64 MaxPairCount, haversine_pair *Pairs)
|
||||
{
|
||||
u64 PairCount = 0;
|
||||
|
||||
json_element *JSON = ParseJSON(InputJSON);
|
||||
json_element *PairsArray = LookupElement(JSON, CONSTANT_STRING("pairs"));
|
||||
if(PairsArray)
|
||||
{
|
||||
for(json_element *Element = PairsArray->FirstSubElement;
|
||||
Element && (PairCount < MaxPairCount);
|
||||
Element = Element->NextSibling)
|
||||
{
|
||||
haversine_pair *Pair = Pairs + PairCount++;
|
||||
|
||||
Pair->X0 = ConvertElementToF64(Element, CONSTANT_STRING("x0"));
|
||||
Pair->Y0 = ConvertElementToF64(Element, CONSTANT_STRING("y0"));
|
||||
Pair->X1 = ConvertElementToF64(Element, CONSTANT_STRING("x1"));
|
||||
Pair->Y1 = ConvertElementToF64(Element, CONSTANT_STRING("y1"));
|
||||
}
|
||||
}
|
||||
|
||||
FreeJSON(JSON);
|
||||
|
||||
return PairCount;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 70
|
||||
======================================================================== */
|
||||
|
||||
#if _WIN32
|
||||
|
||||
#include <intrin.h>
|
||||
#include <windows.h>
|
||||
|
||||
static u64 GetOSTimerFreq(void)
|
||||
{
|
||||
LARGE_INTEGER Freq;
|
||||
QueryPerformanceFrequency(&Freq);
|
||||
return Freq.QuadPart;
|
||||
}
|
||||
|
||||
static u64 ReadOSTimer(void)
|
||||
{
|
||||
LARGE_INTEGER Value;
|
||||
QueryPerformanceCounter(&Value);
|
||||
return Value.QuadPart;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <x86intrin.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static u64 GetOSTimerFreq(void)
|
||||
{
|
||||
return 1000000;
|
||||
}
|
||||
|
||||
static u64 ReadOSTimer(void)
|
||||
{
|
||||
// NOTE(casey): The "struct" keyword is not necessary here when compiling in C++,
|
||||
// but just in case anyone is using this file from C, I include it.
|
||||
struct timeval Value;
|
||||
gettimeofday(&Value, 0);
|
||||
|
||||
u64 Result = GetOSTimerFreq()*(u64)Value.tv_sec + (u64)Value.tv_usec;
|
||||
return Result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* NOTE(casey): This does not need to be "inline", it could just be "static"
|
||||
because compilers will inline it anyway. But compilers will warn about
|
||||
static functions that aren't used. So "inline" is just the simplest way
|
||||
to tell them to stop complaining about that. */
|
||||
inline u64 ReadCPUTimer(void)
|
||||
{
|
||||
// NOTE(casey): If you were on ARM, you would need to replace __rdtsc
|
||||
// with one of their performance counter read instructions, depending
|
||||
// on which ones are available on your platform.
|
||||
|
||||
return __rdtsc();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 71
|
||||
======================================================================== */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef uint64_t u64;
|
||||
typedef double f64;
|
||||
|
||||
#include "listing_0070_platform_metrics.cpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
u64 OSFreq = GetOSTimerFreq();
|
||||
printf(" OS Freq: %llu\n", OSFreq);
|
||||
|
||||
u64 OSStart = ReadOSTimer();
|
||||
u64 OSEnd = 0;
|
||||
u64 OSElapsed = 0;
|
||||
while(OSElapsed < OSFreq)
|
||||
{
|
||||
OSEnd = ReadOSTimer();
|
||||
OSElapsed = OSEnd - OSStart;
|
||||
}
|
||||
|
||||
printf(" OS Timer: %llu -> %llu = %llu elapsed\n", OSStart, OSEnd, OSElapsed);
|
||||
printf(" OS Seconds: %.4f\n", (f64)OSElapsed/(f64)OSFreq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 72
|
||||
======================================================================== */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef uint64_t u64;
|
||||
typedef double f64;
|
||||
|
||||
#include "listing_0070_platform_metrics.cpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
u64 OSFreq = GetOSTimerFreq();
|
||||
printf(" OS Freq: %llu\n", OSFreq);
|
||||
|
||||
u64 CPUStart = ReadCPUTimer();
|
||||
u64 OSStart = ReadOSTimer();
|
||||
u64 OSEnd = 0;
|
||||
u64 OSElapsed = 0;
|
||||
while(OSElapsed < OSFreq)
|
||||
{
|
||||
OSEnd = ReadOSTimer();
|
||||
OSElapsed = OSEnd - OSStart;
|
||||
}
|
||||
|
||||
u64 CPUEnd = ReadCPUTimer();
|
||||
u64 CPUElapsed = CPUEnd - CPUStart;
|
||||
|
||||
printf(" OS Timer: %llu -> %llu = %llu elapsed\n", OSStart, OSEnd, OSElapsed);
|
||||
printf(" OS Seconds: %.4f\n", (f64)OSElapsed/(f64)OSFreq);
|
||||
|
||||
printf(" CPU Timer: %llu -> %llu = %llu elapsed\n", CPUStart, CPUEnd, CPUElapsed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 73
|
||||
======================================================================== */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef uint64_t u64;
|
||||
typedef double f64;
|
||||
|
||||
#include "listing_0070_platform_metrics.cpp"
|
||||
|
||||
int main(int ArgCount, char **Args)
|
||||
{
|
||||
u64 MillisecondsToWait = 1000;
|
||||
if(ArgCount == 2)
|
||||
{
|
||||
MillisecondsToWait = atol(Args[1]);
|
||||
}
|
||||
|
||||
u64 OSFreq = GetOSTimerFreq();
|
||||
printf(" OS Freq: %llu (reported)\n", OSFreq);
|
||||
|
||||
u64 CPUStart = ReadCPUTimer();
|
||||
u64 OSStart = ReadOSTimer();
|
||||
u64 OSEnd = 0;
|
||||
u64 OSElapsed = 0;
|
||||
u64 OSWaitTime = OSFreq * MillisecondsToWait / 1000;
|
||||
while(OSElapsed < OSWaitTime)
|
||||
{
|
||||
OSEnd = ReadOSTimer();
|
||||
OSElapsed = OSEnd - OSStart;
|
||||
}
|
||||
|
||||
u64 CPUEnd = ReadCPUTimer();
|
||||
u64 CPUElapsed = CPUEnd - CPUStart;
|
||||
u64 CPUFreq = 0;
|
||||
if(OSElapsed)
|
||||
{
|
||||
CPUFreq = OSFreq * CPUElapsed / OSElapsed;
|
||||
}
|
||||
|
||||
printf(" OS Timer: %llu -> %llu = %llu elapsed\n", OSStart, OSEnd, OSElapsed);
|
||||
printf(" OS Seconds: %.4f\n", (f64)OSElapsed/(f64)OSFreq);
|
||||
|
||||
printf(" CPU Timer: %llu -> %llu = %llu elapsed\n", CPUStart, CPUEnd, CPUElapsed);
|
||||
printf(" CPU Freq: %llu (guessed)\n", CPUFreq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 74
|
||||
======================================================================== */
|
||||
|
||||
#if _WIN32
|
||||
|
||||
#include <intrin.h>
|
||||
#include <windows.h>
|
||||
|
||||
static u64 GetOSTimerFreq(void)
|
||||
{
|
||||
LARGE_INTEGER Freq;
|
||||
QueryPerformanceFrequency(&Freq);
|
||||
return Freq.QuadPart;
|
||||
}
|
||||
|
||||
static u64 ReadOSTimer(void)
|
||||
{
|
||||
LARGE_INTEGER Value;
|
||||
QueryPerformanceCounter(&Value);
|
||||
return Value.QuadPart;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <x86intrin.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static u64 GetOSTimerFreq(void)
|
||||
{
|
||||
return 1000000;
|
||||
}
|
||||
|
||||
static u64 ReadOSTimer(void)
|
||||
{
|
||||
// NOTE(casey): The "struct" keyword is not necessary here when compiling in C++,
|
||||
// but just in case anyone is using this file from C, I include it.
|
||||
struct timeval Value;
|
||||
gettimeofday(&Value, 0);
|
||||
|
||||
u64 Result = GetOSTimerFreq()*(u64)Value.tv_sec + (u64)Value.tv_usec;
|
||||
return Result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* NOTE(casey): This does not need to be "inline", it could just be "static"
|
||||
because compilers will inline it anyway. But compilers will warn about
|
||||
static functions that aren't used. So "inline" is just the simplest way
|
||||
to tell them to stop complaining about that. */
|
||||
inline u64 ReadCPUTimer(void)
|
||||
{
|
||||
// NOTE(casey): If you were on ARM, you would need to replace __rdtsc
|
||||
// with one of their performance counter read instructions, depending
|
||||
// on which ones are available on your platform.
|
||||
|
||||
return __rdtsc();
|
||||
}
|
||||
|
||||
static u64 EstimateCPUTimerFreq(void)
|
||||
{
|
||||
u64 MillisecondsToWait = 100;
|
||||
u64 OSFreq = GetOSTimerFreq();
|
||||
|
||||
u64 CPUStart = ReadCPUTimer();
|
||||
u64 OSStart = ReadOSTimer();
|
||||
u64 OSEnd = 0;
|
||||
u64 OSElapsed = 0;
|
||||
u64 OSWaitTime = OSFreq * MillisecondsToWait / 1000;
|
||||
while(OSElapsed < OSWaitTime)
|
||||
{
|
||||
OSEnd = ReadOSTimer();
|
||||
OSElapsed = OSEnd - OSStart;
|
||||
}
|
||||
|
||||
u64 CPUEnd = ReadCPUTimer();
|
||||
u64 CPUElapsed = CPUEnd - CPUStart;
|
||||
|
||||
u64 CPUFreq = 0;
|
||||
if(OSElapsed)
|
||||
{
|
||||
CPUFreq = OSFreq * CPUElapsed / OSElapsed;
|
||||
}
|
||||
|
||||
return CPUFreq;
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 75
|
||||
======================================================================== */
|
||||
|
||||
/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot
|
||||
call fopen(). If we replace fopen() with fopen_s() to avoid the warning,
|
||||
then the code doesn't compile on Linux anymore, since fopen_s() does not
|
||||
exist there.
|
||||
|
||||
What exactly the CRT maintainers were thinking when they made this choice,
|
||||
I have no idea. */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int32_t b32;
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
struct haversine_pair
|
||||
{
|
||||
f64 X0, Y0;
|
||||
f64 X1, Y1;
|
||||
};
|
||||
|
||||
#include "listing_0074_platform_metrics.cpp"
|
||||
#include "listing_0065_haversine_formula.cpp"
|
||||
#include "listing_0068_buffer.cpp"
|
||||
#include "listing_0069_lookup_json_parser.cpp"
|
||||
|
||||
static buffer ReadEntireFile(char *FileName)
|
||||
{
|
||||
buffer Result = {};
|
||||
|
||||
FILE *File = fopen(FileName, "rb");
|
||||
if(File)
|
||||
{
|
||||
#if _WIN32
|
||||
struct __stat64 Stat;
|
||||
_stat64(FileName, &Stat);
|
||||
#else
|
||||
struct stat Stat;
|
||||
stat(FileName, &Stat);
|
||||
#endif
|
||||
|
||||
Result = AllocateBuffer(Stat.st_size);
|
||||
if(Result.Data)
|
||||
{
|
||||
if(fread(Result.Data, Result.Count, 1, File) != 1)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName);
|
||||
FreeBuffer(&Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs)
|
||||
{
|
||||
f64 Sum = 0;
|
||||
|
||||
f64 SumCoef = 1 / (f64)PairCount;
|
||||
for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex)
|
||||
{
|
||||
haversine_pair Pair = Pairs[PairIndex];
|
||||
f64 EarthRadius = 6372.8;
|
||||
f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius);
|
||||
Sum += SumCoef*Dist;
|
||||
}
|
||||
|
||||
return Sum;
|
||||
}
|
||||
|
||||
static void PrintTimeElapsed(char const *Label, u64 TotalTSCElapsed, u64 Begin, u64 End)
|
||||
{
|
||||
u64 Elapsed = End - Begin;
|
||||
f64 Percent = 100.0 * ((f64)Elapsed / (f64)TotalTSCElapsed);
|
||||
printf(" %s: %llu (%.2f%%)\n", Label, Elapsed, Percent);
|
||||
}
|
||||
|
||||
int main(int ArgCount, char **Args)
|
||||
{
|
||||
u64 Prof_Begin = 0;
|
||||
u64 Prof_Read = 0;
|
||||
u64 Prof_MiscSetup = 0;
|
||||
u64 Prof_Parse = 0;
|
||||
u64 Prof_Sum = 0;
|
||||
u64 Prof_MiscOutput = 0;
|
||||
u64 Prof_End = 0;
|
||||
|
||||
Prof_Begin = ReadCPUTimer();
|
||||
|
||||
int Result = 1;
|
||||
|
||||
if((ArgCount == 2) || (ArgCount == 3))
|
||||
{
|
||||
Prof_Read = ReadCPUTimer();
|
||||
buffer InputJSON = ReadEntireFile(Args[1]);
|
||||
Prof_MiscSetup = ReadCPUTimer();
|
||||
|
||||
u32 MinimumJSONPairEncoding = 6*4;
|
||||
u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding;
|
||||
if(MaxPairCount)
|
||||
{
|
||||
buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair));
|
||||
if(ParsedValues.Count)
|
||||
{
|
||||
haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data;
|
||||
|
||||
Prof_Parse = ReadCPUTimer();
|
||||
u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs);
|
||||
Prof_Sum = ReadCPUTimer();
|
||||
f64 Sum = SumHaversineDistances(PairCount, Pairs);
|
||||
Prof_MiscOutput = ReadCPUTimer();
|
||||
|
||||
Result = 0;
|
||||
|
||||
fprintf(stdout, "Input size: %llu\n", InputJSON.Count);
|
||||
fprintf(stdout, "Pair count: %llu\n", PairCount);
|
||||
fprintf(stdout, "Haversine sum: %.16f\n", Sum);
|
||||
|
||||
if(ArgCount == 3)
|
||||
{
|
||||
buffer AnswersF64 = ReadEntireFile(Args[2]);
|
||||
if(AnswersF64.Count >= sizeof(f64))
|
||||
{
|
||||
f64 *AnswerValues = (f64 *)AnswersF64.Data;
|
||||
|
||||
fprintf(stdout, "\nValidation:\n");
|
||||
|
||||
u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64);
|
||||
if(PairCount != RefAnswerCount)
|
||||
{
|
||||
fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount);
|
||||
}
|
||||
|
||||
f64 RefSum = AnswerValues[RefAnswerCount];
|
||||
fprintf(stdout, "Reference sum: %.16f\n", RefSum);
|
||||
fprintf(stdout, "Difference: %.16f\n", Sum - RefSum);
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FreeBuffer(&ParsedValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Malformed input JSON\n");
|
||||
}
|
||||
|
||||
FreeBuffer(&InputJSON);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]);
|
||||
fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]);
|
||||
}
|
||||
|
||||
Prof_End = ReadCPUTimer();
|
||||
|
||||
if(Result == 0)
|
||||
{
|
||||
u64 TotalCPUElapsed = Prof_End - Prof_Begin;
|
||||
|
||||
u64 CPUFreq = EstimateCPUTimerFreq();
|
||||
if(CPUFreq)
|
||||
{
|
||||
printf("\nTotal time: %0.4fms (CPU freq %llu)\n", 1000.0 * (f64)TotalCPUElapsed / (f64)CPUFreq, CPUFreq);
|
||||
}
|
||||
|
||||
PrintTimeElapsed("Startup", TotalCPUElapsed, Prof_Begin, Prof_Read);
|
||||
PrintTimeElapsed("Read", TotalCPUElapsed, Prof_Read, Prof_MiscSetup);
|
||||
PrintTimeElapsed("MiscSetup", TotalCPUElapsed, Prof_MiscSetup, Prof_Parse);
|
||||
PrintTimeElapsed("Parse", TotalCPUElapsed, Prof_Parse, Prof_Sum);
|
||||
PrintTimeElapsed("Sum", TotalCPUElapsed, Prof_Sum, Prof_MiscOutput);
|
||||
PrintTimeElapsed("MiscOutput", TotalCPUElapsed, Prof_MiscOutput, Prof_End);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 76
|
||||
======================================================================== */
|
||||
|
||||
#include "listing_0074_platform_metrics.cpp"
|
||||
|
||||
struct profile_anchor
|
||||
{
|
||||
u64 TSCElapsed;
|
||||
u64 HitCount;
|
||||
char const *Label;
|
||||
};
|
||||
|
||||
struct profiler
|
||||
{
|
||||
profile_anchor Anchors[4096];
|
||||
|
||||
u64 StartTSC;
|
||||
u64 EndTSC;
|
||||
};
|
||||
static profiler GlobalProfiler;
|
||||
|
||||
struct profile_block
|
||||
{
|
||||
profile_block(char const *Label_, u32 AnchorIndex_)
|
||||
{
|
||||
AnchorIndex = AnchorIndex_;
|
||||
Label = Label_;
|
||||
StartTSC = ReadCPUTimer();
|
||||
}
|
||||
|
||||
~profile_block(void)
|
||||
{
|
||||
u64 Elapsed = ReadCPUTimer() - StartTSC;
|
||||
|
||||
profile_anchor *Anchor = GlobalProfiler.Anchors + AnchorIndex;
|
||||
Anchor->TSCElapsed += Elapsed;
|
||||
++Anchor->HitCount;
|
||||
|
||||
/* NOTE(casey): This write happens every time solely because there is no
|
||||
straightforward way in C++ to have the same ease-of-use. In a better programming
|
||||
language, it would be simple to have the anchor points gathered and labeled at compile
|
||||
time, and this repetative write would be eliminated. */
|
||||
Anchor->Label = Label;
|
||||
}
|
||||
|
||||
char const *Label;
|
||||
u64 StartTSC;
|
||||
u32 AnchorIndex;
|
||||
};
|
||||
|
||||
#define NameConcat2(A, B) A##B
|
||||
#define NameConcat(A, B) NameConcat2(A, B)
|
||||
#define TimeBlock(Name) profile_block NameConcat(Block, __LINE__)(Name, __COUNTER__ + 1);
|
||||
#define TimeFunction TimeBlock(__func__)
|
||||
|
||||
static void PrintTimeElapsed(u64 TotalTSCElapsed, profile_anchor *Anchor)
|
||||
{
|
||||
u64 Elapsed = Anchor->TSCElapsed;
|
||||
f64 Percent = 100.0 * ((f64)Elapsed / (f64)TotalTSCElapsed);
|
||||
printf(" %s[%llu]: %llu (%.2f%%)\n", Anchor->Label, Anchor->HitCount, Elapsed, Percent);
|
||||
}
|
||||
|
||||
static void BeginProfile(void)
|
||||
{
|
||||
GlobalProfiler.StartTSC = ReadCPUTimer();
|
||||
}
|
||||
|
||||
static void EndAndPrintProfile()
|
||||
{
|
||||
GlobalProfiler.EndTSC = ReadCPUTimer();
|
||||
u64 CPUFreq = EstimateCPUTimerFreq();
|
||||
|
||||
u64 TotalCPUElapsed = GlobalProfiler.EndTSC - GlobalProfiler.StartTSC;
|
||||
|
||||
if(CPUFreq)
|
||||
{
|
||||
printf("\nTotal time: %0.4fms (CPU freq %llu)\n", 1000.0 * (f64)TotalCPUElapsed / (f64)CPUFreq, CPUFreq);
|
||||
}
|
||||
|
||||
for(u32 AnchorIndex = 0; AnchorIndex < ArrayCount(GlobalProfiler.Anchors); ++AnchorIndex)
|
||||
{
|
||||
profile_anchor *Anchor = GlobalProfiler.Anchors + AnchorIndex;
|
||||
if(Anchor->TSCElapsed)
|
||||
{
|
||||
PrintTimeElapsed(TotalCPUElapsed, Anchor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,509 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 77
|
||||
======================================================================== */
|
||||
|
||||
enum json_token_type
|
||||
{
|
||||
Token_end_of_stream,
|
||||
Token_error,
|
||||
|
||||
Token_open_brace,
|
||||
Token_open_bracket,
|
||||
Token_close_brace,
|
||||
Token_close_bracket,
|
||||
Token_comma,
|
||||
Token_colon,
|
||||
Token_string_literal,
|
||||
Token_number,
|
||||
Token_true,
|
||||
Token_false,
|
||||
Token_null,
|
||||
|
||||
Token_count,
|
||||
};
|
||||
|
||||
struct json_token
|
||||
{
|
||||
json_token_type Type;
|
||||
buffer Value;
|
||||
};
|
||||
|
||||
struct json_element
|
||||
{
|
||||
buffer Label;
|
||||
buffer Value;
|
||||
json_element *FirstSubElement;
|
||||
|
||||
json_element *NextSibling;
|
||||
};
|
||||
|
||||
struct json_parser
|
||||
{
|
||||
buffer Source;
|
||||
u64 At;
|
||||
b32 HadError;
|
||||
};
|
||||
|
||||
static b32 IsJSONDigit(buffer Source, u64 At)
|
||||
{
|
||||
b32 Result = false;
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Val = Source.Data[At];
|
||||
Result = ((Val >= '0') && (Val <= '9'));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static b32 IsJSONWhitespace(buffer Source, u64 At)
|
||||
{
|
||||
b32 Result = false;
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Val = Source.Data[At];
|
||||
Result = ((Val == ' ') || (Val == '\t') || (Val == '\n') || (Val == '\r'));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static b32 IsParsing(json_parser *Parser)
|
||||
{
|
||||
b32 Result = !Parser->HadError && IsInBounds(Parser->Source, Parser->At);
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void Error(json_parser *Parser, json_token Token, char const *Message)
|
||||
{
|
||||
Parser->HadError = true;
|
||||
fprintf(stderr, "ERROR: \"%.*s\" - %s\n", (u32)Token.Value.Count, (char *)Token.Value.Data, Message);
|
||||
}
|
||||
|
||||
static void ParseKeyword(buffer Source, u64 *At, buffer KeywordRemaining, json_token_type Type, json_token *Result)
|
||||
{
|
||||
if((Source.Count - *At) >= KeywordRemaining.Count)
|
||||
{
|
||||
buffer Check = Source;
|
||||
Check.Data += *At;
|
||||
Check.Count = KeywordRemaining.Count;
|
||||
if(AreEqual(Check, KeywordRemaining))
|
||||
{
|
||||
Result->Type = Type;
|
||||
Result->Value.Count += KeywordRemaining.Count;
|
||||
*At += KeywordRemaining.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static json_token GetJSONToken(json_parser *Parser)
|
||||
{
|
||||
json_token Result = {};
|
||||
|
||||
buffer Source = Parser->Source;
|
||||
u64 At = Parser->At;
|
||||
|
||||
while(IsJSONWhitespace(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
Result.Type = Token_error;
|
||||
Result.Value.Count = 1;
|
||||
Result.Value.Data = Source.Data + At;
|
||||
u8 Val = Source.Data[At++];
|
||||
switch(Val)
|
||||
{
|
||||
case '{': {Result.Type = Token_open_brace;} break;
|
||||
case '[': {Result.Type = Token_open_bracket;} break;
|
||||
case '}': {Result.Type = Token_close_brace;} break;
|
||||
case ']': {Result.Type = Token_close_bracket;} break;
|
||||
case ',': {Result.Type = Token_comma;} break;
|
||||
case ':': {Result.Type = Token_colon;} break;
|
||||
|
||||
case 'f':
|
||||
{
|
||||
ParseKeyword(Source, &At, CONSTANT_STRING("alse"), Token_false, &Result);
|
||||
} break;
|
||||
|
||||
case 'n':
|
||||
{
|
||||
ParseKeyword(Source, &At, CONSTANT_STRING("ull"), Token_null, &Result);
|
||||
} break;
|
||||
|
||||
case 't':
|
||||
{
|
||||
ParseKeyword(Source, &At, CONSTANT_STRING("rue"), Token_true, &Result);
|
||||
} break;
|
||||
|
||||
case '"':
|
||||
{
|
||||
Result.Type = Token_string_literal;
|
||||
|
||||
u64 StringStart = At;
|
||||
|
||||
while(IsInBounds(Source, At) && (Source.Data[At] != '"'))
|
||||
{
|
||||
if(IsInBounds(Source, (At + 1)) &&
|
||||
(Source.Data[At] == '\\') &&
|
||||
(Source.Data[At + 1] == '"'))
|
||||
{
|
||||
// NOTE(casey): Skip escaped quotation marks
|
||||
++At;
|
||||
}
|
||||
|
||||
++At;
|
||||
}
|
||||
|
||||
Result.Value.Data = Source.Data + StringStart;
|
||||
Result.Value.Count = At - StringStart;
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
} break;
|
||||
|
||||
case '-':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
{
|
||||
u64 Start = At - 1;
|
||||
Result.Type = Token_number;
|
||||
|
||||
// NOTE(casey): Move past a leading negative sign if one exists
|
||||
if((Val == '-') && IsInBounds(Source, At))
|
||||
{
|
||||
Val = Source.Data[At++];
|
||||
}
|
||||
|
||||
// NOTE(casey): If the leading digit wasn't 0, parse any digits before the decimal point
|
||||
if(Val != '0')
|
||||
{
|
||||
while(IsJSONDigit(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(casey): If there is a decimal point, parse any digits after the decimal point
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '.'))
|
||||
{
|
||||
++At;
|
||||
while(IsJSONDigit(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(casey): If it's in scientific notation, parse any digits after the "e"
|
||||
if(IsInBounds(Source, At) && ((Source.Data[At] == 'e') || (Source.Data[At] == 'E')))
|
||||
{
|
||||
++At;
|
||||
|
||||
if(IsInBounds(Source, At) && ((Source.Data[At] == '+') || (Source.Data[At] == '-')))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
|
||||
while(IsJSONDigit(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
}
|
||||
|
||||
Result.Value.Count = At - Start;
|
||||
} break;
|
||||
|
||||
default:
|
||||
{
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
Parser->At = At;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static json_element *ParseJSONList(json_parser *Parser, json_token_type EndType, b32 HasLabels);
|
||||
static json_element *ParseJSONElement(json_parser *Parser, buffer Label, json_token Value)
|
||||
{
|
||||
b32 Valid = true;
|
||||
|
||||
json_element *SubElement = 0;
|
||||
if(Value.Type == Token_open_bracket)
|
||||
{
|
||||
SubElement = ParseJSONList(Parser, Token_close_bracket, false);
|
||||
}
|
||||
else if(Value.Type == Token_open_brace)
|
||||
{
|
||||
SubElement = ParseJSONList(Parser, Token_close_brace, true);
|
||||
}
|
||||
else if((Value.Type == Token_string_literal) ||
|
||||
(Value.Type == Token_true) ||
|
||||
(Value.Type == Token_false) ||
|
||||
(Value.Type == Token_null) ||
|
||||
(Value.Type == Token_number))
|
||||
{
|
||||
// NOTE(casey): Nothing to do here, since there is no additional data
|
||||
}
|
||||
else
|
||||
{
|
||||
Valid = false;
|
||||
}
|
||||
|
||||
json_element *Result = 0;
|
||||
|
||||
if(Valid)
|
||||
{
|
||||
Result = (json_element *)malloc(sizeof(json_element));
|
||||
Result->Label = Label;
|
||||
Result->Value = Value.Value;
|
||||
Result->FirstSubElement = SubElement;
|
||||
Result->NextSibling = 0;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static json_element *ParseJSONList(json_parser *Parser, json_token_type EndType, b32 HasLabels)
|
||||
{
|
||||
json_element *FirstElement = {};
|
||||
json_element *LastElement = {};
|
||||
|
||||
while(IsParsing(Parser))
|
||||
{
|
||||
buffer Label = {};
|
||||
json_token Value = GetJSONToken(Parser);
|
||||
if(HasLabels)
|
||||
{
|
||||
if(Value.Type == Token_string_literal)
|
||||
{
|
||||
Label = Value.Value;
|
||||
|
||||
json_token Colon = GetJSONToken(Parser);
|
||||
if(Colon.Type == Token_colon)
|
||||
{
|
||||
Value = GetJSONToken(Parser);
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(Parser, Colon, "Expected colon after field name");
|
||||
}
|
||||
}
|
||||
else if(Value.Type != EndType)
|
||||
{
|
||||
Error(Parser, Value, "Unexpected token in JSON");
|
||||
}
|
||||
}
|
||||
|
||||
json_element *Element = ParseJSONElement(Parser, Label, Value);
|
||||
if(Element)
|
||||
{
|
||||
LastElement = (LastElement ? LastElement->NextSibling : FirstElement) = Element;
|
||||
}
|
||||
else if(Value.Type == EndType)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(Parser, Value, "Unexpected token in JSON");
|
||||
}
|
||||
|
||||
json_token Comma = GetJSONToken(Parser);
|
||||
if(Comma.Type == EndType)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if(Comma.Type != Token_comma)
|
||||
{
|
||||
Error(Parser, Comma, "Unexpected token in JSON");
|
||||
}
|
||||
}
|
||||
|
||||
return FirstElement;
|
||||
}
|
||||
|
||||
static json_element *ParseJSON(buffer InputJSON)
|
||||
{
|
||||
json_parser Parser = {};
|
||||
Parser.Source = InputJSON;
|
||||
|
||||
json_element *Result = ParseJSONElement(&Parser, {}, GetJSONToken(&Parser));
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void FreeJSON(json_element *Element)
|
||||
{
|
||||
while(Element)
|
||||
{
|
||||
json_element *FreeElement = Element;
|
||||
Element = Element->NextSibling;
|
||||
|
||||
FreeJSON(FreeElement->FirstSubElement);
|
||||
free(FreeElement);
|
||||
}
|
||||
}
|
||||
|
||||
static json_element *LookupElement(json_element *Object, buffer ElementName)
|
||||
{
|
||||
json_element *Result = 0;
|
||||
|
||||
if(Object)
|
||||
{
|
||||
for(json_element *Search = Object->FirstSubElement; Search; Search = Search->NextSibling)
|
||||
{
|
||||
if(AreEqual(Search->Label, ElementName))
|
||||
{
|
||||
Result = Search;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 ConvertJSONSign(buffer Source, u64 *AtResult)
|
||||
{
|
||||
u64 At = *AtResult;
|
||||
|
||||
f64 Result = 1.0;
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '-'))
|
||||
{
|
||||
Result = -1.0;
|
||||
++At;
|
||||
}
|
||||
|
||||
*AtResult = At;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 ConvertJSONNumber(buffer Source, u64 *AtResult)
|
||||
{
|
||||
u64 At = *AtResult;
|
||||
|
||||
f64 Result = 0.0;
|
||||
while(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Char = Source.Data[At] - (u8)'0';
|
||||
if(Char < 10)
|
||||
{
|
||||
Result = 10.0*Result + (f64)Char;
|
||||
++At;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*AtResult = At;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 ConvertElementToF64(json_element *Object, buffer ElementName)
|
||||
{
|
||||
f64 Result = 0.0;
|
||||
|
||||
json_element *Element = LookupElement(Object, ElementName);
|
||||
if(Element)
|
||||
{
|
||||
buffer Source = Element->Value;
|
||||
u64 At = 0;
|
||||
|
||||
f64 Sign = ConvertJSONSign(Source, &At);
|
||||
f64 Number = ConvertJSONNumber(Source, &At);
|
||||
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '.'))
|
||||
{
|
||||
++At;
|
||||
f64 C = 1.0 / 10.0;
|
||||
while(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Char = Source.Data[At] - (u8)'0';
|
||||
if(Char < 10)
|
||||
{
|
||||
Number = Number + C*(f64)Char;
|
||||
C *= 1.0 / 10.0;
|
||||
++At;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(IsInBounds(Source, At) && ((Source.Data[At] == 'e') || (Source.Data[At] == 'E')))
|
||||
{
|
||||
++At;
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '+'))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
|
||||
f64 ExponentSign = ConvertJSONSign(Source, &At);
|
||||
f64 Exponent = ExponentSign*ConvertJSONNumber(Source, &At);
|
||||
Number *= pow(10.0, Exponent);
|
||||
}
|
||||
|
||||
Result = Sign*Number;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static u64 ParseHaversinePairs(buffer InputJSON, u64 MaxPairCount, haversine_pair *Pairs)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
u64 PairCount = 0;
|
||||
|
||||
json_element *JSON = ParseJSON(InputJSON);
|
||||
|
||||
json_element *PairsArray = LookupElement(JSON, CONSTANT_STRING("pairs"));
|
||||
if(PairsArray)
|
||||
{
|
||||
for(json_element *Element = PairsArray->FirstSubElement;
|
||||
Element && (PairCount < MaxPairCount);
|
||||
Element = Element->NextSibling)
|
||||
{
|
||||
haversine_pair *Pair = Pairs + PairCount++;
|
||||
|
||||
Pair->X0 = ConvertElementToF64(Element, CONSTANT_STRING("x0"));
|
||||
Pair->Y0 = ConvertElementToF64(Element, CONSTANT_STRING("y0"));
|
||||
Pair->X1 = ConvertElementToF64(Element, CONSTANT_STRING("x1"));
|
||||
Pair->Y1 = ConvertElementToF64(Element, CONSTANT_STRING("y1"));
|
||||
}
|
||||
}
|
||||
|
||||
FreeJSON(JSON);
|
||||
|
||||
return PairCount;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 78
|
||||
======================================================================== */
|
||||
|
||||
/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot
|
||||
call fopen(). If we replace fopen() with fopen_s() to avoid the warning,
|
||||
then the code doesn't compile on Linux anymore, since fopen_s() does not
|
||||
exist there.
|
||||
|
||||
What exactly the CRT maintainers were thinking when they made this choice,
|
||||
I have no idea. */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int32_t b32;
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0]))
|
||||
|
||||
struct haversine_pair
|
||||
{
|
||||
f64 X0, Y0;
|
||||
f64 X1, Y1;
|
||||
};
|
||||
|
||||
#include "listing_0076_simple_profiler.cpp"
|
||||
#include "listing_0065_haversine_formula.cpp"
|
||||
#include "listing_0068_buffer.cpp"
|
||||
#include "listing_0077_profiled_lookup_json_parser.cpp"
|
||||
|
||||
static buffer ReadEntireFile(char *FileName)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
buffer Result = {};
|
||||
|
||||
FILE *File = fopen(FileName, "rb");
|
||||
if(File)
|
||||
{
|
||||
#if _WIN32
|
||||
struct __stat64 Stat;
|
||||
_stat64(FileName, &Stat);
|
||||
#else
|
||||
struct stat Stat;
|
||||
stat(FileName, &Stat);
|
||||
#endif
|
||||
|
||||
Result = AllocateBuffer(Stat.st_size);
|
||||
if(Result.Data)
|
||||
{
|
||||
if(fread(Result.Data, Result.Count, 1, File) != 1)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName);
|
||||
FreeBuffer(&Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
f64 Sum = 0;
|
||||
|
||||
f64 SumCoef = 1 / (f64)PairCount;
|
||||
for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex)
|
||||
{
|
||||
haversine_pair Pair = Pairs[PairIndex];
|
||||
f64 EarthRadius = 6372.8;
|
||||
f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius);
|
||||
Sum += SumCoef*Dist;
|
||||
}
|
||||
|
||||
return Sum;
|
||||
}
|
||||
|
||||
int main(int ArgCount, char **Args)
|
||||
{
|
||||
BeginProfile();
|
||||
|
||||
int Result = 1;
|
||||
|
||||
if((ArgCount == 2) || (ArgCount == 3))
|
||||
{
|
||||
buffer InputJSON = ReadEntireFile(Args[1]);
|
||||
|
||||
u32 MinimumJSONPairEncoding = 6*4;
|
||||
u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding;
|
||||
if(MaxPairCount)
|
||||
{
|
||||
buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair));
|
||||
if(ParsedValues.Count)
|
||||
{
|
||||
haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data;
|
||||
|
||||
u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs);
|
||||
f64 Sum = SumHaversineDistances(PairCount, Pairs);
|
||||
|
||||
Result = 0;
|
||||
|
||||
fprintf(stdout, "Input size: %llu\n", InputJSON.Count);
|
||||
fprintf(stdout, "Pair count: %llu\n", PairCount);
|
||||
fprintf(stdout, "Haversine sum: %.16f\n", Sum);
|
||||
|
||||
if(ArgCount == 3)
|
||||
{
|
||||
buffer AnswersF64 = ReadEntireFile(Args[2]);
|
||||
if(AnswersF64.Count >= sizeof(f64))
|
||||
{
|
||||
f64 *AnswerValues = (f64 *)AnswersF64.Data;
|
||||
|
||||
fprintf(stdout, "\nValidation:\n");
|
||||
|
||||
u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64);
|
||||
if(PairCount != RefAnswerCount)
|
||||
{
|
||||
fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount);
|
||||
}
|
||||
|
||||
f64 RefSum = AnswerValues[RefAnswerCount];
|
||||
fprintf(stdout, "Reference sum: %.16f\n", RefSum);
|
||||
fprintf(stdout, "Difference: %.16f\n", Sum - RefSum);
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FreeBuffer(&ParsedValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Malformed input JSON\n");
|
||||
}
|
||||
|
||||
FreeBuffer(&InputJSON);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]);
|
||||
fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]);
|
||||
}
|
||||
|
||||
if(Result == 0)
|
||||
{
|
||||
EndAndPrintProfile();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static_assert(__COUNTER__ < ArrayCount(profiler::Anchors), "Number of profile points exceeds size of profiler::Anchors array");
|
||||
@@ -0,0 +1,510 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 79
|
||||
======================================================================== */
|
||||
|
||||
enum json_token_type
|
||||
{
|
||||
Token_end_of_stream,
|
||||
Token_error,
|
||||
|
||||
Token_open_brace,
|
||||
Token_open_bracket,
|
||||
Token_close_brace,
|
||||
Token_close_bracket,
|
||||
Token_comma,
|
||||
Token_colon,
|
||||
Token_string_literal,
|
||||
Token_number,
|
||||
Token_true,
|
||||
Token_false,
|
||||
Token_null,
|
||||
|
||||
Token_count,
|
||||
};
|
||||
|
||||
struct json_token
|
||||
{
|
||||
json_token_type Type;
|
||||
buffer Value;
|
||||
};
|
||||
|
||||
struct json_element
|
||||
{
|
||||
buffer Label;
|
||||
buffer Value;
|
||||
json_element *FirstSubElement;
|
||||
|
||||
json_element *NextSibling;
|
||||
};
|
||||
|
||||
struct json_parser
|
||||
{
|
||||
buffer Source;
|
||||
u64 At;
|
||||
b32 HadError;
|
||||
};
|
||||
|
||||
static b32 IsJSONDigit(buffer Source, u64 At)
|
||||
{
|
||||
b32 Result = false;
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Val = Source.Data[At];
|
||||
Result = ((Val >= '0') && (Val <= '9'));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static b32 IsJSONWhitespace(buffer Source, u64 At)
|
||||
{
|
||||
b32 Result = false;
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Val = Source.Data[At];
|
||||
Result = ((Val == ' ') || (Val == '\t') || (Val == '\n') || (Val == '\r'));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static b32 IsParsing(json_parser *Parser)
|
||||
{
|
||||
b32 Result = !Parser->HadError && IsInBounds(Parser->Source, Parser->At);
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void Error(json_parser *Parser, json_token Token, char const *Message)
|
||||
{
|
||||
Parser->HadError = true;
|
||||
fprintf(stderr, "ERROR: \"%.*s\" - %s\n", (u32)Token.Value.Count, (char *)Token.Value.Data, Message);
|
||||
}
|
||||
|
||||
static void ParseKeyword(buffer Source, u64 *At, buffer KeywordRemaining, json_token_type Type, json_token *Result)
|
||||
{
|
||||
if((Source.Count - *At) >= KeywordRemaining.Count)
|
||||
{
|
||||
buffer Check = Source;
|
||||
Check.Data += *At;
|
||||
Check.Count = KeywordRemaining.Count;
|
||||
if(AreEqual(Check, KeywordRemaining))
|
||||
{
|
||||
Result->Type = Type;
|
||||
Result->Value.Count += KeywordRemaining.Count;
|
||||
*At += KeywordRemaining.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static json_token GetJSONToken(json_parser *Parser)
|
||||
{
|
||||
json_token Result = {};
|
||||
|
||||
buffer Source = Parser->Source;
|
||||
u64 At = Parser->At;
|
||||
|
||||
while(IsJSONWhitespace(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
Result.Type = Token_error;
|
||||
Result.Value.Count = 1;
|
||||
Result.Value.Data = Source.Data + At;
|
||||
u8 Val = Source.Data[At++];
|
||||
switch(Val)
|
||||
{
|
||||
case '{': {Result.Type = Token_open_brace;} break;
|
||||
case '[': {Result.Type = Token_open_bracket;} break;
|
||||
case '}': {Result.Type = Token_close_brace;} break;
|
||||
case ']': {Result.Type = Token_close_bracket;} break;
|
||||
case ',': {Result.Type = Token_comma;} break;
|
||||
case ':': {Result.Type = Token_colon;} break;
|
||||
|
||||
case 'f':
|
||||
{
|
||||
ParseKeyword(Source, &At, CONSTANT_STRING("alse"), Token_false, &Result);
|
||||
} break;
|
||||
|
||||
case 'n':
|
||||
{
|
||||
ParseKeyword(Source, &At, CONSTANT_STRING("ull"), Token_null, &Result);
|
||||
} break;
|
||||
|
||||
case 't':
|
||||
{
|
||||
ParseKeyword(Source, &At, CONSTANT_STRING("rue"), Token_true, &Result);
|
||||
} break;
|
||||
|
||||
case '"':
|
||||
{
|
||||
Result.Type = Token_string_literal;
|
||||
|
||||
u64 StringStart = At;
|
||||
|
||||
while(IsInBounds(Source, At) && (Source.Data[At] != '"'))
|
||||
{
|
||||
if(IsInBounds(Source, (At + 1)) &&
|
||||
(Source.Data[At] == '\\') &&
|
||||
(Source.Data[At + 1] == '"'))
|
||||
{
|
||||
// NOTE(casey): Skip escaped quotation marks
|
||||
++At;
|
||||
}
|
||||
|
||||
++At;
|
||||
}
|
||||
|
||||
Result.Value.Data = Source.Data + StringStart;
|
||||
Result.Value.Count = At - StringStart;
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
} break;
|
||||
|
||||
case '-':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
{
|
||||
u64 Start = At - 1;
|
||||
Result.Type = Token_number;
|
||||
|
||||
// NOTE(casey): Move past a leading negative sign if one exists
|
||||
if((Val == '-') && IsInBounds(Source, At))
|
||||
{
|
||||
Val = Source.Data[At++];
|
||||
}
|
||||
|
||||
// NOTE(casey): If the leading digit wasn't 0, parse any digits before the decimal point
|
||||
if(Val != '0')
|
||||
{
|
||||
while(IsJSONDigit(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(casey): If there is a decimal point, parse any digits after the decimal point
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '.'))
|
||||
{
|
||||
++At;
|
||||
while(IsJSONDigit(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(casey): If it's in scientific notation, parse any digits after the "e"
|
||||
if(IsInBounds(Source, At) && ((Source.Data[At] == 'e') || (Source.Data[At] == 'E')))
|
||||
{
|
||||
++At;
|
||||
|
||||
if(IsInBounds(Source, At) && ((Source.Data[At] == '+') || (Source.Data[At] == '-')))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
|
||||
while(IsJSONDigit(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
}
|
||||
|
||||
Result.Value.Count = At - Start;
|
||||
} break;
|
||||
|
||||
default:
|
||||
{
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
Parser->At = At;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static json_element *ParseJSONList(json_parser *Parser, json_token_type EndType, b32 HasLabels);
|
||||
static json_element *ParseJSONElement(json_parser *Parser, buffer Label, json_token Value)
|
||||
{
|
||||
b32 Valid = true;
|
||||
|
||||
json_element *SubElement = 0;
|
||||
if(Value.Type == Token_open_bracket)
|
||||
{
|
||||
SubElement = ParseJSONList(Parser, Token_close_bracket, false);
|
||||
}
|
||||
else if(Value.Type == Token_open_brace)
|
||||
{
|
||||
SubElement = ParseJSONList(Parser, Token_close_brace, true);
|
||||
}
|
||||
else if((Value.Type == Token_string_literal) ||
|
||||
(Value.Type == Token_true) ||
|
||||
(Value.Type == Token_false) ||
|
||||
(Value.Type == Token_null) ||
|
||||
(Value.Type == Token_number))
|
||||
{
|
||||
// NOTE(casey): Nothing to do here, since there is no additional data
|
||||
}
|
||||
else
|
||||
{
|
||||
Valid = false;
|
||||
}
|
||||
|
||||
json_element *Result = 0;
|
||||
|
||||
if(Valid)
|
||||
{
|
||||
Result = (json_element *)malloc(sizeof(json_element));
|
||||
Result->Label = Label;
|
||||
Result->Value = Value.Value;
|
||||
Result->FirstSubElement = SubElement;
|
||||
Result->NextSibling = 0;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static json_element *ParseJSONList(json_parser *Parser, json_token_type EndType, b32 HasLabels)
|
||||
{
|
||||
json_element *FirstElement = {};
|
||||
json_element *LastElement = {};
|
||||
|
||||
while(IsParsing(Parser))
|
||||
{
|
||||
buffer Label = {};
|
||||
json_token Value = GetJSONToken(Parser);
|
||||
if(HasLabels)
|
||||
{
|
||||
if(Value.Type == Token_string_literal)
|
||||
{
|
||||
Label = Value.Value;
|
||||
|
||||
json_token Colon = GetJSONToken(Parser);
|
||||
if(Colon.Type == Token_colon)
|
||||
{
|
||||
Value = GetJSONToken(Parser);
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(Parser, Colon, "Expected colon after field name");
|
||||
}
|
||||
}
|
||||
else if(Value.Type != EndType)
|
||||
{
|
||||
Error(Parser, Value, "Unexpected token in JSON");
|
||||
}
|
||||
}
|
||||
|
||||
json_element *Element = ParseJSONElement(Parser, Label, Value);
|
||||
if(Element)
|
||||
{
|
||||
LastElement = (LastElement ? LastElement->NextSibling : FirstElement) = Element;
|
||||
}
|
||||
else if(Value.Type == EndType)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(Parser, Value, "Unexpected token in JSON");
|
||||
}
|
||||
|
||||
json_token Comma = GetJSONToken(Parser);
|
||||
if(Comma.Type == EndType)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if(Comma.Type != Token_comma)
|
||||
{
|
||||
Error(Parser, Comma, "Unexpected token in JSON");
|
||||
}
|
||||
}
|
||||
|
||||
return FirstElement;
|
||||
}
|
||||
|
||||
static json_element *ParseJSON(buffer InputJSON)
|
||||
{
|
||||
json_parser Parser = {};
|
||||
Parser.Source = InputJSON;
|
||||
|
||||
json_element *Result = ParseJSONElement(&Parser, {}, GetJSONToken(&Parser));
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void FreeJSON(json_element *Element)
|
||||
{
|
||||
while(Element)
|
||||
{
|
||||
json_element *FreeElement = Element;
|
||||
Element = Element->NextSibling;
|
||||
|
||||
FreeJSON(FreeElement->FirstSubElement);
|
||||
free(FreeElement);
|
||||
}
|
||||
}
|
||||
|
||||
static json_element *LookupElement(json_element *Object, buffer ElementName)
|
||||
{
|
||||
json_element *Result = 0;
|
||||
|
||||
if(Object)
|
||||
{
|
||||
for(json_element *Search = Object->FirstSubElement; Search; Search = Search->NextSibling)
|
||||
{
|
||||
if(AreEqual(Search->Label, ElementName))
|
||||
{
|
||||
Result = Search;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 ConvertJSONSign(buffer Source, u64 *AtResult)
|
||||
{
|
||||
u64 At = *AtResult;
|
||||
|
||||
f64 Result = 1.0;
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '-'))
|
||||
{
|
||||
Result = -1.0;
|
||||
++At;
|
||||
}
|
||||
|
||||
*AtResult = At;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 ConvertJSONNumber(buffer Source, u64 *AtResult)
|
||||
{
|
||||
u64 At = *AtResult;
|
||||
|
||||
f64 Result = 0.0;
|
||||
while(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Char = Source.Data[At] - (u8)'0';
|
||||
if(Char < 10)
|
||||
{
|
||||
Result = 10.0*Result + (f64)Char;
|
||||
++At;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*AtResult = At;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 ConvertElementToF64(json_element *Object, buffer ElementName)
|
||||
{
|
||||
f64 Result = 0.0;
|
||||
|
||||
json_element *Element = LookupElement(Object, ElementName);
|
||||
if(Element)
|
||||
{
|
||||
buffer Source = Element->Value;
|
||||
u64 At = 0;
|
||||
|
||||
f64 Sign = ConvertJSONSign(Source, &At);
|
||||
f64 Number = ConvertJSONNumber(Source, &At);
|
||||
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '.'))
|
||||
{
|
||||
++At;
|
||||
f64 C = 1.0 / 10.0;
|
||||
while(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Char = Source.Data[At] - (u8)'0';
|
||||
if(Char < 10)
|
||||
{
|
||||
Number = Number + C*(f64)Char;
|
||||
C *= 1.0 / 10.0;
|
||||
++At;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(IsInBounds(Source, At) && ((Source.Data[At] == 'e') || (Source.Data[At] == 'E')))
|
||||
{
|
||||
++At;
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '+'))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
|
||||
f64 ExponentSign = ConvertJSONSign(Source, &At);
|
||||
f64 Exponent = ExponentSign*ConvertJSONNumber(Source, &At);
|
||||
Number *= pow(10.0, Exponent);
|
||||
}
|
||||
|
||||
Result = Sign*Number;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static u64 ParseHaversinePairs(buffer InputJSON, u64 MaxPairCount, haversine_pair *Pairs)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
u64 PairCount = 0;
|
||||
|
||||
json_element *JSON = ParseJSON(InputJSON);
|
||||
|
||||
json_element *PairsArray = LookupElement(JSON, CONSTANT_STRING("pairs"));
|
||||
if(PairsArray)
|
||||
{
|
||||
TimeBlock("Lookup and Convert");
|
||||
for(json_element *Element = PairsArray->FirstSubElement;
|
||||
Element && (PairCount < MaxPairCount);
|
||||
Element = Element->NextSibling)
|
||||
{
|
||||
haversine_pair *Pair = Pairs + PairCount++;
|
||||
|
||||
Pair->X0 = ConvertElementToF64(Element, CONSTANT_STRING("x0"));
|
||||
Pair->Y0 = ConvertElementToF64(Element, CONSTANT_STRING("y0"));
|
||||
Pair->X1 = ConvertElementToF64(Element, CONSTANT_STRING("x1"));
|
||||
Pair->Y1 = ConvertElementToF64(Element, CONSTANT_STRING("y1"));
|
||||
}
|
||||
}
|
||||
|
||||
FreeJSON(JSON);
|
||||
|
||||
return PairCount;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 80
|
||||
======================================================================== */
|
||||
|
||||
/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot
|
||||
call fopen(). If we replace fopen() with fopen_s() to avoid the warning,
|
||||
then the code doesn't compile on Linux anymore, since fopen_s() does not
|
||||
exist there.
|
||||
|
||||
What exactly the CRT maintainers were thinking when they made this choice,
|
||||
I have no idea. */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int32_t b32;
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0]))
|
||||
|
||||
struct haversine_pair
|
||||
{
|
||||
f64 X0, Y0;
|
||||
f64 X1, Y1;
|
||||
};
|
||||
|
||||
#include "listing_0076_simple_profiler.cpp"
|
||||
#include "listing_0065_haversine_formula.cpp"
|
||||
#include "listing_0068_buffer.cpp"
|
||||
#include "listing_0079_timedblock_lookup_json_parser.cpp"
|
||||
|
||||
static buffer ReadEntireFile(char *FileName)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
buffer Result = {};
|
||||
|
||||
FILE *File = fopen(FileName, "rb");
|
||||
if(File)
|
||||
{
|
||||
#if _WIN32
|
||||
struct __stat64 Stat;
|
||||
_stat64(FileName, &Stat);
|
||||
#else
|
||||
struct stat Stat;
|
||||
stat(FileName, &Stat);
|
||||
#endif
|
||||
|
||||
Result = AllocateBuffer(Stat.st_size);
|
||||
if(Result.Data)
|
||||
{
|
||||
if(fread(Result.Data, Result.Count, 1, File) != 1)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName);
|
||||
FreeBuffer(&Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
f64 Sum = 0;
|
||||
|
||||
f64 SumCoef = 1 / (f64)PairCount;
|
||||
for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex)
|
||||
{
|
||||
haversine_pair Pair = Pairs[PairIndex];
|
||||
f64 EarthRadius = 6372.8;
|
||||
f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius);
|
||||
Sum += SumCoef*Dist;
|
||||
}
|
||||
|
||||
return Sum;
|
||||
}
|
||||
|
||||
int main(int ArgCount, char **Args)
|
||||
{
|
||||
BeginProfile();
|
||||
|
||||
int Result = 1;
|
||||
|
||||
if((ArgCount == 2) || (ArgCount == 3))
|
||||
{
|
||||
buffer InputJSON = ReadEntireFile(Args[1]);
|
||||
|
||||
u32 MinimumJSONPairEncoding = 6*4;
|
||||
u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding;
|
||||
if(MaxPairCount)
|
||||
{
|
||||
buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair));
|
||||
if(ParsedValues.Count)
|
||||
{
|
||||
haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data;
|
||||
|
||||
u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs);
|
||||
f64 Sum = SumHaversineDistances(PairCount, Pairs);
|
||||
|
||||
Result = 0;
|
||||
|
||||
fprintf(stdout, "Input size: %llu\n", InputJSON.Count);
|
||||
fprintf(stdout, "Pair count: %llu\n", PairCount);
|
||||
fprintf(stdout, "Haversine sum: %.16f\n", Sum);
|
||||
|
||||
if(ArgCount == 3)
|
||||
{
|
||||
buffer AnswersF64 = ReadEntireFile(Args[2]);
|
||||
if(AnswersF64.Count >= sizeof(f64))
|
||||
{
|
||||
f64 *AnswerValues = (f64 *)AnswersF64.Data;
|
||||
|
||||
fprintf(stdout, "\nValidation:\n");
|
||||
|
||||
u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64);
|
||||
if(PairCount != RefAnswerCount)
|
||||
{
|
||||
fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount);
|
||||
}
|
||||
|
||||
f64 RefSum = AnswerValues[RefAnswerCount];
|
||||
fprintf(stdout, "Reference sum: %.16f\n", RefSum);
|
||||
fprintf(stdout, "Difference: %.16f\n", Sum - RefSum);
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FreeBuffer(&ParsedValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Malformed input JSON\n");
|
||||
}
|
||||
|
||||
FreeBuffer(&InputJSON);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]);
|
||||
fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]);
|
||||
}
|
||||
|
||||
if(Result == 0)
|
||||
{
|
||||
EndAndPrintProfile();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static_assert(__COUNTER__ < ArrayCount(profiler::Anchors), "Number of profile points exceeds size of profiler::Anchors array");
|
||||
@@ -0,0 +1,118 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 81
|
||||
======================================================================== */
|
||||
|
||||
#include "listing_0074_platform_metrics.cpp"
|
||||
|
||||
struct profile_anchor
|
||||
{
|
||||
u64 TSCElapsed;
|
||||
u64 TSCElapsedChildren;
|
||||
u64 HitCount;
|
||||
char const *Label;
|
||||
};
|
||||
|
||||
struct profiler
|
||||
{
|
||||
profile_anchor Anchors[4096];
|
||||
|
||||
u64 StartTSC;
|
||||
u64 EndTSC;
|
||||
};
|
||||
static profiler GlobalProfiler;
|
||||
static u32 GlobalProfilerParent;
|
||||
|
||||
struct profile_block
|
||||
{
|
||||
profile_block(char const *Label_, u32 AnchorIndex_)
|
||||
{
|
||||
ParentIndex = GlobalProfilerParent;
|
||||
|
||||
AnchorIndex = AnchorIndex_;
|
||||
Label = Label_;
|
||||
|
||||
GlobalProfilerParent = AnchorIndex;
|
||||
StartTSC = ReadCPUTimer();
|
||||
}
|
||||
|
||||
~profile_block(void)
|
||||
{
|
||||
u64 Elapsed = ReadCPUTimer() - StartTSC;
|
||||
GlobalProfilerParent = ParentIndex;
|
||||
|
||||
profile_anchor *Parent = GlobalProfiler.Anchors + ParentIndex;
|
||||
profile_anchor *Anchor = GlobalProfiler.Anchors + AnchorIndex;
|
||||
|
||||
Parent->TSCElapsedChildren += Elapsed;
|
||||
Anchor->TSCElapsed += Elapsed;
|
||||
++Anchor->HitCount;
|
||||
|
||||
/* NOTE(casey): This write happens every time solely because there is no
|
||||
straightforward way in C++ to have the same ease-of-use. In a better programming
|
||||
language, it would be simple to have the anchor points gathered and labeled at compile
|
||||
time, and this repetative write would be eliminated. */
|
||||
Anchor->Label = Label;
|
||||
}
|
||||
|
||||
char const *Label;
|
||||
u64 StartTSC;
|
||||
u32 ParentIndex;
|
||||
u32 AnchorIndex;
|
||||
};
|
||||
|
||||
#define NameConcat2(A, B) A##B
|
||||
#define NameConcat(A, B) NameConcat2(A, B)
|
||||
#define TimeBlock(Name) profile_block NameConcat(Block, __LINE__)(Name, __COUNTER__ + 1);
|
||||
#define TimeFunction TimeBlock(__func__)
|
||||
|
||||
static void PrintTimeElapsed(u64 TotalTSCElapsed, profile_anchor *Anchor)
|
||||
{
|
||||
u64 Elapsed = Anchor->TSCElapsed - Anchor->TSCElapsedChildren;
|
||||
f64 Percent = 100.0 * ((f64)Elapsed / (f64)TotalTSCElapsed);
|
||||
printf(" %s[%llu]: %llu (%.2f%%", Anchor->Label, Anchor->HitCount, Elapsed, Percent);
|
||||
if(Anchor->TSCElapsedChildren)
|
||||
{
|
||||
f64 PercentWithChildren = 100.0 * ((f64)Anchor->TSCElapsed / (f64)TotalTSCElapsed);
|
||||
printf(", %.2f%% w/children", PercentWithChildren);
|
||||
}
|
||||
printf(")\n");
|
||||
}
|
||||
|
||||
static void BeginProfile(void)
|
||||
{
|
||||
GlobalProfiler.StartTSC = ReadCPUTimer();
|
||||
}
|
||||
|
||||
static void EndAndPrintProfile()
|
||||
{
|
||||
GlobalProfiler.EndTSC = ReadCPUTimer();
|
||||
u64 CPUFreq = EstimateCPUTimerFreq();
|
||||
|
||||
u64 TotalCPUElapsed = GlobalProfiler.EndTSC - GlobalProfiler.StartTSC;
|
||||
|
||||
if(CPUFreq)
|
||||
{
|
||||
printf("\nTotal time: %0.4fms (CPU freq %llu)\n", 1000.0 * (f64)TotalCPUElapsed / (f64)CPUFreq, CPUFreq);
|
||||
}
|
||||
|
||||
for(u32 AnchorIndex = 0; AnchorIndex < ArrayCount(GlobalProfiler.Anchors); ++AnchorIndex)
|
||||
{
|
||||
profile_anchor *Anchor = GlobalProfiler.Anchors + AnchorIndex;
|
||||
if(Anchor->TSCElapsed)
|
||||
{
|
||||
PrintTimeElapsed(TotalCPUElapsed, Anchor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 82
|
||||
======================================================================== */
|
||||
|
||||
/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot
|
||||
call fopen(). If we replace fopen() with fopen_s() to avoid the warning,
|
||||
then the code doesn't compile on Linux anymore, since fopen_s() does not
|
||||
exist there.
|
||||
|
||||
What exactly the CRT maintainers were thinking when they made this choice,
|
||||
I have no idea. */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int32_t b32;
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0]))
|
||||
|
||||
struct haversine_pair
|
||||
{
|
||||
f64 X0, Y0;
|
||||
f64 X1, Y1;
|
||||
};
|
||||
|
||||
#include "listing_0081_nesting_profiler.cpp"
|
||||
#include "listing_0065_haversine_formula.cpp"
|
||||
#include "listing_0068_buffer.cpp"
|
||||
#include "listing_0079_timedblock_lookup_json_parser.cpp"
|
||||
|
||||
static buffer ReadEntireFile(char *FileName)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
buffer Result = {};
|
||||
|
||||
FILE *File = fopen(FileName, "rb");
|
||||
if(File)
|
||||
{
|
||||
#if _WIN32
|
||||
struct __stat64 Stat;
|
||||
_stat64(FileName, &Stat);
|
||||
#else
|
||||
struct stat Stat;
|
||||
stat(FileName, &Stat);
|
||||
#endif
|
||||
|
||||
Result = AllocateBuffer(Stat.st_size);
|
||||
if(Result.Data)
|
||||
{
|
||||
if(fread(Result.Data, Result.Count, 1, File) != 1)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName);
|
||||
FreeBuffer(&Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
f64 Sum = 0;
|
||||
|
||||
f64 SumCoef = 1 / (f64)PairCount;
|
||||
for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex)
|
||||
{
|
||||
haversine_pair Pair = Pairs[PairIndex];
|
||||
f64 EarthRadius = 6372.8;
|
||||
f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius);
|
||||
Sum += SumCoef*Dist;
|
||||
}
|
||||
|
||||
return Sum;
|
||||
}
|
||||
|
||||
int main(int ArgCount, char **Args)
|
||||
{
|
||||
BeginProfile();
|
||||
|
||||
int Result = 1;
|
||||
|
||||
if((ArgCount == 2) || (ArgCount == 3))
|
||||
{
|
||||
buffer InputJSON = ReadEntireFile(Args[1]);
|
||||
|
||||
u32 MinimumJSONPairEncoding = 6*4;
|
||||
u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding;
|
||||
if(MaxPairCount)
|
||||
{
|
||||
buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair));
|
||||
if(ParsedValues.Count)
|
||||
{
|
||||
haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data;
|
||||
|
||||
u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs);
|
||||
f64 Sum = SumHaversineDistances(PairCount, Pairs);
|
||||
|
||||
Result = 0;
|
||||
|
||||
fprintf(stdout, "Input size: %llu\n", InputJSON.Count);
|
||||
fprintf(stdout, "Pair count: %llu\n", PairCount);
|
||||
fprintf(stdout, "Haversine sum: %.16f\n", Sum);
|
||||
|
||||
if(ArgCount == 3)
|
||||
{
|
||||
buffer AnswersF64 = ReadEntireFile(Args[2]);
|
||||
if(AnswersF64.Count >= sizeof(f64))
|
||||
{
|
||||
f64 *AnswerValues = (f64 *)AnswersF64.Data;
|
||||
|
||||
fprintf(stdout, "\nValidation:\n");
|
||||
|
||||
u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64);
|
||||
if(PairCount != RefAnswerCount)
|
||||
{
|
||||
fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount);
|
||||
}
|
||||
|
||||
f64 RefSum = AnswerValues[RefAnswerCount];
|
||||
fprintf(stdout, "Reference sum: %.16f\n", RefSum);
|
||||
fprintf(stdout, "Difference: %.16f\n", Sum - RefSum);
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FreeBuffer(&ParsedValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Malformed input JSON\n");
|
||||
}
|
||||
|
||||
FreeBuffer(&InputJSON);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]);
|
||||
fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]);
|
||||
}
|
||||
|
||||
if(Result == 0)
|
||||
{
|
||||
EndAndPrintProfile();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static_assert(__COUNTER__ < ArrayCount(profiler::Anchors), "Number of profile points exceeds size of profiler::Anchors array");
|
||||
@@ -0,0 +1,512 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 83
|
||||
======================================================================== */
|
||||
|
||||
enum json_token_type
|
||||
{
|
||||
Token_end_of_stream,
|
||||
Token_error,
|
||||
|
||||
Token_open_brace,
|
||||
Token_open_bracket,
|
||||
Token_close_brace,
|
||||
Token_close_bracket,
|
||||
Token_comma,
|
||||
Token_colon,
|
||||
Token_string_literal,
|
||||
Token_number,
|
||||
Token_true,
|
||||
Token_false,
|
||||
Token_null,
|
||||
|
||||
Token_count,
|
||||
};
|
||||
|
||||
struct json_token
|
||||
{
|
||||
json_token_type Type;
|
||||
buffer Value;
|
||||
};
|
||||
|
||||
struct json_element
|
||||
{
|
||||
buffer Label;
|
||||
buffer Value;
|
||||
json_element *FirstSubElement;
|
||||
|
||||
json_element *NextSibling;
|
||||
};
|
||||
|
||||
struct json_parser
|
||||
{
|
||||
buffer Source;
|
||||
u64 At;
|
||||
b32 HadError;
|
||||
};
|
||||
|
||||
static b32 IsJSONDigit(buffer Source, u64 At)
|
||||
{
|
||||
b32 Result = false;
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Val = Source.Data[At];
|
||||
Result = ((Val >= '0') && (Val <= '9'));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static b32 IsJSONWhitespace(buffer Source, u64 At)
|
||||
{
|
||||
b32 Result = false;
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Val = Source.Data[At];
|
||||
Result = ((Val == ' ') || (Val == '\t') || (Val == '\n') || (Val == '\r'));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static b32 IsParsing(json_parser *Parser)
|
||||
{
|
||||
b32 Result = !Parser->HadError && IsInBounds(Parser->Source, Parser->At);
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void Error(json_parser *Parser, json_token Token, char const *Message)
|
||||
{
|
||||
Parser->HadError = true;
|
||||
fprintf(stderr, "ERROR: \"%.*s\" - %s\n", (u32)Token.Value.Count, (char *)Token.Value.Data, Message);
|
||||
}
|
||||
|
||||
static void ParseKeyword(buffer Source, u64 *At, buffer KeywordRemaining, json_token_type Type, json_token *Result)
|
||||
{
|
||||
if((Source.Count - *At) >= KeywordRemaining.Count)
|
||||
{
|
||||
buffer Check = Source;
|
||||
Check.Data += *At;
|
||||
Check.Count = KeywordRemaining.Count;
|
||||
if(AreEqual(Check, KeywordRemaining))
|
||||
{
|
||||
Result->Type = Type;
|
||||
Result->Value.Count += KeywordRemaining.Count;
|
||||
*At += KeywordRemaining.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static json_token GetJSONToken(json_parser *Parser)
|
||||
{
|
||||
json_token Result = {};
|
||||
|
||||
buffer Source = Parser->Source;
|
||||
u64 At = Parser->At;
|
||||
|
||||
while(IsJSONWhitespace(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
Result.Type = Token_error;
|
||||
Result.Value.Count = 1;
|
||||
Result.Value.Data = Source.Data + At;
|
||||
u8 Val = Source.Data[At++];
|
||||
switch(Val)
|
||||
{
|
||||
case '{': {Result.Type = Token_open_brace;} break;
|
||||
case '[': {Result.Type = Token_open_bracket;} break;
|
||||
case '}': {Result.Type = Token_close_brace;} break;
|
||||
case ']': {Result.Type = Token_close_bracket;} break;
|
||||
case ',': {Result.Type = Token_comma;} break;
|
||||
case ':': {Result.Type = Token_colon;} break;
|
||||
|
||||
case 'f':
|
||||
{
|
||||
ParseKeyword(Source, &At, CONSTANT_STRING("alse"), Token_false, &Result);
|
||||
} break;
|
||||
|
||||
case 'n':
|
||||
{
|
||||
ParseKeyword(Source, &At, CONSTANT_STRING("ull"), Token_null, &Result);
|
||||
} break;
|
||||
|
||||
case 't':
|
||||
{
|
||||
ParseKeyword(Source, &At, CONSTANT_STRING("rue"), Token_true, &Result);
|
||||
} break;
|
||||
|
||||
case '"':
|
||||
{
|
||||
Result.Type = Token_string_literal;
|
||||
|
||||
u64 StringStart = At;
|
||||
|
||||
while(IsInBounds(Source, At) && (Source.Data[At] != '"'))
|
||||
{
|
||||
if(IsInBounds(Source, (At + 1)) &&
|
||||
(Source.Data[At] == '\\') &&
|
||||
(Source.Data[At + 1] == '"'))
|
||||
{
|
||||
// NOTE(casey): Skip escaped quotation marks
|
||||
++At;
|
||||
}
|
||||
|
||||
++At;
|
||||
}
|
||||
|
||||
Result.Value.Data = Source.Data + StringStart;
|
||||
Result.Value.Count = At - StringStart;
|
||||
if(IsInBounds(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
} break;
|
||||
|
||||
case '-':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
{
|
||||
u64 Start = At - 1;
|
||||
Result.Type = Token_number;
|
||||
|
||||
// NOTE(casey): Move past a leading negative sign if one exists
|
||||
if((Val == '-') && IsInBounds(Source, At))
|
||||
{
|
||||
Val = Source.Data[At++];
|
||||
}
|
||||
|
||||
// NOTE(casey): If the leading digit wasn't 0, parse any digits before the decimal point
|
||||
if(Val != '0')
|
||||
{
|
||||
while(IsJSONDigit(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(casey): If there is a decimal point, parse any digits after the decimal point
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '.'))
|
||||
{
|
||||
++At;
|
||||
while(IsJSONDigit(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(casey): If it's in scientific notation, parse any digits after the "e"
|
||||
if(IsInBounds(Source, At) && ((Source.Data[At] == 'e') || (Source.Data[At] == 'E')))
|
||||
{
|
||||
++At;
|
||||
|
||||
if(IsInBounds(Source, At) && ((Source.Data[At] == '+') || (Source.Data[At] == '-')))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
|
||||
while(IsJSONDigit(Source, At))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
}
|
||||
|
||||
Result.Value.Count = At - Start;
|
||||
} break;
|
||||
|
||||
default:
|
||||
{
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
Parser->At = At;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static json_element *ParseJSONList(json_parser *Parser, json_token_type EndType, b32 HasLabels);
|
||||
static json_element *ParseJSONElement(json_parser *Parser, buffer Label, json_token Value)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
b32 Valid = true;
|
||||
|
||||
json_element *SubElement = 0;
|
||||
if(Value.Type == Token_open_bracket)
|
||||
{
|
||||
SubElement = ParseJSONList(Parser, Token_close_bracket, false);
|
||||
}
|
||||
else if(Value.Type == Token_open_brace)
|
||||
{
|
||||
SubElement = ParseJSONList(Parser, Token_close_brace, true);
|
||||
}
|
||||
else if((Value.Type == Token_string_literal) ||
|
||||
(Value.Type == Token_true) ||
|
||||
(Value.Type == Token_false) ||
|
||||
(Value.Type == Token_null) ||
|
||||
(Value.Type == Token_number))
|
||||
{
|
||||
// NOTE(casey): Nothing to do here, since there is no additional data
|
||||
}
|
||||
else
|
||||
{
|
||||
Valid = false;
|
||||
}
|
||||
|
||||
json_element *Result = 0;
|
||||
|
||||
if(Valid)
|
||||
{
|
||||
Result = (json_element *)malloc(sizeof(json_element));
|
||||
Result->Label = Label;
|
||||
Result->Value = Value.Value;
|
||||
Result->FirstSubElement = SubElement;
|
||||
Result->NextSibling = 0;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static json_element *ParseJSONList(json_parser *Parser, json_token_type EndType, b32 HasLabels)
|
||||
{
|
||||
json_element *FirstElement = {};
|
||||
json_element *LastElement = {};
|
||||
|
||||
while(IsParsing(Parser))
|
||||
{
|
||||
buffer Label = {};
|
||||
json_token Value = GetJSONToken(Parser);
|
||||
if(HasLabels)
|
||||
{
|
||||
if(Value.Type == Token_string_literal)
|
||||
{
|
||||
Label = Value.Value;
|
||||
|
||||
json_token Colon = GetJSONToken(Parser);
|
||||
if(Colon.Type == Token_colon)
|
||||
{
|
||||
Value = GetJSONToken(Parser);
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(Parser, Colon, "Expected colon after field name");
|
||||
}
|
||||
}
|
||||
else if(Value.Type != EndType)
|
||||
{
|
||||
Error(Parser, Value, "Unexpected token in JSON");
|
||||
}
|
||||
}
|
||||
|
||||
json_element *Element = ParseJSONElement(Parser, Label, Value);
|
||||
if(Element)
|
||||
{
|
||||
LastElement = (LastElement ? LastElement->NextSibling : FirstElement) = Element;
|
||||
}
|
||||
else if(Value.Type == EndType)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(Parser, Value, "Unexpected token in JSON");
|
||||
}
|
||||
|
||||
json_token Comma = GetJSONToken(Parser);
|
||||
if(Comma.Type == EndType)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if(Comma.Type != Token_comma)
|
||||
{
|
||||
Error(Parser, Comma, "Unexpected token in JSON");
|
||||
}
|
||||
}
|
||||
|
||||
return FirstElement;
|
||||
}
|
||||
|
||||
static json_element *ParseJSON(buffer InputJSON)
|
||||
{
|
||||
json_parser Parser = {};
|
||||
Parser.Source = InputJSON;
|
||||
|
||||
json_element *Result = ParseJSONElement(&Parser, {}, GetJSONToken(&Parser));
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void FreeJSON(json_element *Element)
|
||||
{
|
||||
while(Element)
|
||||
{
|
||||
json_element *FreeElement = Element;
|
||||
Element = Element->NextSibling;
|
||||
|
||||
FreeJSON(FreeElement->FirstSubElement);
|
||||
free(FreeElement);
|
||||
}
|
||||
}
|
||||
|
||||
static json_element *LookupElement(json_element *Object, buffer ElementName)
|
||||
{
|
||||
json_element *Result = 0;
|
||||
|
||||
if(Object)
|
||||
{
|
||||
for(json_element *Search = Object->FirstSubElement; Search; Search = Search->NextSibling)
|
||||
{
|
||||
if(AreEqual(Search->Label, ElementName))
|
||||
{
|
||||
Result = Search;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 ConvertJSONSign(buffer Source, u64 *AtResult)
|
||||
{
|
||||
u64 At = *AtResult;
|
||||
|
||||
f64 Result = 1.0;
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '-'))
|
||||
{
|
||||
Result = -1.0;
|
||||
++At;
|
||||
}
|
||||
|
||||
*AtResult = At;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 ConvertJSONNumber(buffer Source, u64 *AtResult)
|
||||
{
|
||||
u64 At = *AtResult;
|
||||
|
||||
f64 Result = 0.0;
|
||||
while(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Char = Source.Data[At] - (u8)'0';
|
||||
if(Char < 10)
|
||||
{
|
||||
Result = 10.0*Result + (f64)Char;
|
||||
++At;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*AtResult = At;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 ConvertElementToF64(json_element *Object, buffer ElementName)
|
||||
{
|
||||
f64 Result = 0.0;
|
||||
|
||||
json_element *Element = LookupElement(Object, ElementName);
|
||||
if(Element)
|
||||
{
|
||||
buffer Source = Element->Value;
|
||||
u64 At = 0;
|
||||
|
||||
f64 Sign = ConvertJSONSign(Source, &At);
|
||||
f64 Number = ConvertJSONNumber(Source, &At);
|
||||
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '.'))
|
||||
{
|
||||
++At;
|
||||
f64 C = 1.0 / 10.0;
|
||||
while(IsInBounds(Source, At))
|
||||
{
|
||||
u8 Char = Source.Data[At] - (u8)'0';
|
||||
if(Char < 10)
|
||||
{
|
||||
Number = Number + C*(f64)Char;
|
||||
C *= 1.0 / 10.0;
|
||||
++At;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(IsInBounds(Source, At) && ((Source.Data[At] == 'e') || (Source.Data[At] == 'E')))
|
||||
{
|
||||
++At;
|
||||
if(IsInBounds(Source, At) && (Source.Data[At] == '+'))
|
||||
{
|
||||
++At;
|
||||
}
|
||||
|
||||
f64 ExponentSign = ConvertJSONSign(Source, &At);
|
||||
f64 Exponent = ExponentSign*ConvertJSONNumber(Source, &At);
|
||||
Number *= pow(10.0, Exponent);
|
||||
}
|
||||
|
||||
Result = Sign*Number;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static u64 ParseHaversinePairs(buffer InputJSON, u64 MaxPairCount, haversine_pair *Pairs)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
u64 PairCount = 0;
|
||||
|
||||
json_element *JSON = ParseJSON(InputJSON);
|
||||
|
||||
json_element *PairsArray = LookupElement(JSON, CONSTANT_STRING("pairs"));
|
||||
if(PairsArray)
|
||||
{
|
||||
TimeBlock("Lookup and Convert");
|
||||
for(json_element *Element = PairsArray->FirstSubElement;
|
||||
Element && (PairCount < MaxPairCount);
|
||||
Element = Element->NextSibling)
|
||||
{
|
||||
haversine_pair *Pair = Pairs + PairCount++;
|
||||
|
||||
Pair->X0 = ConvertElementToF64(Element, CONSTANT_STRING("x0"));
|
||||
Pair->Y0 = ConvertElementToF64(Element, CONSTANT_STRING("y0"));
|
||||
Pair->X1 = ConvertElementToF64(Element, CONSTANT_STRING("x1"));
|
||||
Pair->Y1 = ConvertElementToF64(Element, CONSTANT_STRING("y1"));
|
||||
}
|
||||
}
|
||||
|
||||
FreeJSON(JSON);
|
||||
|
||||
return PairCount;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 84
|
||||
======================================================================== */
|
||||
|
||||
/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot
|
||||
call fopen(). If we replace fopen() with fopen_s() to avoid the warning,
|
||||
then the code doesn't compile on Linux anymore, since fopen_s() does not
|
||||
exist there.
|
||||
|
||||
What exactly the CRT maintainers were thinking when they made this choice,
|
||||
I have no idea. */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int32_t b32;
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0]))
|
||||
|
||||
struct haversine_pair
|
||||
{
|
||||
f64 X0, Y0;
|
||||
f64 X1, Y1;
|
||||
};
|
||||
|
||||
#include "listing_0081_nesting_profiler.cpp"
|
||||
#include "listing_0065_haversine_formula.cpp"
|
||||
#include "listing_0068_buffer.cpp"
|
||||
#include "listing_0083_recursive_timed_lookup_json_parser.cpp"
|
||||
|
||||
static buffer ReadEntireFile(char *FileName)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
buffer Result = {};
|
||||
|
||||
FILE *File = fopen(FileName, "rb");
|
||||
if(File)
|
||||
{
|
||||
#if _WIN32
|
||||
struct __stat64 Stat;
|
||||
_stat64(FileName, &Stat);
|
||||
#else
|
||||
struct stat Stat;
|
||||
stat(FileName, &Stat);
|
||||
#endif
|
||||
|
||||
Result = AllocateBuffer(Stat.st_size);
|
||||
if(Result.Data)
|
||||
{
|
||||
if(fread(Result.Data, Result.Count, 1, File) != 1)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName);
|
||||
FreeBuffer(&Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
f64 Sum = 0;
|
||||
|
||||
f64 SumCoef = 1 / (f64)PairCount;
|
||||
for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex)
|
||||
{
|
||||
haversine_pair Pair = Pairs[PairIndex];
|
||||
f64 EarthRadius = 6372.8;
|
||||
f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius);
|
||||
Sum += SumCoef*Dist;
|
||||
}
|
||||
|
||||
return Sum;
|
||||
}
|
||||
|
||||
int main(int ArgCount, char **Args)
|
||||
{
|
||||
BeginProfile();
|
||||
|
||||
int Result = 1;
|
||||
|
||||
if((ArgCount == 2) || (ArgCount == 3))
|
||||
{
|
||||
buffer InputJSON = ReadEntireFile(Args[1]);
|
||||
|
||||
u32 MinimumJSONPairEncoding = 6*4;
|
||||
u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding;
|
||||
if(MaxPairCount)
|
||||
{
|
||||
buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair));
|
||||
if(ParsedValues.Count)
|
||||
{
|
||||
haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data;
|
||||
|
||||
u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs);
|
||||
f64 Sum = SumHaversineDistances(PairCount, Pairs);
|
||||
|
||||
Result = 0;
|
||||
|
||||
fprintf(stdout, "Input size: %llu\n", InputJSON.Count);
|
||||
fprintf(stdout, "Pair count: %llu\n", PairCount);
|
||||
fprintf(stdout, "Haversine sum: %.16f\n", Sum);
|
||||
|
||||
if(ArgCount == 3)
|
||||
{
|
||||
buffer AnswersF64 = ReadEntireFile(Args[2]);
|
||||
if(AnswersF64.Count >= sizeof(f64))
|
||||
{
|
||||
f64 *AnswerValues = (f64 *)AnswersF64.Data;
|
||||
|
||||
fprintf(stdout, "\nValidation:\n");
|
||||
|
||||
u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64);
|
||||
if(PairCount != RefAnswerCount)
|
||||
{
|
||||
fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount);
|
||||
}
|
||||
|
||||
f64 RefSum = AnswerValues[RefAnswerCount];
|
||||
fprintf(stdout, "Reference sum: %.16f\n", RefSum);
|
||||
fprintf(stdout, "Difference: %.16f\n", Sum - RefSum);
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FreeBuffer(&ParsedValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Malformed input JSON\n");
|
||||
}
|
||||
|
||||
FreeBuffer(&InputJSON);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]);
|
||||
fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]);
|
||||
}
|
||||
|
||||
if(Result == 0)
|
||||
{
|
||||
EndAndPrintProfile();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static_assert(__COUNTER__ < ArrayCount(profiler::Anchors), "Number of profile points exceeds size of profiler::Anchors array");
|
||||
@@ -0,0 +1,125 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 85
|
||||
======================================================================== */
|
||||
|
||||
#include "listing_0074_platform_metrics.cpp"
|
||||
|
||||
struct profile_anchor
|
||||
{
|
||||
u64 TSCElapsed;
|
||||
u64 TSCElapsedChildren;
|
||||
u64 TSCElapsedAtRoot;
|
||||
u64 HitCount;
|
||||
char const *Label;
|
||||
};
|
||||
|
||||
struct profiler
|
||||
{
|
||||
profile_anchor Anchors[4096];
|
||||
|
||||
u64 StartTSC;
|
||||
u64 EndTSC;
|
||||
};
|
||||
static profiler GlobalProfiler;
|
||||
static u32 GlobalProfilerParent;
|
||||
|
||||
struct profile_block
|
||||
{
|
||||
profile_block(char const *Label_, u32 AnchorIndex_)
|
||||
{
|
||||
ParentIndex = GlobalProfilerParent;
|
||||
|
||||
AnchorIndex = AnchorIndex_;
|
||||
Label = Label_;
|
||||
|
||||
profile_anchor *Anchor = GlobalProfiler.Anchors + AnchorIndex;
|
||||
OldTSCElapsedAtRoot = Anchor->TSCElapsedAtRoot;
|
||||
|
||||
GlobalProfilerParent = AnchorIndex;
|
||||
StartTSC = ReadCPUTimer();
|
||||
}
|
||||
|
||||
~profile_block(void)
|
||||
{
|
||||
u64 Elapsed = ReadCPUTimer() - StartTSC;
|
||||
GlobalProfilerParent = ParentIndex;
|
||||
|
||||
profile_anchor *Parent = GlobalProfiler.Anchors + ParentIndex;
|
||||
profile_anchor *Anchor = GlobalProfiler.Anchors + AnchorIndex;
|
||||
|
||||
Parent->TSCElapsedChildren += Elapsed;
|
||||
Anchor->TSCElapsedAtRoot = OldTSCElapsedAtRoot + Elapsed;
|
||||
Anchor->TSCElapsed += Elapsed;
|
||||
++Anchor->HitCount;
|
||||
|
||||
/* NOTE(casey): This write happens every time solely because there is no
|
||||
straightforward way in C++ to have the same ease-of-use. In a better programming
|
||||
language, it would be simple to have the anchor points gathered and labeled at compile
|
||||
time, and this repetative write would be eliminated. */
|
||||
Anchor->Label = Label;
|
||||
}
|
||||
|
||||
char const *Label;
|
||||
u64 OldTSCElapsedAtRoot;
|
||||
u64 StartTSC;
|
||||
u32 ParentIndex;
|
||||
u32 AnchorIndex;
|
||||
};
|
||||
|
||||
#define NameConcat2(A, B) A##B
|
||||
#define NameConcat(A, B) NameConcat2(A, B)
|
||||
#define TimeBlock(Name) profile_block NameConcat(Block, __LINE__)(Name, __COUNTER__ + 1);
|
||||
#define TimeFunction TimeBlock(__func__)
|
||||
|
||||
static void PrintTimeElapsed(u64 TotalTSCElapsed, profile_anchor *Anchor)
|
||||
{
|
||||
u64 TSCElapsedSelf = Anchor->TSCElapsed - Anchor->TSCElapsedChildren;
|
||||
f64 Percent = 100.0 * ((f64)TSCElapsedSelf / (f64)TotalTSCElapsed);
|
||||
printf(" %s[%llu]: %llu (%.2f%%", Anchor->Label, Anchor->HitCount, TSCElapsedSelf, Percent);
|
||||
if(Anchor->TSCElapsedAtRoot != TSCElapsedSelf)
|
||||
{
|
||||
f64 PercentWithChildren = 100.0 * ((f64)Anchor->TSCElapsedAtRoot / (f64)TotalTSCElapsed);
|
||||
printf(", %.2f%% w/children", PercentWithChildren);
|
||||
}
|
||||
printf(")\n");
|
||||
}
|
||||
|
||||
static void BeginProfile(void)
|
||||
{
|
||||
GlobalProfiler.StartTSC = ReadCPUTimer();
|
||||
}
|
||||
|
||||
static void EndAndPrintProfile()
|
||||
{
|
||||
GlobalProfiler.EndTSC = ReadCPUTimer();
|
||||
u64 CPUFreq = EstimateCPUTimerFreq();
|
||||
|
||||
u64 TotalCPUElapsed = GlobalProfiler.EndTSC - GlobalProfiler.StartTSC;
|
||||
|
||||
if(CPUFreq)
|
||||
{
|
||||
printf("\nTotal time: %0.4fms (CPU freq %llu)\n", 1000.0 * (f64)TotalCPUElapsed / (f64)CPUFreq, CPUFreq);
|
||||
}
|
||||
|
||||
for(u32 AnchorIndex = 0; AnchorIndex < ArrayCount(GlobalProfiler.Anchors); ++AnchorIndex)
|
||||
{
|
||||
profile_anchor *Anchor = GlobalProfiler.Anchors + AnchorIndex;
|
||||
if(Anchor->TSCElapsed)
|
||||
{
|
||||
PrintTimeElapsed(TotalCPUElapsed, Anchor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 86
|
||||
======================================================================== */
|
||||
|
||||
/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot
|
||||
call fopen(). If we replace fopen() with fopen_s() to avoid the warning,
|
||||
then the code doesn't compile on Linux anymore, since fopen_s() does not
|
||||
exist there.
|
||||
|
||||
What exactly the CRT maintainers were thinking when they made this choice,
|
||||
I have no idea. */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int32_t b32;
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0]))
|
||||
|
||||
struct haversine_pair
|
||||
{
|
||||
f64 X0, Y0;
|
||||
f64 X1, Y1;
|
||||
};
|
||||
|
||||
#include "listing_0085_recursive_profiler.cpp"
|
||||
#include "listing_0065_haversine_formula.cpp"
|
||||
#include "listing_0068_buffer.cpp"
|
||||
#include "listing_0083_recursive_timed_lookup_json_parser.cpp"
|
||||
|
||||
static buffer ReadEntireFile(char *FileName)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
buffer Result = {};
|
||||
|
||||
FILE *File = fopen(FileName, "rb");
|
||||
if(File)
|
||||
{
|
||||
#if _WIN32
|
||||
struct __stat64 Stat;
|
||||
_stat64(FileName, &Stat);
|
||||
#else
|
||||
struct stat Stat;
|
||||
stat(FileName, &Stat);
|
||||
#endif
|
||||
|
||||
Result = AllocateBuffer(Stat.st_size);
|
||||
if(Result.Data)
|
||||
{
|
||||
if(fread(Result.Data, Result.Count, 1, File) != 1)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName);
|
||||
FreeBuffer(&Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
f64 Sum = 0;
|
||||
|
||||
f64 SumCoef = 1 / (f64)PairCount;
|
||||
for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex)
|
||||
{
|
||||
haversine_pair Pair = Pairs[PairIndex];
|
||||
f64 EarthRadius = 6372.8;
|
||||
f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius);
|
||||
Sum += SumCoef*Dist;
|
||||
}
|
||||
|
||||
return Sum;
|
||||
}
|
||||
|
||||
int main(int ArgCount, char **Args)
|
||||
{
|
||||
BeginProfile();
|
||||
|
||||
int Result = 1;
|
||||
|
||||
if((ArgCount == 2) || (ArgCount == 3))
|
||||
{
|
||||
buffer InputJSON = ReadEntireFile(Args[1]);
|
||||
|
||||
u32 MinimumJSONPairEncoding = 6*4;
|
||||
u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding;
|
||||
if(MaxPairCount)
|
||||
{
|
||||
buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair));
|
||||
if(ParsedValues.Count)
|
||||
{
|
||||
haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data;
|
||||
|
||||
u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs);
|
||||
f64 Sum = SumHaversineDistances(PairCount, Pairs);
|
||||
|
||||
Result = 0;
|
||||
|
||||
fprintf(stdout, "Input size: %llu\n", InputJSON.Count);
|
||||
fprintf(stdout, "Pair count: %llu\n", PairCount);
|
||||
fprintf(stdout, "Haversine sum: %.16f\n", Sum);
|
||||
|
||||
if(ArgCount == 3)
|
||||
{
|
||||
buffer AnswersF64 = ReadEntireFile(Args[2]);
|
||||
if(AnswersF64.Count >= sizeof(f64))
|
||||
{
|
||||
f64 *AnswerValues = (f64 *)AnswersF64.Data;
|
||||
|
||||
fprintf(stdout, "\nValidation:\n");
|
||||
|
||||
u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64);
|
||||
if(PairCount != RefAnswerCount)
|
||||
{
|
||||
fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount);
|
||||
}
|
||||
|
||||
f64 RefSum = AnswerValues[RefAnswerCount];
|
||||
fprintf(stdout, "Reference sum: %.16f\n", RefSum);
|
||||
fprintf(stdout, "Difference: %.16f\n", Sum - RefSum);
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FreeBuffer(&ParsedValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Malformed input JSON\n");
|
||||
}
|
||||
|
||||
FreeBuffer(&InputJSON);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]);
|
||||
fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]);
|
||||
}
|
||||
|
||||
if(Result == 0)
|
||||
{
|
||||
EndAndPrintProfile();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static_assert(__COUNTER__ < ArrayCount(profiler::Anchors), "Number of profile points exceeds size of profiler::Anchors array");
|
||||
@@ -0,0 +1,123 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 87
|
||||
======================================================================== */
|
||||
|
||||
#include "listing_0074_platform_metrics.cpp"
|
||||
|
||||
struct profile_anchor
|
||||
{
|
||||
u64 TSCElapsedExclusive; // NOTE(casey): Does NOT include children
|
||||
u64 TSCElapsedInclusive; // NOTE(casey): DOES include children
|
||||
u64 HitCount;
|
||||
char const *Label;
|
||||
};
|
||||
|
||||
struct profiler
|
||||
{
|
||||
profile_anchor Anchors[4096];
|
||||
|
||||
u64 StartTSC;
|
||||
u64 EndTSC;
|
||||
};
|
||||
static profiler GlobalProfiler;
|
||||
static u32 GlobalProfilerParent;
|
||||
|
||||
struct profile_block
|
||||
{
|
||||
profile_block(char const *Label_, u32 AnchorIndex_)
|
||||
{
|
||||
ParentIndex = GlobalProfilerParent;
|
||||
|
||||
AnchorIndex = AnchorIndex_;
|
||||
Label = Label_;
|
||||
|
||||
profile_anchor *Anchor = GlobalProfiler.Anchors + AnchorIndex;
|
||||
OldTSCElapsedInclusive = Anchor->TSCElapsedInclusive;
|
||||
|
||||
GlobalProfilerParent = AnchorIndex;
|
||||
StartTSC = ReadCPUTimer();
|
||||
}
|
||||
|
||||
~profile_block(void)
|
||||
{
|
||||
u64 Elapsed = ReadCPUTimer() - StartTSC;
|
||||
GlobalProfilerParent = ParentIndex;
|
||||
|
||||
profile_anchor *Parent = GlobalProfiler.Anchors + ParentIndex;
|
||||
profile_anchor *Anchor = GlobalProfiler.Anchors + AnchorIndex;
|
||||
|
||||
Parent->TSCElapsedExclusive -= Elapsed;
|
||||
Anchor->TSCElapsedExclusive += Elapsed;
|
||||
Anchor->TSCElapsedInclusive = OldTSCElapsedInclusive + Elapsed;
|
||||
++Anchor->HitCount;
|
||||
|
||||
/* NOTE(casey): This write happens every time solely because there is no
|
||||
straightforward way in C++ to have the same ease-of-use. In a better programming
|
||||
language, it would be simple to have the anchor points gathered and labeled at compile
|
||||
time, and this repetative write would be eliminated. */
|
||||
Anchor->Label = Label;
|
||||
}
|
||||
|
||||
char const *Label;
|
||||
u64 OldTSCElapsedInclusive;
|
||||
u64 StartTSC;
|
||||
u32 ParentIndex;
|
||||
u32 AnchorIndex;
|
||||
};
|
||||
|
||||
#define NameConcat2(A, B) A##B
|
||||
#define NameConcat(A, B) NameConcat2(A, B)
|
||||
#define TimeBlock(Name) profile_block NameConcat(Block, __LINE__)(Name, __COUNTER__ + 1);
|
||||
#define TimeFunction TimeBlock(__func__)
|
||||
|
||||
static void PrintTimeElapsed(u64 TotalTSCElapsed, profile_anchor *Anchor)
|
||||
{
|
||||
f64 Percent = 100.0 * ((f64)Anchor->TSCElapsedExclusive / (f64)TotalTSCElapsed);
|
||||
printf(" %s[%llu]: %llu (%.2f%%", Anchor->Label, Anchor->HitCount, Anchor->TSCElapsedExclusive, Percent);
|
||||
if(Anchor->TSCElapsedInclusive != Anchor->TSCElapsedExclusive)
|
||||
{
|
||||
f64 PercentWithChildren = 100.0 * ((f64)Anchor->TSCElapsedInclusive / (f64)TotalTSCElapsed);
|
||||
printf(", %.2f%% w/children", PercentWithChildren);
|
||||
}
|
||||
printf(")\n");
|
||||
}
|
||||
|
||||
static void BeginProfile(void)
|
||||
{
|
||||
GlobalProfiler.StartTSC = ReadCPUTimer();
|
||||
}
|
||||
|
||||
static void EndAndPrintProfile()
|
||||
{
|
||||
GlobalProfiler.EndTSC = ReadCPUTimer();
|
||||
u64 CPUFreq = EstimateCPUTimerFreq();
|
||||
|
||||
u64 TotalCPUElapsed = GlobalProfiler.EndTSC - GlobalProfiler.StartTSC;
|
||||
|
||||
if(CPUFreq)
|
||||
{
|
||||
printf("\nTotal time: %0.4fms (CPU freq %llu)\n", 1000.0 * (f64)TotalCPUElapsed / (f64)CPUFreq, CPUFreq);
|
||||
}
|
||||
|
||||
for(u32 AnchorIndex = 0; AnchorIndex < ArrayCount(GlobalProfiler.Anchors); ++AnchorIndex)
|
||||
{
|
||||
profile_anchor *Anchor = GlobalProfiler.Anchors + AnchorIndex;
|
||||
if(Anchor->TSCElapsedInclusive)
|
||||
{
|
||||
PrintTimeElapsed(TotalCPUElapsed, Anchor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
/* ========================================================================
|
||||
|
||||
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Please see https://computerenhance.com for more information
|
||||
|
||||
======================================================================== */
|
||||
|
||||
/* ========================================================================
|
||||
LISTING 88
|
||||
======================================================================== */
|
||||
|
||||
/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot
|
||||
call fopen(). If we replace fopen() with fopen_s() to avoid the warning,
|
||||
then the code doesn't compile on Linux anymore, since fopen_s() does not
|
||||
exist there.
|
||||
|
||||
What exactly the CRT maintainers were thinking when they made this choice,
|
||||
I have no idea. */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int32_t b32;
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0]))
|
||||
|
||||
struct haversine_pair
|
||||
{
|
||||
f64 X0, Y0;
|
||||
f64 X1, Y1;
|
||||
};
|
||||
|
||||
#include "listing_0087_simplified_profiler.cpp"
|
||||
#include "listing_0065_haversine_formula.cpp"
|
||||
#include "listing_0068_buffer.cpp"
|
||||
#include "listing_0083_recursive_timed_lookup_json_parser.cpp"
|
||||
|
||||
static buffer ReadEntireFile(char *FileName)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
buffer Result = {};
|
||||
|
||||
FILE *File = fopen(FileName, "rb");
|
||||
if(File)
|
||||
{
|
||||
#if _WIN32
|
||||
struct __stat64 Stat;
|
||||
_stat64(FileName, &Stat);
|
||||
#else
|
||||
struct stat Stat;
|
||||
stat(FileName, &Stat);
|
||||
#endif
|
||||
|
||||
Result = AllocateBuffer(Stat.st_size);
|
||||
if(Result.Data)
|
||||
{
|
||||
if(fread(Result.Data, Result.Count, 1, File) != 1)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName);
|
||||
FreeBuffer(&Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs)
|
||||
{
|
||||
TimeFunction;
|
||||
|
||||
f64 Sum = 0;
|
||||
|
||||
f64 SumCoef = 1 / (f64)PairCount;
|
||||
for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex)
|
||||
{
|
||||
haversine_pair Pair = Pairs[PairIndex];
|
||||
f64 EarthRadius = 6372.8;
|
||||
f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius);
|
||||
Sum += SumCoef*Dist;
|
||||
}
|
||||
|
||||
return Sum;
|
||||
}
|
||||
|
||||
int main(int ArgCount, char **Args)
|
||||
{
|
||||
BeginProfile();
|
||||
|
||||
int Result = 1;
|
||||
|
||||
if((ArgCount == 2) || (ArgCount == 3))
|
||||
{
|
||||
buffer InputJSON = ReadEntireFile(Args[1]);
|
||||
|
||||
u32 MinimumJSONPairEncoding = 6*4;
|
||||
u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding;
|
||||
if(MaxPairCount)
|
||||
{
|
||||
buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair));
|
||||
if(ParsedValues.Count)
|
||||
{
|
||||
haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data;
|
||||
|
||||
u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs);
|
||||
f64 Sum = SumHaversineDistances(PairCount, Pairs);
|
||||
|
||||
Result = 0;
|
||||
|
||||
fprintf(stdout, "Input size: %llu\n", InputJSON.Count);
|
||||
fprintf(stdout, "Pair count: %llu\n", PairCount);
|
||||
fprintf(stdout, "Haversine sum: %.16f\n", Sum);
|
||||
|
||||
if(ArgCount == 3)
|
||||
{
|
||||
buffer AnswersF64 = ReadEntireFile(Args[2]);
|
||||
if(AnswersF64.Count >= sizeof(f64))
|
||||
{
|
||||
f64 *AnswerValues = (f64 *)AnswersF64.Data;
|
||||
|
||||
fprintf(stdout, "\nValidation:\n");
|
||||
|
||||
u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64);
|
||||
if(PairCount != RefAnswerCount)
|
||||
{
|
||||
fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount);
|
||||
}
|
||||
|
||||
f64 RefSum = AnswerValues[RefAnswerCount];
|
||||
fprintf(stdout, "Reference sum: %.16f\n", RefSum);
|
||||
fprintf(stdout, "Difference: %.16f\n", Sum - RefSum);
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FreeBuffer(&ParsedValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Malformed input JSON\n");
|
||||
}
|
||||
|
||||
FreeBuffer(&InputJSON);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]);
|
||||
fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]);
|
||||
}
|
||||
|
||||
if(Result == 0)
|
||||
{
|
||||
EndAndPrintProfile();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static_assert(__COUNTER__ < ArrayCount(profiler::Anchors), "Number of profile points exceeds size of profiler::Anchors array");
|
||||
Binary file not shown.
Reference in New Issue
Block a user