mirror of
https://github.com/deater/dos33fsprogs.git
synced 2025-01-07 12:31:57 +00:00
994 lines
20 KiB
ArmAsm
994 lines
20 KiB
ArmAsm
;============================================================================
|
|
; Disassembly of a module generated by Bill Budge's 3-D Graphics System and
|
|
; Game Tool.
|
|
;
|
|
; The tool itself is copyright 1980 California Pacific Computer Co. Modules
|
|
; may be marketed and sold so long as they provide a credit notice.
|
|
;
|
|
; The HRCG (code and font) is credited to Christopher Espinosa.
|
|
;===========================================================================
|
|
; Disassembly by Andy McFadden, using 6502bench SourceGen v1.6.
|
|
; Last updated 2020/03/11
|
|
;
|
|
; The manual refers to "points" and "lines" rather than "vertices" and
|
|
; "edges". For consistency the same nomenclature is used here.
|
|
;
|
|
; Two shapes are defined: the space shuttle model from the manual, and a
|
|
; simple cube 11 units on a side. The module is configured for XOR drawing
|
|
|
|
; This makes extensive use of self-modifying code. Labels that begin with an
|
|
; underscore indicate self-modification targets.
|
|
|
|
; Note from Vince Weaver
|
|
; + I've taken the disassembly and am converting it to assembly language
|
|
; I guess it might make more sense to get an assembly language kernel
|
|
; from the program, but I'm going to modify the BASIC anyway
|
|
|
|
|
|
|
|
;===========================================================================
|
|
;
|
|
; You can define up to 16 shapes. Their parameters are stored in the various
|
|
; arrays:
|
|
;
|
|
; CODE_arr+N: 0 (do nothing), 1 (transform & draw), 2 (erase previous,
|
|
; transform, draw new), 3 (erase).
|
|
; X_arr+N: X coordinate of center (0-255).
|
|
; Y_arr+N: Y coordinate of center (0-191).
|
|
; SCALE_arr+N: scale factor, 0-15. 15 is full size, 0 is 1/16th.
|
|
; XROT_arr+N: rotation about X axis, 0-27. 0 is no rotation, 27 is just shy
|
|
; of 360 degrees.
|
|
; YROT_arr+N: rotation about Y axis.
|
|
; ZROT_arr+N: rotation about Z axis.
|
|
; SX_arr+N: (output) X coordinate of last point drawn.
|
|
; SY_arr+N: (output) Y coordinate of last point drawn.
|
|
;
|
|
; The code entry points are:
|
|
; RESET: initializes graphics module, clears the screen, switches display
|
|
; to primary hi-res page.
|
|
; CLR: clears both hi-res screens and switches to primary hi-res page.
|
|
; HIRES: turns on primary hi-res page.
|
|
; CRUNCH: primary animation function.
|
|
;
|
|
; The "CRUNCH" function:
|
|
; - erases objects whose CODE value is 2 or 3
|
|
; - computes new transformations for objects whose CODE value is 1 or 2
|
|
; - draws objects whose CODE value is 1 or 2
|
|
; - flips the display to the other page
|
|
;============================================================================
|
|
|
|
.include "zp.inc"
|
|
|
|
.include "hardware.inc"
|
|
|
|
|
|
entry:
|
|
jsr reset
|
|
jsr make_tables
|
|
|
|
; CODE[0]=1 -> transform and draw
|
|
; CODE[1]=1 -> transform and draw
|
|
|
|
lda #1
|
|
sta CODE_arr
|
|
sta CODE_arr+1
|
|
|
|
; X[0]=127 Y[0]=96:X[1]=20:Y[1]=30 -> co-ord of center
|
|
; SCALE[0]=15:XROT[0]=2:YROT[0]=5:ZROT[0]=0
|
|
; SCALE[1]=15:XROT[1]=2:YROT[1]=5:ZROT[1]=0
|
|
|
|
jsr CRUNCH
|
|
jsr CRUNCH
|
|
|
|
; CODE[0]=2:CODE[1]=2 -> erase and draw new
|
|
|
|
lda #2
|
|
sta CODE_arr
|
|
sta CODE_arr+1
|
|
|
|
loop:
|
|
; ZROT[0]+=1: IF ZEROT[0]==28 THEN ZROT[0]=0
|
|
; YROT[1]=ZROT[0]
|
|
|
|
inc ZROT_arr
|
|
lda ZROT_arr
|
|
cmp #28
|
|
bne zrot_ok
|
|
lda #0
|
|
sta ZROT_arr
|
|
zrot_ok:
|
|
sta YROT_arr+1
|
|
|
|
jsr CRUNCH
|
|
|
|
jmp loop
|
|
|
|
|
|
;===========================================================
|
|
; Note that all tables are page-aligned for performance. *
|
|
;===========================================================
|
|
|
|
.include "shapes.s"
|
|
|
|
.align $100
|
|
|
|
.include "math_constants.s"
|
|
|
|
.include "scale_constants.s"
|
|
|
|
|
|
make_tables:
|
|
|
|
; Make hires row lookup table using HPOSN
|
|
; From the Applesoft ROM
|
|
|
|
ldx #0
|
|
hgr_table_loop:
|
|
ldy #0
|
|
txa
|
|
jsr HPOSN ; (Y,X),(A) (values stores in HGRX,XH,Y)
|
|
ldx HGR_X
|
|
lda GBASL
|
|
sta YTableLo,X
|
|
lda GBASH
|
|
sta YTableHi,X
|
|
inx
|
|
cpx #192
|
|
bne hgr_table_loop
|
|
|
|
;=====================
|
|
; Make Div7 table
|
|
|
|
lda #0
|
|
tax
|
|
tay
|
|
div7_table_loop:
|
|
sta Div7Tab,X
|
|
iny
|
|
cpy #7
|
|
bne not_7
|
|
ldy #0
|
|
clc
|
|
adc #1
|
|
not_7:
|
|
inx
|
|
bne div7_table_loop
|
|
|
|
|
|
;
|
|
; Hi-res bit table. Converts the X coordinate (0-255) into a bit position
|
|
; within a byte. (Essentially 2 to the power of the remainder of the coordinate
|
|
; divided by 7.)
|
|
;
|
|
|
|
lda #1
|
|
ldx #0
|
|
ldy #0
|
|
bittab_table_loop:
|
|
sta HiResBitTab,X
|
|
asl
|
|
|
|
iny
|
|
cpy #7
|
|
bne again_not_7
|
|
ldy #0
|
|
lda #1
|
|
again_not_7:
|
|
inx
|
|
bne bittab_table_loop
|
|
|
|
|
|
rts
|
|
|
|
|
|
;====================================
|
|
; DrawLineList
|
|
;====================================
|
|
DrawLineList:
|
|
|
|
;
|
|
; Draw a list of lines using exclusive-or, which inverts the pixels. Drawing
|
|
; the same thing twice erases it.
|
|
;
|
|
; On entry:
|
|
; $45 - index of first line
|
|
; $46 - index of last line
|
|
; XCoord_0E/YCoord_0F or XCoord_10/YCoord_11 have transformed points in screen
|
|
; coordinates
|
|
;
|
|
; When the module is configured for OR-mode drawing, this code is replaced with
|
|
; a dedicated erase function. The erase code is nearly identical to the draw
|
|
; code, but saves a little time by simply zeroing out whole bytes instead of
|
|
; doing a read-modify-write.
|
|
;
|
|
|
|
|
|
|
|
; Draw code calls here. Since we're configured for XOR mode,
|
|
; this just jumps to the exclusive-or version.
|
|
|
|
|
|
|
|
|
|
DrawLineListEOR:
|
|
ldx FIRST_LINE ; 3 start with the first line in this object
|
|
DrawLoop:
|
|
lda LineStartPoint,X; 4+ get X0,Y0
|
|
tay ; 2
|
|
_0E_or_10_1:
|
|
lda XCoord0_0E,Y ; 4+ the instructions here are modified to load from
|
|
sta XSTART ; 3 the appropriate set of X/Y coordinate tables
|
|
_0F_or_11_1:
|
|
lda YCoord0_0F,Y ; 4+
|
|
sta YSTART ; 3
|
|
lda LineEndPoint,X ; 4+ get X1,Y1
|
|
tay ; 2
|
|
_0E_or_10_2:
|
|
lda XCoord0_0E,Y ; 4+
|
|
sta XEND ; 3
|
|
_0F_or_11_2:
|
|
lda YCoord0_0F,Y ; 4+
|
|
sta YEND ; 3
|
|
stx LINE_INDEX ; 3 save this off
|
|
|
|
; Prep the line draw code. We need to compute deltaX/deltaY, and set a register
|
|
; increment / decrement / no-op instruction depending on which way the line is
|
|
; going.
|
|
|
|
lda XSTART ; 3 compute delta X
|
|
sec ; 2
|
|
sbc XEND ; 3
|
|
bcs L1A2F ; 2+ left to right
|
|
eor #$ff ; 2 right to left; invert value
|
|
adc #$01 ; 2
|
|
ldy #OpINX ; 2
|
|
bne GotDeltaX ; 3
|
|
|
|
L1A2F:
|
|
beq IsVertical ; 2+ branch if deltaX=0
|
|
ldy #OpDEX ; 2
|
|
bne GotDeltaX ; 3
|
|
|
|
IsVertical:
|
|
ldy #OpNOP ; 2 fully vertical, use no-op
|
|
GotDeltaX:
|
|
sta DELTA_X ; 3
|
|
sty _InxDexNop1 ; 4
|
|
sty _InxDexNop2 ; 4
|
|
lda YSTART ; 3 compute delta Y
|
|
sec ; 2
|
|
sbc YEND ; 3
|
|
bcs L1A4E ; 2+ end < start, we're good
|
|
eor #$ff ; 2 invert value
|
|
adc #$01 ; 2
|
|
ldy #OpINY ; 2
|
|
bne GotDeltaY ; 3
|
|
|
|
L1A4E:
|
|
beq IsHorizontal ; 2+ branch if deltaY=0
|
|
ldy #OpDEY ; 2
|
|
bne GotDeltaY ; 3
|
|
|
|
IsHorizontal:
|
|
ldy #OpNOP ; 2 fully horizontal, use no-op
|
|
GotDeltaY:
|
|
sta DELTA_Y ; 3
|
|
sty _InyDeyNop1 ; 4
|
|
sty _InyDeyNop2 ; 4
|
|
ldx XSTART ; 3
|
|
ldy YSTART ; 3
|
|
lda #$00 ; 2
|
|
sta LINE_ADJ ; 3
|
|
lda DELTA_X ; 3
|
|
cmp DELTA_Y ; 3
|
|
bcs HorizDomLine ; 2+
|
|
|
|
; Line draw: vertically dominant (move vertically every step)
|
|
;
|
|
; On entry: X=xpos, Y=ypos
|
|
|
|
VertDomLine:
|
|
cpy YEND ; 3
|
|
beq LineDone ; 2+
|
|
_InyDeyNop1:
|
|
nop ; 2 self-mod INY/DEY/NOP
|
|
lda YTableLo,Y ; 4+ new line, update Y position
|
|
sta HPTR ; 3
|
|
lda YTableHi,Y ; 4+
|
|
ora HPAGE ; 3
|
|
sta HPTR+1 ; 3
|
|
lda LINE_ADJ ; 3 Bresenham update
|
|
clc ; 2
|
|
adc DELTA_X ; 3
|
|
cmp DELTA_Y ; 3
|
|
bcs NewColumn ; 2+
|
|
sta LINE_ADJ ; 3
|
|
bcc SameColumn ; 3
|
|
|
|
NewColumn:
|
|
sbc DELTA_Y ; 3
|
|
sta LINE_ADJ ; 3
|
|
_InxDexNop1:
|
|
nop ;2 self-mod INX/DEX/NOP
|
|
SameColumn:
|
|
sty YSAVE ; 3
|
|
ldy Div7Tab,X ; 4+ XOR-draw the point
|
|
lda (HPTR),Y ; 5+
|
|
eor HiResBitTab,X ; 4+
|
|
sta (HPTR),Y ; 6
|
|
ldy YSAVE ; 3
|
|
jmp VertDomLine ; 3
|
|
|
|
LineDone:
|
|
ldx LINE_INDEX ; 3
|
|
inx ; 2
|
|
cpx LAST_LINE ; 3 reached end?
|
|
beq DrawDone ; 2+
|
|
jmp DrawLoop ; 3
|
|
|
|
DrawDone:
|
|
rts ; 6
|
|
|
|
; Line draw: horizontally dominant (move horizontally every step)
|
|
;
|
|
; On entry: X=xpos, Y=ypos
|
|
|
|
HorizDomLine:
|
|
lda YTableLo,Y ; 4+ set up hi-res pointer
|
|
sta HPTR ; 3
|
|
lda YTableHi,Y ; 4+
|
|
ora HPAGE ; 3
|
|
sta HPTR+1 ; 3
|
|
HorzLoop:
|
|
cpx XEND ; 3 X at end?
|
|
beq LineDone ; 2+ yes, finish
|
|
_InxDexNop2:
|
|
nop ; 2
|
|
lda LINE_ADJ ; 3 Bresenham update
|
|
clc ; 2
|
|
adc DELTA_Y ; 3
|
|
cmp DELTA_X ; 3
|
|
bcs NewRow ; 2+
|
|
sta LINE_ADJ ; 3
|
|
bcc SameRow ; 3
|
|
|
|
NewRow:
|
|
sbc DELTA_X ; 3
|
|
sta LINE_ADJ ; 3
|
|
_InyDeyNop2:
|
|
nop ; 2
|
|
lda YTableLo,Y ; 4+ update Y position
|
|
sta HPTR ; 3
|
|
lda YTableHi,Y ; 4+
|
|
ora HPAGE ; 3
|
|
sta HPTR+1 ; 3
|
|
SameRow:
|
|
sty YSAVE ; 3
|
|
ldy Div7Tab,X ; 4+ XOR-draw the point
|
|
lda (HPTR),Y ; 5+
|
|
eor HiResBitTab,X ; 4+
|
|
sta (HPTR),Y ; 6
|
|
ldy YSAVE ; 3
|
|
jmp HorzLoop ; 3
|
|
|
|
|
|
|
|
|
|
; Current hi-res page.
|
|
;
|
|
; $00 = draw page 1, show page 2
|
|
; $FF = draw page 2, show page 1
|
|
CurPage: .byte $ff
|
|
|
|
;
|
|
; Switch to the other hi-res page.
|
|
;
|
|
SwapPage:
|
|
lda CurPage ; 4
|
|
eor #$ff ; 2 flip to other page
|
|
sta CurPage ; 4
|
|
beq DrawOnPage1 ; 3+
|
|
sta TXTPAGE1 ; 4 draw on page 2, show page 1
|
|
lda #$40 ; 2
|
|
ldx #>XCoord1_10 ; 2
|
|
ldy #>YCoord1_11 ; 2
|
|
bne L1C0F ; 3
|
|
|
|
DrawOnPage1:
|
|
sta TXTPAGE2 ; 4 draw on page 1, show page 2
|
|
lda #$20 ; 2
|
|
ldx #>XCoord0_0E ; 2
|
|
ldy #>YCoord0_0F ; 2
|
|
|
|
; Save the hi-res page, and modify the instructions that read from or write data
|
|
; to the transformed point arrays.
|
|
L1C0F:
|
|
sta HPAGE ; 3
|
|
stx _0E_or_10_1+2 ; 4
|
|
stx _0E_or_10_2+2 ; 4
|
|
stx _0E_or_10_3+2 ; 4
|
|
sty _0F_or_11_1+2 ; 4
|
|
sty _0F_or_11_2+2 ; 4
|
|
sty _0F_or_11_3+2 ; 4
|
|
rts ; 6
|
|
|
|
|
|
; Coordinate transformation function. Transforms all points in a single object.
|
|
;
|
|
; On entry:
|
|
; 1c = SCALE (00-0f)
|
|
; 1d = XC (00-ff)
|
|
; 1e = YC (00-bf)
|
|
; 1f = ZROT (00-1b)
|
|
; 3c = YROT (00-1b)
|
|
; 3d = XROT (00-1b)
|
|
; 45 = index of first point to transform
|
|
; 46 = index of last point to transform
|
|
;
|
|
; Rotation values greater than $1B, and scale factors greater than $0F, disable
|
|
; the calculation. This has the same effect as a rotation value of 0 or a scale
|
|
; of 15, but is more efficient, because this uses self-modifying code to skip
|
|
; the computation entirely.
|
|
;
|
|
|
|
|
|
CompTransform:
|
|
ldx FIRST_POINT ; 3 get first point index; this stays in X for a while
|
|
|
|
; Configure Z rotation.
|
|
|
|
ldy ZROT ; 3
|
|
cpy #$1c ; 2 valid rotation value?
|
|
bcc ConfigZrot ; 2+ yes, configure
|
|
lda #<DoYrot ; 2 no, modify code to skip Z-rot
|
|
sta _BeforeZrot+1 ; 4
|
|
bne NoZrot ; 3
|
|
|
|
ConfigZrot:
|
|
lda #<DoZrot ; 2
|
|
sta _BeforeZrot+1 ; 4
|
|
lda RotIndexLo_sin,Y ; 4+
|
|
sta _zrotLS1+1 ; 4
|
|
sta _zrotLS2+1 ; 4
|
|
lda RotIndexHi_sin,Y ; 4+
|
|
sta _zrotHS1+1 ; 4
|
|
sta _zrotHS2+1 ; 4
|
|
lda RotIndexLo_cos,Y ; 4+
|
|
sta _zrotLC1+1 ; 4
|
|
sta _zrotLC2+1 ; 4
|
|
lda RotIndexHi_cos,Y ; 4+
|
|
sta _zrotHC1+1 ; 4
|
|
sta _zrotHC2+1 ; 4
|
|
|
|
; Configure Y rotation.
|
|
|
|
NoZrot:
|
|
ldy YROT ; 3
|
|
cpy #$1c ; 2 valid rotation value?
|
|
bcc ConfigYrot ; 2+ yes, configure
|
|
lda #<DoXrot ; 2 no, modify code to skip Y-rot
|
|
sta _BeforeYrot+1 ; 4
|
|
bne NoYrot ; 3
|
|
|
|
ConfigYrot:
|
|
lda #<DoYrot ; 2
|
|
sta _BeforeYrot+1 ; 4
|
|
lda RotIndexLo_sin,Y ; 4+
|
|
sta _yrotLS1+1 ; 4
|
|
sta _yrotLS2+1 ; 4
|
|
lda RotIndexHi_sin,Y ; 4+
|
|
sta _yrotHS1+1 ; 4
|
|
sta _yrotHS2+1 ; 4
|
|
lda RotIndexLo_cos,Y ; 4+
|
|
sta _yrotLC1+1 ; 4
|
|
sta _yrotLC2+1 ; 4
|
|
lda RotIndexHi_cos,Y ; 4+
|
|
sta _yrotHC1+1 ; 4
|
|
sta _yrotHC2+1 ; 4
|
|
|
|
; Configure X rotation.
|
|
|
|
NoYrot:
|
|
ldy XROT ; 3
|
|
cpy #$1c ; 2 valid rotation value?
|
|
bcc ConfigXrot ; 2+ yes, configure
|
|
lda #<DoScale ; 2 no, modify code to skip X-rot
|
|
sta _BeforeXrot+1 ; 4
|
|
bne ConfigScale ; 3
|
|
|
|
ConfigXrot:
|
|
lda #<DoXrot ; 2
|
|
sta _BeforeXrot+1 ; 4
|
|
lda RotIndexLo_sin,Y ; 4+
|
|
sta _xrotLS1+1 ; 4
|
|
sta _xrotLS2+1 ; 4
|
|
lda RotIndexHi_sin,Y ; 4+
|
|
sta _xrotHS1+1 ; 4
|
|
sta _xrotHS2+1 ; 4
|
|
lda RotIndexLo_cos,Y ; 4+
|
|
sta _xrotLC1+1 ; 4
|
|
sta _xrotLC2+1 ; 4
|
|
lda RotIndexHi_cos,Y ; 4+
|
|
sta _xrotHC1+1 ; 4
|
|
sta _xrotHC2+1 ; 4
|
|
|
|
; Configure scaling.
|
|
|
|
ConfigScale:
|
|
ldy SCALE ; 3
|
|
cpy #$10 ; 2 valid SCALE value?
|
|
bcc SetScale ; 2+ yes, configure it
|
|
lda #<DoTranslate ; 2 no, skip it
|
|
sta _BeforeScale+1 ; 4
|
|
lda #>DoTranslate ; 2
|
|
sta _BeforeScale+2 ; 4
|
|
bne TransformLoop ; 4
|
|
|
|
SetScale:
|
|
lda #<DoScale ; 2
|
|
sta _BeforeScale+1 ; 4
|
|
lda #>DoScale ; 2
|
|
sta _BeforeScale+2 ; 4
|
|
lda ScaleIndexLo,Y ; 4+ $00, $10, $20, ... $F0
|
|
sta _scaleLX+1 ; 4
|
|
sta _scaleLY+1 ; 4
|
|
lda ScaleIndexHi,Y ; 4+ $00, $01, $02, ... $0F
|
|
sta _scaleHX+1 ; 4
|
|
sta _scaleHY+1 ; 4
|
|
|
|
|
|
;
|
|
; Now that we've got the code modified, perform the computation for all points
|
|
; in the object.
|
|
;
|
|
TransformLoop:
|
|
lda ShapeXCoords,X ; 4+
|
|
sta XC ; 3
|
|
lda ShapeYCoords,X ; 4+
|
|
sta YC ; 3
|
|
lda ShapeZCoords,X ; 4+
|
|
sta ZC ; 3
|
|
stx OUT_INDEX ; 3 save for later
|
|
_BeforeZrot:
|
|
jmp DoZrot ; 3
|
|
|
|
DoZrot:
|
|
lda XC ; 3 rotating about Z, so we need to update X/Y coords
|
|
and #$0f ; 2 split X/Y into nibbles
|
|
sta ROT_TMP ; 3
|
|
lda XC ; 3
|
|
and #$f0 ; 2
|
|
sta ROT_TMP+1 ; 3
|
|
lda YC ; 3
|
|
and #$0f ; 2
|
|
sta ROT_TMP+2 ; 3
|
|
lda YC ; 3
|
|
and #$f0 ; 2
|
|
sta ROT_TMP+3 ; 3
|
|
ldy ROT_TMP ; 3 transform X coord
|
|
ldx ROT_TMP+1 ; 3 XC = X * cos(theta) - Y * sin(theta)
|
|
_zrotLC1:
|
|
lda RotTabLo,Y ; 4+
|
|
clc ; 2
|
|
_zrotHC1:
|
|
adc RotTabHi,X ; 4+
|
|
ldy ROT_TMP+2 ; 3
|
|
ldx ROT_TMP+3 ; 3
|
|
sec ; 2
|
|
_zrotLS1:
|
|
sbc RotTabLo,Y ; 4+
|
|
sec ; 2
|
|
_zrotHS1:
|
|
sbc RotTabHi,X ; 4+
|
|
sta XC ; 3 save updated coord
|
|
_zrotLC2:
|
|
lda RotTabLo,Y ; 4+ transform Y coord
|
|
clc ; 2 YC = Y * cos(theta) + X * sin(theta)
|
|
_zrotHC2:
|
|
adc RotTabHi,X ; 4+
|
|
ldy ROT_TMP ; 3
|
|
ldx ROT_TMP+1 ; 3
|
|
clc ; 2
|
|
_zrotLS2:
|
|
adc RotTabLo,Y ; 4+
|
|
clc ; 2
|
|
_zrotHS2:
|
|
adc RotTabHi,X ; 4+
|
|
sta YC ; 3 save updated coord
|
|
_BeforeYrot:
|
|
jmp DoYrot ; 3
|
|
|
|
DoYrot:
|
|
lda XC ; 3 rotating about Y, so update X/Z
|
|
and #$0f ; 2
|
|
sta ROT_TMP ; 3
|
|
lda XC ; 3
|
|
and #$f0 ; 2
|
|
sta ROT_TMP+1 ; 3
|
|
lda ZC ; 3
|
|
and #$0f ; 2
|
|
sta ROT_TMP+2 ; 3
|
|
lda ZC ; 3
|
|
and #$f0 ; 2
|
|
sta ROT_TMP+3 ; 3
|
|
ldy ROT_TMP ; 3
|
|
ldx ROT_TMP+1 ; 3
|
|
_yrotLC1:
|
|
lda RotTabLo,Y ; 4+
|
|
clc ; 2
|
|
_yrotHC1:
|
|
adc RotTabHi,X ; 4+
|
|
ldy ROT_TMP+2 ; 3
|
|
ldx ROT_TMP+3 ; 3
|
|
sec ; 2
|
|
_yrotLS1:
|
|
sbc RotTabLo,Y ; 4+
|
|
sec ; 2
|
|
_yrotHS1:
|
|
sbc RotTabHi,X ; 4+
|
|
sta XC ; 3
|
|
_yrotLC2:
|
|
lda RotTabLo,Y ; 4+
|
|
clc ; 2
|
|
_yrotHC2:
|
|
adc RotTabHi,X ; 4+
|
|
ldy ROT_TMP ; 3
|
|
ldx ROT_TMP+1 ; 3
|
|
clc ; 2
|
|
_yrotLS2:
|
|
adc RotTabLo,Y ; 4+
|
|
clc ; 2
|
|
_yrotHS2:
|
|
adc RotTabHi,X ; 4+
|
|
sta ZC ; 3
|
|
_BeforeXrot:
|
|
jmp DoXrot ; 3
|
|
|
|
DoXrot:
|
|
lda ZC ; 3 rotating about X, so update Z/Y
|
|
and #$0f ; 2
|
|
sta ROT_TMP ; 3
|
|
lda ZC ; 3
|
|
and #$f0 ; 2
|
|
sta ROT_TMP+1 ; 3
|
|
lda YC ; 3
|
|
and #$0f ; 2
|
|
sta ROT_TMP+2 ; 3
|
|
lda YC ; 3
|
|
and #$f0 ; 2
|
|
sta ROT_TMP+3 ; 3
|
|
ldy ROT_TMP ; 3
|
|
ldx ROT_TMP+1 ; 3
|
|
_xrotLC1:
|
|
lda RotTabLo,Y ; 4+
|
|
clc ; 2
|
|
_xrotHC1:
|
|
adc RotTabHi,X ; 4+
|
|
ldy ROT_TMP+2 ; 3
|
|
ldx ROT_TMP+3 ; 3
|
|
sec ; 2
|
|
_xrotLS1:
|
|
sbc RotTabLo,Y ; 4+
|
|
sec ; 2
|
|
_xrotHS1:
|
|
sbc RotTabHi,X ; 4+
|
|
sta ZC ; 3
|
|
_xrotLC2:
|
|
lda RotTabLo,Y ; 4+
|
|
clc ; 2
|
|
_xrotHC2:
|
|
adc RotTabHi,X ; 4+
|
|
ldy ROT_TMP ; 3
|
|
ldx ROT_TMP+1 ; 3
|
|
clc ; 2
|
|
_xrotLS2:
|
|
adc RotTabLo,Y ; 4+
|
|
clc ; 2
|
|
_xrotHS2:
|
|
adc RotTabHi,X ; 4+
|
|
sta YC ; 3
|
|
_BeforeScale:
|
|
jmp DoScale ; 3
|
|
|
|
|
|
; Apply scaling. Traditionally this is applied before rotation.
|
|
DoScale:
|
|
lda XC ; 3 scale the X coordinate
|
|
and #$f0 ; 2
|
|
tax ; 2
|
|
lda XC ; 3
|
|
and #$0f ; 2
|
|
tay ; 2
|
|
_scaleLX:
|
|
lda ScaleTabLo,Y ; 4+
|
|
clc ; 2
|
|
_scaleHX:
|
|
adc ScaleTabHi,X ; 4+
|
|
sta XC ; 3
|
|
lda YC ; 3 scale the Y coordinate
|
|
and #$f0 ; 2
|
|
tax ; 2
|
|
lda YC ; 3
|
|
and #$0f ; 2
|
|
tay ; 2
|
|
_scaleLY:
|
|
lda ScaleTabLo,Y ; 4+
|
|
clc ; 2
|
|
_scaleHY:
|
|
adc ScaleTabHi,X ; 4+
|
|
sta YC ; 3
|
|
|
|
;
|
|
; Apply translation.
|
|
;
|
|
; This is the final step, so the result is written to the transformed-point
|
|
; arrays.
|
|
;
|
|
DoTranslate:
|
|
ldx OUT_INDEX ; 3
|
|
lda XC ; 3
|
|
clc ; 2
|
|
adc XPOSN ; 3 object center in screen coordinates
|
|
_0E_or_10_3:
|
|
sta XCoord0_0E,X ; 5
|
|
lda YPOSN ; 3
|
|
sec ; 2
|
|
sbc YC ; 3
|
|
_0F_or_11_3:
|
|
sta YCoord0_0F,X ; 5
|
|
inx ; 2
|
|
cpx LAST_POINT ; 3 done?
|
|
beq TransformDone ; 2+ yes, bail
|
|
jmp TransformLoop ; 3
|
|
|
|
TransformDone:
|
|
rts ; 6
|
|
|
|
SavedShapeIndex:
|
|
.byte $ad ;holds shape index while we work
|
|
|
|
|
|
|
|
|
|
;*******************************************************************************
|
|
; CRUNCH/CRNCH% entry point *
|
|
; *
|
|
; For each object, do what CODE%(n) tells us to: *
|
|
; *
|
|
; 0 - do nothing *
|
|
; 1 - transform and draw *
|
|
; 2 - erase, transform, draw *
|
|
; 3 - erase *
|
|
;*******************************************************************************
|
|
|
|
CRUNCH:
|
|
|
|
;==============================
|
|
; First pass: erase old shapes
|
|
;==============================
|
|
|
|
ldx #NumObjects ; 2 number of defined objects
|
|
ShapeLoop:
|
|
dex ; 2
|
|
bmi Transform ; 2+ done
|
|
_codeAR1:
|
|
lda CODE_arr,X ; 4+
|
|
cmp #$02 ; 2 2 or 3?
|
|
bcc ShapeLoop ; 2+ no, move on
|
|
stx SavedShapeIndex ; 4
|
|
lda FirstLineIndex,X; 4+
|
|
sta FIRST_LINE ; 3
|
|
lda LastLineIndex,X ; 4+
|
|
sta LAST_LINE ; 3
|
|
cmp FIRST_LINE ; 3 is number of lines <= 0?
|
|
bcc NoLines1 ; 2+
|
|
beq NoLines1 ; 2+ yes, skip draw
|
|
jsr DrawLineListEOR ; 6 erase with EOR version, regardless of config
|
|
NoLines1:
|
|
ldx SavedShapeIndex ; 4
|
|
jmp ShapeLoop ; 3 ...always
|
|
|
|
;===============================
|
|
; Second pass: transform shapes
|
|
;===============================
|
|
Transform:
|
|
ldx #NumObjects ; 2
|
|
TransLoop:
|
|
dex ; 2
|
|
bmi DrawNew ; 2+
|
|
_codeAR2:
|
|
lda CODE_arr,X ; 4+
|
|
beq TransLoop ; 2+ is it zero or three?
|
|
cmp #$03 ; 2
|
|
beq TransLoop ; 2+ yes, we only draw on 1 or 2
|
|
|
|
; Extract the scale, X/Y, and rotation values out of the arrays and copy them to
|
|
; zero-page locations.
|
|
|
|
_scaleAR:
|
|
lda SCALE_arr,X ; 4+
|
|
sta SCALE ; 3
|
|
_xAR:
|
|
lda X_arr,X ; 4+
|
|
sta XPOSN ; 3
|
|
_yAR:
|
|
lda Y_arr,X ; 4+
|
|
sta YPOSN ; 3
|
|
_zrotAR:
|
|
lda ZROT_arr,X ; 4+
|
|
sta ZROT ; 3
|
|
_yrotAR:
|
|
lda YROT_arr,X ; 4+
|
|
sta YROT ; 3
|
|
_xrotAR:
|
|
lda XROT_arr,X ; 4+
|
|
sta XROT ; 3
|
|
stx SavedShapeIndex ; 4 save this off
|
|
lda FirstPointIndex,X; 4+
|
|
sta FIRST_LINE ; 3 (actually first_point)
|
|
lda LastPointIndex,X; 4+
|
|
sta LAST_LINE ; 3
|
|
cmp FIRST_LINE ; 3 is number of points <= 0?
|
|
bcc NoPoints ; 2+
|
|
beq NoPoints ; 2+ yes, skip transform
|
|
jsr CompTransform ; 6 transform all points
|
|
NoPoints:
|
|
ldx SavedShapeIndex ; 4
|
|
lda XC ; 3
|
|
clc ; 2
|
|
adc XPOSN ; 3
|
|
_sxAR:
|
|
sta SX_arr,X ; 5
|
|
lda YPOSN ; 3
|
|
sec ; 2
|
|
sbc YC ; 3
|
|
_syAR:
|
|
sta SY_arr,X ; 5
|
|
jmp TransLoop ; 3
|
|
|
|
;=============================
|
|
; Third pass: draw shapes
|
|
;=============================
|
|
|
|
DrawNew:
|
|
ldx #NumObjects ; 2
|
|
L1ECE:
|
|
dex ; 2
|
|
bmi L1EF9 ; 2+
|
|
_codeAR3:
|
|
lda CODE_arr,X ; 4+ is it 0 or 3?
|
|
beq L1ECE ; 2+
|
|
cmp #$03 ; 2
|
|
beq L1ECE ; 2+ yup, no draw
|
|
stx SavedShapeIndex ; 4 save index
|
|
lda FirstLineIndex,X; 4+ draw all the lines in the shape
|
|
sta FIRST_LINE ; 3
|
|
lda LastLineIndex,X ; 4+
|
|
sta LAST_LINE ; 3
|
|
cmp FIRST_LINE ; 3 is number of lines <= 0?
|
|
bcc NoLines2 ; 2+
|
|
beq NoLines2 ; 2+ yes, skip draw
|
|
jsr DrawLineList ; 6 draw all lines
|
|
NoLines2:
|
|
ldx SavedShapeIndex ; 4
|
|
bpl L1ECE ; 3 ...always
|
|
|
|
L1EF9:
|
|
jmp SwapPage ; 3
|
|
|
|
|
|
;===============================================
|
|
;===============================================
|
|
; RESET entry point
|
|
;
|
|
; + Zeroes out the CODE array
|
|
; + erases both hi-res screens
|
|
; + enables display of the primary hi-res page
|
|
;===============================================
|
|
;===============================================
|
|
|
|
reset:
|
|
; lda #$0
|
|
; ldy #$F
|
|
;zero_code_loop:
|
|
; sta CODE_arr,y ; 5 zero out CODE
|
|
; dey ; 2
|
|
; bpl zero_code_loop ; 3+
|
|
jsr CLEAR ; 6
|
|
jsr SwapPage ; 6
|
|
jsr SwapPage ; 6
|
|
rts ; 6
|
|
|
|
;*******************************************************************************
|
|
; CLEAR/CLR% entry point *
|
|
; *
|
|
; Clears both hi-res pages. *
|
|
;*******************************************************************************
|
|
|
|
; Clear variables
|
|
|
|
CLEAR:
|
|
lda #$20 ; 2 hi-res page 1
|
|
sta PTR1+1 ; 3
|
|
lda #$40 ; 2 hi-res page 2
|
|
sta PTR2+1 ; 3
|
|
ldy #$00 ; 2
|
|
sty PTR1 ; 3
|
|
sty PTR2 ; 3
|
|
L1F1D:
|
|
tya ; 2
|
|
L1F1E:
|
|
sta (PTR1),Y ; 6 erase both pages
|
|
sta (PTR2),Y ; 6
|
|
iny ; 2
|
|
bne L1F1E ; 2+
|
|
inc PTR1+1 ; 5
|
|
inc PTR2+1 ; 5
|
|
lda PTR1+1 ; 3 (could hold counter in X-reg)
|
|
and #$3f ; 2
|
|
bne L1F1D ; 2+
|
|
|
|
;==============================
|
|
; HIRES entry point
|
|
;
|
|
; Displays primary hi-res page.
|
|
;===============================
|
|
|
|
hi_res:
|
|
sta HIRES ; 4
|
|
sta MIXCLR ; 4
|
|
sta TXTCLR ; 4
|
|
rts ; 6
|
|
|
|
|
|
; size these as appropriate
|
|
; we are assuming only 2 shapes here, can be up to 16
|
|
CODE_arr:
|
|
.byte $00,$00
|
|
X_arr:
|
|
.byte 127,20
|
|
Y_arr:
|
|
.byte 96,30
|
|
XROT_arr:
|
|
.byte 2,2
|
|
YROT_arr:
|
|
.byte 5,5
|
|
ZROT_arr:
|
|
.byte 0,0
|
|
SCALE_arr:
|
|
.byte 15,15
|
|
SX_arr:
|
|
.byte $00,$00
|
|
SY_arr:
|
|
.byte $00,$00
|
|
|
|
|
|
; layout:
|
|
|
|
;
|
|
; These four buffers hold transformed points in screen coordinates. The points
|
|
; are in the same order as they are in the mesh definition.
|
|
;
|
|
; One pair of tables holds the X/Y screen coordinates from the previous frame,
|
|
; the other pair of tables holds the coordinates being transformed for the
|
|
; current frame. We need two sets because we're display set 0 while generating
|
|
; set 1, and after we flip we need to use set 0 again to erase the display.
|
|
;
|
|
; ----------
|
|
;
|
|
; Computed X coordinate, set 0.
|
|
XCoord0_0E = $6000
|
|
; Computed Y coordinate, set 0.
|
|
YCoord0_0F = $6100
|
|
; Computed X coordinate, set 1.
|
|
XCoord1_10 = $6200
|
|
; Computed Y coordinate, set 1.
|
|
YCoord1_11 = $6300
|
|
|
|
; hgr lookup
|
|
|
|
YTableLo = $6400
|
|
YTableHi = $6500
|
|
Div7Tab = $6600
|
|
HiResBitTab = $6700
|