Skip to navigation

Aviator on the BBC Micro

Maths: DivideScaled

Name: DivideScaled [Show more] Type: Subroutine Category: Maths Summary: Divide a 16-bit number by a scaled 16-bit number
Context: See this subroutine in context in the source code References: This subroutine is called as follows: * ProjectPoint (Part 2 of 3) calls DivideScaled

The commentary in this routine is a work in progress. This routine calculates: (Q P) = (X Y) divided by (TT S) where (X Y) is a positive 16-bit number and (TT S) is the entry in the (divisionHi divisionLo) table for the denominator after being scaled by the ScaleUp routine.
Arguments: (X Y) A positive 16-bit number containing a number to be divided (the dividend) Z flag Set according to the high byte in X (i.e. the routine is called after setting register X) R Contains %00010000 (which is set at the start of ProjectPoint) (TT S) The lookup from (divisionHi divisionLo) for the high byte of the scaled up denominator (i.e. the number we are dividing by) K The low byte of the scaled up denominator
Returns: (Q P) (X Y) divided by the denominator used to look up the value of (TT S)
JSR ScaleUp \ This instruction appears to be an unused duplicate of \ the first instruction of DivideScaled, so perhaps this \ is a remnant of an unused entry point DivideScaled-3, \ which would run ScaleUp twice? .DivideScaled JSR ScaleUp \ Set (A Y) = (X Y), scaled up until it doesn't fit into \ 16 bits any more, and set WW to the minimum number of \ bits in the original number STA J \ Set (J I) = (A Y) STY I LDX TT \ Set X = TT \ = %TTTTtttt LDY J \ Set Y = J \ = %JJJJjjjj LDA highNibble,X \ Set T = (X AND %11110000) OR (Y >> 4) ORA shift4Right,Y \ = %TTTTJJJJ STA T AND #%11110000 \ Set U = (T AND %11110000) OR (Y AND %00001111) ORA lowNibble,Y \ = %TTTTjjjj STA U AND #%00001111 \ Set V = (U AND %00001111) OR (X << 4) ORA shift4Left,X \ = %ttttjjjj STA V AND #%11110000 \ Set Y = (V AND %11110000) OR (Y >> 4) ORA shift4Right,Y \ = %ttttJJJJ TAY LDX S \ Set X = S \ = %SSSSssss AND #%00001111 \ Set X = (Y AND %00001111) OR (X AND %11110000) ORA highNibble,X \ = %SSSSJJJJ TAX \ So by this point we have: \ \ T = %TTTTJJJJ \ U = %TTTTjjjj \ V = %ttttjjjj \ Y = %ttttJJJJ \ X = %SSSSJJJJ \ \ where the following are arguments that were passed to \ the routine: \ \ (TT S) = (%TTTTtttt %SSSSssss) \ \ (J I) = (%JJJJjjjj %IIIIiiii) \ \ The first one is the denominator's lookup from the \ division table, while the second is the dividend, \ scaled up \ We now calculate (Q P) = (TT S) * (1 J I) \ \ (I am unsure about the following) LDA timesTable,X \ Set A = %SSSS * %JJJJ CLC \ Set P = A + (%tttt * %jjjj) LDX V \ = (%SSSS * %JJJJ) + (%tttt * %jjjj) ADC timesTable,X STA P LDX T \ Set Q = (%TTTT * %JJJJ) + 1 LDA timesTable,X ADC #1 STA Q \ So (Q P) = (%SSSS * %JJJJ) + (%tttt * %jjjj) \ + ((%TTTT * %JJJJ) + 1) << 8 LDX U \ Set X = (%TTTT * %jjjj) + (%tttt * %JJJJ) LDA timesTable,X ADC timesTable,Y TAX LDY #0 \ Set Y = 0, to use as the high byte of (Y A) BCC divs1 \ If the above addition didn't overflow, skip the \ following instruction LDY #16 \ The above addition overflowed, so set Y = 16 to use as \ the high byte of (Y A), where Y = %00010000 .divs1 LDA shift4Left,X \ Set A = (X << 4) ADC P \ Set (Y A) = (Y A) + P \ \ starting with the low bytes BCC divs2 \ And then the high bytes INY .divs2 ADC J \ Set (Y A) = (Y A) + J \ \ starting with the low bytes BCC divs3 \ And then the high bytes INY .divs3 ADC S \ Set (Y A) = (Y A) + S \ \ just doing the low bytes STA P \ Set (A P) = (Y A) TYA ADC shift4Right,X \ Set A = A + X << 4 + Q ADC Q BCC divs4 \ If the addition didn't overflow, jump to divs4 CLC \ Set A = A + TT ADC TT SEC \ Set the C flag to feed into bit 7 of (A P) when we \ shift it right JMP divs5 \ Skip the following instruction .divs4 ADC TT \ Set A = A + TT \ \ and set the C flag to the carry .divs5 ROR A \ Set (A P) = (A P) >> 1 ROR P STA Q \ Set (Q P) = (A P) LDA I \ Set A = I = %IIIIiiii BEQ divs6 \ If I = 0, jump to divs6 AND #%11110000 \ Set A = %IIII0000 LDX TT \ Set X = %TTTTtttt ORA shift4Right,X \ Set A = %IIII0000 OR %0000TTTT \ = %IIIITTTT TAY \ Set Y = %IIIITTTT AND #%11110000 \ Set X = %IIII0000 OR %0000tttt ORA lowNibble,X \ = %IIIItttt TAX LDA timesTable,X \ Set X = %IIII * %tttt TAX \ = %XXXXxxxx CLC \ Set the C flag from adding %xxxx0000 + %IIIIiiii LDA shift4Left,X ADC I LDA timesTable,Y \ Set A = %IIII * %TTTT + %XXXX + C ADC shift4Right,X \ = %IIII * %TTTT + (%IIII * %tttt) >> 4 + C ROR A \ Set A = A >> 1 CLC \ Set (Q P) = (Q P) + A ADC P \ STA P \ starting with the low bytes BCC divs6 INC Q \ And then the high bytes BNE divs6 LDA #&FF \ Set (Q P) = &FFFF STA Q STA P .divs6 LDA K \ If bits 6 and 7 of K are clear, jump to divs9 to AND #%11000000 \ return from the subroutine BEQ divs9 STA K \ Clear bits 0 to 5 of K CLC \ Set W = TT >> 1 + 1 LDA TT ADC #1 ROR A STA W LSR A \ Set A = W >> 1 BIT K \ If bit 6 of K is set, jump to divs7 BVS divs7 LDA #0 \ Bit 6 of K is clear, so set A = 0 BIT K \ Set the flags for K again .divs7 BPL divs8 \ If bit 7 of K is clear, jump to divs8 CLC \ Bit 7 of K is set, so set A = A + W ADC W .divs8 TAY \ Set Y = A LDX Q \ Set X = Q JSR Multiply8x8 \ Set (A V) = X * Y \ = A * Q STA G \ Set (Q P) = (Q P) - A LDA P \ SEC \ starting with the high bytes SBC G STA P BCS divs9 \ If the subtraction didn't underflow, skip the next \ instruction DEC Q \ Decrement the high byte .divs9 RTS \ Return from the subroutine