
        __  __             ______
       |  ||__|  ______   /  __  \   ______   __  __   ______    ____   __  __
   ____|  | __  /  __  \ |  |  |__|_/  __  \ |  ||  | /  __  \  /    \ |  ||  |
  /  __ / ||  ||  |__|__||  | |____/  |  |  ||  ||  ||  |__|  ||  ||__||  ||  |
 |  |  |  ||  |`\____  \ |  | |____\  |  |  ||  ||  ||   _____||  |  W |  ||  |
 |  |__|  ||  ||  |__|  ||  |__|  ||  |__|  ||  ||  ||  |__|  ||  |  a |  ||  |
 `\_______||__|`\______/'`\______/'`\______/'`\____/'`\______/'| /'  D `\    /'
          _     _  _ __ ___ ____ ______________________________|/________|  |
           _     _  _ __ ___ ____ _________________________________________/'


             The   Journal   of   the   Commodore   Enthusiast


                      I s s u e  3  :  March 26, 1997


                             P R E A M B L E


Welcome to the third issue of disC=overy, the Journal of the Commodore
Enthusiast.  We greet you proudly, the ones who still hold the beloved
Commodore 8-bit machines in high regard and respect.  We thank you from
the bottom of our hearts and look forward to forging a solid productive
relationship with the C= 8-bit community.

  - Mike Gordillo, Steven Judd, Ernest Stokes, George Taylor, and the
    authors of disC=overy.



:::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::3::::::::::::::::::::
::::::::::::::::::::::T A B L E  O F  C O N T E N T S:::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 


-Software Section-

/S01 - "Rommaging around : $A480-$A856"
$a480   by Stephen L. Judd

/S02 - "A Closer Look at the VIC II's Output"
$d000   by Adrian Gonzalez and George Taylor

/S03 - "Innovation in the 90s : Revisiting The Super Hi-Res Flexible Line
$d000    Interpretation Technique"
        by Roland Toegel, 'Count Zero', and George Taylor

/S04 - "Defeating Digital Corrosion (AKA "Cracking") - A beginner's guide
$dd00    to software archiving"
        by Jan Lund Thomsen

/S05 - "A possibility to be explored : Real Time Video with the Ram Expansion
$df00    Unit"
        by Nate Dannenberg

/S06 - "A look into 'The Fridge'"
$f00d   by Stephen L. Judd

/S07 - "C128 CP/M, trailblazer in a jungle of formats"
0100h   by Mike Gordillo


-Hardware Section-

/H01 - "The X-10 Powerhouse, What is it?"
        by Dan Barber

/H02 - "The Metal Shop"
        with 'XmikeX', David Wood, Marc-Jano Knopp and Daniel Krug


-Legal Section-

/L01 - Articles of Operation, Distribution, et al.



:::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::3::::::::::::::::::::
/S01::$A480:::::::::::::::::::S O F T W A R E:::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


Rommaging around : $A480-$A856
----------------              by
                                Stephen L. Judd  (sjudd@nwu.edu)
                                
"Disassemble? ...Disassemble!!!" -- No. 5, 'Short Circuit'

        This series has a simple goal: to completely disassemble and document
the Commodore 64 ROMs.  There is a nice ROM disassembly available on the
internet, with the actual hex code displayed and HTML hypertexted.  This
version, on the other hand, is written in a more 'human readable' form,
heavily commented and labeled, and is intended to complement the other.
To that end, it does not duplicate very much information which is easily
obtainable elsewhere (ftp.funet.fi, or Marko Makela's homepage, for instance).

        BLARG is a program which adds several hires graphics commands to BASIC,
and was the main motivation to finally get started on this project.  This
article will thus focus on the BASIC routines from $A480 (Main Loop) to 
$A856 (END), with a goal of understanding how to add new commands to BASIC.

        The first part of the article gives an overview of BASIC and its
internal workings, and sets up some of the things used later on.  The
second part discusses the parts of BASIC which relate specifically to
the disassembled ROM, with a focus on the vectored routines.  The third
part gives a brief overview of BLARG.  The ROM source, BLARG source,
and BLARG binaires are all included.  These files are all available at

                http://stratus.esam.nwu.edu/~judd/fridge

        About the ROM listings: I did them up in Merlinesque format of
course.  Probably the only unfamiliar thing is the concept of global
and local lables.  Local labels are prefixed with a colon, and their
definition is only valid between two global lables.  Otherwise they
may be redefined.  Thus a piece of code like

PROC1   BNE :CONT
        INC TEMP1
:CONT   RTS

PROC2   BNE :CONT
        INY
:CONT   DEC TEMP1
        RTS

contains two global labels (PROC1 and PROC2).  PROC1 will branch to the RTS
instruction and PROC2 will branch to the DEC TEMP1 instruction, since the
:CONT label is redefined once the global label PROC2 appears.  The bottom line
is that if you see a branch to a local label, that label is nearby and
after the last global label.

BASIC overview
--------------

        Before starting, there are some important things to know about
BASIC.  When you type in a line of text, and hit [RETURN], that text
is entered into a text buffer located at $0200.  The text from this buffer
is then tokenized by the BASIC interpreter.  If it is an immediate mode
command (doesn't start with a line number) that command is then executed,
otherwise it is stored in memory as a BASIC program line.
        BASIC lines are stored in memory as follows:

      ______________________________   _______________________  ...__
     /                              \ /                       \      \
0 [link] [line #] [Basic line] [0] [link] [line #] [Basic] [0]  ...  [0] [0]
|    |       |          |       |     | 
|    |       |          |       |    Link to next line
|    |       |          |       | 
|    |       |          |      End of line marker
|    |       |          |
|    |       |     Tokenized line of basic program
|    |       |
|    |     Two byte (lo,hi) line number
|    |
|  Two byte (lo,hi) pointer to next line in program
|
Beginning of program

        A zero marks the beginning of the program, which normally begins
at $0800 (2048).  The next two bytes are the address in (lo,hi) form of
the next line in the program.  The following two bytes are the line
number for the current line.  Then the tokenized line of BASIC follows;
a null byte marks the end of the line.  The next line follows immediately.
A line link of value 0 (actually only the high byte needs to be 0) marks
the end of the program.
        Thus a program like

        10 PRINT "HELLO"

will in memory look like

2048    0               ;Zero byte at beginning
2049    15              ;Next link = $080F = 15+8*256 = 2063
2050    8
2051    10              ;Line number = 10 + 0*256 = 10
2052    0
2053    153             ;PRINT token
2054    32              ;space
2055    34      "       ;quote
2056    72      H       ;PETSCII string
2057    69      E
2058    76      L
2059    76      L
2060    79      O
2061    34      "
2062    0               ;end of line
2063    0               ;end of program
2064    0

        Variables begin immediately following the end of the program.
Strings are stored beginning at the top of memory ($9FFF) and work
their way downwards towards the variables.

        Another very important feature of BASIC is that the important
routines are vectored.  These are indeed filled vectors -- filled with
the address of the routines in question.  The BASIC indirect vector
table is located at $0300-$030B:

IERROR  $0300-$0301  Vector to print BASIC error message routine
IMAIN   $0302-$0303  Vector to main BASIC program loop
ICRNCH  $0304-$0305  Vector to CRUNCH routine (tokenize ASCII text)
IQPLOP  $0306-$0307  Vector to QPLOP routine (tokens -> ASCII)
IGONE   $0308-$0309  Vector to GONE, executes BASIC tokens
IEVAL   $030A-$030B  Vector to routine which evaluates single-term math
                     expression

Calls to these routines are vectored through these addresses via an
indirect JMP -- they are called using JMP (IMAIN) for instance.  These
vectors may then be redirected to new routines, and so they provide
a smooth way of adding new keywords to BASIC.  CRUNCH is used to
tokenize lines of text when they are entered.  QPLOP is used by
LIST to print tokens to ASCII text.  GONE is used to execute tokens.
        Before modifying these vectors, keep in mind that many programs,
such as JiffyDOS, have already modified them!

        Finally, the routines CHRGET and CHRGOT are very important
in BASIC.  They are copied into zero page when the system first starts
up, and are located at $0073:

$73     CHRGET  INC TXTPTR      ;Increment low byte
$75             BNE CHRGOT
$77             INC TXTPTR+1    ;High byte if necessary
$79     CHRGOT  LDA             ;Entry here doesn't increment TXTPTR
$7A     TXTPTR  $0207           ;low byte, high byte -- the LDA operand.
                                ;Generally points at BASIC or points
                                ;at input buffer at $0200 when in
                                ;immediate mode.
$7C     POINTB  CMP #$3A        ;Set carry if > ASCII 9
$7E             BCS EXIT        ;Exit if not a numeral
$80             CMP #$20        ;Check for ASCII space
$82             BEQ CHRGET      ;...skip space and move to next char
$84             SEC
$85             SBC #$30        ;Digits 0-9 are ASCII $30-$39
$87             SEC
$88             SBC #$D0        ;Carry set if < ASCII 0 ($30)
$8A     EXIT    RTS

On exit, the accumulator contains the character that was read; Carry clear
means character is ASCII digit 0-9, Carry set otherwise; Zero set if
character is a statement terminator 0 or an ASCII colon ($3A), otherwise
zero clear.
        Wedge programs make this their entry point -- by redirecting
CHRGET and CHRGOT a program can look at the input and act accordingly.
Scanning text can get awfully slow, which is why most wedge programs you
see use a single character to prefix wedge commands.
        Finally, a list of BASIC tokens is a handy thing to have, along
with the address of the routine which executes the statement:

$80     END     43057 $A831
$81     FOR     42818 $A742
$82     NEXT    44318 $AD1E
$83     DATA    43256 $A8F8
$84     INPUT#  43941 $ABA5
$85     INPUT   43967 $ABBF
$86     DIM     45185 $B081
$87     READ    44038 $AC06
$88     LET     43429 $A9A5
$89     GOTO    43168 $A8A0
$8A     RUN     43121 $A871
$8B     IF      43304 $A928
$8C     RESTORE 43037 $A81D
$8D     GOSUB   43139 $A883
$8E     RETURN  43218 $A8D2
$8F     REM     43323 $A93B
$90     STOP    43055 $A82F
$91     ON      43339 $A94B
$92     WAIT    47149 $B82D
$93     LOAD    57704 $E168
$94     SAVE    57686 $E156
$95     VERIFY  57701 $E165
$96     DEF     46002 $B3B3
$97     POKE    47140 $B824
$98     PRINT#  43648 $AA80
$99     PRINT   43680 $AAA0
$9A     CONT    43095 $A857
$9B     LIST    42652 $A69C
$9C     CLR     42590 $A65E
$9D     CMD     43654 $AA86
$9E     SYS     57642 $E12A
$9F     OPEN    57790 $E1BE
$A0     CLOSE   57799 $E1C7
$A1     GET     43899 $AB7B
$A2     NEW     42562 $A642

$A3     TAB(                    ;Keywords which never begin a statement
$A4     TO
$A5     FN
$A6     SPC(
$A7     THEN
$A8     NOT
$A9     STEP

$AA     +       47210 $B86A     ;Math operators
$AB     -       47187 $B853
$AC     *       47659 $BA2B
$AD     /       47890 $BB12
$AE     ^       49019 $BF7B
$AF     AND     45033 $AFE9
$B0     OR      45030 $AFE6
$B1     >       49076 $BFB4
$B2     =       44756 $AED4
$B3     <       45078 $B016

$B4     SGN     48185 $BC39     ;Functions
$B5     INT     48332 $BCCC
$B6     ABS     48216 $BC58
$B7     USR     784   $0310
$B8     FRE     45949 $B37D
$B9     POS     45982 $B39E
$BA     SQR     49009 $BF71
$BB     RND     57495 $E097
$BC     LOG     47594 $B9EA
$BD     EXP     49133 $BFED
$BE     COS     57956 $E264
$BF     SIN     57963 $E26B
$C0     TAN     58036 $E2B4
$C1     ATN     58126 $E30E
$C2     PEEK    47117 $B80D
$C3     LEN     46972 $B77C
$C4     STR$    46181 $B465
$C5     VAL     47021 $B7AD
$C6     ASC     46987 $B78B
$C7     CHR$    46828 $B6EC
$C8     LEFT$   46848 $B700
$C9     RIGHT$  46892 $B72C
$CA     MID$    46903 $B737

$CB     GO                      ;Makes GO TO legal.

BASIC ROM
---------
        
        The BASIC ROM begins at $A000:

$A000-$A001     Cold start vector
$A002-$A003     Warm start vector
$A004-$A00B     ASCII text "CBMBASIC"

$A00C-$A051     Statement dispatch table

        This is the first of several important tables used by BASIC.
When a new BASIC statement is to be executed the interpreter looks
here to find the address of the statement routine.  Each entry is
a two-byte vector containting the routine address minus one.  The
address is pushed onto the stack, so that the next RTS will jump
to the correct location.  The addresses are in token order, beginning
with token $80 (END) and ending with token $A2 (NEW):

$A052-$A07F     Function dispatch vector table

        This is another table of two-byte vectors for BASIC functions --
that is, commands which are followed by an argument inside of parenthesis,
for example INT(3.14159).  The entries are again in token order beginning
with token $B4 (SGN) and ending with token $CA (MID$).

$A080-$A09D     Operator dispatch vector table

        This table is for math operators, beginning with token $AA (+)
and ending with token $B3 (<).

$A09E-$A19D     List of keywords

        This is a table of all the reserved BASIC keywords.  The
high bit of the last character set, so it is easy to detect the end
of a keyword.  The keywords are listed in token order, so to find
the token corresponding to a given keyword the BASIC interpreter
simply moves down this list, counting as it goes.  If a match
occurs, then the token value is simply the counter value.
        The table is searched using SBC instead of CMP.  If the
result of the subtraction is $80 -- keywords end with the high bit set --
then a valid keyword has been found.  When input is placed into the
input buffer at $0200, character codes 192-223 are used for shifted
characters.  From this it should be clear why the keyboard shortcuts work,
for instance typing pO instead of poke.  It should also be clear why poK will
also work, but pokE will not work.

$A19E-$A327     ASCII text of BASIC error messages (dextral character inverted)
$A328-$A364     Error message vector table
$A365-$A389     Miscellaneous messages (null-terminated)

$A38A-$A47F     Some BASIC routines, to be be disassembled at a later date

$A480           Main loop

        Since the goal is to get a good enough understanding of BASIC to
add new keywords, the main program loop is a good place to start.  This
routine is vectored through IMAIN at $0302.  It gets a line of input
from the keyboard, checks for a line number, and processes the line
appropriately.
        When I started programming BLARG, I had no idea what routines like
CRUNCH were expected to return -- is the Y register expected to have
a certain value upon exit?  Or maybe a certain variable needs to be
set up so that another routine may reference it?  Thus it is a good
idea to begin at the main loop and see how a line is normally
processed.  The disassembly listing begins at this point.

        Other important vectored routines are:

$A579           CRUNCH

        This routine goes through the BASIC text buffer at $0200 and
tokenizes any keywords which aren't in quotes.  As the routine begins
to scan the input buffer it discards any characters which have their
high bit set, such as shifted characters.  (This is only in the
initial search for keywords -- shifted characters within quotes or
as part of a keyword are taken care of by another routine).  As
a practical matter, this means that any routine which adds extra
tokens must call this routine _first_, since it will just skip over
custom tokens otherwise!
        When it is finished processing the input buffer, the
input buffer contains the tokenized line, terminated by a null
byte, _and_ with another null byte at the end of the line +2 (much
like the terminating byte which marks the end of a program).  See
the disassembly and blarg source for other things which are set up.

$A717   QPLOP

        This is the part of the LIST routine (which begins at $A69C)
which converts tokens into ASCII characters and prints them to the
screen.

$A7E4   GONE

        This is the routine which gets the next token (every statement
begins with a token or an implied LET) and executes the appropriate command.
Invalid tokens generate a SYNTAX ERROR.  Character values less than 128 cause
LET to be executed.
        The IF/THEN routine actually bypasses the IGONE vector and jumps
directly into this routine, which means that lines like
        IF A=0 THEN MYCOMMAND
will generate a syntax error.  BLARG currently has no mechanism for
getting around this (because I did not discover this until recently,
and the article deadline was several days ago :), except to place a
colon after the THEN, i.e. IF A=0 THEN:MYCOMMAND.  Apparently some
programs get around this by defining their own IF statement; probably
the best way to get around it is to redirect the IERROR vector.
When an error is generated, check to see if it is a custom token,
then check to see if it was preceded by a THEN token.

BLARG
-----
        By now, we have all the necessary information to add new keywords
to BASIC.  To that end I present BLARG, which adds several hires bitmap
commands to BASIC.  As a bonus, BLARG can take advantage of a SuperCPU
if you have one.  These commands are much faster than the 128's BASIC7.0
commands and in my quite biased opinion much more intuitive.  The CIRCLE
and LINE commands are adaptations of my routines from C=Hacking; look there
for more info on the actual routines.
        Some things in BLARG are done a little differently than their
corresponding BASIC routines, in large part because I wrote some of the
routines before disassembling the BASIC ones.
        BLARG documentation is below.

References
----------
        I have found Mapping the 64 to be an invaluable reference, along
with "Programming the Commodore 64", by Raeto West.  ftp.funet.fi has
some good documentation, and I use Marko Makela's web page quite
often at http://www.hut.fi/~msmakela -- there are many useful documents
there, including a complete html cross-referenced ROM listing.



*                       
* A480-A856  (MAIN-END)
*   Basic ROM disassembly starting with main loop
*
* Stephen L. Judd 1997
*

*
* Labels are at the end of the listing, along with a list
* of major routines and their addresses.
*
                          
*                          
* MAIN -- Main loop, receives input and executes
* immediately or stores as a program line.
*

* $A480                          
JMPMAIN  JMP (IMAIN)      ;Main loop, below
                          ;Vectored through IMAIN, so it
                          ;may be redirected.
MAIN     JSR INLIN        ;Input a line from keyboard
         STX TXTPTR       
         STY TXTPTR+1     
         JSR CHRGET       ;Get 1st char out of buffer
         TAX             
         BEQ JMPMAIN      ;Empty line
         LDX #$FF         ;Signal that BASIC is in immediate
         STX CURLIN+1     ;mode.
         BCC MAIN1        ;CHRGET clears C when digit is read
         JSR CRUNCH       ;Tokenize line
         JMP JMPGONE      ;Execute line

*                          
* MAIN1 -- Add or replace a line of program text.
*
* TEMP2 points to the current line to be deleted
* TEMP1 will point to the start of memory to be copied
* INDEX1 will point to the destination for the copy

* $A49C
MAIN1    JSR LINGET       ;Convert ASCII to binary line number
         JSR CRUNCH       ;Tokenize buffer
         STY COUNT        ;Y=length of line
         JSR FINDLINE     ;Find the address of the line number
         BCC :NEWLINE
         LDY #$01         ;Replace line of text
         LDA (TEMP2),Y    ;$5F = pointer to line
         STA TEMP1+1      ;(TEMP1) = xx, Hi byte of next line
         LDA VARTAB       ;End of BASIC program
         STA TEMP1        ;(TEMP1) = basic end, high next
         LDA TEMP2+1    
         STA INDEX1+1     ;(INDEX1) = xx, Hi byte current line
         LDA TEMP2        ;Compute -length of current line
         DEY              
         SBC (TEMP2),Y    ;Current address - next address
         CLC              
         ADC VARTAB       ;end - (length of line to be deleted)
         STA VARTAB
         STA INDEX1       ;(INDEX1) = bytes to delete, high current
         LDA VARTAB+1     ;Back up end of program if necessary
         ADC #$FF         
         STA VARTAB+1     
         SBC TEMP2+1      ;Number of pages of memory to move
         TAX 
         SEC
         LDA TEMP2        ;Pretty confusing, eh?
         SBC VARTAB
         TAY              ;256 - number of bytes to move
         BCS :CONT1       
         INX              
         DEC INDEX1+1
:CONT1   CLC              
         ADC TEMP1        ;old basic end - number of bytes to move
         BCC :CONT2       
         DEC TEMP1+1
         CLC              
:CONT2   LDA (TEMP1),Y    ;Delete old line, fall through to create
         STA (INDEX1),Y   ;new line.
         INY              
         BNE :CONT2      
         INC TEMP1+1      
         INC INDEX1+1          
         DEX              
         BNE :CONT2       

:NEWLINE JSR RESCLR       ;Reset variables and TXTPTR
         JSR LINKPRG      ;Relink program lines
         LDA BUF          
         BEQ JMPMAIN      ;Empty statement -- nothing to do
         CLC              
         LDA VARTAB       ;Start of variables
         STA $5A          
         ADC COUNT
         STA $58          ;Start of vars after line is added
         LDY VARTAB+1     
         STY $5B          
         BCC :CONT3       
         INY              
:CONT3   STY $59          
         JSR MALLOC       ;Open up some space in memory
         LDA LINNUM       ;Number of line to be added
         LDY LINNUM+1     
         STA H01FE        ;Rock bottom on the stack.
         STY H01FF        
         LDA STREND       ;Bottom of strings to top of variables
         LDY STREND+1     
         STA VARTAB       
         STY VARTAB+1     
         LDY COUNT        ;Number of chars in BUF
         DEY              
:LOOP    LDA BUF-4,Y      ;Copy buffer contents into program
         STA (TEMP2),Y    
         DEY              
         BPL :LOOP        
         JSR RESCLR        
         JSR LINKPRG      
         JMP JMPMAIN      ;Wheeeeeeee...

*
* LINKPRG -- Relink lines of program text
*
* $A533                          
LINKPRG  LDA TXTTAB       ;Start of BASIC text
         LDY TXTTAB+1     
         STA TEMP1        
         STY TEMP1+1      
         CLC              
:LOOP1   LDY #$01         
         LDA (TEMP1),Y    
         BEQ :RTS         ;0 means end of program
         LDY #$04         ;Skip link, line number, and 1st char
:LOOP2   INY              
         LDA (TEMP1),Y    
         BNE :LOOP2       ;Find end of line
         INY              
         TYA              
         ADC TEMP1        ;Add offset to pointer to get address
         TAX              ;of next line
         LDY #$00         
         STA (TEMP1),Y    ;And store in link
         LDA TEMP1+1      
         ADC #$00         
         INY              
         STA (TEMP1),Y    
         STX TEMP1        ;Move to next line
         STA TEMP1+1      
         BCC :LOOP1       ;Keep on truckin'!
:RTS     RTS              

*
* INLIN -- Input a line from keyboard to buffer
*
* $A560
INLIN    LDX #$00         
:LOOP    JSR HE112        ;BASIC's way of calling Kernal routines
         CMP #$0D         
         BEQ :DONE        
         STA BUF,X        
         INX              
         CPX #$59         ;Maximum buffer size = 88 chars
         BCC :LOOP        
         LDX #$17         ;STRING TOO LONG error
         JMP ERROR        
:DONE    JMP HAACA        ;Part of the PRINT routine

*
* CRUNCH -- Tokenize a line of text contained in the input buffer
*
* $A579
*
* On exit:
*   Y = last character of crunched text + 4
*   X = last char of input buffer read in.
*   TEXTPTR = $01FF
*   GARBFL = 00 if colon was read, $49 if DATA statement,
*            04 otherwise.
*   ENDCHR = $22 (if quote was found) or 0 (if REM)
*   COUNT = Last token read - 128 (contrary to what
*                                  Mapping the 64 says)
*   TEMP1 = More or less random
*   BUF = Tokenized text, followed by a 00, random byte, and 00
*

CRUNCH                    ;$A579
         JMP (ICRNCH)     
         LDX TXTPTR       ;Low byte
         LDY #$04         
         STY GARBFL       
                          
MAINLOOP LDA BUF,X        ;Search input buffer for text
         BPL :GOTCHAR     ;characters or #$FF
         CMP #$FF         
         BEQ STALOOP      
         INX              
         BNE MAINLOOP         
                          
:GOTCHAR CMP #$20         ;Is it a space?
         BEQ STALOOP      
         STA ENDCHR       
         CMP #$22         ;Is it a quote?
         BEQ QUOTE2       
         BIT GARBFL       ;This either equals 00 if a
                          ;colon was hit, $49 if a DATA
                          ;statement was found, and #04
                          ;initially.  Thus, it prints the
                          ;text within DATA statements.
         BVS STALOOP      
         CMP #'?'         ;Short print
         BNE :CONT1       
         LDA #$99         ;Print token
         BNE STALOOP      
                          
:CONT1   CMP #'0'         ;Check for a number,
         BCC :NOTNUM       
         CMP #'<'         ;colon, or semi-colon
         BCC STALOOP      
                          
:NOTNUM  STY TEMP1        ;So, search for a keyword
         LDY #$00         
         STY COUNT        
         DEY              
         STX TXTPTR       
         DEX              
                          
FINDWORD INY              ;Find keyword
         INX              
CMPWORD  LDA BUF,X        ;Match against keyword table
         SEC              
         SBC RESLST,Y     
         BEQ FINDWORD     
         CMP #$80         ;High bit of last char is set
                          ;Also means p shift-O gives short
                          ;version of POKE (for instance),
                          ;as should po shift-K.
         BNE NEXTWORD     
         ORA COUNT        ;A now contains token value
STABUF   LDY TEMP1        ;Crunched text index
STALOOP  INX              ;Store text in crunched buffer
         INY              
         STA BUF-5,Y      
         LDA BUF-5,Y      ;Goofy -- use AND #$FF or CMP #0
         BEQ ALLDONE      ;Zero byte terminates string
         SEC              
         SBC #$3A         ;Is it a colon?
         BEQ :COLON       
         CMP #$49         ;Was it $83, a DATA statement?
         BNE :SKIP        
:COLON   STA GARBFL       
:SKIP    SEC              
         SBC #$55         ;Was it $8F, a REM statement?
         BNE MAINLOOP         
         STA ENDCHR       ;If REM, then read rest of line
                          
QUOTE    LDA BUF,X        ;Look for matching quote char
         BEQ STALOOP      ;or end of statement, and embed
         CMP ENDCHR       ;text directly.
         BEQ STALOOP      
QUOTE2   INY              
         STA BUF-5,Y      
         INX              
         BNE QUOTE        
                          
NEXTWORD LDX TXTPTR       ;Skip to next keyword
         INC COUNT        ;Next token
:LOOP    INY              
         LDA RESLST-1,Y   ;Find last char
         BPL :LOOP        
         LDA RESLST,Y     
         BNE CMPWORD      
         LDA BUF,X        
         BPL STABUF       
                          
ALLDONE  STA BUF-3,Y      
         DEC TXTPTR+1     
         LDA #$FF         
         STA TXTPTR       
         RTS              

*
* FINDLINE
*   Search for the line number contained in $14.  If found, set $5F
*   to link address and set carry.  Carry clear means line number
*   not found.
*

* $A613                          
FINDLINE LDA TXTTAB       ;Start of program text
         LDX TXTTAB+1     
:LOOP    LDY #$01         
         STA TEMP2        
         STX TEMP2+1      
         LDA (TEMP2),Y    
         BEQ :EXIT        ;Exit if at end of program
         INY          
         INY              
         LDA LINNUM+1     ;High byte
         CMP (TEMP2),Y    
         BCC :RTS         ;Less than -> line doesn't exist
         BEQ :CONT1        
         DEY              
         BNE :CONT2       ;...always taken
:CONT1   LDA LINNUM       ;Compare low byte
         DEY              
         CMP (TEMP2),Y    
         BCC :RTS         ;Punt when past line number
         BEQ :RTS         ;Success!
:CONT2   DEY              ;Get next line link
         LDA (TEMP2),Y
         TAX              
         DEY              
         LDA (TEMP2),Y    
         BCS :LOOP
:EXIT    CLC              
:RTS     RTS
                          
* Perform NEW
* $A642
NEW      BNE *-2          ;RTS above
         LDA #$00
         TAY
         STA (TXTTAB),Y   ;Zero out first two bytes of program
         INY              
         STA (TXTTAB),Y   
         LDA TXTTAB       
         CLC              
         ADC #$02         
         STA VARTAB       ;Move end of BASIC to begin+2
         LDA TXTTAB+1     
         ADC #$00         
         STA VARTAB+1     
RESCLR   JSR RUNC         ;Reset TXTPTR
         LDA #$00         

* Perform CLR (Calcium Lime and Rust remover!)
* $A65E
CLEAR    BNE CLEAREND     
         JSR CLALL        ;Close files
         LDA MEMSIZ       ;Highest address used by BASIC
         LDY MEMSIZ+1
         STA FRETOP       ;Bottom of string text storage
         STY FRETOP+1
         LDA VARTAB       ;End of BASIC program/variable start
         LDY VARTAB+1     
         STA ARYTAB       ;Start of array storage
         STY ARYTAB+1
         STA STREND       ;End of array storage/start of free RAM
         STY STREND+1     
         JSR RESTORE      
         LDX #$19         
         STX TEMPPT       ;Temporary string stack
         PLA              
         TAY              
         PLA              
         LDX #$FA         ;Reset stack
         TXS              
         PHA              ;Restore correct return address
         TYA              
         PHA              
         LDA #$00         
         STA OLDTXT+1     ;Address of current basic statement
         STA SUBFLG       ;
CLEAREND RTS              

*                          
* RUNC -- reset current text character pointer to the
* beginning of program text.
*
* $A68E
RUNC     CLC              
         LDA TXTTAB       
         ADC #$FF         
         STA TXTPTR       
         LDA TXTTAB+1     
         ADC #$FF         
         STA TXTPTR+1     
         RTS              
                         
* 
* LIST -- perform LIST
* entered via return from CHRGET
*
* $A69C
LIST     BCC :SKIP        ;Is next char an ASCII digit
         BEQ :SKIP        ;Is next char ':' or 00
         CMP #$AB         ;Is next char '-' (token)
         BNE CLEAREND     ;RTS, above
:SKIP    JSR LINGET       ;Read decimal, convert to number
         JSR FINDLINE     ;Find line number
         JSR CHRGOT       ;Get char again
         BEQ :CONT        ;statement terminator
         CMP #$AB         
         BNE NEW-1        ;RTS
         JSR CHRGET       ;Advance and get end of
         JSR LINGET       ;range to list xx-xx
         BNE NEW-1        ;RTS
:CONT    PLA              
         PLA              
         LDA LINNUM       
         ORA LINNUM+1     
         BNE LISTLOOP     
         LDA #$FF         ;Signal to list program to end
         STA LINNUM       
         STA LINNUM+1     
LISTLOOP LDY #$01         
         STY GARBFL       
         LDA (TEMP2),Y    
         BEQ ENDLIST      
         JSR TESTSTOP     ;Test for STOP key
         JSR HAAD7        ;part of PRINT routine
         INY              
         LDA (TEMP2),Y    
         TAX              
         INY              
         LDA (TEMP2),Y    
         CMP LINNUM+1     ;Check to see if at last line
         BNE :CONT1       
         CPX LINNUM       
         BEQ :CONT2       
:CONT1   BCS ENDLIST      
:CONT2   STY FORPNT       ;temporary storage
         JSR LINPRT       ;Print line number
         LDA #$20         ;space
LISTENT1 LDY FORPNT       
         AND #$7F         ;strip high bit
LISTENT2 JSR HAB47        ;Prints char, AND #$FF
         CMP #$22         ;Look for a quote
         BNE :CONT        
         LDA GARBFL       
         EOR #$FF         
         STA GARBFL       
:CONT    INY              ;$A700
         BEQ ENDLIST      ;256 character lines perhaps?
         LDA (TEMP2),Y    
         BNE JMPPLOP      ;Print the token
         TAY              ;Get line link
         LDA (TEMP2),Y    
         TAX              
         INY              
         LDA (TEMP2),Y    
         STX TEMP2        
         STA TEMP2+1      
         BNE LISTLOOP     
ENDLIST  JMP HE386        ;Exit through warm start

*                          
* QPLOP -- print BASIC tokens as ASCII characters.
*
* $A717
JMPPLOP  JMP (IQPLOP)     
QPLOP    BPL LISTENT2     ;Exit if not a token
         CMP #$FF         
         BEQ LISTENT2     
         BIT GARBFL       
         BMI LISTENT2     ;Exit if inside a quote
         SEC              
         SBC #$7F         ;Table offset+1
         TAX              
         STY FORPNT       ;Temp storage
         LDY #$FF         
:LOOP1   DEX              ;Traverse the keyword table
         BEQ :PLOOP       
:LOOP2   INY              ;read a keyword
         LDA RESLST,Y   
         BPL :LOOP2       
         BMI :LOOP1       
:PLOOP   INY              ;Print out the keyword
         LDA RESLST,Y   
         BMI LISTENT1     ;Exit if on last char
         JSR HAB47        ;Print char, AND #$FF
         BNE :PLOOP       

*                          
* Perform FOR
*
* $A742
FOR      LDA #$80         
         STA SUBFLG       
         JSR LET
         JSR FINDFOR
         BNE :CONT
         TXA              
         ADC #$0F         
         TAX              
         TXS              
:CONT    PLA
         PLA              
         LDA #$09         
         JSR CHKSTACK     
         JSR ENDSTAT      
         CLC              
         TYA              
         ADC TXTPTR       
         PHA              
         LDA TXTPTR+1     
         ADC #$00         
         PHA              
         LDA CURLIN+1     
         PHA              
         LDA CURLIN       
         PHA              
         LDA #$A4         
         JSR CHKCOM       
         JSR HAD8D        
         JSR FRMNUM       
         LDA FACSGN       
         ORA #$7F         
         AND $62          
         STA $62          
         LDA #$8B         
         LDY #$A7         
         STA TEMP1        
         STY TEMP1+1      
         JMP HAE43        
                          
         LDA #$BC         
         LDY #$B9         
         JSR MTOFAC       
         JSR CHRGOT       
         CMP #$A9         
         BNE HA79F        
         JSR CHRGET       
         JSR FRMNUM       
HA79F    JSR SIGN         
         JSR HAE38        
         LDA $4A          
         PHA              
         LDA FORPNT       
         PHA              
         LDA #$81         
         PHA              
      
*                    
* NEWSTT -- Set up next statement for execution.
*
* $A7AE
NEWSTT   JSR TESTSTOP     
         LDA TXTPTR       
         LDY TXTPTR+1     
         CPY #$02         ;Text buffer?
         NOP              
         BEQ :CONT        
         STA OLDTXT       
         STY OLDTXT+1     
:CONT    LDY #$00         ;End of last statement.
         LDA (TXTPTR),Y   
         BNE HA807        ;branch into GONE
         LDY #$02         
         LDA (TXTPTR),Y   ;End of program?
         CLC              
         BNE :CONT2       
         JMP HA84B        ;exit through END

:CONT2   INY              
         LDA (TXTPTR),Y   ;Line number
         STA CURLIN       
         INY              
         LDA (TXTPTR),Y   
         STA CURLIN+1     
         TYA              
         ADC TXTPTR       ;Advance pointer
         STA TXTPTR       
         BCC JMPGONE      
         INC TXTPTR+1     
                         
* 
* GONE -- Read and execute next statment
*
* $A7E1
JMPGONE  JMP (IGONE)      
         JSR CHRGET       
         JSR GONE         
         JMP NEWSTT       
* $A7ED
GONE     BEQ :RTS         ;Exit if statement terminator
         SBC #$80         
         BCC :JMPLET      ;If not a token then a variable
         CMP #$23         ;Tokens above $A3 never begin
         BCS CHKGOTO      ;a statement (except GO TO)
         ASL              ;Otherwise, get entry point
         TAY              ;of the statement...
         LDA STATVEC+1,Y  
         PHA              
         LDA STATVEC,Y    
         PHA              
         JMP CHRGET       ;... so CHRGET can RTS to it.
                          
:JMPLET  JMP LET          
                          
HA807    CMP #':'
         BEQ JMPGONE
JMPSYN   JMP SYNERR       
                          
CHKGOTO  CMP #$4B         ;Is it "GO TO"?
         BNE JMPSYN       
         JSR CHRGET       
         LDA #$A4         ;Make sure next char is "TO"
         JSR CHKCOM       ;token, and skip it.
         JMP GOTO

* 
* Perform RESTORE
*
* $A81D
RESTORE  SEC              
         LDA TXTTAB       ;Set DATA pointer to start of program
         SBC #$01         
         LDY TXTTAB+1     
         BCS :CONT
         DEY              
:CONT    STA DATPTR       ;Address of current DATA item
         STY DATPTR+1
:RTS     RTS              
                          
TESTSTOP JSR STOP         
         BCS END+1        

*
* END -- perform END
*
* $A831
END      CLC
         BNE $A870        ;RTS
         LDA TXTPTR
         LDY TXTPTR+1
         LDX CURLIN+1     ;Current line number
         INX              
         BEQ :CONT1       ;Branch if in immediate mode
         STA OLDTXT       ;Save statement address for CONT
         STY OLDTXT+1
         LDA CURLIN
         LDY CURLIN+1
         STA OLDLIN       ;Restored by CONT
         STY OLDLIN+1
:CONT1   PLA              ;Discard return address
         PLA
* $A84B
HA84B    LDA #$81         ;Message at $A381
         LDY #$A3
         BCC :CONT2
         JMP $A469        ;BREAK
:CONT2   JMP $E386        ;BASIC warm start

                          

ENDCHR   = $0008          ;See CRUNCH
COUNT    = $0B            
GARBFL   = $000F          ;Work byte
SUBFLG   = $0010          
LINNUM   = $14            
TEMPPT   = $0016          ;Next available space in temp string stack
TEMP1    = $22            ;Temporary pointer/variable
INDEX1   = $0024          
TXTTAB   = $002B          ;Pointer to start of BASIC text
ARYTAB   = $002F          ;Pointer to start of arrays
STREND   = $0031          ;End array storage/start free RAM
FRETOP   = $0033          ;End of string text/top free RAM
MEMSIZ   = $0037          ;Highest address used by BASIC
CURLIN   = $39            ;Current BASIC line number
OLDTXT   = $003D          ;Pointer to current BASIC statement
DATPTR   = $0041          ;Pointer to current DATA item
FORPNT   = $0049          ;Temp pointer to FOR index variable
TEMP2    = $5F            
FAC1     = $61
FACSGN   = $0066          
TEMP1    = $71            
FBUFPT   = $0071          
CHRGET   = $0073          ;Get next BASIC text char
CHRGOT   = $0079          ;Get current BASIC text char
TXTPTR   = $7A            ;Text pointer
H01FE    = $01FE          
H01FF    = $01FF          
BUF      = $0200          ;Text input buffer
IMAIN    = $0302          ;System vectors
ICRNCH   = $0304          
IQPLOP   = $0306          
IGONE    = $0308          
STATVEC  = $A00C          ;Statement dispatch vector table
RESLST   = $A09E          ;Reserved keywords list
FINDFOR  = $A38A          ;Find FOR on stack
MALLOC   = $A3B8          ;Make space for new line or var
CHKSTACK = $A3FB          ;Check for space on stack
ERROR    = $A437          ;General error handler
END      = $A831          ;Perform END
GOTO     = $A8A0          ;Perform GOTO
ENDSTAT  = $A906          ;Search for end of statement
                          ;(00 or colon)
LINGET   = $A96B          ;Convert ASCII decimal to 2-byte line number
LET      = $A9A5          ;Perform LET
HAACA    = $AACA          
HAAD7    = $AAD7          
HAB47    = $AB47          
FRMNUM   = $AD8A          ;Eval expression/check data type
HAD8D    = $AD8D          
HAE38    = $AE38          
HAE43    = $AE43          
CHKCOM   = $AEFF          ;Check for and skip comma
SYNERR   = $AF08          ;Print Syntax Error message
MTOFAC   = $BBA2          ;Move FP number from mem to FAC1
SIGN     = $BC2B          ;Sign of FAC1 in A
LINPRT   = $BDCD          ;Print num X,A=lo,hi in ASCII
HE112    = $E112          
HE386    = $E386          
STOP     = $FFE1          ;Kernal
CLALL    = $FFE7          

*
* Major routine entry points
*
* $A480 JMPMAIN  JMP (IMAIN)      ;Main loop, below
* $A483 MAIN     JSR INLIN        ;Input a line from keyboard
* $A49C MAIN1    JSR LINGET       ;Convert ASCII to binary line number
* $A533 LINKPRG  LDA TXTTAB       ;Start of BASIC text
* $A560 INLIN    LDX #$00         
* $A579 CRUNCH   JMP (ICRNCH)
* $A613 FINDLINE LDA TXTTAB       ;Start of program text
* $A642 NEW      BNE *-2          ;RTS above
* $A65E CLEAR    BNE CLEAREND     
* $A68E RUNC     CLC              
* $A69C LIST     BCC :SKIP        ;Is next char an ASCII digit
* $A717 JMPPLOP  JMP (IQPLOP)     
* $A71A QPLOP    BPL LISTENT2     ;Exit if not a token
* $A7AE NEWSTT   JSR TESTSTOP     
* $A7E1 JMPGONE  JMP (IGONE)      
* $A7ED GONE     BEQ :RTS         ;Exit if statement terminator
* $A81D RESTORE  SEC              
* $A831 END      CLC


BLARG -- Basic Language Graphics extension
-----
version 1.0 2/10/97

BLARG is a little BASIC extension which adds some graphics commands to
the normal C-64 BASIC.  In addition it supports the SuperCPU optimization
modes, as well as double buffering.  Finally, it is free for use in
your own programs, so feel free to do so!

My goal was to write a BASIC extension which was compact, fast,
and actually available for downloading :).  Also, something that wasn't
BASIC7.0.  It doesn't have tons of features but I deem it to be Nifty.

Anyways, these are adaptations of my algorithms from C=Hacking and such.
They are not the most efficient implementations, but they are fairly zippy,
and fairly well beat the snot out of BASIC7.0 commands!  For instance,
the times from moire3 (a line drawing test):

        Stock 64        1200 jiffies    (1X)
        SCPU Mode 17    137 jiffies     (9.1X)
        SCPU Mode 16    59 jiffies      (20.2X)
        BASIC7.0 (1MHz) 4559 jiffies    (1/3.5 X)

So lines are nearly 4x faster on a stock 64 than a 128 running BASIC7.0.
Running in mode16 on a SuperCPU is 77 times faster than BASIC7.0!!!

Let's not even talk about BASIC7.0 circles.  Well, what the heck, let's
talk about them :)
circletest1:
        Stock 64        360 jiffies     (1x)
        SCPU Mode 17    50 jiffies      (7x)
        SCPU Mode 16    22 jiffies      (15.4x)
        BASIC7.0        17394 j :)      1/49x

Bottom line: mode 16 circles are, if you can believe it, 790x faster
than BASIC7.0 circles (and much better looking, especially at large
radii -- BASIC7.0 circles are actually 128-sided polygons :-/  ).

The total size of the program right now is a bit over 2k, and sits at
$C000.  To install the program, just load and run.  To re-initialize
the system (after a warm reset for instance) just type SYS49152.  The
command list is located near $C000, immediately followed by the
routine addresses, in case you want to take a peek.

A second program, BLARG$8000, is included in the archive.  To use it,
load ,8,1 and type SYS32768 to initialize.  This program is included
so that it may be loaded from a BASIC program.

Several demo programs are included, and offer a good way of learning
the commands (for instance, try typing ORIGIN 10,10 before running
MOIRE3).

Words to the wise:

        1 - If you use a DOS extension like JiffyDOS then be sure to
            enclose filenames etc. in quotes (unless you want them to
            be tokenized, in which case feel free to omit quotes).

        2 - If it looks like your machine has completely frozen try
            typing RUN-STOP, shift-clear, MODE 17 (Sometimes you can
            break the program before it can tell VIC where the screen
            is located).  MODE16 and MODE17 will always fix stuff up,
            whereas run/stop-restore doesn't always do the trick.

        3 - Always keep in mind that MODE 16,17, and 18 may hose
            string variables.

        4 - IF/THEN bypasses the IGONE vector, so a statement like
            IF A=0 THEN GRON will fail.  The statement IF A=0 THEN:GRON
            will work fine.

Without further ado:

GRON [COLOR] -- Turns graphics on.  If the optional parameter COLOR is
        specified the bitmap is cleared and the colormap is initialized to
        that value, specifically,
        COLOR = 16*foreground color + background color

        Examples:
                GRON  -- Turn on bitmap without clearing it.
                GRON20 -- Turn on bitmap, clear, purple bg, white fg

GROFF -- Turns graphics off (sticks you back into text mode, or
        whatever mode you were in when you last called GRON)

CLEAR [color] -- Clear current graphics buffer.  CLEAR is part of
        the GRON routine, but will not set VIC or CIA#2.

COLOR n -- COLOR 1 sets the drawing color to the foreground color;
        COLOR 0 sets it to background.

ORIGIN CX, CY -- Sets the upper-left corner of the screen to have
        coordinates CX,CY.  More precisely, commands will subtract
        CX,CY from coordinates passed into it.  Among other things,
        this provides a mechanism for negative numbers to be
        handled -- LINE -10,0,40,99 will not work, but ORIGIN 10,0:
        LINE 0,0,50,99 will.  This value is initialized to (0,0) whenever
        SYS49152 is called.

PLOT X,Y -- Sticks a point at coordinates X,Y.  (Actually at
        coordinates X-XC, Y-YC).  X may be in the range 0..319
        and Y may be 0..199; points outside this range will not be
        plotted.

LINE X1,Y1,X2,Y2 -- Draws a line from X1,Y1 to X2,Y2 (subtracting
        XC,YC as necessary).  X1 may be any 16-bit value and Y2
        may be any 8-bit value.  Coordinates off of the screen will
        simply not be plotted!

CIRCLE XC,YC,R -- Draws a circle of radius R centered at XC,YC
        (translating as necessary).  The algorithm is smart and can
        handle R=0..255 correctly (as far as I know!).  I used a
        modified version of my algorithm, which makes very nice
        circles in my quite biased opinion, except for a few radii
        which come out a little ovalish.  Oh well.

MODE n -- New graphics MODE.
        MODE 16 -- SuperCPU mode.  This moves the bitmap screen to
                $A000, the colormap to $8C00, the text screen to $8800,
                and sets the SuperCPU bank 2 optimization mode.
                $9000-$9FFF is totally unused :(.
        MODE 17 -- Normal mode.  Bitmap->$E000, Colormap ->$CC00,
                text screen -> $0400.
        MODE 18 -- Double buffer mode.  MODE16 memory configuration,
                no SCPU optimization.
        MODE16, MODE17, and MODE18 reset the BASIC memtop and stringtop
        pointers, so any defined strings may get hosed.
        (They also execute GROFF, and hence turn on the text screen).
        Any other MODE parameter will be set to the BITMASK parameter.
        What is BITMASK?  Anything drawn to the screen is first ANDed
        with BITMASK.  (Try MODE 85 sometime).
        (Although these numbers are reserved for future expansion).

BUFFER n -- Set drawing buffer.
        When double-buffer mode is activated (MODE 18), both buffers
        are available for drawing and displaying.  It is then
        possible to draw in one buffer while displaying the other.
        BUFFER n selects which buffer the PLOT,LINE,CIRCLE, and CLEAR
        commands will affect.  If n=0 then it swaps the target
        buffer.  Otherwise, n=odd references the buffer at $A000
        and n=even selects the buffer at $E000.

SWAP -- Swap displayed buffer.
        Assuming MODE18 is selected, SWAP simply selects which buffer
        is displayed on the screen; specifically, it flips between
        the two.  SWAP only affects what is displayed on the
        screen; BUFFER only affects the target of the drawing commands.

I think that's it :).

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

*                       
* GRABAS
*
* A graphics extension for C-64 BASIC
*
* SLJ 12/29/96 (Completed 2/10/97)
* v1.0
*
         ORG $0801        
                          
* Constants
                          
TXTPTR   = $7A            ;BASIC text pointer
IERROR   = $0300          
ICRUNCH  = $0304          ;Crunch ASCII into token
IQPLOP   = $0306          ;List
IGONE    = $0308          ;Execute next BASIC token
                          
CHRGET   = $73            
CHRGOT   = $79            
CHROUT   = $FFD2          
GETBYT   = $B79E          ;BASIC routine
GETPAR   = $B7EB          ;Get a 16,8 pair of numbers
CHKCOM   = $AEFD          
NEW      = $A642          
CLR      = $A65E          
                          
LINNUM   = $14            ;Number returned by GETPAR
                          
TEMP     = $FF            
TEMP2    = $FB            
POINT    = $FD            
Y1       = $05            
X1       = LINNUM         
X2       = $02            
Y2       = $04            
DY       = $26            
DX       = $27            
BUF      = $0200          ;Input buffer
                          
CHUNK1   = $69            ;Circle routine stuff
OLDCH1   = $6A            
CHUNK2   = $6B            
OLDCH2   = $6C            
CX       = $A3            
CY       = $A5            
X        = $6D            
Y        = $6E            
RADIUS   = $6F            
LCOL     = $A6            ;Left column
RCOL     = $A7            
TROW     = $A8            ;Top row
BROW     = $A9            ;Bottom row
                          
                          
                          
         DA :LINK         ;link
         DA 1997          
         DFB $9E          ;SYS
         TXT '2063:'      
         DFB $A2          ;NEW
         DFB 00           ;End of line
:LINK    DA 0             ;end of program
                          
INSTALL  LDA #<PBEGIN     
         STA POINT        
         LDA #>PBEGIN     
         STA POINT+1      
         LDA #<PEND       ;Number of bytes to copy
         SEC              
         SBC #<PBEGIN     
         STA TEMP2        
         LDA #>PEND       
         SBC #>PBEGIN     
         STA TEMP2+1      
         LDA #$C0         ;Copy to $C000
         STA X2+1         
         LDY #00          
         STY X2           
:LOOP    LDA (POINT),Y    
         STA (X2),Y       
         INY              
         BNE :LOOP        
         INC POINT+1      
         INC X2+1         
         DEC TEMP2+1      
         BNE :LOOP        
         LDY TEMP2        
:LOOP2   LDA (POINT),Y    
         STA (X2),Y       
         DEY              
         CPY #$FF         
         BNE :LOOP2       
         LDX #5           ;Copy CURRENT vectors
:LOOP3   LDA ICRUNCH,X    
         STA OLDCRNCH,X   
         DEX              
         BPL :LOOP3       
         JMP INIT         
                          
         TXT 'so, you want a secret message, eh? '
         TXT 'narnia, narnia, narnia, awake.'
         TXT ' love. think. speak. be walking trees.'
         TXT ' be talking beasts. be divine waters.'
         TXT 'stephen l. judd wuz here 1/20/97'
                          
PBEGIN                    
         ORG $C000        
                          
*
* Init routine -- modify vectors
* and set up values.
*
INIT                      
         LDX #5           ;Copy vectors
:LOOP    LDA :TABLE,X     
         STA ICRUNCH,X    
         DEX              
         BPL :LOOP        
         INX              
         STX ORGX         
         STX ORGY         

         JMP MODE17       ;Mode 17
:TEMP    DFB 05           
:TABLE   DA CRUNCH        
         DA LIST          
         DA EXECUTE       
JMPCRUN  DFB $4C          ;JMP
OLDCRNCH DS 2             ;Old CRUNCH vector
OLDLIST  DS 2             
OLDEXEC  DS 2             
                          
*
* Keyword list
* Keywords are stored as normal text,
* followed by the token number.
* All tokens are >128,
* so they easily mark the end of the keyword
*
                          
KEYWORDS                  
         TXT 'plot',E0    
         TXT 'line',E1    
         TXT 'circle',E2  
         TXT 'gr',91,E3   ;grON
         TXT 'groff',E4   ;Graphics off
         TXT 'mode',E5    
         DFB $B0          ;OR
         TXT 'igin',E6    
         TXT 'clear',E7   ;Clear bitmap
         TXT 'buffer',E8  ;Set draw buffer
         TXT 'swap',E9    ;Swap foreground and background
         TXT 'col',B0,EA  ;Set color
         DFB 00           ;End of list
                          
*
* Table of token locations-1
* Subtract $E0 first
* Then check to make sure number isn't greater than NUMWORDS
*
TOKENLOC                  
:E0      DA PLOT-1        
:E1      DA LINE-1        
:E2      DA CIRCLE-1      
:E3      DA GRON-1        
:E4      DA GROFF-1       
:E5      DA MODE-1        
:E6      DA ORIGIN-1      
:E7      DA CLEAR-1       
:E8      DA BUFFER-1      
:E9      DA SWAP-1        
:EA      DA COLOR-1       
HITOKEN  EQU $EB          
                          
*
* CRUNCH -- If this is one of our keywords, then tokenize it
*
CRUNCH                    
         JSR JMPCRUN      ;First crunch line normally
         LDY #05          ;Offset for KERNAL
                          ;Y will contain line length+5
:LOOP    STY TEMP         
         JSR ISWORD       ;Are we at a keyword?
         BCS :GOTCHA      
:NEXT                     
         JSR NEXTCHAR     
         BNE :LOOP        ;Null byte marks end
         STA BUF-3,Y      ;00 line number
         LDA #$FF         ;'tis what A should be
         RTS              ;Buh-bye
* Insert token and crunch line
:GOTCHA                   
         LDX TEMP         ;If so, A contains opcode
         STA BUF-5,X      
:MOVE    INX              
         LDA BUF-5,Y      
         STA BUF-5,X      ;Move text backwards
         BEQ :NEXT        
         INY              
         BPL :MOVE        
                          
*
* ISWORD -- Checks to see if word is
* in table.  If a word is found, then
* C is set, Y is one past the last char
* and A contains opcode.  Otherwise,
* carry is clear.
*
* On entry, TEMP must contain current
* character position.
*
ISWORD                    
         LDX #00          
:LOOP    LDY TEMP         
:LOOP2   LDA KEYWORDS,X   
         BEQ :NOTMINE     
         CMP #$E0         
         BCS :RTS         ;Tokens are >=$E0
         CMP BUF-5,Y      
         BNE :NEXT        
         INY              ;Success!  Go to next char
         INX              
         BNE :LOOP2       
:NEXT                     
         INX              
         LDA KEYWORDS,X   ;Find next keyword
         CMP #$E0         
         BCC :NEXT        
         INX              
         BNE :LOOP        ;And check again
:NOTMINE CLC              
:RTS     RTS              
                          
*
* NEXTCHAR finds the next char
* in the buffer, skipping
* spaces and quotes.  On
* entry, TEMP contains the
* position of the last spot
* read.  On exit, Y contains
* the index to the next char,
* A contains that char, and Z is set if at end of line.
*
NEXTCHAR                  
         LDY TEMP         
:LOOP    INY              
         LDA BUF-5,Y      
         BEQ :DONE        
         CMP #$8F         ;REM
         BNE :CONT        
         LDA #00          
:SKIP    STA TEMP2        ;Find matching character
:LOOP2   INY              
         LDA BUF-5,Y      
         BEQ :DONE        
         CMP TEMP2        
         BNE :LOOP2       ;Skip to end of line
         BEQ :LOOP        
:CONT                     
         CMP #$20         ;Space
         BEQ :LOOP        
         CMP #$22         ;Quote
         BEQ :SKIP        
:DONE    RTS              
                          
*
* LIST -- patches the LIST routine
* to list my tokens correctly.
*
LIST                      
         CMP #$E0         
         BCC :NOTMINE     ;Not my token
         CMP #HITOKEN     
         BCS :NOTMINE     
         BIT $0F          ;Check for quote mode
         BMI :NOTMINE     
         SEC              
         SBC #$DF         ;Find the corresponding text
         TAX              
         STY $49          
         LDY #00          
:LOOP    DEX              
         BEQ :DONE        
:LOOP2   INY              
         LDA KEYWORDS,Y   
         CMP #$E0         
         BCC :LOOP2       
         INY              
         BNE :LOOP        
:DONE    LDA KEYWORDS,Y   
         BMI :OUT         
         JSR $FFD2        
         INY              
         BNE :DONE        
:OUT     CMP #$B0         ;OR
         BEQ :OR          
         CMP #$E0         ;It might be BASIC token
         BCS :CONT        ;e.g. GRON
         LDY $49          
:NOTMINE AND #$FF         
         JMP (OLDLIST)    ;QPLOP
:CONT    LDY $49          
         JMP $A700        ;Normal exit
:OR      LDA #'o'         ;For ORIGIN
         JSR CHROUT       
         LDA #'r'         
         JSR CHROUT       
         INY              
         BNE :DONE        
                          
*
* EXECUTE -- if this is one of my
* tokens, then execute it.
*
EXECUTE                   
         JSR CHRGET       
         PHP              
         CMP #$E0         
         BCC :NOTMINE     
         CMP #HITOKEN     
         BCS :NOTMINE     
         PLP              
         JSR :DISP        
         JMP $A7AE        ;Exit through NEWSTT
:DISP                     
         EOR #$E0         
         ASL              ;Mult by two
         TAX              
         LDA TOKENLOC+1,X 
         PHA              
         LDA TOKENLOC,X   
         PHA              
         JMP CHRGET       ;Exit to routine
:NOTMINE PLP              
         JMP $A7E7        ;Normal routine
                          
*
* PLOT -- plot a point!
*
ORGX     DFB 00           ;Upper-left corner of the screen
ORGY     DFB 00           
DONTPLOT DFB 01           ;0=Don't plot point, just compute
                          ;coordinates (used by e.g. circles)
                          
PLOT                      
         JSR GETPAR       ;Get coordinate pair
         LDA LINNUM       ;Add in origin offset
         SEC              
         SBC ORGX         
         STA LINNUM       
         BCS :CONT1       
         DEC LINNUM+1     
         BMI :ERROR       ;Underflow
         SEC              
:CONT1   TXA              
         SBC ORGY         
         BCC :ERROR       
         TAX              
         CPX #200         ;Check range
         BCS :ERROR       
         LDA LINNUM       
         CMP #<320        
         LDA LINNUM+1     
         SBC #>320        
         BCC SETPOINT     
:ERROR   RTS              ;Just don't plot point
*:ERROR LDX #14
* JMP (IERROR)
SETPOINT                  ;Alternative entry point
                          ;X=y-coord, LINNUM=x-coord
* ;X is preserved
* STX TEMP2
* STY TEMP2+1
                          ;On exit, X,Y are AND #$07
                          ;i.e. are set up correctly.
         TXA              
         AND #248         
         STA POINT        
         LSR              
         LSR              
         LSR              
         ADC BASE         ;Base of bitmap
         STA POINT+1      
         LDA #00          
         ASL POINT        
         ROL              
         ASL POINT        
         ROL              
         ASL POINT        
         ROL              
         ADC LINNUM+1     
         ADC POINT+1      
         STA POINT+1      
         TXA              
         AND #7           
         TAY              
         LDA LINNUM       
         AND #248         
         CLC              ;Overflow is possible!
         ADC POINT        
         STA POINT        
         BCC SETPIXEL     
         INC POINT+1      
SETPIXEL                  
         LDA LINNUM       
         AND #$07         
         TAX              
         LDA DONTPLOT     
         BEQ :RTS         
         LDA POINT+1      
         SEC              
         SBC BASE         ;Overflow check
         CMP #$20         
         BCS :RTS         
         SEI              ;Get underneath ROM
         LDA #$34         
         STA $01          
                          
         LDA (POINT),Y    
         EOR BITMASK      
         AND BITTAB,X     
         EOR (POINT),Y    
         STA (POINT),Y    
                          
         LDA #$37         
         STA $01          
         CLI              
* LDX TEMP2
* LDY TEMP2+1
                          ;On exit, X,Y are AND #$07
                          ;i.e. are set up correctly.
                          ;for more plotting
:RTS     RTS              
                          
BITMASK  DFB #$FF         ;Set point
BITTAB   DFB $80,$40,$20,$10,$08,$04,$02,$01
                          
*-------------------------------
* Drawin' a line.  A fahn lahn.
*
* To deal with off-screen coordinates, the current row
* and column (40x25) is kept track of.  These are set
* negative when the point is off the screen, and made
* positive when the point is within the visible screen.
                          
* Little bit position table
BITCHUNK HEX FF7F3F1F0F070301
CHUNK    EQU X2           
OLDCHUNK EQU X2+1         
                          
* DOTTED -- Set to $01 if doing dotted draws (diligently)
* X1,X2 etc. are set up above (x2=LINNUM in particular)
* Format is LINE x2,y2,x1,y1
                          
LINE                      
         JSR GETPAR       
         STX Y2           
         LDA LINNUM       
         STA X2           
         LDA LINNUM+1     
         STA X2+1         
         JSR CHKCOM       
         JSR GETPAR       
         STX Y1           
                          
:CHECK   LDA X2           ;Make sure x1<x2
         SEC              
         SBC X1           
         TAX              
         LDA X2+1         
         SBC X1+1         
         BCS :CONT        
         LDA Y2           ;If not, swap P1 and P2
         LDY Y1           
         STA Y1           
         STY Y2           
         LDA X1           
         LDY X2           
         STY X1           
         STA X2           
         LDA X2+1         
         LDY X1+1         
         STA X1+1         
         STY X2+1         
         BCC :CHECK       
                          
:CONT    STA DX+1         
         STX DX           
                          
         LDX #$C8         ;INY
         LDA Y2           ;Calculate dy
         SEC              
         SBC Y1           
         BCS :DYPOS       ;Is y2>=y1?
         EOR #$FF         ;Otherwise dy=y1-y2
         ADC #$01         
         LDX #$88         ;DEY
                          
:DYPOS   STA DY           
         STX YINCDEC      
         STX XINCDEC      
                          
         LDA X1           ;Sub origin from 1st point
         SEC              
         SBC ORGX         
         STA X1           
         LDA X1+1         
         SBC #00          
         STA X1+1         
         PHP              ;Save carry flag
         STA TEMP         ;Next compute column
         LDA X1           
         LSR TEMP         
         ROR              
         LSR TEMP         
         ROR              
         LSR TEMP         
         ROR              
         STA CX           ;X-column
         PLP              
         BCC :NEGX        ;If negative, then fix up
         CMP #40          ;If past column 40, then punt!
         BCC :CONT1       
         RTS              
:NEGX    LDA X1           ;coordinate start and count
         AND #$07         
         STA X1           
         LDA #00          
         STA X1+1         
         LDA CX           
                          
:CONT1   LDA Y1           ;Now do the same for Y
         SEC              
         SBC ORGY         
         STA Y1           
         TAX              ;X=y-coord
         PHP              ;Save carry bit
         LSR              
         LSR              
         LSR              
         STA CY           ;Y-column (well, OK, row then)
         PLP              
         BCC :NEGY        ;If negative, then fix stuff up!
         SBC #25          ;Check if we are past bottom of
         BCC :CONT2       ;screen
         ORA #$80         ;Otherwise, 128+rows past 24
         STA CY           ;(for plot range checking)
         TXA              
         AND #$07         
         ORA #8*24        ;Start in last row
         TAX              
         BMI :CONT2       
:NEGY    ORA #$E0         ;Set high bits of column
         STA CY           
         TXA              
         AND #$07         
         TAX              ;Start in 1st row
:CONT2                    
                          
         LDA #00          
         STA DONTPLOT     
         JSR SETPOINT     ;Set up X,Y and POINT
         INC DONTPLOT     
         LDA BITCHUNK,X   
         STA OLDCHUNK     
         STA CHUNK        
                          
         SEI              ;Get underneath ROM
         LDA #$34         
         STA $01          
                          
         LDX DY           
         CPX DX           ;Who's bigger: dy or dx?
         BCC STEPINX      ;If dx, then...
         LDA DX+1         
         BNE STEPINX      
                          
*
* Big steps in Y
*
*   To simplify my life, just use PLOT to plot points.
*
*   No more!
*   Added special plotting routine -- cool!
*
*   X is now counter, Y is y-coordinate
*
* On entry, X=DY=number of loop iterations, and Y=
*   Y1 AND #$07
STEPINY                   
         LDA #00          
         STA OLDCHUNK     ;So plotting routine will work right
         LDA CHUNK        
         SEC              
         LSR              ;Strip the bit
         EOR CHUNK        
         STA CHUNK        
         TXA              
         BNE :CONT        ;If dy=0 it's just a point
         INX              
:CONT    LSR              ;Init counter to dy/2
*
* Main loop
*
YLOOP    STA TEMP         
* JSR LINEPLOT
                          
         LDA CX           ;Range check
         ORA CY           
         BMI :SKIP        
                          
         LDA (POINT),Y    ;Otherwise plot
         EOR BITMASK      
         AND CHUNK        
         EOR (POINT),Y    
         STA (POINT),Y    
:SKIP                     
YINCDEC  INY              ;Advance Y coordinate
         CPY #8           
         BCC :CONT        ;No prob if Y=0..7
         JSR FIXY         
:CONT    LDA TEMP         ;Restore A
         SEC              
         SBC DX           
         BCC YFIXX        
YCONT    DEX              ;X is counter
         BNE YLOOP        
YCONT2   LDA (POINT),Y    ;Plot endpoint
         EOR BITMASK      
         AND CHUNK        
         EOR (POINT),Y    
         STA (POINT),Y    
YDONE                     
         LDA #$37         
         STA $01          
         CLI              
         RTS              
                          
YFIXX                     ;x=x+1
         ADC DY           
         LSR CHUNK        
         BNE YCONT        ;If we pass a column boundary...
         ROR CHUNK        ;then reset CHUNK to $80
         STA TEMP2        
         LDA CX           
         BMI :CONT        ;Skip if column is negative
         CMP #39          ;End if move past end of screen
         BCS YDONE        
                          
         LDA POINT        ;And add 8 to POINT
         ADC #8           
         STA POINT        
         BCC :CONT        
         INC POINT+1      
:CONT    INC CX           ;Increment column
         LDA TEMP2        
         DEX              
         BNE YLOOP        
         BEQ YCONT2       
                          
*
* Big steps in X direction
*
* On entry, X=DY=number of loop iterations, and Y=
*   Y1 AND #$07
                          
COUNTHI  DFB 00           ;Temporary counter
                          ;only used once
STEPINX                   
         LDX DX           
         LDA DX+1         
         STA COUNTHI      
         LSR              ;Need bit for initialization
         STA Y1           ;High byte of counter
         TXA              
         BNE :CONT        ;Could be $100
         DEC COUNTHI      
:CONT    ROR              
*
* Main loop
*
XLOOP                     
         LSR CHUNK        
         BEQ XFIXC        ;If we pass a column boundary...
XCONT1   SBC DY           
         BCC XFIXY        ;Time to step in Y?
XCONT2   DEX              
         BNE XLOOP        
         DEC COUNTHI      ;High bits set?
         BPL XLOOP        
XDONE                     
         LSR CHUNK        ;Advance to last point
         JSR LINEPLOT     ;Plot the last chunk
EXIT     LDA #$37         
         STA $01          
         CLI              
         RTS              
*
* CHUNK has passed a column, so plot and increment pointer
* and fix up CHUNK, OLDCHUNK.
*
XFIXC                     
         STA TEMP         
         JSR LINEPLOT     
         LDA #$FF         
         STA CHUNK        
         STA OLDCHUNK     
         LDA CX           
         BMI :CONT        ;Skip if column is negative
         CMP #39          ;End if move past end of screen
         BCS EXIT         
                          
         LDA POINT        
         ADC #8           
         STA POINT        
         BCC :CONT        
         INC POINT+1      
:CONT    INC CX           
         LDA TEMP         
         JMP XCONT1       
*
* Check to make sure there isn't a high bit, plot chunk,
* and update Y-coordinate.
*
XFIXY                     
         DEC Y1           ;Maybe high bit set
         BPL XCONT2       
         ADC DX           
         STA TEMP         
         LDA DX+1         
         ADC #$FF         ;Hi byte
         STA Y1           
                          
         JSR LINEPLOT     ;Plot chunk
         LDA CHUNK        
         STA OLDCHUNK     
                          
         LDA TEMP         
XINCDEC  INY              ;Y-coord
         CPY #8           ;0..7 is ok
         BCC XCONT2       
         STA TEMP         
         JSR FIXY         
         LDA TEMP         
         JMP XCONT2       
                          
*
* Subroutine to plot chunks/points (to save a little
* room, gray hair, etc.)
*
LINEPLOT                  ;Plot the line chunk
                          
         LDA CX           
         ORA CY           
         BMI :SKIP        
                          
         LDA (POINT),Y    ;Otherwise plot
         EOR BITMASK      
         ORA CHUNK        
         AND OLDCHUNK     
         EOR CHUNK        
         EOR (POINT),Y    
         STA (POINT),Y    
:SKIP                     
         RTS              
                          
*
* Subroutine to fix up pointer when Y decreases through
* zero or increases through 7.
*
FIXY     CPY #255         ;Y=255 or Y=8
         BEQ :DECPTR      
:INCPTR                   ;Add 320 to pointer
         LDY #0           ;Y increased through 7
         LDA CY           
         BMI :CONT1       ;If negative, then don't update
         CMP #24          
         BCS :TOAST       ;If at bottom of screen then quit
         LDA POINT        
         ADC #<320        
         STA POINT        
         LDA POINT+1      
         ADC #>320        
         STA POINT+1      
:CONT1   INC CY           
         RTS              
:DECPTR                   ;Okay, subtract 320 then
         LDY #7           ;Y decreased through 0
         LDA CY           
         BEQ :TOAST       
         BMI :CONT2       
         CMP #$7F         ;It is possible we just decreased
         BNE :C1          ;through row 25
         LDA #24          
         STA CY           ;In which case, set correct row
:C1      LDA POINT        
         SEC              
         SBC #<320        
         STA POINT        
         LDA POINT+1      
         SBC #>320        
         STA POINT+1      
:CONT2   DEC CY           
         RTS              
:TOAST   PLA              ;Remove old return address
         PLA              
         JMP EXIT         ;Restore interrupts, etc.
                          
*
* CIRCLE draws a circle of course, using my
* super-sneaky algorithm.
*   CIRCLE cx,cy,radius (16,8,8)
*
                          
CIRCLE                    
         JSR GETPAR       
         STX CY           ;CX,CY = center
                          
         LDA X1           
         SEC              
         SBC ORGX         
         STA CX           
         STA X1           
         LDA X1+1         
         SBC #00          
         STA CX+1         
         STA X1+1         
         PHP              ;Save carry
         LSR              ;Compute which column we start
         LDA CX           ;in
         ROR              
         LSR              
         LSR              
         PLP              
         BCS :CONT        ;Underflow means negative column
         TAX              
         LDA X1           ;Set X to first column
         AND #$07         
         STA X1           
         LDA #00          
         STA X1+1         
         TXA              
         ORA #$E0         ;so set high bits
:CONT    STA RCOL         
         STA LCOL         
         BMI :SKIP        
         CMP #40          ;Check for benefit of SETPOINT
         BCC :SKIP        
         LDA X1           ;Set X in last column
         AND #$07         
         ORA #64-8        ;312+X AND 7
         STA X1           
         LDA #1           
         STA X1+1         
:SKIP                     
         JSR CHKCOM       
         JSR GETBYT       
CIRCENT                   ;Alternative entry point
         STX Y            
         STX RADIUS       
         TXA              
         BNE :C           ;Skip R=0
         LDX CY           
         JMP SETPOINT     ;Plot it as a point.
:C       CLC              
         ADC CY           
         BCS :BLAH        
         SEC              
         SBC ORGY         
         BCS :C4          ;cy+y<orgy implies circle off screen
:RTS     RTS              
                          
:BLAH    SBC ORGY         ;Always positive
         BCS :C3          ;Handle overflow sneaky
:C4      TAX              
         CMP #200         ;If Y>200 then set pointer to
         BCC :C2          ;last row, but set TROW
         CLC              ;correctly
:C3      TAY              
         AND #$07         
         ORA #$C0         ;Last row, set Y1 correctly
         TAX              
         TYA              
:C2      ROR              
         LSR              
         LSR              
         STA TROW         ;Top row
                          
         LDA #00          
         STA DONTPLOT     ;Don't plot points
         JSR SETPOINT     ;Plot XC,YC+Y
         STY Y2           ;Y AND 07
         LDA BITCHUNK,X   
         STA CHUNK1       ;Forwards chunk
         STA OLDCH1       
         LSR              
         EOR #$FF         
         STA CHUNK2       ;Backwards chunk
         STA OLDCH2       
         LDA POINT        
         STA TEMP2        ;TEMP2 = forwards high pointer
         STA X2           ;X2 = backwards high pointer
         LDA POINT+1      
         STA TEMP2+1      
         STA X2+1         
                          
         LDA CY           ;Now compute upper points
         SEC              
         SBC ORGY         
         BCS :CSET        
         SEC              ;We are so very negative
         SBC Y            
         CLC              
         BCC :BNEG        
:CSET    SBC Y            ;Compute CY-Y-ORGY
:BNEG    PHP              
         TAX              
         LSR              ;Compute row
         LSR              
         LSR              
         STA BROW         
         PLP              
         BCS :CONT        
         ORA #$E0         ;Make row negative
         STA BROW         
         TXA              
         AND #07          ;Handle underflow special!
         TAX              
:CONT    JSR SETPOINT     ;Compute new coords
         STY Y1           
         LDA POINT        
         STA X1           ;X1 will be the backwards
         LDA POINT+1      ;low-pointer
         STA X1+1         ;POINT will be forwards
                          
         SEI              ;Get underneath ROM
         LDA #$34         
         STA $01          
                          
         LDA Y            
         LSR              ;A=r/2
         LDX #00          
         STX X            ;y=0
                          
* Main loop
                          
:LOOP                     
         INC X            ;x=x+1
                          
         LSR CHUNK1       ;Right chunk
         BNE :CONT1       
         JSR UPCHUNK1     ;Update if we move past a column
:CONT1   ASL CHUNK2       
         BNE :CONT2       
         JSR UPCHUNK2     
:CONT2                    ;LDA TEMP
         SEC              
         SBC X            ;a=a-x
         BCS :LOOP        
                          
         ADC Y            ;if a<0 then a=a+y; y=y-1
         TAX              
         JSR PCHUNK1      
         JSR PCHUNK2      
         LDA CHUNK1       
         STA OLDCH1       
         LDA CHUNK2       
         STA OLDCH2       
         TXA              
                          
         DEC Y            ;(y=y-1)
                          
         DEC Y2           ;Decrement y-offest for upper
         BPL :CONT3       ;points
         JSR DECYOFF      
:CONT3   LDY Y1           
         INY              
         STY Y1           
         CPY #8           
         BCC :CONT4       
         JSR INCYOFF      
:CONT4                    
         LDY X            
         CPY Y            ;if y<=x then punt
         BCC :LOOP        ;Now draw the other half
*
* Draw the other half of the circle by exactly reversing
* the above!
*
NEXTHALF                  
         LSR OLDCH1       ;Only plot a bit at a time
         ASL OLDCH2       
         LDA RADIUS       ;A=-R/2-1
         LSR              
         EOR #$FF         
:LOOP                     
         TAX              
         JSR PCHUNK1      ;Plot points
         JSR PCHUNK2      
         TXA              
         DEC Y2           ;Y2=bottom
         BPL :CONT1       
         JSR DECYOFF      
:CONT1   INC Y1           
         LDY Y1           
         CPY #8           
         BCC :CONT2       
         JSR INCYOFF      
:CONT2                    
         LDX Y            
         BEQ :DONE        
         CLC              
         ADC Y            ;a=a+y
         DEC Y            ;y=y-1
         BCC :LOOP        
                          
         INC X            
         SBC X            ;if a<0 then x=x+1; a=a+x
         LSR CHUNK1       
         BNE :CONT3       
         TAX              
         JSR UPCH1        ;Upchunk, but no plot
:CONT3   LSR OLDCH1       ;Only the bits...
         ASL CHUNK2       ;Fix chunks
         BNE :CONT4       
         TAX              
         JSR UPCH2        
:CONT4   ASL OLDCH2       
         BCS :LOOP        
:DONE                     
CIRCEXIT                  ;Restore interrupts
         LDA #$37         
         STA $01          
         CLI              
         LDA #1           ;Re-enable plotting
         STA DONTPLOT     
         RTS              
*
* Decrement upper pointers
*
DECYOFF                   
         TAY              
         LDA #7           
         STA Y2           
         LDA TROW         ;First check to see if Y is in
         BEQ EXIT2        
         CMP #25          ;range (rows 0-24)
         BCS :SKIP        
         LDA X2           ;If we pass through zero, then
         SEC              
         SBC #<320        ;subtract 320
         STA X2           
         LDA X2+1         
         SBC #>320        
         STA X2+1         
         LDA TEMP2        
         SEC              
         SBC #<320        
         STA TEMP2        
         LDA TEMP2+1      
         SBC #>320        
         STA TEMP2+1      
:SKIP    TYA              
         DEC TROW         
         RTS              
EXIT2    PLA              ;Grab return address
         PLA              
         JMP CIRCEXIT     ;Restore interrupts, etc.
                          
* Increment lower pointers
INCYOFF                   
         TAY              
         LDA #00          
         STA Y1           
         LDA BROW         
         BMI :ISKIP       ;If <0 then don't update pointer.
         CMP #24          ;If we hit bottom of screen then
         BEQ EXIT2        ;just quit
         LDA X1           
         CLC              
         ADC #<320        
         STA X1           
         LDA X1+1         
         ADC #>320        
         STA X1+1         
         LDA POINT        
         CLC              
         ADC #<320        
         STA POINT        
         LDA POINT+1      
         ADC #>320        
         STA POINT+1      
:ISKIP   TYA              
         INC BROW         
         RTS              
                          
*
* UPCHUNK1 -- Update right-moving chunk pointers
*             Due to passing through a column
*
UPCHUNK1                  
         TAX              
         JSR PCHUNK1      
UPCH1    LDA #$FF         ;Alternative entry point
         STA CHUNK1       
         STA OLDCH1       
         LDA RCOL         
         BMI :DONE        ;Can start negative
         LDA TEMP2        
         CLC              
         ADC #8           
         STA TEMP2        
         BCC :CONT        
         INC TEMP2+1      
         CLC              
:CONT    LDA POINT        
         ADC #8           
         STA POINT        
         BCC :DONE        
         INC POINT+1      
:DONE    TXA              
         INC RCOL         
         RTS              
                          
*
* UPCHUNK2 -- Update left-moving chunk pointers
*
UPCHUNK2                  
         TAX              
         JSR PCHUNK2      
UPCH2    LDA #$FF         
         STA CHUNK2       
         STA OLDCH2       
         LDA LCOL         
         CMP #40          
         BCS :DONE        
         LDA X2           
         SEC              
         SBC #8           
         STA X2           
         BCS :CONT        
         DEC X2+1         
         SEC              
:CONT    LDA X1           
         SBC #8           
         STA X1           
         BCS :DONE        
         DEC X1+1         
:DONE    TXA              
         DEC LCOL         
         RTS              
*
* Plot right-moving chunk pairs for circle routine
*
PCHUNK1                   
                          
         LDA RCOL         ;Make sure we're in range
         CMP #40          
         BCS :SKIP2       
         LDA CHUNK1       ;Otherwise plot
         EOR OLDCH1       
         STA TEMP         
         LDA BROW         ;Check for underflow
         BMI :SKIP        
         LDY Y1           
         LDA (POINT),Y    
         EOR BITMASK      
         AND TEMP         
         EOR (POINT),Y    
         STA (POINT),Y    
                          
:SKIP    LDA TROW         ;If CY+Y >= 200...
         CMP #25          
         BCS :SKIP2       
         LDY Y2           
         LDA (TEMP2),Y    
         EOR BITMASK      
         AND TEMP         
         EOR (TEMP2),Y    
         STA (TEMP2),Y    
:SKIP2                    
         RTS              
                          
*
* Plot left-moving chunk pairs for circle routine
*
PCHUNK2                   
                          
         LDA LCOL         ;Range check in X
         CMP #40          
         BCS :SKIP2       
         LDA CHUNK2       ;Otherwise plot
         EOR OLDCH2       
         STA TEMP         
         LDA BROW         ;Check for underflow
         BMI :SKIP        
         LDY Y1           
         LDA (X1),Y       
         EOR BITMASK      
         AND TEMP         
         EOR (X1),Y       
         STA (X1),Y       
                          
:SKIP    LDA TROW         ;If CY+Y >= 200...
         CMP #25          
         BCS :SKIP2       
         LDY Y2           
         LDA (X2),Y       
         EOR BITMASK      
         AND TEMP         
         EOR (X2),Y       
         STA (X2),Y       
:SKIP2                    
         RTS              
                          
*
* GRON -- turn graphics on.  If a number appears
* afterwards, then initialize the colormap to that
* number and clear the bitmap.
*
BASE     DFB $E0          ;Address of bitmap, hi byte
BANK     DFB 0            ;Bank 3=default
OLDBANK  DFB $FF          ;VIC old bank
OLDD018  DFB 00           
                          
GRON                      
         LDA $D011        ;Skip if bitmap is already on.
         AND #$20         
         BNE CLEAR        
         LDA $DD02        ;Set the data direction regs
         ORA #3           
         STA $DD02        
         LDA $DD00        
         PHA              
         AND #$03         
         STA OLDBANK      
         PLA              
         AND #252         
         ORA BANK         
         STA $DD00        
                          
         LDA $D018        
         STA OLDD018      
         LDA #$38         ;Set color map to base+$1C00
         STA $D018        ;bitmap to 2nd 8k
                          
         LDA $D011        ;And turn on bitmap
         ORA #$20         
         STA $D011        
                          
CLEAR    JSR CHRGOT       ;See if there's a color
         BEQ GRONDONE     
         JSR GETBYT       ;Get the char
CLEARCOL LDA #00          ;Low byte of base address
         STA POINT        
         LDA BASE         ;Colormap is at base-$14
         SEC              
         SBC #$14         
         STA POINT+1      
         TXA              
         LDY #00          
         LDX #4           
:LOOP    STA (POINT),Y    
         INY              
         BNE :LOOP        
         INC POINT+1      
         DEX              
         BNE :LOOP        
         LDA BASE         ;Now clear bitmap
         STA POINT+1      
         LDX #32          
         TYA              
:LOOP2   STA (POINT),Y    
         INY              
         BNE :LOOP2       
         INC POINT+1      
         DEX              
         BNE :LOOP2       
                          
GRONDONE RTS              
                          
* GROFF -- Restore old values if graphics are on.
GROFF                     
         LDA $D011        
         AND #$20         
         BEQ GDONE        
GSET     LDA $DD02        ;Set the data direction regs
         ORA #3           
         STA $DD02        
         LDA $DD00        
         AND #$7C         
         ORA OLDBANK      
         STA $DD00        
                          
         LDA OLDD018      
         STA $D018        
                          
         LDA $D011        
         AND #$FF-$20     
         STA $D011        
GDONE    RTS              
                          
*
* COLOR -- Set drawing color
*
COLOR                     
         JSR GETBYT       
COLENT   CPX #00          ;MODE enters here
         BEQ :C2          
:C1      CPX #01          
         BNE :RTS         
         LDX #$FF         
:C2      STX BITMASK      
:RTS     RTS              
                          
*
* MODE -- catch-all command.  Currently implemented:
*   00  Erase (background color)
*   01  Foreground color
*   16  SuperCPU mode -- screen -> A000, etc.
*   17  Normal mode
*   18  Double buffer mode
*
*  Anything else -> BITMASK
*
MODENUM  DFB 17           ;Current mode
MODE                      
         JSR GETBYT       
         CPX #2           
         BCC COLENT       
:C16     CPX #16          
         BNE :C18         
         STX MODENUM      
:SET16   LDA #$A0         ;Bitmap -> $A000
         STA BASE         
         LDA #01          
         STA BANK         ;Bank 2
         STA OLDBANK      
         LDA #$FF         ;End of BASIC memory
         STA $37          
         STA $33          
         LDA #$87         
         STA $38          
         STA $34          
         LDA #$24         ;Screen mem -> $8800
         STA OLDD018      
         JSR GSET         ;Part of GROFF
         LDA #$88         
         STA 648          ;Tell BASIC where the screen is
         STA $D07E        ;Enable SuperCPU regs
         STA $D074        ;Bank 2 optimization
         STA $D07F        ;Disable regs
         RTS              
:C18     CPX #18          ;Double-buffer mode!
         BNE :C17         
         STX MODENUM      
         JSR :SET16       ;Set up mode 16
         STA $D07E        
         STA $D077        ;Turn off optimization
         STA $D07F        
         RTS              
:C17     CPX #17          
         BNE MODEDONE     
MODE17   STX MODENUM      
         LDA #$E0         
         STA BASE         
         LDA #00          ;Bank 3
         STA BANK         
         LDA #3           ;Bank 0 == normal bank
         STA OLDBANK      
         LDA #$FF         
         STA $37          
         STA $33          
         LDA #$9F         
         STA $38          
         STA $34          
         LDA #$14         ;Screen mem -> $0400
         STA OLDD018      
         JSR GSET         ;Part of GROFF
         LDA #$04         
         STA 648          ;Tell BASIC where the screen is
         STA $D07E        
         STA $D077        ;No optimization
         STA $D07F        
         RTS              
MODEDONE STX BITMASK      
         RTS              
                          
                          
*
* BUFFER -- Sets the current drawing buffer to 1 or 2,
*   depending on arg being even or odd.  If double-
*   buffer mode is not enabled then punt.
*
*   Now, buffer=0 swaps draw buffers, even/odd otherwise.
*
BUFFER                    
         JSR GETBYT       
         LDA MODENUM      
         CMP #18          
         BNE :PUNT        
         LDY #$A0         
         TXA              
         BNE :CONT        
         CPY BASE         
         BNE :CONT        
         LDA #1           
:CONT    LSR              
         BCC :LOW         ;even = low buffer
         LDY #$E0         ;odd = high buffer
:LOW     STY BASE         
:PUNT    RTS              
                          
*
* SWAP -- Swap displayed buffers.  MODE 18 must
*   be enabled first.
*
SWAP                      
         LDA MODENUM      
         CMP #18          
         BNE :PUNT        
         LDA $DD00        ;Ooooooohhh, real tough!
         EOR #$01         
         STA $DD00        
:PUNT    RTS              
                          
*
* ORIGIN -- Set upper-left corner of the screen to
*   new coordinate offset.
*
ORIGIN                    
         JSR GETBYT       
         STX ORGX         
         JSR CHKCOM       
         JSR GETBYT       
         STX ORGY         
         RTS              
                          
         ORG              ;re-org
PEND                      ;To get that label right :)

--
The BLARG distribution binary is available from 'The Fridge', Mr. Judd's
archival web page on the internet at http://stratus.esam.nwu.edu/judd/fridge



:::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::3::::::::::::::::::::
/S02::$d000:::::::::::::::::::S O F T W A R E:::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


A CLOSER LOOK AT THE VIC II'S OUTPUT
by Adrian Gonzalez (DW/Style) & George Taylor (Repose/Style)
------------------------------------------------------------------------------
         1         2         3         4         5         6         7
123456789012345678901234567890123456789012345678901234567890123456789012345678


Contents
--------

0.  Preface

1.  Introduction
     1.1  Target audience
     1.2  The legal stuff

2.  The NTSC Y/C video signal
     2.1  A little history
     2.2  A closer look at black and white TV
     2.3  Let's talk about color
     2.4  The chroma signal

3.  Wrapup

    Bibliography


0.  Preface
-----------

This text was originally formatted to 78 columns for ease of use.  This
ensures compatibility with MSDOS "edit" with scrollbars, as well as 80 column
readers that cause double spacing if the 81st character is a carriage return.
The text is written in conversation style to ease the problem of dual
authorship.  Subsections not marked may be considered generic text and may
have been written by either of us.  You will not be able to view the ASCII
diagrams in 40 columns. Major headings are marked with ---'s.


1.  Introduction
----------------

DW:

While working on an image conversion project, I stumbled upon the need to
find RGB values for the c64's colors.  My first idea was using two monitors
and matching the colors "by eye", however, this turned out to be more
difficult than I expected.  After trying other methods I decided the next
logical step was to analyze the c64's output and determine the RGB values
from there.  Simple enough, eh?  The only problem was that I had no clue as
to what the VIC-II's output looked like.  After digging up some books on TV
theory, I decided to embark on a quest to find RGB values for the c64's
colors, and I found many interesting things along the way. 

This article is the result of many hours of research, programming, and taking
measurements.  I hope you find it interesting and perhaps even learn
something new from it (!).

Repose:

Coincidently, I was working on exactly the same kind of image conversion
project.  So I got together with DW to discuss our common problem of finding
the RGB colors of the 64.  I also put out a request to comp.sys.cbm and was
sent measurements from several people, including an excellent effort from
Marko Makela.

1.1  Target audience

This article should be very interesting for people doing emulators, image
converters, and other similar projects.  Due to the rather technical nature
of its content, it is not meant for beginners, however, the end result could
be very useful for anybody interested in using other platforms to do
graphics work for the c64.  If you're not sure whether this article is for
you or not, here are some terms you should be familiar with:  scanline,
frame, refresh rate, CRT, RGB, vertical and horizontal blanking.  These terms
are used all throughout the article, and it is assumed you already have a
basic knowledge of how a TV works.

1.2  The legal stuff

Part of this article deals with taking measurements from your c64.  If you
decide to tinker with your c64 and something goes wrong, it's your fault, not
ours.  The authors will not be held responsible for damaged equipment, data
loss, loss of sleep, loss of sanity, etc.  Now, on with the show.



2.  The NTSC Y/C video signal
-----------------------------

There's basically three ways you can connect your c64 to a TV or monitor and
get a color picture.  The first and perhaps the most common is to use the RF
modulated output with a TV tuned to channel 3 or 4.  The second and third
use the Video/Audio connector and require either a monitor or a TV/VCR with
A/V inputs.  We will get to each of these later on, but first, a little bit
of history.

2.1  A little history.

DW:

In 1953, the National Television Systems Committee (NTSC) developed a
standard that allowed the transmission of color images while remaining
compatible with the large amount of black and white TV sets in widespread use
at the time.   In the US, public broadcasting (using the color NTSC system)
began in 1954.  The same system was adopted in Japan, where it came into
service in 1960. Other countries favored modifications of the NTSC system,
such as PAL (Phase Alternating Line) and SECAM (Systeme Electronique Couleur
Avec Memoire).

Repose:

PAL is used in many western european countries, and has technical advantages
to NTSC.  SECAM is used in eastern and middle eastern europe.  It is only a
semi standard in a way because video production is always done in PAL format,
and only converted to SECAM for final transmission.  The main point of this
was information control, as it offers no advantages over PAL.  As far as I
know, VIC IIs were only made to conform to PAL or NTSC standards.

2.2  A closer look at black and white TV.

In designing a TV system, the engineers had to make several considerations.
One had to do with bandwidth, which is the space that a TV channel takes in
the radio frequency spectrum.  To allow for many channels, and also to be
easy for the ancient technology of that time, it was decided to split up the
picture into two parts, and send each half sequentially.

The display on your TV is made up of a several hundred scanlines, composed
of two fields which are interlaced to form a complete display, called a
frame.  This interlacing doesn't happen on the c64, but we will get to that
later.  First let's look at the fields:

Odd field                                Even field

                                     Scanline     1 +++++++++++++++++++++++
Scanline   2 ----------------------
                                     Scanline     3 +++++++++++++++++++++++
Scanline   4 ----------------------
                                     Scanline     5 +++++++++++++++++++++++
Scanline   6 ----------------------      .
.                                        .
.
                                     Scanline 2*n-1 +++++++++++++++++++++++
Scanline 2*n ----------------------
                                     Scanline 2*n+1 +++++++++++++++++++++++

Each of the fields is 262 1/2 lines long (NTSC), (312 1/2 PAL) which means
each frame is 525 (625 PAL) lines long.  Your TV displays odd fields and
even fields one after another, and thanks to what is called "persistence of
vision" we see something like:

Scanline     1 +++++++++++++++++++++++
Scanline     2 -----------------------
Scanline     3 +++++++++++++++++++++++
Scanline     4 -----------------------
Scanline     6 +++++++++++++++++++++++
.
.

The second consideration the engineers had was how to represent a 2d image as
a 1d voltage.  To do this, they needed markers to separate a horizontal line
and also odd and even fields.

So, let's take a closer look at what the voltage waveform for black and
white scanlines looks like:

^  Voltage
|
|             Scanline n                                  Scanline n+2
|          ___________________________________         _________________..
|         |   White level                    |         |
|         |                                  |         |
|         |                                  |         |
|         |                                  |         |
|         |                                  |         |
|___  ____|   Black level                    |___  ____|
|  |  |                                         |  |
|  |__| <- Horizontal sync pulses ------------> |__|
|
-------------------------------------------------------------------------> 
Time

The scanline in this example is a simple white line.  If you were to feed
enough of  these  to  your  TV  plus  some vertical sync signals, you would
get a white screen.  The horizontal sync pulses tell the TV receiver when a
scanline starts.  They  make  up  25% of  the  total  height  of  the signal.

The brightness at a particular point of the scanline is defined by the
voltage of  the  waveform  at that instant.  With this in mind, if we wanted
to create a display with a simple white vertical line at the middle of the
screen, the waveform would look like this:

^  Voltage
|
|                   Scanline n                              Scanline n+2
|                        __                                           __
|           White level |  |                                         |  |
|                       |  |                                         |  |
|                       |  |                                         |  |
|                       |  |                                         |  |
|           Black level |  |                                         |  |
|___  __________________|  |_____________________  __________________|  |
|  |  |                                         |  |
|  |__| <- Horizontal sync pulses ------------> |__|
|
-------------------------------------------------------------------------> 
Time


Let us now turn our attention to the visible display area on your TV:

___                                                          ___
 ^        XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX     6% of frame
 |        XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ___ Vert. blank
 |        XXXXXXXX                                               ^
 |        XXXXXXXX                                               |
 |        XXXXXXXX                                               |
 |        XXXXXXXX                                               |
 | Field  XXXXXXXX                                               |
 | period XXXXXXXX                                               |
 | (60hz) XXXXXXXX                                               |
 |        XXXXXXXX                                               |
 |        XXXXXXXX                                               |
 |        XXXXXXXX                                               |
 |        XXXXXXXX                                               |
 |        XXXXXXXX                                               |
 |        XXXXXXXX                                               |
 |        XXXXXXXX                                               |
_v_       XXXXXXXX                                              _v_
          |<---->|<---------------------------------------->|
           17% of                  83% of line
           line                    is visible
           Horiz.
           blank
          |<-------------------------------------------------->|
                      Line period: 63.5 microseconds

The X's in the graph represent areas in which the in which your screen is
not visible.  At the top we have the vertical blanking interval, and at the
left we have the horizontal blanking interval.  Please note that whether
these intervals occur at the top or bottom, left or right or both, is not
relevant. It is shown this way in the diagram for the sake of clarity. So,
with that out of the way, let's see what happens in the vertical blank
interval (vblank from now on):

^ Voltage
|
|    last                                                        time   ---->
|    scanline
|  __ _______ ___ ___ ___ ___ ___ __                            _ ___ ___ _..
|    |       |   |   |   |   |   |  |    |   |   |   |   |   | | |   |   |
|    |       |   |   |   |   |   |  |____|___|___|___|___|___|_| |   |   |
     ^       ^                                      ^            ^
Horizontal   End of even field         Serrated vertical sync    6 equalizing
sync pulse   6 equalizing pulses       pulse                     pulses

Please note that the time scale has been compressed to be able to cover most
of the vblank interval.  Basically the vblank interval is composed of 6
equalizing pulses, one serrated vertical sync pulse, and 6 more equalizing
pulses.  The equalizing pulses and the serrations in the vertical sync pulse
are spaced half a scanline apart.  An interesting thing happens if we take
away the equalizing pulses and serrations:

^ Voltage
|
|    last                                                        time   ---->
|    scanline
|  _________________________________                            ___________..
|                                   |                          |
|                                   |__________________________|

                                       Vertical sync pulse

We end up with a vertical sync pulse that looks very much like the
horizontal sync pulses, except it runs at a much lower frequency.  In NTSC,
the field frequency is 60 Hz, and the frame frequency is 30hz (PAL has a 50Hz
field rate and 25Hz frame rate).   In other words, we get one of these
vertical pulses on every field, to let the TV know when the field starts
(just like the horizontal sync pulses tell it when a line starts).  According
to an old book on TV theory, "serrations are placed in the vertical sync
pulse to stabilize the operation of the horizontal scanning generator during
vertical retrace time".

One last thing remains before we move on to color television: interlacing.
Interlacing is achieved by making the field size 262 1/2 lines long, which
also changes the spacing between the equalizing pulses and the vertical sync
pulse. It will not be discussed any further, though, because the c-64's
output is not interlaced.  Instead, it is composed of always fields, thus
there are visible spaces between the scanlines.  The field rate may be
considered also the frame rate, since one field is a complete image in
itself.

Note:  it is not known to us if the c64 produces even or odd fields.

2.3  Let's talk about color.

Up until now, we've only talked about black and white TV signals, but if you
remember the little bit of history in section 2.1, you know that color has
to be introduced in a way that doesn't interfere with the BW signal.  The
VIC-II really outputs two signals (NTSC version only):  the Luminance (Y)
signal and the Chrominance (C) signal.  The Y signal contains the brightness
information of the output, while the C signal contains color information.
The Y signal is just like the one described in the previous section, except
it is not interlaced.  The c64 has circuitry to mix the Y and C signals into
a composite video signal which has both BW and color info.  It then goes to
an RF modulator which allows these signals (plus audio) to be viewed on a TV
tuned to channel 3 or 4 (in the North American TV frequency).

Before we move on to describe the color signal, we will talk briefly about
RGB.  RGB stands for Red Green Blue, or in other words, a set of three
primary colors, which are mixed in different amounts in order to obtain a
broad gammut (range) of colors.  An interesting thing to note is that it is
impossible to reproduce the entire spectrum of visible light with a set of
three (or any finite number) of real primary colors, however, RGB and other 
color systems do a fair job of being able to represent typical images.
RGB color is probably quite familiar to you, as it is the basis of the video
circuitry in most computers and their monitors.  However, NTSC/PAL signals
use an encoding called YUV.  Y is the brightness information, while UV are
the C or color information.  U and V are components of a vector.  You can 
picture this vector as originating from the centre of a circle.  It's angle
is the tint of the color, and it's radius is the saturation of the color.
The angle represents a full sprectrum, from red, to orange, yellow, green,
blue, purple, back to red again.  A highly satured color will be pure, while
no saturation would give a grey.  There are usually controls related to these
properties on a TV, called tint and color, or hue and saturation.

2.4  The color signal

Color was added to B/W NTSC in a very clever way.  It was added as a carrier
on top of the normal signal.  It is basically a sinewave of 3.574545Mhz
(NTSC) (4.43Mhz PAL) with a varying phase and amplitude.  To a B/W TV, this
will be seen as a fine wavey detail in the luminance.  But, a color TV uses a
filter to separate the high frequencies as the color signal, and pass on the
lower frequencies as the normal B/W signal.  This separation is not perfect,
and can cause several strange effects on the picture, known as "crawling
dots", "hanging dots", and "color moire patterns" (NTSC, similar effects may
appear in PAL).  That is why, coincidently, the S-VHS format was invented, to
physically separate the color signal from the luma signal.  One question,
that may have occurred to you, is how do you measure phase without a
reference point?  Fortunately, at the start of every rasterline there is a
calibration color signal known as the color burst, and from this sinewave,
the phase of the color signal is measured.

The sinewave then continues, at the same time as the luminence portion of 
rasterline is sent, both signals varying to specify a color and luminance.

   ***
  *   *           *
 *     *         * 
 *     *         *
*-------*-------*----> Time
         *     *
         *     * 
          *   *
           *** 
ColorBurst Reference Signal

     ***
    *   *
   *     * 
   *     *
--*-------*-------*--> Time
 *         *     *
 *         *     * 
*           *   *
             *** 
An Orange color 30 degrees
out of phase with ColorBurst


3.  Wrapup
----------

For now, we will end here.  Look forward to a second part, which will 
describe the signals as measured on the 64 in more detail, and finally 
provide the most accurate measurements of the 64's 16 colors known.  One 
important point which will be explained is how gamma affects RGB color 
measurements, and why any measurement must be given with gamma information,
and why putting the same RGB values into two different monitors will not 
cause the same colors to be seen.

Also provided will be a program which can display the 64s colors on a PC and
let you play with hue and saturation controls.  When our measurements are
finalized, we would expect all emulator writers to add our calibrated 
colors to their programs, so that the original feeling of the 64 can be
retained.

Please see http://nlaredo.globalpc.net/~agonzalez/index.html
for up to date information.


Bibliography
------------

Introduction to Digital Video, Charles Poynton.
comp.graphics FAQ.
Television Theory and Servicing, second edition.  Clyde N. Herrick



:::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::3::::::::::::::::::::
/S03::$d000:::::::::::::::::::S O F T W A R E:::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
 
 
                        : Innovation in the 90s :
 
Revisiting The Super Hi-Res Interlace Flexible Line Interpretation Technique

         by  Roland Toegel (Crossbow/Crest), Count Zero/TRC*SCS, and
             George Taylor (aa601@chebucto.ns.ca)


Prelude from George Taylor, technical editor
----------------+--+--+---------------------
Once you have FLI, why not just overlay some sprites on it to make better
graphics?  A simple idea you may have thought of, but as you will see,
implementing the concept was very difficult.  There are two key concepts
to making it work.  The following article is a brilliant piece of work on
the subject.  It was presented in disC=overy (issue 2) and subsequently
raised a great deal of discussion.  There are some confusing concepts
involved so I have added extra interpretation to the original text as
as editorial notes, which I hope will help bridge the gap from foreign
language translated text and difficult explanations.
 
Prelude from Count Zero
--------+--+--+--------

First seen earlier this year, the new SHIFLI video technique that is described
in this article is -the- paramount example of programming brilliance.  The
inventor of the technique, Roland Toegel (Crossbow of Crest), was helpful
in providing the original SHIFLI documentation, which I translated from 
German into the English text that follows for the exclusive use of the
disC=overy journal.

So without further ado, let us now learn from Mr. Toegel how to achieve
the award winning :
 
   'Super Hires InterLace Flexible Line Interpretation' Graphics mode !
 
Please note that the technique is described primarily for Commodore computers
based on the European/Australian PAL TV standard. NTSC-based Commodore machines
require an extra 2 cycles per line to be added to the raster routine.  
This may require the programmer to time out the routines by hand, but this
should not be a major obstacle to overcome.
 
Count Zero
--
 
i.      Foreword
 
As the inventor of the SHI-FLI mode, I am pleased to have the services of
Count Zero and the disC=overy journal for the dissemination of my technique
into the English language.  I must add that the text below does require the
reader to be already familiar to a high degree with VIC-II programming on the
C64.  I would suggest books such as 'Mapping the 64' and the programming texts
found at ftp.funet.fi/pub/cbm/documents, for a solid base of instruction.  Also,
some terms used in this document (e.g., mix-color) are meant to be uniquely
descriptive and hence, will not be found in any 'standard' programming text.
The terminology is a result of the strain that occurs when new methodology
meets old semantics.  However, the experienced programmer should find the
words to be self-evident in the context which they are used.

[Mix-color: a new color derived from the original 16 by the process
of alternating normal colors at such a high rate of speed that they
blend to the eye.  Also know as flashcolors. -GT]
--
 
1.      Introduction to Super Hires

[Hires FLI: a graphics mode which uses rasters to enable the use of a separate
color memory on each line, thus giving 2 colors chosen freely in each
8x1 hires pixel area. Interlace: alternating any two graphics modes to allow
the use of mix colors. FLI without the 'hires' modifier usually means
the use of the FLI technique with a multicolor mode as the basis.  Super
Hires FLI might be more aptly named as Sprite Hires FLI, but the super means
that sprites are overlayed on top of another graphics mode. -GT] 

Super-Hires Interlace FLI : The absolute successor of the Super-Hires-Modes.
Just like normal Super-Hires, the width is 96 Hires Pixels, which is equal to
12 Characters or 4 Sprites next to each other.  For visual enjoyment this
area is centered though using Char-Position 15 to 26 inclusive on the
screen.  The Y-Axis is 167 pixels high having nearly 21 Character lines or
8 Sprites and to have as much flexibility as possible on choosing colors or
pixels, 2 layers of sprites in a row of 4 beside each other are used
(8 sprites on the rasterline) over the bitmap graphics.  Due to the fact that
all 8 sprites are used for a 96 * 21 pixel-wide area, a small multiplexer is
needed to repeat the 8 layered sprites for 8 times, at 21 pixel intervals as
you go dowards on the screen, with each row using their own bitmaps.  
We thereby win 2 colors plus the 2 normal colors of the Hires-Bitmap Mode 
in an 8*8 pixel block.  Please note that the 2 additional colors
are the same throughout the whole picture.
 
 
2.      Super Hires with FLI !?!
 
FLI is, for most coders, still quite hard. Sprites over FLI, for most, quite
impossible. Maybe one or two sprites, but 8 !?! next to each other and
still FLI in each rasterline!  Hard to believe, eh?
 
First of all you need to know how to do FLI and what it does and also
what effect sprites have on it.
 
 
2.1     Normal FLI
 
On each eighth rasterline (the Badlines, the first rasterline of each charline)
the VIC stops the processor for 40-43 cycles to read the new Characters and
colors of the video and color-ram.  This is the case when the bits 0-2 of the
registers $D011 and $D012 are the same.  Now if you change on each rasterline
the bits 4-7 of $D018, which holds the length of the video-ram (handling the
colors on bitmap graphics) and set the bits 0-2 of $D011 to get a badline on
each rasterline, you will get new colors on each rasterline in the bitmap
graphics.  The only condition to be followed is that on each rasterline 23
cycles are used but for the *used cycles - 22* char of the textline the
next 3 chars have the byte $FF (light grey) read from the video ram and the
FLI effect starts after that (the FLI bug).  In the multicolor mode, for the
color-ram the next byte in the program after writing to $D011 is chosen as
the color.  As we are using the Hires Mode here, this is irrelevant.
 
> NOTE: The last 3 sentences were pretty hard to translate and I advise you
>       to read other articles about FLI aswell, if you want to know more
>       about Multicolor FLI. (CZ)
 
 
2.2     Sprites over FLI
 
So what does a sprite do over FLI?  Pretty simple, as it just eats up some
cycles.  All 8 sprites use 19 cycles per rasterline, meaning in the case where 
we code our FLI routine without loops, we just need 2 x (LDA, STA) commands 
(for $D018 and $D011), using 12 cycles per rasterline.  All together with the
8 used sprites that makes 31 cycles.  Thus, the 'light grey' FLI-Bug occurs on
char-positions 9,10 and 11 and from char 12+, the FLI effect comes up.  This
doesn't matter much to us, as the Super Hires Picture starts at Char-Pos 15.
We therefore get 3 cycles per rasterline for other commands.

[The explanation of when the FLI effect starts in 2.1 was unclear.  Here
we can understand that the effect starts 3 cycles after the write to $d011.
The only catch is that this code is delayed by the extra DMA time used by
the sprites.  Thus the formula (12+19)-22=9 gives the start of the grey
pattern, and 9+3=12 is the start of the FLI effect.  The constant 22 comes
from a measurement of when the CPU continues after DMA is released, relative
to the position on the raster line. -GT]

 
3.      Mulitplexing over FLI
 
Now we have to increase the Y-Coordinates of the sprites by 21 pixels
each 21 rasterlines and give them new patterns.  As we use 8 different
video-rams on FLI for the colors and the sprite-pointers are always at the
end of the video-ram, we are supposed to write 8 * 8 values for the patterns
plus 8 values for the Y-Coordinates, resulting in 72 different addresses.
Thats far too much for a single rasterline and adding the FLI routine will
bust the limits.  Therefore we have to do a little trick.
 
 
3.1     Changing the sprite-pointers

The trick is not to change anything at all!  On the other hand we don't want
the patterns to look the same everywhere.  Luckily, the height of a sprite
(21 pixels) is not capable of being divided by the height of a textline
(8) and the smallest mutual multiple is 168 (meaning 21 * 8).  As we are
writing (due to the FLI) a new value to $D018 on each rasterline and we use
8 video rams, we can abuse this and have different sprite-pointers on every
video-ram.  The handling of where the graphics for the sprite-patterns are
located becomes a little bit confusing, but it doesn't eat up any rastertime
as we don't have to change the pointers.

[Put another way, we take advantage of the fact that the video
matrix location is changing every raster line (and therefore also the
sprite pointers at the end of the video matrix), and slice up the sprites
so that the bitmap definition is located in a different place in memory
for each line of the sprite.  This is very confusing, and the graphics
are spread throughout memory in essentially random locations to make
them fit around the other graphics areas.  But at least it is possible
to give every part of every sprite a separate bitmap without changing
sprite pointers at all, and is the first key concept to making this
technique work, and a brilliant invention by Mr. Togel. -GT]


3.1.1   Table to illustrate the sprite pattern-handling
 
Spriteline      Video-Ram     Screenline (NOT equal to rasterline!)
 
1               1             1
2               2             2
3               3             3
4               4             4
5               5             5
6               6             6
7               7             7
8               8             8
 
9               1             9
10              2             10
11              3             11
12              4             12
13              5             13
14              6             14
15              7             15
16              8             16
 
17              1             17
18              2             18
19              3             19
20              4             20
21              5             21
--------------------------------
1               6             22
 
2               7             23
3               8             24
 
4               1             25
.               .             .
.               .             .
.               .             .
20              1             41
21              2             42
--------------------------------
1               3             43
2               4             44
3               5             45
.               .             .
.               .             .
.               .             .
 
 
3.1.2   Example
 
Small example for Sprite 0 under the following conditions:
 
Used 8 Video-Rams            : $4000 - $5FFF
content of the sprite-pointer: $43F8  80 00 00 00 00 00 00 00
                               $47F8  81 00 00 00 00 00 00 00
                               $4BF8  82 00 00 00 00 00 00 00
                               $4FF8  83 00 00 00 00 00 00 00
                               $53F8  84 00 00 00 00 00 00 00
                               $57F8  85 00 00 00 00 00 00 00
                               $5BF8  86 00 00 00 00 00 00 00
                               $5FF8  87 00 00 00 00 00 00 00
 
Thus the Sprite-Patterns are in memory from $6000-$61FF.
 
Therefore the pattern-handling looks like this:
 
Screenline      Memorylocation
 
1               $6000-$6002
2               $6043-$6045
3               $6086-$6088
4               $60C9-$60CB
5               $610C-$610E
6               $614F-$6151
7               $6192-$6194
8               $61D5-$61D7
9               $6018-$601A
10              $605B-$605D
11              $609E-$60A0
12              $60E1-$60E3
13              $6124-$6126
14              $6167-$6169
15              $61AA-$61AC
16              $61ED-$61EF
17              $6030-$6032
18              $6073-$6075
19              $60B6-$60B8
20              $60F9-$60FB
21              $613C-$613E
22              $6180-$6182
23              $61C3-$61C5
.               .
.               .
.               .
 
 
3.1.3   Remark to the display-routine of the editor
 
As the changing of the video-ram on the editor-routine happens inside of the
textscreen (but the spritepointers are read inside the sideborder), the change
takes effect one rasterline later.  

[To state this again, the changes in the raster routine are made at a time
when the sprite pointers have already been read by the VIC, therefore,
even though the changes are made on the same raster line, they don't
take effect until the next raster line. -GT]

This means that whenever the colors for the bitmap of color ram 2 are read,
the sprite pointers or video ram 1 are still active.  This is the reason why
the editor uses only 167 screenlines (instead of 168) and why the first
textline of the first video-ram and the first rasterline of the bitmap
stays empty.

[I would consider this a minor bug which potentially could be fixed in
a future version, by starting the raster routine one line earlier. -GT]

 
3.2     Changing the Sprite Y-Values
 
As the changing of the sprite pointers more or less happens by itself, we just
have to make sure the correct Y-Value comes into the game.  These are still
8 values, but they don't have to be set in one rasterline and we got 21
rasterlines to set them.  As we have just 3 cycles left on each rasterline on
the FLI routine described and that wouldn't be enough for a simple LDA : STA,
we have to change the FLI routine a little bit.
 
 
3.2.1   Load new sprite Y-Value
 
As the height of all sprites is the same, we just have to do a single
LDX #$VALUE.  Therefore, 2 of the 3 free cycles are used and we cannot do
anything else with the last free cycle.
 
LDX #$VALUE
LDA #$08
STA $D018
LDA #$38
STA $D011
 
 
3.2.2   Preload the Upcoming $D011 Value
 
As a STA $SPRITE0Y needs 4 cycles, we cannot include it in the next rasterline,
but we can already load the next $D011 value into the Y-Register.  The free
cycle stays unused.

LDY #$3A
LDA #$18
STA $D018
LDA #$39
STA $D011
 
 
3.2.3   Write new Sprite Y-Value
 
As we already did the loading of the $D011 value for the next line, we now
have 5 cycles left and therefore enough time for a STA $SPRITE0Y.
 
STX $SPRITE0Y
LDA #$28
STA $D018
STY $D011
 
Now plot 3.2.2 and 3.2.3 have to be repeated for the remaining 7 sprites with
changed values for $D011 and $D018.  So there is now 1 rasterline for loading
the new sprite Y-value and 8 * 2 rasterlines to write the new sprite Y-Value.
We now have 17 rasterlines and the 4 remaining ones just need an additional
NOP so that all rasterlines use the same amount of cycles.

[This tricky piece of raster code is the second brilliant concept to
making the technique work.  Note also that there will be some staggering
in the timing of FLI effect, due to the unused cycles, but still
within the parameters required.  Also it seems that parts of the
row of 4 sprites in 2 layers would gradually be moved downward after
parts of them have already been drawn, therefore causing a repetition
of graphics, except for the fact that the sprite pointers are different
in the new position, and that the sprites are really sliced apart in
lines, so it doesn't matter.  I find this a very difficult concept. -GT] 

 
3.2.4   Remark to the Display-routine of the editor
 
For simplification of the routine, which generates the FLI routine, in the
remaining 4 rasterlines an LDX #$VALUE was used instead of an NOP.
 
 
4.      Memory-allocation

Now video-rams, the bitmap, and the sprites have to be placed reasonably in
a VIC-Bank.  As the banks from $0000 - $3FFF and $8000 - $BFFF are useless
for graphics due to the overlay of the Char-rom we choose the back from
$4000 - $7FFF for now.
 
 
4.1     Video-rams
 
The 8 video-rams need $2000 Bytes.  They are located from $4000 - $5FFF.

 
4.2     Bitmap
 
The bitmap needs $1F40 Bytes.  It's located at $6000 - $7F3F.
 
 
4.3     Sprites
 
As we need 2 sprites overlayed {two layers of 4 sprites next to each other}
(four times next to each other and 8 times below each other), we need
2 * 4 * 8 sprites, meaning 64 overall.  We need $1000 bytes for the
sprites.  We check what the video-rams and the bitmaps already allocate
and recognize that only $7F40 - $7FFF, enough memory for 3 sprites, is
left open.  How do we rectify this situation?
 
As the video-rams and the bitmap just need a small part for displaying the
picture, the sprites can be put into the spare parts of the video-rams and
the bitmap.  They have to be masked by choosing the right color in the video-
rams.
 
A textline of a bitmap covers $140 bytes.  Our Super Hires cutout just needs
$60 bytes though and is centered.  Thus the first and the last $70 bytes of
a textline of the bitmap is free.  As a sprite needs $40 bytes, we can put 2 
sprites in each textline of the bitmap (one to the left and one to the right).
Due to the height of the picture (21 textlines), this results in space for
42 sprites.  From textline 22 on (in memory from $7A40) we can use the whole
textline for sprites, resulting in 5 sprites per line.  Continuing this until
textline 24 inclusive, we have space for 15 additional sprites.  So overall
we already have 57 sprites and just 7 are missing now.  These we could place
in the remaining free area of the bitmap ($7E00-$7FFF), but that's not very
efficient as we have some space left in the video-rams.
 
The Textline of a video-ram contains $28 bytes.  The Super Hires cutout just
needs the middle $0C bytes.  As the sprites we put to the left and to the right
of the picture are supposed to be invisible, we need to set a background-color
in the video-ram (in our case, the color light-grey $FF).  So we don't have
enough spare room for the sprites to the left and the right of the picture
in the video-ram.

If we finish the FLI Routine from textline 22 on and keep the video-ram on
until the end of the screen (filling the this area ($4370-$43E8) with the
backgroundcolor $FF to hide the sprites) we can use the remaining 7 video-rams
from textline 22 (from $4770, $4B70, $4F70, $5370, $5770, $5B70, $5F70) for
one sprite each.  Now we have placed all 64 sprites and the allocation of the
sprite pointers looks like this:
 
$43F8  80 84 85 89 8A 8E 8F 93
$47F8  94 98 99 9D 9E A2 A3 A7
$4BF8  A8 AC AD B1 B2 B6 B7 BB
$4FF8  BC C0 C1 C5 C6 CA CB CF
$53F8  D0 D4 D5 D9 DA DE DF E3
$57F8  E4 E8 E9 EA EB EC ED EE
$5BF8  EF F0 F1 F2 F3 F4 F5 F6
$5FF8  F7 1E 2E 3E 4E 5E 6E 7E
 
The pointers from $80 to $E4 are the 2 sprites which are left and right
next to the picture in the bitmap.
 
The pointers from $E8 to $F7 are the sprites from textline 22 to 24 below
the picture in the bitmap.
 
The pointers from $1E to $7E are the sprites from textline 22 to 24 below
the picture in the video-rams.
 
 
5.      Interlace
 
Until now we had the normal Super Hires FLI mode, supplying the basics for
interlace.  For the interlace mode we need 2 pictures of this kind switching,
displayed 25 times per second (PAL).  [30 times/sec in NTSC -GT]  As such
a picture fits into the VIC-Bank from $4000 - $7FFF and we have another
VIC-Bank ($C000-$FFFF) with the same assumptions we can easily place the
2nd picture there.
 
We had in the Super Hires FLI mode (on a 8 * 1 pixel-area) the choice between
4 colors (2 sprite-colors, being the same for the whole picture + 2 FLI
colors).  Now, in the interlace mode, we have the choice between 16 mix-colors,
meaning the combined 4 colors from picture one and two.  When using interlace,
mix-colors are created except for the case when the same colors are used for
both pictures on the same 8 * 1 pixel-area (Check the following example) :
 
 
      Pic2 ->        Sprite1:$E [ Sprite2:$0 [ FLI1:$6 [ FLI2:$9
----------------------------------------------------------------
Pic1    Sprite1:$1  [   $1E     [    $10     [   $16   [   $19
  [     Sprite2:$3  [   $3E     [    $30     [   $36   [   $39
  V     FLI1   :$E  [   $EE     [    $E0     [   $E6   [   $E9
        FLI2   :$6  [   $6E     [    $60     [   $66   [   $69
 
 
This results in the following 16 mixcolors:
 
 1. White-Lightblue
 2. Cyan-Lightblue
 3. Lightblue-Lightblue (pure Lightblue)
 4. Blue-Lightblue
 5. White-Black
 6. Cyan-Black
 7. Lightblue-Black
 8. Blue-Black
 9. White-Blue
10. Cyan-Blue
11. Lightblue-Blue
12. Blue-Blue (pure Blue)
13. White-Brown
14. Cyan-Brown
15. Lightblue-Brown
16. Blue-Brown
 
 
When choosing the colors you should take care that the brightness-values of
the 2 mix-colors are about the same and that they do not differ by more than
2 brightness steps, as things otherwise start to flicker too much.
(e.g. Black-White flickers a lot).
 
Here is a table with brightness-values from light to dark.
(Colors on the same line have the same brightness)
 
$1     : White
$7, $D : Yellow, Lightgreen
$3, $F : Cyan,   Lightgrey
$5, $A : Green,  Lightred
$C, $E : Grey,   Lightblue
$4, $8 : Lilac (Purple), Orange
$2, $B : Red ,   Darkgrey
$6, $9 : Blue,   Brown
$0     : Black

[On very old 64's (64 cycle raster line NTSC VICs), there are only 5 luminance
values in the 16 colors, making them less distinct.  Yellow and cyan are
the same brightness, then green, grey, and purple, then red and blue.  White
and black form the last 2 values of brightness.  The pairing of colors above
are included. -GT] 

 
6.      Additional Graphics (Not handled by the editor)
 
We found out that on the left or right of the picture in the bitmap, $70 bytes
was left for spritedata.  We used just $40 bytes of that space, meaning we
still have $30 bytes (6 Chars or 48 Pixels) left to both sides of the picture.
 
 
6.1     Left to the picture
 
Our Super Hires Picture starts at position 15.  The spare $30 bytes are from
position 9 to 14.  As we use 14 cycles in our FLI routine and the 8 sprites use
19 cycles per rasterline, the light-grey FLI Bug now uses the chars 11, 12, 14.
Thus meaning we could use char 14 for Hires FLI.  On chars 9 and 10 we could
just use 2 different colors (respectively 4 mix-colors for interlace) on the
height of 21 textlines in the bitmap, as the FLI effect starts from Char 14 and
before that no new data (colors in this case) are read from the video-ram.
The colors are in the first video-ram in memory from $4008+$4009 respectively
$C008+$c009.

[The shifli pic could probably be expanded by rearranging the raster
code very carefully, and even areas outside the shifli effect could still
use normal graphics modes, but I expect only a small improvement. -GT] 

 
6.2     Right to the picture
 
Our Super Hires Picture lasts until char-position 26.  The spare $30 bytes in
the bitmap are from position 27 - 32.  Here we could use all 6 chars for Hires
FLI (or Interlace Hires FLI).
 
 
7.      Memory-allocation of a picture startable with RUN
 
The included SHIFLI picture, once unpacked, can be easily modified for your own
use, as follows :
 
$0801-$080C Basic Startline
$080D-$0860 Routine for copying the Graphic-data to the correct memory area
$0861-$095B Routine which is setting the I/O registers and creates the
            display-routine (from $085F-$10FB)
$095C-$475B Data of the 1. Picture (to be copied to $4000)
$475C-$855B Data of the 2. Picture (to be copied to $C000)

 
8.      Conclusion
 
For questions or comments concerning this article :
Roland Toegel is available at      : toegelrd@trick.informatik.uni-stuttgart.de
Count Zero/TRC*SCS is available at : count0@mail.netwave.de



:::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::3::::::::::::::::::::
/S04::$dd00:::::::::::::::::::S O F T W A R E:::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


                   Defeating Digital Corrosion (aka "cracking")

                    - a beginners guide to software archiving

                      By Jan Lund Thomsen (aka QED/Triangle)


   Pontus Berg briefly touched the subject of tape cracking in his disC=overy
1 article (/S08: Software analysis and reconstructive therapy, a historical
view on "cracking"). But then again, "great minds think alike". :)
   The purpose of this article is to provide a broader, more detailed view at
the general components - as well as some insight on the way a cracker did his
"thing" back in the old days. Although some degree of technical knowledge is
required to make the most of this article the casual C64 enthusiast should also
be able benefit from it.


Disclaimer
----------
   Despite the issues discussed in this article it is in no way written to
encourage software piracy. You are not allowed to use the knowledge found here
to make copies for other people. If you go ahead and do this despite what I
have just said it is not my problem and I can't be held responsible in any way.

[Ed. note : As always, disC=overy and the editors and staff of disC=overy can
 not be held responsible for the use or misuse of any information presented
 in this article.]

Preface
-------
   In a world of digital corrosion the need to create backups is something
most of us deal with sooner or later. You might never have had any problems
yourself - but tapes or disks have been known to "go bad". And what if your
tape deck suddenly blows up, rendering you unable to play your favourite games?
Wouldn't it be nice to have a backup? Some people (myself included) prefer
using the backup copy and storing the original in a safe place. Copies can be
stored on floppy disks or hard drives for easier/faster loading. Creating an
exact copy of a backup is not only very easy; it eliminates the risk of
losing data due to corroded tapes.

   Yes, the techniques described here are exactly the same as software pirates
have been using since the dawn of time; defeating a protection scheme in order
to create something that can easily be backed up. This article is *not* about
Software Piracy (and I will not answer any comments on that issue). I strongly
believe that owners of original software have every right to create backups. 

   During the course of this article I will be looking into the specifics of
transferring original tape software to disk. I have several reasons for
choosing the subject of tape rather than disk software:

 - I have far more tape originals than disk originals. (I.e. more research
   material.)

 - From a beginners point of view, Tape software is far easier to crack.

 - Disk originals are easier to reproduce, and therefore not as vulnerable to
   digital corrosion as tapes.


Requirements
------------
   Apart from a tape deck and one or more original tape programs it goes
without saying that a good deal of 6510 Assembly skills are required to be
able to be a successful cracker. A good cartridge is really a must as well.
I prefer the Action Replay Mk V or the Expert V4.2 due to their awesome
debugging features.


Why don't I just use the backup option in my cartridge?
-------------------------------------------------------
   To be able to answer this question we need to go back in time and take a
look at the C64 crackers of yesteryear. As different people all over the world
were pirating software, cracking soon became a quest for quality rather than
quantity. Of course some people cared less about quality and more about
releasing a steady stream of software.
   A real cracker didn't care about speed. Any cracker could produce a working
copy in a couple of hours - but the real cracker wasn't satisfied with just
producing a "working copy". Why would anyone capable of doing a *great* crack
settle for less? For some crackers "good" simply wasn't good enough. Bugs were
fixed, cheat modes installed, and excess data was surgically removed to
produce shorter, cleaner cracks.
   Real cracking is about technical achievement, pride, dignity, and a
dedication to quality. Needless to say, no cracker worth his salt will want
to freeze/backup anything. I, for one, have never used this feature of my
Action Replay - not even for the purpose of producing copies for my eyes only.

   Although a backup created with the "backup" option of the Action Replay,
Expert, or any other freezer cartridge might run just fine, it can *never* be
as good as the result produced by a dedicated cracker who knows what he is
doing. 

   In short: *Anyone* can press a button.


Getting started
---------------
   As this article is aimed at the beginner level we will only deal with
"single-loaders" - i.e. programs loaded into memory in a single pass. Programs
using additional I/O (highscore savers, intermission screens, levels, etc.)
are known as "multi-loaders" and will be briefly mentioned in chapter 5: What's
next?

   Cracking (let's not beat about the bush and call it something else just to
be politically correct) a tape game boils down to the following four steps:

       1) Transferring the loader to disk.
       2) Analysing the loader.
       3) Modifying the loader and pulling the files off tape.
       4) Wrapping up.


Chapter 1: Transferring the loader to disk  (or "One small step...")
--------------------------------------------------------------------
   By definition, copy protection prevents the user from duplicating a piece
of software. In the case of tape software programs are stored in a non-standard
format, incorporating a fast loader of some sort. The "loader" - a short
assembly program at the start of the tape contains the program code necessary
to load the rest of the data. As the loader is stored in the standard tape
format it is fitted with an autostart feature to prevent tampering.

   Therefore, the first step to transferring a tape original to a copyable
form is to load the "Loader" program *without* starting it. Luckily Commodore
themselves come to our rescue with the following ROM routines:

        Decimal  Hex.    Description
        -----------------------------------------------
        #63278   $F72C   Read program header off tape
        #62828   $F56C   Read rest of program off tape

By inserting a tape and issuing the 'SYS 63278' command the program header
will be read into the tape buffer at $033C-03FC. The first bytes of a typical
program header will look something like this:

   .:033C 03 A7 02 04 03 4C 4F 41 .....LOA
   .:0344 44 45 52 00 00 00 00 00 DER.....

As you might have noticed, the filename is stored from $0341 onwards. However,
the five bytes from $033C-0340 are of much more interest to us. The first
byte describes the type of header: in this case "03" - a machine language
program. The next four bytes contain the start and end address (Low/High byte)
of the program. This header above denotes "LOADER", a machine language
program loaded from $02A7-0304. As the $0300-0304 area contains a number of
system vectors that can be modified to to point at the loader program and
this explains the autostart.

If we modify the start/end address before reading the rest of the program
(using the routine at $F56C) the file will be relocated, thereby circumventing
the autostart. I recommend adding #$10 to each of the high bytes (thus
shifting the entire loader $1000 bytes upwards in memory) as this makes
further studies a lot easier. The file in question would be relocated to
$12A7-$1304.

Having read the "loader" program to another memory location we are now able
to save it to disk. But that's not all! Program code can also be stored in the
tape buffer so be sure to examine the contents of $033C-03FC before moving on
to step 2. If the buffer contains any other data than the five control bytes
and the filename discussed earlier, save it to disk as well.

Congratulations! You have now taken the first step... there's much more to
come!


Chapter 2: Analysing the loader.
--------------------------------
   Brace yourselves! This is the tough part.

   There are a *lot* of different protections out there. It would be rather
impossible to discuss even a fraction of them here. Some tape protections are
quite easy to crack once you've gotten past the initial autostart loader.
Others might require you to decrypt data in one way or another. Some particular
nasty systems even load a new copy of the loader on top of the old one to
prevent tampering (the Firebird Gold loader being a good example of this).
Cyberload, the mother of all tape protection schemes, not only uses this
technique - it also features encryption as well as two different load
systems. If you can force a Cyberloader to it's knees, you have every
reason to be proud of yourself.

   It really goes without saying that a good deal of assembly language skills
are required in order to understand what goes on. However, as long as you are
able to grasp the overall function of the various parts of the program under
scrutiny - you do not need to know every detail about the inner workings of it.
Having said that it should be obvious that the odds increase the more you are
able to understand. In other words: Practice! Practice! Practice!

Consider the following example:

        l1     JSR $XXXX        ; get byte from tape
               STA ($F9),Y      ; store in memory
               INC $F9          ; increase pointers
               BNE l2
               INC $FA
        l2
               [...]            ; check for end-of-file/last file/etc.
               BNE l1
               SEI
               LDA #$35
               STA $01
               JMP $1000        ; start the program.
        
        XXXX   [...]
        l3     LDA $DC0D
               AND #$10
               BEQ l3
               LDA $DD0D
               STX $DD07
               LSR
               LSR
               LDA #$19
               STA $DD0F
               [...]
               RTS

   The subroutine at XXXX seems to be doing something with certain I/O
registers. Understanding exactly what goes on isn't all that important because
by looking at the code following the JSR (at l1) we can clearly see that
something is stored in memory after calling the routine - i.e. the routine at
XXXX must be some kind of loader. As Sir Arthur Conan Doyle's famous detective
would put it, "Elementary, my dear Watson."

   Sometimes you will have to overcome various obstacles to get to the heart
of a loader. Some protections schemes rely on encryption, others on obfuscated
coding. The creators of the most sophisticated protection schemes knew that
understanding what goes on is the key issue regarding cracking, and went to
great lengths to discourage the "professional" as well as the casual cracker.


Chapter 3: Modifying the loader and pulling the files off tape.
---------------------------------------------------------------
   Now that you have penetrated the outer layers, you will want to pull the
individual files off the tape and store them on disk. One approach would be
to locate the piece of code that launches the main program and replace it with
something that halts the computer using a infinite loop, allowing you to enter
your cartridges in-built monitor, and save a memory dump to disk. In some cases
it is advisable to halt the computer after each file has loaded and save the
data to disk before carrying on to the next file.
   Whatever approach you choose the key issue is to halt the C64 and save the
relevant data. 

If space permits, I prefer to insert the following piece of code:

        SEI
        LDA #$37
        STA $01
     l1 INC $D020
        CLC
        BCC l1

If there is no room in loader for big modifications like the above make a note
of the original code before modifying the loader.

     l1 CLC
        BCC l1

also works quite well, not to mention only taking up three bytes. However,
this requires you not to enter the monitor until you are completely sure the
program has entered the infinite loop. I have used this approach so much that
I know the assembly opcodes ($18,$90,$FD) by heart, even though I haven't used
them in years.

   Now that the C64 is halted and we are ready to save the data to disk we
need to obtain the relevant addresses. I mean, why take up too much diskspace?
:)  Using the example from the previous chapter the data was stored at the
memory location held in $F9/$FA <STA ($F9),Y>. Thus, looking at the content of
$F9/$FA will give us the end address of the file. For the start-address we can
either make an educated guess using the 'M'emorydump or 'I'nterrogate to scroll
backwards until we encounter something that does not look like it belongs to
the file (after lots of practice real crackers can be quite good at this.)
A more precise (not to mention subtle) approach is to patch the loader to store
the content of the pointer elsewhere and modify the loader back to normal to
prevent the start-address from being increased due to the pointer being
increased.


Chapter 4: Wrapping up.
-----------------------
   Having transfered the files to disk it is time to wrap everything up. This
is by far the easiest step. Link the relevant files, then use a Char/RLE
packing program (EBC, Link & Crunch, X-Terminator, etc.) followed by a
"cruncher" program (DarkSqueezer, Byteboiler, Cruelcrunch, etc.)

   For shorter, cleaner cracks be sure to removing any excess data. Try
having a look at the game code - be sure to examine any memory-ranges that
are cleared by the startup code. Often quite a bit of data can be surgically
removed without causing any damage to the "patient".

   Loading pictures are most often turned into stand-alone files. Just add a
short piece of code to get the graphics on screen and pack everything.

   *Test* the finished product. In the old days a lot of bad quality could
have been avoided if crackers had taken the time to make sure everything worked
before releasing it onto the public. Again, this boils down to real cracking
being more than just churning out a heap of "warez" every day. I suppose the
modern day term is "quality assurance". :)


Chapter 5: What's next?
-----------------------

 Practice
 --------
    Just because you suddenly find yourself able to breach one protection
scheme doesn't mean that you have suddenly become an expert cracker. Lots of
practice and further studies are needed to become successful. If you have more
than one original tape using the same copy protection, don't be afraid of
experimenting. Maybe you'll want to try out some new approaches. A lot can be
learned from writing a program to automatically transfer files saved by a
specific system. Gradually progress to other systems. If a system seems
impossible to breach, try sharpening your skills on easier systems and coming
back to it later.

As mentioned earlier, there are a *lot* of different loaders on the market.
Although the schemes used vary from one program to another, chapters 2 and 3
should give you a rough idea about the basic idea and the steps needed to
circumvent various systems.


 Multiloaders
 ------------
    Multi-loaders (Programs using additional I/O such as intermission screens,
levels, etc.) will require you to replace the original loader with a custom
disk-loader. If you poke around the original loader you will most likely
discover some sort of internal level-counter that can be used to refer to the
name of the file you want to load next. Be sure to compress data files with
a Levelpacker for a result consuming a lot less disk space. Levelpackers
include a short loader/depacker program that will decompress the files on
the fly.


 Disk originals
 --------------
    Feeling confident about tape cracking? Why not move on to disks? Be
prepared to encounter everything from minor obstacles to the meanest mothers
in the business. Disk cracking is, as they say, "A whole different ballgame!"


---
The author has been a C64 enthusiast for the past 11 years - and a general
8-bit addict for even longer. In his earlier years he was part of the European
C64 cracking scene. He is an active participant both on the #c-64 IRC channel,
and in the comp.emulators.cbm and comp.sys.cbm newsgroups. Recently he took his
group "Triangle 3532" to the World Wide Web. When he isn't busy playing Lode
Runner on his C64 he is available for questions and/or general comments at the
following Internet address: kwed@pip.dknet.dk



:::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::3::::::::::::::::::::
/S05::$df00:::::::::::::::::::S O F T W A R E:::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


 A possibility to be explored : Real Time Video with the Ram Expansion Unit

                    by Nate Dannenberg (Natedac@dfw.net)


A few weeks ago, there was a discussion on the comp.sys.cbm usenet newsgroup
concerning the viability of using the Ram Expansion Unit as a medium for
displaying real-time video animation on the C64 or C128.  I thought about
the possibility and came to a few conclusions.  The following text is how
I would undertake such a project.  Along the way, I shall illustrate some
previous "reu-animation" efforts and how the peculiar qualities of the REU
allowed such efforts to be accomplished.
--

While I've never actually done video, Commodore did it at least twice.

On the 1750 and 1764 Test/Demo disks there is a "Pound" demo and a "Globe" 
demo.  These two are full motion displays using your REU.  The "Pound" 
demo is a 3-dimensional # sign transforming in and out, the "Globe" is a
full rotating Earth/globe. 

The controlling program for these is written in BASIC, with a small ML
program to decompress the graphic data that's stored on the disk, as it's
loaded into the REU.

The REU has a transfer rate of about 1,022,000 bytes per second.  Since a
single video frame is 1/60 of a second, that gives you 16,666 clock cycles
per video frame (including video sync time).

Chances are good you will probably want to only display about 15 frames a
second, which is the MPC-1 specification for PC full motion video playing
from a CD-ROM disc.

With simple code, you can get about a 7.7Khz sample rate for the audio, by
playing one sample byte every other raster line, and maintain a smooth 60
FPS video transfer rate, should you want to go this high. 

All you have to worry about is making sure you start an animation frame
transfer at the top of the intended video refresh, raster line 0.  You do
this with a simple wait loop:

loop BIT $D012
     BMI loop

As soon as bit 7 of $D012 goes low, the BMI will fail, signalling raster
line 0 on a new frame.

The VIC chip steals 43 cycles from the CPU (and any REU transfer in
progress) on the first of every 8 raster lines.  The line where this
occurs now only has about 21 cycles free instead of the usual 64, this is
called a "bad" line in demospeak.

You want your audio routines to use the odd lines, to stay out of the way
of the bad lines that occur every 8 lines.  The video routines and any
additional overhead can be used after the audio routine has done it's job
on it's line. 

An algorithm like this might do the job:

0 bad line, start audio REU xfer from last video refresh (see below)
1 Play 1 sample of audio data and run animation copier routine
2 ::::
3 Play 1 sample and run animation copier
4 ::::
5 Play 1 sample and run animation copier
6 ::::
7 Play 1 sample, set REU to grab 2 more audio bytes
8 bad line, start the audio REU xfer (should take 8-10 cycles)
9 Play 1 sample and run animation copier
10 ::::
11 Play 1 sample and run animation copier
12 ::::
13 Play 1 sample and run animation copier
14 ::::
15 Play 1 sample, set REU to grab two more audio bytes
16 bad line, start the audio REU xfer (8-10 cycles)
17 Play 1 sample of audio data
:

You use a 2-byte transfer buffer for the digi audio data, to cut REU
overhead to a minimum, and to maximize the usage of those bad lines.
Remember that packed RAW data, as stored in a file, takes 1 byte for 2
samples.  So we're really transferring 4 samples here..

Basically on every badline you simply tell the REU to start the 2 byte
transfer that was set up on the previous line.  In the case of line 0 of
the very first frame when starting the code, simply set up everything
needed, including the first 2 byte audio pointer, which will be used by
the REU at the end of line 0 to copy those 2 bytes..

This only takes 6 cycles to activate, plus 2 cycles for the dma copy. You
have 11 cycles left, which may or may not be useful.  Just use four NOPs
and a BIT, to waste this time out to the next line.

Play 1 sample of audio data on every odd line, this takes only 6 cycles if
the data from the REU was copied to Zero Page. 

You have the remainder of the this rasterline and all of the next to run
your animation copier, a total of about 120 cycles.  You should be able to
squeeze in about 80 or so bytes into this space (80 bytes plus about 40
cycles of REU overhead is about 120 cycles).

The REU can be programmed to remember where it left off after a transfer,
making subsequent contiguous transfers painless. It takes about 40 cycles
to set up and execute the first transfer (plus the actual copy time), and
then only 6 cycles to repeat the transfer (plus transfer time of course),
by using LDA #imm:STA $DF01 , where #imm is the value that tells the REU
to start, I forget offhand, something like #$95 I think... 

This means that you might only be able to copy 80 or so bytes the first
time after an audio data copy (the first line following a bad line), but
you can transfer another 80-byte block just by using only 6 cycles to
re-execute the copy.

Use another 6 cycles to adjust the size of the transfer if you want more
to be transferred this time, LDA #imm : STA $xfer_size_reg where #imm is
your new size and xfer_size_reg is the REU register for the low byte of
the transfer length..

In this way you use 12 cycles to start a new size of transfer, and are
able to copy around 108 bytes, instead of just 80.

On every raster line that'S just before a badline, you play the last
sample in the buffer, and use the rest of the line to set up the REU to
transfer another 2 bytes of audio data into your audio buffer.  This
should only take about 40 cycles or so, plus whatever time is needed to
update your audio data pointers. 

To keep your timing easy, you should definitely keep this line down to
less than 64 cycles.  You can push it into the next (bad) line, but then
you gotta make sure the REU has completed the 2 byte transfer before the
VIC starts the line following the bad line, and you only have 21 cycles on
that bad line in which to do it. 

Use as much raster time as you can for the REU copier code, since the
audio code is so simple and requires comparatively little CPU time.

The REU is predictable; you will always know precisely how many cycles a
transfer will take.  However, you may not always know how much time your
own code will take, so you may need a simple wait loop like this at the
ends of certain raster lines, to wait for the next raster line: 

     LDA $D011
loop CMP $D011
     BEQ loop

This takes 11 cycles, plus 7 each time it loops.

Needless to say, you need to know a little about synchronizing all of this
to the video display and the raster lines. 

Location $D011 in the VIC is the low byte of the raster counter.  Reading
this register will tell you what raster line the VIC is on at any given
moment.

Remember that an NTSC TV has 525 lines, which consist of two 262 line
fields, often called "even" and "odd" fields.  In a TV displaying a normal
broadcast signal, One set of fields is normally positioned half a raster
line below the other, resulting in true 525 line interlaced display at a
30Hz refresh rate.

The VIC chip gets around this by displaying the exact same data in both
fields, and by placing both fields on  the exact same position on screen,
instead of moving one down half a line.  The result is the "normal" 262
line screen with a 60Hz refresh rate.

Synchronizing to a particular raster line is a simple matter of: 

     LDA #your_raster_line
loop CMP $D011
     BNE loop

This type of wait loop takes 9 cycles for the first run, plus 5 cycles
each time it loops.  

When you start a new frame update/copy, you need to synchronize your
routine to start on raster line 0.  You can do this by watching the high
bit of the raster counter:

loop  BIT $D012
      BMI loop

This only takes 5 cycles per loop.  As soon as bit 7 of $D012 goes low
(0), the BMI will fail, signalling that the VIC has started an even video
field.  This  way you know you are at the start of the TV's video frame as
well as the VIC's idea of a frame.  I don7t knwo if this would really
matter, however. :)

Remember that line 0 is NOT a bad line, nor are any other lines outside
the range 48 to 199, because there is no visible display here, only
border.

On these lines outside the visible display, you have 43 cycles more on
every 8th line, which are normally the bad lines on the visible display.
They are all a full 64 cycles long like any other non-bad line elsewhere
on the display.

Let's take a general routine that copies 80 bytes on every two raster
lines (minus the bad line and the line before that), not using the REU's
ability to remember where it left off.  80 bytes * 3 transfers per row is
240 bytes.  25 visible rows * 240 bytes yields exactly 6000 bytes.

Well we still have those 56 rasters off screen.  That'S about 7 rows
tall, and we can use the same routine as we do for the on-screen rows
(except we have no bad lines to deal with).. 

So 7 * 240 = 1680 bytes + 6000 from the on-screen rows = 7680 bytes.

That'S not quiet fast enough to do 60 FPS, but you can certainly get 15
out of it. :)  In fact that looks to be good enough for 50 FPS.  Well we
can improve this! 

Since the REU can remember where it left off, why not get it to transfer
80 bytes the first time on each row, and 108 bytes each of the next two
times (as described way up there someplace)? 

Well, let's add it up...  80 + 108 + 108 = 296 bytes per row.  25 rows
times 296 bytes yields 7400 bytes on the visible screen.  We only need
another 600 bytes to finish our 8000 byte bitmap... 

LeT's just use the next three rows and waste a few rasters..  296 * 3 =
888, add the 7400 bytes we've done on the visible screen and we get 8288
bytes.  Just slightly more than enough for the full screen. 

After all this we still have another 32 rasters left, on which the audio
routine is still running (on every other line).  That audio routine is
probably only using maybe 1/20 of the total raster time. 

Even then we could still speed this up by using some of that leftover
raster time, and with a little fiddling you can get 15Khz instead of
7.7Khz, but that'S kinda tricky and involves a lot more cycle counting. 

Now remember, all of this audio and video data takes a LOT of ram....

You can only get 8 animation frames into a 64K bank in the REU if you go
with full-size 320x200 bitmaps.  A 1750 REU only has 8 banks, that'S a
total of 64 animation frames.

At 15 FPS, that'S only about 4 seconds of video..

A 2MB REU on the other hand has 32 banks, which yields 256 frames.  At 15
FPS, that'S only about 17 seconds of video.

One way to increase the video time is to use custom characters to simulate
an 80x100 bitmap using a text screen.  This would still be monochrome and
would be much lower resolution, but you would now have about 8 times
longer video.  That 2MB REU would now be able to handle about 140 seconds,
or just over two minutes.

The advantage of that method is you would only have to copy 1K of data
instead of 8K, which could be done in about 5 rasterlines using the above
copy-and-remember method.

And then you still have your audio track with that..  a 7.7Khz sample uses
7700 bytes for 2 seconds (packed 4 bit data) of sound, so a 4 second long
sample, will take just over 15K.   A 17 second sample would take about
65K.  And to match up that 140 seconds of video with 140 seconds of audio
would take about 537K, or about 1/4 of the 2MB REU.

Obviously you would have to find a break-even point here, where you have
as much audio time as video time.  From the looks of it, video takes about
4 times as much data as audio, when using 1K charctermapped frames, or
about 32 times as much when using full 8K bitmaps.

If you were playing the Audio data from the C64's memory, or if you had
one ram device to hold audio data and one to hold video, then the overhead
would be virtually nil.  You could probably get away with a 44 KHz sample
rate and 60 FPS video, depending on the code and the speed of the ram
device(s) in question.  Two REU's mapped at different addresses in memory
should in theory be enough to do it. 



:::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::3::::::::::::::::::::
/S06::$f00d:::::::::::::::::::S O F T W A R E:::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


                          A look into 'The Fridge'

                     by Stephen L. Judd  (sjudd@nwu.edu)


[Ed. note : The following text is from The Fridge, an archive on the World
 Wide Web dedicated to the preservation of useful routines and algorithms.
 The Fridge (http://stratus.esam.nwu.edu/judd/fridge) is authored and
 maintained by our technical editor, Mr. Stephen L. Judd.  All contributions
 to The Fridge should be sent to him at sjudd@nwu.edu]


Sample descriptions
-------------------

     * ascii2bin.doc - A description of an ASCII to binary number converter.

     * ascii32.s     - A routine to convert a 32 bit number to ASCII and print
                       it to the screen.
 
     * rand1.s       - A simple pseudo-random number generator from dim4.
                       Not that great of a random sequence, but fine for
                       simple sequences.

     * bitchin.doc   - "A totally bitchin' circle algorithm"

--
ascii2bin.doc
 
The idea here is pretty simple.  Numbers are read in left to right, and
each time a number is read in, the total is multiplied by ten and the
digit is added in:

        total=0
        digit=0
:loop   total= total*10
        total= total + digit
        read digit (and convert from ASCII to integer)
        if not EOF then goto :loop

There are two ways to multiply by ten.  The first way is to use a general
multiplication method.  The second is to realize that 10 = 8+2, thus
x*10 = x*8 + x*2 = x*2*(1+4), so with a few shifts, a temporary location, and
an addition, multiplying by ten can be done very quickly.

Example: read in the number 1653

        total=0
        digit=0

1st read: digit=1
        total= total*10 => total=0
        total= total+digit => total=1

2nd read: digit=6
        total= total*10 => total=10
        total= total+digit => total=16

3rd read: digit=5
        total= total*10 => total=160
        total= total+digit => total=165

4th read: digit=3
        total= total*10 => total=1650
        total= total+digit => total=1653

Voila!

SLJ 10/96
--

ascii32.s
*-------------------------------
*
* 32 bit -> ASCII conversion
*
* Take 2 -- Divide by 10 manually; remainder is coefficient
* of successive powers of 10
*
* Number to convert -> faq2..faq2+3 (lo..hi)
*
* SLJ 8/28/96

          ORG $1300

FAQ2      EQU $6A
TEMP      EQU $FE

CHROUT    EQU $FFD2

          LDA #$FF
          STA FAQ2
          STA FAQ2+1
          STA FAQ2+2
          STA FAQ2+3

          LDA #10
          STA FAQ2+4

          LDY #10
:LOOP     STY TEMP
          JSR DIV32
          LDY TEMP
          CLC
          ADC #48
          STA $0400,Y     ;Stick it on the screen
          DEY
          BNE :LOOP
          RTS


*
* Routine to divide a 32-bit number (in faq2..faq2+3) by
* the 8-bit number in faq2+4.  Result -> faq2..faq2+3, remainder
* in A.  Numbers all go lo..hi
*

DIV32     LDA #00
          LDY #$20
:LOOP     ASL FAQ2
          ROL FAQ2+1
          ROL FAQ2+2
          ROL FAQ2+3
          ROL
          CMP FAQ2+4
          BCC :DIV2
          SBC FAQ2+4
          INC FAQ2
:DIV2     DEY
          BNE :LOOP
          RTS
--

rand1.s
*-------------------------------
*
* GETRAND
*
* Generate a somewhat random repeating sequence.  I use
* a typical linear congruential algorithm
*      I(n+1) = (I(n)*a + c) mod m
* with m=65536, a=5, and c=13841 ($3611).  c was chosen
* to be a prime number near (1/2 - 1/6 sqrt(3))*m.
*
* Note that in general the higher bits are "more random"
* than the lower bits, so for instance in this program
* since only small integers (0..15, 0..39, etc.) are desired,
* they should be taken from the high byte RANDOM+1, which
* is returned in A.
*
GETRAND
         LDA RANDOM+1
         STA TEMP1
         LDA RANDOM
         ASL
         ROL TEMP1
         ASL
         ROL TEMP1
* ASL
* ROL TEMP1
* ASL
* ROL TEMP1
         CLC
         ADC RANDOM
         PHA
         LDA TEMP1
         ADC RANDOM+1
         STA RANDOM+1
         PLA
         ADC #$11
         STA RANDOM
         LDA RANDOM+1
         ADC #$36
         STA RANDOM+1
         RTS
--

bitchin.doc

                    A totally bitchin' circle algorithm

Last revised: 2/20/97 SLJ

With a revision!  The old algorithm ignored one small detail -- the plot8
8-fold plotter, which can be a real pain!  One thing to do is to make the
algorithm draw a quarter of a circle instead of an eighth, and the
way to do that is to attempt to reverse the procedure that drew the
first eighth of the circle.

BLARG does exactly this; in the process, I also experimented with how the
order of statements below makes a difference in the way the circles
look.  So the algorithm below is now the best version I know of, i.e.
makes the most beautiful circles at all radii.

This is the entire algorithm:

        Y = Radius
        X = 0
        A = Radius/2
:loop
        Plot4(x,y)      ;Could just use Plot8
        X = X + 1
        A = A - X
        if A<0 then A=A+Y : Y=Y-1
        if X < Y then :loop

; Now more or less reverse the above to get the other eighth

        A = -R/2 - 1
:loop2
        Plot4(x,y)
        A = A + Y
        Y = Y - 1
        if A<0 then X=X+1:A=A-X
        if Y>=0 then :loop2

Neat!

The other possibility is to use the first half of this algorithm with
an 8-fold symmetric plot, and be sure to plot the last point X=Y.

See C=Hacking Issue #9 for more details, but note that A=R/2 above, which
will make the circle correct for small R (it just rounds numbers).

--
These and other useful bits of information can be obtained through Mr. Judd's
web page, - The Fridge -, at http://stratus.esam.nwu.edu/judd/fridge



:::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::3::::::::::::::::::::
/S07::0100h:::::::::::::::::::S O F T W A R E:::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

  
               C-128 CP/M, trailblazer in a jungle of formats.
 
                                    By
 
                               Mike Gordillo
 
 
The microcomputer world was once awash in an estimated 100 different major
operating systems with over 2000 proprietary disk format-types.  The numbers
are much less intimidating today, but with the resurgence of older systems
amongst the public (IMHO), the need to extract information from an obscure
disk format may become very real to the hobbyist.  Your C-128 and its
drive peripherals are extremely flexible when coupled to the right software.
When running CP/M, your C-128 is at its best in its role as an inter-platform
intermediary.  This article will attempt to deliniate what your C-128, under
CP/M, can do for you in this regard.
--
 
The original C-128 CP/M BIOS directly supports six 5.25" CP/M MFM (Modified
Frequency Modulation) and two 5.25" CBM GCR (Group Coded Recording)
format-types.  They are as follows:
 
      Epson QX10   (MFM) - CP/M Z80 DS/DD
      Epson Euro   (MFM) - CP/M Z80 DS/DD
      IBM-86       (MFM) - CP/M 8086 DS/DD
      Kaypro II    (MFM) - CP/M Z80 SS/DD
      Kaypro IV    (MFM) - CP/M Z80 DS/DD
      Osborne      (MFM) - CP/M Z80 SS/DD
      C-128 DS/DD  (GCR) - The "Default" C-128 CP/M format-type.
      C-64  SS/DD  (GCR) - C-64 CP/M (Z80 Cartridge) SS/DD
 
   Note: The 1581 is "officially" supported in the 28 MAY 87 version of
         C-128 CP/M 3.0+.  The official 1581 format-type is MFM DS/DD.
         (SS = Single Sided, DS = Double Sided, DD = Double Density)
 
MFM disks usually have the same number of sectors on each track while GCR
disks have a variable number of sectors for different ranges of tracks.
Commodore-DOS-style GCR is the "default" format-type for C-128 CP/M
because this allows "Joe User" to easily boot up CP/M from a 1571 or even
a 1541 (slow!) drive.  (Fortunately, when Commodore finalized the 28 May 87
version of C-128 CP/M, they not only provided a format-type for use with the
1581 but added a FORMAT.COM that could create a 1581 3.5" C-128 CP/M bootable
system disk.)
 
What can you do with these format-types?
 
Taking advantage of what CP/M provides is as simple as inserting a diskette.
For example, let's say I have some files on my Kaypro which I need to use on
C-128 CP/M.  All I have to do is put the Kaypro disk in my trusty 1571 and
voila, I can use it and the files within as I please with no hassles.  This
is no small point of trivia.  The message here is that you can use data on
these "alien" format-types as if the data were sitting on the "default"
C-128 CP/M disk.
 
What about copying them over to C-128 CP/M disks?
 
No problem!  The transfer process is as easy as using an off-the-shelf file
copier because as far as CP/M is concerned, the "alien" and "default" (native)
format-types are one in the same.  This degree of transparency is due largely
to the internal disk format table which exists within the computer's memory
as part of the CP/M BIOS (Basic Input/Output System).  This arrangement
constitutes better support and flexibility over the rigid Commodore DOS system
when it comes to juggling different format-types.
 
What happens if I run across a format-type that is not directly supported by
C-128 CP/M?
 
Flexibility to the rescue!  The "built-in" drive table is modifiable (both in
memory or on the system disk).  If you need to access other CP/M format-types,
the solution may be as easy as installing the parameters that match the disks
in question.  A good example of a utility that does this is the UNIDRIVE
utility by Frank Prindle.  It reconfigures the drive table, allowing up
to ten different 5.25 MFM format-types at any one time from an overall
selection of twenty-four.  Because of the drive table, UNIDRIVE is a simple
elegant program that can make these changes without sacrificing any TPA
(Transient Program Area) memory.  The undisputed king of format "jugglers" and
nowhere near as simple is the JUGGLER utility by Miklos Garamszeghy.  This
program can read and write and format over ONE HUNDRED THIRTY different
format-types, mostly 5.25" MFM (with 1571) but also 3.5" MFM (with 1581) based
disks.  This includes the popular space extending, speedy alternative formats
for C-128 CP/M, namely Maxi 1571/1581 and MG 1581.  A demo version of the
JUGGLER utility is publically available.  It does not have as many features
as the full version, you can only access twenty-two format-types and modifying
the "built-in" drive table is out of the question.  This would be inconvenient
if the full version were hard to find as it once was.  This has changed as
its author as put up the full version of Juggler on the World Wide Web at
http://www.herne.com/cpm.htm
 
What about MSDOS MFM disks, can I use them as well?
 
Yes you can!  I know of at least three separate utilities that will allow
communication between C-128 CP/M and MSDOS diskettes.  First on the list
is the RDMS 2.33 utility.  This is an old program designed to read MSDOS
diskettes, and that's all it does.  It will read from MSDOS into CP/M but
will not write back to MSDOS.  Frank Prindle ported over this program
(for the 1571 drive) and he would have probably added a "write MSDOS" option
if someone else hadn't beaten him to the punchline.  The TRANSFER 128 v1.2c
utility (ported over by Gilly Cabral from David Koski's original TRANSFER
util.) came soon after Prindle's rendition of RDMS 2.33.  In fact, the routines
used to communicate with the 1571 are based on Prindle's work.  TRANSFER 128,
however, goes way beyond RDMS 2.33.  Not only does it read and write to MSDOS
disks but it also provides a suprisingly high level of sophisticated access to
the internal workings of MSDOS disks.  For example, it can display the MSDOS
FAT (File Allocation Table) and reconstruct it, if needed, using a backup FAT
as a template.  TRANSFER 128 supports single and double sided 5.25" format
types with eight or nine sectors per track.  It places no limits on the size
of the data it can transfer.  The only limitation is the free space left
on the destination diskette.  This is a huge advantage over the first "native"
mode MSDOS to CBM DOS programs which use internal memory buffering.  For many
years, TRANSFER 128 was my only standby for large files.  When better utilities
came out for the "native" modes, I still found TRANSFER 128 to be my choice.
Why?  Because of the excellent Ram Expansion support under the CP/M BIOS.  CP/M
thinks the Ram Expansion Unit is truly a real drive.  TRANSFER 128 (and other
CP/M programs) does not have to worry about steering clear of "interface pages"
and silly stuff like that when dealing with the additional Ram.  Eventually,
people wrote transfer utilities that did work with CBM RAMDOS, for example.
Too late, my heart was set on TRANSFER 128.  It's been a good companion and
since it comes bundled with its Turbo Pascal source, I can always modify it
in case of trouble.  In fact, I had to do just that when I set up several
BIOS/BDOS/CCP enhancements on my system.  (But that's another story :)))
The one tiny snag with TRANSFER 128, however, is that it will format the
various MSDOS format-types perfectly as far as MSDOS itself is concerned,
but "native" mode transfer programs do not recognize the disks which TRANSFER
128 has formatted.  I am at a loss to explain this, so I won't ;), but its safe
to say i'll give TRANSFER 128 the benefit of the doubt anytime!
 
What about 3.5" MSDOS format-types using my 1581?
 
Ah, yes, I've neglected to mention what may be the best "transfer" utility
available for the C-128.  It may be too sophisticated for young viewers so
this section of this article will be rated PG-13 :).
 
Well Well Well !??!?  Don't keep us in suspense!
 
Not to worry!  The utility i'm harping about is called MSDOSEM (by Nichita
Sandru).  It doesn't just give you a temporary transfer "window of
opportunity", it makes CP/M think of MSDOS disks as native CP/M disks and
it provides (get this) *full* subdirectory support!!!
 
This is similar to the transparent "juggling" of CP/M format-types, yes?
 
Yes, except that CP/M disks carry the same directory structure no matter how
their format-type is arranged.  In order to support the MSDOS directory
structure, MSDOSEM creates a separate buffer area for internal conversions
between CP/M and MSDOS logical constructs.  The actual physical nature of the
MSDOS format-types, however, is handled (once again) with a modified CP/M drive
table.  MSDOSEM has no problem with 5.25" (1571) or 3.5" (1581) double density
MSDOS format-types.  Again, as described earlier in this article, transparency
means that you can play with the "alien" disk as if it were a "default" one.
Any copy utility you have sitting around can now access MSDOS diskettes with
MSDOSEM as the go-between.  This is the ultimate in MSDOS to C-128 transferring
because you (essentially) do not have to transfer anything anymore! The "stuff"
on your MSDOS disks is no longer anathema to CP/M, even though it may be to
you :).
 
What about support for regular Commodore DOS (GCR) disks?
 
In reality, the original "default" C-128 CP/M format-type is as Commodore
DOSian GCRish as anything Commodore has done for the "native" modes.  I take
advantage of this when backing up my CP/M disks, either with FastHack'em
(or any GCR copier) in C-128 mode or IMAGE.COM under CP/M itself.  These 
programs allow us to manipulate Commodore GCR on a gross level.  For a more
refined manipulation of files "stuck" in regular CBM GCR disks, I generally
use the RDCBM 2.1 utility by Rob Tillotson and Turbo Penguin Software.  This
program can read files from 1541 or 1571 "native" Commodore GCR disks and spit
them out as straight binaries (eg., no translation) or it can convert files from
 
PETSCII to ASCII.  Like RDMS 2.33, RDCBM 2.1 does not write back to the disks
it so aptly read and it does not read anything on the 1581.  These quirks are
*highly irritating* considering its a pretty good program otherwise.  Good
enough to provide limited support for files under the GEOS file structure.
Fortunately, I surmise that I have not run into a more recent version of
RDCBM to evaluate.  I say this because the source listing of this program
contains "Hooks and Crannies" pointing to the author's efforts to implement
a "write-back" option and 1581 support.
 
You mean I cannot transfer material from my CP/M disks to my regular 15xx
disks???
 
No I don't.  There exist several "native" C-64 and C-128 utilities which will
do this.  The C-128 "native" mode version of the Big Blue Reader program has
this ability.  Utilities like Supersweep and Crosslink can also port your
files to CBM DOS 15xx disks, albeit these two programs set limitations on
the size of the files in question.
 
That's nice, but i'm a C-128 CP/M'er through and through!
 
Ok, you beat it out of me.  There *is* a way to "save" files to a regular
CBM DOS diskette from within C-128 CP/M.  You first use the C128LOAD utility
by David Bratton to load a file into Bank 1 of the CP/M memory map.  Next, you
hit CTRL-ENTER (on the numeric keypad, do not press RETURN) and this will put
you in C-128 "native" mode.  Finally, you use the built in C-128 ML monitor
to save to disk the memory block (Bank 1) where the program was loaded.
Please note the following example;
 
 
     A> C128LOAD DIEHARD.C64
 
        Program loaded - from 2000 to 8192  <-- Last byte-address
 
     --When the A> prompt reappears, press the CONTROL and ENTER keys
       simultaneously.  (The ENTER key is on the NUMERIC KEYPAD.)
 
       <The C-128 will now boot Basic 7.0 -- C-128 "Native" mode>             
 
     READY.
     MONITOR (activate the C-128 ML monitor & insert CBM DOS disk into drive 8)
 
     S "DIEHARD.C64",8,2000,8193  <-- Always add +1 to the last byte-address.
                            ----
 
In effect, we have just moved the file DIEHARD.C64 from C-128 CP/M to a CBM
DOS disk (remember to use the bank address of 1 when saving).  While this
method places an absolute restriction on the file size of what you can "save",
it means that files can be "saved" to any drive supported by the C-128.  It
is possible to save only one file per iteration of this cycle.  In order to
save many files, you will have to reboot CP/M and go through these steps
for each file involved.  This is a roundabout way to "write-back" to CBM DOS
and it certainly isn't as contained within CP/M as most of us would like.
 
How do the exciting CMD FD-2000 and FD-4000 drives factor into all this?
 
They are "covered" insofar the programs that support the 1581 also support
them, namely the JUGGLER, MSDOSEM, and C128LOAD utilities, and also the 28 MAY
87 Version of CP/M 3.0+ on the C-128.  The FD series drives do a pretty good
job at emulating the 1581 and should present no problems under C-128 CP/M.  The
1581 is limited to around 800 kilobytes and this may not be adequate for you.
As far as accessing the high capacity disks within the scope of the FD-drives,
the key issue is software (again).  For example, there is nothing in terms of
hardware that says we cannot use these drives to play with MSDOS 2.88 megabyte
formats (ala FD-4000) or create 2.88+ megabyte CP/M formats of our own design.
I do not know if FD-based software exists to allow this under C-128 CP/M.
 
How do I use the strengths of some programs to get around the limitations
of other programs?
 
I take it you are asking this because of the software gap in linking CP/M
to CBM DOS (at least from the CP/M side of the equation)?  Well, we have
a trump card in our sleeves in the form of the MSDOS format-types.  These
tend to be the lowest common denominator for information inter-change in
the entire microcomputer world.  For example, if the JUGGLER utility lacks
the ability to communicate with a particular brand of CP/M format-type (a
rare event indeed), chances are the system (with the funny format-type) has
some means to get information from its disks onto MSDOS disks.  Obviously,
I can simply use the MSDOS format-types as a bridge "over troubled sectors" :)
and not worry anymore.  The same holds true for CP/M to CBM DOS.  If it becomes
too inconvenient to save with C128LOAD and the built in C-128 mode monitor,
I simply pop up TRANSFER 128 or MSDOSEM and let them put things into MSDOS
disks for later retrieval by CBM DOS-able "native" mode programs such as Big
Blue Reader or Little Red Reader/Writer 128.  Mind you, this has its own
share of inconveniences, but it does present an attractive alternative to
some people.
 
What if I don't have a C-128 but I need to put files on a C-128 CP/M disk
or some other type of CP/M disk?
 
Hmmm...that's a tad outside the scope of this article but I can see a situation
arising from this where a C-128 CP/M user might miss out on some software.  If
you have a machine that runs MSDOS, you can avoid that situation.  There is a
MSDOS to CP/M utility called 22DISK140 which supports 140 different CP/M MFM
formats in its demo version and over 400 (!) in the full version.  The key
thing here is that it supports the Commodore 1581 CP/M MFM format.  Any files
you put in there are going to be useful to a C-128 CP/M user as long as he/she
has a 1581 and the 28 MAY 87 Version of CP/M 3.0+ on the C-128 (or a suitable
patch which supports the Commodore 1581 CP/M MFM format).
 
You've shown me the software, but where do I get it?
 
All the C-128 CP/M utilities I mentioned are available on Internet FTP sites
such as  ccnga.uwaterloo.ca  in directory  /pub/cbm/os/cpm.  I have also seen
some of them in the GEnie and Delphi information networks.  The 22DISK140
utility is available on the Internet FTP site  oak.oakland.edu  in directory
/pub/msdos/diskutil
 
Final thoughts...
 
Software Software Software... This article is by no means the final word
on what you can or can't "port" out of C-128 CP/M.  For example, Apple CP/M
GCR disks are completely isolated from C-128 CP/M, 22DISK140 or anything out
there besides an Apple drive!  However, some clever magician among you may
set to master the Apple GCR format using a Commodore GCR drive.  If you do it,
drop me a line, good CP/M software is usually a thing of beauty.  As I hinted
the last time we met, CP/M's strength lies in its software, (: and in its users
does its software lie. :)
 
-------
Mike Gordillo is an expert in CP/M and Z80 programming as well as
a devout Commodore fanatic.  He may be reached on the Internet as
s0621126@dominic.barry.edu for general comments or questions.



:::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::3::::::::::::::::::::
\H01::::::::::::::::::::::::::H:A:R:D:W:A:R:E:::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


                     The X10 PowerHouse, What is it?

                     by Dan Barber (xy3951@epix.net)


Well to put simply, it is a system that allows you to control up to 256 lights
and or appliances in your house automatically, or from a distance.

The most basic part of the system is the modules.  They are small boxes that
plug into a plain 2 or 3 pin electrical outlet (depending on the module you
have purchased) and the device you wish to control is plugged into the module.
Then you can get numerous remote controls that send signals to a receiver
that then sends the signals over your wiring to the individual modules,
which then turn on, off, or dim (if you are using a lamp module).

As I have said, you can get remote controls to control the appliances,
but why not just control them with the normal switch and save yourself some
money?  Because a computer interface can also be purchased for the system,
and this interface can be programmed with a Commodore 64/128!  Or if you are
one of the unfortunate that don't own a Commodore, then you can use a MS-DOS,
Apple Macintosh, or Apple IIe/IIc.

The CP290 Home Control Interface is 5 inches wide by 7 long, and 1 1/2 deep.
The top face is covered by rocker keys which allow you to control 8 modules
from one HOUSECODE which is adjustable.  The modules have two dials on them,
the first is the letters A-O.  This is called the the HOUSECODE.  The second
dial on the modules is the number 1-15, this is the UNITCODE.  So, your
first module would probably be name d A1 and the second A2 and so on, for
a total of 256 modules.  

The interface can handle control of all 256 modules but if you are using
the Commodore 64/128, you can only program 95 using the included point-and
-click GUI (Graphic User Interface) program.   If you have more than 95
modules you will have to use the included BASIC extension program that is
included on the disk.  Then you can create your own simple program to control
all of the modules if need be.  But I can't personally imagine anyone using
over 95 modules, unless you have a mansion.  :)

The program that sets up the interface is very simple to use.  With the
power off you connect the cable to the userport and the interface turn it
on and load the program.  You set the time and the HOUSECODE you want the
rocker keys to respond to and then the screen shows the different rooms
you can "install" modules in.  There are 9 "virtual" rooms you can place and
program modules, though it does not make any difference what room you have
the module in as long as the dials on it are set properly.  Programs for use
with other computer systems have differences, some you type in the name of
the room instead of using the "virtual" rooms.  And some can control all 256
modules with the supplied software, where as other systems must write there
own software if they need to control all 256 modules.

Once you pick a room (by using the cursor keys or the joystick to move a
pointer and clicking) you are presented with 11 "tabs" where you can "place"
modules.  When you select one you are presented with choices of icons
(graphics) that look like appliances and lamps.  When you chose one it is
stored in the interface as well as the HOUSECODE and UNITCODE.  Note, it does
not make any difference what icon you select to go along with a module, it
is just to remind you of what the device is.  So you could have a toaster
icon (graphic) actually controlling a coffee pot, the only thing that really 
matters is the dials on the modules and the interface codes are the same.

Once you have the rooms set up with icons you like, you go to the operate
mode.  This is where you program the interface to come on or off at specific
times of days.  You can set it to go on NOW, which turns it on immediately.
OFF immediately, ON TODAY, OFF TODAY, ON/OFF SPECIFIC DAYS, ON/OFF EVERYDAY.
And this is the beauty of the system, you can have your house have a "lived-in"
look even though it isn't.  With SPECIFIC DAYS and EVERYDAY modes, you can set
a security mode.  This means that if you set something to come on at 5:00pm,
it would come on between 5:00pm and 5:30pm.  It just picks a random time and
ON (or OFF) it goes.  The interface is very versatile and you can enter
numerous ON and OFF times, so for instance, if you have kids that forget to
turn off the lights you can have them go off-only at designated times, the
same goes for ON.  Now this is why this system shines over others, when you
aredone programming you unplug the interface from the computer!  It does
not tie the computer up.  You only use the computer to program the interface.
Afterwards it runs completely on its own.

The interface will handle up to 128 timed events, which means if you were
controlling a Christmas light setup, and you were changing the lights every
minute (the closest span that you can program a timed event) you could have
over 2 hours of automatic control.  I should mention that it does not make
any difference how many modules have been setup to go on or off for one
particular time (up to 16 modules).  It would still be considered one time
event (ex. modules A1, A4, A7, and A15 programmed to go on at 70% brightness
on Mondays, Wednesdays, and Fridays at 7:30 p.m. is just one event).  Also, 
you can only set brightness for a lamp module with the interface hooked up
to the computer to send the commands manually, or as a pre-programmed event.
So, if you just hit one of the rocker keys or turn the lamp on, off, and
back on (the module will sense this and turn on), the lamp will just come
on full.


AFTER PROGRAMMING IS COMPLETE
And after you get the interface all programmed just the way you want it-the
power fails and you loose your entire program.  Well, if you have a 9 volt
battery installed the program would have been safe.  The battery can hold the
memory of the interface for 10 hours (or so).  And it is a good precaution,
the only problem might be to forget it is in there and after a few years,
the battery leaks.  But that applies to any battery-powered device.  There
is also the option to save the entire contents of the interface to disk.  
This is available as a separate program on the disk, and it saves the interface
data as a file to the disk.  Three saves can be on the disk at one time, and if
you need more just copy the disk, it is not protected.  One of the greatest
benefits of this feature is the ability to swap setups in and out so you can
have one for different times of the year.  For instance you could save your
Christmas light setup to disk and next year get out the same setup and save 
yourself a lot of time in programming.

If you have a elaborate house setup that takes 20 minutes or more to program.
It is great to be able to load the settings back in a minute or so!  Another
good reason for having a backup is because the interface can "lock-up" and
you must remove the battery and unplug it to reset it.  This of course causes
the program to be lost so having a backup is necessary in this case, otherwise
you have to reprogram it.  I have had it happen once, but I was moving the
interface.  I think that the battery lost contact momentarily while I was
moving it to another room and caused a "lock-up" to occur.  But I had just
done a backup so nothing was lost.

Power failures have different effects on the modules as well.  The appliance
module will stay in the same position (they have a latching mechanical relay
type switches) as it was before the outage.  Where as, the lamp modules will
be off after a power failure (they have a triac for dimming), no matter what
state the module was in before the failure.


HARDWARE
There are a lot of clones on the market, and they all appear to be compatible.  
X-10 was the first, but other places such as Radio Shack has both the interface,
modules, and other addons for the system.  But in the case of Radio Shack's
interface, it is missing a very important feature (at least in my opinion):
no manual rocker keys!  Other than that, I don't know of any differences
between the systems.

** Just as this issue of disC=overy was going to press, I found out that
   X-10 manufactures all the clones themselves!  The differences are minor
   and limited to cosmetic changes as the example listed above.

The equipment is long lasting (people have had the system in place for over
20 years).  But of course if you have a lightning strike I would not expect
them to survive, and don't try to put a surge suppressor on them either.  Most
filter the lines and will block any signals going out or coming in to the
attached equipment.  And a plug-in intercom system or baby monitor will
interfere with the signals if they happen to be sent at the same time as
the intercom is on.

In short this system is MUCH easier than running cabling all over your house,
and much cheaper.  You can get the interface and 4 modules for around $100.
And if you want to control a chandler or other such lighting fixture, just
replace the switch with an X-10 controllable switch, and the same applies
for outlets.  And replacing them is very easy, and can be done in an afternoon
(unless you are doing an entire mansion).  Just make sure to turn off the
power at the breaker!!!!  Other addons for X-10 include entire alarm systems,
motion sensing flood lights, emergency dialer (dial for assistance when you
push the remote control), telephone responder (remote control your home
while you are away), and many other interesting products.

There have been some interesting uses I have heard of for the X-10 system.
Like, magicians using it in there act, remote control lighting for underground
cavetours, and lighting control for chicken farms to fool the chickens into
laying more eggs.  And many others, this proves that is no limit to what the
system can do if you put your mind to it.


TECHNICAL SEPCIFICATIONS
The processor is in the interface is a 80C48.  And I mentioned before a maximum 
of 256 modules with a maximum of 128 timed events.

The transmissions are complex and are sent twice for verification and basically
the signals involve short RF burst which represent digital information.  The
transmissions are synchronized to the zero crossing point of the AC power line.
The goal is to transmit as close to zero crossing point as possible, but
certainly within 200 microseconds of the crossing point.  A Binary 1 is
represented by a 1 millisecond burst of 120 kHz at the zero crossing point,
and Binary 0 by the absence of 120 khz.  And the instructions to the module is
sent with those two signals (as is all computer information).
  
The complete code transmission involves eleven cycles of the power line.  The
different cycles represent different commands to the module.  Such as the first
two cycles represent start, next four represent the House Code and the last
five represent either a Number Code (1 thru 16) or a Function code (ON, OFF,
etc).  The programming manual is reproduced below with permission and for the
edification of the reader.
--

[Ed. Note : Portions of the technical information below require some basic
 understanding of signal theory and electronics.]


INTRODUCTION
Software is required to use your computer to program the X-10 Home control
Interface.  The interface is packaged with software and connecting cable for
either IBM PC, Apple Macintosh, Apple IIe/IIc or Commodore 64/128.
THAT'S ALL YOU NEED.

A utility program of Basic statements is included with the Home Control
Software for IBM, Apple IIe/IIc and Commodore Computers.  These Utility
programs let you write your own programs in Basic.

For more advanced programming you may also need to refer to this programing
guide.

This programing Guide is for advanced programmers who wish to write their
own software using Machine code and need information than is supplied in the
owner's manual which comes with the X-10 Home Control Software.

If you do NOT intend to write your own software, don't be intimidated by
this programming guide- you don't need it.

The Interface must be connected to your computer for programming but once
programmed it can be disconnected and will continue to send commands to the
X-10 Modules under time control.

The Main functions of the Interface are summarized as follows.

*Maintains a real time clock
*Stores the timed events relating to control of lights and appliances in the
home.
*Stores the graphics data required by the computer to display the details of
lamps and appliances installed into the house by the user.
*Transmits X-10 Control Signals onto existing house wiring to control lights
and appliances connected to the X-10 Modules.

A 9volt Alkaline battery will provide approximately 100 hours back up for the
Interface clock and stored data.  When the Interface is running on battery
power, the L.E.D. pulses approximately once every 5 seconds.

Up to 128 timed events + 256 ICONS (Graphical pictures of lights and
appliances) can be stored in the Interface.  A timer event is any number of
unit codes on the same Housecode programmed to go on or off at a particular
time at a specified brightness level on any day or days of the week.
(E.G. Modules A1, A4, A7 and A15 programmed to go on at 70% brightness on
 Mondays,Wednesdays and Fridays at 7:30 P.M. is just one event and 128
 events can be stored.)

The Interface has 8 rocker keys to give manual control of unit codes 1 thru 8
onthe Base Housecode.  Base Housecode is set to "A" on power up but can be
changed by the software.

PROGRAMMING

The Interface is programmed to recognize 8 different types of instruction
for the computer and each instruction has an ID number between 0 and 7.  Each
instruction from the computer has a leading SYNC pattern of 16 x FF bytes. The
ID number tells the Interface what type of data to expect and a check sum is
maintained which is compared with the last byte of data in the instruction.
If the check sums agree, the Interface will acknowledge back to the computer
and obey the instruction.  It is suggested that if no response to an
instruction is received within 10 seconds, the computer should advise the
user of potential problem with the connections to the Interface. 
(E. G. the program could display a message such as "Error check Interface
 connections.  Press Enter to continue".)

The Interface is programmed via the 5 pin DIN socket on the back of the
Interface.  The pin connections are shown below.

                      Looking at Back of Interface
     5         1
       4     2
          3
Pin  Description
1    -
2    Receive (Input)
3    Ground
4    Transmit (Output)

The input signals from the computer (receive data input) are connected between
pins 3 and 2.

The output signals to the computer (transmit data output) are connected
between pins 3 and 4.

A data cable is available for IBM, Macintosh, Apple IIe, and Commodore 64/128
and is included with the Interface and software for these computers.
<Note-The Commodore cable and software must be purchased separately, it is no
longer sold with the interface.  To get the cable-software package you must
order one from X-10..>

Voltage levels meet RS-232 specifications and the data format is RS-232 with
the following characteristics.

Baud rate:  600.
Data bits:   8.
Parity:      None.
Stop bits:   1.


BYTE FORMAT
           Start                                Stop
            Bit  D0  D1  D2  D3  D4  D5  D6  D7   Bit
     V+    ---     ---            ---     ---
           ! 0 ! 1  ! 0   1   1   1 ! 0 ! 1 ! 0 !  1
     V- ---     ---    -----------     ---    ---

1011   D   D0 to D3
1010   5   D4 to D7

Dec = 93
Hex = 5D
0 = Mark
1 = Space

A gap of 1 Millisecond should be left between each byte of data sent.

A start bit signifies that a string of 8 data bits will follow.  A start
bit is always a SPACE bit, i.e. "0".  A stop bit signifies that the data is
finished and separates one byte from another.  A stop bit is always a MARK
bit, "1".


DOWNLOAD BASE HOUSECODE

When the Interface is first powered up, the Base Housecode is set to "A".
To change this you must first send a leading SYNC pattern of 16 x FF bytes
to the interface, followed by the identifier "0" for "download base Housecode"
and then of data, the upper nibble of which contains the Housecode information.
See below.


BYTE    D7    D6    D5    D4    D3    D2    D1    D0
1-16    1      1     1     1     1     1     1     1  Sync, 16 X FF
 17     0      0     0     0     0     0     0     0  ID 0, download Base 
                                                      Housecode
 18    ---HOUSECODE------------- 0     0     0     0  See table 1.



TABLE 1

House   Byte 18     House   Byte 18     House   Byte 18
  A       60         B       E0          C       20
  D       A0         E       10          F       90
  G       50         H       D0          I       70
  J       F0         K       30          L       B0
  M       00         N       80          O       40
  P       C0 

Base Housecode is used by the rocker keys on the Interface.  Changing the
Base Housecode will reset all timer events and graphics data stored in the
Interface, therefore before downloading a new Base Housecode to the Interface,
the program should warn the user of this.  (E.G. the program could display
"Warning changing Base Housecode will erase all program information.  Continue
with change yes/no".)

After a successful download, the Interface will acknowledge by sending the
"ACK" message to the computer.


ACK MESSAGE

Byte    D7    D6    D5    D3   D2   D1   D0
1-6      1     1     1     1    1    1    1  FF X 6
 7       0     0     0     0    0    0    S  Status

The STATUS bit is reset to "0" during power up of the Interface and is set
to "1" by a download of data from the computer (any data with byte 17 equal
to ID 0, 1, 2, or 3).  The STATUS bit is used to warn the computer that the
Interface has been powered down.  E.G. a STATUS bit equal to "0" could tell
the program to display a message such as "The Interface has been powered
down and contains no data.  Press Enter to continue".


DIRECT COMMAND (instant ON or OFF)

To turn something ON or OFF or adjust the brightness level of a light
instantly,m it is first necessary to send a leading SYNC pattern of 16 x FF
bytes of data to the interface.  This is then followed by the identifier "1"
for "direct command" and then 4 bytes of data followed by a check sum.  The
check sum is the sum of bytes 18 through 21.  See below.


BYTE     D7  D6  D5  D4  D3  D2  D1  D0
1-16      1   1   1   1   1   1   1   1   SYNC FF x 16.
 17       0   0   0   0   0   0   0   0   ID1, Direct command.
 18           LEVEL          FUNCTION       See notes 1 and 2.
 19         HOUSECODE     0   0   0   0     See table 1.
 20       9  10  11  12  13  14  15  16   Bit mapped unit codes.
 21       1   2   3   4   5   6   7   8   X-10 Modules.
 22                   CHECK SUM           Sum of bytes 18-21.


NOTE 1
"LEVEL" is dimmer settings for lamps.  This applies only to X-10 Lamp Modules
and Wall Switch Modules.  Level F Hex is full DIM and level 0 Hex if full
BRIGHT.  The lamps specified by bytes 20 and 21 will switch on, adjust to full
brightness and then DIM to level specified by the upper nibble of byte 18.
All codes between 0 Hex and F Hex are acceptable thus providing 16 discrete
light levels.

NOTE 2
D3  D2  D1  D0  Function    Explanation
0    0   1   0    ON        Modules with house codes as specified   
                            by upper nibble byte 19 and unit codes as 
                            specified by bytes 20 and 21 will turn on.

0    0   1   1    OFF      As above, except turn OFF.

0    1   0   1    DIM      Lamp Modules and Wall Switch Modules addressed as 
                           above will turn on, adjust to full intensity and 
                           then DIM to the level specified by upper nibble of 
                           byte 18.  Appliance Modules do not respond to 
                           bright and dim codes.


NOTE 3
If the check sum is accepted, the Interface will send the ACK response to the
computer and will then transmit the X-10 codes onto the house wiring.  When
the power line transmission is complete the command is uploaded to the computer
(see command upload).  Also, if X-10 codes are transmitted by pressing the keys
on the Interface, at the end of each transmission the codes are uploaded to
the computer.  This allows the computer to keep track of the ON/OFF status
of the Modules while it is connected to the Interface.


DIRECT COMMAND EXAMPLES
Example 1:  Turn ON modules A1 and A4
Bytes:   1-16  17  18  19  20  21  22
Data:     FF   01  02  60  00  90  F2

Example 2:  Turn OFF modules A1 and A4
Data      FF  01  03   60  00  90  F3

Example 3:  Turn on lamp module B9 and DIM to 50%
Data      FF  01  75   E0  80  00  D5

Example 4:  Turn OFF all modules with housecode A
Data      FF  01  03   60  FF  FF  61


COMMAND UPLOAD (Interface to Computer)
This follows every transmission of X-10 onto the power line either from
pressing the rocker keys, or from direct commands, or from timed events.
This enables the computer to keep track of the ON/OFF status of lights and
appliances.

Byte   D7  D6  D5  D4  D3  D2  D1  D0
1-6     1   1   1   1   1   1   1   1   FF X 6.
7       0   0   0   0   0   0   0   0   Status.
8          HOUSECODE       FUNCTION     See note 4 below.
9       9  10  11  12  13  14  15  16   Bit mapped unit codes of
10      1   2   3   4   5   6   7   8   X-10 Modules.
11      BASE HOUSECODE  0   0   0   0   Same as table 1.
12                 CHECK SUM            Sum of bytes 8-11.

NOTE 4 -House code same as table 1.  Function same as note 2, except that
the code for DIM is UPLOADED to the computer as 0100 (4hex).


SET CLOCK (Computer To Interface)
To set the clock in the Interface it is first necessary to send a leading
SYNC pattern of 16 X FF bytes of data.  This is followed by the identifier "2"
for set clock and then 3 bytes of data followed by a check sum.  This check
sum is the sum of bytes 18 thru 20.  See below.

Byte   D7   D6   D5   D4   D3   D2   D1   D0
1-16    1    1    1    1    1   1    1    1     SYNC 16 X FF.
17      0    0    0    0    0   0    1    0     ID2, set for clock.
18      0    0             MINUTES              HEX 00 to 3B (0 TO 59).
19      0    0    0         HOURS               HEX 00 to 17 (0 TO 23).
20      0   Sun  Sat  Fri  Thu Wed  Tue  Mon    Bit mapped Days.
21                    CHECK SUM                 Sum of bytes 18 to 20.

SET CLOCK EXAMPLES

EXAMPLE 1  To set clock to 9:30 a.m. on Monday.
BYTE  1-16   17   18   19   20   21
DATA   FF    02   1E   09   01   28

EXAMPLE 2  To set clock to 7:45 p.m. on Friday.
BYTE  1-16   17   18   19   20   21
DATA   FF    02   2D   13   10   50



TIMER EVENT OR GRAPHICS DATA DOWNLOAD
(Computer to Interface)

To download either a timed event or graphics data you must frist send a
leading sync pattern of 16 X FF bytes.  The ID. (Byte 17) is 3 HEX for both
timer events and graphics data but D2 in Byte 19 is a "0" for timer events
and a "1" for graphics data.

Timer events are stored in bytes 0 to 1023 of the 2k X 8 RAM in the interface.
Only bytes 20 to 27 of the downloadable message are stored.  Each event (group
of 8 bytes) is assigned a Start Address in the RAM in the Interface.  This
Star Address is specified by A0-A4 in Byte 18 and A5-A6 in Byte 19.  D0, D1,
and D2 in byte 18 must ALWAYS be 0, so that the Start Addresses increase in
multiples of 8 (0, 8, 16, .... 1016).  The computer should keep track of the
event Start Addresses and load new events into vacant address locations in RAM.
Byte 20 designates the type of timer event as shown in table 4.  Bytes 21
through 23 set the time and day of the event.  Bytes 24 and 25 specify which
Modules will be controlled and byte 26 specifies the Housecode of these
Modules.  Byte 27 specifies whether the Module(s) will turn ON, OFF, or
DIM and to what brightness level.  Byte 28 is the sum of bytes 20 to 27.


TIMER EVENT DOWNLOAD
BYTE   D7   D6   D5   D4   D3   D2   D1   D0
1-16    1    1    1    1    1    1    1    1    SYNC 16 X FF
17      0    0    0    0    0    0    1    1    ID3, event/graphics download.
18     A4   A3   A2   A1   A0    0    0    0    A0 to A6 binary coding of
19      x    x    x    x    x    0   A6   A5    event number.
20      0    0    0    0         MODE           See table 4.
21      0   Sun  Sat  Fri  Thu  Wed  Tue  Mon   Bit map of days.
22      0    0              HOUR                HEX 00 to 17 (0 to 23).
23      0    0    0        MINUTE               HEX 00 to 3B (0 to 59).
24      1    2    3    4    5    6    7    8    Bit map of unit codes.
25      9   10   11   12   13   14   15   16    Bit map of unit codes.
26         HOUSECODE        0    0    0    0    Same as table 1.
27          LEVEL               FUNCTION        Same as Notes 1 and 2.
28                   CHECKSUM                   Sum of bytes 20 to 27.

X=DON'T CARE



TABLE 4 - TIMER MODE SELECTION

BYTE 20 lower nibble
D3    D2    D1    D0    MODE      EXPLANATION
 1     0     0     0    NORMAL       Occurs on a weekly cycle at 
                                     the same time each day, on 
                                     day or days specified by byte 
                                     21 and at the time specified 
                                     by bytes 22 and 23.  The 
                                     function and codes for event 
                                     are specified by bytes 24 to 27.

 1      0     0     1    SECURITY    Same as NORMAL mode 
                                     except that the event time 
                                     will be different each day 
                                     and will be within one hour 
                                     after the time specified by 
                                     byte 22.  (varies in a 
                                     pseudo random pattern). 
                                     SECURITY is only available in 
                                     EVERYDAY and SPECIFIC DAYS  
                                     modes, see note 5.



TABLE 4 - TIMER MODE SELECTION
BYTE 20 lower nibble
D3    D2    D1    D0    MODE      DESCRIPTION
 0     1     0     0    TODAY     EVENT occurs only TODAY at 
                                  the time specified by bytes 22 
                                  and 23. and will be cleared from 
                                  memory at midnight TODAY.

 0     0     1     0    TOMORROW   EVENT occurs only TOMORROW 
                                   at the time specified by bytes
                                   22 and 23. and will be cleared 
                                   from memory at midnight 
                                   TOMORROW.

 0     0     0     0     CLEAR      Clears from memory, the 
                                    event specified by the event 
                                    number stored in bytes 18 
                                    and 19.


NOTE 5
In addition to TODAY and TOMORROW, it is suggested that the program offer the
user the choice of EVERYDAY and SPECIFIC DAYS.  If EVERYDAY is chosen, byte
21 should be sent as 7F HEX (all days selected).  If SPECIFIC DAYS is chosen,
byte 21 should indicate which days were chosen.


GRAPHICS DATA DOWNLOAD
Graphics data is stored in bytes 1024 to 1535 of the 2K  x 8 RAM in the
interface.  Only bytes 20 and 21 of the downloaded message are stored.  Each
pair of bytes is assigned a number between 0 and 511 as specified by A0 to A6
in byte 18 and A7 in byte 19.  D0 in byte 18 is ALWAYS '0' so these address
numbers increase in steps of 2 (for graphics type and X-10 code of 256
objects).  Note also that byte 19 D1 is ALWAYS "0" and D2 is ALWAYS "1".
The computer should keep track of the message numbers and load new messages
into vacant address locations.  The contents of bytes 20 and 21 depends on
the graphics approach used by the programmer (see note 6), the interface
merely stores this data and will upload it to the computer upon request (see
graphics upload).  Byte 22 is the sum of bytes 20 and 21.


GRAPHICS DATA DOWNLOAD
BYTE   D7   D6   D5   D4   D3   D2   D1   D0
1-16    1    1    1    1    1    1    1    1   Sync 16 X FF
17      0    0    0    0    0    0    1    1   ID3, event/graphics download.
18     A6   A5   A4   A3   A2   A1   A0    0   A0 to A7, binary number for
19      X    X    X    X    X    1    0   A7   graphics object- 256 objects.
20                 GRAPHICS DATA               User RAM to define type and
21                 GRAPHICS DATA               X-10 code of graphics object.
22                   CHECK SUM                 Sum of bytes 20 and 21.

X= DON'T CARE 


Note 6
A suggested allocation for byte 20 is shown below.
BYTE 20   D7   D6   D5   D4   D3   D2   D1   D0
          1=ON          ICON TYPE
          0=OFF

FOR EXAMPLE
ICON of a lamp shown in the ON state.
           1    0    0    0    0    0    0    1
ICON of a T.V. shown in the ON state.
           1    0    0    0    0    0    1    0
ICON of a coffee pot shown in the ON state.
           1    0    0    0    0    0    1    1
ICON of a fan shown in the OFF state.
           0    0    0    0    0    1    0    0

Byte 20 = 0 indicates a vacant ICON storage location.  To clear an ICON from
the Interface you need to send a graphics download with byte 20 = 0.  Note,
after doing this, you should also send a DOWNLOAD TIMER EVENT message with
byte 20 = 0 (to clear any timed events for the removed ICON).

A suggested allocation for byte 21 is shown below.

BYTE 21    D7   D6   D5   D4  !  D3   D2   D1   D0
              HOUSECODE OF    !  UNIT CODE OF 
              STORED ICON     !  STORED ICON



REQUEST CLOCK AND BASE HOUSECODE
(Interface to Computer)

To upload the time and Base Housecode from the Interface it is first necessary
to send a leading SYNC pattern of 16 X FF bytes, followed by an ID4 for the
request clock and Base Housecode.  See below.

BYTE    D7   D6   D5   D4   D3   D2   D1   D0
1-16     1    1    1    1    1    1    1    1    SYNC FF X 16
17       0    0    0    0    0    1    0    0    BASE Housecode.

If the Interface receives the request correctly it will respond by uploading
the clock and Base Housecode to the computer, as shown.  If the request is
not received correctly, no response is given.



CLOCK AND BASE HOUSECODE UPLOAD
Byte   D7   D6   D5   D4   D3   D2   D1   D0
1-6     1    1    1    1    1    1    1    1   SYNC 6 X FF
7       0    0    0    0    0    0    0    S   status bit. *
8       0    0             MINUTES             HEX 00 to 3B (0 to 59)
9       0    0    0         HOURS              HEX 00 to 17 (0 to 23).
10      0   Sun  Sat  Fri  Thu  Wed  Tue  Mon  Bit mapped days.
11      BASE HOUSECODE      0    0    0    0   Same as table 1.
12                  CHECK SUM                 Sum of bytes 8 to 11.

* The STATUS bit is reset to "0" during power up of the Interface and is set
to "1" by a DOWNLOAD of data from the computer (any data with byte 17 equal to
ID 0
, 1, 2, or 3).  The STATUS bit is used to warn the computer that the Interface
has been powered down.  E.G. a STATUS bit equal to "0" could tell the program
to display a message such as "The Interface has been powered down and contains
no data.  Press Enter to continue".



REQUEST TIMER EVENTS (Interface to Computer)
To upload the timer events from the Interface it is first necessary to send a
leading SYNC pattern of 16 X FF bytes, followed by an ID5, for request timer
events.  See below.

BYTE   D7   D6   D5   D4   D3   D2   D1   D0
1-16    1    1    1    1    1    1    1    1    SYNC FF X 16
17      0    0    0    0    0    1    0    1    ID5, request timer events.

The Interface will respond by uploading to the computer, all of the 128 events
starting with number 1 as shown below.  A vacant event is represented by a
single FF byte, this shortens the time for the upload.  The check sum does
not include these FF bytes.



TIMER EVENTS UPLOAD
EXAMPLE WHERE ONLY FIRST TWO EVENTS ARE PROGRAMMED

BYTE   D7   D6   D5   D4   D3   D2   D1   D0
16      1    1    1    1    1    1    1    1     SYNC FF X 6.
7       0    0    0    0    0    0    0    0     Status.
8-15   EVENT NUMBER 1 AS DOWNLOADED, 8 BYTES.
24-149  1    1    1    1    1    1    1    1     FF X 126 to indicate 126 
                                                 vacant event spaces.  
150                   CHECK SUM                  Sum of bytes 8 to 23 (FF 
                                                 bytes ignored).


REQUEST GRAPHICS DATA (Interface to Computer)

To upload graphics data from the Interface it is first necessary to send a
leading SYNC pattern of 16 X FF bytes followed by and ID6, for request
graphics data.  See below.

BYTE    D7   D6   D5   D3   D2   D1   D0
1-16     1    1    1    1    1    1    1    SYNC FF X 16
17       0    0    0    0    1    1    0    ID6, request graphics data.

The Interface will respond by uploading to the computer, all of the 256
ICONS starting with number 1, as shown on page 33.  A vacant ICON space is
represented by a single FF byte, this shortens the time for the upload.  The
check sum does not include these FF bytes.



GRAPHICS DATA UPLOAD
EXAMPLE WHERE ONLY 5 ICONS ARE PROGRAMMED

BYTE    D7   D6   D5   D4   D3   D2   D1   D0
1-6      1    1    1    1    1    1    1    1    SYNC FF X 6.
7        0    0    0    0    0    0    0    0    Status.
8-9                   ICON NUMBER 1              2 bytes.
10-11                 ICON NUMBER 2              2 bytes.
12-13                 ICON NUMBER 3              2 bytes.
14-15                 ICON NUMBER 4              2 bytes.
16-17                 ICON NUMBER 5              2 bytes.
18-268   1    1    1    1    1    1    1    1    FF X 251 to indicate 251 
                                                 vacant ICON spaces.
269                    CHECK SUM                 Sum of bytes 8 to 17 (FF 
                                                 bytes ignored).


DIAGNOSTIC

The Interface has a self test diagnostic routine which is initiated by sending
a leading SYNC pattern of 16 X FF bytes followed by a ID7.  Upon receiving
this instruction, the Interface will run a self check on it's own hardware and
software (firmware).  The output of the Interface (pins 3 and 4) will go low
for 10 seconds as part of this test.  If the check is o.k. the Interface
will respond by sending the ACK with status "0".  If a fault is diagnosed,
the interface will Send ACK with status "1" or will not respond.

BYTE    D7   D6   D5   D4   D2   D1   D0
1-16     1    1    1    1    1    1    1    SYNC FF X 16
17       0    0    0    0    1    1    1    ID7, initiate self test.



The above programming guide is included when you purchase the interface.  I
checked and it is freely distributable.  And remember, if you don't want to
control more than 95 modules, you don't need to create your own program (as
stated previously.

If you want to read further on the X-10 system, then go to there web page at
http://www.x10.com.  They have information on all the products they carry,
as well as ones still in the works.  Also on the web page is a FAQ (Frequently
Asked Questions) file that has a wealth of information on the X-10 system
and compatible products.  Unfortunately, the interface is no longer sold
packaged with the Commodore cable and software.  So, you must purchase the
interface packaged with some other software and call X-10 and order the
Commodore connection package separately for $25.  Of course you can create
a cable and copy the software (it is also freely distributable) if you
wanted to.  To get the interface and any modules or compatible products,
Home Controls has a great selection, and there catalogue is filled with
home automation equipment.  X-10's and Home Controls addresses and phone
numbers are as follows:


X-10 (USA) INC.
91 Ruckman Rd.
Closter, NJ  07624
Phone: 201-784-9700

Home Controls INC.
7626 Miramar Road, Suite 3300
San Diego, CA  92126-4446
Phone: 800-266-8765  or  619-693-8887



:::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::3::::::::::::::::::::
\H02::::::::::::::::::::::::::H:A:R:D:W:A:R:E:::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


 !......The Metal Shop......!
                                                     // - The Metal Shop - //

                      ^...!..The Metal Shop..!...^


  Contributors : XmikeX  (xmikex@eyrie.stanford.edu)
                 Co-Sysop of DiamondBack BBS (305)258-5039

                 David Wood (jbevren@willowtree.com)

                 Marc-Jano Knopp and Daniel Krug
                 http://www.student.informatik.th-darmstadt.de/%7Emjk/c64.html



Dear Metal Shop,

    I recently found a 16 KB Commodore expander for my trusty VIC-20.  My
glee turned to disgust after realizing that I needed the proper DIP scheme
required to bank the memory into the VIC.  I barely know the old '20, much
less this expander.  Please tell me this will be easy.... :)

RE
--

RE,

You are in luck RE because as the Good Lord would have it, I have also run
into a VIC-20 expander!  Ok...I didn't just "run" into it...  I actively
sought one out to view the new Veni Vedi Vic (Vedi Veni Vic?) demo by Marko
Makela (with umlauts over the a's).  :)  And oh Yes, this will be easy!

Without much further ado, I decoded the DIP scheme for my 16 KB VIC-20
expansion cartridge, as follows :

 
CBM VIC-1111 16 KiloByte RAM expansion cartridge with eight DIP scheme
----------------------------------------------------------------------
 
Expansion RAM bank 1                            Expansion RAM bank 2
(8 KB)                                          (8 KB)
                          8 KB Mapped to :
1     2     3     4                             5     6     7     8
on    off   off   off   -> $A000-RAM/ROM4 <-    on    off   off  off
off   on    off   off   -> $6000-RAM/ROM3 <-    off   on    off  off
off   off   on    off   -> $4000-RAM/ROM2 <-    off   off   on   off
off   off   off   on    -> $2000-RAM/ROM1 <-    off   off   off  on

 
As is evident above, the DIP settings for each bank yield identical results 
so I suspect that the settings for the 8 KB CBM ram expander follow the same
scheme.  8 KB chunks do keep things simple, and we can either give the VIC-20
just 8 KB of expansion memory at $2000, $4000, etc., or we can activate both
banks and map them to these RAM/ROM expansion blocks within the VIC's memory
map for 16 KB total.  For example, whereas 8 KB ROM images are usually quite
happy with 8 KB of memory at RAM/ROM4, most 16 KB rom images tend to require
that RAM/ROM blocks 3 and 4 be active.  We can accomplish this by mapping
expansion bank 1 into $6000 and bank 2 into $a000 (or vice versa!).  In this
case, our DIP settings could be :
 
        1     2     3     4     5     6     7     8
        off   on    off   off   on    off   off   off
 
                             OR
 
        on    off   off   off   off   on    off   off
 
 
Please note that with the CBM 16k expander, we cannot map any of its memory
to the 3 KB expansion area sitting at $0400-0fff (1024-4095).  - XmikeX
--

[Ed. Note : The following is a response to the 1541 <-> Apple disk ][
 discussion that appeared in this column, issue 2.  It has been reprinted
 in its entirety with permission from the author.]


>From    :  jbevren@willowtree.com
Date    :  19-JAN-1997 08:58:43.19
Subject :  info on 1541/disk ][ transfers

First: Thank you disC=overy magazine.  I've read a few zines, and only yours
and C= Hacking make the bet for me.

Keep up the _good_ work!


Dear Metal Shop,

I was reading the article in the disC=overy magazine, issue two.  In there
was a section from a user who has an Apple ][ computer who wished to
transfer files from the computer's disk drive to the Commodore 1541 (or was
it vice-versa?).

I am commenting on the hardware aspect of the disk ][ drives on the Apple
computer.  When I was in college, my 1541 failed completely due to
lightning.  The main board was fine, but the read/write head was blown
"open" as I discovered in the school's electronics lab.  Remembering my
days on the ol' ][ in high school, I thought of hooking a disk ][ assembly
to the 1541 board.

I acquired a junk drive assembly from a friend, unhooked all the
electronics, and re-wired the assembly to the 1541 drive's CPU board. 
Voila!  The system ran fine, until I could come up with a low-density
single-sided assembly from a PC to fit "inside" the 1541's case.  It even
ran fastloaders and GEOS.

Point at hand?  The disk ]['s mechanical assembly is functionally identical
to the 1541's drive assembly.  In fact, the Alps assembly was used in some
Apple compatible drives (am I right?).  Reading Apple disks on the 1541 will
be one hell of a challenge, because of the drive's limited RAM.  

However, reading Commodore's GCR format should be fairly easy if you have a
little advanced knowledge of the disk ][ interface.  All you have to do is
get a modified CBM DOS binary into the Apple ][, and have it read the
Commodore disk.  Not a trivial task mind you.  When the DOS is in the Apple,
the rest is easy.  simply change the locations the program uses to
read/write the disk, and you have it.

Hope I was helpful!

-David Wood

ps:  All the disk ][ is (with the interface included), is a 1541 with no
brains. :)
--

[Ed. Note : The text below to the end of the article is a sample of the C=
 hardware repair and enhancement projects put out by Marc-Jano Knopp & Daniel
 Krug on the WWW http://www.student.informatik.th-darmstadt.de/%7Emjk/c64.html
 Reprinted with permission.]

 
                                   SAFER SID!
                                       

Project: Protection circuit for SID
Target : C64 (all models)
Time   : 20 min
Cost   : ~1 US$
Use    : Protect the SID from overvoltage.


Cause
  
 The SID cannot stand more than 3Vss or 1Veff, respectively.
 Therefore, if you use the SID audio input, the input voltage
 should be limited. The circuit below limits the input signal
 to about 1.4Vss. This is done by two antiparallel silicon diodes
 which cut off the signal at +/- 0.7V. The resistor limits the
 current coming out of the audio source.

Ingredients

  - 1 resistor 470 ohm
  - 2 diodes 1N4148 (or 1N914)

Instructions

  1. Build the following circuit:

  SID protection circuit
  ----------------------


  (5)  O---------o----------o-------XXXXX-----O  Audio in
                 |          |
                 |          |                    __
              +-----+     -----                 /  \
 <-- to SID    \   /       / \          <--   -+----+----+-
                \ /       /   \                      \__/
               -----     +-----+
                 |          |
  (2)  O---------o----------o-----------------O  GND


  XXXXX  = resistor 470ohm
  diodes = 1N4148 or 1N914


 2. ... and stuff it in a DIN plug for the AV jack.



                         SPONTANEOUSLY RESETTING C-64?
                                       

Symptom: C-64 resets at random.
Target : C64 (all models) and VC20
Time   : 10 min.
Cost   : <1 US$


Cause

  Voltage level floats near to forbidden zone (unstable voltage
  below ~4 volts).

Fix

  Pull-up resistor between pin 3 (/RESET) and pin 2 (+5V) of
  user port. Value should be in the range of 4.7k to 10k,
  starting with 10k.

Note

  C-64s which are used industrially, should be equipped with an
  additional 100nF capacitor connected between pin 3 (/RESET) and
  GND (pin 1), which provides excellent noise immunity.
  The same applies to users encountering reset problems due to
  a long reset switch cable.

Ingredients

  - resistor of 10k (or less)

Instructions

  1. Open case, disconnect power plug!

  2. Solder one end of resistor to pin 2 the other end to pin 3
     of the user port connector. DO NOT solder the leads directly
     onto the contact area, instead try soldering them to the
     nearest visible bare point connected to the above pins.

  3. Connect power plug and keep your C-64 running for several
     hours. See if it resets spontaneously as before; if not,
     close the case again.

Possible failures

  - Resistor's value is too high, try 5.6k or 4.7k otherwise.



                               'FIX' RESTORE KEY!
                                       

Project: Make RESTORE key behave like all the other keys
Target : C64 (old)
Time   : 15 min.
Cost   : <1 US$
Use    : Never hear the RESTORE key singing
         'Killing me softly ...' again.


  Extract

   Replace 'NMI-capacitor' C38 by a 4.7nF one.
  
  Cause

   The value of the built in capacitor responsible for triggering
   the NMI via the timer 556 is too small in old C-64's (big PCB).
   On account of the pressure dependant resistance of the keys,
   pressing the RESTORE key just as softly as the other keys is not
   sufficient for producing a trigger signal at the timer's TRIG
   input. Therefore, we will replace it with a 4.7nF capacitor.
  
   The problem described above was fixed in the new (small) PCB's
   and in the SX-64.
  
Ingredients

  - 1 capacitor 4.7nF

Instructions

  1. Open case, disconnect power plug!

  2. Search for capacitor C38 (51pF) below the leftmost CIA. In
     most boards it is built into a resistor case with green base
     color! Its color code is: green-brown-black

  3. Unsolder it and replace it with the 4.7nF one.

  4. Connect power plug.

  5. Switch your C-64 on. After the startup screen appeared press
     [STOP]-[RESTORE] just as softly as you would usually hit the
     other keys. Repeat that procedure a few times and if the
     RESTORE key seems to behave like the other keys, close the
     case again.

Possible failures

 - Replaced the wrong capacitor. Often the capacitor is built into
   a resistor case.



                                 BLACK SCREEN
                                       

Symptom: Screen remains black.
Target : C-64


  Possible sources of failure
    - Power supply's fuse is blown -> no power :)
    - continuous reset -> no startup
    - Fuse -> no 12V for VIC (old C-64)
    - Voltage regulator -> no 12V, no fun (old C-64)
    - CPU -> blocks phi2 or bus
    - VIC -> blocks bus
    - SID -> blocks bus
    - PLA -> well.... :)
    - 8701 -> no clock cycles
  
  
Analysis - Power supply ok?

  If the power LED is not lit, check the power supply's fuse and
  replace it, if necessary. Next, measure the voltage directly on
  the power plug. If you cannot find 5VDC and 9VAC anywhere, kick
  the power supply and get a new one.

  If you have an old C-64, check its internal fuse now.

  Then check the 5V at the power jack (before the switch!) with
  the power supply plugged in and the C-64 switched on. It should
  be between 4.9 and 5.1V. If not, a defective IC might demand a
  current so high that the 5V level gets pulled below 4.8V. You
  should be able to locate the very IC by simply touching all the
  chips and checking for especially hot ones. Replace it.

  Next, measure the 5V supply voltage between pin 7 (GND) and pin
  14 (Vcc) of a 74xx chip. If the 5V show to be ok, then continue
  with 'Voltage regulator ok?'.
 
  Otherwise, it is very likely that the cause for the low vol-
  tage level is - believe it or not, it DID happen to a lot of
  people - the power switch(!), so that it could be a good idea to
  either rock the switch several dozen times, open and clean it or
  to replace it completely.

Reset line HIGH?

  Since it is possible that the black screen is caused by a con-
  tinuous reset, you should check the voltage between pin 1 (GND)
  and pin 3 (/RESET) of the user port. If it is below 1V, then
  check the reset switch, if you got one. Otherwise, check wether
  the output levels of the 7406 and 74LS14 (new board) match their
  input levels. Next, you should check the timer 556 (old board)
  when I managed to offer you a description for that chip :) Until
  then, simply replace it, it is not socketed, but cheap.

  If that does not help, some other chips load the reset line so
  that the level is below 2.4V which causes the CPU to reset. Try
  a pull-up resistor (4.7k) between pin 2 (+5V) and pin 3 (/RESET)
  of the user port. If that does not make the blank screen vanish,
  check for other defective chips (which probably become very hot,
  see later in this document).

Voltage regulator ok?

  In case you have an old C-64, check the 12V voltage regulator
  VR1 (7812). At the output (right pin, 2) you should measure
  around 12V (11.8 to 12.3V). If not, measure the input voltage
  (left pin, 1), the voltmeter should show about 15 to 20V. If the
  latter is the case, replace the VR. If the input voltage is out
  of range, check the 9VAC on the power plug (with the C-64 still
  switched on). If they prove to be ok, then the two diodes before
  the voltage regulator could be damaged; replace them. If you cannot
  find the 9VAC, make sure you have checked for the C-64's internal
  fuse and otherwise get another power supply.

CPU running?

  Try accessing the floppy drive by typing blindly (you do not
  need to close your eyes)

    LOAD"$",8   (assuming your device id is '8')

  If the floppy drive's motor starts spinning, you are lucky. The
  processor, the CIA's and the address manager seem to work.
  If the floppy does not react in any way, you probably have a
  serious problem and should continue reading...

Other chips alive?

  Test VIC, SID, PLA and, where applicable, the clock circuit 8701
  near the VIC (not in very old PCB revisions), whether they get
  extraordinaryly hot. If any of the chips gets hot, it is very
  likely that they are defective and, e.g. block the bus. These
  should be replaced. In C-64s with the new board, you will find
  a 64-pin multifunction chip, the MMU. If the other chips are ok,
  it is probably the MMU which is damaged. Alas, this MMU is prac-
  tically impossible to replace, there are neither suitable
  sockets nor spare chips.

  Of course, every chip connected to the bus in any way could be
  damaged and blocking the bus, therefore you should also check
  if the CIAs, the ROMs, the RAMs, the RIMs or the RUMs (oops) are
  getting extremly hot.  Especially in the old C-64 board, the
  various 74xx/74LSxx might be responsible for the failure.

If all else fails...

  ... you should consider the very low price of a used C-64 on
  the free market or, if you live in a strange country with prices
  over US$ 30 for a C-64 (including power supply), check for
  hair cracks and dry joints.



:::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::3::::::::::::::::::::
\L01:::::::::::::::::::::::::::::L:E:G:A:L::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


                A R T I C L E S    O F    O P E R A T I O N


Article 1 : Mission Statement
 
Our intent is to present useful information in order to enhance and preserve
the knowledge base of the Commodore 8-bit domain, including, but not limited
to, the Commodore 64 and Commodore 128 home computers.  To this end, we shall
require that every article contain what in our discretion should be a viable
Commodore 8-bit hardware and/or software point of relevance.  Likewise, each 
issue should include material that can both potentially enlighten the most
saavy of users as well as the layman.  We intend to complement and assist all
others engaged in similar endeavours.  We believe it is of paramount concern
to stave off entropy as long as possible.
 
 
Article 2 : disC=overy Staff
 
The current staff of disC=overy, the Journal of the Commodore Enthusiast,
is as follows:
 
      Editor-in-Chief       : Mike Gordillo  (s0621126@dominic.barry.edu)
      Associate/Tech Editor : Steven Judd    (sjudd@nwu.edu)
      Associate/Tech Editor : George Taylor  (aa601@chebucto.ns.ca)
      Webmaster             : Ernest Stokes  (drray@eskimo.com)

      disC=overy, issue 3 logo by 'WaD'
 
We invite any and all interested parties to join us as authors, panelists,
and staff members.
 
 
Article 3 : General Procedures

- Submission Outline -

a. Articles may range in size from 1 kilobyte and up.  Approximately 15
   kilobytes of text is the preferred length, including any software present.

b. Sufficient technical content about Commodore 8-bit home computers,
   concerning software and/or hardware relevant to these systems, is a 
   requirement.  What constitutes a sufficient amount of 'technical
   content' is left to the discretion of the Editor-in-Chief and/or the
   review panel (see below).


- Staff Priorities -

The Editor-in-Chief shall supervise the organization of each issue in regards
to grammatical and syntactical errors, flow of content, and overall layout of
presentation.  The Editor-in-Chief and Associate Editor shall form a review
panel whose function it shall be to referee literary work which the Editor
in-Chief has deemed to be of advanced technical merit.  The Editor-in-Chief
iand disC=overy, the Journal of the Commodore Enthusiast, shall retain
copyright solely on the unique and particular presentation of its included body
of literary work in its entirety.  Authors shall retain all copyrights and
responsibilities with regards to the content of their particular literary
work.  Authors shall be required to submit their works to the Editor-in-Chief
approximately two weeks prior to publication.


Article 4 : Peer Review
 
To the best of our knowledge, disC=overy shall be the first Commodore 8-bit
journal with a review panel dedicated to uphold the technical integrity and
legitimacy of its content.  The Editor-in-Chief and the Associate Editor
shall be responsible for the formation of the panel.  The appointed
panelists shall have the option of anonymity if desired.  The panel shall
review works primarily for technical merit if the Editor-in-Chief and
the Associate Editor deem it necessary.  Authors may be asked to modify
their works in accordance with the panel's recommendations.  The Editor-in-
Chief shall have final discretion regarding all such "refereed" articles.


Article 5 : Distribution

Although we welcome open distribution by non-commercial organizations, there
are currently four "official" distribution channels available to interested
parties.  This journal may be obtained by directly mailing the Editor-in-Chief
or via the World Wide Web at http://www.eskimo.com/~drray/discovery.html and
at FTP site : ftp.eskimo.com - directory /u/t/tpinfo/C64/Magazines/discovery
A "snail" mail disk copy of any issue of this journal may also be obtained
from Arkanix Labs at the following physical address :

   disC=overy, c/o Jon Mines
   Arkanix Labs
   17730 15th Ave NE  Suite #229
   Shoreline, WA  98155

This source should follow the distribution guidelines as listed in Article 6
below.  However, specific questions concerning billing, etc., should be
directed at them.

Several versions of this journal may be available for your convenience, please
check with the aforementioned sources.


Article 6 : Disclaimers
 
The Editor-in-Chief and disC=overy, the Journal of the Commodore Enthusiast,
retain all copyrights regarding the presentation of its articles.  Authors
retain all copyrights on their specific articles in and of themselves,
regarding the full legal responsibility concerning the originality of their
works and its contents.

The Editor-in-Chief and disC=overy, the Journal of the Commodore Enthusiast,
grants the reader an exclusive license to redistribute each issue in its
entirety without modification or omission under the following additional
stipulations:
 
      - If distribution involves physical media and is part of a commercial,
        not-for-profit, or PD distribution, the maximum allowable monetary
        charge shall not exceed $4 1996 United States Dollars per issue
        unless more than one issue is distributed on a single media item
        (i.e., two or more issues on one disk), in which case maximum
        allowable charge shall not exceed $4 1996 United States Dollars per
        media item.  All dollar values given assume shipping costs are
        -included- as part of the maximum allowable charge.
 
      - If distribution involves non-physical media and is part of a
        commercial, not-for-profit, or PD distribution, the maximum
        allowable charge shall be limited to the actual cost of the
        distribution, whether said cost be in the form of telephony or
        other electronic means.

      - Software included within articles (as text) may be subject to separate
        distribution requirements as binary executables.  Please check directly
        with authors regarding distribution of software in binary form.

      - disC=overy should be distributed on per-issue basis, and any potential
        subscription arrangements are strictly between distributor and reader.
        We do not guarantee subscriptions nor do we take responsibility for
        distribution outside of the disC=overy home page on the World Wide
        Web (http://www.eskimo.com/~drray/discovery.html)

It is understood that distribution denotes acceptance of the terms listed and
that under no condition shall any particular party claim copyright or public
domain status to disC=overy, the Journal of the Commodore Enthusiast, in its
entirety.
 
The Editor-in-Chief and disC=overy, the Journal of the Commodore Enthusiast,
reserve the right to modify any and all portions of the Preamble and the
Articles of Operation.

:::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::3::::::::::::::::::::
\END:::::::::::::::::::::::::::March 26, 1997:::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::



