From bedb1c7b01c0b50dc4ba78e4eab4dffcf799a2ba Mon Sep 17 00:00:00 2001 From: Maximilian Rehkopf Date: Wed, 22 Apr 2026 18:16:01 +0200 Subject: [PATCH] Fix signed overflow in SA-1 clock frequency multiplication When the loop counter exceeds 32767, the hardware multiplier treats it as signed negative. Correct for this by conditionally adding the constant multipler to the upper 16 bits of the result (the loop counter is added unconditionally since the constant multiplier is always "signed negative") Also fix the subsequent division: halve the dividend before division so it stays out of signed negative territory, then double the quotient and remainder of the result and adjust for odd dividends and "modulo overflow" (remainder >= quotient). This allows the test to actually show frequencies between ~29 and ~59MHz. Before it would wrap to ~3MHz and scale strangely. --- speed_test.asm | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/speed_test.asm b/speed_test.asm index d9dc599..b94b39d 100644 --- a/speed_test.asm +++ b/speed_test.asm @@ -1844,6 +1844,7 @@ sa1_clock_finish_16: sta $2251 asl lda.w #63018 ; 8.16 fixed point. ==> 315.0 / 88.0 * 1000000 * 6 / (262 * 1364) * 16 / 1000 * 65536 + sta $4a ; save multiplier bra sa1_clock_finish_continue sa1_clock_finish: @@ -1851,28 +1852,53 @@ sa1_clock_finish: inc sta $46 sta $2251 - asl + asl ; save sign bit for later, adjustment needed for unsigned multiplication lda.w #59079 ; 8.16 fixed point. ==> 315.0 / 88.0 * 1000000 * 6 / (262 * 1364) * 15 / 1000 * 65536 + sta $4a ; save multiplier .continue ; +/- 0.9 kHz resolution (or 0.00090 MHz) sta $2253 ldy #$01 - bcs + - lda $46 -+ ldx $2307 + ldx $2307 stx $48 ldx $2306 - clc - adc $2308 + lda $2308 + bcc + ; if loop counter was "signed negative" (>32767), add the other factor + clc ; to the upper 16 bits of the result to turn the signed multiplication + adc $4a ; into an unsigned one => add the fixed multiplier. ++ clc ; fixed multiplier (59079 or 63018) is always "signed negative" + adc $46 ; so always adjust => always add the loop counter. sty $2250 + pha + lsr ; halve the dividend to make sure it is signed positive sta $2251 - lda.w #10000 + lda.w #10000 ; divisor is known signed positive (10000) sta $2253 nop xba lda $2306 - sta $46 + asl + sta $46 ; quotient lda $2308 - sta $2251 + asl + sta $4a ; remainder + pla ; get the original dividend + ror ; check lsb + bcc + + inc $4a ; adjust remainder if lsb was set in dividend ++ lda $4a ; it might now be equal to the divisor + cmp.w #10000 + bcc + ; if remainder is smaller than divisor, we're done. + ; otherwise adjust results: + ; e.g. 50001/10000 => 25000/10000 => q=2, r=5000 + ; double both => q=4, r=10000. + ; original lsb of dividend was 1 => add to remainder => r=10001. + ; since the remainder is >= 10000 increase the quotient by one and + ; subtract the divisor from the remainder (modulo operation): + ; q=5; r=(10001-10000)=1. + inc $46 ; increase quotient + sec + sbc.w #10000 ; adjust remainder to be less than divisor ++ sta $2251 lda.w #100 sta $2253 nop