Skip to content

Instantly share code, notes, and snippets.

@fesh0r
Created January 10, 2021 07:02
Show Gist options
  • Save fesh0r/0bfc8b14fe97c54637a0bebe944df561 to your computer and use it in GitHub Desktop.
Save fesh0r/0bfc8b14fe97c54637a0bebe944df561 to your computer and use it in GitHub Desktop.
\ ******************************************************************************
\
\ ELITE LOADER SOURCE
\
\ Elite was written by Ian Bell and David Braben and is copyright Acornsoft 1984
\
\ The code on this site is identical to the version released on Ian Bell's
\ personal website at http://www.elitehomepage.org/
\
\ The commentary is copyright Mark Moxon, and any misunderstandings or mistakes
\ in the documentation are entirely my fault
\
\ The terminology and notations used in this commentary are explained at
\ https://www.bbcelite.com/about_site/terminology_used_in_this_commentary.html
\
\ ******************************************************************************
\ ******************************************************************************
\
\ Configuration variables
\
\ ******************************************************************************
OSWRCH = &FFEE \ The address for the OSWRCH routine
ZP = &70 \ Temporary storage, used all over the place
P = &72 \ Temporary storage, used all over the place
Q = &73 \ Temporary storage, used all over the place
YY = &74 \ Temporary storage, used when drawing Saturn
T = &75 \ Temporary storage, used all over the place
ORG &1C05
.start
INC fix+1 \ fix 0D byte in PIX
LDX #2
.crtc_loop
LDA crtc_reg, X
STA &FE00
LDA crtc_val, X
STA &FE01
DEX
BPL crtc_loop
\ ******************************************************************************
\
\ Name: PLL1
\ Summary: Draw Saturn on the loading screen
\
\ ******************************************************************************
.PLL1
\ The following loop iterates CNT(1 0) times, i.e. &500
\ or 1280 times, and draws the planet part of the
\ loading screen's Saturn
JSR DORND \ Set A and X to random numbers, say A = r1
JSR SQUA2 \ Set (A P) = A * A
\ = r1^2
STA ZP+1 \ Set ZP(1 0) = (A P)
LDA P \ = r1^2
STA ZP
JSR DORND \ Set A and X to random numbers, say A = r2
STA YY \ Set YY = A
\ = r2
JSR SQUA2 \ Set (A P) = A * A
\ = r2^2
TAX \ Set (X P) = (A P)
\ = r2^2
LDA P \ Set (A ZP) = (X P) + ZP(1 0)
ADC ZP \
STA ZP \ first adding the low bytes
TXA \ And then adding the high bytes
ADC ZP+1
BCS PLC1 \ If the addition overflowed, jump down to PLC1 to skip
\ to the next pixel
STA ZP+1 \ Set ZP(1 0) = (A ZP)
\ = r1^2 + r2^2
LDA #1 \ Set ZP(1 0) = &4001 - ZP(1 0) - (1 - C)
SBC ZP \ = 128^2 - ZP(1 0)
STA ZP \
\ (as the C flag is clear), first subtracting the low
\ bytes
LDA #&40 \ And then subtracting the high bytes
SBC ZP+1
STA ZP+1
BCC PLC1 \ If the subtraction underflowed, jump down to PLC1 to
\ skip to the next pixel
\ If we get here, then both calculations fitted into
\ 16 bits, and we have:
\
\ ZP(1 0) = 128^2 - (r1^2 + r2^2)
\
\ where ZP(1 0) >= 0
JSR ROOT \ Set ZP = SQRT(ZP(1 0))
LDA ZP \ Set X = ZP >> 1
LSR A \ = SQRT(128^2 - (a^2 + b^2)) / 2
TAX
LDA YY \ Set A = YY
\ = r2
CMP #128 \ If YY >= 128, set the C flag (so the C flag is now set
\ to bit 7 of A)
ROR A \ Rotate A and set the sign bit to the C flag, so bits
\ 6 and 7 are now the same, i.e. A is a random number in
\ one of these ranges:
\
\ %00000000 - %00111111 = 0 to 63 (r2 = 0 - 127)
\ %11000000 - %11111111 = 192 to 255 (r2 = 128 - 255)
\
\ The PIX routine flips bit 7 of A before drawing, and
\ that makes -A in these ranges:
\
\ %10000000 - %10111111 = 128-191
\ %01000000 - %01111111 = 64-127
\
\ so that's in the range 64 to 191
JSR PIX \ Draw a pixel at screen coordinate (X, -A), i.e. at
\
\ (ZP / 2, -A)
\
\ where ZP = SQRT(128^2 - (r1^2 + r2^2))
\
\ So this is the same as plotting at (x, y) where:
\
\ r1 = random number from 0 to 255
\ r1 = random number from 0 to 255
\ (r1^2 + r1^2) < 128^2
\
\ y = r2, squished into 64 to 191 by negation
\
\ x = SQRT(128^2 - (r1^2 + r1^2)) / 2
\
\ which is what we want
.PLC1
DEC CNT \ Decrement the counter in CNT (the low byte)
BNE PLL1 \ Loop back to PLL1 until CNT = 0
DEC CNT+1 \ Decrement the counter in CNT+1 (the high byte)
BNE PLL1 \ Loop back to PLL1 until CNT+1 = 0
\ The following loop iterates CNT2(1 0) times, i.e. &1DD
\ or 477 times, and draws the background stars on the
\ loading screen
.PLL2
JSR DORND \ Set A and X to random numbers, say A = r3
TAX \ Set X = A
\ = r3
JSR SQUA2 \ Set (A P) = A * A
\ = r3^2
STA ZP+1 \ Set ZP+1 = A
\ = r3^2 / 256
JSR DORND \ Set A and X to random numbers, say A = r4
STA YY \ Set YY = r4
JSR SQUA2 \ Set (A P) = A * A
\ = r4^2
ADC ZP+1 \ Set A = A + r3^2 / 256
\ = r4^2 / 256 + r3^2 / 256
\ = (r3^2 + r4^2) / 256
CMP #&11 \ If A < 17, jump down to PLC2 to skip to the next pixel
BCC PLC2
LDA YY \ Set A = r4
JSR PIX \ Draw a pixel at screen coordinate (X, -A), i.e. at
\ (r3, -r4), where (r3^2 + r4^2) / 256 >= 17
\
\ Negating a random number from 0 to 255 still gives a
\ random number from 0 to 255, so this is the same as
\ plotting at (x, y) where:
\
\ x = random number from 0 to 255
\ y = random number from 0 to 255
\ (x^2 + y^2) div 256 >= 17
\
\ which is what we want
.PLC2
DEC CNT2 \ Decrement the counter in CNT2 (the low byte)
BNE PLL2 \ Loop back to PLL2 until CNT2 = 0
DEC CNT2+1 \ Decrement the counter in CNT2+1 (the high byte)
BNE PLL2 \ Loop back to PLL2 until CNT2+1 = 0
\ The following loop iterates CNT3(1 0) times, i.e. &500
\ or 1280 times, and draws the rings around the loading
\ screen's Saturn
.PLL3
JSR DORND \ Set A and X to random numbers, say A = r5
STA ZP \ Set ZP = r5
JSR SQUA2 \ Set (A P) = A * A
\ = r5^2
STA ZP+1 \ Set ZP+1 = A
\ = r5^2 / 256
JSR DORND \ Set A and X to random numbers, say A = r6
STA YY \ Set YY = r6
JSR SQUA2 \ Set (A P) = A * A
\ = r6^2
STA T \ Set T = A
\ = r6^2 / 256
ADC ZP+1 \ Set ZP+1 = A + r5^2 / 256
STA ZP+1 \ = r6^2 / 256 + r5^2 / 256
\ = (r5^2 + r6^2) / 256
LDA ZP \ Set A = ZP
\ = r5
CMP #128 \ If A >= 128, set the C flag (so the C flag is now set
\ to bit 7 of ZP, i.e. bit 7 of A)
ROR A \ Rotate A and set the sign bit to the C flag, so bits
\ 6 and 7 are now the same
CMP #128 \ If A >= 128, set the C flag (so again, the C flag is
\ set to bit 7 of A)
ROR A \ Rotate A and set the sign bit to the C flag, so bits
\ 5-7 are now the same, i.e. A is a random number in one
\ of these ranges:
\
\ %00000000 - %00011111 = 0-31
\ %11100000 - %11111111 = 224-255
\
\ In terms of signed 8-bit integers, this is a random
\ number from -32 to 31. Let's call it r7
ADC YY \ Set X = A + YY
TAX \ = r7 + r6
JSR SQUA2 \ Set (A P) = r7 * r7
TAY \ Set Y = A
\ = r7 * r7 / 256
ADC ZP+1 \ Set A = A + ZP+1
\ = r7^2 / 256 + (r5^2 + r6^2) / 256
\ = (r5^2 + r6^2 + r7^2) / 256
BCS PLC3 \ If the addition overflowed, jump down to PLC3 to skip
\ to the next pixel
CMP #80 \ If A >= 80, jump down to PLC3 to skip to the next
BCS PLC3 \ pixel
CMP #32 \ If A < 32, jump down to PLC3 to skip to the next pixel
BCC PLC3
TYA \ Set A = Y + T
ADC T \ = r7^2 / 256 + r6^2 / 256
\ = (r6^2 + r7^2) / 256
CMP #16 \ If A > 16, skip to PL1 to plot the pixel
BCS PL1
LDA ZP \ If ZP is positive (50% chance), jump down to PLC3 to
BPL PLC3 \ skip to the next pixel
.PL1
LDA YY \ Set A = YY
\ = r6
JSR PIX \ Draw a pixel at screen coordinate (X, -A), where:
\
\ X = (random -32 to 31) + r6
\ A = r6
\
\ Negating a random number from 0 to 255 still gives a
\ random number from 0 to 255, so this is the same as
\ plotting at (x, y) where:
\
\ r5 = random number from 0 to 255
\ r6 = random number from 0 to 255
\ r7 = r5, squashed into -32 to 31
\
\ x = r5 + r7
\ y = r5
\
\ 32 <= (r5^2 + r6^2 + r7^2) / 256 <= 79
\ Draw 50% fewer pixels when (r6^2 + r7^2) / 256 <= 16
\
\ which is what we want
.PLC3
DEC CNT3 \ Decrement the counter in CNT3 (the low byte)
BNE PLL3 \ Loop back to PLL3 until CNT3 = 0
DEC CNT3+1 \ Decrement the counter in CNT3+1 (the high byte)
BNE PLL3 \ Loop back to PLL3 until CNT3+1 = 0
\ ******************************************************************************
\
\ Name: DORND
\ Summary: Generate random numbers
\
\ ******************************************************************************
.DORND
LDA RAND+1 \ r1´ = r1 + r3 + C
TAX \ r3´ = r1
ADC RAND+3
STA RAND+1
STX RAND+3
LDA RAND \ X = r2´ = r0
TAX \ A = r0´ = r0 + r2
ADC RAND+2
STA RAND
STX RAND+2
RTS
.RAND
EQUD &6C785349 \ The random number seed used for drawing Saturn
\--------------------------------------------- BREAK FOR NEXT LINE
NOP:NOP:NOP:NOP:NOP
\--------------------------------------------- BREAK FOR NEXT LINE
\ ******************************************************************************
\
\ Name: SQUA2
\ Summary: Calculate (A P) = A * A
\
\ ******************************************************************************
.SQUA2
BPL SQUA \ If A > 0, jump to SQUA
EOR #&FF \ Otherwise we need to negate A for the SQUA algorithm
CLC \ to work, so we do this using two's complement, by
ADC #1 \ setting A = ~A + 1
.SQUA
STA Q \ Set Q = A and P = A
STA P \ Set P = A
LDA #0 \ Set A = 0 so we can start building the answer in A
LDY #8 \ Set up a counter in Y to count the 8 bits in P
LSR P \ Set P = P >> 1
\ and C flag = bit 0 of P
.SQL1
BCC SQ1 \ If C (i.e. the next bit from P) is set, do the
CLC \ addition for this bit of P:
ADC Q \
\ A = A + Q
.SQ1
ROR A \ Shift A right to catch the next digit of our result,
\ which the next ROR sticks into the left end of P while
\ also extracting the next bit of P
ROR P \ Add the overspill from shifting A to the right onto
\ the start of P, and shift P right to fetch the next
\ bit for the calculation into the C flag
DEY \ Decrement the loop counter
BNE SQL1 \ Loop back for the next bit until P has been rotated
\ all the way
RTS
\ ******************************************************************************
\
\ Name: ROOT
\ Summary: Calculate ZP = SQRT(ZP(1 0))
\
\ ******************************************************************************
.ROOT
LDY ZP+1 \ Set (Y Q) = ZP(1 0)
LDA ZP
STA Q
\ So now to calculate ZP = SQRT(Y Q)
LDX #0 \ Set X = 0, to hold the remainder
STX ZP \ Set ZP = 0, to hold the result
LDA #8 \ Set P = 8, to use as a loop counter
STA P
.LL6
CPX ZP \ If X < ZP, jump to LL7
BCC LL7
BNE LL8 \ If X > ZP, jump to LL8
CPY #64 \ If Y < 64, jump to LL7 with the C flag clear,
BCC LL7 \ otherwise fall through into LL8 with the C flag set
.LL8
TYA \ Set Y = Y - 64
SBC #64 \
TAY \ This subtraction will work as we know C is set from
\ the BCC above, and the result will not underflow as we
\ already checked that Y >= 64, so the C flag is also
\ set for the next subtraction
TXA \ Set X = X - ZP
SBC ZP
TAX
.LL7
ROL ZP \ Shift the result in Q to the left, shifting the C flag
\ into bit 0 and bit 7 into the C flag
ASL Q \ Shift the dividend in (Y S) to the left, inserting
TYA \ bit 7 from above into bit 0
ROL A
TAY
TXA \ Shift the remainder in X to the left
ROL A
TAX
ASL Q \ Shift the dividend in (Y S) to the left
TYA
ROL A
TAY
TXA \ Shift the remainder in X to the left
ROL A
TAX
DEC P \ Decrement the loop counter
BNE LL6 \ Loop back to LL6 until we have done 8 loops
RTS
\ ******************************************************************************
\
\ Name: PIX
\ Summary: Draw a single pixel at a specific coordinate
\
\ ******************************************************************************
.PIX
TAY \ Copy A into Y, for use later
EOR #%10000000 \ Flip the sign of A
LSR A \ Set ZP+1 = &60 + A >> 3
LSR A
LSR A
ORA #&60
STA ZP+1
TXA \ Set ZP = (X >> 3) * 8
EOR #%10000000
AND #%11111000
STA ZP
TYA \ Set Y = Y AND %111
AND #%00000111
TAY
TXA \ Set X = X AND %111
AND #%00000111
TAX
.fix
LDA &C40C,X \ Otherwise fetch a pixel from TWOS and OR it into ZP+Y
ORA (ZP),Y
STA (ZP),Y
RTS \ Return from the subroutine
\ ******************************************************************************
\
\ Name: CNT
\ Summary: A counter for use in drawing Saturn's planetary body
\
\ ******************************************************************************
.CNT
EQUW &0500 \ The number of iterations of the PLL1 loop (1280)
\ ******************************************************************************
\
\ Name: CNT2
\ Summary: A counter for use in drawing Saturn's background stars
\
\ ******************************************************************************
.CNT2
EQUW &01DD \ The number of iterations of the PLL2 loop (477)
\ ******************************************************************************
\
\ Name: CNT3
\ Summary: A counter for use in drawing Saturn's rings
\
\ ******************************************************************************
.CNT3
EQUW &0500 \ The number of iterations of the PLL3 loop (1280)
\ values to poke at 6845
.crtc_val
EQUB 32
EQUB &0C
EQUB &00
\ register to poke at 6845
.crtc_reg
EQUB 1
EQUB 12
EQUB 13
.end
SAVE "saturn", start, end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment