A Different Perspective, part III
by Stephen Judd --- sjudd@nwu.edu
George Taylor --- aa601@cfn.cs.dal.ca
Whew! What a busy time it's been -- research to get done,
conferences, classes... between getting things done and blowing other
things off, I one day reflected for a moment and realized that I had
three days left to get the next article together for C=Hacking! So
everything has been slapped together at the last minute, and I hope
you'll forgive any bugs or unclear concepts.
>>> ANECDOTE ALERT <<<
And that reminds me: I just got JiffyDOS and an FD-2000 drive --
what a wonderful device. I have a 1.6 megabyte disk formatted into
three partitions. The first contains my Merlin 128 assembler, the
second is some 4000 blocks large and I use it for all my various
versions of code while debugging, and the third is maybe 1000 blocks,
and contains only finished code -- no more swapping disks, no more
deleting old versions that I hope I don't need to make room on the
disk. Also, when I installed JiffyDOS I found a serious bug in my
128D -- a cricket, dead among the IC's.
This time we will cover a lot of ground which isn't so much
cutting-edge as it is very useful. Let's face it: cubes are getting
more than a little dull. A worthy end goal is to have a completely
general routine for plotting a series of polygons -- that is, you supply
a list of (x,y,z) coordinates from which the program can form a list of
polygons. These polygons may then be displayed in 2D, rotated, magnified,
filled, etc. And, much to my three-day astonishment, that is exactly
what we are going to do.
But first, a little excursion. One thing we are of course always
thinking about is optimization possibilities: in the shower, while
sleeping/dreaming, out on dates, etc. So, where to begin? The biggest
cycle hogs in the program are line drawing and face filling -- well,
filling faces is pretty straightforward. What about line drawing?
Well, one downer of the routine is that every single pixel is
plotted. But as we know, on a computer any given line is made up of
several smaller vertical and horizontal lines -- wouldn't it be neat
if we could think of a way to plot these line chunks all at once,
instead of a pixel at a time?
Heck yes it would! So here we go:
Neat-o Enhanced Chunky Line Drawing Routine
First we need to be in the right mindframe. Let's say you're
drawing a line where you move three pixels in x before it's time to take
a step in y. Instead of plotting all three pixels it would of course
be much more efficient to just stick a number like %00011100 in the
drawing buffer. But somehow we need to keep track of a) how large the
chunk needs to be, and b) where exactly the chunk is.
In the above example, we started at a particular x-value:
%00010000
and we want to keep adding ones to the right of the starting point; three,
to be exact. Hmmm... we need to somehow rotate the starting bit in a way
that leaves a trail of ones behind it. Maybe rotate and ORA with the
original bit? But what happens when you take a step in Y?
No, we need something far sneakier. Let's say that instead of
%00010000 we start with
x = %00011111
Now, with each step in the x direction, we do an arithmetic shift on x. So
after one step we have
x = %00001111
and after two steps
x = %00000111
and at the third step of course
x = %00000011
Now it is time to take a step in Y. But now look: if we EOR x with its
original value xold = %00011111, we get
x EOR xold = %00011100
which is exactly the chunk we wanted. Moreover, x still remembers where it
is, so we don't have to do anything special each time a step is taken in
the y-direction.
So here is the algorithm for drawing a line in the x-direction:
initialize x, dx, etc.
xold = x
take a step in x: LSR X
have we hit the end of a column? If so, then plot and check on y
is it time to take a step in y?
if not, take another step in x
if it is, then let a=x EOR xold
plot a into the buffer
let xold=x
keep on going until we're finished
This simple modification gives us a substantial speed increase --
on the old filled hires cube3d program, I measured a gain of one frame per
second. Not earth-shattering, but not bad either! When faces are not
filled, the difference is of course much more noticable.
There are a few things to be careful of. There was a bug in the
old routine when the line was a single point. In that case dx=dy=0, and
the program would draw a vertical line on the screen. There are probably
some other things to be careful of, but since I wrote this part of the
code three months ago I really don't remember any of them!
This takes care of horizontal line chunks -- what about vertical
chunks? Well, because of the way points are plotted there is nothing
we can do about them. But, as we shall soon see, if we use an EOR-buffer
to fill faces we will be forced to take care of the vertical chunks!
General Polygon Routine
Now we can begin thinking about a general polygon routine. First
we need a list of sets of points, where each set corresponds to a
polygon. The first number in a set could be the number of (x,y,z) points
in that set, and the points could then follow. So a triangle could
be given by the data set:
3 -1 0 0 0 1 0 1 0 0
This would be a triangle with vertices at (-1,0,0), (0,1,0), and (1,0,0).
We can mash a bunch of these sets together, but somehow we have to know
when we've hit the end -- for this we can use a zero, since we don't
want to plot polygons with zero points in them.
For that matter, how many points should there be in a polygon?
There must be at least three, otherwise it makes no sense. Since we
want our polygons to be closed, the computer should be smart enough to
connect the last point to the first point -- in our triangle above,
the computer would join (-1,0,0) to (0,1,0), (0,1,0) to (1,0,0), and
(1,0,0) to (-1,0,0).
Now that we have a polygon, we want to rotate it. You will
recall that we have calculated a rotation matrix M, which acts on
points. So we need apply our rotation transform to each of the
points in the polygon, i.e. multiply M times each point of the
polygon. Furthermore, we need to project each of these points.
Uh-oh: matrix multiplication. In the past we have avoided this
issue by putting the vertices of our cube at 1 or -1. So we need to
use our multiplication routine from last time. But wait! As you recall,
the last program used a specially modified multiplication table. To get
a wider range of numbers to multiply we will need another set of
multiplication tables -- no big whoop.
Now, if you review the multiplication routine from last time,
it adds two numbers and subtracts two numbers. What kinds of numbers
will we be dealing with? The matrix elements vary between -64..64.
This then fixes our range of polygon coordinates from -64..64. Why?
If the matrix element is 64, and we multiply it by 64, the multiplication
routine will add 64 and 64 and get 128, which is right on the edge of
our multiplication table.
Can we improve this rotation process in any way? In fact, we can
cut down on the number of multiplications (i.e. do eight or even seven
instead of nine multiplications). However, there is a fair amount of
overhead involved in doing so, and our multiply routine is fast enough
that the extra overhead and complexity really gain us very little in all
but the most complicated of polygons. In other words, I didn't bother.
What about hidden faces? Again, from last time you may recall
that a method was described which used the cross-product of the projected
vectors. How do we implement this in the program? Well, if we take
the first three points of the polygon, we have two vectors. Let's say
these points are P1 P2 and P3. Then V1=P1-P2 and V2=P3-P2 are two
vectors in the plane of the polygon which are connected at the point P2
(this analysis will of course only work if the polygon lies in some plane).
Depending on how we take the cross product, the sign will be positive or
negative, and this will tell us if the polygon is visible.
Depending on how we take the cross product? Absolutely.
v1 x v2 = -v2 x v1. What it really boils down to is how you define the
points in your polygon. Specifically, what order they are in. Points
that are specified in a clockwise manner will give a face pointing in
the opposite direction of a polygon with the same points specified in
a counter-clockwise order. In my program, the polygons must be entered
in counter-clockwise order (with you facing the polygon) for hidden
faces to work the way you want them to ;-).
One other neat thing to have is the ability to zoom in and out.
We know from the very first article that zooming corresponds to multiplying
the projected points by a number, so that's what we'll do. The multiplication
routine returns A=A*Y/64, so a zoom factor of 64 would be like multiplying
the point by one. All the program does is multiply the projected points
by a number zoom, unless zoom=64, in which case the program skips the
zoom multiply. Be warned! No checks of any sort are made in the program,
so you can zoom at your own risk!
The important things to remember are: when entering polygons,
make sure the numbers range from -64 to 64, and that you enter points
in counterclockwise. Our triangle example above really should have been
entered as, say,
3 -64 0 0 64 0 0 0 64 0
Filled Faces -- Using an EOR buffer
Well we still have one thing left, which was alluded to in the
previous article: using EOR to make a filled face. Some possible
difficulties were raised, but when you plot a single polygon at a
time, the problem becomes vastly simplified.
First I should perhaps remind you what exclusive-or is: either
A or B, but not both. So 1 EOR 0 = 1, as does 0 EOR 1, but 0 EOR 0 = 0
and 1 EOR 1 = 0. As a simple introduction to using this for filling
faces, consider the following piece of the drawing buffer:
00001011 M1
00000000 M2
00000001 M3
00001010 M4
Lets say we move down memory, EORing as we go. Let M2 = M1 EOR M2. Then
let M3 = M2 EOR M3. Then let M4 = M3 EOR M4. Our little piece of memory
is now:
00001011 M1
00001011 M2
00001010 M3
00000000 M4
What just happened? Imagine that the original memory was a series of
pieces of line segments. We have just filled in the area between the
two line segments, like magic!
If you still aren't getting it, draw a large section of memory,
and then draw an object in it, like a triangle, or a trapazoid, and
EOR the memory by hand, starting from the top and moving downwards.
EOR flips bits. If you start with a zero, it stays zero until
it hits a one. It will then stay one until it hits another one. So
you can see that if you have an object bounded by ones, EORing
successive memory locations will automagically fill the object.
Right? Well, we have to be careful. One major problem is
a vertical line:
1 1
1 goes to 0
1 1
1 0
Not only is the resultant line dashed, but if there are an odd number of
points in the line segment, the last one will happily move downwards in
memory, and give you a much longer vertical line than you expected! Since
any line with slope greater than one is made up of a series of line
segments, this is a major consideration.
Another problem arises with single points: a one just sitting all
by itself will also generate a nice streak down your drawing area.
If you think about it, what we ideally want to have is an object
that at any given value of x there are exactly two points, one defining
the top of the object, and the other defining the bottom. This gives us
the insight to solve the above two problems.
First let's think about vertical lines. In principle we could
plot the first and last endpoints of each vertical line chunk, but that
is exactly what we don't want! Remember that these are closed polygons,
which means that there are _two_ lines we need to think about. If I
plot just a single point in each vertical line segment, there must
be another point somehwere, either above or below it, from another
line segment, which will close the point to EOR-filling. Remember, we
want exactly two points at each value of x: one will come from the
line, and the other will come from the other line which must lie above
or below the current one.
Furthermore, with any convex polygon there are exactly two
lines which come together at each vertex of the polygon. This means
that there are only certain cases which we need to worry about.
For instance, two lines might join in any of the following ways:
\ / \ /
\ / \ /
\_____ _____/ \/ etc.
If you draw out the different cases involving vertical lines, you can see
that you have to be careful about plotting the lines. One tricky one
is where two vertical lines with different slopes overlap at the point
of intersection.
So after staring at these pictures for a while, you can find
a consistent method which solves these difficulties. As long as you
follow the following rules, the problems all disappear; the line routine
needs to be modified slightly:
- When plotting a vertical line (i.e. big steps in Y direction),
don't plot the endpoints (i.e. x1,y1 and x2,y2).
- When plotting a vertical line, consistently plot either the
first part of each chunk or the last part of each chunk
(excluding the endpoints of course). In other words, only
plot a point when you take a step in x, and then plot one
and only one point.
Now I deduced these by staring at pictures for a few hours and trying
different things like top/bottom of chunk, left/right, first/last, etc.
You can see that in some cases this ensures that only one point appears
on a given line segment. But to me the only way to convince yourself
that this really does work is to draw a bunch of pictures, and try it
out! You have cases where two vertical lines intersect, and where
a vertical line intersects a horizontal line.
But there is still one thing which we have forgotten -- the
case of a single point. This can happen in, for instance, a pointy
triangle, pointing in the x-direction. How do we fix this? By
simply avoiding the point: in the line drawing routine, use EOR
to plot the points instead of ORA. Since vertical lines skip the
endpoints, vertical-horizontal intersections are OK. Horizontal-
horizontal intersections will force the point of intersection to
be zero.
Uh-oh, what about intersections like -----*------. Quite frankly
I just thought of it, and I think my program will fail on intersections
like these. Drat. Well, that just gives us something for next time!
One other thing needs to be mentioned: for EOR-filling to be useful
you need to draw the polygon in a special buffer, and then EOR this buffer
into the main display buffer. If you try to EOR the display buffer directly
you are going to have a whole heap of trouble, such as the concerns raised
last time.
Finally, this gives a simple way of filling with patterns instead
of boring monocolor. Instead of EOR (EORBUF),Y : ORA (DRAWBUF),Y you can
use EOR (EORBUF),Y : AND PATTERN,Y : ORA (DRAWBUF),Y (as long as you
preserve the original EOR (EORBUF),Y).
Well I am extremely tired and I hope Craig hasn't sent out C=Hacking
without me! I hope you have fun playing with the program, and I would be
very interested in seeing any neat geometric shapes you might design!
Program notes:
- Hidden faces defaults to "on". If you enter a shape and a black
screen comes up, hit 'h' to turn off hidden faces (you probably
entered the polygon clockwise).
- There is no pattern filling -- just simple EOR with a twist:
the EOR buffer is EOR'd into the drawing buffer.
- You might start hosing memory if you zoom too large.
SLJ 6/15/95
Addendum
Stephen Judd sjudd@nwu.edu
Last time we put a circle into the 2D graphics toolbox. Chris
McBride has pointed something out to me about the algorithm, which makes
it complete. As you may recall, the algorithm gave a very squarish
circle for small radii. Chris told me that setting the initial counter
value to R/2, instead of R, gave a perfect circle. What is going on?
If you recall the algorithm, we are computing a fractional quantity,
and when that quantity becomes larger than one, we decrease X. Wouldn't
it be a whole lot smarter to round off that fraction instead of
truncate it? Of course it would, and that is what starting the counter
at R/2 does.
So, to update the previous algorithm, A should be initialized to
R/2 instead of R, which means that we change
LDA R
to
LDA R
LSR
for a perfect circle every time.
Here are the binaries:
And here is the source code:
********************************
*``````````````````````````````*
*`Stephen`Judd`````````````````*
*`George`Taylor````````````````*
*`Started:`7/11/94`````````````*
*`Finished:`7/19/94````````````*
*`v2.0`Completed:`12/17/94`````*
*`v3.0`Completed:`3/20/95``````*
*`v3.1`Completed:`6/14/95``````*
*`v3.2`Completed:`6/15/95``````*
*``````````````````````````````*
*`Well,`if`all`goes`well`this``*
*`program`will`rotate`a`cube.``*
*``````````````````````````````*
*`v2.0`+`New`and`Improved!`````*
*`Now`with`faster`routines,````*
*`hidden`surfaces,`filled``````*
*`faces,`and`extra`top`secret``*
*`text`messages!```````````````*
*``````````````````````````````*
*`v3.0`+`Fast`chunky`line``````*
*`routine.`````````````````````*
*``````````````````````````````*
*`v3.1`+`General`polygon`plot``*
*`with`hidden`faces`(X-product)*
*`and`zoom`feature.````````````*
*``````````````````````````````*
*`v3.2`+`EOR-buffer`filling````*
*``````````````````````````````*
*`This`program`is`intended`to``*
*`accompany`the`article`in`````*
*`C=Hacking,`Jun.`95`issue.````*
*`For`details`on`this`program,`*
*`read`the`article!````````````*
*``````````````````````````````*
*`Write`to`us!`````````````````*
*``````````````````````````````*
*`Myself`when`young`did````````*
*`eagerly`frequent`````````````*
*`Doctor`and`Saint,`and`heard``*
*`great`Argument```````````````*
*``About`it`and`about:`but`````*
*``evermore````````````````````*
*`Came`out`by`the`same`Door````*
*`as`in`I`went.````````````````*
*````-`Rubaiyat````````````````*
*``````````````````````````````*
*`Though`I`speak`with`the``````*
*`tongues`of`men`and`of`angles`*
*`and`have`not`love,`I`am``````*
*`become`as`sounding`brass,`or`*
*`a`tinkling`cymbal.```````````*
*````-`1`Corinthians`13````````*
*``````````````````````````````*
*`P.S.`This`was`written`using``*
*``````Merlin`128.`````````````*
********************************
ORG $8000
*`Constants
BUFF1 EQU $3000 ;First`character`set
BUFF2 EQU $3800 ;Second`character`set
EORBUF EQU $4000 ;EOR-buffer
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
OLDX EQU $FD
CHUNK EQU $FE
DX EQU $67 ;This`is`shared`with`T1`below
DY EQU $68
TEMP1 EQU $FB ;Of`course,`could`conflict`with`x1
TEMP2 EQU $FC ;Temporary`variables
ZTEMP EQU $02 ;Used`for`buffer`swap.``Don't`touch.
Z1 EQU $22 ;Used`by`math`routine
Z2 EQU $24 ;Don't`touch`these`either!
Z3 EQU $26
Z4 EQU $28
K EQU $B6 ;Constant`used`for`hidden
;surface`detection`-`don't`touch
HIDE EQU $B5 ;Are`surfaces`hidden?
FILL EQU $50 ;Are`we`using`EOR-fill?
ANGMAX EQU 120 ;There`are`2*pi/angmax`angles
*`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
*`Some`variables
GLOBXMIN = $3F ;These`are`used`in`clearing`the
GLOBXMAX = $40 ;drawing`(global)`buffer
GLOBYMIN = $41
GLOBYMAX = $42
LOCXMIN = $57 ;These`are`used`in`clearing`the
LOCXMAX = $58 ;EOR`(local)`buffer
LOCYMIN = $59
LOCYMAX = $60
P1X = $92 ;These`are`temporary`storage
P1Y = $93 ;Used`in`plotting`the`projection
P1Z = $94
P2X = $95 ;They`are`here`so`that`we
P2Y = $96 ;don't`have`to`recalculate`them.
P2Z = $AE
P3X = $AF ;They`make`life`easy.
P3Y = $B0
P3Z = $B1 ;Why`are`you`looking`at`me`like`that?
P1T = A Different Perspective,
P2T = $B3
P3T = $B4 ;Having`another`child`wasn't`my`idea.
INDEX = $51
COUNTPTS = $52
ZOOM = $71 ;Zoom`factor
DSX = $61 ;DSX`is`the`increment`for
;rotating`around`x
DSY = $62 ;Similar`for`DSY,`DSZ
DSZ = $63
SX = $64 ;These`are`the`actual`angles`in`x`y`and`z
SY = $65
SZ = $66
T1 = $67 ;These`are`used`in`the`rotation
T2 = $68
T3 = $69 ;See`the`article`for`more`details
T4 = $6A
T5 = $6B
T6 = $6C
T7 = $6D
T8 = $6E
T9 = $6F
T10 = $70
A11 = $A5 ;These`are`the`elements`of`the`rotation`matrix
B12 = $A6 ;XYZ
C13 = $A7
D21 = $A8 ;The`number`denotes`(row,column)
E22 = $A9
F23 = $AA
G31 = $AB
H32 = $AC
I33 = $AD
***`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
CLI
>>> 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 <<<
*-------------------------------
LDA #$00
STA BKGND
STA BORDER
LDA VMCSB
AND #%00001111 ;Screen`memory`to`1024
ORA #%00010000
STA VMCSB
LDY #00
LDA #TTEXT
STA TEMP2
JMP TITLE
TTEXT HEX 9305111111 ;clear`screen,`white,`crsr`dn
TXT '`````````````cube3d`v3.2',0d,0d
TXT '``````````````````by',0d
HEX 9F ;cyan
TXT '````stephen`judd'
HEX 99
TXT '````george`taylor',0d,0d
HEX 9B
TXT '``check`out`the`jan.`95`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 '`-`reset',0d
HEX 1D1D12
TXT '`+/-`',92
TXT '`-`zoom`in/out',0d
HEX 1D1D12
TXT '``h``',92
TXT '`-`toggle`hidden`surfaces',0d
HEX 1D1D12
TXT 'space',92
TXT '`-`toggle`surface`filling',0d,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 LDA #>TMATH1
STA Z1+1
STA Z2+1
LDA #>TMATH2
STA Z3+1
STA Z4+1
****`Clear`screen`and`set`up`"bitmap"
SETUP LDA #$01 ;White
STA $D021 ;This`is`done`so`that`older
LDA #147 ;machines`will`set`up
JSR CHROUT
LDA #$00 ;correctly
STA $D021
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
****`Clear`buffers
LDA #BUFF1
STA BUFFER+1
LDY #$00
LDX #24 ;Assuming`all`three`buffers`are
LDA #$00 ;back-to-back
:BLOOP STA (BUFFER),Y
INY
BNE :BLOOP
INC BUFFER+1
DEX
BNE :BLOOP
****`Set`up`buffers
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 LOCXMIN
STA LOCXMAX
STA LOCYMIN
STA LOCYMAX
STA GLOBXMIN
STA GLOBYMIN
STA GLOBXMAX
STA GLOBYMAX
STA DSX
STA DSY
STA DSZ
STA SX
STA SY
STA SZ
STA FILL
LDA #01
STA HIDE
LDA #64
STA ZOOM
*-------------------------------
*`Main`loop
****`Get`keypress
MAIN
CLI
KPRESS JSR GETIN
CMP #133 ;F1?
BNE :F2
LDA DSX
CMP #ANGMAX/2 ;No`more`than`pi
BEQ :CONT1
INC DSX ;otherwise`increase`x-rotation
JMP :CONT
:F2 CMP #137 ;F2?
BNE :F3
LDA DSX
BEQ :CONT1
DEC DSX
JMP :CONT
:F3 CMP #134
BNE :F4
LDA DSY
CMP #ANGMAX/2
BEQ :CONT1
INC DSY ;Increase`y-rotation
JMP :CONT
:F4 CMP #138
BNE :F5
LDA DSY
BEQ :CONT1
DEC DSY
JMP :CONT
:F5 CMP #135
BNE :F6
LDA DSZ
CMP #ANGMAX/2
BEQ :CONT1
INC DSZ ;z-rotation
JMP :CONT
:F6 CMP #139
BNE :F7
LDA DSZ
BEQ :CONT1
DEC DSZ
JMP :CONT
:F7 CMP #136
BNE :PLUS
JMP INIT
:CONT1 JMP :CONT
:PLUS CMP #'+'
BNE :MINUS
INC ZOOM ;Bah,`who`needs`error`checking?
INC ZOOM
JMP :CONT
:MINUS CMP #'-'
BNE :H
DEC ZOOM
DEC ZOOM
BPL :CONT
INC ZOOM
INC ZOOM
JMP :CONT
:H CMP #'h'
BNE :SPACE
LDA HIDE
EOR #$01
STA HIDE
JMP :CONT
:SPACE CMP #'`'
BNE :Q
LDA FILL
EOR #$01
STA FILL
JMP :CONT
:Q CMP #'q' ;q`quits
BNE :CONT
JMP CLEANUP
:CONT SEI ;Speed`things`up`a`bit
****`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
: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.
DOWNHILL
****`Clear`buffer
*`A`little`macro
SETBUF MAC ;Put`buffers`where`they`can`be`hurt
LDA #00
STA BUFFER
LDA ZTEMP ;High`byte
STABUF STA BUFFER+1
<<<
>>> SETBUF
CLRDRAW LDX #08
LDA #00
:FOOL LDY #00
:DOPE STA (BUFFER),Y
INY
BNE :DOPE
INC BUFFER+1
DEX
BNE :FOOL
****`My`goodness`but`I'm`a`dope
*CLRDRAW`LDA`GLOBXMIN
*`LSR``;Need`to`get`into`the`right`column
*`BCC`:EVEN`;Explained`in`more`detail`below
*`LDY`#$80
*`STY`BUFFER`;Presumably`this`will`be`a`little
*`CLC``;more`efficient.
*:EVEN`ADC`BUFFER+1
*`STA`BUFFER+1
*`LDA`GLOBXMAX
*`SEC
*`SBC`GLOBXMIN
*`TAX
*`INX
*`LDY`GLOBYMAX
*`BEQ`:RESET
*:YAY`LDA`#$00
*`LDY`GLOBYMAX
*:BLAH`STA`(BUFFER),Y
*`DEY
*`CPY`GLOBYMIN
*`BCS`:BLAH
*`LDA`BUFFER
*`EOR`#$80
*`STA`BUFFER
*`BNE`:WHOPEE
*`INC`BUFFER+1
*:WHOPEE`DEX
*`BNE`:YAY
*:RESET`LDA`#0`;Need`to`reset`these`guys
*`STA`GLOBXMAX
*`STA`GLOBYMAX
*`LDA`#$FF
*`STA`GLOBXMIN
*`STA`GLOBYMIN
****`Next,`read`and`draw`polygons
READDRAW LDY #00
STY INDEX
OBJLOOP LDY INDEX
LDA POLYLIST,Y ;First,`the`number`of`points
BNE :CONT ;But`if`numpoints`is`zero`then
JMP OBJDONE ;we`are`at`the`end`of`the`list
:CONT STA COUNTPTS
INC INDEX
*`Rotate`project`and`draw`the`polygon
*`Make`sure`buffer`being`drawn`to`is`clear!
:DOIT JSR ROTPROJ
*`Convert`xmin`and`xmax`to`columns
LDA LOCXMIN
LSR
LSR
LSR ;x`mod`8
STA LOCXMIN
CMP GLOBXMIN
BCS :NAH
STA GLOBXMIN
:NAH LDA LOCYMIN
CMP GLOBYMIN
BCS :UHUH
STA GLOBYMIN
:UHUH LDA LOCXMAX
LSR
LSR
LSR
STA LOCXMAX
CMP GLOBXMAX
BCC :NOWAY
STA GLOBXMAX
:NOWAY LDA LOCYMAX
CMP GLOBYMAX
BCC EORFILL
STA GLOBYMAX
*`If`using`the`EOR-buffer,`copy`into`drawing`buffer
*`And`then`clear`the`EOR-buffer
EORFILL LDA FILL
BEQ OBJLOOP
>>> SETBUF
LDA #EORBUF
STA TEMP1+1
LDA LOCXMIN ;LOCXMIN`now`contains`column
LSR ;Each`column`is`128`bytes
BCC :EVEN ;So`there`might`be`a`carry
LDY #$80
STY BUFFER
STY TEMP1
CLC
:EVEN STA T2
ADC BUFFER+1
STA BUFFER+1 ;Each`column`is`128`bytes
LDA T2
ADC TEMP1+1 ;Now`we`will`start`at`the
STA TEMP1+1 ;column
LDA LOCXMAX
SEC
SBC LOCXMIN
TAX ;Total`number`of`columns`to`do
INX ;e.g.`fill`columns`1..3
LDY LOCYMAX
BNE :FOOP
INC LOCYMAX
:FOOP LDY LOCYMAX
LDA #00
:GOOP EOR (TEMP1),Y ;EOR-buffer
PHA
*`Maybe`put`an`EOR`below?
EOR (BUFFER),Y
STA (BUFFER),Y
LDA #00 ;Might`as`well`clear`it`now
STA (TEMP1),Y
PLA
DEY
CPY LOCYMIN
BCS :GOOP
LDA BUFFER
EOR #$80
STA BUFFER
STA TEMP1
BNE :BOOP
INC BUFFER+1
INC TEMP1+1
:BOOP DEX
BNE :FOOP
JMP OBJLOOP
OBJDONE
****`Swap`buffers
SWAPBUF LDA VMCSB
EOR #$02 ;Pretty`tricky,`eh?
STA VMCSB
LDA #$08
EOR ZTEMP ;ztemp=high`byte`just`flips
STA ZTEMP ;between`$30`and`$38
JMP MAIN ;Around`and`around`we`go...
TXT 'Gee`Brain,`what`do`you`want`to`do`'
TXT 'tonight?'
**`Rotate,`project,`and`store`the`points
*
*`This`part`is`a`significant`change`since
*`v2.0.``Now`it`is`a`completely`general`polygon`plotter.
*`A`set`of`points`is`read`in,`rotated`and`projected,`and
*`plotted`into`the`drawing`buffer`(EOR`or`normal).
ROTPROJ
*`A`neat`macro
NEG MAC ;Change`the`sign`of`a`two's`complement
CLC
LDA ]1 ;number.
EOR #$FF
ADC #$01
<<<
*-------------------------------
*`These`macros`replace`the`previous`projection
*`subroutine.
SMULT`MAC`;Multiply`two`signed`8-bit
;numbers:`A*Y/64`->`A
STA`Z3
CLC``;This`multiply`is`for`normal
EOR`#$FF`;numbers,`i.e.`x=-64..64
ADC`#$01
STA`Z4
LDA`(Z3),Y
SEC
SBC`(Z4),Y
<<<``;All`done`:)
SMULTZ MAC ;Multiply`two`signed`8-bit
;numbers:`A*Y/64`->`A
STA Z1
CLC ;And`this`multiply`is`specifically
EOR #$FF ;for`the`projection`part,`where
ADC #$01 ;numbers`are`-110..110`and`0..40
STA Z2
LDA (Z1),Y
SEC
SBC (Z2),Y
<<< ;All`done`:)
PROJECT MAC ;The`actual`projection`routine
;The`routine`takes`the`point
;]1`]2`]3,`rotates`and
;projects`it,`and`stores`the
;result`in`]1`]2`]3.
LDY ]1 ;Multiply`first`rotation`column
LDA A11
>>> SMULT
STA P1T
LDA D21
>>> SMULT
STA P2T
LDA G31
>>> SMULT
STA P3T
LDY ]2 ;Second`column
LDA B12
>>> SMULT
CLC
ADC P1T
STA P1T
LDA E22
>>> SMULT
CLC
ADC P2T
STA P2T
LDA H32
>>> SMULT
CLC
ADC P3T
STA P3T
LDY ]3 ;Third`column
LDA C13
>>> SMULT
CLC
ADC P1T
STA P1T
LDA F23
>>> SMULT
CLC
ADC P2T
STA P2T
LDA I33
>>> SMULT
CLC
ADC P3T
STA ]3 ;Rotated`Z
TAX
LDY ZDIV,X ;Table`of`d/(z+z0)
;Now`Y`contains`projection`const
LDA P1T
>>> SMULTZ
LDX ZOOM
CPX #64
BEQ CONTX
STY TEMP1
LDY ZOOM
>>> SMULT
LDY TEMP1
CONTX CLC
ADC #64 ;Offset`the`coordinate
STA ]1 ;Rotated`and`projected
CMP LOCXMIN ;See`if`it`is`a`local`minimum
BCS NOTXMIN
STA LOCXMIN
NOTXMIN CMP LOCXMAX
BCC NOTXMAX
STA LOCXMAX
NOTXMAX LDA P2T
>>> SMULTZ
CPX #64
BEQ CONTY
LDY ZOOM
>>> SMULT
CONTY CLC
ADC #64
STA ]2 ;Rotated`and`projected`Y
CMP LOCYMIN
BCS NOTYMIN
STA LOCYMIN
NOTYMIN CMP LOCYMAX
BCC NOTYMAX
STA LOCYMAX
NOTYMAX <<< ;All`done
*`LDA`#EORBUF
*`STA`BUFFER+1
LDA #0 ;Reset`Ymin`and`Ymax
STA LOCYMAX
STA LOCXMAX
LDA #$FF
STA LOCYMIN
STA LOCXMIN
READPTS LDY INDEX
LDA POLYLIST,Y
STA P1X
INY
LDA POLYLIST,Y
STA P1Y
INY
LDA POLYLIST,Y
STA P1Z
INY
DEC COUNTPTS
LDA POLYLIST,Y
STA P2X
INY
LDA POLYLIST,Y
STA P2Y
INY
LDA POLYLIST,Y
STA P2Z
INY
DEC COUNTPTS
LDA POLYLIST,Y
STA P3X
INY
LDA POLYLIST,Y
STA P3Y
INY
LDA POLYLIST,Y
STA P3Z
INY
STY INDEX
>>> PROJECT,P1X;P1Y;P1Z
>>> PROJECT,P2X;P2Y;P2Z
>>> PROJECT,P3X;P3Y;P3Z
LDA HIDE
BEQ :DOIT
LDA P2X ;Hidden`face`check
SEC
SBC P1X
TAY ;Y=(x2-x1)
LDA P3Y
SEC
SBC P2Y ;A=(y3-y2)
>>> SMULT
STA TEMP1
LDA P3X
SEC
SBC P2X
TAY
LDA P2Y
SEC
SBC P1Y
>>> SMULT
CMP TEMP1 ;If`x1*y2-y1*x2`>`0`then`face
BMI :DOIT ;is`visible
DEC COUNTPTS ;Otherwise`read`in`remaining
BEQ :ABORT ;points`and`return
:POOP INC INDEX
INC INDEX
INC INDEX
DEC COUNTPTS
BNE :POOP
:ABORT RTS
:DOIT LDA P1X
STA X1
LDA P1Y
STA Y1
LDA P2X
STA X2
LDA P2Y
STA Y2
JSR DRAW
LDA P2X
STA X1
LDA P2Y
STA Y1
LDA P3X
STA X2
LDA P3Y
STA Y2
JSR DRAW
DEC COUNTPTS
BNE POLYLOOP ;Is`it`just`a`triangle?
JMP POLYDONE
POLYLOOP LDY INDEX
LDA POLYLIST,Y
STA P2X
INY
LDA POLYLIST,Y
STA P2Y
INY
LDA POLYLIST,Y
STA P2Z
INY
STY INDEX
>>> PROJECT,P2X;P2Y;P2Z
LDA P2X
STA X1
LDA P2Y
STA Y1
LDA P3X
STA X2
LDA P3Y
STA Y2
JSR DRAW
LDA P2X
STA P3X
LDA P2Y
STA P3Y
DEC COUNTPTS
BEQ POLYDONE
JMP POLYLOOP
POLYDONE LDA P1X ;Close`the`polygon
STA X2
LDA P1Y
STA Y2
LDA P3X
STA X1
LDA P3Y
STA Y1
JSR DRAW
RTS
TXT 'Same`thing`we`do`every`night,`Pinky:`'
TXT 'try`to`take`over`the`world!'
*-------------------------------
*`General`questionable-value`error`procedure
*CHOKE`LDX`#00
*:LOOP`LDA`:CTEXT,X
*`BEQ`:DONE
*`JSR`CHROUT
*`INX
*`JMP`:LOOP
*:DONE`RTS
*:CTEXT`HEX`0D`;CR
*`TXT`'something`choked`:('
*`HEX`0D00
*
TXT 'Narf!'
*-------------------------------
*`Drawin'`a`line.``A`fahn`lahn.
***`Some`useful`macros
CINIT MAC ;Macro`to`initialize`the`counter
LDA ]1 ;dx`or`dy
LSR
<<< ;The`dx/2`makes`a`nicer`looking`line
*****`Macro`to`take`a`step`in`X
XSTEP MAC
LDX DX ;Number`of`loop`iterations
>>> CINIT,DX
XLOOP LSR CHUNK
BEQ FIXC ;Update`column
SBC DY
BCC FIXY ;Time`to`step`in`Y
DEX
BNE XLOOP
DONE LDA OLDX ;Plot`the`last`chunk
EOR CHUNK
ORA (BUFFER),Y
STA (BUFFER),Y
RTS
FIXC PHA
LDA OLDX
ORA (BUFFER),Y ;Plot
STA (BUFFER),Y
LDA #$FF ;Update`chunk
STA OLDX
STA CHUNK
LDA #$80 ;Increase`the`column
EOR BUFFER
STA BUFFER
BNE C2
INC BUFFER+1
C2
PLA
SBC DY
BCS CONT
ADC DX
IF I,]1 ;Do`we`use`INY`or`DEY?
INY
ELSE
DEY
FIN
CONT DEX
BNE XLOOP
JMP DONE
FIXY ADC DX
PHA
LDA OLDX
EOR CHUNK
ORA (BUFFER),Y
STA (BUFFER),Y
LDA CHUNK
STA OLDX
PLA
IF I,]1 ;Update`Y
INY
ELSE
DEY
FIN
DEX
BNE XLOOP
RTS
<<< ;End`of`Macro`xstep
*****`Take`a`step`in`Y
YSTEP MAC
LDX DY ;Number`of`loop`iterations
BEQ DONE ;If`dy=0`it's`just`a`point
>>> CINIT,DY
SEC
YLOOP PHA
LDA OLDX
ORA (BUFFER),Y
STA (BUFFER),Y
PLA
IF I,]1
INY
ELSE
DEY
FIN
SBC DX
BCC FIXX
DEX
BNE YLOOP
DONE LDA OLDX
ORA (BUFFER),Y
STA (BUFFER),Y
RTS
FIXX ADC DY
LSR OLDX
SEC ;Important!
BEQ FIXC
DEX
BNE YLOOP
JMP DONE
FIXC PHA
LDA #$80
STA OLDX
EOR BUFFER
STA BUFFER
BNE C2
INC BUFFER+1
C2 PLA
DEX
BNE YLOOP
JMP DONE
<<< ;End`of`Macro`ystep
*`Take`an`x`step`in`the`EOR`buffer
*`The`sole`change`is`to`use`EOR`instead`of`ORA
EORXSTEP MAC
LDX DX ;Number`of`loop`iterations
>>> CINIT,DX
XLOOP LSR CHUNK
BEQ FIXC ;Update`column
SBC DY
BCC FIXY ;Time`to`step`in`Y
DEX
BNE XLOOP
DONE LDA OLDX ;Plot`the`last`chunk
EOR CHUNK
EOR (BUFFER),Y
STA (BUFFER),Y
RTS
FIXC PHA
LDA OLDX
EOR (BUFFER),Y ;Plot
STA (BUFFER),Y
LDA #$FF ;Update`chunk
STA OLDX
STA CHUNK
LDA #$80 ;Increase`the`column
EOR BUFFER
STA BUFFER
BNE C2
INC BUFFER+1
C2
PLA
SBC DY
BCS CONT
ADC DX
IF I,]1 ;Do`we`use`INY`or`DEY?
INY
ELSE
DEY
FIN
CONT DEX
BNE XLOOP
JMP DONE
FIXY ADC DX
PHA
LDA OLDX
EOR CHUNK
EOR (BUFFER),Y
STA (BUFFER),Y
LDA CHUNK
STA OLDX
PLA
IF I,]1 ;Update`Y
INY
ELSE
DEY
FIN
DEX
BNE XLOOP
RTS
<<< ;End`of`Macro`xstep
*`Take`a`y-step`in`the`EOR-buffer
*`Changes`from`above`are:`only`plot`last`part`of`each
*`vertical`chunk,`don't`plot`last`point,`plot`with`EOR
EORYSTEP MAC
LDX DY ;Number`of`loop`iterations
BEQ DONE ;If`dy=0`it's`just`a`point
>>> CINIT,DY
SEC
*YLOOP`PHA
*`LDA`OLDX
*`ORA`(BUFFER),Y
*`STA`(BUFFER),Y
*`PLA
YLOOP IF I,]1
INY
ELSE
DEY
FIN
SBC DX
BCC FIXX
DEX
BNE YLOOP
*DONE`LDA`OLDX
*`ORA`(BUFFER),Y
*`STA`(BUFFER),Y
DONE RTS
FIXX ADC DY
PHA ;We`only`plot`the`last`part`of`each`chunk
LDA OLDX
EOR (BUFFER),Y
STA (BUFFER),Y
PLA
LSR OLDX
SEC ;Important!
BEQ FIXC
DEX
BNE YLOOP
JMP DONE
FIXC PHA
LDA #$80
STA OLDX
EOR BUFFER
STA BUFFER
BNE C2
INC BUFFER+1
C2 PLA
DEX
BNE YLOOP
JMP DONE
<<< ;End`of`Macro`ystep
****`Initial`line`setup
**`The`commented`lines`below`are`now`taken`care`of`by`the
**`calling`routine.
*DRAW`>>>`MOVE,TX1;X1``;Move`stuff`into`zero`page
*`>>>`MOVE,TX2;X2``;Where`it`can`be`modified
*`>>>`MOVE,TY1;Y1
*`>>>`MOVE,TY2;Y2
DRAW LDA FILL
BNE :SETEOR
>>> SETBUF
JMP :SETUP
:SETEOR LDA #EORBUF
STA BUFFER+1
:SETUP SEC ;Make`sure`x1y1?
EOR #$FF ;Otherwise`dy=y1-y2
ADC #$01
:CONT2 STA DY
CMP DX ;Who's`bigger:`dy`or`dx?
BCC STEPINX ;If`dx,`then...
JMP STEPINY
STEPINX LDY Y1
CPY Y2
LDA BITP,X ;X`currently`contains`x1
STA OLDX
STA CHUNK
BCC XINCY ;Do`we`step`forwards`or`backwards`in`Y?
JMP XDECY
XINCY LDA FILL
BEQ NORMXINC
>>> EORXSTEP,INY
NORMXINC >>> XSTEP,INY
XDECY LDA FILL
BEQ NORMXDEC
>>> EORXSTEP,DEY
NORMXDEC >>> XSTEP,DEY
STEPINY LDY Y1
LDA BITP,X ;X=x1
STA OLDX
LSR ;Y`doesn't`use`chunks
EOR OLDX ;So`we`just`want`the`bit
STA OLDX
CPY Y2
BCS YDECY
YINCY LDA FILL
BEQ NORMINC
>>> EORYSTEP,INY
NORMINC >>> YSTEP,INY
YDECY LDA FILL
BEQ NORMDEC
>>> EORYSTEP,DEY
NORMDEC >>> YSTEP,DEY
*-------------------------------
*`Clean`up
CLEANUP LDA VMCSB ;Switch`char`rom`back`in
AND #%11110101 ;default
STA VMCSB
RTS ;bye!
TXT 'spinal`cracker`'
TXT 'slj`6/95'
*-------------------------------
*`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 %11111111
DFB %01111111
DFB %00111111
DFB %00011111
DFB %00001111
DFB %00000111
DFB %00000011
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
TMATH1 EQU ZDIV+384 ;Math`table`of`f(x)=x*x/256
TMATH2 EQU TMATH1+512 ;Second`math`table
POLYLIST EQU TMATH2+512 ;List`of`polygons