ROM calls and other tricks
Posted: Mon Aug 01, 2005 5:24 pm
As Leif pointed out, there are more or less annotated ROM listings on the Internet and in various books. Each listing tends to use different labels, entry points and vary in detail information.
Here are a collection of (what I've found) particulary useful ROM calls to use. Rather than sorting them memory-wise, I tried to arrange them after what they can be used for. All addresses are of course for VIC-20, but most of them could even be used on the C64 by subtracting $2000 for addresses < $E000. In a few cases, the two ROMs are off by a few more bytes though.
First, a few zero page locations used further down:
INDEX1: $0022-$0023, First utility pointer
INDEX2: $0024-$0025, Second utility pointer
FORNAM: $0049-$004A, Pointer to FOR/NEXT index variable etc
TEMPF3: $004E-$0052, Temporary FLPT storage
TEMPF1: $0057-$005B, Temporary FLPT storage
TEMPF2: $005C-$0060: Temporary FLPT storage
FAC: $0061-$0066, Floating-point Accumulator (FAC)
AFAC: $0069-$006E, Alternative/Auxilary FAC
Basic does most of its calculations in floating-point (referred to as FLPT or MFLPT below), as you probably know. Therefore, a number of interesting routines working with floating-point numbers are stored in Basic ROM.
Notice that the ROM routines will use these, and possibly more zero page addreses, so if you use the zero page for own things, carefully disassemble or experiment which addresses are not touched. If you are not using floating point at all in your program, I would say that all these addresses are available for your own usage.
The syntax (A/Y) means the accumulator should contain the low byte, and the Y register contains the high byte.
Routines for execution of a Basic program
$C533: Rebuild Basic text link pointers, as discussed in the VIC utility cartridge thread. This would rarely be used unless you are moving a Basic program within memory (or make an OLD command or similar).
$C659: Reset execution to start of program and perform CLR (set pointers to variables).
$C7AE: Handle next Basic statement from current position. Not really the entry point for RUN ($C871), but it works..
Routines for SYS parameter passing
$CEFD: Read comma from BASIC text. If another symbol is found, ?SYNTAX ERROR. There are sister routines: $CEF7 checks right parenthesis, $CEFA checks left parenthesis, $CEFF compares vs accumulator (i.e. any character).
$D08B: Find variable from BASIC text. Returns address to variable in A/Y.
$D79B: Evaluate expression from BASIC text. Returns integer 0-255 in X register. Call $D79E if text pointer has already advanced.
$DBD0: Store FAC in variable pointed to by FORNAM ($49/4A).
Routines for the jiffy clock
$C9E3: Set TI$ from string. Store the address to the string into INDEX1 (low/hi) and load the accumulator with 6 (the string length). Not so useful maybe, but nice to know. If the string contains non-numeric values, you get ?ILLEGAL QUANTITY just like you would get from Basic. Notice that there are Kernel calls both to read and set the clock numerically.
$CF48: Convert TI to ASCII string and set (FAC+3) to point to string. This is useful in combination with $CB21 below to PRINT TI$.
Routines for printing
$CB1E: Print string pointed to by (A/Y) until zero byte.
$CB21: Print string pointed to by (FAC+3) until zero byte.
$CB24: Print string pointed to by (INDEX1) of length (A).
$DDCD: Print integer in (X/A). This is actually part of the routine that prints the current line number, stored in $0039-$003A.
$DDD7: Print (FAC) as ASCII string.
Routines for converting from/to FLPT/FAC
$D391: Convert integer in (A/Y) to FLPT in (FAC) within range 0 to 32767.
$D3A2: Convert (Y) to FLPT in (FAC) within range 0 to 255. These two routines are common entry points if we want to take advantage of the FAC.
$D7B5: Convert string starting at (INDEX1) of length (A) to FLPT value in (FAC). Could be useful if you have floating point constants in text format?
$D1AA: Convert (FAC) to integer in (A/Y).
$D7F7: Convert FAC to integer in (INDEX1) in range 0 to 65535.
$DDDD: Convert FAC to ASCII string starting at STACK ($0100) and ending with null byte. It corrupts $00FF. It can be useful if you want the string in memory but not print it on screen.
Routines for loading and storing FAC
$DA8C: Load AFAC with MFLPT pointed to by (A/Y).
$DBA2: Load FAC with MFLPT pointed to by (A/Y).
$DBC7: Store (FAC) into TEMPF2.
$DBCA: Store (FAC) into TEMPF1.
$DBD4: Store (FAC) into location pointed to by (X/Y).
$DBFC: Load (FAC) from (AFAC).
$DC0C: Load (AFAC) from (FAC).
Arithmetic FAC routines
$D850: Subtraction: (FAC) = MFLPT value at (A/Y) - (FAC). It calls $DA8C, so entering at $D853 would mean (FAC) = (AFAC) - (FAC).
$D867: Addition: (FAC) = MFLPT value at (A/Y) + (FAC). Ditto call as for subtraction.
$DA28: Multiply (FAC) by MFLPT value at (A/Y), answer in (FAC). It also calls $DA8C, checks whether the multiplier is zero and then continues at $DA30.
$DAE2: Multiply (FAC) by 10.
$DAFE: Divide (FAC) by 10.
$DB07: Divide (AFAC) by MFLPT value at (A/Y), sign in (X), answer to (FAC).
$DC2B: Find sign of (FAC), result in A: $01 = positive, $00 = zero, $FF = negative.
$DC58: Perform ABS function. It actually only shifts down the FAC sign ($0066) by one bit, so it becomes a positive value..
$DC5B: Compare (FAC) with MFLPT value at (A/Y): $01 = (FAC) > MFLPT, $00 = equal, $FF = (FAC) < MFLPT.
$DCCC: Perform INT function, convert (FAC) to integer and back to FLPT format again.
Various Basic calls
$E094: Perform RND. To get RND(1) functionality, enter at $E0BB. It will leave its value in (FAC).
Kernal calls - these may differ between VIC and 64
$E518: Initalize input/output. Pretty much what happens to the screen when you press RUN/STOP + RESTORE.
$E581: Home cursor, reset screen line link table.
Oh dear, this is a long list, and it is nowhere a complete disassembly. Most of it deals with how to juggle and use the FAC. While you probably can do integer arithmetic anyway, complex multiplication or use of other Basic routines (SQR [$DF71], EXP [$DFED], SIN [$E268], ATN [$E30B] etc) will be more handy - although maybe not execution time efficient - than reinventing the wheel.
I didn't even list the Kernel vector table ($FF8A to $FFF3), as it should be fairly documented elsewhere.
Here are a collection of (what I've found) particulary useful ROM calls to use. Rather than sorting them memory-wise, I tried to arrange them after what they can be used for. All addresses are of course for VIC-20, but most of them could even be used on the C64 by subtracting $2000 for addresses < $E000. In a few cases, the two ROMs are off by a few more bytes though.
First, a few zero page locations used further down:
INDEX1: $0022-$0023, First utility pointer
INDEX2: $0024-$0025, Second utility pointer
FORNAM: $0049-$004A, Pointer to FOR/NEXT index variable etc
TEMPF3: $004E-$0052, Temporary FLPT storage
TEMPF1: $0057-$005B, Temporary FLPT storage
TEMPF2: $005C-$0060: Temporary FLPT storage
FAC: $0061-$0066, Floating-point Accumulator (FAC)
AFAC: $0069-$006E, Alternative/Auxilary FAC
Basic does most of its calculations in floating-point (referred to as FLPT or MFLPT below), as you probably know. Therefore, a number of interesting routines working with floating-point numbers are stored in Basic ROM.
Notice that the ROM routines will use these, and possibly more zero page addreses, so if you use the zero page for own things, carefully disassemble or experiment which addresses are not touched. If you are not using floating point at all in your program, I would say that all these addresses are available for your own usage.
The syntax (A/Y) means the accumulator should contain the low byte, and the Y register contains the high byte.
Routines for execution of a Basic program
$C533: Rebuild Basic text link pointers, as discussed in the VIC utility cartridge thread. This would rarely be used unless you are moving a Basic program within memory (or make an OLD command or similar).
$C659: Reset execution to start of program and perform CLR (set pointers to variables).
$C7AE: Handle next Basic statement from current position. Not really the entry point for RUN ($C871), but it works..
Routines for SYS parameter passing
$CEFD: Read comma from BASIC text. If another symbol is found, ?SYNTAX ERROR. There are sister routines: $CEF7 checks right parenthesis, $CEFA checks left parenthesis, $CEFF compares vs accumulator (i.e. any character).
$D08B: Find variable from BASIC text. Returns address to variable in A/Y.
$D79B: Evaluate expression from BASIC text. Returns integer 0-255 in X register. Call $D79E if text pointer has already advanced.
$DBD0: Store FAC in variable pointed to by FORNAM ($49/4A).
Routines for the jiffy clock
$C9E3: Set TI$ from string. Store the address to the string into INDEX1 (low/hi) and load the accumulator with 6 (the string length). Not so useful maybe, but nice to know. If the string contains non-numeric values, you get ?ILLEGAL QUANTITY just like you would get from Basic. Notice that there are Kernel calls both to read and set the clock numerically.
$CF48: Convert TI to ASCII string and set (FAC+3) to point to string. This is useful in combination with $CB21 below to PRINT TI$.
Routines for printing
$CB1E: Print string pointed to by (A/Y) until zero byte.
$CB21: Print string pointed to by (FAC+3) until zero byte.
$CB24: Print string pointed to by (INDEX1) of length (A).
$DDCD: Print integer in (X/A). This is actually part of the routine that prints the current line number, stored in $0039-$003A.
$DDD7: Print (FAC) as ASCII string.
Routines for converting from/to FLPT/FAC
$D391: Convert integer in (A/Y) to FLPT in (FAC) within range 0 to 32767.
$D3A2: Convert (Y) to FLPT in (FAC) within range 0 to 255. These two routines are common entry points if we want to take advantage of the FAC.
$D7B5: Convert string starting at (INDEX1) of length (A) to FLPT value in (FAC). Could be useful if you have floating point constants in text format?
$D1AA: Convert (FAC) to integer in (A/Y).
$D7F7: Convert FAC to integer in (INDEX1) in range 0 to 65535.
$DDDD: Convert FAC to ASCII string starting at STACK ($0100) and ending with null byte. It corrupts $00FF. It can be useful if you want the string in memory but not print it on screen.
Routines for loading and storing FAC
$DA8C: Load AFAC with MFLPT pointed to by (A/Y).
$DBA2: Load FAC with MFLPT pointed to by (A/Y).
$DBC7: Store (FAC) into TEMPF2.
$DBCA: Store (FAC) into TEMPF1.
$DBD4: Store (FAC) into location pointed to by (X/Y).
$DBFC: Load (FAC) from (AFAC).
$DC0C: Load (AFAC) from (FAC).
Arithmetic FAC routines
$D850: Subtraction: (FAC) = MFLPT value at (A/Y) - (FAC). It calls $DA8C, so entering at $D853 would mean (FAC) = (AFAC) - (FAC).
$D867: Addition: (FAC) = MFLPT value at (A/Y) + (FAC). Ditto call as for subtraction.
$DA28: Multiply (FAC) by MFLPT value at (A/Y), answer in (FAC). It also calls $DA8C, checks whether the multiplier is zero and then continues at $DA30.
$DAE2: Multiply (FAC) by 10.
$DAFE: Divide (FAC) by 10.
$DB07: Divide (AFAC) by MFLPT value at (A/Y), sign in (X), answer to (FAC).
$DC2B: Find sign of (FAC), result in A: $01 = positive, $00 = zero, $FF = negative.
$DC58: Perform ABS function. It actually only shifts down the FAC sign ($0066) by one bit, so it becomes a positive value..
$DC5B: Compare (FAC) with MFLPT value at (A/Y): $01 = (FAC) > MFLPT, $00 = equal, $FF = (FAC) < MFLPT.
$DCCC: Perform INT function, convert (FAC) to integer and back to FLPT format again.
Various Basic calls
$E094: Perform RND. To get RND(1) functionality, enter at $E0BB. It will leave its value in (FAC).
Kernal calls - these may differ between VIC and 64
$E518: Initalize input/output. Pretty much what happens to the screen when you press RUN/STOP + RESTORE.
$E581: Home cursor, reset screen line link table.
Oh dear, this is a long list, and it is nowhere a complete disassembly. Most of it deals with how to juggle and use the FAC. While you probably can do integer arithmetic anyway, complex multiplication or use of other Basic routines (SQR [$DF71], EXP [$DFED], SIN [$E268], ATN [$E30B] etc) will be more handy - although maybe not execution time efficient - than reinventing the wheel.
I didn't even list the Kernel vector table ($FF8A to $FFF3), as it should be fairly documented elsewhere.