Skip to content

Instantly share code, notes, and snippets.

@cbmeeks
Created July 7, 2017 13:04
Show Gist options
  • Save cbmeeks/65c0f2acc1f0ad041c236637732aac8c to your computer and use it in GitHub Desktop.
Save cbmeeks/65c0f2acc1f0ad041c236637732aac8c to your computer and use it in GitHub Desktop.
VIC-20 ROM Disassembly
;***********************************************************************************;
;***********************************************************************************;
;
; The almost completely commented VIC 20 ROM disassembly. V1.01 Lee Davison 2005-2012.
; With enhancements by Simon Rowe <srowe@mose.org.uk>.
; This is a bit correct assembly listing for the VIC 20 BASIC and KERNAL ROMs as one 16K
; ROM. You should be able to assemble the VIC ROMs from this with most 6502 assemblers,
; as no macros or 'special' features were used. This has been tested using Michal
; Kowalski's 6502 Simulator assemble function. See http://exifpro.com/utils.html for
; this program.
; Many references were used to complete this disassembly including, but not limited to,
; "Mapping the VIC 20", "Mapping the C64", "VIC 20 Programmers Reference", "VIC 20 User
; Guide", "The Complete Commodore Inner Space Anthology", "VIC Revealed" and various
; text files, pictures and other documents.
;***********************************************************************************;
;***********************************************************************************;
;
; BASIC zero page
; These locations contain the JMP instruction target address of the USR command. They
; are initialised so that if you try to execute a USR call without changing them you
; will receive an ILLEGAL QUANTITY error message.
USRPPOK = $00 ; USR() JMP instruction
ADDPRC = $01 ; USR() vector
; This vector points to the address of the BASIC routine which converts a floating point
; number to an integer, however BASIC does not use this vector. It may be of assistance
; to the programmer who wishes to use data that is stored in floating point format. The
; parameter passed by the USR command is available only in that format for example.
ADRAY1 = $03 ; float to fixed vector
; This vector points to the address of the BASIC routine which converts an integer to a
; floating point number, however BASIC does not use this vector. It may be used by the
; programmer who needs to make such a conversion for a machine language program that
; interacts with BASIC. To return an integer value with the USR command for example.
ADRAY2 = $05 ; fixed to float vector
; The cursor column position prior to the TAB or SPC is moved here from PNTR, and is used
; to calculate where the cursor ends up after one of these functions is invoked.
; Note that the value contained here shows the position of the cursor on a logical line.
; Since one logical line can be up to four physical lines long, the value stored here
; can range from 0 to 87.
CHARAC = $07 ; search character
ENDCHR = $08 ; scan quotes flag
TRMPOS = $09 ; TAB column save
; The routine that converts the text in the input buffer into lines of executable program
; tokens, and the routines that link these program lines together, use this location as an
; index into the input buffer area. After the job of converting text to tokens is done,
; the value in this location is equal to the length of the tokenised line.
; The routines which build an array or locate an element in an array use this location to
; calculate the number of DIMensions called for and the amount of storage required for a
; newly created array, or the number of subscripts when referencing an array element.
VERCHK = $0A ; load/verify flag, 0 = load, 1 = verify
COUNT = $0B ; temporary byte, line crunch/array access/logic operators
; This is used as a flag by the routines that build an array or reference an existing
; array. It is used to determine whether a variable is in an array, whether the array
; has already been DIMensioned, and whether a new array should assume the default size.
DIMFLG = $0C ; DIM flag
; This flag is used to indicate whether data being operated upon is string or numeric. A
; value of $FF in this location indicates string data while a $00 indicates numeric data.
VALTYP = $0D ; data type flag, $FF = string, $00 = numeric
; If the above flag indicates numeric then a $80 in this location identifies the number
; as an integer, and a $00 indicates a floating point number.
INTFLG = $0E ; data type flag, $80 = integer, $00 = floating point
; The garbage collection routine uses this location as a flag to indicate that garbage
; collection has already been tried before adding a new string. If there is still not
; enough memory, an OUT OF MEMORY error message will result.
; LIST uses this byte as a flag to let it know when it has come to a character string in
; quotes. It will then print the string, rather than search it for BASIC keyword tokens.
; This location is also used during the process of converting a line of text in the BASIC
; input buffer into a linked program line of BASIC keyword tokens to flag a DATA line is
; being processed.
GARBFL = $0F ; garbage collected/open quote/DATA flag
; If an opening parenthesis is found, this flag is set to indicate that the variable in
; question is either an array variable or a user-defined function.
SUBFLG = $10 ; subscript/FNx flag
; This location is used to determine whether the sign of the value returned by the
; functions SIN, COS, ATN or TAN is positive or negative.
; Also the comparison routines use this location to indicate the outcome of the compare.
; For A <=> B the value here will be $01 if A > B, $02 if A = B, and $04 if A < B. If
; more than one comparison operator was used to compare the two variables then the value
; here will be a combination of the above values.
INPFLG = $11 ; input mode flag, $00 = INPUT, $40 = GET, $98 = READ
TANSGN = $12 ; ATN sign/comparison evaluation flag
; When the default input or output device is used the value here will be a zero, and the
; format of prompting and output will be the standard screen output format. The location
; $B8 is used to decide what device actually to put input from or output to.
CHANNL = $13 ; current I/O channel
; Used whenever a 16 bit integer is used e.g. the target line number for GOTO, LIST, ON,
; and GOSUB also the number of a BASIC line that is to be added or replaced. Additionally
; PEEK, POKE, WAIT, and SYS use this location as a pointer to the address which is the
; subject of the command.
LINNUM = $14 ; temporary integer low byte
; $15 ; temporary integer high byte
; This location points to the next available slot in the temporary string descriptor
; stack located at TEMPST.
TEMPPT = $16 ; descriptor stack pointer, next free
; This contains information about temporary strings which have not yet been assigned to
; a string variable.
LASTPT = $17 ; current descriptor stack item pointer low byte
; $18 ; current descriptor stack item pointer high byte
TEMPST = $19 ; to $21, descriptor stack
; These locations are used by BASIC multiplication and division routines. They are also
; used by the routines which compute the size of the area required to store an array
; which is being created.
INDEX = $22 ; misc temp byte
; $23 ; misc temp byte
; $24 ; misc temp byte
; $25 ; misc temp byte
RESHO = $26 ; temp mantissa 1
; $27 ; temp mantissa 2
; $28 ; temp mantissa 3
; $29 ; temp mantissa 4
; Word pointer to where the BASIC program text is stored.
TXTTAB = $2B ; start of memory low byte
; $2C ; start of memory high byte
; Word pointer to the start of the BASIC variable storage area.
VARTAB = $2D ; start of variables low byte
; $2E ; start of variables high byte
; Word pointer to the start of the BASIC array storage area.
ARYTAB = $2F ; end of variables low byte
; $30 ; end of variables high byte
; Word pointer to end of the start of free RAM.
STREND = $31 ; end of arrays low byte
; $32 ; end of arrays high byte
; Word pointer to the bottom of the string text storage area.
FRETOP = $33 ; bottom of string space low byte
; $34 ; bottom of string space high byte
; Used as a temporary pointer to the most current string added by the routines which
; build strings or move them in memory.
FRESPC = $35 ; string utility ptr low byte
; $36 ; string utility ptr high byte
; Word pointer to the highest address used by BASIC +1.
MEMSIZ = $37 ; end of memory low byte
; $38 ; end of memory high byte
; These locations contain the line number of the BASIC statement which is currently being
; executed. A value of $FF in location $3A means that BASIC is in immediate mode.
CURLIN = $39 ; current line number low byte
; $3A ; current line number high byte
; When program execution ends or stops the last line number executed is stored here.
OLDLIN = $3B ; break line number low byte
; $3C ; break line number high byte
; These locations contain the address of the start of the text of the BASIC statement
; that is being executed. The value of the pointer to the address of the BASIC text
; character currently being scanned is stored here each time a new BASIC statement begins
; execution.
OLDTXT = $3D ; continue pointer low byte
; $3E ; continue pointer high byte
; These locations hold the line number of the current DATA statement being READ. If an
; error concerning the DATA occurs this number will be moved to CURLIN so that the error
; message will show the line that contains the DATA statement rather than in the line that
; contains the READ statement.
DATLIN = $3F ; current DATA line number low byte
; $40 ; current DATA line number high byte
; These locations point to the address where the next DATA will be READ from. RESTORE
; sets this pointer back to the address indicated by the start of BASIC pointer.
DATPTR = $41 ; DATA pointer low byte
; $42 ; DATA pointer high byte
; READ, INPUT and GET all use this as a pointer to the address of the source of incoming
; data, such as DATA statements, or the text input buffer.
INPPTR = $43 ; READ pointer low byte
; $44 ; READ pointer high byte
VARNAM = $45 ; current variable name first byte
; $46 ; current variable name second byte
; These locations point to the value of the current BASIC variable. Specifically they
; point to the byte just after the two-character variable name.
VARPNT = $47 ; current variable address low byte
; $48 ; current variable address high byte
; The address of the BASIC variable which is the subject of a FOR/NEXT loop is first
; stored here before being pushed onto the stack.
FORPNT = $49 ; FOR/NEXT variable pointer low byte
; $4A ; FOR/NEXT variable pointer high byte
; The expression evaluation routine creates this to let it know whether the current
; comparison operation is a < $01, = $02 or > $04 comparison or combination.
OPPTR = $4B ; BASIC execute pointer temporary low byte/precedence flag
; $4C ; BASIC execute pointer temporary high byte
OPMASK = $4D ; comparison evaluation flag
; These locations are used as a pointer to the function that is created during function
; definition. During function execution it points to where the evaluation results should
; be saved.
DEFPNT = $4E ; FAC temp store/function/variable/garbage pointer low byte
; $4F ; FAC temp store/function/variable/garbage pointer high byte
; Temporary pointer to the current string descriptor.
DSCPTN = $50 ; FAC temp store/descriptor pointer low byte
; $51 ; FAC temp store/descriptor pointer high byte
FOUR6 = $53 ; garbage collection step size
; The first byte is the 6502 JMP instruction $4C, followed by the address of the required
; function taken from the table at FUNDSP.
JMPER = $54 ; JMP opcode for functions
; $55 ; functions jump vector
TEMPF3 = $57 ; FAC temp store
GENPTR = $58 ; FAC temp store
; $59 ; FAC temp store
GEN2PTR = $5A ; FAC temp store
; $5B ; block end high byte
LAB_5C = $5C ; FAC temp store
LAB_5D = $5D ; FAC temp store
EXPCNT = $5E ; exponent count byte
; $5F ; FAC temp store
TMPPTR = $5F
; $60 ; block start high byte
FAC1 = $61 ; FAC1 exponent
; $62 ; FAC1 mantissa 1
; $63 ; FAC1 mantissa 2
; $64 ; FAC1 mantissa 3
; $65 ; FAC1 mantissa 4
; $66 ; FAC1 sign
SGNFLG = $67 ; constant count/-ve flag
BITS = $68 ; FAC1 overflow
FAC2 = $69 ; FAC2 exponent
; $6A ; FAC2 mantissa 1
; $6B ; FAC2 mantissa 2
; $6C ; FAC2 mantissa 3
; $6D ; FAC2 mantissa 4
; $6E ; FAC2 sign
ARISGN = $6F ; FAC sign comparison
FACOV = $70 ; FAC1 rounding
FBUFPT = $71 ; temp BASIC execute/array pointer low byte/index
; $72 ; temp BASIC execute/array pointer high byte
CHRGET = $73 ; increment and scan memory, BASIC byte get
CHRGOT = $79 ; scan memory, BASIC byte get
; $7A ; BASIC execute pointer low byte
; $7B ; BASIC execute pointer high byte
CHRSPC = $80 ; numeric test entry
RNDX = $8B ; RND() seed, five bytes
; KERNAL zero page
STATUS = $90 ; I/O status byte
; function
; bit cassette serial bus
; --- -------- ----------
; 7 end of tape device not present
; 6 end of file EOI
; 5 checksum error
; 4 read error
; 3 long block
; 2 short block
; 1 time out read
; 0 time out write
STKEY = $91 ; keyboard row, bx = 0 = key down
; bit key
; --- ------
; 7 [DOWN]
; 6 /
; 5 ,
; 4 N
; 3 V
; 2 X
; 1 [L SHIFT]
; 0 [STOP]
SVXT = $92 ; timing constant for tape read
VERCK = $93 ; load/verify flag, load = $00, verify = $01
C3PO = $94 ; serial output: deferred character flag
; $00 = no character waiting, $xx = character waiting
BSOUR = $95 ; serial output: deferred character
; $FF = no character waiting, $xx = waiting character
SYNO = $96 ; tape: leader length
; $00 = no block, $10-$7E = leader bits read
XSAV = $97 ; register save
; The number of currently open I/O files is stored here. The maximum number that can be
; open at one time is ten. The number stored here is used as the index to the end of the
; tables that hold the file numbers, device numbers, and secondary addresses (LAT, FAT,
; SAT).
LDTND = $98 ; open file count
; The default value of this location is 0.
DFLTN = $99 ; input device number
; The default value of this location is 3.
DFLTO = $9A ; output device number
;
; number device
; ------ ------
; 0 keyboard
; 1 cassette
; 2 RS-232
; 3 screen
; 4-31 serial bus
PRTY = $9B ; tape character parity
DPSW = $9C ; tape dipole switch/byte received flag
MSGFLG = $9D ; KERNAL message mode flag,
; $C0 = both control and error messages,
; $80 = control messages only,
; $40 = error messages only,
; $00 = neither control nor error messages
PTR1 = $9E ; tape pass 1 error log/character buffer
PTR2 = $9F ; tape pass 2 error log corrected
; These three locations form a counter which is updated 60 times a second, and serves as
; a software clock which counts the number of jiffies that have elapsed since the computer
; was turned on. After 24 hours and one jiffy these locations are set back to $000000.
TIME = $A0 ; jiffy clock high byte
; $A1 ; jiffy clock mid byte
; $A2 ; jiffy clock low byte
PCNTR = $A3 ; serial input bit count/tape bit count
; b0 of this location reflects the current phase of the tape output cycle.
FIRT = $A4 ; input byte/tape bit cycle phase
CNTDN = $A5 ; tape synchronisation byte count/serial bus bit count
BUFPNT = $A6 ; tape buffer index
INBIT = $A7 ; tape write leader count/block count/RS-232 input bit
BITCI = $A8 ; tape error flags/tape long word marker/RS-232 input bit count
RINONE = $A9 ; tape dipole count/tape medium word marker/RS-232 start bit flag,
; $90 = no start bit received,
; $00 = start bit received
RIDATA = $AA ; tape input status/tape sync status/RS-232 byte assembly
RIPRTY = $AB ; tape leader counter/tape read checksum/RS-232 parity bit
SAL = $AC ; tape buffer start pointer low byte
; next/previous line character pointer low byte
; $AD ; tape buffer start pointer high byte
; next/previous line character pointer high byte
EAL = $AE ; tape buffer end pointer low byte
; next/previous line colour pointer low byte
; $AF ; tape buffer end pointer high byte
; next/previous line colour pointer high byte
CMP0 = $B0 ; tape timing constant min byte
; $B1 ; tape timing constant max byte
; These two locations point to the address of the cassette buffer. This pointer must
; be greater than or equal to $0200 or an ILLEGAL DEVICE NUMBER error will be sent
; when tape I/O is tried. This pointer must also be less that $8000 or the routine
; will terminate early.
TAPE1 = $B2 ; tape buffer start pointer low byte
; $B3 ; tape buffer start pointer high byte
; RS-232 routines use this to count the number of bits transmitted and for parity and
; stop bit manipulation. Tape load routines use this location to flag when they are
; ready to receive data bytes.
BITTS = $B4 ; transmitter bit count out
; This location is used by the RS-232 routines to hold the next bit to be sent and by the
; tape routines to indicate what part of a block the read routine is currently reading.
NXTBIT = $B5 ; transmitter next bit to be sent
; RS-232 routines use this area to disassemble each byte to be sent from the transmission
; buffer pointed to by ROBUF. Tape load routines use this location to record read errors.
RODATA = $B6 ; transmitter byte buffer/disassembly location
; Disk filenames may be up to 16 characters in length while tape filenames may be up to
; 187 characters in length.
; If a tape name is longer than 16 characters the excess will be truncated by the
; SEARCHING and FOUND messages, but will still be present on the tape.
; A disk file is always referred to by a name. This location will always be greater than
; zero if the current file is a disk file.
; An RS-232 OPEN command may specify a filename of up to four characters. These characters
; are copied to M51CTR to M51CTR+3 and determine baud rate, word length, and parity, or
; they would do if the feature was fully implemented.
FNLEN = $B7 ; file name length
LA = $B8 ; logical file
SA = $B9 ; secondary address
FA = $BA ; current device number
; number device
; ------ ------
; 0 keyboard
; 1 cassette
; 2 RS-232
; 3 screen
; 4-31 serial bus
FNADR = $BB ; file name pointer low byte
; $BC ; file name pointer high byte
ROPRTY = $BD ; tape write byte/RS-232 parity byte
; Used by the tape routines to count the number of copies of a data block remaining to
; be read or written.
FSBLK = $BE ; tape copies remaining
MYCH = $BF ; tape read byte
CAS1 = $C0 ; tape motor interlock
STAL = $C1 ; I/O start address low byte
; $C2 ; I/O start address high byte
MEMUSS = $C3 ; load start address low byte
; $C4 ; load start address high byte
LSTX = $C5 ; last key pressed
;
; # key # key # key # key
; -- --- -- --- -- --- -- ---
; 00 1 10 none 20 [SPACE] 30 Q
; 01 3 11 A 21 Z 31 E
; 02 5 12 D 22 C 32 T
; 03 7 13 G 23 B 33 U
; 04 9 14 J 24 M 34 O
; 05 + 15 L 25 . 35 @
; 06 £ 16 ; 26 none 36 ^
; 07 [DEL] 17 [CSR R] 27 [F1] 37 [F5]
; 08 [<-] 18 [STOP] 28 none 38 2
; 09 W 19 none 29 S 39 4
; 0A R 1A X 2A F 3A 6
; 0B Y 1B V 2B H 3B 8
; 0C I 1C N 2C K 3C 0
; 0D P 1D , 2D : 3D -
; 0E * 1E / 2E = 3E [HOME]
; 0F [RET] 1F [CSR D] 2F [F3] 3F [F7]
NDX = $C6 ; keyboard buffer length/index
; When the [CTRL][RVS-ON] characters are printed this flag is set to $12, and the print
; routines will add $80 to the screen code of each character which is printed, so that
; the character will appear on the screen with its colours reversed.
; Note that the contents of this location are cleared not only upon entry of a
; [CTRL][RVS-OFF] character but also at every carriage return.
RVS = $C7 ; reverse flag, $12 = reverse, $00 = normal
; This pointer indicates the column number of the last non-blank character on the logical
; line that is to be input. Since a logical line can be up to 88 characters long this
; number can range from 0-87.
INDX = $C8 ; input EOL pointer
; These locations keep track of the logical line that the cursor is on and its column
; position on that logical line.
; Each logical line may contain up to four 22 column physical lines. So there may be as
; many as 23 logical lines, or as few as 6 at any one time. Therefore, the logical line
; number might be anywhere from 1-23. Depending on the length of the logical line, the
; cursor column may be from 1-22, 1-44, 1-66 or 1-88.
; For more on logical lines, see the description of the screen line link table, LDTB1.
LXSP = $C9 ; input cursor row
; $CA ; input cursor column
; The keyscan interrupt routine uses this location to indicate which key is currently
; being pressed. The value here is then used as an index into the appropriate keyboard
; table to determine which character to print when a key is struck.
; The correspondence between the key pressed and the number stored here is as follows:
; $00 1 $10 not used $20 [SPACE] $30 Q $40 [NO KEY]
; $01 3 $11 A $21 Z $31 E $xx invalid
; $02 5 $12 D $22 C $32 T
; $03 7 $13 G $23 B $33 U
; $04 9 $14 J $24 M $34 O
; $05 + $15 L $25 . $35 @
; $06 £ $16 ; $26 not used $36 ^
; $07 [DEL] $17 [CRSR R] $27 [F1] $37 [F5]
; $08 [<-] $18 [STOP] $28 not used $38 2
; $09 W $19 not used $29 S $39 4
; $0A R $1A X $2A F $3A 6
; $0B Y $1B V $2B H $3B 8
; $0C I $1C N $2C K $3C 0
; $0D P $1D , $2D : $3D -
; $0E * $1E / $2E = $3E [HOME]
; $0F [RET] $1F [CRSR D] $2F [F3] $3F [F7]
SFDX = $CB ; which key
; When this flag is set to a non-zero value, it indicates to the routine that normally
; flashes the cursor not to do so. The cursor blink is turned off when there are
; characters in the keyboard buffer, or when the program is running.
BLNSW = $CC ; cursor enable, $00 = flash cursor
; The routine that blinks the cursor uses this location to tell when it's time for a
; blink. The number 20 is put here and decremented every jiffy until it reaches zero.
; Then the cursor state is changed, the number 20 is put back here, and the cycle starts
; all over again.
BLNCT = $CD ; cursor timing countdown
; The cursor is formed by printing the inverse of the character that occupies the cursor
; position. If that characters is the letter A, for example, the flashing cursor merely
; alternates between printing an A and a reverse-A. This location keeps track of the
; normal screen code of the character that is located at the cursor position, so that it
; may be restored when the cursor moves on.
CDBLN = $CE ; character under cursor
; This location keeps track of whether, during the current cursor blink, the character
; under the cursor was reversed, or was restored to normal. This location will contain
; $00 if the character is reversed, and $01 if the character is not reversed.
BLNON = $CF ; cursor blink phase
CRSW = $D0 ; input from keyboard or screen, $xx = input is available
; from the screen, $00 = input should be obtained from the
; keyboard
; These locations point to the address in screen RAM of the first column of the logical
; line upon which the cursor is currently positioned.
PNT = $D1 ; current screen line pointer low byte
; $D2 ; current screen line pointer high byte
; This holds the cursor column position within the logical line pointed to by PNT.
; Since a logical line can comprise up to four physical lines, this value may be from
; 0 to 87.
PNTR = $D3 ; cursor column
; A non-zero value in this location indicates that the editor is in quote mode. Quote
; mode is toggled every time that you type in a quotation mark on a given line, the
; first quote mark turns it on, the second turns it off, the third turns it on, etc.
; If the editor is in this mode when a cursor control character or other non-printing
; character is entered, a printed equivalent will appear on the screen instead of the
; cursor movement or other control operation taking place. Instead, that action is
; deferred until the string is sent to the string by a PRINT statement, at which time
; the cursor movement or other control operation will take place.
; The exception to this rule is the DELETE key, which will function normally within
; quote mode. The only way to print a character which is equivalent to the DELETE key
; is by entering insert mode. Quote mode may be exited by printing a closing quote or
; by hitting the RETURN or SHIFT-RETURN keys.
QTSW = $D4 ; cursor quote flag
; The line editor uses this location when the end of a line has been reached to determine
; whether another physical line can be added to the current logical line or if a new
; logical line must be started.
LNMX = $D5 ; current screen line length
; This location contains the current physical screen line position of the cursor, 0 to 22.
TBLX = $D6 ; cursor row
; The ASCII value of the last character printed to the screen is held here temporarily.
ASCII = $D7 ; checksum byte/temporary last character
; When the INSERT key is pressed, the screen editor shifts the line to the right, allocates
; another physical line to the logical line if necessary (and possible), updates the
; screen line length in LNMX, and adjusts the screen line link table at LDTB1. This location
; is used to keep track of the number of spaces that has been opened up in this way.
; Until the spaces that have been opened up are filled, the editor acts as if in quote
; mode. See location QTSW, the quote mode flag. This means that cursor control characters
; that are normally non-printing will leave a printed equivalent on the screen when
; entered, instead of having their normal effect on cursor movement, etc. The only
; difference between insert and quote mode is that the DELETE key will leave a printed
; equivalent in insert mode, while the INSERT key will insert spaces as normal.
INSRT = $D8 ; insert count
; This table contains 23 entries, one for each row of the screen display. Each entry has
; two functions. Bits 0-3 indicate on which of the four pages of screen memory the first
; byte of memory for that row is located. This is used in calculating the pointer to the
; starting address of a screen line at PNT.
;
; The high byte is calculated by adding the value of the starting page of screen memory
; held in HIBASE to the displacement page held here.
;
; The other function of this table is to establish the makeup of logical lines on the
; screen. While each screen line is only 22 characters long, BASIC allows the entry of
; program lines that contain up to 88 characters. Therefore, some method must be used
; to determine which physical lines are linked into a longer logical line, so that this
; longer logical line may be edited as a unit.
;
; The high bit of each byte here is used as a flag by the screen editor. That bit is set
; when a line is the first or only physical line in a logical line. The high bit is reset
; to 0 only when a line is an extension to this logical line.
LDTB1 = $D9 ; to LDTB1 + $18 inclusive, screen line link table
LLNKSV = $F2 ; screen row marker
; This pointer is synchronised with the pointer to the address of the first byte of
; screen RAM for the current line kept in PNT. It holds the address of the first byte
; of colour RAM for the corresponding screen line.
USER = $F3 ; colour RAM pointer low byte
; $F4 ; colour RAM pointer high byte
; This pointer points to the address of the keyboard matrix lookup table currently being
; used. Although there are only 64 keys on the keyboard matrix, each key can be used to
; print up to four different characters, depending on whether it is struck by itself or
; in combination with the SHIFT, CTRL, or C= keys.
; These tables hold the ASCII value of each of the 64 keys for one of these possible
; combinations of keypresses. When it comes time to print the character, the table that
; is used determines which character is printed.
; The addresses of the tables are:
; NORMKEYS ; unshifted
; SHFTKEYS ; shifted
; LOGOKEYS ; commodore
; CTRLKEYS ; control
KEYTAB = $F5 ; keyboard pointer low byte
; $F6 ; keyboard pointer high byte
; When device the RS-232 channel is opened two buffers of 256 bytes each are created at
; the top of memory. These locations point to the address of the one which is used to
; store characters as they are received.
RIBUF = $F7 ; RS-232 Rx pointer low byte
; $F8 ; RS-232 Rx pointer high byte
; These locations point to the address of the 256 byte output buffer that is used for
; transmitting data to RS-232 devices.
ROBUF = $F9 ; RS-232 Tx pointer low byte
; $FA ; RS-232 Tx pointer high byte
; $FB to $FE - unused
BASZPT = $FF ; FAC1 to string output base
STACK = $0100 ; bottom of the stack page
CHNLNK = $01FC ; chain link pointer high byte
; = $01FD ; chain link pointer low byte
PREVLN = $01FE ; line number low byte before crunched line
; $01FF ; line number high byte before crunched line
BUF = $0200 ; input buffer, for some routines the byte before the input
; buffer needs to be set to a specific value for the routine
; to work correctly
LAT = $0259 ; .. to $0262 logical file table
FAT = $0263 ; .. to $026C device number table
SAT = $026D ; .. to $0276 secondary address table
KEYD = $0277 ; .. to $0280 keyboard buffer
MEMSTR = $0281 ; OS start of memory low byte
; $0282 ; OS start of memory high byte
MEMHIGH = $0283 ; OS top of memory low byte
; $0284 ; OS top of memory high byte
TIMOUT = $0285 ; IEEE-488 bus timeout flag ( unused )
COLOR = $0286 ; current colour code
GDCOL = $0287 ; colour under cursor
HIBASE = $0288 ; screen memory page
XMAX = $0289 ; maximum keyboard buffer size
RPTFLG = $028A ; key repeat. $80 = repeat all, $40 = repeat none,
; $00 = repeat cursor movement keys, insert/delete
; key and the space bar
KOUNT = $028B ; repeat speed counter
DELAY = $028C ; repeat delay counter
; This flag signals which of the SHIFT, CTRL, or C= keys are currently being pressed.
; A value of $01 signifies that one of the SHIFT keys is being pressed, a $02 shows that
; the C= key is down, and $04 means that the CTRL key is being pressed. If more than one
; key is held down, these values will be added e.g. $03 indicates that SHIFT and C= are
; both held down.
; Pressing the SHIFT and C= keys at the same time will toggle the character set that is
; presently being used between the uppercase/graphics set, and the lowercase/uppercase
; set.
; While this changes the appearance of all of the characters on the screen at once it
; has nothing whatever to do with the keyboard shift tables and should not be confused
; with the printing of SHIFTed characters, which affects only one character at a time.
SHFLAG = $028D ; keyboard shift/control flag
; bit key(s) 1 = down
; --- ---------------
; 7-3 unused
; 2 CTRL
; 1 C=
; 0 SHIFT
; This location, in combination with the one above, is used to debounce the special
; SHIFT keys. This will keep the SHIFT/C= combination from changing character sets
; back and forth during a single pressing of both keys.
LSTSHF = $028E ; SHIFT/CTRL/C= keypress last pattern
; This location points to the address of the Operating System routine which actually
; determines which keyboard matrix lookup table will be used.
; The routine looks at the value of the SHIFT flag at SHFLAG, and based on what value
; it finds there, stores the address of the correct table to use at location KEYTAB.
KEYLOG = $028F ; keyboard decode logic pointer low byte
; $0290 ; keyboard decode logic pointer high byte
; This flag is used to enable or disable the feature which lets you switch between the
; uppercase/graphics and upper/lowercase character sets by pressing the SHIFT and
; Commodore logo keys simultaneously.
MODE = $0291 ; shift mode switch, $00 = enabled, $80 = locked
; This location is used to determine whether moving the cursor past the ??xx column of
; a logical line will cause another physical line to be added to the logical line.
; A value of 0 enables the screen to scroll the following lines down in order to add
; that line; any nonzero value will disable the scroll.
; This flag is set to disable the scroll temporarily when there are characters waiting
; in the keyboard buffer, these may include cursor movement characters that would
; eliminate the need for a scroll.
AUTODN = $0292 ; screen scrolling flag, $00 = enabled
M51CTR = $0293 ; pseudo 6551 control register. the first character of
; the OPEN RS-232 filename will be stored here
; bit function
; --- --------
; 7 2 stop bits/1 stop bit
; 65 word length
; --- -----------
; 00 8 bits
; 01 7 bits
; 10 6 bits
; 11 5 bits
; 4 unused
; 3210 baud rate
; ---- ---------
; 0000 user rate *
; 0001 50
; 0010 75
; 0011 110
; 0100 134.5
; 0101 150
; 0110 300
; 0111 600
; 1000 1200
; 1001 1800
; 1010 2400
; 1011 3600
; 1100 4800 *
; 1101 7200 *
; 1110 9600 *
; 1111 19200 * * = not implemented
M51CDR = $0294 ; pseudo 6551 command register. the second character of
; the OPEN RS-232 filename will be stored here
; bit function
; --- --------
; 765 parity
; --- ------
; xx0 disabled
; 001 odd
; 011 even
; 101 mark
; 111 space
; 4 duplex half/full
; 3 unused
; 2 unused
; 1 unused
; 0 handshake - X line/3 line
;LAB_0295 = $0295 ; Nonstandard Bit Timing low byte. the third character
; of the OPEN RS-232 filename will be stored here
;LAB_0296 = $0296 ; Nonstandard Bit Timing high byte. the fourth character
; of the OPEN RS-232 filename will be stored here
RSSTAT = $0297 ; RS-232 status register
; bit function
; --- --------
; 7 break
; 6 no DSR detected
; 5 unused
; 4 no CTS detected
; 3 unused
; 2 Rx buffer overrun
; 1 framing error
; 0 parity error
BITNUM = $0298 ; number of bits to be sent/received
BAUDOF = $0299 ; time of one bit cell low byte
; $029A ; time of one bit cell high byte
RIDBE = $029B ; index to Rx buffer end
RIDBS = $029C ; index to Rx buffer start
RODBS = $029D ; index to Tx buffer start
RODBE = $029E ; index to Tx buffer end
IRQTMP = $029F ; saved IRQ low byte
; $02A0 ; saved IRQ high byte
; $02A1 to $02FF - unused
IERROR = $0300 ; BASIC vector - print error message
IMAIN = $0302 ; BASIC vector - main command processor
ICRNCH = $0304 ; BASIC vector - tokenise keywords
IQPLOP = $0306 ; BASIC vector - list program
IGONE = $0308 ; BASIC vector - execute next command
IEVAL = $030A ; BASIC vector - get value from line
; Before every SYS command each of the registers is loaded with the value found in the
; corresponding storage address. Upon returning to BASIC with an RTS instruction, the new
; value of each register is stored in the appropriate storage address.
; This feature allows you to place the necessary values into the registers from BASIC
; before you SYS to a KERNAL or BASIC ML routine. It also enables you to examine the
; resulting effect of the routine on the registers, and to preserve the condition of the
; registers on exit for subsequent SYS calls.
SAREG = $030C ; .A for SYS command
SXREG = $030D ; .X for SYS command
SYREG = $030E ; .Y for SYS command
SPREG = $030F ; .P for SYS command
; $0310 to $0313 - unused
CINV = $0314 ; IRQ vector
CBINV = $0316 ; BRK vector
NMINV = $0318 ; NMI vector
IOPEN = $031A ; KERNAL vector - open a logical file
ICLOSE = $031C ; KERNAL vector - close a specified logical file
ICHKIN = $031E ; KERNAL vector - open channel for input
ICKOUT = $0320 ; KERNAL vector - open channel for output
ICLRCN = $0322 ; KERNAL vector - close input and output channels
IBASIN = $0324 ; KERNAL vector - input character from channel
IBSOUT = $0326 ; KERNAL vector - output character to channel
ISTOP = $0328 ; KERNAL vector - scan stop key
IGETIN = $032A ; KERNAL vector - get character from keyboard queue
ICLALL = $032C ; KERNAL vector - close all channels and files
USRCMD = $032E ; User vector ( unused )
ILOAD = $0330 ; KERNAL vector - load
ISAVE = $0332 ; KERNAL vector - save
; $0334 to $033B - unused
TBUFFR = $033C ; to $03FB - cassette buffer
; $03FC to $03FF - unused
;***********************************************************************************;
;
; hardware equates
VICCR0 = $9000 ; screen origin - horizontal
; bit function
; --- --------
; 7 interlace mode (NTSC only)
; 6-0 horizontal origin
VICCR1 = $9001 ; screen origin - vertical
VICCR2 = $9002 ; video address and screen columns
; bit function
; --- --------
; 7 video memory address va9
; colour memory address va9
; 6-0 number of columns
VICCR3 = $9003 ; screen rows and character height
; bit function
; --- --------
; 7 raster line b0
; 6-1 number of rows
; 0 character height (8/16 bits)
VICCR4 = $9004 ; raster line b8-b1
VICCR5 = $9005 ; video and character memory addresses
; bit function
; --- --------
; 7-4 video memory address va13-va10
; 3-0 character memory address va13-va10
; 0000 ROM $8000 set 1
; 0001 " $8400
; 0010 " $8800 set 2
; 0011 " $8C00
; 1100 RAM $1000
; 1101 " $1400
; 1110 " $1800
; 1111 " $1C00
VICCR6 = $9006 ; light pen horizontal position
VICCR7 = $9007 ; light pen vertical position
VICCR8 = $9008 ; paddle X
VICCR9 = $9009 ; paddle Y
VICCRA = $900A ; oscillator 1
; bit function
; --- --------
; 7 enable
; 6-0 frequency
VICCRB = $900B ; oscillator 2
; bit function
; --- --------
; 7 enable
; 6-0 frequency
VICCRC = $900C ; oscillator 3
; bit function
; --- --------
; 7 enable
; 6-0 frequency
VICCRD = $900D ; white noise
; bit function
; --- --------
; 7 enable
; 6-0 frequency
VICCRE = $900E ; auxiliary colour and volume
; bit function
; --- --------
; 7-4 auxiliary colour
; 3-0 volume
VICCRF = $900F ; background and border colour
; bit function
; --- --------
; 7-4 background colour
; 3 reverse video
; 2-0 border colour
VIA1PB = $9110 ; VIA 1 DRB
; bit function
; --- --------
; 7 DSR in
; 6 CTS in
; 5 unused
; 4 DCD in
; 3 RI in
; 2 DTR out
; 1 RTS out
; 0 data in
VIA1PA1 = $9111 ; VIA 1 DRA
; bit function
; --- --------
; 7 serial ATN out
; 6 cassette switch
; 5 joystick fire, light pen
; 4 joystick left, paddle X fire
; 3 joystick down
; 2 joystick up
; 1 serial DATA in
; 0 serial CLK in
VIA1DDRB = $9112 ; VIA 1 DDRB
VIA1DDRA = $9113 ; VIA 1 DDRA
VIA1T1CL = $9114 ; VIA 1 T1C_l
VIA1T1CH = $9115 ; VIA 1 T1C_h
VIA1T2CL = $9118 ; VIA 1 T2C_l
VIA1T2CH = $9119 ; VIA 1 T2C_h
VIA1ACR = $911B ; VIA 1 ACR
; bit function
; --- --------
; 7 T1 PB7 enabled/disabled
; 6 T1 free run/one shot
; 5 T2 clock PB6/ø2
; 432 function
; --- --------
; 000 shift register disabled
; 001 shift in, rate controlled by T2
; 010 shift in, rate controlled by ø2
; 011 shift in, rate controlled by external clock
; 100 shift out, rate controlled by T2, free run mode
; 101 shift out, rate controlled by T2
; 110 shift out, rate controlled by ø2
; 111 shift out, rate controlled by external clock
; 1 PB latch enabled/disabled
; 0 PA latch enabled/disabled
VIA1PCR = $911C ; VIA 1 PCR
; bit function
; --- --------
; 765 CB2 Tx RS-232 data
; 4 CB1 Rx RS-232 data
; 321 CA2 cassette motor control
; 0 CA1 [RESTORE] key
; The status bit is a not normal flag. It goes high if both an interrupt flag in the IFR
; and the corresponding enable bit in the IER are set. It can be cleared only by clearing
; all the active flags in the IFR or disabling all active interrupts in the IER.
VIA1IFR = $911D ; VIA 1 IFR
; bit function cleared by
; --- -------- ----------
; 7 interrupt status clearing all enabled interrupts
; 6 T1 interrupt read T1C_l, write T1C_h
; 5 T2 interrupt read T2C_l, write T2C_h
; 4 CB1 transition read or write port B
; 3 CB2 transition read or write port B
; 2 8 shifts done read or write the shift register
; 1 CA1 transition read or write port A
; 0 CA2 transition read or write port A
; If enable/disable bit is a zero during a write to this register, each 1 in bits 0-6
; clears the corresponding bit in the IER. If this bit is a one during a write to this
; register, each 1 in bits 0-6 will set the corresponding IER bit.
VIA1IER = $911E ; VIA 1 IER
; bit function
; --- --------
; 7 enable/disable
; 6 T1 interrupt
; 5 T2 interrupt
; 4 CB1 transition
; 3 CB2 transition
; 2 8 shifts done
; 1 CA1 transition
; 0 CA2 transition
VIA1PA2 = $911F ; VIA 1 DRA, no handshake
; bit function
; --- --------
; 7 serial ATN out
; 6 cassette switch
; 5 joystick fire, light pen
; 4 joystick left, paddle X fire
; 3 joystick down
; 2 joystick up
; 1 serial DATA in
; 0 serial CLK in
VIA2PB = $9120 ; VIA 2 DRB, keyboard column
; bit function
; --- --------
; 7 joystick right, paddle Y fire
; 3 cassette write line
VIA2PA1 = $9121 ; VIA 2 DRA, keyboard row
; VIC 20 keyboard matrix layout
; c7 c6 c5 c4 c3 c2 c1 c0
; +----------------------------------------------------------------
; r7| [F7] [F5] [F3] [F1] [DWN] [RGT] [RET] [DEL]
; r6| [HOME] [UP] = [RSH] / ; * £
; r5| - @ : . , L P +
; r4| 0 O K M N J I 9
; r3| 8 U H B V G Y 7
; r2| 6 T F C X D R 5
; r1| 4 E S Z [LSH] A W 3
; r0| 2 Q [C=] [SP] [RUN] [CTL] [<-] 1
VIA2DDRB = $9122 ; VIA 2 DDRB
VIA2DDRA = $9123 ; VIA 2 DDRA
VIA2T1CL = $9124 ; VIA 2 T1C_l
VIA2T1CH = $9125 ; VIA 2 T1C_h
VIA2T2CL = $9128 ; VIA 2 T2C_l
VIA2T2CH = $9129 ; VIA 2 T2C_h
VIA2ACR = $912B ; VIA 2 ACR
VIA2PCR = $912C ; VIA 2 PCR
; bit function
; --- --------
; 765 CB2 serial DATA out
; 4 CB1 serial SRQ in
; 321 CA2 serial CLK out
; 0 CA1 cassette read line
; The status bit is a not normal flag. it goes high if both an interrupt flag in the IFR
; and the corresponding enable bit in the IER are set. It can be cleared only by clearing
; all the active flags in the IFR or disabling all active interrupts in the IER.
VIA2IFR = $912D ; VIA 2 IFR
; bit function cleared by
; --- -------- ----------
; 7 interrupt status clearing all enabled interrupts
; 6 T1 interrupt read T1C_l, write T1C_h
; 5 T2 interrupt read T2C_l, write T2C_h
; 4 CB1 transition read or write port B
; 3 CB2 transition read or write port B
; 2 8 shifts done read or write the shift register
; 1 CA1 transition read or write port A
; 0 CA2 transition read or write port A
; If enable/disable bit is a zero during a write to this register, each 1 in bits 0-6
; clears the corresponding bit in the IER. If this bit is a one during a write to this
; register, each 1 in bits 0-6 will set the corresponding IER bit.
VIA2IER = $912E ; VIA 2 IER
; bit function
; --- --------
; 7 enable/disable
; 6 T1 interrupt
; 5 T2 interrupt
; 4 CB1 transition
; 3 CB2 transition
; 2 8 shifts done
; 1 CA1 transition
; 0 CA2 transition
VIA2PA2 = $912F ; VIA 2 DRA, keyboard row, no handshake
XROMCOLD = $A000 ; autostart ROM initial entry vector
XROMWARM = $A002 ; autostart ROM break entry vector
XROMID = $A004 ; .. to $A008 autostart ROM identifier string start
;***********************************************************************************;
;
; BASIC keyword token values. Tokens not used in the source are included for
; completeness.
; command tokens
TK_END = $80 ; END token
TK_FOR = $81 ; FOR token
TK_NEXT = $82 ; NEXT token
TK_DATA = $83 ; DATA token
TK_INFL = $84 ; INPUT# token
TK_INPUT = $85 ; INPUT token
TK_DIM = $86 ; DIM token
TK_READ = $87 ; READ token
TK_LET = $88 ; LET token
TK_GOTO = $89 ; GOTO token
TK_RUN = $8A ; RUN token
TK_IF = $8B ; IF token
TK_RESTORE = $8C ; RESTORE token
TK_GOSUB = $8D ; GOSUB token
TK_RETURN = $8E ; RETURN token
TK_REM = $8F ; REM token
TK_STOP = $90 ; STOP token
TK_ON = $91 ; ON token
TK_WAIT = $92 ; WAIT token
TK_LOAD = $93 ; LOAD token
TK_SAVE = $94 ; SAVE token
TK_VERIFY = $95 ; VERIFY token
TK_DEF = $96 ; DEF token
TK_POKE = $97 ; POKE token
TK_PRINFL = $98 ; PRINT# token
TK_PRINT = $99 ; PRINT token
TK_CONT = $9A ; CONT token
TK_LIST = $9B ; LIST token
TK_CLR = $9C ; CLR token
TK_CMD = $9D ; CMD token
TK_SYS = $9E ; SYS token
TK_OPEN = $9F ; OPEN token
TK_CLOSE = $A0 ; CLOSE token
TK_GET = $A1 ; GET token
TK_NEW = $A2 ; NEW token
; secondary keyword tokens
TK_TAB = $A3 ; TAB( token
TK_TO = $A4 ; TO token
TK_FN = $A5 ; FN token
TK_SPC = $A6 ; SPC( token
TK_THEN = $A7 ; THEN token
TK_NOT = $A8 ; NOT token
TK_STEP = $A9 ; STEP token
; operator tokens
TK_PLUS = $AA ; + token
TK_MINUS = $AB ; - token
TK_MUL = $AC ; * token
TK_DIV = $AD ; / token
TK_POWER = $AE ; ^ token
TK_AND = $AF ; AND token
TK_OR = $B0 ; OR token
TK_GT = $B1 ; > token
TK_EQUAL = $B2 ; = token
TK_LT = $B3 ; < token
; function tokens
TK_SGN = $B4 ; SGN token
TK_INT = $B5 ; INT token
TK_ABS = $B6 ; ABS token
TK_USR = $B7 ; USR token
TK_FRE = $B8 ; FRE token
TK_POS = $B9 ; POS token
TK_SQR = $BA ; SQR token
TK_RND = $BB ; RND token
TK_LOG = $BC ; LOG token
TK_EXP = $BD ; EXP token
TK_COS = $BE ; COS token
TK_SIN = $BF ; SIN token
TK_TAN = $C0 ; TAN token
TK_ATN = $C1 ; ATN token
TK_PEEK = $C2 ; PEEK token
TK_LEN = $C3 ; LEN token
TK_STRS = $C4 ; STR$ token
TK_VAL = $C5 ; VAL token
TK_ASC = $C6 ; ASC token
TK_CHRS = $C7 ; CHR$ token
TK_LEFTS = $C8 ; LEFT$ token
TK_RIGHTS = $C9 ; RIGHT$ token
TK_MIDS = $CA ; MID$ token
TK_GO = $CB ; GO token
TK_PI = $FF ; PI token
;***********************************************************************************;
;
; floating point accumulator offsets
FAC_EXPT = $00
FAC_MANT = $01
FAC_SIGN = $05
;***********************************************************************************;
;***********************************************************************************;
;
; BASIC ROM start
* = $C000
COLDST
.word COLDBA ; BASIC cold start entry point
WARMST
.word WARMBAS ; BASIC warm start entry point
;CBMBASIC
.byte "CBMBASIC" ; ROM name, unreferenced
;***********************************************************************************;
;
; Action addresses for primary commands. These are called by pushing the address
; onto the stack and doing an RTS so the actual address - 1 needs to be pushed.
STMDSP
.word END-1 ; perform END
.word FOR-1 ; perform FOR
.word NEXT-1 ; perform NEXT
.word SKIPST-1 ; perform DATA
.word INPUTN-1 ; perform INPUT#
.word INPUT-1 ; perform INPUT
.word DIM-1 ; perform DIM
.word READ-1 ; perform READ
.word LET-1 ; perform LET
.word GOTO-1 ; perform GOTO
.word RUN-1 ; perform RUN
.word IF-1 ; perform IF
.word RESTORE-1 ; perform RESTORE
.word GOSUB-1 ; perform GOSUB
.word RETURN-1 ; perform RETURN
.word REM-1 ; perform REM
.word BSTOP-1 ; perform STOP
.word ON-1 ; perform ON
.word WAIT-1 ; perform WAIT
.word BLOAD-1 ; perform LOAD
.word BSAVE-1 ; perform SAVE
.word BVERIF-1 ; perform VERIFY
.word DEF-1 ; perform DEF
.word POKE-1 ; perform POKE
.word PRINTN-1 ; perform PRINT#
.word PRINT-1 ; perform PRINT
.word CONT-1 ; perform CONT
.word LIST-1 ; perform LIST
.word CLR-1 ; perform CLR
.word CMD-1 ; perform CMD
.word SYSTEM-1 ; perform SYS
.word BOPEN-1 ; perform OPEN
.word BCLOSE-1 ; perform CLOSE
.word GET-1 ; perform GET
.word NEW-1 ; perform NEW
;***********************************************************************************;
;
; action addresses for functions
FUNDSP
.word SGN ; perform SGN()
.word INT ; perform INT()
.word ABS ; perform ABS()
.word USRPPOK ; perform USR()
.word FRE ; perform FRE()
.word POS ; perform POS()
.word SQR ; perform SQR()
.word RND ; perform RND()
.word LOG ; perform LOG()
.word EXP ; perform EXP()
.word COS ; perform COS()
.word SIN ; perform SIN()
.word TAN ; perform TAN()
.word ATN ; perform ATN()
.word PEEK ; perform PEEK()
.word LEN ; perform LEN()
.word STR ; perform STR$()
.word VAL ; perform VAL()
.word ASC ; perform ASC()
.word CHR ; perform CHR$()
.word LEFT ; perform LEFT$()
.word RIGHT ; perform RIGHT$()
.word MID ; perform MID$()
;***********************************************************************************;
;
; Precedence byte and action addresses for operators. Like the primary commands these
; are called by pushing the address onto the stack and doing an RTS, so again the actual
; address - 1 needs to be pushed.
OPTAB
.byte $79
.word PLUS-1 ; +
.byte $79
.word SUB-1 ; -
.byte $7B
.word MULT-1 ; *
.byte $7B
.word DIVIDE-1 ; /
.byte $7F
.word EXPONT-1 ; ^
.byte $50
.word ANDD-1 ; AND
.byte $46
.word ORR-1 ; OR
.byte $7D
.word NEGFAC-1 ; >
.byte $5A
.word EQUAL-1 ; =
LAB_C09B
.byte $64
.word COMPAR-1 ; <
;***********************************************************************************;
;
; BASIC keywords. Each word has b7 set in its last character as an end marker,
; even the one character keywords such as "<" or "=".
; first are the primary command keywords, only these can start a statement
RESLST
.byte "EN",'D'+$80 ; END
.byte "FO",'R'+$80 ; FOR
.byte "NEX",'T'+$80 ; NEXT
.byte "DAT",'A'+$80 ; DATA
.byte "INPUT",'#'+$80 ; INPUT#
.byte "INPU",'T'+$80 ; INPUT
.byte "DI",'M'+$80 ; DIM
.byte "REA",'D'+$80 ; READ
.byte "LE",'T'+$80 ; LET
.byte "GOT",'O'+$80 ; GOTO
.byte "RU",'N'+$80 ; RUN
.byte "I",'F'+$80 ; IF
.byte "RESTOR",'E'+$80 ; RESTORE
.byte "GOSU",'B'+$80 ; GOSUB
.byte "RETUR",'N'+$80 ; RETURN
.byte "RE",'M'+$80 ; REM
.byte "STO",'P'+$80 ; STOP
.byte "O",'N'+$80 ; ON
.byte "WAI",'T'+$80 ; WAIT
.byte "LOA",'D'+$80 ; LOAD
.byte "SAV",'E'+$80 ; SAVE
.byte "VERIF",'Y'+$80 ; VERIFY
.byte "DE",'F'+$80 ; DEF
.byte "POK",'E'+$80 ; POKE
.byte "PRINT",'#'+$80 ; PRINT#
.byte "PRIN",'T'+$80 ; PRINT
.byte "CON",'T'+$80 ; CONT
.byte "LIS",'T'+$80 ; LIST
.byte "CL",'R'+$80 ; CLR
.byte "CM",'D'+$80 ; CMD
.byte "SY",'S'+$80 ; SYS
.byte "OPE",'N'+$80 ; OPEN
.byte "CLOS",'E'+$80 ; CLOSE
.byte "GE",'T'+$80 ; GET
.byte "NE",'W'+$80 ; NEW
; next are the secondary command keywords, these cannot start a statement
.byte "TAB",'('+$80 ; TAB(
.byte "T",'O'+$80 ; TO
.byte "F",'N'+$80 ; FN
.byte "SPC",'('+$80 ; SPC(
.byte "THE",'N'+$80 ; THEN
.byte "NO",'T'+$80 ; NOT
.byte "STE",'P'+$80 ; STEP
; the operators
.byte '+'+$80 ; +
.byte '-'+$80 ; -
.byte '*'+$80 ; *
.byte '/'+$80 ; /
.byte '^^'+$80 ; ^
.byte "AN",'D'+$80 ; AND
.byte "O",'R'+$80 ; OR
.byte '>'+$80 ; >
.byte '='+$80 ; =
.byte '<'+$80 ; <
; the functions
.byte "SG",'N'+$80 ; SGN
.byte "IN",'T'+$80 ; INT
.byte "AB",'S'+$80 ; ABS
.byte "US",'R'+$80 ; USR
.byte "FR",'E'+$80 ; FRE
.byte "PO",'S'+$80 ; POS
.byte "SQ",'R'+$80 ; SQR
.byte "RN",'D'+$80 ; RND
.byte "LO",'G'+$80 ; LOG
.byte "EX",'P'+$80 ; EXP
.byte "CO",'S'+$80 ; COS
.byte "SI",'N'+$80 ; SIN
.byte "TA",'N'+$80 ; TAN
.byte "AT",'N'+$80 ; ATN
.byte "PEE",'K'+$80 ; PEEK
.byte "LE",'N'+$80 ; LEN
.byte "STR",'$'+$80 ; STR$
.byte "VA",'L'+$80 ; VAL
.byte "AS",'C'+$80 ; ASC
.byte "CHR",'$'+$80 ; CHR$
.byte "LEFT",'$'+$80 ; LEFT$
.byte "RIGHT",'$'+$80 ; RIGHT$
.byte "MID",'$'+$80 ; MID$
; lastly is GO, this is an add on so that GO TO, as well as GOTO, will work
.byte "G",'O'+$80 ; GO
.byte $00 ; end marker
;***********************************************************************************;
;
; error messages
ER_STOP = $00
ER_2MANYF = $01
ER_FOPEN = $02
ER_FNOTOPEN = $03
ER_FNOTFND = $04
ER_DEVNOTP = $05
ER_NOTINF = $06
ER_NOTOUTF = $07
ER_MISSFNAM = $08
ER_ILLDEVN = $09
ER_NXTWOFOR = $0A
ER_SYNTAX = $0B
ER_RETWOGSB = $0C
ER_OODATA = $0D
ER_ILLQUAN = $0E
ER_OVFLOW = $0F
ER_OOMEM = $10
ER_UNDSMNT = $11
ER_BADSSCPT = $12
ER_REDIMARY = $13
ER_DIVBY0 = $14
ER_ILLDIR = $15
ER_TYPMSMCH = $16
ER_STR2LONG = $17
ER_FDATA = $18
ER_FMLA2CPLX = $19
ER_CANTCONT = $1A
ER_UNDEFUN = $1B
ER_VERIFY = $1C
ER_LOAD = $1D
ER_BREAK = $1E
ERRSTR01
.byte "TOO MANY FILE",'S'+$80
ERRSTR02
.byte "FILE OPE",'N'+$80
ERRSTR03
.byte "FILE NOT OPE",'N'+$80
ERRSTR04
.byte "FILE NOT FOUN",'D'+$80
ERRSTR05
.byte "DEVICE NOT PRESEN",'T'+$80
ERRSTR06
.byte "NOT INPUT FIL",'E'+$80
ERRSTR07
.byte "NOT OUTPUT FIL",'E'+$80
ERRSTR08
.byte "MISSING FILE NAM",'E'+$80
ERRSTR09
.byte "ILLEGAL DEVICE NUMBE",'R'+$80
ERRSTR0A
.byte "NEXT WITHOUT FO",'R'+$80
ERRSTR0B
.byte "SYNTA",'X'+$80
ERRSTR0C
.byte "RETURN WITHOUT GOSU",'B'+$80
ERRSTR0D
.byte "OUT OF DAT",'A'+$80
ERRSTR0E
.byte "ILLEGAL QUANTIT",'Y'+$80
ERRSTR0F
.byte "OVERFLO",'W'+$80
ERRSTR10
.byte "OUT OF MEMOR",'Y'+$80
ERRSTR11
.byte "UNDEF'D STATEMEN",'T'+$80
ERRSTR12
.byte "BAD SUBSCRIP",'T'+$80
ERRSTR13
.byte "REDIM'D ARRA",'Y'+$80
ERRSTR14
.byte "DIVISION BY ZER",'O'+$80
ERRSTR15
.byte "ILLEGAL DIREC",'T'+$80
ERRSTR16
.byte "TYPE MISMATC",'H'+$80
ERRSTR17
.byte "STRING TOO LON",'G'+$80
ERRSTR18
.byte "FILE DAT",'A'+$80
ERRSTR19
.byte "FORMULA TOO COMPLE",'X'+$80
ERRSTR1A
.byte "CAN'T CONTINU",'E'+$80
ERRSTR1B
.byte "UNDEF'D FUNCTIO",'N'+$80
ERRSTR1C
.byte "VERIF",'Y'+$80
ERRSTR1D
.byte "LOA",'D'+$80
; error message pointer table
BMSGS
.word ERRSTR01 ; $01 TOO MANY FILES
.word ERRSTR02 ; $02 FILE OPEN
.word ERRSTR03 ; $03 FILE NOT OPEN
.word ERRSTR04 ; $04 FILE NOT FOUND
.word ERRSTR05 ; $05 DEVICE NOT PRESENT
.word ERRSTR06 ; $06 NOT INPUT FILE
.word ERRSTR07 ; $07 NOT OUTPUT FILE
.word ERRSTR08 ; $08 MISSING FILE NAME
.word ERRSTR09 ; $09 ILLEGAL DEVICE NUMBER
.word ERRSTR0A ; $0A NEXT WITHOUT FOR
.word ERRSTR0B ; $0B SYNTAX
.word ERRSTR0C ; $0C RETURN WITHOUT GOSUB
.word ERRSTR0D ; $0D OUT OF DATA
.word ERRSTR0E ; $0E ILLEGAL QUANTITY
.word ERRSTR0F ; $0F OVERFLOW
.word ERRSTR10 ; $10 OUT OF MEMORY
.word ERRSTR11 ; $11 UNDEF'D STATEMENT
.word ERRSTR12 ; $12 BAD SUBSCRIPT
.word ERRSTR13 ; $13 REDIM'D ARRAY
.word ERRSTR14 ; $14 DIVISION BY ZERO
.word ERRSTR15 ; $15 ILLEGAL DIRECT
.word ERRSTR16 ; $16 TYPE MISMATCH
.word ERRSTR17 ; $17 STRING TOO LONG
.word ERRSTR18 ; $18 FILE DATA
.word ERRSTR19 ; $19 FORMULA TOO COMPLEX
.word ERRSTR1A ; $1A CAN'T CONTINUE
.word ERRSTR1B ; $1B UNDEF'D FUNCTION
.word ERRSTR1C ; $1C VERIFY
.word ERRSTR1D ; $1D LOAD
.word BREAKSTR ; $1E BREAK
;***********************************************************************************;
;
; BASIC messages
OKSTR
.byte $0D,"OK",$0D,$00
ERRORSTR
.byte $0D," ERROR",$00
INSTR
.byte " IN ",$00
READYSTR
.byte $0D,$0A,"READY.",$0D,$0A,$00
CRLFBRK
.byte $0D,$0A
BREAKSTR
.byte "BREAK",$00
;***********************************************************************************;
;
; spare byte, not referenced
;LAB_C389
.byte $A0
;***********************************************************************************;
;
; search the stack for FOR or GOSUB activity
; return Zb=1 if FOR variable found
SCNSTK
TSX ; copy stack pointer
INX ; +1 pass return address
INX ; +2 pass return address
INX ; +3 pass calling routine return address
INX ; +4 pass calling routine return address
LAB_C38F
LDA STACK+1,X ; get token byte from stack
CMP #TK_FOR ; is it FOR token
BNE LAB_C3B7 ; exit if not FOR token
; was FOR token
LDA FORPNT+1 ; get FOR/NEXT variable pointer high byte
BNE LAB_C3A4 ; branch if not null
LDA STACK+2,X ; get FOR variable pointer low byte
STA FORPNT ; save FOR/NEXT variable pointer low byte
LDA STACK+3,X ; get FOR variable pointer high byte
STA FORPNT+1 ; save FOR/NEXT variable pointer high byte
LAB_C3A4
CMP STACK+3,X ; compare variable pointer with stacked variable pointer
; high byte
BNE LAB_C3B0 ; branch if no match
LDA FORPNT ; get FOR/NEXT variable pointer low byte
CMP STACK+2,X ; compare variable pointer with stacked variable pointer
; low byte
BEQ LAB_C3B7 ; exit if match found
LAB_C3B0
TXA ; copy index
CLC ; clear carry for add
ADC #$12 ; add FOR stack use size
TAX ; copy back to index
BNE LAB_C38F ; loop if not at start of stack
LAB_C3B7
RTS
;***********************************************************************************;
;
; open up space in memory, set end of arrays
MAKSPC
JSR RAMSPC ; check available memory, do out of memory error if no room
STA STREND ; set end of arrays low byte
STY STREND+1 ; set end of arrays high byte
; open up space in memory, don't set array end
MOVEBL
SEC ; set carry for subtract
LDA GEN2PTR ; get block end low byte
SBC TMPPTR ; subtract block start low byte
STA INDEX ; save MOD(block length/$100) byte
TAY ; copy MOD(block length/$100) byte to .Y
LDA GEN2PTR+1 ; get block end high byte
SBC TMPPTR+1 ; subtract block start high byte
TAX ; copy block length high byte to .X
INX ; +1 to allow for count=0 exit
TYA ; copy block length low byte to .A
BEQ LAB_C3F3 ; branch if length low byte=0
; block is (.X-1)*$100+.Y bytes, do the .Y bytes first
LDA GEN2PTR ; get block end low byte
SEC ; set carry for subtract
SBC INDEX ; subtract MOD(block length/$100) byte
STA GEN2PTR ; save corrected old block end low byte
BCS LAB_C3DC ; if no underflow skip the high byte decrement
DEC GEN2PTR+1 ; else decrement block end high byte
SEC ; set carry for subtract
LAB_C3DC
LDA GENPTR ; get destination end low byte
SBC INDEX ; subtract MOD(block length/$100) byte
STA GENPTR ; save modified new block end low byte
BCS LAB_C3EC ; if no underflow skip the high byte decrement
DEC GENPTR+1 ; else decrement block end high byte
BCC LAB_C3EC ; branch always
LAB_C3E8
LDA (GEN2PTR),Y ; get byte from source
STA (GENPTR),Y ; copy byte to destination
LAB_C3EC
DEY ; decrement index
BNE LAB_C3E8 ; loop until .Y=0
; now do .Y=0 indexed byte
LDA (GEN2PTR),Y ; get byte from source
STA (GENPTR),Y ; save byte to destination
LAB_C3F3
DEC GEN2PTR+1 ; decrement source pointer high byte
DEC GENPTR+1 ; decrement destination pointer high byte
DEX ; decrement block count
BNE LAB_C3EC ; loop until count = $0
RTS
;***********************************************************************************;
;
; check there is room on the stack for .A bytes
; if the stack is too deep do an out of memory error
STKSPC
ASL ; *2
ADC #$3E ; need at least 62d bytes free
BCS MEMERR ; if overflow go do out of memory error then warm start
STA INDEX ; save result in temp byte
TSX ; copy stack
CPX INDEX ; compare new limit with stack
BCC MEMERR ; if stack < limit do out of memory error then warm start
RTS
;***********************************************************************************;
;
; check available memory, do out of memory error if no room
RAMSPC
CPY FRETOP+1 ; compare with bottom of string space high byte
BCC LAB_C434 ; if less then exit (is ok)
BNE LAB_C412 ; skip next test if greater (tested <)
; high byte was =, now do low byte
CMP FRETOP ; compare with bottom of string space low byte
BCC LAB_C434 ; if less then exit (is ok)
; address is > string storage ptr (oops!)
LAB_C412
PHA ; push address low byte
LDX #$09 ; set index to save TEMPF3 to TMPPTR+1 inclusive
TYA ; copy address high byte (to push on stack)
; save misc numeric work area
LAB_C416
PHA ; push byte
LDA TEMPF3,X ; get byte from TEMPF3 to TMPPTR+1
DEX ; decrement index
BPL LAB_C416 ; loop until all done
JSR GRBCOL ; do garbage collection routine
; restore misc numeric work area
LDX #$F7 ; set index to restore bytes
LAB_C421
PLA ; pop byte
STA TMPPTR+2,X ; save byte to TEMPF3 to TMPPTR+2
INX ; increment index
BMI LAB_C421 ; loop while -ve
PLA ; pop address high byte
TAY ; copy back to .Y
PLA ; pop address low byte
CPY FRETOP+1 ; compare with bottom of string space high byte
BCC LAB_C434 ; if less then exit (is ok)
BNE MEMERR ; if greater do out of memory error then warm start
; high byte was =, now do low byte
CMP FRETOP ; compare with bottom of string space low byte
BCS MEMERR ; if >= do out of memory error then warm start
; ok exit, carry clear
LAB_C434
RTS
;***********************************************************************************;
;
; do out of memory error then warm start
MEMERR
LDX #ER_OOMEM ; error code $10, out of memory error
; do error #.X then warm start
ERROR
JMP (IERROR) ; do error message
; do error #.X then warm start, the error message vector is initialised to point here
ERROR2
TXA ; copy error number
ASL ; *2
TAX ; copy to index
LDA BMSGS-2,X ; get error message pointer low byte
STA INDEX ; save it
LDA BMSGS-1,X ; get error message pointer high byte
STA INDEX+1 ; save it
JSR CLRCHN ; close input and output channels
LDA #$00 ; clear .A
STA CHANNL ; clear current I/O channel, flag default
JSR LAB_CAD7 ; print CR/LF
JSR LAB_CB45 ; print "?"
LDY #$00 ; clear index
LAB_C456
LDA (INDEX),Y ; get byte from message
PHA ; save status
AND #$7F ; mask 0xxx xxxx, clear b7
JSR LAB_CB47 ; output character
INY ; increment index
PLA ; restore status
BPL LAB_C456 ; loop if character was not end marker
JSR LAB_C67A ; flush BASIC stack and clear continue pointer
LDA #<ERRORSTR ; set " ERROR" pointer low byte
LDY #>ERRORSTR ; set " ERROR" pointer high byte
;***********************************************************************************;
;
; print string and do warm start, break entry
PRDY
JSR PRTSTR ; print null terminated string
LDY CURLIN+1 ; get current line number high byte
INY ; increment it
BEQ READY ; branch if was in immediate mode
JSR PRTIN ; do " IN " line number message
;***********************************************************************************;
;
; do warm start
READY
LDA #<READYSTR ; set "READY." pointer low byte
LDY #>READYSTR ; set "READY." pointer high byte
JSR PRTSTR ; print null terminated string
LDA #$80 ; set for control messages only
JSR SETMSG ; control KERNAL messages
MAIN
JMP (IMAIN) ; do BASIC warm start
;***********************************************************************************;
;
; BASIC warm start, the warm start vector is initialised to point here
MAIN2
JSR GETLIN ; call for BASIC input
STX CHRGOT+1 ; save BASIC execute pointer low byte
STY CHRGOT+2 ; save BASIC execute pointer high byte
JSR CHRGET ; increment and scan memory
TAX ; copy byte to set flags
BEQ MAIN ; loop if no input
; got to interpret input line now ...
LDX #$FF ; current line high byte to -1, indicates immediate mode
STX CURLIN+1 ; set current line number high byte
BCC NEWLIN ; if numeric character go handle new BASIC line
; no line number .. immediate mode
JSR CRNCH ; crunch keywords into BASIC tokens
JMP LAB_C7E1 ; go scan and interpret code
; handle new BASIC line
NEWLIN
JSR DECBIN ; get fixed-point number into temporary integer
JSR CRNCH ; crunch keywords into BASIC tokens
STY COUNT ; save index pointer to end of crunched line
JSR FINLIN ; search BASIC for temporary integer line number
BCC LAB_C4ED ; if not found skip the line delete
; line # already exists so delete it
LDY #$01 ; set index to next line pointer high byte
LDA (TMPPTR),Y ; get next line pointer high byte
STA INDEX+1 ; save it
LDA VARTAB ; get start of variables low byte
STA INDEX ; save it
LDA TMPPTR+1 ; get found line pointer high byte
STA INDEX+3 ; save it
LDA TMPPTR ; get found line pointer low byte
DEY ; decrement index
SBC (TMPPTR),Y ; subtract next line pointer low byte
CLC ; clear carry for add
ADC VARTAB ; add start of variables low byte
STA VARTAB ; set start of variables low byte
STA INDEX+2 ; save destination pointer low byte
LDA VARTAB+1 ; get start of variables high byte
ADC #$FF ; -1 + carry
STA VARTAB+1 ; set start of variables high byte
SBC TMPPTR+1 ; subtract found line pointer high byte
TAX ; copy to block count
SEC ; set carry for subtract
LDA TMPPTR ; get found line pointer low byte
SBC VARTAB ; subtract start of variables low byte
TAY ; copy to bytes in first block count
BCS LAB_C4D7 ; if no underflow skip the high byte decrement
INX ; increment block count, correct for = 0 loop exit
DEC INDEX+3 ; decrement destination high byte
LAB_C4D7
CLC ; clear carry for add
ADC INDEX ; add source pointer low byte
BCC LAB_C4DF ; if no underflow skip the high byte decrement
DEC INDEX+1 ; else decrement source pointer high byte
CLC ; clear carry
; close up memory to delete old line
LAB_C4DF
LDA (INDEX),Y ; get byte from source
STA (INDEX+2),Y ; copy to destination
INY ; increment index
BNE LAB_C4DF ; while <> 0 do this block
INC INDEX+1 ; increment source pointer high byte
INC INDEX+3 ; increment destination pointer high byte
DEX ; decrement block count
BNE LAB_C4DF ; loop until all done
; got new line in buffer and no existing same #
LAB_C4ED
JSR LAB_C659 ; reset execution to start, clear variables, flush stack
; and return
JSR LNKPRG ; rebuild BASIC line chaining
LDA BUF ; get first byte from buffer
BEQ MAIN ; if no line go do BASIC warm start
; else insert line into memory
CLC ; clear carry for add
LDA VARTAB ; get start of variables low byte
STA GEN2PTR ; save as source end pointer low byte
ADC COUNT ; add index pointer to end of crunched line
STA GENPTR ; save as destination end pointer low byte
LDY VARTAB+1 ; get start of variables high byte
STY GEN2PTR+1 ; save as source end pointer high byte
BCC LAB_C508 ; if no carry skip the high byte increment
INY ; else increment the high byte
LAB_C508
STY GENPTR+1 ; save as destination end pointer high byte
JSR MAKSPC ; open up space in memory
; Most of what remains to do is copy the crunched line into the space opened up in memory,
; however, before the crunched line comes the next line pointer and the line number. The
; line number is retrieved from the temporary integer and stored in memory, this
; overwrites the bottom two bytes on the stack. Next the line is copied and the next line
; pointer is filled with whatever was in two bytes above the line number in the stack.
; This is ok because the line pointer gets fixed in the line chain re-build.
LDA LINNUM ; get line number low byte
LDY LINNUM+1 ; get line number high byte
STA PREVLN ; save line number low byte before crunched line
STY PREVLN+1 ; save line number high byte before crunched line
LDA STREND ; get end of arrays low byte
LDY STREND+1 ; get end of arrays high byte
STA VARTAB ; set start of variables low byte
STY VARTAB+1 ; set start of variables high byte
LDY COUNT ; get index to end of crunched line
DEY ; -1
LAB_C522
LDA CHNLNK,Y ; get byte from crunched line
STA (TMPPTR),Y ; save byte to memory
DEY ; decrement index
BPL LAB_C522 ; loop while more to do
; reset execution, clear variables, flush stack, rebuild BASIC chain and do warm start
LAB_C52A
JSR LAB_C659 ; reset execution to start, clear variables and flush stack
JSR LNKPRG ; rebuild BASIC line chaining
JMP MAIN ; go do BASIC warm start
;***********************************************************************************;
;
; rebuild BASIC line chaining
LNKPRG
LDA TXTTAB ; get start of memory low byte
LDY TXTTAB+1 ; get start of memory high byte
STA INDEX ; set line start pointer low byte
STY INDEX+1 ; set line start pointer high byte
CLC ; clear carry for add
LAB_C53C
LDY #$01 ; set index to pointer to next line high byte
LDA (INDEX),Y ; get pointer to next line high byte
BEQ LAB_C55F ; exit if null, [EOT]
LDY #$04 ; point to first code byte of line
; there is always 1 byte + [EOL] as null entries are deleted
LAB_C544
INY ; next code byte
LDA (INDEX),Y ; get byte
BNE LAB_C544 ; loop if not [EOL]
INY ; point to byte past [EOL], start of next line
TYA ; copy it
ADC INDEX ; add line start pointer low byte
TAX ; copy to .X
LDY #$00 ; clear index, point to this line's next line pointer
STA (INDEX),Y ; set next line pointer low byte
LDA INDEX+1 ; get line start pointer high byte
ADC #$00 ; add any overflow
INY ; increment index to high byte
STA (INDEX),Y ; set next line pointer high byte
STX INDEX ; set line start pointer low byte
STA INDEX+1 ; set line start pointer high byte
BCC LAB_C53C ; go do next line, branch always
LAB_C55F
RTS
;***********************************************************************************;
;
; call for BASIC input
GETLIN
LDX #$00 ; set channel $00, keyboard
LAB_C562
JSR LAB_E10F ; input character from channel with error check
CMP #$0D ; compare with [RETURN]
BEQ LAB_C576 ; if [RETURN] set .X.Y to BUF - 1, print [RETURN] and exit
; character was not [RETURN]
STA BUF,X ; save character to buffer
INX ; increment buffer index
CPX #$59 ; compare with max+1
BCC LAB_C562 ; branch if < max+1
LDX #ER_STR2LONG ; error $17, string too long error
JMP ERROR ; do error #.X then warm start
LAB_C576
JMP LAB_CACA ; set .X.Y to BUF - 1 and print [CR]
;***********************************************************************************;
;
; crunch BASIC tokens vector
CRNCH
JMP (ICRNCH) ; do crunch BASIC tokens
;***********************************************************************************;
;
; crunch BASIC tokens, the crunch BASIC tokens vector is initialised to point here
CRNCH2
LDX CHRGOT+1 ; get BASIC execute pointer low byte
LDY #$04 ; set save index
STY GARBFL ; clear open quote/DATA flag
LAB_C582
LDA BUF,X ; get a byte from the input buffer
BPL LAB_C58E ; if b7 clear go do crunching
CMP #TK_PI ; compare with the token for PI, this token is input
; directly from the keyboard as the [PI] character.
BEQ LAB_C5C9 ; if PI save byte then continue crunching
; this is the bit of code that stops you being able to enter
; some keywords as just single shifted characters. If this
; dropped through you would be able to enter GOTO as just
; [SHIFT]G
INX ; increment read index
BNE LAB_C582 ; loop if more to do, branch always
LAB_C58E
CMP #' ' ; compare with [SPACE]
BEQ LAB_C5C9 ; if [SPACE] save byte then continue crunching
STA ENDCHR ; save buffer byte as search character
CMP #$22 ; compare with quote character
BEQ LAB_C5EE ; if quote go copy quoted string
BIT GARBFL ; get open quote/DATA token flag
BVS LAB_C5C9 ; branch if b6 of open quote set, was DATA
; go save byte then continue crunching
CMP #'?' ; compare with "?" character
BNE LAB_C5A4 ; if not "?" continue crunching
LDA #TK_PRINT ; else set the token for PRINT
BNE LAB_C5C9 ; go save byte then continue crunching, branch always
LAB_C5A4
CMP #'0' ; compare with "0"
BCC LAB_C5AC ; if < "0" continue crunching
CMP #'<' ; compare with "<"
BCC LAB_C5C9 ; if <, 0123456789:; go save byte then continue crunching
; gets here with next character not numeric, ";" or ":"
LAB_C5AC
STY FBUFPT ; copy save index
LDY #$00 ; clear table pointer
STY COUNT ; clear word index
DEY ; adjust for pre increment loop
STX CHRGOT+1 ; save BASIC execute pointer low byte, buffer index
DEX ; adjust for pre increment loop
LAB_C5B6
INY ; next table byte
INX ; next buffer byte
LAB_C5B8
LDA BUF,X ; get byte from input buffer
SEC ; set carry for subtract
SBC RESLST,Y ; subtract table byte
BEQ LAB_C5B6 ; go compare next if match
CMP #$80 ; was it end marker match ?
BNE LAB_C5F5 ; if not go try the next keyword
; actually this works even if the input buffer byte is the
; end marker, i.e. a shifted character. As you can't enter
; any keywords as a single shifted character, see above,
; you can enter keywords in shorthand by shifting any
; character after the first. so RETURN can be entered as
; R[SHIFT]E, RE[SHIFT]T, RET[SHIFT]U or RETU[SHIFT]R.
; RETUR[SHIFT]N however will not work because the [SHIFT]N
; will match the RETURN end marker so the routine will try
; to match the next character.
; else found keyword
ORA COUNT ; OR with word index, +$80 in .A makes token
LAB_C5C7
LDY FBUFPT ; restore save index
; save byte then continue crunching
LAB_C5C9
INX ; increment buffer read index
INY ; increment save index
STA BUF-5,Y ; save byte to output
LDA BUF-5,Y ; get byte from output, set flags
BEQ LAB_C609 ; branch if was null [EOL]
; .A holds the token here
SEC ; set carry for subtract
SBC #':' ; subtract ":"
BEQ LAB_C5DC ; branch if it was (is now $00)
; .A now holds token-':'
CMP #TK_DATA-':' ; compare with the token for DATA-':'
BNE LAB_C5DE ; if not DATA go try REM
; token was : or DATA
LAB_C5DC
STA GARBFL ; save token-':'
LAB_C5DE
SEC ; set carry for subtract
SBC #TK_REM-':' ; subtract the token for REM-':'
BNE LAB_C582 ; if wasn't REM go crunch next bit of line
STA ENDCHR ; else was REM so set search for [EOL]
; loop for "..." etc.
LAB_C5E5
LDA BUF,X ; get byte from input buffer
BEQ LAB_C5C9 ; if null [EOL] save byte then continue crunching
CMP ENDCHR ; compare with stored character
BEQ LAB_C5C9 ; if match save byte then continue crunching
LAB_C5EE
INY ; increment save index
STA BUF-5,Y ; save byte to output
INX ; increment buffer index
BNE LAB_C5E5 ; loop while <> 0, should never reach 0
; not found keyword this go
LAB_C5F5
LDX CHRGOT+1 ; restore BASIC execute pointer low byte
INC COUNT ; increment word index (next word)
; now find end of this word in the table
LAB_C5F9
INY ; increment table index
LDA RESLST-1,Y ; get table byte
BPL LAB_C5F9 ; loop if not end of word yet
LDA RESLST,Y ; get byte from keyword table
BNE LAB_C5B8 ; go test next word if not zero byte, end of table
; reached end of table with no match
LDA BUF,X ; restore byte from input buffer
BPL LAB_C5C7 ; branch always, all unmatched bytes in the buffer are
; $00 to $7F, go save byte in output and continue crunching
; reached [EOL]
LAB_C609
STA BUF-3,Y ; save [EOL]
DEC CHRGOT+2 ; decrement BASIC execute pointer high byte
LDA #$FF ; point to start of buffer - 1
STA CHRGOT+1 ; set BASIC execute pointer low byte
RTS
;***********************************************************************************;
;
; search BASIC for temporary integer line number
FINLIN
LDA TXTTAB ; get start of memory low byte
LDX TXTTAB+1 ; get start of memory high byte
; search BASIC for temp integer line number from .A.X
; returns carry set if found
LAB_C617
LDY #$01 ; set index to next line pointer high byte
STA TMPPTR ; save low byte as current
STX TMPPTR+1 ; save high byte as current
LDA (TMPPTR),Y ; get next line pointer high byte from address
BEQ LAB_C640 ; pointer was zero so done, exit
INY ; increment index ...
INY ; ... to line # high byte
LDA LINNUM+1 ; get temporary integer high byte
CMP (TMPPTR),Y ; compare with line # high byte
BCC LAB_C641 ; exit if temp < this line, target line passed
BEQ LAB_C62E ; go check low byte if =
DEY ; else decrement index
BNE LAB_C637 ; branch always
LAB_C62E
LDA LINNUM ; get temporary integer low byte
DEY ; decrement index to line # low byte
CMP (TMPPTR),Y ; compare with line # low byte
BCC LAB_C641 ; exit if temp < this line, target line passed
BEQ LAB_C641 ; exit if temp = (found line#)
; not quite there yet
LAB_C637
DEY ; decrement index to next line pointer high byte
LDA (TMPPTR),Y ; get next line pointer high byte
TAX ; copy to .X
DEY ; decrement index to next line pointer low byte
LDA (TMPPTR),Y ; get next line pointer low byte
BCS LAB_C617 ; go search for line # in temporary integer
; from .A.X, carry always set
LAB_C640
CLC ; clear found flag
LAB_C641
RTS
;***********************************************************************************;
;
; perform NEW
NEW
BNE LAB_C641 ; exit if following byte to allow syntax error
LAB_C644
LDA #$00 ; clear .A
TAY ; clear index
STA (TXTTAB),Y ; clear pointer to next line low byte
INY ; increment index
STA (TXTTAB),Y ; clear pointer to next line high byte, erase program
LDA TXTTAB ; get start of memory low byte
CLC ; clear carry for add
ADC #$02 ; add null program length
STA VARTAB ; set start of variables low byte
LDA TXTTAB+1 ; get start of memory high byte
ADC #$00 ; add carry
STA VARTAB+1 ; set start of variables high byte
; reset execute pointer and do CLR
LAB_C659
JSR STXTPT ; set BASIC execute pointer to start of memory - 1
LDA #$00 ; set Zb for CLR entry
;***********************************************************************************;
;
; perform CLR
CLR
BNE LAB_C68D ; exit if following byte to allow syntax error
LAB_C660
JSR CLALL ; close all channels and files
LAB_C663
LDA MEMSIZ ; get end of memory low byte
LDY MEMSIZ+1 ; get end of memory high byte
STA FRETOP ; set bottom of string space low byte, clear strings
STY FRETOP+1 ; set bottom of string space high byte
LDA VARTAB ; get start of variables low byte
LDY VARTAB+1 ; get start of variables high byte
STA ARYTAB ; set end of variables low byte, clear variables
STY ARYTAB+1 ; set end of variables high byte
STA STREND ; set end of arrays low byte, clear arrays
STY STREND+1 ; set end of arrays high byte
;***********************************************************************************;
;
; do RESTORE and clear the stack
LAB_C677
JSR RESTORE ; perform RESTORE
; flush BASIC stack and clear the continue pointer
LAB_C67A
LDX #TEMPST ; get descriptor stack start
STX TEMPPT ; set descriptor stack pointer
PLA ; pull return address low byte
TAY ; copy it
PLA ; pull return address high byte
LDX #$FA ; set cleared stack pointer
TXS ; set stack
PHA ; push return address high byte
TYA ; restore return address low byte
PHA ; push return address low byte
LDA #$00 ; clear .A
STA OLDTXT+1 ; clear continue pointer high byte
STA SUBFLG ; clear subscript/FNx flag
LAB_C68D
RTS
;***********************************************************************************;
;
; set BASIC execute pointer to start of memory - 1
STXTPT
CLC ; clear carry for add
LDA TXTTAB ; get start of memory low byte
ADC #$FF ; add -1 low byte
STA CHRGOT+1 ; set BASIC execute pointer low byte
LDA TXTTAB+1 ; get start of memory high byte
ADC #$FF ; add -1 high byte
STA CHRGOT+2 ; save BASIC execute pointer high byte
RTS
;***********************************************************************************;
;
; perform LIST
LIST
BCC LAB_C6A4 ; branch if next character not token (LIST n...)
BEQ LAB_C6A4 ; branch if next character [NULL] (LIST)
CMP #TK_MINUS ; compare with token for "-"
BNE LAB_C68D ; exit if not - (LIST -m)
; LIST [[n][-m]]
; this bit sets the n, if present, as the start and end
LAB_C6A4
JSR DECBIN ; get fixed-point number into temporary integer
JSR FINLIN ; search BASIC for temporary integer line number
JSR CHRGOT ; scan memory
BEQ LAB_C6BB ; branch if no more chrs
; this bit checks the - is present
CMP #TK_MINUS ; compare with token for "-"
BNE LAB_C641 ; return if not "-" (will be SN error)
; LIST [n]-m
; the - was there so set m as the end value
JSR CHRGET ; increment and scan memory
JSR DECBIN ; get fixed-point number into temporary integer
BNE LAB_C641 ; exit if not ok
LAB_C6BB
PLA ; dump return address low byte, exit via warm start
PLA ; dump return address high byte
LDA LINNUM ; get temporary integer low byte
ORA LINNUM+1 ; OR temporary integer high byte
BNE LAB_C6C9 ; branch if start set
LDA #$FF ; set for -1
STA LINNUM ; set temporary integer low byte
STA LINNUM+1 ; set temporary integer high byte
LAB_C6C9
LDY #$01 ; set index for line
STY GARBFL ; clear open quote flag
LDA (TMPPTR),Y ; get next line pointer high byte
BEQ LAB_C714 ; if null all done so exit
JSR TSTSTOP ; do STOP check vector
JSR LAB_CAD7 ; print CR/LF
INY ; increment index for line
LDA (TMPPTR),Y ; get line number low byte
TAX ; copy to .X
INY ; increment index
LDA (TMPPTR),Y ; get line number high byte
CMP LINNUM+1 ; compare with temporary integer high byte
BNE LAB_C6E6 ; branch if no high byte match
CPX LINNUM ; compare with temporary integer low byte
BEQ LAB_C6E8 ; branch if = last line to do, < will pass next branch
LAB_C6E6 ; else ...
BCS LAB_C714 ; if greater all done so exit
LAB_C6E8
STY FORPNT ; save index for line
JSR PRTFIX ; print .X.A as unsigned integer
LDA #' ' ; space is the next character
LAB_C6EF
LDY FORPNT ; get index for line
AND #$7F ; mask top out bit of character
LAB_C6F3
JSR LAB_CB47 ; go print the character
CMP #$22 ; was it " character
BNE LAB_C700 ; if not skip the quote handle
; we are either entering or leaving a pair of quotes
LDA GARBFL ; get open quote flag
EOR #$FF ; toggle it
STA GARBFL ; save it back
LAB_C700
INY ; increment index
BEQ LAB_C714 ; line too long so just bail out and do a warm start
LDA (TMPPTR),Y ; get next byte
BNE LAB_C717 ; if not [EOL] (go print character)
; was [EOL]
TAY ; else clear index
LDA (TMPPTR),Y ; get next line pointer low byte
TAX ; copy to .X
INY ; increment index
LDA (TMPPTR),Y ; get next line pointer high byte
STX TMPPTR ; set pointer to line low byte
STA TMPPTR+1 ; set pointer to line high byte
BNE LAB_C6C9 ; go do next line if not [EOT]
; else ...
LAB_C714
JMP READY ; do warm start
;***********************************************************************************;
;
LAB_C717
JMP (IQPLOP) ; do uncrunch BASIC tokens
;***********************************************************************************;
;
; uncrunch BASIC tokens, the uncrunch BASIC tokens vector is initialised to point here
QPLOP
BPL LAB_C6F3 ; just go print it if not token byte
; else was token byte so uncrunch it
CMP #TK_PI ; compare with the token for PI. in this case the token
; is the same as the PI character so it just needs printing
BEQ LAB_C6F3 ; just print it if so
BIT GARBFL ; test the open quote flag
BMI LAB_C6F3 ; just go print character if open quote set
SEC ; else set carry for subtract
SBC #$7F ; reduce token range to 1 to whatever
TAX ; copy token # to .X
STY FORPNT ; save index for line
LDY #$FF ; start from -1, adjust for pre increment
LAB_C72C
DEX ; decrement token #
BEQ LAB_C737 ; if now found go do printing
LAB_C72F
INY ; else increment index
LDA RESLST,Y ; get byte from keyword table
BPL LAB_C72F ; loop until keyword end marker
BMI LAB_C72C ; go test if this is required keyword, branch always
; found keyword, it's the next one
LAB_C737
INY ; increment keyword table index
LDA RESLST,Y ; get byte from table
BMI LAB_C6EF ; go restore index, mask byte and print if
; byte was end marker
JSR LAB_CB47 ; else go print the character
BNE LAB_C737 ; go get next character, branch always
;***********************************************************************************;
;
; perform FOR
FOR
LDA #$80 ; set FNx
STA SUBFLG ; set subscript/FNx flag
JSR LET ; perform LET
JSR SCNSTK ; search the stack for FOR or GOSUB activity
BNE LAB_C753 ; branch if FOR, this variable, not found
; FOR, this variable, was found so first we dump the old one
TXA ; copy index
ADC #$0F ; add FOR structure size-2
TAX ; copy to index
TXS ; set stack (dump FOR structure (-2 bytes))
LAB_C753
PLA ; pull return address
PLA ; pull return address
LDA #$09 ; we need 18d bytes !
JSR STKSPC ; check room on stack for 2*.A bytes
JSR FIND2 ; scan for next BASIC statement ([:] or [EOL])
CLC ; clear carry for add
TYA ; copy index to .A
ADC CHRGOT+1 ; add BASIC execute pointer low byte
PHA ; push onto stack
LDA CHRGOT+2 ; get BASIC execute pointer high byte
ADC #$00 ; add carry
PHA ; push onto stack
LDA CURLIN+1 ; get current line number high byte
PHA ; push onto stack
LDA CURLIN ; get current line number low byte
PHA ; push onto stack
LDA #TK_TO ; set TO token
JSR SYNCHR ; scan for CHR$(.A), else do syntax error then warm start
JSR LAB_CD8D ; check if source is numeric, else do type mismatch
JSR TYPCHK ; evaluate expression and check is numeric, else do
; type mismatch
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
ORA #$7F ; set all non sign bits
AND FAC1+FAC_MANT ; and FAC1 mantissa 1
STA FAC1+FAC_MANT ; save FAC1 mantissa 1
LDA #<LAB_C78B ; set return address low byte
LDY #>LAB_C78B ; set return address high byte
STA INDEX ; save return address low byte
STY INDEX+1 ; save return address high byte
JMP LAB_CE43 ; round FAC1 and put on stack, returns to next instruction
LAB_C78B
LDA #<FPC1 ; set 1 pointer low address, default step size
LDY #>FPC1 ; set 1 pointer high address
JSR LODFAC ; unpack memory (.A.Y) into FAC1
JSR CHRGOT ; scan memory
CMP #TK_STEP ; compare with STEP token
BNE LAB_C79F ; branch if not STEP
; was step so ...
JSR CHRGET ; increment and scan memory
JSR TYPCHK ; evaluate expression and check is numeric, else do
; type mismatch
LAB_C79F
JSR SGNFAC ; get FAC1 sign, return .A = $FF -ve, .A = $01 +ve
JSR LAB_CE38 ; push sign, round FAC1 and put on stack
LDA FORPNT+1 ; get FOR/NEXT variable pointer high byte
PHA ; push on stack
LDA FORPNT ; get FOR/NEXT variable pointer low byte
PHA ; push on stack
LDA #TK_FOR ; get FOR token
PHA ; push on stack
;***********************************************************************************;
;
; interpreter inner loop
NEWSTT
JSR TSTSTOP ; do STOP check vector
LDA CHRGOT+1 ; get BASIC execute pointer low byte
LDY CHRGOT+2 ; get BASIC execute pointer high byte
CPY #>BUF ; compare with input buffer high byte
NOP ; unused byte
BEQ LAB_C7BE ; if immediate mode skip the continue pointer save
STA OLDTXT ; save the continue pointer low byte
STY OLDTXT+1 ; save the continue pointer high byte
LAB_C7BE
LDY #$00 ; clear the index
LDA (CHRGOT+1),Y ; get BASIC byte
BNE LAB_C807 ; if not [EOL] go test for ":"
LDY #$02 ; else set the index
LDA (CHRGOT+1),Y ; get next line pointer high byte
CLC ; clear carry for no "BREAK" message
BNE LAB_C7CE ; branch if not end of program
JMP LAB_C84B ; else go to immediate mode,was immediate or [EOT] marker
LAB_C7CE
INY ; increment index
LDA (CHRGOT+1),Y ; get line number low byte
STA CURLIN ; save current line number low byte
INY ; increment index
LDA (CHRGOT+1),Y ; get line # high byte
STA CURLIN+1 ; save current line number high byte
TYA ; .A now = 4
ADC CHRGOT+1 ; add BASIC execute pointer low byte, now points to code
STA CHRGOT+1 ; save BASIC execute pointer low byte
BCC LAB_C7E1 ; if no overflow skip the high byte increment
INC CHRGOT+2 ; else increment BASIC execute pointer high byte
LAB_C7E1
JMP (IGONE) ; do start new BASIC code
;***********************************************************************************;
;
; start new BASIC code, the start new BASIC code vector is initialised to point here
GONE
JSR CHRGET ; increment and scan memory
JSR LAB_C7ED ; go interpret BASIC code from BASIC execute pointer
JMP NEWSTT ; loop
;***********************************************************************************;
;
; go interpret BASIC code from BASIC execute pointer
LAB_C7ED
BEQ LAB_C82B ; if the first byte is null just exit
LAB_C7EF
SBC #$80 ; normalise the token
BCC LAB_C804 ; if wasn't token go do LET
CMP #TK_TAB-$80 ; compare with token for TAB(-$80
BCS LAB_C80E ; branch if >= TAB(
ASL ; *2 bytes per vector
TAY ; copy to index
LDA STMDSP+1,Y ; get vector high byte
PHA ; push on stack
LDA STMDSP,Y ; get vector low byte
PHA ; push on stack
JMP CHRGET ; increment and scan memory and return. the return in
; this case calls the command code, the return from
; that will eventually return to the interpreter inner
; loop above
LAB_C804
JMP LET ; perform LET
; was not [EOL]
LAB_C807
CMP #':' ; compare with ":"
BEQ LAB_C7E1 ; if ":" go execute new code
; else ...
LAB_C80B
JMP LAB_CF08 ; do syntax error then warm start
; token was >= TAB(
LAB_C80E
CMP #TK_GO-$80 ; compare with token for GO
BNE LAB_C80B ; if not GO do syntax error then warm start
; else was GO
JSR CHRGET ; increment and scan memory
LDA #TK_TO ; set TO token
JSR SYNCHR ; scan for CHR$(.A), else do syntax error then warm start
JMP GOTO ; perform GOTO
;***********************************************************************************;
;
; perform RESTORE
RESTORE
SEC ; set carry for subtract
LDA TXTTAB ; get start of memory low byte
SBC #$01 ; -1
LDY TXTTAB+1 ; get start of memory high byte
BCS LAB_C827 ; if no rollunder skip the high byte decrement
DEY ; else decrement high byte
LAB_C827
STA DATPTR ; set DATA pointer low byte
STY DATPTR+1 ; set DATA pointer high byte
LAB_C82B
RTS
;***********************************************************************************;
;
; do STOP check vector
TSTSTOP
JSR STOP ; scan stop key
;***********************************************************************************;
;
; perform STOP
BSTOP
BCS LAB_C832 ; if carry set do BREAK instead of just END
;***********************************************************************************;
;
; perform END
END
CLC ; clear carry
LAB_C832
BNE LAB_C870 ; return if wasn't STOP
LDA CHRGOT+1 ; get BASIC execute pointer low byte
LDY CHRGOT+2 ; get BASIC execute pointer high byte
LDX CURLIN+1 ; get current line number high byte
INX ; increment it
BEQ LAB_C849 ; branch if was immediate mode
STA OLDTXT ; save continue pointer low byte
STY OLDTXT+1 ; save continue pointer high byte
LDA CURLIN ; get current line number low byte
LDY CURLIN+1 ; get current line number high byte
STA OLDLIN ; save break line number low byte
STY OLDLIN+1 ; save break line number high byte
LAB_C849
PLA ; dump return address low byte
PLA ; dump return address high byte
LAB_C84B
LDA #<CRLFBRK ; set [CR][LF]"BREAK" pointer low byte
LDY #>CRLFBRK ; set [CR][LF]"BREAK" pointer high byte
BCC LAB_C854 ; branch if was program end
JMP PRDY ; print string and do warm start
LAB_C854
JMP READY ; do warm start
;***********************************************************************************;
;
; perform CONT
CONT
BNE LAB_C870 ; exit if following byte to allow syntax error
LDX #ER_CANTCONT ; error code $1A, can't continue error
LDY OLDTXT+1 ; get continue pointer high byte
BNE LAB_C862 ; go do continue if we can
JMP ERROR ; else do error #.X then warm start
; we can continue so ...
LAB_C862
LDA OLDTXT ; get continue pointer low byte
STA CHRGOT+1 ; save BASIC execute pointer low byte
STY CHRGOT+2 ; save BASIC execute pointer high byte
LDA OLDLIN ; get break line low byte
LDY OLDLIN+1 ; get break line high byte
STA CURLIN ; set current line number low byte
STY CURLIN+1 ; set current line number high byte
LAB_C870
RTS
;***********************************************************************************;
;
; perform RUN
RUN
PHP ; save status
LDA #$00 ; no control or KERNAL messages
JSR SETMSG ; control KERNAL messages
PLP ; restore status
BNE LAB_C87D ; branch if RUN n
JMP LAB_C659 ; reset execution to start, clear variables, flush stack
; and return
LAB_C87D
JSR LAB_C660 ; go do CLR
JMP LAB_C897 ; get n and do GOTO n
;***********************************************************************************;
;
; perform GOSUB
GOSUB
LDA #$03 ; need 6 bytes for GOSUB
JSR STKSPC ; check room on stack for 2*.A bytes
LDA CHRGOT+2 ; get BASIC execute pointer high byte
PHA ; save it
LDA CHRGOT+1 ; get BASIC execute pointer low byte
PHA ; save it
LDA CURLIN+1 ; get current line number high byte
PHA ; save it
LDA CURLIN ; get current line number low byte
PHA ; save it
LDA #TK_GOSUB ; token for GOSUB
PHA ; save it
LAB_C897
JSR CHRGOT ; scan memory
JSR GOTO ; perform GOTO
JMP NEWSTT ; go do interpreter inner loop
;***********************************************************************************;
;
; perform GOTO
GOTO
JSR DECBIN ; get fixed-point number into temporary integer
JSR LAB_C909 ; scan for next BASIC line
SEC ; set carry for subtract
LDA CURLIN ; get current line number low byte
SBC LINNUM ; subtract temporary integer low byte
LDA CURLIN+1 ; get current line number high byte
SBC LINNUM+1 ; subtract temporary integer high byte
BCS LAB_C8BC ; if current line number >= temporary integer, go search
; from the start of memory
TYA ; else copy line index to .A
SEC ; set carry (+1)
ADC CHRGOT+1 ; add BASIC execute pointer low byte
LDX CHRGOT+2 ; get BASIC execute pointer high byte
BCC LAB_C8C0 ; if no overflow skip the high byte increment
INX ; increment high byte
BCS LAB_C8C0 ; go find the line, branch always
;***********************************************************************************;
;
; search for line number in temporary integer from start of memory pointer
LAB_C8BC
LDA TXTTAB ; get start of memory low byte
LDX TXTTAB+1 ; get start of memory high byte
; search for line # in temporary integer from (.A.X)
LAB_C8C0
JSR LAB_C617 ; search BASIC for temp integer line number from .A.X
BCC LAB_C8E3 ; if carry clear go do undefined statement error
; carry all ready set for subtract
LDA TMPPTR ; get pointer low byte
SBC #$01 ; -1
STA CHRGOT+1 ; save BASIC execute pointer low byte
LDA TMPPTR+1 ; get pointer high byte
SBC #$00 ; subtract carry
STA CHRGOT+2 ; save BASIC execute pointer high byte
LAB_C8D1
RTS
;***********************************************************************************;
;
; perform RETURN
RETURN
BNE LAB_C8D1 ; exit if following token to allow syntax error
LDA #$FF ; set byte so no match possible
STA FORPNT+1 ; save FOR/NEXT variable pointer high byte
JSR SCNSTK ; search the stack for FOR or GOSUB activity,
; get token off stack
TXS ; correct the stack
CMP #TK_GOSUB ; compare with GOSUB token
BEQ LAB_C8EB ; if matching GOSUB go continue RETURN
LDX #ER_RETWOGSB ; else error code $0C, return without gosub error
.byte $2C ; makes next line BIT $11A2
LAB_C8E3
LDX #ER_UNDSMNT ; error code $11, undefined statement error
JMP ERROR ; do error #.X then warm start
LAB_C8E8
JMP LAB_CF08 ; do syntax error then warm start
; was matching GOSUB token
LAB_C8EB
PLA ; dump token byte
PLA ; pull return line low byte
STA CURLIN ; save current line number low byte
PLA ; pull return line high byte
STA CURLIN+1 ; save current line number high byte
PLA ; pull return address low byte
STA CHRGOT+1 ; save BASIC execute pointer low byte
PLA ; pull return address high byte
STA CHRGOT+2 ; save BASIC execute pointer high byte
;***********************************************************************************;
;
; perform DATA
SKIPST
JSR FIND2 ; scan for next BASIC statement ([:] or [EOL])
; add .Y to the BASIC execute pointer
BUMPTP
TYA ; copy index to .A
CLC ; clear carry for add
ADC CHRGOT+1 ; add BASIC execute pointer low byte
STA CHRGOT+1 ; save BASIC execute pointer low byte
BCC LAB_C905 ; skip increment if no carry
INC CHRGOT+2 ; else increment BASIC execute pointer high byte
LAB_C905
RTS
;***********************************************************************************;
;
; scan for next BASIC statement ([:] or [EOL])
; returns .Y as index to [:] or [EOL]
FIND2
LDX #':' ; set look for character = ":"
.byte $2C ; makes next line BIT $00A2
; scan for next BASIC line
; returns .Y as index to [EOL]
LAB_C909
LDX #$00 ; set alternate search character = [EOL]
STX CHARAC ; store alternate search character
LDY #$00 ; set search character = [EOL]
STY ENDCHR ; save the search character
LAB_C911
LDA ENDCHR ; get search character
LDX CHARAC ; get alternate search character
STA CHARAC ; make search character = alternate search character
STX ENDCHR ; make alternate search character = search character
LAB_C919
LDA (CHRGOT+1),Y ; get BASIC byte
BEQ LAB_C905 ; exit if null [EOL]
CMP ENDCHR ; compare with search character
BEQ LAB_C905 ; exit if found
INY ; else increment index
CMP #$22 ; compare current character with open quote
BNE LAB_C919 ; if found go swap search character for alternate search
; character
BEQ LAB_C911 ; loop for next character, branch always
;***********************************************************************************;
;
; perform IF
IF
JSR FRMEVL ; evaluate expression
JSR CHRGOT ; scan memory
CMP #TK_GOTO ; compare with GOTO token
BEQ LAB_C937 ; if it was the token for GOTO go do IF ... GOTO
; wasn't IF ... GOTO so must be IF ... THEN
LDA #TK_THEN ; $A7 = "THEN" token
JSR SYNCHR ; scan for CHR$(.A), else do syntax error then warm start
LAB_C937
LDA FAC1+FAC_EXPT ; get FAC1 exponent
BNE LAB_C940 ; if result was non zero continue execution
; else REM the rest of the line
;***********************************************************************************;
;
; perform REM
REM
JSR LAB_C909 ; scan for next BASIC line
BEQ BUMPTP ; add .Y to the BASIC execute pointer and return, branch
; always
;***********************************************************************************;
;
; IF continued .. result was non zero so do rest of line
LAB_C940
JSR CHRGOT ; scan memory
BCS LAB_C948 ; if not numeric character, is variable or keyword
JMP GOTO ; else perform GOTO n
; is variable or keyword
LAB_C948
JMP LAB_C7ED ; interpret BASIC code from BASIC execute pointer
;***********************************************************************************;
;
; perform ON
ON
JSR LAB_D79E ; get byte parameter
PHA ; push next character
CMP #TK_GOSUB ; compare with GOSUB token
BEQ LAB_C957 ; if GOSUB go see if it should be executed
LAB_C953
CMP #TK_GOTO ; compare with GOTO token
BNE LAB_C8E8 ; if not GOTO do syntax error then warm start
; next character was GOTO or GOSUB, see if it should be executed
LAB_C957
DEC FAC1+4 ; decrement the byte value
BNE LAB_C95F ; if not zero go see if another line number exists
PLA ; pull keyword token
JMP LAB_C7EF ; go execute it
LAB_C95F
JSR CHRGET ; increment and scan memory
JSR DECBIN ; get fixed-point number into temporary integer
; skip this n
CMP #',' ; compare next character with ","
BEQ LAB_C957 ; loop if ","
PLA ; else pull keyword token, ran out of options
LAB_C96A
RTS
;***********************************************************************************;
;
; get fixed-point number into temporary integer
DECBIN
LDX #$00 ; clear .X
STX LINNUM ; clear temporary integer low byte
STX LINNUM+1 ; clear temporary integer high byte
LAB_C971
BCS LAB_C96A ; return if carry set, end of scan, character was not 0-9
SBC #$2F ; subtract $30, $2F+carry, from byte
STA CHARAC ; store #
LDA LINNUM+1 ; get temporary integer high byte
STA INDEX ; save it for now
CMP #$19 ; compare with $19
BCS LAB_C953 ; branch if >= this makes the maximum line number 63999
; because the next bit does $1900 * $0A = $FA00 = 64000
; decimal. the branch target is really the SYNTAX error
; at LAB_C8E8 but that is too far so an intermediate
; compare and branch to that location is used. the problem
; with this is that line number that gives a partial result
; from $8900 to $89FF, 35072x to 35327x, will pass the new
; target compare and will try to execute the remainder of
; the ON n GOTO/GOSUB. a solution to this is to copy the
; byte in .A before the branch to .X and then branch to
; LAB_C955 skipping the second compare
LDA LINNUM ; get temporary integer low byte
ASL ; *2 low byte
ROL INDEX ; *2 high byte
ASL ; *2 low byte
ROL INDEX ; *2 high byte (*4)
ADC LINNUM ; + low byte (*5)
STA LINNUM ; save it
LDA INDEX ; get high byte temp
ADC LINNUM+1 ; + high byte (*5)
STA LINNUM+1 ; save it
ASL LINNUM ; *2 low byte (*10d)
ROL LINNUM+1 ; *2 high byte (*10d)
LDA LINNUM ; get low byte
ADC CHARAC ; add #
STA LINNUM ; save low byte
BCC LAB_C99F ; if no overflow skip high byte increment
INC LINNUM+1 ; else increment high byte
LAB_C99F
JSR CHRGET ; increment and scan memory
JMP LAB_C971 ; loop for next character
;***********************************************************************************;
;
; perform LET
LET
JSR EVLVAR ; get variable address
STA FORPNT ; save variable address low byte
STY FORPNT+1 ; save variable address high byte
LDA #TK_EQUAL ; $B2 is "=" token
JSR SYNCHR ; scan for CHR$(.A), else do syntax error then warm start
LDA INTFLG ; get data type flag, $80 = integer, $00 = float
PHA ; push data type flag
LDA VALTYP ; get data type flag, $FF = string, $00 = numeric
PHA ; push data type flag
JSR FRMEVL ; evaluate expression
PLA ; pop data type flag
ROL ; string bit into carry
JSR LAB_CD90 ; do type match check
BNE LAB_C9D9 ; if string go assign a string value
PLA ; pop integer/float data type flag
; assign value to numeric variable
LET2
BPL LAB_C9D6 ; if float go assign a floating value
; expression is numeric integer
JSR ROUND ; round FAC1
JSR MAKINT ; evaluate integer expression, no sign check
LDY #$00 ; clear index
LDA FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
STA (FORPNT),Y ; save as integer variable low byte
INY ; increment index
LDA FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
STA (FORPNT),Y ; save as integer variable high byte
RTS
LAB_C9D6
JMP FACTFP ; pack FAC1 into variable pointer and return
; assign value to string variable
LAB_C9D9
PLA ; dump integer/float data type flag
LET5
LDY FORPNT+1 ; get variable pointer high byte
CPY #>NULLVAR ; was it TI$ pointer
BNE LET9 ; branch if not
; else it's TI$ = <expr$>
JSR LAB_D6A6 ; pop string off descriptor stack, or from top of string
; space returns with .A = length, .X = pointer low byte,
; .Y = pointer high byte
CMP #$06 ; compare length with 6
BNE LAB_CA24 ; if length not 6 do illegal quantity error then warm start
LDY #$00 ; clear index
STY FAC1+FAC_EXPT ; clear FAC1 exponent
STY FAC1+FAC_SIGN ; clear FAC1 sign (b7)
LAB_C9ED
STY FBUFPT ; save index
JSR LAB_CA1D ; check and evaluate numeric digit
JSR MULTEN ; multiply FAC1 by 10
INC FBUFPT ; increment index
LDY FBUFPT ; restore index
JSR LAB_CA1D ; check and evaluate numeric digit
JSR RFTOA ; round and copy FAC1 to FAC2
TAX ; copy FAC1 exponent
BEQ LAB_CA07 ; branch if FAC1 zero
INX ; increment index, * 2
TXA ; copy back to .A
JSR LAB_DAED ; FAC1 = (FAC1 + (FAC2 * 2)) * 2 = FAC1 * 6
LAB_CA07
LDY FBUFPT ; get index
INY ; increment index
CPY #$06 ; compare index with 6
BNE LAB_C9ED ; loop if not 6
JSR MULTEN ; multiply FAC1 by 10
JSR FPINT ; convert FAC1 floating to fixed
LDX FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
LDY FAC1+FAC_MANT+1 ; get FAC1 mantissa 2
LDA FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
JMP SETTIM ; set real time clock and return
; check and evaluate numeric digit
LAB_CA1D
LDA (INDEX),Y ; get byte from string
JSR CHRSPC ; clear Cb if numeric. this call should be to CHRSPC+4
; as the code from CHRSPC first compares the byte with
; [SPACE] and does a BASIC increment and get if it is
BCC LAB_CA27 ; branch if numeric
LAB_CA24
JMP ILQUAN ; do illegal quantity error then warm start
LAB_CA27
SBC #$2F ; subtract $2F + carry to convert ASCII to binary
JMP ASCI8 ; evaluate new ASCII digit and return
; assign value to string variable, but not TI$
LET9
LDY #$02 ; index to string pointer high byte
LDA (FAC1+3),Y ; get string pointer high byte
CMP FRETOP+1 ; compare with bottom of string space high byte
BCC LAB_CA4B ; branch if string pointer high byte is less than bottom
; of string space high byte
BNE LAB_CA3D ; branch if string pointer high byte is greater than
; bottom of string space high byte
; else high bytes were equal
DEY ; decrement index to string pointer low byte
LDA (FAC1+3),Y ; get string pointer low byte
CMP FRETOP ; compare with bottom of string space low byte
BCC LAB_CA4B ; branch if string pointer low byte is less than bottom
; of string space low byte
LAB_CA3D
LDY FAC1+4 ; get descriptor pointer high byte
CPY VARTAB+1 ; compare with start of variables high byte
BCC LAB_CA4B ; branch if less, is on string stack
BNE LAB_CA52 ; if greater make space and copy string
; else high bytes were equal
LDA FAC1+3 ; get descriptor pointer low byte
CMP VARTAB ; compare with start of variables low byte
BCS LAB_CA52 ; if greater or equal make space and copy string
LAB_CA4B
LDA FAC1+3 ; get descriptor pointer low byte
LDY FAC1+4 ; get descriptor pointer high byte
JMP LAB_CA68 ; go copy descriptor to variable
LAB_CA52
LDY #$00 ; clear index
LDA (FAC1+3),Y ; get string length
JSR ALC1 ; copy descriptor pointer and make string space .A bytes long
LDA DSCPTN ; copy old descriptor pointer low byte
LDY DSCPTN+1 ; copy old descriptor pointer high byte
STA ARISGN ; save old descriptor pointer low byte
STY FACOV ; save old descriptor pointer high byte
JSR XFERSTR ; copy string from descriptor to utility pointer
LDA #<FAC1 ; get descriptor pointer low byte
LDY #>FAC1 ; get descriptor pointer high byte
LAB_CA68
STA DSCPTN ; save descriptor pointer low byte
STY DSCPTN+1 ; save descriptor pointer high byte
JSR DELTSD ; clean descriptor stack, .Y.A = pointer
LDY #$00 ; clear index
LDA (DSCPTN),Y ; get string length from new descriptor
STA (FORPNT),Y ; copy string length to variable
INY ; increment index
LDA (DSCPTN),Y ; get string pointer low byte from new descriptor
STA (FORPNT),Y ; copy string pointer low byte to variable
INY ; increment index
LDA (DSCPTN),Y ; get string pointer high byte from new descriptor
STA (FORPNT),Y ; copy string pointer high byte to variable
RTS
;***********************************************************************************;
;
; perform PRINT#
PRINTN
JSR CMD ; perform CMD
JMP LAB_CBB5 ; close input and output channels and return
;***********************************************************************************;
;
; perform CMD
CMD
JSR LAB_D79E ; get byte parameter
BEQ LAB_CA90 ; branch if following byte is ":" or [EOT]
LDA #',' ; set ","
JSR SYNCHR ; scan for CHR$(.A), else do syntax error then warm start
LAB_CA90
PHP ; save status
STX CHANNL ; set current I/O channel
JSR LAB_E115 ; open channel for output with error check
PLP ; restore status
JMP PRINT ; perform PRINT
;***********************************************************************************;
;
; print string, scan memory and continue PRINT
PRT1
JSR LAB_CB21 ; print string from utility pointer
; scan memory and continue PRINT
LAB_CA9D
JSR CHRGOT ; scan memory
;***********************************************************************************;
;
; perform PRINT
PRINT
BEQ LAB_CAD7 ; if nothing following just print CR/LF
LAB_CAA2
BEQ LAB_CAE7 ; if nothing following exit, end of PRINT branch
CMP #TK_TAB ; compare with token for TAB(
BEQ PRT7 ; if TAB( go handle it
CMP #TK_SPC ; compare with token for SPC(
CLC ; flag SPC(
BEQ PRT7 ; if SPC( go handle it
CMP #',' ; compare with ","
BEQ PRT6 ; if "," go skip to the next TAB position
CMP #$3B ; compare with ";"
BEQ LAB_CB13 ; if ";" go continue the print loop
JSR FRMEVL ; evaluate expression
BIT VALTYP ; test data type flag, $FF = string, $00 = numeric
BMI PRT1 ; if string go print string, scan memory and continue PRINT
JSR FLTASC ; convert FAC1 to ASCII string result in (.A.Y)
JSR MAKSTR ; print " terminated string to utility pointer
JSR LAB_CB21 ; print string from utility pointer
JSR PRTOS ; print [SPACE] or [CURSOR RIGHT]
BNE LAB_CA9D ; go scan memory and continue PRINT, branch always
;***********************************************************************************;
;
; set .X.Y to BUF - 1 and print [CR]
LAB_CACA
LDA #$00 ; clear .A
STA BUF,X ; clear first byte of input buffer
LDX #<BUF-1 ; BUF - 1 low byte
LDY #>BUF-1 ; BUF - 1 high byte
LDA CHANNL ; get current I/O channel
BNE LAB_CAE7 ; exit if not default channel
;***********************************************************************************;
;
; print CR/LF
LAB_CAD7
LDA #$0D ; set [CR]
JSR LAB_CB47 ; print the character
BIT CHANNL ; test current I/O channel
BPL LAB_CAE5 ; if the AutoLF bit is not set skip the [LF]
LDA #$0A ; set [LF]
JSR LAB_CB47 ; print the character
;***********************************************************************************;
;
; invert .A
LAB_CAE5
EOR #$FF ; ones' complement .A
LAB_CAE7
RTS
;***********************************************************************************;
;
; continuing PRINT, the character was ","
PRT6
SEC ; set Cb for read cursor position
JSR PLOT ; read/set X,Y cursor position
TYA ; copy cursor .Y
SEC ; set carry for subtract
LAB_CAEE
SBC #$0B ; subtract one TAB length
BCS LAB_CAEE ; loop if result was +ve
EOR #$FF ; complement it
ADC #$01 ; +1, two's complement
BNE LAB_CB0E ; print .A spaces, branch always, result is never $00
;***********************************************************************************;
;
; handle TAB( or SPC(
PRT7
PHP ; save TAB( or SPC( status
SEC ; set Cb for read cursor position
JSR PLOT ; read/set X,Y cursor position
STY TRMPOS ; save current cursor position
JSR GETBYT ; scan and get byte parameter
CMP #$29 ; compare with ")"
BNE LAB_CB5F ; if not ")" do syntax error
PLP ; restore TAB( or SPC( status
BCC LAB_CB0F ; branch if was SPC(
; else was TAB(
TXA ; copy TAB() byte to .A
SBC TRMPOS ; subtract current cursor position
BCC LAB_CB13 ; go loop for next if already past required position
LAB_CB0E
TAX ; copy [SPACE] count to .X
LAB_CB0F
INX ; increment count
LAB_CB10
DEX ; decrement count
BNE LAB_CB19 ; branch if count was not zero
; was ";" or [SPACES] printed
LAB_CB13
JSR CHRGET ; increment and scan memory
JMP LAB_CAA2 ; continue print loop
LAB_CB19
JSR PRTOS ; print [SPACE] or [CURSOR RIGHT]
BNE LAB_CB10 ; loop, branch always
;***********************************************************************************;
;
; print null terminated string
PRTSTR
JSR MAKSTR ; print " terminated string to utility pointer
; print string from utility pointer
LAB_CB21
JSR LAB_D6A6 ; pop string off descriptor stack, or from top of string
; space returns with .A = length, .X = pointer low byte,
; .Y = pointer high byte
TAX ; copy length
LDY #$00 ; clear index
INX ; increment length, for pre decrement loop
LAB_CB28
DEX ; decrement length
BEQ LAB_CAE7 ; exit if done
LDA (INDEX),Y ; get byte from string
JSR LAB_CB47 ; print the character
INY ; increment index
CMP #$0D ; compare byte with [CR]
BNE LAB_CB28 ; loop if not [CR]
JSR LAB_CAE5 ; toggle .A, EOR #$FF. what is the point of this ??
JMP LAB_CB28 ; loop
;***********************************************************************************;
;
; print [SPACE] or [CURSOR RIGHT]
PRTOS
LDA CHANNL ; get current I/O channel
BEQ LAB_CB42 ; if default channel go output [CURSOR RIGHT]
LDA #' ' ; else output [SPACE]
.byte $2C ; makes next line BIT $1DA9
LAB_CB42
LDA #$1D ; set [CURSOR RIGHT]
.byte $2C ; makes next line BIT $3FA9
;***********************************************************************************;
;
; print "?"
LAB_CB45
LDA #'?' ; set "?"
;***********************************************************************************;
;
; print a character
LAB_CB47
JSR LAB_E109 ; output character to channel with error check
AND #$FF ; set the flags on .A
RTS
;***********************************************************************************;
;
; bad input routine
IGRERR
LDA INPFLG ; get INPUT mode flag, $00 = INPUT, $40 = GET, $98 = READ
BEQ LAB_CB62 ; branch if INPUT
BMI LAB_CB57 ; branch if READ
; else was GET
LDY #$FF ; set current line high byte to -1, indicate immediate mode
BNE LAB_CB5B ; branch always
LAB_CB57
LDA DATLIN ; get current DATA line number low byte
LDY DATLIN+1 ; get current DATA line number high byte
LAB_CB5B
STA CURLIN ; set current line number low byte
STY CURLIN+1 ; set current line number high byte
LAB_CB5F
JMP LAB_CF08 ; do syntax error then warm start
; was INPUT
LAB_CB62
LDA CHANNL ; get current I/O channel
BEQ LAB_CB6B ; if default channel go do "?REDO FROM START" message
LDX #ER_FDATA ; else error $18, file data error
JMP ERROR ; do error #.X then warm start
LAB_CB6B
LDA #<LAB_CD0C ; set "?REDO FROM START" pointer low byte
LDY #>LAB_CD0C ; set "?REDO FROM START" pointer high byte
JSR PRTSTR ; print null terminated string
LDA OLDTXT ; get continue pointer low byte
LDY OLDTXT+1 ; get continue pointer high byte
STA CHRGOT+1 ; save BASIC execute pointer low byte
STY CHRGOT+2 ; save BASIC execute pointer high byte
RTS
;***********************************************************************************;
;
; perform GET
GET
JSR NODIRM ; check not Direct, back here if ok
CMP #'#' ; compare with "#"
BNE LAB_CB92 ; branch if not GET#
JSR CHRGET ; increment and scan memory
JSR LAB_D79E ; get byte parameter
LDA #',' ; set ","
JSR SYNCHR ; scan for CHR$(.A), else do syntax error then warm start
STX CHANNL ; set current I/O channel
JSR LAB_E11B ; open channel for input with error check
LAB_CB92
LDX #<BUF+1 ; set BUF+1 pointer low byte
LDY #>BUF+1 ; set BUF+1 pointer high byte
LDA #$00 ; clear .A
STA BUF+1 ; ensure null terminator
LDA #$40 ; input mode = GET
JSR LAB_CC0F ; perform GET part of READ
LDX CHANNL ; get current I/O channel
BNE LAB_CBB7 ; if not default channel go do channel close and return
RTS
;***********************************************************************************;
;
; perform INPUT#
INPUTN
JSR LAB_D79E ; get byte parameter
LDA #',' ; set ","
JSR SYNCHR ; scan for CHR$(.A), else do syntax error then warm start
STX CHANNL ; set current I/O channel
JSR LAB_E11B ; open channel for input with error check
JSR LAB_CBCE ; perform INPUT with no prompt string
; close input and output channels
LAB_CBB5
LDA CHANNL ; get current I/O channel
LAB_CBB7
JSR CLRCHN ; close input and output channels
LDX #$00 ; clear .X
STX CHANNL ; clear current I/O channel, flag default
RTS
;***********************************************************************************;
;
; perform INPUT
INPUT
CMP #$22 ; compare next byte with open quote
BNE LAB_CBCE ; if no prompt string just do INPUT
JSR LAB_CEBD ; print "..." string
LDA #$3B ; load .A with ";"
JSR SYNCHR ; scan for CHR$(.A), else do syntax error then warm start
JSR LAB_CB21 ; print string from utility pointer
; done with prompt, now get data
LAB_CBCE
JSR NODIRM ; check not Direct, back here if ok
LDA #',' ; set ","
STA BUF-1 ; save to start of buffer - 1
LAB_CBD6
JSR LAB_CBF9 ; print "? " and get BASIC input
LDA CHANNL ; get current I/O channel
BEQ LAB_CBEA ; branch if default I/O channel
JSR READST ; read I/O status word
AND #$02 ; mask no DSR/timeout
BEQ LAB_CBEA ; branch if not error
JSR LAB_CBB5 ; close input and output channels
JMP SKIPST ; perform DATA
LAB_CBEA
LDA BUF ; get first byte in input buffer
BNE LAB_CC0D ; branch if not null
; else ..
LDA CHANNL ; get current I/O channel
BNE LAB_CBD6 ; if not default channel go get BASIC input
JSR FIND2 ; scan for next BASIC statement ([:] or [EOL])
JMP BUMPTP ; add .Y to the BASIC execute pointer and return
;***********************************************************************************;
;
; print "? " and get BASIC input
LAB_CBF9
LDA CHANNL ; get current I/O channel
BNE LAB_CC03 ; skip "?" prompt if not default channel
JSR LAB_CB45 ; print "?"
JSR PRTOS ; print [SPACE] or [CURSOR RIGHT]
LAB_CC03
JMP GETLIN ; call for BASIC input and return
;***********************************************************************************;
;
; perform READ
READ
LDX DATPTR ; get DATA pointer low byte
LDY DATPTR+1 ; get DATA pointer high byte
LDA #$98 ; set input mode = READ
.byte $2C ; makes next line BIT $00A9
LAB_CC0D
LDA #$00 ; set input mode = INPUT
;***********************************************************************************;
;
; perform GET
LAB_CC0F
STA INPFLG ; set input mode flag, $00 = INPUT, $40 = GET, $98 = READ
STX INPPTR ; save READ pointer low byte
STY INPPTR+1 ; save READ pointer high byte
; READ, GET or INPUT next variable from list
LAB_CC15
JSR EVLVAR ; get variable address
STA FORPNT ; save address low byte
STY FORPNT+1 ; save address high byte
LDA CHRGOT+1 ; get BASIC execute pointer low byte
LDY CHRGOT+2 ; get BASIC execute pointer high byte
STA OPPTR ; save BASIC execute pointer low byte
STY OPPTR+1 ; save BASIC execute pointer high byte
LDX INPPTR ; get READ pointer low byte
LDY INPPTR+1 ; get READ pointer high byte
STX CHRGOT+1 ; save as BASIC execute pointer low byte
STY CHRGOT+2 ; save as BASIC execute pointer high byte
JSR CHRGOT ; scan memory
BNE LAB_CC51 ; branch if not null
; pointer was to null entry
BIT INPFLG ; test input mode flag, $00 = INPUT, $40 = GET, $98 = READ
BVC LAB_CC41 ; branch if not GET
; else was GET
JSR LAB_E121 ; get character from input device with error check
STA BUF ; save to buffer
LDX #<BUF-1 ; set BUF-1 pointer low byte
LDY #>BUF-1 ; set BUF-1 pointer high byte
BNE LAB_CC4D ; go interpret single character
LAB_CC41
BMI LAB_CCB8 ; if READ go get some DATA
; else it was INPUT
LDA CHANNL ; get current I/O channel
BNE LAB_CC4A ; skip "?" prompt if not default channel
JSR LAB_CB45 ; print "?"
LAB_CC4A
JSR LAB_CBF9 ; print "? " and get BASIC input
LAB_CC4D
STX CHRGOT+1 ; save BASIC execute pointer low byte
STY CHRGOT+2 ; save BASIC execute pointer high byte
LAB_CC51
JSR CHRGET ; increment and scan memory, execute pointer now points to
; start of next data or null terminator
BIT VALTYP ; test data type flag, $FF = string, $00 = numeric
BPL LAB_CC89 ; branch if numeric
; type is string
BIT INPFLG ; test INPUT mode flag, $00 = INPUT, $40 = GET, $98 = READ
BVC LAB_CC65 ; branch if not GET
; else do string GET
INX ; clear .X ??
STX CHRGOT+1 ; save BASIC execute pointer low byte
LDA #$00 ; clear .A
STA CHARAC ; clear search character
BEQ LAB_CC71 ; branch always
; is string INPUT or string READ
LAB_CC65
STA CHARAC ; save search character
CMP #$22 ; compare with "
BEQ LAB_CC72 ; if quote only search for "..." string
; else the string is not in quotes so ":", "," or $00 are
; the termination characters
LDA #':' ; set ":"
STA CHARAC ; set search character
LDA #',' ; set ","
LAB_CC71
CLC ; clear carry for add
LAB_CC72
STA ENDCHR ; set scan quotes flag
LDA CHRGOT+1 ; get BASIC execute pointer low byte
LDY CHRGOT+2 ; get BASIC execute pointer high byte
ADC #$00 ; add to pointer low byte. this add increments the pointer
; if the mode is INPUT or READ and the data is a "..."
; string
BCC LAB_CC7D ; if no rollover skip the high byte increment
INY ; else increment pointer high byte
LAB_CC7D
JSR LAB_D48D ; print string to utility pointer
JSR LAB_D7E2 ; restore BASIC execute pointer from temp
JSR LET5 ; perform string LET
JMP LAB_CC91 ; continue processing command
; GET, INPUT or READ is numeric
LAB_CC89
JSR ASCFLT ; get FAC1 from string
LDA INTFLG ; get data type flag, $80 = integer, $00 = float
JSR LET2 ; assign value to numeric variable
LAB_CC91
JSR CHRGOT ; scan memory
BEQ LAB_CC9D ; if ":" or [EOL] go handle the string end
CMP #',' ; compare with ","
BEQ LAB_CC9D ; if "," go handle the string end
JMP IGRERR ; else go do bad input routine
; string terminated with ":", "," or $00
LAB_CC9D
LDA CHRGOT+1 ; get BASIC execute pointer low byte
LDY CHRGOT+2 ; get BASIC execute pointer high byte
STA INPPTR ; save READ pointer low byte
STY INPPTR+1 ; save READ pointer high byte
LDA OPPTR ; get saved BASIC execute pointer low byte
LDY OPPTR+1 ; get saved BASIC execute pointer high byte
STA CHRGOT+1 ; restore BASIC execute pointer low byte
STY CHRGOT+2 ; restore BASIC execute pointer high byte
JSR CHRGOT ; scan memory
BEQ LAB_CCDF ; branch if ":" or [EOL]
JSR COMCHK ; scan for ",", else do syntax error then warm start
JMP LAB_CC15 ; go READ or INPUT next variable from list
; was READ
LAB_CCB8
JSR FIND2 ; scan for next BASIC statement ([:] or [EOL])
INY ; increment index to next byte
TAX ; copy byte to .X
BNE LAB_CCD1 ; if ":" go look for the next DATA
LDX #ER_OODATA ; else set error $0D, out of data error
INY ; increment index to next line pointer high byte
LDA (CHRGOT+1),Y ; get next line pointer high byte
BEQ LAB_CD32 ; if program end go do error, eventually does error .X
INY ; increment index
LDA (CHRGOT+1),Y ; get next line # low byte
STA DATLIN ; save current DATA line low byte
INY ; increment index
LDA (CHRGOT+1),Y ; get next line # high byte
INY ; increment index
STA DATLIN+1 ; save current DATA line high byte
LAB_CCD1
JSR BUMPTP ; add .Y to the BASIC execute pointer
JSR CHRGOT ; scan memory
TAX ; copy byte
CPX #TK_DATA ; compare with token for DATA
BNE LAB_CCB8 ; loop if not DATA
JMP LAB_CC51 ; continue evaluating READ
LAB_CCDF
LDA INPPTR ; get READ pointer low byte
LDY INPPTR+1 ; get READ pointer high byte
LDX INPFLG ; get INPUT mode flag, $00 = INPUT, $40 = GET, $98 = READ
BPL LAB_CCEA ; if INPUT or GET go exit or ignore extra input
JMP LAB_C827 ; else set data pointer and exit
LAB_CCEA
LDY #$00 ; clear index
LDA (INPPTR),Y ; get READ byte
BEQ LAB_CCFB ; exit if [EOL]
LDA CHANNL ; get current I/O channel
BNE LAB_CCFB ; exit if not default channel
LDA #<EXTRA ; set "?EXTRA IGNORED" pointer low byte
LDY #>EXTRA ; set "?EXTRA IGNORED" pointer high byte
JMP PRTSTR ; print null terminated string
LAB_CCFB
RTS
;***********************************************************************************;
;
; input error messages
EXTRA
.byte "?EXTRA IGNORED",$0D,$00
LAB_CD0C
.byte "?REDO FROM START",$0D,$00
;***********************************************************************************;
;
; perform NEXT
NEXT
BNE LAB_CD24 ; if NEXT variable go find the variable
LDY #$00 ; else clear .Y
BEQ LAB_CD27 ; use any variable, branch always
; NEXT variable
LAB_CD24
JSR EVLVAR ; get variable address
LAB_CD27
STA FORPNT ; save FOR/NEXT variable pointer low byte
STY FORPNT+1 ; save FOR/NEXT variable pointer high byte
; (high byte cleared if no variable defined)
JSR SCNSTK ; search the stack for FOR or GOSUB activity
BEQ LAB_CD35 ; if FOR found continue
LDX #ER_NXTWOFOR ; else set error $0A, next without for error
LAB_CD32
JMP ERROR ; do error #.X then warm start
; found this FOR variable
LAB_CD35
TXS ; update stack pointer
TXA ; copy stack pointer
CLC ; clear carry for add
ADC #$04 ; point to STEP value
PHA ; save it
ADC #$06 ; point to TO value
STA INDEX+2 ; save pointer to TO variable for compare
PLA ; restore pointer to STEP value
LDY #$01 ; point to stack page
JSR LODFAC ; unpack memory (.A.Y) into FAC1
TSX ; get stack pointer back
LDA STACK+9,X ; get step sign
STA FAC1+FAC_SIGN ; save FAC1 sign (b7)
LDA FORPNT ; get FOR/NEXT variable pointer low byte
LDY FORPNT+1 ; get FOR/NEXT variable pointer high byte
JSR LAPLUS ; add FOR variable to FAC1
JSR FACTFP ; pack FAC1 into FOR variable
LDY #$01 ; point to stack page
JSR LAB_DC5D ; compare FAC1 with TO value
TSX ; get stack pointer back
SEC ; set carry for subtract
SBC STACK+9,X ; subtract step sign
BEQ LAB_CD78 ; if = loop complete, go unstack the FOR
; loop back and do it all again
LDA STACK+$0F,X ; get FOR line low byte
STA CURLIN ; save current line number low byte
LDA STACK+$10,X ; get FOR line high byte
STA CURLIN+1 ; save current line number high byte
LDA STACK+$12,X ; get BASIC execute pointer low byte
STA CHRGOT+1 ; save BASIC execute pointer low byte
LDA STACK+$11,X ; get BASIC execute pointer high byte
STA CHRGOT+2 ; save BASIC execute pointer high byte
LAB_CD75
JMP NEWSTT ; go do interpreter inner loop
; NEXT loop complete
LAB_CD78
TXA ; stack copy to .A
ADC #$11 ; add $12, $11 + carry, to dump FOR structure
TAX ; copy back to index
TXS ; copy to stack pointer
JSR CHRGOT ; scan memory
CMP #',' ; compare with ","
BNE LAB_CD75 ; if not "," go do interpreter inner loop
; was "," so another NEXT variable to do
JSR CHRGET ; increment and scan memory
JSR LAB_CD24 ; do NEXT variable
;***********************************************************************************;
;
; evaluate expression and check type mismatch
TYPCHK
JSR FRMEVL ; evaluate expression
; check if source and destination are numeric
LAB_CD8D
CLC
.byte $24 ; makes next line BIT $38
; check if source and destination are string
LAB_CD8F
SEC ; destination is string
; type match check, set Cb for string, clear Cb for numeric
LAB_CD90
BIT VALTYP ; test data type flag, $FF = string, $00 = numeric
BMI LAB_CD97 ; if string go check string is required
; type found is numeric, check required
BCS LAB_CD99 ; if string is required go do type mismatch error
LAB_CD96
RTS
; type found is string, check required
LAB_CD97
BCS LAB_CD96 ; exit if string is required
; do type mismatch error
LAB_CD99
LDX #ER_TYPMSMCH ; error code $16, type mismatch error
JMP ERROR ; do error #.X then warm start
;***********************************************************************************;
;
; evaluate expression
FRMEVL
LDX CHRGOT+1 ; get BASIC execute pointer low byte
BNE LAB_CDA4 ; skip next if not zero
DEC CHRGOT+2 ; else decrement BASIC execute pointer high byte
LAB_CDA4
DEC CHRGOT+1 ; decrement BASIC execute pointer low byte
LDX #$00 ; set null precedence, flag done
.byte $24 ; makes next line BIT $48
LAB_CDA9
PHA ; push compare evaluation byte if branch to here
TXA ; copy precedence byte
PHA ; push precedence byte
LDA #$01 ; 2 bytes
JSR STKSPC ; check room on stack for .A*2 bytes
JSR EVAL ; get value from line
LDA #$00 ; clear .A
STA OPMASK ; clear comparison evaluation flag
LAB_CDB8
JSR CHRGOT ; scan memory
LAB_CDBB
SEC ; set carry for subtract
SBC #TK_GT ; subtract token for ">"
BCC LAB_CDD7 ; if < ">" skip comparison test check
CMP #$03 ; compare with ">" to +3
BCS LAB_CDD7 ; if >= 3 skip comparison test check
; was token for ">" "=" or "<"
CMP #$01 ; compare with token for "="
ROL ; *2, b0 = carry (=1 if token was "=" or "<")
EOR #$01 ; toggle b0
EOR OPMASK ; XOR with comparison evaluation flag
CMP OPMASK ; compare with comparison evaluation flag
BCC LAB_CE30 ; if < saved flag do syntax error then warm start
STA OPMASK ; save new comparison evaluation flag
JSR CHRGET ; increment and scan memory
JMP LAB_CDBB ; go do next character
LAB_CDD7
LDX OPMASK ; get comparison evaluation flag
BNE LAB_CE07 ; if compare function flagged go evaluate right hand side
BCS LAB_CE58 ; go do functions
; else was < TK_GT so is operator or lower
ADC #$07 ; add # of operators (+, -, *, /, ^, AND or OR)
BCC LAB_CE58 ; if < + operator go do the function
; carry was set so token was +, -, *, /, ^, AND or OR
ADC VALTYP ; add data type flag, $FF = string, $00 = numeric
BNE LAB_CDE8 ; if not string or not + token skip concatenate
; will only be $00 if type is string and token was +
JMP ADDSTR ; add strings, string 1 is in the descriptor, string 2
; is in line, and return
LAB_CDE8
ADC #$FF ; -1 (corrects for carry add)
STA INDEX ; save it
ASL ; *2
ADC INDEX ; *3
TAY ; copy to index
LAB_CDF0
PLA ; pull previous precedence
CMP OPTAB,Y ; compare with precedence byte
BCS LAB_CE5D ; if .A >= go do the function
JSR LAB_CD8D ; check if source is numeric, else do type mismatch
LAB_CDF9
PHA ; save precedence
LAB_CDFA
JSR LAB_CE20 ; get vector, execute function then continue evaluation
PLA ; restore precedence
LDY OPPTR ; get precedence stacked flag
BPL LAB_CE19 ; if stacked values go check the precedence
TAX ; copy precedence, set flags
BEQ LAB_CE5B ; exit if done
BNE LAB_CE66 ; else pop FAC2 and return, branch always
LAB_CE07
LSR VALTYP ; clear data type flag, $FF = string, $00 = numeric
TXA ; copy compare function flag
ROL ; <<1, shift data type flag into b0, 1 = string, 0 = num
LDX CHRGOT+1 ; get BASIC execute pointer low byte
BNE LAB_CE11 ; if no underflow skip the high byte decrement
DEC CHRGOT+2 ; else decrement BASIC execute pointer high byte
LAB_CE11
DEC CHRGOT+1 ; decrement BASIC execute pointer low byte
LDY #LAB_C09B-OPTAB
; set offset to = operator precedence entry
STA OPMASK ; save new comparison evaluation flag
BNE LAB_CDF0 ; branch always
LAB_CE19
CMP OPTAB,Y ; compare with stacked function precedence
BCS LAB_CE66 ; if .A >=, pop FAC2 and return
BCC LAB_CDF9 ; else go stack this one and continue, branch always
;***********************************************************************************;
;
; get vector, execute function then continue evaluation
LAB_CE20
LDA OPTAB+2,Y ; get function vector high byte
PHA ; onto stack
LDA OPTAB+1,Y ; get function vector low byte
PHA ; onto stack
; now push sign, round FAC1 and put on stack
JSR LAB_CE33 ; function will return here, then the next RTS will call
; the function
LDA OPMASK ; get comparison evaluation flag
JMP LAB_CDA9 ; continue evaluating expression
LAB_CE30
JMP LAB_CF08 ; do syntax error then warm start
LAB_CE33
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
LDX OPTAB,Y ; get precedence byte
;***********************************************************************************;
;
; push sign, round FAC1 and put on stack
LAB_CE38
TAY ; copy sign
PLA ; get return address low byte
STA INDEX ; save it
INC INDEX ; increment it as return - 1 is pushed
; note, no check is made on the high byte so if the calling
; routine ever assembles to a page edge then this all goes
; horribly wrong!
PLA ; get return address high byte
STA INDEX+1 ; save it
TYA ; restore sign
PHA ; push sign
;***********************************************************************************;
;
; round FAC1 and put on stack
LAB_CE43
JSR ROUND ; round FAC1
LDA FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
PHA ; save it
LDA FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
PHA ; save it
LDA FAC1+FAC_MANT+1 ; get FAC1 mantissa 2
PHA ; save it
LDA FAC1+FAC_MANT ; get FAC1 mantissa 1
PHA ; save it
LDA FAC1+FAC_EXPT ; get FAC1 exponent
PHA ; save it
JMP (INDEX) ; return, sort of
;***********************************************************************************;
;
; do functions
LAB_CE58
LDY #$FF ; flag function
PLA ; pull precedence byte
LAB_CE5B
BEQ LAB_CE80 ; exit if done
LAB_CE5D
CMP #$64 ; compare previous precedence with $64
BEQ LAB_CE64 ; if was $64 (< function) skip the type check
JSR LAB_CD8D ; check if source is numeric, else do type mismatch
LAB_CE64
STY OPPTR ; save precedence stacked flag
; pop FAC2 and return
LAB_CE66
PLA ; pop byte
LSR ; shift out comparison evaluation lowest bit
STA TANSGN ; save the comparison evaluation flag
PLA ; pop exponent
STA FAC2+FAC_EXPT ; save FAC2 exponent
PLA ; pop mantissa 1
STA FAC2+FAC_MANT ; save FAC2 mantissa 1
PLA ; pop mantissa 2
STA FAC2+FAC_MANT+1 ; save FAC2 mantissa 2
PLA ; pop mantissa 3
STA FAC2+FAC_MANT+2 ; save FAC2 mantissa 3
PLA ; pop mantissa 4
STA FAC2+FAC_MANT+3 ; save FAC2 mantissa 4
PLA ; pop sign
STA FAC2+FAC_SIGN ; save FAC2 sign (b7)
EOR FAC1+FAC_SIGN ; XOR FAC1 sign (b7)
STA ARISGN ; save sign compare (FAC1 XOR FAC2)
LAB_CE80
LDA FAC1+FAC_EXPT ; get FAC1 exponent
RTS
;***********************************************************************************;
;
; get value from line
EVAL
JMP (IEVAL) ; get arithmetic element
;***********************************************************************************;
;
; get arithmetic element, the get arithmetic element vector is initialised to point here
FEVAL
LDA #$00 ; clear byte
STA VALTYP ; clear data type flag, $FF = string, $00 = numeric
LAB_CE8A
JSR CHRGET ; increment and scan memory
BCS LAB_CE92 ; if not numeric character continue
; else numeric string found (e.g. 123)
LAB_CE8F
JMP ASCFLT ; get FAC1 from string and return
; get value from line .. continued, wasn't a number so ...
LAB_CE92
JSR CHRTST ; check byte, return Cb = 0 if <"A" or >"Z"
BCC LAB_CE9A ; if not variable name continue
JMP FACT12 ; variable name set-up and return
; get value from line .. continued, wasn't a variable name so ...
LAB_CE9A
CMP #TK_PI ; compare with token for PI
BNE LAB_CEAD ; if not PI continue
; else return PI in FAC1
LDA #<PIVAL ; get PI pointer low byte
LDY #>PIVAL ; get PI pointer high byte
JSR LODFAC ; unpack memory (.A.Y) into FAC1
JMP CHRGET ; increment and scan memory and return
;***********************************************************************************;
;
; PI as floating number
PIVAL
.byte $82,$49,$0F,$DA,$A1
; 3.141592653
;***********************************************************************************;
;
; get value from line .. continued, wasn't PI so ...
LAB_CEAD
CMP #'.' ; compare with "."
BEQ LAB_CE8F ; if so get FAC1 from string and return, e.g. was .123
; wasn't .123 so ...
CMP #TK_MINUS ; compare with token for "-"
BEQ FACT10 ; if "-" token, do set-up for functions
; wasn't -123 so ...
CMP #TK_PLUS ; compare with token for "+"
BEQ LAB_CE8A ; if "+" token ignore the leading +, +1 = 1
; it wasn't any sort of number so ...
CMP #$22 ; compare with "
BNE LAB_CECC ; if not open quote continue
; was open quote so get the enclosed string
; print "..." string to string utility area
LAB_CEBD
LDA CHRGOT+1 ; get BASIC execute pointer low byte
LDY CHRGOT+2 ; get BASIC execute pointer high byte
ADC #$00 ; add carry to low byte
BCC LAB_CEC6 ; branch if no overflow
INY ; increment high byte
LAB_CEC6
JSR MAKSTR ; print " terminated string to utility pointer
JMP LAB_D7E2 ; restore BASIC execute pointer from temp and return
; get value from line .. continued, wasn't a string so ...
LAB_CECC
CMP #TK_NOT ; compare with token for NOT
BNE LAB_CEE3 ; if not token for NOT continue
; was NOT token
LDY #$18 ; offset to NOT function
BNE LAB_CF0F ; do set-up for function then execute, branch always
; do = compare
EQUAL
JSR MAKINT ; evaluate integer expression, no sign check
LDA FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
EOR #$FF ; invert it
TAY ; copy it
LDA FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
EOR #$FF ; invert it
JMP MAKFP ; convert fixed integer .A.Y to float FAC1 and return
; get value from line .. continued, wasn't NOT so ...
LAB_CEE3
CMP #TK_FN ; compare with token for FN
BNE LAB_CEEA ; if not token for FN continue
JMP EVALFN ; else go evaluate FNx
; get value from line .. continued, wasn't FN so ...
LAB_CEEA
CMP #TK_SGN ; compare with token for SGN
BCC PAREXP ; if less than SGN token go evaluate expression in ()
; else was a function token
JMP FACT17 ; go set up function references, branch always
;***********************************************************************************;
;
; get value from line .. continued
; if here it can only be something in brackets so ...
; evaluate expression within parentheses
PAREXP
JSR LPACHK ; scan for "(", else do syntax error then warm start
JSR FRMEVL ; evaluate expression
;***********************************************************************************;
;
; all the 'scan for' routines return the character after the sought character
; scan for ")", else do syntax error then warm start
RPACHK
LDA #$29 ; load .A with ")"
.byte $2C ; makes next line BIT $28A9
; scan for "(", else do syntax error then warm start
LPACHK
LDA #$28 ; load .A with "("
.byte $2C ; makes next line BIT $2CA9
; scan for ",", else do syntax error then warm start
COMCHK
LDA #',' ; load .A with ","
; scan for CHR$(.A), else do syntax error then warm start
SYNCHR
LDY #$00 ; clear index
CMP (CHRGOT+1),Y ; compare with BASIC byte
BNE LAB_CF08 ; if not expected byte do syntax error then warm start
JMP CHRGET ; else increment and scan memory and return
;***********************************************************************************;
;
; syntax error then warm start
LAB_CF08
LDX #ER_SYNTAX ; error code $0B, syntax error
JMP ERROR ; do error #.X then warm start
FACT10
LDY #$15 ; set offset from base to > operator
LAB_CF0F
PLA ; dump return address low byte
PLA ; dump return address high byte
JMP LAB_CDFA ; execute function then continue evaluation
;***********************************************************************************;
;
; check address range, return Cb = 1 if address in BASIC ROM
VARRANGE
SEC ; set carry for subtract
LDA FAC1+3 ; get variable address low byte
SBC #<COLDST ; subtract $C000 low byte
LDA FAC1+4 ; get variable address high byte
SBC #>COLDST ; subtract $C000 high byte
BCC LAB_CF27 ; exit if address < $C000
LDA #<CGIMAG ; get end of BASIC marker low byte
SBC FAC1+3 ; subtract variable address low byte
LDA #>CGIMAG ; get end of BASIC marker high byte
SBC FAC1+4 ; subtract variable address high byte
LAB_CF27
RTS
;***********************************************************************************;
;
; variable name set-up
FACT12
JSR EVLVAR ; get variable address
STA FAC1+3 ; save variable pointer low byte
STY FAC1+4 ; save variable pointer high byte
LDX VARNAM ; get current variable name first character
LDY VARNAM+1 ; get current variable name second character
LDA VALTYP ; get data type flag, $FF = string, $00 = numeric
BEQ LAB_CF5D ; if numeric go handle a numeric variable
; variable is string
LDA #$00 ; else clear .A
STA FACOV ; clear FAC1 rounding byte
JSR VARRANGE ; check address range
BCC LAB_CF5C ; exit if not in BASIC ROM
CPX #'T' ; compare variable name first character with "T"
BNE LAB_CF5C ; exit if not "T"
CPY #'I'+$80 ; compare variable name second character with "I$"
BNE LAB_CF5C ; exit if not "I$"
; variable name was "TI$"
JSR LAB_CF84 ; read real time clock into FAC1 mantissa, 0HML
STY EXPCNT ; clear exponent count adjust
DEY ; .Y = $FF
STY FBUFPT ; set output string index, -1 to allow for pre increment
LDY #$06 ; HH:MM:SS is six digits
STY LAB_5D ; set number of characters before the decimal point
LDY #HMSCON-FLTCON
; index to jiffy conversion table
JSR LAB_DE68 ; convert jiffy count to string
JMP LAB_D46F ; exit via STR$() code tail
LAB_CF5C
RTS
; variable name set-up, variable is numeric
LAB_CF5D
BIT INTFLG ; test data type flag, $80 = integer, $00 = float
BPL LAB_CF6E ; if float go handle float
; else handle integer variable
LDY #$00 ; clear index
LDA (FAC1+3),Y ; get integer variable low byte
TAX ; copy to .X
INY ; increment index
LDA (FAC1+3),Y ; get integer variable high byte
TAY ; copy to .Y
TXA ; copy low byte to .A
JMP MAKFP ; convert fixed integer .A.Y to float FAC1 and return
; variable name set-up, variable is float
LAB_CF6E
JSR VARRANGE ; check address range
BCC LAB_CFA0 ; if not in BASIC ROM get pointer and unpack into FAC1
CPX #'T' ; compare variable name first character with "T"
BNE LAB_CF92 ; if not "T" skip Tx variables
CPY #'I' ; compare variable name second character with "I"
BNE LAB_CFA0 ; if not "I" go do plain float
; variable name was "TI"
JSR LAB_CF84 ; read real time clock into FAC1 mantissa, 0HML
TYA ; clear .A
LDX #$A0 ; set exponent to 32 bit value
JMP LAB_DC4F ; set exponent = .X and normalise FAC1
;***********************************************************************************;
;
; read real time clock into FAC1 mantissa, 0HML
LAB_CF84
JSR RDTIM ; read real time clock
STX FAC1+FAC_MANT+2 ; save jiffy clock mid byte as FAC1 mantissa 3
STY FAC1+FAC_MANT+1 ; save jiffy clock high byte as FAC1 mantissa 2
STA FAC1+FAC_MANT+3 ; save jiffy clock low byte as FAC1 mantissa 4
LDY #$00 ; clear .Y
STY FAC1+FAC_MANT ; clear FAC1 mantissa 1
RTS
;***********************************************************************************;
;
; variable name set-up, variable is float and not "Tx"
LAB_CF92
CPX #'S' ; compare variable name first character with "S"
BNE LAB_CFA0 ; if not "S" go do normal floating variable
CPY #'T' ; compare variable name second character with "T"
BNE LAB_CFA0 ; if not "T" go do normal floating variable
; variable name was "ST"
JSR READST ; read I/O status word
JMP INTFP ; save .A as integer byte and return
; variable is plain float
LAB_CFA0
LDA FAC1+3 ; get variable pointer low byte
LDY FAC1+4 ; get variable pointer high byte
JMP LODFAC ; unpack memory (.A.Y) into FAC1
;***********************************************************************************;
;
; get value from line continued
; only functions left so ..
; set up function references
FACT17
ASL ; *2 (2 bytes per function address)
PHA ; save function offset
TAX ; copy function offset
JSR CHRGET ; increment and scan memory
CPX #$8F ; compare function offset to CHR$ token offset+1
BCC LAB_CFD1 ; if < LEFT$ (cannot be =) go do function setup
; get value from line .. continued
; was LEFT$, RIGHT$ or MID$ so..
JSR LPACHK ; scan for "(", else do syntax error then warm start
JSR FRMEVL ; evaluate, should be string, expression
JSR COMCHK ; scan for ",", else do syntax error then warm start
JSR LAB_CD8F ; check if source is string, else do type mismatch
PLA ; restore function offset
TAX ; copy it
LDA FAC1+4 ; get descriptor pointer high byte
PHA ; push string pointer high byte
LDA FAC1+3 ; get descriptor pointer low byte
PHA ; push string pointer low byte
TXA ; restore function offset
PHA ; save function offset
JSR LAB_D79E ; get byte parameter
PLA ; restore function offset
TAY ; copy function offset
TXA ; copy byte parameter to .A
PHA ; push byte parameter
JMP LAB_CFD6 ; go call function
; get value from line .. continued
; was SGN() to CHR$() so..
LAB_CFD1
JSR PAREXP ; evaluate expression within parentheses
PLA ; restore function offset
TAY ; copy to index
LAB_CFD6
LDA FUNDSP-$68,Y ; get function jump vector low byte
STA JMPER+1 ; save functions jump vector low byte
LDA FUNDSP-$67,Y ; get function jump vector high byte
STA JMPER+2 ; save functions jump vector high byte
JSR JMPER ; do function call
JMP LAB_CD8D ; check if source is numeric and RTS, else do type mismatch
; string functions avoid this by dumping the return address
;***********************************************************************************;
;
; perform OR
; this works because NOT(NOT(x) AND NOT(y)) = x OR y
ORR
LDY #$FF ; set .Y for OR
.byte $2C ; makes next line BIT $00A0
;***********************************************************************************;
;
; perform AND
ANDD
LDY #$00 ; clear .Y for AND
STY COUNT ; set AND/OR invert value
JSR MAKINT ; evaluate integer expression, no sign check
LDA FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
EOR COUNT ; XOR low byte
STA CHARAC ; save it
LDA FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
EOR COUNT ; XOR high byte
STA ENDCHR ; save it
JSR ATOF ; copy FAC2 to FAC1, get 2nd value in expression
JSR MAKINT ; evaluate integer expression, no sign check
LDA FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
EOR COUNT ; XOR high byte
AND ENDCHR ; AND with expression 1 high byte
EOR COUNT ; XOR result high byte
TAY ; save in .Y
LDA FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
EOR COUNT ; XOR low byte
AND CHARAC ; AND with expression 1 low byte
EOR COUNT ; XOR result low byte
JMP MAKFP ; convert fixed integer .A.Y to float FAC1 and return
;***********************************************************************************;
;
; perform comparisons
; do < compare
COMPAR
JSR LAB_CD90 ; type match check, set Cb for string
BCS CMPST ; if string go do string compare
; do numeric < compare
LDA FAC2+FAC_SIGN ; get FAC2 sign (b7)
ORA #$7F ; set all non sign bits
AND FAC2+FAC_MANT ; and FAC2 mantissa 1 (AND in sign bit)
STA FAC2+FAC_MANT ; save FAC2 mantissa 1
LDA #<FAC2 ; set pointer low byte to FAC2
LDY #>FAC2 ; set pointer high byte to FAC2
JSR CMPFAC ; compare FAC1 with (.A.Y)
TAX ; copy the result
JMP LAB_D061 ; go evaluate result
; do string < compare
CMPST
LDA #$00 ; clear byte
STA VALTYP ; clear data type flag, $FF = string, $00 = numeric
DEC OPMASK ; clear < bit in comparison evaluation flag
JSR LAB_D6A6 ; pop string off descriptor stack, or from top of string
; space returns with .A = length, .X = pointer low byte,
; .Y = pointer high byte
STA FAC1 ; save length
STX FAC1+1 ; save string pointer low byte
STY FAC1+2 ; save string pointer high byte
LDA FAC2+3 ; get descriptor pointer low byte
LDY FAC2+4 ; get descriptor pointer high byte
JSR LAB_D6AA ; pop (.Y.A) descriptor off stack or from top of string space
; returns with .A = length, .X = pointer low byte,
; .Y = pointer high byte
STX FAC2+3 ; save string pointer low byte
STY FAC2+4 ; save string pointer high byte
TAX ; copy length
SEC ; set carry for subtract
SBC FAC1 ; subtract string 1 length
BEQ LAB_D056 ; if str 1 length = string 2 length go compare the strings
LDA #$01 ; set str 1 length > string 2 length
BCC LAB_D056 ; if so return +1 if otherwise equal
LDX FAC1 ; get string 1 length
LDA #$FF ; set str 1 length < string 2 length
LAB_D056
STA FAC1+5 ; save length compare
LDY #$FF ; set index
INX ; adjust for loop
LAB_D05B
INY ; increment index
DEX ; decrement count
BNE LAB_D066 ; if still bytes to do go compare them
LDX FAC1+5 ; get length compare back
LAB_D061
BMI LAB_D072 ; branch if str 1 < str 2
CLC ; flag str 1 <= str 2
BCC LAB_D072 ; go evaluate result, branch always
LAB_D066
LDA (FAC2+3),Y ; get string 2 byte
CMP (FAC1+1),Y ; compare with string 1 byte
BEQ LAB_D05B ; loop if bytes =
LDX #$FF ; set str 1 < string 2
BCS LAB_D072 ; branch if so
LDX #$01 ; set str 1 > string 2
LAB_D072
INX ; x = 0, 1 or 2
TXA ; copy to .A
ROL ; * 2 (1, 2 or 4)
AND TANSGN ; AND with the comparison evaluation flag
BEQ LAB_D07B ; branch if 0 (compare is false)
LDA #$FF ; else set result true
LAB_D07B
JMP INTFP ; save .A as integer byte and return
LAB_D07E
JSR COMCHK ; scan for ",", else do syntax error then warm start
;***********************************************************************************;
;
; perform DIM
DIM
TAX ; copy "DIM" flag to .X
JSR LAB_D090 ; search for variable
JSR CHRGOT ; scan memory
BNE LAB_D07E ; scan for "," and loop if not null
RTS
;***********************************************************************************;
;
; search for variable
EVLVAR
LDX #$00 ; set DIM flag = $00
JSR CHRGOT ; scan memory, 1st character
LAB_D090
STX DIMFLG ; save DIM flag
LAB_D092
STA VARNAM ; save 1st character
JSR CHRGOT ; scan memory
JSR CHRTST ; check byte, return Cb = 0 if <"A" or >"Z"
BCS LAB_D09F ; if ok continue
LAB_D09C
JMP LAB_CF08 ; else syntax error then warm start
; was variable name so ...
LAB_D09F
LDX #$00 ; clear 2nd character temp
STX VALTYP ; clear data type flag, $FF = string, $00 = numeric
STX INTFLG ; clear data type flag, $80 = integer, $00 = float
JSR CHRGET ; increment and scan memory, 2nd character
BCC LAB_D0AF ; if character = "0"-"9" (ok) go save 2nd character
; 2nd character wasn't "0" to "9" so ...
JSR CHRTST ; check byte, return Cb = 0 if <"A" or >"Z"
BCC LAB_D0BA ; if <"A" or >"Z" go check if string
LAB_D0AF
TAX ; copy 2nd character
; ignore further (valid) characters in the variable name
LAB_D0B0
JSR CHRGET ; increment and scan memory, 3rd character
BCC LAB_D0B0 ; loop if character = "0"-"9" (ignore)
JSR CHRTST ; check byte, return Cb = 0 if <"A" or >"Z"
BCS LAB_D0B0 ; loop if character = "A"-"Z" (ignore)
; check if string variable
LAB_D0BA
CMP #'$' ; compare with "$"
BNE LAB_D0C4 ; if not string go check integer
; type is string
LDA #$FF ; set data type = string
STA VALTYP ; set data type flag, $FF = string, $00 = numeric
BNE LAB_D0D4 ; branch always
LAB_D0C4
CMP #$25 ; compare with "%"
BNE LAB_D0DB ; if not integer go check for an array
LDA SUBFLG ; get subscript/FNx flag
BNE LAB_D09C ; if ?? do syntax error then warm start
LDA #$80 ; set integer type
STA INTFLG ; set data type = integer
ORA VARNAM ; OR current variable name first byte
STA VARNAM ; save current variable name first byte
LAB_D0D4
TXA ; get 2nd character back
ORA #$80 ; set top bit, indicate string or integer variable
TAX ; copy back to 2nd character temp
JSR CHRGET ; increment and scan memory
LAB_D0DB
STX VARNAM+1 ; save 2nd character
SEC ; set carry for subtract
ORA SUBFLG ; or with subscript/FNx flag - or FN name
SBC #$28 ; subtract "("
BNE FNDVAR ; if not "(" go find a plain numeric variable
JMP ARY ; else go find, or make, array
; either find or create variable
; variable name wasn't xx(... so look for plain variable
FNDVAR
LDY #$00 ; clear .Y
STY SUBFLG ; clear subscript/FNx flag
LDA VARTAB ; get start of variables low byte
LDX VARTAB+1 ; get start of variables high byte
LAB_D0EF
STX TMPPTR+1 ; save search address high byte
LAB_D0F1
STA TMPPTR ; save search address low byte
CPX ARYTAB+1 ; compare with end of variables high byte
BNE LAB_D0FB ; skip next compare if <>
; high addresses were = so compare low addresses
CMP ARYTAB ; compare low address with end of variables low byte
BEQ MAKVAR ; if not found go make new variable
LAB_D0FB
LDA VARNAM ; get 1st character of variable to find
CMP (TMPPTR),Y ; compare with variable name 1st character
BNE LAB_D109 ; if no match go try the next variable
; 1st characters match so compare 2nd character
LDA VARNAM+1 ; get 2nd character of variable to find
INY ; index to point to variable name 2nd character
CMP (TMPPTR),Y ; compare with variable name 2nd character
BEQ RETVP ; if match go return the variable
DEY ; else decrement index (now = $00)
LAB_D109
CLC ; clear carry for add
LDA TMPPTR ; get search address low byte
ADC #$07 ; +7, offset to next variable name
BCC LAB_D0F1 ; loop if no overflow to high byte
INX ; else increment high byte
BNE LAB_D0EF ; loop always, RAM doesn't extend to $FFFF
;***********************************************************************************;
;
; check byte, return Cb = 0 if<"A" or >"Z"
CHRTST
CMP #$41 ; compare with "A"
BCC LAB_D11C ; exit if less
; carry is set
SBC #$5B ; subtract "Z"+1
SEC ; set carry
SBC #$A5 ; subtract $A5 (restore byte)
; carry clear if byte > $5A
LAB_D11C
RTS
;***********************************************************************************;
;
; reached end of variable memory without match
; ... so create new variable
MAKVAR
PLA ; pop return address low byte
PHA ; push return address low byte
CMP #<FACT12+2 ; compare with expected calling routine return low byte
BNE LAB_D128 ; if not get variable go create new variable
; This will only drop through if the call was from FACT12 and is only called
; from there if it is searching for a variable from the right hand side of a LET a=b
; statement, it prevents the creation of variables not assigned a value.
; Value returned by this is either numeric zero, exponent byte is $00, or null string,
; descriptor length byte is $00. in fact a pointer to any $00 byte would have done.
; else return dummy null value
LAB_D123
LDA #<NULLVAR ; set result pointer low byte
LDY #>NULLVAR ; set result pointer high byte
RTS
; create new numeric variable
LAB_D128
LDA VARNAM ; get variable name first character
LDY VARNAM+1 ; get variable name second character
CMP #'T' ; compare first character with "T"
BNE LAB_D13B ; if not "T" continue
CPY #'I'+$80 ; compare second character with "I$"
BEQ LAB_D123 ; if "I$" return null value
CPY #'I' ; compare second character with "I"
BNE LAB_D13B ; if not "I" continue
; if name is "TI" do syntax error
LAB_D138
JMP LAB_CF08 ; do syntax error then warm start
LAB_D13B
CMP #'S' ; compare first character with "S"
BNE LAB_D143 ; if not "S" continue
CPY #'T' ; compare second character with "T"
BEQ LAB_D138 ; if name is "ST" do syntax error
LAB_D143
LDA ARYTAB ; get end of variables low byte
LDY ARYTAB+1 ; get end of variables high byte
STA TMPPTR ; save old block start low byte
STY TMPPTR+1 ; save old block start high byte
LDA STREND ; get end of arrays low byte
LDY STREND+1 ; get end of arrays high byte
STA GEN2PTR ; save old block end low byte
STY GEN2PTR+1 ; save old block end high byte
CLC ; clear carry for add
ADC #$07 ; +7, space for one variable
BCC LAB_D159 ; if no overflow skip the high byte increment
INY ; else increment high byte
LAB_D159
STA GENPTR ; set new block end low byte
STY GENPTR+1 ; set new block end high byte
JSR MAKSPC ; open up space in memory
LDA GENPTR ; get new start low byte
LDY GENPTR+1 ; get new start high byte (-$100)
INY ; correct high byte
STA ARYTAB ; set end of variables low byte
STY ARYTAB+1 ; set end of variables high byte
LDY #$00 ; clear index
LDA VARNAM ; get variable name 1st character
STA (TMPPTR),Y ; save variable name 1st character
INY ; increment index
LDA VARNAM+1 ; get variable name 2nd character
STA (TMPPTR),Y ; save variable name 2nd character
LDA #$00 ; clear .A
INY ; increment index
STA (TMPPTR),Y ; initialise variable byte
INY ; increment index
STA (TMPPTR),Y ; initialise variable byte
INY ; increment index
STA (TMPPTR),Y ; initialise variable byte
INY ; increment index
STA (TMPPTR),Y ; initialise variable byte
INY ; increment index
STA (TMPPTR),Y ; initialise variable byte
; found a match for variable
RETVP
LDA TMPPTR ; get variable address low byte
CLC ; clear carry for add
ADC #$02 ; +2, offset past variable name bytes
LDY TMPPTR+1 ; get variable address high byte
BCC LAB_D18F ; if no overflow skip the high byte increment
INY ; else increment high byte
LAB_D18F
STA VARPNT ; save current variable pointer low byte
STY VARPNT+1 ; save current variable pointer high byte
RTS
;***********************************************************************************;
;
; set-up array pointer to first element in array
ARYHED
LDA COUNT ; get # of dimensions (1, 2 or 3)
ASL ; *2 (also clears the carry !)
ADC #$05 ; +5 (result is 7, 9 or 11 here)
ADC TMPPTR ; add array start pointer low byte
LDY TMPPTR+1 ; get array pointer high byte
BCC LAB_D1A0 ; if no overflow skip the high byte increment
INY ; else increment high byte
LAB_D1A0
STA GENPTR ; save array data pointer low byte
STY GENPTR+1 ; save array data pointer high byte
RTS
;***********************************************************************************;
;
; -32768 as floating value
MAXINT
.byte $90,$80,$00,$00,$00 ; -32768
;***********************************************************************************;
;
; convert float to fixed
INTIDX
JSR MAKINT ; evaluate integer expression, no sign check
LDA FAC1+3 ; get result low byte
LDY FAC1+4 ; get result high byte
RTS
;***********************************************************************************;
;
; evaluate integer expression
GETSUB
JSR CHRGET ; increment and scan memory
JSR FRMEVL ; evaluate expression
; evaluate integer expression, sign check
LAB_D1B8
JSR LAB_CD8D ; check if source is numeric, else do type mismatch
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
BMI LAB_D1CC ; do illegal quantity error if -ve
; evaluate integer expression, no sign check
MAKINT
LDA FAC1+FAC_EXPT ; get FAC1 exponent
CMP #$90 ; compare with exponent = 2^16 (n>2^15)
BCC LAB_D1CE ; if n<2^16 go convert FAC1 floating to fixed and return
LDA #<MAXINT ; set pointer low byte to -32768
LDY #>MAXINT ; set pointer high byte to -32768
JSR CMPFAC ; compare FAC1 with (.A.Y)
LAB_D1CC
BNE ILQUAN ; if <> do illegal quantity error then warm start
LAB_D1CE
JMP FPINT ; convert FAC1 floating to fixed and return
;***********************************************************************************;
;
; an array is stored as follows
;
; array name ; two bytes with the following patterns for different types
; ; 1st char 2nd char
; ; b7 b7 type element size
; ; -------- -------- ----- ------------
; ; 0 0 floating point 5
; ; 0 1 string 3
; ; 1 1 integer 2
; offset to next array ; word
; dimension count ; byte
; 1st dimension size ; word, this is the number of elements including 0
; 2nd dimension size ; word, only here if the array has a second dimension
; 2nd dimension size ; word, only here if the array has a third dimension
; ; note: the dimension size word is in high byte low byte
; ; format, unlike most 6502 words
; then for each element the required number of bytes given as the element size above
; find or make array
ARY
LDA DIMFLG ; get DIM flag
ORA INTFLG ; OR with data type flag
PHA ; push it
LDA VALTYP ; get data type flag, $FF = string, $00 = numeric
PHA ; push it
LDY #$00 ; clear dimensions count
; now get the array dimension(s) and stack it (them) before the data type and DIM flag
LAB_D1DB
TYA ; copy dimensions count
PHA ; save it
LDA VARNAM+1 ; get array name 2nd byte
PHA ; save it
LDA VARNAM ; get array name 1st byte
PHA ; save it
JSR GETSUB ; evaluate integer expression
PLA ; pull array name 1st byte
STA VARNAM ; restore array name 1st byte
PLA ; pull array name 2nd byte
STA VARNAM+1 ; restore array name 2nd byte
PLA ; pull dimensions count
TAY ; restore it
TSX ; copy stack pointer
LDA STACK+2,X ; get DIM flag
PHA ; push it
LDA STACK+1,X ; get data type flag
PHA ; push it
LDA FAC1+3 ; get this dimension size high byte
STA STACK+2,X ; stack before flag bytes
LDA FAC1+4 ; get this dimension size low byte
STA STACK+1,X ; stack before flag bytes
INY ; increment dimensions count
JSR CHRGOT ; scan memory
CMP #',' ; compare with ","
BEQ LAB_D1DB ; if found go do next dimension
STY COUNT ; store dimensions count
JSR RPACHK ; scan for ")", else do syntax error then warm start
PLA ; pull data type flag
STA VALTYP ; restore data type flag, $FF = string, $00 = numeric
PLA ; pull data type flag
STA INTFLG ; restore data type flag, $80 = integer, $00 = float
AND #$7F ; mask dim flag
STA DIMFLG ; restore DIM flag
LDX ARYTAB ; set end of variables low byte
; (array memory start low byte)
LDA ARYTAB+1 ; set end of variables high byte
; (array memory start high byte)
; now check to see if we are at the end of array memory, we would be if there were
; no arrays.
LAB_D21C
STX TMPPTR ; save as array start pointer low byte
STA TMPPTR+1 ; save as array start pointer high byte
CMP STREND+1 ; compare with end of arrays high byte
BNE LAB_D228 ; if not reached array memory end continue searching
CPX STREND ; else compare with end of arrays low byte
BEQ ARY6 ; go build array if not found
; search for array
LAB_D228
LDY #$00 ; clear index
LDA (TMPPTR),Y ; get array name first byte
INY ; increment index to second name byte
CMP VARNAM ; compare with this array name first byte
BNE LAB_D237 ; if no match go try the next array
LDA VARNAM+1 ; else get this array name second byte
CMP (TMPPTR),Y ; compare with array name second byte
BEQ ARY2 ; array found so branch
; no match
LAB_D237
INY ; increment index
LDA (TMPPTR),Y ; get array size low byte
CLC ; clear carry for add
ADC TMPPTR ; add array start pointer low byte
TAX ; copy low byte to .X
INY ; increment index
LDA (TMPPTR),Y ; get array size high byte
ADC TMPPTR+1 ; add array memory pointer high byte
BCC LAB_D21C ; if no overflow go check next array
; do bad subscript error
BADSUB
LDX #ER_BADSSCPT ; error $12, bad subscript error
.byte $2C ; makes next line BIT $0EA2
;***********************************************************************************;
;
; do illegal quantity error
ILQUAN
LDX #ER_ILLQUAN ; error $0E, illegal quantity error
LAB_D24A
JMP ERROR ; do error #.X then warm start
;***********************************************************************************;
;
; array found
ARY2
LDX #ER_REDIMARY ; set error $13, double dimension error
LDA DIMFLG ; get DIM flag
BNE LAB_D24A ; if we are trying to dimension it do error #.X then warm
; start
; found the array and we're not dimensioning it so we must find an element in it
JSR ARYHED ; set-up array pointer to first element in array
LDA COUNT ; get dimensions count
LDY #$04 ; set index to array's # of dimensions
CMP (TMPPTR),Y ; compare with no of dimensions
BNE BADSUB ; if wrong do bad subscript error
JMP ARY14 ; found array so go get element
; array not found, so build it
ARY6
JSR ARYHED ; set-up array pointer to first element in array
JSR RAMSPC ; check available memory, do out of memory error if no room
LDY #$00 ; clear .Y
STY FBUFPT+1 ; clear array data size high byte
LDX #$05 ; set default element size
LDA VARNAM ; get variable name 1st byte
STA (TMPPTR),Y ; save array name 1st byte
BPL LAB_D274 ; branch if not string or floating point array
DEX ; decrement element size, $04
LAB_D274
INY ; increment index
LDA VARNAM+1 ; get variable name 2nd byte
STA (TMPPTR),Y ; save array name 2nd byte
BPL LAB_D27D ; branch if not integer or string
DEX ; decrement element size, $03
DEX ; decrement element size, $02
LAB_D27D
STX FBUFPT ; save element size
LDA COUNT ; get dimensions count
INY ; increment index ..
INY ; .. to array ..
INY ; .. dimension count
STA (TMPPTR),Y ; save array dimension count
LAB_D286
LDX #$0B ; set default dimension size low byte
LDA #$00 ; set default dimension size high byte
BIT DIMFLG ; test DIM flag
BVC LAB_D296 ; if default to be used don't pull a dimension
PLA ; pull dimension size low byte
CLC ; clear carry for add
ADC #$01 ; add 1, allow for zeroth element
TAX ; copy low byte to .X
PLA ; pull dimension size high byte
ADC #$00 ; add carry to high byte
LAB_D296
INY ; increment index to dimension size high byte
STA (TMPPTR),Y ; save dimension size high byte
INY ; increment index to dimension size low byte
TXA ; copy dimension size low byte
STA (TMPPTR),Y ; save dimension size low byte
JSR M16 ; compute array size
STX FBUFPT ; save result low byte
STA FBUFPT+1 ; save result high byte
LDY INDEX ; restore index
DEC COUNT ; decrement dimensions count
BNE LAB_D286 ; loop if not all done
ADC GENPTR+1 ; add array data pointer high byte
BCS LAB_D30B ; if overflow do out of memory error then warm start
STA GENPTR+1 ; save array data pointer high byte
TAY ; copy array data pointer high byte
TXA ; copy array size low byte
ADC GENPTR ; add array data pointer low byte
BCC LAB_D2B9 ; if no rollover skip the high byte increment
INY ; else increment next array pointer high byte
BEQ LAB_D30B ; if rolled over do out of memory error then warm start
LAB_D2B9
JSR RAMSPC ; check available memory, do out of memory error if no room
STA STREND ; set end of arrays low byte
STY STREND+1 ; set end of arrays high byte
; now the array is created we need to zero all the elements in it
LDA #$00 ; clear .A for array clear
INC FBUFPT+1 ; increment array size high byte, now block count
LDY FBUFPT ; get array size low byte, now index to block
BEQ LAB_D2CD ; if $00 go do the high byte decrement
LAB_D2C8
DEY ; decrement index, do 0 to n - 1
STA (GENPTR),Y ; clear array element byte
BNE LAB_D2C8 ; loop until this block done
LAB_D2CD
DEC GENPTR+1 ; decrement array pointer high byte
DEC FBUFPT+1 ; decrement block count high byte
BNE LAB_D2C8 ; loop until all blocks done
INC GENPTR+1 ; correct for last loop
SEC ; set carry for subtract
LDA STREND ; get end of arrays low byte
SBC TMPPTR ; subtract array start low byte
LDY #$02 ; index to array size low byte
STA (TMPPTR),Y ; save array size low byte
LDA STREND+1 ; get end of arrays high byte
INY ; index to array size high byte
SBC TMPPTR+1 ; subtract array start high byte
STA (TMPPTR),Y ; save array size high byte
LDA DIMFLG ; get default DIM flag
BNE LAB_D34B ; exit if this was a DIM command
; else, find element
INY ; set index to # of dimensions, the dimension indices
; are on the stack and will be removed as the position
; of the array element is calculated
ARY14
LDA (TMPPTR),Y ; get array's dimension count
STA COUNT ; save it
LDA #$00 ; clear byte
STA FBUFPT ; clear array data pointer low byte
LAB_D2F2
STA FBUFPT+1 ; save array data pointer high byte
INY ; increment index, point to array bound high byte
PLA ; pull array index low byte
TAX ; copy to .X
STA FAC1+FAC_MANT+2 ; save index low byte to FAC1 mantissa 3
PLA ; pull array index high byte
STA FAC1+FAC_MANT+3 ; save index high byte to FAC1 mantissa 4
CMP (TMPPTR),Y ; compare with array bound high byte
BCC LAB_D30E ; if within bounds continue
BNE LAB_D308 ; if outside bounds do bad subscript error
; else high byte was = so test low bytes
INY ; index to array bound low byte
TXA ; get array index low byte
CMP (TMPPTR),Y ; compare with array bound low byte
BCC LAB_D30F ; if within bounds continue
LAB_D308
JMP BADSUB ; do bad subscript error
LAB_D30B
JMP MEMERR ; do out of memory error then warm start
LAB_D30E
INY ; index to array bound low byte
LAB_D30F
LDA FBUFPT+1 ; get array data pointer high byte
ORA FBUFPT ; OR with array data pointer low byte
CLC ; clear carry for either add, carry always clear here ??
BEQ LAB_D320 ; if array data pointer = null skip the multiply
JSR M16 ; compute array size
TXA ; get result low byte
ADC FAC1+FAC_MANT+2 ; add index low byte from FAC1 mantissa 3
TAX ; save result low byte
TYA ; get result high byte
LDY INDEX ; restore index
LAB_D320
ADC FAC1+FAC_MANT+3 ; add index high byte from FAC1 mantissa 4
STX FBUFPT ; save array data pointer low byte
DEC COUNT ; decrement dimensions count
BNE LAB_D2F2 ; loop if dimensions still to do
STA FBUFPT+1 ; save array data pointer high byte
LDX #$05 ; set default element size
LDA VARNAM ; get variable name 1st byte
BPL LAB_D331 ; branch if not string or floating point array
DEX ; decrement element size, $04
LAB_D331
LDA VARNAM+1 ; get variable name 2nd byte
BPL LAB_D337 ; branch if not integer or string
DEX ; decrement element size, $03
DEX ; decrement element size, $02
LAB_D337
STX RESHO+2 ; save dimension size low byte
LDA #$00 ; clear dimension size high byte
JSR LAB_D355 ; compute array size
TXA ; copy array size low byte
ADC GENPTR ; add array data start pointer low byte
STA VARPNT ; save as current variable pointer low byte
TYA ; copy array size high byte
ADC GENPTR+1 ; add array data start pointer high byte
STA VARPNT+1 ; save as current variable pointer high byte
TAY ; copy high byte to .Y
LDA VARPNT ; get current variable pointer low byte
; pointer to element is now in .A.Y
LAB_D34B
RTS
;***********************************************************************************;
;
; compute array size, result in .X.Y
M16
STY INDEX ; save index
LDA (TMPPTR),Y ; get dimension size low byte
STA RESHO+2 ; save dimension size low byte
DEY ; decrement index
LDA (TMPPTR),Y ; get dimension size high byte
LAB_D355
STA RESHO+3 ; save dimension size high byte
LDA #$10 ; count = $10 (16 bit multiply)
STA LAB_5D ; save bit count
LDX #$00 ; clear result low byte
LDY #$00 ; clear result high byte
LAB_D35F
TXA ; get result low byte
ASL ; *2
TAX ; save result low byte
TYA ; get result high byte
ROL ; *2
TAY ; save result high byte
BCS LAB_D30B ; if overflow go do "Out of memory" error
ASL FBUFPT ; shift element size low byte
ROL FBUFPT+1 ; shift element size high byte
BCC LAB_D378 ; skip add if no carry
CLC ; else clear carry for add
TXA ; get result low byte
ADC RESHO+2 ; add dimension size low byte
TAX ; save result low byte
TYA ; get result high byte
ADC RESHO+3 ; add dimension size high byte
TAY ; save result high byte
BCS LAB_D30B ; if overflow go do "Out of memory" error
LAB_D378
DEC LAB_5D ; decrement bit count
BNE LAB_D35F ; loop until all done
RTS
;***********************************************************************************;
;
; perform FRE()
FRE
LDA VALTYP ; get data type flag, $FF = string, $00 = numeric
BEQ LAB_D384 ; if numeric don't pop the string
JSR LAB_D6A6 ; pop string off descriptor stack, or from top of string
; space returns with .A = length, .X=$71=pointer low byte,
; .Y=$72=pointer high byte
; FRE(n) was numeric so do this
LAB_D384
JSR GRBCOL ; go do garbage collection
SEC ; set carry for subtract
LDA FRETOP ; get bottom of string space low byte
SBC STREND ; subtract end of arrays low byte
TAY ; copy result to .Y
LDA FRETOP+1 ; get bottom of string space high byte
SBC STREND+1 ; subtract end of arrays high byte
;***********************************************************************************;
;
; convert fixed integer .A.Y to float FAC1
MAKFP
LDX #$00 ; set type = numeric
STX VALTYP ; clear data type flag, $FF = string, $00 = numeric
STA FAC1+FAC_MANT ; save FAC1 mantissa 1
STY FAC1+FAC_MANT+1 ; save FAC1 mantissa 2
LDX #$90 ; set exponent=2^16 (integer)
JMP INTFP1 ; set exp = .X, clear FAC1 3 and 4, normalise and return
;***********************************************************************************;
;
; perform POS()
POS
SEC ; set Cb for read cursor position
JSR PLOT ; read/set X,Y cursor position
LAB_D3A2
LDA #$00 ; clear high byte
BEQ MAKFP ; convert fixed integer .A.Y to float FAC1, branch always
;***********************************************************************************;
;
; check not Direct, used by DEF and INPUT
NODIRM
LDX CURLIN+1 ; get current line number high byte
INX ; increment it
BNE LAB_D34B ; return if not direct mode
; else do illegal direct error
LDX #ER_ILLDIR ; error $15, illegal direct error
.byte $2C ; makes next line BIT $1BA2
UNDEF
LDX #ER_UNDEFUN ; error $1B, undefined function error
JMP ERROR ; do error #.X then warm start
;***********************************************************************************;
;
; perform DEF
DEF
JSR FN ; check FNx syntax
JSR NODIRM ; check not direct, back here if ok
JSR LPACHK ; scan for "(", else do syntax error then warm start
LDA #$80 ; set flag for FNx
STA SUBFLG ; save subscript/FNx flag
JSR EVLVAR ; get variable address
JSR LAB_CD8D ; check if source is numeric, else do type mismatch
JSR RPACHK ; scan for ")", else do syntax error then warm start
LDA #TK_EQUAL ; get = token
JSR SYNCHR ; scan for CHR$(.A), else do syntax error then warm start
PHA ; push next character
LDA VARPNT+1 ; get current variable pointer high byte
PHA ; push it
LDA VARPNT ; get current variable pointer low byte
PHA ; push it
LDA CHRGOT+2 ; get BASIC execute pointer high byte
PHA ; push it
LDA CHRGOT+1 ; get BASIC execute pointer low byte
PHA ; push it
JSR SKIPST ; perform DATA
JMP EVFN3 ; put execute pointer and variable pointer into function
; and return
;***********************************************************************************;
;
; check FNx syntax
FN
LDA #TK_FN ; set FN token
JSR SYNCHR ; scan for CHR$(.A), else do syntax error then warm start
ORA #$80 ; set FN flag bit
STA SUBFLG ; save FN name
JSR LAB_D092 ; search for FN variable
STA DEFPNT ; save function pointer low byte
STY DEFPNT+1 ; save function pointer high byte
JMP LAB_CD8D ; check if source is numeric and return, else do type
; mismatch
;***********************************************************************************;
;
; Evaluate FNx
EVALFN
JSR FN ; check FNx syntax
LDA DEFPNT+1 ; get function pointer high byte
PHA ; push it
LDA DEFPNT ; get function pointer low byte
PHA ; push it
JSR PAREXP ; evaluate expression within parentheses
JSR LAB_CD8D ; check if source is numeric, else do type mismatch
PLA ; pop function pointer low byte
STA DEFPNT ; restore it
PLA ; pop function pointer high byte
STA DEFPNT+1 ; restore it
LDY #$02 ; index to variable pointer high byte
LDA (DEFPNT),Y ; get variable address low byte
STA VARPNT ; save current variable pointer low byte
TAX ; copy address low byte
INY ; index to variable address high byte
LDA (DEFPNT),Y ; get variable pointer high byte
BEQ UNDEF ; if high byte zero go do undefined function error
STA VARPNT+1 ; save current variable pointer high byte
INY ; index to mantissa 3
; now stack the function variable value before use
LAB_D418
LDA (VARPNT),Y ; get byte from variable
PHA ; stack it
DEY ; decrement index
BPL LAB_D418 ; loop until variable stacked
LDY VARPNT+1 ; get current variable pointer high byte
JSR STORFAC ; pack FAC1 into (.X.Y)
LDA CHRGOT+2 ; get BASIC execute pointer high byte
PHA ; push it
LDA CHRGOT+1 ; get BASIC execute pointer low byte
PHA ; push it
LDA (DEFPNT),Y ; get function execute pointer low byte
STA CHRGOT+1 ; save BASIC execute pointer low byte
INY ; index to high byte
LDA (DEFPNT),Y ; get function execute pointer high byte
STA CHRGOT+2 ; save BASIC execute pointer high byte
LDA VARPNT+1 ; get current variable pointer high byte
PHA ; push it
LDA VARPNT ; get current variable pointer low byte
PHA ; push it
JSR TYPCHK ; evaluate expression and check is numeric, else do
; type mismatch
PLA ; pull variable address low byte
STA DEFPNT ; save variable address low byte
PLA ; pull variable address high byte
STA DEFPNT+1 ; save variable address high byte
JSR CHRGOT ; scan memory
BEQ LAB_D449 ; if null (should be [EOL] marker) continue
JMP LAB_CF08 ; else syntax error then warm start
; restore BASIC execute pointer and function variable from stack
LAB_D449
PLA ; pull BASIC execute pointer low byte
STA CHRGOT+1 ; save BASIC execute pointer low byte
PLA ; pull BASIC execute pointer high byte
STA CHRGOT+2 ; save BASIC execute pointer high byte
; put execute pointer and variable pointer into function
EVFN3
LDY #$00 ; clear index
PLA ; pull BASIC execute pointer low byte
STA (DEFPNT),Y ; save to function
PLA ; pull BASIC execute pointer high byte
INY ; increment index
STA (DEFPNT),Y ; save to function
PLA ; pull current variable address low byte
INY ; increment index
STA (DEFPNT),Y ; save to function
PLA ; pull current variable address high byte
INY ; increment index
STA (DEFPNT),Y ; save to function
PLA ; pull character following '='
INY ; increment index
STA (DEFPNT),Y ; save to function
RTS
;***********************************************************************************;
;
; perform STR$()
STR
JSR LAB_CD8D ; check if source is numeric, else do type mismatch
LDY #$00 ; set string index
JSR LAB_DDDF ; convert FAC1 to string
PLA ; dump return address (skip type check)
PLA ; dump return address (skip type check)
LAB_D46F
LDA #<BASZPT ; set result string low pointer
LDY #>BASZPT ; set result string high pointer
BEQ MAKSTR ; print null terminated string to utility pointer
;***********************************************************************************;
;
; do string vector
; copy descriptor pointer and make string space .A bytes long
ALC1
LDX FAC1+3 ; get descriptor pointer low byte
LDY FAC1+4 ; get descriptor pointer high byte
STX DSCPTN ; save descriptor pointer low byte
STY DSCPTN+1 ; save descriptor pointer high byte
;***********************************************************************************;
;
; make string space .A bytes long
LAB_D47D
JSR ALCSPAC ; make space in string memory for string .A long
STX FAC1+1 ; save string pointer low byte
STY FAC1+2 ; save string pointer high byte
STA FAC1 ; save length
RTS
;***********************************************************************************;
;
; scan, set up string
; print " terminated string to utility pointer
MAKSTR
LDX #$22 ; set terminator to "
STX CHARAC ; set search character, terminator 1
STX ENDCHR ; set terminator 2
; print search or alternate terminated string to utility pointer
; source is .A.Y
LAB_D48D
STA ARISGN ; store string start low byte
STY FACOV ; store string start high byte
STA FAC1+1 ; save string pointer low byte
STY FAC1+2 ; save string pointer high byte
LDY #$FF ; set length to -1
LAB_D497
INY ; increment length
LDA (ARISGN),Y ; get byte from string
BEQ LAB_D4A8 ; exit loop if null byte [EOS]
CMP CHARAC ; compare with search character, terminator 1
BEQ LAB_D4A4 ; branch if terminator
CMP ENDCHR ; compare with terminator 2
BNE LAB_D497 ; loop if not terminator 2
LAB_D4A4
CMP #$22 ; compare with "
BEQ LAB_D4A9 ; branch if " (carry set if = !)
LAB_D4A8
CLC ; clear carry for add (only if [EOL] terminated string)
LAB_D4A9
STY FAC1+FAC_EXPT ; save length in FAC1 exponent
TYA ; copy length to .A
ADC ARISGN ; add string start low byte
STA FBUFPT ; save string end low byte
LDX FACOV ; get string start high byte
BCC LAB_D4B5 ; if no low byte overflow skip the high byte increment
INX ; else increment high byte
LAB_D4B5
STX FBUFPT+1 ; save string end high byte
LDA FACOV ; get string start high byte
BEQ LAB_D4BF ; branch if in utility area
CMP #$02 ; compare with input buffer memory high byte
BNE LAB_D4CA ; branch if not in input buffer memory
; string in input buffer or utility area, move to string
; memory
LAB_D4BF
TYA ; copy length to .A
JSR ALC1 ; copy descriptor pointer and make string space .A bytes long
LDX ARISGN ; get string start low byte
LDY FACOV ; get string start high byte
JSR LAB_D688 ; store string .A bytes long from .X.Y to utility pointer
; check for space on descriptor stack then ...
; put string address and length on descriptor stack and update stack pointers
LAB_D4CA
LDX TEMPPT ; get descriptor stack pointer
CPX #$22 ; compare with max+1
BNE LAB_D4D5 ; branch if space on string stack
; else do string too complex error
LDX #ER_FMLA2CPLX ; error $19, formula too complex error
LAB_D4D2
JMP ERROR ; do error #.X then warm start
; put string address and length on descriptor stack and update stack pointers
LAB_D4D5
LDA FAC1 ; get string length
STA $00,X ; put on string stack
LDA FAC1+1 ; get string pointer low byte
STA $01,X ; put on string stack
LDA FAC1+2 ; get string pointer high byte
STA $02,X ; put on string stack
LDY #$00 ; clear .Y
STX FAC1+3 ; save string descriptor pointer low byte
STY FAC1+4 ; save string descriptor pointer high byte, always $00
STY FACOV ; clear FAC1 rounding byte
DEY ; .Y = $FF
STY VALTYP ; save data type flag, $FF = string
STX LASTPT ; save current descriptor stack item pointer low byte
INX ; update stack pointer
INX ; update stack pointer
INX ; update stack pointer
STX TEMPPT ; set new descriptor stack pointer
RTS
; make space in string memory for string .A long
; return .X = pointer low byte, .Y = pointer high byte
ALCSPAC
LSR GARBFL ; clear garbage collected flag (b7)
; make space for string .A long
LAB_D4F6
PHA ; save string length
EOR #$FF ; complement it
SEC ; set carry for subtract, two's complement add
ADC FRETOP ; add bottom of string space low byte, subtract length
LDY FRETOP+1 ; get bottom of string space high byte
BCS LAB_D501 ; skip decrement if no underflow
DEY ; decrement bottom of string space high byte
LAB_D501
CPY STREND+1 ; compare with end of arrays high byte
BCC LAB_D516 ; do out of memory error if less
BNE LAB_D50B ; if not = skip next test
CMP STREND ; compare with end of arrays low byte
BCC LAB_D516 ; do out of memory error if less
LAB_D50B
STA FRETOP ; save bottom of string space low byte
STY FRETOP+1 ; save bottom of string space high byte
STA FRESPC ; save string utility ptr low byte
STY FRESPC+1 ; save string utility ptr high byte
TAX ; copy low byte to .X
PLA ; get string length back
RTS
LAB_D516
LDX #$10 ; error code $10, out of memory error
LDA GARBFL ; get garbage collected flag
BMI LAB_D4D2 ; if set then do error code .X
JSR GRBCOL ; else go do garbage collection
LDA #$80 ; flag for garbage collected
STA GARBFL ; set garbage collected flag
PLA ; pull length
BNE LAB_D4F6 ; go try again (loop always, length should never be = $00)
;***********************************************************************************;
;
; garbage collection routine
GRBCOL
LDX MEMSIZ ; get end of memory low byte
LDA MEMSIZ+1 ; get end of memory high byte
; re-run routine from last ending
LAB_D52A
STX FRETOP ; set bottom of string space low byte
STA FRETOP+1 ; set bottom of string space high byte
LDY #$00 ; clear index
STY DEFPNT+1 ; clear working pointer high byte
STY DEFPNT ; clear working pointer low byte
LDA STREND ; get end of arrays low byte
LDX STREND+1 ; get end of arrays high byte
STA TMPPTR ; save as highest uncollected string pointer low byte
STX TMPPTR+1 ; save as highest uncollected string pointer high byte
LDA #TEMPST ; set descriptor stack pointer
LDX #$00 ; clear .X
STA INDEX ; save descriptor stack pointer low byte
STX INDEX+1 ; save descriptor stack pointer high byte ($00)
LAB_D544
CMP TEMPPT ; compare with descriptor stack pointer
BEQ LAB_D54D ; branch if =
JSR LAB_D5C7 ; check string salvageability
BEQ LAB_D544 ; loop always
; done stacked strings, now do string variables
LAB_D54D
LDA #$07 ; set step size = $07, collecting variables
STA FOUR6 ; save garbage collection step size
LDA VARTAB ; get start of variables low byte
LDX VARTAB+1 ; get start of variables high byte
STA INDEX ; save as pointer low byte
STX INDEX+1 ; save as pointer high byte
LAB_D559
CPX ARYTAB+1 ; compare end of variables high byte,
; start of arrays high byte
BNE LAB_D561 ; branch if no high byte match
CMP ARYTAB ; else compare end of variables low byte,
; start of arrays low byte
BEQ LAB_D566 ; branch if = variable memory end
LAB_D561
JSR GCOL13 ; check variable salvageability
BEQ LAB_D559 ; loop always
; done string variables, now do string arrays
LAB_D566
STA GENPTR ; save start of arrays low byte as working pointer
STX GENPTR+1 ; save start of arrays high byte as working pointer
LDA #$03 ; set step size, collecting descriptors
STA FOUR6 ; save step size
LAB_D56E
LDA GENPTR ; get pointer low byte
LDX GENPTR+1 ; get pointer high byte
LAB_D572
CPX STREND+1 ; compare with end of arrays high byte
BNE LAB_D57D ; branch if not at end
CMP STREND ; else compare with end of arrays low byte
BNE LAB_D57D ; branch if not at end
JMP COLLECT ; collect string, tidy up and exit if at end ??
LAB_D57D
STA INDEX ; save pointer low byte
STX INDEX+1 ; save pointer high byte
LDY #$00 ; set index
LDA (INDEX),Y ; get array name first byte
TAX ; copy it
INY ; increment index
LDA (INDEX),Y ; get array name second byte
PHP ; push the flags
INY ; increment index
LDA (INDEX),Y ; get array size low byte
ADC GENPTR ; add start of this array low byte
STA GENPTR ; save start of next array low byte
INY ; increment index
LDA (INDEX),Y ; get array size high byte
ADC GENPTR+1 ; add start of this array high byte
STA GENPTR+1 ; save start of next array high byte
PLP ; restore the flags
BPL LAB_D56E ; skip if not string array
; was possibly string array so ...
TXA ; get name first byte back
BMI LAB_D56E ; skip if not string array
INY ; increment index
LDA (INDEX),Y ; get # of dimensions
LDY #$00 ; clear index
ASL ; *2
ADC #$05 ; +5 (array header size)
ADC INDEX ; add pointer low byte
STA INDEX ; save pointer low byte
BCC LAB_D5AE ; if no rollover skip the high byte increment
INC INDEX+1 ; else increment pointer high byte
LAB_D5AE
LDX INDEX+1 ; get pointer high byte
LAB_D5B0
CPX GENPTR+1 ; compare pointer high byte with end of this array high byte
BNE LAB_D5B8 ; branch if not there yet
CMP GENPTR ; compare pointer low byte with end of this array low byte
BEQ LAB_D572 ; if at end of this array go check next array
LAB_D5B8
JSR LAB_D5C7 ; check string salvageability
BEQ LAB_D5B0 ; loop
; check variable salvageability
GCOL13
LDA (INDEX),Y ; get variable name first byte
BMI LAB_D5F6 ; add step and exit if not string
INY ; increment index
LDA (INDEX),Y ; get variable name second byte
BPL LAB_D5F6 ; add step and exit if not string
INY ; increment index
; check string salvageability
LAB_D5C7
LDA (INDEX),Y ; get string length
BEQ LAB_D5F6 ; add step and exit if null string
INY ; increment index
LDA (INDEX),Y ; get string pointer low byte
TAX ; copy to .X
INY ; increment index
LDA (INDEX),Y ; get string pointer high byte
CMP FRETOP+1 ; compare string pointer high byte with bottom of string
; space high byte
BCC LAB_D5DC ; if bottom of string space greater go test against highest
; uncollected string
BNE LAB_D5F6 ; if bottom of string space less string has been collected
; so go update pointers, step to next and return
; high bytes were equal so test low bytes
CPX FRETOP ; compare string pointer low byte with bottom of string
; space low byte
BCS LAB_D5F6 ; if bottom of string space less string has been collected
; so go update pointers, step to next and return
; else test string against highest uncollected string so far
LAB_D5DC
CMP TMPPTR+1 ; compare string pointer high byte with highest uncollected
; string high byte
BCC LAB_D5F6 ; if highest uncollected string is greater then go update
; pointers, step to next and return
BNE LAB_D5E6 ; if highest uncollected string is less then go set this
; string as highest uncollected so far
; high bytes were equal so test low bytes
CPX TMPPTR ; compare string pointer low byte with highest uncollected
; string low byte
BCC LAB_D5F6 ; if highest uncollected string is greater then go update
; pointers, step to next and return
; else set current string as highest uncollected string
LAB_D5E6
STX TMPPTR ; save string pointer low byte as highest uncollected string
; low byte
STA TMPPTR+1 ; save string pointer high byte as highest uncollected
; string high byte
LDA INDEX ; get descriptor pointer low byte
LDX INDEX+1 ; get descriptor pointer high byte
STA DEFPNT ; save working pointer high byte
STX DEFPNT+1 ; save working pointer low byte
LDA FOUR6 ; get step size
STA JMPER+1 ; copy step size
LAB_D5F6
LDA FOUR6 ; get step size
CLC ; clear carry for add
ADC INDEX ; add pointer low byte
STA INDEX ; save pointer low byte
BCC LAB_D601 ; if no rollover skip the high byte increment
INC INDEX+1 ; else increment pointer high byte
LAB_D601
LDX INDEX+1 ; get pointer high byte
LDY #$00 ; flag not moved
RTS
;***********************************************************************************;
;
; collect string
COLLECT
LDA DEFPNT+1 ; get working pointer low byte
ORA DEFPNT ; OR working pointer high byte
BEQ LAB_D601 ; exit if nothing to collect
LDA JMPER+1 ; get copied step size
AND #$04 ; mask step size, $04 for variables, $00 for array or stack
LSR ; >> 1
TAY ; copy to index
STA JMPER+1 ; save offset to descriptor start
LDA (DEFPNT),Y ; get string length low byte
ADC TMPPTR ; add string start low byte
STA GEN2PTR ; set block end low byte
LDA TMPPTR+1 ; get string start high byte
ADC #$00 ; add carry
STA GEN2PTR+1 ; set block end high byte
LDA FRETOP ; get bottom of string space low byte
LDX FRETOP+1 ; get bottom of string space high byte
STA GENPTR ; save destination end low byte
STX GENPTR+1 ; save destination end high byte
JSR MOVEBL ; open up space in memory, don't set array end. this
; copies the string from where it is to the end of the
; uncollected string memory
LDY JMPER+1 ; restore offset to descriptor start
INY ; increment index to string pointer low byte
LDA GENPTR ; get new string pointer low byte
STA (DEFPNT),Y ; save new string pointer low byte
TAX ; copy string pointer low byte
INC GENPTR+1 ; increment new string pointer high byte
LDA GENPTR+1 ; get new string pointer high byte
INY ; increment index to string pointer high byte
STA (DEFPNT),Y ; save new string pointer high byte
JMP LAB_D52A ; re-run routine from last ending, .X.A holds new bottom
; of string memory pointer
;***********************************************************************************;
;
; concatenate
; add strings, the first string is in the descriptor, the second string is in line
ADDSTR
LDA FAC1+4 ; get descriptor pointer high byte
PHA ; put on stack
LDA FAC1+3 ; get descriptor pointer low byte
PHA ; put on stack
JSR EVAL ; get value from line
JSR LAB_CD8F ; check if source is string, else do type mismatch
PLA ; get descriptor pointer low byte back
STA ARISGN ; set pointer low byte
PLA ; get descriptor pointer high byte back
STA FACOV ; set pointer high byte
LDY #$00 ; clear index
LDA (ARISGN),Y ; get length of first string from descriptor
CLC ; clear carry for add
ADC (FAC1+3),Y ; add length of second string
BCC LAB_D65D ; if no overflow continue
LDX #ER_STR2LONG ; else error $17, string too long error
JMP ERROR ; do error #.X then warm start
LAB_D65D
JSR ALC1 ; copy descriptor pointer and make string space .A bytes long
JSR XFERSTR ; copy string from descriptor to utility pointer
LDA DSCPTN ; get descriptor pointer low byte
LDY DSCPTN+1 ; get descriptor pointer high byte
JSR LAB_D6AA ; pop (.Y.A) descriptor off stack or from top of string space
; returns with .A = length, .X = pointer low byte,
; .Y = pointer high byte
JSR LAB_D68C ; store string from pointer to utility pointer
LDA ARISGN ; get descriptor pointer low byte
LDY FACOV ; get descriptor pointer high byte
JSR LAB_D6AA ; pop (.Y.A) descriptor off stack or from top of string space
; returns with .A = length, .X = pointer low byte,
; .Y = pointer high byte
JSR LAB_D4CA ; check space on descriptor stack then put string address
; and length on descriptor stack and update stack pointers
JMP LAB_CDB8 ; continue evaluation
;***********************************************************************************;
;
; copy string from descriptor to utility pointer
XFERSTR
LDY #$00 ; clear index
LDA (ARISGN),Y ; get string length
PHA ; save it
INY ; increment index
LDA (ARISGN),Y ; get string pointer low byte
TAX ; copy to .X
INY ; increment index
LDA (ARISGN),Y ; get string pointer high byte
TAY ; copy to .Y
PLA ; get length back
LAB_D688
STX INDEX ; save string pointer low byte
STY INDEX+1 ; save string pointer high byte
;***********************************************************************************;
;
; store string from pointer to utility pointer
LAB_D68C
TAY ; copy length as index
BEQ LAB_D699 ; branch if null string
PHA ; save length
LAB_D690
DEY ; decrement length/index
LDA (INDEX),Y ; get byte from string
STA (FRESPC),Y ; save byte to destination
TYA ; copy length/index
BNE LAB_D690 ; loop if not all done yet
PLA ; restore length
LAB_D699
CLC ; clear carry for add
ADC FRESPC ; add string utility ptr low byte
STA FRESPC ; save string utility ptr low byte
BCC LAB_D6A2 ; if no rollover skip the high byte increment
INC FRESPC+1 ; increment string utility ptr high byte
LAB_D6A2
RTS
;***********************************************************************************;
;
; evaluate string
DELST
JSR LAB_CD8F ; check if source is string, else do type mismatch
; pop string off descriptor stack, or from top of string space
; returns with .A = length, .X = pointer low byte, .Y = pointer high byte
LAB_D6A6
LDA FAC1+3 ; get descriptor pointer low byte
LDY FAC1+4 ; get descriptor pointer high byte
; pop (.Y.A) descriptor off stack or from top of string space
; returns with .A = length, .X = pointer low byte, .Y = pointer high byte
LAB_D6AA
STA INDEX ; save string pointer low byte
STY INDEX+1 ; save string pointer high byte
JSR DELTSD ; clean descriptor stack, .Y.A = pointer
PHP ; save status flags
LDY #$00 ; clear index
LDA (INDEX),Y ; get length from string descriptor
PHA ; put on stack
INY ; increment index
LDA (INDEX),Y ; get string pointer low byte from descriptor
TAX ; copy to .X
INY ; increment index
LDA (INDEX),Y ; get string pointer high byte from descriptor
TAY ; copy to .Y
PLA ; get string length back
PLP ; restore status
BNE LAB_D6D6 ; branch if pointer <> last_sl,last_sh
CPY FRETOP+1 ; compare with bottom of string space high byte
BNE LAB_D6D6 ; branch if <>
CPX FRETOP ; else compare with bottom of string space low byte
BNE LAB_D6D6 ; branch if <>
PHA ; save string length
CLC ; clear carry for add
ADC FRETOP ; add bottom of string space low byte
STA FRETOP ; set bottom of string space low byte
BCC LAB_D6D5 ; skip increment if no overflow
INC FRETOP+1 ; increment bottom of string space high byte
LAB_D6D5
PLA ; restore string length
LAB_D6D6
STX INDEX ; save string pointer low byte
STY INDEX+1 ; save string pointer high byte
RTS
;***********************************************************************************;
;
; clean descriptor stack, .Y.A = pointer
; checks if .A.Y is on the descriptor stack, if so does a stack discard
DELTSD
CPY LASTPT+1 ; compare high byte with current descriptor stack item
; pointer high byte
BNE LAB_D6EB ; exit if <>
CMP LASTPT ; compare low byte with current descriptor stack item
; pointer low byte
BNE LAB_D6EB ; exit if <>
STA TEMPPT ; set descriptor stack pointer
SBC #$03 ; update last string pointer low byte
STA LASTPT ; save current descriptor stack item pointer low byte
LDY #$00 ; clear high byte
LAB_D6EB
RTS
;***********************************************************************************;
;
; perform CHR$()
CHR
JSR LAB_D7A1 ; evaluate byte expression, result in .X
TXA ; copy to .A
PHA ; save character
LDA #$01 ; string is single byte
JSR LAB_D47D ; make string space A bytes long
PLA ; get character back
LDY #$00 ; clear index
STA (FAC1+1),Y ; save byte in string - byte IS string!
PLA ; dump return address (skip type check)
PLA ; dump return address (skip type check)
JMP LAB_D4CA ; check space on descriptor stack then put string address
; and length on descriptor stack and update stack pointers
;***********************************************************************************;
;
; perform LEFT$()
LEFT
JSR FINLMR ; pull string data and byte parameter from stack
; return pointer in descriptor, byte in .A (and .X), .Y=0
CMP (DSCPTN),Y ; compare byte parameter with string length
TYA ; clear .A
LAB_D706
BCC LAB_D70C ; branch if string length > byte parameter
LDA (DSCPTN),Y ; else make parameter = length
TAX ; copy to byte parameter copy
TYA ; clear string start offset
LAB_D70C
PHA ; save string start offset
LAB_D70D
TXA ; copy byte parameter (or string length if <)
LAB_D70E
PHA ; save string length
JSR LAB_D47D ; make string space .A bytes long
LDA DSCPTN ; get descriptor pointer low byte
LDY DSCPTN+1 ; get descriptor pointer high byte
JSR LAB_D6AA ; pop (.Y.A) descriptor off stack or from top of string space
; returns with .A = length, .X = pointer low byte,
; .Y = pointer high byte
PLA ; get string length back
TAY ; copy length to .Y
PLA ; get string start offset back
CLC ; clear carry for add
ADC INDEX ; add start offset to string start pointer low byte
STA INDEX ; save string start pointer low byte
BCC LAB_D725 ; if no overflow skip the high byte increment
INC INDEX+1 ; else increment string start pointer high byte
LAB_D725
TYA ; copy length to .A
JSR LAB_D68C ; store string from pointer to utility pointer
JMP LAB_D4CA ; check space on descriptor stack then put string address
; and length on descriptor stack and update stack pointers
;***********************************************************************************;
;
; perform RIGHT$()
RIGHT
JSR FINLMR ; pull string data and byte parameter from stack
; return pointer in descriptor, byte in .A (and .X), .Y=0
CLC ; clear carry for add - 1
SBC (DSCPTN),Y ; subtract string length
EOR #$FF ; invert it (.A=LEN(expression$)-l)
JMP LAB_D706 ; go do rest of LEFT$()
;***********************************************************************************;
;
; perform MID$()
MID
LDA #$FF ; set default length = 255
STA FAC1+4 ; save default length
JSR CHRGOT ; scan memory
CMP #$29 ; compare with ")"
BEQ LAB_D748 ; branch if = ")" (skip second byte get)
JSR COMCHK ; scan for ",", else do syntax error then warm start
JSR LAB_D79E ; get byte parameter
LAB_D748
JSR FINLMR ; pull string data and byte parameter from stack
; return pointer in descriptor, byte in .A (and .X), .Y=0
BEQ LAB_D798 ; if null do illegal quantity error then warm start
DEX ; decrement start index
TXA ; copy to .A
PHA ; save string start offset
CLC ; clear carry for sub - 1
LDX #$00 ; clear output string length
SBC (DSCPTN),Y ; subtract string length
BCS LAB_D70D ; if start>string length go do null string
EOR #$FF ; complement -length
CMP FAC1+4 ; compare byte parameter
BCC LAB_D70E ; if length>remaining string go do RIGHT$
LDA FAC1+4 ; get length byte
BCS LAB_D70E ; go do string copy, branch always
;***********************************************************************************;
;
; pull string data and byte parameter from stack
; return pointer in descriptor, byte in .A (and .X), .Y=0
FINLMR
JSR RPACHK ; scan for ")", else do syntax error then warm start
PLA ; pull return address low byte
TAY ; save return address low byte
PLA ; pull return address high byte
STA JMPER+1 ; save return address high byte
PLA ; dump call to function vector low byte
PLA ; dump call to function vector high byte
PLA ; pull byte parameter
TAX ; copy byte parameter to .X
PLA ; pull string pointer low byte
STA DSCPTN ; save it
PLA ; pull string pointer high byte
STA DSCPTN+1 ; save it
LDA JMPER+1 ; get return address high byte
PHA ; back on stack
TYA ; get return address low byte
PHA ; back on stack
LDY #$00 ; clear index
TXA ; copy byte parameter
RTS
;***********************************************************************************;
;
; perform LEN()
LEN
JSR GSINFO ; evaluate string, get length in .A (and .Y)
JMP LAB_D3A2 ; convert .Y to byte in FAC1 and return
;***********************************************************************************;
;
; evaluate string, get length in .Y
GSINFO
JSR DELST ; evaluate string
LDX #$00 ; set data type = numeric
STX VALTYP ; clear data type flag, $FF = string, $00 = numeric
TAY ; copy length to .Y
RTS
;***********************************************************************************;
;
; perform ASC()
ASC
JSR GSINFO ; evaluate string, get length in .A (and .Y)
BEQ LAB_D798 ; if null do illegal quantity error then warm start
LDY #$00 ; set index to first character
LDA (INDEX),Y ; get byte
TAY ; copy to .Y
JMP LAB_D3A2 ; convert .Y to byte in FAC1 and return
;***********************************************************************************;
;
; do illegal quantity error then warm start
LAB_D798
JMP ILQUAN ; do illegal quantity error then warm start
;***********************************************************************************;
;
; scan and get byte parameter
GETBYT
JSR CHRGET ; increment and scan memory
; get byte parameter
LAB_D79E
JSR TYPCHK ; evaluate expression and check is numeric, else do
; type mismatch
; evaluate byte expression, result in .X
LAB_D7A1
JSR LAB_D1B8 ; evaluate integer expression, sign check
LDX FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
BNE LAB_D798 ; if not null do illegal quantity error then warm start
LDX FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
JMP CHRGOT ; scan memory and return
;***********************************************************************************;
;
; perform VAL()
VAL
JSR GSINFO ; evaluate string, get length in .A (and .Y)
BNE LAB_D7B5 ; if not a null string go evaluate it
; string was null so set result = $00
JMP ZERFAC ; clear FAC1 exponent and sign and return
LAB_D7B5
LDX CHRGOT+1 ; get BASIC execute pointer low byte
LDY CHRGOT+2 ; get BASIC execute pointer high byte
STX FBUFPT ; save BASIC execute pointer low byte
STY FBUFPT+1 ; save BASIC execute pointer high byte
LDX INDEX ; get string pointer low byte
STX CHRGOT+1 ; save BASIC execute pointer low byte
CLC ; clear carry for add
ADC INDEX ; add string length
STA INDEX+2 ; save string end low byte
LDX INDEX+1 ; get string pointer high byte
STX CHRGOT+2 ; save BASIC execute pointer high byte
BCC LAB_D7CD ; if no rollover skip the high byte increment
INX ; increment string end high byte
LAB_D7CD
STX INDEX+3 ; save string end high byte
LDY #$00 ; set index to $00
LDA (INDEX+2),Y ; get string end byte
PHA ; push it
TYA ; clear .A
STA (INDEX+2),Y ; terminate string with $00
JSR CHRGOT ; scan memory
JSR ASCFLT ; get FAC1 from string
PLA ; restore string end byte
LDY #$00 ; clear index
STA (INDEX+2),Y ; put string end byte back
; restore BASIC execute pointer from temp
LAB_D7E2
LDX FBUFPT ; get BASIC execute pointer low byte back
LDY FBUFPT+1 ; get BASIC execute pointer high byte back
STX CHRGOT+1 ; save BASIC execute pointer low byte
STY CHRGOT+2 ; save BASIC execute pointer high byte
RTS
;***********************************************************************************;
;
; get parameters for POKE/WAIT
GETAD
JSR TYPCHK ; evaluate expression and check is numeric, else do
; type mismatch
JSR MAKADR ; convert FAC1 to integer in temporary integer
LAB_D7F1
JSR COMCHK ; scan for ",", else do syntax error then warm start
JMP LAB_D79E ; get byte parameter and return
;***********************************************************************************;
;
; convert FAC1 to integer in temporary integer
MAKADR
LDA FAC1+FAC_SIGN ; get FAC1 sign
BMI LAB_D798 ; if -ve do illegal quantity error then warm start
LDA FAC1+FAC_EXPT ; get FAC1 exponent
CMP #$91 ; compare with exponent = 2^16
BCS LAB_D798 ; if >= do illegal quantity error then warm start
JSR FPINT ; convert FAC1 floating to fixed
LDA FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
LDY FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
STY LINNUM ; save temporary integer low byte
STA LINNUM+1 ; save temporary integer high byte
RTS
;***********************************************************************************;
;
; perform PEEK()
PEEK
LDA LINNUM+1 ; get line number high byte
PHA ; save line number high byte
LDA LINNUM ; get line number low byte
PHA ; save line number low byte
JSR MAKADR ; convert FAC1 to integer in temporary integer
LDY #$00 ; clear index
LDA (LINNUM),Y ; read byte
TAY ; copy byte to .A
PLA ; pull byte
STA LINNUM ; restore line number low byte
PLA ; pull byte
STA LINNUM+1 ; restore line number high byte
JMP LAB_D3A2 ; convert .Y to byte in FAC1 and return
;***********************************************************************************;
;
; perform POKE
POKE
JSR GETAD ; get parameters for POKE/WAIT
TXA ; copy byte to .A
LDY #$00 ; clear index
STA (LINNUM),Y ; write byte
RTS
;***********************************************************************************;
;
; perform WAIT
WAIT
JSR GETAD ; get parameters for POKE/WAIT
STX FORPNT ; save byte
LDX #$00 ; clear mask
JSR CHRGOT ; scan memory
BEQ LAB_D83C ; skip if no third argument
JSR LAB_D7F1 ; scan for "," and get byte, else syntax error then
; warm start
LAB_D83C
STX FORPNT+1 ; save XOR argument
LDY #$00 ; clear index
LAB_D840
LDA (LINNUM),Y ; get byte via temporary integer (address)
EOR FORPNT+1 ; XOR with second argument (mask)
AND FORPNT ; AND with first argument (byte)
BEQ LAB_D840 ; loop if result is zero
LAB_D848
RTS
;***********************************************************************************;
;
; add 0.5 to FAC1 (round FAC1)
ADD05
LDA #<FLP05 ; set 0.5 pointer low byte
LDY #>FLP05 ; set 0.5 pointer high byte
JMP LAPLUS ; add (.A.Y) to FAC1
;***********************************************************************************;
;
; perform subtraction, FAC1 from (.A.Y)
LAMIN
JSR LODARG ; unpack memory (.A.Y) into FAC2
; perform subtraction, FAC1 from FAC2
SUB
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
EOR #$FF ; complement it
STA FAC1+FAC_SIGN ; save FAC1 sign (b7)
EOR FAC2+FAC_SIGN ; XOR with FAC2 sign (b7)
STA ARISGN ; save sign compare (FAC1 XOR FAC2)
LDA FAC1+FAC_EXPT ; get FAC1 exponent
JMP PLUS ; add FAC2 to FAC1 and return
PLUS1
JSR LAB_D999 ; shift FAC.X .A times right (>8 shifts)
BCC LAB_D8A3 ; go subtract the mantissas, branch always
;***********************************************************************************;
;
; add (.A.Y) to FAC1
LAPLUS
JSR LODARG ; unpack memory (.A.Y) into FAC2
; add FAC2 to FAC1
PLUS
BNE LAB_D86F ; if FAC1 is not zero go do the add
JMP ATOF ; FAC1 was zero so copy FAC2 to FAC1 and return
; FAC1 is non zero
LAB_D86F
LDX FACOV ; get FAC1 rounding byte
STX JMPER+2 ; save as FAC2 rounding byte
LDX #FAC2 ; set index to FAC2 exponent address
LDA FAC2 ; get FAC2 exponent
LAB_D877
TAY ; copy exponent
BEQ LAB_D848 ; exit if zero
SEC ; set carry for subtract
SBC FAC1+FAC_EXPT ; subtract FAC1 exponent
BEQ LAB_D8A3 ; if equal go add mantissas
BCC LAB_D893 ; if FAC2 < FAC1 then go shift FAC2 right
; else FAC2 > FAC1
STY FAC1+FAC_EXPT ; save FAC1 exponent
LDY FAC2+FAC_SIGN ; get FAC2 sign (b7)
STY FAC1+FAC_SIGN ; save FAC1 sign (b7)
EOR #$FF ; complement .A
ADC #$00 ; +1, two's complement, carry is set
LDY #$00 ; clear .Y
STY JMPER+2 ; clear FAC2 rounding byte
LDX #FAC1+FAC_EXPT ; set index to FAC1 exponent address
BNE LAB_D897 ; branch always
; FAC2 < FAC1
LAB_D893
LDY #$00 ; clear .Y
STY FACOV ; clear FAC1 rounding byte
LAB_D897
CMP #$F9 ; compare exponent diff with $F9
BMI PLUS1 ; branch if range $79-$F8
TAY ; copy exponent difference to .Y
LDA FACOV ; get FAC1 rounding byte
LSR FAC_MANT,X ; shift FAC.X mantissa 1
JSR LAB_D9B0 ; shift FAC.X .Y times right
; exponents are equal now do mantissa subtract
LAB_D8A3
BIT ARISGN ; test sign compare (FAC1 XOR FAC2)
BPL NORMLZ ; if = add FAC2 mantissa to FAC1 mantissa and return
LDY #FAC1+FAC_EXPT ; set index to FAC1 exponent address
CPX #FAC2+FAC_EXPT ; compare .X to FAC2 exponent address
BEQ LAB_D8AF ; branch if =
LDY #FAC2+FAC_EXPT ; else set index to FAC2 exponent address
; subtract smaller from bigger (take sign of bigger)
LAB_D8AF
SEC ; set carry for subtract
EOR #$FF ; ones' complement .A
ADC JMPER+2 ; add FAC2 rounding byte
STA FACOV ; save FAC1 rounding byte
LDA FAC_MANT+3,Y ; get FAC.Y mantissa 4
SBC FAC_MANT+3,X ; subtract FAC.X mantissa 4
STA FAC1+FAC_MANT+3 ; save FAC1 mantissa 4
LDA FAC_MANT+2,Y ; get FAC.Y mantissa 3
SBC FAC_MANT+2,X ; subtract FAC.X mantissa 3
STA FAC1+FAC_MANT+2 ; save FAC1 mantissa 3
LDA FAC_MANT+1,Y ; get FAC.Y mantissa 2
SBC FAC_MANT+1,X ; subtract FAC.X mantissa 2
STA FAC1+FAC_MANT+1 ; save FAC1 mantissa 2
LDA FAC_MANT,Y ; get FAC.Y mantissa 1
SBC FAC_MANT,X ; subtract FAC.X mantissa 1
STA FAC1+FAC_MANT ; save FAC1 mantissa 1
;***********************************************************************************;
;
; do ABS and normalise FAC1
LAB_D8D2
BCS LAB_D8D7 ; branch if number is +ve
JSR COMFAC ; negate FAC1
; normalise FAC1
LAB_D8D7
LDY #$00 ; clear .Y
TYA ; clear .A
CLC ; clear carry for add
LAB_D8DB
LDX FAC1+FAC_MANT ; get FAC1 mantissa 1
BNE LAB_D929 ; if not zero normalise FAC1
LDX FAC1+FAC_MANT+1 ; get FAC1 mantissa 2
STX FAC1+FAC_MANT ; save FAC1 mantissa 1
LDX FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
STX FAC1+FAC_MANT+1 ; save FAC1 mantissa 2
LDX FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
STX FAC1+FAC_MANT+2 ; save FAC1 mantissa 3
LDX FACOV ; get FAC1 rounding byte
STX FAC1+FAC_MANT+3 ; save FAC1 mantissa 4
STY FACOV ; clear FAC1 rounding byte
ADC #$08 ; add x to exponent offset
CMP #$20 ; compare with $20, max offset, all bits would be = 0
BNE LAB_D8DB ; loop if not max
;***********************************************************************************;
;
; clear FAC1 exponent and sign
ZERFAC
LDA #$00 ; clear .A
LAB_D8F9
STA FAC1+FAC_EXPT ; set FAC1 exponent
; save FAC1 sign
LAB_D8FB
STA FAC1+FAC_SIGN ; save FAC1 sign (b7)
RTS
;***********************************************************************************;
;
; add FAC2 mantissa to FAC1 mantissa
NORMLZ
ADC JMPER+2 ; add FAC2 rounding byte
STA FACOV ; save FAC1 rounding byte
LDA FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
ADC FAC2+FAC_MANT+3 ; add FAC2 mantissa 4
STA FAC1+FAC_MANT+3 ; save FAC1 mantissa 4
LDA FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
ADC FAC2+FAC_MANT+2 ; add FAC2 mantissa 3
STA FAC1+FAC_MANT+2 ; save FAC1 mantissa 3
LDA FAC1+FAC_MANT+1 ; get FAC1 mantissa 2
ADC FAC2+FAC_MANT+1 ; add FAC2 mantissa 2
STA FAC1+FAC_MANT+1 ; save FAC1 mantissa 2
LDA FAC1+FAC_MANT ; get FAC1 mantissa 1
ADC FAC2+FAC_MANT ; add FAC2 mantissa 1
STA FAC1+FAC_MANT ; save FAC1 mantissa 1
JMP LAB_D936 ; test and normalise FAC1 for Cb=0/1
LAB_D91D
ADC #$01 ; add 1 to exponent offset
ASL FACOV ; shift FAC1 rounding byte
ROL FAC1+FAC_MANT+3 ; shift FAC1 mantissa 4
ROL FAC1+FAC_MANT+2 ; shift FAC1 mantissa 3
ROL FAC1+FAC_MANT+1 ; shift FAC1 mantissa 2
ROL FAC1+FAC_MANT ; shift FAC1 mantissa 1
;***********************************************************************************;
;
; normalise FAC1
LAB_D929
BPL LAB_D91D ; loop if not normalised
SEC ; set carry for subtract
SBC FAC1+FAC_EXPT ; subtract FAC1 exponent
BCS ZERFAC ; branch if underflow (set result = $0)
EOR #$FF ; complement exponent
ADC #$01 ; +1 (two's complement)
STA FAC1+FAC_EXPT ; save FAC1 exponent
; test and normalise FAC1 for Cb=0/1
LAB_D936
BCC LAB_D946 ; exit if no overflow
; normalise FAC1 for Cb=1
LAB_D938
INC FAC1+FAC_EXPT ; increment FAC1 exponent
BEQ OVERFL ; if zero do overflow error then warm start
ROR FAC1+FAC_MANT ; shift FAC1 mantissa 1
ROR FAC1+FAC_MANT+1 ; shift FAC1 mantissa 2
ROR FAC1+FAC_MANT+2 ; shift FAC1 mantissa 3
ROR FAC1+FAC_MANT+3 ; shift FAC1 mantissa 4
ROR FACOV ; shift FAC1 rounding byte
LAB_D946
RTS
;***********************************************************************************;
;
; negate FAC1
COMFAC
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
EOR #$FF ; complement it
STA FAC1+FAC_SIGN ; save FAC1 sign (b7)
; two's complement FAC1 mantissa
LAB_D94D
LDA FAC1+FAC_MANT ; get FAC1 mantissa 1
EOR #$FF ; complement it
STA FAC1+FAC_MANT ; save FAC1 mantissa 1
LDA FAC1+FAC_MANT+1 ; get FAC1 mantissa 2
EOR #$FF ; complement it
STA FAC1+FAC_MANT+1 ; save FAC1 mantissa 2
LDA FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
EOR #$FF ; complement it
STA FAC1+FAC_MANT+2 ; save FAC1 mantissa 3
LDA FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
EOR #$FF ; complement it
STA FAC1+FAC_MANT+3 ; save FAC1 mantissa 4
LDA FACOV ; get FAC1 rounding byte
EOR #$FF ; complement it
STA FACOV ; save FAC1 rounding byte
INC FACOV ; increment FAC1 rounding byte
BNE LAB_D97D ; exit if no overflow
; increment FAC1 mantissa
LAB_D96F
INC FAC1+FAC_MANT+3 ; increment FAC1 mantissa 4
BNE LAB_D97D ; finished if no rollover
INC FAC1+FAC_MANT+2 ; increment FAC1 mantissa 3
BNE LAB_D97D ; finished if no rollover
INC FAC1+FAC_MANT+1 ; increment FAC1 mantissa 2
BNE LAB_D97D ; finished if no rollover
INC FAC1+FAC_MANT ; increment FAC1 mantissa 1
LAB_D97D
RTS
;***********************************************************************************;
;
; do overflow error then warm start
OVERFL
LDX #ER_OVFLOW ; error $0F, overflow error
JMP ERROR ; do error #.X then warm start
;***********************************************************************************;
;
; shift FACtemp << A+8 times
ASRRES
LDX #$25 ; set offset to FACtemp
LAB_D985
LDY FAC_MANT+3,X ; get FAC.X mantissa 4
STY FACOV ; save as FAC1 rounding byte
LDY FAC_MANT+2,X ; get FAC.X mantissa 3
STY FAC_MANT+3,X ; save FAC.X mantissa 4
LDY FAC_MANT+1,X ; get FAC.X mantissa 2
STY FAC_MANT+2,X ; save FAC.X mantissa 3
LDY FAC_MANT,X ; get FAC.X mantissa 1
STY FAC_MANT+1,X ; save FAC.X mantissa 2
LDY BITS ; get FAC1 overflow byte
STY FAC_MANT,X ; save FAC.X mantissa 1
; shift FAC.X -.A times right (> 8 shifts)
LAB_D999
ADC #$08 ; add 8 to shift count
BMI LAB_D985 ; go do 8 shift if still -ve
BEQ LAB_D985 ; go do 8 shift if zero
SBC #$08 ; else subtract 8 again
TAY ; save count to .Y
LDA FACOV ; get FAC1 rounding byte
BCS LAB_D9BA ;.
LAB_D9A6
ASL FAC_MANT,X ; shift FAC.X mantissa 1
BCC LAB_D9AC ; branch if +ve
INC FAC_MANT,X ; this sets b7 eventually
LAB_D9AC
ROR FAC_MANT,X ; shift FAC.X mantissa 1 (correct for ASL)
ROR FAC_MANT,X ; shift FAC.X mantissa 1 (put carry in b7)
; shift FAC.X .Y times right
LAB_D9B0
ROR FAC_MANT+1,X ; shift FAC.X mantissa 2
ROR FAC_MANT+2,X ; shift FAC.X mantissa 3
ROR FAC_MANT+3,X ; shift FAC.X mantissa 4
ROR ; shift FAC.X rounding byte
INY ; increment exponent diff
BNE LAB_D9A6 ; branch if range adjust not complete
LAB_D9BA
CLC ; just clear it
RTS
;***********************************************************************************;
;
; constants and series for LOG(n)
FPC1
.byte $81,$00,$00,$00,$00 ; 1
LOGCON
.byte $03 ; series counter
.byte $7F,$5E,$56,$CB,$79 ; 0.43425 LOG10(e)
.byte $80,$13,$9B,$0B,$64 ; 0.57658
.byte $80,$76,$38,$93,$16 ; 0.9618
.byte $82,$38,$AA,$3B,$20 ; 2.88539 2/LOG(2)
LAB_D9D6
.byte $80,$35,$04,$F3,$34 ; 0.70711 1/root 2
LAB_D9DB
.byte $81,$35,$04,$F3,$34 ; 1.41421 root 2
LAB_D9E0
.byte $80,$80,$00,$00,$00 ; -0.5 1/2
LAB_D9E5
.byte $80,$31,$72,$17,$F8 ; 0.69315 LOG(2)
;***********************************************************************************;
;
; perform LOG()
LOG
JSR SGNFAC ; test sign and zero
BEQ LAB_D9F1 ; if zero do illegal quantity error then warm start
BPL LAB_D9F4 ; skip error if +ve
LAB_D9F1
JMP ILQUAN ; do illegal quantity error then warm start
LAB_D9F4
LDA FAC1+FAC_EXPT ; get FAC1 exponent
SBC #$7F ; normalise it
PHA ; save it
LDA #$80 ; set exponent to zero
STA FAC1+FAC_EXPT ; save FAC1 exponent
LDA #<LAB_D9D6 ; pointer to 1/root 2 low byte
LDY #>LAB_D9D6 ; pointer to 1/root 2 high byte
JSR LAPLUS ; add (.A.Y) to FAC1 (1/root2)
LDA #<LAB_D9DB ; pointer to root 2 low byte
LDY #>LAB_D9DB ; pointer to root 2 high byte
JSR LADIV ; convert .A.Y and do (.A.Y)/FAC1 (root2/(x+(1/root2)))
LDA #<FPC1 ; pointer to 1 low byte
LDY #>FPC1 ; pointer to 1 high byte
JSR LAMIN ; subtract FAC1 ((root2/(x+(1/root2)))-1) from (.A.Y)
LDA #<LOGCON ; pointer to series for LOG(n) low byte
LDY #>LOGCON ; pointer to series for LOG(n) high byte
JSR SEREVL ; ^2 then series evaluation
LDA #<LAB_D9E0 ; pointer to -0.5 low byte
LDY #>LAB_D9E0 ; pointer to -0.5 high byte
JSR LAPLUS ; add (.A.Y) to FAC1
PLA ; restore FAC1 exponent
JSR ASCI8 ; evaluate new ASCII digit
LDA #<LAB_D9E5 ; pointer to LOG(2) low byte
LDY #>LAB_D9E5 ; pointer to LOG(2) high byte
; do convert .A.Y, FAC1*(.A.Y)
TIMES
JSR LODARG ; unpack memory (.A.Y) into FAC2
MULT
BNE LAB_DA30 ; multiply FAC1 by FAC2 ??
JMP LAB_DA8B ; exit if zero
LAB_DA30
JSR MULDIV ; test and adjust accumulators
LDA #$00 ; clear .A
STA RESHO ; clear temp mantissa 1
STA RESHO+1 ; clear temp mantissa 2
STA RESHO+2 ; clear temp mantissa 3
STA RESHO+3 ; clear temp mantissa 4
LDA FACOV ; get FAC1 rounding byte
JSR TIMES3 ; go do shift/add FAC2
LDA FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
JSR TIMES3 ; go do shift/add FAC2
LDA FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
JSR TIMES3 ; go do shift/add FAC2
LDA FAC1+FAC_MANT+1 ; get FAC1 mantissa 2
JSR TIMES3 ; go do shift/add FAC2
LDA FAC1+FAC_MANT ; get FAC1 mantissa 1
JSR LAB_DA5E ; go do shift/add FAC2
JMP LAB_DB8F ; copy temp to FAC1, normalise and return
TIMES3
BNE LAB_DA5E ; branch if byte <> zero
JMP ASRRES ; shift FACtemp << .A+8 times
; else do shift and add
LAB_DA5E
LSR ; shift byte
ORA #$80 ; set top bit (mark for 8 times)
LAB_DA61
TAY ; copy result
BCC LAB_DA7D ; skip next if bit was zero
CLC ; clear carry for add
LDA RESHO+3 ; get temp mantissa 4
ADC FAC2+FAC_MANT+3 ; add FAC2 mantissa 4
STA RESHO+3 ; save temp mantissa 4
LDA RESHO+2 ; get temp mantissa 3
ADC FAC2+FAC_MANT+2 ; add FAC2 mantissa 3
STA RESHO+2 ; save temp mantissa 3
LDA RESHO+1 ; get temp mantissa 2
ADC FAC2+FAC_MANT+1 ; add FAC2 mantissa 2
STA RESHO+1 ; save temp mantissa 2
LDA RESHO ; get temp mantissa 1
ADC FAC2+FAC_MANT ; add FAC2 mantissa 1
STA RESHO ; save temp mantissa 1
LAB_DA7D
ROR RESHO ; shift temp mantissa 1
ROR RESHO+1 ; shift temp mantissa 2
ROR RESHO+2 ; shift temp mantissa 3
ROR RESHO+3 ; shift temp mantissa 4
ROR FACOV ; shift temp rounding byte
TYA ; get byte back
LSR ; shift byte
BNE LAB_DA61 ; loop if all bits not done
LAB_DA8B
RTS
;***********************************************************************************;
;
; unpack memory (.A.Y) into FAC2
LODARG
STA INDEX ; save pointer low byte
STY INDEX+1 ; save pointer high byte
LDY #$04 ; 5 bytes to get (0-4)
LDA (INDEX),Y ; get mantissa 4
STA FAC2+FAC_MANT+3 ; save FAC2 mantissa 4
DEY ; decrement index
LDA (INDEX),Y ; get mantissa 3
STA FAC2+FAC_MANT+2 ; save FAC2 mantissa 3
DEY ; decrement index
LDA (INDEX),Y ; get mantissa 2
STA FAC2+FAC_MANT+1 ; save FAC2 mantissa 2
DEY ; decrement index
LDA (INDEX),Y ; get mantissa 1 + sign
STA FAC2+FAC_SIGN ; save FAC2 sign (b7)
EOR FAC1+FAC_SIGN ; XOR with FAC1 sign (b7)
STA ARISGN ; save sign compare (FAC1 XOR FAC2)
LDA FAC2+FAC_SIGN ; recover FAC2 sign (b7)
ORA #$80 ; set 1xxx xxxx (set normal bit)
STA FAC2+FAC_MANT ; save FAC2 mantissa 1
DEY ; decrement index
LDA (INDEX),Y ; get exponent byte
STA FAC2+FAC_EXPT ; save FAC2 exponent
LDA FAC1+FAC_EXPT ; get FAC1 exponent
RTS
;***********************************************************************************;
;
; test and adjust accumulators
MULDIV
LDA FAC2+FAC_EXPT ; get FAC2 exponent
LAB_DAB9
BEQ LAB_DADA ; branch if FAC2 = $00 (handle underflow)
CLC ; clear carry for add
ADC FAC1+FAC_EXPT ; add FAC1 exponent
BCC LAB_DAC4 ; branch if sum of exponents < $0100
BMI LAB_DADF ; do overflow error
CLC ; clear carry for the add
.byte $2C ; makes next line BIT $1410
LAB_DAC4
BPL LAB_DADA ; if +ve go handle underflow
ADC #$80 ; adjust exponent
STA FAC1+FAC_EXPT ; save FAC1 exponent
BNE LAB_DACF ; branch if not zero
JMP LAB_D8FB ; save FAC1 sign and return
LAB_DACF
LDA ARISGN ; get sign compare (FAC1 XOR FAC2)
STA FAC1+FAC_SIGN ; save FAC1 sign (b7)
RTS
;***********************************************************************************;
;
; handle overflow and underflow
LAB_DAD4
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
EOR #$FF ; complement it
BMI LAB_DADF ; do overflow error
; handle underflow
LAB_DADA
PLA ; pop return address low byte
PLA ; pop return address high byte
JMP ZERFAC ; clear FAC1 exponent and sign and return
LAB_DADF
JMP OVERFL ; do overflow error then warm start
;***********************************************************************************;
;
; multiply FAC1 by 10
MULTEN
JSR RFTOA ; round and copy FAC1 to FAC2
TAX ; copy exponent (set the flags)
BEQ LAB_DAF8 ; exit if zero
CLC ; clear carry for add
ADC #$02 ; add two to exponent (*4)
BCS LAB_DADF ; do overflow error if > $FF
; FAC1 = (FAC1 + FAC2) * 2
LAB_DAED
LDX #$00 ; clear byte
STX ARISGN ; clear sign compare (FAC1 XOR FAC2)
JSR LAB_D877 ; add FAC2 to FAC1 (*5)
INC FAC1+FAC_EXPT ; increment FAC1 exponent (*10)
BEQ LAB_DADF ; if exponent now zero go do overflow error
LAB_DAF8
RTS
;***********************************************************************************;
;
; 10 as a floating value
FPCTEN
.byte $84,$20,$00,$00,$00 ; 10
;***********************************************************************************;
;
; divide FAC1 by 10
DIVTEN
JSR RFTOA ; round and copy FAC1 to FAC2
LDA #<FPCTEN ; set 10 pointer low byte
LDY #>FPCTEN ; set 10 pointer high byte
LDX #$00 ; clear sign
; divide by (.A.Y) (.X=sign)
LAB_DB07
STX ARISGN ; save sign compare (FAC1 XOR FAC2)
JSR LODFAC ; unpack memory (.A.Y) into FAC1
JMP DIVIDE ; do FAC2/FAC1
; Perform divide-by
; convert .A.Y and do (.A.Y)/FAC1
LADIV
JSR LODARG ; unpack memory (.A.Y) into FAC2
DIVIDE
BEQ LAB_DB8A ; if zero go do /0 error
JSR ROUND ; round FAC1
LDA #$00 ; clear .A
SEC ; set carry for subtract
SBC FAC1+FAC_EXPT ; subtract FAC1 exponent (2's complement)
STA FAC1+FAC_EXPT ; save FAC1 exponent
JSR MULDIV ; test and adjust accumulators
INC FAC1+FAC_EXPT ; increment FAC1 exponent
BEQ LAB_DADF ; if zero do overflow error
LDX #$FC ; set index to FAC temp
LDA #$01 ;.set byte
LAB_DB29
LDY FAC2+FAC_MANT ; get FAC2 mantissa 1
CPY FAC1+FAC_MANT ; compare FAC1 mantissa 1
BNE LAB_DB3F ; if <> go use the result
LDY FAC2+FAC_MANT+1 ; get FAC2 mantissa 2
CPY FAC1+FAC_MANT+1 ; compare FAC1 mantissa 2
BNE LAB_DB3F ; if <> go use the result
LDY FAC2+FAC_MANT+2 ; get FAC2 mantissa 3
CPY FAC1+FAC_MANT+2 ; compare FAC1 mantissa 3
BNE LAB_DB3F ; if <> go use the result
LDY FAC2+FAC_MANT+3 ; get FAC2 mantissa 4
CPY FAC1+FAC_MANT+3 ; compare FAC1 mantissa 4
LAB_DB3F
PHP ; save the FAC2-FAC1 compare status
ROL ;.shift byte
BCC LAB_DB4C ; skip next if no carry
INX ; increment index to FAC temp
STA RESHO+3,X ;.
BEQ LAB_DB7A ;.
BPL LAB_DB7E ;.
LDA #$01 ;.
LAB_DB4C
PLP ; restore FAC2-FAC1 compare status
BCS LAB_DB5D ; if FAC2 >= FAC1 then do subtract
; FAC2 = FAC2*2
LAB_DB4F
ASL FAC2+FAC_MANT+3 ; shift FAC2 mantissa 4
ROL FAC2+FAC_MANT+2 ; shift FAC2 mantissa 3
ROL FAC2+FAC_MANT+1 ; shift FAC2 mantissa 2
ROL FAC2+FAC_MANT ; shift FAC2 mantissa 1
BCS LAB_DB3F ; loop with no compare
BMI LAB_DB29 ; loop with compare
BPL LAB_DB3F ; loop always with no compare
LAB_DB5D
TAY ; save FAC2-FAC1 compare status
LDA FAC2+FAC_MANT+3 ; get FAC2 mantissa 4
SBC FAC1+FAC_MANT+3 ; subtract FAC1 mantissa 4
STA FAC2+FAC_MANT+3 ; save FAC2 mantissa 4
LDA FAC2+FAC_MANT+2 ; get FAC2 mantissa 3
SBC FAC1+FAC_MANT+2 ; subtract FAC1 mantissa 3
STA FAC2+FAC_MANT+2 ; save FAC2 mantissa 3
LDA FAC2+FAC_MANT+1 ; get FAC2 mantissa 2
SBC FAC1+FAC_MANT+1 ; subtract FAC1 mantissa 2
STA FAC2+FAC_MANT+1 ; save FAC2 mantissa 2
LDA FAC2+FAC_MANT ; get FAC2 mantissa 1
SBC FAC1+FAC_MANT ; subtract FAC1 mantissa 1
STA FAC2+FAC_MANT ; save FAC2 mantissa 1
TYA ; restore FAC2-FAC1 compare status
JMP LAB_DB4F ; go shift FAC2
LAB_DB7A
LDA #$40 ;.
BNE LAB_DB4C ; branch always
; do .A<<6, save as FAC1 rounding byte, normalise and return
LAB_DB7E
ASL ;
ASL ;
ASL ;
ASL ;
ASL ;
ASL ;
STA FACOV ; save FAC1 rounding byte
PLP ; dump FAC2-FAC1 compare status
JMP LAB_DB8F ; copy temp to FAC1, normalise and return
; do "Divide by zero" error
LAB_DB8A
LDX #ER_DIVBY0 ; error $14, divide by zero error
JMP ERROR ; do error #.X then warm start
LAB_DB8F
LDA RESHO ; get temp mantissa 1
STA FAC1+FAC_MANT ; save FAC1 mantissa 1
LDA RESHO+1 ; get temp mantissa 2
STA FAC1+FAC_MANT+1 ; save FAC1 mantissa 2
LDA RESHO+2 ; get temp mantissa 3
STA FAC1+FAC_MANT+2 ; save FAC1 mantissa 3
LDA RESHO+3 ; get temp mantissa 4
STA FAC1+FAC_MANT+3 ; save FAC1 mantissa 4
JMP LAB_D8D7 ; normalise FAC1 and return
;***********************************************************************************;
;
; unpack memory (.A.Y) into FAC1
LODFAC
STA INDEX ; save pointer low byte
STY INDEX+1 ; save pointer high byte
LDY #$04 ; 5 bytes to do
LDA (INDEX),Y ; get fifth byte
STA FAC1+FAC_MANT+3 ; save FAC1 mantissa 4
DEY ; decrement index
LDA (INDEX),Y ; get fourth byte
STA FAC1+FAC_MANT+2 ; save FAC1 mantissa 3
DEY ; decrement index
LDA (INDEX),Y ; get third byte
STA FAC1+FAC_MANT+1 ; save FAC1 mantissa 2
DEY ; decrement index
LDA (INDEX),Y ; get second byte
STA FAC1+FAC_SIGN ; save FAC1 sign (b7)
ORA #$80 ; set 1xxx xxxx (add normal bit)
STA FAC1+FAC_MANT ; save FAC1 mantissa 1
DEY ; decrement index
LDA (INDEX),Y ; get first byte (exponent)
STA FAC1+FAC_EXPT ; save FAC1 exponent
STY FACOV ; clear FAC1 rounding byte
RTS
;***********************************************************************************;
;
; pack FAC1 into LAB_5C
FACTF2
LDX #<LAB_5C ; set pointer low byte
.byte $2C ; makes next line BIT $57A2
; pack FAC1 into TEMPF3
FACTF1
LDX #<TEMPF3 ; set pointer low byte
LDY #>TEMPF3 ; set pointer high byte
BEQ STORFAC ; pack FAC1 into (.X.Y) and return, branch always
; pack FAC1 into variable pointer
FACTFP
LDX FORPNT ; get destination pointer low byte
LDY FORPNT+1 ; get destination pointer high byte
; pack FAC1 into (.X.Y)
STORFAC
JSR ROUND ; round FAC1
STX INDEX ; save pointer low byte
STY INDEX+1 ; save pointer high byte
LDY #$04 ; set index
LDA FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
STA (INDEX),Y ; store in destination
DEY ; decrement index
LDA FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
STA (INDEX),Y ; store in destination
DEY ; decrement index
LDA FAC1+FAC_MANT+1 ; get FAC1 mantissa 2
STA (INDEX),Y ; store in destination
DEY ; decrement index
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
ORA #$7F ; set bits x111 1111
AND FAC1+FAC_MANT ; AND in FAC1 mantissa 1
STA (INDEX),Y ; store in destination
DEY ; decrement index
LDA FAC1+FAC_EXPT ; get FAC1 exponent
STA (INDEX),Y ; store in destination
STY FACOV ; clear FAC1 rounding byte
RTS
;***********************************************************************************;
;
; copy FAC2 to FAC1
ATOF
LDA FAC2+FAC_SIGN ; get FAC2 sign (b7)
; save FAC1 sign and copy ABS(FAC2) to FAC1
LAB_DBFE
STA FAC1+FAC_SIGN ; save FAC1 sign (b7)
LDX #$05 ; 5 bytes to copy
LAB_DC02
LDA FAC2-1,X ; get byte from FAC2,X
STA FAC1-1,X ; save byte at FAC1,X
DEX ; decrement count
BNE LAB_DC02 ; loop if not all done
STX FACOV ; clear FAC1 rounding byte
RTS
;***********************************************************************************;
;
; round and copy FAC1 to FAC2
RFTOA
JSR ROUND ; round FAC1
; copy FAC1 to FAC2
FTOA
LDX #$06 ; 6 bytes to copy
LAB_DC11
LDA FAC1-1,X ; get byte from FAC1,X
STA FAC2-1,X ; save byte at FAC2,X
DEX ; decrement count
BNE LAB_DC11 ; loop if not all done
STX FACOV ; clear FAC1 rounding byte
LAB_DC1A
RTS
;***********************************************************************************;
;
; round FAC1
ROUND
LDA FAC1+FAC_EXPT ; get FAC1 exponent
BEQ LAB_DC1A ; exit if zero
ASL FACOV ; shift FAC1 rounding byte
BCC LAB_DC1A ; exit if no overflow
; round FAC1 (no check)
LAB_DC23
JSR LAB_D96F ; increment FAC1 mantissa
BNE LAB_DC1A ; branch if no overflow
JMP LAB_D938 ; normalise FAC1 for Cb=1 and return
; get FAC1 sign
; return .A = $FF, Cb = 1/-ve .A = $01, Cb = 0/+ve, .A = $00, Cb = ?/0
SGNFAC
LDA FAC1+FAC_EXPT ; get FAC1 exponent
BEQ LAB_DC38 ; exit if zero (already correct SGN(0)=0)
; return .A = $FF, Cb = 1/-ve .A = $01, Cb = 0/+ve
; no = 0 check
LAB_DC2F
LDA FAC1+FAC_SIGN ; else get FAC1 sign (b7)
; return .A = $FF, Cb = 1/-ve .A = $01, Cb = 0/+ve
; no = 0 check, sign in .A
LAB_DC31
ROL ; move sign bit to carry
LDA #$FF ; set byte for -ve result
BCS LAB_DC38 ; return if sign was set (-ve)
LDA #$01 ; else set byte for +ve result
LAB_DC38
RTS
;***********************************************************************************;
;
; perform SGN()
SGN
JSR SGNFAC ; get FAC1 sign, return .A = $FF -ve, .A = $01 +ve
; save .A as integer byte
INTFP
STA FAC1+FAC_MANT ; save FAC1 mantissa 1
LDA #$00 ; clear A
STA FAC1+FAC_MANT+1 ; clear FAC1 mantissa 2
LDX #$88 ; set exponent
; set exponent = .X, clear FAC1 3 and 4 and normalise
INTFP1
LDA FAC1+FAC_MANT ; get FAC1 mantissa 1
EOR #$FF ; complement it
ROL ; sign bit into carry
; set exponent = .X, clear mantissa 4 and 3 and normalise FAC1
LAB_DC49
LDA #$00 ; clear .A
STA FAC1+FAC_MANT+3 ; clear FAC1 mantissa 4
STA FAC1+FAC_MANT+2 ; clear FAC1 mantissa 3
; set exponent = .X and normalise FAC1
LAB_DC4F
STX FAC1+FAC_EXPT ; set FAC1 exponent
STA FACOV ; clear FAC1 rounding byte
STA FAC1+FAC_SIGN ; clear FAC1 sign (b7)
JMP LAB_D8D2 ; do ABS and normalise FAC1
; perform ABS()
ABS
LSR FAC1+FAC_SIGN ; clear FAC1 sign, put zero in b7
RTS
;***********************************************************************************;
;
; compare FAC1 with (.A.Y)
; returns .A=$00 if FAC1 = (.A.Y)
; returns .A=$01 if FAC1 > (.A.Y)
; returns .A=$FF if FAC1 < (.A.Y)
CMPFAC
STA INDEX+2 ; save pointer low byte
LAB_DC5D
STY INDEX+3 ; save pointer high byte
LDY #$00 ; clear index
LDA (INDEX+2),Y ; get exponent
INY ; increment index
TAX ; copy (.A.Y) exponent to .X
BEQ SGNFAC ; branch if (.A.Y) exponent=0 and get FAC1 sign
; .A = $FF, Cb = 1/-ve .A = $01, Cb = 0/+ve
LDA (INDEX+2),Y ; get (.A.Y) mantissa 1, with sign
EOR FAC1+FAC_SIGN ; XOR FAC1 sign (b7)
BMI LAB_DC2F ; if signs <> do return .A = $FF, Cb = 1/-ve
; .A = $01, Cb = 0/+ve and return
CPX FAC1+FAC_EXPT ; compare (.A.Y) exponent with FAC1 exponent
BNE LAB_DC92 ; branch if different
LDA (INDEX+2),Y ; get (.A.Y) mantissa 1, with sign
ORA #$80 ; normalise top bit
CMP FAC1+FAC_MANT ; compare with FAC1 mantissa 1
BNE LAB_DC92 ; branch if different
INY ; increment index
LDA (INDEX+2),Y ; get mantissa 2
CMP FAC1+FAC_MANT+1 ; compare with FAC1 mantissa 2
BNE LAB_DC92 ; branch if different
INY ; increment index
LDA (INDEX+2),Y ; get mantissa 3
CMP FAC1+FAC_MANT+2 ; compare with FAC1 mantissa 3
BNE LAB_DC92 ; branch if different
INY ; increment index
LDA #$7F ; set for 1/2 value rounding byte
CMP FACOV ; compare with FAC1 rounding byte (set carry)
LDA (INDEX+2),Y ; get mantissa 4
SBC FAC1+FAC_MANT+3 ; subtract FAC1 mantissa 4
BEQ LAB_DCBA ; exit if mantissa 4 equal
; gets here if number <> FAC1
LAB_DC92
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
BCC LAB_DC98 ; branch if FAC1 > (.A.Y)
EOR #$FF ; else toggle FAC1 sign
LAB_DC98
JMP LAB_DC31 ; return .A = $FF, Cb = 1/-ve .A = $01, Cb = 0/+ve
;***********************************************************************************;
;
; convert FAC1 floating to fixed
FPINT
LDA FAC1+FAC_EXPT ; get FAC1 exponent
BEQ FILFAC ; if zero go clear FAC1 and return
SEC ; set carry for subtract
SBC #$A0 ; subtract maximum integer range exponent
BIT FAC1+FAC_SIGN ; test FAC1 sign (b7)
BPL LAB_DCAF ; branch if FAC1 +ve
; FAC1 was -ve
TAX ; copy subtracted exponent
LDA #$FF ; overflow for -ve number
STA BITS ; set FAC1 overflow byte
JSR LAB_D94D ; two's complement FAC1 mantissa
TXA ; restore subtracted exponent
LAB_DCAF
LDX #FAC1 ; set index to FAC1
CMP #$F9 ; compare exponent result
BPL LAB_DCBB ; if < 8 shifts shift FAC1 .A times right and return
JSR LAB_D999 ; shift FAC1 .A times right (> 8 shifts)
STY BITS ; clear FAC1 overflow byte
LAB_DCBA
RTS
;***********************************************************************************;
;
; shift FAC1 .A times right
LAB_DCBB
TAY ; copy shift count
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
AND #$80 ; mask sign bit only (x000 0000)
LSR FAC1+FAC_MANT ; shift FAC1 mantissa 1
ORA FAC1+FAC_MANT ; OR sign in b7 FAC1 mantissa 1
STA FAC1+FAC_MANT ; save FAC1 mantissa 1
JSR LAB_D9B0 ; shift FAC1 .Y times right
STY BITS ; clear FAC1 overflow byte
RTS
;***********************************************************************************;
;
; perform INT()
INT
LDA FAC1+FAC_EXPT ; get FAC1 exponent
CMP #$A0 ; compare with max int
BCS LAB_DCF2 ; exit if >= (already int, too big for fractional part!)
JSR FPINT ; convert FAC1 floating to fixed
STY FACOV ; save FAC1 rounding byte
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
STY FAC1+FAC_SIGN ; save FAC1 sign (b7)
EOR #$80 ; toggle FAC1 sign
ROL ; shift into carry
LDA #$A0 ; set new exponent
STA FAC1+FAC_EXPT ; save FAC1 exponent
LDA FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
STA CHARAC ; save FAC1 mantissa 4 for power function
JMP LAB_D8D2 ; do ABS and normalise FAC1
;***********************************************************************************;
;
; clear FAC1 and return
FILFAC
STA FAC1+FAC_MANT ; clear FAC1 mantissa 1
STA FAC1+FAC_MANT+1 ; clear FAC1 mantissa 2
STA FAC1+FAC_MANT+2 ; clear FAC1 mantissa 3
STA FAC1+FAC_MANT+3 ; clear FAC1 mantissa 4
TAY ; clear .Y
LAB_DCF2
RTS
;***********************************************************************************;
;
; get FAC1 from string
ASCFLT
LDY #$00 ; clear .Y
LDX #$0A ; set index
LAB_DCF7
STY LAB_5D,X ; clear byte
DEX ; decrement index
BPL LAB_DCF7 ; loop until numexp to negnum (and FAC1) = $00
BCC LAB_DD0D ; branch if first character is numeric
CMP #'-' ; else compare with "-"
BNE LAB_DD06 ; branch if not "-"
STX SGNFLG ; set flag for -ve n (negnum = $FF)
BEQ LAB_DD0A ; branch always
LAB_DD06
CMP #'+' ; else compare with "+"
BNE LAB_DD0F ; branch if not "+"
LAB_DD0A
JSR CHRGET ; increment and scan memory
LAB_DD0D
BCC LAB_DD6A ; branch if numeric character
LAB_DD0F
CMP #'.' ; else compare with "."
BEQ LAB_DD41 ; branch if "."
CMP #'E' ; else compare with "E"
BNE LAB_DD47 ; branch if not "E"
; was "E" so evaluate exponential part
JSR CHRGET ; increment and scan memory
BCC LAB_DD33 ; branch if numeric character
CMP #TK_MINUS ; else compare with token for "-"
BEQ LAB_DD2E ; branch if token for "-"
CMP #'-' ; else compare with "-"
BEQ LAB_DD2E ; branch if "-"
CMP #TK_PLUS ; else compare with token for "+"
BEQ LAB_DD30 ; branch if token for "+"
CMP #'+' ; else compare with "+"
BEQ LAB_DD30 ; branch if "+"
BNE LAB_DD35 ; branch always
LAB_DD2E
ROR TMPPTR+1 ; set exponent -ve flag (C, which=1, into b7)
LAB_DD30
JSR CHRGET ; increment and scan memory
LAB_DD33
BCC LAB_DD91 ; branch if numeric character
LAB_DD35
BIT TMPPTR+1 ; test exponent -ve flag
BPL LAB_DD47 ; if +ve go evaluate exponent
; else do exponent = -exponent
LDA #$00 ; clear result
SEC ; set carry for subtract
SBC EXPCNT ; subtract exponent byte
JMP LAB_DD49 ; go evaluate exponent
LAB_DD41
ROR TMPPTR ; set decimal point flag
BIT TMPPTR ; test decimal point flag
BVC LAB_DD0A ; branch if only one decimal point so far
; evaluate exponent
LAB_DD47
LDA EXPCNT ; get exponent count byte
LAB_DD49
SEC ; set carry for subtract
SBC LAB_5D ; subtract numerator exponent
STA EXPCNT ; save exponent count byte
BEQ LAB_DD62 ; branch if no adjustment
BPL LAB_DD5B ; else if +ve go do FAC1*10^expcnt
; else go do FAC1/10^(0-expcnt)
LAB_DD52
JSR DIVTEN ; divide FAC1 by 10
INC EXPCNT ; increment exponent count byte
BNE LAB_DD52 ; loop until all done
BEQ LAB_DD62 ; branch always
LAB_DD5B
JSR MULTEN ; multiply FAC1 by 10
DEC EXPCNT ; decrement exponent count byte
BNE LAB_DD5B ; loop until all done
LAB_DD62
LDA SGNFLG ; get -ve flag
BMI LAB_DD67 ; if -ve do - FAC1 and return
RTS
; do - FAC1 and return
LAB_DD67
JMP NEGFAC ; do - FAC1
; do unsigned FAC1*10+number
LAB_DD6A
PHA ; save character
BIT TMPPTR ; test decimal point flag
BPL LAB_DD71 ; skip exponent increment if not set
INC LAB_5D ; else increment number exponent
LAB_DD71
JSR MULTEN ; multiply FAC1 by 10
PLA ; restore character
SEC ; set carry for subtract
SBC #'0' ; convert to binary
JSR ASCI8 ; evaluate new ASCII digit
JMP LAB_DD0A ; go do next character
; evaluate new ASCII digit
; multiply FAC1 by 10 then (ABS) add in new digit
ASCI8
PHA ; save digit
JSR RFTOA ; round and copy FAC1 to FAC2
PLA ; restore digit
JSR INTFP ; save .A as integer byte
LDA FAC2+FAC_SIGN ; get FAC2 sign (b7)
EOR FAC1+FAC_SIGN ; toggle with FAC1 sign (b7)
STA ARISGN ; save sign compare (FAC1 XOR FAC2)
LDX FAC1+FAC_EXPT ; get FAC1 exponent
JMP PLUS ; add FAC2 to FAC1 and return
; evaluate next character of exponential part of number
LAB_DD91
LDA EXPCNT ; get exponent count byte
CMP #$0A ; compare with 10 decimal
BCC LAB_DDA0 ; branch if less
LDA #$64 ; make all -ve exponents = -100 decimal (causes underflow)
BIT TMPPTR+1 ; test exponent -ve flag
BMI LAB_DDAE ; branch if -ve
JMP OVERFL ; else do overflow error then warm start
LAB_DDA0
ASL ; *2
ASL ; *4
CLC ; clear carry for add
ADC EXPCNT ; *5
ASL ; *10
CLC ; clear carry for add
LDY #$00 ; set index
ADC (CHRGOT+1),Y ; add character (will be $30 too much!)
SEC ; set carry for subtract
SBC #'0' ; convert character to binary
LAB_DDAE
STA EXPCNT ; save exponent count byte
JMP LAB_DD30 ; go get next character
;***********************************************************************************;
;
FPC12
.byte $9B,$3E,$BC,$1F,$FD
; 99999999.90625, maximum value with at least one decimal
LAB_DDB8
.byte $9E,$6E,$6B,$27,$FD
; 999999999.25, maximum value before scientific notation
LAB_DDBD
.byte $9E,$6E,$6B,$28,$00
; 1000000000
;***********************************************************************************;
;
; do " IN " line number message
PRTIN
LDA #<INSTR ; set " IN " pointer low byte
LDY #>INSTR ; set " IN " pointer high byte
JSR LAB_DDDA ; print null terminated string
LDA CURLIN+1 ; get the current line number high byte
LDX CURLIN ; get the current line number low byte
;***********************************************************************************;
;
; print .X.A as unsigned integer
PRTFIX
STA FAC1+FAC_MANT ; save high byte as FAC1 mantissa 1
STX FAC1+FAC_MANT+1 ; save low byte as FAC1 mantissa 2
LDX #$90 ; set exponent to 16d bits
SEC ; set integer is +ve flag
JSR LAB_DC49 ; set exponent = .X, clear mantissa 4 and 3 and normalise
; FAC1
JSR LAB_DDDF ; convert FAC1 to string
LAB_DDDA
JMP PRTSTR ; print null terminated string
;***********************************************************************************;
;
; convert FAC1 to ASCII string result in (.A.Y)
FLTASC
LDY #$01 ; set index = 1
LAB_DDDF
LDA #' ' ; character = " " (assume +ve)
BIT FAC1+FAC_SIGN ; test FAC1 sign (b7)
BPL LAB_DDE7 ; if +ve skip the - sign set
LDA #'-' ; else character = "-"
LAB_DDE7
STA BASZPT,Y ; save leading character (" " or "-")
STA FAC1+FAC_SIGN ; save FAC1 sign (b7)
STY FBUFPT ; save the index
INY ; increment index
LDA #'0' ; set character = "0"
LDX FAC1+FAC_EXPT ; get FAC1 exponent
BNE LAB_DDF8 ; if FAC1<>0 go convert it
; exponent was $00 so FAC1 is 0
JMP LAB_DF04 ; save last character, [EOT] and exit
; FAC1 is some non zero value
LAB_DDF8
LDA #$00 ; clear (number exponent count)
CPX #$80 ; compare FAC1 exponent with $80 (<1.00000)
BEQ LAB_DE00 ; branch if 0.5 <= FAC1 < 1.0
BCS LAB_DE09 ; branch if FAC1=>1
LAB_DE00
LDA #<LAB_DDBD ; set 1000000000 pointer low byte
LDY #>LAB_DDBD ; set 1000000000 pointer high byte
JSR TIMES ; do convert .A.Y, FAC1*(.A.Y)
LDA #$F7 ; set number exponent count
LAB_DE09
STA LAB_5D ; save number exponent count
LAB_DE0B
LDA #<LAB_DDB8 ; set 999999999.25 pointer low byte (max before sci note)
LDY #>LAB_DDB8 ; set 999999999.25 pointer high byte
JSR CMPFAC ; compare FAC1 with (.A.Y)
BEQ LAB_DE32 ; exit if FAC1 = (.A.Y)
BPL LAB_DE28 ; go do /10 if FAC1 > (.A.Y)
; FAC1 < (.A.Y)
LAB_DE16
LDA #<FPC12 ; set 99999999.90625 pointer low byte
LDY #>FPC12 ; set 99999999.90625 pointer high byte
JSR CMPFAC ; compare FAC1 with (.A.Y)
BEQ LAB_DE21 ; branch if FAC1 = (.A.Y) (allow decimal places)
BPL LAB_DE2F ; branch if FAC1 > (.A.Y) (no decimal places)
; FAC1 <= (.A.Y)
LAB_DE21
JSR MULTEN ; multiply FAC1 by 10
DEC LAB_5D ; decrement number exponent count
BNE LAB_DE16 ; go test again, branch always
LAB_DE28
JSR DIVTEN ; divide FAC1 by 10
INC LAB_5D ; increment number exponent count
BNE LAB_DE0B ; go test again, branch always
; now we have just the digits to do
LAB_DE2F
JSR ADD05 ; add 0.5 to FAC1 (round FAC1)
LAB_DE32
JSR FPINT ; convert FAC1 floating to fixed
LDX #$01 ; set default digits before dp = 1
LDA LAB_5D ; get number exponent count
CLC ; clear carry for add
ADC #$0A ; up to 9 digits before point
BMI LAB_DE47 ; if -ve then 1 digit before dp
CMP #$0B ; .A>=$0B if n>=1E9
BCS LAB_DE48 ; branch if >= $0B
; carry is clear
ADC #$FF ; take 1 from digit count
TAX ; copy to .X
LDA #$02 ; set the exponent adjust
LAB_DE47
SEC ; set carry for subtract
LAB_DE48
SBC #$02 ; -2
STA EXPCNT ; save the exponent adjust
STX LAB_5D ; save digits before dp count
TXA ; copy digits before dp count to .A
BEQ LAB_DE53 ; if no digits before the dp go do the "."
BPL LAB_DE66 ; if there are digits before the dp go do them
LAB_DE53
LDY FBUFPT ; get the output string index
LDA #'.' ; character "."
INY ; increment the index
STA STACK-1,Y ; save the "." to the output string
TXA ; copy digits before dp count to .A
BEQ LAB_DE64 ; if no digits before the dp skip the "0"
LDA #'0' ; character "0"
INY ; increment index
STA STACK-1,Y ; save the "0" to the output string
LAB_DE64
STY FBUFPT ; save the output string index
LAB_DE66
LDY #$00 ; clear the powers of 10 index (point to -100,000,000)
LAB_DE68
LDX #$80 ; clear the digit, set the test sense
LAB_DE6A
LDA FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
CLC ; clear carry for add
ADC FLTCON+3,Y ; add byte 4, least significant
STA FAC1+FAC_MANT+3 ; save FAC1 mantissa 4
LDA FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
ADC FLTCON+2,Y ; add byte 3
STA FAC1+FAC_MANT+2 ; save FAC1 mantissa 3
LDA FAC1+FAC_MANT+1 ; get FAC1 mantissa 2
ADC FLTCON+1,Y ; add byte 2
STA FAC1+FAC_MANT+1 ; save FAC1 mantissa 2
LDA FAC1+FAC_MANT ; get FAC1 mantissa 1
ADC FLTCON+0,Y ; add byte 1, most significant
STA FAC1+FAC_MANT ; save FAC1 mantissa 1
INX ; increment the digit, set the sign on the test sense bit
BCS LAB_DE8E ; if the carry is set go test if the result was positive
; else the result needs to be negative
BPL LAB_DE6A ; not -ve so try again
BMI LAB_DE90 ; else done so return the digit
LAB_DE8E
BMI LAB_DE6A ; not +ve so try again
; else done so return the digit
LAB_DE90
TXA ; copy the digit
BCC LAB_DE97 ; if Cb=0 just use it
EOR #$FF ; else make the two's complement ..
ADC #$0A ; .. and subtract it from 10
LAB_DE97
ADC #'0'-1 ; add "0"-1 to result
INY ; increment ..
INY ; .. index to..
INY ; .. next less ..
INY ; .. power of ten
STY VARPNT ; save the powers of ten table index
LDY FBUFPT ; get output string index
INY ; increment output string index
TAX ; copy character to .X
AND #$7F ; mask out top bit
STA STACK-1,Y ; save to output string
DEC LAB_5D ; decrement # of characters before the dp
BNE LAB_DEB2 ; if still characters to do skip the decimal point
; else output the point
LDA #'.' ; character "."
INY ; increment output string index
STA STACK-1,Y ; save to output string
LAB_DEB2
STY FBUFPT ; save the output string index
LDY VARPNT ; get the powers of ten table index
TXA ; get the character back
EOR #$FF ; toggle the test sense bit
AND #$80 ; clear the digit
TAX ; copy it to the new digit
CPY #HMSCON-FLTCON
; compare the table index with the max for decimal numbers
BEQ LAB_DEC4 ; if at the max exit the digit loop
CPY #LAB_DF52-FLTCON
; compare the table index with the max for time
BNE LAB_DE6A ; loop if not at the max
; now remove trailing zeroes
LAB_DEC4
LDY FBUFPT ; restore the output string index
LAB_DEC6
LDA STACK-1,Y ; get character from output string
DEY ; decrement output string index
CMP #'0' ; compare with "0"
BEQ LAB_DEC6 ; loop until non "0" character found
CMP #'.' ; compare with "."
BEQ LAB_DED3 ; branch if was dp
; restore last character
INY ; increment output string index
LAB_DED3
LDA #'+' ; character "+"
LDX EXPCNT ; get exponent count
BEQ LAB_DF07 ; if zero go set null terminator and exit
; exponent isn't zero so write exponent
BPL LAB_DEE3 ; branch if exponent count +ve
LDA #$00 ; clear .A
SEC ; set carry for subtract
SBC EXPCNT ; subtract exponent count adjust (convert -ve to +ve)
TAX ; copy exponent count to .X
LDA #'-' ; character "-"
LAB_DEE3
STA STACK+1,Y ; save to output string
LDA #'E' ; character "E"
STA STACK,Y ; save exponent sign to output string
TXA ; get exponent count back
LDX #$2F ; one less than "0" character
SEC ; set carry for subtract
LAB_DEEF
INX ; increment 10's character
SBC #$0A ; subtract 10 from exponent count
BCS LAB_DEEF ; loop while still >= 0
ADC #':' ; add character ":" ($30+$0A, result is 10 less that value)
STA STACK+3,Y ; save to output string
TXA ; copy 10's character
STA STACK+2,Y ; save to output string
LDA #$00 ; set null terminator
STA STACK+4,Y ; save to output string
BEQ LAB_DF0C ; go set string pointer (.A.Y) and exit, branch always
; save last character, [EOT] and exit
LAB_DF04
STA STACK-1,Y ; save last character to output string
; set null terminator and exit
LAB_DF07
LDA #$00 ; set null terminator
STA STACK,Y ; save after last character
; set string pointer (.A.Y) and exit
LAB_DF0C
LDA #<STACK ; set result string pointer low byte
LDY #>STACK ; set result string pointer high byte
RTS
;***********************************************************************************;
;
FLP05
.byte $80,$00 ; 0.5, first two bytes
NULLVAR
.byte $00,$00,$00 ; null return for undefined variables
; decimal conversion tables
FLTCON
.byte $FA,$0A,$1F,$00 ; -100000000
.byte $00,$98,$96,$80 ; +10000000
.byte $FF,$F0,$BD,$C0 ; -1000000
.byte $00,$01,$86,$A0 ; +100000
.byte $FF,$FF,$D8,$F0 ; -10000
.byte $00,$00,$03,$E8 ; +1000
.byte $FF,$FF,$FF,$9C ; -100
.byte $00,$00,$00,$0A ; +10
.byte $FF,$FF,$FF,$FF ; -1
; jiffy count conversion table
HMSCON
.byte $FF,$DF,$0A,$80 ; -2160000 10s hours
.byte $00,$03,$4B,$C0 ; +216000 hours
.byte $FF,$FF,$73,$60 ; -36000 10s mins
.byte $00,$00,$0E,$10 ; +3600 mins
.byte $FF,$FF,$FD,$A8 ; -600 10s secs
.byte $00,$00,$00,$3C ; +60 secs
LAB_DF52
;***********************************************************************************;
;
; spare bytes, not referenced
.byte $BF,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA
.byte $AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA
;***********************************************************************************;
;
; perform SQR()
SQR
JSR RFTOA ; round and copy FAC1 to FAC2
LDA #<FLP05 ; set 0.5 pointer low address
LDY #>FLP05 ; set 0.5 pointer high address
JSR LODFAC ; unpack memory (.A.Y) into FAC1
;***********************************************************************************;
;
; perform power function
EXPONT
BEQ EXP ; perform EXP()
LDA FAC2+FAC_EXPT ; get FAC2 exponent
BNE LAB_DF84 ; branch if FAC2<>0
JMP LAB_D8F9 ; clear FAC1 exponent and sign and return
LAB_DF84
LDX #<DEFPNT ; set destination pointer low byte
LDY #>DEFPNT ; set destination pointer high byte
JSR STORFAC ; pack FAC1 into (.X.Y)
LDA FAC2+FAC_SIGN ; get FAC2 sign (b7)
BPL LAB_DF9E ; branch if FAC2>0
; else FAC2 is -ve and can only be raised to an
; integer power which gives an x + j0 result
JSR INT ; perform INT()
LDA #<DEFPNT ; set source pointer low byte
LDY #>DEFPNT ; set source pointer high byte
JSR CMPFAC ; compare FAC1 with (.A.Y)
BNE LAB_DF9E ; branch if FAC1 <> (.A.Y) to allow Function Call error
; this will leave FAC1 -ve and cause a Function Call
; error when LOG() is called
TYA ; clear sign b7
LDY CHARAC ; get FAC1 mantissa 4 from INT() function as sign in
; .Y for possible later negation, b0 only needed
LAB_DF9E
JSR LAB_DBFE ; save FAC1 sign and copy ABS(FAC2) to FAC1
TYA ; copy sign back ..
PHA ; .. and save it
JSR LOG ; perform LOG()
LDA #<DEFPNT ; set pointer low byte
LDY #>DEFPNT ; set pointer high byte
JSR TIMES ; do convert .A.Y, FAC1*(.A.Y)
JSR EXP ; perform EXP()
PLA ; pull sign from stack
LSR ; b0 is to be tested
BCC LAB_DFBE ; if no bit then exit
; do - FAC1
NEGFAC
LDA FAC1+FAC_EXPT ; get FAC1 exponent
BEQ LAB_DFBE ; exit if FAC1_e = $00
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
EOR #$FF ; complement it
STA FAC1+FAC_SIGN ; save FAC1 sign (b7)
LAB_DFBE
RTS
;***********************************************************************************;
;
; exp(n) constant and series
EXPCON
.byte $81,$38,$AA,$3B,$29 ; 1.443
LAB_DFC4
.byte $07 ; series count
.byte $71,$34,$58,$3E,$56 ; 2.14987637E-5
.byte $74,$16,$7E,$B3,$1B ; 1.43523140E-4
.byte $77,$2F,$EE,$E3,$85 ; 1.34226348E-3
.byte $7A,$1D,$84,$1C,$2A ; 9.61401701E-3
.byte $7C,$63,$59,$58,$0A ; 5.55051269E-2
.byte $7E,$75,$FD,$E7,$C6 ; 2.40226385E-1
.byte $80,$31,$72,$18,$10 ; 6.93147186E-1
.byte $81,$00,$00,$00,$00 ; 1.00000000
;***********************************************************************************;
;
; perform EXP()
EXP
LDA #<EXPCON ; set 1.443 pointer low byte
LDY #>EXPCON ; set 1.443 pointer high byte
JSR TIMES ; do convert .A.Y, FAC1*(.A.Y)
LDA FACOV ; get FAC1 rounding byte
ADC #$50 ; +$50/$100
BCC LAB_DFFD ; skip rounding if no carry
JSR LAB_DC23 ; round FAC1 (no check)
LAB_DFFD
STA JMPER+2 ; save FAC2 rounding byte
JSR FTOA ; copy FAC1 to FAC2
LDA FAC1+FAC_EXPT ; get FAC1 exponent
CMP #$88 ; compare with EXP limit (256)
BCC LAB_E00B ; branch if less
LAB_E008
JSR LAB_DAD4 ; handle overflow and underflow
LAB_E00B
JSR INT ; perform INT()
LDA CHARAC ; get mantissa 4 from INT()
CLC ; clear carry for add
ADC #$81 ; normalise +1
BEQ LAB_E008 ; if $00 result has overflowed so go handle it
SEC ; set carry for subtract
SBC #$01 ; exponent now correct
PHA ; save FAC2 exponent
; swap FAC1 and FAC2
LDX #$05 ; 4 bytes to do
LAB_E01B
LDA FAC2,X ; get FAC2,X
LDY FAC1,X ; get FAC1,X
STA FAC1,X ; save FAC1,X
STY FAC2,X ; save FAC2,X
DEX ; decrement count/index
BPL LAB_E01B ; loop if not all done
LDA JMPER+2 ; get FAC2 rounding byte
STA FACOV ; save as FAC1 rounding byte
JSR SUB ; perform subtraction, FAC2 from FAC1
JSR NEGFAC ; do - FAC1
LDA #<LAB_DFC4 ; set counter pointer low byte
LDY #>LAB_DFC4 ; set counter pointer high byte
JSR SER2 ; go do series evaluation
LDA #$00 ; clear .A
STA ARISGN ; clear sign compare (FAC1 XOR FAC2)
PLA ; pull the saved FAC2 exponent
JSR LAB_DAB9 ; test and adjust accumulators
RTS
;***********************************************************************************;
;
; ^2 then series evaluation
SEREVL
STA FBUFPT ; save count pointer low byte
STY FBUFPT+1 ; save count pointer high byte
JSR FACTF1 ; pack FAC1 into LAB_57
LDA #<TEMPF3 ; set pointer low byte (.Y already $00)
JSR TIMES ; do convert .A.Y, FAC1*(.A.Y)
JSR LAB_E05A ; go do series evaluation
LDA #<TEMPF3 ; pointer to original # low byte
LDY #>TEMPF3 ; pointer to original # high byte
JMP TIMES ; do convert .A.Y, FAC1*(.A.Y)
;***********************************************************************************;
;
; do series evaluation
SER2
STA FBUFPT ; save count pointer low byte
STY FBUFPT+1 ; save count pointer high byte
; do series evaluation
LAB_E05A
JSR FACTF2 ; pack FAC1 into LAB_5C
LDA (FBUFPT),Y ; get constants count
STA SGNFLG ; save constants count
LDY FBUFPT ; get count pointer low byte
INY ; increment it (now constants pointer)
TYA ; copy it
BNE LAB_E069 ; skip next if no overflow
INC FBUFPT+1 ; else increment high byte
LAB_E069
STA FBUFPT ; save low byte
LDY FBUFPT+1 ; get high byte
LAB_E06D
JSR TIMES ; do convert .A.Y, FAC1*(.A.Y)
LDA FBUFPT ; get constants pointer low byte
LDY FBUFPT+1 ; get constants pointer high byte
CLC ; clear carry for add
ADC #$05 ; +5 to low pointer (5 bytes per constant)
BCC LAB_E07A ; skip next if no overflow
INY ; increment high byte
LAB_E07A
STA FBUFPT ; save pointer low byte
STY FBUFPT+1 ; save pointer high byte
JSR LAPLUS ; add (.A.Y) to FAC1
LDA #<LAB_5C ; set pointer low byte to partial
LDY #>LAB_5C ; set pointer high byte to partial
DEC SGNFLG ; decrement constants count
BNE LAB_E06D ; loop until all done
RTS
;***********************************************************************************;
;
; RND values
RNDC1
.byte $98,$35,$44,$7A,$00
; 11879546 multiplier
LAB_E08F
.byte $68,$28,$B1,$46,$00
; 3.927677739E-8 offset
;***********************************************************************************;
;
; perform RND()
RND
JSR SGNFAC ; get FAC1 sign
; return .A = $FF -ve, .A = $01 +ve
BMI LAB_E0D0 ; if n<0 copy byte swapped FAC1 into RND() seed
BNE LAB_E0BB ; if n>0 get next number in RND() sequence
; else n=0 so get the RND() number from VIA 1 timers
JSR IOBASE ; return base address of I/O devices
STX INDEX ; save pointer low byte
STY INDEX+1 ; save pointer high byte
LDY #$04 ; set index to T1 low byte
LDA (INDEX),Y ; get T1 low byte
STA FAC1+FAC_MANT ; save FAC1 mantissa 1
INY ; increment index
LDA (INDEX),Y ; get T1 high byte
STA FAC1+FAC_MANT+2 ; save FAC1 mantissa 3
LDY #$08 ; set index to T2 low byte
LDA (INDEX),Y ; get T2 low byte
STA FAC1+FAC_MANT+1 ; save FAC1 mantissa 2
INY ; increment index
LDA (INDEX),Y ; get T2 high byte
STA FAC1+FAC_MANT+3 ; save FAC1 mantissa 4
JMP LAB_E0E0 ; set exponent and exit
LAB_E0BB
LDA #<RNDX ; set seed pointer low address
LDY #>RNDX ; set seed pointer high address
JSR LODFAC ; unpack memory (.A.Y) into FAC1
LDA #<RNDC1 ; set 11879546 pointer low byte
LDY #>RNDC1 ; set 11879546 pointer high byte
JSR TIMES ; do convert .A.Y, FAC1*(.A.Y)
LDA #<LAB_E08F ; set 3.927677739E-8 pointer low byte
LDY #>LAB_E08F ; set 3.927677739E-8 pointer high byte
JSR LAPLUS ; add (.A.Y) to FAC1
LAB_E0D0
LDX FAC1+FAC_MANT+3 ; get FAC1 mantissa 4
LDA FAC1+FAC_MANT ; get FAC1 mantissa 1
STA FAC1+FAC_MANT+3 ; save FAC1 mantissa 4
STX FAC1+FAC_MANT ; save FAC1 mantissa 1
LDX FAC1+FAC_MANT+1 ; get FAC1 mantissa 2
LDA FAC1+FAC_MANT+2 ; get FAC1 mantissa 3
STA FAC1+FAC_MANT+1 ; save FAC1 mantissa 2
STX FAC1+FAC_MANT+2 ; save FAC1 mantissa 3
LAB_E0E0
LDA #$00 ; clear byte
STA FAC1+FAC_SIGN ; clear FAC1 sign (always +ve)
LDA FAC1+FAC_EXPT ; get FAC1 exponent
STA FACOV ; save FAC1 rounding byte
LDA #$80 ; set exponent = $80
STA FAC1+FAC_EXPT ; save FAC1 exponent
JSR LAB_D8D7 ; normalise FAC1
LDX #<RNDX ; set seed pointer low address
LDY #>RNDX ; set seed pointer high address
;***********************************************************************************;
;
; pack FAC1 into (.X.Y)
LAB_E0F3
JMP STORFAC ; pack FAC1 into (.X.Y)
;***********************************************************************************;
;
; handle BASIC I/O error
PATCHBAS
CMP #$F0 ; compare error with $F0
BNE LAB_E101 ; branch if not $F0
STY MEMSIZ+1 ; set end of memory high byte
STX MEMSIZ ; set end of memory low byte
JMP LAB_C663 ; clear from start to end and return
; error was not $F0
LAB_E101
TAX ; copy error #
BNE LAB_E106 ; branch if not $00
LDX #ER_BREAK ; else error $1E, break error
LAB_E106
JMP ERROR ; do error #.X then warm start
;***********************************************************************************;
;
; output character to channel with error check
LAB_E109
JSR CHROUT ; output character to channel
BCS PATCHBAS ; if error go handle BASIC I/O error
RTS
;***********************************************************************************;
;
; input character from channel with error check
LAB_E10F
JSR CHRIN ; input character from channel
BCS PATCHBAS ; if error go handle BASIC I/O error
RTS
;***********************************************************************************;
;
; open channel for output with error check
LAB_E115
JSR CHKOUT ; open channel for output
BCS PATCHBAS ; if error go handle BASIC I/O error
RTS
;***********************************************************************************;
;
; open channel for input with error check
LAB_E11B
JSR CHKIN ; open channel for input
BCS PATCHBAS ; if error go handle BASIC I/O error
RTS
;***********************************************************************************;
;
; get character from input device with error check
LAB_E121
JSR GETIN ; get character from input device
BCS PATCHBAS ; if error go handle BASIC I/O error
RTS
;***********************************************************************************;
;
; perform SYS
SYSTEM
JSR TYPCHK ; evaluate expression and check is numeric, else do
; type mismatch
JSR MAKADR ; convert FAC1 to integer in temporary integer
LDA #>LAB_E144-1 ; get return address high byte
PHA ; push as return address
LDA #<LAB_E144-1 ; get return address low byte
PHA ; push as return address
LDA SPREG ; get saved status register
PHA ; put on stack
LDA SAREG ; get saved .A
LDX SXREG ; get saved .X
LDY SYREG ; get saved .Y
PLP ; pull processor status
JMP (LINNUM) ; call SYS address
LAB_E144
PHP ; save status
STA SAREG ; save returned .A
STX SXREG ; save returned .X
STY SYREG ; save returned .Y
PLA ; restore saved status
STA SPREG ; save status
RTS
;***********************************************************************************;
;
; perform SAVE
BSAVE
JSR PARSL ; get parameters for LOAD/SAVE
LDX VARTAB ; get start of variables low byte
LDY VARTAB+1 ; get start of variables high byte
LDA #TXTTAB ; index to start of program memory
JSR SAVE ; save RAM to device, .A = index to start address, .X.Y = end
; address low/high
BCS PATCHBAS ; if error go handle BASIC I/O error
RTS
;***********************************************************************************;
;
; perform VERIFY
BVERIF
LDA #$01 ; flag verify
.byte $2C ; makes next line BIT $00A9
;***********************************************************************************;
;
; perform LOAD
BLOAD
LDA #$00 ; flag load
STA VERCHK ; set load/verify flag
JSR PARSL ; get parameters for LOAD/SAVE
LDA VERCHK ; get load/verify flag
LDX TXTTAB ; get start of memory low byte
LDY TXTTAB+1 ; get start of memory high byte
JSR LOAD ; load RAM from a device
BCS LAB_E1CE ; if error go handle BASIC I/O error
LDA VERCHK ; get load/verify flag
BEQ LAB_E195 ; branch if load
LDX #ER_VERIFY ; error $1C, verify error
JSR READST ; read I/O status word
AND #$10 ; mask for tape read error
BEQ LAB_E187 ; branch if no read error
JMP ERROR ; do error #.X then warm start
LAB_E187
LDA CHRGOT+1 ; get BASIC execute pointer low byte
; The above code is wrong. The high byte is in CHRGOT+2, the code should read ..
;
; LDA CHRGOT+2 ; get BASIC execute pointer high byte
CMP #>BUF ; compare with input buffer high byte
BEQ LAB_E194 ; if immediate mode skip "OK" prompt
LDA #<OKSTR ; set "OK" pointer low byte
LDY #>OKSTR ; set "OK" pointer high byte
JMP PRTSTR ; print null terminated string
LAB_E194
RTS
;***********************************************************************************;
;
; rebuild BASIC line chaining following a LOAD
LAB_E195
JSR READST ; read I/O status word
AND #$BF ; mask x0xx xxxx, all except EOF
BEQ LAB_E1A1 ; branch if no errors
LDX #ER_LOAD ; error $1D, load error
JMP ERROR ; do error #.X then warm start
LAB_E1A1
LDA CHRGOT+2 ; get BASIC execute pointer high byte
CMP #>BUF ; compare with input buffer high byte
BNE LAB_E1B5 ; branch if not immediate mode
STX VARTAB ; set start of variables low byte
STY VARTAB+1 ; set start of variables high byte
LDA #<READYSTR ; set "READY." pointer low byte
LDY #>READYSTR ; set "READY." pointer high byte
JSR PRTSTR ; print null terminated string
JMP LAB_C52A ; reset execution, clear variables, flush stack,
; rebuild BASIC chain and do warm start
LAB_E1B5
JSR STXTPT ; set BASIC execute pointer to start of memory - 1
JMP PATCHER ; rebuild BASIC line chaining, do RESTORE and return
;***********************************************************************************;
;
; perform OPEN
BOPEN
JSR PAROC ; get parameters for OPEN/CLOSE
JSR OPEN ; open a logical file
BCS LAB_E1CE ; branch if error
RTS
;***********************************************************************************;
;
; perform CLOSE
BCLOSE
JSR PAROC ; get parameters for OPEN/CLOSE
LDA FORPNT ; get logical file number
JSR CLOSE ; close a specified logical file
BCC LAB_E194 ; exit if no error
LAB_E1CE
JMP PATCHBAS ; go handle BASIC I/O error
;***********************************************************************************;
;
; get parameters for LOAD/SAVE
PARSL
LDA #$00 ; clear file name length
JSR SETNAM ; clear filename
LDX #$01 ; set default device number, cassette
LDY #$00 ; set default command
JSR SETLFS ; set logical, first and second addresses
JSR IFCHRG ; exit function if [EOT] or ":"
JSR LAB_E254 ; set filename
JSR IFCHRG ; exit function if [EOT] or ":"
JSR LAB_E1FD ; scan and get byte, else do syntax error then warm start
LDY #$00 ; clear command
STX FORPNT ; save device number
JSR SETLFS ; set logical, first and second addresses
JSR IFCHRG ; exit function if [EOT] or ":"
JSR LAB_E1FD ; scan and get byte, else do syntax error then warm start
TXA ; copy command to .A
TAY ; copy command to .Y
LDX FORPNT ; get device number back
JMP SETLFS ; set logical, first and second addresses and return
;***********************************************************************************;
;
; scan and get byte, else do syntax error then warm start
LAB_E1FD
JSR SKPCOM ; scan for ",byte", else do syntax error then warm start
JMP LAB_D79E ; get byte parameter and return
;***********************************************************************************;
;
; exit function if [EOT] or ":"
IFCHRG
JSR CHRGOT ; scan memory
BNE LAB_E20A ; branch if not [EOL] or ":"
PLA ; dump return address low byte
PLA ; dump return address high byte
LAB_E20A
RTS
;***********************************************************************************;
;
; scan for ",valid byte", else do syntax error then warm start
SKPCOM
JSR COMCHK ; scan for ",", else do syntax error then warm start
; scan for valid byte, not [EOL] or ":", else do syntax error then warm start
CHRERR
JSR CHRGOT ; scan memory
BNE LAB_E20A ; exit if following byte
JMP LAB_CF08 ; else do syntax error then warm start
;***********************************************************************************;
;
; get parameters for OPEN/CLOSE
PAROC
LDA #$00 ; clear file name length
JSR SETNAM ; clear filename
JSR CHRERR ; scan for valid byte, else do syntax error then warm start
JSR LAB_D79E ; get byte parameter, logical file number
STX FORPNT ; save logical file number
TXA ; copy logical file number to .A
LDX #$01 ; set default device number, cassette
LDY #$00 ; set default command
JSR SETLFS ; set logical, first and second addresses
JSR IFCHRG ; exit function if [EOT] or ":"
JSR LAB_E1FD ; scan and get byte, else do syntax error then warm start
STX FORPNT+1 ; save device number
LDY #$00 ; clear command
LDA FORPNT ; get logical file number
CPX #$03 ; compare device number with screen
BCC LAB_E23C ; branch if less than screen
DEY ; else decrement command
LAB_E23C
JSR SETLFS ; set logical, first and second addresses
JSR IFCHRG ; exit function if [EOT] or ":"
JSR LAB_E1FD ; scan and get byte, else do syntax error then warm start
TXA ; copy command to .A
TAY ; copy command to .Y
LDX FORPNT+1 ; get device number
LDA FORPNT ; get logical file number
JSR SETLFS ; set logical, first and second addresses
JSR IFCHRG ; exit function if [EOT] or ":"
JSR SKPCOM ; scan for ",byte", else do syntax error then warm start
;***********************************************************************************;
;
; set filename
LAB_E254
JSR FRMEVL ; evaluate expression
JSR DELST ; evaluate string
LDX INDEX ; get string pointer low byte
LDY INDEX+1 ; get string pointer high byte
JMP SETNAM ; set filename and return
;***********************************************************************************;
;
; perform COS()
COS
LDA #<FPC20 ; set pi/2 pointer low byte
LDY #>FPC20 ; set pi/2 pointer high byte
JSR LAPLUS ; add (.A.Y) to FAC1
;***********************************************************************************;
;
; perform SIN()
SIN
JSR RFTOA ; round and copy FAC1 to FAC2
LDA #<LAB_E2E2 ; set 2*pi pointer low byte
LDY #>LAB_E2E2 ; set 2*pi pointer high byte
LDX FAC2+FAC_SIGN ; get FAC2 sign (b7)
JSR LAB_DB07 ; divide by (.A.Y) (.X=sign)
JSR RFTOA ; round and copy FAC1 to FAC2
JSR INT ; perform INT()
LDA #$00 ; clear byte
STA ARISGN ; clear sign compare (FAC1 XOR FAC2)
JSR SUB ; perform subtraction, FAC2 from FAC1
LDA #<LAB_E2E7 ; set 0.25 pointer low byte
LDY #>LAB_E2E7 ; set 0.25 pointer high byte
JSR LAMIN ; perform subtraction, FAC1 from (.A.Y)
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
PHA ; save FAC1 sign
BPL LAB_E29A ; branch if +ve
; FAC1 sign was -ve
JSR ADD05 ; add 0.5 to FAC1 (round FAC1)
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
BMI LAB_E29D ; branch if -ve
LDA TANSGN ; get the comparison evaluation flag
EOR #$FF ; toggle flag
STA TANSGN ; save the comparison evaluation flag
LAB_E29A
JSR NEGFAC ; do - FAC1
LAB_E29D
LDA #<LAB_E2E7 ; set 0.25 pointer low byte
LDY #>LAB_E2E7 ; set 0.25 pointer high byte
JSR LAPLUS ; add (.A.Y) to FAC1
PLA ; restore FAC1 sign
BPL LAB_E2AA ; branch if was +ve
; else correct FAC1
JSR NEGFAC ; do - FAC1
LAB_E2AA
LDA #<LAB_E2EC ; set pointer low byte to counter
LDY #>LAB_E2EC ; set pointer high byte to counter
JMP SEREVL ; ^2 then series evaluation and return
;***********************************************************************************;
;
; perform TAN()
TAN
JSR FACTF1 ; pack FAC1 into LAB_57
LDA #$00 ; clear .A
STA TANSGN ; clear the comparison evaluation flag
JSR SIN ; perform SIN()
LDX #<DEFPNT ; set sin(n) pointer low byte
LDY #>DEFPNT ; set sin(n) pointer high byte
JSR LAB_E0F3 ; pack FAC1 into (.X.Y)
LDA #<TEMPF3 ; set n pointer low byte
LDY #>TEMPF3 ; set n pointer high byte
JSR LODFAC ; unpack memory (.A.Y) into FAC1
LDA #$00 ; clear byte
STA FAC1+FAC_SIGN ; clear FAC1 sign (b7)
LDA TANSGN ; get the comparison evaluation flag
JSR LAB_E2D9 ; save flag and go do series evaluation
LDA #<DEFPNT ; set sin(n) pointer low byte
LDY #>DEFPNT ; set sin(n) pointer high byte
JMP LADIV ; convert .A.Y and do (.A.Y)/FAC1
;***********************************************************************************;
;
; save comparison flag and do series evaluation
LAB_E2D9
PHA ; save comparison flag
JMP LAB_E29A ; add 0.25, ^2 then series evaluation
;***********************************************************************************;
;
; constants and series for SIN/COS(n)
FPC20
.byte $81,$49,$0F,$DA,$A2 ; 1.570796371, pi/2, as floating number
LAB_E2E2
.byte $83,$49,$0F,$DA,$A2 ; 6.28319, 2*pi, as floating number
LAB_E2E7
.byte $7F,$00,$00,$00,$00 ; 0.25
LAB_E2EC
.byte $05 ; series counter
.byte $84,$E6,$1A,$2D,$1B ; -14.3813907
.byte $86,$28,$07,$FB,$F8 ; 42.0077971
.byte $87,$99,$68,$89,$01 ; -76.7041703
.byte $87,$23,$35,$DF,$E1 ; 81.6052237
.byte $86,$A5,$5D,$E7,$28 ; -41.3417021
.byte $83,$49,$0F,$DA,$A2 ; 6.28318531
;***********************************************************************************;
;
; perform ATN()
ATN
LDA FAC1+FAC_SIGN ; get FAC1 sign (b7)
PHA ; save sign
BPL LAB_E313 ; branch if +ve
JSR NEGFAC ; else do - FAC1
LAB_E313
LDA FAC1+FAC_EXPT ; get FAC1 exponent
PHA ; push exponent
CMP #$81 ; compare with 1
BCC LAB_E321 ; branch if FAC1 < 1
LDA #<FPC1 ; pointer to 1 low byte
LDY #>FPC1 ; pointer to 1 high byte
JSR LADIV ; convert .A.Y and do (.A.Y)/FAC1
LAB_E321
LDA #<ATNCON ; pointer to series low byte
LDY #>ATNCON ; pointer to series high byte
JSR SEREVL ; ^2 then series evaluation
PLA ; restore old FAC1 exponent
CMP #$81 ; compare with 1
BCC LAB_E334 ; branch if FAC1 < 1
LDA #<FPC20 ; pointer to (pi/2) low byte
LDY #>FPC20 ; pointer to (pi/2) low byte
JSR LAMIN ; perform subtraction, FAC1 from (.A.Y)
LAB_E334
PLA ; restore FAC1 sign
BPL LAB_E33A ; exit if was +ve
JMP NEGFAC ; else do - FAC1 and return
LAB_E33A
RTS
;***********************************************************************************;
;
; series for ATN(n)
ATNCON
.byte $0B ; series counter
.byte $76,$B3,$83,$BD,$D3 ; -6.84793912e-04
.byte $79,$1E,$F4,$A6,$F5 ; 4.85094216e-03
.byte $7B,$83,$FC,$B0,$10 ; -0.0161117015
.byte $7C,$0C,$1F,$67,$CA ; 0.034209638
.byte $7C,$DE,$53,$CB,$C1 ; -0.054279133
.byte $7D,$14,$64,$70,$4C ; 0.0724571965
.byte $7D,$B7,$EA,$51,$7A ; -0.0898019185
.byte $7D,$63,$30,$88,$7E ; 0.110932413
.byte $7E,$92,$44,$99,$3A ; -0.142839808
.byte $7E,$4C,$CC,$91,$C7 ; 0.19999912
.byte $7F,$AA,$AA,$AA,$13 ; -0.333333316
.byte $81,$00,$00,$00,$00 ; 1.000000000
;***********************************************************************************;
;
; BASIC cold start entry point
COLDBA
JSR INITVCTRS ; initialise BASIC vector table
JSR INITBA ; initialise BASIC RAM locations
JSR FREMSG ; print start up message and initialise memory pointers
LDX #$FB ; value for start stack
TXS ; set stack pointer
JMP READY ; do "READY." warm start
;***********************************************************************************;
;
; character get subroutine for zero page
; the target address for the LDA LAB_EA60 becomes the BASIC execute pointer once the
; block is copied to its destination, any non zero page address will do at assembly
; time, to assemble a three byte instruction.
; page 0 initialisation table from CHRGET
; increment and scan memory
CGIMAG
INC CHRGOT+1 ; increment BASIC execute pointer low byte
BNE LAB_E38D ; branch if no carry
; else
INC CHRGOT+2 ; increment BASIC execute pointer high byte
; page 0 initialisation table from CHRGOT
; scan memory
LAB_E38D
LDA LAB_EA60 ; get byte to scan, address set by call routine
CMP #':' ; compare with ":"
BCS LAB_E39E ; exit if >=
; page 0 initialisation table from CHRSPC
; clear Cb if numeric
CMP #' ' ; compare with " "
BEQ CGIMAG ; if " " go do next
SEC ; set carry for SBC
SBC #'0' ; subtract "0"
SEC ; set carry for SBC
SBC #$D0 ; subtract -"0"
; clear carry if byte = "0"-"9"
LAB_E39E
RTS
;***********************************************************************************;
;
; spare bytes, not referenced
;LAB_E39F
.byte $80,$4F,$C7,$52,$58
; 0.811635157
;***********************************************************************************;
;
; initialise BASIC RAM locations
INITBA
LDA #$4C ; opcode for JMP
STA JMPER ; save for functions vector jump
STA USRPPOK ; save for USR() vector jump
; set USR() vector to illegal quantity error
LDA #<ILQUAN ; set USR() vector low byte
LDY #>ILQUAN ; set USR() vector high byte
STA ADDPRC ; save USR() vector low byte
STY ADDPRC+1 ; save USR() vector high byte
LDA #<MAKFP ; set fixed to float vector low byte
LDY #>MAKFP ; set fixed to float vector high byte
STA ADRAY2 ; save fixed to float vector low byte
STY ADRAY2+1 ; save fixed to float vector high byte
LDA #<INTIDX ; set float to fixed vector low byte
LDY #>INTIDX ; set float to fixed vector high byte
STA ADRAY1 ; save float to fixed vector low byte
STY ADRAY1+1 ; save float to fixed vector high byte
; copy block from CGIMAG to CHRGET
LDX #$1C ; set byte count
LAB_E3C4
LDA CGIMAG,X ; get byte from table
STA CHRGET,X ; save byte in page zero
DEX ; decrement count
BPL LAB_E3C4 ; loop if not all done
LDA #$03 ; set step size, collecting descriptors
STA FOUR6 ; save garbage collection step size
LDA #$00 ; clear .A
STA BITS ; clear FAC1 overflow byte
STA CHANNL ; clear current I/O channel, flag default
STA LASTPT+1 ; clear current descriptor stack item pointer high byte
LDX #$01 ; set .X
STX CHNLNK+1 ; set chain link pointer low byte
STX CHNLNK ; set chain link pointer high byte
LDX #TEMPST ; initial value for descriptor stack
STX TEMPPT ; set descriptor stack pointer
SEC ; set Cb = 1 to read the bottom of memory
JSR MEMBOT ; read/set the bottom of memory
STX TXTTAB ; save start of memory low byte
STY TXTTAB+1 ; save start of memory high byte
SEC ; set Cb = 1 to read the top of memory
JSR MEMTOP ; read/set the top of memory
STX MEMSIZ ; save end of memory low byte
STY MEMSIZ+1 ; save end of memory high byte
STX FRETOP ; set bottom of string space low byte
STY FRETOP+1 ; set bottom of string space high byte
LDY #$00 ; clear index
TYA ; clear .A
STA (TXTTAB),Y ; clear first byte of memory
INC TXTTAB ; increment start of memory low byte
BNE LAB_E403 ; branch if no rollover
INC TXTTAB+1 ; increment start of memory high byte
LAB_E403
RTS
;***********************************************************************************;
;
; print start up message and initialise memory pointers
FREMSG
LDA TXTTAB ; get start of memory low byte
LDY TXTTAB+1 ; get start of memory high byte
JSR RAMSPC ; check available memory, do out of memory error if no room
LDA #<BASMSG ; set "**** CBM BASIC V2 ****" pointer low byte
LDY #>BASMSG ; set "**** CBM BASIC V2 ****" pointer high byte
JSR PRTSTR ; print null terminated string
LDA MEMSIZ ; get end of memory low byte
SEC ; set carry for subtract
SBC TXTTAB ; subtract start of memory low byte
TAX ; copy result to .X
LDA MEMSIZ+1 ; get end of memory high byte
SBC TXTTAB+1 ; subtract start of memory high byte
JSR PRTFIX ; print .X.A as unsigned integer
LDA #<BFREMSG ; set " BYTES FREE" pointer low byte
LDY #>BFREMSG ; set " BYTES FREE" pointer high byte
JSR PRTSTR ; print null terminated string
JMP LAB_C644 ; do NEW, CLR, RESTORE and return
;***********************************************************************************;
;
BFREMSG
.byte " BYTES FREE",$0D,$00
BASMSG
.byte $93,"**** CBM BASIC V2 ****",$0D,$00
;***********************************************************************************;
;
; BASIC vectors, these are copied to RAM from IERROR onwards
BASVCTRS
.word ERROR2 ; error message IERROR
.word MAIN2 ; BASIC warm start IMAIN
.word CRNCH2 ; crunch BASIC tokens ICRNCH
.word QPLOP ; uncrunch BASIC tokens IQPLOP
.word GONE ; start new BASIC code IGONE
.word FEVAL ; get arithmetic element IEVAL
;***********************************************************************************;
;
; initialise BASIC vectors
INITVCTRS
LDX #$0B ; set byte count
LAB_E45D
LDA BASVCTRS,X ; get byte from table
STA IERROR,X ; save byte to RAM
DEX ; decrement index
BPL LAB_E45D ; loop if more to do
RTS
;***********************************************************************************;
;
; BASIC warm start entry point
WARMBAS
JSR CLRCHN ; close input and output channels
LDA #$00 ; clear .A
STA CHANNL ; set current I/O channel, flag default
JSR LAB_C67A ; flush BASIC stack and clear continue pointer
CLI ; enable interrupts
JMP READY ; do warm start
;***********************************************************************************;
;
; checksum byte, not referenced
;LAB_E475
.byte $E8 ; [PAL]
; .byte $41 ; [NTSC]
;***********************************************************************************;
;
; rebuild BASIC line chaining and do RESTORE
PATCHER
JSR LNKPRG ; rebuild BASIC line chaining
JMP LAB_C677 ; do RESTORE, clear stack and return
;***********************************************************************************;
;
; spare bytes, not referenced
;LAB_E47C
.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
.byte $FF,$FF,$FF,$FF
;***********************************************************************************;
;
; set serial data out high
SEROUT1
LDA VIA2PCR ; get VIA 2 PCR
AND #$DF ; set CB2 low, serial data out high
STA VIA2PCR ; set VIA 2 PCR
RTS
;***********************************************************************************;
;
; set serial data out low
SEROUT0
LDA VIA2PCR ; get VIA 2 PCR
ORA #$20 ; set CB2 high, serial data out low
STA VIA2PCR ; set VIA 2 PCR
RTS
;***********************************************************************************;
;
; get serial clock status
SERGET
LDA VIA1PA2 ; get VIA 1 DRA, no handshake
CMP VIA1PA2 ; compare with self
BNE SERGET ; loop if changing
LSR ; shift serial clock to Cb
RTS
;***********************************************************************************;
;
; get secondary address and print "SEARCHING..."
PATCH1
LDX SA ; get secondary address
JMP SRCHING ; print "SEARCHING..." and return
;***********************************************************************************;
;
; set LOAD address if secondary address = 0
PATCH2
TXA ; copy secondary address
BNE LAB_E4CC ; load location not set in LOAD call, so
; continue with load
LDA MEMUSS ; get load start address low byte
STA EAL ; save program start address low byte
LDA MEMUSS+1 ; get load start address high byte
STA EAL+1 ; save program start address high byte
LAB_E4CC
JMP LDVRMSG ; display "LOADING" or "VERIFYING" and return
;***********************************************************************************;
;
; patch for CLOSE
PATCH3
JSR WBLK ; initiate tape write
BCC LAB_E4D7 ; branch if no error
PLA ; else dump stacked exit code
LDA #$00 ; clear exit code
LAB_E4D7
JMP LAB_F39E ; go do I/O close
;***********************************************************************************;
;
; spare bytes, not referenced
.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
.byte $FF,$FF,$FF,$FF,$FF,$FF
;***********************************************************************************;
;
; return base address of I/O devices
; This routine will set .X.Y to the address of the memory section where the memory
; mapped I/O devices are located. This address can then be used with an offset to
; access the memory mapped I/O devices in the computer.
FIOBASE
LDX #<VIA1PB ; get I/O base address low byte
LDY #>VIA1PB ; get I/O base address high byte
RTS
;***********************************************************************************;
;
; return X,Y organisation of screen
; this routine returns the x,y organisation of the screen in .X,.Y
FSCREEN
LDX #$16 ; get screen X, 22 columns
LDY #$17 ; get screen Y, 23 rows
RTS
;***********************************************************************************;
;
; read/set X,Y cursor position, Cb = 1 to read, Cb = 0 to set
; This routine, when called with the carry flag set, loads the current position of
; the cursor on the screen into the .X and .Y registers. .Y is the column number of
; the cursor location and .X is the row number of the cursor. A call with the carry
; bit clear moves the cursor to the position determined by the .X and .Y registers.
FPLOT
BCS LAB_E513 ; if read cursor skip the set cursor
STX TBLX ; save cursor row
STY PNTR ; save cursor column
JSR SETSLINK ; set screen pointers for cursor row, column
LAB_E513
LDX TBLX ; get cursor row
LDY PNTR ; get cursor column
RTS
;***********************************************************************************;
;
; initialise hardware
INITSK
JSR SETIODEF ; set default devices and initialise VIC chip
LDA HIBASE ; get screen memory page
AND #$FD ; mask xxxx xx0x, all but va9
ASL ; << 1 xxxx x0x0
ASL ; << 2 xxxx 0x00
ORA #$80 ; set 1xxx 0x00
STA VICCR5 ; set screen and character memory location
LDA HIBASE ; get screen memory page
AND #$02 ; mask va9 bit
BEQ LAB_E536 ; if zero just go normalise screen
; else set va9 in VIC chip
LDA #$80 ; set b7
ORA VICCR2 ; OR in as video address 9
STA VICCR2 ; save new video address
; now normalise screen
LAB_E536
LDA #$00 ; clear .A
STA MODE ; clear shift mode switch
STA BLNON ; clear cursor blink phase
LDA #<SETKEYS ; get keyboard decode logic pointer low byte
STA KEYLOG ; set keyboard decode logic pointer low byte
LDA #>SETKEYS ; get keyboard decode logic pointer high byte
STA KEYLOG+1 ; set keyboard decode logic pointer high byte
LDA #$0A ; 10d
STA XMAX ; set maximum size of keyboard buffer
STA DELAY ; set repeat delay counter
LDA #$06 ; colour blue
STA COLOR ; set current colour code
LDA #$04 ; speed 4
STA KOUNT ; set repeat speed counter
LDA #$0C ; cursor flash timing
STA BLNCT ; set cursor timing countdown
STA BLNSW ; set cursor enable, $00 = flash cursor
; clear screen
CLSR
LDA HIBASE ; get screen memory page
ORA #$80 ; set high bit, flag every line is logical line start
TAY ; copy to .Y
LDA #$00 ; clear line start low byte
TAX ; clear index
LAB_E568
STY LDTB1,X ; save start of line .X pointer high byte
CLC ; clear carry for add
ADC #$16 ; add line length to low byte
BCC LAB_E570 ; if no rollover skip the high byte increment
INY ; else increment high byte
LAB_E570
INX ; increment line index
CPX #$18 ; compare with number of lines + 1
BNE LAB_E568 ; loop if not all done
LDA #$FF ; end of table marker ??
STA LDTB1,X ; mark end of table
LDX #$16 ; set line count, 23 lines to do, 0 to 22
LAB_E57B
JSR CLRALINE ; clear screen line .X
DEX ; decrement count
BPL LAB_E57B ; loop if more to do
; home cursor
HOME
LDY #$00 ; clear .Y
STY PNTR ; clear cursor column
STY TBLX ; clear cursor row
; set screen pointers for cursor row, column
SETSLINK
LDX TBLX ; get cursor row
LDA PNTR ; get cursor column
LAB_E58B
LDY LDTB1,X ; get start of line .X pointer high byte
BMI LAB_E597 ; continue if logical line start
CLC ; else clear carry for add
ADC #$16 ; add one line length
STA PNTR ; save cursor column
DEX ; decrement cursor row
BPL LAB_E58B ; loop, branch always
LAB_E597
LDA LDTB1,X ; get start of line .X pointer high byte
AND #$03 ; mask 0000 00xx, line memory page
ORA HIBASE ; OR with screen memory page
STA PNT+1 ; set current screen line pointer high byte
LDA LDTB2,X ; get start of line low byte from ROM table
STA PNT ; set current screen line pointer low byte
LDA #$15 ; set line length
INX ; increment cursor row
LAB_E5A8
LDY LDTB1,X ; get start of line .X pointer high byte
BMI LAB_E5B2 ; exit if logical line start
CLC ; else clear carry for add
ADC #$16 ; add one line length to current line length
INX ; increment cursor row
BPL LAB_E5A8 ; loop, branch always
LAB_E5B2
STA LNMX ; save current screen line length
RTS
;***********************************************************************************;
;
; set default devices, initialise VIC chip and home cursor
;
; unreferenced code
;UNUSDNMI
JSR SETIODEF ; set default devices and initialise VIC chip
JMP HOME ; home cursor and return
;***********************************************************************************;
;
; set default devices and initialise VIC chip
SETIODEF
LDA #$03 ; set screen
STA DFLTO ; set output device number
LDA #$00 ; set keyboard
STA DFLTN ; set input device number
; initialise VIC chip
INITVIC
LDX #$10 ; set byte count
LAB_E5C5
LDA VICINIT-1,X ; get byte from setup table
STA VICCR0-1,X ; save byte to VIC chip
DEX ; decrement count/index
BNE LAB_E5C5 ; loop if more to do
RTS
;***********************************************************************************;
;
; input from keyboard buffer
LP2
LDY KEYD ; get current character from buffer
LDX #$00 ; clear index
LAB_E5D4
LDA KEYD+1,X ; get next character,.X from buffer
STA KEYD,X ; save as current character,.X in buffer
INX ; increment index
CPX NDX ; compare with keyboard buffer index
BNE LAB_E5D4 ; loop if more to do
DEC NDX ; decrement keyboard buffer index
TYA ; copy key to .A
CLI ; enable interrupts
CLC ; flag got byte
RTS
;***********************************************************************************;
;
; write character and wait for key
GETQUE
JSR SCRNOUT ; output character
; wait for key from keyboard
LAB_E5E8
LDA NDX ; get keyboard buffer index
STA BLNSW ; cursor enable, $00 = flash cursor, $xx = no flash
STA AUTODN ; screen scrolling flag, $00 = scroll, $xx = no scroll
; this disables both the cursor flash and the screen scroll
; while there are characters in the keyboard buffer
BEQ LAB_E5E8 ; loop if buffer empty
SEI ; disable interrupts
LDA BLNON ; get cursor blink phase
BEQ LAB_E602 ; branch if cursor phase
; else character phase
LDA CDBLN ; get character under cursor
LDX GDCOL ; get colour under cursor
LDY #$00 ; clear .Y
STY BLNON ; clear cursor blink phase
JSR SYNPRT ; print character .A and colour .X
LAB_E602
JSR LP2 ; input from keyboard buffer
CMP #$83 ; compare with [SHIFT][RUN]
BNE GET2RTN ; branch if not [SHIFT][RUN]
; keys are [SHIFT][RUN] so put "LOAD",$0D,"RUN",$0D into
; the buffer
LDX #$09 ; set byte count
SEI ; disable interrupts
STX NDX ; set keyboard buffer index
LAB_E60E
LDA RUNTB-1,X ; get byte from auto load/run table
STA KEYD-1,X ; save to keyboard buffer
DEX ; decrement count/index
BNE LAB_E60E ; loop while more to do
BEQ LAB_E5E8 ; loop for next key, branch always
; was not [SHIFT][RUN]
GET2RTN
CMP #$0D ; compare with [RETURN]
BNE GETQUE ; if not [RETURN] print character and get next key
; was [RETURN]
LDY LNMX ; get current screen line length
STY CRSW ; set input from screen
LAB_E621
LDA (PNT),Y ; get character from current screen line
CMP #' ' ; compare with [SPACE]
BNE LAB_E62A ; branch if not [SPACE]
DEY ; else eliminate the space, decrement end of input line
BNE LAB_E621 ; loop, branch always
LAB_E62A
INY ; increment past last non space character on line
STY INDX ; save input EOL pointer
LDY #$00 ; clear .Y
STY AUTODN ; clear screen scrolling flag, $00 = scroll, $xx = no scroll
STY PNTR ; clear cursor column
STY QTSW ; clear cursor quote flag
LDA LXSP ; get input cursor row
BMI LAB_E657 ; branch if input cursor row has become -ve
LDX TBLX ; get cursor row
JSR LAB_E719 ; find and set pointers for start of logical line
CPX LXSP ; compare with input cursor row
BNE LAB_E657 ;.
BNE LAB_E657 ;.?? what's this? just to make sure or something
LDA LXSP+1 ; get input cursor column
STA PNTR ; save cursor column
CMP INDX ; compare with input EOL pointer
BCC LAB_E657 ; branch if less, cursor is in line
BCS LAB_E691 ; else cursor is beyond the line end, branch always
;***********************************************************************************;
;
; input from screen or keyboard
GETSCRN
TYA ; copy .Y
PHA ; save .Y
TXA ; copy .X
PHA ; save .X
LDA CRSW ; get input from keyboard or screen, $xx = screen,
; $00 = keyboard
BEQ LAB_E5E8 ; if keyboard go wait for key
LAB_E657
LDY PNTR ; get cursor column
LDA (PNT),Y ; get character from the current screen line
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ; just a few wasted cycles
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
; map to ASCII
STA ASCII ; save temporary last character
AND #$3F ; leave b5 to b0 in .A
ASL ASCII ; shift b7 into Cb
BIT ASCII ; copy b6 into Sb, b5 into Ob
BPL LAB_E67E ; branch if b6 = 0
ORA #$80 ; map $40-$7F to $C0-$FF
LAB_E67E
BCC LAB_E684 ; branch if b7 = 0
LDX QTSW ; get cursor quote flag, $01 = quote, $00 = no quote
BNE LAB_E688 ; branch if in quote mode
LAB_E684
BVS LAB_E688 ; branch if b5 = 1
ORA #$40 ; map $00-$1F to $40-5F
LAB_E688
INC PNTR ; increment cursor column
JSR QUOTECK ; if open quote toggle cursor quote flag
CPY INDX ; compare with input EOL pointer
BNE LAB_E6A8 ; branch if not at line end
LAB_E691
LDA #$00 ; clear .A
STA CRSW ; set input from keyboard
LDA #$0D ; set character [CR]
LDX DFLTN ; get input device number
CPX #$03 ; compare with screen
BEQ LAB_E6A3 ; branch if screen
LDX DFLTO ; get output device number
CPX #$03 ; compare with screen
BEQ LAB_E6A6 ; branch if screen
LAB_E6A3
JSR SCRNOUT ; output character
LAB_E6A6
LDA #$0D ; set character [CR]
LAB_E6A8
STA ASCII ; save character
PLA ; pull .X
TAX ; restore .X
PLA ; pull .Y
TAY ; restore .Y
LDA ASCII ; restore character
CMP #$DE ; compare with [PI]
BNE LAB_E6B6 ; exit if not [PI]
LDA #TK_PI ; set character to BASIC token
LAB_E6B6
CLC ; flag ok
RTS
;***********************************************************************************;
;
; if open quote toggle cursor quote flag
QUOTECK
CMP #$22 ; compare byte with "
BNE LAB_E6C4 ; exit if not "
LDA QTSW ; get cursor quote flag
EOR #$01 ; toggle b0
STA QTSW ; save cursor quote flag
LDA #$22 ; restore the "
LAB_E6C4
RTS
;***********************************************************************************;
;
; insert uppercase/graphic character
SETCHAR
ORA #$40 ; change to uppercase/graphic
LAB_E6C7
LDX RVS ; get reverse flag
BEQ LAB_E6CD ; branch if not reverse
; else ..
; insert reversed character
LAB_E6CB
ORA #$80 ; reverse character
LAB_E6CD
LDX INSRT ; get insert count
BEQ LAB_E6D3 ; branch if none
DEC INSRT ; else decrement insert count
LAB_E6D3
LDX COLOR ; get current colour code
JSR SYNPRT ; print character .A and colour .X
JSR SCROLL ; advance cursor
; restore registers, set quote flag and exit
LAB_E6DC
PLA ; pull .Y
TAY ; restore .Y
LDA INSRT ; get insert count
BEQ LAB_E6E4 ; skip quote flag clear if inserts to do
LSR QTSW ; clear cursor quote flag
LAB_E6E4
PLA ; pull .X
TAX ; restore .X
PLA ; restore .A
CLC ; flag ok
CLI ; enable interrupts
RTS
;***********************************************************************************;
;
; advance cursor
SCROLL
JSR FORWARD ; test for line increment
INC PNTR ; increment cursor column
LDA LNMX ; get current screen line length
CMP PNTR ; compare with cursor column
BCS LAB_E72C ; exit if line length >= cursor column
CMP #$57 ; compare with max length
BEQ LAB_E723 ; if at max clear column, back cursor up and do newline
LDA AUTODN ; get autoscroll flag
BEQ LAB_E701 ; branch if autoscroll on
JMP LAB_E9F0 ; else open space on screen
LAB_E701
LDX TBLX ; get cursor row
CPX #$17 ; compare with max + 1
BCC LAB_E70E ; if less than max + 1 go add this row to the current
; logical line
JSR SCRL ; else scroll screen
DEC TBLX ; decrement cursor row
LDX TBLX ; get cursor row
; add this row to the current logical line
LAB_E70E
ASL LDTB1,X ; shift start of line .X pointer high byte
LSR LDTB1,X ; shift start of line .X pointer high byte back,
; clear b7, start of logical line
JMP WRAPLINE ; make next screen line start of logical line, increment
; line length and set pointers
; add one line length and set pointers for start of line
LAB_E715
ADC #$16 ; add one line length
STA LNMX ; save current screen line length
; find and set pointers for start of logical line
LAB_E719
LDA LDTB1,X ; get start of line .X pointer high byte
BMI LAB_E720 ; exit loop if start of logical line
DEX ; else back up one line
BNE LAB_E719 ; loop if not on first line
LAB_E720
JMP LINPTR ; set start of line .X and return
; clear cursor column, back cursor up one line and do newline
LAB_E723
DEC TBLX ; decrement cursor row. if the cursor was incremented past
; the last line then this decrement and the scroll will
; leave the cursor one line above the bottom of the screen
JSR NXTLINE ; do newline
LDA #$00 ; clear .A
STA PNTR ; clear cursor column
LAB_E72C
RTS
; back onto previous line if possible
RETREAT
LDX TBLX ; get cursor row
BNE LAB_E737 ; branch if not top row
STX PNTR ; clear cursor column
PLA ; dump return address low byte
PLA ; dump return address high byte
BNE LAB_E6DC ; restore registers, set quote flag and exit, branch always
LAB_E737
DEX ; decrement cursor row
STX TBLX ; save cursor row
JSR SETSLINK ; set screen pointers for cursor row, column
LDY LNMX ; get current screen line length
STY PNTR ; save as cursor column
RTS
;***********************************************************************************;
;
; output character to screen
SCRNOUT
PHA ; save character
STA ASCII ; save temporary last character
TXA ; copy .X
PHA ; save .X
TYA ; copy .Y
PHA ; save .Y
LDA #$00 ; clear .A
STA CRSW ; set input from keyboard
LDY PNTR ; get cursor column
LDA ASCII ; restore last character
BPL LAB_E756 ; branch if unshifted
JMP LAB_E800 ; do shifted characters and return
LAB_E756
CMP #$0D ; compare with [CR]
BNE LAB_E75D ; branch if not [CR]
JMP RTRN ; else output [CR] and return
LAB_E75D
CMP #' ' ; compare with [SPACE]
BCC LAB_E771 ; branch if < [SPACE]
; map to screen code
CMP #$60 ; compare with first graphic character
BCC LAB_E769 ; branch if $20 to $5F
; character is between $60 and $7F
AND #$DF ; mask xx0x xxxx, map to $40-$5F
BNE LAB_E76B ; branch always
LAB_E769
AND #$3F ; mask 00xx xxxx, map $40-$5F to $00-$1F
LAB_E76B
JSR QUOTECK ; if open quote toggle cursor direct/programmed flag
JMP LAB_E6C7 ; print character, scroll if needed and exit
; character was < [SPACE] so is a control character
; of some sort
LAB_E771
LDX INSRT ; get insert count
BEQ LAB_E778 ; branch if no characters to insert
JMP LAB_E6CB ; insert reversed character
LAB_E778
CMP #$14 ; compare with [DELETE]
BNE LAB_E7AA ; branch if not [DELETE]
TYA ; copy cursor column to .A
BNE LAB_E785 ; branch if not at start of line
JSR RETREAT ; back onto previous line if possible
JMP LAB_E79F ; clear last character on current screen line
LAB_E785
JSR BACKUP ; test for line decrement
; now close up the line
DEY ; decrement index to previous character
STY PNTR ; save cursor column
JSR COLORSYN ; calculate pointer to colour RAM
LAB_E78E
INY ; increment index to next character
LDA (PNT),Y ; get character from current screen line
DEY ; decrement index to previous character
STA (PNT),Y ; save character to current screen line
INY ; increment index to next character
LDA (USER),Y ; get colour RAM byte
DEY ; decrement index to previous character
STA (USER),Y ; save colour RAM byte
INY ; increment index to next character
CPY LNMX ; compare with current screen line length
BNE LAB_E78E ; loop if not there yet
LAB_E79F
LDA #' ' ; set [SPACE]
STA (PNT),Y ; clear last character on current screen line
LDA COLOR ; get current colour code
STA (USER),Y ; save to colour RAM
BPL LAB_E7F7 ; restore registers, set quote flag and exit, branch always
LAB_E7AA
LDX QTSW ; get cursor quote flag, $01 = quote, $00 = no quote
BEQ LAB_E7B1 ; branch if not quote mode
JMP LAB_E6CB ; insert reversed character, scroll if needed and exit
LAB_E7B1
CMP #$12 ; compare with [RVS ON]
BNE LAB_E7B7 ; branch if not [RVS ON]
STA RVS ; set reverse flag
LAB_E7B7
CMP #$13 ; compare with [HOME]
BNE LAB_E7BE ; branch if not [HOME]
JSR HOME ; home cursor
LAB_E7BE
CMP #$1D ; compare with [CURSOR RIGHT]
BNE LAB_E7D9 ; branch if not [CURSOR RIGHT]
INY ; increment cursor column
JSR FORWARD ; test for line increment
STY PNTR ; save cursor column
DEY ; decrement cursor column
CPY LNMX ; compare cursor column with current screen line length
BCC LAB_E7D6 ; exit if less
; else the cursor column is >= the current screen line
; length so back onto the current line and do a newline
DEC TBLX ; decrement cursor row
JSR NXTLINE ; do newline
LDY #$00 ; clear cursor column
LAB_E7D4
STY PNTR ; save cursor column
LAB_E7D6
JMP LAB_E6DC ; restore registers, set quote flag and exit
LAB_E7D9
CMP #$11 ; compare with [CURSOR DOWN]
BNE LAB_E7FA ; branch if not [CURSOR DOWN]
CLC ; clear carry for add
TYA ; copy cursor column
ADC #$16 ; add one line
TAY ; copy back to .A
INC TBLX ; increment cursor row
CMP LNMX ; compare cursor column with current screen line length
BCC LAB_E7D4 ; save cursor column and exit if less
BEQ LAB_E7D4 ; save cursor column and exit if equal
; else the cursor has moved beyond the end of this line
; so back it up until it's on the start of the logical line
DEC TBLX ; decrement cursor row
LAB_E7EC
SBC #$16 ; subtract one line
BCC LAB_E7F4 ; exit loop if on previous line
STA PNTR ; else save cursor column
BNE LAB_E7EC ; loop if not at start of line
LAB_E7F4
JSR NXTLINE ; do newline
LAB_E7F7
JMP LAB_E6DC ; restore registers, set quote flag and exit
LAB_E7FA
JSR COLORSET ; set the colour from the character in .A
JMP CHARSET ; select VIC character set
; character is $80 or greater
LAB_E800
NOP ; just a few wasted cycles
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
AND #$7F ; mask 0xxx xxxx, clear b7
CMP #$7F ; was it [PI] before the mask
BNE LAB_E81D ; branch if not
LDA #$5E ; else make it $5E
LAB_E81D
NOP ; just a few wasted cycles
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
CMP #' ' ; compare with [SPACE]
BCC LAB_E82A ; branch if < [SPACE]
JMP SETCHAR ; insert uppercase/graphic character and return
; character was $80 to $9F and is now $00 to $1F
LAB_E82A
CMP #$0D ; compare with [CR]
BNE LAB_E831 ; branch if not [CR]
JMP RTRN ; else output [CR] and return
; was not [CR]
LAB_E831
LDX QTSW ; get cursor quote flag, $01 = quote, $00 = no quote
BNE LAB_E874 ; branch if quote mode
CMP #$14 ; compare with [DELETE]
BNE LAB_E870 ; branch if not [DELETE]
LDY LNMX ; get current screen line length
LDA (PNT),Y ; get character from current screen line
CMP #' ' ; compare with [SPACE]
BNE LAB_E845 ; branch if not [SPACE]
CPY PNTR ; compare current column with cursor column
BNE LAB_E84C ; if not cursor column go open up space on line
LAB_E845
CPY #$57 ; compare current column with max line length
BEQ LAB_E86D ; exit if at line end
JSR OPENLIN ; else open space on screen
; now open up space on the line to insert a character
LAB_E84C
LDY LNMX ; get current screen line length
JSR COLORSYN ; calculate pointer to colour RAM
LAB_E851
DEY ; decrement index to previous character
LDA (PNT),Y ; get character from current screen line
INY ; increment index to next character
STA (PNT),Y ; save character to current screen line
DEY ; decrement index to previous character
LDA (USER),Y ; get current screen line colour RAM byte
INY ; increment index to next character
STA (USER),Y ; save current screen line colour RAM byte
DEY ; decrement index to previous character
CPY PNTR ; compare with cursor column
BNE LAB_E851 ; loop if not there yet
LDA #' ' ; set [SPACE]
STA (PNT),Y ; clear character at cursor position on current screen line
LDA COLOR ; get current colour code
STA (USER),Y ; save to cursor position on current screen line colour RAM
INC INSRT ; increment insert count
LAB_E86D
JMP LAB_E6DC ; restore registers, set quote flag and exit
LAB_E870
LDX INSRT ; get insert count
BEQ LAB_E879 ; branch if no insert space
LAB_E874
ORA #$40 ; change to uppercase/graphic
JMP LAB_E6CB ; insert reversed character, scroll if needed and exit
LAB_E879
CMP #$11 ; compare with [CURSOR UP]
BNE LAB_E893 ; branch if not [CURSOR UP]
LDX TBLX ; get cursor row
BEQ LAB_E8B8 ; branch if on top line
DEC TBLX ; decrement cursor row
LDA PNTR ; get cursor column
SEC ; set carry for subtract
SBC #$16 ; subtract one line length
BCC LAB_E88E ; branch if stepped back to previous line
STA PNTR ; else save cursor column ..
BPL LAB_E8B8 ; .. and exit, branch always
LAB_E88E
JSR SETSLINK ; set screen pointers for cursor row, column ..
BNE LAB_E8B8 ; .. and exit, branch always
LAB_E893
CMP #$12 ; compare with [RVS OFF]
BNE LAB_E89B ; branch if not [RVS OFF]
LDA #$00 ; clear .A
STA RVS ; clear reverse flag
LAB_E89B
CMP #$1D ; compare with [CURSOR LEFT]
BNE LAB_E8B1 ; branch if not [CURSOR LEFT]
TYA ; copy cursor column
BEQ LAB_E8AB ; branch if at start of line
JSR BACKUP ; test for line decrement
DEY ; decrement cursor column
STY PNTR ; save cursor column
JMP LAB_E6DC ; restore registers, set quote flag and exit
LAB_E8AB
JSR RETREAT ; back onto previous line if possible
JMP LAB_E6DC ; restore registers, set quote flag and exit
LAB_E8B1
CMP #$13 ; compare with [CLR]
BNE LAB_E8BB ; branch if not [CLR]
JSR CLSR ; clear screen
LAB_E8B8
JMP LAB_E6DC ; restore registers, set quote flag and exit
LAB_E8BB
ORA #$80 ; restore b7, colour can only be black, cyan, magenta
; or yellow, or [SWITCH TO UPPER CASE]
JSR COLORSET ; set the colour from the character in .A
JMP GRAPHMODE ; select VIC character set
;***********************************************************************************;
;
; do newline
NXTLINE
LSR LXSP ; shift >> input cursor row
LDX TBLX ; get cursor row
LAB_E8C7
INX ; increment row
CPX #$17 ; compare with last row + 1
BNE LAB_E8CF ; branch if not last row + 1
JSR SCRL ; else scroll screen
LAB_E8CF
LDA LDTB1,X ; get start of line .X pointer high byte
BPL LAB_E8C7 ; loop if not start of logical line
STX TBLX ; else save cursor row
JMP SETSLINK ; set screen pointers for cursor row, column and return
;***********************************************************************************;
;
; output [CR]
RTRN
LDX #$00 ; clear .X
STX INSRT ; clear insert count
STX RVS ; clear reverse flag
STX QTSW ; clear cursor quote flag
STX PNTR ; clear cursor column
JSR NXTLINE ; do newline
JMP LAB_E6DC ; restore registers, set quote flag and exit
;***********************************************************************************;
;
; test for line decrement
BACKUP
LDX #$04 ; set count
LDA #$00 ; set column
LAB_E8EC
CMP PNTR ; compare with cursor column
BEQ LAB_E8F7 ; branch if at start of line
CLC ; else clear carry for add
ADC #$16 ; increment to next line
DEX ; decrement loop count
BNE LAB_E8EC ; loop if more to test
RTS
LAB_E8F7
DEC TBLX ; else decrement cursor row
RTS
;***********************************************************************************;
;
; test for line increment. if at end of line, but not at end of last line, increment the
; cursor row
FORWARD
LDX #$04 ; set count
LDA #$15 ; set column
LAB_E8FE
CMP PNTR ; compare with cursor column
BEQ LAB_E909 ; if at end of line test and possibly increment cursor row
CLC ; else clear carry for add
ADC #$16 ; increment to next line
DEX ; decrement loop count
BNE LAB_E8FE ; loop if more to test
RTS
; cursor is at end of line
LAB_E909
LDX TBLX ; get cursor row
CPX #$17 ; compare with end of screen
BEQ LAB_E911 ; exit if end of screen
INC TBLX ; else increment cursor row
LAB_E911
RTS
;***********************************************************************************;
;
; set colour code. enter with the colour character in .A. if .A does not contain a
; colour character this routine exits without changing the colour
COLORSET
LDX #LAB_E928-COLORTBL
; set colour code count
LAB_E914
CMP COLORTBL,X ; compare the character with the table code
BEQ LAB_E91D ; if a match go save the colour and exit
DEX ; else decrement the index
BPL LAB_E914 ; loop if more to do
RTS
LAB_E91D
STX COLOR ; set current colour code
RTS
;***********************************************************************************;
;
; ASCII colour code table
; CHR$() colour
COLORTBL ; ------ ------
.byte $90 ; 144 black
.byte $05 ; 5 white
.byte $1C ; 28 red
.byte $9F ; 159 cyan
.byte $9C ; 156 magenta
.byte $1E ; 30 green
.byte $1F ; 31 blue
LAB_E928
.byte $9E ; 158 yellow
;***********************************************************************************;
;
; code conversion, these don't seem to be used anywhere
;CNVRTCD
.byte $EF,$A1,$DF,$A6,$E1,$B1,$E2,$B2,$E3,$B3,$E4,$B4,$E5,$B5,$E6,$B6
.byte $E7,$B7,$E8,$B8,$E9,$B9,$FA,$BA,$FB,$BB,$FC,$BC,$EC,$BD,$FE,$BE
.byte $84,$BF,$F7,$C0,$F8,$DB,$F9,$DD,$EA,$DE,$5E,$E0,$5B,$E1,$5D,$E2
.byte $40,$B0,$61,$B1,$78,$DB,$79,$DD,$66,$B6,$77,$C0,$70,$F0,$71,$F1
.byte $72,$F2,$73,$F3,$74,$F4,$75,$F5,$76,$F6,$7D,$FD
;***********************************************************************************;
;
; scroll screen
SCRL
LDA SAL ; copy tape buffer start pointer
PHA ; save it
LDA SAL+1 ; copy tape buffer start pointer
PHA ; save it
LDA EAL ; copy tape buffer end pointer
PHA ; save it
LDA EAL+1 ; copy tape buffer end pointer
PHA ; save it
LAB_E981
LDX #$FF ; set to -1 for pre increment loop
DEC TBLX ; decrement cursor row
DEC LXSP ; decrement input cursor row
DEC LLNKSV ; decrement screen row marker
LAB_E989
INX ; increment line number
JSR LINPTR ; set start of line .X
CPX #$16 ; compare with last line
BCS LAB_E99D ; branch if >= $16
LDA LDTB2+1,X ; get start of next line pointer low byte
STA SAL ; save next line pointer low byte
LDA LDTB1+1,X ; get start of next line pointer high byte
JSR MOVLIN ; shift screen line up
BMI LAB_E989 ; loop, branch always
LAB_E99D
JSR CLRALINE ; clear screen line .X
; now shift up the start of logical line bits
LDX #$00 ; clear index
LAB_E9A2
LDA LDTB1,X ; get start of line .X pointer high byte
AND #$7F ; clear line .X start of logical line bit
LDY LDTB1+1,X ; get start of next line pointer high byte
BPL LAB_E9AC ; branch if next line not start of line
ORA #$80 ; set line .X start of logical line bit
LAB_E9AC
STA LDTB1,X ; set start of line .X pointer high byte
INX ; increment line number
CPX #$16 ; compare with last line
BNE LAB_E9A2 ; loop if not last line
LDA LDTB1+$16 ; get start of last line pointer high byte
ORA #$80 ; mark as start of logical line
STA LDTB1+$16 ; set start of last line pointer high byte
LDA LDTB1 ; get start of first line pointer high byte
BPL LAB_E981 ; if not start of logical line loop back and
; scroll the screen up another line
INC TBLX ; increment cursor row
INC LLNKSV ; increment screen row marker
LDA #$FB ; set keyboard column c2
STA VIA2PB ; set VIA 2 DRB, keyboard column
LDA VIA2PA1 ; get VIA 2 DRA, keyboard row
CMP #$FE ; compare with row r0 active, [CTRL]
PHP ; save status
LDA #$F7 ; set keyboard column c3
STA VIA2PB ; set VIA 2 DRB, keyboard column
PLP ; restore status
BNE LAB_E9DF ; skip delay if [CTRL] not pressed
; first time round the inner loop .X will be $16
LDY #$00 ; clear delay outer loop count, do this 256 times
LAB_E9D6
NOP ; waste cycles
DEX ; decrement inner loop count
BNE LAB_E9D6 ; loop if not all done
DEY ; decrement outer loop count
BNE LAB_E9D6 ; loop if not all done
STY NDX ; clear keyboard buffer index
LAB_E9DF
LDX TBLX ; get cursor row
PLA ; pull tape buffer end pointer
STA EAL+1 ; restore it
PLA ; pull tape buffer end pointer
STA EAL ; restore it
PLA ; pull tape buffer pointer
STA SAL+1 ; restore it
PLA ; pull tape buffer pointer
STA SAL ; restore it
RTS
;***********************************************************************************;
;
; open space on screen
OPENLIN
LDX TBLX ; get cursor row
LAB_E9F0
INX ; increment row
LDA LDTB1,X ; get start of line .X pointer high byte
BPL LAB_E9F0 ; branch if not start of logical line
STX LLNKSV ; set screen row marker
CPX #$16 ; compare with last line
BEQ LAB_EA08 ; branch if = last line
BCC LAB_EA08 ; branch if < last line
; else was > last line
JSR SCRL ; else scroll screen
LDX LLNKSV ; get screen row marker
DEX ; decrement screen row marker
DEC TBLX ; decrement cursor row
JMP LAB_E70E ; add this row to the current logical line and return
LAB_EA08
LDA SAL ; copy tape buffer pointer
PHA ; save it
LDA SAL+1 ; copy tape buffer pointer
PHA ; save it
LDA EAL ; copy tape buffer end pointer
PHA ; save it
LDA EAL+1 ; copy tape buffer end pointer
PHA ; save it
LDX #$17 ; set to end line + 1 for predecrement loop
LAB_EA16
DEX ; decrement line number
JSR LINPTR ; set start of line .X
CPX LLNKSV ; compare with screen row marker
BCC LAB_EA2C ; branch if < screen row marker
BEQ LAB_EA2C ; branch if = screen row marker
LDA LDTB2-1,X ; else get start of previous line low byte from ROM table
STA SAL ; save previous line pointer low byte
LDA LDTB1-1,X ; get start of previous line pointer high byte
JSR MOVLIN ; shift screen line down
BMI LAB_EA16 ; loop, branch always
LAB_EA2C
JSR CLRALINE ; clear screen line .X
LDX #$15 ; set index to last screen row - 1
LAB_EA31
CPX LLNKSV ; compare with saved screen row
BCC LAB_EA44 ; reached insertion point so stop
LDA LDTB1+1,X ; get start of line .X + 1 pointer high byte
AND #$7F ; mask start of logical line bit
LDY LDTB1,X ; get start of line .X pointer high byte
BPL LAB_EA3F ; branch if start of logical line bit clear
ORA #$80 ; set start of logical line bit
LAB_EA3F
STA LDTB1+1,X ; update start of line .X + 1 pointer high byte
DEX ; decrement index
BNE LAB_EA31 ; branch always
LAB_EA44
LDX LLNKSV ; get saved screen row
JSR LAB_E70E ; add this row to the current logical line
PLA ; pull tape buffer end pointer
STA EAL+1 ; restore it
PLA ; pull tape buffer end pointer
STA EAL ; restore it
PLA ; pull tape buffer pointer
STA SAL+1 ; restore it
PLA ; pull tape buffer pointer
STA SAL ; restore it
RTS
;***********************************************************************************;
;
; shift screen line up/down
MOVLIN
AND #$03 ; mask 0000 00xx, line memory page
ORA HIBASE ; OR with screen memory page
STA SAL+1 ; save next/previous line pointer high byte
JSR SETADDR ; calculate pointers to screen lines colour RAM
LAB_EA60
LDY #$15 ; set column count
LAB_EA62
LDA (SAL),Y ; get character from next/previous screen line
STA (PNT),Y ; save character to current screen line
LDA (EAL),Y ; get colour from next/previous screen line colour RAM
STA (USER),Y ; save colour to current screen line colour RAM
DEY ; decrement column index/count
BPL LAB_EA62 ; loop if more to do
RTS
;***********************************************************************************;
;
; calculate pointers to screen lines colour RAM
SETADDR
JSR COLORSYN ; calculate pointer to current screen line colour RAM
LDA SAL ; get next screen line pointer low byte
STA EAL ; save next screen line colour RAM pointer low byte
LDA SAL+1 ; get next screen line pointer high byte
AND #$03 ; mask 0000 00xx, line memory page
ORA #$94 ; set 1001 01xx, colour memory page
STA EAL+1 ; save next screen line colour RAM pointer high byte
RTS
;***********************************************************************************;
;
; set start of line .X
LINPTR
LDA LDTB2,X ; get start of line low byte from ROM table
STA PNT ; set current screen line pointer low byte
LDA LDTB1,X ; get start of line high byte from RAM table
AND #$03 ; mask 0000 00xx, line memory page
ORA HIBASE ; OR with screen memory page
STA PNT+1 ; set current screen line pointer high byte
RTS
;***********************************************************************************;
;
; clear screen line .X
CLRALINE
LDY #$15 ; set number of columns to clear
JSR LINPTR ; set start of line .X
JSR COLORSYN ; calculate pointer to colour RAM
LAB_EA95
LDA #' ' ; set [SPACE]
STA (PNT),Y ; clear character in current screen line
LDA #$01 ; set colour, blue on white
STA (USER),Y ; set colour RAM in current screen line
DEY ; decrement index
BPL LAB_EA95 ; loop if more to do
RTS
;***********************************************************************************;
;
; print character .A and colour .X to screen
SYNPRT
TAY ; copy character
LDA #$02 ; set count to $02, usually $14 ??
STA BLNCT ; set cursor countdown
JSR COLORSYN ; calculate pointer to colour RAM
TYA ; get character back
; save character and colour to screen @ cursor
PUTSCRN
LDY PNTR ; get cursor column
STA (PNT),Y ; save character from current screen line
TXA ; copy colour to .A
STA (USER),Y ; save to colour RAM
RTS
;***********************************************************************************;
;
; calculate pointer to colour RAM
COLORSYN
LDA PNT ; get current screen line pointer low byte
STA USER ; save pointer to colour RAM low byte
LDA PNT+1 ; get current screen line pointer high byte
AND #$03 ; mask 0000 00xx, line memory page
ORA #$94 ; set 1001 01xx, colour memory page
STA USER+1 ; save pointer to colour RAM high byte
RTS
;***********************************************************************************;
;
; update the clock, flash the cursor, control the cassette and scan the keyboard
; IRQ handler
IRQ
JSR UDTIM ; increment real time clock
LDA BLNSW ; get cursor enable
BNE LAB_EAEF ; branch if not flash cursor
DEC BLNCT ; else decrement cursor timing countdown
BNE LAB_EAEF ; branch if not done
LDA #$14 ; set count
STA BLNCT ; save cursor timing countdown
LDY PNTR ; get cursor column
LSR BLNON ; shift b0 cursor blink phase into carry
LDX GDCOL ; get colour under cursor
LDA (PNT),Y ; get character from current screen line
BCS LAB_EAEA ; branch if cursor phase b0 was 1
INC BLNON ; set cursor blink phase to 1
STA CDBLN ; save character under cursor
JSR COLORSYN ; calculate pointer to colour RAM
LDA (USER),Y ; get colour RAM byte
STA GDCOL ; save colour under cursor
LDX COLOR ; get current colour code
LDA CDBLN ; get character under cursor
LAB_EAEA
EOR #$80 ; toggle b7 of character under cursor
JSR PUTSCRN ; save character and colour to screen @ cursor
LAB_EAEF
LDA VIA1PA2 ; get VIA 1 DRA, no handshake
AND #$40 ; mask cassette switch sense
BEQ LAB_EB01 ; branch if cassette sense low
; cassette sense was high so turn off motor and clear
; the interlock
LDY #$00 ; clear .Y
STY CAS1 ; clear the tape motor interlock
LDA VIA1PCR ; get VIA 1 PCR
ORA #$02 ; set CA2 high, turn off motor
BNE LAB_EB0A ; branch always
; cassette sense was low so turn on motor, perhaps
LAB_EB01
LDA CAS1 ; get tape motor interlock
BNE LAB_EB12 ; if cassette interlock <> 0 don't turn on motor
LDA VIA1PCR ; get VIA 1 PCR
AND #$FD ; set CA2 low, turn on motor
LAB_EB0A
BIT VIA1IER ; test VIA 1 IER
BVS LAB_EB12 ; if T1 interrupt enabled don't change motor state
STA VIA1PCR ; set VIA 1 PCR, set CA2 high/low
LAB_EB12
JSR FSCNKEY ; scan keyboard
BIT VIA2T1CL ; test VIA 2 T1C_l, clear the timer interrupt flag
PLA ; pull .Y
TAY ; restore .Y
PLA ; pull .X
TAX ; restore .X
PLA ; restore .A
RTI
;***********************************************************************************;
;
; scan keyboard performs the following ..
;
; 1) check if key pressed, if not then exit the routine
;
; 2) init I/O ports of VIA 2 for keyboard scan and set pointers to decode table 1.
; clear the character counter
;
; 3) set one line of port B low and test for a closed key on port A by shifting the
; byte read from the port. if the carry is clear then a key is closed so save the
; count which is incremented on each shift. check for SHIFT/STOP/C= keys and
; flag if closed
;
; 4) repeat step 3 for the whole matrix
;
; 5) evaluate the SHIFT/CTRL/C= keys, this may change the decode table selected
;
; 6) use the key count saved in step 3 as an index into the table selected in step 5
;
; 7) check for key repeat operation
;
; 8) save the decoded key to the buffer if first press or repeat
; scan keyboard
; This routine will scan the keyboard and check for pressed keys. It is the same
; routine called by the interrupt handler. If a key is down, its ASCII value is
; placed in the keyboard queue.
FSCNKEY
LDA #$00 ; clear .A
STA SHFLAG ; clear keyboard shift/control/C= flag
LDY #$40 ; set no key
STY SFDX ; save which key
STA VIA2PB ; clear VIA 2 DRB, keyboard column
LDX VIA2PA1 ; get VIA 2 DRA, keyboard row
CPX #$FF ; compare with all bits set
BEQ LAB_EB8F ; if no key pressed clear current key and exit (does
; further BEQ to LAB_EBBA)
LDA #$FE ; set column 0 low
STA VIA2PB ; set VIA 2 DRB, keyboard column
LDY #$00 ; clear key count
LDA #<NORMKEYS ; get decode table low byte
STA KEYTAB ; set keyboard pointer low byte
LDA #>NORMKEYS ; get decode table high byte
STA KEYTAB+1 ; set keyboard pointer high byte
LAB_EB40
LDX #$08 ; set row count
LDA VIA2PA1 ; get VIA 2 DRA, keyboard row
CMP VIA2PA1 ; compare with itself
BNE LAB_EB40 ; loop if changing
LAB_EB4A
LSR ; shift row to Cb
BCS LAB_EB63 ; if no key closed on this row go do next row
PHA ; save row
LDA (KEYTAB),Y ; get character from decode table
CMP #$05 ; compare with $05, there is no $05 key but the control
; keys are all less than $05
BCS LAB_EB60 ; if not shift/control/C=/stop go save key count
; else was shift/control/C=/stop key
CMP #$03 ; compare with $03, stop
BEQ LAB_EB60 ; if stop go save key count and continue
; character is $01 - shift, $02 - C= or $04 - control
ORA SHFLAG ; OR keyboard shift/control/C= flag
STA SHFLAG ; save keyboard shift/control/C= flag
BPL LAB_EB62 ; skip save key, branch always
LAB_EB60
STY SFDX ; save which key
LAB_EB62
PLA ; restore row
LAB_EB63
INY ; increment key count
CPY #$41 ; compare with max+1
BCS LAB_EB71 ; exit loop if >= max+1
; else still in matrix
DEX ; decrement row count
BNE LAB_EB4A ; loop if more rows to do
SEC ; set carry for keyboard column shift
ROL VIA2PB ; shift VIA 2 DRB, keyboard column
BNE LAB_EB40 ; loop for next column, branch always
LAB_EB71
JMP (KEYLOG) ; evaluate the SHIFT/CTRL/C= keys, SETKEYS
; key decoding continues here after the SHIFT/CTRL/C= keys are evaluated
LAB_EB74
LDY SFDX ; get which key
LDA (KEYTAB),Y ; get character from decode table
TAX ; copy character to .X
CPY LSTX ; compare which key with last key
BEQ LAB_EB84 ; if this key = current key, key held, go test repeat
LDY #$10 ; set repeat delay count
STY DELAY ; save repeat delay count
BNE LAB_EBBA ; go save key to buffer and exit, branch always
LAB_EB84
AND #$7F ; clear b7
BIT RPTFLG ; test key repeat
BMI LAB_EBA1 ; branch if repeat all
BVS LAB_EBD6 ; branch if repeat none
CMP #$7F ; compare with end marker
LAB_EB8F
BEQ LAB_EBBA ; if $00/end marker go save key to buffer and exit
CMP #$14 ; compare with [INSERT]/[DELETE]
BEQ LAB_EBA1 ; if [INSERT]/[DELETE] go test for repeat
CMP #' ' ; compare with [SPACE]
BEQ LAB_EBA1 ; if [SPACE] go test for repeat
CMP #$1D ; compare with [CURSOR RIGHT]/[CURSOR LEFT]
BEQ LAB_EBA1 ; if [CURSOR RIGHT]/[CURSOR LEFT] go test for repeat
CMP #$11 ; compare with [CURSOR DOWN]/[CURSOR UP]
BNE LAB_EBD6 ; if not [CURSOR DOWN]/[CURSOR UP] just exit
; was one of the cursor movement keys, insert/delete
; key or the space bar so always do repeat tests
LAB_EBA1
LDY DELAY ; get repeat delay counter
BEQ LAB_EBAB ; branch if delay expired
DEC DELAY ; else decrement repeat delay counter
BNE LAB_EBD6 ; branch if delay not expired
; repeat delay counter has expired
LAB_EBAB
DEC KOUNT ; decrement repeat speed counter
BNE LAB_EBD6 ; branch if repeat speed count not expired
LDY #$04 ; set for 4/60ths of a second
STY KOUNT ; set repeat speed counter
LDY NDX ; get keyboard buffer index
DEY ; decrement it
BPL LAB_EBD6 ; if the buffer isn't empty just exit
; else repeat the key immediately
; Possibly save the key to the keyboard buffer. If there was no key pressed or the key
; was not found during the scan (possibly due to key bounce) then .X will be $FF here.
LAB_EBBA
LDY SFDX ; get which key
STY LSTX ; save as last key pressed
LDY SHFLAG ; get keyboard shift/control/C= flag
STY LSTSHF ; save as last keyboard shift pattern
CPX #$FF ; compare character with table end marker or no key
BEQ LAB_EBD6 ; if table end marker or no key just exit
TXA ; copy character to .A
LDX NDX ; get keyboard buffer index
CPX XMAX ; compare with keyboard buffer size
BCS LAB_EBD6 ; if buffer full just exit
STA KEYD,X ; save character to keyboard buffer
INX ; increment index
STX NDX ; save keyboard buffer index
LAB_EBD6
LDA #$F7 ; enable column 3 for stop key
STA VIA2PB ; set VIA 2 DRB, keyboard column
RTS
; evaluate SHIFT/CTRL/C= keys
;
; 0 $00 EC5E
; 1 $02 EC9F
; 2 $04 ECE0
; 3 .. ....
; 4 $06 EDA3
; 5 $06 EDA3
; 6 $06 EDA3
; 7 $06 EDA3
SETKEYS
LDA SHFLAG ; get keyboard shift/control/C= flag
CMP #$03 ; compare with [SHIFT][C=]
BNE LAB_EC0F ; branch if not [SHIFT][C=]
CMP LSTSHF ; compare with last
BEQ LAB_EBD6 ; exit if still the same
LDA MODE ; get shift mode switch $00 = enabled, $80 = locked
BMI LAB_EC43 ; if locked continue keyboard decode
NOP ; just a few wasted cycles
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
; toggle text mode
LDA VICCR5 ; get start of character memory, ROM
EOR #$02 ; toggle $8000,$8800
STA VICCR5 ; set start of character memory, ROM
NOP ;
NOP ;
NOP ;
NOP ;
JMP LAB_EC43 ; continue keyboard decode
; was not [SHIFT][C=] but could be any other combination
LAB_EC0F
ASL ; << 1
CMP #$08 ; compare with [CTRL]
BCC LAB_EC18 ; branch if not [CTRL] pressed
LDA #$06 ; else [CTRL] was pressed so make index = $06
NOP ;
NOP ;
LAB_EC18
NOP ; just a few wasted cycles
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
NOP ;
TAX ; copy index to .X
LDA KEYVCTRS,X ; get decode table pointer low byte
STA KEYTAB ; save decode table pointer low byte
LDA KEYVCTRS+1,X ; get decode table pointer high byte
STA KEYTAB+1 ; save decode table pointer high byte
LAB_EC43
JMP LAB_EB74 ; continue keyboard decode
;***********************************************************************************;
;
; keyboard decode table pointers
KEYVCTRS
.word NORMKEYS ; unshifted
.word SHFTKEYS ; shifted
.word LOGOKEYS ; commodore
.word CTRLKEYS ; control
.word NORMKEYS ; unshifted
.word SHFTKEYS ; shifted
.word WHATKEYS ; shifted
.word CTRLKEYS ; control
.word CHARSET ; graphics/text control
.word WHATKEYS ; shifted
.word WHATKEYS ; shifted
.word CTRLKEYS ; control
; keyboard decode table - unshifted
NORMKEYS
.byte $31,$33,$35,$37,$39,$2B,$5C,$14
.byte $5F,$57,$52,$59,$49,$50,$2A,$0D
.byte $04,$41,$44,$47,$4A,$4C,$3B,$1D
.byte $03,$01,$58,$56,$4E,$2C,$2F,$11
.byte $20,$5A,$43,$42,$4D,$2E,$01,$85
.byte $02,$53,$46,$48,$4B,$3A,$3D,$86
.byte $51,$45,$54,$55,$4F,$40,$5E,$87
.byte $32,$34,$36,$38,$30,$2D,$13,$88
.byte $FF
; keyboard decode table - shifted
SHFTKEYS
.byte $21,$23,$25,$27,$29,$DB,$A9,$94
.byte $5F,$D7,$D2,$D9,$C9,$D0,$C0,$8D
.byte $04,$C1,$C4,$C7,$CA,$CC,$5D,$9D
.byte $83,$01,$D8,$D6,$CE,$3C,$3F,$91
.byte $A0,$DA,$C3,$C2,$CD,$3E,$01,$89
.byte $02,$D3,$C6,$C8,$CB,$5B,$3D,$8A
.byte $D1,$C5,$D4,$D5,$CF,$BA,$DE,$8B
.byte $22,$24,$26,$28,$30,$DD,$93,$8C
.byte $FF
; keyboard decode table - commodore
LOGOKEYS
.byte $21,$23,$25,$27,$29,$A6,$A8,$94
.byte $5F,$B3,$B2,$B7,$A2,$AF,$DF,$8D
.byte $04,$B0,$AC,$A5,$B5,$B6,$5D,$9D
.byte $83,$01,$BD,$BE,$AA,$3C,$3F,$91
.byte $A0,$AD,$BC,$BF,$A7,$3E,$01,$89
.byte $02,$AE,$BB,$B4,$A1,$5B,$3D,$8A
.byte $AB,$B1,$A3,$B8,$B9,$A4,$DE,$8B
.byte $22,$24,$26,$28,$30,$DC,$93,$8C
.byte $FF
;***********************************************************************************;
;
; select VIC character set
CHARSET
CMP #$0E ; compare with [SWITCH TO LOWER CASE]
BNE GRAPHMODE ; branch if not [SWITCH TO LOWER CASE]
LDA #$02 ; set for $8800, lower case characters
ORA VICCR5 ; OR with start of character memory, ROM
STA VICCR5 ; save start of character memory, ROM
JMP LAB_E6DC ; restore registers, set quote flag and exit
GRAPHMODE
CMP #$8E ; compare with [SWITCH TO UPPER CASE]
BNE LAB_ED3F ; branch if not [SWITCH TO UPPER CASE]
LDA #$FD ; set for $8000, upper case characters
AND VICCR5 ; AND with start of character memory, ROM
STA VICCR5 ; save start of character memory, ROM
LAB_ED3C
JMP LAB_E6DC ; restore registers, set quote flag and exit
LAB_ED3F
CMP #$08 ; compare with disable [SHIFT][C=]
BNE LAB_ED4D ; branch if not disable [SHIFT][C=]
LDA #$80 ; set to lock shift mode switch
ORA MODE ; OR with shift mode switch, $00 = enabled, $80 = locked
STA MODE ; save shift mode switch
BMI LAB_ED3C ; branch always
LAB_ED4D
CMP #$09 ; compare with enable [SHIFT][C=]
BNE LAB_ED3C ; exit if not enable [SHIFT][C=]
LDA #$7F ; set to unlock shift mode switch
AND MODE ; AND with shift mode switch, $00 = enabled, $80 = locked
STA MODE ; save shift mode switch
BPL LAB_ED3C ; branch always
; make next screen line start of logical line, increment line length and set pointers
WRAPLINE
INX ; increment screen row
LDA LDTB1,X ; get start of line X pointer high byte
ORA #$80 ; mark as start of logical line
STA LDTB1,X ; set start of line X pointer high byte
DEX ; restore screen row
LDA LNMX ; get current screen line length
CLC ; clear carry for add
JMP LAB_E715 ; add one line length, set pointers for start of line and
; return
;***********************************************************************************;
;
; keyboard decode table - shifted
WHATKEYS
.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
.byte $FF,$04,$FF,$FF,$FF,$FF,$FF,$E2
.byte $9D,$83,$01,$FF,$FF,$FF,$FF,$FF
.byte $91,$A0,$FF,$FF,$FF,$FF,$EE,$01
.byte $89,$02,$FF,$FF,$FF,$FF,$E1,$FD
.byte $8A,$FF,$FF,$FF,$FF,$FF,$B0,$E0
.byte $8B,$F2,$F4,$F6,$FF,$F0,$ED,$93
.byte $8C,$FF
; keyboard decode table - control
CTRLKEYS
.byte $90,$1C,$9C,$1F,$12,$FF,$FF,$FF
.byte $06,$FF,$12,$FF,$FF,$FF,$FF,$FF
.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
.byte $05,$9F,$1E,$9E,$92,$FF,$FF,$FF
.byte $FF
;***********************************************************************************;
;
; initial values for VIC registers
VICINIT
.byte $0C ; interlace and horizontal origin [PAL]
; .byte $05 ; interlace and horizontal origin [NTSC]
; bit function
; --- --------
; 7 interlace / non interlace
; 6-0 horizontal origin
.byte $26 ; vertical origin [PAL]
; .byte $19 ; vertical origin [NTSC]
.byte $16 ; video address and columns, $9400 for colour RAM
; bit function
; --- --------
; 7 video memory address va9
; 6-0 number of columns
.byte $2E ; screen rows and character height
; bit function
; --- --------
; 7 raster line b0
; 6-1 number of rows
; 0 character height (8/16 bits)
.byte $00 ; b8-b1 raster line
.byte $C0 ; video memory addresses, RAM $1000, ROM $8000
; bit function
; --- --------
; 7-4 video memory address va13-va10
; 3-0 character memory address va13-va10
; 0000 ROM $8000 set 1 - we use this
; 0001 " $8400
; 0010 " $8800 set 2
; 0011 " $8C00
; 1100 RAM $1000
; 1101 " $1400
; 1110 " $1800
; 1111 " $1C00
.byte $00 ; light pen horizontal position
.byte $00 ; light pen vertical position
.byte $00 ; paddle X
.byte $00 ; paddle Y
.byte $00 ; oscillator 1 frequency
.byte $00 ; oscillator 2 frequency
.byte $00 ; oscillator 3 frequency
.byte $00 ; white noise frequency
.byte $00 ; auxiliary colour and volume
; bit function
; --- --------
; 7-4 auxiliary colour
; 3-0 volume
.byte $1B ; background and border colour
; bit function
; --- --------
; 7-4 background colour
; 3 reverse video
; 2-0 border colour
;***********************************************************************************;
;
; keyboard buffer for auto load/run
RUNTB
.byte "LOAD",$0D,"RUN",$0D
;***********************************************************************************;
;
; low byte screen line addresses
LDTB2
.byte $00,$16,$2C,$42
.byte $58,$6E,$84,$9A
.byte $B0,$C6,$DC,$F2
.byte $08,$1E,$34,$4A
.byte $60,$76,$8C,$A2
.byte $B8,$CE,$E4
;***********************************************************************************;
;
; command a serial bus device to TALK
; To use this routine the accumulator must first be loaded with a device number
; between 4 and 30. When called this routine converts this device number to a talk
; address. Then this data is transmitted as a command on the Serial bus.
FTALK
ORA #$40 ; OR with the TALK command
.byte $2C ; makes next line BIT $2009
;***********************************************************************************;
;
; command devices on the serial bus to LISTEN
; This routine will command a device on the serial bus to receive data. The
; accumulator must be loaded with a device number between 4 and 30 before calling
; this routine. LISTEN convert this to a listen address then transmit this data as
; a command on the serial bus. The specified device will then go into listen mode
; and be ready to accept information.
FLISTEN
ORA #$20 ; OR with the LISTEN command
JSR RSPAUSE ; check RS-232 bus idle
;***********************************************************************************;
;
; send control character
LIST1
PHA ; save device address
BIT C3PO ; test deferred character flag
BPL LAB_EE2B ; branch if no deferred character
SEC ; flag EOI
ROR PCNTR ; rotate into EOI flag
JSR SRSEND ; Tx byte on serial bus
LSR C3PO ; clear deferred character flag
LSR PCNTR ; clear EOI flag
LAB_EE2B
PLA ; restore device address
STA BSOUR ; save as serial deferred character
JSR SEROUT1 ; set serial data out high
CMP #$3F ; compare read byte with $3F
BNE LAB_EE38 ; branch if not $3F, this branch will always be taken as
; after VIA 2's PCR is read it is ANDed with $DF, so the
; result can never be $3F
JSR SRCLKHI ; set serial clock high
LAB_EE38
LDA VIA1PA2 ; get VIA 1 DRA, no handshake
ORA #$80 ; set serial ATN low
STA VIA1PA2 ; set VIA 1 DRA, no handshake
;***********************************************************************************;
;
; if the code drops through to here the serial clock is low and the serial data has been
; released so the following code will have no effect apart from delaying the first byte
; by 1ms
; set clk/data, wait and Tx byte on serial bus
LAB_EE40
JSR SRCLKLO ; set serial clock low
JSR SEROUT1 ; set serial data out high
JSR WAITABIT ; 1ms delay
;***********************************************************************************;
;
; Tx byte on serial bus
SRSEND
SEI ; disable interrupts
JSR SEROUT1 ; set serial data out high
JSR SERGET ; get serial clock status
LSR ; shift serial data to Cb
BCS SRBAD ; if data high do device not present
JSR SRCLKHI ; set serial clock high
BIT PCNTR ; test EOI flag
BPL LAB_EE66 ; branch if not EOI
; I think this is the EOI sequence so the serial clock has been released and the serial
; data is being held low by the peripherals. First up wait for the serial data to rise.
LAB_EE5A
JSR SERGET ; get serial clock status
LSR ; shift serial data to Cb
BCC LAB_EE5A ; loop if data low
; Now the data is high, EOI is signalled by waiting for at least 200us without pulling
; the serial clock line low again. The listener should respond by pulling the serial
; data line low.
LAB_EE60
JSR SERGET ; get serial clock status
LSR ; shift serial data to Cb
BCS LAB_EE60 ; loop if data high
; The serial data has gone low ending the EOI sequence, now just wait for the serial
; data line to go high again or, if this isn't an EOI sequence, just wait for the serial
; data to go high the first time.
LAB_EE66
JSR SERGET ; get serial clock status
LSR ; shift serial data to Cb
BCC LAB_EE66 ; loop if data low
; serial data is high now pull the clock low, preferably within 60us
JSR SRCLKLO ; set serial clock low
; Now the VIC has to send the eight bits, LSB first. First it sets the serial data line
; to reflect the bit in the byte, then it sets the serial clock to high. The serial
; clock is left high for 26 cycles, 23us on a PAL VIC, before it is again pulled low
; and the serial data is allowed high again.
LDA #$08 ; eight bits to do
STA CNTDN ; set serial bus bit count
LAB_EE73
LDA VIA1PA2 ; get VIA 1 DRA, no handshake
CMP VIA1PA2 ; compare with self
BNE LAB_EE73 ; loop if changing
LSR ; serial clock to carry
LSR ; serial data to carry
BCC LAB_EEB7 ; if data low do timeout on serial bus
ROR BSOUR ; rotate transmit byte
BCS LAB_EE88 ; branch if bit = 1
JSR SEROUT0 ; else set serial data out low
BNE LAB_EE8B ; branch always
LAB_EE88
JSR SEROUT1 ; set serial data out high
LAB_EE8B
JSR SRCLKHI ; set serial clock high
NOP ; waste ..
NOP ; .. a ..
NOP ; .. cycle ..
NOP ; .. or two
LDA VIA2PCR ; get VIA 2 PCR
AND #$DF ; set CB2 low, serial data out high
ORA #$02 ; set CA2 high, serial clock out low
STA VIA2PCR ; save VIA 2 PCR
DEC CNTDN ; decrement serial bus bit count
BNE LAB_EE73 ; loop if not all done
; Now all eight bits have been sent it's up to the peripheral to signal the byte was
; received by pulling the serial data low. This should be done within one millisecond.
LDA #$04 ; wait for up to about 1ms
STA VIA2T2CH ; set VIA 2 T2C_h
LAB_EEA5
LDA VIA2IFR ; get VIA 2 IFR
AND #$20 ; mask T2 interrupt
BNE LAB_EEB7 ; if T2 interrupt do timeout on serial bus
JSR SERGET ; get serial clock status
LSR ; shift serial data to Cb
BCS LAB_EEA5 ; if data high go wait some more
CLI ; enable interrupts
RTS
;***********************************************************************************;
;
; device not present
SRBAD
LDA #$80 ; set device not present bit
.byte $2C ; makes next line BIT $03A9
;***********************************************************************************;
;
; timeout on serial bus
LAB_EEB7
LDA #$03 ; set time out read and write bits
LAB_EEB9
JSR ORIOST ; OR into I/O status byte
CLI ; enable interrupts
CLC ; clear for branch
BCC LAB_EF09 ; ATN high, delay, clock high then data high, branch always
;***********************************************************************************;
;
; send secondary address after LISTEN
; This routine is used to send a secondary address to an I/O device after a call to
; the LISTEN routine is made and the device commanded to LISTEN. The routine cannot
; be used to send a secondary address after a call to the TALK routine.
; A secondary address is usually used to give set-up information to a device before
; I/O operations begin.
; When a secondary address is to be sent to a device on the serial bus the address
; must first be ORed with $60.
FSECOND
STA BSOUR ; save deferred byte
JSR LAB_EE40 ; set clk/data, wait and Tx byte on serial bus
; set serial ATN high
SCATN
LDA VIA1PA2 ; get VIA 1 DRA, no handshake
AND #$7F ; set serial ATN high
STA VIA1PA2 ; set VIA 1 DRA, no handshake
RTS
;***********************************************************************************;
;
; send secondary address after TALK
; This routine transmits a secondary address on the serial bus for a TALK device.
; This routine must be called with a number between 4 and 30 in the accumulator.
; The routine will send this number as a secondary address command over the serial
; bus. This routine can only be called after a call to the TALK routine. It will
; not work after a LISTEN.
; A secondary address is usually used to give set-up information to a device before
; I/O operations begin.
; When a secondary address is to be sent to a device on the serial bus the address
; must first be ORed with $60.
FTKSA
STA BSOUR ; save the secondary address byte to transmit
JSR LAB_EE40 ; set clk/data, wait and Tx byte on serial bus
;***********************************************************************************;
;
; wait for bus end after send
LAB_EED3
SEI ; disable interrupts
JSR SEROUT0 ; set serial data out low
JSR SCATN ; set serial ATN high
JSR SRCLKHI ; set serial clock high
LAB_EEDD
JSR SERGET ; get serial clock status
BCS LAB_EEDD ; branch if clock high
CLI ; enable interrupts
RTS
;***********************************************************************************;
;
; output a byte to the serial bus
; This routine is used to send information to devices on the serial bus. A call to
; this routine will put a data byte onto the serial bus using full handshaking.
; Before this routine is called the LISTEN routine must be used to command a device
; on the serial bus to get ready to receive data.
; The accumulator is loaded with a byte to output as data on the serial bus. A
; device must be listening or the status word will return a timeout. This routine
; always buffers one character. So when a call to the UNLSN routine is made to end
; the data transmission, the buffered character is sent with EOI set. Then the
; UNLISTEN command is sent to the device.
FCIOUT
BIT C3PO ; test deferred character flag
BMI LAB_EEED ; branch if deferred character
SEC ; set carry
ROR C3PO ; shift into deferred character flag
BNE LAB_EEF2 ; save byte and exit, branch always
LAB_EEED
PHA ; save byte
JSR SRSEND ; Tx byte on serial bus
PLA ; restore byte
LAB_EEF2
STA BSOUR ; save deferred byte
CLC ; flag ok
RTS
;***********************************************************************************;
;
; command the serial bus to UNTALK
; This routine will transmit an UNTALK command on the serial bus. All devices
; previously set to TALK will stop sending data when this command is received.
FUNTLK
JSR SRCLKLO ; set serial clock low
LDA VIA1PA2 ; get VIA 1 DRA, no handshake
ORA #$80 ; set serial ATN low
STA VIA1PA2 ; set VIA 1 DRA, no handshake
LDA #$5F ; set the UNTALK command
.byte $2C ; makes next line BIT $3FA9
;***********************************************************************************;
;
; command the serial bus to UNLISTEN
; This routine commands all devices on the serial bus to stop receiving data from
; the computer. Calling this routine results in an UNLISTEN command being transmitted
; on the serial bus. Only devices previously commanded to listen will be affected.
; This routine is normally used after the computer is finished sending data to
; external devices. Sending the UNLISTEN will command the listening devices to get
; off the serial bus so it can be used for other purposes.
FUNLSN
LDA #$3F ; set the UNLISTEN command
JSR LIST1 ; send control character
; ATN high, delay, clock high then data high
LAB_EF09
JSR SCATN ; set serial ATN high
; 1ms delay, clock high then data high
LAB_EF0C
TXA ; save device number
LDX #$0B ; short delay
LAB_EF0F
DEX ; decrement count
BNE LAB_EF0F ; loop if not all done
TAX ; restore device number
JSR SRCLKHI ; set serial clock high
JMP SEROUT1 ; set serial data out high and return
;***********************************************************************************;
;
; input a byte from the serial bus
; This routine reads a byte of data from the serial bus using full handshaking. The
; data is returned in the accumulator. Before using this routine the TALK routine
; must have been called first to command the device on the serial bus to send data on
; the bus. If the input device needs a secondary command it must be sent by using the
; TKSA routine before calling this routine.
; Errors are returned in the status word which can be read by calling the READST
; routine.
FACPTR
SEI ; disable interrupts
LDA #$00 ; clear .A
STA CNTDN ; clear serial bus bit count
JSR SRCLKHI ; set serial clock high
LAB_EF21
JSR SERGET ; get serial clock status
BCC LAB_EF21 ; loop while clock low
JSR SEROUT1 ; set serial data out high
LAB_EF29
LDA #$01 ; set timeout count high byte
STA VIA2T2CH ; set VIA 2 T2C_h
LAB_EF2E
LDA VIA2IFR ; get VIA 2 IFR
AND #$20 ; mask T2 interrupt
BNE LAB_EF3C ; branch if T2 interrupt
JSR SERGET ; get serial clock status
BCS LAB_EF2E ; loop if clock high
BCC LAB_EF54 ; else go set 8 bits to do, branch always
; T2 timed out
LAB_EF3C
LDA CNTDN ; get serial bus bit count
BEQ LAB_EF45 ; if not already EOI then go flag EOI
LDA #$02 ; error $02, read timeout
JMP LAB_EEB9 ; set I/O status and exit
LAB_EF45
JSR SEROUT0 ; set serial data out low
JSR LAB_EF0C ; 1ms delay, clock high then data high
LDA #$40 ; set EOI bit
JSR ORIOST ; OR into I/O status byte
INC CNTDN ; increment serial bus bit count, do error on next timeout
BNE LAB_EF29 ; go try again
LAB_EF54
LDA #$08 ; 8 bits to do
STA CNTDN ; set serial bus bit count
LAB_EF58
LDA VIA1PA2 ; get VIA 1 DRA, no handshake
CMP VIA1PA2 ; compare with self
BNE LAB_EF58 ; loop if changing
LSR ; serial clock into carry
BCC LAB_EF58 ; loop while serial clock low
LSR ; serial data into carry
ROR FIRT ; shift data bit into input byte
LAB_EF66
LDA VIA1PA2 ; get VIA 1 DRA, no handshake
CMP VIA1PA2 ; compare with self
BNE LAB_EF66 ; loop if changing
LSR ; serial clock into carry
BCS LAB_EF66 ; loop while serial clock high
DEC CNTDN ; decrement serial bus bit count
BNE LAB_EF58 ; loop if not all done
JSR SEROUT0 ; set serial data out low
LDA STATUS ; get I/O status byte
BEQ LAB_EF7F ; branch if no error
JSR LAB_EF0C ; 1ms delay, clock high then data high
LAB_EF7F
LDA FIRT ; get input byte
CLI ; enable interrupts
CLC
RTS
;***********************************************************************************;
;
; set serial clock high
SRCLKHI
LDA VIA2PCR ; get VIA 2 PCR
AND #$FD ; set CA2 low, serial clock out high
STA VIA2PCR ; set VIA 2 PCR
RTS
;***********************************************************************************;
;
; set serial clock low
SRCLKLO
LDA VIA2PCR ; get VIA 2 PCR
ORA #$02 ; set CA2 high, serial clock out low
STA VIA2PCR ; set VIA 2 PCR
RTS
;***********************************************************************************;
;
; 1ms delay
WAITABIT
LDA #$04 ; set for 1024 cycles
STA VIA2T2CH ; set VIA 2 T2C_h
LAB_EF9B
LDA VIA2IFR ; get VIA 2 IFR
AND #$20 ; mask T2 interrupt
BEQ LAB_EF9B ; loop until T2 interrupt
RTS
;***********************************************************************************;
;
; RS-232 Tx NMI routine
RSNXTBIT
LDA BITTS ; get RS-232 bit count
BEQ RSNXTBYT ; if zero go setup next RS-232 Tx byte and return
BMI RSSTOPS ; if -ve go do stop bit(s)
; else bit count is non zero and +ve
LSR RODATA ; shift RS-232 output byte buffer
LDX #$00 ; set $00 for bit = 0
BCC LAB_EFB0 ; branch if bit was 0
DEX ; set $FF for bit = 1
LAB_EFB0
TXA ; copy bit to .A
EOR ROPRTY ; XOR with RS-232 parity byte
STA ROPRTY ; save RS-232 parity byte
DEC BITTS ; decrement RS-232 bit count
BEQ RSPRTY ; if RS-232 bit count now zero go do parity bit
; save bit and exit
LAB_EFB9
TXA ; copy bit to .A
AND #$20 ; mask for CB2 control bit
STA NXTBIT ; save RS-232 next bit to send
RTS
; do RS-232 parity bit, enters with RS-232 bit count = 0
RSPRTY
LDA #$20 ; mask 00x0 0000, parity enable bit
BIT M51CDR ; test pseudo 6551 command register
BEQ LAB_EFDA ; branch if parity disabled
BMI LAB_EFE4 ; branch if fixed mark or space parity
BVS LAB_EFDE ; branch if even parity
; else odd parity
LDA ROPRTY ; get RS-232 parity byte
BNE LAB_EFCF ; if parity not zero leave parity bit = 0
LAB_EFCE
DEX ; make parity bit = 1
LAB_EFCF
DEC BITTS ; decrement RS-232 bit count, 1 stop bit
LDA M51CTR ; get pseudo 6551 control register
BPL LAB_EFB9 ; if 1 stop bit save parity bit and exit
; else two stop bits ..
DEC BITTS ; decrement RS-232 bit count, 2 stop bits
BNE LAB_EFB9 ; save bit and exit, branch always
; parity is disabled so the parity bit becomes the first,
; and possibly only, stop bit. to do this increment the bit
; count which effectively decrements the stop bit count.
LAB_EFDA
INC BITTS ; increment RS-232 bit count, = -1 stop bit
BNE LAB_EFCE ; set stop bit = 1 and exit, branch always
; do even parity
LAB_EFDE
LDA ROPRTY ; get RS-232 parity byte
BEQ LAB_EFCF ; if parity zero leave parity bit = 0
BNE LAB_EFCE ; else make parity bit = 1, branch always
; fixed mark or space parity
LAB_EFE4
BVS LAB_EFCF ; if fixed space parity leave parity bit = 0
BVC LAB_EFCE ; else fixed mark parity make parity bit = 1, branch always
; decrement stop bit count, set stop bit = 1 and exit. $FF is one stop bit, $FE is two
; stop bits
RSSTOPS
INC BITTS ; decrement RS-232 bit count
LDX #$FF ; set stop bit = 1
BNE LAB_EFB9 ; save stop bit and exit, branch always
; setup next RS-232 Tx byte
RSNXTBYT
LDA M51CDR ; get pseudo 6551 command register
LSR ; handshake bit into Cb
BCC LAB_EFFB ; branch if 3 line interface
BIT VIA2PB ; test VIA 2 DRB
; The above code is wrong, the address should be VIA1PB which is VIA 1 which is where the
; DSR and CTS inputs really are, the code should read ..
;
; BIT VIA1PB ; test VIA 1 DRB
BPL RSMISSNG ; if DSR = 0 set DSR signal not present and exit
BVC LAB_F019 ; if CTS = 0 set CTS signal not present and exit
; was 3 line interface
LAB_EFFB
LDA #$00 ; clear .A
STA ROPRTY ; clear RS-232 parity byte
STA NXTBIT ; clear RS-232 next bit to send
LDX BITNUM ; get number of bits to be sent/received
STX BITTS ; set RS-232 bit count
LDY RODBS ; get index to Tx buffer start
CPY RODBE ; compare with index to Tx buffer end
BEQ LAB_F021 ; if all done go disable T1 interrupt and return
LDA (ROBUF),Y ; else get byte from buffer
STA RODATA ; save to RS-232 output byte buffer
INC RODBS ; increment index to Tx buffer start
RTS
;***********************************************************************************;
;
; set DSR signal not present
RSMISSNG
LDA #$40 ; set DSR signal not present
.byte $2C ; makes next line BIT $10A9
; set CTS signal not present
LAB_F019
LDA #$10 ; set CTS signal not present
ORA RSSTAT ; OR with RS-232 status register
STA RSSTAT ; save RS-232 status register
; disable T1 interrupt
LAB_F021
LDA #$40 ; disable T1 interrupt
STA VIA1IER ; set VIA 1 IER
RTS
;***********************************************************************************;
;
; compute bit count
RSCPTBIT
LDX #$09 ; set bit count to 9, 8 data + 1 stop bit
LDA #$20 ; mask for 8/7 data bits
BIT M51CTR ; test pseudo 6551 control register
BEQ LAB_F031 ; branch if 8 bits
DEX ; else decrement count for 7 data bits
LAB_F031
BVC LAB_F035 ; branch if 7 bits
DEX ; else decrement count ..
DEX ; .. for 5 data bits
LAB_F035
RTS
;***********************************************************************************;
;
; RS-232 Rx NMI
RSINBIT
LDX RINONE ; get RS-232 start bit check flag
BNE RSSTRBIT ; branch if no start bit received
DEC BITCI ; decrement RS-232 input bit count
BEQ RSINBYTE ; if the byte is complete go add it to the buffer
BMI LAB_F04D ; determine if all stop bits have been received
LDA INBIT ; get RS-232 input bit
EOR RIPRTY ; XOR with RS-232 parity bit
STA RIPRTY ; save in RS-232 parity bit
LSR INBIT ; shift RS-232 input bit into Cb
ROR RIDATA ; shift Cb into RS-232 byte assembly
LAB_F04A
RTS
;***********************************************************************************;
;
; determine if all stop bits have been received
RSSTPBIT
DEC BITCI ; decrement RS-232 input bit count
LAB_F04D
LDA INBIT ; get RS-232 input bit
BEQ LAB_F0B3 ; branch if bit clear
LDA M51CTR ; get pseudo 6551 control register
ASL ; shift stop bits into Cb
LDA #$01 ; one stop bit
ADC BITCI ; add stop bits to RS-232 input bit count
BNE LAB_F04A ; branch always
;***********************************************************************************;
;
; prepare to receive next input byte
RSPREPIN
LDA #$90 ; enable CB1 interrupt, Rx data bit transition
STA VIA1IER ; set VIA 1 IER
STA RINONE ; set RS-232 start bit check flag, no start bit received
LDA #$20 ; disable T2 interrupt
STA VIA1IER ; set VIA 1 IER
RTS
;***********************************************************************************;
;
; no RS-232 start bit received
RSSTRBIT
LDA INBIT ; get RS-232 input bit
BNE RSPREPIN ; branch if bit set
STA RINONE ; set RS-232 start bit check flag, start bit received
RTS
;***********************************************************************************;
;
; put byte into receive buffer
RSINBYTE
LDY RIDBE ; get index to Rx buffer end
INY ; increment index
CPY RIDBS ; compare with index to Rx buffer start
BEQ RSOVERUN ; if buffer full go do Rx overrun error
STY RIDBE ; save index to Rx buffer end
DEY ; decrement index
LDA RIDATA ; get assembled byte
LDX BITNUM ; get bit count
LAB_F081
CPX #$09 ; compare with byte + stop
BEQ LAB_F089 ; branch if all nine bits received
LSR ; else shift byte
INX ; increment bit count
BNE LAB_F081 ; loop, branch always
LAB_F089
STA (RIBUF),Y ; save received byte to RS-232 Rx buffer
LDA #$20 ; mask 00x0 0000, parity enable bit
BIT M51CDR ; test pseudo 6551 command register
BEQ RSSTPBIT ; branch if parity disabled
BMI LAB_F04A ; exit if mark or space parity
LDA INBIT ; get RS-232 input bit
EOR RIPRTY ; XOR with RS-232 parity bit
BEQ RSPRTYER ; branch if parity error
BVS LAB_F04A ; exit if even parity
.byte $2C ; makes next line BIT $AB50
RSPRTYER
BVC LAB_F04A ; exit if odd parity
LDA #$01 ; set Rx parity error
.byte $2C ; makes next line BIT $04A9
RSOVERUN
LDA #$04 ; set Rx overrun error
.byte $2C ; makes next line BIT $80A9
RSBREAK
LDA #$80 ; Rx break error
.byte $2C ; makes next line BIT $02A9
RSFRAMER
LDA #$02 ; Rx frame error
ORA RSSTAT ; OR with RS-232 status byte
STA RSSTAT ; save RS-232 status byte
JMP RSPREPIN ; prepare to receive next input byte
LAB_F0B3
LDA RIDATA ; get assembled byte
BNE RSFRAMER ; if not break do frame error
BEQ RSBREAK ; else do break error, branch always
;***********************************************************************************;
;
; do illegal device number
RSDVCERR
JMP FE_ILDEV ; do illegal device number and return
;***********************************************************************************;
;
; open RS-232 channel for output
RSOPNOUT
STA DFLTO ; save output device number
LDA M51CDR ; get pseudo 6551 command register
LSR ; shift handshake bit to carry
BCC LAB_F0EB ; branch if 3 line interface
LDA #$02 ; mask for RTS out
BIT VIA1PB ; test VIA 1 DRB
BPL LAB_F0E8 ; if DSR = 0 set DSR not present and exit
BNE LAB_F0EB ; if RTS = 1 just exit
; RTS is low because a half-duplex input channel
; was opened, wait for current receive to finish
LAB_F0CD
LDA VIA1IER ; get VIA 1 IER
AND #$30 ; mask 00xx 0000, T2 and CB1 interrupts
BNE LAB_F0CD ; loop while either enabled
LAB_F0D4
BIT VIA1PB ; test VIA 1 DRB
BVS LAB_F0D4 ; loop while CTS high
LDA VIA1PB ; get VIA 1 DRB
ORA #$02 ; set RTS high
STA VIA1PB ; save VIA 1 DRB
LAB_F0E1
BIT VIA1PB ; test VIA 1 DRB
BVS LAB_F0EB ; exit if CTS high
BMI LAB_F0E1 ; loop while DSR high
LAB_F0E8
JSR RSMISSNG ; set DSR signal not present
LAB_F0EB
CLC ; flag ok
RTS
;***********************************************************************************;
;
; send byte to RS-232 buffer
RSOUTSAV
LDY RODBE ; get index to Tx buffer end
INY ; + 1
CPY RODBS ; compare with index to Tx buffer start
BEQ RSOUTSAV ; loop while buffer full
STY RODBE ; set index to Tx buffer end
DEY ; index to available buffer byte
STA (ROBUF),Y ; save byte to buffer
BIT VIA1IER ; test VIA 1 IER
BVC RSPREPOT ; branch if T1 not enabled
RTS
RSPREPOT
LDA BAUDOF ; get baud rate bit time low byte
STA VIA1T1CL ; set VIA 1 T1C_l
LDA BAUDOF+1 ; get baud rate bit time high byte
STA VIA1T1CH ; set VIA 1 T1C_h
LDA #$C0 ; enable T1 interrupt
STA VIA1IER ; set VIA 1 IER
JMP RSNXTBYT ; setup next RS-232 Tx byte and return
;***********************************************************************************;
;
; open RS-232 channel for input
RSOPNIN
STA DFLTN ; save input device number
LDA M51CDR ; get pseudo 6551 command register
LSR ; shift b0 into Cb
BCC LAB_F146 ; branch if 3 line interface
AND #$08 ; mask duplex bit, pseudo 6551 command is >> 1
BEQ LAB_F146 ; branch if full duplex
; half-duplex, X-line handshaking
LDA #$02 ; mask for RTS out
BIT VIA1PB ; test VIA 1 DRB
BPL LAB_F0E8 ; if DSR = 0 set DSR not present and exit
BEQ LAB_F144 ; if RTS = 0 just exit
; wait for current transmit to finish
LAB_F12B
BIT VIA1IER ; test VIA 1 IER
BVS LAB_F12B ; loop while T1 interrupt enabled
LDA VIA1PB ; get VIA 1 DRB
AND #$FD ; mask xxxx xx0x, clear RTS out
STA VIA1PB ; save VIA 1 DRB
LAB_F138
LDA VIA1PB ; get VIA 1 DRB
AND #$04 ; mask xxxx x1xx, DTR out
; DTR is an output and always held high so the
; following test should never branch
BEQ LAB_F138 ; loop while DTR low
LAB_F13F
LDA #$90 ; enable CB1 interrupt, Rx data bit transition
STA VIA1IER ; set VIA 1 IER
LAB_F144
CLC ; flag no error
RTS
LAB_F146
LDA VIA1IER ; get VIA 1 IER
AND #$30 ; mask 0xx0 0000, T1 and T2 interrupts
BEQ LAB_F13F ; if both interrupts disabled go enable CB1
; interrupt and exit
CLC ; flag no error
RTS
;***********************************************************************************;
;
; get byte from RS-232 buffer
RSNXTIN
LDY RIDBS ; get index to Rx buffer start
CPY RIDBE ; compare with index to Rx buffer end
BEQ LAB_F15D ; return null if buffer empty
LDA (RIBUF),Y ; get byte from RS-232 Rx buffer
INC RIDBS ; increment index to Rx buffer start
RTS
LAB_F15D
LDA #$00 ; return null
RTS
;***********************************************************************************;
;
; check RS-232 bus idle
RSPAUSE
PHA ; save .A
LDA VIA1IER ; get VIA 1 IER
BEQ LAB_F172 ; branch if no interrupts enabled. this branch will
; never be taken as b7 of IER always reads as 1
; according to the 6522 data sheet
LAB_F166
LDA VIA1IER ; get VIA 1 IER
AND #$60 ; mask 0xx0 0000, T1 and T2 interrupts
BNE LAB_F166 ; loop if T1 or T2 active
LDA #$10 ; disable CB1 interrupt, Rx data bit transition
STA VIA1IER ; set VIA 1 IER
LAB_F172
PLA ; restore .A
RTS
;***********************************************************************************;
;
; KERNAL I/O messages
KMSGTBL
KM_IOERR
.byte $0D,"I/O ERROR ",'#'+$80
KM_SRCHG
.byte $0D,"SEARCHING",' '+$80
KM_FOR
.byte "FOR",' '+$80
KM_PRPLY
.byte $0D,"PRESS PLAY ON TAP",'E'+$80
KM_RECPY
.byte "PRESS RECORD & PLAY ON TAP",'E'+$80
KM_LODNG
.byte $0D,"LOADIN",'G'+$80
KM_SAVNG
.byte $0D,"SAVING",' '+$80
KM_VFYNG
.byte $0D,"VERIFYIN",'G'+$80
KM_FOUND
.byte $0D,"FOUND",' '+$80
KM_OK
.byte $0D,"OK",$0D+$80
;***********************************************************************************;
;
; display control I/O message if in direct mode
SPMSG
BIT MSGFLG ; test KERNAL message mode flag
BPL LAB_F1F3 ; exit if control messages off
; display KERNAL I/O message
KMSGSHOW
LDA KMSGTBL,Y ; get byte from message table
PHP ; save status
AND #$7F ; clear b7
JSR CHROUT ; output character to channel
INY ; increment index
PLP ; restore status
BPL KMSGSHOW ; loop if not end of message
LAB_F1F3
CLC ; flag no error
RTS
;***********************************************************************************;
;
; get a character from the input device
; In practice this routine operates identically to the CHRIN routine for all devices
; except for the keyboard. If the keyboard is the current input device this routine
; will get one character from the keyboard buffer. It depends on the IRQ routine to
; read the keyboard and put characters into the buffer.
; If the keyboard buffer is empty the value returned in the accumulator will be zero.
FGETIN
LDA DFLTN ; get input device number
BNE LAB_F201 ; branch if not keyboard
; input device was keyboard
LDA NDX ; get keyboard buffer length
BEQ LAB_F26A ; if buffer empty go flag no byte and return
SEI ; disable interrupts
JMP LP2 ; input from keyboard buffer and return
; input device was not keyboard
LAB_F201
CMP #$02 ; compare device with RS-232 device
BNE LAB_F21D ; branch if not RS-232 device
; input device is RS-232 device
LAB_F205
STY XSAV ; save .Y
JSR RSNXTIN ; get byte from RS-232 buffer
LDY XSAV ; restore .Y
CLC ; flag no error
RTS
;***********************************************************************************;
;
; input character from channel
; This routine will get a byte of data from the channel already set up as the input
; channel by the CHKIN routine.
; If CHKIN has not been used to define another input channel the data is expected to be
; from the keyboard. the data byte is returned in the accumulator. The channel remains
; open after the call.
; Input from the keyboard is handled in a special way. First, the cursor is turned on
; and it will blink until a carriage return is typed on the keyboard. All characters
; on the logical line, up to 88 characters, will be stored in the BASIC input buffer.
; Then the characters can be returned one at a time by calling this routine once for
; each character. When the carriage return is returned the entire line has been
; processed. the next time this routine is called the whole process begins again.
FCHRIN
LDA DFLTN ; get input device number
BNE LAB_F21D ; if it's not the keyboard continue
; the input device is the keyboard
LDA PNTR ; get cursor column
STA LXSP+1 ; set input cursor column
LDA TBLX ; get cursor row
STA LXSP ; set input cursor row
JMP GETSCRN ; go get input from the keyboard
; the input device was not the keyboard
LAB_F21D
CMP #$03 ; compare device number with screen
BNE LAB_F22A ; if it's not the screen continue
; the input device is the screen
STA CRSW ; set input from screen
LDA LNMX ; get current screen line length
STA INDX ; save input EOL pointer
JMP GETSCRN ; go get input from the screen
; the input device was not the screen
LAB_F22A
BCS CHRINSR ; if input device is the serial bus go handle it
; the input device is < the screen so must be the RS-232 or tape device
CMP #$02 ; compare device with RS-232 device
BEQ CHRINRS ; if it's the RS-232 device go handle it
; else there's only the tape device left ..
STX XSAV ; save .X
JSR CHRINTP2 ; get byte from tape
BCS LAB_F24D ; exit if error
PHA ; save byte
JSR CHRINTP2 ; get next byte from tape
BCS LAB_F24A ; exit if error
BNE LAB_F244 ; branch if end not reached
LDA #$40 ; set EOF bit
JSR ORIOST ; OR into I/O status byte
LAB_F244
DEC BUFPNT ; back up tape buffer index
LDX XSAV ; restore .X
PLA ; restore saved byte
RTS
; error exit from input character
LAB_F24A
TAX ; copy error byte
PLA ; dump saved byte
TXA ; restore error byte
LAB_F24D
LDX XSAV ; restore .X
RTS
;***********************************************************************************;
;
; get byte from tape
CHRINTP2
JSR JTP20 ; bump tape pointer
BNE LAB_F260 ; if not end get next byte and exit
JSR RDTPBLKS ; initiate tape read
BCS LAB_F26B ; exit if error flagged
LDA #$00 ; clear .A
STA BUFPNT ; clear tape buffer index
BEQ CHRINTP2 ; loop, branch always
LAB_F260
LDA (TAPE1),Y ; get next byte from buffer
CLC ; flag no error
RTS
;***********************************************************************************;
;
; the input device was the serial bus
CHRINSR
LDA STATUS ; get I/O status byte
BEQ LAB_F26C ; if no errors flagged go input byte and return
LDA #$0D ; else return [EOL]
LAB_F26A
CLC ; flag no error
LAB_F26B
RTS
LAB_F26C
JMP FACPTR ; input a byte from the serial bus and return
;***********************************************************************************;
;
; input device was RS-232 device
CHRINRS
JSR LAB_F205 ; get byte from RS-232 device
BCS LAB_F279 ; branch if error, this doesn't get taken as the last
; instruction in the get byte from RS-232 device routine
; is CLC
CMP #$00 ; compare with null
BEQ CHRINRS ; loop if null
CLC ; flag no error
LAB_F279
RTS
;***********************************************************************************;
;
; output a character to channel
; This routine will output a character to an already opened channel. Use the OPEN
; routine, OPEN, and the CHKOUT routine, to set up the output channel before calling
; this routine. If these calls are omitted, data will be sent to the default output
; device, device 3, the screen. The data byte to be output is loaded into the accumulator,
; and this routine is called. The data is then sent to the specified output device.
; The channel is left open after the call.
; NOTE: Care must be taken when using routine to send data to a serial device since
; data will be sent to all open output channels on the bus. Unless this is desired,
; all open output channels on the serial bus other than the actually intended
; destination channel must be closed by a call to the KERNAL close channel routine.
FCHROUT
PHA ; save the character to send
LDA DFLTO ; get output device number
CMP #$03 ; compare device number with screen
BNE LAB_F285 ; if output device not screen continue
; the output device is the screen
PLA ; restore character to send
JMP SCRNOUT ; output character and return
; the output device was not the screen
LAB_F285
BCC LAB_F28B ; if output device < screen continue
; the output device was > screen so it is a serial bus device
PLA ; restore character to send
JMP FCIOUT ; output a byte to the serial bus and return
; the output device is < screen
LAB_F28B
CMP #$02 ; compare the device with RS-232 device
BEQ LAB_F2B9 ; if output device is RS-232 device go handle it
; else the output device is the cassette
PLA ; restore the character to send
;***********************************************************************************;
;
; output a character to the cassette
CHROUTTP
STA PTR1 ; save character to character buffer
PHA ; save .A
TXA ; copy .X
PHA ; save .X
TYA ; copy .Y
PHA ; save .Y
JSR JTP20 ; bump tape pointer
BNE LAB_F2AA ; if not end save next byte and exit
JSR WBLK ; initiate tape write
BCS LAB_F2AF ; exit if error
LDA #$02 ; set data block file type
LDY #$00 ; clear index
STA (TAPE1),Y ; save file type to tape buffer
INY ; increment index
STY BUFPNT ; save tape buffer index
LAB_F2AA
LDA PTR1 ; restore character from character buffer
STA (TAPE1),Y ; save to buffer
CLC ; flag no error
LAB_F2AF
PLA ; pull .Y
TAY ; restore .Y
PLA ; pull .X
TAX ; restore .X
PLA ; restore .A
BCC LAB_F2B8 ; exit if no error
LDA #$00 ; else clear .A
LAB_F2B8
RTS
;***********************************************************************************;
;
; the output device is RS-232 device
LAB_F2B9
PLA ; restore character to send
STX XSAV ; save .X
STY PTR1 ; save .Y
JSR RSOUTSAV ; send byte to RS-232 buffer
LDX XSAV ; restore .Y
LDY PTR1 ; restore .X
CLC ; flag ok
RTS
;***********************************************************************************;
;
; open a channel for input
; Any logical file that has already been opened by the OPEN routine can be defined as
; an input channel by this routine. The device on the channel must be an input device
; or an error will occur and the routine will abort.
; If you are getting data from anywhere other than the keyboard, this routine must be
; called before using either the CHRIN routine or the GETIN routine. If you are
; getting data from the keyboard and no other input channels are open then the calls
; to this routine and to the OPEN routine are not needed.
; When used with a device on the serial bus this routine will automatically send the
; listen address specified by the OPEN routine and any secondary address.
; Possible errors are:
;
; 3 : file not open
; 5 : device not present
; 6 : file is not an input file
FCHKIN
JSR FNDFLNO ; find file
BEQ LAB_F2CF ; branch if file opened
JMP FE_NTOPN ; do file not open error and return
LAB_F2CF
JSR SETFLCH ; set file details from table,.X
LDA FA ; get device number
BEQ LAB_F2EC ; if device was keyboard save device #, flag ok and exit
CMP #$03 ; compare device number with screen
BEQ LAB_F2EC ; if device was screen save device #, flag ok and exit
BCS LAB_F2F0 ; branch if serial bus device
CMP #$02 ; compare device with RS-232 device
BNE LAB_F2E3 ; branch if not RS-232 device
JMP RSOPNIN ; open RS-232 channel for input
LAB_F2E3
LDX SA ; get secondary address
CPX #$60 ; compare with read
BEQ LAB_F2EC ; branch if read
JMP FE_NTINP ; do not input file error and return
LAB_F2EC
STA DFLTN ; save input device number
CLC ; flag ok
RTS
; device was serial bus device
LAB_F2F0
TAX ; copy device number to .X
JSR FTALK ; command a serial bus device to TALK
LDA SA ; get secondary address
BPL LAB_F2FE ; branch if address to send
JSR LAB_EED3 ; wait for bus end after send
JMP LAB_F301 ; do I/O status test
LAB_F2FE
JSR FTKSA ; send secondary address after TALK
LAB_F301
TXA ; copy device back to .A
BIT STATUS ; test I/O status byte
BPL LAB_F2EC ; if device present save device number and exit
JMP FE_DVNTP ; do device not present error and return
;***********************************************************************************;
;
; open a channel for output
; Any logical file that has already been opened by the OPEN routine can be defined
; as an output channel by this routine the device on the channel must be an output
; output device or an error will occur and the routine will abort.
; If you are sending data to anywhere other than the screen this routine must be
; called before using the CHROUT routine. If you are sending data to the screen and
; no other output channels are open then the calls to this routine and to the OPEN
; routine are not needed.
; When used with a device on the serial bus this routine will automatically send the
; listen address specified by the OPEN routine and any secondary address.
; Possible errors are:
;
; 3 : file not open
; 5 : device not present
; 7 : file is not an output file
FCHKOUT
JSR FNDFLNO ; find file
BEQ LAB_F311 ; branch if file found
JMP FE_NTOPN ; do file not open error and return
LAB_F311
JSR SETFLCH ; set file details from table,.X
LDA FA ; get device number
BNE LAB_F31B ; branch if device is not keyboard
LAB_F318
JMP FE_NTOUT ; do not output file error and return
LAB_F31B
CMP #$03 ; compare device number with screen
BEQ LAB_F32E ; if screen save output device number and exit
BCS LAB_F332 ; branch if > screen, serial bus device
CMP #$02 ; compare device with RS-232 device
BNE LAB_F328 ; branch if not RS-232 device, must be tape
JMP RSOPNOUT ; open RS-232 channel for output
; open tape channel for output
LAB_F328
LDX SA ; get secondary address
CPX #$60 ; compare with read
BEQ LAB_F318 ; if read do not output file error and return
LAB_F32E
STA DFLTO ; save output device number
CLC ; flag ok
RTS
LAB_F332
TAX ; copy device number
JSR FLISTEN ; command devices on the serial bus to LISTEN
LDA SA ; get secondary address
BPL LAB_F33F ; branch if address to send
JSR SCATN ; else set serial ATN high
BNE LAB_F342 ; branch always
LAB_F33F
JSR FSECOND ; send secondary address after LISTEN
LAB_F342
TXA ; copy device number back to .A
BIT STATUS ; test I/O status byte
BPL LAB_F32E ; if device present save output device number and exit
JMP FE_DVNTP ; else do device not present error and return
;***********************************************************************************;
;
; close a specified logical file
; This routine is used to close a logical file after all I/O operations have been
; completed on that file. This routine is called after the accumulator is loaded
; with the logical file number to be closed, the same number used when the file was
; opened using the OPEN routine.
FCLOSE
JSR LAB_F3D4 ; find file .A
BEQ LAB_F351 ; if the file is found go close it
CLC ; else the file was closed so just flag ok
RTS
; found the file so close it
LAB_F351
JSR SETFLCH ; set file details from table,.X
TXA ; copy file index to .A
PHA ; save file index
LDA FA ; get device number
BEQ LAB_F3B1 ; if $00, keyboard, restore index and close file
CMP #$03 ; compare device number with screen
BEQ LAB_F3B1 ; if screen restore index and close file
BCS LAB_F3AE ; if > screen go do serial bus device close
CMP #$02 ; compare device with RS-232 device
BNE LAB_F38D ; branch if not RS-232 device
; else close RS-232 device
PLA ; restore file index
JSR LAB_F3B2 ; close file index .A
LDA #$7D ; disable T1, T2, CB1, CB2, SR and CA2
STA VIA1IER ; set VIA 1 IER
LDA #$06 ; set DTR and RTS high
STA VIA1PB ; set VIA 1 DRB
LDA #$EE ; CB2 high, CB1 -ve edge, CA2 high, CA1 -ve edge
STA VIA1PCR ; set VIA 1 PCR
JSR LAB_FE75 ; read the top of memory
LDA RIBUF+1 ; get RS-232 Rx buffer pointer high byte
BEQ LAB_F37F ; branch if no RS-232 input buffer
INY ; else reclaim RS-232 input buffer memory
LAB_F37F
LDA ROBUF+1 ; get RS-232 Tx buffer pointer high byte
BEQ LAB_F384 ; branch if no RS-232 output buffer
INY ; else reclaim RS-232 output buffer memory
LAB_F384
LDA #$00 ; clear .A
STA RIBUF+1 ; clear RS-232 Rx buffer pointer high byte
STA ROBUF+1 ; clear RS-232 Tx buffer pointer high byte
JMP LAB_F53C ; go set top of memory and exit
LAB_F38D
LDA SA ; get secondary address
AND #$0F ; mask the OPEN CHANNEL command
BEQ LAB_F3B1 ; if read restore index and close file
JSR TPBUFA ; get tape buffer start pointer in .X.Y
LDA #$00 ; character $00
JSR CHROUTTP ; output character to cassette
JMP PATCH3 ; go do CLOSE tail
LAB_F39E
BCS LAB_F3CE ; just exit if error
LDA SA ; get secondary address
CMP #$62 ; compare with end of tape flag
BNE LAB_F3B1 ; if not end of tape restore index and close file
LDA #$05 ; set logical end of the tape
JSR TAPEH ; write tape header
JMP LAB_F3B1 ; restore index and close file
;***********************************************************************************;
;
; do serial bus device file close
LAB_F3AE
JSR LAB_F6DA ; close serial bus device
LAB_F3B1
PLA ; restore file index
;***********************************************************************************;
;
; close file index .A
LAB_F3B2
TAX ; copy index to file to close
DEC LDTND ; decrement open file count
CPX LDTND ; compare index with open file count
BEQ LAB_F3CD ; exit if equal, last entry was closing file
; else entry was not last in list so copy last table entry
; file details over the details of the closing one
LDY LDTND ; get open file count as index
LDA LAT,Y ; get last+1 logical file number from logical file table
STA LAT,X ; save logical file number over closed file
LDA FAT,Y ; get last+1 device number from device number table
STA FAT,X ; save device number over closed file
LDA SAT,Y ; get last+1 secondary address from secondary address table
STA SAT,X ; save secondary address over closed file
LAB_F3CD
CLC ; flag no error
LAB_F3CE
RTS
;***********************************************************************************;
;
; find file
FNDFLNO
LDA #$00 ; clear .A
STA STATUS ; clear I/O status byte
TXA ; copy logical file number to .A
; find file .A
LAB_F3D4
LDX LDTND ; get open file count
LAB_F3D6
DEX ; decrement count to give index
BMI LAB_F3EE ; exit if no files
CMP LAT,X ; compare logical file number with table logical file number
BNE LAB_F3D6 ; loop if no match
RTS
;***********************************************************************************;
;
; set file details from table,.X
SETFLCH
LDA LAT,X ; get logical file from logical file table
STA LA ; set logical file
LDA FAT,X ; get device number from device number table
STA FA ; set device number
LDA SAT,X ; get secondary address from secondary address table
STA SA ; set secondary address
LAB_F3EE
RTS
;***********************************************************************************;
;
; close all channels and files
; This routine closes all open files. When this routine is called, the pointers into
; the open file table are reset, closing all files. Also the routine automatically
; resets the I/O channels.
FCLALL
LDA #$00 ; clear .A
STA LDTND ; clear open file count
;***********************************************************************************;
;
; close input and output channels
; This routine is called to clear all open channels and restore the I/O channels to
; their original default values. It is usually called after opening other I/O
; channels and using them for input/output operations. The default input device is
; 0, the keyboard. The default output device is 3, the screen.
; If one of the channels to be closed is to the serial bus, an UNTALK signal is sent
; first to clear the input channel or an UNLISTEN is sent to clear the output channel.
; By not calling this routine and leaving listener(s) active on the serial bus,
; several devices can receive the same data from the VIC at the same time. One way to
; take advantage of this would be to command the printer to LISTEN and the disk to
; TALK. This would allow direct printing of a disk file.
FCLRCHN
LDX #$03 ; set .X to screen
CPX DFLTO ; compare output device number with screen
BCS LAB_F3FC ; branch if screen >= device
; else was serial bus
JSR FUNLSN ; command the serial bus to UNLISTEN
LAB_F3FC
CPX DFLTN ; compare input device number with screen
BCS LAB_F403 ; branch if screen >= device
; else was serial bus
JSR FUNTLK ; command the serial bus to UNTALK
LAB_F403
STX DFLTO ; set output device number to screen
LDA #$00 ; set for keyboard
STA DFLTN ; set input device number to keyboard
RTS
;***********************************************************************************;
;
; open a logical file
; This routine is used to open a logical file. Once the logical file is set up it
; can be used for input/output operations. Most of the I/O KERNAL routines call on
; this routine to create the logical files to operate on. No arguments need to be
; set up to use this routine, but both the SETLFS and SETNAM KERNAL routines must
; be called before using this routine.
FOPEN
LDX LA ; get logical file
BNE LAB_F411 ; branch if there is a file
JMP FE_NTINP ; else do not input file error and return
LAB_F411
JSR FNDFLNO ; find file
BNE LAB_F419 ; branch if file not found
JMP FE_ALOPN ; else do file already open error and return
LAB_F419
LDX LDTND ; get open file count
CPX #$0A ; compare with max
BCC LAB_F422 ; branch if less
JMP FE_2MNYF ; else do too many files error and return
LAB_F422
INC LDTND ; increment open file count
LDA LA ; get logical file
STA LAT,X ; save to logical file table
LDA SA ; get secondary address
ORA #$60 ; OR with the OPEN CHANNEL command
STA SA ; set secondary address
STA SAT,X ; save to secondary address table
LDA FA ; get device number
STA FAT,X ; save to device number table
BEQ LAB_F493 ; do ok exit if keyboard
CMP #$03 ; compare device number with screen
BEQ LAB_F493 ; do ok exit if screen
BCC LAB_F444 ; branch if < screen, tape or RS-232
; else is serial bus device
JSR SERNAME ; send secondary address and filename
BCC LAB_F493 ; do ok exit, branch always
LAB_F444
CMP #$02 ; compare device with RS-232 device
BNE LAB_F44B ; branch if not RS-232 device, must be tape
JMP OPENRS ; go open RS-232 device and return
LAB_F44B
JSR TPBUFA ; get tape buffer start pointer in .X.Y
BCS LAB_F453 ; branch if >= $0200
JMP FE_ILDEV ; do illegal device number and return
LAB_F453
LDA SA ; get secondary address
AND #$0F ; mask the OPEN CHANNEL command
BNE LAB_F478 ; branch if write
JSR CSTEL ; wait for PLAY
BCS LAB_F494 ; exit if STOP was pressed
JSR SRCHING ; print "SEARCHING..."
LDA FNLEN ; get file name length
BEQ LAB_F46F ; if null file name just go find header
JSR FNDHDR ; find specific tape header
BCC LAB_F482 ; branch if no error
BEQ LAB_F494 ; branch always
LAB_F46C
JMP FE_NTFND ; do file not found error and return
LAB_F46F
JSR FAH ; find tape header, exit with header in buffer
BEQ LAB_F494 ; exit if end of tape found
BCC LAB_F482 ; branch if no error
BCS LAB_F46C ; branch if error
LAB_F478
JSR CSTE2 ; wait for PLAY/RECORD
BCS LAB_F494 ; exit if STOP was pressed
LDA #$04 ; set data file header
JSR TAPEH ; write tape header
LAB_F482
LDA #$BF ; set tape buffer length
LDY SA ; get secondary address
CPY #$60 ; compare with read
BEQ LAB_F491 ; branch if read
LDY #$00 ; clear index
LDA #$02 ; set data file block file type
STA (TAPE1),Y ; save file type to tape buffer
TYA ; clear .A
LAB_F491
STA BUFPNT ; save tape buffer index
LAB_F493
CLC ; flag ok
LAB_F494
RTS
;***********************************************************************************;
;
; send secondary address and filename
SERNAME
LDA SA ; get secondary address
BMI LAB_F4C5 ; ok exit if no address
LDY FNLEN ; get file name length
BEQ LAB_F4C5 ; ok exit if null
LDA FA ; get device number
JSR FLISTEN ; command devices on the serial bus to LISTEN
LDA SA ; get the secondary address
ORA #$F0 ; OR with the OPEN command
JSR FSECOND ; send secondary address after LISTEN
LDA STATUS ; get I/O status byte
BPL LAB_F4B2 ; branch if device present
PLA ; else dump calling address low byte
PLA ; dump calling address high byte
JMP FE_DVNTP ; do device not present error and return
LAB_F4B2
LDA FNLEN ; get file name length
BEQ LAB_F4C2 ; branch if null name
LDY #$00 ; clear index
LAB_F4B8
LDA (FNADR),Y ; get file name byte
JSR FCIOUT ; output a byte to the serial bus
INY ; increment index
CPY FNLEN ; compare with file name length
BNE LAB_F4B8 ; loop if not all done
LAB_F4C2
JSR FUNLSN ; command the serial bus to UNLISTEN
LAB_F4C5
CLC ; flag ok
RTS
;***********************************************************************************;
;
; open RS-232
OPENRS
LDA #$06 ; IIII IOOI, DTR and RTS only as outputs
STA VIA1DDRB ; set VIA 1 DDRB
STA VIA1PB ; set VIA 1 DRB, DTR and RTS high
LDA #$EE ; CB2 high, CB1 -ve edge, CA2 high, CA1 -ve edge
STA VIA1PCR ; set VIA 1 PCR
LDY #$00 ; clear index
STY RSSTAT ; clear RS-232 status byte
LAB_F4D9
CPY FNLEN ; compare with file name length
BEQ LAB_F4E7 ; exit loop if done
LDA (FNADR),Y ; get file name byte
STA M51CTR,Y ; copy to pseudo 6551 register set
INY ; increment index
CPY #$04 ; compare with $04
BNE LAB_F4D9 ; loop if not to 4 yet
LAB_F4E7
JSR RSCPTBIT ; compute bit count
STX BITNUM ; save bit count
LDA M51CTR ; get pseudo 6551 control register
AND #$0F ; mask 0000 xxxx, baud rate
BNE LAB_F4F4 ; short delay. was this intended to skip code used to
; implement the user baud rate ??
LAB_F4F4
ASL ; * 2, 2 bytes per baud count
TAX ; copy to index
LDA BAUDTBL-2,X ; get timer constant low byte
ASL ; * 2
TAY ; copy to .Y
LDA BAUDTBL-1,X ; get timer constant high byte
ROL ; * 2
PHA ; save it
TYA ; get timer constant low byte back
ADC #$C8 ; + 200, carry cleared by previous ROL
STA BAUDOF ; save bit cell time low byte
PLA ; restore high byte
ADC #$00 ; add carry
STA BAUDOF+1 ; save bit cell time high byte
LDA M51CDR ; get pseudo 6551 command register
LSR ; shift b0 into Cb
BCC LAB_F51B ; branch if 3 line interface
LDA VIA2PB ; get VIA 2 DRB
; The above code is wrong, the address should be VIA1PB which is where the DSR input
; really is
;
; LDA VIA1PB ; get VIA 1 DRB
ASL ; shift DSR into Cb
BCS LAB_F51B ; branch if DSR = 1
JMP RSMISSNG ; set DSR signal not present and return
LAB_F51B
LDA RIDBE ; get index to Rx buffer end
STA RIDBS ; set index to Rx buffer start, clear Rx buffer
LDA RODBE ; get index to Tx buffer end
STA RODBS ; set index to Tx buffer start, clear Tx buffer
JSR LAB_FE75 ; read the top of memory
LDA RIBUF+1 ; get RS-232 Rx buffer pointer high byte
BNE LAB_F533 ; branch if buffer already set
DEY ; decrement top of memory high byte, 256 byte buffer
STY RIBUF+1 ; set RS-232 Rx buffer pointer high byte
STX RIBUF ; set RS-232 Rx buffer pointer low byte
LAB_F533
LDA ROBUF+1 ; get RS-232 Tx buffer pointer high byte
BNE LAB_F53C ; branch if buffer already set
DEY ; decrement Rx buffer pointer high byte, 256 byte buffer
STY ROBUF+1 ; set RS-232 Tx buffer pointer high byte
STX ROBUF ; set RS-232 Tx buffer pointer low byte
LAB_F53C
SEC ; non-standard exit, Cb set
LDA #$F0 ; non-standard exit, $F0 error code
JMP LAB_FE7B ; set the top of memory and return
;***********************************************************************************;
;
; load RAM from a device
; This routine will load data bytes from any input device directly into the memory
; of the computer. It can also be used for a verify operation comparing data from a
; device with the data already in memory, leaving the data stored in RAM unchanged.
; The accumulator must be set to 0 for a load operation or 1 for a verify. If the
; input device was OPENed with a secondary address of 0 the header information from
; device will be ignored. In this case .X.Y must contain the starting address for the
; load. If the device was addressed with a secondary address of 1 or 2 the data will
; load into memory starting at the location specified by the header. This routine
; returns the address of the highest RAM location which was loaded.
; Before this routine can be called, the SETLFS and SETNAM routines must be called.
FLOAD
STX MEMUSS ; set load start address low byte
STY MEMUSS+1 ; set load start address high byte
JMP (ILOAD) ; do LOAD vector, usually points to FLOAD2
;***********************************************************************************;
;
; load
FLOAD2
STA VERCK ; save load/verify flag
LDA #$00 ; clear .A
STA STATUS ; clear I/O status byte
LDA FA ; get device number
BNE LAB_F556 ; branch if not keyboard
; can't load from keyboard so ..
LAB_F553
JMP FE_ILDEV ; do illegal device number and return
LAB_F556
CMP #$03 ; compare device number with screen
BEQ LAB_F553 ; if screen go do illegal device number and return
BCC LAB_F5CA ; branch if less than screen
; else is serial bus device
LDY FNLEN ; get file name length
BNE LAB_F563 ; branch if not null name
JMP FE_MISFN ; else do missing file name error and return
LAB_F563
JSR PATCH1 ; get secondary address and print "SEARCHING..."
LDA #$60 ; set secondary address to $00
STA SA ; save secondary address
JSR SERNAME ; send secondary address and filename
LDA FA ; get device number
JSR FTALK ; command a serial bus device to TALK
LDA SA ; get secondary address
JSR FTKSA ; send secondary address after TALK
JSR FACPTR ; input a byte from the serial bus
STA EAL ; save program start address low byte
LDA STATUS ; get I/O status byte
LSR ; shift time out read ..
LSR ; .. into carry bit
BCS LAB_F5C7 ; if timed out go do file not found error and return
JSR FACPTR ; input a byte from the serial bus
STA EAL+1 ; save program start address high byte
JSR PATCH2 ; set LOAD address if secondary address = 0
LAB_F58A
LDA #$FD ; mask xxxx xx0x, clear time out read bit
AND STATUS ; mask I/O status byte
STA STATUS ; set I/O status byte
JSR STOP ; scan stop key, return Zb = 1 = [STOP]
BNE LAB_F598 ; branch if not [STOP]
JMP LAB_F6CB ; else close the serial bus device and flag stop
LAB_F598
JSR FACPTR ; input a byte from the serial bus
TAX ; copy byte
LDA STATUS ; get I/O status byte
LSR ; shift time out read ..
LSR ; .. into carry bit
BCS LAB_F58A ; if timed out clear I/O status and retry
TXA ; copy received byte back
LDY VERCK ; get load/verify flag
BEQ LAB_F5B3 ; branch if load
; else is verify
LDY #$00 ; clear index
CMP (EAL),Y ; compare byte with previously loaded byte
BEQ LAB_F5B5 ; branch if match
LDA #$10 ; set read error bit
JSR ORIOST ; OR into I/O status byte
.byte $2C ; makes next line BIT $AE91
LAB_F5B3
STA (EAL),Y ; save byte to memory
LAB_F5B5
INC EAL ; increment save pointer low byte
BNE LAB_F5BB ; if no rollover skip the high byte increment
INC EAL+1 ; else increment save pointer high byte
LAB_F5BB
BIT STATUS ; test I/O status byte
BVC LAB_F58A ; loop if not end of file
JSR FUNTLK ; command the serial bus to UNTALK
JSR LAB_F6DA ; close serial bus device
BCC LAB_F641 ; if no error go flag ok and exit
LAB_F5C7
JMP FE_NTFND ; do file not found error and return
LAB_F5CA
CMP #$02 ; compare device with RS-232 device
BNE LOADTP ; if not RS-232 device continue
JMP RSDVCERR ; else do illegal device number and return
LOADTP
JSR TPBUFA ; get tape buffer start pointer in .X.Y
BCS LAB_F5D9 ; branch if >= $0200
JMP FE_ILDEV ; do illegal device number and return
LAB_F5D9
JSR CSTEL ; wait for PLAY
BCS LAB_F646 ; exit if STOP was pressed
JSR SRCHING ; print "SEARCHING..."
LAB_F5E1
LDA FNLEN ; get file name length
BEQ LAB_F5EE
JSR FNDHDR ; find specific tape header
BCC LAB_F5F5 ; if no error continue
BEQ LAB_F646 ; exit if end of tape found
BCS LAB_F5C7 ; exit on error
LAB_F5EE
JSR FAH ; find tape header, exit with header in buffer
BEQ LAB_F646 ; exit if end of tape found
BCS LAB_F5C7 ; exit on error
LAB_F5F5
LDA STATUS ; get I/O status byte
AND #$10 ; mask 000x 0000, read error
SEC ; flag fail
BNE LAB_F646 ; if read error just exit
CPX #$01 ; compare file type with relocatable program
BEQ LAB_F611 ; branch if relocatable program
CPX #$03 ; compare file type with non relocatable program
BNE LAB_F5E1 ; branch if not non relocatable program
LAB_F604
LDY #$01 ; index to start address
LDA (TAPE1),Y ; get start address low byte
STA MEMUSS ; save load start address low byte
INY ; increment index
LDA (TAPE1),Y ; get start address high byte
STA MEMUSS+1 ; set load start address high byte
BCS LAB_F615 ; branch always
LAB_F611
LDA SA ; get secondary address
BNE LAB_F604 ; branch if not relocatable
LAB_F615
LDY #$03 ; index to end address low byte
LDA (TAPE1),Y ; get end address low byte
LDY #$01 ; index to start address low byte
SBC (TAPE1),Y ; subtract start address low byte
TAX ; copy file length low byte
LDY #$04 ; index to end address high byte
LDA (TAPE1),Y ; get end address high byte
LDY #$02 ; index to start address high byte
SBC (TAPE1),Y ; subtract start address high byte
TAY ; copy file length high byte
CLC ; clear carry for add
TXA ; get file length low byte back
ADC MEMUSS ; add load start address low byte
STA EAL ; save LOAD end pointer low byte
TYA ; get file length high byte back
ADC MEMUSS+1 ; add load start address high byte
STA EAL+1 ; save LOAD end pointer high byte
LDA MEMUSS ; get load start address low byte
STA STAL ; save I/O start address low byte
LDA MEMUSS+1 ; get load start address high byte
STA STAL+1 ; save I/O start address high byte
JSR LDVRMSG ; display "LOADING" or "VERIFYING"
JSR RBLK ; do the tape read
.byte $24 ; makes next line BIT $18, keep the error flag in Cb
LAB_F641
CLC ; flag ok
LDX EAL ; get the LOAD end pointer low byte
LDY EAL+1 ; get the LOAD end pointer high byte
LAB_F646
RTS
;***********************************************************************************;
;
; print "SEARCHING..."
SRCHING
LDA MSGFLG ; get KERNAL message mode flag
BPL LAB_F669 ; exit if control messages off
LDY #KM_SRCHG-KMSGTBL
; index to "SEARCHING "
JSR KMSGSHOW ; display KERNAL I/O message
LDA FNLEN ; get file name length
BEQ LAB_F669 ; exit if null name
LDY #KM_FOR-KMSGTBL
; else index to "FOR "
JSR KMSGSHOW ; display KERNAL I/O message
; print file name
FILENAME
LDY FNLEN ; get file name length
BEQ LAB_F669 ; exit if null file name
LDY #$00 ; clear index
LAB_F65F
LDA (FNADR),Y ; get file name byte
JSR CHROUT ; output character to channel
INY ; increment index
CPY FNLEN ; compare with file name length
BNE LAB_F65F ; loop if more to do
LAB_F669
RTS
; display "LOADING" or "VERIFYING"
LDVRMSG
LDY #KM_LODNG-KMSGTBL
; point to "LOADING"
LDA VERCK ; get load/verify flag
BEQ LAB_F672 ; branch if load
LDY #KM_VFYNG-KMSGTBL
; point to "VERIFYING"
LAB_F672
JMP SPMSG ; display KERNAL I/O message if in direct mode and return
;***********************************************************************************;
;
; save RAM to device, .A = index to start address, .X.Y = end address low/high
; This routine saves a section of memory. Memory is saved from an indirect address
; on page 0 specified by A, to the address stored in .X.Y, to a logical file. The
; SETLFS and SETNAM routines must be used before calling this routine. However, a
; file name is not required to SAVE to device 1, the cassette. Any attempt to save to
; other devices without using a file name results in an error.
; NOTE: device 0, the keyboard, and device 3, the screen, cannot be SAVEd to. If
; the attempt is made, an error will occur, and the SAVE stopped.
FSAVE
STX EAL ; save end address low byte
STY EAL+1 ; save end address high byte
TAX ; copy index to start pointer
LDA $00,X ; get start address low byte
STA STAL ; set I/O start address low byte
LDA $01,X ; get start address high byte
STA STAL+1 ; set I/O start address high byte
JMP (ISAVE) ; go save, usually points to FSAVE2
;***********************************************************************************;
;
; save
FSAVE2
LDA FA ; get device number
BNE LAB_F68C ; branch if not keyboard
; else ..
LAB_F689
JMP FE_ILDEV ; do illegal device number and return
LAB_F68C
CMP #$03 ; compare device number with screen
BEQ LAB_F689 ; if screen do illegal device number and return
BCC SAVETP ; branch if < screen
; is greater than screen so is serial bus
LDA #$61 ; set secondary address to $01
; when a secondary address is to be sent to a device on
; the serial bus the address must first be ORed with $60
STA SA ; save secondary address
LDY FNLEN ; get file name length
BNE LAB_F69D ; branch if filename not null
JMP FE_MISFN ; else do missing file name error and return
LAB_F69D
JSR SERNAME ; send secondary address and filename
JSR SAVING ; print "SAVING <file name>"
LDA FA ; get device number
JSR FLISTEN ; command devices on the serial bus to LISTEN
LDA SA ; get secondary address
JSR FSECOND ; send secondary address after LISTEN
LDY #$00 ; clear index
JSR RD300 ; copy I/O start address to buffer address
LDA SAL ; get buffer address low byte
JSR FCIOUT ; output a byte to the serial bus
LDA SAL+1 ; get buffer address high byte
JSR FCIOUT ; output a byte to the serial bus
LAB_F6BC
JSR VPRTY ; check read/write pointer, return Cb = 1 if pointer >= end
BCS LAB_F6D7 ; go do UNLISTEN if at end
LDA (SAL),Y ; get byte from buffer
JSR FCIOUT ; output a byte to the serial bus
JSR STOP ; scan stop key
BNE LAB_F6D2 ; if stop not pressed go increment pointer and loop for next
; else ..
; close the serial bus device and flag stop
LAB_F6CB
JSR LAB_F6DA ; close serial bus device
LDA #ER_STOP ; terminated by STOP key
SEC ; flag stop
RTS
LAB_F6D2
JSR WRT62 ; increment read/write pointer
BNE LAB_F6BC ; loop, branch always
;***********************************************************************************;
;
; command serial bus to UNLISTEN then close channel
LAB_F6D7
JSR FUNLSN ; command the serial bus to UNLISTEN
; close the serial bus device
LAB_F6DA
BIT SA ; test the secondary address
BMI LAB_F6EF ; if already closed just exit
LDA FA ; get device number
JSR FLISTEN ; command devices on the serial bus to LISTEN
LDA SA ; get secondary address
AND #$EF ; mask the channel number
ORA #$E0 ; OR with the CLOSE command
JSR FSECOND ; send secondary address after LISTEN
JSR FUNLSN ; command the serial bus to UNLISTEN
LAB_F6EF
CLC ; flag ok
RTS
SAVETP
CMP #$02 ; compare device with RS-232 device
BNE LAB_F6F8 ; branch if not RS-232 device
JMP RSDVCERR ; else do illegal device number and return
LAB_F6F8
JSR TPBUFA ; get tape buffer start pointer in .X.Y
BCC LAB_F689 ; if < $0200 do illegal device number and return
JSR CSTE2 ; wait for PLAY/RECORD
BCS LAB_F727 ; exit if STOP was pressed
JSR SAVING ; print "SAVING <file name>"
LDX #$03 ; set header for a non relocatable program file
LDA SA ; get secondary address
AND #$01 ; mask non relocatable bit
BNE LAB_F70F ; branch if non relocatable program
LDX #$01 ; else set header for a relocatable program file
LAB_F70F
TXA ; copy header type to .A
JSR TAPEH ; write tape header
BCS LAB_F727 ; exit if error
JSR LAB_F8E6 ; do tape write, 20 cycle count
BCS LAB_F727 ; exit if error
LDA SA ; get secondary address
AND #$02 ; mask end of tape flag
BEQ LAB_F726 ; branch if not end of tape
LDA #$05 ; else set logical end of the tape
JSR TAPEH ; write tape header
.byte $24 ; makes next line BIT $18 so Cb is not changed
LAB_F726
CLC ; flag ok
LAB_F727
RTS
;***********************************************************************************;
;
; print "SAVING <file name>"
SAVING
LDA MSGFLG ; get KERNAL message mode flag
BPL LAB_F727 ; exit if control messages off
LDY #KM_SAVNG-KMSGTBL
; index to "SAVING "
JSR KMSGSHOW ; display KERNAL I/O message
JMP FILENAME ; print file name and return
;***********************************************************************************;
;
; increment real time clock
; This routine updates the system clock. Normally this routine is called by the
; normal KERNAL interrupt routine every 1/60th of a second. If the user program
; processes its own interrupts this routine must be called to update the time. Also,
; the STOP key routine must be called if the stop key is to remain functional.
FUDTIM
LDX #$00 ; clear .X
INC TIME+2 ; increment jiffy low byte
BNE LAB_F740 ; if no rollover skip the mid byte increment
INC TIME+1 ; increment jiffy mid byte
BNE LAB_F740 ; if no rollover skip the high byte increment
INC TIME ; increment jiffy high byte
; now subtract a days worth of jiffies from current count
; and remember only the Cb result
LAB_F740
SEC ; set carry for subtract
LDA TIME+2 ; get jiffy clock low byte
SBC #$01 ; subtract $4F1A01 low byte
LDA TIME+1 ; get jiffy clock mid byte
SBC #$1A ; subtract $4F1A01 mid byte
LDA TIME ; get jiffy clock high byte
SBC #$4F ; subtract $4F1A01 high byte
BCC LAB_F755 ; branch if less than $4F1A01 jiffies
; else ..
STX TIME ; clear jiffies high byte
STX TIME+1 ; clear jiffies mid byte
STX TIME+2 ; clear jiffies low byte
; this is wrong, there are $4F1A00 jiffies in a day so
; the reset to zero should occur when the value reaches
; $4F1A00 and not $4F1A01. this would give an extra jiffy
; every day and a possible TI value of 24:00:00
LAB_F755
LDA VIA2PA2 ; get VIA 2 DRA, keyboard row, no handshake
CMP VIA2PA2 ; compare with self
BNE LAB_F755 ; loop if changing
STA STKEY ; save VIA 2 DRA, keyboard row
RTS
;***********************************************************************************;
;
; read the real time clock
; This routine returns the time, in jiffies, in .Y.X.A. The accumulator contains the
; most significant byte.
FRDTIM
SEI ; disable interrupts
LDA TIME+2 ; get jiffy clock low byte
LDX TIME+1 ; get jiffy clock mid byte
LDY TIME ; get jiffy clock high byte
;***********************************************************************************;
;
; set the real time clock
; The system clock is maintained by an interrupt routine that updates the clock
; every 1/60th of a second. The clock is three bytes long which gives the capability
; to count from zero up to 5,184,000 jiffies - 24 hours plus one jiffy. At that point
; the clock resets to zero. Before calling this routine to set the clock the new time,
; in jiffies, should be in .Y.X.A, the accumulator containing the most significant byte.
FSETTIM
SEI ; disable interrupts
STA TIME+2 ; save jiffy clock low byte
STX TIME+1 ; save jiffy clock mid byte
STY TIME ; save jiffy clock high byte
CLI ; enable interrupts
RTS
;***********************************************************************************;
;
; scan stop key, return Zb = 1 = [STOP]
; If the STOP key on the keyboard is pressed when this routine is called the Z flag
; will be set. All other flags remain unchanged. If the STOP key is not pressed then
; the accumulator will contain a byte representing the last row of the keyboard scan.
; The user can also check for certain other keys this way.
FSTOP
LDA STKEY ; get keyboard row
CMP #$FE ; compare with r0 down
BNE LAB_F77D ; branch if not just r0
PHP ; save status
JSR CLRCHN ; close input and output channels
STA NDX ; save keyboard buffer length
PLP ; restore status
LAB_F77D
RTS
;***********************************************************************************;
;
; file error messages
FE_2MNYF
LDA #$01 ; too many files
.byte $2C ; makes next line BIT $02A9
FE_ALOPN
LDA #$02 ; file already open
.byte $2C ; makes next line BIT $03A9
FE_NTOPN
LDA #$03 ; file not open
.byte $2C ; makes next line BIT $04A9
FE_NTFND
LDA #$04 ; file not found
.byte $2C ; makes next line BIT $05A9
FE_DVNTP
LDA #$05 ; device not present
.byte $2C ; makes next line BIT $06A9
FE_NTINP
LDA #$06 ; not input file
.byte $2C ; makes next line BIT $07A9
FE_NTOUT
LDA #$07 ; not output file
.byte $2C ; makes next line BIT $08A9
FE_MISFN
LDA #$08 ; missing file name
.byte $2C ; makes next line BIT $09A9
FE_ILDEV
LDA #$09 ; illegal device number
PHA ; save error #
JSR CLRCHN ; close input and output channels
LDY #KM_IOERR-KMSGTBL
; index to "I/O ERROR #"
BIT MSGFLG ; test KERNAL message mode flag
BVC LAB_F7AC ; exit if error messages off
JSR KMSGSHOW ; display KERNAL I/O message
PLA ; restore error #
PHA ; copy error #
ORA #'0' ; convert to ASCII
JSR CHROUT ; output character to channel
LAB_F7AC
PLA ; pull error number
SEC ; flag error
RTS
;***********************************************************************************;
;
; find tape header, exit with header in buffer
FAH
LDA VERCK ; get load/verify flag
PHA ; save load/verify flag
JSR RDTPBLKS ; initiate tape read
PLA ; restore load/verify flag
STA VERCK ; save load/verify flag
BCS LAB_F7E6 ; exit if error
LDY #$00 ; clear index
LDA (TAPE1),Y ; read first byte from tape buffer
CMP #$05 ; compare with logical end of the tape
BEQ LAB_F7E6 ; exit if end of the tape
CMP #$01 ; compare with header for a relocatable program file
BEQ LAB_F7CE ; branch if program file header
CMP #$03 ; compare with header for a non relocatable program file
BEQ LAB_F7CE ; branch if program file header
CMP #$04 ; compare with data file header
BNE FAH ; if data file loop to find tape header
; was program file header
LAB_F7CE
TAX ; copy header type
BIT MSGFLG ; get KERNAL message mode flag
BPL LAB_F7E4 ; exit if control messages off
LDY #KM_FOUND-KMSGTBL
; index to "FOUND "
JSR KMSGSHOW ; display KERNAL I/O message
LDY #$05 ; index to tape filename
LAB_F7DA
LDA (TAPE1),Y ; get byte from tape buffer
JSR CHROUT ; output character to channel
INY ; increment index
CPY #$15 ; compare with end+1
BNE LAB_F7DA ; loop if more to do
LAB_F7E4
CLC ; flag no error
DEY ; decrement index
LAB_F7E6
RTS
;***********************************************************************************;
;
; write tape header
TAPEH
STA PTR1 ; save header type
JSR TPBUFA ; get tape buffer start pointer in .X.Y
BCC LAB_F84C ; exit if < $0200
LDA STAL+1 ; get I/O start address high byte
PHA ; save it
LDA STAL ; get I/O start address low byte
PHA ; save it
LDA EAL+1 ; get tape end address high byte
PHA ; save it
LDA EAL ; get tape end address low byte
PHA ; save it
LDY #$BF ; index to header end
LDA #' ' ; clear byte, [SPACE]
LAB_F7FE
STA (TAPE1),Y ; clear header byte
DEY ; decrement index
BNE LAB_F7FE ; loop if more to do
LDA PTR1 ; get header type back
STA (TAPE1),Y ; write to header
INY ; increment index
LDA STAL ; get I/O start address low byte
STA (TAPE1),Y ; write to header
INY ; increment index
LDA STAL+1 ; get I/O start address high byte
STA (TAPE1),Y ; write to header
INY ; increment index
LDA EAL ; get tape end address low byte
STA (TAPE1),Y ; write to header
INY ; increment index
LDA EAL+1 ; get tape end address high byte
STA (TAPE1),Y ; write to header
INY ; increment index
STY PTR2 ; save index
LDY #$00 ; clear .Y
STY PTR1 ; clear name index
LAB_F822
LDY PTR1 ; get name index
CPY FNLEN ; compare with file name length
BEQ LAB_F834 ; exit loop if all done
LDA (FNADR),Y ; get file name byte
LDY PTR2 ; get buffer index
STA (TAPE1),Y ; save file name byte to buffer
INC PTR1 ; increment file name index
INC PTR2 ; increment tape buffer index
BNE LAB_F822 ; loop, branch always
LAB_F834
JSR LDAD1 ; set tape buffer start and end pointers
LDA #$69 ; set write lead cycle count
STA RIPRTY ; save write lead cycle count
JSR LAB_F8EA ; do tape write, no cycle count set
TAY ;.
PLA ; pull tape end address low byte
STA EAL ; restore it
PLA ; pull tape end address high byte
STA EAL+1 ; restore it
PLA ; pull I/O start address low byte
STA STAL ; restore it
PLA ; pull I/O start address high byte
STA STAL+1 ; restore it
TYA ;.
LAB_F84C
RTS
;***********************************************************************************;
;
; get tape buffer start pointer
TPBUFA
LDX TAPE1 ; get tape buffer start pointer low byte
LDY TAPE1+1 ; get tape buffer start pointer high byte
CPY #$02 ; compare high byte with $02xx
RTS
;***********************************************************************************;
;
; set tape buffer start and end pointers
LDAD1
JSR TPBUFA ; get tape buffer start pointer in .X.Y
TXA ; copy tape buffer start pointer low byte
STA STAL ; save as I/O address low byte
CLC ; clear carry for add
ADC #$C0 ; add buffer length low byte
STA EAL ; save tape buffer end pointer low byte
TYA ; copy tape buffer start pointer high byte
STA STAL+1 ; save as I/O address high byte
ADC #$00 ; add buffer length high byte
STA EAL+1 ; save tape buffer end pointer high byte
RTS
;***********************************************************************************;
;
; find specific tape header
FNDHDR
JSR FAH ; find tape header, exit with header in buffer
BCS LAB_F889 ; just exit if error
LDY #$05 ; index to name
STY PTR2 ; save as tape buffer index
LDY #$00 ; clear .Y
STY PTR1 ; save as name buffer index
LAB_F874
CPY FNLEN ; compare with file name length
BEQ LAB_F888 ; ok exit if match
LDA (FNADR),Y ; get file name byte
LDY PTR2 ; get index to tape buffer
CMP (TAPE1),Y ; compare with tape header name byte
BNE FNDHDR ; if no match go get next header
INC PTR1 ; else increment name buffer index
INC PTR2 ; increment tape buffer index
LDY PTR1 ; get name buffer index
BNE LAB_F874 ; loop, branch always
LAB_F888
CLC ; flag ok
LAB_F889
RTS
;***********************************************************************************;
;
; bump tape pointer
JTP20
JSR TPBUFA ; get tape buffer start pointer in .X.Y
INC BUFPNT ; increment tape buffer index
LDY BUFPNT ; get tape buffer index
CPY #$C0 ; compare with buffer length
RTS
;***********************************************************************************;
;
; wait for PLAY
CSTEL
JSR CS10 ; return cassette sense in Zb
BEQ LAB_F8B5 ; exit if switch closed
; cassette switch was open
LDY #KM_PRPLY-KMSGTBL
; index to "PRESS PLAY ON TAPE"
LAB_F89B
JSR KMSGSHOW ; display KERNAL I/O message
LAB_F89E
JSR TSTOP ; scan stop key and flag abort if pressed
; note if STOP was pressed the return is to the
; routine that called this one and not here
JSR CS10 ; return cassette sense in Zb
BNE LAB_F89E ; loop if cassette switch open
LDY #KM_OK-KMSGTBL
; index to "OK"
JMP KMSGSHOW ; display KERNAL I/O message and return
;***********************************************************************************;
;
; return cassette sense in Zb
CS10
LDA #$40 ; mask for cassette switch
BIT VIA1PA2 ; test VIA 1 DRA, no handshake
BNE LAB_F8B5 ; branch if cassette sense high
BIT VIA1PA2 ; test VIA 1 DRA again
LAB_F8B5
CLC
RTS
;***********************************************************************************;
;
; wait for PLAY/RECORD
CSTE2
JSR CS10 ; return cassette sense in Zb
BEQ LAB_F8B5 ; exit if switch closed
; cassette switch was open
LDY #KM_RECPY-KMSGTBL
; index to "PRESS RECORD & PLAY ON TAPE"
BNE LAB_F89B ; display message and wait for switch, branch always
;***********************************************************************************;
;
; initiate tape read
RDTPBLKS
LDA #$00 ; clear .A
STA STATUS ; clear I/O status byte
STA VERCK ; clear the load/verify flag
JSR LDAD1 ; set tape buffer start and end pointers
RBLK
JSR CSTEL ; wait for PLAY
BCS LAB_F8ED ; exit if STOP was pressed, uses further BCS at target
; address to reach final target at LAB_F957
SEI ; disable interrupts
LDA #$00 ; clear .A
STA RIDATA ; clear tape input status
STA BITTS ; clear tape read ready
STA CMP0 ; clear tape timing constant min byte
STA PTR1 ; clear tape pass 1 error log/char buffer
STA PTR2 ; clear tape pass 2 error log corrected
STA DPSW ; clear tape dipole switch
LDA #$82 ; enable CA1 interrupt
LDX #$0E ; set index for tape read vector
BNE TAPE ; go do tape read/write, branch always
;***********************************************************************************;
;
; initiate tape write
WBLK
JSR LDAD1 ; set tape buffer start and end pointers
; do tape write, 20 cycle count
LAB_F8E6
LDA #$14 ; set write lead cycle count
STA RIPRTY ; save write lead cycle count
; do tape write, no cycle count set
LAB_F8EA
JSR CSTE2 ; wait for PLAY/RECORD
LAB_F8ED
BCS LAB_F957 ; if STOPped clear save IRQ address and exit
SEI ; disable interrupts
LDA #$A0 ; enable VIA 2 T2 interrupt
LDX #$08 ; set index for tape write tape leader vector
;***********************************************************************************;
;
; tape read/write
TAPE
LDY #$7F ; disable all interrupts
STY VIA2IER ; set VIA 2 IER, disable interrupts
STA VIA2IER ; set VIA 2 IER, enable interrupts according to .A
JSR RSPAUSE ; check RS-232 bus idle
LDA CINV ; get IRQ vector low byte
STA IRQTMP ; save IRQ vector low byte
LDA CINV+1 ; get IRQ vector high byte
STA IRQTMP+1 ; save IRQ vector high byte
JSR LAB_FCFB ; set tape vector
LDA #$02 ; set copies remaining. the first copy is the load copy, the
; second copy is the verify copy
STA FSBLK ; save copies remaining
JSR NEWCH ; new tape byte setup
LDA VIA1PCR ; get VIA 1 PCR
AND #$FD ; CA2 low, turn on tape motor
ORA #$0C ; manual output mode
STA VIA1PCR ; set VIA 1 PCR
STA CAS1 ; set tape motor interlock
; 326656 cycle delay, allow tape motor speed to stabilise
LDX #$FF ; outer loop count
LAB_F923
LDY #$FF ; inner loop count
LAB_F925
DEY ; decrement inner loop count
BNE LAB_F925 ; loop if more to do
DEX ; decrement outer loop count
BNE LAB_F923 ; loop if more to do
STA VIA2T2CH ; set VIA 2 T2C_h
CLI ; enable tape interrupts
LAB_F92F
LDA IRQTMP+1 ; get saved IRQ high byte
CMP CINV+1 ; compare with the current IRQ high byte
CLC ; flag ok
BEQ LAB_F957 ; if tape write done go clear saved IRQ address and exit
JSR TSTOP ; scan stop key and flag abort if pressed
; note if STOP was pressed the return is to the
; routine that called this one and not here
LDA VIA2IFR ; get VIA 2 IFR
AND #$40 ; mask T1 interrupt
BEQ LAB_F92F ; loop if not T1 interrupt
; else increment jiffy clock
LDA VIA1T1CL ; get VIA 1 T1C_l, clear T1 interrupt
JSR FUDTIM ; increment the real time clock
JMP LAB_F92F ; loop
;***********************************************************************************;
;
; scan stop key and flag abort if pressed
TSTOP
JSR STOP ; scan stop key
CLC ; flag no stop
BNE LAB_F95C ; exit if no stop
JSR TNIF ; restore everything for STOP
SEC ; flag stopped
PLA ; dump return address low byte
PLA ; dump return address high byte
;***********************************************************************************;
;
; clear saved IRQ address
LAB_F957
LDA #$00 ; clear .A
STA IRQTMP+1 ; clear saved IRQ address high byte
LAB_F95C
RTS
;***********************************************************************************;
;
;## set timing
STT1
STX CMP0+1 ; save tape timing constant max byte
LDA CMP0 ; get tape timing constant min byte
ASL ; *2
ASL ; *4
CLC ; clear carry for add
ADC CMP0 ; add tape timing constant min byte *5
CLC ; clear carry for add
ADC CMP0+1 ; add tape timing constant max byte
STA CMP0+1 ; save tape timing constant max byte
LDA #$00 ;.
BIT CMP0 ; test tape timing constant min byte
BMI LAB_F972 ; branch if b7 set
ROL ; else shift carry into ??
LAB_F972
ASL CMP0+1 ; shift tape timing constant max byte
ROL ;.
ASL CMP0+1 ; shift tape timing constant max byte
ROL ;.
TAX ;.
LAB_F979
LDA VIA2T2CL ; get VIA 2 T2C_l
CMP #$15 ;.compare with ??
BCC LAB_F979 ; loop if less
ADC CMP0+1 ; add tape timing constant max byte
STA VIA2T1CL ; set VIA 2 T1C_l
TXA ;.
ADC VIA2T2CH ; add VIA 2 T2C_h
STA VIA2T1CH ; set VIA 2 T1C_h
CLI ; enable interrupts
RTS
;***********************************************************************************;
;
;; On Commodore computers, the streams consist of four kinds of symbols
;; that denote different kinds of low-to-high-to-low transitions on the
;; read or write signals of the Commodore cassette interface.
;;
;; A A break in the communications, or a pulse with very long cycle
;; time.
;;
;; B A short pulse, whose cycle time typically ranges from 296 to 424
;; microseconds, depending on the computer model.
;;
;; C A medium-length pulse, whose cycle time typically ranges from
;; 440 to 576 microseconds, depending on the computer model.
;;
;; D A long pulse, whose cycle time typically ranges from 600 to 744
;; microseconds, depending on the computer model.
;;
;; The actual interpretation of the serial data takes a little more work to
;; explain. The typical ROM tape loader (and the turbo loaders) will
;; initialise a timer with a specified value and start it counting down. If
;; either the tape data changes or the timer runs out, an IRQ will occur. The
;; loader will determine which condition caused the IRQ. If the tape data
;; changed before the timer ran out, we have a short pulse, or a "0" bit. If
;; the timer ran out first, we have a long pulse, or a "1" bit. Doing this
;; continuously and we decode the entire file.
; read tape bits, IRQ routine
; read T2C which has been counting down from $FFFF. subtract this from $FFFF
READT
LDX VIA2T2CH ; get VIA 2 T2C_h
LDY #$FF ;.set $FF
TYA ; .A = $FF
SBC VIA2T2CL ; subtract VIA 2 T2C_l
CPX VIA2T2CH ; compare VIA 2 T2C_h with previous
BNE READT ; loop if timer low byte rolled over
STX CMP0+1 ; save tape timing constant max byte
TAX ;.copy $FF - T2C_l
STY VIA2T2CL ; set VIA 2 T2C_l to $FF
STY VIA2T2CH ; set VIA 2 T2C_h to $FF
TYA ;.$FF
SBC CMP0+1 ; subtract tape timing constant max byte
; .A = $FF - T2C_h
STX CMP0+1 ; save tape timing constant max byte
; CMP0+1 = $FF - T2C_l
LSR ; .A = $FF - T2C_h >> 1
ROR CMP0+1 ; shift tape timing constant max byte
; CMP0+1 = $FF - T2C_l >> 1
LSR ; .A = $FF - T2C_h >> 1
ROR CMP0+1 ; shift tape timing constant max byte
; CMP0+1 = $FF - T2C_l >> 1
LDA CMP0 ; get tape timing constant min byte
CLC ; clear carry for add
ADC #$3C ;.
BIT VIA2PA1 ; test VIA 2 DRA, keyboard row
CMP CMP0+1 ; compare with tape timing constant max byte
; compare with ($FFFF - T2C) >> 2
BCS LAB_FA06 ;.branch if min + $3C >= ($FFFF - T2C) >> 2
;.min + $3C < ($FFFF - T2C) >> 2
LDX DPSW ; get tape byte received flag
BEQ LAB_F9C3 ; branch if not byte received
JMP TPSTORE ;.store tape character
LAB_F9C3
LDX PCNTR ; get tape bit count
BMI LAB_F9E2 ; branch if character complete
LDX #$00 ; clear .X
ADC #$30 ;.
ADC CMP0 ; add tape timing constant min byte
CMP CMP0+1 ; compare with tape timing constant max byte
BCS LAB_F9ED ;.
INX ;.
ADC #$26 ;.
ADC CMP0 ; add tape timing constant min byte
CMP CMP0+1 ; compare with tape timing constant max byte
BCS LAB_F9F1 ;.
ADC #$2C ;.
ADC CMP0 ; add tape timing constant min byte
CMP CMP0+1 ; compare with tape timing constant max byte
BCC LAB_F9E5 ;.
LAB_F9E2
JMP LAB_FA60 ;.
LAB_F9E5
LDA BITTS ; get tape read ready
BEQ LAB_FA06 ; branch if zero
STA BITCI ; save tape long word marker
BNE LAB_FA06 ; branch always
LAB_F9ED
INC RINONE ; increment tape dipole count
BCS LAB_F9F3 ;.
LAB_F9F1
DEC RINONE ; decrement tape dipole count
LAB_F9F3
SEC ;.
SBC #$13 ;.
SBC CMP0+1 ; subtract tape timing constant max byte
ADC SVXT ; add timing constant for tape
STA SVXT ; save timing constant for tape
LDA FIRT ; get tape bit cycle phase
EOR #$01 ; toggle b0
STA FIRT ; save tape bit cycle phase
BEQ LAB_FA25 ; if first cycle complete go to second cycle
STX ASCII ; save bit value
LAB_FA06
LDA BITTS ; get tape read ready
BEQ LAB_FA22 ; exit if zero
BIT VIA2IFR ; test get 2 IFR
BVC LAB_FA22 ; exit if no T1 interrupt
LDA #$00 ; clear .A
STA FIRT ; clear tape bit cycle phase
LDA PCNTR ; get tape bit count
BPL LAB_FA47 ; branch of more bits
BMI LAB_F9E2 ; branch always
LAB_FA19
LDX #$A6 ; set timing max byte
JSR STT1 ; set timing
LDA PRTY ; get tape character parity
BNE LAB_F9E5 ;.
LAB_FA22
JMP _RTI ; restore registers and exit interrupt
LAB_FA25
LDA SVXT ; get timing constant for tape
BEQ LAB_FA30 ;.
BMI LAB_FA2E ;.
DEC CMP0 ; decrement tape timing constant min byte
.byte $2C ; makes next line BIT $B0E6
LAB_FA2E
INC CMP0 ; increment tape timing constant min byte
LAB_FA30
LDA #$00 ; clear .A
STA SVXT ; clear timing constant for tape
CPX ASCII ;.
BNE LAB_FA47 ;.
TXA ;.
BNE LAB_F9E5 ;.
LDA RINONE ; get tape dipole count
BMI LAB_FA06 ;.
CMP #$10 ;.
BCC LAB_FA06 ;.
STA SYNO ; save leader length
BCS LAB_FA06 ; branch always
LAB_FA47
TXA
EOR PRTY ; XOR with tape character parity
STA PRTY ; save tape character parity
LDA BITTS ; get tape read ready
BEQ LAB_FA22 ; if zero exit interrupt
DEC PCNTR ; decrement tape bit count
BMI LAB_FA19 ; branch if character complete
LSR ASCII ; shift dipole into Cb
ROR MYCH ; rotate Cb into tape read byte
LDX #$DA ; set timing max byte
JSR STT1 ; set timing
JMP _RTI ; restore registers and exit interrupt
LAB_FA60
LDA SYNO ; get leader length
BEQ LAB_FA68 ; branch if no block
LDA BITTS ; get tape read ready
BEQ LAB_FA6C ;.
LAB_FA68
LDA PCNTR ; get tape bit count
BPL LAB_F9F1 ; branch if more bits
LAB_FA6C
LSR CMP0+1 ; shift tape timing constant max byte
LDA #$93 ;.
SEC ;.
SBC CMP0+1 ; subtract tape timing constant max byte
ADC CMP0 ; add tape timing constant min byte
ASL ;.
TAX ; copy timing high byte
JSR STT1 ; set timing
INC DPSW ; increment tape dipole switch/byte received flag
LDA BITTS ; get tape read ready
BNE LAB_FA91 ;.
LDA SYNO ; get leader length
BEQ LAB_FAAA ; branch if no block
STA BITCI ; save tape long word marker
LDA #$00 ; clear .A
STA SYNO ; clear leader length
LDA #$C0 ; enable T1 interrupt
STA VIA2IER ; set VIA 2 IER
STA BITTS ; save tape read ready
LAB_FA91
LDA SYNO ; get leader length
STA NXTBIT ;.
BEQ LAB_FAA0 ;.
LDA #$00 ; clear .A
STA BITTS ; save tape read ready
LDA #$40 ; disable T1 interrupt
STA VIA2IER ; set VIA 2 IER
LAB_FAA0
LDA MYCH ; get tape read byte
STA ROPRTY ; save tape byte read
LDA BITCI ; get tape error flags
ORA RINONE ;.
STA RODATA ; save tape read errors
LAB_FAAA
JMP _RTI ; restore registers and exit interrupt
;***********************************************************************************;
;
;## store character
TPSTORE
JSR NEWCH ; new tape byte setup
STA DPSW ; clear tape dipole switch/byte received flag
LDX #$DA ; set timing max byte
JSR STT1 ; set timing
LDA FSBLK ; get tape copies remaining
BEQ LAB_FABD ; branch if all copies done
STA INBIT ; save tape read block count
LAB_FABD
LDA #$0F ; set block countdown bits
BIT RIDATA ; mask from tape input status
BPL LAB_FADA ; branch in first block has been loaded
LDA NXTBIT ;.
BNE LAB_FAD3 ;.
LDX FSBLK ; get tape copies remaining
DEX ; decrement copies remaining
BNE LAB_FAD7 ; if copies remaining restore registers and exit interrupt
LDA #$08 ; set long block bit
JSR ORIOST ; OR into I/O status byte
BNE LAB_FAD7 ; restore registers and exit interrupt, branch always
LAB_FAD3
LDA #$00 ; clear .A
STA RIDATA ; clear tape input status flags
LAB_FAD7
JMP _RTI ; restore registers and exit interrupt
LAB_FADA
BVS LAB_FB0D ; branch if valid data byte received
BNE LAB_FAF6 ; branch if block countdown bytes received
LDA NXTBIT ;.
BNE LAB_FAD7 ;.
LDA RODATA ; get tape read errors
BNE LAB_FAD7 ; if errors then exit interrupt
LDA INBIT ; get tape write leader count
LSR ; shift b0 into Cb
LDA ROPRTY ; get tape write byte
BMI LAB_FAF0 ; branch if b7 set
BCC LAB_FB07 ;.
CLC ;.
LAB_FAF0
BCS LAB_FB07 ;.
AND #$0F ; mask block countdown and first block flags
STA RIDATA ; clear tape input status flags
LAB_FAF6
DEC RIDATA ; decrement block countdown
BNE LAB_FAD7 ; exit if block countdown bytes received
LDA #$40 ; set valid block countdown
STA RIDATA ; set tape input status flags
JSR RD300 ; copy I/O start address to buffer address
LDA #$00 ; clear .A
STA RIPRTY ; clear tape read checksum
BEQ LAB_FAD7 ; exit interrupt always
;***********************************************************************************;
;
;## reset pointer
LAB_FB07
LDA #$80 ; set first block loaded
STA RIDATA ; save tape input status flags
BNE LAB_FAD7 ; restore registers and exit interrupt, branch always
LAB_FB0D
LDA NXTBIT ;.
BEQ LAB_FB1B ;.
LDA #$04 ; set short block bit
JSR ORIOST ; OR into I/O status byte
LDA #$00 ;.
JMP LAB_FB97 ;.
LAB_FB1B
JSR VPRTY ; check read/write pointer, return Cb = 1 if pointer >= end
BCC LAB_FB23 ; branch if not at end
JMP LAB_FB95 ;.
LAB_FB23
LDX INBIT ; get tape write leader count
DEX ; decrement count
BEQ LAB_FB55 ; branch if all blocks loaded
LDA VERCK ; get load/verify flag
BEQ LAB_FB38 ; branch if load
LDY #$00 ; clear index
LDA ROPRTY ;.get tape byte read
CMP (SAL),Y ; compare with byte in buffer
BEQ LAB_FB38 ; branch if equal
LDA #$01 ; set read error
STA RODATA ; save read errors
LAB_FB38
LDA RODATA ; get read errors
BEQ LAB_FB87 ; branch if no error
LDX #$3D ; maximum pass 1 errors
CPX PTR1 ; compare with tape pass 1 error index
BCC LAB_FB80 ; branch if space
LDX PTR1 ; get tape pass 1 error index
LDA SAL+1 ;.
STA STACK+1,X ; store in error log
LDA SAL ;.
STA STACK,X ; store in error log
INX ; increment index
INX ; increment index
STX PTR1 ; store in tape pass 1 error index
JMP LAB_FB87 ;.
LAB_FB55
LDX PTR2 ; get tape pass 2 error index
CPX PTR1 ; compare with tape pass 1 error index
BEQ LAB_FB90 ; branch if equal
LDA SAL ;.
CMP STACK,X ; compare with pass 1 error
BNE LAB_FB90 ; branch if not equal
LDA SAL+1 ;.
CMP STACK+1,X ; compare with pass 1 error
BNE LAB_FB90 ; branch if not equal
INC PTR2 ; increment tape pass 2 error index
INC PTR2 ; increment tape pass 2 error index
LDA VERCK ; get load/verify flag
BEQ LAB_FB7C ; branch if load
LDA ROPRTY ; get tape byte read
LDY #$00 ; clear index
CMP (SAL),Y ; compare with byte in buffer
BEQ LAB_FB90 ; branch if equal
INY ; increment read errors
STY RODATA ; save read errors
LAB_FB7C
LDA RODATA ; get read errors
BEQ LAB_FB87 ; branch if no error
LAB_FB80
LDA #$10 ; set read error bit
JSR ORIOST ; OR into I/O status byte
BNE LAB_FB90 ; branch always
LAB_FB87
LDA VERCK ; get load/verify flag
BNE LAB_FB90 ; branch if verify
TAY ; save load/verify flag
LDA ROPRTY ; get tape byte read
STA (SAL),Y ; save byte into buffer
LAB_FB90
JSR WRT62 ; increment read/write pointer
BNE LAB_FBCF ; restore registers and exit interrupt, branch always
LAB_FB95
LDA #$80 ; set first block loaded
LAB_FB97
STA RIDATA ; save tape input status flags
LDX FSBLK ; get tape copies remaining
DEX ; decrement copies remaining
BMI LAB_FBA0 ; branch if become -ve
STX FSBLK ; save copies remaining
LAB_FBA0
DEC INBIT ; decrement tape write leader count
BEQ LAB_FBAC ;.
LDA PTR1 ; get tape pass 1 error log
BNE LAB_FBCF ; if errors restore registers and exit interrupt
STA FSBLK ; save tape copies remaining
BEQ LAB_FBCF ; restore registers and exit interrupt, branch always
LAB_FBAC
JSR TNIF ; restore everything for STOP
JSR RD300 ; copy I/O start address to buffer address
LDY #$00 ; clear index
STY RIPRTY ; clear tape read checksum
LAB_FBB6
LDA (SAL),Y ; get byte from buffer
EOR RIPRTY ; XOR with read checksum
STA RIPRTY ; save new read checksum
JSR WRT62 ; increment read/write pointer
JSR VPRTY ; check read/write pointer, return Cb = 1 if pointer >= end
BCC LAB_FBB6 ; loop if not at end
LDA RIPRTY ; get computed checksum
EOR ROPRTY ; compare with tape write byte
BEQ LAB_FBCF ; if checksum ok restore registers and exit interrupt
LDA #$20 ; else set checksum error bit
JSR ORIOST ; OR into I/O status byte
LAB_FBCF
JMP _RTI ; restore registers and exit interrupt
;***********************************************************************************;
;
; copy I/O start address to buffer address
RD300
LDA STAL+1 ; get I/O start address high byte
STA SAL+1 ; set buffer address high byte
LDA STAL ; get I/O start address low byte
STA SAL ; set buffer address low byte
RTS
;***********************************************************************************;
;
; new tape byte setup
NEWCH
LDA #$08 ; eight bits to do
STA PCNTR ; set tape bit count
LDA #$00 ; clear .A
STA FIRT ; clear tape bit cycle phase
STA BITCI ; clear tape error flags
STA PRTY ; clear tape character parity
STA RINONE ; clear tape dipole count
RTS
;***********************************************************************************;
;
; send LSB from tape write byte to tape
; This routine tests the least significant bit in the tape write byte and sets VIA 2 T2
; depending on the state of the bit. If the bit is a 1 a time of $00B0 cycles is set, if
; the bit is a 0 a time of $0060 cycles is set. Note that this routine does not shift the
; bits of the tape write byte but uses a copy of that byte, the byte itself is shifted
; elsewhere.
TPTOGLE
LDA ROPRTY ; get tape write byte
LSR ; shift LSB into Cb
LDA #$60 ; set time constant low byte for bit = 0
BCC LAB_FBF3 ; branch if bit was 0
; set time constant for bit = 1 and toggle tape
LAB_FBF1
LDA #$B0 ; set time constant low byte for bit = 1
; write time constant and toggle tape
LAB_FBF3
LDX #$00 ; set time constant high byte
; write time constant and toggle tape
LAB_FBF5
STA VIA2T2CL ; set VIA 2 T2C_l
STX VIA2T2CH ; set VIA 2 T2C_h
LDA VIA2PB ; get VIA 2 DRB, keyboard column
EOR #$08 ; toggle tape out bit
STA VIA2PB ; set VIA 2 DRB
AND #$08 ; mask tape out bit
RTS
;***********************************************************************************;
;
; flag block done and exit interrupt
BLKEND
SEC ; set carry flag
ROR SAL+1 ; set buffer address high byte negative, flag all sync,
; data and checksum bytes written
BMI LAB_FC47 ; restore registers and exit interrupt, branch always
;***********************************************************************************;
;
; tape write, IRQ routine.
; This is the routine that writes the bits to the tape. It is called each time VIA 2 T2
; times out and checks if the start bit is done, if so checks if the data bits are done,
; if so it checks if the byte is done, if so it checks if the synchronisation bytes are
; done, if so it checks if the data bytes are done, if so it checks if the checksum byte
; is done, if so it checks if both the load and verify copies have been done, if so it
; stops the tape.
WRITE
LDA BITCI ; get tape long word marker
BNE LAB_FC21 ; if long word marker done go do rest of byte
; each byte sent starts with two half cycles of $0110 system clocks and the whole block
; ends with two more such half cycles
LDA #$10 ; set first start cycle time constant low byte
LDX #$01 ; set first start cycle time constant high byte
JSR LAB_FBF5 ; write time constant and toggle tape
BNE LAB_FC47 ; if first half cycle go restore registers and exit
; interrupt
INC BITCI ; set tape long word marker
LDA SAL+1 ; get buffer address high byte
BPL LAB_FC47 ; if block not complete go restore registers and exit
; interrupt. the end of a block is indicated by the tape
; buffer high byte b7 being set to 1
JMP WRTN1 ; else do tape routine, block complete exit
; Continue tape byte write. The first start cycle, both half cycles of it, is complete
; so the routine drops straight through to here.
LAB_FC21
LDA RINONE ; get tape medium word marker
BNE LAB_FC2E ; if word marker already written go send the byte bits
; After the two half cycles of $0110 system clocks the start bit is completed with two
; half cycles of $00B0 system clocks. This is the same as the first part of a 1 bit.
JSR LAB_FBF1 ; set time constant for bit = 1 and toggle tape
BNE LAB_FC47 ; if first half cycle go restore registers and exit
; interrupt
INC RINONE ; set tape medium word marker
BNE LAB_FC47 ; restore registers and exit interrupt, branch always
; Continue tape byte write. The start bit, both cycles of it, is complete so the routine
; drops straight through to here. Now the cycle pairs for each bit, and the parity bit,
; are sent.
LAB_FC2E
JSR TPTOGLE ; send LSB from tape write byte to tape
BNE LAB_FC47 ; if first half cycle go restore registers and exit
; interrupt
; else two half cycles have been done
LDA FIRT ; get tape bit cycle phase
EOR #$01 ; toggle b0
STA FIRT ; save tape bit cycle phase
BEQ LAB_FC4A ; if bit cycle phase complete go setup for next bit
; Each bit is written as two full cycles. A 1 is sent as a full cycle of $0160 system
; clocks then a full cycle of $00C0 system clocks. A 0 is sent as a full cycle of $00C0
; system clocks then a full cycle of $0160 system clocks. To do this each bit from the
; write byte is inverted during the second bit cycle phase. As the bit is inverted it
; is also added to the, one bit, parity count for this byte.
LDA ROPRTY ; get tape write byte
EOR #$01 ; invert bit being sent
STA ROPRTY ; save tape write byte
AND #$01 ; mask b0
EOR PRTY ; XOR with tape write byte parity bit
STA PRTY ; save tape write byte parity bit
LAB_FC47
JMP _RTI ; restore registers and exit interrupt
; the bit cycle phase is complete so shift out the just written bit and test for byte
; end
LAB_FC4A
LSR ROPRTY ; shift bit out of tape write byte
DEC PCNTR ; decrement tape bit count
LDA PCNTR ; get tape bit count
BEQ LAB_FC8C ; if all the data bits have been written go setup for
; sending the parity bit next and exit the interrupt
BPL LAB_FC47 ; if all the data bits are not yet sent just restore the
; registers and exit the interrupt
; do next tape byte
; The byte is complete. The start bit, data bits and parity bit have been written to
; the tape so setup for the next byte.
LAB_FC54
JSR NEWCH ; new tape byte setup
CLI ; enable interrupts
LDA CNTDN ; get tape synchronisation character count
BEQ LAB_FC6E ; if synchronisation characters done go do block data
; At the start of each block sent to tape there are a number of synchronisation bytes
; that count down to the actual data. The Commodore tape system saves two copies of all
; the tape data, the first is loaded and is indicated by the synchronisation bytes
; having b7 set, and the second copy is indicated by the synchronisation bytes having b7
; clear. the sequence goes $09, $08, ... $02, $01, data bytes.
LDX #$00 ; clear .X
STX ASCII ; clear checksum byte
DEC CNTDN ; decrement tape synchronisation byte count
LDX FSBLK ; get tape copies remaining
CPX #$02 ; compare with load block indicator
BNE LAB_FC6A ; branch if not the load block
ORA #$80 ; this is the load block so make the synchronisation count
; go $89, $88, ... $82, $81
LAB_FC6A
STA ROPRTY ; save the synchronisation byte as the tape write byte
BNE LAB_FC47 ; restore registers and exit interrupt, branch always
; the synchronisation bytes have been done so now check and do the actual block data
LAB_FC6E
JSR VPRTY ; check read/write pointer, return Cb = 1 if pointer >= end
BCC LAB_FC7D ; if not all done yet go get the byte to send
BNE BLKEND ; if pointer > end go flag block done and exit interrupt
; else the block is complete, it only remains to write the
; checksum byte to the tape so setup for that
INC SAL+1 ; increment buffer pointer high byte, this means the block
; done branch will always be taken next time without having
; to worry about the low byte wrapping to zero
LDA ASCII ; get checksum byte
STA ROPRTY ; save checksum as tape write byte
BCS LAB_FC47 ; restore registers and exit interrupt, branch always
; the block isn't finished so get the next byte to write to tape
LAB_FC7D
LDY #$00 ; clear index
LDA (SAL),Y ; get byte from buffer
STA ROPRTY ; save as tape write byte
EOR ASCII ; XOR with checksum byte
STA ASCII ; save new checksum byte
JSR WRT62 ; increment read/write pointer
BNE LAB_FC47 ; restore registers and exit interrupt, branch always
; set parity as next bit and exit interrupt
LAB_FC8C
LDA PRTY ; get tape write byte parity bit
EOR #$01 ; toggle it
STA ROPRTY ; save as tape write byte
LAB_FC92
JMP _RTI ; restore registers and exit interrupt
; tape routine, block complete exit
WRTN1
DEC FSBLK ; decrement tape copies remaining to read/write
BNE LAB_FC9C ; branch if more to do
JSR TNOFF ; else stop cassette motor
LAB_FC9C
LDA #$50 ; set tape write leader count
STA INBIT ; save tape write leader count
LDX #$08 ; set index for write tape leader vector
SEI ; disable interrupts
JSR LAB_FCFB ; set tape vector
BNE LAB_FC92 ; restore registers and exit interrupt, branch always
;***********************************************************************************;
;
; write tape leader IRQ routine
WRTZ
LDA #$78 ; set time constant low byte for bit = leader
JSR LAB_FBF3 ; write time constant and toggle tape
BNE LAB_FC92 ; if tape bit high restore registers and exit interrupt
DEC INBIT ; decrement tape write leader count
BNE LAB_FC92 ; if not all done restore registers and exit interrupt
JSR NEWCH ; new tape byte setup
DEC RIPRTY ; decrement tape leader count
BPL LAB_FC92 ; if not all done restore registers and exit interrupt
LDX #$0A ; set index for tape write vector
JSR LAB_FCFB ; set tape vector
CLI ; enable interrupts
INC RIPRTY ; clear clear leader counter, was $FF
LDA FSBLK ; get tape copies remaining
BEQ BSIV ; if all done restore everything for STOP and exit interrupt
JSR RD300 ; copy I/O start address to buffer address
LDX #$09 ; set nine synchronisation bytes
STX CNTDN ; save tape synchronisation byte count
BNE LAB_FC54 ; go do next tape byte, branch always
;***********************************************************************************;
;
; restore everything for STOP
TNIF
PHP ; save status
SEI ; disable interrupts
JSR TNOFF ; stop cassette motor
LDA #$7F ; disable all interrupts
STA VIA2IER ; set VIA 2 IER
LDA #$F7 ; set keyboard column 3 active
STA VIA2PB ; set VIA 2 DRB, keyboard column
LDA #$40 ; set T1 free run, T2 clock ø2,
; SR disabled, latches disabled
STA VIA2ACR ; set VIA 2 ACR
JSR LAB_FE39 ; set 60Hz and enable timer
LDA IRQTMP+1 ; get saved IRQ vector high byte
BEQ LAB_FCF4 ; branch if null
STA CINV+1 ; restore IRQ vector high byte
LDA IRQTMP ; get saved IRQ vector low byte
STA CINV ; restore IRQ vector low byte
LAB_FCF4
PLP ; restore status
RTS
;***********************************************************************************;
;
; reset vector
BSIV
JSR TNIF ; restore everything for STOP
BEQ LAB_FC92 ; restore registers and exit interrupt, branch always
;***********************************************************************************;
;
; set tape vector
LAB_FCFB
LDA IRQVCTRS-8,X ; get tape IRQ vector low byte
STA CINV ; set IRQ vector low byte
LDA IRQVCTRS-7,X ; get tape IRQ vector high byte
STA CINV+1 ; set IRQ vector high byte
RTS
;***********************************************************************************;
;
; stop cassette motor
TNOFF
LDA VIA1PCR ; get VIA 1 PCR
ORA #$0E ; set CA2 high, cassette motor off
STA VIA1PCR ; set VIA 1 PCR
RTS
;***********************************************************************************;
;
; check read/write pointer
; return Cb = 1 if pointer >= end
VPRTY
SEC ; set carry for subtract
LDA SAL ; get buffer address low byte
SBC EAL ; subtract buffer end low byte
LDA SAL+1 ; get buffer address high byte
SBC EAL+1 ; subtract buffer end high byte
RTS
;***********************************************************************************;
;
; increment read/write pointer
WRT62
INC SAL ; increment buffer address low byte
BNE LAB_FD21 ; if no overflow skip the high byte increment
INC SAL+1 ; increment buffer address high byte
LAB_FD21
RTS
;***********************************************************************************;
;
; RESET, hardware reset starts here
START
LDX #$FF ; set .X for stack
SEI ; disable interrupts
TXS ; clear stack
CLD ; clear decimal mode
JSR CHKAUTO ; scan for autostart ROM at $A000
BNE LAB_FD2F ; if not there continue VIC startup
JMP (XROMCOLD) ; call ROM start code
LAB_FD2F
JSR INITMEM ; initialise and test RAM
JSR FRESTOR ; restore default I/O vectors
JSR INITVIA ; initialise I/O registers
JSR INITSK ; initialise hardware
CLI ; enable interrupts
JMP (COLDST) ; execute BASIC
;***********************************************************************************;
;
; scan for autostart ROM at $A000, returns Zb=1 if ROM found
CHKAUTO
LDX #$05 ; five characters to test
LAB_FD41
LDA A0CBM-1,X ; get test character
CMP XROMID-1,X ; compare with byte in ROM space
BNE LAB_FD4C ; exit if no match
DEX ; decrement index
BNE LAB_FD41 ; loop if not all done
LAB_FD4C
RTS
;***********************************************************************************;
;
; autostart ROM signature
A0CBM
.byte "A0",$C3,$C2,$CD ; A0CBM
;***********************************************************************************;
;
; restore default I/O vectors
; This routine restores the default values of all system vectors used in KERNAL and
; BASIC routines and interrupts. The KERNAL VECTOR routine is used to read and alter
; individual system vectors.
FRESTOR
LDX #<VECTORS ; pointer to vector table low byte
LDY #>VECTORS ; pointer to vector table high byte
CLC ; flag set vectors
;***********************************************************************************;
;
; set/read vectored I/O from (.X.Y), Cb = 1 to read, Cb = 0 to set
; This routine manages all system vector jump addresses stored in RAM. Calling this
; routine with the accumulator carry bit set will store the current contents of the
; RAM vectors in a list pointed to by the .X and .Y registers.
; When this routine is called with the carry bit clear, the user list pointed to by
; the .X and .Y registers is transferred to the system RAM vectors.
; NOTE: This routine requires caution in its use. The best way to use it is to first
; read the entire vector contents into the user area, alter the desired vectors, and
; then copy the contents back to the system vectors.
FVECTOR
STX MEMUSS ; save pointer low byte
STY MEMUSS+1 ; save pointer high byte
LDY #$1F ; set byte count
LAB_FD5D
LDA CINV,Y ; read vector byte from vectors
BCS LAB_FD64 ; if read vectors skip the read from .X.Y
LDA (MEMUSS),Y ; read vector byte from (.X.Y)
LAB_FD64
STA (MEMUSS),Y ; save byte to (.X.Y)
STA CINV,Y ; save byte to vector
DEY ; decrement index
BPL LAB_FD5D ; loop if more to do
RTS
; The above code works but it tries to write to the ROM. While this is usually harmless
; systems that use flash ROM may suffer. Here is a version that makes the extra write
; to RAM instead but is otherwise identical in function.
;
; set/read vectored I/O from (.X.Y), Cb = 1 to read, Cb = 0 to set
;
;FVECTOR
; STX MEMUSS ; save pointer low byte
; STY MEMUSS+1 ; save pointer high byte
; LDY #$1F ; set byte count
;LAB_FD5D
; LDA (MEMUSS),Y ; read vector byte from (.X.Y)
; BCC LAB_FD66 ; if set vectors skip the read from .X.Y
;
; LDA CINV,Y ; else read vector byte from vectors
; STA (MEMUSS),Y ; save byte to (.X.Y)
;LAB_FD66
; STA CINV,Y ; save byte to vector
; DEY ; decrement index
; BPL LAB_FD5D ; loop if more to do
;
; RTS
;***********************************************************************************;
;
; KERNAL vectors
VECTORS
.word IRQ ; CINV IRQ vector
.word BREAK ; CBINV BRK vector
.word NMI2 ; NMINV NMI vector
.word FOPEN ; IOPEN open a logical file
.word FCLOSE ; ICLOSE close a specified logical file
.word FCHKIN ; ICHKIN open channel for input
.word FCHKOUT ; ICKOUT open channel for output
.word FCLRCHN ; ICLRCN close input and output channels
.word FCHRIN ; IBASIN input character from channel
.word FCHROUT ; IBSOUT output character to channel
.word FSTOP ; ISTOP scan stop key
.word FGETIN ; IGETIN get character from keyboard queue
.word FCLALL ; ICLALL close all channels and files
.word BREAK ; USRCMD user function
; Vector to user defined command, currently points to BRK.
; This appears to be a holdover from PET days, when the built-in machine language monitor
; would jump through the $032E vector when it encountered a command that it did not
; understand, allowing the user to add new commands to the monitor.
; Although this vector is initialised to point to the routine called by STOP/RESTORE and
; the BRK interrupt, and is updated by the KERNAL vector routine at $FD57, it no longer
; has any function.
.word FLOAD2 ; ILOAD load
.word FSAVE2 ; ISAVE save
;***********************************************************************************;
;
; Initialise and test RAM, the RAM from $000 to $03FF is never tested and is just assumed
; to work. First a search is done from $0401 for the start of memory and this is saved, if
; this start is at or beyond $1100 then the routine dead ends. Once the start of memory is
; found the routine looks for the end of memory, if this end is before $2000 the routine
; again dead ends. Lastly, if the end of memory is at $2000 then the screen is set to
; $1E00, but if the memory extends to or beyond $2100 then the screen is moved to $1000.
INITMEM
LDA #$00 ; clear .A
TAX ; clear index
LAB_FD90
STA USRPPOK,X ; clear page 0
STA BUF,X ; clear page 2
STA IERROR,X ; clear page 3
INX ; increment index
BNE LAB_FD90 ; loop if more to do
LDX #<TBUFFR ; set cassette buffer pointer low byte
LDY #>TBUFFR ; set cassette buffer pointer high byte
STX TAPE1 ; save tape buffer start pointer low byte
STY TAPE1+1 ; save tape buffer start pointer high byte
STA STAL ; clear RAM test pointer low byte
STA XSAV ; clear looking for end flag
STA MEMSTR ; clear OS start of memory low byte
TAY ; clear .Y
LDA #$04 ; set RAM test pointer high byte
STA STAL+1 ; save RAM test pointer high byte
LAB_FDAF
INC STAL ; increment RAM test pointer low byte
BNE LAB_FDB5 ; if no rollover skip the high byte increment
INC STAL+1 ; increment RAM test pointer high byte
LAB_FDB5
JSR TSTMEM ; test RAM byte, return Cb=0 if failed
LDA XSAV ; test looking for end flag
BEQ LAB_FDDE ; branch if not looking for end
; else now looking for the end of memory
BCS LAB_FDAF ; loop if byte test passed
LDY STAL+1 ; get test address high byte
LDX STAL ; get test address low byte
CPY #$20 ; compare with $2000, RAM should always end at or after
; $2000 even with no expansion memory as the built in RAM
; ends at $1FFF. therefore the following test should
; never branch
BCC LAB_FDEB ; if end address < $2000 go do dead end loop
CPY #$21 ; compare with $2100
BCS LAB_FDD2 ; branch if >= $2100
; else memory ended before $2100
LDY #$1E ; set screen memory page to $1E00
STY HIBASE ; save screen memory page
LAB_FDCF
JMP LAB_FE7B ; set the top of memory and return
; memory ends beyond $2100
LAB_FDD2
LDA #$12 ; set OS start of memory high byte
STA MEMSTR+1 ; save OS start of memory high byte
LDA #$10 ; set screen memory page to $1000
STA HIBASE ; save screen memory page
BNE LAB_FDCF ; set the top of memory and return, branch always
LAB_FDDE
BCC LAB_FDAF ; loop if byte test failed, not found start yet
; else found start of RAM
LDA STAL+1 ; get test address high byte
STA MEMSTR+1 ; save OS start of memory high byte
STA XSAV ; set looking for end flag
CMP #$11 ; compare start with $1100, RAM should always start before
; $1100 even with no expansion memory as the built in RAM
; starts at $1000. therefore the following test should
; always branch
BCC LAB_FDAF ; go find end of RAM, branch always
; if the code drops through here then the RAM has failed
; and there is not much else to be done
LAB_FDEB
JSR INITVIC ; initialise VIC chip
JMP LAB_FDEB ; loop forever
;***********************************************************************************;
;
; tape IRQ vectors
IRQVCTRS
.word WRTZ ; $08 write tape leader IRQ routine
.word WRITE ; $0A tape write IRQ routine
.word IRQ ; $0C normal IRQ vector
.word READT ; $0E read tape bits IRQ routine
;***********************************************************************************;
;
; initialise I/O registers
INITVIA
LDA #$7F ; disable all interrupts
STA VIA1IER ; on VIA 1 IER ..
STA VIA2IER ; .. and VIA 2 IER
LDA #$40 ; set T1 free run, T2 clock ø2,
; SR disabled, latches disabled
STA VIA2ACR ; set VIA 2 ACR
LDA #$40 ; set T1 free run, T2 clock ø2,
; SR disabled, latches disabled
STA VIA1ACR ; set VIA 1 ACR
LDA #$FE ; CB2 high, RS-232 Tx
; CB1 +ve edge,
; CA2 high, tape motor off
; CA1 -ve edge
STA VIA1PCR ; set VIA 1 PCR
LDA #$DE ; CB2 low, serial data out high
; CB1 +ve edge,
; CA2 high, serial clock out low
; CA1 -ve edge
STA VIA2PCR ; set VIA 2 PCR
LDX #$00 ; all inputs, RS-232 interface or parallel user port
STX VIA1DDRB ; set VIA 1 DDRB
LDX #$FF ; all outputs, keyboard column
STX VIA2DDRB ; set VIA 2 DDRB
LDX #$00 ; all inputs, keyboard row
STX VIA2DDRA ; set VIA 2 DDRA
LDX #$80 ; OIII IIII, ATN out, light pen, joystick, serial data
; in, serial clk in
STX VIA1DDRA ; set VIA 1 DDRA
LDX #$00 ; ATN out low, set ATN high
STX VIA1PA2 ; set VIA 1 DRA, no handshake
JSR SRCLKHI ; set serial clock high
LDA #$82 ; enable CA1 interrupt, [RESTORE] key
STA VIA1IER ; set VIA 1 IER
JSR SRCLKLO ; set serial clock low
;***********************************************************************************;
;
; set 60Hz and enable timer
LAB_FE39
LDA #$C0 ; enable T1 interrupt
STA VIA2IER ; set VIA 2 IER
LDA #$26 ; set timer constant low byte [PAL]
; LDA #$89 ; set timer constant low byte [NTSC]
STA VIA2T1CL ; set VIA 2 T1C_l
LDA #$48 ; set timer constant high byte [PAL]
; LDA #$42 ; set timer constant high byte [NTSC]
STA VIA2T1CH ; set VIA 2 T1C_h
RTS
;***********************************************************************************;
;
; set filename
; This routine is used to set up the file name for the OPEN, SAVE, or LOAD routines.
; The accumulator must be loaded with the length of the file and .X.Y with the pointer
; to file name, .X being the low byte. The address can be any valid memory address in
; the system where a string of characters for the file name is stored. If no file
; name desired the accumulator must be set to 0, representing a zero file length,
; in that case .X.Y may be set to any memory address.
FSETNAM
STA FNLEN ; set file name length
STX FNADR ; set file name pointer low byte
STY FNADR+1 ; set file name pointer high byte
RTS
;***********************************************************************************;
;
; set logical file, first and second addresses
; This routine will set the logical file number, device address, and secondary
; address, command number, for other KERNAL routines.
; The logical file number is used by the system as a key to the file table created
; by the OPEN file routine. Device addresses can range from 0 to 30. The following
; codes are used by the computer to stand for the following devices:
; ADDRESS DEVICE
; ======= ======
; 0 Keyboard
; 1 Cassette
; 2 RS-232
; 3 CRT display
; 4 Serial bus printer
; 8 Serial bus disk drive
; device numbers of four or greater automatically refer to devices on the serial
; bus.
; A command to the device is sent as a secondary address on the serial bus after
; the device number is sent during the serial attention handshaking sequence. If
; no secondary address is to be sent .Y should be set to $FF.
FSETLFS
STA LA ; set logical file
STX FA ; set device number
STY SA ; set secondary address or command
RTS
;***********************************************************************************;
;
; read I/O status word
; This routine returns the current status of the I/O device in the accumulator. The
; routine is usually called after new communication to an I/O device. The routine
; will give information about device status, or errors that have occurred during the
; I/O operation.
FREADST
LDA FA ; get device number
CMP #$02 ; compare device with RS-232 device
BNE READIOST ; branch if not RS-232 device
; get RS-232 device status
LDA RSSTAT ; read RS-232 status word
LDA #$00 ; clear .A
STA RSSTAT ; clear RS-232 status
; The above code is wrong. The RS-232 status is in .A but .A is cleared and that is used
; to clear the RS-232 status byte. So whatever the status the result is always $00 and
; the status byte is always cleared. The C64 code saves the status byte to the stack
; before clearing it ..
;
; PHA ; save RS-232 status
; LDA #$00 ; clear .A
; STA RSSTAT ; clear RS-232 status
; PLA ; restore RS-232 status
RTS
;***********************************************************************************;
;
; control KERNAL messages
; This routine controls the printing of error and control messages by the KERNAL.
; Either print error messages or print control messages can be selected by setting
; the accumulator when the routine is called.
; FILE NOT FOUND is an example of an error message. PRESS PLAY ON CASSETTE is an
; example of a control message.
; Bits 6 and 7 of this value determine where the message will come from. If bit 7
; is set one of the error messages from the KERNAL will be printed. If bit 6 is set
; a control message will be printed.
FSETMSG
STA MSGFLG ; set KERNAL message mode flag
READIOST
LDA STATUS ; read I/O status byte
; OR into I/O status byte
ORIOST
ORA STATUS ; OR with I/O status byte
STA STATUS ; save I/O status byte
RTS
;***********************************************************************************;
;
; set timeout on IEEE-488 bus
; This routine sets the timeout flag for the serial bus. When the timeout flag is
; set, the computer will wait for a device on the serial bus for 64 milliseconds.
; If the device does not respond to the computer's DAV signal within that time the
; computer will recognize an error condition and leave the handshake sequence. When
; this routine is called and the accumulator contains a 0 in bit 7, timeouts are
; enabled. A 1 in bit 7 will disable the timeouts.
; NOTE: The timeout feature is used to communicate that a disk file is not found on
; an attempt to OPEN a file.
FSETTMO
STA TIMOUT ; save serial bus timeout flag
RTS
;***********************************************************************************;
;
; read/set the top of memory, Cb = 1 to read, Cb = 0 to set
; This routine is used to read and set the top of RAM. When this routine is called
; with the carry bit set the pointer to the top of RAM will be loaded into .X.Y. When
; this routine is called with the carry bit clear .X.Y will be saved as the top of
; memory pointer changing the top of memory.
FMEMTOP
BCC LAB_FE7B ; if Cb clear go set the top of memory
; read the top of memory
LAB_FE75
LDX MEMHIGH ; get memory top low byte
LDY MEMHIGH+1 ; get memory top high byte
; set the top of memory
LAB_FE7B
STX MEMHIGH ; set memory top low byte
STY MEMHIGH+1 ; set memory top high byte
RTS
;***********************************************************************************;
;
; read/set the bottom of memory, Cb = 1 to read, Cb = 0 to set
; This routine is used to read and set the bottom of RAM. When this routine is
; called with the carry bit set the pointer to the bottom of RAM will be loaded
; into .X.Y. When this routine is called with the carry bit clear .X.Y will be saved as
; the bottom of memory pointer changing the bottom of memory.
FMEMBOT
BCC LAB_FE8A ; if Cb clear go set the bottom of memory
; read the bottom of memory
LDX MEMSTR ; read OS start of memory low byte
LDY MEMSTR+1 ; read OS start of memory high byte
; set the bottom of memory
LAB_FE8A
STX MEMSTR ; set OS start of memory low byte
STY MEMSTR+1 ; set OS start of memory high byte
RTS
;***********************************************************************************;
;
; non-destructive test RAM byte, return Cb=0 if failed
TSTMEM
LDA (STAL),Y ; get existing RAM byte
TAX ; copy to .X
LDA #$55 ; set first test byte
STA (STAL),Y ; save to RAM
CMP (STAL),Y ; compare with saved
BNE LAB_FEA4 ; branch if fail
ROR ; make byte $AA, carry is set here
STA (STAL),Y ; save to RAM
CMP (STAL),Y ; compare with saved
BNE LAB_FEA4 ; branch if fail
.byte $A9 ; makes next line LDA #$18
LAB_FEA4
CLC ; flag test failed
TXA ; get original byte back
STA (STAL),Y ; restore original byte
RTS
;***********************************************************************************;
;
; NMI vector
NMI
SEI ; disable interrupts
JMP (NMINV) ; do NMI vector
;***********************************************************************************;
;
; NMI handler
NMI2
PHA ; save .A
TXA ; copy .X
PHA ; save .X
TYA ; copy .Y
PHA ; save .Y
LDA VIA1IFR ; get VIA 1 IFR
BPL LAB_FEFF ; if no interrupt restore registers and exit
AND VIA1IER ; AND with VIA 1 IER
TAX ; copy to .X
AND #$02 ; mask CA1 interrupt, [RESTORE] key
BEQ RSNMI ; branch if not [RESTORE] key
; This code does not properly handle other bits of the IFR being set.
; When neither an autostart ROM nor the [STOP] key are down the CA1 interrupt
; bit will clear but any others will remain set. Because the NMI interrupt is
; edge-triggered no further interrupts will be triggered and the other VIA
; events will never be processed.
; else was [RESTORE] key ..
JSR CHKAUTO ; scan for autostart ROM at $A000
BNE LAB_FEC7 ; branch if no autostart ROM
JMP (XROMWARM) ; else do autostart ROM break entry
LAB_FEC7
BIT VIA1PA1 ; test VIA 1 DRA, clear CA1 interrupt
JSR FUDTIM ; increment the real time clock
JSR STOP ; scan stop key
BNE LAB_FEFF ; if not [STOP] restore registers and exit interrupt
;***********************************************************************************;
;
; BRK handler
BREAK
JSR FRESTOR ; restore default I/O vectors
JSR INITVIA ; initialise I/O registers
JSR INITSK ; initialise hardware
JMP (WARMST) ; do BASIC break entry
;***********************************************************************************;
;
; RS-232 NMI routine
;
; This code only processes the bits set in the copy of the ISR taken by the caller. If more
; bits become set while the ISR is executing they will be delayed until another interrupt can
; be delivered to the CPU. This would add at least 60 cycles of latency.
;
; It also only processes a single interrupt source in the following order of priority:
; 1. Tx timer
; 2. Rx timer
; 3. Rx data start
; If multiple bits are set in the ISR multiple interrupts must be delivered to the CPU to process
; them.
RSNMI
LDA VIA1IER ; get VIA 1 IER
ORA #$80 ; set enable bit, this bit should be set according to the
; Rockwell 6522 datasheet but clear according to the MOS
; datasheet. best to assume it's not in the state required
; and set it so
PHA ; save to re-enable interrupts
LDA #$7F ; disable all interrupts
STA VIA1IER ; set VIA 1 IER
TXA ; get active interrupts back
AND #$40 ; mask T1 interrupt
BEQ LAB_FF02 ; branch if not T1 interrupt
; was VIA 1 T1 interrupt, Tx timer expired
LDA #$CE ; CB2 low, CB1 -ve edge, CA2 high, CA1 -ve edge
ORA NXTBIT ; OR RS-232 next bit to send, sets CB2 high if set
STA VIA1PCR ; set VIA 1 PCR
LDA VIA1T1CL ; get VIA 1 T1C_l, clear T1 interrupt
PLA ; restore interrupt enable byte to restore previously
; enabled interrupts
STA VIA1IER ; set VIA 1 IER
JSR RSNXTBIT ; RS-232 Tx NMI routine
LAB_FEFF
JMP _RTI ; restore registers and exit interrupt
; was not VIA 1 T1 interrupt
LAB_FF02
TXA ; get active interrupts back
AND #$20 ; mask T2 interrupt
BEQ LAB_FF2C ; branch if not T2 interrupt
; was VIA 1 T2 interrupt, Rx timer expired
; The timer has wrapped and is counting down from $FFFF, no further interrupts
; will be generated until the latch is written to. Adding the baud rate bit time
; to the current value will result in another interrupt at the right interval.
LDA VIA1PB ; get VIA 1 DRB
AND #$01 ; mask RS-232 data in
STA INBIT ; save RS-232 input bit
LDA VIA1T2CL ; get VIA 1 T2C_l, clear T2 interrupt
SBC #$16 ; adjust by 22 cycles to cover time taken by the
; six instructions needed to write to the latch
ADC BAUDOF ; add baud rate bit time low byte
STA VIA1T2CL ; set VIA 1 T2C_l
LDA VIA1T2CH ; get VIA 1 T2C_h
ADC BAUDOF+1 ; add baud rate bit time high byte
STA VIA1T2CH ; set VIA 1 T2C_h
PLA ; restore interrupt enable byte to restore previously
; enabled interrupts
STA VIA1IER ; set VIA 1 IER, restore interrupts
JSR RSINBIT ; RS-232 Rx
JMP _RTI ; restore registers and exit interrupt
; was not VIA 1 T2 interrupt
LAB_FF2C
TXA ; get active interrupts back
AND #$10 ; mask CB1 interrupt
BEQ _RTI ; if no bit restore registers and exit interrupt
; was VIA 1 CB1 interrupt, Rx data bit transition
LDA M51CTR ; get pseudo 6551 control register
AND #$0F ; mask 0000 xxxx, baud rate
BNE LAB_FF38 ; short delay. was this intended to skip code used to
; implement the user baud rate ??
LAB_FF38
ASL ; *2, 2 bytes per baud count
TAX ; copy to index
LDA BAUDTBL-2,X ; get baud count low byte
STA VIA1T2CL ; set VIA 1 T2C_l
LDA BAUDTBL-1,X ; get baud count high byte
STA VIA1T2CH ; set VIA 1 T2C_h
LDA VIA1PB ; read VIA 1 DRB, clear interrupt flag
PLA ; restore interrupt enable byte to restore previously
; enabled interrupts
ORA #$20 ; enable T2 interrupt
AND #$EF ; disable CB1 interrupt, Rx data bit transition
STA VIA1IER ; set VIA 1 IER
LDX BITNUM ; get number of bits to be sent/received
STX BITCI ; save RS-232 input bit count
;***********************************************************************************;
;
; restore the registers and exit the interrupt
;
; If you write your own interrupt code you should either return from the interrupt
; using code that ends up here or code that replicates this code.
_RTI
PLA ; pull .Y
TAY ; restore .Y
PLA ; pull .X
TAX ; restore .X
PLA ; restore .A
RTI
;***********************************************************************************;
;
; baud rate word is calculated from ..
;
; (system clock / baud rate) / 2 - 100
;
; system clock
; ------------
; PAL 1108404 Hz
; NTSC 1022727 Hz
; baud rate tables for PAL VIC 20
BAUDTBL
.word $2AE6 ; 50 baud
.word $1C78 ; 75 baud
.word $1349 ; 110 baud
.word $0FB1 ; 134.5 baud
.word $0E0A ; 150 baud
.word $06D3 ; 300 baud
.word $0338 ; 600 baud
.word $016A ; 1200 baud
.word $00D0 ; 1800 baud
.word $0083 ; 2400 baud
.word $0036 ; 3600 baud
; baud rate tables for NTSC VIC 20
; .word $2792 ; 50 baud
; .word $1A40 ; 75 baud
; .word $11C6 ; 110 baud
; .word $0E74 ; 134.5 baud
; .word $0CEE ; 150 baud
; .word $0645 ; 300 baud
; .word $02F1 ; 600 baud
; .word $0146 ; 1200 baud
; .word $00B8 ; 1800 baud
; .word $0071 ; 2400 baud
; .word $002A ; 3600 baud
;***********************************************************************************;
;
; IRQ vector
IRQROUT
PHA ; save .A
TXA ; copy .X
PHA ; save .X
TYA ; copy .Y
PHA ; save .Y
TSX ; copy stack pointer
LDA STACK+4,X ; get the stacked status register
AND #$10 ; mask the BRK flag bit
BEQ LAB_FF82 ; if not BRK go do the hardware IRQ vector
JMP (CBINV) ; else do the BRK vector (iBRK)
LAB_FF82
JMP (CINV) ; do IRQ vector (iIRQ)
;***********************************************************************************;
;
; spare bytes, not referenced
.byte $FF,$FF,$FF,$FF,$FF
;***********************************************************************************;
;
; restore default I/O vectors
; This routine restores the default values of all system vectors used in KERNAL and
; BASIC routines and interrupts. The KERNAL VECTOR routine is used to read and alter
; individual system vectors.
RESTOR
JMP FRESTOR ; restore default I/O vectors
;***********************************************************************************;
;
; read/set vectored I/O
; This routine manages all system vector jump addresses stored in RAM. Calling this
; routine with the accumulator carry bit set will store the current contents of the
; RAM vectors in a list pointed to by the .X and .Y registers.
; When this routine is called with the carry bit clear, the user list pointed to by
; the .X and .Y registers is transferred to the system RAM vectors.
; NOTE: This routine requires caution in its use. The best way to use it is to first
; read the entire vector contents into the user area, alter the desired vectors, and
; then copy the contents back to the system vectors.
VECTOR
JMP FVECTOR ; set/read vectored I/O from (.X.Y)
;***********************************************************************************;
;
; control KERNAL messages
; This routine controls the printing of error and control messages by the KERNAL.
; Either print error messages or print control messages can be selected by setting
; the accumulator when the routine is called.
; FILE NOT FOUND is an example of an error message. PRESS PLAY ON CASSETTE is an
; example of a control message.
; Bits 6 and 7 of this value determine where the message will come from. If bit 7
; is set one of the error messages from the KERNAL will be printed. If bit 6 is set
; a control message will be printed.
SETMSG
JMP FSETMSG ; control KERNAL messages
;***********************************************************************************;
;
; send secondary address after LISTEN
; This routine is used to send a secondary address to an I/O device after a call to
; the LISTEN routine is made and the device commanded to LISTEN. The routine cannot
; be used to send a secondary address after a call to the TALK routine.
; A secondary address is usually used to give set-up information to a device before
; I/O operations begin.
; When a secondary address is to be sent to a device on the serial bus the address
; must first be ORed with $60.
SECOND
JMP FSECOND ; send secondary address after LISTEN
;***********************************************************************************;
;
; send secondary address after TALK
; This routine transmits a secondary address on the serial bus for a TALK device.
; This routine must be called with a number between 4 and 30 in the accumulator.
; The routine will send this number as a secondary address command over the serial
; bus. This routine can only be called after a call to the TALK routine. It will
; not work after a LISTEN.
TKSA
JMP FTKSA ; send secondary address after TALK
;***********************************************************************************;
;
; read/set the top of memory
; This routine is used to read and set the top of RAM. When this routine is called
; with the carry bit set the pointer to the top of RAM will be loaded into .X.Y. When
; this routine is called with the carry bit clear .X.Y will be saved as the top of
; memory pointer changing the top of memory.
MEMTOP
JMP FMEMTOP ; read/set the top of memory
;***********************************************************************************;
;
; read/set the bottom of memory
; This routine is used to read and set the bottom of RAM. When this routine is
; called with the carry bit set the pointer to the bottom of RAM will be loaded
; into .X.Y. When this routine is called with the carry bit clear .X.Y will be saved
; as the bottom of memory pointer changing the bottom of memory.
MEMBOT
JMP FMEMBOT ; read/set the bottom of memory
;***********************************************************************************;
;
; scan the keyboard
; This routine will scan the keyboard and check for pressed keys. It is the same
; routine called by the interrupt handler. If a key is down, its ASCII value is
; placed in the keyboard queue.
SCNKEY
JMP FSCNKEY ; scan keyboard
;***********************************************************************************;
;
; set timeout on IEEE-488 bus
; This routine sets the timeout flag for the serial bus. When the timeout flag is
; set, the computer will wait for a device on the serial bus for 64 milliseconds.
; If the device does not respond to the computer's DAV signal within that time the
; computer will recognize an error condition and leave the handshake sequence. When
; this routine is called and the accumulator contains a 0 in bit 7, timeouts are
; enabled. A 1 in bit 7 will disable the timeouts.
; NOTE: The timeout feature is used to communicate that a disk file is not found on
; an attempt to OPEN a file.
SETTMO
JMP FSETTMO ; set timeout on serial bus
;************************************************************************************
;
; input a byte from the serial bus
; This routine reads a byte of data from the serial bus using full handshaking. The
; data is returned in the accumulator. Before using this routine the TALK routine
; must have been called first to command the device on the serial bus to send data on
; the bus. If the input device needs a secondary command it must be sent by using the
; TKSA routine before calling this routine.
; Errors are returned in the status word which can be read by calling the READST
; routine.
ACPTR
JMP FACPTR ; input byte from serial bus
;************************************************************************************
;
; output a byte to the serial bus
; This routine is used to send information to devices on the serial bus. A call to
; this routine will put a data byte onto the serial bus using full handshaking.
; Before this routine is called the LISTEN routine must be used to command a device
; on the serial bus to get ready to receive data.
; The accumulator is loaded with a byte to output as data on the serial bus. A
; device must be listening or the status word will return a timeout. This routine
; always buffers one character. So when a call to the UNLSN routine is made to end
; the data transmission, the buffered character is sent with EOI set. The UNLISTEN
; command is sent to the device.
CIOUT
JMP FCIOUT ; output a byte to the serial bus
;***********************************************************************************;
;
; command the serial bus to UNTALK
; This routine will transmit an UNTALK command on the serial bus. All devices
; previously set to TALK will stop sending data when this command is received.
UNTLK
JMP FUNTLK ; command the serial bus to UNTALK
;***********************************************************************************;
;
; command the serial bus to UNLISTEN
; This routine commands all devices on the serial bus to stop receiving data from
; the computer. Calling this routine results in an UNLISTEN command being transmitted
; on the serial bus. Only devices previously commanded to listen will be affected.
; This routine is normally used after the computer is finished sending data to
; external devices. Sending the UNLISTEN will command the listening devices to get
; off the serial bus so it can be used for other purposes.
UNLSN
JMP FUNLSN ; command the serial bus to UNLISTEN
;***********************************************************************************;
;
; command devices on the serial bus to LISTEN
; This routine will command a device on the serial bus to receive data. The
; accumulator must be loaded with a device number between 4 and 30 before calling
; this routine. LISTEN convert this to a listen address then transmit this data as
; a command on the serial bus. The specified device will then go into listen mode
; and be ready to accept information.
LISTEN
JMP FLISTEN ; command devices on the serial bus to LISTEN
;***********************************************************************************;
;
; command a serial bus device to TALK
; To use this routine the accumulator must first be loaded with a device number
; between 4 and 30. When called this routine converts this device number to a talk
; address. Then this data is transmitted as a command on the serial bus.
TALK
JMP FTALK ; command serial bus device to TALK
;***********************************************************************************;
;
; read I/O status word
; This routine returns the current status of the I/O device in the accumulator. The
; routine is usually called after new communication to an I/O device. The routine
; will give information about device status, or errors that have occurred during the
; I/O operation.
READST
JMP FREADST ; read I/O status word
;***********************************************************************************;
;
; set logical, first and second addresses
; This routine will set the logical file number, device address, and secondary
; address, command number, for other KERNAL routines.
; The logical file number is used by the system as a key to the file table created
; by the OPEN file routine. Device addresses can range from 0 to 30. The following
; codes are used by the computer to stand for the following devices:
; ADDRESS DEVICE
; ======= ======
; 0 Keyboard
; 1 Cassette
; 2 RS-232
; 3 CRT display
; 4 Serial bus printer
; 8 Serial bus disk drive
; device numbers of four or greater automatically refer to devices on the serial
; bus.
; A command to the device is sent as a secondary address on the serial bus after
; the device number is sent during the serial attention handshaking sequence. If
; no secondary address is to be sent .Y should be set to $FF.
SETLFS
JMP FSETLFS ; set logical, first and second addresses
;***********************************************************************************;
;
; set the filename
; This routine is used to set up the file name for the OPEN, SAVE, or LOAD routines.
; The accumulator must be loaded with the length of the file and .X.Y with the pointer
; to file name, .X being the low byte. The address can be any valid memory address in
; the system where a string of characters for the file name is stored. If no file
; name desired the accumulator must be set to 0, representing a zero file length,
; in that case .X.Y may be set to any memory address.
SETNAM
JMP FSETNAM ; set filename
;***********************************************************************************;
;
; open a logical file
; This routine is used to open a logical file. Once the logical file is set up it
; can be used for input/output operations. Most of the I/O KERNAL routines call on
; this routine to create the logical files to operate on. No arguments need to be
; set up to use this routine, but both the SETLFS and SETNAM KERNAL routines must
; be called before using this routine.
OPEN
JMP (IOPEN) ; do open file vector
;***********************************************************************************;
;
; close a specified logical file
; This routine is used to close a logical file after all I/O operations have been
; completed on that file. This routine is called after the accumulator is loaded
; with the logical file number to be closed, the same number used when the file was
; opened using the OPEN routine.
CLOSE
JMP (ICLOSE) ; do close file vector
;************************************************************************************
;
; open a channel for input
; Any logical file that has already been opened by the OPEN routine can be defined as
; an input channel by this routine. the device on the channel must be an input device
; or an error will occur and the routine will abort.
; If you are getting data from anywhere other than the keyboard, this routine must be
; called before using either the CHRIN routine or the GETIN routine. If you are
; getting data from the keyboard and no other input channels are open then the calls to
; this routine and to the OPEN routine are not needed.
; When used with a device on the serial bus this routine will automatically send the
; listen address specified by the OPEN routine and any secondary address.
; Possible errors are:
;
; 3 : file not open
; 5 : device not present
; 6 : file is not an input file
CHKIN
JMP (ICHKIN) ; do open for input vector
;************************************************************************************
;
; open a channel for output
; Any logical file that has already been opened by the OPEN routine can be defined as
; an output channel by this routine the device on the channel must be an output device
; or an error will occur and the routine will abort.
; If you are sending data to anywhere other than the screen this routine must be
; called before using the CHROUT routine. if you are sending data to the screen and no
; other output channels are open then the calls to this routine and to the OPEN routine
; are not needed.
; When used with a device on the serial bus this routine will automatically send the
; listen address specified by the OPEN routine and any secondary address.
; Possible errors are:
;
; 3 : file not open
; 5 : device not present
; 7 : file is not an output file
CHKOUT
JMP (ICKOUT) ; do open for output vector
;************************************************************************************
;
; close input and output channels
; This routine is called to clear all open channels and restore the I/O channels to
; their original default values. It is usually called after opening other I/O
; channels and using them for input/output operations. The default input device is
; 0, the keyboard. The default output device is 3, the screen.
; If one of the channels to be closed is to the serial bus, an UNTALK signal is sent
; first to clear the input channel or an UNLISTEN is sent to clear the output channel.
; By not calling this routine and leaving listener(s) active on the serial bus,
; several devices can receive the same data from the VIC at the same time. One way to
; take advantage of this would be to command the printer to LISTEN and the disk to
; TALK. This would allow direct printing of a disk file.
CLRCHN
JMP (ICLRCN) ; do close vector
;************************************************************************************
;
; input character from channel
; This routine will get a byte of data from the channel already set up as the input
; channel by the CHKIN routine.
; If CHKIN has not been used to define another input channel the data is expected to
; be from the keyboard. the data byte is returned in the accumulator. The channel
; remains open after the call.
; Input from the keyboard is handled in a special way. first, the cursor is turned on
; and it will blink until a carriage return is typed on the keyboard. All characters
; on the logical line, up to 88 characters, will be stored in the BASIC input buffer.
; then the characters can be returned one at a time by calling this routine once for
; each character. When the carriage return is returned the entire line has been
; processed. the next time this routine is called the whole process begins again.
CHRIN
JMP (IBASIN) ; do input vector
;************************************************************************************
;
; output a character to channel
; This routine will output a character to an already opened channel. Use the OPEN
; routine, OPEN, and the CHKOUT routine to set up the output channel before calling
; this routine. If these calls are omitted, data will be sent to the default output
; device, device 3, the screen. The data byte to be output is loaded into the
; accumulator, and this routine is called. The data is then sent to the specified
; output device. The channel is left open after the call.
; NOTE: Care must be taken when using routine to send data to a serial device since
; data will be sent to all open output channels on the bus. Unless this is desired,
; all open output channels on the serial bus other than the actually intended
; destination channel must be closed by a call to the KERNAL close channel routine.
CHROUT
JMP (IBSOUT) ; do output vector
;***********************************************************************************;
;
; load RAM from a device
; This routine will load data bytes from any input device directly into the memory
; of the computer. It can also be used for a verify operation comparing data from a
; device with the data already in memory, leaving the data stored in RAM unchanged.
; The accumulator must be set to 0 for a load operation or 1 for a verify. If the
; input device was OPENed with a secondary address of 0 the header information from
; device will be ignored. In this case .X.Y must contain the starting address for the
; load. If the device was addressed with a secondary address of 1 or 2 the data will
; load into memory starting at the location specified by the header. This routine
; returns the address of the highest RAM location which was loaded.
; Before this routine can be called, the SETLFS and SETNAM routines must be called.
LOAD
JMP FLOAD ; load RAM from a device
;***********************************************************************************;
;
; save RAM to a device
; This routine saves a section of memory. Memory is saved from an indirect address
; on page 0 specified by .A, to the address stored in .X.Y, to a logical file. The
; SETLFS and SETNAM routines must be used before calling this routine. However, a
; file name is not required to SAVE to device 1, the cassette. Any attempt to save to
; other devices without using a file name results in an error.
; NOTE: device 0, the keyboard, and device 3, the screen, cannot be SAVEd to. If
; the attempt is made, an error will occur, and the SAVE stopped.
SAVE
JMP FSAVE ; save RAM to device
;***********************************************************************************;
;
; set the real time clock
; The system clock is maintained by an interrupt routine that updates the clock
; every 1/60th of a second. The clock is three bytes long which gives the capability
; to count from zero up to 5,184,000 jiffies - 24 hours plus one jiffy. At that point
; the clock resets to zero. Before calling this routine to set the clock the new time,
; in jiffies, should be in .Y.X.A, the accumulator containing the most significant byte.
SETTIM
JMP FSETTIM ; set real time clock
;***********************************************************************************;
;
; read the real time clock
; This routine returns the time, in jiffies, in .Y.X.A. The accumulator contains the
; most significant byte.
RDTIM
JMP FRDTIM ; read real time clock
;***********************************************************************************;
;
; scan the stop key
; If the STOP key on the keyboard is pressed when this routine is called the Z flag
; will be set. All other flags remain unchanged. If the STOP key is not pressed then
; the accumulator will contain a byte representing the last row of the keyboard scan.
; The user can also check for certain other keys this way.
STOP
JMP (ISTOP) ; do stop key vector
;***********************************************************************************;
;
; get a character from an input device
; In practice this routine operates identically to the CHRIN routine for all devices
; except for the keyboard. If the keyboard is the current input device this routine
; will get one character from the keyboard buffer. It depends on the IRQ routine to
; read the keyboard and put characters into the buffer.
; If the keyboard buffer is empty the value returned in the accumulator will be zero
GETIN
JMP (IGETIN) ; do get vector
;***********************************************************************************;
;
; close all channels and files
; This routine closes all open files. When this routine is called, the pointers into
; the open file table are reset, closing all files. Also the routine automatically
; resets the I/O channels.
CLALL
JMP (ICLALL) ; do close all vector
;***********************************************************************************;
;
; increment the real time clock
; This routine updates the system clock. Normally this routine is called by the
; normal KERNAL interrupt routine every 1/60th of a second. If the user program
; processes its own interrupts this routine must be called to update the time. Also,
; the STOP key routine must be called if the stop key is to remain functional.
UDTIM
JMP FUDTIM ; increment real time clock
;***********************************************************************************;
;
; return X,Y organisation of screen
; this routine returns the x,y organisation of the screen in .X,.Y
SCREEN
JMP FSCREEN ; return X,Y organisation of screen
;***********************************************************************************;
;
; read/set X,Y cursor position
; This routine, when called with the carry flag set, loads the current position of
; the cursor on the screen into the .X and .Y registers. .Y is the column number of
; the cursor location and .X is the row number of the cursor. A call with the carry
; bit clear moves the cursor to the position determined by the .X and .Y registers.
PLOT
JMP FPLOT ; read/set X,Y cursor position
;***********************************************************************************;
;
; return the base address of the I/O devices
; This routine will set .X.Y to the address of the memory section where the memory
; mapped I/O devices are located. This address can then be used with an offset to
; access the memory mapped I/O devices in the computer.
IOBASE
JMP FIOBASE ; return base address of I/O devices
;***********************************************************************************;
;
; spare bytes, not referenced
.byte $FF,$FF,$FF,$FF
;***********************************************************************************;
;
; hardware vectors
.word NMI ; NMI vector
.word START ; RESET vector
.word IRQROUT ; IRQ vector
.END
;***********************************************************************************;
;***********************************************************************************;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment