P********************************
*                              *
* Stephen Judd                 *
* George Taylor                *
* Started: 7/11/94             *
* Finished: 7/19/94            *
*                              *
* Well, if all goes well this  *
* program will rotate a cube.  *
*                              *
* This program is intended to  *
* accompany the article in     *
* C=Hacking, July 94 issue.    *
* For details on this program, *
* read the article!            *
*                              *
* Write to us!                 *
*                              *
* un(bee)mo                    *
*                              *
* vi                           *
* n(in)g                       *
* are(th                       *
* e)you(o                      *
* nly)                         *
*                              *
* asl(rose)eep                 *
*              e.e.cummings    *
*                              *
* P.S. This was written using  *
*      Merlin 128.  With a     *
*      little modification it  *
*      will work fine with     *
*      Merlin 64.  If you      *
*      don't have either...    *
*      well, we all have our   *
*      little faults.          *
********************************

         ORG $1000

* Constants

BUFF1    EQU $3000        ;First character set
BUFF2    EQU $3800        ;Second character set
BUFFER   EQU $A3          ;Presumably the tape won't be running
X1       EQU $FB          ;Points for drawing a line
Y1       EQU $FC          ;These zero page addresses
X2       EQU $FD          ;don't conflict with BASIC
Y2       EQU $FE
DX       EQU $F9
DY       EQU $FA
TEMP1    EQU $FB          ;Of course, could conflict with x1
TEMP2    EQU $FC          ;Temporary variables
ACC      EQU $FB          ;These four variables are used
AUX      EQU $FC          ;by the multiplication routine
EXT      EQU $FD
REM      EQU $FE
ZTEMP    EQU $02          ;Used for buffer swap.  Don't touch.

ANGMAX   EQU 120          ;There are 2*pi/angmax angles
OFFSET   EQU 6            ;Float offset: x=xactual*2^offset

* VIC

VMCSB    EQU $D018
BKGND    EQU $D020
BORDER   EQU $D021
SSTART   EQU 1344         ;row 9 in screen memory at 1024


* Kernal

CHROUT   EQU $FFD2
GETIN    EQU $FFE4

*** Macros

MOVE     MAC
         LDA ]1
         STA ]2
         <<<

GETKEY   MAC              ;Wait for a keypress
WAIT     JSR GETIN
         CMP #00
         BEQ WAIT
         <<<

DEBUG    MAC              ;Print a character
          DO 0            ;Don't assemble

         LDA #]1
         JSR CHROUT
         >>> GETKEY       ;And wait to continue
         CMP #'s'         ;My secrect switch key
         BNE L1
         JSR CLEANUP
         JMP DONE
L1       CMP #'x'         ;My secret abort key
         BNE DONE
         JMP CLEANUP
         FIN
DONE     <<<

DEBUGA   MAC
         DO 0
         LDA ]1
         STA 1024
         FIN
DONEA    <<<

SETBUF   MAC              ;Put buffers where they can be hurt
         LDA #00
         STA BUFFER
         LDA ZTEMP        ;ztemp contains the high byte here
         STA BUFFER+1
         <<<

*-------------------------------

         LDA #$00
         STA BKGND
         STA BORDER
         LDA VMCSB
         AND #%00001111   ;Screen memory to 1024
         ORA #%00010000
         STA VMCSB

         LDY #00
         LDA #<TTEXT
         STA TEMP1
         LDA #>TTEXT
         STA TEMP2
         JMP TITLE
TTEXT    HEX 9305111111   ;clear screen, white, crsr dn
         TXT '               cube3d',0d,0d
         TXT '                 by',0d
         HEX 9F           ;cyan
         TXT '    stephen judd'
         HEX 99
         TXT '    george taylor',0d,0d
         HEX 9B
         TXT '  check out the july 94 issue of',0d
         HEX 96
         TXT '  c=hacking'
         HEX 9B
         TXT ' for more details!',0d
         HEX 0D1D1D9E12
         TXT 'f1/f2',92
         TXT ' - inc/dec x-rotation',0d
         HEX 1D1D12
         TXT 'f3/f4',92
         TXT ' - inc/dec y-rotation',0d
         HEX 1D1D12
         TXT 'f5/f6',92
         TXT ' - inc/dec z-rotation',0d
         HEX 1D1D12
         TXT 'f7',92
         TXT ' resets',0d
         TXT '  press q to quit',0d
         HEX 0D05
         TXT '      press any key to begin',0d
         HEX 00
TITLE    LDA (TEMP1),Y
         BEQ :CONT
         JSR CHROUT
         INY
         BNE TITLE
         INC TEMP2
         JMP TITLE
:CONT    >>> GETKEY

**** Set up tables(?)

* Tables are currently set up in BASIC
* and by the assembler.

TABLES   

**** Clear screen and set up "bitmap"

SETUP    LDA #147
         JSR CHROUT
         LDA #<SSTART
         ADC #12          ;The goal is to center the graphics
         STA TEMP1        ;Column 12
         LDA #>SSTART     ;Row 9
         STA TEMP1+1      ;SSTART points to row 9
         LDA #00
         LDY #00
         LDX #00          ;x will count 16 rows for us
         CLC

:LOOP    STA (TEMP1),Y
         INY
         ADC #16
         BCC :LOOP
         CLC
         LDA TEMP1
         ADC #40          ;Need to add 40 to the base pointer
         STA TEMP1        ;To jump to the next row
         LDA TEMP1+1
         ADC #00          ;Take care of carries
         STA TEMP1+1
         LDY #00
         INX
         TXA              ;X is also an index into the character number
         CPX #16
         BNE :LOOP        ;Need to do it 16 times

         >>> DEBUG,'2'
**** Set up buffers

         LDA #<BUFF1
         STA BUFFER
         LDA #>BUFF1
         STA BUFFER+1
         STA ZTEMP        ;ztemp will make life simple for us
         LDA VMCSB
         AND #%11110001   ;Start here so that swap buffers will work right
         ORA #%00001110
         STA VMCSB


**** Set up initial values

INIT     LDA #00
         STA DSX
         STA DSY
         STA DSZ
         STA SX
         STA SY
         STA SZ

         >>> DEBUG,'4'

*-------------------------------
* Main loop

**** Get keypress

MAIN     
KPRESS   JSR GETIN
         CMP #133         ;F1?
         BNE :F2
         LDA DSX
         CMP #ANGMAX/2    ;No more than pi
         BEQ :CONT
         INC DSX          ;otherwise increase x-rotation
         JMP :CONT
:F2      CMP #137         ;F2?
         BNE :F3
         LDA DSX
         BEQ :CONT
         DEC DSX
         JMP :CONT
:F3      CMP #134
         BNE :F4
         LDA DSY
         CMP #ANGMAX/2
         BEQ :CONT
         INC DSY          ;Increase y-rotation
         JMP :CONT
:F4      CMP #138
         BNE :F5
         LDA DSY
         BEQ :CONT
         DEC DSY
         JMP :CONT
:F5      CMP #135
         BNE :F6
         LDA DSZ
         CMP #ANGMAX/2
         BEQ :CONT
         INC DSZ          ;z-rotation
         JMP :CONT
:F6      CMP #139
         BNE :F7
         LDA DSZ
         BEQ :CONT
         DEC DSZ
         JMP :CONT
:F7      CMP #136
         BNE :Q
         JMP INIT
:Q       CMP #'q'         ;q quits
         BNE :CONT
         JMP CLEANUP

:CONT    
* >>> DEBUG,'5'

**** Update angles

UPDATE   CLC
         LDA SX
         ADC DSX
         CMP #ANGMAX      ;Are we >= maximum angle?
         BCC :CONT1
         SBC #ANGMAX :If so, reset
:CONT1   STA SX
         CLC
         LDA SY
         ADC DSY
         CMP #ANGMAX
         BCC :CONT2
         SBC #ANGMAX      ;Same deal
:CONT2   STA SY
         CLC
         LDA SZ
         ADC DSZ
         CMP #ANGMAX
         BCC :CONT3
         SBC #ANGMAX
:CONT3   STA SZ


**** Rotate coordinates

ROTATE   

*** First, calculate t1,t2,...,t10

** Two macros to simplify our life
ADDA     MAC              ;Add two angles together
         CLC
         LDA ]1
         ADC ]2
         CMP #ANGMAX      ;Is the sum > 2*pi?
         BCC DONE
         SBC #ANGMAX      ;If so, subtract 2*pi
DONE     <<<

SUBA     MAC              ;Subtract two angles
         SEC
         LDA ]1
         SBC ]2
         BCS DONE
         ADC #ANGMAX      ;Oops, we need to add 2*pi
DONE     <<<

** Now calculate t1,t2,etc.

         >>> SUBA,SY;SZ
         STA T1           ;t1=sy-sz
         >>> ADDA,SY;SZ
         STA T2           ;t2=sy+sz
         >>> ADDA,SX;SZ
         STA T3           ;t3=sx+sz
         >>> SUBA,SX;SZ
         STA T4           ;t4=sx-sz
         >>> ADDA,SX;T2
         STA T5           ;t5=sx+t2
         >>> SUBA,SX;T1
         STA T6           ;t6=sx-t1
         >>> ADDA,SX;T1
         STA T7           ;t7=sx+t1
         >>> SUBA,T2;SX
         STA T8           ;t8=t2-sx
         >>> SUBA,SY;SX
         STA T9           ;t9=sy-sx
         >>> ADDA,SX;SY
         STA T10          ;t10=sx+sy

* Et voila!

*** Next, calculate A,B,C,...,I

** Another useful little macro
DIV2     MAC              ;Divide a signed number by 2
                          ;It is assumed that the number
         BPL POS          ;is in the accumulator
         CLC
         EOR #$FF         ;We need to un-negative the number
         ADC #01          ;by taking it's complement
         LSR              ;divide by two
         CLC
         EOR #$FF
         ADC #01          ;Make it negative again
         JMP DONEDIV
POS      LSR              ;Number is positive
DONEDIV  <<<

MUL2     MAC              ;Multiply a signed number by 2
         BPL POSM
         CLC
         EOR #$FF
         ADC #$01
         ASL
         CLC
         EOR #$FF
         ADC #$01
         JMP DONEMUL
POSM     ASL
DONEMUL  <<<

** Note that we are currently making a minor leap
** of faith that no overflows will occur.

:CALCA   CLC
         LDX T1
         LDA COS,X
         LDX T2
         ADC COS,X
         STA A11          ;A=(cos(t1)+cos(t2))/2
:CALCB   LDX T1
         LDA SIN,X
         SEC
         LDX T2
         SBC SIN,X
         STA B12          ;B=(sin(t1)-sin(t2))/2
:CALCC   LDX SY
         LDA SIN,X
         >>> MUL2
         STA C13          ;C=sin(sy)
:CALCD   SEC
         LDX T8
         LDA COS,X
         LDX T7
         SBC COS,X
         SEC
         LDX T5
         SBC COS,X
         CLC
         LDX T6
         ADC COS,X        ;Di=(cos(t8)-cos(t7)+cos(t6)-cos(t5))/2
         >>> DIV2
         CLC
         LDX T3
         ADC SIN,X
         SEC
         LDX T4
         SBC SIN,X
         STA D21          ;D=(sin(t3)-sin(t4)+Di)/2
:CALCE   SEC
         LDX T5
         LDA SIN,X
         LDX T6
         SBC SIN,X
         SEC
         LDX T7
         SBC SIN,X
         SEC
         LDX T8
         SBC SIN,X        ;Ei=(sin(t5)-sin(t6)-sin(t7)-sin(t8))/2
         >>> DIV2
         CLC
         LDX T3
         ADC COS,X
         CLC
         LDX T4
         ADC COS,X
         STA E22          ;E=(cos(t3)+cos(t4)+Ei)/2
:CALCF   LDX T9
         LDA SIN,X
         SEC
         LDX T10
         SBC SIN,X
         STA F23          ;F=(sin(t9)-sin(t10))/2
:CALCG   LDX T6
         LDA SIN,X
         SEC
         LDX T8
         SBC SIN,X
         SEC
         LDX T7
         SBC SIN,X
         SEC
         LDX T5
         SBC SIN,X        ;Gi=(sin(t6)-sin(t8)-sin(t7)-sin(t5))/2
         >>> DIV2
         CLC
         LDX T4
         ADC COS,X
         SEC
         LDX T3
         SBC COS,X
         STA G31          ;G=(cos(t4)-cos(t3)+Gi)/2
         >>> DEBUGA,G31
         >>> DEBUG,'g'
:CALCH   CLC
         LDX T6
         LDA COS,X
         LDX T7
         ADC COS,X
         SEC
         LDX T5
         SBC COS,X
         SEC
         LDX T8
         SBC COS,X        ;Hi=(cos(t6)+cos(t7)-cos(t5)-cos(t8))/2
         >>> DIV2
         CLC
         LDX T3
         ADC SIN,X
         CLC
         LDX T4
         ADC SIN,X
         STA H32          ;H=(sin(t3)+sin(t4)+Hi)/2
:WHEW    CLC
         LDX T9
         LDA COS,X
         LDX T10
         ADC COS,X
         STA I33          ;I=(cos(t9)+cos(t10))/2

** It's all downhill from here.

** Rotate, project, and store the points
DOWNHILL LDA A11          ;This is getting to be a real mess
         STA TA
         LDA B12          ;The reason this is done
         STA TB           ;is to make the code a little
         LDA C13          ;easier to read (and debug!)
         STA TC
         LDA D21          ;These are all temporary locations
         STA TD           ;Used by the projection subroutine.
         LDA E22
         STA TE           ;Otherwise, there would be eight
         LDA F23          ;long routines here.
         STA TF
         LDA G31          ;But it would be significantly faster
         STA TG
         LDA H32
         STA TH
         LDA I33
         STA TI

* A neat macro
NEG      MAC              ;Change the sign of a two's complement
         CLC
         LDA ]1           ;number.
         EOR #$FF
         ADC #$01
         <<<

* P1=[1 1 1]
         JSR PROJECT      ;Unroll this whole thing
         LDX TX1          ;(sorry about these two lines)
         LDY TY1          ;(see PROJECT for reason why)
         STX P1X          ;For a pretty big speed increase!
         STY P1Y
* P2=[1 -1 1]
         >>> NEG,B12      ;Change these elements
         STA TB
         >>> NEG,E22      ;Since y is now -1
         STA TE
         >>> NEG,H32
         STA TH
         JSR PROJECT
         LDX TX1
         LDY TY1
         STX P2X
         STY P2Y
* P3=[-1 -1 1]
         >>> NEG,A11
         STA TA
         >>> NEG,D21
         STA TD
         >>> NEG,G31
         STA TG
         JSR PROJECT
         LDX TX1
         LDY TY1
         STX P3X
         STY P3Y
* P4=[-1 1 1]
         LDA B12
         STA TB
         LDA E22
         STA TE
         LDA H32
         STA TH
         JSR PROJECT
         LDX TX1
         LDY TY1
         STX P4X
         STY P4Y
* P8=[-1 1 -1]
         >>> NEG,C13
         STA TC
         >>> NEG,F23
         STA TF
         >>> NEG,I33
         STA TI
         JSR PROJECT
         LDX TX1
         LDY TY1
         STX P8X
         STY P8Y
* P7=[-1 -1 -1]
         >>> NEG,B12
         STA TB
         >>> NEG,E22
         STA TE
         >>> NEG,H32
         STA TH
         JSR PROJECT
         LDX TX1
         LDY TY1
         STX P7X
         STY P7Y
* P6=[1 -1 -1]
         LDA A11
         STA TA
         LDA D21
         STA TD
         LDA G31
         STA TG
         JSR PROJECT
         LDX TX1
         LDY TY1
         STX P6X
         STY P6Y
* P5=[1 1 -1]
         LDA B12
         STA TB
         LDA E22
         STA TE
         LDA H32
         STA TH
         JSR PROJECT
         LDX TX1
         LDY TY1
         STX P5X
         STY P5Y

**** Clear buffer

         >>> SETBUF
CLRBUF   LDA #$00         ;Pretty straightforward,
         LDX #$08         ;I think
         LDY #$00
:LOOP    STA (BUFFER),Y
         INY
         BNE :LOOP
         INC BUFFER+1
         DEX
         BNE :LOOP
         LDA BUFFER+1

**** Finally, draw the lines.

         LDA P1X          ;[1 1 1]
         STA TX1
         LDA P1Y
         STA TY1
         LDA P2X          ;[1 -1 1]
         STA TX2
         LDA P2Y
         STA TY2
         JSR DRAW         ;First line

         LDA P3X          ;[-1 -1 1]
         STA TX1
         LDA P3Y
         STA TY1
         JSR DRAW         ;Second line

         LDA P4X          ;[-1 1 1]
         STA TX2
         LDA P4Y
         STA TY2
         JSR DRAW         ;Third line

         LDA P1X          ;[1 1 1]
         STA TX1
         LDA P1Y
         STA TY1
         JSR DRAW         ;Fourth line.  One face done.

         LDA P5X          ;[1 1 -1]
         STA TX2
         LDA P5Y
         STA TY2
         JSR DRAW         ;Five

         LDA P6X          ;[1 -1 -1]
         STA TX1
         LDA P6Y
         STA TY1
         JSR DRAW         ;Six

         LDA P2X          ;[1 -1 1]
         STA TX2
         LDA P2Y
         STA TY2
         JSR DRAW         ;Seven

         LDA P7X          ;[-1 -1 -1]
         STA TX2
         LDA P7Y
         STA TY2
         JSR DRAW         ;Eight

         LDA P3X          ;[-1 -1 1]
         STA TX1
         LDA P3Y
         STA TY1
         JSR DRAW         ;Nine

         LDA P8X          ;[-1 1 -1]
         STA TX1
         LDA P8Y
         STA TY1
         JSR DRAW         ;Ten

         LDA P4X          ;[-1 1 1]
         STA TX2
         LDA P4Y
         STA TY2
         JSR DRAW         ;Eleven

         LDA P5X          ;[1 1 -1]
         STA TX2
         LDA P5Y
         STA TY2
         JSR DRAW         ;Twelve!

**** Swap buffers

SWAPBUF  LDA VMCSB
         EOR #$02         ;PretP4Y      DS 1
P5X      DS 1             ;Why are you looking at me like that?
P5Y      DS 1             ;Don't you trust me?
P6X      DS 1
P6Y      DS 1             ;Having another child wasn't my idea.
P7X      DS 1
P7Y      DS 1
P8X      DS 1
P8Y      DS 1
DSX      DS 1             ;DSX is the increment for rotating around x
DSY      DS 1             ;Similar for DSY, DSZ
DSZ      DS 1
SX       DS 1             ;These are the actual angles in x y and z
SY       DS 1
SZ       DS 1
T1       DS 1             ;These are used in the rotation
T2       DS 1
T3       DS 1             ;See the article for more details
T4       DS 1
T5       DS 1
T6       DS 1
T7       DS 1
T8       DS 1
T9       DS 1
T10      DS 1
A11      DS 1             ;These are the elements of the rotation matrix
B12      DS 1             ;XYZ
C13      DS 1
D21      DS 1             ;The number denotes (row,column)
E22      DS 1
F23      DS 1
G31      DS 1
H32      DS 1
I33      DS 1
TA       DS 1             ;These are temporary locations
TB       DS 1             ;for use by the projection routine
TC       DS 1
TD       DS 1
TE       DS 1
TF       DS 1
TG       DS 1
TH       DS 1
TI       DS 1

*-------------------------------
* Set up bit table

         DS ^             ;Clear to end of page
                          ;So that tables start on a page boundary
BITP     LUP 16           ;128 Entries for X
         DFB %10000000
         DFB %01000000
         DFB %00100000
         DFB %00010000
         DFB %00001000
         DFB %00000100
         DFB %00000010
         DFB %00000001
         --^
SIN                       ;Table of sines, 120 bytes
COS      EQU SIN+128      ;Table of cosines
                          ;Both of these trig tables are
                          ;currently set up from BASIC
ZDIV     EQU COS+128      ;Division table
ion matrix
