Compare commits

...

22 Commits

Author SHA1 Message Date
doylet d01cf53ff8 perfaware/part2: Add bandwidth tracking 2023-08-24 22:14:24 +10:00
doylet 5817060a8b perfaware/part2: Add disable switch for profiler 2023-07-24 22:07:16 +10:00
doylet bc2da4df4c perfaware/part2: Incorporate recursive profiler changes 2023-07-16 22:58:26 +10:00
doylet 3e9d6980df perfaware/part2: Do profiling of nested blocks 2023-07-08 00:44:38 +10:00
doylet b91869a49e perfaware/part2: Do RDTSC homework 2023-07-04 21:40:08 +10:00
doylet 662f5afd9b perfaware/part2: Parse strings to f64, calc & validate haversine 2023-07-01 23:12:57 +10:00
doylet 1f9a9bdc21 perfaware/part2: Extract the strings containing the haversine points 2023-07-01 22:32:50 +10:00
doylet f8ac671e3a perfaware/part2: Setup haversine parser task 2023-06-30 21:30:31 +10:00
doylet 7dfd1b1927 perfaware/part2: Write the haversine input generator 2023-06-26 23:51:45 +10:00
doylet a4658b1951 perfaware: Setup part2 2023-06-25 22:30:56 +10:00
doylet 9db292f616 perfware/part1: Support listing 0057 2023-06-20 23:19:17 +10:00
doylet 56cc90c57b perfware/part1: Handle 8088 cycle counter exercise in listing 56 2023-06-14 22:56:27 +10:00
doylet fb286c7f11 perfware/part1: Handle 8086 cycle count exercise 2023-06-14 22:29:40 +10:00
doylet 16b8483312 perfware/part1: Start to add clock counts for MOV 2023-06-13 22:14:48 +10:00
doylet d888968ce0 perfaware/part1: Support listing 0055 2023-04-18 22:38:07 +10:00
doylet dd83866f2b perfaware/part1: Correctly load and execute program from 8086 1mb memory 2023-04-18 21:41:19 +10:00
doylet 35fe0b899a perfaware/part1: Support listing 0053" 2023-04-18 21:28:41 +10:00
doylet 39ec4213d6 perfaware/part1: Support listing 0051 & 0052 2023-04-17 23:33:31 +10:00
doylet bd3dc5f2bc perfaware/part1: Support listing 0050" 2023-04-16 23:13:04 +10:00
doylet 00ad047d9a perfaware/part1: Support listing 0049 2023-04-16 22:29:00 +10:00
doylet 6f0715b0ca perfaware/part1: Support listing 0048 2023-04-16 21:36:10 +10:00
doylet 20be5d0892 perfaware/part1: Build ASM for exec files and test, move prototype to header 2023-04-16 21:22:00 +10:00
67 changed files with 61346 additions and 302 deletions
+3 -1
View File
@@ -1 +1,3 @@
*/build
Build
part2/*.f64
part2/*.json
+337
View File
@@ -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
-94
View File
@@ -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.
+23
View File
@@ -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
+13
View File
@@ -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.
+24
View File
@@ -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
+18
View File
@@ -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.
+38
View File
@@ -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
+38
View File
@@ -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.
+30
View File
@@ -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]
+19
View File
@@ -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.
+36
View File
@@ -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
+43
View File
@@ -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.
+35
View File
@@ -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
+36
View File
@@ -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.
+43
View File
@@ -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.
+41
View File
@@ -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
+72
View File
@@ -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)
+2
View File
@@ -0,0 +1,2 @@
»иЅРѕё ї  
‰ ‹‹и‰€и‹Љи‰‰и’иѓL’й•зѓK
+42
View File
@@ -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
+70
View File
@@ -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
+543 -181
View File
@@ -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);
@@ -418,9 +424,11 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it,
S86_ASSERT(op_decode_type != S86_OpDecodeType_Count && "Unknown instruction");
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;
}
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;
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"))) {
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;
}
exec_mode = true;
arg_index++;
} else if (S86_Str8_Equals(arg_str8, CLI_ARG_DUMP_STR8)) {
dump = true;
} else {
file_path = argv[1];
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;
}
}
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 = &register_file.ax, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_AL, .mnemonic_op_reg16 = S86_MnemonicOp_AX, .reg = &register_file.ax, .byte = S86_RegisterByte_Lo},
{.mnemonic_op = S86_MnemonicOp_AH, .mnemonic_op_reg16 = S86_MnemonicOp_AX, .reg = &register_file.ax, .byte = S86_RegisterByte_Hi},
{.mnemonic_op = S86_MnemonicOp_AX, .reg = &register_file.reg.file.ax, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_AL, .reg = &register_file.reg.file.ax, .byte = S86_RegisterByte_Lo},
{.mnemonic_op = S86_MnemonicOp_AH, .reg = &register_file.reg.file.ax, .byte = S86_RegisterByte_Hi},
{.mnemonic_op = S86_MnemonicOp_CX, .mnemonic_op_reg16 = S86_MnemonicOp_CX, .reg = &register_file.cx, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_CL, .mnemonic_op_reg16 = S86_MnemonicOp_CX, .reg = &register_file.cx, .byte = S86_RegisterByte_Lo},
{.mnemonic_op = S86_MnemonicOp_CH, .mnemonic_op_reg16 = S86_MnemonicOp_CX, .reg = &register_file.cx, .byte = S86_RegisterByte_Lo},
{.mnemonic_op = S86_MnemonicOp_CX, .reg = &register_file.reg.file.cx, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_CL, .reg = &register_file.reg.file.cx, .byte = S86_RegisterByte_Lo},
{.mnemonic_op = S86_MnemonicOp_CH, .reg = &register_file.reg.file.cx, .byte = S86_RegisterByte_Lo},
{.mnemonic_op = S86_MnemonicOp_DX, .mnemonic_op_reg16 = S86_MnemonicOp_DX, .reg = &register_file.dx, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_DL, .mnemonic_op_reg16 = S86_MnemonicOp_DX, .reg = &register_file.dx, .byte = S86_RegisterByte_Lo},
{.mnemonic_op = S86_MnemonicOp_DH, .mnemonic_op_reg16 = S86_MnemonicOp_DX, .reg = &register_file.dx, .byte = S86_RegisterByte_Hi},
{.mnemonic_op = S86_MnemonicOp_DX, .reg = &register_file.reg.file.dx, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_DL, .reg = &register_file.reg.file.dx, .byte = S86_RegisterByte_Lo},
{.mnemonic_op = S86_MnemonicOp_DH, .reg = &register_file.reg.file.dx, .byte = S86_RegisterByte_Hi},
{.mnemonic_op = S86_MnemonicOp_BX, .mnemonic_op_reg16 = S86_MnemonicOp_BX, .reg = &register_file.bx, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_BL, .mnemonic_op_reg16 = S86_MnemonicOp_BX, .reg = &register_file.bx, .byte = S86_RegisterByte_Lo},
{.mnemonic_op = S86_MnemonicOp_BH, .mnemonic_op_reg16 = S86_MnemonicOp_BX, .reg = &register_file.bx, .byte = S86_RegisterByte_Hi},
{.mnemonic_op = S86_MnemonicOp_BX, .reg = &register_file.reg.file.bx, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_BL, .reg = &register_file.reg.file.bx, .byte = S86_RegisterByte_Lo},
{.mnemonic_op = S86_MnemonicOp_BH, .reg = &register_file.reg.file.bx, .byte = S86_RegisterByte_Hi},
{.mnemonic_op = S86_MnemonicOp_SP, .mnemonic_op_reg16 = S86_MnemonicOp_SP, .reg = &register_file.sp, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_BP, .mnemonic_op_reg16 = S86_MnemonicOp_BP, .reg = &register_file.bp, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_SI, .mnemonic_op_reg16 = S86_MnemonicOp_SI, .reg = &register_file.si, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_DI, .mnemonic_op_reg16 = S86_MnemonicOp_DI, .reg = &register_file.di, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_SP, .reg = &register_file.reg.file.sp, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_BP, .reg = &register_file.reg.file.bp, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_SI, .reg = &register_file.reg.file.si, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_DI, .reg = &register_file.reg.file.di, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_ES, .mnemonic_op_reg16 = S86_MnemonicOp_ES, .reg = &register_file.es, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_CS, .mnemonic_op_reg16 = S86_MnemonicOp_CS, .reg = &register_file.cs, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_SS, .mnemonic_op_reg16 = S86_MnemonicOp_SS, .reg = &register_file.ss, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_DS, .mnemonic_op_reg16 = S86_MnemonicOp_DS, .reg = &register_file.ds, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_ES, .reg = &register_file.reg.file.es, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_CS, .reg = &register_file.reg.file.cs, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_SS, .reg = &register_file.reg.file.ss, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_DS, .reg = &register_file.reg.file.ds, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_BX_SI, .reg = &register_file.reg.file.bx, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_BX_DI, .reg = &register_file.reg.file.bx, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_BP_SI, .reg = &register_file.reg.file.bp, .byte = S86_RegisterByte_Nil},
{.mnemonic_op = S86_MnemonicOp_BP_DI, .reg = &register_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;
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*/
@@ -1384,11 +1569,10 @@ 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;
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;
}
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);
}
}
+80 -6
View File
@@ -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;
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);
+36
View File
@@ -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) {
+1
View File
@@ -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
// ============================================================================
+173
View File
@@ -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;
}
+174
View File
@@ -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;
}
+361
View File
@@ -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);
}
+149
View File
@@ -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, ...);
+53
View File
@@ -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;
}
+220
View File
@@ -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;
}
+165
View File
@@ -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;
}
+72
View File
@@ -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 = {};
}
+508
View File
@@ -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;
}
+70
View File
@@ -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();
}
+43
View File
@@ -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;
}
+49
View File
@@ -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;
}
+98
View File
@@ -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;
}
+209
View File
@@ -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;
}
+101
View File
@@ -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");
+118
View File
@@ -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");
+125
View File
@@ -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");
+123
View File
@@ -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");
BIN
View File
Binary file not shown.