AOH :: CHACK_10.TXT

C= Hacking Magazine, Issue #10



Message #6 (6 is last):
Date: Fri Jul 14 00:21:09 1995
From: ui423 ("Philip C. Cavanagh")
Subject: Issue10


                   ########
             ##################
         ######            ######
      #####
    #####  ####  ####      ##      #####   ####  ####  ####  ####  ####   #####
  #####    ##    ##      ####    ##   ##   ##  ###     ##    ####  ##   ##   ##
 #####    ########     ##  ##   ##        #####       ##    ## ## ##   ##
#####    ##    ##    ########  ##   ##   ##  ###     ##    ##  ####   ##   ##
#####  ####  ####  ####  ####  #####   ####  ####  ####  ####  ####   ######
#####                                                                     ##
 ######            ######            Issue #10
   ##################              June 30, 1995
       ########

---------------------------------------------------------------------(v1.5)---
Editor's Notes
by Craig Taylor

This is my last issue of Commodore Hacking (having finally gotten out the
door, but I couldn't break tradition and get it out on time :-) ). I'm having
to give it up because I've gradually lost interest in Commodore computers over
the years and with the search for a job (anyone wanna hire a csc graduate?)
and as I get older I seem to have less and less time.

I'm gonna be handing the reigns of Commodore Hacking over to Jim Brain, who is
a very active member of the Commodore Internet community. He will also be
running a mailserver that will take the place of mine (Mine will become
unavailable after July 1st and will send pointers to Jim Brain's mailserver).

It's been interesting to watch the Commodore computers evolve, take off like a
rocket and then have Commodore go into liquidation. Commodore computers have
been and still are, (with some exceptions - 1541 head-banging comes to mind),
technologically sound.  For a "hacking" machine they're wonderful.

My email address has changed to duck@nando.net. I periodically still check
mail at duck@pembvax1.pembroke.edu but only every 2 weeks or so. I am still
going to try to be in the Commodore community but time will govern my ability
to do that. I'm going to miss editing this rag....

And here is Jim Brain: 

Mail Server Changes:

With Issue 10, the address for the Commodore Hacking mail server has changed.
The new address is brain@mail.msen.com  The commands are the same as before.
Not all of the files have been moved yet, so please email the administrator
(Jim Brain, brain@mail.msen.com) if a file you need is not on the new site.

Howdy:
Howdy, my name is Jim Brain, and I will be taking over the position of 
editor for Commodore Hacking starting with Issue 11.  Some of you may know
me as the Commodore Trivia Contest administrator, the USENET newsgroup
comp.sys.cbm FAQ Manitainer, or the keeper of a Commodore Information
WWW Site at http://www.msen.com/~brain/cbmhome.html.  Wherever you have 
heard of me from, or even if you haven't, I will state that I plan on
handling Commodore Hacking in the following way.  The next issue will
possibly look different cosmetically, as I edit somewhat differently than
Craig, but the content and basic layout will remain the same.  The types
of material will not change, and the structure for submitting articles will
change only in the address to mail them to: brain@mail.msen.com.  However,
I do have a few changes in mind:

1) Try to stabilize the issue generation so that Commodore Hacking will
   become a quarterly publication.

2) Attempt a fully "HTML"ized version of the magazine, while still providing a
   text version. 
   
3) Pursue the possibility of providing a printed version of these issues
   for those who have no online access to them.
   
4) Encourage User Groups and other CBM related organizations to carry this
   magazine for their members.
   
So, again I say howdy.  As always, Commodore Hacking will accept your 
articles at any time, so please help us keep this quality magazine running.
If you have any questions or comments about the change in editorship, the
possible changes, or other matters, please feel free to drop me a note.
I look forward to hearing from you and publishing your articles.

Jim Brain
brain@mail.msen.com
===========================================================================
Legal Mumbo-Jumbo

Permission is granted to re-distribute this "net-magazine", in whole,
freely for non-profit use. However, please contact individual authors for
permission to publish or re-distribute articles separately. A charge of no
greater than 5 US dollars or equivlent may be charged for library service /
diskettte costs for this "net-magazine".

-------------------------------------------------------------------------------

Please note that this issue and prior ones are available via anonymous FTP
from ccnga.uwaterloo.ca (among others) under /pub/cbm/hacking.mag and via a
mailserver which documentation can be obtained by sending mail to
"brain@mail.msen.com" with a subject line of "mailserver" and the
lines of "help" and "catalog" in the body of the message.

-------------------------------------------------------------------------------
In This Issue:

Commodore Trivia 

Trivia Edition #13-18 are in this article.  As you may know, these questions
form part of a contest in which the monthly winner gets a prize (Thanks to my
various prize donators). The whole thing is mainly just for fun, so please
enjoy.  Try your hand at Commodore trivia!!

BFLI - New graphics modes 2

FLI gave us more color to the screen, AFLI increased the horizontal
resolution and color selection by using the hires mode.  BFLI stands
for 'Big FLI' and gives us 400 lines instead of the usual two
hundred.  AFLI and BFLI can be combined, but we are not going into
that.

Making stable raster routines (C64 and VIC-20)

In this article, I document two methods of creating stable raster
routines on Commodore computers.  The principles apply for most 8-bit
computers, not only Commodores, but raster effects are very rarely
seen on other computers.

A Differant Perspective - Part III.

Yes!!!  It's yet another article on 3D graphics!  Even if you
haven't been following this series, you can use this program.  This
time around we will write a completely general polygon plotter --
if you can type basic data statements, you can create a three-dimensional
object out of polygons and rotate and project it to your heart's content.
For the more technically inclined we will look at optimizations to the
line routine, EOR-buffer filling, and more!  Yow!

Second SID Chip Installation

This article describes how to add a second sid chip for use in SidPlayer and
other programs. As always, be extra careful when making modifications to your
computer.

Solving Large Systems of Linear Equations on a C64 Without Memory

OK, now that I have your attention, I lied.  You can't solve dense
linear systems of equations by direct methods without using memory to
store the problem data.  However, I'll come back to this memory free
assertion later.  The main purpose of this article is to rescue a
usefull numerical algorithm, "Quartersolve", and also to provide a brief
look at the COMAL programming language and BLAS routines.

The World of IRC - A New Life for the C64/128

I've heard people talking about IRC. What is it? Why is it useful to me as a
Commodore user? Bill "Coolhand" Lueck explains the hows and whys in this 
article.

SwiftLink-232 Application Notes (version 1.0b)

This information is made available from a paper document published by CMD,
with CMD's permission. 

Design and Implementation of a Simple/Efficient Upload/Download Protocol

This article details how to implement a custom upload/download protocol that
is faster than most of the ones common to the C64/128 computers.

Design and Implementation of a 'Real' Operating System for the 128: Part II

There has been a slight change in plans.  I originally intended this 
article to give the design of a theoretical distributed multitasking 
microkernel operating systemfor the C128.  I have decided to go a 
different route: to take out the distributed component for now and implement
a real multitasking microkernel OS for a single machine and extend the system
to be distributed later.  The implementation so far is, of course, only in 
the prototype stage and the application for it is only a demo.  Part III of 
this series will extend this demo system into, perhaps, a usable distributed
operating system.
========================================================================
Trivia
by Jim Brain (brain@mail.msen.com)

Well, summer is upon the Brain household, and things are moving at a fast
clip at the house.  However, the trivia still keeps coming.  I appreciate
all the people who contribute to the trivia and all the people who take
part in the monthly contest.  I have collected Trivia Edition #13-18 in this
article.  As you may know, these questions form part of a contest in which
the monthly winner gets a prize (Thanks to my various prize donators).
The whole thing is mainly just for fun, so please enjoy.

As the summer months start up, news on the trivia includes:

1) I now have access to some more orphan machines (C65, C116), so expect
   some trivia questions on those models.
   
2) The new home now has a number of machines set up, so testing answers to
   the trivia is even easier.  I am still trying to get the old PET 
   machines in house, but the others are here.
   
3) The Commodore World Wide Web Pages (http://www.msen.com/~brain/cbmhome.html)
   that I maintain and place the trivia on caught the eye of USA Today and
   the Pheonix Gazette.  I was interviewed for both articles.  Look in the
   June 20th edition of USA Today for the segment, and possibly a picture of
   Jim Brain and the machines he uses to create the trivia.

As always, I welcome any questions (with answers), and encourage people
to enter their responses to the trivia, now at #18. 


Jim.


The following article contains the answers to the December edition of trivia
($0C0 - $0CF), the questions and answers for January ($0D0 - $0DF), 
February ($0E0 - $0EF), March ($0F0 - $0FF), April ($100 - $10F), and the
questions for the May edition ($110 - $11F).  Enjoy them!


   Here are the answers to Commodore Trivia Edition #13 for December, 1994

Q $0C0) The early 1541 drives used a mechanism developed by ______.  Name
        the company.

A $0C0) Alps.

Q $0C1) On later models, Commodore subsequently changed manufacturers
        for the 1541 drive mechanism.  Name the new manufacturer.

A $0C1) Newtronics.

Q $0C2) What is the most obvious difference(s).  (Only one difference is
        necessary)

A $0C2) Alps:        push-type latch, round LED.
        Newtronics:  lever-type latch, rectangular LED.
                   
Q $0C3) On Commodore BASIC V2.0, what answer does the following give:
        PRINT (SQR(9)=3)

A $0C3) 0.  According to Commodore BASIC, the answer should bby -1, which
        is the BASIC value of TRUE.  However, the above equation is NOT
        true.  Doing PRINT SQR(9) yields 3, but doing PRINT (SQR(9)-3)
        yields 9.31322575E-10 (C64).  This anomaly can be attributed to
        roundoff errors in the floating point math routines in Commodore BASIC.

Q $0C4) In Commodore BASIC (Any version) what does B equal after the following
        runs: C=0:B=C=0 
           
A $0C4) B = -1.  The second statement is the one to look at.  The second
        equals sign is treated as a comparison, while the first is treated
        as a assignment.  B gets set to the outcome of the comparison, which
        is TRUE (-1).

Q $0C5) The first PET cassette decks were actually _______ brand cassette 
        players, modified for the PET computers.  Name the comapny.

A $0C5) Sanyo. Specifically, Model M1540A.  What a model number!

Q $0C6) In Commodore BASIC (Any version), what happens if the following
        program is run:
        
        10 J=0
        20 IF J=0 GO TO 40
        30 PRINT "J<>0"
        40 PRINT "J=0"

A $0C6) On BASIC 2.0 or greater:
   
   ?SYNTAX  ERROR IN 20
        READY.
        
        On BASIC 1.0:  (found on the PET 2001 series)
        
        J=0
        READY.

        BASIC 1.0 totally ignored spaces, so line 20 became "IFJ=0GOTO40".
        That statement would be correctly parsed, sicne it contains the "GOTO"
        keyword.  
        
        However, on BASIC 2.0 or greater, spaces weren't ignored so
        completely, and the "TO" in "GO TO" would be tokenized separately, so
        some code was added to BASIC to check to "GO".  As the code that
        accepts GOTO as a special case for THEN after an IF statement wasn't
        patched this way, the above fails, because GO is not a valid keyword
        after IF.  The statement SHOULD work correctly, but does not because
        of this failure to fix the IF command parsing.

        On BASIC 2.0 or greater, substituting the following line for line 20
        will cause the program to work:

        20 IF J=0 THEN GO TO 40

Q $0C7) In question $068, we learned how Jack Tramiel first happened upon the
        name "COMMODORE".  According to the story, though, in what country
        was he in when he first saw it?

A $0C7) Germany.  

Q $0C8) On the Commodore user port connector, how many edge contacts are
        there?

A $0C8) 24.  Two rows of 12 contacts each.

Q $0C9) On most Commodore computers, a logical BASIC screen line can contain
        up to 80 characters.  On what Commodore computer(s) is this not true?

A $0C9) According to Commodore documentation, a _physical_ screen line is 
        defined as one screen line of characters.  A _logical_ screen line is 
        defined as how many _physical_ lines can be chained together to 
        create a valid BASIC program line.  

        With that in mind, most Commodore computers chose a _logical_
        screen line that was a multiple of the screen width.  This works fine
        for 40 and 80 column screens, but what do we do with the VIC-20, with
        its 22 column screen.  Solution:  make the _logical_ line length equal
        to 4 _physical_ lines, or 88 columns.

        When the Commdore 128 was introduced, the number rose to 160
        characters, which is 4 _physical_ lines in 40 column mode, or
        2 _physical_ lines in 80 column mode.  However, you can only take
        advantage of this in 128 mode.  64 mode is limited to 80 characters.
        
        To add to all this confusion, a valid BASIC program line (in memory)
        can actually be 255 (tokenized) characters long, but creating such
        a long line cannot be done from the built-in editor in direct mode.
        
        The AmigaBASIC, available on the Amiga, also does not have the 80
        column line limit.  However, that BASIC is SOOO much different that
        I am not surprised.  The older CBM BASICs, on the other hand, were
        all derivatives of the original Level 1 BASIC for the PET.

Q $0CA) If a file is saved to a Commodore Disk Drive with the following
        characters: chr$(65);chr$(160);chr$(66), what will the directory
        entry look like?

A $0CA) The filename will show up as "A"B, with the 'B' showing up to the
        right of the '"' mark.  This could be used to make program loading
        easier.  A file that showed up as "filename",8,1 could be loaded
        by simply hitting shift-run/stop on that line.

Q $0CB) What is the maximum length (in characters) of a CBM datasette
        filename?

A $0CB) References I have on hand say 128 characters.  However, the actual
        code on the 8032 and the C64 acts as though 187 characters can
        actually be sent (tape buffer-5 control bytes = 192-5=187).  The
        references that claim 128 characters are Nick Hampshire's
        _The VIC Revealed_ and _The PET Revealed_.  ANyone care to lay
        this one to rest? 

Q $0CC) How many keys are on a stock Commodore 64 keyboard?

A $0CC) 66 keys.  This is the same number as found on the VIC-20 and the
        Commodore 16.

Q $0CD) Commodore BASIC uses keyword "tokens" to save program space.  Token
        129 becomes "FOR".  What two tokens expand to include a left
        parenthesis as well as a BASIC keyword?

A $0CD) TAB( (163) and SPC( (166).

Q $0CE) There are 6 wires in the Commodore serial bus.  Name the 6 wires.

A $0CE) 1) Serial /SRQIN
        2) GND
        3) Serial ATN IN/OUT
        4) Serial CLK IN/OUT
        5) Serial DATA IN/OUT
        6) /RESET

Q $0CF) On the Commodore datasette connector, how many logical connections are
        there?

A $0CF) 6. Opposing pins on the connector are hooked together electrically.


   Here are the answers to Commodore Trivia Edition #14 for January, 1995

Q $0D0) How many keys were there on the "original" PET and what was special
        about them?

A $0D0) the original PET had 73 calculator-style keys that were laid out
        in a rectangular matrix, not typewriter-style.

Q $0D1) How do you produce the "hidden" message(s) on the Commodore 128?

A $0D1) SYS 32800,123,45,6.  The screen will clear, and the software
        and hardware developers on the 128 project will be named.

        The exact text is as follows:
                
[RVS]   Brought to you by...

Software:
 Fred Bowen
 Terry Ryan
 Von Ertwine

Herdware:
 Bil Herd
 Dave Haynie
 Frank Palaia

[RVS]Link arms,don't make them.

Q $0D2) How much memory did the "original" PET show on bootup?

A $0D2) The "original" PET came in two configurations, 4K and 8K, so:
         
          The PET 2001-4 had 3071 bytes.
          The PET 2001-8 had 7167 bytes.

Q $0D3) We all know the "reboot" sys for the 64 is sys 64738, but who knows
        the same sys location to reboot the CBM 8032?

A $0D3) sys 64790

Q $0D4) Which computer(s) beeped at bootup?  (May be more than one, but only
        one required)
           
A $0D4) I know some of these are corect, but the sheer size of the
        list prevents me from checking them ALL out.
        
        FAT 40XX series
        80XX series
        PC-10  (I suspect a number of IBM clones did, and these things have
                no consistent naming convention across country boundaries.)
        PC-20
        Amiga 1000
        SP9000 (SuperPET)
        
Q $0D5) How much memory did the CBM 8032 show on bootup?

A $0D5) 31743 bytes.

Q $0D6) Certain Commodore computers provided emtpy EPROM sockets on the
        motherboard.  Give me the number of empty sockets on the following
        machines:

        a)  CBM 30XX.
        b)  CBM 8XXX.
        c)  CBM C128.
        d)  Plus/4.

A $0D6) a)  3 sockets.
        b)  2 sockets.
        c)  1 socket.
        d)  1 socket.

Q $0D7) In Germany, the CBM 8032 came with a 4kB EPROM for the EXXX area,
        while the US version only had a 2kB EPROM.  Why?

A $0D7) The German version had additional keybaord drivers for umlaut
        characters and dead keys.  

Q $0D8) Who published the first PET memory map in the "PET Gazette"?

A $0D8) None other than the infamous Jim Butterfield.

Q $0D9) Which is faster to move the sursor on a PET/CBM or C64: SYS or 
        PRINT?

A $0D9) PRINT is faster, since the sys approach must process the pokes
        before the sys, which are very slow.

Q $0DA) On the Amiga 1000, where are the signatures of the first Amiga
        developers located?

A $0DA) Inside the top case of the Amiga (1000).

        There is an interesting footnote to this question.  It seems
        that at least some original Amiga machines were labeled as
        Amiga (with nu number).  Then, at some later point, the number was
        added.  In addition, Commodore produced some Amiga 1000 machines
        without the signatures, but most had the telltale handwriting on
        the inside of the case.  

Q $0DB) On the 6502, what does the accumulator contain after the following
        is executed:

        lda #$aa
        sed
        adc #01

A $0DB) Assume carry was clear.  If so, then $11 is the correct answer. 

Q $0DC) What is the model number of the US NTSC VIC-II chip?

A $0DC) Its first number was 6567, and that is the number most people know
        it by, but Commodore produced a VIC-II using a new manufacturing 
        process that was numbered the 8562. 

Q $0DD) What is the European PAL VIC-II chip's model number?
        (Not sure if that's its rightful term, but I hope you understand).

A $0DD) Same here.  The part number 6569 is the most remembered number, but
        an 8565 will work as well.

Q $0DE) Assume you have two computers, one with each of the above chips inside.
        Which chip draws more pixels on the screen per second?

A $0DE) Note, for the purposes of the calculation I am performing, "pixels"
        refers to picture elements that can be adddress and modified using
        normal VIC modes, so there are 320*200 "pixels" on both the PAL
        and NTSC screens.  (I probably should have stated this, but it is
        too late now.)  Also, the screen refresh rates used in the 
        calculations are those defined by the respective television
        standards (60Hz U.S., 50Hz European), even though the actual
        frequencies are off by a small percentage. (for example, the actual
        50Hz refresh rate on European VIC-II chips was calculates as 
        50.124567Hz by Andreas Boose)
        
        So, the PAL draws 320*200*50 pixels per second = 3200000 pixels/s
        NTSC draws 320*200*60 pixels per second = 3840000 pixles/s
        
        Now, some people thought I meant the whole screen, not just the 
        display area provided by the VIC-II chip.  Well, I am not sure
        exactly you calculate pixels on a screen, since the numbers could
        vary from display to display, but if we measure in scanlines:
        
        PAL = 312 scanlines * 50 = 15600 scanlines/s
        NTSC = 262 scanlines * 60 = 15720 scanlines/s
        
        The NTSC machines wins both ways.  

Q $0DF) In Commodore BASIC, which statement executes faster:

        a = 2--2

        or

        a = 2+2

A $0DF) b is the correct answer, and there are a couple of reasons why:

        1) 2--2 takes longer to parse in the BASIC interpreter.
        2) Commodore BASIC subtracts by complementing the sign of the
           second number and adding.  This incurs extra time.

        There are even more subtle ones, but I leave them as an
        exercise for the reader.  Send me your reason why.


   Here are the answers to Commodore Trivia Edition #15 for February, 1995

Q $0E0) What is the difference(s) between the Newtronics 1541 and the 1541C?
        (only one difference is needed)

A $0E0) (George Page, a noted authority on CBM Drives, indicated that Commodore
        made this a tough question to answer.)  By the time the 1541C was 
        introduced, Commodore threw a number of drives together and called
        them 1541Cs.  The theoretical 1541C exhibited the following
        features:

        No head banging, and other problems fixed by modified ROMs.
        Case color matches C64C and C128 computers.

Q $0E1) What happens when you type 35072121 in direct mode on the C64 and
        hit return?

A $0E1) Simple answer:  Most likely, the screen clears and the word READY.
        is printed at screen top.  This is the behavior seen when pressing 
        RUN-STOP/RESTORE.  Alternately, nothing could happen, or the computer
        could lock up.

        Involved answer:  There is a bug in BASIC 2.0.  Easily fixed, but 
        destined to live life immortal.  (long)

        The bug is in the PETSCII number to binary conversion routine at
        $a69b (LINGET).  The routine basically reads in a character from the
        line, multiplies a partial result by 10 and adds the new character
        to the partial result.  Here is a code snippet:

        a96a     rts
        a96b     ldx #$00  ; zero out partial result
        a96d     stx $14
        a96f     stx $15
        a971     bcs $a96a ; not a number, return
        a973     sbc #$2f  ; PETSCII to binary
        a975     sta $07   
        a977     lda $15   ; get hi byte or partial result
        a979     sta $22
        a97b     cmp #$19  ; partial > 6399
        a97d     bcs $a953 ; yes, goto error
        a97f     lda $14   ; load lo byte of result
        a981     asl       ; lo*2
        a982     rol $22   ; hi*2 + c
        a984     asl       ; lo*2
        a985     rol $22   ; hi*2 + c
        a987     adc $14   ; complete lo*5
        a989     sta $14
        a98b     lda $22  
        a98d     adc $15   ; complete hi*5
        a98f     sta $15
        a991     asl $14   ; lo*2 complete lo*10
        a993     rol $15   ; hi*2 complete hi*10
        a995     lda $14
        a997     adc $07   ; add new char
        a999     sta $14
        a99b     bcc $a99f ; did lo overflow?
        a99d     inc $15   ; yes, inc hi
        a99f     jsr $0073 ; get next char
        a9a2     jmp $a971 ; go through it again.
        
        The problem is at $a97d.  when the partial result is greater than 6399,
        (if partial > 6399, then new partial result will be over 63999)
        the routine needs to get to $af08 to print an error, but can't due to
        branch restrictions.  However, a branch that will get there is in the
        preceding function, which handles the ON GOTO/GOSUB keywords ($a94b,
        ONGOTO).  

        So, the BASIC writers just branched to the code in ONGOTO; specifically
        $a953:
        
        a94b     jsr $b79e
        a94e     pha
        a94f     cmp #$8d  ; is the keyword GOSUB ($8d)
        a951     beq $a957 ; yes
        a953     cmp #$89  ; is the keyword GOTO ($89)
        a955     bne $a8e8 ; no, print SYNTAX ERROR.
        a957     ...       ; handle ON GOTO/GOSUB

        This code is checking to make sure the ON (var) is followed with a
        GOTO or GOSUB keyword.

        The LINGET error handler branches to $a953, which compares
        .A (which holds hi byte of partial result) to $89.  Normally, this
        fails, and the normal SYNTAX ERROR code is reached through the branch
        to $a8e8.  However, for partial results of the form $89XX, the check
        succeeds, and BASIC tries to execute an ON GOTO/GOSUB call.

        By the way, it is no coincidence that this error occurs on 35072121,
        since one of the partial results is $8900 (hi byte is $89). In fact,
        350721 will achieve the same result.

        If the check succeeds, the code limps along until $a96a:

        a969     pla       ; complement to $a94e
        a96a     rts       ; return

        But we never executed $a94e, the push, so the stack is now
        messed up.  Since the stack held $9e, $79, $a5 before the PLA,
        (The stack could hold other values, but I always saw these)
        the RTS gets address $a579 to return to, which usually holds a BRK
        opcode.  The break handler is invoked, and the screen clears with the
        READY. at the top.

        Now, the BASIC 2.0 authors were justified in reusing the error
        handler code in ONGOTO for LINGET, but they calculated the branch
        offset wrong, according to my tests.  If you have the LINGET error
        handler branch to $a955, all these troubles disappear.  You can
        verify this procedure with the following BASIC program on a 64:
        
        10 for t=57344 to 65535:poke t,peek(t):next
        20 for t=40960 to 49151:poke t,peek(t):next
        30 poke 43390, 214
        40 poke 1, peek(1) and 254

        Just to be complete, this error occurs when a 6 digit or greater line
        number is entered and the first 6 digits indicate a number in the
        range 35072-35327 ($8900-$89ff).  Also, it appears the error occurs
        on the VIC-20, but I didn't completely verify it.  It would be
        interesting to note if the error is found on all version of CBM BASIC.

        Whew, what a mouthful.

Q $0E2) If a SID chip is producing a "sawtooth waveform", does the waveform look
        like: 

        a) "/|/|/|/|"  or
        b) "|\|\|\|\"  ?

A $0E2) a is the correct answer.

Q $0E3) On BASIC 2.0, what special precaution(s) must one take when working with
        relative files? (only one is needed)

A $0E3) Because BASIC 2.0 doesn't handle positioning in relative files quite
        right, one must position the relative file pointer before AND AFTER
        a read or write to a relative file.

Q $0E4) What incompatibility existed between C128 Rev. 0 ROMS and the REU?
           
A $0E4) OK, I admit it.  I placed this answer and its discussion somewhere
        in my store of information, and it must have fallen behind the 
        cabinet, because I cannot find it.  I will post an answer to this
        as soon as I can find it, but the answers really must go out, as
        they have been held up long enough.

Q $0E5) What can trigger an NMI interrupt? (count all sources on one chip as
        one)

A $0E5) The following sources can trigger an NMI interrupt:

        1) The expansion port.
        2) CIA #2.
        3) The RESTORE key.

Q $0E6) What can trigger an IRQ interrupt? (count all sources on one chip as
        one)

A $0E6) The following sources can trigger an IRQ interrupt:

        1) The VIC-II chip.
        2) CIA #1.
        3) The expansion port.

Q $0E7) Where is the ROM in a 1541 located in the 64K memory map?

A $0E7) The ROM is located from $C000 to $FFFF, yet the ROM code does not
        begin until $C100.

Q $0E8) Which VIA on the 1541 is hooked to the read/write head?

A $0E8) VIA #2, located in memory from $1C00 to $1C0E.

Q $0E9) In the Commodore DOS, what bit in the file type byte denotes a "locked"
        file?

A $0E9) bit 6.

Q $0EA) If files are "locked" under Commodore DOS, under what condition(s) may
        the file be changed?

A $0EA) Depending on the file, the following operations can be done on a 
        locked file:
        
        1) Rename will change file name, although not contents of file.
        2) Random access can be used to alter file.
        3) Formatting the disk will alter the file. (duh!)
        4) Save-with-replace (@0:) will replace file and unlock it.
        5) Opening file in append mode will allow it to be changed, and
           unlock it.
        6) Opening a relative file and adding or changing a record will
           succeed and unlock file.

Q $0EB) How big can a program file be on a 1541 or similar?

A $0EB) The file can be as large as a sequential file, since both are stored
        in the same way: 168656 bytes.  However, since a program contains its
        load address as bytes 0 and 1, the largest program size is 168654
        bytes.

Q $0EC) Under BASIC 2.0, how does one open a random access file on a disk
        drive?

A $0EC) Random access (or direct access) files are a misnomer.  What you
        really doing is opening the disk for reading and writing.  You need
        two open command to access a random file: (assume drive 8)
        
        open 15,8,15     and
        
        open 1,8,4,"#1" will open a random access file using buffer 1.
        open 1,8,4,"#" will open a random access file using the first
        available buffer
        
        Now, by using B-R, B-W, B-A or their replacements, you can write
        data to sectors on the disk.
        
        Note that Random access files are different from relative files.
        
Q $0ED) A file that has a '*' immediately before the filetype is called
        a _________ file.

A $0ED) a splat file.  This is its correct term, believe it or not.

Q $0EE) We know the 1541 and similar drives have 5 internal buffer areas, but
        how many does an 8050 drive have?

A $0EE) Since the 8050 has twice the on-board RAM (4kB), it has 16 buffers, but
        only 13 are available.  (All CBM drives use one buffer for zero-page
        memory, one for stack memory, and one for temporary variables.) 

Q $0EF) On a "save-with-replace", where is the location of the first track and
        sector of the new copy of the program saved in the directory entry for
        the old copy?

A $0EF) The new first track is stored at location 26, and the new first sector
        is stored at location 27.  These values are copied to their
        correct locations after the save is completed.


   Here are the answers to Commodore Trivia Edition #16 for March, 1995

Q $0F0) What size matrix of pixels comprises a character on a PET 2001
        computer?

A $0F0) The matrix was 8 by 8.  

Q $0F1) How many bytes did the opening screen on a CBM 4016 show as
        available for use by BASIC?

A $0F1) 15359 bytes free.

Q $0F2) The character set that produces uppercase letters on unshifted keys 
        is the ________________ character set.

A $0F2) "standard mode".  

Q $0F3) The character set that produces lowercase letters on unshifted keys
        is the ________________ character set.

A $0F3) "alternate mode"

Q $0F4) To get to the set mentioned in $F2, what character code would be
        printed to the screen?
 
A $0F4) chr$(142)

Q $0F5) What character code would one print to the screen to invoke the 
        chararacter set in $F3?

A $0F5) chr$(14)

Q $0F6) If one does LIST 60-100, will line 100 get "listed"?

A $0F6) Yes.  The above translates as: LIST 60 through to and including 100.

Q $0F7) The abbreviation for the BASIC 4.0 command "COLLECT" is ________.

A $0F7) coL. "C" "O" "SHIFT-L".  For those who are interested, the 
        COLLECT command is analogous to the VALIDATE operation.

Q $0F8) When you use a subscripted variable in BASIC, how many elements
        are created by default if no DIM statement is issued?

A $0F8) 11 elements.  A(0) - A(10).  Almost everyone who has ever programmed 
        in Commodore BASIC has seen the "BAD SUBSCRIPT" error when they try 
        to use the 12th element in a un-DIMensioned array.

Q $0F9) How large is the keyboard buffer in CBM computers?

A $0F9) 10 bytes.  Since this area could be POKEd to, many boot programs
        would poke characters into this buffer to simulate keypresses.

Q $0FA) On the Commodore 1581, how large is a physical sector in bytes?

A $0FA) A physical sector is 512 bytes in length.  Internally, the 1581
        creates 2 256 "logical" sectors in a physical sector, to maintain
        compatibility with older Commodore drives.

Q $0FB) You'll find BASIC 3.5 on the _____________ line of CBM computers.

A $0FB) The X64 series.  That includes the Commodore 16, the Commodore 116,
        and the Commodore Plus/4.

Q $0FC) On the Commodore 1351 mouse, what registers in the Commodore
        computer would the X and Y proportional information be read
        from?

A $0FC) Even though you are looking for digital information (how far the
        mouse has traveled since the last movement in a particular axis), 
        the information is read from the "paddle" or potentiometer (POT)
        registers.  On the C64, the POT registers are part of the SID
        chip, and are at 54297 ($D419) for POTX, and 54298 ($D41A) for
        POTY.

Q $0FD) What is the maximum size of a sequential file on a 1581 drive?

A $0FD) 802640 bytes.

Q $0FE) What flaw exists in the early Commodore 1670 modems?

A $0FE) When the 1670 modem was first introduced, it powered up in auto-
        answer mode, which means it would answer incoming calls after
        the phong rang.  You could turn this feature off through software
        control, but if the power was reset, the modem would answer the
        phone.  So many people complained to Commodore that CBM revised
        the 1670 to include an extra DIP switch that turned this feature
        off.

Q $0FF) What is the model number of the first modem for the VIC and C64?

A $0FF) The 1600 manual dial/manual answer 0-300 bps modem.  The author 
        owns one, and used it for many years.  To operate, you must use
        a phone with a detachable handset cord.  You dialed the number
        on the phone, waited for the answer, unplugged the handset, and
        plugged the cord into the 1600.  A switch toggled between using
        originate or answer frequencies.  The 1600 was manufactured by
        Anchor Automation for Commodore.  (As an aside, this unit claimed
        300 bps, but I never could get 300 to work well.  Most of my
        telecommunications happened at 150 bps.)


-------Commodore Trivia Edition #17 Questions and Answers (BEGIN)--------

Q $100) On the MOS Technology's KIM-1, how many keys were on the keypad?

A $100) 23 keys.  The keypad has room for 24, but one spot is taken by
        a switch that puts the system into single-step mode.  Interestingly,
        some pictures have the switch on the upper left, some on the upper
        right.

Q $101) The KIM-1 keypad had the common 0-9A-F keys on the keypad, but
        also had some special keys.  Name them.

A $101) GO (Go) Executes an instruction and displays the address of next,
        ST (Stop) Stops execution of program and return control to monitor, 
        RS (Reset), 
        AD (Address) Address entry mode, 
        DA (Data) Data entry mode, 
        PC (Program Counter) Displays and restores program counter to values
                             in PCL and PCH,
        +  (Increment) Increments the address without changing the entry mode.

Q $102) The KIM-1 was a set of modules that could be plugged together to
        expand the system.  Each module had a model number.  What was the 
        model number of the KIM-1 motherboard?

A $102) The KIM-4.  

Q $103) On the 1525 line of printers, if you wanted to create the following
        graphic, what bytes would you send to the printer after turning on
        graphics mode?
        
        ****
        *  *
        *  *
        *  *
        *  *
        *  *
        ****

A $103) I guess I should have stipulated that this is a bitmap.  ASCII just
        has a few limitations.  Anyway, the correct bytes to send are:
        255, 193, 193, 255.  You got these by assigning each bit in a column
        a value, and adding 128 to the result for each column.  

Q $104) What is the horizontal resolution of the 1525 line of printers?

A $104) Character resolution:   80 chars, or 10 chars/inch (cpi).
        Graphics resolution:    480 dots, or 60 dots/inch (dpi).  

Q $105) On Commodore drives, explain the difference between the B-R command
        and the U1 command.

A $105) The two commands read in data from a disk sector.  However, the 
        U1 command always reads a full sector (255 bytes).  The B-R
        command reads the number of bytes specified in the first byte of
        the sector.  If the first byte is a 15, B-R will read 15 bytes
        from the sector.  (From the 1581 manual)

Q $106) On the Commodore 1541 drive, what does the U: command do?

A $106) This command has been traditionally used to reset Commodore drives,
        including the CBM 1541.  However, some early versions of the Drive
        DOS did not correctly handle this command.  In these versions, the 
        drive and computer failed to complete the command transaction 
        successfully, and what looked like a hung machine resulted.  
        Commodore later fixed this problem.  If U: seems to not work on
        your drive, try U; instead.  

Q $107) What does the first routine in the 1541 drive ROM actually do?

A $107) The function, called SETLDA and residing at $C100, turns on the
        drive active LED for the current drive.  The routine loads the
        current drive from $7F and sets bit 3 of DSKCNT ($1C00).

Q $108) How many files will a 1581 disk drive hold?

A $108) 296 files.  Note that it is not a multiple of 144.  

Q $109) Commodore 1581 drives have a special "autoboot" feature that enables
        the drive to load and run a program off a disk upon drive bootup.
        What is the required name of the file?

A $109) COPYRIGHT CBM 86

Q $10A) What filetype must the file mentioned in $109 be?

A $10A) USR.

Q $10B) To power up a 1351 mouse in "joystick mode", what must the user do?

A $10B) If one depresses the right mouse button during power-up, the 1351
        will behave just like a joystick.

Q $10C) Describe the contents of the POTX or POTY registers when using a
        1351 mouse.

A $10C) Each register holds the same type of information, just for a 
        separate axis, so we will describe just one register:
        
        Bit:   Function
        
        7      Don't care
        6-1    Mouse axis position mod 64.
        0      Noise Bit. (check this bit to see whether mouse has moved)

Q $10D) Commodore computers typically use most of zero page for temporary
        variables and other items.  However, both the VIC-20 and the 64
        reserve 4 bytes for user programs that need zero page memory.  Where
        are these locations?

A $10D) $FB-$FE (251-254).  I am not sure these were "reserved" for 
        programmers as much as they were just not utilized by the 
        CBM programmers.  

Q $10E) Name the 16 colors available on the 64.

A $10E) Black
        White
        Red
        Cyan (Light Blue-Green)
        Purple
        Green
        Blue
        Yellow
        Orange
        Brown
        Light Red
        Dark Gray (Gray 1)
        Medium Grey (Gray 2)
        Light Green 
        Light Blue
        Light Gray (Gray 3)
                         
Q $10F) Both the VIC-20 and the C64 emulate the operation of the 6551 UART.
        How many "mock 6551" registers are mapped into the memory map?

A $10F) 5, from $293-$297 (659-663).  The register contents:

        $293   6551 Control Register
        $294   6551 Command Register
        $295-6 6551 User Defined Baud Rate value.
        $297   6551 Status Register 


------------Commodore Trivia Edition #18 Questions (BEGIN)--------------

Q $110) What is the name of the company that recently purchased the
        liquidated Commodore assets?

Q $111) At one time, Commodore attempted to manufacture a dual drive
        version of the 1571 called the 1572.  For what technical reason
        did it utimately fail?

Q $112) Over what computer system did a User Group sue Commodore and win?

Q $113) In $103, the question asked how to create a graphic of a small box
        on the 1525.  In this quesrtion, we have made a different design.
        If you wanted to create the following graphic using individual
        dots on the printer, what bytes would you send to the printer after
        turning on graphics mode?
        
         **     * *
        *       ***
        *   **  ***
        *   * * * *
         ** **  * *
            * *
            **

Q $114) (Some C65 questions)  How many SID chips does the the development
        Commodore 65 machine contain?

Q $115) What CPU does the Commodore 65 use?

Q $116) What is the alternate name for the Commodore 65?

Q $117) How many processors does the internal 1581-compatible drive 
        on the C65 contain?

Q $118) In the tradition of naming certian ICs after famous cartoon
        characters, one of the ICs in the C65 is named after a Warner
        Brothers cartoon character.  Which one?

Q $119) What version of BASIC is included on the Commodore 65 in C65 mode?

Q $11A) How many I/O ports does a Commodore 65 contain?

Q $11B) What common Commodore 64 I/O port does the C65 NOT have?

Q $11C) How many function keys are on a Commodore 65?

Q $11D) What CBM disk drive DOS was used as the template for the internal
        C65 drive DOS?

Q $11E) What resolution of text screen does the C65 power up in? (Please
        give answers in characters).

Q $11F) What distinguishing non-textual characteristic in the C65 is not
        present in othe Commodore 8-bit computers? 

The information in this between the lines marked by (BEGIN) and (END)
is copyright 1995 by Jim Brain.  Provided that the information
between the (BEGIN) and (END) lines is not changed except to correct
typographical errors, the so marked copyrighted information may be
reproduced in its entirety on other networks or in other mediums.  For 
more information about using this file, please contact the address 
shown below.

Jim Brain
brain@mail.msen.com 
602 North Lemen
Fenton, MI  48430
(810) 737-7300 x8528

Some are easy, some are hard, try your hand at:
    Commodore Trivia #18!
========================================================================
BFLI - New graphics modes 2
by Pasi 'Albert' Ojala <albert@cs.tut.fi>

One day I was watching some demos that used linecrunch routines for
whole-screen multicolor-graphics upscrollers.  I already had my
theories about how and why linecrunch worked, but because I had not
used it anywhere, the details were a bit vague.  In fact, I have
many times accidentally created linecrunch effects when trying to do
something else with $D011.  Probably every demo coder has.

But you learn by doing.  I had the idea of using linecrunch for FLI
instead of a simple multicolor picture as it always seemed to be
used.  However, this has probably been done before and because I
don't like to do things that have been done before, I decided to use
linecrunch to show a two-screen-tall FLI picture.


_Linecrunch Basics_

For those not familiar with linecrunch routines:  linecrunch is used
to scroll the screen UPWARDS by convincing VIC-II that it has
already showed more character rows than it in reality has shown.
Surprisingly (or then, maybe not :) this consists of fiddling with
$D011.  The timing is critical as always.

Linecrunch works by setting $D011 equal the line before the current
line and VIC-II will happily think that it is time to move on to the
next character row - add 40 to the video matrix counter, 320 to the
graphics memory counter and be ready to start a bad line.  Or, maybe
'NOT to go back to the current row' would be a more suitable
description.  (Programming VIC-II is slowly becoming a science.)

The required timing also does not cause bad lines so that you can
skip another line immediately on the successive line.  In addition,
lines can be skipped only after the first character row and half of
the second character row have been displayed.  This has something to
do with the way VIC-II decides when there is a bad line.

Because linecrunch causes VIC-II to skip rows, it will run out of
video matrix and color memory (and graphics memory) before reaching
the end of the screen.  However, VIC-II does not stop displaying the
graphics nor does it reset the internal counters.  The counters keep
on running and wrap around instead.

Normally, when VIC-II is displaying the last character row, it is
showing the memory from offsets $3c0 to $3e7.  If VIC-II has skipped
one character row, it is displaying from $3e8 to $40f instead.  But,
there are only 10 bits for the video matrix counter (0..1023), so it
wraps around to zero after $3ff.  This means that the beginning of
the video matrix is displayed at the bottom of the screen.  The
character rows become shifted by 24 character positions to the right
because there were originally 24 unused memory locations at the end
of the memory (1000..1023).  (To be honest, sprite image pointers
are not unused memory, but they are not used with normal FLI.)

         ____________________    ____________________
        |abcdefghijklmnopqrst|  |abcdefghijklmnopqrst|
        |                    |  |--------------------| <- Skipped row
        :                    :  :                    :
        :                    :  :                    :
        :                    :  :                    :
        |                    |  |normally last line  |
        |normally last line  |  |XXXXXXXXZZZZabcdefgh|
        `--------------------'  `--------------------'
                                X = unused mem      (1000..1015)
                                Z = sprite pointers (1016..1023)

        Figure 1: Linecrunch


The same thing happens for color memory because it uses the same
counter for addressing the memory (in fact, color memory access and
character data access are performed simultaneosly, 12 bits at a
time).  The graphics memory behaves the same way, except that the
counter has three bits more and it counts at eight times the speed,
so that it wraps at the exact same time as the other counter.

The first character row can't be used for linecrunch and the second
one is also lost in the process.  The first usable line to display
is the third character row.  However, those two lost rows can still
be used as an extension at the end of the first screen.  You must
notice, however, that the alignment has been changed.  After these
two rows have been displayed, the video bank is switched to get new
fresh data on the screen.


_Back to BFLI_

Wrapped data is nothing difficult to work with.  It is just the
matter of writing the right conversion program.  Also, the normal
FLI routine can be used, we just have to make sure VIC always has
the right bank visible - simple LDA bank,x:sta $DD00 can accomplish
that.  The more difficult aspect is to make the display freely
locatable.  We have 32 kilobytes of graphics data, this is the main
reason we can't even think about using copying.  Linecrunch combined
with the bad line delaying technique will do the job much more
nicely.

Figure 2 shows the principles.  To make things simpler I have chosen
location 0 to mean that the top of the picture is visible, 1 means
that the picture is scrolled one line upwards and so on.  We can see
that linecrunch is not used at all for the location 0.  To make the
picture start at the same point whether linecrunch has crunched
lines or not we compensate the non-lost raster lines by delaying the
next bad line.  When the location is n*8 (n=0,1,2..), the sum of the
linecrunched and delayed lines is constant - the graphics display
always starts at the same point.

Then how do we deal with the location values that are not evenly
dividable by eight ?  Now, lets assume that the location is L, and
we have C, which is the location divided by eight (C = L/8), and R,
which is the remainder (R = L%8).  To make the picture scroll to the
right position, we need to delay the bad line less than before - R
lines less for location L than for location C*8.  E.g.  for location
2 we delay the bad line two lines less than for location 0.  This
also shows that we need 7 lines more than is needed for to
compensate for the linecrunch.

Determining the number of linecrunch lines is a recursive process,
because when you use more linecrunch lines, that decreases the
number of lines you have available for the display and you need
bigger range for the location value.  The linecrunch can be started
after 12 lines, and we need at least 7 lines to use the soft
y-scroll.  This makes 181 lines available for the display
originally.

Because we need to show 400 lines of graphics, we would need
(400-181)/8=28 linecrunch lines.  However, this in turn reduces the
number of lines we have for graphics to 181-28=153 and we need
(400-153)/8=31 linecrunch lines.  Again, 181-31 is 150.  We get
(400-150)/8=32 and there it finally converges and we have 149 lines
for graphics, which makes location values 0..251 valid.


Location        0       1       2  ..   8       9  ..   251

                ___________________..   ___________..   ________
                ___________________..   ___________..   ________
Linecrunch      -------------------..   ___________..
                ^       ^       ^
                |       |       |       ^       ^
                |       |       |       |       |
Bad line delayed|       |       |       |       |
                |       |       |       |       |       ========
                |       |       v       |       |       244
                |       v       ___..   |       v       :
                v       ________0       v       ___..   :
Gfx Enabled     ________0_______1__..   ________8__..   250_____
                0       1       2       8       9       251
                1       2       3       9       10      252
                2       3       4       10      11      253
                3       4       5       11      12      254
                4       5       6       12      13      255
                5       6       7       13      14      256
                6       7       8       14      15      257
                7       8       9       15      16      258
                :       :       :       :       :       :
                :       :       :       :       :       :
                148     149     150..   156     157..   399

        Figure 2: Linecrunch and DMA delay in BFLI
                  (Graphics lines not in scale)


_Clipping added_

Now we can scroll the picture to any location we want, but the top
of the picture is not clipped and it is very annoying to watch.  We
need to enable the graphics at the same point regardless of the
y-scroll value.  The answer is in the extended color mode (ECM).

When both ECM and multicolor mode (MCM) are selected, VIC-II will
turn the display to black.  This is because there is a conflicting
situation and it just can't decide which color scheme to use.  The
video accesses will continue to happen just like before, the data is
just not displayed.  When the ECM bit is cleared again, the normal
multicolor graphics is shown.

So, we set the ECM bit and start to display the first eight lines of
the FLI.  Because the FLI routine already writes to $D011, we just
make sure the ECM bit is set in the first R number of writes to
$D011 and zero in all other.

The viewer is now 'complete'.  You can take a look at the code below
or you can get C64Gfx1_4.lha and see it in action yourself and not
just rely on my word.  The package includes converter programs for
BFLI, FLI and Koala (ANSI-C), couple of example pictures and viewers
for PAL and NTSC machines.

-Pasi 'Albert' Ojala    albert@cs.tut.fi

--------------------------------------------------------------------------

BFLI viewer program for PAL machines

UPOS   = $C00   ; temporary area for tables
BANK   = $D00   ;  UPOS for linecrunch, BANK for FLI bank select
RASTER = 29     ; where to position the sprite -> IRQ 20 lines later
DUMMY  = $FFF   ; dummy location for timing purposes
FLISZ  = 19-1   ; visible FLI size in character rows - 1

*= $810
        SEI
        LDA #$7F:STA $DC0D      ; IRQ setup
        LDA #1:STA $D01A
        STA $D015:STA KEYW+1
        LDA #<IRQ:STA $314
        LDA #>IRQ:STA $315
        LDA #RASTER:STA $D001:CLC:ADC #20:STA $D012
        LDA #0:STA $D017
        LDA #0:STA 2
        JSR NEWPOS              ; Init the FLI routines
        LDA #$A:STA $D011       ; Blank screen
        LDX #23                 ; Init tables
BLOOP   LDA #$94:STA BANK,X
        LDA #$96:STA BANK+24,X
        DEX:BPL BLOOP
        LDX #15
LOOP0   LDA YINIT,X:AND #$77    ; Change to $37 to better see the
        STA UPOS,X              ;  workings of the routines
        STA UPOS+16,X
        STA UPOS+32,X
        DEX:BPL LOOP0

        LDA #$34:STA 1          ; Copy to the last video bank
        LDA #$80:STA SRC+2      ; from $8000-$BFFF to $C000-$FFFF
        LDA #$C0:STA DST+2
        LDX #0:LDY #$40
SRC     LDA $8000,X
DST     STA $C000,X
        INX:BNE SRC
        INC SRC+2:INC DST+2
        DEY:BNE SRC
        LDA #$37:STA 1

        LDX #0                  ; Init color memory
LP      LDA $3C00,X:STA $D800,X ; All 1024 bytes are used
        LDA $3D00,X:STA $D900,X ;  - some even twice!
        LDA $3E00,X:STA $DA00,X
        LDA $3F00,X:STA $DB00,X
        INX:BNE LP
        LDA $DC0D:CLI

KEYW    LDX #0:BNE KEYW         ; Wait for space to be pressed
        SEI                     ; System to normal
        LDA #$37:STA 1
        JSR $FDA3
        LDA #$97:STA $DD00
        JSR $E5A0
        LDY #3
IRQL    LDA $FD30,Y:STA $314,Y
        DEY:BPL IRQL

        LDX #0:LDA #1           ; Clear color memory
CLL     STA $D800,X:STA $D900,X
        STA $DA00,X:STA $DB00,X
        INX:BNE CLL
        CLI:RTS

YINIT   BYT $78,$79,$7A,$7B,$7C,$7D,$7E,$7F
        BYT $78,$79,$7A,$7B,$7C,$7D,$7E,$7F

*=*-<*+256

IRQ     LDA #$18:STA $D016:LDX #0:LDA #$5A
        INC DUMMY:DEC DUMMY             ; Synchronization
        STX $D020:STX $D021:STA $D011

        LDA #$15:STA $D018
        LDA #$97:STA $DD00
        LDX #44                 ; Wait for the 4th line
LL      DEX:BPL LL:NOP
        LDX #0

LOOP3   NOP                     ; Linecrunch-part routine
        LDA UPOS+6,X:INC DUMMY:STA $D011
        NOP:NOP:INC DUMMY
        NOP:NOP:NOP:NOP:NOP
        NOP:NOP:NOP:NOP:NOP
        NOP:NOP:NOP:NOP:NOP
        INX
E1      CPX #$10:BNE LOOP3      ; Skip that many character rows-4
        BIT $EA
LOOP4   LDA UPOS,X:INC DUMMY:STA $D011
        NOP:NOP:NOP:INC DUMMY
        NOP:NOP:NOP:NOP:NOP
        NOP:NOP:NOP:NOP:NOP
        NOP:NOP:NOP:NOP:LDA #0
        INX
E2      CPX #$1F:BNE LOOP4      ; Delay DMA until we are at the
                                ;  'same place' each time

        LDA #0:STA $D020        ; Now wait for the bad line and start FLI
        BIT $EA:NOP
        NOP:NOP:NOP:NOP
        NOP:NOP:NOP:NOP
        NOP:NOP:NOP:NOP
B0      LDA #$92:STA $DD00:NOP  ; The right video bank

        ; Wait for 0-7 lines to set the ECM mode off
        ;  (makes the graphics visible)

F0      LDA #0:STA $D011:LDA #$08:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F1      LDA #0:STA $D011:LDA #$18:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F2      LDA #0:STA $D011:LDA #$28:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F3      LDA #0:STA $D011:LDA #$38:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F4      LDA #0:STA $D011:LDA #$48:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F5      LDA #0:STA $D011:LDA #$58:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F6      LDA #0:STA $D011:LDA #$68:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F7      LDA #0:STA $D011:LDA #$78:STA $D018
        LDX #FLISZ:NOP:NOP:NOP:BIT $EA

        ; Do FLI 18 more character rows

F8      LDA #0:STA $D011:LDA #$08:STA $D018
B1      LDA BANK,X:STA $DD00:BIT $EA
F9      LDA #0:STA $D011:LDA #$18:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
FA      LDA #0:STA $D011:LDA #$28:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
FB      LDA #0:STA $D011:LDA #$38:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
FC      LDA #0:STA $D011:LDA #$48:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
FD      LDA #0:STA $D011:LDA #$58:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
FE      LDA #0:STA $D011:LDA #$68:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
FF      LDA #0:STA $D011:LDA #$78:STA $D018:NOP:NOP:DEX:BMI EFLI:JMP F8
EFLI    NOP
        LDA #$FC
WL      CMP $D012:BNE WL
        INC DUMMY:INC DUMMY:INC DUMMY:INC DUMMY
        INC DUMMY:INC DUMMY:INC DUMMY:INC DUMMY
        INC DUMMY:INC DUMMY:STA $D020

        JSR NEWPOS      ; Update the location
        JSR CHPOS       ; Change to a new location
        LDA $DC01:AND #$10:BNE OV3      ; Check for the space bar
        LDA #0:STA KEYW+1
OV3     LDX #$53:STX $D011:INC $D019:JMP $EA81

NEWPOS  LDA #0          ; Init the IRQ routine for this position
        LSR:LSR:LSR:CLC:ADC #4:STA E1+1
        LDA #7:SEC:SBC NEWPOS+1:AND #7:TAX:TAY:CLC:ADC #35:STA E2+1
        LDA UPOS+3+7,Y:DEX:BMI J0:AND #$3F
J0      STA F7+1:AND #$3F:STA FF+1
        LDA UPOS+3+6,Y:DEX:BMI J1:AND #$3F
J1      STA F6+1:AND #$3F:STA FE+1
        LDA UPOS+3+5,Y:DEX:BMI J2:AND #$3F
J2      STA F5+1:AND #$3F:STA FD+1
        LDA UPOS+3+4,Y:DEX:BMI J3:AND #$3F
J3      STA F4+1:AND #$3F:STA FC+1
        LDA UPOS+3+3,Y:DEX:BMI J4:AND #$3F
J4      STA F3+1:AND #$3F:STA FB+1
        LDA UPOS+3+2,Y:DEX:BMI J5:AND #$3F
J5      STA F2+1:AND #$3F:STA FA+1
        LDA UPOS+3+1,Y:DEX:BMI J6:AND #$3F
J6      STA F1+1:AND #$3F:STA F9+1
        LDA UPOS+3+0,Y:DEX:BMI J7:AND #$3F
J7      STA F0+1:AND #$3F:STA F8+1
        LDA #$96:STA B0+1:LDA #199:SEC:SBC NEWPOS+1:BCC OV2
        LSR:LSR:LSR:CLC:ADC #5:STA B1+1
        RTS
OV2     LDA #0:STA B1+1:LDX #$94:STX B0+1:RTS

CHPOS   LDX NEWPOS+1
        LDA $DC00:TAY           ; Get joystick
        AND #$10:BNE DIR        ; If no button pressed
        TYA:AND #1:BEQ UP       ; If joy up
        TYA:AND #2:BEQ DOWN     ; If joy down
        RTS
DIR     LDA #0:BEQ UP
DOWN    DEX:CPX #$FF:BNE DOK
        LDX #0:STX DIR+1        ; Change direction
DOK     STX NEWPOS+1:RTS
UP      INX:CPX #$FD:BCC UOK    ; 251(locations)+149(visible)=400
        LDX #$FC:STX DIR+1      ; Change direction
UOK     STX NEWPOS+1:RTS


--------------------------------------------------------------------------

The BFLI file format:

                        File            BFLI Display
        Lines           Offset          Offset          Lines     Size
Colors  0-1.3           0..55           944..999        22.7-24   56
  I     1.3-2           56..79          -                         24
        2-24            80..999         0..919          0-22      920
        24-24.7         1000..1023      920..943        22-22.7   24

  II    0-1.3           0..55           1968..2024      49.3-50.6 56
        1.3-24.7        56..1023        1000..1967      24-49.3   968


Gfx     0-1.3           0..447          7552..7999      22.7-24   448
  I     1.3-2           448..639        -                         192
        2-24            640..7999       0..7359         0-22      7360
        24-24.7         8000..8191      7360..7551      22-22.7   192

  II    0-1.3           0..447          15744..16192    49.3-50.6 448
        1.3-24.7        448..8191       8000..15743     24-49.3   7744

========================================================================
Making stable raster routines (C64 and VIC-20)
by Marko Makela (Marko.Makela@HUT.FI)

Preface

Too many graphical effects, also called raster effects, have been
coded in a very sloppy way.  For instance, if there are any color bars
on the screen in a game or demo, the colors often jitter a bit,
e.g. they are not stable.  And also, it is far too easy to make
virtually any demo crash by hitting the Restore key, or at least cause
visual distortions on the screen.

As late as a year ago I still hadn't coded a stable raster interrupt
routine myself.  But then I had to do it, since I was researching the
video chip timing details together with my German friend Andreas
Boose.  It was ashaming that we had the same level of knowledge when
it came to the hardware, but he was the only of us who had written a
stable raster routine.  Well, finally I made me to start coding.  I
used the same double-interrupt idea as Andreas used in his routine.

After a couple of errors my routine worked, and I understood how it
works exactly.  (This is something that separates us normal coders
from demo people: They often code by instinct; by patching the routine
until it works, without knowing exactly what is happening.  That's why
demos often rely on weird things, like crash if the memory is not
initialized properly.)

In this article, I document two methods of creating stable raster
routines on Commodore computers.  The principles apply for most 8-bit
computers, not only Commodores, but raster effects are very rarely
seen on other computers.


Background

What are raster effects?  They are effects, where you change the
screen appearance while it is being drawn.  For instance, you can set
the screen color to white in the top of the screen, and to black in
the middle of the screen.  In that way, you will get a picture whose
top half is white and bottom half black.  Normally such effects are
implemented with interrupt routines that are executed synchronized
with the screen refresh.

The video chip on the Commodore 64 and many other videochips have a
special interrupt feature called the Raster interrupt.  It will
generate an IRQ in the beginning of a specified raster line.  On other
computers, like the VIC-20, there is no Raster interrupt, but you can
generate the interrupts with a timer, provided that the timer and the
videochip are clocked from the same source.

Even if the processor gets an interrupt signal at the same position on
each video frame, it won't always be executing the first instruction
of the interrupt routine at the same screen position.  The NMOS 6502
machine instructions can take 2 to 9 machine cycles to execute, and if
the main program contains instructions of very varying lengths, the
beginning position of the interrupt can jump between 7 different
positions.  This is why you need to synchronize the raster routine
when doing serious effects.

Also, executing the interrupt sequence will take 7 additional cycles,
and the interrupt sequence will only start after the current
instruction if the interrupt arrived at least two cycles before the
end of the current instruction.  It is even possible that an interrupt
arrives while interrupts are disabled and the processor is just
starting to execute a CLI instruction.  Alas, the processor will not
jump to the interrupt right after the CLI, but it will execute the
next instruction before jumping to it.  This is natural, since the CLI
takes only two cycles.  But anyway, this is only a constant in our
equation, and actually out of the scope of this article.

How to synchronize a raster interrupt routine?  The only way is to
check the current screen position and delay appropriately many cycles.
There are several ways of doing this, some of which are very awful and
inefficient.  The ugliest ways of doing this on the Commodore 64 I
know are busy-waiting several raster lines and polling the raster line
value, or using the Light pen feature, which will fail if the user
presses the fire button on Joystick port 1.  Here I will present two
ways, both very elegant in my opinion.


Using an auxiliary timer

On the VIC-20, there is no Raster interrupt feature in the video chip.
All you can do is to use a timer for generating raster interrupts.
And if you use two timers running at a constant phase difference, you
can get full synchronization.  The first timer generates the raster
interrupt, and the second timer, the auxiliary timer, tells the raster
routine where it is running.  Actually you could even use the first
timer also for the checking, but the code will look nicer in the way I
will be presenting now.  Besides, you can use the auxiliary timer idea
even when real raster interrupts are available.

The major drawback of using an auxiliary timer is initializing it.
The initialization routine must synchronize with the screen, that is,
wait for the beginning of the wanted raster line.  To accomplish this,
the routine must first wait for a raster line that occurs a bit
earlier.  About the only way to do this is with a loop like

                LDA #value
        loop    CMP raster
                BNE loop

One round of this loop will take 4+3=7 cycles to execute, assuming
that absolute addressing is being used.  The loop will be finished if
the raster register contains the wanted value while the processor
reads it on the last cycle of the CMP instruction.  The raster
register can actually have changed already on the first cycle of the
BNE instruction on the previous run of the loop, that is 7 cycles
earlier!

Because of this, the routine must poll the raster register for several
raster lines, always consuming one cycle more if the raster register
changed too early.  As the synchronization can be off at most by 7
cycles, a loop of 7 raster register value changes would do, but I made
the loop a bit longer in my VIC-20 routine.  (Well, I have to admit it,
I was too lazy to make it work only with 7 rounds.)

After the initialization routine is fully synchronized the screen, it
can set up the timer(s) and interrupts and exit.  The auxiliary timer
in my VIC-20 demo routine is several dozens of cycles after the
primary timer, see the source code for comments.  It is arranged so
that the auxiliary timer will be at least 0 when it is being read in
the raster routine.  The raster routine will wait as many extra cycles
as the auxiliary timer reads, however at most 15 cycles.


Using double raster interrupt

On the Commodore 64, I have never seen the auxiliary timer scheme
being used.  Actually I haven't seen it being used anywhere, I was
probably the first one who made a stable raster interrupt routine on
the VIC-20.  Instead, the double interrupt method is becoming the
standard on the C64 side.

The double interrupt method is based entirely on the Raster interrupt
feature of the video chip.  In the first raster interrupt routine, the
program sets up another raster interrupt on a further line, changes
the interrupt vector and enables interrupts.

In the place where the second raster interrupt will occur, there will
be 2-byte instructions in the first interrupt routine.  In this way,
the beginning of the next raster interrupt will be off at most by one
cycle.  Some coders might not care about this one cycle, but if you
can do it right, why wouldn't you do it right until the end?

At the beginning of the second raster interrupt routine, you will read
the raster line counter register at the point where it is about to
change.  When the raster routine is being executed, there are two
possibilities: Either the raster counter has just changed, or it will
change on the next cycle.  So, you just need to compare if the
register changed one cycle too early or not, and delay a cycle when
needed.  This is easily accomplished with a branch to the next address.

Of course, somewhere in your second raster interrupt routine you must
restore the original raster interrupt position and set the interrupt
vector to point to the first interrupt routine.


Applying in practice

I almost forgot my complaints about demos crashing when you actively
hit the Restore key.  On the VIC-20, you can disable NMI interrupts
generated by the Restore key, and on the C64, you can generate an NMI
interrupt with the CIA2 timer and leave the NMI-line low, so that no
further high-to-low transitions will be recognized on the line.  The
example programs demonstrate how to do this.

So far, this article has been pretty theoretical.  To apply these
results in practice, you must definitely know how many CPU clock
cycles the video chip consumes while drawing a scan line.  This is
fairly easy to measure with a timer interrupt, if you patch the
interrupt handler so that it changes the screen color on each run.
Set the timer interval to LINES*COLUMNS cycles, where LINES is the
amount of raster lines and COLUMNS is your guess for the amount of
clock cycles spent in a raster line.

If your guess is right, the color will always be changed in the same
screen position (neglecting the 7-cycle jitter).  When adjusting the
timer, remember that the timers on the 6522 VIA require 2 cycles for
re-loading, and the ones on the 6526 CIA need one extra cycle.  Keep
trying different timer values until you the screen color changes at
one fixed position.

Commodore used several different values for LINES and COLUMNS on its
videochips.  They never managed to make the screen refresh rate
exactly 50 or 60 Hertz, but they didn't hesitate to claim that their
computers comply with the PAL-B or NTSC-M standards.  In the following
tables I have gathered some information of some Commodore video chips.


  NTSC-M systems:

            Chip      Crystal  Dot      Processor Cycles/ Lines/
    Host    ID        freq/Hz  clock/Hz clock/Hz  line    frame
    ------  --------  -------- -------- --------- ------- ------
    VIC-20  6560-101  14318181  4090909   1022727      65    261
    C64     6567R56A  14318181  8181818   1022727      64    262
    C64     6567R8    14318181  8181818   1022727      65    263

  Later NTSC-M video chips were most probably like the 6567R8.  Note
  that the processor clock is a 14th of the crystal frequency on all
  NTSC-M systems.

  PAL-B systems:

            Chip      Crystal  Dot      Processor Cycles/ Lines/
    Host    ID        freq/Hz  clock/Hz clock/Hz  line    frame
    ------  --------  -------- -------- --------- ------- ------
    VIC-20  6561-101   4433618  4433618   1108405      71    312
    C64     6569      17734472  7881988    985248      63    312

  On the PAL-B VIC-20, the crystal frequency is simultaneously the dot
  clock, which is BTW a 4th of the crystal frequency used on the C64.
  On the C64, the crystal frequency is divided by 18 to generate the
  processor clock, which in turn is multiplied by 8 to generate the
  dot clock.

  The basic timings are the same on all 6569 revisions, and also on
  any later C64 and C128 video chips.  If I remember correctly, these
  values were the same on the C16 videochip TED as well.

Note that the dot clock is 4 times the processor clock on the VIC-20,
and 8 times that on the C64.  That is, one processor cycle is half a
character wide on the VIC-20, and a full character on a C64.  I don't
have exact measurements of the VIC-20 timing, but it seems that while
the VIC-20 videochips draw the characters on the screen, it first
reads the character code, and then, on the following video cycle, the
appearance on the current character line.  There are no bad lines,
like on the C64, where the character codes (and colors) are fetched on
every 8th raster line.

Those ones who got upset when I said that Commodore has never managed
to make a fully PAL-B or NTSC-M compliant 8-bit computer should take a
closer look at the "Lines/frame" columns.  If that does not convince
you, calculate the raster line rate and the screen refresh rate from
the values in the table and see that they don't comply with the
standards.  To calculate the line rate, divide the processor clock
frequency by the amount of cycles per line.  To get the screen refresh
rate, divide that frequency by the amount of raster lines.


The Code

OK, enough theory and background.  Here are the two example programs,
one for the VIC-20 and one for the C64.  In order to fully understand
them, you need to know the exact execution times of NMOS 6502
instructions.  (All 8-bit Commodore computers use the NMOS 6502
processor core, except the C65 prototype, which used a inferior CMOS
version with all nice poorly-documented features removed.)  You should
check the 64doc document, available on my WWW pages at
http://www.hut.fi/~msmakela/cbm/emul/x64/64doc.html, or via FTP at
ftp.funet.fi:/pub/cbm/documents/64doc.  I can also e-mail it to you on
request.

Also, I have written a complete description of the video timing on the
6567R56A, 6567R8 and 6569 video chips, which could maybe be turned
into another C=Hacking article.  The document is currently partially
in English and partially in German.  The English part is available
from ftp.funet.fi as /pub/cbm/documents/pal.timing, and I can send
copies of the German part (screen resolution, sprite disturbance
measurements, and more precise timing information) via e-mail.

The code is written for the DASM assembler, or more precisely for a
extended ANSI C port of it made by Olaf Seibert.  This excellent
cross-assembler is available at ftp.funet.fi in /pub/cbm/programming.

First the raster demo for the VIC-20.  Note that on the VIC-20, the
$9004 register contains the upper 8 bits of the raster counter.  So,
this register changes only on every second line.  I have tested the
program on my 6561-101-based VIC-20, but not on an NTSC-M system.

It was hard to get in contact with NTSC-M VIC-20 owners.  Daniel
Dallmann, who has a NTSC-M VIC-20, although he lives in Germany, ran
my test to determine the amount of cycles per line and lines per frame
on the 6560-101.  Unfortunately, the second VIA of his VIC-20 is
partially broken, and because of this, this program did not work on
his computer.  Craig Bruce ran the program once, and he reported that
it almost worked.  I corrected a little bug in the code, so that now
the display should be stable on an NTSC-M system, too.  But the actual
raster effect, six 16*16-pixel boxes centered at the top border, are
very likely to be off their position.


  processor 6502

NTSC    = 1
PAL     = 2

;SYSTEM = NTSC  ; 6560-101: 65 cycles per raster line, 261 lines
SYSTEM  = PAL   ; 6561-101: 71 cycles per raster line, 312 lines

#if SYSTEM & PAL
LINES = 312
CYCLES_PER_LINE = 71
#endif
#if SYSTEM & NTSC
LINES = 261
CYCLES_PER_LINE = 65
#endif
TIMER_VALUE = LINES * CYCLES_PER_LINE - 2

  .org $1001    ; for the unexpanded Vic-20

; The BASIC line

basic:
  .word 0$      ; link to next line
  .word 1995    ; line number
  .byte $9E     ; SYS token

; SYS digits

  .if (* + 8) / 10000
  .byte $30 + (* + 8) / 10000
  .endif
  .if (* + 7) / 1000
  .byte $30 + (* + 7) % 10000 / 1000
  .endif
  .if (* + 6) / 100
  .byte $30 + (* + 6) % 1000 / 100
  .endif
  .if (* + 5) / 10
  .byte $30 + (* + 5) % 100 / 10
  .endif
  .byte $30 + (* + 4) % 10
0$:
  .byte 0,0,0   ; end of BASIC program

start:
  lda #$7f
  sta $912e     ; disable and acknowledge interrupts
  sta $912d
  sta $911e     ; disable NMIs (Restore key)

;synchronize with the screen
sync:
  ldx #28       ; wait for this raster line (times 2)
0$:
  cpx $9004
  bne 0$        ; at this stage, the inaccuracy is 7 clock cycles
                ; the processor is in this place 2 to 9 cycles
                ; after $9004 has changed
  ldy #9
  bit $24
1$:
  ldx $9004
  txa
  bit $24
#if SYSTEM & PAL
  ldx #24
#endif
#if SYSTEM & NTSC
  bit $24
  ldx #21
#endif
  dex
  bne *-1       ; first spend some time (so that the whole
  cmp $9004     ; loop will be 2 raster lines)
  bcs *+2       ; save one cycle if $9004 changed too late
  dey
  bne 1$
                ; now it is fully synchronized
                ; 6 cycles have passed since last $9004 change
                ; and we are on line 2(28+9)=74

;initialize the timers
timers:
  lda #$40      ; enable Timer A free run of both VIAs
  sta $911b
  sta $912b

  lda #<TIMER_VALUE
  ldx #>TIMER_VALUE
  sta $9116     ; load the timer low byte latches
  sta $9126

#if SYSTEM & PAL
  ldy #7        ; make a little delay to get the raster effect to the
  dey           ; right place
  bne *-1
  nop
  nop
#endif
#if SYSTEM & NTSC
  ldy #6
  dey
  bne *-1
  bit $24
#endif

  stx $9125     ; start the IRQ timer A
                ; 6560-101: 65 cycles from $9004 change
                ; 6561-101: 77 cycles from $9004 change
  ldy #10       ; spend some time (1+5*9+4=55 cycles)
  dey           ; before starting the reference timer
  bne *-1
  stx $9115     ; start the reference timer

pointers:
  lda #<irq     ; set the raster IRQ routine pointer
  sta $314
  lda #>irq
  sta $315
  lda #$c0
  sta $912e     ; enable Timer A underflow interrupts
  rts           ; return

irq:
; irq (event)   ; > 7 + at least 2 cycles of last instruction (9 to 16 total)
; pha           ; 3
; txa           ; 2
; pha           ; 3
; tya           ; 2
; pha           ; 3
; tsx           ; 2
; lda $0104,x   ; 4
; and #xx       ; 2
; beq           ; 3
; jmp ($314)    ; 5
                ; ---
                ; 38 to 45 cycles delay at this stage

  lda $9114     ; get the NMI timer A value
                ; (42 to 49 cycles delay at this stage)
; sta $1e00     ; uncomment these if you want to monitor
; ldy $9115     ; the reference timer on the screen
; sty $1e01
  cmp #8        ; are we more than 7 cycles ahead of time?
  bcc 0$
  pha           ; yes, spend 8 extra cycles
  pla
  and #7        ; and reset the high bit
0$:
  cmp #4
  bcc 1$
  bit $24       ; waste 4 cycles
  and #3
1$:
  cmp #2        ; spend the rest of the cycles
  bcs *+2
  bcs *+2
  lsr
  bcs *+2       ; now it has taken 82 cycles from the beginning of the IRQ

effect:
  ldy #16       ; perform amazing video effect
  lda $900f
  tax
  eor #$f7
0$:
  sta $900f
  stx $900f
  sta $900f
  stx $900f
  sta $900f
  stx $900f
  sta $900f
  stx $900f
  sta $900f
  stx $900f
  sta $900f
  stx $900f
  pha
  pla
#if SYSTEM & PAL
  pha
  pla
  nop
#endif
#if SYSTEM & NTSC
  bit $24
#endif
  nop
  dey
  bne 0$        ; end of amazing video effect

  jmp $eabf     ; return to normal IRQ


And after you have recovered from the schock of seeing a VIC-20
program, here is an example for the C64.  It does also something
noteworthy; it removes the side borders on a normal screen while
displaying all eight sprites.  Well, it cannot remove the borders on
bad lines, and the bad lines look pretty bad.  But I could use the
program for what I wanted: I measured the sprite distortions on all
videochip types I had at hand.  (FYI: the sprites 0-2 get distorted at
the very right of the screen, and the sprites 6 and 7 are invisible at
the very left of the screen.  You will need a monitor with horizontal
size controls to witness these effects.)

This program is really robust, it installs itself nicely to the
interrupt routine chain.  It even has an entry point for deinstalling
itself.  But in its robustness it uses self-modifying code to store
the original interrupt routine address. :-)

The code also relies on the page boundaries in being where they are.
The cycles are counted so that the branches "irqloop" must take 4
cycles.  If the "irqloop" comes to the same CPU page with the branch
instructions, you must add one cycle to the loop in a way or another.
When coding the routine, I noticed again how stupid assembly coding
can be, especially conditional assembling.  In a machine language
monitor you have far better control on page boundaries.  BTW, you
might wonder why I disable the Restore key in a subroutine at the end
and not in the beginning of the program.  Well, the routine was so
long that it would have affected the "irqloop" page boundaries.  And I
didn't want to risk the modified programs working on all three
different videochip types on the first try.


In the code, there are some comments that document the video timing,
like this one:

;3s4s5s6s7srrrrrgggggggggggggggggggggggggggggggggggggggg--||0s1s2s Phi-1 VIC-II
;ssssssssss                                               ||ssssss Phi-2 VIC-II
;==========xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx||XXX====== Phi-2 6510
;          ^ now we are here

The two vertical bars "|" denote optional cycles.  On PAL-B systems
(63 cycles per line), they are not present.  On 6567R56A, which has 64
cycles per line, there is one additional cycle on this position, and
the 6567R8 has two additional cycles there.

The numbers 0 through 7 are sprite pointer fetches (from the end of
the character matrix, e.g. the text screen), the "s" characters denote
sprite image fetches, the "r"s are memory refresh, and the "g" are
graphics fetches.  The two idle video chip cycles are marked with "-".
On the processor timing line, the "=" signs show halted CPU, "x" means
free bus, and "X" means that the processor will be halted at once,
unless it is performing write cycles.

  processor 6502

; Select the video timing (processor clock cycles per raster line)
CYCLES = 65     ; 6567R8 and above, NTSC-M
;CYCLES = 64    ; 6567R5 6A, NTSC-M
;CYCLES = 63    ; 6569 (all revisions), PAL-B

cinv = $314
cnmi = $318
raster = 52     ; start of raster interrupt
m = $fb         ; zero page variable

  .org $801
basic:
  .word 0$      ; link to next line
  .word 1995    ; line number
  .byte $9E     ; SYS token

; SYS digits

  .if (* + 8) / 10000
  .byte $30 + (* + 8) / 10000
  .endif
  .if (* + 7) / 1000
  .byte $30 + (* + 7) % 10000 / 1000
  .endif
  .if (* + 6) / 100
  .byte $30 + (* + 6) % 1000 / 100
  .endif
  .if (* + 5) / 10
  .byte $30 + (* + 5) % 100 / 10
  .endif
  .byte $30 + (* + 4) % 10

0$:
  .byte 0,0,0   ; end of BASIC program

start:
  jmp install
  jmp deinstall

install:        ; install the raster routine
  jsr restore   ; Disable the Restore key (disable NMI interrupts)
checkirq:
  lda cinv      ; check the original IRQ vector
  ldx cinv+1    ; (to avoid multiple installation)
  cmp #<irq1
  bne irqinit
  cpx #>irq1
  beq skipinit
irqinit:
  sei
  sta oldirq    ; store the old IRQ vector
  stx oldirq+1
  lda #<irq1
  ldx #>irq1
  sta cinv      ; set the new interrupt vector
  stx cinv+1
skipinit:
  lda #$1b
  sta $d011     ; set the raster interrupt location
  lda #raster
  sta $d012
  ldx #$e
  clc
  adc #3
  tay
  lda #0
  sta m
0$:
  lda m
  sta $d000,x   ; set the sprite X
  adc #24
  sta m
  tya
  sta $d001,x   ; and Y coordinates
  dex
  dex
  bpl 0$
  lda #$7f
  sta $dc0d     ; disable timer interrupts
  sta $dd0d
  ldx #1
  stx $d01a     ; enable raster interrupt
  lda $dc0d     ; acknowledge CIA interrupts
  lsr $d019     ; and video interrupts
  ldy #$ff
  sty $d015     ; turn on all sprites
  cli
  rts

deinstall:
  sei           ; disable interrupts
  lda #$1b
  sta $d011     ; restore text screen mode
  lda #$81
  sta $dc0d     ; enable Timer A interrupts on CIA 1
  lda #0
  sta $d01a     ; disable video interrupts
  lda oldirq
  sta cinv      ; restore old IRQ vector
  lda oldirq+1
  sta cinv+1
  bit $dd0d     ; re-enable NMI interrupts
  cli
  rts

; Auxiliary raster interrupt (for syncronization)
irq1:
; irq (event)   ; > 7 + at least 2 cycles of last instruction (9 to 16 total)
; pha           ; 3
; txa           ; 2
; pha           ; 3
; tya           ; 2
; pha           ; 3
; tsx           ; 2
; lda $0104,x   ; 4
; and #xx       ; 2
; beq           ; 3
; jmp ($314)    ; 5
                ; ---
                ; 38 to 45 cycles delay at this stage
  lda #<irq2
  sta cinv
  lda #>irq2
  sta cinv+1
  nop           ; waste at least 12 cycles
  nop           ; (up to 64 cycles delay allowed here)
  nop
  nop
  nop
  nop
  inc $d012     ; At this stage, $d012 has already been incremented by one.
  lda #1
  sta $d019     ; acknowledge the first raster interrupt
  cli           ; enable interrupts (the second interrupt can now occur)
  ldy #9
  dey
  bne *-1       ; delay
  nop           ; The second interrupt will occur while executing these
  nop           ; two-cycle instructions.
  nop
  nop
  nop
oldirq = * + 1  ; Placeholder for self-modifying code
  jmp *         ; Return to the original interrupt

; Main raster interrupt
irq2:
; irq (event)   ; 7 + 2 or 3 cycles of last instruction (9 or 10 total)
; pha           ; 3
; txa           ; 2
; pha           ; 3
; tya           ; 2
; pha           ; 3
; tsx           ; 2
; lda $0104,x   ; 4
; and #xx       ; 2
; beq           ; 3
; jmp (cinv)    ; 5
                ; ---
                ; 38 or 39 cycles delay at this stage
  lda #<irq1
  sta cinv
  lda #>irq1
  sta cinv+1
  ldx $d012
  nop
#if CYCLES - 63
#if CYCLES - 64
  nop           ; 6567R8, 65 cycles/line
  bit $24
#else
  nop           ; 6567R56A, 64 cycles/line
  nop
#endif
#else
  bit $24       ; 6569, 63 cycles/line
#endif
  cpx $d012     ; The comparison cycle is executed CYCLES or CYCLES+1 cycles
                ; after the interrupt has occurred.
  beq *+2       ; Delay by one cycle if $d012 hadn't changed.
                ; Now exactly CYCLES+3 cycles have passed since the interrupt.
  dex
  dex
  stx $d012     ; restore original raster interrupt position
  ldx #1
  stx $d019     ; acknowledge the raster interrupt
  ldx #2
  dex
  bne *-1
  nop
  nop
  lda #20       ; set the amount of raster lines-1 for the loop
  sta m
  ldx #$c8
irqloop:
  ldy #2
  dey
  bne *-1       ; delay
  dec $d016     ; narrow the screen (exact timing required)
;3s4s5s6s7srrrrrgggggggggggggggggggggggggggggggggggggggg--||0s1s2s Phi-1 VIC-II
;ssssssssss                                               ||ssssss Phi-2 VIC-II
;==========xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx||XXX====== Phi-2 6510
;          ^ now we are here
  stx $d016     ; expand the screen
#if CYCLES - 63
#if CYCLES - 64
  bit $24       ; 6567R8
#else
  nop           ; 6567R56A
#endif
#else
  nop           ; 6569
#endif
  dec m
  bmi endirq
  clc
  lda $d011
  sbc $d012
  and #7
  bne irqloop   ; This instruction takes 4 cycles instead of 3,
                ; because the page boundary is crossed.
badline:
  dec m
  nop
  nop
  nop
  nop
  dec $d016
;3s4s5s6s7srrrrrgggggggggggggggggggggggggggggggggggggggg--||0s1s2s Phi-1 VIC-II
;ssssssssss    cccccccccccccccccccccccccccccccccccccccc   ||ssssss Phi-2 VIC-II
;==========xXXX========================================||***====== Phi-2 6510
;          ^ we are here
  stx $d016
;3s4s5s6s7srrrrrgggggggggggggggggggggggggggggggggggggggg--||0s1s2s Phi-1 VIC-II
;ssssssssss                                               ||ssssss Phi-2 VIC-II
;==========xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx||XXX====== Phi-2 6510
;          ^ ^^- we are here (6569)
;          | \- or here (6567R56A)
;          \- or here (6567R8)
  ldy #2
  dey
  bne *-1
  nop
  nop
#if CYCLES - 63
#if CYCLES - 64
  nop           ; 6567R8, 65 cycles/line
  nop
  nop
#else
  bit $24       ; 6567R56A, 64 cycles/line
#endif
#else
  nop           ; 6569, 63 cycles/line
#endif
  dec m
  bpl irqloop   ; This is a 4-cycle branch (page boundary crossed)
endirq:
  jmp $ea81     ; return to the auxiliary raster interrupt

restore:        ; disable the Restore key
  lda cnmi
  ldy cnmi+1
  pha
  lda #<nmi     ; Set the NMI vector
  sta cnmi
  lda #>nmi
  sta cnmi+1
  ldx #$81
  stx $dd0d     ; Enable CIA 2 Timer A interrupt
  ldx #0
  stx $dd05
  inx
  stx $dd04     ; Prepare Timer A to count from 1 to 0.
  ldx #$dd
  stx $dd0e     ; Cause an interrupt.
nmi = * + 1
  lda #$40      ; RTI placeholder
  pla
  sta cnmi
  sty cnmi+1    ; restore original NMI vector (although it won't be used)
  rts


Binaries

Here are the programs in uuencoded format.  First the VIC-20 programs:

Color boxes for the VIC-20, NTSC-M version (probably distorted display):

begin 644 copper.6560
M`1`*$,L'GC0Q,#D```"I?XTND8TMD8T>D:(<[`20T/N@"20DK@20BB0D)"2B
M%<K0_<T$D+``B-#KJ4"-&Y&-*Y&I0Z)"C1:1C2:1H`:(T/TD)(XED:`*B-#]
MCA61J6R-%`.I$(T5`ZG`C2Z18*T4D<D(D`1(:"D'R020!"0D*0/)`K``L`!*
ML`"@$*T/D*I)]XT/D(X/D(T/D(X/D(T/D(X/D(T/D(X/D(T/D(X/D(T/D(X/
,D$AH)"3JB-#43+_J
`
end


Color boxes for the VIC-20, PAL-B version:

begin 644 copper.6561
M`1`*$,L'GC0Q,#D```"I?XTND8TMD8T>D:(<[`20T/N@"20DK@20BB0DHAC*
MT/W-!)"P`(C0[:E`C1N1C2N1J8:B5HT6D8TFD:`'B-#]ZNJ.)9&@"HC0_8X5
MD:EJC10#J1"-%0.IP(TND6"M%)')")`$2&@I!\D$D`0D)"D#R0*P`+``2K``
MH!"M#Y"J2?>-#Y".#Y"-#Y".#Y"-#Y".#Y"-#Y".#Y"-#Y".#Y"-#Y".#Y!(
+:$AHZNJ(T--,O^J.
`
end


Removed sideborders with 8 sprites and bad lines, PAL-B version:

begin 644 raster.63
M`0@*",L'GC(P-C$```!,$PA,=`@@'0FM%`.N%0/)E=`$X`CP$7B-N0B.N@BI
ME:((C10#CA4#J1N-$="I-(T2T*(.&&D#J*D`A?NE^YT`T&D8A?N8G0'0RLH0
M[ZE_C0W<C0W=H@&.&M"M#=Q.&="@_XP5T%A@>*D;C1'0J8&-#=RI`(T:T*VY
M"(T4`ZVZ"(T5`RP-W5A@J;N-%`.I"(T5`^KJZNKJZNX2T*D!C1G06*`)B-#]
MZNKJZNI,N`BIE8T4`ZD(C14#KA+0ZB0D[!+0\`#*RHX2T*(!CAG0H@+*T/WJ
MZJD4A?NBR*`"B-#]SA;0CA;0ZL;[,",8K1'0[1+0*0?0Y<;[ZNKJZLX6T(X6
MT*`"B-#]ZNKJQOL0S4R!ZJT8`ZP9`TBI0HT8`ZD)C1D#HH&.#=VB`(X%W>B.
1!-VBW8X.W:E`:(T8`XP9`V`8
`
end


Removed sideborders with 8 sprites and bad lines, 6567R56A version
(very old NTSC-M C64s):

begin 644 raster.64
M`0@*",L'GC(P-C$```!,$PA,=`@@'@FM%`.N%0/)E=`$X`CP$7B-N0B.N@BI
ME:((C10#CA4#J1N-$="I-(T2T*(.&&D#J*D`A?NE^YT`T&D8A?N8G0'0RLH0
M[ZE_C0W<C0W=H@&.&M"M#=Q.&="@_XP5T%A@>*D;C1'0J8&-#=RI`(T:T*VY
M"(T4`ZVZ"(T5`RP-W5A@J;N-%`.I"(T5`^KJZNKJZNX2T*D!C1G06*`)B-#]
MZNKJZNI,N`BIE8T4`ZD(C14#KA+0ZNKJ[!+0\`#*RHX2T*(!CAG0H@+*T/WJ
MZJD4A?NBR*`"B-#]SA;0CA;0ZL;[,"08K1'0[1+0*0?0Y<;[ZNKJZLX6T(X6
MT*`"B-#]ZNHD),;[$,Q,@>JM&`.L&0-(J4.-&`.I"8T9`Z*!C@W=H@".!=WH
2C@3=HMV.#MVI0&B-&`.,&0-@
`
end

Removed sideborders with 8 sprites and bad lines, 6567R8 and above
(not too old NTSC-M C64s and all C128s)

begin 644 raster.65
M`0@*",L'GC(P-C$```!,$PA,=`@@(0FM%`.N%0/)E=`$X`CP$7B-N0B.N@BI
ME:((C10#CA4#J1N-$="I-(T2T*(.&&D#J*D`A?NE^YT`T&D8A?N8G0'0RLH0
M[ZE_C0W<C0W=H@&.&M"M#=Q.&="@_XP5T%A@>*D;C1'0J8&-#=RI`(T:T*VY
M"(T4`ZVZ"(T5`RP-W5A@J;N-%`.I"(T5`^KJZNKJZNX2T*D!C1G06*`)B-#]
MZNKJZNI,N`BIE8T4`ZD(C14#KA+0ZNHD).P2T/``RLJ.$M"B`8X9T*("RM#]
MZNJI%(7[HLB@`HC0_<X6T(X6T"0DQOLP)1BM$=#M$M`I!]#DQOOJZNKJSA;0
MCA;0H`*(T/WJZNKJZL;[$,I,@>JM&`.L&0-(J4:-&`.I"8T9`Z*!C@W=H@".
5!=WHC@3=HMV.#MVI0&B-&`.,&0-@
`
end


That was all, folks!  I hope you learned something from this article.
Feel free to e-mail me at Marko.Makela@HUT.FI, should anything remain
unclear.

========================================================================
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:

        1) When plotting a vertical line (i.e. big steps in Y direction),
           don't plot the endpoints (i.e. x1,y1 and x2,y2).
        2) 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.

begin 666 cube3d3.2.s
M ' J*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*@TJH*"@H*"@H*"@
MH*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH'-415!(14Z@:E5$1*"@H*"@H*"@
MH*"@H*"@H*"@*@TJH&=%3U)'1:!T05E,3U*@H*"@H*"@H*"@H*"@H*"@*@TJ
MH'-405)4140ZH#<O,3$O.32@H*"@H*"@H*"@H*"@*@TJH&9)3DE32$5$.J W
M+S$Y+SDTH*"@H*"@H*"@H*"@*@TJH%8R+C"@8T]-4$Q%5$5$.J Q,B\Q-R\Y
M-*"@H*"@*@TJH%8S+C"@8T]-4$Q%5$5$.J S+S(P+SDUH*"@H*"@*@TJH%8S
M+C&@8T]-4$Q%5$5$.J V+S$T+SDUH*"@H*"@*@TJH%8S+C*@8T]-4$Q%5$5$
M.J V+S$U+SDUH*"@H*"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@
MH*"@*@TJH'=%3$PLH$E&H$%,3*!'3T53H%=%3$R@5$A)4Z"@*@TJH%!23T=2
M04V@5TE,3*!23U1!5$6@0:!#54)%+J"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@
MH*"@H*"@H*"@H*"@*@TJH%8R+C"@*Z!N15>@04Y$H&E-4%)/5D5$(:"@H*"@
M*@TJH&Y/5Z!7251(H$9!4U1%4J!23U5424Y%4RR@H*"@*@TJH$A)1$1%3J!3
M55)&04-%4RR@1DE,3$5$H*"@H*"@*@TJH$9!0T53+*!!3D2@15A44D&@5$]0
MH%-%0U)%5*"@*@TJH%1%6%2@34534T%'15,AH*"@H*"@H*"@H*"@H*"@*@TJ
MH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH%8S+C"@*Z!F05-4
MH$-(54Y+6:!,24Y%H*"@H*"@*@TJH%)/551)3D4NH*"@H*"@H*"@H*"@H*"@
MH*"@H*"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH%8S
M+C&@*Z!G14Y%4D%,H%!/3%E'3TZ@4$Q/5*"@*@TJH%=)5$B@2$E$1$5.H$9!
M0T53H"AX+5!23T150U0I*@TJH$%.1*!:3T]-H$9%05154D4NH*"@H*"@H*"@
MH*"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH%8S+C*@
M*Z!E;W(M0E5&1D52H$9)3$Q)3D>@H*"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@
MH*"@H*"@H*"@H*"@*@TJH'1(25.@4%)/1U)!3:!)4Z!)3E1%3D1%1*!43Z"@
M*@TJH$%#0T]-4$%.6:!42$6@05)424-,1:!)3J"@H*"@*@TJH&,]:$%#2TE.
M1RR@:E5.+J Y-:!)4U-512Z@H*"@*@TJH&9/4J!$151!24Q3H$].H%1(25.@
M4%)/1U)!32R@*@TJH%)%042@5$A%H$%25$E#3$4AH*"@H*"@H*"@H*"@*@TJ
MH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH'=2251%H%1/H%53
M(:"@H*"@H*"@H*"@H*"@H*"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@
MH*"@H*"@*@TJH&U94T5,1J!72$5.H%E/54Y'H$1)1*"@H*"@H*"@*@TJH$5!
M1T523%F@1E)%455%3E2@H*"@H*"@H*"@H*"@*@TJH&1/0U1/4J!!3D2@<T%)
M3E0LH$%.1*!(14%21*"@*@TJH$=214%4H&%21U5-14Y4H*"@H*"@H*"@H*"@
MH*"@*@TJH*!A0D]55*!)5*!!3D2@04)/550ZH$)55*"@H*"@*@TJH*!%5D52
M34]21:"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH&-!346@3U54H$)9H%1(1:!3
M04U%H&1/3U*@H*"@*@TJH$%3H$E.H&F@5T5.5"Z@H*"@H*"@H*"@H*"@H*"@
M*@TJH*"@H"V@<E5"04E9052@H*"@H*"@H*"@H*"@H*"@*@TJH*"@H*"@H*"@
MH*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH'1(3U5'2*!IH%-014%+H%=)5$B@
M5$A%H*"@H*"@*@TJH%1/3D=515.@3T:@345.H$%.1*!/1J!!3D=,15.@*@TJ
MH$%.1*!(059%H$Y/5*!,3U9%+*!IH$%-H*"@H*"@*@TJH$)%0T]-1:!!4Z!3
M3U5.1$E.1Z!"4D%34RR@3U*@*@TJH$&@5$E.2TQ)3D>@0UE-0D%,+J"@H*"@
MH*"@H*"@*@TJH*"@H"V@,:!C3U))3E1(24%.4Z Q,Z"@H*"@H*"@*@TJH*"@
MH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH' N<RZ@=$A)4Z!705.@
M5U))5%1%3J!54TE.1Z"@*@TJH*"@H*"@;4523$E.H#$R."Z@H*"@H*"@H*"@
MH*"@*@TJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*@T-(&]R9R D
M.# P, T-*J!C3TY35$%.5%,-#6)U9F8Q(&5Q=2 D,S P," [9DE24U2@0TA!
M4D%#5$52H%-%5 UB=69F,B!E<74@)#,X,# @.W-%0T].1*!#2$%204-415*@
M4T54#65O<F)U9B!E<74@)#0P,# @.V5O<BU"549&15(-8G5F9F5R(&5Q=2 D
M83,@.W!215-534%"3%F@5$A%H%1!4$6@5T].)U2@0D6@4E5.3DE.1PUX,2!E
M<74@)&9B(#MP3TE.5%.@1D]2H$1205=)3D>@0:!,24Y%#7DQ(&5Q=2 D9F,@
M.W1(15-%H%I%4D^@4$%'1:!!1$1215-315,->#(@97%U("1F9" [1$].)U2@
M0T].1DQ)0U2@5TE42*!B87-I8PUY,B!E<74@)&9E#6]L9'@@97%U("1F9 UC
M:'5N:R!E<74@)&9E#61X(&5Q=2 D-C<@.W1(25.@25.@4TA!4D5$H%=)5$B@
M=#&@0D5,3U<-9'D@97%U("0V. UT96UP,2!E<74@)&9B(#MO1J!#3U524T4L
MH$-/54Q$H$-/3D9,24-4H%=)5$B@6#$-=&5M<#(@97%U("1F8R [=$5-4$]2
M05)9H%9!4DE!0DQ%4PUZ=&5M<"!E<74@)# R(#MU4T5$H$9/4J!"549&15*@
M4U=!4"Z@H&1/3B=4H%1/54-(+@UZ,2!E<74@)#(R(#MU4T5$H$)9H$U!5$B@
M4D]55$E.10UZ,B!E<74@)#(T(#MD3TXG5*!43U5#2*!42$531:!%251(15(A
M#7HS(&5Q=2 D,C8->C0@97%U("0R. UK(&5Q=2 D8C8@.V-/3E-404Y4H%53
M142@1D]2H$A)1$1%3@T@(" [4U521D%#1:!$151%0U1)3TZ@+:!$3TXG5*!4
M3U5#2 UH:61E(&5Q=2 D8C4@.V%21:!355)&04-%4Z!(241$14X_#69I;&P@
M97%U("0U," [85)%H%=%H%5324Y'H&5O<BU&24Q,/PUA;F=M87@@97%U(#$R
M," [=$A%4D6@05)%H#(J4$DO04Y'34%8H$%.1TQ%4PT-*J!V:6,-#79M8W-B
M(&5Q=2 D9# Q. UB:V=N9"!E<74@)&0P,C -8F]R9&5R(&5Q=2 D9# R,0US
M<W1A<G0@97%U(#$S-#0@.U)/5Z YH$E.H%-#4D5%3J!-14U/4EF@052@,3 R
M- T-#2J@:T523D%,#0UC:')O=70@97%U("1F9F0R#6=E=&EN(&5Q=2 D9F9E
M- T-*J!S3TU%H%9!4DE!0DQ%4PT-9VQO8GAM:6X@/2 D,V8@.W1(15-%H$%2
M1:!54T5$H$E.H$-,14%224Y'H%1(10UG;&]B>&UA>" ]("0T," [1%)!5TE.
M1Z H1TQ/0D%,*:!"549&15(-9VQO8GEM:6X@/2 D-#$-9VQO8GEM87@@/2 D
M-#(-;&]C>&UI;B ]("0U-R [=$A%4T6@05)%H%53142@24Z@0TQ%05))3D>@
M5$A%#6QO8WAM87@@/2 D-3@@.V5O<J H3$]#04PIH$)51D9%4@UL;V-Y;6EN
M(#T@)#4Y#6QO8WEM87@@/2 D-C -<#%X(#T@)#DR(#MT2$531:!!4D6@5$5-
M4$]205)9H%-43U)!1T4-<#%Y(#T@)#DS(#MU4T5$H$E.H%!,3U1424Y'H%1(
M1:!04D]*14-424].#7 Q>B ]("0Y- UP,G@@/2 D.34@.W1(15F@05)%H$A%
M4D6@4T^@5$A!5*!710UP,GD@/2 D.38@.T1/3B=4H$A!5D6@5$^@4D5#04Q#
M54Q!5$6@5$A%32X-<#)Z(#T@)&%E#7 S>" ]("1A9B [=$A%6:!-04M%H$Q)
M1D6@14%362X-<#-Y(#T@)&(P#7 S>B ]("1B,2 [=TA9H$%21:!93U6@3$]/
M2TE.1Z!!5*!-1:!,24M%H%1(050_#7 Q=" ]("1B,B [9$].)U2@64]5H%12
M55-4H$U%/PUP,G0@/2 D8C,-<#-T(#T@)&(T(#MH059)3D>@04Y/5$A%4J!#
M2$E,1*!705-.)U2@35F@241%02X-:6YD97@@/2 D-3$-8V]U;G1P=',@/2 D
M-3(->F]O;2 ]("0W,2 [>D]/3:!&04-43U(-9'-X(#T@)#8Q(#MD<WB@25.@
M5$A%H$E.0U)%345.5*!&3U(-(" @.U)/5$%424Y'H$%23U5.1*!8#61S>2 ]
M("0V,B [<TE-24Q!4J!&3U*@9'-Y+*!D<WH-9'-Z(#T@)#8S#7-X(#T@)#8T
M(#MT2$531:!!4D6@5$A%H$%#5%5!3*!!3D=,15.@24Z@6*!9H$%.1*!:#7-Y
M(#T@)#8U#7-Z(#T@)#8V#70Q(#T@)#8W(#MT2$531:!!4D6@55-%1*!)3J!4
M2$6@4D]4051)3TX-=#(@/2 D-C@-=#,@/2 D-CD@.W-%1:!42$6@05)424-,
M1:!&3U*@34]21:!$151!24Q3#70T(#T@)#9A#70U(#T@)#9B#70V(#T@)#9C
M#70W(#T@)#9D#70X(#T@)#9E#70Y(#T@)#9F#70Q," ]("0W, UA,3$@/2 D
M834@.W1(15-%H$%21:!42$6@14Q%345.5%.@3T:@5$A%H%)/5$%424].H$U!
M5%))6 UB,3(@/2 D838@.WAY>@UC,3,@/2 D83<-9#(Q(#T@)&$X(#MT2$6@
M3E5-0D52H$1%3D]415.@*%)/5RQ#3TQ534XI#64R,B ]("1A.0UF,C,@/2 D
M86$-9S,Q(#T@)&%B#6@S,B ]("1A8PUI,S,@/2 D860-#0TJ*BJ@;4%#4D]3
M#0UM;W9E(&UA8PT@;&1A(%TQ#2!S=&$@73(-(#P\/ T-9V5T:V5Y(&UA8R @
M.W=!252@1D]2H$&@2T594%)%4U,-=V%I="!J<W(@9V5T:6X-(&-M<" C,# -
M(&)E<2!W86ET#2 \/#P-#61E8G5G(&UA8R @.W!224Y4H$&@0TA!4D%#5$52
M#2!D;Z PH* [9$].)U2@05-314U"3$4-#2!L9&&@(UTQ#2!J<W*@8VAR;W5T
M#2!C;&D-(#X^/B!G971K97D@.V%.1*!704E4H%1/H$-/3E1)3E5%#2!C;7 @
M(R=3)R [;5F@4T5#4D5#5*!35TE40TB@2T59#2!B;F4@;#$-(&IS<B!C;&5A
M;G5P#2!J;7 @9&]N90UL,2!C;7 @(R=8)R [;5F@4T5#4D54H$%"3U)4H$M%
M60T@8FYE(&1O;F4-(&IM<"!C;&5A;G5P#2!F:6X-9&]N92 \/#P-#61E8G5G
M82!M86,-(&1OH# -(&QD82!=,0T@<W1A(#$P,C0-(&9I;@UD;VYE82 \/#P-
M#2HM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM#0T@;&1A(",D,# -
M('-T82!B:V=N9 T@<W1A(&)O<F1E<@T@;&1A('9M8W-B#2!A;F0@(R4P,# P
M,3$Q,2 [<T-2145.H$U%34]26:!43Z Q,#(T#2!O<F$@(R4P,# Q,# P, T@
M<W1A('9M8W-B#0T@;&1Y(",P, T@;&1A(",\='1E>'0-('-T82!T96UP,0T@
M;&1A(",^='1E>'0-('-T82!T96UP,@T@:FUP('1I=&QE#71T97AT(&AE>" Y
M,S U,3$Q,3$Q(#M#3$5!4J!30U)%14XLH%=(251%+*!#4E-2H$1.#2!T>'0@
M)Z"@H*"@H*"@H*"@H*!#54)%,T2@5C,N,B<L,$0L,$0-('1X=" GH*"@H*"@
MH*"@H*"@H*"@H*"@0EDG+#!$#2!H97@@.68@.T-904X-('1X=" GH*"@H%-4
M15!(14Z@2E5$1"<-(&AE>" Y.0T@='AT(">@H*"@1T5/4D=%H%1!64Q/4B<L
M,$0L,$0-(&AE>" Y8@T@='AT(">@H$-(14-+H$]55*!42$6@2D%.+J Y-:!)
M4U-51:!/1B<L,$0-(&AE>" Y-@T@='AT(">@H$,]2$%#2TE.1R<-(&AE>" Y
M8@T@='AT(">@1D]2H$U/4D6@1$5404E,4R$G+#!$#2!H97@@,&0Q9#%D.64Q
M,@T@='AT("=&,2]&,B<L.3(-('1X=" GH"V@24Y#+T1%0Z!8+5)/5$%424].
M)RPP1 T@:&5X(#%D,60Q,@T@='AT("=&,R]&-"<L.3(-('1X=" GH"V@24Y#
M+T1%0Z!9+5)/5$%424].)RPP1 T@:&5X(#%D,60Q,@T@='AT("=&-2]&-B<L
M.3(-('1X=" GH"V@24Y#+T1%0Z!:+5)/5$%424].)RPP1 T@:&5X(#%D,60Q
M,@T@='AT(">@1C>@H"<L.3(-('1X=" GH"V@4D53150G+#!$#2!H97@@,60Q
M9#$R#2!T>'0@)Z K+RV@)RPY,@T@='AT(">@+:!:3T]-H$E.+T]55"<L,$0-
M(&AE>" Q9#%D,3(-('1X=" GH*!(H* G+#DR#2!T>'0@)Z MH%1/1T=,1:!(
M241$14Z@4U521D%#15,G+#!$#2!H97@@,60Q9#$R#2!T>'0@)U-004-%)RPY
M,@T@='AT(">@+:!43T='3$6@4U521D%#1:!&24Q,24Y')RPP1"PP1 T@='AT
M(">@H%!215-3H%&@5$^@455)5"<L,$0-(&AE>" P9# U#2!T>'0@)Z"@H*"@
MH%!215-3H$%.6:!+15F@5$^@0D5'24XG+#!$#2!H97@@,# -=&ET;&4@;&1A
M("AT96UP,2DL>0T@8F5Q(#IC;VYT#2!J<W(@8VAR;W5T#2!I;GD-(&)N92!T
M:71L90T@:6YC('1E;7 R#2!J;7 @=&ET;&4-.F-O;G0@/CX^(&=E=&ME>0T-
M*BHJ*J!S152@55"@5$%"3$53*#\I#0TJH'1!0DQ%4Z!!4D6@0U524D5.5$Q9
MH%-%5*!54*!)3J!B87-I8PTJH$%.1*!"6:!42$6@05-314U"3$52+@T-=&%B
M;&5S(&QD82 C/G1M871H,0T@<W1A('HQ*S$-('-T82!Z,BLQ#2!L9&$@(SYT
M;6%T:#(-('-T82!Z,RLQ#2!S=&$@>C0K,0T-*BHJ*J!C3$5!4J!30U)%14Z@
M04Y$H%-%5*!54* B0DE434%0(@US971U<"!L9&$@(R0P,2 [=TA)5$4-('-T
M82 D9# R,2 [=$A)4Z!)4Z!$3TY%H%-/H%1(052@3TQ$15(-(&QD82 C,30W
M(#M-04-(24Y%4Z!724Q,H%-%5*!54 T@:G-R(&-H<F]U= T@;&1A(",D,# @
M.T-/4E)%0U1,60T@<W1A("1D,#(Q#2!L9&$@(SQS<W1A<G0-(&%D8R C,3(@
M.W1(1:!'3T%,H$E3H%1/H$-%3E1%4J!42$6@1U)!4$A)0U,-('-T82!T96UP
M,2 [8T],54U.H#$R#2!L9&$@(SYS<W1A<G0@.W)/5Z Y#2!S=&$@=&5M<#$K
M,2 [<W-T87)TH%!/24Y44Z!43Z!23U>@.0T@;&1A(",P, T@;&1Y(",P, T@
M;&1X(",P," [6*!724Q,H$-/54Y4H#$VH%)/5U.@1D]2H%53#2!C;&,-#3IL
M;V]P('-T82 H=&5M<#$I+'D-(&EN>0T@861C(",Q-@T@8F-C(#IL;V]P#2!C
M;&,-(&QD82!T96UP,0T@861C(",T," [;D5%1*!43Z!!1$2@-#"@5$^@5$A%
MH$)!4T6@4$])3E1%4@T@<W1A('1E;7 Q(#MT3Z!*54U0H%1/H%1(1:!.15A4
MH%)/5PT@;&1A('1E;7 Q*S$-(&%D8R C,# @.W1!2T6@0T%21:!/1J!#05)2
M2453#2!S=&$@=&5M<#$K,0T@;&1Y(",P, T@:6YX#2!T>&$@(#MXH$E3H$%,
M4T^@04Z@24Y$15B@24Y43Z!42$6@0TA!4D%#5$52H$Y534)%4@T@8W!X(",Q
M-@T@8FYE(#IL;V]P(#MN145$H%1/H$1/H$E4H#$VH%1)3453#0TJ*BHJH&-,
M14%2H$)51D9%4E,-#2!L9&$@(SQB=69F,0T@<W1A(&)U9F9E<@T@;&1A(",^
M8G5F9C$-('-T82!B=69F97(K,0T@;&1Y(",D,# -(&QD>" C,C0@.V%34U5-
M24Y'H$%,3*!42%)%1:!"549&15)3H$%210T@;&1A(",D,# @.T)!0TLM5$\M
M0D%#2PTZ8FQO;W @<W1A("AB=69F97(I+'D-(&EN>0T@8FYE(#IB;&]O< T@
M:6YC(&)U9F9E<BLQ#2!D97@-(&)N92 Z8FQO;W -#2HJ*BJ@<T54H%50H$)5
M1D9%4E,-#2!L9&$@(SQB=69F,0T@<W1A(&)U9F9E<@T@;&1A(",^8G5F9C$-
M('-T82!B=69F97(K,0T@<W1A('IT96UP(#M:5$5-4*!724Q,H$U!2T6@3$E&
M1:!324U03$6@1D]2H%53#2!L9&$@=FUC<V(-(&%N9" C)3$Q,3$P,# Q(#MS
M5$%25*!(15)%H%-/H%1(052@4U=!4*!"549&15)3H%=)3$R@5T]22Z!224=(
M5 T@;W)A(",E,# P,#$Q,3 -('-T82!V;6-S8@T-*BHJ*J!S152@55"@24Y)
M5$E!3*!604Q515,-#6EN:70@;&1A(",P, T@<W1A(&QO8WAM:6X-('-T82!L
M;V-X;6%X#2!S=&$@;&]C>6UI;@T@<W1A(&QO8WEM87@-('-T82!G;&]B>&UI
M;@T@<W1A(&=L;V)Y;6EN#2!S=&$@9VQO8GAM87@-('-T82!G;&]B>6UA> T@
M<W1A(&1S> T@<W1A(&1S>0T@<W1A(&1S>@T@<W1A('-X#2!S=&$@<WD-('-T
M82!S>@T@<W1A(&9I;&P-(&QD82 C,#$-('-T82!H:61E#2!L9&$@(S8T#2!S
M=&$@>F]O;0T-*BTM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2T-*J!M
M04E.H$Q/3U -#2HJ*BJ@9T54H$M%65!215-3#0UM86EN#2!C;&D-:W!R97-S
M(&IS<B!G971I;@T@8VUP(",Q,S,@.V8Q/PT@8FYE(#IF,@T@;&1A(&1S> T@
M8VUP("-A;F=M87@O,B [;D^@34]21:!42$%.H%!)#2!B97$@.F-O;G0Q#2!I
M;F,@9'-X(#M/5$A%4E=)4T6@24Y#4D5!4T6@6"U23U1!5$E/3@T@:FUP(#IC
M;VYT#3IF,B!C;7 @(S$S-R [9C(_#2!B;F4@.F8S#2!L9&$@9'-X#2!B97$@
M.F-O;G0Q#2!D96,@9'-X#2!J;7 @.F-O;G0-.F8S(&-M<" C,3,T#2!B;F4@
M.F8T#2!L9&$@9'-Y#2!C;7 @(V%N9VUA>"\R#2!B97$@.F-O;G0Q#2!I;F,@
M9'-Y(#MI3D-214%31:!9+5)/5$%424].#2!J;7 @.F-O;G0-.F8T(&-M<" C
M,3,X#2!B;F4@.F8U#2!L9&$@9'-Y#2!B97$@.F-O;G0Q#2!D96,@9'-Y#2!J
M;7 @.F-O;G0-.F8U(&-M<" C,3,U#2!B;F4@.F8V#2!L9&$@9'-Z#2!C;7 @
M(V%N9VUA>"\R#2!B97$@.F-O;G0Q#2!I;F,@9'-Z(#M:+5)/5$%424].#2!J
M;7 @.F-O;G0-.F8V(&-M<" C,3,Y#2!B;F4@.F8W#2!L9&$@9'-Z#2!B97$@
M.F-O;G0Q#2!D96,@9'-Z#2!J;7 @.F-O;G0-.F8W(&-M<" C,3,V#2!B;F4@
M.G!L=7,-(&IM<"!I;FET#3IC;VYT,2!J;7 @.F-O;G0-.G!L=7,@8VUP(",G
M*R<-(&)N92 Z;6EN=7,-(&EN8R!Z;V]M(#MB04@LH%=(3Z!.145$4Z!%4E)/
M4J!#2$5#2TE.1S\-(&EN8R!Z;V]M#2!J;7 @.F-O;G0-.FUI;G5S(&-M<" C
M)RTG#2!B;F4@.F@-(&1E8R!Z;V]M#2!D96,@>F]O;0T@8G!L(#IC;VYT#2!I
M;F,@>F]O;0T@:6YC('IO;VT-(&IM<" Z8V]N= TZ:"!C;7 @(R=()PT@8FYE
M(#IS<&%C90T@;&1A(&AI9&4-(&5O<B C)# Q#2!S=&$@:&ED90T@:FUP(#IC
M;VYT#3IS<&%C92!C;7 @(R>@)PT@8FYE(#IQ#2!L9&$@9FEL; T@96]R(",D
M,#$-('-T82!F:6QL#2!J;7 @.F-O;G0-.G$@8VUP(",G42<@.U&@455)5%,-
M(&)N92 Z8V]N= T@:FUP(&-L96%N=7 -#3IC;VYT('-E:2 @.W-0145$H%1(
M24Y'4Z!54*!!H$))5 T-*BHJ*J!U4$1!5$6@04Y'3$53#0UU<&1A=&4@8VQC
M#2!L9&$@<W@-(&%D8R!D<W@-(&-M<" C86YG;6%X(#MA4D6@5T6@/CV@34%8
M24U53:!!3D=,13\-(&)C8R Z8V]N=#$-('-B8R C86YG;6%X(#II1B!33RP@
M4D53150-.F-O;G0Q('-T82!S> T@8VQC#2!L9&$@<WD-(&%D8R!D<WD-(&-M
M<" C86YG;6%X#2!B8V,@.F-O;G0R#2!S8F,@(V%N9VUA>" [<T%-1:!$14%,
M#3IC;VYT,B!S=&$@<WD-(&-L8PT@;&1A('-Z#2!A9&,@9'-Z#2!C;7 @(V%N
M9VUA> T@8F-C(#IC;VYT,PT@<V)C("-A;F=M87@-.F-O;G0S('-T82!S>@T-
M*BHJ*J!R3U1!5$6@0T]/4D1)3D%415,-#7)O=&%T90T-*BHJH&9)4E-4+*!#
M04Q#54Q!5$6@5#$L5#(L+BXN+%0Q, T-*BJ@=%=/H$U!0U)/4Z!43Z!324U0
M3$E&6:!/55*@3$E&10UA9&1A(&UA8R @.V%$1*!45T^@04Y'3$53H%1/1T54
M2$52#2!C;&,-(&QD82!=,0T@861C(%TR#2!C;7 @(V%N9VUA>" [:5.@5$A%
MH%-53: ^H#(J4$D_#2!B8V,@9&]N90T@<V)C("-A;F=M87@@.VE&H%-/+*!3
M54)44D%#5* R*E!)#61O;F4@/#P\#0US=6)A(&UA8R @.W-50E1204-4H%17
M3Z!!3D=,15,-('-E8PT@;&1A(%TQ#2!S8F,@73(-(&)C<R!D;VYE#2!A9&,@
M(V%N9VUA>" [;T]04RR@5T6@3D5%1*!43Z!!1$2@,BI020UD;VYE(#P\/ T-
M*BJ@;D]7H$-!3$-53$%41:!4,2Q4,BQ%5$,N#0T@/CX^('-U8F$L<WD[<WH-
M('-T82!T,2 [5#$]4UDM4UH-(#X^/B!A9&1A+'-Y.W-Z#2!S=&$@=#(@.U0R
M/5-9*U-:#2 ^/CX@861D82QS>#MS>@T@<W1A('0S(#M4,SU36"M36@T@/CX^
M('-U8F$L<W@[<WH-('-T82!T-" [5#0]4U@M4UH-(#X^/B!A9&1A+'-X.W0R
M#2!S=&$@=#4@.U0U/5-8*U0R#2 ^/CX@<W5B82QS>#MT,0T@<W1A('0V(#M4
M-CU36"U4,0T@/CX^(&%D9&$L<W@[=#$-('-T82!T-R [5#<]4U@K5#$-(#X^
M/B!S=6)A+'0R.W-X#2!S=&$@=#@@.U0X/50R+5-8#2 ^/CX@<W5B82QS>3MS
M> T@<W1A('0Y(#M4.3U362U36 T@/CX^(&%D9&$L<W@[<WD-('-T82!T,3 @
M.U0Q,#U36"M360T-*J!E5*!63TE,02$-#2HJ*J!N15A4+*!#04Q#54Q!5$6@
M82QB+&,L+BXN+&D-#2HJH&%.3U1(15*@55-%1E5,H$Q)5%1,1:!-04-23PUD
M:78R(&UA8R @.V1)5DE$1:!!H%-)1TY%1*!.54U"15*@0EF@,@T[:52@25.@
M05-354U%1*!42$%4H%1(1:!.54U"15(-(&)P;"!P;W,@.TE3H$E.H%1(1:!!
M0T-5355,051/4@T@8VQC#2!E;W(@(R1F9B [=T6@3D5%1*!43Z!53BU.14=!
M5$E61:!42$6@3E5-0D52#2!A9&,@(S Q(#M"6:!404M)3D>@250G4Z!#3TU0
M3$5-14Y4#2!L<W(@(#M$259)1$6@0EF@5%=/#2!C;&,-(&5O<B C)&9F#2!A
M9&,@(S Q(#MM04M%H$E4H$Y%1T%4259%H$%'04E.#2!J;7 @9&]N961I=@UP
M;W,@;'-R(" [;E5-0D52H$E3H%!/4TE4259%#61O;F5D:78@/#P\#0UM=6PR
M(&UA8R @.VU53%1)4$Q9H$&@4TE'3D5$H$Y534)%4J!"6: R#2!B<&P@<&]S
M;0T@8VQC#2!E;W(@(R1F9@T@861C(",D,#$-(&%S; T@8VQC#2!E;W(@(R1F
M9@T@861C(",D,#$-(&IM<"!D;VYE;75L#7!O<VT@87-L#61O;F5M=6P@/#P\
M#0TJ*J!N3U1%H%1(052@5T6@05)%H$-54E)%3E1,6:!-04M)3D>@0:!-24Y/
M4J!,14%0#2HJH$]&H$9!251(H%1(052@3D^@3U9%4D9,3U=3H%=)3$R@3T-#
M55(N#0TZ8V%L8V$@8VQC#2!L9'@@=#$-(&QD82!C;W,L> T@;&1X('0R#2!A
M9&,@8V]S+'@-('-T82!A,3$@.V$]*$-/4RA4,2DK0T]3*%0R*2DO,@TZ8V%L
M8V(@;&1X('0Q#2!L9&$@<VEN+'@-('-E8PT@;&1X('0R#2!S8F,@<VEN+'@-
M('-T82!B,3(@.V(]*%-)3BA4,2DM4TE.*%0R*2DO,@TZ8V%L8V,@;&1X('-Y
M#2!L9&$@<VEN+'@-(#X^/B!M=6PR#2!S=&$@8S$S(#MC/5-)3BA362D-.F-A
M;&-D('-E8PT@;&1X('0X#2!L9&$@8V]S+'@-(&QD>"!T-PT@<V)C(&-O<RQX
M#2!S96,-(&QD>"!T-0T@<V)C(&-O<RQX#2!C;&,-(&QD>"!T-@T@861C(&-O
M<RQX(#MD23TH0T]3*%0X*2U#3U,H5#<I*T-/4RA4-BDM0T]3*%0U*2DO,@T@
M/CX^(&1I=C(-(&-L8PT@;&1X('0S#2!A9&,@<VEN+'@-('-E8PT@;&1X('0T
M#2!S8F,@<VEN+'@-('-T82!D,C$@.V0]*%-)3BA4,RDM4TE.*%0T*2MD22DO
M,@TZ8V%L8V4@<V5C#2!L9'@@=#4-(&QD82!S:6XL> T@;&1X('0V#2!S8F,@
M<VEN+'@-('-E8PT@;&1X('0W#2!S8F,@<VEN+'@-('-E8PT@;&1X('0X#2!S
M8F,@<VEN+'@@.V5)/2A324XH5#4I+5-)3BA4-BDM4TE.*%0W*2U324XH5#@I
M*2\R#2 ^/CX@9&EV,@T@8VQC#2!L9'@@=#,-(&%D8R!C;W,L> T@8VQC#2!L
M9'@@=#0-(&%D8R!C;W,L> T@<W1A(&4R,B [93TH0T]3*%0S*2M#3U,H5#0I
M*V5)*2\R#3IC86QC9B!L9'@@=#D-(&QD82!S:6XL> T@<V5C#2!L9'@@=#$P
M#2!S8F,@<VEN+'@-('-T82!F,C,@.V8]*%-)3BA4.2DM4TE.*%0Q,"DI+S(-
M.F-A;&-G(&QD>"!T-@T@;&1A('-I;BQX#2!S96,-(&QD>"!T. T@<V)C('-I
M;BQX#2!S96,-(&QD>"!T-PT@<V)C('-I;BQX#2!S96,-(&QD>"!T-0T@<V)C
M('-I;BQX(#MG23TH4TE.*%0V*2U324XH5#@I+5-)3BA4-RDM4TE.*%0U*2DO
M,@T@/CX^(&1I=C(-(&-L8PT@;&1X('0T#2!A9&,@8V]S+'@-('-E8PT@;&1X
M('0S#2!S8F,@8V]S+'@-('-T82!G,S$@.V<]*$-/4RA4-"DM0T]3*%0S*2MG
M22DO,@TZ8V%L8V@@8VQC#2!L9'@@=#8-(&QD82!C;W,L> T@;&1X('0W#2!A
M9&,@8V]S+'@-('-E8PT@;&1X('0U#2!S8F,@8V]S+'@-('-E8PT@;&1X('0X
M#2!S8F,@8V]S+'@@.VA)/2A#3U,H5#8I*T-/4RA4-RDM0T]3*%0U*2U#3U,H
M5#@I*2\R#2 ^/CX@9&EV,@T@8VQC#2!L9'@@=#,-(&%D8R!S:6XL> T@8VQC
M#2!L9'@@=#0-(&%D8R!S:6XL> T@<W1A(&@S,B [:#TH4TE.*%0S*2M324XH
M5#0I*VA)*2\R#3IW:&5W(&-L8PT@;&1X('0Y#2!L9&$@8V]S+'@-(&QD>"!T
M,3 -(&%D8R!C;W,L> T@<W1A(&DS,R [:3TH0T]3*%0Y*2M#3U,H5#$P*2DO
M,@T-*BJ@:50G4Z!!3$R@1$]73DA)3$R@1E)/3:!(15)%+@T-9&]W;FAI;&P-
M*BHJ*J!C3$5!4J!"549&15(-*J!AH$Q)5%1,1:!-04-23PT-<V5T8G5F(&UA
M8R @.W!55*!"549&15)3H%=(15)%H%1(15F@0T%.H$)%H$A54E0-(&QD82 C
M,# -('-T82!B=69F97(-(&QD82!Z=&5M<" [:$E'2*!"651%#7-T86)U9B!S
M=&$@8G5F9F5R*S$-(#P\/ T-(#X^/B!S971B=68-8VQR9')A=R!L9'@@(S X
M#2!L9&$@(S P#3IF;V]L(&QD>2 C,# -.F1O<&4@<W1A("AB=69F97(I+'D-
M(&EN>0T@8FYE(#ID;W!E#2!I;F,@8G5F9F5R*S$-(&1E> T@8FYE(#IF;V]L
M#0TJ*BHJH&U9H$=/3T1.15-3H$)55*!I)TV@0:!$3U!%#2IC;')D<F%WH&QD
M8:!G;&]B>&UI;@TJH&QS<J"@.VY%142@5$^@1T54H$E.5$^@5$A%H%))1TA4
MH$-/3%5-3@TJH&)C8Z Z979E;J [95A03$%)3D5$H$E.H$U/4D6@1$5404E,
MH$)%3$]7#2J@;&1YH",D.# -*J!S='F@8G5F9F5RH#MP4D5354U!0DQ9H%1(
M25.@5TE,3*!"1:!!H$Q)5%1,10TJH&-L8Z"@.TU/4D6@149&24-)14Y4+@TJ
M.F5V96Z@861CH&)U9F9E<BLQ#2J@<W1AH&)U9F9E<BLQ#2J@;&1AH&=L;V)X
M;6%X#2J@<V5C#2J@<V)CH&=L;V)X;6EN#2J@=&%X#2J@:6YX#2J@;&1YH&=L
M;V)Y;6%X#2J@8F5QH#IR97-E= TJ.GEA>:!L9&&@(R0P, TJH&QD>:!G;&]B
M>6UA> TJ.F)L86B@<W1AH"AB=69F97(I+'D-*J!D97D-*J!C<'F@9VQO8GEM
M:6X-*J!B8W.@.F)L86@-*J!L9&&@8G5F9F5R#2J@96]RH",D.# -*J!S=&&@
M8G5F9F5R#2J@8FYEH#IW:&]P964-*J!I;F.@8G5F9F5R*S$-*CIW:&]P966@
M9&5X#2J@8FYEH#IY87D-*CIR97-E=*!L9&&@(S"@.VY%142@5$^@4D53152@
M5$A%4T6@1U594PTJH'-T8:!G;&]B>&UA> TJH'-T8:!G;&]B>6UA> TJH&QD
M8: C)&9F#2J@<W1AH&=L;V)X;6EN#2J@<W1AH&=L;V)Y;6EN#0TJ*BHJH&Y%
M6%0LH%)%042@04Y$H$1205>@4$],64=/3E,-#7)E861D<F%W(&QD>2 C,# -
M('-T>2!I;F1E> UO8FIL;V]P(&QD>2!I;F1E> T@;&1A('!O;'EL:7-T+'D@
M.V9)4E-4+*!42$6@3E5-0D52H$]&H%!/24Y44PT@8FYE(#IC;VYT(#MB552@
M24:@3E5-4$])3E13H$E3H%I%4D^@5$A%3@T@:FUP(&]B:F1O;F4@.U=%H$%2
M1:!!5*!42$6@14Y$H$]&H%1(1:!,25-4#3IC;VYT('-T82!C;W5N='!T<PT@
M:6YC(&EN9&5X#0TJH')/5$%41:!04D]*14-4H$%.1*!$4D%7H%1(1:!03TQ9
M1T].#2J@;4%+1:!355)%H$)51D9%4J!"14E.1Z!$4D%73J!43Z!)4Z!#3$5!
M4B$-#3ID;VET(&IS<B!R;W1P<F]J#0TJH&-/3E9%4E2@6$U)3J!!3D2@6$U!
M6*!43Z!#3TQ534Y3#0T@;&1