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
ScaledUp 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)
STA P

LDX T                  \ Set Q = (%TTTT * %JJJJ) + 1
LDA timesTable,X
STA Q

\ So (Q P) = (%SSSS * %JJJJ) + (%tttt * %jjjj)
\            + ((%TTTT * %JJJJ) + 1) << 8

LDX U                  \ Set X = (%TTTT * %jjjj) + (%tttt * %JJJJ)
LDA timesTable,X
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

CLC                    \ Set A = A + 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

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

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
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
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

.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
```