Copy Link
Add to Bookmark
Report
disC=overy Issue 3
__ __ ______
| ||__| ______ / __ \ ______ __ __ ______ ____ __ __
____| | __ / __ \ | | |__|_/ __ \ | || | / __ \ / \ | || |
/ __ / || || |__|__|| | |____/ | | || || || |__| || ||__|| || |
| | | || |`\____ \ | | |____\ | | || || || _____|| | 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:::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::