From d93dd2e6a6b6c9adedd9cb718c5227f0bff62558 Mon Sep 17 00:00:00 2001 From: Brooke Vibber Date: Sun, 23 Aug 2026 15:18:25 -0700 Subject: [PATCH 1/2] step 1: sqr16_impl and imul16_impl take argument addresses first step to removing function call overhead --- mandel-core.s | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/mandel-core.s b/mandel-core.s index 64c5540..2e1c8b2 100644 --- a/mandel-core.s +++ b/mandel-core.s @@ -799,15 +799,15 @@ inner_loop: .endproc -; min base mem: 81 * 4 + 22 * 2 + 10 + 6 = 384 cyc -; max base mem: 92 * 4 + 27 * 2 + 50 + 6 = 478 cyc -; min ext-mem: 50 * 4 + 22 * 2 + 10 + 6 = 260 cyc -; max ext-mem: 55 * 4 + 27 * 2 + 50 + 6 = 330 cyc -.macro imul16_impl xe +; min base mem: 81 * 4 + 22 * 2 + 10 = 378 cyc +; max base mem: 92 * 4 + 27 * 2 + 50 = 472 cyc +; min ext-mem: 50 * 4 + 22 * 2 + 10 = 254 cyc +; max ext-mem: 55 * 4 + 27 * 2 + 50 = 324 cyc +.macro imul16_impl result, arg1, arg2, xe .scope - arg1 = FR0 ; 16-bit arg - arg2 = FR1 ; 16-bit arg - result = FR2 ; 32-bit result (output) + ;arg1 ; 16-bit arg + ;arg2 ; 16-bit arg + ;result ; 32-bit result (output) inter = temp2 ; 16-bit temporary (clobbered) ; h1l1 * h2l2 @@ -843,8 +843,6 @@ inner_loop: bpl arg2_pos ; 2 cyc sub16 result + 2, result + 2, arg1 ; 20 cyc arg2_pos: - - rts ; 6 cyc .endscope .endmacro @@ -877,15 +875,15 @@ inner_loop: .endmacro -; min base ram: 5 + 163 + 6 = 174 -; max base ram: 7 + 184 + 18 + 6 = 215 -; min ext ram: 5 + 132 + 6 = 143 -; max ext ram: 7 + 147 + 18 + 6 = 178 -.macro sqr16_impl xe +; min base ram: 5 + 163 + 3 = 171 +; max base ram: 7 + 184 + 18 = 209 +; min ext ram: 5 + 132 + 3 = 140 +; max ext ram: 7 + 147 + 18 = 172 +.macro sqr16_impl result, arg, xe .scope - arg = FR0 ; 16-bit arg + ; arg ; 16-bit arg negated = FR1 - result = FR2 ; 32-bit result + ; result ; 32-bit result ; 5-7 cycles lda arg + 1 ; 3 cyc @@ -894,29 +892,34 @@ inner_loop: arg_pos: sqr16_impl_inner result, arg, xe ; 163-184 / 132-147 cyc - rts ; 6 cyc + jmp done ; 3 cyc arg_neg: copy_neg16 negated, arg ; 18 cyc sqr16_impl_inner result, negated, xe ; 163-184 / 132-147 cyc - rts ; 6 cyc + + done: .endscope .endmacro .proc imul16_func - imul16_impl 0 + imul16_impl FR2, FR0, FR1, 0 + rts .endproc .proc imul16xe_func - imul16_impl 1 + imul16_impl FR2, FR0, FR1, 1 + rts .endproc .proc sqr16_func - sqr16_impl 0 + sqr16_impl FR2, FR0, 0 + rts .endproc .proc sqr16xe_func - sqr16_impl 1 + sqr16_impl FR2, FR0, 1 + rts .endproc ; 11-27 cycles From e58df379e72ced940342df9a34b8aeee53480355 Mon Sep 17 00:00:00 2001 From: Brooke Vibber Date: Sun, 23 Aug 2026 15:48:48 -0700 Subject: [PATCH 2/2] step 2: break out the multiplication hotspot mandelbrot_hotspot_impl macro calls through to the sqr16_impl and imul16_impl macros with the xe mode this is backed by two realized functions, as mandelbrot_hotspot and mandelbrot_hotspot_xe these are also called via direct jmp instead of jsr/rts because there's only one call site so we can save 6 cycles per iteration by jmp/jmp could save 6 more cycles per iter by specializing all of the mandelbrot proc but there isn't room in ram right now sqr16_func is removed as it is unused and we ran out of code space adding the hotspot's extra implementations imul16_func is kept, as it's called via zoom_factor in a couple of places. this forwards to the xe version at a cost of 3 cycles, as the call sites aren't patched --- mandel-core.s | 85 +++++++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/mandel-core.s b/mandel-core.s index 2e1c8b2..17ecfc4 100644 --- a/mandel-core.s +++ b/mandel-core.s @@ -558,8 +558,6 @@ input_max: ; input: arg1, arg2 as fixed4.12 ; output: dest as fixed8.24 -; patch point jsr at 16 bytes in -imul16_patch_offset = 16 .macro imul16 dest, arg1, arg2 copy16 FR0, arg1 ; 12 cyc copy16 FR1, arg2 ; 12 cyc @@ -569,13 +567,11 @@ imul16_patch_offset = 16 ; input: arg as fixed4.12 ; output: dest as fixed8.24 -; patch point jsr at 8 bytes in -sqr16_patch_offset = 8 -.macro sqr16 dest, arg - copy16 FR0, arg ; 12 cyc - jsr sqr16_func ; ? cyc - copy32 dest, FR2 ; 24 cyc -.endmacro +;.macro sqr16 dest, arg +; copy16 FR0, arg ; 12 cyc +; jsr sqr16_func ; ? cyc +; copy32 dest, FR2 ; 24 cyc +;.endmacro ; input: arg as u8 ; output: dest as u16 @@ -912,15 +908,15 @@ inner_loop: rts .endproc -.proc sqr16_func - sqr16_impl FR2, FR0, 0 - rts -.endproc +;.proc sqr16_func +; sqr16_impl FR2, FR0, 0 +; rts +;.endproc -.proc sqr16xe_func - sqr16_impl FR2, FR0, 1 - rts -.endproc +;.proc sqr16xe_func +; sqr16_impl FR2, FR0, 1 +; rts +;.endproc ; 11-27 cycles .macro round16 arg @@ -1160,16 +1156,11 @@ keep_going: shift_round_16 zy, 3 ; zx_2 = zx * zx -fixup_sqr16_1: - sqr16 zx_2, zx + 2 - ; zy_2 = zy * zy -fixup_sqr16_2: - sqr16 zy_2, zy + 2 - ; zx_zy = zx * zy -fixup_imul16_1: - imul16 zx_zy, zx + 2, zy + 2 +fixup_mandelbrot_hotspot: + jmp mandelbrot_hotspot +after_mandelbrot_hotspot: ; dist = zx_2 + zy_2 add32 dist, zx_2, zy_2 @@ -1265,6 +1256,27 @@ next: .endproc +.macro mandelbrot_hotspot_impl xe + ; zx_2 = zx * zx + sqr16_impl zx_2, zx + 2, xe + + ; zy_2 = zy * zy + sqr16_impl zy_2, zy + 2, xe + + ; zx_zy = zx * zy + imul16_impl zx_zy, zx + 2, zy + 2, xe + + jmp mandelbrot::after_mandelbrot_hotspot +.endmacro + +.proc mandelbrot_hotspot + mandelbrot_hotspot_impl 0 +.endproc + +.proc mandelbrot_hotspot_xe + mandelbrot_hotspot_impl 1 +.endproc + .macro scale_zoom dest ; clobbers X, flags .local cont @@ -2192,22 +2204,21 @@ init: sta imul16_func lda #.lobyte(imul16xe_func) sta imul16_func + 1 - sta mandelbrot::fixup_imul16_1 + imul16_patch_offset + 1 lda #.hibyte(imul16xe_func) sta imul16_func + 2 - sta mandelbrot::fixup_imul16_1 + imul16_patch_offset + 2 ; ditto for sqr16_func -> sqr16xe_func - lda #$4c ; 'jmp' opcode - sta sqr16_func - lda #.lobyte(sqr16xe_func) - sta sqr16_func + 1 - sta mandelbrot::fixup_sqr16_1 + sqr16_patch_offset + 1 - sta mandelbrot::fixup_sqr16_2 + sqr16_patch_offset + 1 - lda #.hibyte(sqr16xe_func) - sta sqr16_func + 2 - sta mandelbrot::fixup_sqr16_1 + sqr16_patch_offset + 2 - sta mandelbrot::fixup_sqr16_2 + sqr16_patch_offset + 2 + ;lda #$4c ; 'jmp' opcode + ;sta sqr16_func + ;lda #.lobyte(sqr16xe_func) + ;sta sqr16_func + 1 + ;lda #.hibyte(sqr16xe_func) + ;sta sqr16_func + 2 + + lda #.lobyte(mandelbrot_hotspot_xe) + sta mandelbrot::fixup_mandelbrot_hotspot + 1 + lda #.hibyte(mandelbrot_hotspot_xe) + sta mandelbrot::fixup_mandelbrot_hotspot + 2 ; create the lookup table