Date: Thu, 8 Aug 1996 20:01:02 -0700 To: tghack-list@cpac.washington.edu From: daves@interlog.com (David Shadoff) X-Software: MLF v2.3, Copyright 1995, 1996 by Bt X-Original-Id: <199608090253.WAA24696@smtp.interlog.com> Subject: More Math Subroutines ! Hi Gang... Today, we have another several math function subroutines documented from the CD system card... $E0C0 = 8-bit Signed Multiply $E0C6 = 16-bit Signed Divide $E0CC = Square Root $E0CF = sin() $E0D2 = cos() Multiply 8-bit (signed): - see previous posts this week; same inputs/outputs used, but are interpreted as signed values, rather than unsigned Divide 16-bit (signed): - see previous posts this week; same inputs, except division-by-0 does not appear to be checked; same outputs, except that the signs are changed appropriately Square Root: Method of operation: This is a very interesting algorithm, which uses 2-shifts on the input number for every shift of the 'work number'. Compare/subtract is employed at each bit shift position. It took me a long time to figure out what the actual function of this routine was. Input values: $F8/$F9, a 16-bit integer Output values: $FC, an 8-bit integer Registers Modified: A, Y Flags set: None SIN: Method of operation: Simple table lookup. Input values: Register A = degrees of arc (range = 0-90 degrees) Output values: Register A = 256 * (sine of angle), represented as an unsigned integer value (ie. sin(86) = $FF) Registers modified: A, X Flags set: Carry set if: (1) input value out of range (ie < 0 or > 90), or (2) output value out of range (ie. 256*sin(87) = 255.649 -> 256, which is out of range) COS: Method of operation: Simple table lookup. Input values: Register A = degrees of arc (range = 0-90 degrees) Output values: Register A = 256 * (cosine of angle), represented as an unsigned integer value (ie. cos(4) = $FF) Registers modified: A, X Flags set: Carry set if: (1) input value out of range (ie < 0 or > 90), or (2) output value out of range (ie. 256*cos(3) = 255.649 -> 256, which is out of range) -- Dave ---------------------------------------------------------------------------- Date: Tue, 6 Aug 1996 18:38:12 -0700 To: tghack-list@cpac.washington.edu From: daves@interlog.com (David Shadoff) X-Software: MLF v2.3, Copyright 1995, 1996 by Bt X-Original-Id: <199608070131.VAA15401@smtp.interlog.com> Subject: More CD system internals Hi Gang... More musings on the internals of the CD system card. Today we have a couple of math routines, and a page-swapping technique to discuss. As we previously noted, the first (roughly) $100 bytes of the CD card are a jump table to an API of commonly-used routines. We already knew that $E009 was a 'read data sectors from disk' function. Today, I will show how to use the following routines: Address Name used by develo Function $E0BD 'ma_mul8u' Multiply 8-bit by 8-bit, unsigned $E0C3 'ma_mul16u' Multiply 16-bit by 16-bit, unsigned $E0C9 'ma_div16u' Divide 16-bit into 16-bit, unsigned First of all, it is interesting to note that (at least in my 'reference' version of the CD card) the 'jump addresses' point to multiple-depth subroutines, of the following form: BSR SWAPOUT JSR FUNCTION BRA SWAPIN The first and last subroutines actually 'swap out' and 'swap in' an MMR register, so that a different code page can be accessed. The overall function ends with a 'branch' (to a subroutine), which returns to the original caller. Here is a sample of swap-out/swap-in code: SWAPOUT: PHA ; preserve accumulator TMA #$40 ; obtain original MMR #6 ($C000-$DFFF) STA TEMPMMR LDA #ALT_PAGE TAM #$40 ; replace MMR with new value PLA ; restore accumulator RTS SWAPIN: PHP ; preserve flags register PHA ; preserve accumulator LDA TEMPMMR ; get original MMR value TAM #$40 ; replace MMR with original value PLA ; restore accumulator PLP ; restore flags register RTS One last note, then on to the math functions... Since these three functions all use a 'rotate-and-add' algorithm, they use a countdown loop. Therefore, they use a countdown loop for the number of bits to rotate. So, the Z flag will always be set (N reset) upon completion (because of the countdown). Other flags will be undefined. 8-bit mulitiply: This is a pretty nifty piece of work. It uses a shift-and-add algorithm for the multiply, and appears to be VERY speedy, since it does all of the work in the A and X registers, rather than in memory. Entry: operands in $F8 and $FA on zero-page Exit: result in $FC/$FD on zero-page ($FC = LSB) Registers changed: All Flags set: Z always set (N reset), others undefined 16-bit multiply: This is simply an extension of the above algorithm, but should be significantly slower on small numbers (assuming speed is a consideration), since it uses zero-page memory for scratchpad work, instead of registers. Entry: operands in $F8/$F9, and $FA/$FB (1st byte = LSB) Exit: result in $FC/$FD/$FE/$FF ($FC = LSB, $FF = MSB) Registers changed: A, X Flags set: Z always set (N reset), others undefined 16-bit divide: This is an even more ingenious algorithm, where double-use of the result field (which doubles as a temporary divisor) shifts the divisor into the remainder field, and subtracts the dividend where applicable. The ingenious part is that the carry flag from a succesful subtract (dividend from remainder) is then automatically picked up by the next rotate of the result field, gradually replacing the divisor with the result, bit-by-bit. I guess you've got to see it to appreciate it. Entry: - divisor ("large number") in $F8/$F9 - dividend ("divided by") in $FA/$FB Exit: - result in $FC/$FD, remainder in $FE/FF (1st byte = LSB) - in the Registers changed: A, X Flags set: Z always set (N reset), others undefined -- Dave