# HG changeset patch # User William Astle # Date 1544324221 25200 # Node ID 605ff82c4618c2d8abc63f721246909bf134a7b4 Initial check in with cleaned up sources This is the initial check in the source code in a state where it builds byte accurate copies of all the various ROM versions included. diff -r 000000000000 -r 605ff82c4618 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Sat Dec 08 19:57:01 2018 -0700 @@ -0,0 +1,4 @@ +syntax: glob +*~ +*.rom +*.list diff -r 000000000000 -r 605ff82c4618 00README.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/00README.txt Sat Dec 08 19:57:01 2018 -0700 @@ -0,0 +1,82 @@ +The source files here build to byte accurate versions of the following ROMs +that are found in various Color Computers: + +Color Basic 1.0 +Color Basic 1.1 +Color Basic 1.2 +Color Basic 1.3 +Extended Color Basic 1.0 +Extended Color Basic 1.1 +Extended Color Basic 2.0 (Coco3) +Disk Extended Color Basic 1.0 (shows 2.0 on the Coco3) +Disk Extended Color Basic 1.1 (shows 2.1 on the Coco3) + +The source files are based on the contents of the Unravelled series as +published by Spectral Associates as updated by Walter K. Zydhek. As such, +most of the symbols used will line up with those in the Unravelled series. +There are, however, some significant changes. + +* All source has been convered to use a semicolon to introduce comments + instead of relying on the implied end of line comments. + +* Spaces have been used to line up the columns instead of tabs. 16 spaces + are allowed for the label field. 32 spaces are allowed for the instruction + (both opcode and operand, separated by a single space) in most cases, and + the majority of comments will line up starting at column 49. + +* Source code has been largely converted to lower case. This is a personal + preference, but it also feels a bit less "shouty". Labels, however, have + retained their original case. + +* C style hexadecimal constants have been used in most cases. + +* Features specific to LWASM have been used to create fairly clean listing + files for the various ROMs. LWASM is, of course, required to build the + ROMs from this source. + +* The comments have been extensively rewritten. Any typos now extant in said + comments are entirely the fault of the guy writing them. + +* A few cases where numeric addresses were used instead of labels have been + corrected. + +Comment Accuracy +================ + +This is important to point out. While reasonable efforts have been made to +ensure that the comments accurately reflect what the relevant code is doing, +there are certainly errors that have crept in, or been duplicated from the +original disassembly sources. + +In some cases, the code is just convoluted enough that no reasonable +comments can do it justice. This is especially true in relation to the +floating point implementation. + +In other cases, what the code is doing is relatively clear but why it does +it is not. In other cases, truly understanding what the code is doing +requires a much more advanced understanding of mathematics or other fields +than the commenter has. In these cases, inaccurate comments could well be +present but the commenter would have no way of knowing for sure. + +There are also many cases in the original Unravelled comments where the +comments were just plain wrong or had the sense of a flag or other test +completely reversed. These have been corrected where they were detected, but +there is a great deal of code across the combined 96K of ROM code. + +Finally, some care has been taken to avoid comments the merely describe what +the instruction itself does in a manner that could have come right out of a +CPU reference manual. For instance, an instruction like "STX V4D" is better +left with no comment rather than writing "store X in V4D". + + +Future Updates +============== + +Since this code is basically frozen, any future modifications will be for +aesthetic reasons. Most notably, replacing synthetic address based labels +with more sensible ones would be beneficial. So would replacing variable +names with more descriptive names since the assembler in use supports much +longer symbol names. + +That said, any updates into the future must preserve the output as byte +identicial for obvious reasons. diff -r 000000000000 -r 605ff82c4618 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Sat Dec 08 19:57:01 2018 -0700 @@ -0,0 +1,39 @@ +LWASM=lwasm + +.PHONY: all +all: defs.list bas10.rom bas11.rom bas12.rom bas13.rom exbas10.rom exbas11.rom disk10.rom disk11.rom secb.rom + +bas10.rom bas10.list: bas10.s defs.s + $(LWASM) --raw --list=bas10.list --symbols -o bas10.rom bas10.s + +bas11.rom bas11.list: bas11.s defs.s + $(LWASM) --raw --list=bas11.list --symbols -o bas11.rom bas11.s + +bas12.rom bas12.list: bas12.s defs.s + $(LWASM) --raw --list=bas12.list --symbols -o bas12.rom bas12.s + +bas13.rom bas13.list: bas13.s defs.s + $(LWASM) --raw --list=bas13.list --symbols -o bas13.rom bas13.s + +exbas10.rom exbas10.list: exbas10.s defs.s + $(LWASM) --raw --list=exbas10.list --symbols -o exbas10.rom exbas10.s + +exbas11.rom exbas11.list: exbas11.s defs.s + $(LWASM) --raw --list=exbas11.list --symbols -o exbas11.rom exbas11.s + +disk10.rom disk10.list: disk.s defs.s + $(LWASM) --raw --list=disk10.list --symbols -DDISKVER=0 -o disk10.rom disk.s + +disk11.rom disk11.list: disk.s defs.s + $(LWASM) --raw --list=disk11.list --symbols -DDISKVER=1 -o disk11.rom disk.s + +secb.rom secb.list: secb.s defs.s + $(LWASM) --raw --list=secb.list --symbols -o secb.rom secb.s + +defs.list: defs.s + $(LWASM) --list=defs.list -o defs.bin defs.s + rm -f defs.bin + +.PHONY: clean +clean: + rm -f *.rom *.list *~ diff -r 000000000000 -r 605ff82c4618 bas10.s --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bas10.s Sat Dec 08 19:57:01 2018 -0700 @@ -0,0 +1,4064 @@ + *pragma nolist + include defs.s + *pragma list +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; COLOR BASIC ROM +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + org BASIC +; This is the official set of ROM entry points. It is unfortunate that the generic input ("console in") routine is not exposed +; here. It is also unfortunate that no open and close file routines are exposed. This and other oversignts led to a lot of +; software either having to re-invent the wheel or calling directly into the ROM instead of via these official entry points. +POLCAT fdb KEYIN ; indirect jump, get a keystroke +CHROUT fdb PUTCHR ; indirect jump, output character +CSRDON fdb CASON ; indirect jump, turn cassette on and start reading +BLKIN fdb GETBLK ; indirect jump, read a block from tape +BLKOUT fdb SNDBLK ; indirect jump, write a block to tape +JOYIN fdb GETJOY ; indirect jump, read joystick axes +WRTLDR fdb WRLDR ; indirect jump, turn cassette on and write a leader +; Initialization code. +LA00E lds #LINBUF+LBUFMX+1 ; put the stack in the line input buffer which is a safe place for now + lda #0x37 ; enable the cartidge interrupt (to detect autostarting cartridges) + sta PIA1+3 + lda RSTFLG ; get warm start flag + cmpa #0x55 ; is it valid? + bne BACDST ; brif not - cold start + ldx RSTVEC ; get warm start routine pointer + lda ,x ; get first byte of the routine + cmpa #0x12 ; is it NOP? + bne BACDST ; brif not - the routine is invalid so do a cold start + jmp ,x ; transfer control to the warm start routine +; RESET/power on comes here +RESVEC ldu #LA00E ; point to warm start check code +LA02A clrb ; use page 0 as direct page + tfr b,dp + ldx #PIA0 ; point to PIA0 (keyboard) + clr 1,x ; enable direction register for PIA0 DA + clr 3,x ; enable direction register for PIA0 DB + clr ,x ; set PIA0 DA to input (keyboard rows, comparator) + ldd #0xff34 + sta 2,x ; set PIA0 DB to output (keyboard columns) + stb 1,x ; set PIA0 DA to data mode + stb 3,x ; set PIA0 DB to data mode + ldx #PIA1 ; point to misc PIA + clr 1,x ; enable direction register for PIA1 DA + clr 3,x ; enable direction register for PIA1 DB + deca + sta ,x ; set PIA1 DA as output except for bit 0 (DAC, printer, cassette input) + lda #0xf8 ; set VDG control to output, other bits input (printer handshake, etc.) + sta 2,x + stb 1,x ; enable data mode for PIA1 DA + stb 3,x ; enable data mode for PIA1 DB + clr 2,x ; set VDG to alphanumeric + lda #2 ; set rs232 to marking + sta ,x + lda 2,x ; get RAM jumper setting + ldx #SAMREG ; point to SAM control register + ldb #16 ; 16 bits to clear +LA05E sta ,x++ ; clear a SAM bit + decb ; done all 16? + bne LA05E ; brif not + sta SAMREG+9 ; put display at 0x400 + anda #4 ; keep only RAMSZ input + beq LA06C ; brif 4K RAM + sta -5,x ; set for 16K +LA06C jmp ,u ; go do warm/cold start +BACDST ldx #0 ; point to start of memory +LA071 clr ,x+ ; clear byte + cmpx #VIDRAM ; at display? + bne LA071 ; brif not + jsr LA928 ; clear screen + ldx #LA10D ; point to variabl einitializers + ldu #CMPMID ; point to destination + ldb #28 + jsr LA59A ; copy initializers + ldu #IRQVEC ; point to second destination + ldb #30 + jsr LA59A ; copy initializers + ldx #LB277 ; init extended basic's COMVEC stuff to error + stx 3,u + stx 8,u + ldx #RVEC0 ; point to ram vectors + lda #$39 ; RTS opcode +LA094 sta ,x+ ; init a byte + cmpx #RVEC0+25*3 ; end of vectors? + bne LA094 ; brif not + sta LINHDR-1 ; set "next line address" in line input buffer to nonzero + ldx #VIDRAM+$200 ; point to end of display screen + clr ,x+ ; put a constant zero before start of program + stx TXTTAB ; set start ofprogram +LA0AB lda 2,x ; look for end of memory + coma + sta 2,x + cmpa 2,x + bne LA0BA ; brif it wasn't RAM + leax 1,x ; move pointer forward +LA0B6 com 1,x ; restore memory value + bra LA0AB ; check another byte +LA0BA stx TOPRAM ; set top of memory + stx MEMSIZ ; set top of string space + stx STRTAB ; set bottom of allocated string space + leax -200,x ; allocate 200 bytes for string space + stx FRETOP ; save top of free memory + tfr x,s ; put the stack there too + jsr LAD19 ; do a "NEW" + ldx #'E*256+'X ; magic number to detect ECB ROM + cmpx EXBAS ; is there an ECB ROM? + lbeq EXBAS+2 ; brif so - launch it + andcc #0xaf ; start interrupts + ldx #LA147-1 ; point to sign on message + jsr LB99C ; print it out + ldx #BAWMST ; warm start routine address + stx RSTVEC ; set vector there + lda #0x55 ; warm start valid flag + sta RSTFLG ; mark warm start valid + bra LA0F3 ; go to direct mode +; Warm start entry point +BAWMST nop ; valid routine marker + clr DEVNUM ; reset output/input to screen + jsr LAD33 ; do a partial NEW + andcc #0xaf ; start interrupts + jsr LA928 ; clear the screen +LA0F3 jmp LAC73 ; go to direct mode +; FIRQ service routine - this handles starting autostart cartridges +BFRQSV tst PIA1+3 ; is it the cartridge interrupt? + bmi LA0FC ; brif so + rti +LA0FC jsr LA7D1 ; delay for a while + jsr LA7D1 ; delay for another while + ldu #LA108 ; point to cartridge starter + jmp LA02A ; go initialize everything clean for the cartridge +LA108 clr RSTFLG ; force a cold start a cartridge reset + jmp ROMPAK ; transfer control to the cartridge +; Variable initializers (first batch) +LA10D fcb 18 ; mid band partition of the 1200/2400 Hz period + fcb 24 ; upper limit of 1200 Hz period + fcb 10 ; upper limit of 2400 Hz period + fdb 128 ; number of 0x55s for cassette leader + fcb 11 ; cursor blink delay + fdb 87 ; 600 baud delay constant + fdb 1 ; printer carriage return delay constant + fcb 16 ; printer tab field width + fcb 112 ; last printer tab zone + fcb 132 ; printer carriage width + fcb 0 ; printer carriage position + fdb LB44A ; default execution address for EXEC + inc CHARAD+1 ;* character fetching routines (DP portion) - we first do a two + bne LA123 ;* two stage increment of CHARAD then load the value into A + inc CHARAD ;* before transferring control to the bottom half routine in ROM +LA123 lda >0 ; NOTE: the 0 is a placeholder, extended addressing is required + jmp BROMHK +; Variable initializers (second batch) + jmp BIRQSV ; IRQ handler + jmp BFRQSV ; FIRQ handler + jmp LB44A ; default USR() address + fcb 0x80,0x4f,0xc7,0x52,0x59 ; random seed + fcb 0xff ; capslock flag - default to upper case + fdb DEBDEL ; keyboard debounce delay (why is it a variable?) + jmp LB277 ; exponentiation handler vector + fcb 53 ; (command interpretation table) 53 commands + fdb LAA66 ; (command interpretation table) reserved words list (commands) + fdb LAB67 ; (command interpretation table) jump table (commands) + fcb 20 ; (command interpretation table) 20 functions + fdb LAB1A ; (command interpretation table) reserved words list (functions) + fdb LAA29 ; (command interpretation table) jump table (functions) +; This is the signon message. +LA147 fcc 'COLOR BASIC 1.0' + fcb 0x0d + fcc '(C) 1980 TANDY' + fcb 0 +; This is the "invalid colour" CLS easter egg text. Basically 11 wasted bytes +LA166 fcc 'MICROSOFT' + fcb 0x0d,0 +; Read a character from current device and mask off bit 7 (keep it as 7 bit ASCII) +LA171 bsr LA176 ; get character + anda #0x7f ; mask off high bit + rts +; Generic read routine. Reads a character from the device specified by DEVNUM. If no input was available, +; CINBFL will bet nonzero (on EOF). If input was available, CINBFL will be zero. Note that this routine +; has undefined results when called on an output only device. All registers except CC and A are preserved. +LA176 jsr RVEC4 ; do RAM hook + clr CINBFL ; flag data available + tst DEVNUM ; is it keyboard? + beq LA1B1 ; brif so - blink cursor and wait for key press + tst CINCTR ; is there anything in cassette input buffer? + bne LA186 ; brif so + com CINBFL ; flag EOF + rts +; Read character from cassette file +LA186 pshs u,y,x,b ; preserve registers + ldx CINPTR ; get input buffer pointer + lda ,x+ ; get character from buffer + pshs a ; save it for return + stx CINPTR ; save new input buffer pointer + dec CINCTR ; count character just consumed + bne LA197 ; brif buffer is not empty yet + jsr LA635 ; go read another block, if any, to refill the buffer +LA197 puls a,b,x,y,u,pc ; restore registers and return the character +; Blink the cursor. This might be better timed via an interrupt or something. +LA199 dec BLKCNT ; is it time to blink the cursor? + bne LA1AB ; brif not + ldb #11 ; reset blink timer + stb BLKCNT + ldx CURPOS ; get cursor position + lda ,x ; get character at the cursor + adda #0x10 ; move to next color + ora #0x8f ; make sure it's a grahpics block with all elements lit + sta ,x ; put new cursor block on screen +LA1AB ldx #DEBDEL ; we'll use the debounce delay for the cursor blink timer (10ms) +LA1AE jmp LA7D3 ; go count X down +; Blink cursor while waiting for a key press +LA1B1 pshs x,b ; save registers +LA1B3 bsr LA199 ; go do a cursor iteration + bsr KEYIN ; go read a key + beq LA1B3 ; brif no key pressed + ldb #0x60 ; VDG screen space character + stb [CURPOS] ; blank cursor out + puls b,x,pc ; restore registers and return +; This is the actual keyboard polling routine. Returns 0 if no new key is down. This version of the +; routine has a few issues which are finally fixed mostly properly in Color Basic 1.2 +KEYIN pshs x,b ; save registers + bsr LA1C8 ; get keystroke + tsta ; set flags + puls b,x,pc ; restore registers and return +LA1C8 leas -3,s ; make temp storage space + ldx #KEYBUF ; point to keyboard state table + clr 0,s ; reset column counter + ldb #0xfe ; set column strobe to first column + stb PIA0+2 ; set strobe +LA1D4 bsr LA238 ; read keyboard data + sta 1,s ; save keyboard data + eora ,x ; set any bit where a key state changed + anda ,x ; ignore any where a key was released + ldb 1,s ; get new key data + stb ,x+ ; save in state table + tsta ; was a key down? + bne LA1ED ; brif so + inc 0,s ; bump column counter + comb ; set C + rol PIA0+2 ; move column strobe over + bcs LA1D4 ; brif not done all columns + puls b,x,pc ; restore registers and return +LA1ED ldb PIA0+2 ; get strobe data + stb 2,s ; save it + ldb #0xf8 ; make sure B is 0 after first ADDB +LA1F4 addb #8 ; move to next row base + lsra ; at the right row base? + bcc LA1F4 ; brif not + addb 0,s ; add in column offset + beq LA245 ; brif @ + cmpb #26 ; alpha? + bhi LA247 ; brif not + orb #0x40 ; add in uppercase ASCII bias + bsr LA22D ; get shift status + beq LA20E ; brif shift down + lda CASFLG ; check casplock + bne LA20E ; brif not caps mode + orb #0x20 ; convert to lower case +LA20E stb 0,s ; save ASCII value for return later + ldx DEBVAL ; get debounce delay + jsr LA7D3 ; count X down + ldb 2,s ; get column strobe data + stb PIA0+2 ; re-set strobe + bsr LA238 ; read row data + cmpa 1,s ; does it match the result from before the delay? + puls a ; get back key code (return value) + bne LA22A ; brif not the same result + cmpa #0x12 ; is it SHIFT-0? + bne LA22B ; brif not + com CASFLG ; flip capslock state +LA22A clra ; set Z, return 0 for no key down +LA22B puls x,pc ; clean up stack and return +LA22D lda #0x7f ; column strobe for SHIFT + sta PIA0+2 ; strobe keyboard + lda PIA0 ; get row data + anda #0x40 ; keep only shift data + rts +LA238 lda PIA0 ; read row data + ora #0x80 ; mask comparator + tst PIA0+2 ; reading column 7? + bmi LA244 ; brif not + ora #0xc0 ; mask off SHIFT as well +LA244 rts +LA245 ldb #51 ; code for @ +LA247 ldx #CONTAB-0x36 ; point to control code table, first batch + cmpb #33 ; arrows, space, zero? + blo LA264 ; brif not + ldx #CONTAB-0x54 ; point to control code table, second batch + cmpb #48 ; enter, clear, break, @? + bhs LA264 ; brif so + bsr LA22D ; get shift state + cmpb #43 ; number, colon, semicolon? + bls LA25D ; brif so + eora #0x40 ; invert shift state +LA25D tsta ; test shift status + beq LA20E ; brif shift down - we have a result so debounce things + addb #0x10 ; add in ASCII offset correction + bra LA20E ; go debounce things +LA264 aslb ; two bytes per entry + bsr LA22D ; test shift state + bne LA26A ; brif not shift + incb ; select shifted entry +LA26A ldb b,x ; get return value + bra LA20E ; go debounce keyboard +CONTAB fcb 0x5e,0x5f ; (^, _) + fcb 0x0a,0x5b ; (LF, [) + fcb 0x08,0x15 ; (BS, ^U) + fcb 0x09,0x5d ; (TAB, ]) + fcb 0x20,0x20 ; + fcb 0x30,0x12 ; <0> (0, ^R) + fcb 0x0d,0x0d ; (CR, CR) + fcb 0x0c,0x5c ; (FF, \) + fcb 0x03,0x03 ; (^C, ^C) + fcb 0x40,0x13 ; <@> (@, ^S) +; Generic output routine. +; Output character in A to the device specified by DEVNUM. All registers are preserved except CC. +; Sending output to a device that does not support output is undefined. +PUTCHR jsr RVEC3 ; call RAM hook + pshs b ; save B + ldb DEVNUM ; get desired device number + incb ; set flags (Z for -1, etc.) + puls b ; restore B + bmi LA2BF ; brif < -1 (line printer) + bne LA30A ; brif > -1 (screen) +; Write character to tape file + pshs x,b,a ; save registers + ldb FILSTA ; get file status + decb ; input file? + beq LA2A6 ; brif so + ldb CINCTR ; get character count + incb ; account for this character + bne LA29E ; brif buffer not full + bsr LA2A8 ; write previously full block to tape +LA29E ldx CINPTR ; get output buffer pointer + sta ,x+ ; put character in output + stx CINPTR ; save new buffer pointer + inc CINCTR ; account for this character +LA2A6 puls a,b,x,pc ; restore registers and return +; Write a block of data to tape. +LA2A8 ldb #1 ; data block type +LA2AA stb BLKTYP ; set block type + ldx #CASBUF ; point to output buffer + stx CBUFAD ; set buffer pointer + ldb CINCTR ; get number of bytes in the block + stb BLKLEN ; set length to write + pshs u,y,a ; save registers + jsr LA7E5 ; write a block to tape + puls a,y,u ; restore registers + jmp LA650 ; reset buffer pointers +; This routine is changed to send 8 bits of data as of Color Basic 1.1. +; Color Basic 1.2 adds a handshake ; before sending any data. +LA2BF pshs x,b,a,cc ; save registers and interrupt status + orcc #0x50 ; disable interrupts + bsr LA2FB ; set to marking (stop bit) + asla ; send 7 data bits, one start bit + ldb #8 ; 8 bits to send +LA2C8 pshs b ; save bit counter + clrb ; initialize output byte + lsra ; get output bit to C + rolb ; now move it to the right bit in the output + rolb + stb PIA1 ; send bit to printer + bsr LA302 ; do the baud delay (this delay is improved in later versions) + nop + nop + nop + bsr LA302 + puls b ; get bit counter back + decb ; sent all 8 bits? + bne LA2C8 ; brif not + bsr LA2FB ; send stop bit + puls cc,a ; restore output character and interrupt status + cmpa #0x0d ; carriage return? + beq LA2ED ; brif so + inc LPTPOS ; bump output position + ldb LPTPOS ; get new position + cmpb LPTWID ; at end of line? + blo LA2F3 ; brif not +LA2ED clr LPTPOS ; reset output position to start of line + bsr LA305 ; do carriage return delay + bsr LA305 +LA2F3 ldb PIA1+2 ; read rs232 status + lsrb ; get status bit to C + bcs LA2F3 ; brif still not ready + puls b,x,pc ; restore registers and return +LA2FB ldb #2 ; set output to high (marking) +LA2FD stb PIA1 ; set RS232 output + bsr LA302 ; do baud delay (first iteration) then fall through for second +LA302 ldx LPTBTD ; get buard rate delay constant + skip2 +LA305 ldx LPTLND ; get carriage return delay constant + jmp LA7D3 ; count X down +; Output character to screen +LA30A pshs x,b,a ; save registers + ldx CURPOS ; get cursor pointer + cmpa #0x08 ; backspace? + bne LA31D ; brif not + cmpx #VIDRAM ; at top of screen? + beq LA35D ; brif so - it's a no-op + lda #0x60 ; VDG space character + sta ,-x ; put a space at previous location and move pointer back + bra LA344 ; save new cursor position and return +LA31D cmpa #0x0d ; carriage return? + bne LA32F ; brif not + ldx CURPOS ; get cursor pointer (why? we already have it) +LA323 lda #0x60 ; VDG space character + sta ,x+ ; put output space + tfr x,d ; see if we at a multiple of 32 now + bitb #0x1f + bne LA323 ; brif not + bra LA344 ; go check for scrolling +LA32F cmpa #0x20 ; control character? + blo LA35D ; brif so + tsta ; is it graphics block? + bmi LA342 ; brif so + cmpa #0x40 ; number or special? + blo LA340 ; brif so (flip "case" bit) + cmpa #0x60 ; upper case alpha? + blo LA342 ; brif so - keep it unmodified + anda #0xdf ; clear bit 5 (inverse video) +LA340 eora #0x40 ; flip inverse video bit +LA342 sta ,x+ ; output character +LA344 stx CURPOS ; save new cursor position + cmpx #VIDRAM+511 ; end of screen? + bls LA35D ; brif not + ldx #VIDRAM ; point to start of screen +LA34E ldd 32,x ; get two characters from next row + std ,x++ ; put them on this row + cmpx #VIDRAM+0x1e0 ; at start of last row on screen? + blo LA34E ; brif not + ldb #0x60 ; VDG space + jsr LA92D ; blank out last line (borrow CLS's loop) +LA35D puls a,b,x,pc ; restore registers and return +; Set up device parameters for output +LA35F jsr RVEC2 ; do the RAM hook dance + pshs x,b,a ; save registers + clr PRTDEV ; flag device as a screen + lda DEVNUM ; get devicenumber + beq LA373 ; brif screen + inca ; is it tape? + beq LA384 ; brif so + ldx LPTCFW ; get tab width and last tab stop for printer + ldd LPTWID ; get line width and current position for printer + bra LA37C ; set parameters +LA373 ldb CURPOS+1 ; get LSB of cursor position + andb #0x1f ; now we have the offset into the line + ldx #0x1010 ; 16 character tab, position 16 is last tab stop + lda #32 ; screen is 32 characters wide +LA37C stx DEVCFW ; save tab width and last tab stop for active device + stb DEVPOS ; save line position for current device + sta DEVWID ; save line width for current device + puls a,b,x,pc ; restore registers and return +LA384 com PRTDEV ; flag device as non-display + ldx #0x0100 ; tab width is 1, last tab field is 0 + clra ; line width is 0 + clrb ; character position on line is 0 + bra LA37C ; go set parameters +; This is the line input routine used for reading lines for Basic, both in immediate mode and for +; INPUT and LINE INPUT. Exit with C set if terminated by BREAK and C clear if terminated by ENTER. +; The actualy entry point is LA390. Note that this routine echoes to *all* devices. +LA38D jsr LA928 ; clear screen (CLEAR key handling) +LA390 jsr RVEC12 ; do the RAM hook dance + clr IKEYIM ; reset cached input character from BREAK check + ldx #LINBUF+1 ; point to line input buffer (input pointer) + ldb #1 ; Number of characters in line (we start at 1 so BS handling is easier) +LA39A jsr LA171 ; get an input character, only keep low 7 bits + tst CINBFL ; is it EOF? + bne LA3CC ; brif EOF + tst DEVNUM ; is it keyboard input? + bne LA3C8 ; brif not - don't do line editing + cmpa #0x0c ; form feed (CLEAR)? + beq LA38D ; brif so - clear screen and reset + cmpa #0x08 ; backspace? + bne LA3B4 ; brif not + decb ; move back one character + beq LA390 ; brif we were at the start of the line - reset and start again + leax -1,x ; move input pointer back + bra LA3E8 ; echo the backspace and continue +LA3B4 cmpa #0x15 ; SHIFT-LEFT (kill line)? + bne LA3C2 ; brif not +LA3B8 decb ; at start of line? + beq LA390 ; brif so - reset and restart + lda #0x08 ; echo a backspace + jsr PUTCHR + bra LA3B8 ; see if we've erased everything yet +LA3C2 cmpa #0x03 ; BREAK? + orcc #1 ; set C if it is (only need Z for the next test + beq LA3CD ; brif BREAK - exit +LA3C8 cmpa #0x0d ; ENTER (CR) + bne LA3D9 ; brif not +LA3CC clra ; clear carry (it might not be clear on EOF) +LA3CD pshs cc ; save ENTER/BREAK flag + jsr LB958 ; echo a carriage return + clr ,x ; make sure we have a NUL at the end of the buffer + ldx #LINBUF ; point to input buffer + puls cc,pc ; restore ENTER/BREAK flag and return +LA3D9 cmpa #0x20 ; control character? + blo LA39A ; brif so - skip it + cmpa #'z+1 ; above z? + bhs LA39A ; brif so - ignore it + cmpb #LBUFMX ; is the buffer full? + bhs LA39A ; brif so - ignore extra characters + sta ,x+ ; put character in the buffer + incb ; bump character count +LA3E8 jsr PUTCHR ; echo character + bra LA39A ; go handle next input character +; Check if DEVNUM Is valid for reading. Raise FM error if open but not for reading. NO error if not open. +LA3ED jsr RVEC5 ; do the RAM hook dance + lda DEVNUM ; get device number + beq LA415 ; brif keyboard - always valid + inca ; is it tape? + bne LA403 ; brif not + lda FILSTA ; get tape file status + bne LA400 ; brif file is open +LA3FB ldb #22*2 ; raise NO error + jmp LAC46 +LA400 deca ; is it in input mode? + beq LA415 ; brif so +LA403 jmp LA616 ; raise FM error +; Check if DEVNUM is valid for writing. Raise FM error if open but not for writing. NO error if not open. +LA406 jsr RVEC6 ; do the RAM hook dance + lda DEVNUM ; get device number + inca ; is it tape? + bne LA415 ; brif not + lda FILSTA ; get file status + beq LA3FB ; brif not open + deca ; is it open for reading? + beq LA403 ; brif so - bad mode +LA415 rts +; CLOSE command +CLOSE beq LA426 ; brif no file specified - close all files + jsr LA5A5 ; parse device number +LA41B bsr LA42D ; close specified file + jsr GETCCH ; is there more? + beq LA44B ; brif not + jsr LA5A2 ; check for comma and parse another device number + bra LA41B ; go close this one +; Close all files handler. +LA426 jsr RVEC7 ; Yup. The RAM hook dance. + lda #-1 ; start with tape file + sta DEVNUM +; Close file specified in DEVNUM. Note that this never fails. +LA42D jsr RVEC8 ; You know it. RAM hook. + lda DEVNUM ; get device we're closing + clr DEVNUM ; reset to screen/keyboard + inca ; is it tape? + bne LA44B ; brif not + lda FILSTA ; get file status + cmpa #2 ; is it output? + bne LA449 ; brif not + lda CINCTR ; is there anything waiting to be written? + beq LA449 ; brif not + jsr LA2A8 ; write final block of data +LA444 ldb #0xff ; write EOF block + jsr LA2AA +LA449 clr FILSTA ; mark tape file closed +LA44B rts +; CSAVE command +CSAVE jsr LA578 ; parse filename + jsr GETCCH ; see what we have after the file name + beq LA469 ; brif none + jsr LB26D ; make sure there's a comma + ldb #'A ; make sure there's an A after + jsr LB26F + bne LA44B ; brif not end of line + clra ; file type 0 (basic program) + jsr LA65C ; write out header block + lda #-1 ; set output to tape + sta DEVNUM + clra ; set Z so we list the whole program + jmp LIST ; go list the program to tape +LA469 clra ; file type 0 (basic program) + ldx ZERO ; set to binary file mode + jsr LA65F ; write header block + clr FILSTA ; close files + inc BLKTYP ; set block type to data + jsr WRLDR ; write out a leader + ldx TXTTAB ; point to start of program +LA478 stx CBUFAD ; set buffer location + lda #255 ; block size to 255 bytes (max size) + sta BLKLEN + ldd VARTAB ; get end of program + subd CBUFAD ; how much is left? + beq LA491 ; brif we have nothing left + cmpd #255 ; do we have a full block worth? + bhs LA48C ; brif so + stb BLKLEN ; save actual remainder as block length +LA48C jsr SNDBLK ; write a block out + bra LA478 ; go do another block +LA491 neg BLKTYP ; set block type to 0xff (EOF) + clr BLKLEN ; no data in EOF block + jmp LA7E7 ; write EOF, stop tape, and return +; CLOAD and CLOADM commands +CLOAD clr FILSTA ; close tape file + cmpa #'M ; is it ClOADM? + beq LA4FE ; brif so + leas 2,s ; clean up stack + jsr LA5C5 ; parse file name + jsr LA648 ; go find the file + tst CASBUF+10 ; is it binary? + beq LA4C8 ; brif so + lda CASBUF+9 ; is it ASCII? + beq LA4CD ; brif not + jsr LAD19 ; clear out existing program + lda #-1 ; set up for reading from tape + sta DEVNUM + inc FILSTA ; set tape file to input + jsr LA635 ; go read first block + jmp LAC7C ; go to immediate mode to read in the program +; This is the EOF handler for immediate mode. It's rather assinine for this to be located here. It is +; probably an artifact of a substantially different code layout prior to compacting the ROM to fit in +; 8K. +LA4BF jsr RVEC13 ; do the RAM hook dance + jsr LA42D ; close file + jmp LAC73 ; go back to immediate mode +LA4C8 lda CASBUF+8 ; get file type + beq LA4D0 ; brif basic program +LA4CD jmp LA616 ; raise FM error +LA4D0 jsr LAD19 ; erase existing program + jsr CASON ; start reading tape + ldx TXTTAB ; get start of program storage +LA4D8 stx CBUFAD ; set load address for block + ldd CBUFAD ; get start of block + inca ; bump by 256 + jsr LAC37 ; check if there's room for a maximum sized block of 255 + jsr GETBLK ; go read a block + bne LA4F8 ; brif there was an error during reading + lda BLKTYP ; get type of block read + beq LA4F8 ; brif header block - IO error + bpl LA4D8 ; brif data block - read another + stx VARTAB ; save new end of program + bsr LA53B ; stop tape + ldx #LABED-1 ; point to "OK" prompt + jsr LB99C ; show prompt + jmp LACE9 ; reset various things and return +LA4F8 jsr LAD19 ; clear out partial program load +LA4FB jmp LA619 ; raise IO error +; This is the CLOADM command +LA4FE jsr GETNCH ; eat the "M" + bsr LA578 ; parse file name + jsr LA648 ; go find the file +LA505 ldx ZERO ; default offset is 0 + jsr GETCCH ; see if there's something after the file name + beq LA511 ; brif no offset + jsr LB26D ; make sure there's a comma + jsr LB73D ; evaluate offset to X +LA511 lda CASBUF+8 ; get file mode + cmpa #2 ; M/L program? + bne LA4CD ; brif not - FM error + ldd CASBUF+11 ; get load address + leau D,x ; add in offset + stu EXECJP ; set EXEC default address + tst CASBUF+10 ; is it binary? + bne LA4CD ; brif not + ldd CASBUF+13 ; get load address + leax d,x ; add in offset + stx CBUFAD ; set buffer address for loading + jsr CASON ; start up tape +LA52E jsr GETBLK ; read a block + bne LA4FB ; brif error reading + stx CBUFAD ; save new load address + tst BLKTYP ; set flags on block type + beq LA4FB ; brif another header - IO error + bpl LA52E ; brif it was data - read more +LA53B jmp LA7E9 ; turn off tape and return +; The EXEC command +EXEC beq LA545 ; brif no argument - use default address + jsr LB73D ; evaluate EXEC address to X + stx EXECJP ; set new default EXEC address +LA545 jmp [EXECJP] ; transfer control to execution address +; Break check for LIST so it doesn't interrupt ASCII saves. Why is this here instead of with the rest of the break +; check logic or packaged up with LIST? +LA549 jsr RVEC11 ; do the RAM hook dance + lda DEVNUM ; get device number + inca ; is it tape? + beq LA5A1 ; brif so - don't do break check + jmp LADEB ; do the actual break check +; Evaluate an expression and make sure it is within the limits of the screen and sets the cursor position. +; This really should be located with the PRINT command. +LA554 jsr LB3E4 ; evaluate a positive expression to D + subd #511 ; is it within bounds? + lbhi LB44A ; brif not - error out + addd #VIDRAM+511 ; adjust to be within the screen (and undo the SUBD above) + std CURPOS ; set cursor position + rts +; INKEY$ function +INKEY lda IKEYIM ; was a key down during break check? + bne LA56B ; brif so + jsr KEYIN ; poll the keyboard +LA56B clr IKEYIM ; reset the break check cache + sta FPA0+3 ; store result for later return + lbne LB68F ; brif a key was down - return it as a string + sta STRDES ; set string length to 0 (no key down) + jmp LB69B ; return the NULL string +; Parse a filename +LA578 ldx #CFNBUF ; point to file name buffer + clr ,x+ ; zero out file name length + lda #0x20 ; space character to initialize file name +LA57F sta ,x+ ; put a space in the buffer + cmpx #CASBUF ; at end of file name? + bne LA57F ; brif not + jsr GETCCH ; get input character + beq LA5A1 ; brif no name present + jsr LB156 ; evaluate the file name expression + jsr LB654 ; point to start of the file name + ldu #CFNBUF ; point to file name buffer + stb ,u+ ; save string length + beq LA5A1 ; brif empty - we're done + skip2 +LA598 ldb #8 ; copy 8 bytes +; Move B bytes from (X) to (U) +LA59A lda ,x+ ; copy a byte + sta ,u+ + decb ; done yet? + bne LA59A ; brif not +LA5A1 rts +; Parse a device number and check validity +LA5A2 jsr LB26D ; check for comma and SN error if not +LA5A5 cmpa #'# ; do we have a #? + bne LA5AB ; brif not (it's optional) + jsr GETNCH ; munch the # +LA5AB jsr LB141 ; evaluate the expression +LA5AE jsr INTCNV ; convert it to an integer in D + rolb ; move sign of B into C + adca #0 ; add sign of B to A + bne LA61F ; brif A doesn't match the sign of B + rorb ; restore B (ADCA will have set C if B was negative) + stb DEVNUM ; set device number + jsr RVEC1 ; do the RAM hook dance + beq LA5C4 ; brif device number set to screen/keyboard (valid) + bpl LA61F ; brif not negative (not valid) + cmpb #-2 ; is it printer or tape? + blt LA61F ; brif not (not valid) +LA5C4 rts +; Read file name from the line and do an error if anything follows it +LA5C5 bsr LA578 ; parse file name + jsr GETCCH ; set flags on current character +LA5C9 beq LA5C4 ; brif nothing there - it's good + jmp LB277 ; raise SN error +; EOF functoin +EOF jsr RVEC14 ; do the RAM hook dance + lda DEVNUM ; get device number + pshs a ; save it (so we can restore it later) + bsr LA5AE ; check the device number (which is in FPA0) + jsr LA3ED ; check validity for reading +LA5DA clrb ; not EOF = 0 (FALSE) + lda DEVNUM ; get device number + beq LA5E4 ; brif keyboard - never EOF + tst CINCTR ; is there anything in the input buffer? + bne LA5E4 ; brif so - not EOF + comb ; set EOF flag to -1 (true) +LA5E4 puls a ; get back original device + sta DEVNUM ; restore it +LA5E8 sex ; sign extend result to 16 bits + jmp GIVABF ; go return the result +; SKIPF command +SKIPF bsr LA5C5 ; parse file name + bsr LA648 ; look for the file + jsr LA6D1 ; read the file + bne LA619 ; brif error reading file + rts +; OPEN command +OPEN jsr RVEC0 ; do the RAM hook dance + jsr LB156 ; get file status (input/output) + jsr LB6A4 ; get first character of status string + pshs b ; save status + bsr LA5A2 ; parse a comma then the device number + jsr LB26D ; make sure there's a comma + bsr LA5C5 ; parse the file name + lda DEVNUM ; get device number of the file + clr DEVNUM ; reset actual device to the screen + puls b ; get back status + cmpb #'I ; INPUT? + beq LA624 ; brif so - open a file for INPUT + cmpb #'O ; OUTPUT? + beq LA658 ; brif so - open a file for OUTPUT +LA616 ldb #21*2 ; raise FM error + skip2 +LA619 ldb #20*2 ; raise I/O error + skip2 +LA61C ldb #18*2 ; raise AO error + skip2 +LA61F ldb #19*2 ; raise DN error + jmp LAC46 +LA624 inca ; are we opening the tape? + bmi LA616 ; brif printer - FM error; printer can't be opened for READ + bne LA657 ; brif screen - screen is always open + bsr LA648 ; read header block + lda CASBUF+9 ; clear A if binary or machine language file + anda CASBUF+10 + beq LA616 ; bad file mode if not data file + inc FILSTA ; open file for input +LA635 jsr LA701 ; start tape, read block + bne LA619 ; brif error during read + tst BLKTYP ; check block type + beq LA619 ; brif header block - something's wrong + bmi LA657 ; brif EOF + lda BLKLEN ; get length of block + beq LA635 ; brif empty block - read another +LA644 sta CINCTR ; set buffer count + bra LA652 ; reset buffer pointer +LA648 tst FILSTA ; is the file open? + bne LA61C ; brif so - AO error + bsr LA681 ; search for file + bne LA619 ; brif error on read +LA650 clr CINCTR ; mark buffer empty +LA652 ldx #CASBUF ; set buffer pointer to start of buffer + stx CINPTR +LA657 rts +LA658 inca ; check for tape device + bne LA657 ; brif not tape (nothing doing - it's always open) + inca ; make file type 1 +LA65C ldx #0xffff ; ASCII and data mode +LA65F tst FILSTA ; is file open? + bne LA61C ; brif so - raise error + ldu #CASBUF ; point to tape buffer + stu CBUFAD ; set address of block to write + sta 8,u ; set file type + stx 9,u ; set ASCII flag and mode + ldx #CFNBUF+1 ; point to file name + jsr LA598 ; move file name to the tape buffer + clr BLKTYP ; set for header block + lda #15 ; 15 bytes in a header block + sta BLKLEN ; set block length + jsr LA7E5 ; write the block + lda #2 ; set file type to output + sta FILSTA + bra LA650 ; reset file pointers +; Search for correct cassette file name +LA681 ldx #CASBUF ; point to cassette buffer + stx CBUFAD ; set location to read blocks to +LA686 lda CURLIN ; are we in immediate mode? + inca + bne LA696 ; brif not + jsr LA928 ; clear screen + ldx CURPOS ; get start of screen (set after clear) + ldb #'S ; for "searching" + stb ,x++ ; put it on the screen + stx CURPOS ; save cursor position to be one past the search indicator +LA696 bsr LA701 ; read a block + orb BLKTYP ; merge error flag with block type + bne LA6D0 ; brif error or not header + ldx #CASBUF ; point to block just read + ldu #CFNBUF+1 ; point to the desired name + ldb #8 ; compare 8 characters + clr ,-s ; set flag to "match" +LA6A6 lda ,x+ ; get character from just read block + ldy CURLIN ; immediate mode? + leay 1,y + bne LA6B4 ; brif not + clr DEVNUM ; set output to screen + jsr PUTCHR ; display character +LA6B4 suba ,u+ ; subtract from desired file name (nonzero if no match) + ora ,s ; merge with match flag + sta ,s ; save new match flag (will be nonzero if any character differs) + decb ; done all characters? + bne LA6A6 ; brif not - do another + lda ,s+ ; get match flag (and set flags) + beq LA6CB ; brif we have a match + tst -9,u ; did we actually have a file name or will any file do? + beq LA6CB ; brif any file will do + bsr LA6D1 ; go read past the file + bne LA6D0 ; return on error + bra LA686 ; keep looking +LA6CB lda #'F ; for "found" + bsr LA6F8 ; put "F" on screen + clra ; set Z to indicat eno errors +LA6D0 rts +LA6D1 tst CASBUF+10 ; check type of file + bne LA6DF ; brif "blocked" file + jsr CASON ; turn on tape +LA6D9 bsr GETBLK ; read a block + bsr LA6E5 ; error or EOF? + bra LA6D9 ; read another block +LA6DF bsr LA701 ; read a single block + bsr LA6E5 ; error or EOF? + bra LA6DF ; read another block +LA6E5 bne LA6ED ; got error reading block + lda BLKTYP ; check block type + nega ; A is 0 now if EOF + bmi LA6F3 ; brif not end of file + deca ; clear error indicator +LA6ED sta CSRERR ; set error flag + leas 2,s ; don't return to original caller + bra LA705 ; turn off motor and return +LA6F3 lda VIDRAM ; get first char on screen + eora #0x40 ; flip case +LA6F8 ldb CURLIN ; immediate mode? + incb + bne LA700 ; brif not + sta VIDRAM ; save flipped case character +LA700 rts +; Read a single block from tape (for a "blocked" file) +LA701 bsr CASON ; start tape going + bsr GETBLK ; read block +LA705 jsr LA7E9 ; stop tape + ldb CSRERR ; get error status + rts +; Read a block from tape - this does the heavy lifting +GETBLK orcc #0x50 ; disable interrupts (timing is important) + bsr LA6F3 ; reverse video of upper left character in direct mode + ldx CBUFAD ; point to destination buffer + clra ; reset read byte +LA712 bsr LA755 ; read a bit + rora ; move bit into accumulator + cmpa #0x3c ; have we synched on the start of the block data yet? + bne LA712 ; brif not + bsr LA749 ; read block type + sta BLKTYP + bsr LA749 ; get block size + sta BLKLEN + adda BLKTYP ; accumulate checksum + sta CCKSUM ; save current checksum + lda BLKLEN ; get back count + sta CSRERR ; initialize counter; we use this since it will be ovewritten later anyway + beq LA73B ; brif empty block +LA72B bsr LA749 ; read a byte + sta ,x ; save in buffer + cmpa ,x+ ; make sure it wrote + bne LA744 ; brif error if it didn't match + adda CCKSUM ; accumulate checksum + sta CCKSUM + dec CSRERR ; read all bytes? + bne LA72B ; brif not +LA73B bsr LA749 ; read checksum from tape + suba CCKSUM ; does it match? + beq LA746 ; brif so + lda #1 ; checksum error flag + skip2 +LA744 lda #2 ; non-RAM error flag +LA746 sta CSRERR ; save error status + rts +LA749 lda #8 ; read 8 bits + sta CPULWD ; initialize counter +LA74D bsr LA755 ; read a bit + rora ; put it into accumulator + dec CPULWD ; got all 8 bits? + bne LA74D ; brif not + rts +LA755 bsr LA75D ; get time between transitions + ldb CPERTM ; get timer + decb + cmpb CMPMID ; set C if timer is below the transition point - high or 1; clear otherwise + rts +LA75D clr CPERTM ; reset timer + tst CBTPHA ; check which phase we synched on + bne LA773 ; brif HI-LO synch +LA763 bsr LA76C ; read input + bcs LA763 ; brif still high +LA767 bsr LA76C ; read input + bcc LA767 ; brif still low + rts +LA76C inc CPERTM ; bump timer + ldb PIA1 ; get input bit to C + rorb + rts +LA773 bsr LA76C ; read input + bcc LA773 ; brif still low +LA777 bsr LA76C ; read output + bcs LA777 ; brif still high + rts +; Start tape and look for sync bytes +CASON orcc #0x50 ; disable interrupts + bsr LA7CA ; turn on tape + clr CPULWD ; reset timer +LA782 bsr LA763 ; wait for low-high transition +LA784 bsr LA7AD ; wait for it to go low again + bhi LA797 ; brif in range for 1200 Hz +LA788 bsr LA7A7 ; wait for it to go high again + blo LA79B ; brif in range for 2400 Hz + dec CPULWD ; decrement counter (synched on low-high) + lda CPULWD ; get counter + cmpa #-96 ; have we seen 96 1-0-1-0 patterns (48 0x55s)? +LA792 bne LA782 ; brif not - wait some more + sta CBTPHA ; save phase we synched on + rts +LA797 bsr LA7A7 ; wait for it to go high again + bhi LA784 ; brif another 1200 Hz, 2 in a row, try again +LA79B bsr LA7AD ; wait for it to go low again + blo LA788 ; brif another 2400 Hz; go try again for high + inc CPULWD ; bump counter + lda CPULWD ; get counter + suba #96 ; set 0 if we've seen enought 0-1-0-1 patterns (0xaa) + bra LA792 ; set phase and return or keep waiting +LA7A7 clr CPERTM ; reset period timer + bsr LA767 ; wait for high + bra LA7B1 ; set flags on result +LA7AD clr CPERTM ; reset period timer + bsr LA777 ; wait for low +LA7B1 ldb CPERTM ; get period count + cmpb CMP0 ; is it too long for 1200Hz? + bhi LA7BA ; brif so - reset counts + cmpb CMP1 ; set C if 2400Hz, clear C if 1200 Hz + rts +LA7BA clr CPULWD ; reset sync counter (too slow or drop out) + rts +; MOTOR command +MOTOR tfr a,b ; save ON/OFF + jsr GETNCH ; eat the ON/OFF token + cmpb #0xaa ; OFF? + beq LA7E9 ; brif so - turn off tape + cmpb #0x88 ; ON? + jsr LA5C9 ; SN error if no match +; Turn on tape +LA7CA lda PIA1+1 ; get motor control value + ora #8 ; turn on bit 3 (starts motor) + bsr LA7F0 ; put it back (dumb but it saves a byte) +LA7D1 ldx ZERO ; maximum delay timer +LA7D3 leax -1,x ; count down + bne LA7D3 ; brif not at 0 yet + rts +; Write a synch leader to tape +WRLDR orcc #0x50 ; disable interrupts + bsr LA7CA ; turn on tape + ldx SYNCLN ; get count of 0x55s to write +LA7DE bsr LA828 ; write a 0x55 + leax -1,x ; done? + bne LA7DE ; brif not + rts +; Write sync bytes and a block, then stop tape +LA7E5 bsr WRLDR ; write sync +LA7E7 bsr SNDBLK ; write block +; Turn off tape +LA7E9 andcc #0xaf ; enable interrupts + lda PIA1+1 ; get control register + anda #0xf7 ; disable motor bit +LA7F0 sta PIA1+1 ; set motor enable bit + rts +; Write a block to tape. +SNDBLK orcc #0x50 ; disable interrupts + ldb BLKLEN ; get block size + stb CSRERR ; initialize character counter + lda BLKLEN ; initialize checksum + beq LA805 ; brif empty block + ldx CBUFAD ; point to tape buffer +LA800 adda ,x+ ; accumulate checksum + decb ; end of block data? + bne LA800 ; brif not +LA805 adda BLKTYP ; accumulate block type into checksum + sta CCKSUM ; save calculated checksum + ldx CBUFAD ; point to buffer + bsr LA828 ; send a 0x55 + lda #0x3c ; and then a 0x3c + bsr LA82A + lda BLKTYP ; send block type + bsr LA82A + lda BLKLEN ; send block size + bsr LA82A + tsta ; empty block? + beq LA824 ; brif so +LA81C lda ,x+ ; send character from block data + bsr LA82A + dec CSRERR ; are we done yet? + bne LA81C ; brif not +LA824 lda CCKSUM ; send checksum + bsr LA82A +LA828 lda #0x55 ; send a 0x55 +LA82A pshs a ; save output byte + ldb #1 ; initialize bit probe +LA82E lda CLSTSN ; get ending value of last cycle + sta PIA1 ; set DA + ldy #LA85C ; point to sine wave table + bitb ,s ; is bit set? + bne LA848 ; brif so - do high frequency +LA83B lda ,y+ ; get next sample (use all for low frequency) + cmpy #LA85C+36 ; end of table? + beq LA855 ; brif so + sta PIA1 ; set output sample + bra LA83B ; do another sample +LA848 lda ,y++ ; get next sample (use every other for high frequency) + cmpy #LA85C+36 ; end of table? + beq LA855 ; brif so + sta PIA1 ; send output sample + bra LA848 ; do another sample +LA855 sta CLSTSN ; save last sample that *would* have been sent + lslb ; shift mask to next bit + bcc LA82E ; brif not done all 8 bits + puls a,pc ; get back original character and return +; This is the sample table for the tape sine wave +LA85C fcb 0x82,0x92,0xaa,0xba,0xca,0xda + fcb 0xea,0xf2,0xfa,0xfa,0xfa,0xf2 + fcb 0xea,0xda,0xca,0xba,0xaa,0x92 + fcb 0x7a,0x6a,0x52,0x42,0x32,0x22 + fcb 0x12,0x0a,0x02,0x02,0x02,0x0a + fcb 0x12,0x22,0x32,0x42,0x52,0x6a +; SET command +SET bsr LA8C1 ; get absolute screen position of graphics block + pshs x ; save character location + jsr LB738 ; evaluate comma then expression in B + puls x ; get back character pointer + cmpb #8 ; valid colour? + bhi LA8D5 ; brif not + decb ; normalize colours + bmi LA895 ; brif colour 0 (use current colour) + lda #0x10 ; 16 patterns per colour + mul + bra LA89D ; go save the colour +LA895 ldb ,x ; get current value + bpl LA89C ; brif not grahpic + andb #0x70 ; keep only the colour + skip1 +LA89C clrb ; reset block to all black +LA89D pshs b ; save colour + bsr LA90D ; force a ) + lda ,x ; get current screen value + bmi LA8A6 ; brif graphic block already + clra ; force all pixels off +LA8A6 anda #0x0f ; keep only pixel data + ora GRBLOK ; set the desired pixel + ora ,s+ ; merge with desired colour +LA8AC ora #0x80 ; force it to be a graphic block + sta ,x ; put new block on screen + rts +; RESET command +RESET bsr LA8C1 ; get address of desired block + bsr LA90D ; force a ) + clra ; zero block (no pixels) + ldb ,x ; is it graphics? + bpl LA8AC ; brif not - just blank the block + com GRBLOK ; invert pixel data + andb GRBLOK ; turn off the desired pixel + stb ,x ; put new pixel data on screen + rts +; Parse SET/RESET/POINT coordinates except for closing ) +LA8C1 jsr LB26A ; make sure it starts with ( +LA8C4 jsr RVEC21 ; do the RAM hook dance + jsr LB70B ; get first coordinate + cmpb #63 ; valid horizontal coordinate + bhi LA8D5 ; brif out of range + pshs b ; save horizontal coordinate + jsr LB738 ; look for , followed by vertical coordinate + cmpb #31 ; in range for vertical? +LA8D5 bhi LA948 ; brif not + pshs b ; save vertical coordinate + lsrb ; divide by two (two blocks per row) + lda #32 ; 32 bytes per row + mul ; now we have the offset into video RAM + ldx #VIDRAM ; point to start of screen + leax d,x ; now X points to the correct character row + ldb 1,s ; get horizontal coordinate + lsrb ; divide by two (two per character cell) + abx ; now we're pointing to the correct character cell + puls a,b ; get back coordinates (vertical in A) + anda #1 ; keep only row offset of vertical + rorb ; get column offset of horizontal to C + rola ; now we have "row * 2 + col" in A + ldb #0x10 ; make a bit mask (one bit left of first pixel) +LA8EE lsrb ; move mask right + deca ; at the right pixel? + bpl LA8EE ; brif not + stb GRBLOK ; save graphics block mask + rts +; POINT function +POINT bsr LA8C4 ; evaluate coordinates + ldb #0xff ; default colour value is -1 (not graphics) + lda ,x ; get character + bpl LA90A ; brif not graphics + anda GRBLOK ; is desired pixel set? + beq LA909 ; brif not - return 0 for "black" + ldb ,x ; get graphics data + lsrb ; shift right 4 to get colour in low bits + lsrb + lsrb + lsrb + andb #7 ; lose the graphics block bias +LA909 incb ; shift colours into 1 to 8 range +LA90A jsr LA5E8 ; convert B to floating point +LA90D jmp LB267 ; make sure we have a ) and return +; CLS command +CLS jsr RVEC22 ; do the RAM hook dance +LA913 beq LA928 ; brif no colour - just do a basic screen clear + jsr LB70B ; evaluate colour number + cmpb #8 ; valid colour? + bhi LA937 ; brif not - do the easter egg + tstb ; color 0? + beq LA925 ; brif so + decb ; normalize to 0 based colour numbers + lda #0x10 ; 16 blocks per colour + mul ; now we have the base code for that colour + orb #0x0f ; set all pixels +LA925 orb #0x80 ; make it a graphics block + skip2 +LA928 ldb #0x60 ; VDG screen space character + ldx #VIDRAM ; point to start of screen +LA92D stx CURPOS ; set cursor position +LA92F stb ,x+ ; blank a character + cmpx #VIDRAM+511 ; end of screen? + bls LA92F ; brif not + rts +LA937 bsr LA928 ; clear te screen + ldx #LA166-1 ; point to the easter egg + jmp LB99C ; go display it +; Evaluate an expression to B, prefixed by a comma, and do FC error if 0 +LA93F jsr LB26D ; force a comma +LA942 jsr LB70B ; evaluate expression to B + tstb ; is it 0? + bne LA984 ; brif not - return +LA948 jmp LB44A ; raise FC error +; SOUND command +SOUND bsr LA942 ; evaluate frequency + stb SNDTON ; save it + bsr LA93F ; evaluate duration (after a comma) +LA951 lda #4 ; constant factor for duration (each increment is 1/15 of a second) + mul + std SNDDUR ; save length of sound (IRQ will count it down) + lda PIA0+3 ; enable 60 Hz interrupt + ora #1 + sta PIA0+3 + clr ARYDIS ; clear array disable flag for some reason + bsr LA9A2 ; connect DAC to MUX output + bsr LA976 ; turn on sound +LA964 bsr LA985 ; store mid range output value and delay + lda #0xfe ; store high value and delay + bsr LA987 + bsr LA985 ; store mid range value and delay + lda #2 ; store low value and delay + bsr LA987 + ldx SNDDUR ; has timer expired? + bne LA964 ; brif not, do another wave +; Disable sound output +LA974 clra ; bit 3 to 0 will disable output + skip2 +; Enable sound output +LA976 lda #8 ; bit 3 set to enable output + sta ,-s ; save desired value + lda PIA1+3 ; get control register value + anda #0xf7 ; reset value + ora ,s+ ; set to desired value + sta PIA1+3 ; set new sound output status +LA984 rts +LA985 lda #0x7e ; mid range value for DAC +LA987 sta PIA1 ; set DAC output value + lda SNDTON ; get frequency +LA98C inca ; increment it (gives shorter count with higher values, so higher frequencies work) + bne LA98C ; brif not done yet + rts +; AUDIO command +AUDIO tfr a,b ; save ON/OFF token + jsr GETNCH ; munch the ON/OFF token + cmpb #0xaa ; OFF? + beq LA974 ; brif so + subb #0x88 ; ON? + jsr LA5C9 ; do SN error if not + incb ; now B is 1 - cassette sound source + bsr LA9A2 ; set MUX input to tape + bra LA976 ; enable sound +; Set MUX source to value in B +LA9A2 ldu #PIA0+1 ; point to PIA0 control register A + bsr LA9A7 ; program bit 0 then fall through for bit 1 +LA9A7 lda ,u ; get control register value + anda #0xf7 ; reset mux control bit + asrb ; shift desired value to C + bcc LA9B0 ; brif this bit is clear + ora #8 ; set the bit +LA9B0 sta ,u++ ; set register value and move to next register + rts +; IRQ service routine +BIRQSV lda PIA0+3 ; check for VSYNC interrupt + bpl LA9C5 ; brif not - return. BUG: should clear HSYNC interrupt status first + lda PIA0+2 ; clear VSYNC interrupt status + ldx >SNDDUR ; are we counting down for SOUND? (force extended in case DP is modified) + beq LA9C5 ; brif not + leax -1,x ; count down one tick + stx >SNDDUR ; save new count (forced extended in case DP is modified) +LA9C5 rti +; JOYSTK function +JOYSTK jsr LB70E ; evaluate which joystick axis is desired + cmpb #3 ; valid axis? + lbhi LB44A ; brif not + tstb ; want axis 0? + bne LA9D4 ; brif not + bsr GETJOY ; read axis data if axis 0 +LA9D4 ldx #POTVAL ; point to axis values + ldb FPA0+3 ; get desired axis + ldb b,x ; get axis value + jmp LB4F3 ; return value +; Read all four joystick axes. Note that this routine will try 10 times to get a value that matches +; the value obtained during the *previous call to this routine*. Thus, if the axis value has changed, +; this routine will do the read *ten times* before just returning the last value. This is assininely +; slow and probably a bug since it seems more logical to look for two matching reads in a row. Note +; also that this routine should be using PSHS and PULS but it doesn't. +GETJOY bsr LA974 ; turn off sound + ldx #POTVAL+4 ; point to the end of the axis data (we'll work backwards) + ldb #3 ; start with axis 3 +LA9E5 lda #10 ; 10 tries to see if we match *the last call* to this routine + std ,--s ; save retry counter and axis number + bsr LA9A2 ; set MUX for the correct axis +LA9EB ldd #0x4080 ; set initial trial value to mid range and the next difference to add/subtract to half +LA9EE sta ,-s ; store the add/subtract value + orb #2 ; keep rs232 output marking + stb PIA1 ; set DAC output to the trial value + eorb #2 ; remove RS232 output value + lda PIA0 ; read the comparator + bmi LA9FF ; brif comparator output is high (DAC is lower than the axis value) + subb ,s ; subtract next bit value (split the difference toward 0) + skip2 +LA9FF addb ,s ; add next bit value (split the different toward infinity) + lda ,s+ ; get bit value back + lsra ; cut in half + cmpa #1 ; have we done that last value for the DAC? + bne LA9EE ; brif not + lsrb ; normalize the axis value + lsrb + cmpb -1,x ; does it match the read from the last call to this routine? + beq LAA12 ; brif so + dec ,s ; are we out of retries? + bne LA9EB ; brif not - try again +LAA12 stb ,-x ; save new value and move pointer back + ldd ,s++ ; get axis counter and clean up retry counter + decb ; move to next axis + bpl LA9E5 ; brif still more axes to do + rts +; This is the "bottom half" of the character fetching routines. +BROMHK cmpa #'9+1 ; is it >= colon? + bhs LAA28 ; brif so Z set if colon, C clear. + cmpa #0x20 ; space? + bne LAA24 ; brif not + jmp GETNCH ; move on to another character if space +LAA24 suba #'0 ; normalize ascii digit to 0-9; we already handled above digit 9 + suba #-'0 ; this will cause a carry for any value that was already positive +LAA28 rts +; Jump table for functions +LAA29 fdb SGN ; SGN 0x80 + fdb INT ; INT 0x81 + fdb ABS ; ABS 0x82 + fdb USRJMP ; USR 0x83 + fdb RND ; RND 0x84 + fdb SIN ; SIN 0x85 + fdb PEEK ; PEEK 0x86 + fdb LEN ; LEN 0x87 + fdb STR ; STR$ 0x88 + fdb VAL ; VAL 0x89 + fdb ASC ; ASC 0x8a + fdb CHR ; CHR$ 0x8b + fdb EOF ; EOF 0x8c + fdb JOYSTK ; JOYSTK 0x8d + fdb LEFT ; LEFT$ 0x8e + fdb RIGHT ; RIGHT$ 0x8f + fdb MID ; MID$ 0x90 + fdb POINT ; POINT 0x91 + fdb INKEY ; INKEY$ 0x92 + fdb MEM ; MEM 0x93 +; Operator precedence and jump table (binary ops except relational) +LAA51 fcb 0x79 ; + + fdb LB9C5 + fcb 0x79 ; - + fdb LB9BC + fcb 0x7b ; * + fdb LBACC + fcb 0x7b ; / + fdb LBB91 + fcb 0x7f ; ^ (exponentiation) + fdb EXPJMP + fcb 0x50 ; AND + fdb LB2D5 + fcb 0x46 ; OR + fdb LB2D4 +; Reserved words table for commands +LAA66 fcs 'FOR' ; 0x80 + fcs 'GO' ; 0x81 + fcs 'REM' ; 0x82 + fcs "'" ; 0x83 + fcs 'ELSE' ; 0x84 + fcs 'IF' ; 0x85 + fcs 'DATA' ; 0x86 + fcs 'PRINT' ; 0x87 + fcs 'ON' ; 0x88 + fcs 'INPUT' ; 0x89 + fcs 'END' ; 0x8a + fcs 'NEXT' ; 0x8b + fcs 'DIM' ; 0x8c + fcs 'READ' ; 0x8d + fcs 'RUN' ; 0x8e + fcs 'RESTORE' ; 0x8f + fcs 'RETURN' ; 0x90 + fcs 'STOP' ; 0x91 + fcs 'POKE' ; 0x92 + fcs 'CONT' ; 0x93 + fcs 'LIST' ; 0x94 + fcs 'CLEAR' ; 0x95 + fcs 'NEW' ; 0x96 + fcs 'CLOAD' ; 0x97 + fcs 'CSAVE' ; 0x98 + fcs 'OPEN' ; 0x99 + fcs 'CLOSE' ; 0x9a + fcs 'LLIST' ; 0x9b + fcs 'SET' ; 0x9c + fcs 'RESET' ; 0x9d + fcs 'CLS' ; 0x9e + fcs 'MOTOR' ; 0x9f + fcs 'SOUND' ; 0xa0 + fcs 'AUDIO' ; 0xa1 + fcs 'EXEC' ; 0xa2 + fcs 'SKIPF' ; 0xa3 + fcs 'TAB(' ; 0xa4 + fcs 'TO' ; 0xa5 + fcs 'SUB' ; 0xa6 + fcs 'THEN' ; 0xa7 + fcs 'NOT' ; 0xa8 + fcs 'STEP' ; 0xa9 + fcs 'OFF' ; 0xaa + fcs '+' ; 0xab + fcs '-' ; 0xac + fcs '*' ; 0xad + fcs '/' ; 0xae + fcs '^' ; 0xaf + fcs 'AND' ; 0xb0 + fcs 'OR' ; 0xb1 + fcs '>' ; 0xb2 + fcs '=' ; 0xb3 + fcs '<' ; 0xb4 +; Reserved word list for functions +LAB1A fcs 'SGN' ; 0x80 + fcs 'INT' ; 0x81 + fcs 'ABS' ; 0x82 + fcs 'USR' ; 0x83 + fcs 'RND' ; 0x84 + fcs 'SIN' ; 0x85 + fcs 'PEEK' ; 0x86 + fcs 'LEN' ; 0x87 + fcs 'STR$' ; 0x88 + fcs 'VAL' ; 0x89 + fcs 'ASC' ; 0x8a + fcs 'CHR$' ; 0x8b + fcs 'EOF' ; 0x8c + fcs 'JOYSTK' ; 0x8d + fcs 'LEFT$' ; 0x8e + fcs 'RIGHT$' ; 0x8f + fcs 'MID$' ; 0x90 + fcs 'POINT' ; 0x91 + fcs 'INKEY$' ; 0x92 + fcs 'MEM' ; 0x93 +; Jump table for commands +LAB67 fdb FOR ; 0x80 FOR + fdb GO ; 0x81 GO + fdb REM ; 0x82 REM + fdb REM ; 0x83 ' + fdb REM ; 0x84 ELSE + fdb IFTOK ; 0x85 IF + fdb DATA ; 0x86 DATA + fdb PRINT ; 0x87 PRINT + fdb ON ; 0x88 ON + fdb INPUT ; 0x89 INPUT + fdb ENDTOK ; 0x8a END + fdb NEXT ; 0x8b NEXT + fdb DIM ; 0x8c DIM + fdb READ ; 0x8d READ + fdb RUN ; 0x8e RUN + fdb RESTOR ; 0x8f RESTORE + fdb RETURN ; 0x90 RETURN + fdb STOP ; 0x91 STOP + fdb POKE ; 0x92 POKE + fdb CONT ; 0x93 CONT + fdb LIST ; 0x94 LIST + fdb CLEAR ; 0x95 CLEAR + fdb NEW ; 0x96 NEW + fdb CLOAD ; 0x97 CLOAD + fdb CSAVE ; 0x98 CSAVE + fdb OPEN ; 0x99 OPEN + fdb CLOSE ; 0x9a CLOSE + fdb LLIST ; 0x9b LLIST + fdb SET ; 0x9c SET + fdb RESET ; 0x9d RESET + fdb CLS ; 0x9e CLS + fdb MOTOR ; 0x9f MOTOR + fdb SOUND ; 0xa0 SOUND + fdb AUDIO ; 0xa1 AUDIO + fdb EXEC ; 0xa2 EXEC + fdb SKIPF ; 0xa3 SKIPF +; Error message table +LABAF fcc 'NF' ; 0 NEXT without FOR + fcc 'SN' ; 1 Syntax error + fcc 'RG' ; 2 RETURN without GOSUB + fcc 'OD' ; 3 Out of data + fcc 'FC' ; 4 Illegal function call + fcc 'OV' ; 5 Overflow + fcc 'OM' ; 6 Out of memory + fcc 'UL' ; 7 Undefined line number + fcc 'BS' ; 8 Bad subscript + fcc 'DD' ; 9 Redimensioned array + fcc '/0' ; 10 Division by 0 + fcc 'ID' ; 11 Illegal direct statement + fcc 'TM' ; 12 Type mismatch + fcc 'OS' ; 13 Out of string space + fcc 'LS' ; 14 String too long + fcc 'ST' ; 15 String formula too complex + fcc 'CN' ; 16 Can't continue + fcc 'FD' ; 17 Bad file data + fcc 'AO' ; 18 File already open + fcc 'DN' ; 19 Device number error + fcc 'IO' ; 20 Input/output error + fcc 'FM' ; 21 Bad file mode + fcc 'NO' ; 22 File not open + fcc 'IE' ; 23 Input past end of file + fcc 'DS' ; 24 Direct statement in file +LABE1 fcn ' ERROR' +LABE8 fcn ' IN ' +LABED fcb 0x0d +LABEE fcc 'OK' + fcb 0x0d,0x00 +LABF2 fcb 0x0d + fcn 'BREAK' +; Search stack for a FOR/NEXT stack frame. Stop search when something that isn't a FOR/NEXT +; stack frame is found. Enter with the index variable descriptor pointer in VARDES, or NULL +; for the first match. +; +; NOTE: this routine can be reworked to avoid needed two temporaries by taking advantage of the +; 6809's registers. This requires some minor tweaks where the routine is called. Further, the +; use of B is completely pointless and, even if B is going to be used, why is it reloaded on +; every loop? +LABF9 leax 4,s ; skip past our caller and the main command loop return address +LABFB ldb #18 ; each FOR/NEXT frame is 18 bytes + stx TEMPTR ; save current search pointer + lda ,x ; get first byte of this frame + suba #0x80 ; set to 0 if FOR/NEXT + bne LAC1A ; brif not FOR/NEXT (we hit the end of the stack for a GOSUB frame) + ldx 1,x ; get index variable descriptor + stx TMPTR1 ; save it + ldx VARDES ; get desired index descriptor + beq LAC16 ; brif NULL - we found something + cmpx TMPTR1 ; does this one match? + beq LAC1A ; brif so + ldx TEMPTR ; get back frame pointer + abx ; move to next entry + bra LABFB ; check next block of data +LAC16 ldx TMPTR1 ; get index variable of this frame + stx VARDES ; set it as the one found +LAC1A ldx TEMPTR ; get matching frame pointer + tsta ; set Z if FOR/NEXT + rts +; This is a block copy routine which copies from top to bottom. It's not clear that the use of +; this routine actually saves any ROM space compared to just implementing the copies directly +; once all the marshalling to set up the parameter variables is taken into account. +LAC1E bsr LAC37 ; check to see if stack collides with D +LAC20 ldu V41 ; point to destination + leau 1,u ; offset for pre-dec + ldx V43 ; point to source + leax 1,x ; offset for pre-dec +LAC28 lda ,-x ; get source byte + pshu a ; store at destination (sta ,-u would be less weird) + cmpx V47 ; at the bottom of the copy? + bne LAC28 ; brif not + stu V45 ; save final destination address +LAC32 rts +; Check for 2*B (0 <= B <= 127) bytes for free memory +LAC33 clra ; zero extend + aslb ; times 2 (loses bit 7 of B) + addd ARYEND ; add to top of used memory +LAC37 addd #STKBUF ; add a fudge factor for interpreter operation + bcs LAC44 ; brif >65535! + sts BOTSTK ; get current stack pointer + cmpd BOTSTK ; is our new address above that? + blo LAC32 ; brif not - no error +LAC44 ldb #6*2 ; raise OM error +; The error servicing routine +LAC46 jsr RVEC16 ; do the RAM hook dance (ON ERROR reserved hook) +LAC49 jsr RVEC17 ; do the RAM hook dance again + jsr LA7E9 ; turn off tape + jsr LA974 ; disable sound + jsr LAD33 ; reset stack, etc. + clr DEVNUM ; reset output to screen + jsr LB95C ; do a newline + jsr LB9AF ; send a ? + ldx #LABAF ; point to error table + abx ; offset to correct message + bsr LACA0 ; send a char from X + bsr LACA0 ; send another char from X + ldx #LABE1-1 ; point to "ERROR" message +LAC68 jsr LB99C ; print ERROR message (or BREAK) + lda CURLIN ; are we in immediate mode? + inca + beq LAC73 ; brif not - go to immediate mode + jsr LBDC5 ; print "IN ****" +; This is the immediate mode loop +LAC73 jsr LB95C ; do a newline if needed +LAC76 ldx #LABEE-1 ; point to prompt (without leading CR) + jsr LB99C ; show prompt +LAC7C jsr LA390 ; read an input line + ldu #0xffff ; flag immediate mode + stu CURLIN + bcs LAC7C ; brif we ended on BREAK - just go for another line + tst CINBFL ; EOF? + lbne LA4BF ; brif so + stx CHARAD ; save start of input line as input pointer + jsr GETNCH ; get character from input line + beq LAC7C ; brif no input + bcs LACA5 ; brif numeric - adding or removing a line number + ldb #2*24 ; code for "direct statement in file" + tst DEVNUM ; keyboard input? + bne LAC46 ; brif not - complain about direct statement + jsr LB821 ; go tokenize the input line + jmp LADC0 ; go execute the newly tokenized line +LACA0 lda ,x+ ; get character and advance pointer + jmp LB9B1 ; output it +LACA5 jsr LAF67 ; convert line number to binary + ldx BINVAL ; get converted number + stx LINHDR ; put it before the line we just read + jsr LB821 ; tokenize the input line + stb TMPLOC ; save line length + bsr LAD01 ; find where the line should be in the program + bcs LACC8 ; brif the line number isn't already present + ldd V47 ; get address where the line is in the program + subd ,x ; get the difference between here and the end of the line (negative) + addd VARTAB ; subtract line length from the end of the program + std VARTAB ; save new end of program address + ldu ,x ; get start of next line +LACC0 pulu a ; get source byte (lda ,u+ would be less weird) + sta ,x+ ; move it down + cmpx VARTAB ; have we moved everything yet? + bne LACC0 ; brif not +LACC8 lda LINBUF ; see if there is actually a line to input + beq LACE9 ; brif not - we just needed to remove the line + ldd VARTAB ; get current end of program + std V43 ; set as source pointer + addb TMPLOC ; add in the length of the new line + adca #0 + std V41 ; save destination pointer + jsr LAC1E ; make sure there's enough room and then make a hole for the new line + ldu #LINHDR-2 ; point to the line (well, 4 bytes before it, incl line number and fake next line pointer) +LACDD pulu a ; get byte from new line (lda ,u+ would be less weird) + sta ,x+ ; stow it + cmpx V45 ; at the end of the hole we just made? + bne LACDD ; brif not + ldx V41 ; get save new top of program address + stx VARTAB +LACE9 bsr LAD21 ; reset variables, etc. + bsr LACEF ; adjust next line pointers + bra LAC7C ; go read another input line +; Recompute next line pointers +LACEF ldx TXTTAB ; point to start of program +LACF1 ldd ,x ; get address of next line + beq LAD16 ; brif end of program + leau 4,x ; move past pointer and line number +LACF7 lda ,u+ ; are we at the end of the line? + bne LACF7 ; brif not + stu ,x ; save new next line pointer + ldx ,x ; point to next line + bra LACF1 ; process the next line +; Find a line in the program +LAD01 ldd BINVAL ; get desired line number + ldx TXTTAB ; point to start of program +LAD05 ldu ,x ; get address of next line + beq LAD12 ; brif end of program + cmpd 2,x ; do we have a match? + bls LAD14 ; brif our search number is <= the number here + ldx ,x ; move to next line + bra LAD05 ; check another line +LAD12 orcc #1 ; set C for not found +LAD14 stx V47 ; save address of matching line *or* line just after where it would have been +LAD16 rts +; NEW command +; This routine has multiple entry points used for various "levels" of NEW +NEW bne LAD14 ; brif there was input given; should be LAD16! +LAD19 ldx TXTTAB ; point to start of program + clr ,x+ ; blank out program (with NULL next line pointer) + clr ,x+ + stx VARTAB ; save end of program +LAD21 ldx TXTTAB ; get start of program + jsr LAEBB ; put input pointer there +LAD26 ldx MEMSIZ ; reset string space + stx STRTAB + jsr RESTOR ; reset DATA pointer + ldx VARTAB ; clear out scalars and arrays + stx ARYTAB + stx ARYEND +LAD33 ldx #STRSTK ; reset the string stack + stx TEMPPT + ldx ,s ; get return address (we're going to reset the stack) + lds FRETOP ; reset the stack to top of memory + clr ,-s ; put stopper so FOR/NEXT search will actually stop here + clr OLDPTR ; reset "CONT" state + clr OLDPTR+1 + clr ARYDIS ; un-disable arrays + jmp ,x ; return to original caller +; FOR command +FOR lda #0x80 ; disable array parsing + sta ARYDIS + jsr LET ; assign start value to index + jsr LABF9 ; search stack for matching FOR/NEXT frame + leas 2,s ; lose return address + bne LAD59 ; brif variable not already being used + ldx TEMPTR ; get address of matched data + leas b,x ; move stack pointer to the end of it (B is set to 18 in the stack search) +LAD59 ldb #9 ; is there room for 18 bytes in memory? + jsr LAC33 + jsr LAEE8 ; get address of the end of this statement in X + ldd CURLIN ; get line number + pshs x,b,a ; save next line address and current line number + ldb #0xa5 ; make sure we have TO + jsr LB26F + jsr LB143 ; make sure we have a numeric index + jsr LB141 ; evaluate terminal condition value + ldb FP0SGN ; pack FPA0 in place + orb #0x7f + andb FPA0 + stb FPA0 + ldy #LAD7F ; where to come back to + jmp LB1EA ; stash terminal condition on the stack +LAD7F ldx #LBAC5 ; point to FP 1.0 (default step) + jsr LBC14 ; unpack it to FPA0 + jsr GETCCH ; get character after the terminal + cmpa #0xa9 ; is it STEP? + bne LAD90 ; brif not + jsr GETNCH ; eat STEP + jsr LB141 ; evaluate step condition +LAD90 jsr LBC6D ; get "status" of FPA0 + jsr LB1E6 ; stash FPA0 on the stack (for step value) + ldd VARDES ; get variable descriptor pointer + pshs d ; put that on the stack too + lda #0x80 ; flag the frame as a FOR/NEXT frame + pshs a +; Main command interpretation loop +LAD9E jsr RVEC20 ; do the RAM hook dance + andcc #0xaf ; make sure interrupts are running + bsr LADEB ; check for BREAK/pause + ldx CHARAD ; get input pointer + stx TINPTR ; save input pointer for start of line + lda ,x+ ; get current input character + beq LADB4 ; brif end of line - move to another line + cmpa #': ; end of statement? + beq LADC0 ; brif so - keep processing +LADB1 jmp LB277 ; raise a syntax error +LADB4 lda ,x++ ; get MSB of next line pointer and skip past pointer + sta ENDFLG + beq LAE15 ; brif MSB of next line address is 0 (do END) + ldd ,x+ ; get line number but only advance one + std CURLIN ; set current line number + stx CHARAD ; set input pointer to one before line text +LADC0 jsr GETNCH ; move past statement separator or to first character in line + bsr LADC6 ; process a command + bra LAD9E ; handle next statement or line +LADC6 beq LAE40 ; return if end of statement + tsta ; is it a token? + lbpl LET ; brif not - do a LET + cmpa #0xa3 ; above SKIPF? + bhi LADDC ; brif so + ldx COMVEC+3 ; point to jump table + lsla ; two bytes per entry (loses the token bias) + tfr a,b ; put it in B for unsigned ABX + abx + jsr GETNCH ; move past token + jmp [,x] ; transfer control to the handler (which will return to the main loop) +LADDC cmpa #0xb4 ; is it a non-executable token? + bls LADB1 ; brif so + jmp [COMVEC+13] ; transfer control to ECB command handler +; RESTORE command +RESTOR ldx TXTTAB ; point to beginning of the program + leax -1,x ; move back one (to compensate for "GETNCH") +LADE8 stx DATPTR ; save as new data pointer + rts +; BREAK check +LADEB jsr KEYIN ; read keyboard + beq LADFA ; brif no key down +LADF0 cmpa #3 ; BREAK? + beq STOP ; brif so - do a STOP + cmpa #0x13 ; pause (SHIFT-@)? + beq LADFB ; brif so + sta IKEYIM ; cache key for later INKEY$ so break check doesn't break INKEY$ +LADFA rts +LADFB jsr KEYIN ; read keyboard + beq LADFB ; brif no key down + bra LADF0 ; process pressed key in case BREAK or SHIFT-@ again +; END command +ENDTOK jsr LA426 ; close files + jsr GETCCH ; re-get input character + bra LAE0B +; STOP command +STOP orcc #1 ; flag "STOP" +LAE0B bne LAE40 ; brif not end of statement + ldx CHARAD ; save current input pointer + stx TINPTR +LAE11 ror ENDFLG ; save END/STOP flag (C) + leas 2,s ; lose return address +LAE15 ldx CURLIN ; get current input line (end of program comes here) + cmpx #0xffff ; immediate mode? + beq LAE22 ; brif so + stx OLDTXT ; save line where we stopped executing + ldx TINPTR ; get input pointer + stx OLDPTR ; save location where we stopped executing +LAE22 clr DEVNUM ; reset to screen/keyboard + ldx #LABF2-1 ; point to BREAK message + tst ENDFLG ; are we doing "BREAK"? + lbpl LAC73 ; brif not + jmp LAC68 ; go do the BREAK message and return to main loop +; CONT command +CONT bne LAE40 ; brif not end of statement + ldb #2*16 ; code for can't continue + ldx OLDPTR ; get saved execution pointer + lbeq LAC46 ; brif no saved pointer - raise CN error + stx CHARAD ; reset input pointer + ldx OLDTXT ; reset current line number + stx CURLIN +LAE40 rts +; CLEAR command +CLEAR beq LAE6F ; brif no argument + jsr LB3E6 ; evaluate string space size + pshs d ; save it + ldx MEMSIZ ; get memory size (top of memory) + jsr GETCCH ; is there anything after the string space size? + beq LAE5A ; brif not + jsr LB26D ; force a comma + jsr LB73D ; get top of memory value in X + leax -1,x ; move back one (top of cleared space) + cmpx TOPRAM ; is it within the memory available? + bhi LAE72 ; brif higher than top of memory - OM error +LAE5A tfr x,d ; so we can do math for checking memory usage + subd ,s++ ; subtract out string space value + bcs LAE72 ; brif less than 0 + tfr d,u ; U is bottom of cleared space + subd #STKBUF ; also account for slop space + bcs LAE72 ; brif less than 0 + subd VARTAB ; is there still room for the program? + blo LAE72 ; brif not + stu FRETOP ; set top of free memory + stx MEMSIZ ; set size of usable memory +LAE6F jmp LAD26 ; erase variables, etc. +LAE72 jmp LAC44 ; raise OM error +; RUN command +RUN jsr RVEC18 ; do the RAM hook dance + jsr LA426 ; close any open files + jsr GETCCH ; is there a line number + lbeq LAD21 ; brif no line number - start from beginning + jsr LAD26 ; clear variables, etc. + bra LAE9F ; "GOTO" the line number +; GO command (GOTO and GOSUB) +GO tfr a,b ; save TO/SUB +LAE88 jsr GETNCH ; eat the TO/SUB token + cmpb #0xa5 ; TO? + beq LAEA4 ; brif GOTO + cmpb #0xa6 ; SUB? + bne LAED7 ; brif not + ldb #3 ; room for 6 bytes? + jsr LAC33 + ldu CHARAD ; get input pointer + ldx CURLIN ; get line number + lda #0xa6 ; flag for GOSUB frame + pshs u,x,a ; set stack frame +LAE9F bsr LAEA4 ; do "GOTO" + jmp LAD9E ; go back to main loop +; Actual GOTO is here +LAEA4 jsr GETCCH ; get current input + jsr LAF67 ; convert number to binary + bsr LAEEB ; move input pointer to end of statement + leax 1,x ; point to start of next line + ldd BINVAL ; get desired line number + cmpd CURLIN ; is it beyond here? + bhi LAEB6 ; brif so + ldx TXTTAB ; start search at beginning of program +LAEB6 jsr LAD05 ; find line number + bcs LAED2 ; brif not found +LAEBB leax -1,x ; move to just before start of line + stx CHARAD ; reset input pointer +LAEBF rts +; RETURN command +RETURN bne LAEBF ; exit if argument given + lda #0xff ; set VARDES to an illegal value so we ignore FOR frames + sta VARDES + jsr LABF9 ; look for a GOSUB frame + tfr x,s ; reset stack + cmpa #0xa6-0x80 ; is it a GOSUB frame? + beq LAEDA ; brif so + ldb #2*2 ; code for RETURN without GOSUB + skip2 +LAED2 ldb #7*2 ; code for undefined line number + jmp LAC46 ; raise error +LAED7 jmp LB277 ; raise syntax error +LAEDA puls a,x,u ; get back saved line number and input pointer + stx CURLIN ; reset line number + stu CHARAD ; reset input pointer +; DATA command +DATA bsr LAEE8 ; move input pointer to end of statement + skip2 +; REM command (also ELSE) +REM bsr LAEEB ; move input pointer to end of line + stx CHARAD ; save new input pointer +LAEE7 rts +; Return end of statement (LAEE8) or line (AEEB) in X +LAEE8 ldb #': ; colon is statement terminator + skip1lda +LAEEB clrb ; make main terminator NUL + stb CHARAC ; save terminator + clrb ; end of line - always terminates + ldx CHARAD ; get input pointer +LAEF1 tfr b,a ; save secondary terminator + ldb CHARAC ; get main terminator + sta CHARAC ; save secondary +LAEF7 lda ,x ; get input character + beq LAEE7 ; brif end of line + pshs b ; save terminator + cmpa ,s+ ; does it match? + beq LAEE7 ; brif so - bail + leax 1,x ; move pointer ahead + cmpa #'" ; start of string? + beq LAEF1 ; brif so + inca ; functon token? + bne LAF0C ; brif not + leax 1,x ; skip second part of function token +LAF0C cmpa #0x85+1 ; IF? + bne LAEF7 ; brif not + inc IFCTR ; bump "IF" count + bra LAEF7 ; get check another input character +; IF command +IFTOK jsr LB141 ; evaluate condition + jsr GETCCH ; find out what's after the conditin + cmpa #0x81 ; GO? + beq LAF22 ; treat same as THEN + ldb #0xa7 ; make sure we have a THEN + jsr LB26F +LAF22 lda FP0EXP ; get true/false (false is 0) + bne LAF39 ; brif condition true + clr IFCTR ; reset IF counter +LAF28 bsr DATA ; skip over statement + tsta ; end of line? + beq LAEE7 ; brif so + jsr GETNCH ; get start of this statement + cmpa #0x84 ; ELSE? + bne LAF28 ; brif not + dec IFCTR ; is it a matching ELSE? + bpl LAF28 ; brif not - keep looking + jsr GETNCH ; eat the ELSE +LAF39 jsr GETCCH ; get current input + lbcs LAEA4 ; brif numeric - to a GOTO + jmp LADC6 ; let main loop interpret the next command +; ON command +ON jsr LB70B ; evaluate index expression +LAF45 ldb #0x81 ; make sure we have "GO" + jsr LB26F + pshs a ; save TO/SUB + cmpa #0xa6 ; SUB? + beq LAF54 ; brif so + cmpa #0xa5 ; TO? +LAF52 bne LAED7 ; brif not +LAF54 dec FPA0+3 ; are we at the right index? + bne LAF5D ; brif not + puls b ; get TO/SUB token + jmp LAE88 ; go do GOTO or GOSUB +LAF5D jsr GETNCH ; munch a character + bsr LAF67 ; parse line number + cmpa #', ; is there another line following? + beq LAF54 ; brif so - see if we're there yet + puls b,pc ; clean up TO/SUB token and return - we fell through +; Parse a line number +LAF67 ldx ZERO ; initialize line number accumulator to 0 + stx BINVAL +LAF6B bcc LAFCE ; brif not numeric + suba #'0 ; adjust to actual value of digit + sta CHARAC ; save digit + ldd BINVAL ; get accumulated number + cmpa #24 ; will this overflow? + bhi LAF52 ; brif so - raise syntax error + aslb ; times 2 + rola + aslb ; times 4 + rola + addd BINVAL ; times 5 + aslb ; times 10 + rola + addb CHARAC ; add in digit + adca #0 + std BINVAL ; save new accumulated number + jsr GETNCH ; fetch next character + bra LAF6B ; process next digit +; LET command (the LET keyword requires Extended Basic) +LET jsr LB357 ; evaluate destination variable + stx VARDES ; save descriptor pointer + ldb #0xb3 ; make sure we have = + jsr LB26F + lda VALTYP ; get destination variable type + pshs a ; save it for later + jsr LB156 ; evaluate the expression to assign + puls a ; get back original variable type + rora ; put type in C + jsr LB148 ; make sure the current result matches the type + lbeq LBC33 ; bri fnumeric - copy FPA0 to variable +LAFA4 ldx FPA0+2 ; point to descriptor of replacement string + ldd FRETOP ; get bottom of string space + cmpd 2,x ; is the string already in string space? + bhs LAFBE ; brif so + cmpx VARTAB ; is the descriptor in variable space? + blo LAFBE ; brif not +LAFB1 ldb ,x ; get length of string + jsr LB50D ; allocate space for this string + ldx V4D ; get descriptor pointer back + jsr LB643 ; copy string into string space + ldx #STRDES ; point to temporary string descriptor +LAFBE stx V4D ; save descriptor pointer + jsr LB675 ; remove string from string stack if appropriate + ldu V4D ; get back replacement descriptor + ldx VARDES ; get target descriptor + pulu a,b,y ; get string length (A) and data pointer (Y) + sta ,x ; save new length + sty 2,x ; save new pointer +LAFCE rts +; READ and INPUT commands. +LAFCF fcc '?REDO' ; The ?REDO message + fcb 0x0d,0x00 +LAFD6 ldb #2*17 ; bad file data code + tst DEVNUM ; are we reading from the keyboard? + beq LAFDF ; brif so +LAFDC jmp LAC46 ; raise the error +LAFDF lda INPFLG ; are we doing INPUT? + beq LAFEA ; brif so + ldx DATTXT ; get line number where the DATA statement happened + stx CURLIN ; set current line number to that so can report the correct location + jmp LB277 ; raise a syntax error on bad data +LAFEA ldx #LAFCF-1 ; show the ?REDO if we're doing INPUT + jsr LB99C + ldx TINPTR ;* reset input pointer to start of statement (this will cause the + stx CHARAD ;* INPUT statement to be re-executed + rts +INPUT ldb #11*2 ; code for illegal direct statement + ldx CURLIN ; are we in immediate mode? + leax 1,x + beq LAFDC ; brif so - raise ID error + bsr LB002 ; go do the INPUT thing + clr DEVNUM ; reset device to screen/keyboard + rts +LB002 cmpa #'# ; is there a device number? + bne LB00F ; brif not + jsr LA5A5 ; parse device number + jsr LA3ED ; make sure it's valid for input + jsr LB26D ; make sure we have a comma after the device number +LB00F cmpa #'" ; is there a prompt string? + bne LB01E ; brif not + jsr LB244 ; parse the prompt string + ldb #'; ; make sure we have a semicolon after the prompt + jsr LB26F + jsr LB99F ; print the prompt +LB01E ldx #LINBUF ; point to line input buffer + clr ,x ; NUL first byte to indicate no data + tst DEVNUM ; is it keyboard input? + bne LB049 ; brif not + bsr LB02F ; read a line from the keyboard + ldb #', ; put a comma at the start of the buffer + stb ,x + bra LB049 ; go process some input +LB02F jsr LB9AF ; send a ? + jsr LB9AC ; send a space +LB035 jsr LA390 ; read input from the keyboard + bcc LB03F ; brif not BREAK + leas 4,s ; clean up stack +LB03C jmp LAE11 ; go process BREAK +LB03F ldb #2*23 ; input past end of file error code + tst CINBFL ; was it EOF? + bne LAFDC ; brif so - raise the error + rts +READ ldx DATPTR ; fetch current DATA pointer + skip1lda ; set A to nonzero (for READ) +LB049 clra ; set A to zero (for INPUT) + sta INPFLG ; record whether we're doing READ or INPUT + stx DATTMP ; save current input location +LB04E jsr LB357 ; evaluate a variable (destination of data) + stx VARDES ; save descriptor + ldx CHARAD ; save interpreter input pointer + stx BINVAL + ldx DATTMP ; get data pointer + lda ,x ; is there anything to read? + bne LB069 ; brif so + lda INPFLG ; is it INPUT? + bne LB0B9 ; brif not + jsr RVEC10 ; do the RAM hook dance + jsr LB9AF ; send a ? (so subsequent lines get ??) + bsr LB02F ; go read an input line +LB069 stx CHARAD ; save data pointer + jsr GETNCH ; fetch next data character + ldb VALTYP ; do we want a number? + beq LB098 ; brif so + ldx CHARAD ; get input pointer + sta CHARAC ; save initial character as the delimiter + cmpa #'" ; do we have a string delimiter? + beq LB08B ; brif so - use " as both delimiters + leax -1,x ; back up input if we don't have a delimiter + clra ; set delimiter to NUL (end of line) + sta CHARAC + jsr LA35F ; set up print parameters + tst PRTDEV ; is it a file type device? + bne LB08B ; brif so - use two NULs + lda #': ; use colon as one delimiter + sta CHARAC + lda #', ; and use comma as the other +LB08B sta ENDCHR ; save second terminator + jsr LB51E ; parse out the string + jsr LB249 ; move input pointer past the string + jsr LAFA4 ; assign the string to the variable + bra LB09E ; go see if there's more to read +LB098 jsr LBD12 ; parse a numeric string + jsr LBC33 ; assign the numbe to the variable +LB09E jsr GETCCH ; get current input character + beq LB0A8 ; brif end of line + cmpa #', ; check for comma + lbne LAFD6 ; brif not - we have bad data +LB0A8 ldx CHARAD ; get current data pointer + stx DATTMP ; save the data pointer + ldx BINVAL ; restore the interpreter input pointer + stx CHARAD + jsr GETCCH ; get current input from program + beq LB0D5 ; brif end of statement + jsr LB26D ; make sure there's a comma between variables + bra LB04E ; go read another item +LB0B9 stx CHARAD ; reset input pointer + jsr LAEE8 ; search for end of statement + leax 1,x ; move past end of statement + tsta ; was it end of line? + bne LB0CD ; brif not + ldb #2*3 ; code for out of data + ldu ,x++ ; get pointer to next line + beq LB10A ; brif end of program - raise OD error + ldd ,x++ ; get line number + std DATTXT ; record it for raising errors in DATA statements +LB0CD lda ,x ; do we have a DATA statement? + cmpa #0x86 + bne LB0B9 ; brif not - keep scanning + bra LB069 ; go process the input +LB0D5 ldx DATTMP ; get data pointer + ldb INPFLG ; were we doing READ? + lbne LADE8 ; brif so - save DATA pointer + lda ,x ; is there something after the input in the input buffer? + beq LB0E7 ; brif not - we consumed everything + ldx #LB0E8-1 ; print the ?EXTRA IGNORED message + jmp LB99C +LB0E7 rts +LB0E8 fcc '?EXTRA IGNORED' + fcb 0x0d,0x00 +; NEXT command +NEXT bne LB0FE ; brif argument given + ldx ZERO ; set to NULL descriptor pointer + bra LB101 ; go process "any index will do" +LB0FE jsr LB357 ; evaluate the variable +LB101 stx VARDES ; save the index we're looking for + jsr LABF9 ; search the stack for the matching frame + beq LB10C ; brif we found a matching frame + ldb #0 ; code for NEXT without FOR +LB10A bra LB153 ; raise the error +LB10C tfr x,s ; reset the stack to the start of the stack frame + leax 3,x ; point to the STEP value + jsr LBC14 ; copy the value to FPA0 + lda 8,s ; get step direction + sta FP0SGN ; save as sign of FPA0 + ldx VARDES ; point to index variable + jsr LB9C2 ; add (X) to FPA0 (steps the index) + jsr LBC33 ; save new value to the index + leax 9,s ; point to terminal condition + jsr LBC96 ; compare the new index value with the terminal + subb 8,s ; set B=0 if we hit the terminal (or passed it with nonzero step) + beq LB134 ; brif loop complete + ldx 14,s ; restore line number and input pointer to start of loop + stx CURLIN + ldx 16,s + stx CHARAD +LB131 jmp LAD9E ; return to interpretation loop +LB134 leas 18,s ; remove the frame from the stack + jsr GETCCH ; get character after the index + cmpa #', ; do we have more indexes? + bne LB131 ; brif not + jsr GETNCH ; munch the comma + bsr LB0FE ; go process another value +; NOTE: despite the BSR on the preceding line, execution of the NEXT command will not fall +; through this point, nor will the stack grow without bound. The BSR is required to make sure +; the stack is aligned properly for the stack search for the subsequent index variable. +; +; The following is the expression evaluation system. It has various entry points including for type +; checking. This really consists of two co-routines, one for evaluating operators and one for individual +; terms. However, it does some rather confusing stack operations so it's a bit difficult to follow +; just how some of this works. +; +; Evaluate numeric expression +LB141 bsr LB156 ; evaluate an expression +; TM error if string +LB143 andcc #0xfe ; clear C to indicate we want a number + skip2keepc +; TM error if numeric +LB146 orcc #1 ; set C to indicate we want a string +; TM error if: C = 1 and number, OR C = 0 and string +LB148 tst VALTYP ; set flags on the current value to (doesn't change C) + bcs LB14F ; brif we want a string + bpl LB0E7 ; brif we have a number (we want a number) + skip2 +LB14F bmi LB0E7 ; brif we have a string (we want a string) +LB151 ldb #12*2 ; code for TM error +LB153 jmp LAC46 ; raise the error +; The general expression evaluation entry point +LB156 bsr LB1C6 ; back up input pointer to compensate for GETNCH below +LB158 clra ; set operator precedence to 0 (no previous operator) + skip2 +LB15A pshs b ; save relational operator flags + pshs a ; save previous operator precedence + ldb #1 ; make sure we aren't overflowing the stack + jsr LAC33 + jsr LB223 ; go evaluate the first term +LB166 clr TRELFL ; flag no relational operators seen +LB168 jsr GETCCH ; get input character +LB16A suba #0xb2 ; token for > (lowest relational operator) + blo LB181 ; brif below relational operators + cmpa #3 ; there are three relational operators, is it one? + bhs LB181 ; brif not + cmpa #1 ; set C if > + rola ; shift C into bit 0 (4: <, 2: =, 1: >) + eora TRELFL ; flip the bit for this operator + cmpa TRELFL ; did the result get lower? + blo LB1DF ; brif so - we have a duplicate so raise an error + sta TRELFL ; save new operator flags + jsr GETNCH ; munch the operator + bra LB16A ; go see if we have another one +LB181 ldb TRELFL ; do we have a relational comparison? + bne LB1B8 ; brif so + lbcc LB1F4 ; brif the token is above the relational operators + adda #7 ; put operators starting at 0 + bhs LB1F4 ; brif we're above 0 - it's an operator, Jim + adca VALTYP ; add carry, numeric flag, and modified token number + lbeq LB60F ; brif we have string and A is + - do concatenation + adca #-1 ; restore operator number + pshs a ; save operator number + asla ; times 2 + adda ,s+ ; and times 3 (3 bytes per entry) + ldx #LAA51 ; point to operator pecedence and jump table + leax a,x ; point to correct entry +LB19F puls a ; get precedence of previous operation + cmpa ,x ; is hit higher (or same) than the current one? + bhs LB1FA ; brif so - we need to process that operator + bsr LB143 ; TM error if we have a string +LB1A7 pshs a ; save previous operation precedence + bsr LB1D4 ; push operator handler address and FPA0 onto the stack + ldx RELPTR ; get pointer to arithmetic/logical table entry for last operation + puls a ; get back precedence + bne LB1CE ; brif we had a relational operation + tsta ; check precedence of previous operation + lbeq LB220 ; brif end of expression + bra LB203 ; go handle operation +LB1B8 asl VALTYP ; get type of value to C + rolb ; mix it in to bit 0 of relational flags + bsr LB1C6 ; back up input pointer + ldx #LB1CB ; point to relational operator precedence and handler + stb TRELFL ; save relational comparison flags + clr VALTYP ; result will be numeric + bra LB19F ; to process the operation +LB1C6 ldx CHARAD ; get input pointer + jmp LAEBB ; back it up one and put it back +LB1CB fcb 0x64 ; precedence of relational comparison + fdb LB2F4 ; handler address for relational comparison +LB1CE cmpa ,x ; is last done operation higher (or same) precedence? + bhs LB203 ; brif so - go process it + bra LB1A7 ; go push things on the stack and process this operation otherwise +LB1D4 ldd 1,x ; get address of operatorroutine + pshs d ; save it + bsr LB1E2 ; push FPA0 onto the stack + ldb TRELFL ; get back relational operator flags + lbra LB15A ; go evaluate another operation +LB1DF jmp LB277 ; raise a syntax error +LB1E2 ldb FP0SGN ; get sign of FPA0 + lda ,x ; get precedence of this operation +LB1E6 puls y ; get back original caller + pshs b ; save sign +LB1EA ldb FP0EXP ; get exponent + ldx FPA0 ; get mantissa + ldu FPA0+2 + pshs u,x,b ; stow FPA0 sign and mantissa + jmp ,y ; return to caller +LB1F4 ldx ZERO ; point to dummy value + lda ,s+ ; get precedence of previous operation (and set flags) + beq LB220 ; brif end of expression +LB1FA cmpa #0x64 ; relational operation? + beq LB201 ; brif so + jsr LB143 ; type mismatch if string +LB201 stx RELPTR ; save pointer to operator routine +LB203 puls b ; get relational flags + cmpa #0x5a ; NOT operation? + beq LB222 ; brif so (it was unary) + cmpa #0x7d ; unary negation? + beq LB222 ; brif so + lsrb ; shift value type flag out of relational flags + stb RELFLG ; save relational operator flag + puls a,x,u ; get FP value back + sta FP1EXP ; set exponent and mantissa in FPA1 + stx FPA1 + stu FPA1+2 + puls b ; and the sign + stb FP1SGN + eorb FP0SGN ; set RESSGN if the two operand signs differ + stb RESSGN +LB220 ldb FP0EXP ; get exponent of FPA0 +LB222 rts ; return or transfer control to operator handler routine +LB223 jsr RVEC15 ; do the RAM hook dance + clr VALTYP ; set type to numeric +LB228 jsr GETNCH ; get first character in the term + bcc LB22F ; brif not numeric +LB22C jmp LBD12 ; parse a number (and return) +LB22F jsr LB3A2 ; set carry if not alpha + bcc LB284 ; brif alpha character (variable) + cmpa #'. ; decimal point? + beq LB22C ; brif so - evaluate number + cmpa #0xac ; minus? + beq LB27C ; brif so - process unary negation + cmpa #0xab ; plus? + beq LB228 ; brif so - ignore unary "posation" + cmpa #'" ; string delimiter? + bne LB24E ; brif not +LB244 ldx CHARAD ; get input pointer + jsr LB518 ; go parse the string +LB249 ldx COEFPT ; get address of end of string + stx CHARAD ; move input pointer past string + rts +LB24E cmpa #0xa8 ; NOT? + bne LB25F ; brif not + lda #0x5a ; precedence of unary NOT + jsr LB15A ; process the operand of NOT + jsr INTCNV ; convert to integer in D + coma ; do a bitwise complement + comb + jmp GIVABF ; resturn the result +LB25F inca ; is it a function token? + beq LB290 ; brif so +LB262 bsr LB26A ; only other legal thing must be a (expr) + jsr LB156 ; evaluate parentheticized expression +LB267 ldb #') ; force a ) + skip2 +LB26A ldb #'( ; force a ( + skip2 +LB26D ldb #', ; force a , +LB26F cmpb [CHARAD] ; does character match? + bne LB277 ; brif not + jmp GETNCH ; each the character and return the next +LB277 ldb #2*1 ; raise syntax error + jmp LAC46 +LB27C lda #0x7d ; unary negation precedence + jsr LB15A ; evaluate argument + jmp LBEE9 ; flip sign of FPA0 and return +LB284 jsr LB357 ; evaluate variable +LB287 stx FPA0+2 ; save descriptor address in FPA0 + lda VALTYP ; test variable type + bne LB222 ; brif string - we're done + jmp LBC14 ; copy FP number from (X) into FPA0 +LB290 jsr GETNCH ; get the actual token number + tfr a,b ; save it (for offsetting X) + lslb ; two bytes per jump table entry (and lose high bit) + jsr GETNCH ; eat the token byte + cmpb #2*19 ; is it a valid token for Color Basic? + bls LB29F ; brif so + jmp [COMVEC+18] ; transfer control to Extended Basic if not +LB29F pshs b ; save jump table offset + cmpb #2*14 ; does it expect a numeric argument? + blo LB2C7 ; brif so + cmpb #2*18 ; does it need no arguments? + bhs LB2C9 ; brif so + bsr LB26A ; force a ( + lda ,s ; get token value + cmpa #2*17 ; is it POINT? + bhs LB2C9 ; brif so + jsr LB156 ; evaluate first argument string + bsr LB26D ; force a comma + jsr LB146 ; TM error if string + puls a ; get token value + ldu FPA0+2 ; get string descriptor + pshs u,a ; now we save the first string argument and the token value + jsr LB70B ; evaluate first numeric argument + puls a ; get back token value + pshs b,a ; save second argument and token value + fcb 0x8e ; opcode of LDX immediate (skips two bytes) +LB2C7 bsr LB262 ; force a ( +LB2C9 puls b ; get offset + ldx COMVEC+8 ; get jump table pointer + abx ; add offset into table + jsr [,x] ; go process function + jmp LB143 ; make sure result is numeric +; operator OR +LB2D4 skip1lda ; set flag to nonzero to signal OR +; operator AND +LB2D5 clra ; set flag to zero to signal AND + sta TMPLOC ; save AND/OR flag + jsr INTCNV ; convert second argument to intenger + std CHARAC ; save it + jsr LBC4A ; move first argument to FPA0 + jsr INTCNV ; convert first argument to integer + tst TMPLOC ; is it AND or OR? + bne LB2ED ; brif OR + anda CHARAC ; do the bitwise AND + andb ENDCHR + bra LB2F1 ; finish up +LB2ED ora CHARAC ; do the bitwise OR + orb ENDCHR +LB2F1 jmp GIVABF ; return integer result +; relational comparision operators +LB2F4 jsr LB148 ; TM error if type mismatch + BNE LB309 ; brif we have a string comparison + lda FP1SGN ; pack FPA1 + ora #0x7f + anda FPA1 + sta FPA1 + ldx #FP1EXP ; point to packed FPA1 + jsr LBC96 ; compare FPA0 to FPA1 + bra LB33F ; handle truth comparison +LB309 clr VALTYP ; the result of a comparison is always a number + dec TRELFL ; remove the string flag from the comparison data + jsr LB657 ; get string details for second argument + stb STRDES ; save them in the temporary string descriptor + stx STRDES+2 + ldx FPA1+2 ; get pointer to first argument descriptor + jsr LB659 ; get string details for second argument + lda STRDES ; get length of second argument + pshs b ; save length of first argument + suba ,s+ ; now A is the difference in string lengths + beq LB328 ; brif string lengths are equal + lda #1 ; flag for second argument is longer than first + bcc LB328 ; brif second string is longer than first + ldb STRDES ; get length of second string (shorter) + nega ; invert default comparison result +LB328 sta FP0SGN ; save default truth flag + ldu STRDES+2 ; get pointer to start of second string + incb ; compensate for DECB +LB32D decb ; have we compared everything? + bne LB334 ; brif not + ldb FP0SGN ; get default truth value + bra LB33F ; decide comparison truth +LB334 lda ,x+ ; get byte from first argument + cmpa ,u+ ; compare with second argument + beq LB32D ; brif equal - keep comparing + ldb #0xff ; negative if first string is > second + bcc LB33F ; brif string A > string B + negb ; invert result +LB33F addb #1 ; convert to 0,1,2 + rolb ; shift left - now it's 4,2,1 for <, =, > + andb RELFLG ; keep only the truth we care about + beq LB348 ; brif no matching bits - it's false + ldb #0xff ; set true +LB348 jmp LBC7C ; convert result to FP and return it +; DIM command +LB34B jsr LB26D ; make sure there's a comma between variables +DIM ldb #1 ; flag that we're dimensioning + bsr LB35A ; go allocate the variable + jsr GETCCH ; are we done? + bne LB34B ; brif not + rts +; This routine parses a variable. For scalars, it will return a NULL string or 0 value number +; if it is called from "evaluate term". Otherwise, it allocates the variable. For arrays, it will +; allocate a default sized array if dimensioning is not underway and then attempt to look up +; the requested coordinates in that array. Otherwise, it will allocate an array based on the +; specified dimension values. +LB357 clrb ; flag that we're not setting up an array + jsr GETCCH +LB35A stb DIMFLG ; save dimensioning flag + sta VARNAM ; save first character of variable name + jsr GETCCH ; get input character (why? we already have it) + bsr LB3A2 ; set carry if not alpha + lbcs LB277 ; brif our variable doesn't start with a letter + clrb ; default second variable character to NUL + stb VALTYP ; set value type to numeric + jsr GETNCH ; get second character + bcs LB371 ; brif numeric - numbers are allowed + bsr LB3A2 ; set carry if not alpha + bcs LB37B ; brif not alpha +LB371 tfr a,b ; save set second character of variable name +LB373 jsr GETNCH ; get an input character + bcs LB373 ; brif numeric - still in variable name + bsr LB3A2 ; set carry if not alpha + bcc LB373 ; brif alpha - still in variable name +LB37B cmpa #'$ ; do we have the string sigil? + bne LB385 ; brif not + com VALTYP ; set value type to string + addb #0x80 ; set bit 7 of second variable character to indicate string + jsr GETNCH ; eat the sigil +LB385 stb VARNAM+1 ; save second variable name character + ora ARYDIS ; merge array disable flag (will set bit 7 of input character if no arrays) + suba #'( ; do we have a subscript? + lbeq LB404 ; brif so + clr ARYDIS ; disable the array disable flag - it's single use + ldx VARTAB ; point to the start of the variable table + ldd VARNAM ; get variable name +LB395 cmpx ARYTAB ; are we at the top of the variable table? + beq LB3AB ; brif so + cmpd ,x++ ; does the variable name match (and move pointer to variable data) + beq LB3DC ; brif so + leax 5,x ; move to next table entry + bra LB395 ; see if we have a match +; Set carry if not upper case alpha +LB3A2 cmpa #'A ; set C if less than A + bcs LB3AA ; brif less than A + suba #'Z+1 ; set C if greater than Z + suba #-('Z+1) +LB3AA rts +LB3AB ldx #ZERO ; point to empty location (NULL/0 value) + ldu ,s ; get caller address + cmpu #LB287 ; coming from "evaluate term"? + beq LB3DE ; brif so - don't allocate + ldd ARYEND ; get end of arrays + std V43 ; save as top of source block + addd #7 ; 7 bytes per scalar entry + std V41 ; save as top of destination block + ldx ARYTAB ; get bottom of arrays + stx V47 ; save as bottom of source block + jsr LAC1E ; move the arrays up to make a hole + ldx V41 ; get new top of arrays + stx ARYEND ; set new end of arrays + ldx V45 ; get bottom of destination block + stx ARYTAB ; set as new start of arrays + ldx V47 ; get old end of variables + ldd VARNAM ; get name of variable + std ,x++ ; set variable name and advance X to the value + clra ; zero out the variable value + clrb + std ,x + std 2,x + sta 4,x +LB3DC stx VARPTR ; save descriptor address of return value +LB3DE rts +; Various integer conversion routines +LB3DF fcb 0x90,0x80,0x00,0x00,0x00 ; FP constant -32768 +LB3E4 jsr GETNCH ; fetch input character +LB3E6 jsr LB141 ; evaluate numeric expression +LB3E9 lda FP0SGN ; get sign of value + bmi LB44A ; brif negative (raise FC error) +INTCNV lda FP0EXP ; get exponent + cmpa #0x90 ; is it within the range for a 16 bit integer? + blo LB3FE ; brif smaller than 32768 + ldx #LB3DF ; point to -32678 constant + jsr LBC96 ; is FPA0 equal to -32768? + bne LB44A ; brif not - magnitude is too far negative +LB3FE jsr LBCC8 ; move binary point to the right of FPA0 and correct sign + ldd FPA0+2 ; get the resulting integer + rts +LB404 ldb DIMFLG ; get dimensioning flag + lda VALTYP ; get type of variable + pshs b,a ; save them (to avoid issues while evaluating dimension values) + clrb ; reset dimension counter +LB40A ldx VARNAM ; get variable name + pshs x,b ; save dimension counter and variable name + bsr LB3E4 ; evaluate a dimension value (and skip either ( or ,) + puls b,x,y ; get variable name, dimension counter, and dimensioning/type flag + stx VARNAM ; restore variable name + ldu FPA0+2 ; get dimension size/index + pshs u,y ; save dimension size and dimensioning/type flag + incb ; bump dimension counter + jsr GETCCH ; get what's after the dimension count + cmpa #', ; do we have another dimension? + beq LB40A ; brif so - parse it + stb TMPLOC ; save dimension counter + jsr LB267 ; make sure we have a ) + puls a,b ; get back variable type and dimensioning flag + sta VALTYP ; restore variable type + stb DIMFLG ; restore dimensioning flag + ldx ARYTAB ; get start of arrays +LB42A cmpx ARYEND ; are we at the end of the array table + beq LB44F ; brif so + ldd VARNAM ; get variable name + cmpd ,x ; does it match? + beq LB43B ; brif so + ldd 2,x ; get length of this array + leax d,x ; move to next array + bra LB42A ; go check another entry +LB43B ldb #2*9 ; code for redimensioned array error + lda DIMFLG ; are we dimensioning? + bne LB44C ; brif so - raise error + ldb TMPLOC ; get number of dimensions given + cmpb 4,x ; does it match? + beq LB4A0 ; brif so +LB447 ldb #8*2 ; raise "bad subscript" + skip2 +LB44A ldb #4*2 ; raise "illegal function call" +LB44C jmp LAC46 ; raise error +LB44F ldd #5 ; 5 bytes per array entry + std COEFPT ; initialize array size to entry size + ldd VARNAM ; get variable name + std ,x ; set array name + ldb TMPLOC ; get dimension count + stb 4,x ; set dimension count + jsr LAC33 ; make sure we haven't overflowed memory + stx V41 ; save array descriptor address +LB461 ldb #11 ; default dimension value (zero-based, gives max index of 10) + clra ; zero extend (??? why not LDD above?) + tst DIMFLG ; are we dimensioning? + beq LB46D ; brif not + puls a,b ; get dimension size + addd #1 ; account for zero based indexing +LB46D std 5,x ; save dimension size + bsr LB4CE ; multiply by accumulated array size + std COEFPT ; save new array size + leax 2,x ; move to next dimension + dec TMPLOC ; have we done all dimensions? + bne LB461 ; brif not + stx TEMPTR ; save end of array descriptor (minus 5) + addd TEMPTR ; add total size of array to address of descriptor + lbcs LAC44 ; brif it overflows memory + tfr d,x ; save end of array for later + jsr LAC37 ; does array fit in memory? + subd #STKBUF-5 ; subtract out the "stack fudge factor" but add 5 to the result + std ARYEND ; save new end of arrays + clra ; set up for clearing +LB48C leax -1,x ; move back one + sta 5,x ; blank out a byte in the array data + cmpx TEMPTR ; have we reached the array header? + bne LB48C ; brif not + ldx V41 ; get address of start of descriptor + lda ARYEND ; get MSB of end of array back (B still has LSB) + subd V41 ; subtract start of descriptor + std 2,x ; save length of array in array header + lda DIMFLG ; are we dimensioning? + bne LB4CD ; brif so - we're done +LB4A0 ldb 4,x ; get number of dimensions + stb TMPLOC ; initialize counter + clra ; initialize accumulated offset + clrb +LB4A6 std COEFPT ; save accumulated offset + puls a,b ; get desired index + std FPA0+2 ; save it + cmpd 5,x ; is it in range for this dimension? + bhs LB4EB ; brif not + ldu COEFPT ; get accumulated offset + beq LB4B9 ; brif first dimension + bsr LB4CE ; multiply accumulated offset by dimension length + addd FPA0+2 ; add in offset into this dimension +LB4B9 leax 2,x ; move to next dimension in header + dec TMPLOC ; done all dimensions? + bne LB4A6 ; brif not + std ,--s ; save D for multiply by 5 (should be pshs d) + aslb ; times 2 + rola + aslb ; times 4 + rola + addd ,s++ ; times 5 + leax d,x ; add in offset from start of array data + leax 5,x ; offset to end of header + stx VARPTR ; save pointer to element data +LB4CD rts +; multiply 16 bit number at 5,x by the 16 bit number in COEFPT; return result in D; BS error if carry +LB4CE lda #16 ; 16 shifts to do a multiply + sta V45 ; save shift counter + ldd 5,x ; get multiplier + std BOTSTK ; save it + clra ; zero out product + clrb +LB4D8 aslb ; shift product left + rola + bcs LB4EB ; brif we have a carry + asl COEFPT+1 ; shift other factor left + rol COEFPT + bcc LB4E6 ; brif no carry - this bit position is 0 + addd BOTSTK ; add in multiplier at this bit position + bcs LB4EB ; brif carry - do an error +LB4E6 dec V45 ; have we done all 16 bits? + bne LB4D8 ; brif not + rts +LB4EB jmp LB447 ; raise a BS error +; MEM function +; BUG: this doesn't account for the STKBUF fudge factor used for memory availability checks +MEM tfr s,d ; get stack pointer where we can do math + subd ARYEND ; calculate number of bytes between the stack and the top of arrays + skip1 ; return result +; Convert unsigned value in B to FP +LB4F3 clra ; zero extend +; Convert signed value in D to FP +GIVABF clr VALTYP ; set value type to numeric + std FPA0 ; save value in FPA0 + ldb #0x90 ; exponent for top two bytes to be an integer + jmp LBC82 ; finish conversion to integer +; STR$ function +STR jsr LB143 ; make sure we have a number + ldu #STRBUF+2 ; convert FP number to string in temporary string buffer + jsr LBDDC + leas 2,s ; don't return to the function evaluator (which will do a numeric type check) + ldx #STRBUF+1 ; point to number string + bra LB518 ; to stash the string in string space and return to the "evaluate term" caller +; Reserve B bytes of string space. Return start in X and FRESPC +LB50D stx V4D ; save X somewhere in case the caller needs it +LB50F bsr LB56D ; allocate string space +LB511 stx STRDES+2 ; save pointer to allocated space in the temporary descriptor + stb STRDES ; save length in the temporary descriptor + rts +LB516 leax -1,x ; move pointer back one (to compensate for the increment below) +; Scan from X until either NUL or one of the string terminators is found +LB518 lda #'" ; set terminator to be string delimiter +LB51A sta CHARAC ; set both delimiters + sta ENDCHR +LB51E leax 1,x ; move to next character + stx RESSGN ; save start of string + stx STRDES+2 ; save start of string in the temporary string descriptor + ldb #-1 ; initialize length counter to -1 (compensate for initial INCB) +LB526 incb ; bump string length + lda ,x+ ; get character from string + beq LB537 ; brif end of line + cmpa CHARAC ; is it delimiter #1? + beq LB533 ; brif so + cmpa ENDCHR ; is it delimiter #2? + bne LB526 ; brif not - keep scanning +LB533 cmpa #'" ; string delimiter? + beq LB539 ; brif so - don't move pointer back +LB537 leax -1,x ; move pointer back (so we don't consume the delimiter) +LB539 stx COEFPT ; save end of string address + stb STRDES ; save string length + ldu RESSGN ; get start of string + cmpu #STRBUF+2 ; is it at the start of the string buffer? + bhi LB54C ; brif so - don't copy it to string space + bsr LB50D ; allocate string space + ldx RESSGN ; point to beginning of the string + jsr LB645 ; copy string data (B bytes) from (X) to (FRESPC) +; Put temporary string descriptor on the string stack +LB54C ldx TEMPPT ; get top of string stack + cmpx #CFNBUF ; is the string stack full? + bne LB558 ; brif not + ldb #15*2 ; code for "string formula too complex" +LB555 jmp LAC46 ; raise error +LB558 lda STRDES ; get string length + sta 0,x ; save it in the string stack descriptor + ldd STRDES+2 ; get string data pointer + std 2,x ; save in string stack descriptor + lda #0xff ; set value type to string + sta VALTYP + stx LASTPT ; set pointer to last used entry on the string stack + stx FPA0+2 ; set pointer to descriptor in the current evaluation value + leax 5,x ; advance string stack pointer + stx TEMPPT + rts +; Reserve B bytes in string space. If there isn't enough space, try compacting string space and +; then try the allocation again. If it still fails, raise OS error. +LB56D clr GARBFL ; flag that compaction not yet done +LB56F clra ; zero extend the length + pshs d ; save requested string length + ldd STRTAB ; get current bottom of strings + subd ,s+ ; calculate new bottom of strings and remove zero extension + cmpd FRETOP ; does the string fit? + blo LB585 ; brif not - try compaction + std STRTAB ; save new bottom of strings + ldx STRTAB ; get bottom of strings + leax 1,x ; now X points to the real start of the allocated space + stx FRESPC ; save the string pointer + puls b,pc ; restore length and return +LB585 ldb #2*13 ; code for out of string space + com GARBFL ; have we compacted string space yet? + beq LB555 ; brif so - raise error + bsr LB591 ; compact string space + puls b ; get back string length + bra LB56F ; go try allocation again +; Compact string space +; This is an O(n^2) algorithm. It first searches all extant strings for the highest address data pointer +; that hasn't already been moved into the freshly compacted string space. If then moves that string data +; up to the highest address it can go to. It repeats this process over and over until it finds no string +; that isn't already in the compacted space. While doing this, it has to search all strings on the string +; stack (this is why the string stack is needed - so we can track anonymous strings), all scalar string +; variables, and *every* entry in every string array. +LB591 ldx MEMSIZ ; get to of string space +LB593 stx STRTAB ; save top of uncompacted stringspace + clra ; zero out D and reset pointer to discovered variable to NULL + clrb + std V4B + ldx FRETOP ; point to bottom of string space + stx V47 ; save as lowest match address (match will be higher) + ldx #STRSTK ; point to start of string stack +LB5A0 cmpx TEMPPT ; are we at the top of the string stack? + beq LB5A8 ; brif so - done with the string stack + bsr LB5D8 ; check for string in uncompacted space (and advance pointer) + bra LB5A0 ; check another on the string stack +LB5A8 ldx VARTAB ; point to start of scalar variables +LB5AA cmpx ARYTAB ; end of scalars? + beq LB5B2 ; brif so + bsr LB5D2 ; check for string in uncompacted space and advance pointer + bra LB5AA ; check another variable +LB5B2 stx V41 ; save address of end of variables (address of first array) +LB5B4 ldx V41 ; get start of the next array +LB5B6 cmpx ARYEND ; end of arrays? + beq LB5EF ; brif so + ldd 2,x ; get length of array + addd V41 ; add to start of array + std V41 ; save address of next array + lda 1,x ; get second character of variable name + bpl LB5B4 ; brif numeric + ldb 4,x ; get number of dimensions + aslb ; two bytes per dimension size + addb #5 ; add in fixed overhead for array descriptor + abx ; now X points to first array element +LB5CA cmpx V41 ; at the start of the next array? + beq LB5B6 ; brif so - go handle another array + bsr LB5D8 ; check for string in uncompacted space (and advance pointer) + bra LB5CA ; process next array element +LB5D2 lda 1,x ; get second character of variable name + leax 2,x ; move to variable data + bpl LB5EC ; brif numeric +LB5D8 ldb ,x ; get length of string + beq LB5EC ; brif NULL - don't need to check data pointer + ldd 2,x ; get data pointer + cmpd STRTAB ; is it in compacted string space? + bhi LB5EC ; brif so + cmpd V47 ; is it better match than previous best? + bls LB5EC ; brif not + stx V4B ; save descriptor address of best match + std V47 ; save new best data pointer match +LB5EC leax 5,x ; move to next descriptor +LB5EE rts +LB5EF ldx V4B ; get descriptor address of the matched string + beq LB5EE ; brif we didn't find one - we're done + clra ; zero extend length + ldb ,x ; get string length + decb ; subtract one (we won't have a NULL string here) + addd V47 ; now D points to the address of the end of the string data + std V43 ; save as top address of move + ldx STRTAB ; set top of uncompacted space as destination + stx V41 + jsr LAC20 ; move string to top of uncompactedspace + ldx V4B ; point to string descriptor + ldd V45 ; get new data pointer address + std 2,x ; update descriptor + ldx V45 ; get bottom of copy destination + leax -1,x ; move back below it + jmp LB593 ; go search for another string to move (and set new bottom of string space) +; Concatenate two strings. We come here directly from the operator handler rather than via a JSR. +LB60F ldd FPA0+2 ; get string descriptor for the first string + pshs d ; save it + jsr LB223 ; evaluate a second string (concatenation is left associative) + jsr LB146 ; make sure we have a string + puls x ; get back first string descriptor + stx RESSGN ; save it + ldb ,x ; get length of first string + ldx FPA0+2 ; get pointer to second string + addb ,x ; add length of second string + bcc LB62A ; brif combined length is OK + ldb #2*14 ; raise string too long error + jmp LAC46 +LB62A jsr LB50D ; reserve room for new string + ldx RESSGN ; get descriptor address of the first string + ldb ,x ; get length of first string + bsr LB643 ; copy it to string space + ldx V4D ; get descriptor address of second string + bsr LB659 ; get string details for second string + bsr LB645 ; copy second string into new string space + ldx RESSGN ; get pointer to first string + bsr LB659 ; remove it from the string stack if possible + jsr LB54C ; put new string on the string stack + jmp LB168 ; return to expression evaluator +; Copy B bytes to space pointed to by FRESPC +LB643 ldx 2,x ; get source address from string descriptor +LB645 ldu FRESPC ; get destination address + incb ; compensate for decb + bra LB64E ; do the copy +LB64A lda ,x+ ; copy a byte + sta ,u+ +LB64E decb ; done yet? + bne LB64A ; brif not + stu FRESPC ; save destination pointer + rts +; Fetch details of string in FPA0+2 and remove from the string stack if possible +LB654 jsr LB146 ; make sure we have a string +LB657 ldx FPA0+2 ; get descriptor pointer +LB659 ldb ,x ; get length of string + bsr LB675 ; see if it's at the top of the string stack and remove it if so + bne LB672 ; brif not removed + ldx 5+2,x ; get start address of string just removed + leax -1,x ; move pointer down 1 + cmpx STRTAB ; is it at the bottom of string space? + bne LB66F ; brif not + pshs b ; save length + addd STRTAB ; add length to start of strings (A was cleared previously) + std STRTAB ; save new string space start (deallocated space for this string) + puls b ; get back string length +LB66F leax 1,x ; restore pointer to pointing at the actual string data + rts +LB672 ldx 2,x ; get data pointer for the string + rts +; Remove string pointed to by X from the string stack if it is at the top of the stack; return with +; A clear and Z set if string removed +LB675 cmpx LASTPT ; is it at the top of the string stack? + bne LB680 ; brif not - do nothing + stx TEMPPT ; save new top of stack + leax -5,x ; move the "last" pointer back as well + stx LASTPT + clra ; flag string removed +LB680 rts +; LEN function +LEN bsr LB686 ; get string details +LB683 jmp LB4F3 ; return unsigned length in B +LB686 bsr LB654 ; get string details and remove from string stack + clr VALTYP ; set value type to numeric + tstb ; set flags according to length + rts +; CHR$ function +CHR jsr LB70E ; get 8 bit unsigned integer to B +LB68F ldb #1 ; allocate a one byte string + jsr LB56D + lda FPA0+3 ; get character code + jsr LB511 ; save reserved string details in temp descriptor + sta ,x ; put character in string +LB69B leas 2,s ; don't go back to function handler - avoid numeric type check +LB69D jmp LB54C ; return temporary string on string stack +; ASC function +ASC bsr LB6A4 ; get first character of argument + bra LB683 ; return unsigned code in B +LB6A4 bsr LB686 ; fetch string details + beq LB706 ; brif NULL string + ldb ,x ; get character at start of string + rts +; LEFT$ function +LEFT bsr LB6F5 ; get arguments from the stack +LB6AD clra ; clear pointer offset (set to start of string) +LB6AE cmpb ,x ; are we asking for more characters than there are in the string? + bls LB6B5 ; brif not + ldb ,x ; only return the number that are in the string + clra ; force starting offset to be the start of the string +LB6B5 pshs b,a ; save offset and length + jsr LB50F ; reserve space in string space + ldx V4D ; point to original string descriptor + bsr LB659 ; get string details + puls b ; get string offset + abx ; now X points to the start of the data to copy + puls b ; get length of copy + jsr LB645 ; copy the data to the allocated space + bra LB69D ; return temp string on string stack +; RIGHT$ function +RIGHT bsr LB6F5 ; get arguments from stack + suba ,x ; subtract length of original string from desired length + nega ; now A is offset into old string where we start copying + bra LB6AE ; go handle everything else +; MID$ function +MID ldb #255 ; default length is the whole string + stb FPA0+3 ; save it + jsr GETCCH ; see what we have after offset + cmpa #') ; end of function? + beq LB6DE ; brif so - no length + jsr LB26D ; force a comma + bsr LB70B ; get length parameter +LB6DE bsr LB6F5 ; get string and offset parameters from the stack + beq LB706 ; brif we have a 0 offset requested (string offsets are 1-based) + clrb ; clear length counter + deca ; subtract one from position parameter (we work on 0-based, param is 1-based) + cmpa ,x ; is start greater than length of string? + bhs LB6B5 ; brif so - return NULL string + tfr a,b ; save absolute position parameter + subb ,x ; now B is postition less length + negb ; now B is amount of string to copy + cmpb FPA0+3 ; is it less than the length requested? + bls LB6B5 ; brif so + ldb FPA0+3 ; set length to the requested length + bra LB6B5 ; go finish up copying the substring +; Common routine for LEFT$, RIGHT$, MID$ - check for ) and fetch string data and first parameter +; from the stack. These were evaluated in the function evaluation handler. (It's not clear that doing +; it that way is actually beneficial. However, this is what the ROM designers did, so here we are.) +LB6F5 jsr LB267 ; make sure we have ) + ldu ,s ; get return address - we're going to mess with the stack + ldx 5,s ; get address of string descriptor + stx V4D ; save descriptor adddress + lda 4,s ; get first numeric parameter in both A and B + ldb 4,s + leas 7,s ; clean up stack + tfr u,pc ; return to original caller +LB706 jmp LB44A ; raise FC error +; Evaluate an unsigned 8 bit expression to B +LB709 jsr GETNCH ; move to next character +LB70B jsr LB141 ; evaluate a numeric expression +LB70E jsr LB3E9 ; convert to integer in D + tsta ; are we negative or > 255? + bne LB706 ; brif so - FC error + jmp GETCCH ; fetch current input character and return +; VAL function +VAL jsr LB686 ; get string details + lbeq LBA39 ; brif NULL string - return 0 + ldu CHARAD ; get input pointer so we can replace it later + stx CHARAD ; point interpreter at string data + abx ; calculate end address of the string + lda ,x ; get byte after the end of the string + pshs u,x,a ; save end of string address, input pointer, and character after end of string + clr ,x ; put a NUL after the string (stops the number interpreter) + jsr GETCCH ; get input character at start of string + jsr LBD12 ; evaluate numeric expression in string + puls a,x,u ; get back saved character and pointers + sta ,x ; restore byte after string + stu CHARAD ; restore interpeter's input pointer + rts +; Evaluate unsigned expression to BINVAL, then evaluate an unsigned expression to B +LB734 bsr LB73D ; evaluate expression + stx BINVAL ; save result +LB738 jsr LB26D ; make sure there's a comma + bra LB70B ; evaluate unsigned expression to B +; Evaluate unsigned expression in X +LB73D jsr LB141 ; evaluate numeric expression +LB740 lda FP0SGN ; is it negative? + bmi LB706 ; brif so + lda FP0EXP ; get exponent + cmpa #0x90 ; largest possible exponent for 16 bits + bhi LB706 ; brif too large + jsr LBCC8 ; move binary point to right of FPA0 + ldx FPA0+2 ; get resulting unsigned value + rts +; PEEK function +PEEK bsr LB740 ; get address to X + ldb ,x ; get the value at that address + jmp LB4F3 ; return B as unsigned value +; POKE function +POKE bsr LB734 ; evaluate address and byte value + ldx BINVAL ; get address + stb ,x ; put value there + rts +; LLIST command +LLIST ldb #-2 ; set output device to printer + stb DEVNUM + jsr GETCCH ; reset flags for input character and fall through to LIST +; LIST command +LIST pshs cc ; save zero flag (end of statement) + jsr LAF67 ; parse line number + jsr LAD01 ; find address of that line + stx LSTTXT ; save that address as the start of the list + puls cc ; get back ent of statement flag + beq LB784 ; brif end of line - list whole program + jsr GETCCH ; are we at the end of the line (one number)? + beq LB789 ; brif end of line + cmpa #0xac ; is it "-"? + bne LB783 ; brif not + jsr GETNCH ; eat the "-" + beq LB784 ; brif no second number - list to end of program + jsr LAF67 ; evaluate the second number + beq LB789 ; brif illegal number +LB783 rts +LB784 ldu #0xffff ; this will cause listing to do the entire program + stu BINVAL +LB789 leas 2,s ; don't return to the caller - we'll jump back to the main loop + ldx LSTTXT ; get address of line to list +LB78D jsr LB95C ; do a newline if needed + jsr LA549 ; do a break check + ldd ,x ; get address of next line + bne LB79F ; brif not end of program +LB797 jsr LA42D ; close output file + clr DEVNUM ; reset device to screen + jmp LAC73 ; go back to immediate mode +LB79F stx LSTTXT ; save new line address + ldd 2,x ; get line number of this line + cmpd BINVAL ; is it above the end line? + bhi LB797 ; brif so - return + jsr LBDCC ; display line number + jsr LB9AC ; put a space after it + ldx LSTTXT ; get line address + bsr LB7C2 ; detokenize the line + ldx [LSTTXT] ; get pointer to next line + ldu #LINBUF+1 ; point to start of detokenized line +LB7B9 lda ,u+ ; get byte from detokenized line + beq LB78D ; brif end of line + jsr LB9B1 ; output character + bra LB7B9 ; handle next character +; Detokenize a line from (X) to the line input buffer +LB7C2 jsr RVEC24 ; do the RAM hook dance + leax 4,x ; move past next line pointer and line number + ldy #LINBUF+1 ; point to line input buffer (destination) +LB7CB lda ,x+ ; get character from tokenized line + beq LB820 ; brif end of input + bmi LB7E6 ; brif it's a token + cmpa #': ; colon? + bne LB7E2 ; brif not + ldb ,x ; get what's after the colon + cmpb #0x84 ; ELSE? + beq LB7CB ; brif so - suppress the colon + cmpb #0x83 ; '? + beq LB7CB ; brif so - suppress the colon + skip2 +LB7E0 lda #'! ; placeholder for unknown token +LB7E2 bsr LB814 ; stow output character + bra LB7CB ; go process another input character +LB7E6 ldu #COMVEC-10 ; point to command interptation table + cmpa #0xff ; is it a function? + bne LB7F1 ; brif not + lda ,x+ ; get function token + leau 5,u ; shift to the function half of the interpretation tables +LB7F1 anda #0x7f ; remove token bias +LB7F3 leau 10,u ; move to next command/function table + tst ,u ; is this table active? + beq LB7E0 ; brif not - use place holder + suba ,u ; subtract number of tokens handled by this table entry + bpl LB7F3 ; brif this token isn't handled here + adda ,u ; undo extra subtraction + ldu 1,u ; get reserved word list for this table +LB801 deca ; are we at the right entry? + bmi LB80A ; brif so +LB804 tst ,u+ ; end of entry? + bpl LB804 ; brif not + bra LB801 ; see if we're there yet +LB80A lda ,u ; get character from wordlist + bsr LB814 ; put character in the buffer + tst ,u+ ; end of word? + bpl LB80A ; brif not + bra LB7CB ; go handle another input character +LB814 cmpy #LINBUF+LBUFMX ; is there room? + bhs LB820 ; brif not + anda #0x7f ; lose bit 7 + sta ,y+ ; save character in output + clr ,y ; make sure there's always a NUL terminator +LB820 rts +; Tokenize the line that the input pointer is pointing to; put result in the line input buffer; return +; length in D +LB821 jsr RVEC23 ; do the RAM hook dance + ldx CHARAD ; get input pointer + ldu #LINBUF ; set destination pointer +LB829 clr V43 ; clear alpha string flag + clr V44 ; clear DATA flag +LB82D lda ,x+ ; get input character + beq LB852 ; brif end of input + tst V43 ; are we handling an alphanumeric string? + beq LB844 ; brif not + jsr LB3A2 ; set carry if not alpha + bcc LB852 ; brif alpha + cmpa #'0 ; is it below the digits? + blo LB842 ; brif so + cmpa #'9 ; is it within the digits? + bls LB852 ; brif so +LB842 clr V43 ; flag that we're past the alphanumeric string +LB844 cmpa #0x20 ; space? + beq LB852 ; brif so - keep it + sta V42 ; save scan delimiter + cmpa #'" ; string delimiter? + beq LB886 ; brif so - copy until another " + tst V44 ; doing "DATA"? + beq LB86B ; brif not +LB852 sta ,u+ ; put character in output + beq LB85C ; brif end of input + cmpa #': ; colon? + beq LB829 ; brif so - reset DATA and alpha string flags +LB85A bra LB82D ; go process another input character +LB85C clr ,u+ ; put a double NUL at the end + clr ,u+ + tfr u,d ; calculate length of result (includes double NUL and an extra two bytes) + subd #LINHDR + ldx #LINBUF-1 ; point to one before the output + stx CHARAD ; set input pointer there + rts +LB86B cmpa #'? ; print abbreviation? + bne LB873 ; brif not + lda #0x87 ; token for PRINT + bra LB852 ; go stash it +LB873 cmpa #'' ; REM abbreviation? + bne LB88A ; brif not + ldd #0x3a83 ; colon plus ' token + std ,u++ ; put it in the output +LB87C clr V42 ; set delimiter to NUL +LB87E lda ,x+ ; get input + beq LB852 ; brif end of line + cmpa V42 ; at the delimiter? + beq LB852 ; brif so +LB886 sta ,u+ ; save in output + bra LB87E ; keep scanning for delimiter +LB88A cmpa #'0 ; is it below digits? + blo LB892 ; brif so + cmpa #';+1 ; is it digit, colon, or semicolon? + blo LB852 ; brif so +LB892 leax -1,x ; move input pointer back one (to point at this input character) + pshs u,x ; save input and output pointers + clr V41 ; set token type to 0 (command) + ldu #COMVEC-10 ; point to command interpretation table +LB89B clr V42 ; set token counter to 0 (0x80) +LB89D leau 10,u ; + lda ,u ; get number of reserved words + beq LB8D4 ; brif this table isn't active + ldy 1,u ; point to reserved words list +LB8A6 ldx ,s ; get input pointer +LB8A8 ldb ,y+ ; get character from reserved word table + subb ,x+ ; compare with input character + beq LB8A8 ; brif exact match + cmpb #0x80 ; brif it was the last character in word and exact match + bne LB8EA ; brif not + leas 2,s ; remove original input pointer from stack + puls u ; get back output pointer + orb V42 ; create token value (B has 0x80 from above) + lda V41 ; get token type + bne LB8C2 ; brif function + cmpb #0x84 ; is it ELSE? + bne LB8C6 ; brif not + lda #': ; silently add a colon before ELSE +LB8C2 std ,u++ ; put two byte token into output + bra LB85A ; go handle more input +LB8C6 stb ,u+ ; save single byte token + cmpb #0x86 ; DATA? + bne LB8CE ; brif not + inc V44 ; set DATA flag +LB8CE cmpb #0x82 ; REM? + beq LB87C ; brif so - skip over rest of line +LB8D2 bra LB85A ; go handle more input +LB8D4 ldu #COMVEC-5 ; point to interpretation table, function style +LB8D7 com V41 ; invert token flag + bne LB89B ; brif we haven't already done functions + puls x,u ; restore input and output pointers + lda ,x+ ; copy first character + sta ,u+ + jsr LB3A2 ; set C if not alpha + bcs LB8D2 ; brif not alpha - it isn't a variable + com V43 ; set alphanumeric string flag + bra LB8D2 ; process more input +LB8EA inc V42 ; bump token number + deca ; checked all in this table? + beq LB89D ; brif so + leay -1,y ; unconsume last compared character +LB8F1 ldb ,y+ ; end of entry? + bpl LB8F1 ; brif not + bra LB8A6 ; check next reserved word +; PRINT command +PRINT beq LB958 ; brif no argument - do a newline + bsr LB8FE ; process print options + clr DEVNUM ; reset output to screen + rts +LB8FE cmpa #'@ ; is it PRINT @? + bne LB907 ; brif not + jsr LA554 ; move cursor to correct location + bra LB911 ; handle some more +LB907 cmpa #'# ; device number specified? + bne LB918 ; brif not + jsr LA5A5 ; parse device number + jsr LA406 ; check for valid output file +LB911 jsr GETCCH ; get input character + beq LB958 ; brif nothing - do newline + jsr LB26D ; need comma after @ or # +LB918 jsr RVEC9 ; do the RAM hook boogaloo +LB91B beq LB965 ; brif end of input +LB91D cmpa #0xa4 ; TAB(? + beq LB97E ; brif so + cmpa #', ; comma (next tab field)? + beq LB966 ; brif so + cmpa #'; ; semicolon (do not advance print position) + beq LB997 ; brif so + jsr LB156 ; evaluate expression + lda VALTYP ; get type of value + pshs a ; save it + bne LB938 ; brif string + jsr LBDD9 ; convert FP number to string + jsr LB516 ; parse a string and put on string stack +LB938 bsr LB99F ; print string + puls b ; get back variable type + jsr LA35F ; set up print parameters + tst PRTDEV ; is it a display device? + beq LB949 ; brif so + bsr LB958 ; do a newline + jsr GETCCH ; get input + bra LB91B ; process more print stuff +LB949 tstb ; set flags on print position + bne LB954 ; brif not at start of line + jsr GETCCH ; get current input + cmpa #', ; comma? + beq LB966 ; skip to next tab field if so + bsr LB9AC ; send a space +LB954 jsr GETCCH ; get input character + bne LB91D ; brif not end of statement +LB958 lda #0x0d ; carriage return + bra LB9B1 ; send it to output +LB95C jsr LA35F ; set up print parameters +LB95F beq LB958 ; brif width is 0 + lda DEVPOS ; get line position + bne LB958 ; brif not at start of line +LB965 rts +LB966 jsr LA35F ; set up print parameters + beq LB975 ; brif line width is 0 + ldb DEVPOS ; get line position + cmpb DEVLCF ; at or past last comma field? + blo LB977 ; brif so + bsr LB958 ; move to next line + bra LB997 ; handle more stuff +LB975 ldb DEVPOS ; get line position +LB977 subb DEVCFW ; subtract a comma field width + bhs LB977 ; brif we don't have a remainder yet + negb ; now B is number of of spaces needed + bra LB98E ; go advance +LB97E jsr LB709 ; evaluate TAB distance + cmpa #') ; closing )? + lbne LB277 ; brif not + jsr LA35F ; set up print parameters + subb DEVPOS ; subtract print position from desired position + bls LB997 ; brif we're already past it +LB98E tst PRTDEV ; is it a display device? + bne LB997 ; brif not +LB992 bsr LB9AC ; output a space + decb ; done enough? + bne LB992 ; brif not +LB997 jsr GETNCH ; get input character + jmp LB91B ; process more items +; cpoy string from (X-1) to output +LB99C jsr LB518 ; parse the string +LB99F jsr LB657 ; get string details +LB9A2 incb ; compensate for decb +LB9A3 decb ; done all of the string? + beq LB965 ; brif so + lda ,x+ ; get character from string + bsr LB9B1 ; send to output + bra LB9A3 ; go do another character +LB9AC lda #0x20 ; space character + skip2 +LB9AF lda #'? ; question mark character +LB9B1 jmp PUTCHR ; output character +; The floating point math package and related functions and operations follow from here +; to the end of the Color Basic ROM area +LB9B4 ldx #LBEC0 ; point to FP constant 0.5 + bra LB9C2 ; add 0.5 to FPA0 +LB9B9 jsr LBB2F ; unpack FP data from (X) to FPA1 +; subtraction operator +LB9BC com FP0SGN ; invert sign of FPA0 (subtracting is adding the negative) + com RESSGN ; that also inverts the sign differential + bra LB9C5 ; go add the negative of FPA0 to FPA1 +LB9C2 jsr LBB2F ; unpack FP data from (X) to FPA1 +; addition operator +LB9C5 tstb ; check exponent of FPA0 + lbeq LBC4A ; copy FPA1 to FPA0 if FPA0 is 0 + ldx #FP1EXP ; point X to FPA1 (first operand) as the operand to denormalize +LB9CD tfr a,b ; put exponent of FPA1 into B + tstb ; is FPA1 0? + beq LBA3E ; brif exponent is 0 - no-op; adding 0 to FPA0 + subb FP0EXP ; get difference in exponents - number of bits to shift the smaller mantissa + beq LBA3F ; brif exponents are equal - no need to denormalize + bmi LB9E2 ; brif FPA0 > FPA1 + sta FP0EXP ; replace result exponent with FPA1's (FPA1 is bigger) + lda FP1SGN ; also copy sign over + sta FP0SGN + ldx #FP0EXP ; point to FPA0 (we need to denormalize the smaller number) + negb ; invert the difference - this is the number of bits to shift the mantissa +LB9E2 cmpb #-8 ; do we have to shift by a whole byte? + ble LBA3F ; brif so start by shifting whole bytes to the right + clra ; clear overflow byte + lsr 1,x ; shift high bit of mantissa right (LSR will force a zero into the high bit) + jsr LBABA ; shift remainder of mantissa right -B times +LB9EC ldb RESSGN ; get the sign flag + bpl LB9FB ; brif signs are the same (we add the mantissas then) + com 1,x ; complement the mantissa and extra precision bytes + com 2,x + com 3,x + com 4,x + coma + adca #0 ; add one to A (COM sets C); this may cause a carry to enter the ADD below +LB9FB sta FPSBYT ; save extra precision byte + lda FPA0+3 ; add the main mantissa bytes (and propage carry from above) + adca FPA1+3 + sta FPA0+3 + lda FPA0+2 + adca FPA1+2 + sta FPA0+2 + lda FPA0+1 + adca FPA1+1 + sta FPA0+1 + lda FPA0 + adca FPA1 + sta FPA0 + tstb ; were signs the same? + bpl LBA5C ; brif so - number may have gotten bigger so normalize if needed +LBA18 bcs LBA1C ; brif we had a carry - result is positive?) + bsr LBA79 ; do a proper negation of FPA0 mantissa +LBA1C clrb ; clear temporary exponent accumulator +LBA1D lda FPA0 ; test high byte of mantissa + bne LBA4F ; brif not 0 - we need to do bit shifting + lda FPA0+1 ; shift left 8 bits + sta FPA0 + lda FPA0+2 + sta FPA0+1 + lda FPA0+3 + sta FPA0+2 + lda FPSBYT + sta FPA0+3 + clr FPSBYT + addb #8 ; account for 8 bits shifted + cmpb #5*8 ; shifted 5 bytes worth? + blt LBA1D ; brif not +LBA39 clra ; zero out exponent and sign - result is 0 +LBA3A sta FP0EXP ; set exponent and sign + sta FP0SGN +LBA3E rts +LBA3F bsr LBAAE ; shift FPA0 mantissa to the right + clrb ; clear carry + bra LB9EC ; get on with adding +LBA44 incb ; account for one bit shift + asl FPSBYT ; shift mantissa and extra precision left + rol FPA0+3 + rol FPA0+2 + rol FPA0+1 + rol FPA0 +LBA4F bpl LBA44 ; brif we haven't got a 1 in bit 7 + lda FP0EXP ; get exponent of result + pshs b ; subtract shift count from exponent + suba ,s+ + sta FP0EXP ; save adjusted exponent + bls LBA39 ; brif we underflowed - set result to 0 + skip2 +LBA5C bcs LBA66 ; brif mantissa overflowed + asl FPSBYT ; get bit 7 of expra precision to C (used for round off) + lda #0 ; set to 0 without affecting C + sta FPSBYT ; clear out extra precision bits + bra LBA72 ; go round off result +LBA66 inc FP0EXP ; bump exponent (for a right shift to bring carry in) + beq LBA92 ; brif we overflowed + ror FPA0 ; shift carry into mantissa, shift right + ror FPA0+1 + ror FPA0+2 + ror FPA0+3 +LBA72 bcc LBA78 ; brif no round-off needed + bsr LBA83 ; add one to mantissa + beq LBA66 ; brif carry - need to shift right again +LBA78 rts +LBA79 com FP0SGN ; invert sign of value +LBA7B com FPA0 ; first do a one's copmlement + com FPA0+1 + com FPA0+2 + com FPA0+3 +LBA83 ldx FPA0+2 ; add one to mantissa (after one's complement gives two's complement) + leax 1,x ; bump low word + stx FPA0+2 + bne LBA91 ; brif no carry from low word + ldx FPA0 ; bump high word + leax 1,x + stx FPA0 +LBA91 rts +LBA92 ldb #2*5 ; code for overflow + jmp LAC46 ; raise error +LBA97 ldx #FPA2-1 ; point to FPA2 +LBA9A lda 4,x ; shift mantissa right by 8 bits + sta FPSBYT + lda 3,x + sta 4,x + lda 2,x + sta 3,x + lda 1,x + sta 2,x + lda FPCARY ; and handle extra precision on the left + sta 1,x +LBAAE addb #8 ; account for 8 bits shifted + ble LBA9A ; brif more shifts needed + lda FPSBYT ; get sub byte (extra precision) + subb #8 ; undo the 8 added above + beq LBAC4 ; brif difference is 0 +LBAB8 asr 1,x ; shift mantissa and sub byte one bit (keep mantissa high bit set) +LBABA ror 2,x + ror 3,x + ror 4,x + rora + incb ; account for one shift + bne LBAB8 ; brif not enought shifts yet +LBAC4 rts +LBAC5 fcb 0x81,0x00,0x00,0x00,0x00 ; packed FP 1.0 +LBACA bsr LBB2F ; unpack FP value from (X) to FPA1 +; multiplication operator +LBACC beq LBB2E ; brif exponent of FPA0 is 0 (result is 0) + bsr LBB48 ; calculate exponent of product +LBAD0 lda #0 ; zero out mantissa of FPA2 + sta FPA2 + sta FPA2+1 + sta FPA2+2 + sta FPA2+3 + ldb FPA0+3 ; multiply FPA1 by LSB of FPA0 + bsr LBB00 + ldb FPSBYT ; save extra precision byte + stb VAE + ldb FPA0+2 + bsr LBB00 ; again for next byte of FPA0 + ldb FPSBYT + stb VAD + ldb FPA0+1 ; again for next byte of FPA0 + bsr LBB00 + ldb FPSBYT + stb VAC + ldb FPA0 ; and finally for the high byte + bsr LBB02 + ldb FPSBYT + stb VAB + jsr LBC0B ; copy mantissa from FPA2 to FPA0 (result) + jmp LBA1C ; normalize +LBB00 beq LBA97 ; brif multiplier is 0 - just shift, don't multiply +LBB02 coma ; set carry +LBB03 lda FPA2 ; get FPA2 MS byte + rorb ; data bit to carry; will be 0 when all shifts done + beq LBB2E ; brif 8 shifts done + bcc LBB20 ; brif data bit is 0 - no addition + lda FPA2+3 ; add mantissa of FPA1 and FPA2 + adda FPA1+3 + sta FPA2+3 + lda FPA2+2 + adca FPA1+2 + sta FPA2+2 + lda FPA2+1 + adca FPA1+1 + sta FPA2+1 + lda FPA2 + adca FPA1 +LBB20 rora ; shift carry into FPA2 + sta FPA2 + ror FPA2+1 + ror FPA2+2 + ror FPA2+3 + ror FPSBYT + clra ; clear carry + bra LBB03 +LBB2E rts +; Unpack FP value from (X) to FPA1 +LBB2F ldd 1,x ; copy mantissa (and sign) + sta FP1SGN ; save sign bit + ora #0x80 ; make sure mantissa has bit 7 set + std FPA1 + ldb FP1SGN ; get sign + eorb FP0SGN ; set if FPA0 sign differs + stb RESSGN + ldd 3,x ; copy remainder of mantissa + std FPA1+2 + lda ,x ; and exponent + sta FP1EXP + ldb FP0EXP ; fetch FPA0 exponent and set flags + rts +; Calculate eponent for product of FPA0 and FPA1 +LBB48 tsta ; is FPA1 zero? + beq LBB61 ; brif so + adda FP0EXP ; add to exponent of FPA0 (this is how scientific notation works) + rora ; set V if we *don't* have an overflow + rola + bvc LBB61 ; brif exponent too larger or small + adda #0x80 ; restore the bias + sta FP0EXP ; set result exponent + beq LBB63 ; brif 0 - clear FPA0 + lda RESSGN ; the result sign (negative if signs differ) is the result sign + sta FP0SGN ; so set it as such + rts +LBB5C lda FP0SGN ; get sign of FPA0 + coma ; invert sign + bra LBB63 ; zero sign and exponent +LBB61 leas 2,s ; don't go back to caller (mul/div) - return to previous caller +LBB63 lbpl LBA39 ; brif we underflowed - go zero things out +LBB67 jmp LBA92 ; raise overflow error +; fast multiply by 10 - leave result in FPA0 +LBB6A jsr LBC5F ; copy FPA0 to FPA1 (for addition later) + beq LBB7C ; brif exponent is 0 - it's a no-op then + adda #2 ; this gives "times 4" + bcs LBB67 ; raise overflow if required + clr RESSGN ; set result sign to "signs the same" + jsr LB9CD ; add FPA1 to FPA0 "times 5" + inc FP0EXP ; times 10 + beq LBB67 ; brif overflow +LBB7C rts +LBB7D fcb 0x84,0x20,0x00,0x00,0x00 ; packed FP constant 10.0 +; Divide by 10 +LBB82 jsr LBC5F ; move FPA0 to FPA1 + ldx #LBB7D ; point to constant 10 + clrb ; zero sign +LBB89 stb RESSGN ; result will be positive or zero + jsr LBC14 ; unpack constant 10 to FPA0 + skip2 ; fall through to division (divide FPA1 by 10) +LBB8F bsr LBB2F ; unpack FP number from (X) to FPA1 +; division operator +LBB91 beq LBC06 ; brif FPA0 is 0 - division by zero + neg FP0EXP ; get exponent of reciprocal of the divisor + bsr LBB48 ; calculate exponent of quotient + inc FP0EXP ; bump exponent (due to division algorithm below) + beq LBB67 ; brif overflow + ldx #FPA2 ; point to temporary storage location + ldb #4 ; do 5 bytes + stb TMPLOC ; save counter + ldb #1 ; shift counter and quotient byte +LBBA4 lda FPA0 ; compare mantissa of FPA0 to FPA1, set C if FPA1 less + cmpa FPA1 + bne LBBBD + lda FPA0+1 + cmpa FPA1+1 + bne LBBBD + lda FPA0+2 + cmpa FPA1+2 + bne LBBBD + lda FPA0+3 + cmpa FPA1+3 + bne LBBBD + coma ; set C if FPA0 = FPA1 (it "goes") +LBBBD tfr cc,a ; save "it goes" status + rolb ; rotate carry into quotient + bcc LBBCC ; brif carry clear - haven't done 8 shifts yet + stb ,x+ ; save quotient byte + dec TMPLOC ; done enough bytes? + bmi LBBFC ; brif done all 5 + beq LBBF8 ; brif last byte + ldb #1 ; reset shift counter and quotient byte +LBBCC tfr a,cc ; get back carry status + bcs LBBDE ; brif it "went" +LBBD0 asl FPA1+3 ; shift mantissa (dividend) left + rol FPA1+2 + rol FPA1+1 + rol FPA1 + bcs LBBBD ; brif carry - it "goes" so we have to bump quotient + bmi LBBA4 ; brif high order bit is set - compare mantissas + bra LBBBD ; otherwise, count a 0 bit and try next bit +LBBDE lda FPA1+3 ; subtract mantissa of FPA0 from mantissa of FPA1 + suba FPA0+3 + sta FPA1+3 + lda FPA1+2 + sbca FPA0+2 + sta FPA1+2 + lda FPA1+1 + sbca FPA0+1 + sta FPA1+1 + lda FPA1 + sbca FPA0 + sta FPA1 + bra LBBD0 ; go check for another go +LBBF8 ldb #0x40 ; only two bits in last byte (for rounding) + bra LBBCC ; go do the last byte +LBBFC rorb ; get low bits to bits 7,6 and C to bit 5 + rorb + rorb + stb FPSBYT ; save result extra precision + bsr LBC0B ; move FPA2 mantissa to FPA0 (result) + jmp LBA1C ; go normalize the result +LBC06 ldb #2*10 ; division by zero + jmp LAC46 ; raise error +; Copy mantissa of FPA2 to FPA0 +LBC0B ldx FPA2 ; copy high word + stx FPA0 + ldx FPA2+2 ; copy low word + stx FPA0+2 + rts +; unpack FP number at (X) to FPA0 +LBC14 pshs a ; save register + ldd 1,x ; get mantissa high word and sign + sta FP0SGN ; set sign + ora #0x80 ; make sure mantissa always has bit 7 set + std FPA0 + clr FPSBYT ; clear extra precision + ldb ,x ; get exponent + ldx 3,x ; copy mantissa low word + stx FPA0+2 + stb FP0EXP ; save exponent (and set flags) + puls a,pc ; restore register and return +LBC2A ldx #V45 ; point to FPA4 + bra LBC35 ; pack FPA0 there +LBC2F ldx #V40 ; point to FPA3 + skip2 ; fall through to pack FPA0 there +LBC33 ldx VARDES ; get variable descriptor pointer +; Pack FPA0 to (X) +LBC35 lda FP0EXP ; get exponent + sta ,x ; save it + lda FP0SGN ; get sign + ora #0x7f ; force set low bits - only keep sign in high bit + anda FPA0 ; merge in bits 6-0 of high byte of mantissa + sta 1,x ; save it + lda FPA0+1 ; copy next highest byte + sta 2,x + ldu FPA0+2 ; and the low word of the mantissa + stu 3,x + rts +; Copy FPA1 to FPA0; return with sign in A +LBC4A lda FP1SGN ; copy sign +LBC4C sta FP0SGN + ldx FP1EXP ; copy exponent, mantissa high byte + stx FP0EXP + clr FPSBYT ; clear extra precision + lda FPA1+1 ; copy mantissa second highest byte + sta FPA0+1 + lda FP0SGN ; set sign for return + ldx FPA1+2 ; copy low word of mantissa + stx FPA0+2 + rts +; Copy FPA0 to FPA1 +LBC5F ldd FP0EXP ; copy exponent and high byte of mantissa + std FP1EXP + ldx FPA0+1 ; copy middle bytes of mantissa + stx FPA1+1 + ldx FPA0+3 ; copy low byte of mantissa and sign + stx FPA1+3 + tsta ; set flags on exponent + rts +; check FPA0: return B = 0, if FPA0 is 0, 0xff if negative, and 0x01 if positive +LBC6D ldb FP0EXP ; get exponent + beq LBC79 ; brif 0 +LBC71 ldb FP0SGN ; get sign +LBC73 rolb ; get sign to C + ldb #0xff ; set for negative result + bcs LBC79 ; brif negative + negb ; set to 1 for positive +LBC79 rts +; SGN function +SGN bsr LBC6D ; get sign of FPA0 +LBC7C stb FPA0 ; save result + clr FPA0+1 ; clear next lower 8 bits + ldb #0x88 ; exponent if mantissa is 8 bit integer +LBC82 lda FPA0 ; get high bits of mantissa + suba #0x80 ; set C if mantissa was positive (will cause a negation if it was negative) +LBC86 stb FP0EXP ; set exponent + ldd ZERO ; clear out low word + std FPA0+2 + sta FPSBYT ; clear extra precision + sta FP0SGN ; set sign to positive + jmp LBA18 ; normalize the result +; ABS function +ABS clr FP0SGN ; force FPA0 to be positive (yes, it's that simple) + rts +; Compare packed FP number at (X) to FPA0 +; Return with B = -1, 0, 1 for FPA0 <, =, > (X) and flags set based on that +LBC96 ldb ,x ; get exponent of (X) + beq LBC6D ; brif (X) is 0 + ldb 1,x ; get MS byte of mantissa of (X) + eorb FP0SGN ; set bit 7 if signs of (X) and FPA0 differ + bmi LBC71 ; brif signs differ - no need to compare the magnitude +LBCA0 ldb FP0EXP ; compare exponents and brif different + cmpb ,x + bne LBCC3 + ldb 1,x ; compare mantissa (but we have to pack the FPA0 bits first + orb #0x7f ; keep only sign bit (note: signs are the same) + andb FPA0 ; merge in the mantissa bits from FPA0 + cmpb 1,x ; do the packed versions match? + bne LBCC3 ; brif not + ldb FPA0+1 ; compare second byte of mantissas + cmpb 2,x + bne LBCC3 + ldb FPA0+2 ; compare third byte of mantissas + cmpb 3,x + bne LBCC3 + ldb FPA0+3 ; compare low byte of mantissas, but use subtraction so B = 0 on match + subb 4,x + bne LBCC3 + rts ; return B = 0 if (X) = FPA0 +LBCC3 rorb ; shift carry to bit 7 (C set if FPA0 < (X)) + eorb FP0SGN ; invert the comparision sense if the signs are negative + bra LBC73 ; interpret comparison result +; Shift mantissa of FPA0 until the binary point is immediately to the right of the mantissa and set up the +; result as a two's complement value. +LBCC8 ldb FP0EXP ; get exponent of FPA0 + beq LBD09 ; brif FPA0 is zero - we don't have to do anything, just blank it + subb #0xa0 ; calculate number of shifts to get to the correct exponent (binary point to the right) + lda FP0SGN ; do we have a positive number? + bpl LBCD7 ; brif so + com FPCARY ; negate the mantissa and set extra inbound precision to the correct sign + jsr LBA7B +LBCD7 ldx #FP0EXP ; point to FPA0 + cmpb #-8 ; moving by whole bytes? + bgt LBCE4 ; brif not + jsr LBAAE ; do bit shifting + clr FPCARY ; clear carry in byte + rts +LBCE4 clr FPCARY ; clear the extra carry in precision + lda FP0SGN ; get sign of value + rola ; get sign to carry (so rotate repeats the sign) + ror FPA0 ; shift the first bit + jmp LBABA ; do the shifting dance +; INT function +INT ldb FP0EXP ; get exponent + cmpb #0xa0 ; is the number big enough that there can be no fractional part? + bhs LBD11 ; brif so - we don't have to do anything + bsr LBCC8 ; go shift binary point to the right of the mantissa + stb FPSBYT ; save extra precision bits + lda FP0SGN ; get original sign + stb FP0SGN ; force result to be positive + suba #0x80 ; set C if we had a positive result + lda #0xa0 ; set exponent to match denormalized result + sta FP0EXP + lda FPA0+3 ; save low byte + sta CHARAC + jmp LBA18 ; go normalize (this will correct for the two's complement representation of negatives) +LBD09 stb FPA0 ; replace mantissa of FPA0 with contents of B + stb FPA0+1 + stb FPA0+2 + stb FPA0+3 +LBD11 rts +; Convert ASCII string to FP +; BUG: no overflow is checked on the decimal exponent in exponential notation. +LBD12 ldx ZERO ; zero out FPA0 and temporaries + stx FP0SGN + stx FP0EXP + stx FPA0+1 + stx FPA0+2 + stx V47 + stx V45 + bcs LBD86 ; brif input character is numeric + jsr RVEC19 ; do the RAM hook dance + cmpa #'- ; regular negative sign + bne LBD2D ; brif not + com COEFCT ; invert sign + bra LBD31 ; process stuff after the sign +LBD2D cmpa #'+ ; regular plus? + bne LBD35 ; brif not +LBD31 jsr GETNCH ; get character after sign + bcs LBD86 ; brif numeric +LBD35 cmpa #'. ; decimal point? + beq LBD61 ; brif so + cmpa #'E ; scientific notation + bne LBD65 ; brif not + jsr GETNCH ; eat the "E" + bcs LBDA5 ; brif numeric + cmpa #0xac ; negative sign (token)? + beq LBD53 ; brif so + cmpa #'- ; regular negative? + beq LBD53 ; brif so + cmpa #0xab ; plus sign (token)? + beq LBD55 ; brif so + cmpa #'+ ; regular plus? + beq LBD55 + bra LBD59 ; brif no sign found +LBD53 com V48 ; set exponent sign to negative +LBD55 jsr GETNCH ; eat the sign + bcs LBDA5 ; brif numeric +LBD59 tst V48 ; is the exponent sign negatvie? + beq LBD65 ; brif not + neg V47 ; negate base 10 exponent + bra LBD65 +LBD61 com V46 ; toggle decimal point flag + bne LBD31 ; brif we haven't seen two decimal points +LBD65 lda V47 ; get base 10 exponent + suba V45 ; subtract number of places to the right + sta V47 ; we now have a complete decimal exponent + beq LBD7F ; brif we have no base 10 shifting required + bpl LBD78 ; brif positive exponent +LBD6F jsr LBB82 ; divide FPA0 by 10 (shift decimal point left) + inc V47 ; bump exponent + bne LBD6F ; brif we haven't reached 0 yet + bra LBD7F ; return result +LBD78 jsr LBB6A ; multiply by 10 + dec V47 ; downshift the exponent + bne LBD78 ; brif not at 0 yet +LBD7F lda COEFCT ; get desired sign + bpl LBD11 ; brif it will be positive - no need to do anything + jmp LBEE9 ; flip the sign of FPA0 +LBD86 ldb V45 ; get the decimal count + subb V46 ; (if decimal seen, will add one; otherwise it does nothing) + stb V45 + pshs a ; save new digit + jsr LBB6A ; multiply partial result by 10 + puls b ; get back digit + subb #'0 ; remove ASCII bias + bsr LBD99 ; add B to FPA0 + bra LBD31 ; go process another digit +LBD99 jsr LBC2F ; save FPA0 to FPA3 + jsr LBC7C ; convert B to FP number + ldx #V40 ; point to FPA3 + jmp LB9C2 ; add FPA3 and FPA0 +LBDA5 ldb V47 ; get exponent value + aslb ; times 2 + aslb ; times 4 + addb V47 ; times 5 + aslb ; times 10 + suba #'0 ; remove ASCII bias + pshs b ; save acculated result + adda ,s+ ; add new digit to accumulated result + sta V47 ; save new accumulated decimal exponent + bra LBD55 ; interpret another exponent character +LBDB6 fcb 0x9b,0x3e,0xbc,0x1f,0xfd ; packed FP: 99999999.9 +LBDBB fcb 0x9e,0x6e,0x6b,0x27,0xfd ; packed FP: 999999999 +LBDC0 fcb 0x9e,0x6e,0x6b,0x28,0x00 ; pakced FP: 1E9 +LBDC5 ldx #LABE8-1 ; point to "IN" message + bsr LBDD6 ; output the string + ldd CURLIN ; get basic line number +LBDCC std FPA0 ; save 16 bit unsigned integer + ldb #0x90 ; exponent for upper 16 bits of FPA0 to be an integer + coma ; set C (force normalization to treat as positive) + jsr LBC86 ; zero bottom half, save exponent, and normalize + bsr LBDD9 ; convert FP number to ASCII string +LBDD6 jmp LB99C ; output string +; Convert FP number to ASCII string +LBDD9 ldu #STRBUF+3 ; point to buffer address that will not cause string to go to string space +LBDDC lda #0x20 ; default sign is a space character + ldb FP0SGN ; get sign of value + bpl LBDE4 ; brif positive + lda #'- ; use negative sign +LBDE4 sta ,u+ ; save sign + stu COEFPT ; save output buffer pointer + sta FP0SGN ; save sign character + lda #'0 ; result is 0 if exponent is 0 + ldb FP0EXP ; get exponent + lbeq LBEB8 ; brif FPA0 is 0 + clra ; base 10 exponent is 0 for > 1 + cmpb #0x80 ; is number > 1? + bhi LBDFF ; brif so + ldx #LBDC0 ; point to 1E+09 + jsr LBACA ; shift decimal to the right by 9 spaces + lda #-9 ; account for shift +LBDFF sta V45 ; save base 10 exponent +LBE01 ldx #LBDBB ; point to 999999999 + jsr LBCA0 ; are we above that? + bgt LBE18 ; brif so +LBE09 ldx #LBDB6 ; point to 99999999.9 + jsr LBCA0 ; are we above that? + bgt LBE1F ; brif in range + jsr LBB6A ; multiply by 10 (we were small) + dec V45 ; account for shift + bra LBE09 ; see if we've come into range +LBE18 jsr LBB82 ; divide by 10 + inc V45 ; account for shift + bra LBE01 ; see if we've come into range +LBE1F jsr LB9B4 ; add 0.5 to FPA0 (rounding) + jsr LBCC8 ; do the integer dance + ldb #1 ; default decimal flag (force immediate decimal) + lda V45 ; get base 10 exponent + adda #10 ; account for "unormalized" number + bmi LBE36 ; brif number < 1.0 + cmpa #11 ; do we have more than 9 places? + bhs LBE36 ; brif so - do scientific notation + deca + tfr a,b + lda #2 ; force no scientific notation +LBE36 deca ; subtract wo without affecting carry + deca + sta V47 ; save exponent - 0 is do not display in scientific notation + stb V45 ; save number of places to left of decimal + bgt LBE4B ; brif >= 1 + ldu COEFPT ; point to string buffer + lda #'. ; put decimal + sta ,u+ + tstb ; is there anything to left of decimal? + beq LBE4B ; brif not + lda #'0 ; store a zero + sta ,u+ +LBE4B ldx #LBEC5 ; point to powers of 10 + ldb #0x80 ; set digit counter to 0x80 +LBE50 lda FPA0+3 ; add mantissa to power of 10 + adda 3,x + sta FPA0+3 + lda FPA0+2 + adca 2,x + sta FPA0+2 + lda FPA0+1 + adca 1,x + sta FPA0+1 + lda FPA0 + adca ,x + sta FPA0 + incb ; add one to digit counter + rorb ; put carry into bit 7 + rolb ; set V if carry and sign differ + bvc LBE50 ; brif positive mantissa or carry is 0 and negative mantissa + bcc LBE72 ; brif negative mantissa + subb #10+1 ; take 9's complement if adding mantissa + negb +LBE72 addb #'0-1 ; add ASCII bias + leax 4,x ; move to next power of 10 + tfr b,a ; save digit + anda #0x7f ; remove add/subtract flag + sta ,u+ ; put in output + dec V45 ; do we need a decimal yet? + bne LBE84 ; brif not + lda #'. ; put decimal + sta ,u+ +LBE84 comb ; toggle bit 7 (add/sub flag) + andb #0x80 ; only keep bit 7 + cmpx #LBEC5+9*4 ; done all places? + bne LBE50 ; brif not +LBE8C lda ,-u ; get last character + cmpa #'0 ; was it 0? + beq LBE8C ; brif so + cmpa #'. ; decimal? + bne LBE98 ; brif not + leau -1,u ; move past decimal if it isn't needed +LBE98 lda #'+ ; plus sign + ldb V47 ; get scientific notation exponent + beq LBEBA ; brif not scientific notation + bpl LBEA3 ; brif positive exponent + lda #'- ; negative sign for base 10 exponent + negb ; switch to positive exponent +LBEA3 sta 2,u ; put sign + lda #'E ; put "E" + sta 1,u + lda #'0-1 ; init to ASCII 0 (compensate for INC) +LBEAB inca ; bump digit + subb #10 ; have we hit the correct one yet? + bcc LBEAB ; brif not + addb #'9+1 ; convert units digit to ASCII + std 3,u ; put exponent in output + clr 5,u ; put trailing NUL + bra LBEBC ; go reset pointer +LBEB8 sta ,u ; store last character +LBEBA clr 1,u ; put NUL at the end +LBEBC ldx #STRBUF+3 ; point to start of string + rts +LBEC0 fcb 0x80,0x00,0x00,0x00,0x00 ; packed FP 0.5 +LBEC5 fqb -100000000 + fqb 10000000 + fqb -1000000 + fqb 100000 + fqb -10000 + fqb 1000 + fqb -100 + fqb 10 + fqb -1 +LBEE9 lda FP0EXP ; get exponent of FPA0 + beq LBEEF ; brif 0 - don't flip sign + com FP0SGN ; flip sign +LBEEF rts +; Expand a polynomial of the form +; AQ+BQ^3+CQ^5+DQ^7..... where Q is FPA0 and X points to a table +LBEF0 stx COEFPT ; save coefficient table pointer + jsr LBC2F ; copy FPA0 to FPA3 + bsr LBEFC ; multiply FPA3 by FPA0 + bsr LBF01 ; expand polynomial + ldx #V40 ; point to FPA3 +LBEFC jmp LBACA ; multiply FPA0 by FPA3 +LBEFF stx COEFPT ; save coefficient table counter +LBF01 jsr LBC2A ; move FPA0 to FPA4 + ldx COEFPT ; get the current coefficient + ldb ,x+ ; get the number of entries + stb COEFCT ; save as counter + stx COEFPT ; save new pointer +LBF0C bsr LBEFC ; multiply (X) and FPA0 + ldx COEFPT ; get this coefficient + leax 5,x ; move to next one + stx COEFPT ; save new pointer + jsr LB9C2 ; add (X) to FPA0 + ldx #V45 ; point X to FPA4 + dec COEFCT ; done all coefficients? + bne LBF0C ; brif more left + rts +; RND function +RND jsr LBC6D ; set flags on FPA0 + bmi LBF45 ; brif negative - set seed + beq LBF3B ; brif 0 - do random between 0 and 1 + bsr LBF38 ; convert to integer + jsr LBC2F ; save range value + bsr LBF3B ; get random number + ldx #V40 ; point to FPA3 + bsr LBEFC ; multply (X) by FPA0 + ldx #LBAC5 ; point to FP 1.0 + jsr LB9C2 ; add 1 to FPA0 +LBF38 jmp INT ; return integer value +LBF3B ldx RVSEED+1 ; move variable random number seed to FPA0 + stx FPA0 + ldx RVSEED+3 + stx FPA0+2 +LBF45 ldx RSEED ; move fixed seed to FPA1 + stx FPA1 + ldx RSEED+2 + stx FPA1+2 + jsr LBAD0 ; multiply them + ldd VAD ; get lowest order product bytes + addd #0x658b ; add a constant + std RVSEED+3 ; save it as new seed + std FPA0+2 ; save in result + ldd VAB ; get high order extra product bytes + adcb #0xb0 ; add upper bytes of constant + adca #5 + std RVSEED+1 ; save as new seed + std FPA0 ; save as result + clr FP0SGN ; set result to positive + lda #0x80 ; set exponent to 0 < FPA0 < 1 + sta FP0EXP + lda FPA2+2 ; get a byte from FPA2 + sta FPSBYT ; save as extra precision + jmp LBA1C ; go normalize FPA0 +RSEED fqb 0x40e64dab ; constant random number generator seed +; SIN function +SIN jsr LBC5F ; copy FPA0 to FPA1 + ldx #LBFBD ; point to 2*pi + ldb FP1SGN ; get sign of FPA1 + jsr LBB89 ; divide FPA0 by 2*pi + jsr LBC5F ; copy FPA0 to FPA1 + bsr LBF38 ; convert FPA0 to an integer + clr RESSGN ; set result to positive + lda FP1EXP ; get exponent of FPA1 + ldb FP0EXP ; get exponent of FPA0 + jsr LB9BC ; subtract FPA0 from FPA1 + ldx #LBFC2 ; point to FP 0.25 + jsr LB9B9 ; subtract FPA0 from 0.25 (pi/2) + lda FP0SGN ; get result sign + pshs a ; save it + bpl LBFA6 ; brif positive + jsr LB9B4 ; add 0.5 (pi) to FPA0 + lda FP0SGN ; get sign of result + bmi LBFA9 ; brif negative + com RELFLG ; if 3pi/2 >= arg >= pi/2 +LBFA6 jsr LBEE9 ; flip sign of FPA0 +LBFA9 ldx #LBFC2 ; point to 0.25 + jsr LB9C2 ; add 0.25 (pi/2) to FPA0 + puls a ; get original sign + tsta ; was it positive + bpl LBFB7 ; brif so + jsr LBEE9 ; flip result sign +LBFB7 ldx #LBFC7 ; point to series coefficients + jmp LBEF0 ; go calculate value +LBFBD fcb 0x83,0x49,0x0f,0xda,0xa2 ; 2*pi +LBFC2 fcb 0x7f,0x00,0x00,0x00,0x00 ; 0.25 +; modified taylor series SIN coefficients +LBFC7 fcb 6-1 ; six coefficients + fcb 0x84,0xe6,0x1a,0x2d,0x1b ; -((2pi)^11)/11! + fcb 0x86,0x28,0x07,0xfb,0xf8 ; ((2pi)^9)/9! + fcb 0x87,0x99,0x68,0x89,0x01 ; -((2pi)^7)/7! + fcb 0x87,0x23,0x35,0xdf,0xe1 ; ((2pi)^5)/5! + fcb 0x86,0xa5,0x5d,0xe7,0x28 ; -(2pi)^3)/3! + fcb 0x83,0x49,0x0f,0xda,0xa2 ; 2*pi +; these 12 bytes are unused + fcb 0xa1,0x54,0x46,0x8f,0x13,0x8f,0x52,0x43 + fcb 0x89,0xcd,0xa6,0x81 +; these are the hardware interrupt vectors (coco1/2 only) + fdb SW3VEC + fdb SW2VEC + fdb FRQVEC + fdb IRQVEC + fdb SWIVEC + fdb NMIVEC + fdb RESVEC diff -r 000000000000 -r 605ff82c4618 bas11.s --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bas11.s Sat Dec 08 19:57:01 2018 -0700 @@ -0,0 +1,4068 @@ + *pragma nolist + include defs.s + *pragma list +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; COLOR BASIC ROM +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + org BASIC +; This is the official set of ROM entry points. It is unfortunate that the generic input ("console in") routine is not exposed +; here. It is also unfortunate that no open and close file routines are exposed. This and other oversignts led to a lot of +; software either having to re-invent the wheel or calling directly into the ROM instead of via these official entry points. +POLCAT fdb KEYIN ; indirect jump, get a keystroke +CHROUT fdb PUTCHR ; indirect jump, output character +CSRDON fdb CASON ; indirect jump, turn cassette on and start reading +BLKIN fdb GETBLK ; indirect jump, read a block from tape +BLKOUT fdb SNDBLK ; indirect jump, write a block to tape +JOYIN fdb GETJOY ; indirect jump, read joystick axes +WRTLDR fdb WRLDR ; indirect jump, turn cassette on and write a leader +; Initialization code. +LA00E lds #LINBUF+LBUFMX+1 ; put the stack in the line input buffer which is a safe place for now + lda #0x37 ; enable the cartidge interrupt (to detect autostarting cartridges) + sta PIA1+3 + lda RSTFLG ; get warm start flag + cmpa #0x55 ; is it valid? + bne BACDST ; brif not - cold start + ldx RSTVEC ; get warm start routine pointer + lda ,x ; get first byte of the routine + cmpa #0x12 ; is it NOP? + bne BACDST ; brif not - the routine is invalid so do a cold start + jmp ,x ; transfer control to the warm start routine +; RESET/power on comes here +RESVEC leay LA00E,pcr ; point to warm start check code +LA02A ldx #PIA1 ; point to PIA1 - we're going to rely on the mirroring to reach PIA0 + clr -3,x ; set PIA0 DA to direction mode + clr -1,x ; set PIA0 DB to direction mode + clr -4,x ; set PIA0 DA to inputs + ldd #0xff34 + sta -2,x ; set PIA0 DB to outputs + stb -3,x ; set PIA0 DA to data mode + stb -1,x ; set PIA0 DB to data mode + clr 1,x ; set PIA1 DA to direction mode + clr 3,x ; set PIA1 DB to direction mode + deca + sta ,x ; set PIA1 DA bits 7-1 as output, 0 as input + lda #0xf8 ; set PIA1 DB bits 7-3 as output, 2-0 as input + sta 2,x + stb 1,x ; set PIA1 DA to data mode + stb 3,x ; set PIA1 DB to data mode + clr 2,x ; set VDG to alpha-numeric + ldb #2 ; make RS232 marking ("stop" bit) + stb ,x + ldu #SAMREG ; point to SAM register + ldb #16 ; 16 bits to clear +LA056 sta ,u++ ; clear a bit + decb ; done all? + bne LA056 ; brif not + sta SAMREG+9 ; put display at 0x400 + tfr b,dp ; set direct page to 0 + ldb #4 ; use as a mask to check RAMSZ input + sta -2,x ; set RAMSZ strobe high + bitb 2,x ; check RAMSZ input + beq LA072 ; brif set for 4K RAMs + clr -2,x ; set strobe low + bitb 2,x ; check input + beq LA070 ; brif set for 64K rams + leau -2,u ; adjust pointer to set SAM for 16K RAMs +LA070 sta -3,u ; program SAM for either 16K or 64K RAMs +LA072 jmp ,y ; transfer control to startup routine +; Cold start jumps here +BACDST ldx #VIDRAM+1 ; point past the top of the first 1K of memory (for double predec below) +LA077 clr ,--x ; clear a byte (last will actually try clearing LSB of RESET vector in ROM) + leax 1,x ; move forward one byte (will set Z if we're done) + bne LA077 ; brif not donw yet + jsr LA928 ; clear the screen + clr ,x+ ; put the constant zero that lives before the program + stx TXTTAB ; set beginning of program storage +LA084 lda 2,x ; get value from memory + coma ; make it different + sta 2,x ; try putting different into memory + cmpa 2,x ; did it matcH? + bne LA093 ; brif not - we found the end of memory + leax 1,x ; move pointer forward + com 1,x ; restore the original memory contents + bra LA084 ; try another byte +LA093 stx TOPRAM ; save top of memory (one below actual top because we need a byte for VAL() to work) + stx MEMSIZ ; save top of string space + stx STRTAB ; set bottom of allocated string space + leax -200,x ; allocate 200 bytes of string space + stx FRETOP ; set top of actually free memory + tfr x,s ; put the stack there + ldx #LA10D ; point to variable initializer + ldu #CMPMID ; point to variables to initialize (first batch) + ldb #28 ; 28 bytes in first batch + jsr LA59A ; copy bytes to variables + ldu #IRQVEC ; point to variables to initialize (second batch) + ldb #30 ; 30 bytes this time + jsr LA59A ; copy bytes to variables + ldx -12,x ; get SN error address + stx 3,u ; set ECB's command handlers to error + stx 8,u + ldx #RVEC0 ; point to RAM vectors + ldd #0x394b ; write 75 RTS opcodes (25 RAM vectors) +LA0C0 sta ,x+ ; put an RTS + decb ; done? + bne LA0C0 ; brif not + sta LINHDR-1 ; make temporary line header data for line encoding have a nonzero next line pointer + jsr LAD19 ; do a "NEW" + ldx #'E*256+'X ; magic number to detect ECB ROM + cmpx EXBAS ; is there an ECB ROM? + lbeq EXBAS+2 ; brif so - launch it + andcc #0xaf ; start interrupts + ldx #LA147-1 ; point to sign on message + jsr LB99C ; print it out + ldx #BAWMST ; warm start routine address + stx RSTVEC ; set vector there + lda #0x55 ; warm start valid flag + sta RSTFLG ; mark warm start valid + bra LA0F3 ; go to direct mode +; Warm start entry point +BAWMST nop ; valid routine marker + clr DEVNUM ; reset output/input to screen + jsr LAD33 ; do a partial NEW + andcc #0xaf ; start interrupts + jsr LA928 ; clear the screen +LA0F3 jmp LAC73 ; go to direct mode +; FIRQ service routine - this handles starting autostart cartridges +BFRQSV tst PIA1+3 ; is it the cartridge interrupt? + bmi LA0FC ; brif so + rti +LA0FC jsr LA7D1 ; delay for a while + jsr LA7D1 ; delay for another while + leay 0 ; NOTE: the 0 is a placeholder, extended addressing is required + jmp BROMHK +; Variable initializers (second batch) + jmp BIRQSV ; IRQ handler + jmp BFRQSV ; FIRQ handler + jmp LB44A ; default USR() address + fcb 0x80,0x4f,0xc7,0x52,0x59 ; random seed + fcb 0xff ; capslock flag - default to upper case + fdb DEBDEL ; keyboard debounce delay (why is it a variable?) + jmp LB277 ; exponentiation handler vector + fcb 53 ; (command interpretation table) 53 commands + fdb LAA66 ; (command interpretation table) reserved words list (commands) + fdb LAB67 ; (command interpretation table) jump table (commands) + fcb 20 ; (command interpretation table) 20 functions + fdb LAB1A ; (command interpretation table) reserved words list (functions) + fdb LAA29 ; (command interpretation table) jump table (functions) +; This is the signon message. +LA147 fcc 'COLOR BASIC 1.1' + fcb 0x0d + fcc '(C) 1980 TANDY' + fcb 0 +; This is the "invalid colour" CLS easter egg text. Basically 11 wasted bytes +LA166 fcc 'MICROSOFT' + fcb 0x0d,0 +; Read a character from current device and mask off bit 7 (keep it as 7 bit ASCII) +LA171 bsr LA176 ; get character + anda #0x7f ; mask off high bit + rts +; Generic read routine. Reads a character from the device specified by DEVNUM. If no input was available, +; CINBFL will bet nonzero (on EOF). If input was available, CINBFL will be zero. Note that this routine +; has undefined results when called on an output only device. All registers except CC and A are preserved. +LA176 jsr RVEC4 ; do RAM hook + clr CINBFL ; flag data available + tst DEVNUM ; is it keyboard? + beq LA1B1 ; brif so - blink cursor and wait for key press + tst CINCTR ; is there anything in cassette input buffer? + bne LA186 ; brif so + com CINBFL ; flag EOF + rts +; Read character from cassette file +LA186 pshs u,y,x,b ; preserve registers + ldx CINPTR ; get input buffer pointer + lda ,x+ ; get character from buffer + pshs a ; save it for return + stx CINPTR ; save new input buffer pointer + dec CINCTR ; count character just consumed + bne LA197 ; brif buffer is not empty yet + jsr LA635 ; go read another block, if any, to refill the buffer +LA197 puls a,b,x,y,u,pc ; restore registers and return the character +; Blink the cursor. This might be better timed via an interrupt or something. +LA199 dec BLKCNT ; is it time to blink the cursor? + bne LA1AB ; brif not + ldb #11 ; reset blink timer + stb BLKCNT + ldx CURPOS ; get cursor position + lda ,x ; get character at the cursor + adda #0x10 ; move to next color + ora #0x8f ; make sure it's a grahpics block with all elements lit + sta ,x ; put new cursor block on screen +LA1AB ldx #DEBDEL ; we'll use the debounce delay for the cursor blink timer (10ms) +LA1AE jmp LA7D3 ; go count X down +; Blink cursor while waiting for a key press +LA1B1 pshs x,b ; save registers +LA1B3 bsr LA199 ; go do a cursor iteration + bsr KEYIN ; go read a key + beq LA1B3 ; brif no key pressed + ldb #0x60 ; VDG screen space character + stb [CURPOS] ; blank cursor out +LA1BF puls b,x,pc ; restore registers and return +; This is the actual keyboard polling routine. Returns 0 if no new key is down. Updated compared to 1.0 to reject +; joystick buttons. +KEYIN pshs u,x,b ; save registers + bsr LA1C8 ; get keystroke + tsta ; set flags + puls b,x,u,pc ; restore registers and return +LA1C8 ldu #PIA0 ; point to keyboard PIA + ldx #KEYBUF ; point to state table + clra ; clear carry and set column strobe and counter to 0xff + deca + pshs x,a ; save colomn counter and a couple of holes for temporaries + sta 2,u ; initialize the column strobe to no columns active + skip1 +LA1D5 comb ; set carry flag + rol 2,u ; move to nextcolumn + bcc LA1BF ; brif we've done the last one + inc 0,s ; bump column count + bsr LA239 ; read keyboard row data + sta 1,s ; save key data + eora ,x ; set any bit where a key state changed + anda ,x ; ignore any where a key was released + ldb 1,s ; get new key data + stb ,x+ ; save in state table + tsta ; was a key down? + beq LA1D5 ; brif not - check another + ldb 2,u ; get column strobe data + stb 2,s ; save it for later + ldb #0xf8 ; make sure B is 0 after first ADDB +LA1F1 addb #8 ; adjust to next row + lsra ; are we at the right row base? + bcc LA1F1 ; brif not + addb 0,s ; add in column number + beq LA244 ; brif it was @ + cmpb #26 ; letter? + bhi LA246 ; brif not + orb #0x40 ; add in upper case ASCII bias + bsr LA22E ; check for shift key + beq LA20B ; brif shift down + lda CASFLG ; check casplock + bne LA20B ; brif not caps mode + orb #0x20 ; convert to lower case +LA20B stb 0,s ; temp store ASCII value + ldx DEBVAL ; get debounce dely counter + jsr LA7D3 ; wait while we count X down + ldb #0xff ; set column strobe to no columns + bsr LA237 ; read keyboard data + inca ; do we have anything reading? + bne LA220 ; brif so - reject keyboard read +LA21A ldb 2,s ; get saved column strobe + bsr LA237 ; read the keyboard data + cmpa 1,s ; does it match the result before the delay? +LA220 puls a ; get back return value + bne LA22B ; brif we have a non-match or joystick button + cmpa #0x12 ; SHIFT-0? + bne LA22C ; brif not + com CASFLG ; swap capslock state +LA22B clra ; set Z and return zero for no key down +LA22C puls x,pc ; restore registers and return +LA22E lda #0x7f ; column strobe for SHIFT + sta 2,u ; strobe keyboard + lda ,u ; get row data + anda #0x40 ; only keep shift state + rts +LA237 stb 2,u ; save requested column strobe +LA239 lda ,u ; read row data + ora #0x80 ; mask joystick comparator input + tst 2,u ; are we reading column 7? + bmi LA243 ; brif not + ora #0xc0 ; also mask off the SHIFT key +LA243 rts +LA244 ldb #51 ; scan code for @ +LA246 ldx #CONTAB-0x36 ; point to first batch in control code list + cmpb #33 ; arrows, space, zero? + blo LA263 ; brif so + ldx #CONTAB-0x54 ; point to second batch in control code list + cmpb #48 ; ENTER, CLEAR, BREAK, @? + bhs LA263 ; brif so + bsr LA22E ; get shift status + cmpb #43 ; number, colon, semicolon? + bls LA25C ; brif so + eora #0x40 ; invert shift sense if so +LA25C tsta ; test shift status + beq LA20B ; brif shift down - we have the code so check for debounce + addb #0x10 ; add in ASCII offset + bra LA20B ; check for debounce +LA263 aslb ; two entries for table entry + bsr LA22E ; get shift status + bne LA269 ; brif not down + incb ; move to shifted code entry +LA269 ldb b,x ; get ASCII code + bra LA20B ; go check for debounce + fcb 0 ; unused in Color Basic 1.0 +CONTAB fcb 0x5e,0x5f ; (^, _) + fcb 0x0a,0x5b ; (LF, [) + fcb 0x08,0x15 ; (BS, ^U) + fcb 0x09,0x5d ; (TAB, ]) + fcb 0x20,0x20 ; + fcb 0x30,0x12 ; <0> (0, ^R) + fcb 0x0d,0x0d ; (CR, CR) + fcb 0x0c,0x5c ; (FF, \) + fcb 0x03,0x03 ; (^C, ^C) + fcb 0x40,0x13 ; <@> (@, ^S) +; Generic output routine. +; Output character in A to the device specified by DEVNUM. All registers are preserved except CC. +; Sending output to a device that does not support output is undefined. +PUTCHR jsr RVEC3 ; call RAM hook + pshs b ; save B + ldb DEVNUM ; get desired device number + incb ; set flags (Z for -1, etc.) + puls b ; restore B + bmi LA2BF ; brif < -1 (line printer) + bne LA30A ; brif > -1 (screen) +; Write character to tape file + pshs x,b,a ; save registers + ldb FILSTA ; get file status + decb ; input file? + beq LA2A6 ; brif so + ldb CINCTR ; get character count + incb ; account for this character + bne LA29E ; brif buffer not full + bsr LA2A8 ; write previously full block to tape +LA29E ldx CINPTR ; get output buffer pointer + sta ,x+ ; put character in output + stx CINPTR ; save new buffer pointer + inc CINCTR ; account for this character +LA2A6 puls a,b,x,pc ; restore registers and return +; Write a block of data to tape. +LA2A8 ldb #1 ; data block type +LA2AA stb BLKTYP ; set block type + ldx #CASBUF ; point to output buffer + stx CBUFAD ; set buffer pointer + ldb CINCTR ; get number of bytes in the block + stb BLKLEN ; set length to write + pshs u,y,a ; save registers + jsr LA7E5 ; write a block to tape + puls a,y,u ; restore registers + jmp LA650 ; reset buffer pointers +LA2BF pshs x,b,a,cc ; save registers and interrupt status + orcc #0x50 ; disable interrupts + bsr LA2FB ; set output to marking + clrb ; transmit a start bit + bsr LA2FD + ldb #8 ; send 8 bits +LA2CA pshs b ; save bit counter + clrb ; set output to + lsra ; get output bit to C + rolb ; get it to the correct bit position for output + aslb + bsr LA2FD ; send the bit + puls b ; get back bit counter + decb ; sent all 8 bits? + bne LA2CA ; brif not + bsr LA2FB ; send stop bit (B is 0) + puls cc,a ; restore interrupts and output character + cmpa #0x0d ; carriage return? + beq LA2E7 ; brif so + inc LPTPOS ; bump printer position + ldb LPTPOS ; get current printer position + cmpb LPTWID ; end of line? + blo LA2ED ; brif not +LA2E7 clr LPTPOS ; reset to start of line + bsr LA305 ; delay for carriage return + bsr LA305 +LA2ED ldb PIA1+2 ; get rs232 status + lsrb ; is it "read"? + bcs LA2ED ; brif not + puls b,x,pc ; restore registers and return + fdb 0,0,0 ; unused space +LA2FB ldb #2 ; set output to high (marking) +LA2FD stb PIA1 ; set RS232 output + bsr LA302 ; do baud delay (first iteration) then fall through for second +LA302 ldx LPTBTD ; get buard rate delay constant + skip2 +LA305 ldx LPTLND ; get carriage return delay constant + jmp LA7D3 ; count X down +; Output character to screen +LA30A pshs x,b,a ; save registers + ldx CURPOS ; get cursor pointer + cmpa #0x08 ; backspace? + bne LA31D ; brif not + cmpx #VIDRAM ; at top of screen? + beq LA35D ; brif so - it's a no-op + lda #0x60 ; VDG space character + sta ,-x ; put a space at previous location and move pointer back + bra LA344 ; save new cursor position and return +LA31D cmpa #0x0d ; carriage return? + bne LA32F ; brif not + ldx CURPOS ; get cursor pointer (why? we already have it) +LA323 lda #0x60 ; VDG space character + sta ,x+ ; put output space + tfr x,d ; see if we at a multiple of 32 now + bitb #0x1f + bne LA323 ; brif not + bra LA344 ; go check for scrolling +LA32F cmpa #0x20 ; control character? + blo LA35D ; brif so + tsta ; is it graphics block? + bmi LA342 ; brif so + cmpa #0x40 ; number or special? + blo LA340 ; brif so (flip "case" bit) + cmpa #0x60 ; upper case alpha? + blo LA342 ; brif so - keep it unmodified + anda #0xdf ; clear bit 5 (inverse video) +LA340 eora #0x40 ; flip inverse video bit +LA342 sta ,x+ ; output character +LA344 stx CURPOS ; save new cursor position + cmpx #VIDRAM+511 ; end of screen? + bls LA35D ; brif not + ldx #VIDRAM ; point to start of screen +LA34E ldd 32,x ; get two characters from next row + std ,x++ ; put them on this row + cmpx #VIDRAM+0x1e0 ; at start of last row on screen? + blo LA34E ; brif not + ldb #0x60 ; VDG space + jsr LA92D ; blank out last line (borrow CLS's loop) +LA35D puls a,b,x,pc ; restore registers and return +; Set up device parameters for output +LA35F jsr RVEC2 ; do the RAM hook dance + pshs x,b,a ; save registers + clr PRTDEV ; flag device as a screen + lda DEVNUM ; get devicenumber + beq LA373 ; brif screen + inca ; is it tape? + beq LA384 ; brif so + ldx LPTCFW ; get tab width and last tab stop for printer + ldd LPTWID ; get line width and current position for printer + bra LA37C ; set parameters +LA373 ldb CURPOS+1 ; get LSB of cursor position + andb #0x1f ; now we have the offset into the line + ldx #0x1010 ; 16 character tab, position 16 is last tab stop + lda #32 ; screen is 32 characters wide +LA37C stx DEVCFW ; save tab width and last tab stop for active device + stb DEVPOS ; save line position for current device + sta DEVWID ; save line width for current device + puls a,b,x,pc ; restore registers and return +LA384 com PRTDEV ; flag device as non-display + ldx #0x0100 ; tab width is 1, last tab field is 0 + clra ; line width is 0 + clrb ; character position on line is 0 + bra LA37C ; go set parameters +; This is the line input routine used for reading lines for Basic, both in immediate mode and for +; INPUT and LINE INPUT. Exit with C set if terminated by BREAK and C clear if terminated by ENTER. +; The actualy entry point is LA390. Note that this routine echoes to *all* devices. +LA38D jsr LA928 ; clear screen (CLEAR key handling) +LA390 jsr RVEC12 ; do the RAM hook dance + clr IKEYIM ; reset cached input character from BREAK check + ldx #LINBUF+1 ; point to line input buffer (input pointer) + ldb #1 ; Number of characters in line (we start at 1 so BS handling is easier) +LA39A jsr LA171 ; get an input character, only keep low 7 bits + tst CINBFL ; is it EOF? + bne LA3CC ; brif EOF + tst DEVNUM ; is it keyboard input? + bne LA3C8 ; brif not - don't do line editing + cmpa #0x0c ; form feed (CLEAR)? + beq LA38D ; brif so - clear screen and reset + cmpa #0x08 ; backspace? + bne LA3B4 ; brif not + decb ; move back one character + beq LA390 ; brif we were at the start of the line - reset and start again + leax -1,x ; move input pointer back + bra LA3E8 ; echo the backspace and continue +LA3B4 cmpa #0x15 ; SHIFT-LEFT (kill line)? + bne LA3C2 ; brif not +LA3B8 decb ; at start of line? + beq LA390 ; brif so - reset and restart + lda #0x08 ; echo a backspace + jsr PUTCHR + bra LA3B8 ; see if we've erased everything yet +LA3C2 cmpa #0x03 ; BREAK? + orcc #1 ; set C if it is (only need Z for the next test + beq LA3CD ; brif BREAK - exit +LA3C8 cmpa #0x0d ; ENTER (CR) + bne LA3D9 ; brif not +LA3CC clra ; clear carry (it might not be clear on EOF) +LA3CD pshs cc ; save ENTER/BREAK flag + jsr LB958 ; echo a carriage return + clr ,x ; make sure we have a NUL at the end of the buffer + ldx #LINBUF ; point to input buffer + puls cc,pc ; restore ENTER/BREAK flag and return +LA3D9 cmpa #0x20 ; control character? + blo LA39A ; brif so - skip it + cmpa #'z+1 ; above z? + bhs LA39A ; brif so - ignore it + cmpb #LBUFMX ; is the buffer full? + bhs LA39A ; brif so - ignore extra characters + sta ,x+ ; put character in the buffer + incb ; bump character count +LA3E8 jsr PUTCHR ; echo character + bra LA39A ; go handle next input character +; Check if DEVNUM Is valid for reading. Raise FM error if open but not for reading. NO error if not open. +LA3ED jsr RVEC5 ; do the RAM hook dance + lda DEVNUM ; get device number + beq LA415 ; brif keyboard - always valid + inca ; is it tape? + bne LA403 ; brif not + lda FILSTA ; get tape file status + bne LA400 ; brif file is open +LA3FB ldb #22*2 ; raise NO error + jmp LAC46 +LA400 deca ; is it in input mode? + beq LA415 ; brif so +LA403 jmp LA616 ; raise FM error +; Check if DEVNUM is valid for writing. Raise FM error if open but not for writing. NO error if not open. +LA406 jsr RVEC6 ; do the RAM hook dance + lda DEVNUM ; get device number + inca ; is it tape? + bne LA415 ; brif not + lda FILSTA ; get file status + beq LA3FB ; brif not open + deca ; is it open for reading? + beq LA403 ; brif so - bad mode +LA415 rts +; CLOSE command +CLOSE beq LA426 ; brif no file specified - close all files + jsr LA5A5 ; parse device number +LA41B bsr LA42D ; close specified file + jsr GETCCH ; is there more? + beq LA44B ; brif not + jsr LA5A2 ; check for comma and parse another device number + bra LA41B ; go close this one +; Close all files handler. +LA426 jsr RVEC7 ; Yup. The RAM hook dance. + lda #-1 ; start with tape file + sta DEVNUM +; Close file specified in DEVNUM. Note that this never fails. +LA42D jsr RVEC8 ; You know it. RAM hook. + lda DEVNUM ; get device we're closing + clr DEVNUM ; reset to screen/keyboard + inca ; is it tape? + bne LA44B ; brif not + lda FILSTA ; get file status + cmpa #2 ; is it output? + bne LA449 ; brif not + lda CINCTR ; is there anything waiting to be written? + beq LA444 ; brif not + jsr LA2A8 ; write final block of data +LA444 ldb #0xff ; write EOF block + jsr LA2AA +LA449 clr FILSTA ; mark tape file closed +LA44B rts +; CSAVE command +CSAVE jsr LA578 ; parse filename + jsr GETCCH ; see what we have after the file name + beq LA469 ; brif none + jsr LB26D ; make sure there's a comma + ldb #'A ; make sure there's an A after + jsr LB26F + bne LA44B ; brif not end of line + clra ; file type 0 (basic program) + jsr LA65C ; write out header block + lda #-1 ; set output to tape + sta DEVNUM + clra ; set Z so we list the whole program + jmp LIST ; go list the program to tape +LA469 clra ; file type 0 (basic program) + ldx ZERO ; set to binary file mode + jsr LA65F ; write header block + clr FILSTA ; close files + inc BLKTYP ; set block type to data + jsr WRLDR ; write out a leader + ldx TXTTAB ; point to start of program +LA478 stx CBUFAD ; set buffer location + lda #255 ; block size to 255 bytes (max size) + sta BLKLEN + ldd VARTAB ; get end of program + subd CBUFAD ; how much is left? + beq LA491 ; brif we have nothing left + cmpd #255 ; do we have a full block worth? + bhs LA48C ; brif so + stb BLKLEN ; save actual remainder as block length +LA48C jsr SNDBLK ; write a block out + bra LA478 ; go do another block +LA491 neg BLKTYP ; set block type to 0xff (EOF) + clr BLKLEN ; no data in EOF block + jmp LA7E7 ; write EOF, stop tape, and return +; CLOAD and CLOADM commands +CLOAD clr FILSTA ; close tape file + cmpa #'M ; is it ClOADM? + beq LA4FE ; brif so + leas 2,s ; clean up stack + jsr LA5C5 ; parse file name + jsr LA648 ; go find the file + tst CASBUF+10 ; is it binary? + beq LA4C8 ; brif so + lda CASBUF+9 ; is it ASCII? + beq LA4CD ; brif not + jsr LAD19 ; clear out existing program + lda #-1 ; set up for reading from tape + sta DEVNUM + inc FILSTA ; set tape file to input + jsr LA635 ; go read first block + jmp LAC7C ; go to immediate mode to read in the program +; This is the EOF handler for immediate mode. It's rather assinine for this to be located here. It is +; probably an artifact of a substantially different code layout prior to compacting the ROM to fit in +; 8K. +LA4BF jsr RVEC13 ; do the RAM hook dance + jsr LA42D ; close file + jmp LAC73 ; go back to immediate mode +LA4C8 lda CASBUF+8 ; get file type + beq LA4D0 ; brif basic program +LA4CD jmp LA616 ; raise FM error +LA4D0 jsr LAD19 ; erase existing program + jsr CASON ; start reading tape + ldx TXTTAB ; get start of program storage +LA4D8 stx CBUFAD ; set load address for block + ldd CBUFAD ; get start of block + inca ; bump by 256 + jsr LAC37 ; check if there's room for a maximum sized block of 255 + jsr GETBLK ; go read a block + bne LA4F8 ; brif there was an error during reading + lda BLKTYP ; get type of block read + beq LA4F8 ; brif header block - IO error + bpl LA4D8 ; brif data block - read another + stx VARTAB ; save new end of program + bsr LA53B ; stop tape + ldx #LABED-1 ; point to "OK" prompt + jsr LB99C ; show prompt + jmp LACE9 ; reset various things and return +LA4F8 jsr LAD19 ; clear out partial program load +LA4FB jmp LA619 ; raise IO error +; This is the CLOADM command +LA4FE jsr GETNCH ; eat the "M" + bsr LA578 ; parse file name + jsr LA648 ; go find the file +LA505 ldx ZERO ; default offset is 0 + jsr GETCCH ; see if there's something after the file name + beq LA511 ; brif no offset + jsr LB26D ; make sure there's a comma + jsr LB73D ; evaluate offset to X +LA511 lda CASBUF+8 ; get file mode + cmpa #2 ; M/L program? + bne LA4CD ; brif not - FM error + ldd CASBUF+11 ; get load address + leau D,x ; add in offset + stu EXECJP ; set EXEC default address + tst CASBUF+10 ; is it binary? + bne LA4CD ; brif not + ldd CASBUF+13 ; get load address + leax d,x ; add in offset + stx CBUFAD ; set buffer address for loading + jsr CASON ; start up tape +LA52E jsr GETBLK ; read a block + bne LA4FB ; brif error reading + stx CBUFAD ; save new load address + tst BLKTYP ; set flags on block type + beq LA4FB ; brif another header - IO error + bpl LA52E ; brif it was data - read more +LA53B jmp LA7E9 ; turn off tape and return +; The EXEC command +EXEC beq LA545 ; brif no argument - use default address + jsr LB73D ; evaluate EXEC address to X + stx EXECJP ; set new default EXEC address +LA545 jmp [EXECJP] ; transfer control to execution address +; Break check for LIST so it doesn't interrupt ASCII saves. Why is this here instead of with the rest of the break +; check logic or packaged up with LIST? +LA549 jsr RVEC11 ; do the RAM hook dance + lda DEVNUM ; get device number + inca ; is it tape? + beq LA5A1 ; brif so - don't do break check + jmp LADEB ; do the actual break check +; Evaluate an expression and make sure it is within the limits of the screen and sets the cursor position. +; This really should be located with the PRINT command. +LA554 jsr LB3E4 ; evaluate a positive expression to D + subd #511 ; is it within bounds? + lbhi LB44A ; brif not - error out + addd #VIDRAM+511 ; adjust to be within the screen (and undo the SUBD above) + std CURPOS ; set cursor position + rts +; INKEY$ function +INKEY lda IKEYIM ; was a key down during break check? + bne LA56B ; brif so + jsr KEYIN ; poll the keyboard +LA56B clr IKEYIM ; reset the break check cache + sta FPA0+3 ; store result for later return + lbne LB68F ; brif a key was down - return it as a string + sta STRDES ; set string length to 0 (no key down) + jmp LB69B ; return the NULL string +; Parse a filename +LA578 ldx #CFNBUF ; point to file name buffer + clr ,x+ ; zero out file name length + lda #0x20 ; space character to initialize file name +LA57F sta ,x+ ; put a space in the buffer + cmpx #CASBUF ; at end of file name? + bne LA57F ; brif not + jsr GETCCH ; get input character + beq LA5A1 ; brif no name present + jsr LB156 ; evaluate the file name expression + jsr LB654 ; point to start of the file name + ldu #CFNBUF ; point to file name buffer + stb ,u+ ; save string length + beq LA5A1 ; brif empty - we're done + skip2 +LA598 ldb #8 ; copy 8 bytes +; Move B bytes from (X) to (U) +LA59A lda ,x+ ; copy a byte + sta ,u+ + decb ; done yet? + bne LA59A ; brif not +LA5A1 rts +; Parse a device number and check validity +LA5A2 jsr LB26D ; check for comma and SN error if not +LA5A5 cmpa #'# ; do we have a #? + bne LA5AB ; brif not (it's optional) + jsr GETNCH ; munch the # +LA5AB jsr LB141 ; evaluate the expression +LA5AE jsr INTCNV ; convert it to an integer in D + rolb ; move sign of B into C + adca #0 ; add sign of B to A + bne LA61F ; brif A doesn't match the sign of B + rorb ; restore B (ADCA will have set C if B was negative) + stb DEVNUM ; set device number + jsr RVEC1 ; do the RAM hook dance + beq LA5C4 ; brif device number set to screen/keyboard (valid) + bpl LA61F ; brif not negative (not valid) + cmpb #-2 ; is it printer or tape? + blt LA61F ; brif not (not valid) +LA5C4 rts +; Read file name from the line and do an error if anything follows it +LA5C5 bsr LA578 ; parse file name + jsr GETCCH ; set flags on current character +LA5C9 beq LA5C4 ; brif nothing there - it's good + jmp LB277 ; raise SN error +; EOF functoin +EOF jsr RVEC14 ; do the RAM hook dance + lda DEVNUM ; get device number + pshs a ; save it (so we can restore it later) + bsr LA5AE ; check the device number (which is in FPA0) + jsr LA3ED ; check validity for reading +LA5DA clrb ; not EOF = 0 (FALSE) + lda DEVNUM ; get device number + beq LA5E4 ; brif keyboard - never EOF + tst CINCTR ; is there anything in the input buffer? + bne LA5E4 ; brif so - not EOF + comb ; set EOF flag to -1 (true) +LA5E4 puls a ; get back original device + sta DEVNUM ; restore it +LA5E8 sex ; sign extend result to 16 bits + jmp GIVABF ; go return the result +; SKIPF command +SKIPF bsr LA5C5 ; parse file name + bsr LA648 ; look for the file + jsr LA6D1 ; read the file + bne LA619 ; brif error reading file + rts +; OPEN command +OPEN jsr RVEC0 ; do the RAM hook dance + jsr LB156 ; get file status (input/output) + jsr LB6A4 ; get first character of status string + pshs b ; save status + bsr LA5A2 ; parse a comma then the device number + jsr LB26D ; make sure there's a comma + bsr LA5C5 ; parse the file name + lda DEVNUM ; get device number of the file + clr DEVNUM ; reset actual device to the screen + puls b ; get back status + cmpb #'I ; INPUT? + beq LA624 ; brif so - open a file for INPUT + cmpb #'O ; OUTPUT? + beq LA658 ; brif so - open a file for OUTPUT +LA616 ldb #21*2 ; raise FM error + skip2 +LA619 ldb #20*2 ; raise I/O error + skip2 +LA61C ldb #18*2 ; raise AO error + skip2 +LA61F ldb #19*2 ; raise DN error + jmp LAC46 +LA624 inca ; are we opening the tape? + bmi LA616 ; brif printer - FM error; printer can't be opened for READ + bne LA657 ; brif screen - screen is always open + bsr LA648 ; read header block + lda CASBUF+9 ; clear A if binary or machine language file + anda CASBUF+10 + beq LA616 ; bad file mode if not data file + inc FILSTA ; open file for input +LA635 jsr LA701 ; start tape, read block + bne LA619 ; brif error during read + tst BLKTYP ; check block type + beq LA619 ; brif header block - something's wrong + bmi LA657 ; brif EOF + lda BLKLEN ; get length of block + beq LA635 ; brif empty block - read another +LA644 sta CINCTR ; set buffer count + bra LA652 ; reset buffer pointer +LA648 tst FILSTA ; is the file open? + bne LA61C ; brif so - AO error + bsr LA681 ; search for file + bne LA619 ; brif error on read +LA650 clr CINCTR ; mark buffer empty +LA652 ldx #CASBUF ; set buffer pointer to start of buffer + stx CINPTR +LA657 rts +LA658 inca ; check for tape device + bne LA657 ; brif not tape (nothing doing - it's always open) + inca ; make file type 1 +LA65C ldx #0xffff ; ASCII and data mode +LA65F tst FILSTA ; is file open? + bne LA61C ; brif so - raise error + ldu #CASBUF ; point to tape buffer + stu CBUFAD ; set address of block to write + sta 8,u ; set file type + stx 9,u ; set ASCII flag and mode + ldx #CFNBUF+1 ; point to file name + jsr LA598 ; move file name to the tape buffer + clr BLKTYP ; set for header block + lda #15 ; 15 bytes in a header block + sta BLKLEN ; set block length + jsr LA7E5 ; write the block + lda #2 ; set file type to output + sta FILSTA + bra LA650 ; reset file pointers +; Search for correct cassette file name +LA681 ldx #CASBUF ; point to cassette buffer + stx CBUFAD ; set location to read blocks to +LA686 lda CURLIN ; are we in immediate mode? + inca + bne LA696 ; brif not + jsr LA928 ; clear screen + ldx CURPOS ; get start of screen (set after clear) + ldb #'S ; for "searching" + stb ,x++ ; put it on the screen + stx CURPOS ; save cursor position to be one past the search indicator +LA696 bsr LA701 ; read a block + orb BLKTYP ; merge error flag with block type + bne LA6D0 ; brif error or not header + ldx #CASBUF ; point to block just read + ldu #CFNBUF+1 ; point to the desired name + ldb #8 ; compare 8 characters + clr ,-s ; set flag to "match" +LA6A6 lda ,x+ ; get character from just read block + ldy CURLIN ; immediate mode? + leay 1,y + bne LA6B4 ; brif not + clr DEVNUM ; set output to screen + jsr PUTCHR ; display character +LA6B4 suba ,u+ ; subtract from desired file name (nonzero if no match) + ora ,s ; merge with match flag + sta ,s ; save new match flag (will be nonzero if any character differs) + decb ; done all characters? + bne LA6A6 ; brif not - do another + lda ,s+ ; get match flag (and set flags) + beq LA6CB ; brif we have a match + tst -9,u ; did we actually have a file name or will any file do? + beq LA6CB ; brif any file will do + bsr LA6D1 ; go read past the file + bne LA6D0 ; return on error + bra LA686 ; keep looking +LA6CB lda #'F ; for "found" + bsr LA6F8 ; put "F" on screen + clra ; set Z to indicat eno errors +LA6D0 rts +LA6D1 tst CASBUF+10 ; check type of file + bne LA6DF ; brif "blocked" file + jsr CASON ; turn on tape +LA6D9 bsr GETBLK ; read a block + bsr LA6E5 ; error or EOF? + bra LA6D9 ; read another block +LA6DF bsr LA701 ; read a single block + bsr LA6E5 ; error or EOF? + bra LA6DF ; read another block +LA6E5 bne LA6ED ; got error reading block + lda BLKTYP ; check block type + nega ; A is 0 now if EOF + bmi LA700 ; brif not end of file + deca ; clear error indicator +LA6ED sta CSRERR ; set error flag + leas 2,s ; don't return to original caller + bra LA705 ; turn off motor and return +LA6F3 lda VIDRAM ; get first char on screen + eora #0x40 ; flip case +LA6F8 ldb CURLIN ; immediate mode? + incb + bne LA700 ; brif not + sta VIDRAM ; save flipped case character +LA700 rts +; Read a single block from tape (for a "blocked" file) +LA701 bsr CASON ; start tape going + bsr GETBLK ; read block +LA705 jsr LA7E9 ; stop tape + ldb CSRERR ; get error status + rts +; Read a block from tape - this does the heavy lifting +GETBLK orcc #0x50 ; disable interrupts (timing is important) + bsr LA6F3 ; reverse video of upper left character in direct mode + ldx CBUFAD ; point to destination buffer + clra ; reset read byte +LA712 bsr LA755 ; read a bit + rora ; move bit into accumulator + cmpa #0x3c ; have we synched on the start of the block data yet? + bne LA712 ; brif not + bsr LA749 ; read block type + sta BLKTYP + bsr LA749 ; get block size + sta BLKLEN + adda BLKTYP ; accumulate checksum + sta CCKSUM ; save current checksum + lda BLKLEN ; get back count + sta CSRERR ; initialize counter; we use this since it will be ovewritten later anyway + beq LA73B ; brif empty block +LA72B bsr LA749 ; read a byte + sta ,x ; save in buffer + cmpa ,x+ ; make sure it wrote + bne LA744 ; brif error if it didn't match + adda CCKSUM ; accumulate checksum + sta CCKSUM + dec CSRERR ; read all bytes? + bne LA72B ; brif not +LA73B bsr LA749 ; read checksum from tape + suba CCKSUM ; does it match? + beq LA746 ; brif so + lda #1 ; checksum error flag + skip2 +LA744 lda #2 ; non-RAM error flag +LA746 sta CSRERR ; save error status + rts +LA749 lda #8 ; read 8 bits + sta CPULWD ; initialize counter +LA74D bsr LA755 ; read a bit + rora ; put it into accumulator + dec CPULWD ; got all 8 bits? + bne LA74D ; brif not + rts +LA755 bsr LA75D ; get time between transitions + ldb CPERTM ; get timer + decb + cmpb CMPMID ; set C if timer is below the transition point - high or 1; clear otherwise + rts +LA75D clr CPERTM ; reset timer + tst CBTPHA ; check which phase we synched on + bne LA773 ; brif HI-LO synch +LA763 bsr LA76C ; read input + bcs LA763 ; brif still high +LA767 bsr LA76C ; read input + bcc LA767 ; brif still low + rts +LA76C inc CPERTM ; bump timer + ldb PIA1 ; get input bit to C + rorb + rts +LA773 bsr LA76C ; read input + bcc LA773 ; brif still low +LA777 bsr LA76C ; read output + bcs LA777 ; brif still high + rts +; Start tape and look for sync bytes +CASON orcc #0x50 ; disable interrupts + bsr LA7CA ; turn on tape + clr CPULWD ; reset timer +LA782 bsr LA763 ; wait for low-high transition +LA784 bsr LA7AD ; wait for it to go low again + bhi LA797 ; brif in range for 1200 Hz +LA788 bsr LA7A7 ; wait for it to go high again + blo LA79B ; brif in range for 2400 Hz + dec CPULWD ; decrement counter (synched on low-high) + lda CPULWD ; get counter + cmpa #-96 ; have we seen 96 1-0-1-0 patterns (48 0x55s)? +LA792 bne LA782 ; brif not - wait some more + sta CBTPHA ; save phase we synched on + rts +LA797 bsr LA7A7 ; wait for it to go high again + bhi LA784 ; brif another 1200 Hz, 2 in a row, try again +LA79B bsr LA7AD ; wait for it to go low again + blo LA788 ; brif another 2400 Hz; go try again for high + inc CPULWD ; bump counter + lda CPULWD ; get counter + suba #96 ; set 0 if we've seen enought 0-1-0-1 patterns (0xaa) + bra LA792 ; set phase and return or keep waiting +LA7A7 clr CPERTM ; reset period timer + bsr LA767 ; wait for high + bra LA7B1 ; set flags on result +LA7AD clr CPERTM ; reset period timer + bsr LA777 ; wait for low +LA7B1 ldb CPERTM ; get period count + cmpb CMP0 ; is it too long for 1200Hz? + bhi LA7BA ; brif so - reset counts + cmpb CMP1 ; set C if 2400Hz, clear C if 1200 Hz + rts +LA7BA clr CPULWD ; reset sync counter (too slow or drop out) + rts +; MOTOR command +MOTOR tfr a,b ; save ON/OFF + jsr GETNCH ; eat the ON/OFF token + cmpb #0xaa ; OFF? + beq LA7E9 ; brif so - turn off tape + cmpb #0x88 ; ON? + jsr LA5C9 ; SN error if no match +; Turn on tape +LA7CA lda PIA1+1 ; get motor control value + ora #8 ; turn on bit 3 (starts motor) + bsr LA7F0 ; put it back (dumb but it saves a byte) +LA7D1 ldx ZERO ; maximum delay timer +LA7D3 leax -1,x ; count down + bne LA7D3 ; brif not at 0 yet + rts +; Write a synch leader to tape +WRLDR orcc #0x50 ; disable interrupts + bsr LA7CA ; turn on tape + ldx SYNCLN ; get count of 0x55s to write +LA7DE bsr LA828 ; write a 0x55 + leax -1,x ; done? + bne LA7DE ; brif not + rts +; Write sync bytes and a block, then stop tape +LA7E5 bsr WRLDR ; write sync +LA7E7 bsr SNDBLK ; write block +; Turn off tape +LA7E9 andcc #0xaf ; enable interrupts + lda PIA1+1 ; get control register + anda #0xf7 ; disable motor bit +LA7F0 sta PIA1+1 ; set motor enable bit + rts +; Write a block to tape. +SNDBLK orcc #0x50 ; disable interrupts + ldb BLKLEN ; get block size + stb CSRERR ; initialize character counter + lda BLKLEN ; initialize checksum + beq LA805 ; brif empty block + ldx CBUFAD ; point to tape buffer +LA800 adda ,x+ ; accumulate checksum + decb ; end of block data? + bne LA800 ; brif not +LA805 adda BLKTYP ; accumulate block type into checksum + sta CCKSUM ; save calculated checksum + ldx CBUFAD ; point to buffer + bsr LA828 ; send a 0x55 + lda #0x3c ; and then a 0x3c + bsr LA82A + lda BLKTYP ; send block type + bsr LA82A + lda BLKLEN ; send block size + bsr LA82A + tsta ; empty block? + beq LA824 ; brif so +LA81C lda ,x+ ; send character from block data + bsr LA82A + dec CSRERR ; are we done yet? + bne LA81C ; brif not +LA824 lda CCKSUM ; send checksum + bsr LA82A +LA828 lda #0x55 ; send a 0x55 +LA82A pshs a ; save output byte + ldb #1 ; initialize bit probe +LA82E lda CLSTSN ; get ending value of last cycle + sta PIA1 ; set DA + ldy #LA85C ; point to sine wave table + bitb ,s ; is bit set? + bne LA848 ; brif so - do high frequency +LA83B lda ,y+ ; get next sample (use all for low frequency) + cmpy #LA85C+36 ; end of table? + beq LA855 ; brif so + sta PIA1 ; set output sample + bra LA83B ; do another sample +LA848 lda ,y++ ; get next sample (use every other for high frequency) + cmpy #LA85C+36 ; end of table? + beq LA855 ; brif so + sta PIA1 ; send output sample + bra LA848 ; do another sample +LA855 sta CLSTSN ; save last sample that *would* have been sent + lslb ; shift mask to next bit + bcc LA82E ; brif not done all 8 bits + puls a,pc ; get back original character and return +; This is the sample table for the tape sine wave +LA85C fcb 0x82,0x92,0xaa,0xba,0xca,0xda + fcb 0xea,0xf2,0xfa,0xfa,0xfa,0xf2 + fcb 0xea,0xda,0xca,0xba,0xaa,0x92 + fcb 0x7a,0x6a,0x52,0x42,0x32,0x22 + fcb 0x12,0x0a,0x02,0x02,0x02,0x0a + fcb 0x12,0x22,0x32,0x42,0x52,0x6a +; SET command +SET bsr LA8C1 ; get absolute screen position of graphics block + pshs x ; save character location + jsr LB738 ; evaluate comma then expression in B + puls x ; get back character pointer + cmpb #8 ; valid colour? + bhi LA8D5 ; brif not + decb ; normalize colours + bmi LA895 ; brif colour 0 (use current colour) + lda #0x10 ; 16 patterns per colour + mul + bra LA89D ; go save the colour +LA895 ldb ,x ; get current value + bpl LA89C ; brif not grahpic + andb #0x70 ; keep only the colour + skip1 +LA89C clrb ; reset block to all black +LA89D pshs b ; save colour + bsr LA90D ; force a ) + lda ,x ; get current screen value + bmi LA8A6 ; brif graphic block already + clra ; force all pixels off +LA8A6 anda #0x0f ; keep only pixel data + ora GRBLOK ; set the desired pixel + ora ,s+ ; merge with desired colour +LA8AC ora #0x80 ; force it to be a graphic block + sta ,x ; put new block on screen + rts +; RESET command +RESET bsr LA8C1 ; get address of desired block + bsr LA90D ; force a ) + clra ; zero block (no pixels) + ldb ,x ; is it graphics? + bpl LA8AC ; brif not - just blank the block + com GRBLOK ; invert pixel data + andb GRBLOK ; turn off the desired pixel + stb ,x ; put new pixel data on screen + rts +; Parse SET/RESET/POINT coordinates except for closing ) +LA8C1 jsr LB26A ; make sure it starts with ( +LA8C4 jsr RVEC21 ; do the RAM hook dance + jsr LB70B ; get first coordinate + cmpb #63 ; valid horizontal coordinate + bhi LA8D5 ; brif out of range + pshs b ; save horizontal coordinate + jsr LB738 ; look for , followed by vertical coordinate + cmpb #31 ; in range for vertical? +LA8D5 bhi LA948 ; brif not + pshs b ; save vertical coordinate + lsrb ; divide by two (two blocks per row) + lda #32 ; 32 bytes per row + mul ; now we have the offset into video RAM + ldx #VIDRAM ; point to start of screen + leax d,x ; now X points to the correct character row + ldb 1,s ; get horizontal coordinate + lsrb ; divide by two (two per character cell) + abx ; now we're pointing to the correct character cell + puls a,b ; get back coordinates (vertical in A) + anda #1 ; keep only row offset of vertical + rorb ; get column offset of horizontal to C + rola ; now we have "row * 2 + col" in A + ldb #0x10 ; make a bit mask (one bit left of first pixel) +LA8EE lsrb ; move mask right + deca ; at the right pixel? + bpl LA8EE ; brif not + stb GRBLOK ; save graphics block mask + rts +; POINT function +POINT bsr LA8C4 ; evaluate coordinates + ldb #0xff ; default colour value is -1 (not graphics) + lda ,x ; get character + bpl LA90A ; brif not graphics + anda GRBLOK ; is desired pixel set? + beq LA909 ; brif not - return 0 for "black" + ldb ,x ; get graphics data + lsrb ; shift right 4 to get colour in low bits + lsrb + lsrb + lsrb + andb #7 ; lose the graphics block bias +LA909 incb ; shift colours into 1 to 8 range +LA90A jsr LA5E8 ; convert B to floating point +LA90D jmp LB267 ; make sure we have a ) and return +; CLS command +CLS jsr RVEC22 ; do the RAM hook dance +LA913 beq LA928 ; brif no colour - just do a basic screen clear + jsr LB70B ; evaluate colour number + cmpb #8 ; valid colour? + bhi LA937 ; brif not - do the easter egg + tstb ; color 0? + beq LA925 ; brif so + decb ; normalize to 0 based colour numbers + lda #0x10 ; 16 blocks per colour + mul ; now we have the base code for that colour + orb #0x0f ; set all pixels +LA925 orb #0x80 ; make it a graphics block + skip2 +LA928 ldb #0x60 ; VDG screen space character + ldx #VIDRAM ; point to start of screen +LA92D stx CURPOS ; set cursor position +LA92F stb ,x+ ; blank a character + cmpx #VIDRAM+511 ; end of screen? + bls LA92F ; brif not + rts +LA937 bsr LA928 ; clear te screen + ldx #LA166-1 ; point to the easter egg + jmp LB99C ; go display it +; Evaluate an expression to B, prefixed by a comma, and do FC error if 0 +LA93F jsr LB26D ; force a comma +LA942 jsr LB70B ; evaluate expression to B + tstb ; is it 0? + bne LA984 ; brif not - return +LA948 jmp LB44A ; raise FC error +; SOUND command +SOUND bsr LA942 ; evaluate frequency + stb SNDTON ; save it + bsr LA93F ; evaluate duration (after a comma) +LA951 lda #4 ; constant factor for duration (each increment is 1/15 of a second) + mul + std SNDDUR ; save length of sound (IRQ will count it down) + lda PIA0+3 ; enable 60 Hz interrupt + ora #1 + sta PIA0+3 + clr ARYDIS ; clear array disable flag for some reason + bsr LA9A2 ; connect DAC to MUX output + bsr LA976 ; turn on sound +LA964 bsr LA985 ; store mid range output value and delay + lda #0xfe ; store high value and delay + bsr LA987 + bsr LA985 ; store mid range value and delay + lda #2 ; store low value and delay + bsr LA987 + ldx SNDDUR ; has timer expired? + bne LA964 ; brif not, do another wave +; Disable sound output +LA974 clra ; bit 3 to 0 will disable output + skip2 +; Enable sound output +LA976 lda #8 ; bit 3 set to enable output + sta ,-s ; save desired value + lda PIA1+3 ; get control register value + anda #0xf7 ; reset value + ora ,s+ ; set to desired value + sta PIA1+3 ; set new sound output status +LA984 rts +LA985 lda #0x7e ; mid range value for DAC +LA987 sta PIA1 ; set DAC output value + lda SNDTON ; get frequency +LA98C inca ; increment it (gives shorter count with higher values, so higher frequencies work) + bne LA98C ; brif not done yet + rts +; AUDIO command +AUDIO tfr a,b ; save ON/OFF token + jsr GETNCH ; munch the ON/OFF token + cmpb #0xaa ; OFF? + beq LA974 ; brif so + subb #0x88 ; ON? + jsr LA5C9 ; do SN error if not + incb ; now B is 1 - cassette sound source + bsr LA9A2 ; set MUX input to tape + bra LA976 ; enable sound +; Set MUX source to value in B +LA9A2 ldu #PIA0+1 ; point to PIA0 control register A + bsr LA9A7 ; program bit 0 then fall through for bit 1 +LA9A7 lda ,u ; get control register value + anda #0xf7 ; reset mux control bit + asrb ; shift desired value to C + bcc LA9B0 ; brif this bit is clear + ora #8 ; set the bit +LA9B0 sta ,u++ ; set register value and move to next register + rts +; IRQ service routine +BIRQSV lda PIA0+3 ; check for VSYNC interrupt + bpl LA9C5 ; brif not - return. BUG: should clear HSYNC interrupt status first + lda PIA0+2 ; clear VSYNC interrupt status + ldx >SNDDUR ; are we counting down for SOUND? (force extended in case DP is modified) + beq LA9C5 ; brif not + leax -1,x ; count down one tick + stx >SNDDUR ; save new count (forced extended in case DP is modified) +LA9C5 rti +; JOYSTK function +JOYSTK jsr LB70E ; evaluate which joystick axis is desired + cmpb #3 ; valid axis? + lbhi LB44A ; brif not + tstb ; want axis 0? + bne LA9D4 ; brif not + bsr GETJOY ; read axis data if axis 0 +LA9D4 ldx #POTVAL ; point to axis values + ldb FPA0+3 ; get desired axis + ldb b,x ; get axis value + jmp LB4F3 ; return value +; Read all four joystick axes. Note that this routine will try 10 times to get a value that matches +; the value obtained during the *previous call to this routine*. Thus, if the axis value has changed, +; this routine will do the read *ten times* before just returning the last value. This is assininely +; slow and probably a bug since it seems more logical to look for two matching reads in a row. Note +; also that this routine should be using PSHS and PULS but it doesn't. +GETJOY bsr LA974 ; turn off sound + ldx #POTVAL+4 ; point to the end of the axis data (we'll work backwards) + ldb #3 ; start with axis 3 +LA9E5 lda #10 ; 10 tries to see if we match *the last call* to this routine + std ,--s ; save retry counter and axis number + bsr LA9A2 ; set MUX for the correct axis +LA9EB ldd #0x4080 ; set initial trial value to mid range and the next difference to add/subtract to half +LA9EE sta ,-s ; store the add/subtract value + orb #2 ; keep rs232 output marking + stb PIA1 ; set DAC output to the trial value + eorb #2 ; remove RS232 output value + lda PIA0 ; read the comparator + bmi LA9FF ; brif comparator output is high (DAC is lower than the axis value) + subb ,s ; subtract next bit value (split the difference toward 0) + skip2 +LA9FF addb ,s ; add next bit value (split the different toward infinity) + lda ,s+ ; get bit value back + lsra ; cut in half + cmpa #1 ; have we done that last value for the DAC? + bne LA9EE ; brif not + lsrb ; normalize the axis value + lsrb + cmpb -1,x ; does it match the read from the last call to this routine? + beq LAA12 ; brif so + dec ,s ; are we out of retries? + bne LA9EB ; brif not - try again +LAA12 stb ,-x ; save new value and move pointer back + ldd ,s++ ; get axis counter and clean up retry counter + decb ; move to next axis + bpl LA9E5 ; brif still more axes to do + rts +; This is the "bottom half" of the character fetching routines. +BROMHK cmpa #'9+1 ; is it >= colon? + bhs LAA28 ; brif so Z set if colon, C clear. + cmpa #0x20 ; space? + bne LAA24 ; brif not + jmp GETNCH ; move on to another character if space +LAA24 suba #'0 ; normalize ascii digit to 0-9; we already handled above digit 9 + suba #-'0 ; this will cause a carry for any value that was already positive +LAA28 rts +; Jump table for functions +LAA29 fdb SGN ; SGN 0x80 + fdb INT ; INT 0x81 + fdb ABS ; ABS 0x82 + fdb USRJMP ; USR 0x83 + fdb RND ; RND 0x84 + fdb SIN ; SIN 0x85 + fdb PEEK ; PEEK 0x86 + fdb LEN ; LEN 0x87 + fdb STR ; STR$ 0x88 + fdb VAL ; VAL 0x89 + fdb ASC ; ASC 0x8a + fdb CHR ; CHR$ 0x8b + fdb EOF ; EOF 0x8c + fdb JOYSTK ; JOYSTK 0x8d + fdb LEFT ; LEFT$ 0x8e + fdb RIGHT ; RIGHT$ 0x8f + fdb MID ; MID$ 0x90 + fdb POINT ; POINT 0x91 + fdb INKEY ; INKEY$ 0x92 + fdb MEM ; MEM 0x93 +; Operator precedence and jump table (binary ops except relational) +LAA51 fcb 0x79 ; + + fdb LB9C5 + fcb 0x79 ; - + fdb LB9BC + fcb 0x7b ; * + fdb LBACC + fcb 0x7b ; / + fdb LBB91 + fcb 0x7f ; ^ (exponentiation) + fdb EXPJMP + fcb 0x50 ; AND + fdb LB2D5 + fcb 0x46 ; OR + fdb LB2D4 +; Reserved words table for commands +LAA66 fcs 'FOR' ; 0x80 + fcs 'GO' ; 0x81 + fcs 'REM' ; 0x82 + fcs "'" ; 0x83 + fcs 'ELSE' ; 0x84 + fcs 'IF' ; 0x85 + fcs 'DATA' ; 0x86 + fcs 'PRINT' ; 0x87 + fcs 'ON' ; 0x88 + fcs 'INPUT' ; 0x89 + fcs 'END' ; 0x8a + fcs 'NEXT' ; 0x8b + fcs 'DIM' ; 0x8c + fcs 'READ' ; 0x8d + fcs 'RUN' ; 0x8e + fcs 'RESTORE' ; 0x8f + fcs 'RETURN' ; 0x90 + fcs 'STOP' ; 0x91 + fcs 'POKE' ; 0x92 + fcs 'CONT' ; 0x93 + fcs 'LIST' ; 0x94 + fcs 'CLEAR' ; 0x95 + fcs 'NEW' ; 0x96 + fcs 'CLOAD' ; 0x97 + fcs 'CSAVE' ; 0x98 + fcs 'OPEN' ; 0x99 + fcs 'CLOSE' ; 0x9a + fcs 'LLIST' ; 0x9b + fcs 'SET' ; 0x9c + fcs 'RESET' ; 0x9d + fcs 'CLS' ; 0x9e + fcs 'MOTOR' ; 0x9f + fcs 'SOUND' ; 0xa0 + fcs 'AUDIO' ; 0xa1 + fcs 'EXEC' ; 0xa2 + fcs 'SKIPF' ; 0xa3 + fcs 'TAB(' ; 0xa4 + fcs 'TO' ; 0xa5 + fcs 'SUB' ; 0xa6 + fcs 'THEN' ; 0xa7 + fcs 'NOT' ; 0xa8 + fcs 'STEP' ; 0xa9 + fcs 'OFF' ; 0xaa + fcs '+' ; 0xab + fcs '-' ; 0xac + fcs '*' ; 0xad + fcs '/' ; 0xae + fcs '^' ; 0xaf + fcs 'AND' ; 0xb0 + fcs 'OR' ; 0xb1 + fcs '>' ; 0xb2 + fcs '=' ; 0xb3 + fcs '<' ; 0xb4 +; Reserved word list for functions +LAB1A fcs 'SGN' ; 0x80 + fcs 'INT' ; 0x81 + fcs 'ABS' ; 0x82 + fcs 'USR' ; 0x83 + fcs 'RND' ; 0x84 + fcs 'SIN' ; 0x85 + fcs 'PEEK' ; 0x86 + fcs 'LEN' ; 0x87 + fcs 'STR$' ; 0x88 + fcs 'VAL' ; 0x89 + fcs 'ASC' ; 0x8a + fcs 'CHR$' ; 0x8b + fcs 'EOF' ; 0x8c + fcs 'JOYSTK' ; 0x8d + fcs 'LEFT$' ; 0x8e + fcs 'RIGHT$' ; 0x8f + fcs 'MID$' ; 0x90 + fcs 'POINT' ; 0x91 + fcs 'INKEY$' ; 0x92 + fcs 'MEM' ; 0x93 +; Jump table for commands +LAB67 fdb FOR ; 0x80 FOR + fdb GO ; 0x81 GO + fdb REM ; 0x82 REM + fdb REM ; 0x83 ' + fdb REM ; 0x84 ELSE + fdb IFTOK ; 0x85 IF + fdb DATA ; 0x86 DATA + fdb PRINT ; 0x87 PRINT + fdb ON ; 0x88 ON + fdb INPUT ; 0x89 INPUT + fdb ENDTOK ; 0x8a END + fdb NEXT ; 0x8b NEXT + fdb DIM ; 0x8c DIM + fdb READ ; 0x8d READ + fdb RUN ; 0x8e RUN + fdb RESTOR ; 0x8f RESTORE + fdb RETURN ; 0x90 RETURN + fdb STOP ; 0x91 STOP + fdb POKE ; 0x92 POKE + fdb CONT ; 0x93 CONT + fdb LIST ; 0x94 LIST + fdb CLEAR ; 0x95 CLEAR + fdb NEW ; 0x96 NEW + fdb CLOAD ; 0x97 CLOAD + fdb CSAVE ; 0x98 CSAVE + fdb OPEN ; 0x99 OPEN + fdb CLOSE ; 0x9a CLOSE + fdb LLIST ; 0x9b LLIST + fdb SET ; 0x9c SET + fdb RESET ; 0x9d RESET + fdb CLS ; 0x9e CLS + fdb MOTOR ; 0x9f MOTOR + fdb SOUND ; 0xa0 SOUND + fdb AUDIO ; 0xa1 AUDIO + fdb EXEC ; 0xa2 EXEC + fdb SKIPF ; 0xa3 SKIPF +; Error message table +LABAF fcc 'NF' ; 0 NEXT without FOR + fcc 'SN' ; 1 Syntax error + fcc 'RG' ; 2 RETURN without GOSUB + fcc 'OD' ; 3 Out of data + fcc 'FC' ; 4 Illegal function call + fcc 'OV' ; 5 Overflow + fcc 'OM' ; 6 Out of memory + fcc 'UL' ; 7 Undefined line number + fcc 'BS' ; 8 Bad subscript + fcc 'DD' ; 9 Redimensioned array + fcc '/0' ; 10 Division by 0 + fcc 'ID' ; 11 Illegal direct statement + fcc 'TM' ; 12 Type mismatch + fcc 'OS' ; 13 Out of string space + fcc 'LS' ; 14 String too long + fcc 'ST' ; 15 String formula too complex + fcc 'CN' ; 16 Can't continue + fcc 'FD' ; 17 Bad file data + fcc 'AO' ; 18 File already open + fcc 'DN' ; 19 Device number error + fcc 'IO' ; 20 Input/output error + fcc 'FM' ; 21 Bad file mode + fcc 'NO' ; 22 File not open + fcc 'IE' ; 23 Input past end of file + fcc 'DS' ; 24 Direct statement in file +LABE1 fcn ' ERROR' +LABE8 fcn ' IN ' +LABED fcb 0x0d +LABEE fcc 'OK' + fcb 0x0d,0x00 +LABF2 fcb 0x0d + fcn 'BREAK' +; Search stack for a FOR/NEXT stack frame. Stop search when something that isn't a FOR/NEXT +; stack frame is found. Enter with the index variable descriptor pointer in VARDES, or NULL +; for the first match. +; +; NOTE: this routine can be reworked to avoid needed two temporaries by taking advantage of the +; 6809's registers. This requires some minor tweaks where the routine is called. Further, the +; use of B is completely pointless and, even if B is going to be used, why is it reloaded on +; every loop? +LABF9 leax 4,s ; skip past our caller and the main command loop return address +LABFB ldb #18 ; each FOR/NEXT frame is 18 bytes + stx TEMPTR ; save current search pointer + lda ,x ; get first byte of this frame + suba #0x80 ; set to 0 if FOR/NEXT + bne LAC1A ; brif not FOR/NEXT (we hit the end of the stack for a GOSUB frame) + ldx 1,x ; get index variable descriptor + stx TMPTR1 ; save it + ldx VARDES ; get desired index descriptor + beq LAC16 ; brif NULL - we found something + cmpx TMPTR1 ; does this one match? + beq LAC1A ; brif so + ldx TEMPTR ; get back frame pointer + abx ; move to next entry + bra LABFB ; check next block of data +LAC16 ldx TMPTR1 ; get index variable of this frame + stx VARDES ; set it as the one found +LAC1A ldx TEMPTR ; get matching frame pointer + tsta ; set Z if FOR/NEXT + rts +; This is a block copy routine which copies from top to bottom. It's not clear that the use of +; this routine actually saves any ROM space compared to just implementing the copies directly +; once all the marshalling to set up the parameter variables is taken into account. +LAC1E bsr LAC37 ; check to see if stack collides with D +LAC20 ldu V41 ; point to destination + leau 1,u ; offset for pre-dec + ldx V43 ; point to source + leax 1,x ; offset for pre-dec +LAC28 lda ,-x ; get source byte + pshu a ; store at destination (sta ,-u would be less weird) + cmpx V47 ; at the bottom of the copy? + bne LAC28 ; brif not + stu V45 ; save final destination address +LAC32 rts +; Check for 2*B (0 <= B <= 127) bytes for free memory +LAC33 clra ; zero extend + aslb ; times 2 (loses bit 7 of B) + addd ARYEND ; add to top of used memory +LAC37 addd #STKBUF ; add a fudge factor for interpreter operation + bcs LAC44 ; brif >65535! + sts BOTSTK ; get current stack pointer + cmpd BOTSTK ; is our new address above that? + blo LAC32 ; brif not - no error +LAC44 ldb #6*2 ; raise OM error +; The error servicing routine +LAC46 jsr RVEC16 ; do the RAM hook dance (ON ERROR reserved hook) +LAC49 jsr RVEC17 ; do the RAM hook dance again + jsr LA7E9 ; turn off tape + jsr LA974 ; disable sound + jsr LAD33 ; reset stack, etc. + clr DEVNUM ; reset output to screen + jsr LB95C ; do a newline + jsr LB9AF ; send a ? + ldx #LABAF ; point to error table + abx ; offset to correct message + bsr LACA0 ; send a char from X + bsr LACA0 ; send another char from X + ldx #LABE1-1 ; point to "ERROR" message +LAC68 jsr LB99C ; print ERROR message (or BREAK) + lda CURLIN ; are we in immediate mode? + inca + beq LAC73 ; brif not - go to immediate mode + jsr LBDC5 ; print "IN ****" +; This is the immediate mode loop +LAC73 jsr LB95C ; do a newline if needed +LAC76 ldx #LABEE-1 ; point to prompt (without leading CR) + jsr LB99C ; show prompt +LAC7C jsr LA390 ; read an input line + ldu #0xffff ; flag immediate mode + stu CURLIN + bcs LAC7C ; brif we ended on BREAK - just go for another line + tst CINBFL ; EOF? + lbne LA4BF ; brif so + stx CHARAD ; save start of input line as input pointer + jsr GETNCH ; get character from input line + beq LAC7C ; brif no input + bcs LACA5 ; brif numeric - adding or removing a line number + ldb #2*24 ; code for "direct statement in file" + tst DEVNUM ; keyboard input? + bne LAC46 ; brif not - complain about direct statement + jsr LB821 ; go tokenize the input line + jmp LADC0 ; go execute the newly tokenized line +LACA0 lda ,x+ ; get character and advance pointer + jmp LB9B1 ; output it +LACA5 jsr LAF67 ; convert line number to binary + ldx BINVAL ; get converted number + stx LINHDR ; put it before the line we just read + jsr LB821 ; tokenize the input line + stb TMPLOC ; save line length + bsr LAD01 ; find where the line should be in the program + bcs LACC8 ; brif the line number isn't already present + ldd V47 ; get address where the line is in the program + subd ,x ; get the difference between here and the end of the line (negative) + addd VARTAB ; subtract line length from the end of the program + std VARTAB ; save new end of program address + ldu ,x ; get start of next line +LACC0 pulu a ; get source byte (lda ,u+ would be less weird) + sta ,x+ ; move it down + cmpx VARTAB ; have we moved everything yet? + bne LACC0 ; brif not +LACC8 lda LINBUF ; see if there is actually a line to input + beq LACE9 ; brif not - we just needed to remove the line + ldd VARTAB ; get current end of program + std V43 ; set as source pointer + addb TMPLOC ; add in the length of the new line + adca #0 + std V41 ; save destination pointer + jsr LAC1E ; make sure there's enough room and then make a hole for the new line + ldu #LINHDR-2 ; point to the line (well, 4 bytes before it, incl line number and fake next line pointer) +LACDD pulu a ; get byte from new line (lda ,u+ would be less weird) + sta ,x+ ; stow it + cmpx V45 ; at the end of the hole we just made? + bne LACDD ; brif not + ldx V41 ; get save new top of program address + stx VARTAB +LACE9 bsr LAD21 ; reset variables, etc. + bsr LACEF ; adjust next line pointers + bra LAC7C ; go read another input line +; Recompute next line pointers +LACEF ldx TXTTAB ; point to start of program +LACF1 ldd ,x ; get address of next line + beq LAD16 ; brif end of program + leau 4,x ; move past pointer and line number +LACF7 lda ,u+ ; are we at the end of the line? + bne LACF7 ; brif not + stu ,x ; save new next line pointer + ldx ,x ; point to next line + bra LACF1 ; process the next line +; Find a line in the program +LAD01 ldd BINVAL ; get desired line number + ldx TXTTAB ; point to start of program +LAD05 ldu ,x ; get address of next line + beq LAD12 ; brif end of program + cmpd 2,x ; do we have a match? + bls LAD14 ; brif our search number is <= the number here + ldx ,x ; move to next line + bra LAD05 ; check another line +LAD12 orcc #1 ; set C for not found +LAD14 stx V47 ; save address of matching line *or* line just after where it would have been +LAD16 rts +; NEW command +; This routine has multiple entry points used for various "levels" of NEW +NEW bne LAD14 ; brif there was input given; should be LAD16! +LAD19 ldx TXTTAB ; point to start of program + clr ,x+ ; blank out program (with NULL next line pointer) + clr ,x+ + stx VARTAB ; save end of program +LAD21 ldx TXTTAB ; get start of program + jsr LAEBB ; put input pointer there +LAD26 ldx MEMSIZ ; reset string space + stx STRTAB + jsr RESTOR ; reset DATA pointer + ldx VARTAB ; clear out scalars and arrays + stx ARYTAB + stx ARYEND +LAD33 ldx #STRSTK ; reset the string stack + stx TEMPPT + ldx ,s ; get return address (we're going to reset the stack) + lds FRETOP ; reset the stack to top of memory + clr ,-s ; put stopper so FOR/NEXT search will actually stop here + clr OLDPTR ; reset "CONT" state + clr OLDPTR+1 + clr ARYDIS ; un-disable arrays + jmp ,x ; return to original caller +; FOR command +FOR lda #0x80 ; disable array parsing + sta ARYDIS + jsr LET ; assign start value to index + jsr LABF9 ; search stack for matching FOR/NEXT frame + leas 2,s ; lose return address + bne LAD59 ; brif variable not already being used + ldx TEMPTR ; get address of matched data + leas b,x ; move stack pointer to the end of it (B is set to 18 in the stack search) +LAD59 ldb #9 ; is there room for 18 bytes in memory? + jsr LAC33 + jsr LAEE8 ; get address of the end of this statement in X + ldd CURLIN ; get line number + pshs x,b,a ; save next line address and current line number + ldb #0xa5 ; make sure we have TO + jsr LB26F + jsr LB143 ; make sure we have a numeric index + jsr LB141 ; evaluate terminal condition value + ldb FP0SGN ; pack FPA0 in place + orb #0x7f + andb FPA0 + stb FPA0 + ldy #LAD7F ; where to come back to + jmp LB1EA ; stash terminal condition on the stack +LAD7F ldx #LBAC5 ; point to FP 1.0 (default step) + jsr LBC14 ; unpack it to FPA0 + jsr GETCCH ; get character after the terminal + cmpa #0xa9 ; is it STEP? + bne LAD90 ; brif not + jsr GETNCH ; eat STEP + jsr LB141 ; evaluate step condition +LAD90 jsr LBC6D ; get "status" of FPA0 + jsr LB1E6 ; stash FPA0 on the stack (for step value) + ldd VARDES ; get variable descriptor pointer + pshs d ; put that on the stack too + lda #0x80 ; flag the frame as a FOR/NEXT frame + pshs a +; Main command interpretation loop +LAD9E jsr RVEC20 ; do the RAM hook dance + andcc #0xaf ; make sure interrupts are running + bsr LADEB ; check for BREAK/pause + ldx CHARAD ; get input pointer + stx TINPTR ; save input pointer for start of line + lda ,x+ ; get current input character + beq LADB4 ; brif end of line - move to another line + cmpa #': ; end of statement? + beq LADC0 ; brif so - keep processing +LADB1 jmp LB277 ; raise a syntax error +LADB4 lda ,x++ ; get MSB of next line pointer and skip past pointer + sta ENDFLG + beq LAE15 ; brif MSB of next line address is 0 (do END) + ldd ,x+ ; get line number but only advance one + std CURLIN ; set current line number + stx CHARAD ; set input pointer to one before line text +LADC0 jsr GETNCH ; move past statement separator or to first character in line + bsr LADC6 ; process a command + bra LAD9E ; handle next statement or line +LADC6 beq LAE40 ; return if end of statement + tsta ; is it a token? + lbpl LET ; brif not - do a LET + cmpa #0xa3 ; above SKIPF? + bhi LADDC ; brif so + ldx COMVEC+3 ; point to jump table + lsla ; two bytes per entry (loses the token bias) + tfr a,b ; put it in B for unsigned ABX + abx + jsr GETNCH ; move past token + jmp [,x] ; transfer control to the handler (which will return to the main loop) +LADDC cmpa #0xb4 ; is it a non-executable token? + bls LADB1 ; brif so + jmp [COMVEC+13] ; transfer control to ECB command handler +; RESTORE command +RESTOR ldx TXTTAB ; point to beginning of the program + leax -1,x ; move back one (to compensate for "GETNCH") +LADE8 stx DATPTR ; save as new data pointer + rts +; BREAK check +LADEB jsr KEYIN ; read keyboard + beq LADFA ; brif no key down +LADF0 cmpa #3 ; BREAK? + beq STOP ; brif so - do a STOP + cmpa #0x13 ; pause (SHIFT-@)? + beq LADFB ; brif so + sta IKEYIM ; cache key for later INKEY$ so break check doesn't break INKEY$ +LADFA rts +LADFB jsr KEYIN ; read keyboard + beq LADFB ; brif no key down + bra LADF0 ; process pressed key in case BREAK or SHIFT-@ again +; END command +ENDTOK jsr LA426 ; close files + jsr GETCCH ; re-get input character + bra LAE0B +; STOP command +STOP orcc #1 ; flag "STOP" +LAE0B bne LAE40 ; brif not end of statement + ldx CHARAD ; save current input pointer + stx TINPTR +LAE11 ror ENDFLG ; save END/STOP flag (C) + leas 2,s ; lose return address +LAE15 ldx CURLIN ; get current input line (end of program comes here) + cmpx #0xffff ; immediate mode? + beq LAE22 ; brif so + stx OLDTXT ; save line where we stopped executing + ldx TINPTR ; get input pointer + stx OLDPTR ; save location where we stopped executing +LAE22 clr DEVNUM ; reset to screen/keyboard + ldx #LABF2-1 ; point to BREAK message + tst ENDFLG ; are we doing "BREAK"? + lbpl LAC73 ; brif not + jmp LAC68 ; go do the BREAK message and return to main loop +; CONT command +CONT bne LAE40 ; brif not end of statement + ldb #2*16 ; code for can't continue + ldx OLDPTR ; get saved execution pointer + lbeq LAC46 ; brif no saved pointer - raise CN error + stx CHARAD ; reset input pointer + ldx OLDTXT ; reset current line number + stx CURLIN +LAE40 rts +; CLEAR command +CLEAR beq LAE6F ; brif no argument + jsr LB3E6 ; evaluate string space size + pshs d ; save it + ldx MEMSIZ ; get memory size (top of memory) + jsr GETCCH ; is there anything after the string space size? + beq LAE5A ; brif not + jsr LB26D ; force a comma + jsr LB73D ; get top of memory value in X + leax -1,x ; move back one (top of cleared space) + cmpx TOPRAM ; is it within the memory available? + bhi LAE72 ; brif higher than top of memory - OM error +LAE5A tfr x,d ; so we can do math for checking memory usage + subd ,s++ ; subtract out string space value + bcs LAE72 ; brif less than 0 + tfr d,u ; U is bottom of cleared space + subd #STKBUF ; also account for slop space + bcs LAE72 ; brif less than 0 + subd VARTAB ; is there still room for the program? + blo LAE72 ; brif not + stu FRETOP ; set top of free memory + stx MEMSIZ ; set size of usable memory +LAE6F jmp LAD26 ; erase variables, etc. +LAE72 jmp LAC44 ; raise OM error +; RUN command +RUN jsr RVEC18 ; do the RAM hook dance + jsr LA426 ; close any open files + jsr GETCCH ; is there a line number + lbeq LAD21 ; brif no line number - start from beginning + jsr LAD26 ; clear variables, etc. + bra LAE9F ; "GOTO" the line number +; GO command (GOTO and GOSUB) +GO tfr a,b ; save TO/SUB +LAE88 jsr GETNCH ; eat the TO/SUB token + cmpb #0xa5 ; TO? + beq LAEA4 ; brif GOTO + cmpb #0xa6 ; SUB? + bne LAED7 ; brif not + ldb #3 ; room for 6 bytes? + jsr LAC33 + ldu CHARAD ; get input pointer + ldx CURLIN ; get line number + lda #0xa6 ; flag for GOSUB frame + pshs u,x,a ; set stack frame +LAE9F bsr LAEA4 ; do "GOTO" + jmp LAD9E ; go back to main loop +; Actual GOTO is here +LAEA4 jsr GETCCH ; get current input + jsr LAF67 ; convert number to binary + bsr LAEEB ; move input pointer to end of statement + leax 1,x ; point to start of next line + ldd BINVAL ; get desired line number + cmpd CURLIN ; is it beyond here? + bhi LAEB6 ; brif so + ldx TXTTAB ; start search at beginning of program +LAEB6 jsr LAD05 ; find line number + bcs LAED2 ; brif not found +LAEBB leax -1,x ; move to just before start of line + stx CHARAD ; reset input pointer +LAEBF rts +; RETURN command +RETURN bne LAEBF ; exit if argument given + lda #0xff ; set VARDES to an illegal value so we ignore FOR frames + sta VARDES + jsr LABF9 ; look for a GOSUB frame + tfr x,s ; reset stack + cmpa #0xa6-0x80 ; is it a GOSUB frame? + beq LAEDA ; brif so + ldb #2*2 ; code for RETURN without GOSUB + skip2 +LAED2 ldb #7*2 ; code for undefined line number + jmp LAC46 ; raise error +LAED7 jmp LB277 ; raise syntax error +LAEDA puls a,x,u ; get back saved line number and input pointer + stx CURLIN ; reset line number + stu CHARAD ; reset input pointer +; DATA command +DATA bsr LAEE8 ; move input pointer to end of statement + skip2 +; REM command (also ELSE) +REM bsr LAEEB ; move input pointer to end of line + stx CHARAD ; save new input pointer +LAEE7 rts +; Return end of statement (LAEE8) or line (AEEB) in X +LAEE8 ldb #': ; colon is statement terminator + skip1lda +LAEEB clrb ; make main terminator NUL + stb CHARAC ; save terminator + clrb ; end of line - always terminates + ldx CHARAD ; get input pointer +LAEF1 tfr b,a ; save secondary terminator + ldb CHARAC ; get main terminator + sta CHARAC ; save secondary +LAEF7 lda ,x ; get input character + beq LAEE7 ; brif end of line + pshs b ; save terminator + cmpa ,s+ ; does it match? + beq LAEE7 ; brif so - bail + leax 1,x ; move pointer ahead + cmpa #'" ; start of string? + beq LAEF1 ; brif so + inca ; functon token? + bne LAF0C ; brif not + leax 1,x ; skip second part of function token +LAF0C cmpa #0x85+1 ; IF? + bne LAEF7 ; brif not + inc IFCTR ; bump "IF" count + bra LAEF7 ; get check another input character +; IF command +IFTOK jsr LB141 ; evaluate condition + jsr GETCCH ; find out what's after the conditin + cmpa #0x81 ; GO? + beq LAF22 ; treat same as THEN + ldb #0xa7 ; make sure we have a THEN + jsr LB26F +LAF22 lda FP0EXP ; get true/false (false is 0) + bne LAF39 ; brif condition true + clr IFCTR ; reset IF counter +LAF28 bsr DATA ; skip over statement + tsta ; end of line? + beq LAEE7 ; brif so + jsr GETNCH ; get start of this statement + cmpa #0x84 ; ELSE? + bne LAF28 ; brif not + dec IFCTR ; is it a matching ELSE? + bpl LAF28 ; brif not - keep looking + jsr GETNCH ; eat the ELSE +LAF39 jsr GETCCH ; get current input + lbcs LAEA4 ; brif numeric - to a GOTO + jmp LADC6 ; let main loop interpret the next command +; ON command +ON jsr LB70B ; evaluate index expression +LAF45 ldb #0x81 ; make sure we have "GO" + jsr LB26F + pshs a ; save TO/SUB + cmpa #0xa6 ; SUB? + beq LAF54 ; brif so + cmpa #0xa5 ; TO? +LAF52 bne LAED7 ; brif not +LAF54 dec FPA0+3 ; are we at the right index? + bne LAF5D ; brif not + puls b ; get TO/SUB token + jmp LAE88 ; go do GOTO or GOSUB +LAF5D jsr GETNCH ; munch a character + bsr LAF67 ; parse line number + cmpa #', ; is there another line following? + beq LAF54 ; brif so - see if we're there yet + puls b,pc ; clean up TO/SUB token and return - we fell through +; Parse a line number +LAF67 ldx ZERO ; initialize line number accumulator to 0 + stx BINVAL +LAF6B bcc LAFCE ; brif not numeric + suba #'0 ; adjust to actual value of digit + sta CHARAC ; save digit + ldd BINVAL ; get accumulated number + cmpa #24 ; will this overflow? + bhi LAF52 ; brif so - raise syntax error + aslb ; times 2 + rola + aslb ; times 4 + rola + addd BINVAL ; times 5 + aslb ; times 10 + rola + addb CHARAC ; add in digit + adca #0 + std BINVAL ; save new accumulated number + jsr GETNCH ; fetch next character + bra LAF6B ; process next digit +; LET command (the LET keyword requires Extended Basic) +LET jsr LB357 ; evaluate destination variable + stx VARDES ; save descriptor pointer + ldb #0xb3 ; make sure we have = + jsr LB26F + lda VALTYP ; get destination variable type + pshs a ; save it for later + jsr LB156 ; evaluate the expression to assign + puls a ; get back original variable type + rora ; put type in C + jsr LB148 ; make sure the current result matches the type + lbeq LBC33 ; bri fnumeric - copy FPA0 to variable +LAFA4 ldx FPA0+2 ; point to descriptor of replacement string + ldd FRETOP ; get bottom of string space + cmpd 2,x ; is the string already in string space? + bhs LAFBE ; brif so + cmpx VARTAB ; is the descriptor in variable space? + blo LAFBE ; brif not +LAFB1 ldb ,x ; get length of string + jsr LB50D ; allocate space for this string + ldx V4D ; get descriptor pointer back + jsr LB643 ; copy string into string space + ldx #STRDES ; point to temporary string descriptor +LAFBE stx V4D ; save descriptor pointer + jsr LB675 ; remove string from string stack if appropriate + ldu V4D ; get back replacement descriptor + ldx VARDES ; get target descriptor + pulu a,b,y ; get string length (A) and data pointer (Y) + sta ,x ; save new length + sty 2,x ; save new pointer +LAFCE rts +; READ and INPUT commands. +LAFCF fcc '?REDO' ; The ?REDO message + fcb 0x0d,0x00 +LAFD6 ldb #2*17 ; bad file data code + tst DEVNUM ; are we reading from the keyboard? + beq LAFDF ; brif so +LAFDC jmp LAC46 ; raise the error +LAFDF lda INPFLG ; are we doing INPUT? + beq LAFEA ; brif so + ldx DATTXT ; get line number where the DATA statement happened + stx CURLIN ; set current line number to that so can report the correct location + jmp LB277 ; raise a syntax error on bad data +LAFEA ldx #LAFCF-1 ; show the ?REDO if we're doing INPUT + jsr LB99C + ldx TINPTR ;* reset input pointer to start of statement (this will cause the + stx CHARAD ;* INPUT statement to be re-executed + rts +INPUT ldb #11*2 ; code for illegal direct statement + ldx CURLIN ; are we in immediate mode? + leax 1,x + beq LAFDC ; brif so - raise ID error + bsr LB002 ; go do the INPUT thing + clr DEVNUM ; reset device to screen/keyboard + rts +LB002 cmpa #'# ; is there a device number? + bne LB00F ; brif not + jsr LA5A5 ; parse device number + jsr LA3ED ; make sure it's valid for input + jsr LB26D ; make sure we have a comma after the device number +LB00F cmpa #'" ; is there a prompt string? + bne LB01E ; brif not + jsr LB244 ; parse the prompt string + ldb #'; ; make sure we have a semicolon after the prompt + jsr LB26F + jsr LB99F ; print the prompt +LB01E ldx #LINBUF ; point to line input buffer + clr ,x ; NUL first byte to indicate no data + tst DEVNUM ; is it keyboard input? + bne LB049 ; brif not + bsr LB02F ; read a line from the keyboard + ldb #', ; put a comma at the start of the buffer + stb ,x + bra LB049 ; go process some input +LB02F jsr LB9AF ; send a ? + jsr LB9AC ; send a space +LB035 jsr LA390 ; read input from the keyboard + bcc LB03F ; brif not BREAK + leas 4,s ; clean up stack +LB03C jmp LAE11 ; go process BREAK +LB03F ldb #2*23 ; input past end of file error code + tst CINBFL ; was it EOF? + bne LAFDC ; brif so - raise the error + rts +READ ldx DATPTR ; fetch current DATA pointer + skip1lda ; set A to nonzero (for READ) +LB049 clra ; set A to zero (for INPUT) + sta INPFLG ; record whether we're doing READ or INPUT + stx DATTMP ; save current input location +LB04E jsr LB357 ; evaluate a variable (destination of data) + stx VARDES ; save descriptor + ldx CHARAD ; save interpreter input pointer + stx BINVAL + ldx DATTMP ; get data pointer + lda ,x ; is there anything to read? + bne LB069 ; brif so + lda INPFLG ; is it INPUT? + bne LB0B9 ; brif not + jsr RVEC10 ; do the RAM hook dance + jsr LB9AF ; send a ? (so subsequent lines get ??) + bsr LB02F ; go read an input line +LB069 stx CHARAD ; save data pointer + jsr GETNCH ; fetch next data character + ldb VALTYP ; do we want a number? + beq LB098 ; brif so + ldx CHARAD ; get input pointer + sta CHARAC ; save initial character as the delimiter + cmpa #'" ; do we have a string delimiter? + beq LB08B ; brif so - use " as both delimiters + leax -1,x ; back up input if we don't have a delimiter + clra ; set delimiter to NUL (end of line) + sta CHARAC + jsr LA35F ; set up print parameters + tst PRTDEV ; is it a file type device? + bne LB08B ; brif so - use two NULs + lda #': ; use colon as one delimiter + sta CHARAC + lda #', ; and use comma as the other +LB08B sta ENDCHR ; save second terminator + jsr LB51E ; parse out the string + jsr LB249 ; move input pointer past the string + jsr LAFA4 ; assign the string to the variable + bra LB09E ; go see if there's more to read +LB098 jsr LBD12 ; parse a numeric string + jsr LBC33 ; assign the numbe to the variable +LB09E jsr GETCCH ; get current input character + beq LB0A8 ; brif end of line + cmpa #', ; check for comma + lbne LAFD6 ; brif not - we have bad data +LB0A8 ldx CHARAD ; get current data pointer + stx DATTMP ; save the data pointer + ldx BINVAL ; restore the interpreter input pointer + stx CHARAD + jsr GETCCH ; get current input from program + beq LB0D5 ; brif end of statement + jsr LB26D ; make sure there's a comma between variables + bra LB04E ; go read another item +LB0B9 stx CHARAD ; reset input pointer + jsr LAEE8 ; search for end of statement + leax 1,x ; move past end of statement + tsta ; was it end of line? + bne LB0CD ; brif not + ldb #2*3 ; code for out of data + ldu ,x++ ; get pointer to next line + beq LB10A ; brif end of program - raise OD error + ldd ,x++ ; get line number + std DATTXT ; record it for raising errors in DATA statements +LB0CD lda ,x ; do we have a DATA statement? + cmpa #0x86 + bne LB0B9 ; brif not - keep scanning + bra LB069 ; go process the input +LB0D5 ldx DATTMP ; get data pointer + ldb INPFLG ; were we doing READ? + lbne LADE8 ; brif so - save DATA pointer + lda ,x ; is there something after the input in the input buffer? + beq LB0E7 ; brif not - we consumed everything + ldx #LB0E8-1 ; print the ?EXTRA IGNORED message + jmp LB99C +LB0E7 rts +LB0E8 fcc '?EXTRA IGNORED' + fcb 0x0d,0x00 +; NEXT command +NEXT bne LB0FE ; brif argument given + ldx ZERO ; set to NULL descriptor pointer + bra LB101 ; go process "any index will do" +LB0FE jsr LB357 ; evaluate the variable +LB101 stx VARDES ; save the index we're looking for + jsr LABF9 ; search the stack for the matching frame + beq LB10C ; brif we found a matching frame + ldb #0 ; code for NEXT without FOR +LB10A bra LB153 ; raise the error +LB10C tfr x,s ; reset the stack to the start of the stack frame + leax 3,x ; point to the STEP value + jsr LBC14 ; copy the value to FPA0 + lda 8,s ; get step direction + sta FP0SGN ; save as sign of FPA0 + ldx VARDES ; point to index variable + jsr LB9C2 ; add (X) to FPA0 (steps the index) + jsr LBC33 ; save new value to the index + leax 9,s ; point to terminal condition + jsr LBC96 ; compare the new index value with the terminal + subb 8,s ; set B=0 if we hit the terminal (or passed it with nonzero step) + beq LB134 ; brif loop complete + ldx 14,s ; restore line number and input pointer to start of loop + stx CURLIN + ldx 16,s + stx CHARAD +LB131 jmp LAD9E ; return to interpretation loop +LB134 leas 18,s ; remove the frame from the stack + jsr GETCCH ; get character after the index + cmpa #', ; do we have more indexes? + bne LB131 ; brif not + jsr GETNCH ; munch the comma + bsr LB0FE ; go process another value +; NOTE: despite the BSR on the preceding line, execution of the NEXT command will not fall +; through this point, nor will the stack grow without bound. The BSR is required to make sure +; the stack is aligned properly for the stack search for the subsequent index variable. +; +; The following is the expression evaluation system. It has various entry points including for type +; checking. This really consists of two co-routines, one for evaluating operators and one for individual +; terms. However, it does some rather confusing stack operations so it's a bit difficult to follow +; just how some of this works. +; +; Evaluate numeric expression +LB141 bsr LB156 ; evaluate an expression +; TM error if string +LB143 andcc #0xfe ; clear C to indicate we want a number + skip2keepc +; TM error if numeric +LB146 orcc #1 ; set C to indicate we want a string +; TM error if: C = 1 and number, OR C = 0 and string +LB148 tst VALTYP ; set flags on the current value to (doesn't change C) + bcs LB14F ; brif we want a string + bpl LB0E7 ; brif we have a number (we want a number) + skip2 +LB14F bmi LB0E7 ; brif we have a string (we want a string) +LB151 ldb #12*2 ; code for TM error +LB153 jmp LAC46 ; raise the error +; The general expression evaluation entry point +LB156 bsr LB1C6 ; back up input pointer to compensate for GETNCH below +LB158 clra ; set operator precedence to 0 (no previous operator) + skip2 +LB15A pshs b ; save relational operator flags + pshs a ; save previous operator precedence + ldb #1 ; make sure we aren't overflowing the stack + jsr LAC33 + jsr LB223 ; go evaluate the first term +LB166 clr TRELFL ; flag no relational operators seen +LB168 jsr GETCCH ; get input character +LB16A suba #0xb2 ; token for > (lowest relational operator) + blo LB181 ; brif below relational operators + cmpa #3 ; there are three relational operators, is it one? + bhs LB181 ; brif not + cmpa #1 ; set C if > + rola ; shift C into bit 0 (4: <, 2: =, 1: >) + eora TRELFL ; flip the bit for this operator + cmpa TRELFL ; did the result get lower? + blo LB1DF ; brif so - we have a duplicate so raise an error + sta TRELFL ; save new operator flags + jsr GETNCH ; munch the operator + bra LB16A ; go see if we have another one +LB181 ldb TRELFL ; do we have a relational comparison? + bne LB1B8 ; brif so + lbcc LB1F4 ; brif the token is above the relational operators + adda #7 ; put operators starting at 0 + bhs LB1F4 ; brif we're above 0 - it's an operator, Jim + adca VALTYP ; add carry, numeric flag, and modified token number + lbeq LB60F ; brif we have string and A is + - do concatenation + adca #-1 ; restore operator number + pshs a ; save operator number + asla ; times 2 + adda ,s+ ; and times 3 (3 bytes per entry) + ldx #LAA51 ; point to operator pecedence and jump table + leax a,x ; point to correct entry +LB19F puls a ; get precedence of previous operation + cmpa ,x ; is hit higher (or same) than the current one? + bhs LB1FA ; brif so - we need to process that operator + bsr LB143 ; TM error if we have a string +LB1A7 pshs a ; save previous operation precedence + bsr LB1D4 ; push operator handler address and FPA0 onto the stack + ldx RELPTR ; get pointer to arithmetic/logical table entry for last operation + puls a ; get back precedence + bne LB1CE ; brif we had a relational operation + tsta ; check precedence of previous operation + lbeq LB220 ; brif end of expression + bra LB203 ; go handle operation +LB1B8 asl VALTYP ; get type of value to C + rolb ; mix it in to bit 0 of relational flags + bsr LB1C6 ; back up input pointer + ldx #LB1CB ; point to relational operator precedence and handler + stb TRELFL ; save relational comparison flags + clr VALTYP ; result will be numeric + bra LB19F ; to process the operation +LB1C6 ldx CHARAD ; get input pointer + jmp LAEBB ; back it up one and put it back +LB1CB fcb 0x64 ; precedence of relational comparison + fdb LB2F4 ; handler address for relational comparison +LB1CE cmpa ,x ; is last done operation higher (or same) precedence? + bhs LB203 ; brif so - go process it + bra LB1A7 ; go push things on the stack and process this operation otherwise +LB1D4 ldd 1,x ; get address of operatorroutine + pshs d ; save it + bsr LB1E2 ; push FPA0 onto the stack + ldb TRELFL ; get back relational operator flags + lbra LB15A ; go evaluate another operation +LB1DF jmp LB277 ; raise a syntax error +LB1E2 ldb FP0SGN ; get sign of FPA0 + lda ,x ; get precedence of this operation +LB1E6 puls y ; get back original caller + pshs b ; save sign +LB1EA ldb FP0EXP ; get exponent + ldx FPA0 ; get mantissa + ldu FPA0+2 + pshs u,x,b ; stow FPA0 sign and mantissa + jmp ,y ; return to caller +LB1F4 ldx ZERO ; point to dummy value + lda ,s+ ; get precedence of previous operation (and set flags) + beq LB220 ; brif end of expression +LB1FA cmpa #0x64 ; relational operation? + beq LB201 ; brif so + jsr LB143 ; type mismatch if string +LB201 stx RELPTR ; save pointer to operator routine +LB203 puls b ; get relational flags + cmpa #0x5a ; NOT operation? + beq LB222 ; brif so (it was unary) + cmpa #0x7d ; unary negation? + beq LB222 ; brif so + lsrb ; shift value type flag out of relational flags + stb RELFLG ; save relational operator flag + puls a,x,u ; get FP value back + sta FP1EXP ; set exponent and mantissa in FPA1 + stx FPA1 + stu FPA1+2 + puls b ; and the sign + stb FP1SGN + eorb FP0SGN ; set RESSGN if the two operand signs differ + stb RESSGN +LB220 ldb FP0EXP ; get exponent of FPA0 +LB222 rts ; return or transfer control to operator handler routine +LB223 jsr RVEC15 ; do the RAM hook dance + clr VALTYP ; set type to numeric +LB228 jsr GETNCH ; get first character in the term + bcc LB22F ; brif not numeric +LB22C jmp LBD12 ; parse a number (and return) +LB22F jsr LB3A2 ; set carry if not alpha + bcc LB284 ; brif alpha character (variable) + cmpa #'. ; decimal point? + beq LB22C ; brif so - evaluate number + cmpa #0xac ; minus? + beq LB27C ; brif so - process unary negation + cmpa #0xab ; plus? + beq LB228 ; brif so - ignore unary "posation" + cmpa #'" ; string delimiter? + bne LB24E ; brif not +LB244 ldx CHARAD ; get input pointer + jsr LB518 ; go parse the string +LB249 ldx COEFPT ; get address of end of string + stx CHARAD ; move input pointer past string + rts +LB24E cmpa #0xa8 ; NOT? + bne LB25F ; brif not + lda #0x5a ; precedence of unary NOT + jsr LB15A ; process the operand of NOT + jsr INTCNV ; convert to integer in D + coma ; do a bitwise complement + comb + jmp GIVABF ; resturn the result +LB25F inca ; is it a function token? + beq LB290 ; brif so +LB262 bsr LB26A ; only other legal thing must be a (expr) + jsr LB156 ; evaluate parentheticized expression +LB267 ldb #') ; force a ) + skip2 +LB26A ldb #'( ; force a ( + skip2 +LB26D ldb #', ; force a , +LB26F cmpb [CHARAD] ; does character match? + bne LB277 ; brif not + jmp GETNCH ; each the character and return the next +LB277 ldb #2*1 ; raise syntax error + jmp LAC46 +LB27C lda #0x7d ; unary negation precedence + jsr LB15A ; evaluate argument + jmp LBEE9 ; flip sign of FPA0 and return +LB284 jsr LB357 ; evaluate variable +LB287 stx FPA0+2 ; save descriptor address in FPA0 + lda VALTYP ; test variable type + bne LB222 ; brif string - we're done + jmp LBC14 ; copy FP number from (X) into FPA0 +LB290 jsr GETNCH ; get the actual token number + tfr a,b ; save it (for offsetting X) + lslb ; two bytes per jump table entry (and lose high bit) + jsr GETNCH ; eat the token byte + cmpb #2*19 ; is it a valid token for Color Basic? + bls LB29F ; brif so + jmp [COMVEC+18] ; transfer control to Extended Basic if not +LB29F pshs b ; save jump table offset + cmpb #2*14 ; does it expect a numeric argument? + blo LB2C7 ; brif so + cmpb #2*18 ; does it need no arguments? + bhs LB2C9 ; brif so + bsr LB26A ; force a ( + lda ,s ; get token value + cmpa #2*17 ; is it POINT? + bhs LB2C9 ; brif so + jsr LB156 ; evaluate first argument string + bsr LB26D ; force a comma + jsr LB146 ; TM error if string + puls a ; get token value + ldu FPA0+2 ; get string descriptor + pshs u,a ; now we save the first string argument and the token value + jsr LB70B ; evaluate first numeric argument + puls a ; get back token value + pshs b,a ; save second argument and token value + fcb 0x8e ; opcode of LDX immediate (skips two bytes) +LB2C7 bsr LB262 ; force a ( +LB2C9 puls b ; get offset + ldx COMVEC+8 ; get jump table pointer + abx ; add offset into table + jsr [,x] ; go process function + jmp LB143 ; make sure result is numeric +; operator OR +LB2D4 skip1lda ; set flag to nonzero to signal OR +; operator AND +LB2D5 clra ; set flag to zero to signal AND + sta TMPLOC ; save AND/OR flag + jsr INTCNV ; convert second argument to intenger + std CHARAC ; save it + jsr LBC4A ; move first argument to FPA0 + jsr INTCNV ; convert first argument to integer + tst TMPLOC ; is it AND or OR? + bne LB2ED ; brif OR + anda CHARAC ; do the bitwise AND + andb ENDCHR + bra LB2F1 ; finish up +LB2ED ora CHARAC ; do the bitwise OR + orb ENDCHR +LB2F1 jmp GIVABF ; return integer result +; relational comparision operators +LB2F4 jsr LB148 ; TM error if type mismatch + BNE LB309 ; brif we have a string comparison + lda FP1SGN ; pack FPA1 + ora #0x7f + anda FPA1 + sta FPA1 + ldx #FP1EXP ; point to packed FPA1 + jsr LBC96 ; compare FPA0 to FPA1 + bra LB33F ; handle truth comparison +LB309 clr VALTYP ; the result of a comparison is always a number + dec TRELFL ; remove the string flag from the comparison data + jsr LB657 ; get string details for second argument + stb STRDES ; save them in the temporary string descriptor + stx STRDES+2 + ldx FPA1+2 ; get pointer to first argument descriptor + jsr LB659 ; get string details for second argument + lda STRDES ; get length of second argument + pshs b ; save length of first argument + suba ,s+ ; now A is the difference in string lengths + beq LB328 ; brif string lengths are equal + lda #1 ; flag for second argument is longer than first + bcc LB328 ; brif second string is longer than first + ldb STRDES ; get length of second string (shorter) + nega ; invert default comparison result +LB328 sta FP0SGN ; save default truth flag + ldu STRDES+2 ; get pointer to start of second string + incb ; compensate for DECB +LB32D decb ; have we compared everything? + bne LB334 ; brif not + ldb FP0SGN ; get default truth value + bra LB33F ; decide comparison truth +LB334 lda ,x+ ; get byte from first argument + cmpa ,u+ ; compare with second argument + beq LB32D ; brif equal - keep comparing + ldb #0xff ; negative if first string is > second + bcc LB33F ; brif string A > string B + negb ; invert result +LB33F addb #1 ; convert to 0,1,2 + rolb ; shift left - now it's 4,2,1 for <, =, > + andb RELFLG ; keep only the truth we care about + beq LB348 ; brif no matching bits - it's false + ldb #0xff ; set true +LB348 jmp LBC7C ; convert result to FP and return it +; DIM command +LB34B jsr LB26D ; make sure there's a comma between variables +DIM ldb #1 ; flag that we're dimensioning + bsr LB35A ; go allocate the variable + jsr GETCCH ; are we done? + bne LB34B ; brif not + rts +; This routine parses a variable. For scalars, it will return a NULL string or 0 value number +; if it is called from "evaluate term". Otherwise, it allocates the variable. For arrays, it will +; allocate a default sized array if dimensioning is not underway and then attempt to look up +; the requested coordinates in that array. Otherwise, it will allocate an array based on the +; specified dimension values. +LB357 clrb ; flag that we're not setting up an array + jsr GETCCH +LB35A stb DIMFLG ; save dimensioning flag + sta VARNAM ; save first character of variable name + jsr GETCCH ; get input character (why? we already have it) + bsr LB3A2 ; set carry if not alpha + lbcs LB277 ; brif our variable doesn't start with a letter + clrb ; default second variable character to NUL + stb VALTYP ; set value type to numeric + jsr GETNCH ; get second character + bcs LB371 ; brif numeric - numbers are allowed + bsr LB3A2 ; set carry if not alpha + bcs LB37B ; brif not alpha +LB371 tfr a,b ; save set second character of variable name +LB373 jsr GETNCH ; get an input character + bcs LB373 ; brif numeric - still in variable name + bsr LB3A2 ; set carry if not alpha + bcc LB373 ; brif alpha - still in variable name +LB37B cmpa #'$ ; do we have the string sigil? + bne LB385 ; brif not + com VALTYP ; set value type to string + addb #0x80 ; set bit 7 of second variable character to indicate string + jsr GETNCH ; eat the sigil +LB385 stb VARNAM+1 ; save second variable name character + ora ARYDIS ; merge array disable flag (will set bit 7 of input character if no arrays) + suba #'( ; do we have a subscript? + lbeq LB404 ; brif so + clr ARYDIS ; disable the array disable flag - it's single use + ldx VARTAB ; point to the start of the variable table + ldd VARNAM ; get variable name +LB395 cmpx ARYTAB ; are we at the top of the variable table? + beq LB3AB ; brif so + cmpd ,x++ ; does the variable name match (and move pointer to variable data) + beq LB3DC ; brif so + leax 5,x ; move to next table entry + bra LB395 ; see if we have a match +; Set carry if not upper case alpha +LB3A2 cmpa #'A ; set C if less than A + bcs LB3AA ; brif less than A + suba #'Z+1 ; set C if greater than Z + suba #-('Z+1) +LB3AA rts +LB3AB ldx #ZERO ; point to empty location (NULL/0 value) + ldu ,s ; get caller address + cmpu #LB287 ; coming from "evaluate term"? + beq LB3DE ; brif so - don't allocate + ldd ARYEND ; get end of arrays + std V43 ; save as top of source block + addd #7 ; 7 bytes per scalar entry + std V41 ; save as top of destination block + ldx ARYTAB ; get bottom of arrays + stx V47 ; save as bottom of source block + jsr LAC1E ; move the arrays up to make a hole + ldx V41 ; get new top of arrays + stx ARYEND ; set new end of arrays + ldx V45 ; get bottom of destination block + stx ARYTAB ; set as new start of arrays + ldx V47 ; get old end of variables + ldd VARNAM ; get name of variable + std ,x++ ; set variable name and advance X to the value + clra ; zero out the variable value + clrb + std ,x + std 2,x + sta 4,x +LB3DC stx VARPTR ; save descriptor address of return value +LB3DE rts +; Various integer conversion routines +LB3DF fcb 0x90,0x80,0x00,0x00,0x00 ; FP constant -32768 +LB3E4 jsr GETNCH ; fetch input character +LB3E6 jsr LB141 ; evaluate numeric expression +LB3E9 lda FP0SGN ; get sign of value + bmi LB44A ; brif negative (raise FC error) +INTCNV lda FP0EXP ; get exponent + cmpa #0x90 ; is it within the range for a 16 bit integer? + blo LB3FE ; brif smaller than 32768 + ldx #LB3DF ; point to -32678 constant + jsr LBC96 ; is FPA0 equal to -32768? + bne LB44A ; brif not - magnitude is too far negative +LB3FE jsr LBCC8 ; move binary point to the right of FPA0 and correct sign + ldd FPA0+2 ; get the resulting integer + rts +LB404 ldb DIMFLG ; get dimensioning flag + lda VALTYP ; get type of variable + pshs b,a ; save them (to avoid issues while evaluating dimension values) + clrb ; reset dimension counter +LB40A ldx VARNAM ; get variable name + pshs x,b ; save dimension counter and variable name + bsr LB3E4 ; evaluate a dimension value (and skip either ( or ,) + puls b,x,y ; get variable name, dimension counter, and dimensioning/type flag + stx VARNAM ; restore variable name + ldu FPA0+2 ; get dimension size/index + pshs u,y ; save dimension size and dimensioning/type flag + incb ; bump dimension counter + jsr GETCCH ; get what's after the dimension count + cmpa #', ; do we have another dimension? + beq LB40A ; brif so - parse it + stb TMPLOC ; save dimension counter + jsr LB267 ; make sure we have a ) + puls a,b ; get back variable type and dimensioning flag + sta VALTYP ; restore variable type + stb DIMFLG ; restore dimensioning flag + ldx ARYTAB ; get start of arrays +LB42A cmpx ARYEND ; are we at the end of the array table + beq LB44F ; brif so + ldd VARNAM ; get variable name + cmpd ,x ; does it match? + beq LB43B ; brif so + ldd 2,x ; get length of this array + leax d,x ; move to next array + bra LB42A ; go check another entry +LB43B ldb #2*9 ; code for redimensioned array error + lda DIMFLG ; are we dimensioning? + bne LB44C ; brif so - raise error + ldb TMPLOC ; get number of dimensions given + cmpb 4,x ; does it match? + beq LB4A0 ; brif so +LB447 ldb #8*2 ; raise "bad subscript" + skip2 +LB44A ldb #4*2 ; raise "illegal function call" +LB44C jmp LAC46 ; raise error +LB44F ldd #5 ; 5 bytes per array entry + std COEFPT ; initialize array size to entry size + ldd VARNAM ; get variable name + std ,x ; set array name + ldb TMPLOC ; get dimension count + stb 4,x ; set dimension count + jsr LAC33 ; make sure we haven't overflowed memory + stx V41 ; save array descriptor address +LB461 ldb #11 ; default dimension value (zero-based, gives max index of 10) + clra ; zero extend (??? why not LDD above?) + tst DIMFLG ; are we dimensioning? + beq LB46D ; brif not + puls a,b ; get dimension size + addd #1 ; account for zero based indexing +LB46D std 5,x ; save dimension size + bsr LB4CE ; multiply by accumulated array size + std COEFPT ; save new array size + leax 2,x ; move to next dimension + dec TMPLOC ; have we done all dimensions? + bne LB461 ; brif not + stx TEMPTR ; save end of array descriptor (minus 5) + addd TEMPTR ; add total size of array to address of descriptor + lbcs LAC44 ; brif it overflows memory + tfr d,x ; save end of array for later + jsr LAC37 ; does array fit in memory? + subd #STKBUF-5 ; subtract out the "stack fudge factor" but add 5 to the result + std ARYEND ; save new end of arrays + clra ; set up for clearing +LB48C leax -1,x ; move back one + sta 5,x ; blank out a byte in the array data + cmpx TEMPTR ; have we reached the array header? + bne LB48C ; brif not + ldx V41 ; get address of start of descriptor + lda ARYEND ; get MSB of end of array back (B still has LSB) + subd V41 ; subtract start of descriptor + std 2,x ; save length of array in array header + lda DIMFLG ; are we dimensioning? + bne LB4CD ; brif so - we're done +LB4A0 ldb 4,x ; get number of dimensions + stb TMPLOC ; initialize counter + clra ; initialize accumulated offset + clrb +LB4A6 std COEFPT ; save accumulated offset + puls a,b ; get desired index + std FPA0+2 ; save it + cmpd 5,x ; is it in range for this dimension? + bhs LB4EB ; brif not + ldu COEFPT ; get accumulated offset + beq LB4B9 ; brif first dimension + bsr LB4CE ; multiply accumulated offset by dimension length + addd FPA0+2 ; add in offset into this dimension +LB4B9 leax 2,x ; move to next dimension in header + dec TMPLOC ; done all dimensions? + bne LB4A6 ; brif not + std ,--s ; save D for multiply by 5 (should be pshs d) + aslb ; times 2 + rola + aslb ; times 4 + rola + addd ,s++ ; times 5 + leax d,x ; add in offset from start of array data + leax 5,x ; offset to end of header + stx VARPTR ; save pointer to element data +LB4CD rts +; multiply 16 bit number at 5,x by the 16 bit number in COEFPT; return result in D; BS error if carry +LB4CE lda #16 ; 16 shifts to do a multiply + sta V45 ; save shift counter + ldd 5,x ; get multiplier + std BOTSTK ; save it + clra ; zero out product + clrb +LB4D8 aslb ; shift product left + rola + bcs LB4EB ; brif we have a carry + asl COEFPT+1 ; shift other factor left + rol COEFPT + bcc LB4E6 ; brif no carry - this bit position is 0 + addd BOTSTK ; add in multiplier at this bit position + bcs LB4EB ; brif carry - do an error +LB4E6 dec V45 ; have we done all 16 bits? + bne LB4D8 ; brif not + rts +LB4EB jmp LB447 ; raise a BS error +; MEM function +; BUG: this doesn't account for the STKBUF fudge factor used for memory availability checks +MEM tfr s,d ; get stack pointer where we can do math + subd ARYEND ; calculate number of bytes between the stack and the top of arrays + skip1 ; return result +; Convert unsigned value in B to FP +LB4F3 clra ; zero extend +; Convert signed value in D to FP +GIVABF clr VALTYP ; set value type to numeric + std FPA0 ; save value in FPA0 + ldb #0x90 ; exponent for top two bytes to be an integer + jmp LBC82 ; finish conversion to integer +; STR$ function +STR jsr LB143 ; make sure we have a number + ldu #STRBUF+2 ; convert FP number to string in temporary string buffer + jsr LBDDC + leas 2,s ; don't return to the function evaluator (which will do a numeric type check) + ldx #STRBUF+1 ; point to number string + bra LB518 ; to stash the string in string space and return to the "evaluate term" caller +; Reserve B bytes of string space. Return start in X and FRESPC +LB50D stx V4D ; save X somewhere in case the caller needs it +LB50F bsr LB56D ; allocate string space +LB511 stx STRDES+2 ; save pointer to allocated space in the temporary descriptor + stb STRDES ; save length in the temporary descriptor + rts +LB516 leax -1,x ; move pointer back one (to compensate for the increment below) +; Scan from X until either NUL or one of the string terminators is found +LB518 lda #'" ; set terminator to be string delimiter +LB51A sta CHARAC ; set both delimiters + sta ENDCHR +LB51E leax 1,x ; move to next character + stx RESSGN ; save start of string + stx STRDES+2 ; save start of string in the temporary string descriptor + ldb #-1 ; initialize length counter to -1 (compensate for initial INCB) +LB526 incb ; bump string length + lda ,x+ ; get character from string + beq LB537 ; brif end of line + cmpa CHARAC ; is it delimiter #1? + beq LB533 ; brif so + cmpa ENDCHR ; is it delimiter #2? + bne LB526 ; brif not - keep scanning +LB533 cmpa #'" ; string delimiter? + beq LB539 ; brif so - don't move pointer back +LB537 leax -1,x ; move pointer back (so we don't consume the delimiter) +LB539 stx COEFPT ; save end of string address + stb STRDES ; save string length + ldu RESSGN ; get start of string + cmpu #STRBUF+2 ; is it at the start of the string buffer? + bhi LB54C ; brif so - don't copy it to string space + bsr LB50D ; allocate string space + ldx RESSGN ; point to beginning of the string + jsr LB645 ; copy string data (B bytes) from (X) to (FRESPC) +; Put temporary string descriptor on the string stack +LB54C ldx TEMPPT ; get top of string stack + cmpx #CFNBUF ; is the string stack full? + bne LB558 ; brif not + ldb #15*2 ; code for "string formula too complex" +LB555 jmp LAC46 ; raise error +LB558 lda STRDES ; get string length + sta 0,x ; save it in the string stack descriptor + ldd STRDES+2 ; get string data pointer + std 2,x ; save in string stack descriptor + lda #0xff ; set value type to string + sta VALTYP + stx LASTPT ; set pointer to last used entry on the string stack + stx FPA0+2 ; set pointer to descriptor in the current evaluation value + leax 5,x ; advance string stack pointer + stx TEMPPT + rts +; Reserve B bytes in string space. If there isn't enough space, try compacting string space and +; then try the allocation again. If it still fails, raise OS error. +LB56D clr GARBFL ; flag that compaction not yet done +LB56F clra ; zero extend the length + pshs d ; save requested string length + ldd STRTAB ; get current bottom of strings + subd ,s+ ; calculate new bottom of strings and remove zero extension + cmpd FRETOP ; does the string fit? + blo LB585 ; brif not - try compaction + std STRTAB ; save new bottom of strings + ldx STRTAB ; get bottom of strings + leax 1,x ; now X points to the real start of the allocated space + stx FRESPC ; save the string pointer + puls b,pc ; restore length and return +LB585 ldb #2*13 ; code for out of string space + com GARBFL ; have we compacted string space yet? + beq LB555 ; brif so - raise error + bsr LB591 ; compact string space + puls b ; get back string length + bra LB56F ; go try allocation again +; Compact string space +; This is an O(n^2) algorithm. It first searches all extant strings for the highest address data pointer +; that hasn't already been moved into the freshly compacted string space. If then moves that string data +; up to the highest address it can go to. It repeats this process over and over until it finds no string +; that isn't already in the compacted space. While doing this, it has to search all strings on the string +; stack (this is why the string stack is needed - so we can track anonymous strings), all scalar string +; variables, and *every* entry in every string array. +LB591 ldx MEMSIZ ; get to of string space +LB593 stx STRTAB ; save top of uncompacted stringspace + clra ; zero out D and reset pointer to discovered variable to NULL + clrb + std V4B + ldx FRETOP ; point to bottom of string space + stx V47 ; save as lowest match address (match will be higher) + ldx #STRSTK ; point to start of string stack +LB5A0 cmpx TEMPPT ; are we at the top of the string stack? + beq LB5A8 ; brif so - done with the string stack + bsr LB5D8 ; check for string in uncompacted space (and advance pointer) + bra LB5A0 ; check another on the string stack +LB5A8 ldx VARTAB ; point to start of scalar variables +LB5AA cmpx ARYTAB ; end of scalars? + beq LB5B2 ; brif so + bsr LB5D2 ; check for string in uncompacted space and advance pointer + bra LB5AA ; check another variable +LB5B2 stx V41 ; save address of end of variables (address of first array) +LB5B4 ldx V41 ; get start of the next array +LB5B6 cmpx ARYEND ; end of arrays? + beq LB5EF ; brif so + ldd 2,x ; get length of array + addd V41 ; add to start of array + std V41 ; save address of next array + lda 1,x ; get second character of variable name + bpl LB5B4 ; brif numeric + ldb 4,x ; get number of dimensions + aslb ; two bytes per dimension size + addb #5 ; add in fixed overhead for array descriptor + abx ; now X points to first array element +LB5CA cmpx V41 ; at the start of the next array? + beq LB5B6 ; brif so - go handle another array + bsr LB5D8 ; check for string in uncompacted space (and advance pointer) + bra LB5CA ; process next array element +LB5D2 lda 1,x ; get second character of variable name + leax 2,x ; move to variable data + bpl LB5EC ; brif numeric +LB5D8 ldb ,x ; get length of string + beq LB5EC ; brif NULL - don't need to check data pointer + ldd 2,x ; get data pointer + cmpd STRTAB ; is it in compacted string space? + bhi LB5EC ; brif so + cmpd V47 ; is it better match than previous best? + bls LB5EC ; brif not + stx V4B ; save descriptor address of best match + std V47 ; save new best data pointer match +LB5EC leax 5,x ; move to next descriptor +LB5EE rts +LB5EF ldx V4B ; get descriptor address of the matched string + beq LB5EE ; brif we didn't find one - we're done + clra ; zero extend length + ldb ,x ; get string length + decb ; subtract one (we won't have a NULL string here) + addd V47 ; now D points to the address of the end of the string data + std V43 ; save as top address of move + ldx STRTAB ; set top of uncompacted space as destination + stx V41 + jsr LAC20 ; move string to top of uncompactedspace + ldx V4B ; point to string descriptor + ldd V45 ; get new data pointer address + std 2,x ; update descriptor + ldx V45 ; get bottom of copy destination + leax -1,x ; move back below it + jmp LB593 ; go search for another string to move (and set new bottom of string space) +; Concatenate two strings. We come here directly from the operator handler rather than via a JSR. +LB60F ldd FPA0+2 ; get string descriptor for the first string + pshs d ; save it + jsr LB223 ; evaluate a second string (concatenation is left associative) + jsr LB146 ; make sure we have a string + puls x ; get back first string descriptor + stx RESSGN ; save it + ldb ,x ; get length of first string + ldx FPA0+2 ; get pointer to second string + addb ,x ; add length of second string + bcc LB62A ; brif combined length is OK + ldb #2*14 ; raise string too long error + jmp LAC46 +LB62A jsr LB50D ; reserve room for new string + ldx RESSGN ; get descriptor address of the first string + ldb ,x ; get length of first string + bsr LB643 ; copy it to string space + ldx V4D ; get descriptor address of second string + bsr LB659 ; get string details for second string + bsr LB645 ; copy second string into new string space + ldx RESSGN ; get pointer to first string + bsr LB659 ; remove it from the string stack if possible + jsr LB54C ; put new string on the string stack + jmp LB168 ; return to expression evaluator +; Copy B bytes to space pointed to by FRESPC +LB643 ldx 2,x ; get source address from string descriptor +LB645 ldu FRESPC ; get destination address + incb ; compensate for decb + bra LB64E ; do the copy +LB64A lda ,x+ ; copy a byte + sta ,u+ +LB64E decb ; done yet? + bne LB64A ; brif not + stu FRESPC ; save destination pointer + rts +; Fetch details of string in FPA0+2 and remove from the string stack if possible +LB654 jsr LB146 ; make sure we have a string +LB657 ldx FPA0+2 ; get descriptor pointer +LB659 ldb ,x ; get length of string + bsr LB675 ; see if it's at the top of the string stack and remove it if so + bne LB672 ; brif not removed + ldx 5+2,x ; get start address of string just removed + leax -1,x ; move pointer down 1 + cmpx STRTAB ; is it at the bottom of string space? + bne LB66F ; brif not + pshs b ; save length + addd STRTAB ; add length to start of strings (A was cleared previously) + std STRTAB ; save new string space start (deallocated space for this string) + puls b ; get back string length +LB66F leax 1,x ; restore pointer to pointing at the actual string data + rts +LB672 ldx 2,x ; get data pointer for the string + rts +; Remove string pointed to by X from the string stack if it is at the top of the stack; return with +; A clear and Z set if string removed +LB675 cmpx LASTPT ; is it at the top of the string stack? + bne LB680 ; brif not - do nothing + stx TEMPPT ; save new top of stack + leax -5,x ; move the "last" pointer back as well + stx LASTPT + clra ; flag string removed +LB680 rts +; LEN function +LEN bsr LB686 ; get string details +LB683 jmp LB4F3 ; return unsigned length in B +LB686 bsr LB654 ; get string details and remove from string stack + clr VALTYP ; set value type to numeric + tstb ; set flags according to length + rts +; CHR$ function +CHR jsr LB70E ; get 8 bit unsigned integer to B +LB68F ldb #1 ; allocate a one byte string + jsr LB56D + lda FPA0+3 ; get character code + jsr LB511 ; save reserved string details in temp descriptor + sta ,x ; put character in string +LB69B leas 2,s ; don't go back to function handler - avoid numeric type check +LB69D jmp LB54C ; return temporary string on string stack +; ASC function +ASC bsr LB6A4 ; get first character of argument + bra LB683 ; return unsigned code in B +LB6A4 bsr LB686 ; fetch string details + beq LB706 ; brif NULL string + ldb ,x ; get character at start of string + rts +; LEFT$ function +LEFT bsr LB6F5 ; get arguments from the stack +LB6AD clra ; clear pointer offset (set to start of string) +LB6AE cmpb ,x ; are we asking for more characters than there are in the string? + bls LB6B5 ; brif not + ldb ,x ; only return the number that are in the string + clra ; force starting offset to be the start of the string +LB6B5 pshs b,a ; save offset and length + jsr LB50F ; reserve space in string space + ldx V4D ; point to original string descriptor + bsr LB659 ; get string details + puls b ; get string offset + abx ; now X points to the start of the data to copy + puls b ; get length of copy + jsr LB645 ; copy the data to the allocated space + bra LB69D ; return temp string on string stack +; RIGHT$ function +RIGHT bsr LB6F5 ; get arguments from stack + suba ,x ; subtract length of original string from desired length + nega ; now A is offset into old string where we start copying + bra LB6AE ; go handle everything else +; MID$ function +MID ldb #255 ; default length is the whole string + stb FPA0+3 ; save it + jsr GETCCH ; see what we have after offset + cmpa #') ; end of function? + beq LB6DE ; brif so - no length + jsr LB26D ; force a comma + bsr LB70B ; get length parameter +LB6DE bsr LB6F5 ; get string and offset parameters from the stack + beq LB706 ; brif we have a 0 offset requested (string offsets are 1-based) + clrb ; clear length counter + deca ; subtract one from position parameter (we work on 0-based, param is 1-based) + cmpa ,x ; is start greater than length of string? + bhs LB6B5 ; brif so - return NULL string + tfr a,b ; save absolute position parameter + subb ,x ; now B is postition less length + negb ; now B is amount of string to copy + cmpb FPA0+3 ; is it less than the length requested? + bls LB6B5 ; brif so + ldb FPA0+3 ; set length to the requested length + bra LB6B5 ; go finish up copying the substring +; Common routine for LEFT$, RIGHT$, MID$ - check for ) and fetch string data and first parameter +; from the stack. These were evaluated in the function evaluation handler. (It's not clear that doing +; it that way is actually beneficial. However, this is what the ROM designers did, so here we are.) +LB6F5 jsr LB267 ; make sure we have ) + ldu ,s ; get return address - we're going to mess with the stack + ldx 5,s ; get address of string descriptor + stx V4D ; save descriptor adddress + lda 4,s ; get first numeric parameter in both A and B + ldb 4,s + leas 7,s ; clean up stack + tfr u,pc ; return to original caller +LB706 jmp LB44A ; raise FC error +; Evaluate an unsigned 8 bit expression to B +LB709 jsr GETNCH ; move to next character +LB70B jsr LB141 ; evaluate a numeric expression +LB70E jsr LB3E9 ; convert to integer in D + tsta ; are we negative or > 255? + bne LB706 ; brif so - FC error + jmp GETCCH ; fetch current input character and return +; VAL function +VAL jsr LB686 ; get string details + lbeq LBA39 ; brif NULL string - return 0 + ldu CHARAD ; get input pointer so we can replace it later + stx CHARAD ; point interpreter at string data + abx ; calculate end address of the string + lda ,x ; get byte after the end of the string + pshs u,x,a ; save end of string address, input pointer, and character after end of string + clr ,x ; put a NUL after the string (stops the number interpreter) + jsr GETCCH ; get input character at start of string + jsr LBD12 ; evaluate numeric expression in string + puls a,x,u ; get back saved character and pointers + sta ,x ; restore byte after string + stu CHARAD ; restore interpeter's input pointer + rts +; Evaluate unsigned expression to BINVAL, then evaluate an unsigned expression to B +LB734 bsr LB73D ; evaluate expression + stx BINVAL ; save result +LB738 jsr LB26D ; make sure there's a comma + bra LB70B ; evaluate unsigned expression to B +; Evaluate unsigned expression in X +LB73D jsr LB141 ; evaluate numeric expression +LB740 lda FP0SGN ; is it negative? + bmi LB706 ; brif so + lda FP0EXP ; get exponent + cmpa #0x90 ; largest possible exponent for 16 bits + bhi LB706 ; brif too large + jsr LBCC8 ; move binary point to right of FPA0 + ldx FPA0+2 ; get resulting unsigned value + rts +; PEEK function +PEEK bsr LB740 ; get address to X + ldb ,x ; get the value at that address + jmp LB4F3 ; return B as unsigned value +; POKE function +POKE bsr LB734 ; evaluate address and byte value + ldx BINVAL ; get address + stb ,x ; put value there + rts +; LLIST command +LLIST ldb #-2 ; set output device to printer + stb DEVNUM + jsr GETCCH ; reset flags for input character and fall through to LIST +; LIST command +LIST pshs cc ; save zero flag (end of statement) + jsr LAF67 ; parse line number + jsr LAD01 ; find address of that line + stx LSTTXT ; save that address as the start of the list + puls cc ; get back ent of statement flag + beq LB784 ; brif end of line - list whole program + jsr GETCCH ; are we at the end of the line (one number)? + beq LB789 ; brif end of line + cmpa #0xac ; is it "-"? + bne LB783 ; brif not + jsr GETNCH ; eat the "-" + beq LB784 ; brif no second number - list to end of program + jsr LAF67 ; evaluate the second number + beq LB789 ; brif illegal number +LB783 rts +LB784 ldu #0xffff ; this will cause listing to do the entire program + stu BINVAL +LB789 leas 2,s ; don't return to the caller - we'll jump back to the main loop + ldx LSTTXT ; get address of line to list +LB78D jsr LB95C ; do a newline if needed + jsr LA549 ; do a break check + ldd ,x ; get address of next line + bne LB79F ; brif not end of program +LB797 jsr LA42D ; close output file + clr DEVNUM ; reset device to screen + jmp LAC73 ; go back to immediate mode +LB79F stx LSTTXT ; save new line address + ldd 2,x ; get line number of this line + cmpd BINVAL ; is it above the end line? + bhi LB797 ; brif so - return + jsr LBDCC ; display line number + jsr LB9AC ; put a space after it + ldx LSTTXT ; get line address + bsr LB7C2 ; detokenize the line + ldx [LSTTXT] ; get pointer to next line + ldu #LINBUF+1 ; point to start of detokenized line +LB7B9 lda ,u+ ; get byte from detokenized line + beq LB78D ; brif end of line + jsr LB9B1 ; output character + bra LB7B9 ; handle next character +; Detokenize a line from (X) to the line input buffer +LB7C2 jsr RVEC24 ; do the RAM hook dance + leax 4,x ; move past next line pointer and line number + ldy #LINBUF+1 ; point to line input buffer (destination) +LB7CB lda ,x+ ; get character from tokenized line + beq LB820 ; brif end of input + bmi LB7E6 ; brif it's a token + cmpa #': ; colon? + bne LB7E2 ; brif not + ldb ,x ; get what's after the colon + cmpb #0x84 ; ELSE? + beq LB7CB ; brif so - suppress the colon + cmpb #0x83 ; '? + beq LB7CB ; brif so - suppress the colon + skip2 +LB7E0 lda #'! ; placeholder for unknown token +LB7E2 bsr LB814 ; stow output character + bra LB7CB ; go process another input character +LB7E6 ldu #COMVEC-10 ; point to command interptation table + cmpa #0xff ; is it a function? + bne LB7F1 ; brif not + lda ,x+ ; get function token + leau 5,u ; shift to the function half of the interpretation tables +LB7F1 anda #0x7f ; remove token bias +LB7F3 leau 10,u ; move to next command/function table + tst ,u ; is this table active? + beq LB7E0 ; brif not - use place holder + suba ,u ; subtract number of tokens handled by this table entry + bpl LB7F3 ; brif this token isn't handled here + adda ,u ; undo extra subtraction + ldu 1,u ; get reserved word list for this table +LB801 deca ; are we at the right entry? + bmi LB80A ; brif so +LB804 tst ,u+ ; end of entry? + bpl LB804 ; brif not + bra LB801 ; see if we're there yet +LB80A lda ,u ; get character from wordlist + bsr LB814 ; put character in the buffer + tst ,u+ ; end of word? + bpl LB80A ; brif not + bra LB7CB ; go handle another input character +LB814 cmpy #LINBUF+LBUFMX ; is there room? + bhs LB820 ; brif not + anda #0x7f ; lose bit 7 + sta ,y+ ; save character in output + clr ,y ; make sure there's always a NUL terminator +LB820 rts +; Tokenize the line that the input pointer is pointing to; put result in the line input buffer; return +; length in D +LB821 jsr RVEC23 ; do the RAM hook dance + ldx CHARAD ; get input pointer + ldu #LINBUF ; set destination pointer +LB829 clr V43 ; clear alpha string flag + clr V44 ; clear DATA flag +LB82D lda ,x+ ; get input character + beq LB852 ; brif end of input + tst V43 ; are we handling an alphanumeric string? + beq LB844 ; brif not + jsr LB3A2 ; set carry if not alpha + bcc LB852 ; brif alpha + cmpa #'0 ; is it below the digits? + blo LB842 ; brif so + cmpa #'9 ; is it within the digits? + bls LB852 ; brif so +LB842 clr V43 ; flag that we're past the alphanumeric string +LB844 cmpa #0x20 ; space? + beq LB852 ; brif so - keep it + sta V42 ; save scan delimiter + cmpa #'" ; string delimiter? + beq LB886 ; brif so - copy until another " + tst V44 ; doing "DATA"? + beq LB86B ; brif not +LB852 sta ,u+ ; put character in output + beq LB85C ; brif end of input + cmpa #': ; colon? + beq LB829 ; brif so - reset DATA and alpha string flags +LB85A bra LB82D ; go process another input character +LB85C clr ,u+ ; put a double NUL at the end + clr ,u+ + tfr u,d ; calculate length of result (includes double NUL and an extra two bytes) + subd #LINHDR + ldx #LINBUF-1 ; point to one before the output + stx CHARAD ; set input pointer there + rts +LB86B cmpa #'? ; print abbreviation? + bne LB873 ; brif not + lda #0x87 ; token for PRINT + bra LB852 ; go stash it +LB873 cmpa #'' ; REM abbreviation? + bne LB88A ; brif not + ldd #0x3a83 ; colon plus ' token + std ,u++ ; put it in the output +LB87C clr V42 ; set delimiter to NUL +LB87E lda ,x+ ; get input + beq LB852 ; brif end of line + cmpa V42 ; at the delimiter? + beq LB852 ; brif so +LB886 sta ,u+ ; save in output + bra LB87E ; keep scanning for delimiter +LB88A cmpa #'0 ; is it below digits? + blo LB892 ; brif so + cmpa #';+1 ; is it digit, colon, or semicolon? + blo LB852 ; brif so +LB892 leax -1,x ; move input pointer back one (to point at this input character) + pshs u,x ; save input and output pointers + clr V41 ; set token type to 0 (command) + ldu #COMVEC-10 ; point to command interpretation table +LB89B clr V42 ; set token counter to 0 (0x80) +LB89D leau 10,u ; + lda ,u ; get number of reserved words + beq LB8D4 ; brif this table isn't active + ldy 1,u ; point to reserved words list +LB8A6 ldx ,s ; get input pointer +LB8A8 ldb ,y+ ; get character from reserved word table + subb ,x+ ; compare with input character + beq LB8A8 ; brif exact match + cmpb #0x80 ; brif it was the last character in word and exact match + bne LB8EA ; brif not + leas 2,s ; remove original input pointer from stack + puls u ; get back output pointer + orb V42 ; create token value (B has 0x80 from above) + lda V41 ; get token type + bne LB8C2 ; brif function + cmpb #0x84 ; is it ELSE? + bne LB8C6 ; brif not + lda #': ; silently add a colon before ELSE +LB8C2 std ,u++ ; put two byte token into output + bra LB85A ; go handle more input +LB8C6 stb ,u+ ; save single byte token + cmpb #0x86 ; DATA? + bne LB8CE ; brif not + inc V44 ; set DATA flag +LB8CE cmpb #0x82 ; REM? + beq LB87C ; brif so - skip over rest of line +LB8D2 bra LB85A ; go handle more input +LB8D4 ldu #COMVEC-5 ; point to interpretation table, function style +LB8D7 com V41 ; invert token flag + bne LB89B ; brif we haven't already done functions + puls x,u ; restore input and output pointers + lda ,x+ ; copy first character + sta ,u+ + jsr LB3A2 ; set C if not alpha + bcs LB8D2 ; brif not alpha - it isn't a variable + com V43 ; set alphanumeric string flag + bra LB8D2 ; process more input +LB8EA inc V42 ; bump token number + deca ; checked all in this table? + beq LB89D ; brif so + leay -1,y ; unconsume last compared character +LB8F1 ldb ,y+ ; end of entry? + bpl LB8F1 ; brif not + bra LB8A6 ; check next reserved word +; PRINT command +PRINT beq LB958 ; brif no argument - do a newline + bsr LB8FE ; process print options + clr DEVNUM ; reset output to screen + rts +LB8FE cmpa #'@ ; is it PRINT @? + bne LB907 ; brif not + jsr LA554 ; move cursor to correct location + bra LB911 ; handle some more +LB907 cmpa #'# ; device number specified? + bne LB918 ; brif not + jsr LA5A5 ; parse device number + jsr LA406 ; check for valid output file +LB911 jsr GETCCH ; get input character + beq LB958 ; brif nothing - do newline + jsr LB26D ; need comma after @ or # +LB918 jsr RVEC9 ; do the RAM hook boogaloo +LB91B beq LB965 ; brif end of input +LB91D cmpa #0xa4 ; TAB(? + beq LB97E ; brif so + cmpa #', ; comma (next tab field)? + beq LB966 ; brif so + cmpa #'; ; semicolon (do not advance print position) + beq LB997 ; brif so + jsr LB156 ; evaluate expression + lda VALTYP ; get type of value + pshs a ; save it + bne LB938 ; brif string + jsr LBDD9 ; convert FP number to string + jsr LB516 ; parse a string and put on string stack +LB938 bsr LB99F ; print string + puls b ; get back variable type + jsr LA35F ; set up print parameters + tst PRTDEV ; is it a display device? + beq LB949 ; brif so + bsr LB958 ; do a newline + jsr GETCCH ; get input + bra LB91B ; process more print stuff +LB949 tstb ; set flags on print position + bne LB954 ; brif not at start of line + jsr GETCCH ; get current input + cmpa #', ; comma? + beq LB966 ; skip to next tab field if so + bsr LB9AC ; send a space +LB954 jsr GETCCH ; get input character + bne LB91D ; brif not end of statement +LB958 lda #0x0d ; carriage return + bra LB9B1 ; send it to output +LB95C jsr LA35F ; set up print parameters +LB95F beq LB958 ; brif width is 0 + lda DEVPOS ; get line position + bne LB958 ; brif not at start of line +LB965 rts +LB966 jsr LA35F ; set up print parameters + beq LB975 ; brif line width is 0 + ldb DEVPOS ; get line position + cmpb DEVLCF ; at or past last comma field? + blo LB977 ; brif so + bsr LB958 ; move to next line + bra LB997 ; handle more stuff +LB975 ldb DEVPOS ; get line position +LB977 subb DEVCFW ; subtract a comma field width + bhs LB977 ; brif we don't have a remainder yet + negb ; now B is number of of spaces needed + bra LB98E ; go advance +LB97E jsr LB709 ; evaluate TAB distance + cmpa #') ; closing )? + lbne LB277 ; brif not + jsr LA35F ; set up print parameters + subb DEVPOS ; subtract print position from desired position + bls LB997 ; brif we're already past it +LB98E tst PRTDEV ; is it a display device? + bne LB997 ; brif not +LB992 bsr LB9AC ; output a space + decb ; done enough? + bne LB992 ; brif not +LB997 jsr GETNCH ; get input character + jmp LB91B ; process more items +; cpoy string from (X-1) to output +LB99C jsr LB518 ; parse the string +LB99F jsr LB657 ; get string details +LB9A2 incb ; compensate for decb +LB9A3 decb ; done all of the string? + beq LB965 ; brif so + lda ,x+ ; get character from string + bsr LB9B1 ; send to output + bra LB9A3 ; go do another character +LB9AC lda #0x20 ; space character + skip2 +LB9AF lda #'? ; question mark character +LB9B1 jmp PUTCHR ; output character +; The floating point math package and related functions and operations follow from here +; to the end of the Color Basic ROM area +LB9B4 ldx #LBEC0 ; point to FP constant 0.5 + bra LB9C2 ; add 0.5 to FPA0 +LB9B9 jsr LBB2F ; unpack FP data from (X) to FPA1 +; subtraction operator +LB9BC com FP0SGN ; invert sign of FPA0 (subtracting is adding the negative) + com RESSGN ; that also inverts the sign differential + bra LB9C5 ; go add the negative of FPA0 to FPA1 +LB9C2 jsr LBB2F ; unpack FP data from (X) to FPA1 +; addition operator +LB9C5 tstb ; check exponent of FPA0 + lbeq LBC4A ; copy FPA1 to FPA0 if FPA0 is 0 + ldx #FP1EXP ; point X to FPA1 (first operand) as the operand to denormalize +LB9CD tfr a,b ; put exponent of FPA1 into B + tstb ; is FPA1 0? + beq LBA3E ; brif exponent is 0 - no-op; adding 0 to FPA0 + subb FP0EXP ; get difference in exponents - number of bits to shift the smaller mantissa + beq LBA3F ; brif exponents are equal - no need to denormalize + bmi LB9E2 ; brif FPA0 > FPA1 + sta FP0EXP ; replace result exponent with FPA1's (FPA1 is bigger) + lda FP1SGN ; also copy sign over + sta FP0SGN + ldx #FP0EXP ; point to FPA0 (we need to denormalize the smaller number) + negb ; invert the difference - this is the number of bits to shift the mantissa +LB9E2 cmpb #-8 ; do we have to shift by a whole byte? + ble LBA3F ; brif so start by shifting whole bytes to the right + clra ; clear overflow byte + lsr 1,x ; shift high bit of mantissa right (LSR will force a zero into the high bit) + jsr LBABA ; shift remainder of mantissa right -B times +LB9EC ldb RESSGN ; get the sign flag + bpl LB9FB ; brif signs are the same (we add the mantissas then) + com 1,x ; complement the mantissa and extra precision bytes + com 2,x + com 3,x + com 4,x + coma + adca #0 ; add one to A (COM sets C); this may cause a carry to enter the ADD below +LB9FB sta FPSBYT ; save extra precision byte + lda FPA0+3 ; add the main mantissa bytes (and propage carry from above) + adca FPA1+3 + sta FPA0+3 + lda FPA0+2 + adca FPA1+2 + sta FPA0+2 + lda FPA0+1 + adca FPA1+1 + sta FPA0+1 + lda FPA0 + adca FPA1 + sta FPA0 + tstb ; were signs the same? + bpl LBA5C ; brif so - number may have gotten bigger so normalize if needed +LBA18 bcs LBA1C ; brif we had a carry - result is positive?) + bsr LBA79 ; do a proper negation of FPA0 mantissa +LBA1C clrb ; clear temporary exponent accumulator +LBA1D lda FPA0 ; test high byte of mantissa + bne LBA4F ; brif not 0 - we need to do bit shifting + lda FPA0+1 ; shift left 8 bits + sta FPA0 + lda FPA0+2 + sta FPA0+1 + lda FPA0+3 + sta FPA0+2 + lda FPSBYT + sta FPA0+3 + clr FPSBYT + addb #8 ; account for 8 bits shifted + cmpb #5*8 ; shifted 5 bytes worth? + blt LBA1D ; brif not +LBA39 clra ; zero out exponent and sign - result is 0 +LBA3A sta FP0EXP ; set exponent and sign + sta FP0SGN +LBA3E rts +LBA3F bsr LBAAE ; shift FPA0 mantissa to the right + clrb ; clear carry + bra LB9EC ; get on with adding +LBA44 incb ; account for one bit shift + asl FPSBYT ; shift mantissa and extra precision left + rol FPA0+3 + rol FPA0+2 + rol FPA0+1 + rol FPA0 +LBA4F bpl LBA44 ; brif we haven't got a 1 in bit 7 + lda FP0EXP ; get exponent of result + pshs b ; subtract shift count from exponent + suba ,s+ + sta FP0EXP ; save adjusted exponent + bls LBA39 ; brif we underflowed - set result to 0 + skip2 +LBA5C bcs LBA66 ; brif mantissa overflowed + asl FPSBYT ; get bit 7 of expra precision to C (used for round off) + lda #0 ; set to 0 without affecting C + sta FPSBYT ; clear out extra precision bits + bra LBA72 ; go round off result +LBA66 inc FP0EXP ; bump exponent (for a right shift to bring carry in) + beq LBA92 ; brif we overflowed + ror FPA0 ; shift carry into mantissa, shift right + ror FPA0+1 + ror FPA0+2 + ror FPA0+3 +LBA72 bcc LBA78 ; brif no round-off needed + bsr LBA83 ; add one to mantissa + beq LBA66 ; brif carry - need to shift right again +LBA78 rts +LBA79 com FP0SGN ; invert sign of value +LBA7B com FPA0 ; first do a one's copmlement + com FPA0+1 + com FPA0+2 + com FPA0+3 +LBA83 ldx FPA0+2 ; add one to mantissa (after one's complement gives two's complement) + leax 1,x ; bump low word + stx FPA0+2 + bne LBA91 ; brif no carry from low word + ldx FPA0 ; bump high word + leax 1,x + stx FPA0 +LBA91 rts +LBA92 ldb #2*5 ; code for overflow + jmp LAC46 ; raise error +LBA97 ldx #FPA2-1 ; point to FPA2 +LBA9A lda 4,x ; shift mantissa right by 8 bits + sta FPSBYT + lda 3,x + sta 4,x + lda 2,x + sta 3,x + lda 1,x + sta 2,x + lda FPCARY ; and handle extra precision on the left + sta 1,x +LBAAE addb #8 ; account for 8 bits shifted + ble LBA9A ; brif more shifts needed + lda FPSBYT ; get sub byte (extra precision) + subb #8 ; undo the 8 added above + beq LBAC4 ; brif difference is 0 +LBAB8 asr 1,x ; shift mantissa and sub byte one bit (keep mantissa high bit set) +LBABA ror 2,x + ror 3,x + ror 4,x + rora + incb ; account for one shift + bne LBAB8 ; brif not enought shifts yet +LBAC4 rts +LBAC5 fcb 0x81,0x00,0x00,0x00,0x00 ; packed FP 1.0 +LBACA bsr LBB2F ; unpack FP value from (X) to FPA1 +; multiplication operator +LBACC beq LBB2E ; brif exponent of FPA0 is 0 (result is 0) + bsr LBB48 ; calculate exponent of product +LBAD0 lda #0 ; zero out mantissa of FPA2 + sta FPA2 + sta FPA2+1 + sta FPA2+2 + sta FPA2+3 + ldb FPA0+3 ; multiply FPA1 by LSB of FPA0 + bsr LBB00 + ldb FPSBYT ; save extra precision byte + stb VAE + ldb FPA0+2 + bsr LBB00 ; again for next byte of FPA0 + ldb FPSBYT + stb VAD + ldb FPA0+1 ; again for next byte of FPA0 + bsr LBB00 + ldb FPSBYT + stb VAC + ldb FPA0 ; and finally for the high byte + bsr LBB02 + ldb FPSBYT + stb VAB + jsr LBC0B ; copy mantissa from FPA2 to FPA0 (result) + jmp LBA1C ; normalize +LBB00 beq LBA97 ; brif multiplier is 0 - just shift, don't multiply +LBB02 coma ; set carry +LBB03 lda FPA2 ; get FPA2 MS byte + rorb ; data bit to carry; will be 0 when all shifts done + beq LBB2E ; brif 8 shifts done + bcc LBB20 ; brif data bit is 0 - no addition + lda FPA2+3 ; add mantissa of FPA1 and FPA2 + adda FPA1+3 + sta FPA2+3 + lda FPA2+2 + adca FPA1+2 + sta FPA2+2 + lda FPA2+1 + adca FPA1+1 + sta FPA2+1 + lda FPA2 + adca FPA1 +LBB20 rora ; shift carry into FPA2 + sta FPA2 + ror FPA2+1 + ror FPA2+2 + ror FPA2+3 + ror FPSBYT + clra ; clear carry + bra LBB03 +LBB2E rts +; Unpack FP value from (X) to FPA1 +LBB2F ldd 1,x ; copy mantissa (and sign) + sta FP1SGN ; save sign bit + ora #0x80 ; make sure mantissa has bit 7 set + std FPA1 + ldb FP1SGN ; get sign + eorb FP0SGN ; set if FPA0 sign differs + stb RESSGN + ldd 3,x ; copy remainder of mantissa + std FPA1+2 + lda ,x ; and exponent + sta FP1EXP + ldb FP0EXP ; fetch FPA0 exponent and set flags + rts +; Calculate eponent for product of FPA0 and FPA1 +LBB48 tsta ; is FPA1 zero? + beq LBB61 ; brif so + adda FP0EXP ; add to exponent of FPA0 (this is how scientific notation works) + rora ; set V if we *don't* have an overflow + rola + bvc LBB61 ; brif exponent too larger or small + adda #0x80 ; restore the bias + sta FP0EXP ; set result exponent + beq LBB63 ; brif 0 - clear FPA0 + lda RESSGN ; the result sign (negative if signs differ) is the result sign + sta FP0SGN ; so set it as such + rts +LBB5C lda FP0SGN ; get sign of FPA0 + coma ; invert sign + bra LBB63 ; zero sign and exponent +LBB61 leas 2,s ; don't go back to caller (mul/div) - return to previous caller +LBB63 lbpl LBA39 ; brif we underflowed - go zero things out +LBB67 jmp LBA92 ; raise overflow error +; fast multiply by 10 - leave result in FPA0 +LBB6A jsr LBC5F ; copy FPA0 to FPA1 (for addition later) + beq LBB7C ; brif exponent is 0 - it's a no-op then + adda #2 ; this gives "times 4" + bcs LBB67 ; raise overflow if required + clr RESSGN ; set result sign to "signs the same" + jsr LB9CD ; add FPA1 to FPA0 "times 5" + inc FP0EXP ; times 10 + beq LBB67 ; brif overflow +LBB7C rts +LBB7D fcb 0x84,0x20,0x00,0x00,0x00 ; packed FP constant 10.0 +; Divide by 10 +LBB82 jsr LBC5F ; move FPA0 to FPA1 + ldx #LBB7D ; point to constant 10 + clrb ; zero sign +LBB89 stb RESSGN ; result will be positive or zero + jsr LBC14 ; unpack constant 10 to FPA0 + skip2 ; fall through to division (divide FPA1 by 10) +LBB8F bsr LBB2F ; unpack FP number from (X) to FPA1 +; division operator +LBB91 beq LBC06 ; brif FPA0 is 0 - division by zero + neg FP0EXP ; get exponent of reciprocal of the divisor + bsr LBB48 ; calculate exponent of quotient + inc FP0EXP ; bump exponent (due to division algorithm below) + beq LBB67 ; brif overflow + ldx #FPA2 ; point to temporary storage location + ldb #4 ; do 5 bytes + stb TMPLOC ; save counter + ldb #1 ; shift counter and quotient byte +LBBA4 lda FPA0 ; compare mantissa of FPA0 to FPA1, set C if FPA1 less + cmpa FPA1 + bne LBBBD + lda FPA0+1 + cmpa FPA1+1 + bne LBBBD + lda FPA0+2 + cmpa FPA1+2 + bne LBBBD + lda FPA0+3 + cmpa FPA1+3 + bne LBBBD + coma ; set C if FPA0 = FPA1 (it "goes") +LBBBD tfr cc,a ; save "it goes" status + rolb ; rotate carry into quotient + bcc LBBCC ; brif carry clear - haven't done 8 shifts yet + stb ,x+ ; save quotient byte + dec TMPLOC ; done enough bytes? + bmi LBBFC ; brif done all 5 + beq LBBF8 ; brif last byte + ldb #1 ; reset shift counter and quotient byte +LBBCC tfr a,cc ; get back carry status + bcs LBBDE ; brif it "went" +LBBD0 asl FPA1+3 ; shift mantissa (dividend) left + rol FPA1+2 + rol FPA1+1 + rol FPA1 + bcs LBBBD ; brif carry - it "goes" so we have to bump quotient + bmi LBBA4 ; brif high order bit is set - compare mantissas + bra LBBBD ; otherwise, count a 0 bit and try next bit +LBBDE lda FPA1+3 ; subtract mantissa of FPA0 from mantissa of FPA1 + suba FPA0+3 + sta FPA1+3 + lda FPA1+2 + sbca FPA0+2 + sta FPA1+2 + lda FPA1+1 + sbca FPA0+1 + sta FPA1+1 + lda FPA1 + sbca FPA0 + sta FPA1 + bra LBBD0 ; go check for another go +LBBF8 ldb #0x40 ; only two bits in last byte (for rounding) + bra LBBCC ; go do the last byte +LBBFC rorb ; get low bits to bits 7,6 and C to bit 5 + rorb + rorb + stb FPSBYT ; save result extra precision + bsr LBC0B ; move FPA2 mantissa to FPA0 (result) + jmp LBA1C ; go normalize the result +LBC06 ldb #2*10 ; division by zero + jmp LAC46 ; raise error +; Copy mantissa of FPA2 to FPA0 +LBC0B ldx FPA2 ; copy high word + stx FPA0 + ldx FPA2+2 ; copy low word + stx FPA0+2 + rts +; unpack FP number at (X) to FPA0 +LBC14 pshs a ; save register + ldd 1,x ; get mantissa high word and sign + sta FP0SGN ; set sign + ora #0x80 ; make sure mantissa always has bit 7 set + std FPA0 + clr FPSBYT ; clear extra precision + ldb ,x ; get exponent + ldx 3,x ; copy mantissa low word + stx FPA0+2 + stb FP0EXP ; save exponent (and set flags) + puls a,pc ; restore register and return +LBC2A ldx #V45 ; point to FPA4 + bra LBC35 ; pack FPA0 there +LBC2F ldx #V40 ; point to FPA3 + skip2 ; fall through to pack FPA0 there +LBC33 ldx VARDES ; get variable descriptor pointer +; Pack FPA0 to (X) +LBC35 lda FP0EXP ; get exponent + sta ,x ; save it + lda FP0SGN ; get sign + ora #0x7f ; force set low bits - only keep sign in high bit + anda FPA0 ; merge in bits 6-0 of high byte of mantissa + sta 1,x ; save it + lda FPA0+1 ; copy next highest byte + sta 2,x + ldu FPA0+2 ; and the low word of the mantissa + stu 3,x + rts +; Copy FPA1 to FPA0; return with sign in A +LBC4A lda FP1SGN ; copy sign +LBC4C sta FP0SGN + ldx FP1EXP ; copy exponent, mantissa high byte + stx FP0EXP + clr FPSBYT ; clear extra precision + lda FPA1+1 ; copy mantissa second highest byte + sta FPA0+1 + lda FP0SGN ; set sign for return + ldx FPA1+2 ; copy low word of mantissa + stx FPA0+2 + rts +; Copy FPA0 to FPA1 +LBC5F ldd FP0EXP ; copy exponent and high byte of mantissa + std FP1EXP + ldx FPA0+1 ; copy middle bytes of mantissa + stx FPA1+1 + ldx FPA0+3 ; copy low byte of mantissa and sign + stx FPA1+3 + tsta ; set flags on exponent + rts +; check FPA0: return B = 0, if FPA0 is 0, 0xff if negative, and 0x01 if positive +LBC6D ldb FP0EXP ; get exponent + beq LBC79 ; brif 0 +LBC71 ldb FP0SGN ; get sign +LBC73 rolb ; get sign to C + ldb #0xff ; set for negative result + bcs LBC79 ; brif negative + negb ; set to 1 for positive +LBC79 rts +; SGN function +SGN bsr LBC6D ; get sign of FPA0 +LBC7C stb FPA0 ; save result + clr FPA0+1 ; clear next lower 8 bits + ldb #0x88 ; exponent if mantissa is 8 bit integer +LBC82 lda FPA0 ; get high bits of mantissa + suba #0x80 ; set C if mantissa was positive (will cause a negation if it was negative) +LBC86 stb FP0EXP ; set exponent + ldd ZERO ; clear out low word + std FPA0+2 + sta FPSBYT ; clear extra precision + sta FP0SGN ; set sign to positive + jmp LBA18 ; normalize the result +; ABS function +ABS clr FP0SGN ; force FPA0 to be positive (yes, it's that simple) + rts +; Compare packed FP number at (X) to FPA0 +; Return with B = -1, 0, 1 for FPA0 <, =, > (X) and flags set based on that +LBC96 ldb ,x ; get exponent of (X) + beq LBC6D ; brif (X) is 0 + ldb 1,x ; get MS byte of mantissa of (X) + eorb FP0SGN ; set bit 7 if signs of (X) and FPA0 differ + bmi LBC71 ; brif signs differ - no need to compare the magnitude +LBCA0 ldb FP0EXP ; compare exponents and brif different + cmpb ,x + bne LBCC3 + ldb 1,x ; compare mantissa (but we have to pack the FPA0 bits first + orb #0x7f ; keep only sign bit (note: signs are the same) + andb FPA0 ; merge in the mantissa bits from FPA0 + cmpb 1,x ; do the packed versions match? + bne LBCC3 ; brif not + ldb FPA0+1 ; compare second byte of mantissas + cmpb 2,x + bne LBCC3 + ldb FPA0+2 ; compare third byte of mantissas + cmpb 3,x + bne LBCC3 + ldb FPA0+3 ; compare low byte of mantissas, but use subtraction so B = 0 on match + subb 4,x + bne LBCC3 + rts ; return B = 0 if (X) = FPA0 +LBCC3 rorb ; shift carry to bit 7 (C set if FPA0 < (X)) + eorb FP0SGN ; invert the comparision sense if the signs are negative + bra LBC73 ; interpret comparison result +; Shift mantissa of FPA0 until the binary point is immediately to the right of the mantissa and set up the +; result as a two's complement value. +LBCC8 ldb FP0EXP ; get exponent of FPA0 + beq LBD09 ; brif FPA0 is zero - we don't have to do anything, just blank it + subb #0xa0 ; calculate number of shifts to get to the correct exponent (binary point to the right) + lda FP0SGN ; do we have a positive number? + bpl LBCD7 ; brif so + com FPCARY ; negate the mantissa and set extra inbound precision to the correct sign + jsr LBA7B +LBCD7 ldx #FP0EXP ; point to FPA0 + cmpb #-8 ; moving by whole bytes? + bgt LBCE4 ; brif not + jsr LBAAE ; do bit shifting + clr FPCARY ; clear carry in byte + rts +LBCE4 clr FPCARY ; clear the extra carry in precision + lda FP0SGN ; get sign of value + rola ; get sign to carry (so rotate repeats the sign) + ror FPA0 ; shift the first bit + jmp LBABA ; do the shifting dance +; INT function +INT ldb FP0EXP ; get exponent + cmpb #0xa0 ; is the number big enough that there can be no fractional part? + bhs LBD11 ; brif so - we don't have to do anything + bsr LBCC8 ; go shift binary point to the right of the mantissa + stb FPSBYT ; save extra precision bits + lda FP0SGN ; get original sign + stb FP0SGN ; force result to be positive + suba #0x80 ; set C if we had a positive result + lda #0xa0 ; set exponent to match denormalized result + sta FP0EXP + lda FPA0+3 ; save low byte + sta CHARAC + jmp LBA18 ; go normalize (this will correct for the two's complement representation of negatives) +LBD09 stb FPA0 ; replace mantissa of FPA0 with contents of B + stb FPA0+1 + stb FPA0+2 + stb FPA0+3 +LBD11 rts +; Convert ASCII string to FP +; BUG: no overflow is checked on the decimal exponent in exponential notation. +LBD12 ldx ZERO ; zero out FPA0 and temporaries + stx FP0SGN + stx FP0EXP + stx FPA0+1 + stx FPA0+2 + stx V47 + stx V45 + bcs LBD86 ; brif input character is numeric + jsr RVEC19 ; do the RAM hook dance + cmpa #'- ; regular negative sign + bne LBD2D ; brif not + com COEFCT ; invert sign + bra LBD31 ; process stuff after the sign +LBD2D cmpa #'+ ; regular plus? + bne LBD35 ; brif not +LBD31 jsr GETNCH ; get character after sign + bcs LBD86 ; brif numeric +LBD35 cmpa #'. ; decimal point? + beq LBD61 ; brif so + cmpa #'E ; scientific notation + bne LBD65 ; brif not + jsr GETNCH ; eat the "E" + bcs LBDA5 ; brif numeric + cmpa #0xac ; negative sign (token)? + beq LBD53 ; brif so + cmpa #'- ; regular negative? + beq LBD53 ; brif so + cmpa #0xab ; plus sign (token)? + beq LBD55 ; brif so + cmpa #'+ ; regular plus? + beq LBD55 + bra LBD59 ; brif no sign found +LBD53 com V48 ; set exponent sign to negative +LBD55 jsr GETNCH ; eat the sign + bcs LBDA5 ; brif numeric +LBD59 tst V48 ; is the exponent sign negatvie? + beq LBD65 ; brif not + neg V47 ; negate base 10 exponent + bra LBD65 +LBD61 com V46 ; toggle decimal point flag + bne LBD31 ; brif we haven't seen two decimal points +LBD65 lda V47 ; get base 10 exponent + suba V45 ; subtract number of places to the right + sta V47 ; we now have a complete decimal exponent + beq LBD7F ; brif we have no base 10 shifting required + bpl LBD78 ; brif positive exponent +LBD6F jsr LBB82 ; divide FPA0 by 10 (shift decimal point left) + inc V47 ; bump exponent + bne LBD6F ; brif we haven't reached 0 yet + bra LBD7F ; return result +LBD78 jsr LBB6A ; multiply by 10 + dec V47 ; downshift the exponent + bne LBD78 ; brif not at 0 yet +LBD7F lda COEFCT ; get desired sign + bpl LBD11 ; brif it will be positive - no need to do anything + jmp LBEE9 ; flip the sign of FPA0 +LBD86 ldb V45 ; get the decimal count + subb V46 ; (if decimal seen, will add one; otherwise it does nothing) + stb V45 + pshs a ; save new digit + jsr LBB6A ; multiply partial result by 10 + puls b ; get back digit + subb #'0 ; remove ASCII bias + bsr LBD99 ; add B to FPA0 + bra LBD31 ; go process another digit +LBD99 jsr LBC2F ; save FPA0 to FPA3 + jsr LBC7C ; convert B to FP number + ldx #V40 ; point to FPA3 + jmp LB9C2 ; add FPA3 and FPA0 +LBDA5 ldb V47 ; get exponent value + aslb ; times 2 + aslb ; times 4 + addb V47 ; times 5 + aslb ; times 10 + suba #'0 ; remove ASCII bias + pshs b ; save acculated result + adda ,s+ ; add new digit to accumulated result + sta V47 ; save new accumulated decimal exponent + bra LBD55 ; interpret another exponent character +LBDB6 fcb 0x9b,0x3e,0xbc,0x1f,0xfd ; packed FP: 99999999.9 +LBDBB fcb 0x9e,0x6e,0x6b,0x27,0xfd ; packed FP: 999999999 +LBDC0 fcb 0x9e,0x6e,0x6b,0x28,0x00 ; pakced FP: 1E9 +LBDC5 ldx #LABE8-1 ; point to "IN" message + bsr LBDD6 ; output the string + ldd CURLIN ; get basic line number +LBDCC std FPA0 ; save 16 bit unsigned integer + ldb #0x90 ; exponent for upper 16 bits of FPA0 to be an integer + coma ; set C (force normalization to treat as positive) + jsr LBC86 ; zero bottom half, save exponent, and normalize + bsr LBDD9 ; convert FP number to ASCII string +LBDD6 jmp LB99C ; output string +; Convert FP number to ASCII string +LBDD9 ldu #STRBUF+3 ; point to buffer address that will not cause string to go to string space +LBDDC lda #0x20 ; default sign is a space character + ldb FP0SGN ; get sign of value + bpl LBDE4 ; brif positive + lda #'- ; use negative sign +LBDE4 sta ,u+ ; save sign + stu COEFPT ; save output buffer pointer + sta FP0SGN ; save sign character + lda #'0 ; result is 0 if exponent is 0 + ldb FP0EXP ; get exponent + lbeq LBEB8 ; brif FPA0 is 0 + clra ; base 10 exponent is 0 for > 1 + cmpb #0x80 ; is number > 1? + bhi LBDFF ; brif so + ldx #LBDC0 ; point to 1E+09 + jsr LBACA ; shift decimal to the right by 9 spaces + lda #-9 ; account for shift +LBDFF sta V45 ; save base 10 exponent +LBE01 ldx #LBDBB ; point to 999999999 + jsr LBCA0 ; are we above that? + bgt LBE18 ; brif so +LBE09 ldx #LBDB6 ; point to 99999999.9 + jsr LBCA0 ; are we above that? + bgt LBE1F ; brif in range + jsr LBB6A ; multiply by 10 (we were small) + dec V45 ; account for shift + bra LBE09 ; see if we've come into range +LBE18 jsr LBB82 ; divide by 10 + inc V45 ; account for shift + bra LBE01 ; see if we've come into range +LBE1F jsr LB9B4 ; add 0.5 to FPA0 (rounding) + jsr LBCC8 ; do the integer dance + ldb #1 ; default decimal flag (force immediate decimal) + lda V45 ; get base 10 exponent + adda #10 ; account for "unormalized" number + bmi LBE36 ; brif number < 1.0 + cmpa #11 ; do we have more than 9 places? + bhs LBE36 ; brif so - do scientific notation + deca + tfr a,b + lda #2 ; force no scientific notation +LBE36 deca ; subtract wo without affecting carry + deca + sta V47 ; save exponent - 0 is do not display in scientific notation + stb V45 ; save number of places to left of decimal + bgt LBE4B ; brif >= 1 + ldu COEFPT ; point to string buffer + lda #'. ; put decimal + sta ,u+ + tstb ; is there anything to left of decimal? + beq LBE4B ; brif not + lda #'0 ; store a zero + sta ,u+ +LBE4B ldx #LBEC5 ; point to powers of 10 + ldb #0x80 ; set digit counter to 0x80 +LBE50 lda FPA0+3 ; add mantissa to power of 10 + adda 3,x + sta FPA0+3 + lda FPA0+2 + adca 2,x + sta FPA0+2 + lda FPA0+1 + adca 1,x + sta FPA0+1 + lda FPA0 + adca ,x + sta FPA0 + incb ; add one to digit counter + rorb ; put carry into bit 7 + rolb ; set V if carry and sign differ + bvc LBE50 ; brif positive mantissa or carry is 0 and negative mantissa + bcc LBE72 ; brif negative mantissa + subb #10+1 ; take 9's complement if adding mantissa + negb +LBE72 addb #'0-1 ; add ASCII bias + leax 4,x ; move to next power of 10 + tfr b,a ; save digit + anda #0x7f ; remove add/subtract flag + sta ,u+ ; put in output + dec V45 ; do we need a decimal yet? + bne LBE84 ; brif not + lda #'. ; put decimal + sta ,u+ +LBE84 comb ; toggle bit 7 (add/sub flag) + andb #0x80 ; only keep bit 7 + cmpx #LBEC5+9*4 ; done all places? + bne LBE50 ; brif not +LBE8C lda ,-u ; get last character + cmpa #'0 ; was it 0? + beq LBE8C ; brif so + cmpa #'. ; decimal? + bne LBE98 ; brif not + leau -1,u ; move past decimal if it isn't needed +LBE98 lda #'+ ; plus sign + ldb V47 ; get scientific notation exponent + beq LBEBA ; brif not scientific notation + bpl LBEA3 ; brif positive exponent + lda #'- ; negative sign for base 10 exponent + negb ; switch to positive exponent +LBEA3 sta 2,u ; put sign + lda #'E ; put "E" + sta 1,u + lda #'0-1 ; init to ASCII 0 (compensate for INC) +LBEAB inca ; bump digit + subb #10 ; have we hit the correct one yet? + bcc LBEAB ; brif not + addb #'9+1 ; convert units digit to ASCII + std 3,u ; put exponent in output + clr 5,u ; put trailing NUL + bra LBEBC ; go reset pointer +LBEB8 sta ,u ; store last character +LBEBA clr 1,u ; put NUL at the end +LBEBC ldx #STRBUF+3 ; point to start of string + rts +LBEC0 fcb 0x80,0x00,0x00,0x00,0x00 ; packed FP 0.5 +LBEC5 fqb -100000000 + fqb 10000000 + fqb -1000000 + fqb 100000 + fqb -10000 + fqb 1000 + fqb -100 + fqb 10 + fqb -1 +LBEE9 lda FP0EXP ; get exponent of FPA0 + beq LBEEF ; brif 0 - don't flip sign + com FP0SGN ; flip sign +LBEEF rts +; Expand a polynomial of the form +; AQ+BQ^3+CQ^5+DQ^7..... where Q is FPA0 and X points to a table +LBEF0 stx COEFPT ; save coefficient table pointer + jsr LBC2F ; copy FPA0 to FPA3 + bsr LBEFC ; multiply FPA3 by FPA0 + bsr LBF01 ; expand polynomial + ldx #V40 ; point to FPA3 +LBEFC jmp LBACA ; multiply FPA0 by FPA3 +LBEFF stx COEFPT ; save coefficient table counter +LBF01 jsr LBC2A ; move FPA0 to FPA4 + ldx COEFPT ; get the current coefficient + ldb ,x+ ; get the number of entries + stb COEFCT ; save as counter + stx COEFPT ; save new pointer +LBF0C bsr LBEFC ; multiply (X) and FPA0 + ldx COEFPT ; get this coefficient + leax 5,x ; move to next one + stx COEFPT ; save new pointer + jsr LB9C2 ; add (X) to FPA0 + ldx #V45 ; point X to FPA4 + dec COEFCT ; done all coefficients? + bne LBF0C ; brif more left + rts +; RND function +RND jsr LBC6D ; set flags on FPA0 + bmi LBF45 ; brif negative - set seed + beq LBF3B ; brif 0 - do random between 0 and 1 + bsr LBF38 ; convert to integer + jsr LBC2F ; save range value + bsr LBF3B ; get random number + ldx #V40 ; point to FPA3 + bsr LBEFC ; multply (X) by FPA0 + ldx #LBAC5 ; point to FP 1.0 + jsr LB9C2 ; add 1 to FPA0 +LBF38 jmp INT ; return integer value +LBF3B ldx RVSEED+1 ; move variable random number seed to FPA0 + stx FPA0 + ldx RVSEED+3 + stx FPA0+2 +LBF45 ldx RSEED ; move fixed seed to FPA1 + stx FPA1 + ldx RSEED+2 + stx FPA1+2 + jsr LBAD0 ; multiply them + ldd VAD ; get lowest order product bytes + addd #0x658b ; add a constant + std RVSEED+3 ; save it as new seed + std FPA0+2 ; save in result + ldd VAB ; get high order extra product bytes + adcb #0xb0 ; add upper bytes of constant + adca #5 + std RVSEED+1 ; save as new seed + std FPA0 ; save as result + clr FP0SGN ; set result to positive + lda #0x80 ; set exponent to 0 < FPA0 < 1 + sta FP0EXP + lda FPA2+2 ; get a byte from FPA2 + sta FPSBYT ; save as extra precision + jmp LBA1C ; go normalize FPA0 +RSEED fqb 0x40e64dab ; constant random number generator seed +; SIN function +SIN jsr LBC5F ; copy FPA0 to FPA1 + ldx #LBFBD ; point to 2*pi + ldb FP1SGN ; get sign of FPA1 + jsr LBB89 ; divide FPA0 by 2*pi + jsr LBC5F ; copy FPA0 to FPA1 + bsr LBF38 ; convert FPA0 to an integer + clr RESSGN ; set result to positive + lda FP1EXP ; get exponent of FPA1 + ldb FP0EXP ; get exponent of FPA0 + jsr LB9BC ; subtract FPA0 from FPA1 + ldx #LBFC2 ; point to FP 0.25 + jsr LB9B9 ; subtract FPA0 from 0.25 (pi/2) + lda FP0SGN ; get result sign + pshs a ; save it + bpl LBFA6 ; brif positive + jsr LB9B4 ; add 0.5 (pi) to FPA0 + lda FP0SGN ; get sign of result + bmi LBFA9 ; brif negative + com RELFLG ; if 3pi/2 >= arg >= pi/2 +LBFA6 jsr LBEE9 ; flip sign of FPA0 +LBFA9 ldx #LBFC2 ; point to 0.25 + jsr LB9C2 ; add 0.25 (pi/2) to FPA0 + puls a ; get original sign + tsta ; was it positive + bpl LBFB7 ; brif so + jsr LBEE9 ; flip result sign +LBFB7 ldx #LBFC7 ; point to series coefficients + jmp LBEF0 ; go calculate value +LBFBD fcb 0x83,0x49,0x0f,0xda,0xa2 ; 2*pi +LBFC2 fcb 0x7f,0x00,0x00,0x00,0x00 ; 0.25 +; modified taylor series SIN coefficients +LBFC7 fcb 6-1 ; six coefficients + fcb 0x84,0xe6,0x1a,0x2d,0x1b ; -((2pi)^11)/11! + fcb 0x86,0x28,0x07,0xfb,0xf8 ; ((2pi)^9)/9! + fcb 0x87,0x99,0x68,0x89,0x01 ; -((2pi)^7)/7! + fcb 0x87,0x23,0x35,0xdf,0xe1 ; ((2pi)^5)/5! + fcb 0x86,0xa5,0x5d,0xe7,0x28 ; -(2pi)^3)/3! + fcb 0x83,0x49,0x0f,0xda,0xa2 ; 2*pi +; these 12 bytes are unused + fcb 0xa1,0x54,0x46,0x8f,0x13,0x8f,0x52,0x43 + fcb 0x89,0xcd,0xa6,0x81 +; these are the hardware interrupt vectors (coco1/2 only) + fdb SW3VEC + fdb SW2VEC + fdb FRQVEC + fdb IRQVEC + fdb SWIVEC + fdb NMIVEC + fdb RESVEC diff -r 000000000000 -r 605ff82c4618 bas12.s --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bas12.s Sat Dec 08 19:57:01 2018 -0700 @@ -0,0 +1,4070 @@ + *pragma nolist + include defs.s + *pragma list +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; COLOR BASIC ROM +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + org BASIC +; This is the official set of ROM entry points. It is unfortunate that the generic input ("console in") routine is not exposed +; here. It is also unfortunate that no open and close file routines are exposed. This and other oversignts led to a lot of +; software either having to re-invent the wheel or calling directly into the ROM instead of via these official entry points. +POLCAT fdb KEYIN ; indirect jump, get a keystroke +CHROUT fdb PUTCHR ; indirect jump, output character +CSRDON fdb CASON ; indirect jump, turn cassette on and start reading +BLKIN fdb GETBLK ; indirect jump, read a block from tape +BLKOUT fdb SNDBLK ; indirect jump, write a block to tape +JOYIN fdb GETJOY ; indirect jump, read joystick axes +WRTLDR fdb WRLDR ; indirect jump, turn cassette on and write a leader +; Initialization code. +LA00E lds #LINBUF+LBUFMX+1 ; put the stack in the line input buffer which is a safe place for now + lda #0x37 ; enable the cartidge interrupt (to detect autostarting cartridges) + sta PIA1+3 + lda RSTFLG ; get warm start flag + cmpa #0x55 ; is it valid? + bne BACDST ; brif not - cold start + ldx RSTVEC ; get warm start routine pointer + lda ,x ; get first byte of the routine + cmpa #0x12 ; is it NOP? + bne BACDST ; brif not - the routine is invalid so do a cold start + jmp ,x ; transfer control to the warm start routine +; RESET/power on comes here +RESVEC leay LA00E,pcr ; point to warm start check code +LA02A ldx #PIA1 ; point to PIA1 - we're going to rely on the mirroring to reach PIA0 + clr -3,x ; set PIA0 DA to direction mode + clr -1,x ; set PIA0 DB to direction mode + clr -4,x ; set PIA0 DA to inputs + ldd #0xff34 + sta -2,x ; set PIA0 DB to outputs + stb -3,x ; set PIA0 DA to data mode + stb -1,x ; set PIA0 DB to data mode + clr 1,x ; set PIA1 DA to direction mode + clr 3,x ; set PIA1 DB to direction mode + deca + sta ,x ; set PIA1 DA bits 7-1 as output, 0 as input + lda #0xf8 ; set PIA1 DB bits 7-3 as output, 2-0 as input + sta 2,x + stb 1,x ; set PIA1 DA to data mode + stb 3,x ; set PIA1 DB to data mode + clr 2,x ; set VDG to alpha-numeric + ldb #2 ; make RS232 marking ("stop" bit) + stb ,x + ldu #SAMREG ; point to SAM register + ldb #16 ; 16 bits to clear +LA056 sta ,u++ ; clear a bit + decb ; done all? + bne LA056 ; brif not + sta SAMREG+9 ; put display at 0x400 + tfr b,dp ; set direct page to 0 + ldb #4 ; use as a mask to check RAMSZ input + sta -2,x ; set RAMSZ strobe high + bitb 2,x ; check RAMSZ input + beq LA072 ; brif set for 4K RAMs + clr -2,x ; set strobe low + bitb 2,x ; check input + beq LA070 ; brif set for 64K rams + leau -2,u ; adjust pointer to set SAM for 16K RAMs +LA070 sta -3,u ; program SAM for either 16K or 64K RAMs +LA072 jmp ,y ; transfer control to startup routine +; Cold start jumps here +BACDST ldx #VIDRAM+1 ; point past the top of the first 1K of memory (for double predec below) +LA077 clr ,--x ; clear a byte (last will actually try clearing LSB of RESET vector in ROM) + leax 1,x ; move forward one byte (will set Z if we're done) + bne LA077 ; brif not donw yet + jsr LA928 ; clear the screen + clr ,x+ ; put the constant zero that lives before the program + stx TXTTAB ; set beginning of program storage +LA084 lda 2,x ; get value from memory + coma ; make it different + sta 2,x ; try putting different into memory + cmpa 2,x ; did it matcH? + bne LA093 ; brif not - we found the end of memory + leax 1,x ; move pointer forward + com 1,x ; restore the original memory contents + bra LA084 ; try another byte +LA093 stx TOPRAM ; save top of memory (one below actual top because we need a byte for VAL() to work) + stx MEMSIZ ; save top of string space + stx STRTAB ; set bottom of allocated string space + leax -200,x ; allocate 200 bytes of string space + stx FRETOP ; set top of actually free memory + tfr x,s ; put the stack there + ldx #LA10D ; point to variable initializer + ldu #CMPMID ; point to variables to initialize (first batch) + ldb #28 ; 28 bytes in first batch + jsr LA59A ; copy bytes to variables + ldu #IRQVEC ; point to variables to initialize (second batch) + ldb #30 ; 30 bytes this time + jsr LA59A ; copy bytes to variables + ldx -12,x ; get SN error address + stx 3,u ; set ECB's command handlers to error + stx 8,u + ldx #RVEC0 ; point to RAM vectors + ldd #0x394b ; write 75 RTS opcodes (25 RAM vectors) +LA0C0 sta ,x+ ; put an RTS + decb ; done? + bne LA0C0 ; brif not + sta LINHDR-1 ; make temporary line header data for line encoding have a nonzero next line pointer + jsr LAD19 ; do a "NEW" + ldx #'E*256+'X ; magic number to detect ECB ROM + cmpx EXBAS ; is there an ECB ROM? + lbeq EXBAS+2 ; brif so - launch it + andcc #0xaf ; start interrupts + ldx #LA147-1 ; point to sign on message + jsr LB99C ; print it out + ldx #BAWMST ; warm start routine address + stx RSTVEC ; set vector there + lda #0x55 ; warm start valid flag + sta RSTFLG ; mark warm start valid + bra LA0F3 ; go to direct mode +; Warm start entry point +BAWMST nop ; valid routine marker + clr DEVNUM ; reset output/input to screen + jsr LAD33 ; do a partial NEW + andcc #0xaf ; start interrupts + jsr LA928 ; clear the screen +LA0F3 jmp LAC73 ; go to direct mode +; FIRQ service routine - this handles starting autostart cartridges +BFRQSV tst PIA1+3 ; is it the cartridge interrupt? + bmi LA0FC ; brif so + rti +LA0FC jsr LA7D1 ; delay for a while + jsr LA7D1 ; delay for another while + leay 0 ; NOTE: the 0 is a placeholder, extended addressing is required + jmp BROMHK +; Variable initializers (second batch) + jmp BIRQSV ; IRQ handler + jmp BFRQSV ; FIRQ handler + jmp LB44A ; default USR() address + fcb 0x80,0x4f,0xc7,0x52,0x59 ; random seed + fcb 0xff ; capslock flag - default to upper case + fdb DEBDEL ; keyboard debounce delay (why is it a variable?) + jmp LB277 ; exponentiation handler vector + fcb 53 ; (command interpretation table) 53 commands + fdb LAA66 ; (command interpretation table) reserved words list (commands) + fdb LAB67 ; (command interpretation table) jump table (commands) + fcb 20 ; (command interpretation table) 20 functions + fdb LAB1A ; (command interpretation table) reserved words list (functions) + fdb LAA29 ; (command interpretation table) jump table (functions) +; This is the signon message. +LA147 fcc 'COLOR BASIC 1.2' + fcb 0x0d + fcc '(C) 1982 TANDY' + fcb 0 +; This is the "invalid colour" CLS easter egg text. Basically 11 wasted bytes +LA166 fcc 'MICROSOFT' + fcb 0x0d,0 +; Read a character from current device and mask off bit 7 (keep it as 7 bit ASCII) +LA171 bsr LA176 ; get character + anda #0x7f ; mask off high bit + rts +; Generic read routine. Reads a character from the device specified by DEVNUM. If no input was available, +; CINBFL will bet nonzero (on EOF). If input was available, CINBFL will be zero. Note that this routine +; has undefined results when called on an output only device. All registers except CC and A are preserved. +LA176 jsr RVEC4 ; do RAM hook + clr CINBFL ; flag data available + tst DEVNUM ; is it keyboard? + beq LA1B1 ; brif so - blink cursor and wait for key press + tst CINCTR ; is there anything in cassette input buffer? + bne LA186 ; brif so + com CINBFL ; flag EOF + rts +; Read character from cassette file +LA186 pshs u,y,x,b ; preserve registers + ldx CINPTR ; get input buffer pointer + lda ,x+ ; get character from buffer + pshs a ; save it for return + stx CINPTR ; save new input buffer pointer + dec CINCTR ; count character just consumed + bne LA197 ; brif buffer is not empty yet + jsr LA635 ; go read another block, if any, to refill the buffer +LA197 puls a,b,x,y,u,pc ; restore registers and return the character +; Blink the cursor. This might be better timed via an interrupt or something. +LA199 dec BLKCNT ; is it time to blink the cursor? + bne LA1AB ; brif not + ldb #11 ; reset blink timer + stb BLKCNT + ldx CURPOS ; get cursor position + lda ,x ; get character at the cursor + adda #0x10 ; move to next color + ora #0x8f ; make sure it's a grahpics block with all elements lit + sta ,x ; put new cursor block on screen +LA1AB ldx #DEBDEL ; we'll use the debounce delay for the cursor blink timer (10ms) +LA1AE jmp LA7D3 ; go count X down +; Blink cursor while waiting for a key press +LA1B1 pshs x,b ; save registers +LA1B3 bsr LA199 ; go do a cursor iteration + bsr KEYIN ; go read a key + beq LA1B3 ; brif no key pressed + ldb #0x60 ; VDG screen space character + stb [CURPOS] ; blank cursor out + puls b,x,pc ; restore registers and return +; This is the actual keyboard polling routine. Returns 0 if no new key is down. Compared to the 1.0 and 1.1 +; ROMs, this routine is quite a lot more compact and robust. +LA1C1 clr PIA0+2 ; strobe all columns + lda PIA0 ; get rows + coma ; bits set if keys down + lsla ; remove the comparator input + beq LA244 ; brif no keys down - don't actually poll the keyboard +KEYIN pshs u,x,b ; save registers + ldu #PIA0 ; point to keyboard PIA + ldx #KEYBUF ; point to state table + clra ; clear carry, set column to 0xff (no strobe) + deca ; (note: deca does not affect C) + pshs x,a ; save column counter and make a couple of holes for temporaries + sta 2,u ; set strobe to no columns +LA1D9 rol 2,u ; move to next column (C is 0 initially, 1 after) + bcc LA220 ; brif we shifted out a 0 - we've done 8 columns + inc 0,s ; bump column counter (first bump goes to 0) + bsr LA23A ; read row data + sta 1,s ; save key data (for debounce check and later saving) + eora ,x ; now bits set if key state changed + anda ,x ; now bits are only set if a key has been pressed + ldb 1,s ; get new key data + stb ,x+ ; save in state table + tsta ; was a key down? + beq LA1D9 ; brif not - do another (nothing above cleared C) + ldb 2,u ; get strobe data + stb 2,s ; save it for debounce check + ldb #0xf8 ; set up so B is 0 after first add +LA1F4 addb #8 ; add 8 for each row + lsra ; did we hit the right row? + bcc LA1F4 ; brif not + addb 0,s ; add in column number + beq LA245 ; brif @ + cmpb #26 ; letter? + bhi LA247 ; brif not + orb #0x40 ; bias into letter range + bsr LA22E ; check for SHIFT + ora CASFLG ; merge in capslock state + bne LA20C ; brif either capslock or SHIFT - keep upper case + orb #0x20 ; move to lower case +LA20C stb 0,s ; save ASCII value + ldx DEBVAL ; get debounce delay + bsr LA1AE ; do the 10ms debounce delay + ldb #0xff ; set strobe to none - only joystick buttons register now + bsr LA238 ; read keyboard + inca ; A now 0 if no buttons down + bne LA220 ; brif button down - return nothing since we have interference +LA21A ldb 2,s ; get column strobe data + bsr LA238 ; read row data + cmpa 1,s ; does it match original read? +LA220 puls a,x ; clean up stack and get return value + bne LA22B ; brif failed debounce or a joystick button down + cmpa #0x12 ; is it SHIFT-0? + bne LA22C ; brif not + com CASFLG ; swap capslock state +LA22B clra ; set no key down +LA22C puls b,x,u,pc ; restore registers and return +LA22E lda #0x7f ; column strobe for SHIFT + sta 2,u ; set column + lda ,u ; get row data + coma ; set if key down + anda #0x40 ; only keep SHIFT state + rts +LA238 stb 2,u ; save strobe data +LA23A lda ,u ; get row data + ora #0x80 ; mask off comparator so it doesn't interfere + tst 2,u ; are we on column 7? + bmi LA244 ; brif not + ora #0xc0 ; also mask off SHIFT +LA244 rts +LA245 ldb #51 ; scan code for @ +LA247 ldx #CONTAB-0x36 ; point to code table + cmpb #33 ; arrows, space, zero? + blo LA264 ; brif so + ldx #CONTAB-0x54 ; adjust to other half of table + cmpb #48 ; ENTER, CLEAR, BREAK, @? + bhs LA264 ; brif so + bsr LA22E ; read shift state + cmpb #43 ; is it a number, colon, semicolon? + bls LA25D ; brif so + eora #0x40 ; invert shift state for others +LA25D tsta ; shift down? + bne LA20C ; brif not - return result + addb #0x10 ; add in offset to shifted character + bra LA20C ; go return result +LA264 lslb ; two entries per key + bsr LA22E ; check SHIFT state + beq LA26A ; brif not shift + incb ; point to shifted entry +LA26A ldb b,x ; get actual key code + bra LA20C ; go return result +CONTAB fcb 0x5e,0x5f ; (^, _) + fcb 0x0a,0x5b ; (LF, [) + fcb 0x08,0x15 ; (BS, ^U) + fcb 0x09,0x5d ; (TAB, ]) + fcb 0x20,0x20 ; + fcb 0x30,0x12 ; <0> (0, ^R) + fcb 0x0d,0x0d ; (CR, CR) + fcb 0x0c,0x5c ; (FF, \) + fcb 0x03,0x03 ; (^C, ^C) + fcb 0x40,0x13 ; <@> (@, ^S) +; Generic output routine. +; Output character in A to the device specified by DEVNUM. All registers are preserved except CC. +; Sending output to a device that does not support output is undefined. +PUTCHR jsr RVEC3 ; call RAM hook + pshs b ; save B + ldb DEVNUM ; get desired device number + incb ; set flags (Z for -1, etc.) + puls b ; restore B + bmi LA2BF ; brif < -1 (line printer) + bne LA30A ; brif > -1 (screen) +; Write character to tape file + pshs x,b,a ; save registers + ldb FILSTA ; get file status + decb ; input file? + beq LA2A6 ; brif so + ldb CINCTR ; get character count + incb ; account for this character + bne LA29E ; brif buffer not full + bsr LA2A8 ; write previously full block to tape +LA29E ldx CINPTR ; get output buffer pointer + sta ,x+ ; put character in output + stx CINPTR ; save new buffer pointer + inc CINCTR ; account for this character +LA2A6 puls a,b,x,pc ; restore registers and return +; Write a block of data to tape. +LA2A8 ldb #1 ; data block type +LA2AA stb BLKTYP ; set block type + ldx #CASBUF ; point to output buffer + stx CBUFAD ; set buffer pointer + ldb CINCTR ; get number of bytes in the block + stb BLKLEN ; set length to write + pshs u,y,a ; save registers + jsr LA7E5 ; write a block to tape + puls a,y,u ; restore registers + jmp LA650 ; reset buffer pointers +; Send byte to line printer +LA2BF pshs x,b,a,cc ; save registers and interrupt status + orcc #0x50 ; disable interrupts +LA2C3 ldb PIA1+2 ; get RS232 status + lsrb ; get status to C + bcs LA2C3 ; brif busy - loop until not busy + bsr LA2FB ; set output to marking + clrb ; transmit one start bit + bsr LA2FD + ldb #8 ; counter for 8 bits +LA2D0 pshs b ; save bit count + clrb ; zero output bits + lsra ; bet output bit to C + rolb ; get output bit to correct bit for output byte + lslb + bsr LA2FD ; transmit bit + puls b ; get back bit counter + decb ; are we done yet? + bne LA2D0 ; brif not + bsr LA2FB ; send stop bit (marking) + puls cc,a ; restore interrupt status and output character + cmpa #0x0d ; carriage return? + beq LA2ED ; brif so + inc LPTPOS ; bump output position + ldb LPTPOS ; get new position + cmpb LPTWID ; end of line? + blo LA2F3 ; brif not +LA2ED clr LPTPOS ; reset position to start of line + bsr LA305 ; do carriage return delay + bsr LA305 +LA2F3 ldb PIA1+2 ; get RS232 status + lsrb ; get status to C + bcs LA2F3 ; brif still busy, keep waiting + puls b,x,pc ; restore registers and return +LA2FB ldb #2 ; set output to high (marking) +LA2FD stb PIA1 ; set RS232 output + bsr LA302 ; do baud delay (first iteration) then fall through for second +LA302 ldx LPTBTD ; get buard rate delay constant + skip2 +LA305 ldx LPTLND ; get carriage return delay constant + jmp LA7D3 ; count X down +; Output character to screen +LA30A pshs x,b,a ; save registers + ldx CURPOS ; get cursor pointer + cmpa #0x08 ; backspace? + bne LA31D ; brif not + cmpx #VIDRAM ; at top of screen? + beq LA35D ; brif so - it's a no-op + lda #0x60 ; VDG space character + sta ,-x ; put a space at previous location and move pointer back + bra LA344 ; save new cursor position and return +LA31D cmpa #0x0d ; carriage return? + bne LA32F ; brif not + ldx CURPOS ; get cursor pointer (why? we already have it) +LA323 lda #0x60 ; VDG space character + sta ,x+ ; put output space + tfr x,d ; see if we at a multiple of 32 now + bitb #0x1f + bne LA323 ; brif not + bra LA344 ; go check for scrolling +LA32F cmpa #0x20 ; control character? + blo LA35D ; brif so + tsta ; is it graphics block? + bmi LA342 ; brif so + cmpa #0x40 ; number or special? + blo LA340 ; brif so (flip "case" bit) + cmpa #0x60 ; upper case alpha? + blo LA342 ; brif so - keep it unmodified + anda #0xdf ; clear bit 5 (inverse video) +LA340 eora #0x40 ; flip inverse video bit +LA342 sta ,x+ ; output character +LA344 stx CURPOS ; save new cursor position + cmpx #VIDRAM+511 ; end of screen? + bls LA35D ; brif not + ldx #VIDRAM ; point to start of screen +LA34E ldd 32,x ; get two characters from next row + std ,x++ ; put them on this row + cmpx #VIDRAM+0x1e0 ; at start of last row on screen? + blo LA34E ; brif not + ldb #0x60 ; VDG space + jsr LA92D ; blank out last line (borrow CLS's loop) +LA35D puls a,b,x,pc ; restore registers and return +; Set up device parameters for output +LA35F jsr RVEC2 ; do the RAM hook dance + pshs x,b,a ; save registers + clr PRTDEV ; flag device as a screen + lda DEVNUM ; get devicenumber + beq LA373 ; brif screen + inca ; is it tape? + beq LA384 ; brif so + ldx LPTCFW ; get tab width and last tab stop for printer + ldd LPTWID ; get line width and current position for printer + bra LA37C ; set parameters +LA373 ldb CURPOS+1 ; get LSB of cursor position + andb #0x1f ; now we have the offset into the line + ldx #0x1010 ; 16 character tab, position 16 is last tab stop + lda #32 ; screen is 32 characters wide +LA37C stx DEVCFW ; save tab width and last tab stop for active device + stb DEVPOS ; save line position for current device + sta DEVWID ; save line width for current device + puls a,b,x,pc ; restore registers and return +LA384 com PRTDEV ; flag device as non-display + ldx #0x0100 ; tab width is 1, last tab field is 0 + clra ; line width is 0 + clrb ; character position on line is 0 + bra LA37C ; go set parameters +; This is the line input routine used for reading lines for Basic, both in immediate mode and for +; INPUT and LINE INPUT. Exit with C set if terminated by BREAK and C clear if terminated by ENTER. +; The actualy entry point is LA390. Note that this routine echoes to *all* devices. +LA38D jsr LA928 ; clear screen (CLEAR key handling) +LA390 jsr RVEC12 ; do the RAM hook dance + clr IKEYIM ; reset cached input character from BREAK check + ldx #LINBUF+1 ; point to line input buffer (input pointer) + ldb #1 ; Number of characters in line (we start at 1 so BS handling is easier) +LA39A jsr LA171 ; get an input character, only keep low 7 bits + tst CINBFL ; is it EOF? + bne LA3CC ; brif EOF + tst DEVNUM ; is it keyboard input? + bne LA3C8 ; brif not - don't do line editing + cmpa #0x0c ; form feed (CLEAR)? + beq LA38D ; brif so - clear screen and reset + cmpa #0x08 ; backspace? + bne LA3B4 ; brif not + decb ; move back one character + beq LA390 ; brif we were at the start of the line - reset and start again + leax -1,x ; move input pointer back + bra LA3E8 ; echo the backspace and continue +LA3B4 cmpa #0x15 ; SHIFT-LEFT (kill line)? + bne LA3C2 ; brif not +LA3B8 decb ; at start of line? + beq LA390 ; brif so - reset and restart + lda #0x08 ; echo a backspace + jsr PUTCHR + bra LA3B8 ; see if we've erased everything yet +LA3C2 cmpa #0x03 ; BREAK? + orcc #1 ; set C if it is (only need Z for the next test + beq LA3CD ; brif BREAK - exit +LA3C8 cmpa #0x0d ; ENTER (CR) + bne LA3D9 ; brif not +LA3CC clra ; clear carry (it might not be clear on EOF) +LA3CD pshs cc ; save ENTER/BREAK flag + jsr LB958 ; echo a carriage return + clr ,x ; make sure we have a NUL at the end of the buffer + ldx #LINBUF ; point to input buffer + puls cc,pc ; restore ENTER/BREAK flag and return +LA3D9 cmpa #0x20 ; control character? + blo LA39A ; brif so - skip it + cmpa #'z+1 ; above z? + bhs LA39A ; brif so - ignore it + cmpb #LBUFMX ; is the buffer full? + bhs LA39A ; brif so - ignore extra characters + sta ,x+ ; put character in the buffer + incb ; bump character count +LA3E8 jsr PUTCHR ; echo character + bra LA39A ; go handle next input character +; Check if DEVNUM Is valid for reading. Raise FM error if open but not for reading. NO error if not open. +LA3ED jsr RVEC5 ; do the RAM hook dance + lda DEVNUM ; get device number + beq LA415 ; brif keyboard - always valid + inca ; is it tape? + bne LA403 ; brif not + lda FILSTA ; get tape file status + bne LA400 ; brif file is open +LA3FB ldb #22*2 ; raise NO error + jmp LAC46 +LA400 deca ; is it in input mode? + beq LA415 ; brif so +LA403 jmp LA616 ; raise FM error +; Check if DEVNUM is valid for writing. Raise FM error if open but not for writing. NO error if not open. +LA406 jsr RVEC6 ; do the RAM hook dance + lda DEVNUM ; get device number + inca ; is it tape? + bne LA415 ; brif not + lda FILSTA ; get file status + beq LA3FB ; brif not open + deca ; is it open for reading? + beq LA403 ; brif so - bad mode +LA415 rts +; CLOSE command +CLOSE beq LA426 ; brif no file specified - close all files + jsr LA5A5 ; parse device number +LA41B bsr LA42D ; close specified file + jsr GETCCH ; is there more? + beq LA44B ; brif not + jsr LA5A2 ; check for comma and parse another device number + bra LA41B ; go close this one +; Close all files handler. +LA426 jsr RVEC7 ; Yup. The RAM hook dance. + lda #-1 ; start with tape file + sta DEVNUM +; Close file specified in DEVNUM. Note that this never fails. +LA42D jsr RVEC8 ; You know it. RAM hook. + lda DEVNUM ; get device we're closing + clr DEVNUM ; reset to screen/keyboard + inca ; is it tape? + bne LA44B ; brif not + lda FILSTA ; get file status + cmpa #2 ; is it output? + bne LA449 ; brif not + lda CINCTR ; is there anything waiting to be written? + beq LA444 ; brif not + jsr LA2A8 ; write final block of data +LA444 ldb #0xff ; write EOF block + jsr LA2AA +LA449 clr FILSTA ; mark tape file closed +LA44B rts +; CSAVE command +CSAVE jsr LA578 ; parse filename + jsr GETCCH ; see what we have after the file name + beq LA469 ; brif none + jsr LB26D ; make sure there's a comma + ldb #'A ; make sure there's an A after + jsr LB26F + bne LA44B ; brif not end of line + clra ; file type 0 (basic program) + jsr LA65C ; write out header block + lda #-1 ; set output to tape + sta DEVNUM + clra ; set Z so we list the whole program + jmp LIST ; go list the program to tape +LA469 clra ; file type 0 (basic program) + ldx ZERO ; set to binary file mode + jsr LA65F ; write header block + clr FILSTA ; close files + inc BLKTYP ; set block type to data + jsr WRLDR ; write out a leader + ldx TXTTAB ; point to start of program +LA478 stx CBUFAD ; set buffer location + lda #255 ; block size to 255 bytes (max size) + sta BLKLEN + ldd VARTAB ; get end of program + subd CBUFAD ; how much is left? + beq LA491 ; brif we have nothing left + cmpd #255 ; do we have a full block worth? + bhs LA48C ; brif so + stb BLKLEN ; save actual remainder as block length +LA48C jsr SNDBLK ; write a block out + bra LA478 ; go do another block +LA491 neg BLKTYP ; set block type to 0xff (EOF) + clr BLKLEN ; no data in EOF block + jmp LA7E7 ; write EOF, stop tape, and return +; CLOAD and CLOADM commands +CLOAD clr FILSTA ; close tape file + cmpa #'M ; is it ClOADM? + beq LA4FE ; brif so + leas 2,s ; clean up stack + jsr LA5C5 ; parse file name + jsr LA648 ; go find the file + tst CASBUF+10 ; is it binary? + beq LA4C8 ; brif so + lda CASBUF+9 ; is it ASCII? + beq LA4CD ; brif not + jsr LAD19 ; clear out existing program + lda #-1 ; set up for reading from tape + sta DEVNUM + inc FILSTA ; set tape file to input + jsr LA635 ; go read first block + jmp LAC7C ; go to immediate mode to read in the program +; This is the EOF handler for immediate mode. It's rather assinine for this to be located here. It is +; probably an artifact of a substantially different code layout prior to compacting the ROM to fit in +; 8K. +LA4BF jsr RVEC13 ; do the RAM hook dance + jsr LA42D ; close file + jmp LAC73 ; go back to immediate mode +LA4C8 lda CASBUF+8 ; get file type + beq LA4D0 ; brif basic program +LA4CD jmp LA616 ; raise FM error +LA4D0 jsr LAD19 ; erase existing program + jsr CASON ; start reading tape + ldx TXTTAB ; get start of program storage +LA4D8 stx CBUFAD ; set load address for block + ldd CBUFAD ; get start of block + inca ; bump by 256 + jsr LAC37 ; check if there's room for a maximum sized block of 255 + jsr GETBLK ; go read a block + bne LA4F8 ; brif there was an error during reading + lda BLKTYP ; get type of block read + beq LA4F8 ; brif header block - IO error + bpl LA4D8 ; brif data block - read another + stx VARTAB ; save new end of program + bsr LA53B ; stop tape + ldx #LABED-1 ; point to "OK" prompt + jsr LB99C ; show prompt + jmp LACE9 ; reset various things and return +LA4F8 jsr LAD19 ; clear out partial program load +LA4FB jmp LA619 ; raise IO error +; This is the CLOADM command +LA4FE jsr GETNCH ; eat the "M" + bsr LA578 ; parse file name + jsr LA648 ; go find the file +LA505 ldx ZERO ; default offset is 0 + jsr GETCCH ; see if there's something after the file name + beq LA511 ; brif no offset + jsr LB26D ; make sure there's a comma + jsr LB73D ; evaluate offset to X +LA511 lda CASBUF+8 ; get file mode + cmpa #2 ; M/L program? + bne LA4CD ; brif not - FM error + ldd CASBUF+11 ; get load address + leau D,x ; add in offset + stu EXECJP ; set EXEC default address + tst CASBUF+10 ; is it binary? + bne LA4CD ; brif not + ldd CASBUF+13 ; get load address + leax d,x ; add in offset + stx CBUFAD ; set buffer address for loading + jsr CASON ; start up tape +LA52E jsr GETBLK ; read a block + bne LA4FB ; brif error reading + stx CBUFAD ; save new load address + tst BLKTYP ; set flags on block type + beq LA4FB ; brif another header - IO error + bpl LA52E ; brif it was data - read more +LA53B jmp LA7E9 ; turn off tape and return +; The EXEC command +EXEC beq LA545 ; brif no argument - use default address + jsr LB73D ; evaluate EXEC address to X + stx EXECJP ; set new default EXEC address +LA545 jmp [EXECJP] ; transfer control to execution address +; Break check for LIST so it doesn't interrupt ASCII saves. Why is this here instead of with the rest of the break +; check logic or packaged up with LIST? +LA549 jsr RVEC11 ; do the RAM hook dance + lda DEVNUM ; get device number + inca ; is it tape? + beq LA5A1 ; brif so - don't do break check + jmp LADEB ; do the actual break check +; Evaluate an expression and make sure it is within the limits of the screen and sets the cursor position. +; This really should be located with the PRINT command. +LA554 jsr LB3E4 ; evaluate a positive expression to D + subd #511 ; is it within bounds? + lbhi LB44A ; brif not - error out + addd #VIDRAM+511 ; adjust to be within the screen (and undo the SUBD above) + std CURPOS ; set cursor position + rts +; INKEY$ function +INKEY lda IKEYIM ; was a key down during break check? + bne LA56B ; brif so + jsr KEYIN ; poll the keyboard +LA56B clr IKEYIM ; reset the break check cache + sta FPA0+3 ; store result for later return + lbne LB68F ; brif a key was down - return it as a string + sta STRDES ; set string length to 0 (no key down) + jmp LB69B ; return the NULL string +; Parse a filename +LA578 ldx #CFNBUF ; point to file name buffer + clr ,x+ ; zero out file name length + lda #0x20 ; space character to initialize file name +LA57F sta ,x+ ; put a space in the buffer + cmpx #CASBUF ; at end of file name? + bne LA57F ; brif not + jsr GETCCH ; get input character + beq LA5A1 ; brif no name present + jsr LB156 ; evaluate the file name expression + jsr LB654 ; point to start of the file name + ldu #CFNBUF ; point to file name buffer + stb ,u+ ; save string length + beq LA5A1 ; brif empty - we're done + skip2 +LA598 ldb #8 ; copy 8 bytes +; Move B bytes from (X) to (U) +LA59A lda ,x+ ; copy a byte + sta ,u+ + decb ; done yet? + bne LA59A ; brif not +LA5A1 rts +; Parse a device number and check validity +LA5A2 jsr LB26D ; check for comma and SN error if not +LA5A5 cmpa #'# ; do we have a #? + bne LA5AB ; brif not (it's optional) + jsr GETNCH ; munch the # +LA5AB jsr LB141 ; evaluate the expression +LA5AE jsr INTCNV ; convert it to an integer in D + rolb ; move sign of B into C + adca #0 ; add sign of B to A + bne LA61F ; brif A doesn't match the sign of B + rorb ; restore B (ADCA will have set C if B was negative) + stb DEVNUM ; set device number + jsr RVEC1 ; do the RAM hook dance + beq LA5C4 ; brif device number set to screen/keyboard (valid) + bpl LA61F ; brif not negative (not valid) + cmpb #-2 ; is it printer or tape? + blt LA61F ; brif not (not valid) +LA5C4 rts +; Read file name from the line and do an error if anything follows it +LA5C5 bsr LA578 ; parse file name + jsr GETCCH ; set flags on current character +LA5C9 beq LA5C4 ; brif nothing there - it's good + jmp LB277 ; raise SN error +; EOF functoin +EOF jsr RVEC14 ; do the RAM hook dance + lda DEVNUM ; get device number + pshs a ; save it (so we can restore it later) + bsr LA5AE ; check the device number (which is in FPA0) + jsr LA3ED ; check validity for reading +LA5DA clrb ; not EOF = 0 (FALSE) + lda DEVNUM ; get device number + beq LA5E4 ; brif keyboard - never EOF + tst CINCTR ; is there anything in the input buffer? + bne LA5E4 ; brif so - not EOF + comb ; set EOF flag to -1 (true) +LA5E4 puls a ; get back original device + sta DEVNUM ; restore it +LA5E8 sex ; sign extend result to 16 bits + jmp GIVABF ; go return the result +; SKIPF command +SKIPF bsr LA5C5 ; parse file name + bsr LA648 ; look for the file + jsr LA6D1 ; read the file + bne LA619 ; brif error reading file + rts +; OPEN command +OPEN jsr RVEC0 ; do the RAM hook dance + jsr LB156 ; get file status (input/output) + jsr LB6A4 ; get first character of status string + pshs b ; save status + bsr LA5A2 ; parse a comma then the device number + jsr LB26D ; make sure there's a comma + bsr LA5C5 ; parse the file name + lda DEVNUM ; get device number of the file + clr DEVNUM ; reset actual device to the screen + puls b ; get back status + cmpb #'I ; INPUT? + beq LA624 ; brif so - open a file for INPUT + cmpb #'O ; OUTPUT? + beq LA658 ; brif so - open a file for OUTPUT +LA616 ldb #21*2 ; raise FM error + skip2 +LA619 ldb #20*2 ; raise I/O error + skip2 +LA61C ldb #18*2 ; raise AO error + skip2 +LA61F ldb #19*2 ; raise DN error + jmp LAC46 +LA624 inca ; are we opening the tape? + bmi LA616 ; brif printer - FM error; printer can't be opened for READ + bne LA657 ; brif screen - screen is always open + bsr LA648 ; read header block + lda CASBUF+9 ; clear A if binary or machine language file + anda CASBUF+10 + beq LA616 ; bad file mode if not data file + inc FILSTA ; open file for input +LA635 jsr LA701 ; start tape, read block + bne LA619 ; brif error during read + tst BLKTYP ; check block type + beq LA619 ; brif header block - something's wrong + bmi LA657 ; brif EOF + lda BLKLEN ; get length of block + beq LA635 ; brif empty block - read another +LA644 sta CINCTR ; set buffer count + bra LA652 ; reset buffer pointer +LA648 tst FILSTA ; is the file open? + bne LA61C ; brif so - AO error + bsr LA681 ; search for file + bne LA619 ; brif error on read +LA650 clr CINCTR ; mark buffer empty +LA652 ldx #CASBUF ; set buffer pointer to start of buffer + stx CINPTR +LA657 rts +LA658 inca ; check for tape device + bne LA657 ; brif not tape (nothing doing - it's always open) + inca ; make file type 1 +LA65C ldx #0xffff ; ASCII and data mode +LA65F tst FILSTA ; is file open? + bne LA61C ; brif so - raise error + ldu #CASBUF ; point to tape buffer + stu CBUFAD ; set address of block to write + sta 8,u ; set file type + stx 9,u ; set ASCII flag and mode + ldx #CFNBUF+1 ; point to file name + jsr LA598 ; move file name to the tape buffer + clr BLKTYP ; set for header block + lda #15 ; 15 bytes in a header block + sta BLKLEN ; set block length + jsr LA7E5 ; write the block + lda #2 ; set file type to output + sta FILSTA + bra LA650 ; reset file pointers +; Search for correct cassette file name +LA681 ldx #CASBUF ; point to cassette buffer + stx CBUFAD ; set location to read blocks to +LA686 lda CURLIN ; are we in immediate mode? + inca + bne LA696 ; brif not + jsr LA928 ; clear screen + ldx CURPOS ; get start of screen (set after clear) + ldb #'S ; for "searching" + stb ,x++ ; put it on the screen + stx CURPOS ; save cursor position to be one past the search indicator +LA696 bsr LA701 ; read a block + orb BLKTYP ; merge error flag with block type + bne LA6D0 ; brif error or not header + ldx #CASBUF ; point to block just read + ldu #CFNBUF+1 ; point to the desired name + ldb #8 ; compare 8 characters + clr ,-s ; set flag to "match" +LA6A6 lda ,x+ ; get character from just read block + ldy CURLIN ; immediate mode? + leay 1,y + bne LA6B4 ; brif not + clr DEVNUM ; set output to screen + jsr PUTCHR ; display character +LA6B4 suba ,u+ ; subtract from desired file name (nonzero if no match) + ora ,s ; merge with match flag + sta ,s ; save new match flag (will be nonzero if any character differs) + decb ; done all characters? + bne LA6A6 ; brif not - do another + lda ,s+ ; get match flag (and set flags) + beq LA6CB ; brif we have a match + tst -9,u ; did we actually have a file name or will any file do? + beq LA6CB ; brif any file will do + bsr LA6D1 ; go read past the file + bne LA6D0 ; return on error + bra LA686 ; keep looking +LA6CB lda #'F ; for "found" + bsr LA6F8 ; put "F" on screen + clra ; set Z to indicat eno errors +LA6D0 rts +LA6D1 tst CASBUF+10 ; check type of file + bne LA6DF ; brif "blocked" file + jsr CASON ; turn on tape +LA6D9 bsr GETBLK ; read a block + bsr LA6E5 ; error or EOF? + bra LA6D9 ; read another block +LA6DF bsr LA701 ; read a single block + bsr LA6E5 ; error or EOF? + bra LA6DF ; read another block +LA6E5 bne LA6ED ; got error reading block + lda BLKTYP ; check block type + nega ; A is 0 now if EOF + bmi LA700 ; brif not end of file + deca ; clear error indicator +LA6ED sta CSRERR ; set error flag + leas 2,s ; don't return to original caller + bra LA705 ; turn off motor and return +LA6F3 lda VIDRAM ; get first char on screen + eora #0x40 ; flip case +LA6F8 ldb CURLIN ; immediate mode? + incb + bne LA700 ; brif not + sta VIDRAM ; save flipped case character +LA700 rts +; Read a single block from tape (for a "blocked" file) +LA701 bsr CASON ; start tape going + bsr GETBLK ; read block +LA705 jsr LA7E9 ; stop tape + ldb CSRERR ; get error status + rts +; Read a block from tape - this does the heavy lifting +GETBLK orcc #0x50 ; disable interrupts (timing is important) + bsr LA6F3 ; reverse video of upper left character in direct mode + ldx CBUFAD ; point to destination buffer + clra ; reset read byte +LA712 bsr LA755 ; read a bit + rora ; move bit into accumulator + cmpa #0x3c ; have we synched on the start of the block data yet? + bne LA712 ; brif not + bsr LA749 ; read block type + sta BLKTYP + bsr LA749 ; get block size + sta BLKLEN + adda BLKTYP ; accumulate checksum + sta CCKSUM ; save current checksum + lda BLKLEN ; get back count + sta CSRERR ; initialize counter; we use this since it will be ovewritten later anyway + beq LA73B ; brif empty block +LA72B bsr LA749 ; read a byte + sta ,x ; save in buffer + cmpa ,x+ ; make sure it wrote + bne LA744 ; brif error if it didn't match + adda CCKSUM ; accumulate checksum + sta CCKSUM + dec CSRERR ; read all bytes? + bne LA72B ; brif not +LA73B bsr LA749 ; read checksum from tape + suba CCKSUM ; does it match? + beq LA746 ; brif so + lda #1 ; checksum error flag + skip2 +LA744 lda #2 ; non-RAM error flag +LA746 sta CSRERR ; save error status + rts +LA749 lda #8 ; read 8 bits + sta CPULWD ; initialize counter +LA74D bsr LA755 ; read a bit + rora ; put it into accumulator + dec CPULWD ; got all 8 bits? + bne LA74D ; brif not + rts +LA755 bsr LA75D ; get time between transitions + ldb CPERTM ; get timer + decb + cmpb CMPMID ; set C if timer is below the transition point - high or 1; clear otherwise + rts +LA75D clr CPERTM ; reset timer + tst CBTPHA ; check which phase we synched on + bne LA773 ; brif HI-LO synch +LA763 bsr LA76C ; read input + bcs LA763 ; brif still high +LA767 bsr LA76C ; read input + bcc LA767 ; brif still low + rts +LA76C inc CPERTM ; bump timer + ldb PIA1 ; get input bit to C + rorb + rts +LA773 bsr LA76C ; read input + bcc LA773 ; brif still low +LA777 bsr LA76C ; read output + bcs LA777 ; brif still high + rts +; Start tape and look for sync bytes +CASON orcc #0x50 ; disable interrupts + bsr LA7CA ; turn on tape + clr CPULWD ; reset timer +LA782 bsr LA763 ; wait for low-high transition +LA784 bsr LA7AD ; wait for it to go low again + bhi LA797 ; brif in range for 1200 Hz +LA788 bsr LA7A7 ; wait for it to go high again + blo LA79B ; brif in range for 2400 Hz + dec CPULWD ; decrement counter (synched on low-high) + lda CPULWD ; get counter + cmpa #-96 ; have we seen 96 1-0-1-0 patterns (48 0x55s)? +LA792 bne LA782 ; brif not - wait some more + sta CBTPHA ; save phase we synched on + rts +LA797 bsr LA7A7 ; wait for it to go high again + bhi LA784 ; brif another 1200 Hz, 2 in a row, try again +LA79B bsr LA7AD ; wait for it to go low again + blo LA788 ; brif another 2400 Hz; go try again for high + inc CPULWD ; bump counter + lda CPULWD ; get counter + suba #96 ; set 0 if we've seen enought 0-1-0-1 patterns (0xaa) + bra LA792 ; set phase and return or keep waiting +LA7A7 clr CPERTM ; reset period timer + bsr LA767 ; wait for high + bra LA7B1 ; set flags on result +LA7AD clr CPERTM ; reset period timer + bsr LA777 ; wait for low +LA7B1 ldb CPERTM ; get period count + cmpb CMP0 ; is it too long for 1200Hz? + bhi LA7BA ; brif so - reset counts + cmpb CMP1 ; set C if 2400Hz, clear C if 1200 Hz + rts +LA7BA clr CPULWD ; reset sync counter (too slow or drop out) + rts +; MOTOR command +MOTOR tfr a,b ; save ON/OFF + jsr GETNCH ; eat the ON/OFF token + cmpb #0xaa ; OFF? + beq LA7E9 ; brif so - turn off tape + cmpb #0x88 ; ON? + jsr LA5C9 ; SN error if no match +; Turn on tape +LA7CA lda PIA1+1 ; get motor control value + ora #8 ; turn on bit 3 (starts motor) + bsr LA7F0 ; put it back (dumb but it saves a byte) +LA7D1 ldx ZERO ; maximum delay timer +LA7D3 leax -1,x ; count down + bne LA7D3 ; brif not at 0 yet + rts +; Write a synch leader to tape +WRLDR orcc #0x50 ; disable interrupts + bsr LA7CA ; turn on tape + ldx SYNCLN ; get count of 0x55s to write +LA7DE bsr LA828 ; write a 0x55 + leax -1,x ; done? + bne LA7DE ; brif not + rts +; Write sync bytes and a block, then stop tape +LA7E5 bsr WRLDR ; write sync +LA7E7 bsr SNDBLK ; write block +; Turn off tape +LA7E9 andcc #0xaf ; enable interrupts + lda PIA1+1 ; get control register + anda #0xf7 ; disable motor bit +LA7F0 sta PIA1+1 ; set motor enable bit + rts +; Write a block to tape. +SNDBLK orcc #0x50 ; disable interrupts + ldb BLKLEN ; get block size + stb CSRERR ; initialize character counter + lda BLKLEN ; initialize checksum + beq LA805 ; brif empty block + ldx CBUFAD ; point to tape buffer +LA800 adda ,x+ ; accumulate checksum + decb ; end of block data? + bne LA800 ; brif not +LA805 adda BLKTYP ; accumulate block type into checksum + sta CCKSUM ; save calculated checksum + ldx CBUFAD ; point to buffer + bsr LA828 ; send a 0x55 + lda #0x3c ; and then a 0x3c + bsr LA82A + lda BLKTYP ; send block type + bsr LA82A + lda BLKLEN ; send block size + bsr LA82A + tsta ; empty block? + beq LA824 ; brif so +LA81C lda ,x+ ; send character from block data + bsr LA82A + dec CSRERR ; are we done yet? + bne LA81C ; brif not +LA824 lda CCKSUM ; send checksum + bsr LA82A +LA828 lda #0x55 ; send a 0x55 +LA82A pshs a ; save output byte + ldb #1 ; initialize bit probe +LA82E lda CLSTSN ; get ending value of last cycle + sta PIA1 ; set DA + ldy #LA85C ; point to sine wave table + bitb ,s ; is bit set? + bne LA848 ; brif so - do high frequency +LA83B lda ,y+ ; get next sample (use all for low frequency) + cmpy #LA85C+36 ; end of table? + beq LA855 ; brif so + sta PIA1 ; set output sample + bra LA83B ; do another sample +LA848 lda ,y++ ; get next sample (use every other for high frequency) + cmpy #LA85C+36 ; end of table? + beq LA855 ; brif so + sta PIA1 ; send output sample + bra LA848 ; do another sample +LA855 sta CLSTSN ; save last sample that *would* have been sent + lslb ; shift mask to next bit + bcc LA82E ; brif not done all 8 bits + puls a,pc ; get back original character and return +; This is the sample table for the tape sine wave +LA85C fcb 0x82,0x92,0xaa,0xba,0xca,0xda + fcb 0xea,0xf2,0xfa,0xfa,0xfa,0xf2 + fcb 0xea,0xda,0xca,0xba,0xaa,0x92 + fcb 0x7a,0x6a,0x52,0x42,0x32,0x22 + fcb 0x12,0x0a,0x02,0x02,0x02,0x0a + fcb 0x12,0x22,0x32,0x42,0x52,0x6a +; SET command +SET bsr LA8C1 ; get absolute screen position of graphics block + pshs x ; save character location + jsr LB738 ; evaluate comma then expression in B + puls x ; get back character pointer + cmpb #8 ; valid colour? + bhi LA8D5 ; brif not + decb ; normalize colours + bmi LA895 ; brif colour 0 (use current colour) + lda #0x10 ; 16 patterns per colour + mul + bra LA89D ; go save the colour +LA895 ldb ,x ; get current value + bpl LA89C ; brif not grahpic + andb #0x70 ; keep only the colour + skip1 +LA89C clrb ; reset block to all black +LA89D pshs b ; save colour + bsr LA90D ; force a ) + lda ,x ; get current screen value + bmi LA8A6 ; brif graphic block already + clra ; force all pixels off +LA8A6 anda #0x0f ; keep only pixel data + ora GRBLOK ; set the desired pixel + ora ,s+ ; merge with desired colour +LA8AC ora #0x80 ; force it to be a graphic block + sta ,x ; put new block on screen + rts +; RESET command +RESET bsr LA8C1 ; get address of desired block + bsr LA90D ; force a ) + clra ; zero block (no pixels) + ldb ,x ; is it graphics? + bpl LA8AC ; brif not - just blank the block + com GRBLOK ; invert pixel data + andb GRBLOK ; turn off the desired pixel + stb ,x ; put new pixel data on screen + rts +; Parse SET/RESET/POINT coordinates except for closing ) +LA8C1 jsr LB26A ; make sure it starts with ( +LA8C4 jsr RVEC21 ; do the RAM hook dance + jsr LB70B ; get first coordinate + cmpb #63 ; valid horizontal coordinate + bhi LA8D5 ; brif out of range + pshs b ; save horizontal coordinate + jsr LB738 ; look for , followed by vertical coordinate + cmpb #31 ; in range for vertical? +LA8D5 bhi LA948 ; brif not + pshs b ; save vertical coordinate + lsrb ; divide by two (two blocks per row) + lda #32 ; 32 bytes per row + mul ; now we have the offset into video RAM + ldx #VIDRAM ; point to start of screen + leax d,x ; now X points to the correct character row + ldb 1,s ; get horizontal coordinate + lsrb ; divide by two (two per character cell) + abx ; now we're pointing to the correct character cell + puls a,b ; get back coordinates (vertical in A) + anda #1 ; keep only row offset of vertical + rorb ; get column offset of horizontal to C + rola ; now we have "row * 2 + col" in A + ldb #0x10 ; make a bit mask (one bit left of first pixel) +LA8EE lsrb ; move mask right + deca ; at the right pixel? + bpl LA8EE ; brif not + stb GRBLOK ; save graphics block mask + rts +; POINT function +POINT bsr LA8C4 ; evaluate coordinates + ldb #0xff ; default colour value is -1 (not graphics) + lda ,x ; get character + bpl LA90A ; brif not graphics + anda GRBLOK ; is desired pixel set? + beq LA909 ; brif not - return 0 for "black" + ldb ,x ; get graphics data + lsrb ; shift right 4 to get colour in low bits + lsrb + lsrb + lsrb + andb #7 ; lose the graphics block bias +LA909 incb ; shift colours into 1 to 8 range +LA90A jsr LA5E8 ; convert B to floating point +LA90D jmp LB267 ; make sure we have a ) and return +; CLS command +CLS jsr RVEC22 ; do the RAM hook dance +LA913 beq LA928 ; brif no colour - just do a basic screen clear + jsr LB70B ; evaluate colour number + cmpb #8 ; valid colour? + bhi LA937 ; brif not - do the easter egg + tstb ; color 0? + beq LA925 ; brif so + decb ; normalize to 0 based colour numbers + lda #0x10 ; 16 blocks per colour + mul ; now we have the base code for that colour + orb #0x0f ; set all pixels +LA925 orb #0x80 ; make it a graphics block + skip2 +LA928 ldb #0x60 ; VDG screen space character + ldx #VIDRAM ; point to start of screen +LA92D stx CURPOS ; set cursor position +LA92F stb ,x+ ; blank a character + cmpx #VIDRAM+511 ; end of screen? + bls LA92F ; brif not + rts +LA937 bsr LA928 ; clear te screen + ldx #LA166-1 ; point to the easter egg + jmp LB99C ; go display it +; Evaluate an expression to B, prefixed by a comma, and do FC error if 0 +LA93F jsr LB26D ; force a comma +LA942 jsr LB70B ; evaluate expression to B + tstb ; is it 0? + bne LA984 ; brif not - return +LA948 jmp LB44A ; raise FC error +; SOUND command +SOUND bsr LA942 ; evaluate frequency + stb SNDTON ; save it + bsr LA93F ; evaluate duration (after a comma) +LA951 lda #4 ; constant factor for duration (each increment is 1/15 of a second) + mul + std SNDDUR ; save length of sound (IRQ will count it down) + lda PIA0+3 ; enable 60 Hz interrupt + ora #1 + sta PIA0+3 + clr ARYDIS ; clear array disable flag for some reason + bsr LA9A2 ; connect DAC to MUX output + bsr LA976 ; turn on sound +LA964 bsr LA985 ; store mid range output value and delay + lda #0xfe ; store high value and delay + bsr LA987 + bsr LA985 ; store mid range value and delay + lda #2 ; store low value and delay + bsr LA987 + ldx SNDDUR ; has timer expired? + bne LA964 ; brif not, do another wave +; Disable sound output +LA974 clra ; bit 3 to 0 will disable output + skip2 +; Enable sound output +LA976 lda #8 ; bit 3 set to enable output + sta ,-s ; save desired value + lda PIA1+3 ; get control register value + anda #0xf7 ; reset value + ora ,s+ ; set to desired value + sta PIA1+3 ; set new sound output status +LA984 rts +LA985 lda #0x7e ; mid range value for DAC +LA987 sta PIA1 ; set DAC output value + lda SNDTON ; get frequency +LA98C inca ; increment it (gives shorter count with higher values, so higher frequencies work) + bne LA98C ; brif not done yet + rts +; AUDIO command +AUDIO tfr a,b ; save ON/OFF token + jsr GETNCH ; munch the ON/OFF token + cmpb #0xaa ; OFF? + beq LA974 ; brif so + subb #0x88 ; ON? + jsr LA5C9 ; do SN error if not + incb ; now B is 1 - cassette sound source + bsr LA9A2 ; set MUX input to tape + bra LA976 ; enable sound +; Set MUX source to value in B +LA9A2 ldu #PIA0+1 ; point to PIA0 control register A + bsr LA9A7 ; program bit 0 then fall through for bit 1 +LA9A7 lda ,u ; get control register value + anda #0xf7 ; reset mux control bit + asrb ; shift desired value to C + bcc LA9B0 ; brif this bit is clear + ora #8 ; set the bit +LA9B0 sta ,u++ ; set register value and move to next register + rts +; IRQ service routine +BIRQSV lda PIA0+3 ; check for VSYNC interrupt + bpl LA9C5 ; brif not - return. BUG: should clear HSYNC interrupt status first + lda PIA0+2 ; clear VSYNC interrupt status + ldx >SNDDUR ; are we counting down for SOUND? (force extended in case DP is modified) + beq LA9C5 ; brif not + leax -1,x ; count down one tick + stx >SNDDUR ; save new count (forced extended in case DP is modified) +LA9C5 rti +; JOYSTK function +JOYSTK jsr LB70E ; evaluate which joystick axis is desired + cmpb #3 ; valid axis? + lbhi LB44A ; brif not + tstb ; want axis 0? + bne LA9D4 ; brif not + bsr GETJOY ; read axis data if axis 0 +LA9D4 ldx #POTVAL ; point to axis values + ldb FPA0+3 ; get desired axis + ldb b,x ; get axis value + jmp LB4F3 ; return value +; Read all four joystick axes. Note that this routine will try 10 times to get a value that matches +; the value obtained during the *previous call to this routine*. Thus, if the axis value has changed, +; this routine will do the read *ten times* before just returning the last value. This is assininely +; slow and probably a bug since it seems more logical to look for two matching reads in a row. Note +; also that this routine should be using PSHS and PULS but it doesn't. +GETJOY bsr LA974 ; turn off sound + ldx #POTVAL+4 ; point to the end of the axis data (we'll work backwards) + ldb #3 ; start with axis 3 +LA9E5 lda #10 ; 10 tries to see if we match *the last call* to this routine + std ,--s ; save retry counter and axis number + bsr LA9A2 ; set MUX for the correct axis +LA9EB ldd #0x4080 ; set initial trial value to mid range and the next difference to add/subtract to half +LA9EE sta ,-s ; store the add/subtract value + orb #2 ; keep rs232 output marking + stb PIA1 ; set DAC output to the trial value + eorb #2 ; remove RS232 output value + lda PIA0 ; read the comparator + bmi LA9FF ; brif comparator output is high (DAC is lower than the axis value) + subb ,s ; subtract next bit value (split the difference toward 0) + skip2 +LA9FF addb ,s ; add next bit value (split the different toward infinity) + lda ,s+ ; get bit value back + lsra ; cut in half + cmpa #1 ; have we done that last value for the DAC? + bne LA9EE ; brif not + lsrb ; normalize the axis value + lsrb + cmpb -1,x ; does it match the read from the last call to this routine? + beq LAA12 ; brif so + dec ,s ; are we out of retries? + bne LA9EB ; brif not - try again +LAA12 stb ,-x ; save new value and move pointer back + ldd ,s++ ; get axis counter and clean up retry counter + decb ; move to next axis + bpl LA9E5 ; brif still more axes to do + rts +; This is the "bottom half" of the character fetching routines. +BROMHK cmpa #'9+1 ; is it >= colon? + bhs LAA28 ; brif so Z set if colon, C clear. + cmpa #0x20 ; space? + bne LAA24 ; brif not + jmp GETNCH ; move on to another character if space +LAA24 suba #'0 ; normalize ascii digit to 0-9; we already handled above digit 9 + suba #-'0 ; this will cause a carry for any value that was already positive +LAA28 rts +; Jump table for functions +LAA29 fdb SGN ; SGN 0x80 + fdb INT ; INT 0x81 + fdb ABS ; ABS 0x82 + fdb USRJMP ; USR 0x83 + fdb RND ; RND 0x84 + fdb SIN ; SIN 0x85 + fdb PEEK ; PEEK 0x86 + fdb LEN ; LEN 0x87 + fdb STR ; STR$ 0x88 + fdb VAL ; VAL 0x89 + fdb ASC ; ASC 0x8a + fdb CHR ; CHR$ 0x8b + fdb EOF ; EOF 0x8c + fdb JOYSTK ; JOYSTK 0x8d + fdb LEFT ; LEFT$ 0x8e + fdb RIGHT ; RIGHT$ 0x8f + fdb MID ; MID$ 0x90 + fdb POINT ; POINT 0x91 + fdb INKEY ; INKEY$ 0x92 + fdb MEM ; MEM 0x93 +; Operator precedence and jump table (binary ops except relational) +LAA51 fcb 0x79 ; + + fdb LB9C5 + fcb 0x79 ; - + fdb LB9BC + fcb 0x7b ; * + fdb LBACC + fcb 0x7b ; / + fdb LBB91 + fcb 0x7f ; ^ (exponentiation) + fdb EXPJMP + fcb 0x50 ; AND + fdb LB2D5 + fcb 0x46 ; OR + fdb LB2D4 +; Reserved words table for commands +LAA66 fcs 'FOR' ; 0x80 + fcs 'GO' ; 0x81 + fcs 'REM' ; 0x82 + fcs "'" ; 0x83 + fcs 'ELSE' ; 0x84 + fcs 'IF' ; 0x85 + fcs 'DATA' ; 0x86 + fcs 'PRINT' ; 0x87 + fcs 'ON' ; 0x88 + fcs 'INPUT' ; 0x89 + fcs 'END' ; 0x8a + fcs 'NEXT' ; 0x8b + fcs 'DIM' ; 0x8c + fcs 'READ' ; 0x8d + fcs 'RUN' ; 0x8e + fcs 'RESTORE' ; 0x8f + fcs 'RETURN' ; 0x90 + fcs 'STOP' ; 0x91 + fcs 'POKE' ; 0x92 + fcs 'CONT' ; 0x93 + fcs 'LIST' ; 0x94 + fcs 'CLEAR' ; 0x95 + fcs 'NEW' ; 0x96 + fcs 'CLOAD' ; 0x97 + fcs 'CSAVE' ; 0x98 + fcs 'OPEN' ; 0x99 + fcs 'CLOSE' ; 0x9a + fcs 'LLIST' ; 0x9b + fcs 'SET' ; 0x9c + fcs 'RESET' ; 0x9d + fcs 'CLS' ; 0x9e + fcs 'MOTOR' ; 0x9f + fcs 'SOUND' ; 0xa0 + fcs 'AUDIO' ; 0xa1 + fcs 'EXEC' ; 0xa2 + fcs 'SKIPF' ; 0xa3 + fcs 'TAB(' ; 0xa4 + fcs 'TO' ; 0xa5 + fcs 'SUB' ; 0xa6 + fcs 'THEN' ; 0xa7 + fcs 'NOT' ; 0xa8 + fcs 'STEP' ; 0xa9 + fcs 'OFF' ; 0xaa + fcs '+' ; 0xab + fcs '-' ; 0xac + fcs '*' ; 0xad + fcs '/' ; 0xae + fcs '^' ; 0xaf + fcs 'AND' ; 0xb0 + fcs 'OR' ; 0xb1 + fcs '>' ; 0xb2 + fcs '=' ; 0xb3 + fcs '<' ; 0xb4 +; Reserved word list for functions +LAB1A fcs 'SGN' ; 0x80 + fcs 'INT' ; 0x81 + fcs 'ABS' ; 0x82 + fcs 'USR' ; 0x83 + fcs 'RND' ; 0x84 + fcs 'SIN' ; 0x85 + fcs 'PEEK' ; 0x86 + fcs 'LEN' ; 0x87 + fcs 'STR$' ; 0x88 + fcs 'VAL' ; 0x89 + fcs 'ASC' ; 0x8a + fcs 'CHR$' ; 0x8b + fcs 'EOF' ; 0x8c + fcs 'JOYSTK' ; 0x8d + fcs 'LEFT$' ; 0x8e + fcs 'RIGHT$' ; 0x8f + fcs 'MID$' ; 0x90 + fcs 'POINT' ; 0x91 + fcs 'INKEY$' ; 0x92 + fcs 'MEM' ; 0x93 +; Jump table for commands +LAB67 fdb FOR ; 0x80 FOR + fdb GO ; 0x81 GO + fdb REM ; 0x82 REM + fdb REM ; 0x83 ' + fdb REM ; 0x84 ELSE + fdb IFTOK ; 0x85 IF + fdb DATA ; 0x86 DATA + fdb PRINT ; 0x87 PRINT + fdb ON ; 0x88 ON + fdb INPUT ; 0x89 INPUT + fdb ENDTOK ; 0x8a END + fdb NEXT ; 0x8b NEXT + fdb DIM ; 0x8c DIM + fdb READ ; 0x8d READ + fdb RUN ; 0x8e RUN + fdb RESTOR ; 0x8f RESTORE + fdb RETURN ; 0x90 RETURN + fdb STOP ; 0x91 STOP + fdb POKE ; 0x92 POKE + fdb CONT ; 0x93 CONT + fdb LIST ; 0x94 LIST + fdb CLEAR ; 0x95 CLEAR + fdb NEW ; 0x96 NEW + fdb CLOAD ; 0x97 CLOAD + fdb CSAVE ; 0x98 CSAVE + fdb OPEN ; 0x99 OPEN + fdb CLOSE ; 0x9a CLOSE + fdb LLIST ; 0x9b LLIST + fdb SET ; 0x9c SET + fdb RESET ; 0x9d RESET + fdb CLS ; 0x9e CLS + fdb MOTOR ; 0x9f MOTOR + fdb SOUND ; 0xa0 SOUND + fdb AUDIO ; 0xa1 AUDIO + fdb EXEC ; 0xa2 EXEC + fdb SKIPF ; 0xa3 SKIPF +; Error message table +LABAF fcc 'NF' ; 0 NEXT without FOR + fcc 'SN' ; 1 Syntax error + fcc 'RG' ; 2 RETURN without GOSUB + fcc 'OD' ; 3 Out of data + fcc 'FC' ; 4 Illegal function call + fcc 'OV' ; 5 Overflow + fcc 'OM' ; 6 Out of memory + fcc 'UL' ; 7 Undefined line number + fcc 'BS' ; 8 Bad subscript + fcc 'DD' ; 9 Redimensioned array + fcc '/0' ; 10 Division by 0 + fcc 'ID' ; 11 Illegal direct statement + fcc 'TM' ; 12 Type mismatch + fcc 'OS' ; 13 Out of string space + fcc 'LS' ; 14 String too long + fcc 'ST' ; 15 String formula too complex + fcc 'CN' ; 16 Can't continue + fcc 'FD' ; 17 Bad file data + fcc 'AO' ; 18 File already open + fcc 'DN' ; 19 Device number error + fcc 'IO' ; 20 Input/output error + fcc 'FM' ; 21 Bad file mode + fcc 'NO' ; 22 File not open + fcc 'IE' ; 23 Input past end of file + fcc 'DS' ; 24 Direct statement in file +LABE1 fcn ' ERROR' +LABE8 fcn ' IN ' +LABED fcb 0x0d +LABEE fcc 'OK' + fcb 0x0d,0x00 +LABF2 fcb 0x0d + fcn 'BREAK' +; Search stack for a FOR/NEXT stack frame. Stop search when something that isn't a FOR/NEXT +; stack frame is found. Enter with the index variable descriptor pointer in VARDES, or NULL +; for the first match. +; +; NOTE: this routine can be reworked to avoid needed two temporaries by taking advantage of the +; 6809's registers. This requires some minor tweaks where the routine is called. Further, the +; use of B is completely pointless and, even if B is going to be used, why is it reloaded on +; every loop? +LABF9 leax 4,s ; skip past our caller and the main command loop return address +LABFB ldb #18 ; each FOR/NEXT frame is 18 bytes + stx TEMPTR ; save current search pointer + lda ,x ; get first byte of this frame + suba #0x80 ; set to 0 if FOR/NEXT + bne LAC1A ; brif not FOR/NEXT (we hit the end of the stack for a GOSUB frame) + ldx 1,x ; get index variable descriptor + stx TMPTR1 ; save it + ldx VARDES ; get desired index descriptor + beq LAC16 ; brif NULL - we found something + cmpx TMPTR1 ; does this one match? + beq LAC1A ; brif so + ldx TEMPTR ; get back frame pointer + abx ; move to next entry + bra LABFB ; check next block of data +LAC16 ldx TMPTR1 ; get index variable of this frame + stx VARDES ; set it as the one found +LAC1A ldx TEMPTR ; get matching frame pointer + tsta ; set Z if FOR/NEXT + rts +; This is a block copy routine which copies from top to bottom. It's not clear that the use of +; this routine actually saves any ROM space compared to just implementing the copies directly +; once all the marshalling to set up the parameter variables is taken into account. +LAC1E bsr LAC37 ; check to see if stack collides with D +LAC20 ldu V41 ; point to destination + leau 1,u ; offset for pre-dec + ldx V43 ; point to source + leax 1,x ; offset for pre-dec +LAC28 lda ,-x ; get source byte + pshu a ; store at destination (sta ,-u would be less weird) + cmpx V47 ; at the bottom of the copy? + bne LAC28 ; brif not + stu V45 ; save final destination address +LAC32 rts +; Check for 2*B (0 <= B <= 127) bytes for free memory +LAC33 clra ; zero extend + aslb ; times 2 (loses bit 7 of B) + addd ARYEND ; add to top of used memory +LAC37 addd #STKBUF ; add a fudge factor for interpreter operation + bcs LAC44 ; brif >65535! + sts BOTSTK ; get current stack pointer + cmpd BOTSTK ; is our new address above that? + blo LAC32 ; brif not - no error +LAC44 ldb #6*2 ; raise OM error +; The error servicing routine +LAC46 jsr RVEC16 ; do the RAM hook dance (ON ERROR reserved hook) +LAC49 jsr RVEC17 ; do the RAM hook dance again + jsr LA7E9 ; turn off tape + jsr LA974 ; disable sound + jsr LAD33 ; reset stack, etc. + clr DEVNUM ; reset output to screen + jsr LB95C ; do a newline + jsr LB9AF ; send a ? + ldx #LABAF ; point to error table + abx ; offset to correct message + bsr LACA0 ; send a char from X + bsr LACA0 ; send another char from X + ldx #LABE1-1 ; point to "ERROR" message +LAC68 jsr LB99C ; print ERROR message (or BREAK) + lda CURLIN ; are we in immediate mode? + inca + beq LAC73 ; brif not - go to immediate mode + jsr LBDC5 ; print "IN ****" +; This is the immediate mode loop +LAC73 jsr LB95C ; do a newline if needed +LAC76 ldx #LABEE-1 ; point to prompt (without leading CR) + jsr LB99C ; show prompt +LAC7C jsr LA390 ; read an input line + ldu #0xffff ; flag immediate mode + stu CURLIN + bcs LAC7C ; brif we ended on BREAK - just go for another line + tst CINBFL ; EOF? + lbne LA4BF ; brif so + stx CHARAD ; save start of input line as input pointer + jsr GETNCH ; get character from input line + beq LAC7C ; brif no input + bcs LACA5 ; brif numeric - adding or removing a line number + ldb #2*24 ; code for "direct statement in file" + tst DEVNUM ; keyboard input? + bne LAC46 ; brif not - complain about direct statement + jsr LB821 ; go tokenize the input line + jmp LADC0 ; go execute the newly tokenized line +LACA0 lda ,x+ ; get character and advance pointer + jmp LB9B1 ; output it +LACA5 jsr LAF67 ; convert line number to binary + ldx BINVAL ; get converted number + stx LINHDR ; put it before the line we just read + jsr LB821 ; tokenize the input line + stb TMPLOC ; save line length + bsr LAD01 ; find where the line should be in the program + bcs LACC8 ; brif the line number isn't already present + ldd V47 ; get address where the line is in the program + subd ,x ; get the difference between here and the end of the line (negative) + addd VARTAB ; subtract line length from the end of the program + std VARTAB ; save new end of program address + ldu ,x ; get start of next line +LACC0 pulu a ; get source byte (lda ,u+ would be less weird) + sta ,x+ ; move it down + cmpx VARTAB ; have we moved everything yet? + bne LACC0 ; brif not +LACC8 lda LINBUF ; see if there is actually a line to input + beq LACE9 ; brif not - we just needed to remove the line + ldd VARTAB ; get current end of program + std V43 ; set as source pointer + addb TMPLOC ; add in the length of the new line + adca #0 + std V41 ; save destination pointer + jsr LAC1E ; make sure there's enough room and then make a hole for the new line + ldu #LINHDR-2 ; point to the line (well, 4 bytes before it, incl line number and fake next line pointer) +LACDD pulu a ; get byte from new line (lda ,u+ would be less weird) + sta ,x+ ; stow it + cmpx V45 ; at the end of the hole we just made? + bne LACDD ; brif not + ldx V41 ; get save new top of program address + stx VARTAB +LACE9 bsr LAD21 ; reset variables, etc. + bsr LACEF ; adjust next line pointers + bra LAC7C ; go read another input line +; Recompute next line pointers +LACEF ldx TXTTAB ; point to start of program +LACF1 ldd ,x ; get address of next line + beq LAD16 ; brif end of program + leau 4,x ; move past pointer and line number +LACF7 lda ,u+ ; are we at the end of the line? + bne LACF7 ; brif not + stu ,x ; save new next line pointer + ldx ,x ; point to next line + bra LACF1 ; process the next line +; Find a line in the program +LAD01 ldd BINVAL ; get desired line number + ldx TXTTAB ; point to start of program +LAD05 ldu ,x ; get address of next line + beq LAD12 ; brif end of program + cmpd 2,x ; do we have a match? + bls LAD14 ; brif our search number is <= the number here + ldx ,x ; move to next line + bra LAD05 ; check another line +LAD12 orcc #1 ; set C for not found +LAD14 stx V47 ; save address of matching line *or* line just after where it would have been +LAD16 rts +; NEW command +; This routine has multiple entry points used for various "levels" of NEW +NEW bne LAD14 ; brif there was input given; should be LAD16! +LAD19 ldx TXTTAB ; point to start of program + clr ,x+ ; blank out program (with NULL next line pointer) + clr ,x+ + stx VARTAB ; save end of program +LAD21 ldx TXTTAB ; get start of program + jsr LAEBB ; put input pointer there +LAD26 ldx MEMSIZ ; reset string space + stx STRTAB + jsr RESTOR ; reset DATA pointer + ldx VARTAB ; clear out scalars and arrays + stx ARYTAB + stx ARYEND +LAD33 ldx #STRSTK ; reset the string stack + stx TEMPPT + ldx ,s ; get return address (we're going to reset the stack) + lds FRETOP ; reset the stack to top of memory + clr ,-s ; put stopper so FOR/NEXT search will actually stop here + clr OLDPTR ; reset "CONT" state + clr OLDPTR+1 + clr ARYDIS ; un-disable arrays + jmp ,x ; return to original caller +; FOR command +FOR lda #0x80 ; disable array parsing + sta ARYDIS + jsr LET ; assign start value to index + jsr LABF9 ; search stack for matching FOR/NEXT frame + leas 2,s ; lose return address + bne LAD59 ; brif variable not already being used + ldx TEMPTR ; get address of matched data + leas b,x ; move stack pointer to the end of it (B is set to 18 in the stack search) +LAD59 ldb #9 ; is there room for 18 bytes in memory? + jsr LAC33 + jsr LAEE8 ; get address of the end of this statement in X + ldd CURLIN ; get line number + pshs x,b,a ; save next line address and current line number + ldb #0xa5 ; make sure we have TO + jsr LB26F + jsr LB143 ; make sure we have a numeric index + jsr LB141 ; evaluate terminal condition value + ldb FP0SGN ; pack FPA0 in place + orb #0x7f + andb FPA0 + stb FPA0 + ldy #LAD7F ; where to come back to + jmp LB1EA ; stash terminal condition on the stack +LAD7F ldx #LBAC5 ; point to FP 1.0 (default step) + jsr LBC14 ; unpack it to FPA0 + jsr GETCCH ; get character after the terminal + cmpa #0xa9 ; is it STEP? + bne LAD90 ; brif not + jsr GETNCH ; eat STEP + jsr LB141 ; evaluate step condition +LAD90 jsr LBC6D ; get "status" of FPA0 + jsr LB1E6 ; stash FPA0 on the stack (for step value) + ldd VARDES ; get variable descriptor pointer + pshs d ; put that on the stack too + lda #0x80 ; flag the frame as a FOR/NEXT frame + pshs a +; Main command interpretation loop +LAD9E jsr RVEC20 ; do the RAM hook dance + andcc #0xaf ; make sure interrupts are running + bsr LADEB ; check for BREAK/pause + ldx CHARAD ; get input pointer + stx TINPTR ; save input pointer for start of line + lda ,x+ ; get current input character + beq LADB4 ; brif end of line - move to another line + cmpa #': ; end of statement? + beq LADC0 ; brif so - keep processing +LADB1 jmp LB277 ; raise a syntax error +LADB4 lda ,x++ ; get MSB of next line pointer and skip past pointer + sta ENDFLG + beq LAE15 ; brif MSB of next line address is 0 (do END) + ldd ,x+ ; get line number but only advance one + std CURLIN ; set current line number + stx CHARAD ; set input pointer to one before line text +LADC0 jsr GETNCH ; move past statement separator or to first character in line + bsr LADC6 ; process a command + bra LAD9E ; handle next statement or line +LADC6 beq LAE40 ; return if end of statement + tsta ; is it a token? + lbpl LET ; brif not - do a LET + cmpa #0xa3 ; above SKIPF? + bhi LADDC ; brif so + ldx COMVEC+3 ; point to jump table + lsla ; two bytes per entry (loses the token bias) + tfr a,b ; put it in B for unsigned ABX + abx + jsr GETNCH ; move past token + jmp [,x] ; transfer control to the handler (which will return to the main loop) +LADDC cmpa #0xb4 ; is it a non-executable token? + bls LADB1 ; brif so + jmp [COMVEC+13] ; transfer control to ECB command handler +; RESTORE command +RESTOR ldx TXTTAB ; point to beginning of the program + leax -1,x ; move back one (to compensate for "GETNCH") +LADE8 stx DATPTR ; save as new data pointer + rts +; BREAK check +LADEB jsr LA1C1 ; read keyboard + beq LADFA ; brif no key down +LADF0 cmpa #3 ; BREAK? + beq STOP ; brif so - do a STOP + cmpa #0x13 ; pause (SHIFT-@)? + beq LADFB ; brif so + sta IKEYIM ; cache key for later INKEY$ so break check doesn't break INKEY$ +LADFA rts +LADFB jsr KEYIN ; read keyboard + beq LADFB ; brif no key down + bra LADF0 ; process pressed key in case BREAK or SHIFT-@ again +; END command +ENDTOK jsr LA426 ; close files + jsr GETCCH ; re-get input character + bra LAE0B +; STOP command +STOP orcc #1 ; flag "STOP" +LAE0B bne LAE40 ; brif not end of statement + ldx CHARAD ; save current input pointer + stx TINPTR +LAE11 ror ENDFLG ; save END/STOP flag (C) + leas 2,s ; lose return address +LAE15 ldx CURLIN ; get current input line (end of program comes here) + cmpx #0xffff ; immediate mode? + beq LAE22 ; brif so + stx OLDTXT ; save line where we stopped executing + ldx TINPTR ; get input pointer + stx OLDPTR ; save location where we stopped executing +LAE22 clr DEVNUM ; reset to screen/keyboard + ldx #LABF2-1 ; point to BREAK message + tst ENDFLG ; are we doing "BREAK"? + lbpl LAC73 ; brif not + jmp LAC68 ; go do the BREAK message and return to main loop +; CONT command +CONT bne LAE40 ; brif not end of statement + ldb #2*16 ; code for can't continue + ldx OLDPTR ; get saved execution pointer + lbeq LAC46 ; brif no saved pointer - raise CN error + stx CHARAD ; reset input pointer + ldx OLDTXT ; reset current line number + stx CURLIN +LAE40 rts +; CLEAR command +CLEAR beq LAE6F ; brif no argument + jsr LB3E6 ; evaluate string space size + pshs d ; save it + ldx MEMSIZ ; get memory size (top of memory) + jsr GETCCH ; is there anything after the string space size? + beq LAE5A ; brif not + jsr LB26D ; force a comma + jsr LB73D ; get top of memory value in X + leax -1,x ; move back one (top of cleared space) + cmpx TOPRAM ; is it within the memory available? + bhi LAE72 ; brif higher than top of memory - OM error +LAE5A tfr x,d ; so we can do math for checking memory usage + subd ,s++ ; subtract out string space value + bcs LAE72 ; brif less than 0 + tfr d,u ; U is bottom of cleared space + subd #STKBUF ; also account for slop space + bcs LAE72 ; brif less than 0 + subd VARTAB ; is there still room for the program? + blo LAE72 ; brif not + stu FRETOP ; set top of free memory + stx MEMSIZ ; set size of usable memory +LAE6F jmp LAD26 ; erase variables, etc. +LAE72 jmp LAC44 ; raise OM error +; RUN command +RUN jsr RVEC18 ; do the RAM hook dance + jsr LA426 ; close any open files + jsr GETCCH ; is there a line number + lbeq LAD21 ; brif no line number - start from beginning + jsr LAD26 ; clear variables, etc. + bra LAE9F ; "GOTO" the line number +; GO command (GOTO and GOSUB) +GO tfr a,b ; save TO/SUB +LAE88 jsr GETNCH ; eat the TO/SUB token + cmpb #0xa5 ; TO? + beq LAEA4 ; brif GOTO + cmpb #0xa6 ; SUB? + bne LAED7 ; brif not + ldb #3 ; room for 6 bytes? + jsr LAC33 + ldu CHARAD ; get input pointer + ldx CURLIN ; get line number + lda #0xa6 ; flag for GOSUB frame + pshs u,x,a ; set stack frame +LAE9F bsr LAEA4 ; do "GOTO" + jmp LAD9E ; go back to main loop +; Actual GOTO is here +LAEA4 jsr GETCCH ; get current input + jsr LAF67 ; convert number to binary + bsr LAEEB ; move input pointer to end of statement + leax 1,x ; point to start of next line + ldd BINVAL ; get desired line number + cmpd CURLIN ; is it beyond here? + bhi LAEB6 ; brif so + ldx TXTTAB ; start search at beginning of program +LAEB6 jsr LAD05 ; find line number + bcs LAED2 ; brif not found +LAEBB leax -1,x ; move to just before start of line + stx CHARAD ; reset input pointer +LAEBF rts +; RETURN command +RETURN bne LAEBF ; exit if argument given + lda #0xff ; set VARDES to an illegal value so we ignore FOR frames + sta VARDES + jsr LABF9 ; look for a GOSUB frame + tfr x,s ; reset stack + cmpa #0xa6-0x80 ; is it a GOSUB frame? + beq LAEDA ; brif so + ldb #2*2 ; code for RETURN without GOSUB + skip2 +LAED2 ldb #7*2 ; code for undefined line number + jmp LAC46 ; raise error +LAED7 jmp LB277 ; raise syntax error +LAEDA puls a,x,u ; get back saved line number and input pointer + stx CURLIN ; reset line number + stu CHARAD ; reset input pointer +; DATA command +DATA bsr LAEE8 ; move input pointer to end of statement + skip2 +; REM command (also ELSE) +REM bsr LAEEB ; move input pointer to end of line + stx CHARAD ; save new input pointer +LAEE7 rts +; Return end of statement (LAEE8) or line (AEEB) in X +LAEE8 ldb #': ; colon is statement terminator + skip1lda +LAEEB clrb ; make main terminator NUL + stb CHARAC ; save terminator + clrb ; end of line - always terminates + ldx CHARAD ; get input pointer +LAEF1 tfr b,a ; save secondary terminator + ldb CHARAC ; get main terminator + sta CHARAC ; save secondary +LAEF7 lda ,x ; get input character + beq LAEE7 ; brif end of line + pshs b ; save terminator + cmpa ,s+ ; does it match? + beq LAEE7 ; brif so - bail + leax 1,x ; move pointer ahead + cmpa #'" ; start of string? + beq LAEF1 ; brif so + inca ; functon token? + bne LAF0C ; brif not + leax 1,x ; skip second part of function token +LAF0C cmpa #0x85+1 ; IF? + bne LAEF7 ; brif not + inc IFCTR ; bump "IF" count + bra LAEF7 ; get check another input character +; IF command +IFTOK jsr LB141 ; evaluate condition + jsr GETCCH ; find out what's after the conditin + cmpa #0x81 ; GO? + beq LAF22 ; treat same as THEN + ldb #0xa7 ; make sure we have a THEN + jsr LB26F +LAF22 lda FP0EXP ; get true/false (false is 0) + bne LAF39 ; brif condition true + clr IFCTR ; reset IF counter +LAF28 bsr DATA ; skip over statement + tsta ; end of line? + beq LAEE7 ; brif so + jsr GETNCH ; get start of this statement + cmpa #0x84 ; ELSE? + bne LAF28 ; brif not + dec IFCTR ; is it a matching ELSE? + bpl LAF28 ; brif not - keep looking + jsr GETNCH ; eat the ELSE +LAF39 jsr GETCCH ; get current input + lbcs LAEA4 ; brif numeric - to a GOTO + jmp LADC6 ; let main loop interpret the next command +; ON command +ON jsr LB70B ; evaluate index expression +LAF45 ldb #0x81 ; make sure we have "GO" + jsr LB26F + pshs a ; save TO/SUB + cmpa #0xa6 ; SUB? + beq LAF54 ; brif so + cmpa #0xa5 ; TO? +LAF52 bne LAED7 ; brif not +LAF54 dec FPA0+3 ; are we at the right index? + bne LAF5D ; brif not + puls b ; get TO/SUB token + jmp LAE88 ; go do GOTO or GOSUB +LAF5D jsr GETNCH ; munch a character + bsr LAF67 ; parse line number + cmpa #', ; is there another line following? + beq LAF54 ; brif so - see if we're there yet + puls b,pc ; clean up TO/SUB token and return - we fell through +; Parse a line number +LAF67 ldx ZERO ; initialize line number accumulator to 0 + stx BINVAL +LAF6B bcc LAFCE ; brif not numeric + suba #'0 ; adjust to actual value of digit + sta CHARAC ; save digit + ldd BINVAL ; get accumulated number + cmpa #24 ; will this overflow? + bhi LAF52 ; brif so - raise syntax error + aslb ; times 2 + rola + aslb ; times 4 + rola + addd BINVAL ; times 5 + aslb ; times 10 + rola + addb CHARAC ; add in digit + adca #0 + std BINVAL ; save new accumulated number + jsr GETNCH ; fetch next character + bra LAF6B ; process next digit +; LET command (the LET keyword requires Extended Basic) +LET jsr LB357 ; evaluate destination variable + stx VARDES ; save descriptor pointer + ldb #0xb3 ; make sure we have = + jsr LB26F + lda VALTYP ; get destination variable type + pshs a ; save it for later + jsr LB156 ; evaluate the expression to assign + puls a ; get back original variable type + rora ; put type in C + jsr LB148 ; make sure the current result matches the type + lbeq LBC33 ; bri fnumeric - copy FPA0 to variable +LAFA4 ldx FPA0+2 ; point to descriptor of replacement string + ldd FRETOP ; get bottom of string space + cmpd 2,x ; is the string already in string space? + bhs LAFBE ; brif so + cmpx VARTAB ; is the descriptor in variable space? + blo LAFBE ; brif not +LAFB1 ldb ,x ; get length of string + jsr LB50D ; allocate space for this string + ldx V4D ; get descriptor pointer back + jsr LB643 ; copy string into string space + ldx #STRDES ; point to temporary string descriptor +LAFBE stx V4D ; save descriptor pointer + jsr LB675 ; remove string from string stack if appropriate + ldu V4D ; get back replacement descriptor + ldx VARDES ; get target descriptor + pulu a,b,y ; get string length (A) and data pointer (Y) + sta ,x ; save new length + sty 2,x ; save new pointer +LAFCE rts +; READ and INPUT commands. +LAFCF fcc '?REDO' ; The ?REDO message + fcb 0x0d,0x00 +LAFD6 ldb #2*17 ; bad file data code + tst DEVNUM ; are we reading from the keyboard? + beq LAFDF ; brif so +LAFDC jmp LAC46 ; raise the error +LAFDF lda INPFLG ; are we doing INPUT? + beq LAFEA ; brif so + ldx DATTXT ; get line number where the DATA statement happened + stx CURLIN ; set current line number to that so can report the correct location + jmp LB277 ; raise a syntax error on bad data +LAFEA ldx #LAFCF-1 ; show the ?REDO if we're doing INPUT + jsr LB99C + ldx TINPTR ;* reset input pointer to start of statement (this will cause the + stx CHARAD ;* INPUT statement to be re-executed + rts +INPUT ldb #11*2 ; code for illegal direct statement + ldx CURLIN ; are we in immediate mode? + leax 1,x + beq LAFDC ; brif so - raise ID error + bsr LB002 ; go do the INPUT thing + clr DEVNUM ; reset device to screen/keyboard + rts +LB002 cmpa #'# ; is there a device number? + bne LB00F ; brif not + jsr LA5A5 ; parse device number + jsr LA3ED ; make sure it's valid for input + jsr LB26D ; make sure we have a comma after the device number +LB00F cmpa #'" ; is there a prompt string? + bne LB01E ; brif not + jsr LB244 ; parse the prompt string + ldb #'; ; make sure we have a semicolon after the prompt + jsr LB26F + jsr LB99F ; print the prompt +LB01E ldx #LINBUF ; point to line input buffer + clr ,x ; NUL first byte to indicate no data + tst DEVNUM ; is it keyboard input? + bne LB049 ; brif not + bsr LB02F ; read a line from the keyboard + ldb #', ; put a comma at the start of the buffer + stb ,x + bra LB049 ; go process some input +LB02F jsr LB9AF ; send a ? + jsr LB9AC ; send a space +LB035 jsr LA390 ; read input from the keyboard + bcc LB03F ; brif not BREAK + leas 4,s ; clean up stack +LB03C jmp LAE11 ; go process BREAK +LB03F ldb #2*23 ; input past end of file error code + tst CINBFL ; was it EOF? + bne LAFDC ; brif so - raise the error + rts +READ ldx DATPTR ; fetch current DATA pointer + skip1lda ; set A to nonzero (for READ) +LB049 clra ; set A to zero (for INPUT) + sta INPFLG ; record whether we're doing READ or INPUT + stx DATTMP ; save current input location +LB04E jsr LB357 ; evaluate a variable (destination of data) + stx VARDES ; save descriptor + ldx CHARAD ; save interpreter input pointer + stx BINVAL + ldx DATTMP ; get data pointer + lda ,x ; is there anything to read? + bne LB069 ; brif so + lda INPFLG ; is it INPUT? + bne LB0B9 ; brif not + jsr RVEC10 ; do the RAM hook dance + jsr LB9AF ; send a ? (so subsequent lines get ??) + bsr LB02F ; go read an input line +LB069 stx CHARAD ; save data pointer + jsr GETNCH ; fetch next data character + ldb VALTYP ; do we want a number? + beq LB098 ; brif so + ldx CHARAD ; get input pointer + sta CHARAC ; save initial character as the delimiter + cmpa #'" ; do we have a string delimiter? + beq LB08B ; brif so - use " as both delimiters + leax -1,x ; back up input if we don't have a delimiter + clra ; set delimiter to NUL (end of line) + sta CHARAC + jsr LA35F ; set up print parameters + tst PRTDEV ; is it a file type device? + bne LB08B ; brif so - use two NULs + lda #': ; use colon as one delimiter + sta CHARAC + lda #', ; and use comma as the other +LB08B sta ENDCHR ; save second terminator + jsr LB51E ; parse out the string + jsr LB249 ; move input pointer past the string + jsr LAFA4 ; assign the string to the variable + bra LB09E ; go see if there's more to read +LB098 jsr LBD12 ; parse a numeric string + jsr LBC33 ; assign the numbe to the variable +LB09E jsr GETCCH ; get current input character + beq LB0A8 ; brif end of line + cmpa #', ; check for comma + lbne LAFD6 ; brif not - we have bad data +LB0A8 ldx CHARAD ; get current data pointer + stx DATTMP ; save the data pointer + ldx BINVAL ; restore the interpreter input pointer + stx CHARAD + jsr GETCCH ; get current input from program + beq LB0D5 ; brif end of statement + jsr LB26D ; make sure there's a comma between variables + bra LB04E ; go read another item +LB0B9 stx CHARAD ; reset input pointer + jsr LAEE8 ; search for end of statement + leax 1,x ; move past end of statement + tsta ; was it end of line? + bne LB0CD ; brif not + ldb #2*3 ; code for out of data + ldu ,x++ ; get pointer to next line + beq LB10A ; brif end of program - raise OD error + ldd ,x++ ; get line number + std DATTXT ; record it for raising errors in DATA statements +LB0CD lda ,x ; do we have a DATA statement? + cmpa #0x86 + bne LB0B9 ; brif not - keep scanning + bra LB069 ; go process the input +LB0D5 ldx DATTMP ; get data pointer + ldb INPFLG ; were we doing READ? + lbne LADE8 ; brif so - save DATA pointer + lda ,x ; is there something after the input in the input buffer? + beq LB0E7 ; brif not - we consumed everything + ldx #LB0E8-1 ; print the ?EXTRA IGNORED message + jmp LB99C +LB0E7 rts +LB0E8 fcc '?EXTRA IGNORED' + fcb 0x0d,0x00 +; NEXT command +NEXT bne LB0FE ; brif argument given + ldx ZERO ; set to NULL descriptor pointer + bra LB101 ; go process "any index will do" +LB0FE jsr LB357 ; evaluate the variable +LB101 stx VARDES ; save the index we're looking for + jsr LABF9 ; search the stack for the matching frame + beq LB10C ; brif we found a matching frame + ldb #0 ; code for NEXT without FOR +LB10A bra LB153 ; raise the error +LB10C tfr x,s ; reset the stack to the start of the stack frame + leax 3,x ; point to the STEP value + jsr LBC14 ; copy the value to FPA0 + lda 8,s ; get step direction + sta FP0SGN ; save as sign of FPA0 + ldx VARDES ; point to index variable + jsr LB9C2 ; add (X) to FPA0 (steps the index) + jsr LBC33 ; save new value to the index + leax 9,s ; point to terminal condition + jsr LBC96 ; compare the new index value with the terminal + subb 8,s ; set B=0 if we hit the terminal (or passed it with nonzero step) + beq LB134 ; brif loop complete + ldx 14,s ; restore line number and input pointer to start of loop + stx CURLIN + ldx 16,s + stx CHARAD +LB131 jmp LAD9E ; return to interpretation loop +LB134 leas 18,s ; remove the frame from the stack + jsr GETCCH ; get character after the index + cmpa #', ; do we have more indexes? + bne LB131 ; brif not + jsr GETNCH ; munch the comma + bsr LB0FE ; go process another value +; NOTE: despite the BSR on the preceding line, execution of the NEXT command will not fall +; through this point, nor will the stack grow without bound. The BSR is required to make sure +; the stack is aligned properly for the stack search for the subsequent index variable. +; +; The following is the expression evaluation system. It has various entry points including for type +; checking. This really consists of two co-routines, one for evaluating operators and one for individual +; terms. However, it does some rather confusing stack operations so it's a bit difficult to follow +; just how some of this works. +; +; Evaluate numeric expression +LB141 bsr LB156 ; evaluate an expression +; TM error if string +LB143 andcc #0xfe ; clear C to indicate we want a number + skip2keepc +; TM error if numeric +LB146 orcc #1 ; set C to indicate we want a string +; TM error if: C = 1 and number, OR C = 0 and string +LB148 tst VALTYP ; set flags on the current value to (doesn't change C) + bcs LB14F ; brif we want a string + bpl LB0E7 ; brif we have a number (we want a number) + skip2 +LB14F bmi LB0E7 ; brif we have a string (we want a string) +LB151 ldb #12*2 ; code for TM error +LB153 jmp LAC46 ; raise the error +; The general expression evaluation entry point +LB156 bsr LB1C6 ; back up input pointer to compensate for GETNCH below +LB158 clra ; set operator precedence to 0 (no previous operator) + skip2 +LB15A pshs b ; save relational operator flags + pshs a ; save previous operator precedence + ldb #1 ; make sure we aren't overflowing the stack + jsr LAC33 + jsr LB223 ; go evaluate the first term +LB166 clr TRELFL ; flag no relational operators seen +LB168 jsr GETCCH ; get input character +LB16A suba #0xb2 ; token for > (lowest relational operator) + blo LB181 ; brif below relational operators + cmpa #3 ; there are three relational operators, is it one? + bhs LB181 ; brif not + cmpa #1 ; set C if > + rola ; shift C into bit 0 (4: <, 2: =, 1: >) + eora TRELFL ; flip the bit for this operator + cmpa TRELFL ; did the result get lower? + blo LB1DF ; brif so - we have a duplicate so raise an error + sta TRELFL ; save new operator flags + jsr GETNCH ; munch the operator + bra LB16A ; go see if we have another one +LB181 ldb TRELFL ; do we have a relational comparison? + bne LB1B8 ; brif so + lbcc LB1F4 ; brif the token is above the relational operators + adda #7 ; put operators starting at 0 + bhs LB1F4 ; brif we're above 0 - it's an operator, Jim + adca VALTYP ; add carry, numeric flag, and modified token number + lbeq LB60F ; brif we have string and A is + - do concatenation + adca #-1 ; restore operator number + pshs a ; save operator number + asla ; times 2 + adda ,s+ ; and times 3 (3 bytes per entry) + ldx #LAA51 ; point to operator pecedence and jump table + leax a,x ; point to correct entry +LB19F puls a ; get precedence of previous operation + cmpa ,x ; is hit higher (or same) than the current one? + bhs LB1FA ; brif so - we need to process that operator + bsr LB143 ; TM error if we have a string +LB1A7 pshs a ; save previous operation precedence + bsr LB1D4 ; push operator handler address and FPA0 onto the stack + ldx RELPTR ; get pointer to arithmetic/logical table entry for last operation + puls a ; get back precedence + bne LB1CE ; brif we had a relational operation + tsta ; check precedence of previous operation + lbeq LB220 ; brif end of expression + bra LB203 ; go handle operation +LB1B8 asl VALTYP ; get type of value to C + rolb ; mix it in to bit 0 of relational flags + bsr LB1C6 ; back up input pointer + ldx #LB1CB ; point to relational operator precedence and handler + stb TRELFL ; save relational comparison flags + clr VALTYP ; result will be numeric + bra LB19F ; to process the operation +LB1C6 ldx CHARAD ; get input pointer + jmp LAEBB ; back it up one and put it back +LB1CB fcb 0x64 ; precedence of relational comparison + fdb LB2F4 ; handler address for relational comparison +LB1CE cmpa ,x ; is last done operation higher (or same) precedence? + bhs LB203 ; brif so - go process it + bra LB1A7 ; go push things on the stack and process this operation otherwise +LB1D4 ldd 1,x ; get address of operatorroutine + pshs d ; save it + bsr LB1E2 ; push FPA0 onto the stack + ldb TRELFL ; get back relational operator flags + lbra LB15A ; go evaluate another operation +LB1DF jmp LB277 ; raise a syntax error +LB1E2 ldb FP0SGN ; get sign of FPA0 + lda ,x ; get precedence of this operation +LB1E6 puls y ; get back original caller + pshs b ; save sign +LB1EA ldb FP0EXP ; get exponent + ldx FPA0 ; get mantissa + ldu FPA0+2 + pshs u,x,b ; stow FPA0 sign and mantissa + jmp ,y ; return to caller +LB1F4 ldx ZERO ; point to dummy value + lda ,s+ ; get precedence of previous operation (and set flags) + beq LB220 ; brif end of expression +LB1FA cmpa #0x64 ; relational operation? + beq LB201 ; brif so + jsr LB143 ; type mismatch if string +LB201 stx RELPTR ; save pointer to operator routine +LB203 puls b ; get relational flags + cmpa #0x5a ; NOT operation? + beq LB222 ; brif so (it was unary) + cmpa #0x7d ; unary negation? + beq LB222 ; brif so + lsrb ; shift value type flag out of relational flags + stb RELFLG ; save relational operator flag + puls a,x,u ; get FP value back + sta FP1EXP ; set exponent and mantissa in FPA1 + stx FPA1 + stu FPA1+2 + puls b ; and the sign + stb FP1SGN + eorb FP0SGN ; set RESSGN if the two operand signs differ + stb RESSGN +LB220 ldb FP0EXP ; get exponent of FPA0 +LB222 rts ; return or transfer control to operator handler routine +LB223 jsr RVEC15 ; do the RAM hook dance + clr VALTYP ; set type to numeric + jsr GETNCH ; get first character in the term + bcc LB22F ; brif not numeric +LB22C jmp LBD12 ; parse a number (and return) +LB22F jsr LB3A2 ; set carry if not alpha + bcc LB284 ; brif alpha character (variable) + cmpa #'. ; decimal point? + beq LB22C ; brif so - evaluate number + cmpa #0xac ; minus? + beq LB27C ; brif so - process unary negation + cmpa #0xab ; plus? + beq LB223 ; brif so - ignore unary "posation" + cmpa #'" ; string delimiter? + bne LB24E ; brif not +LB244 ldx CHARAD ; get input pointer + jsr LB518 ; go parse the string +LB249 ldx COEFPT ; get address of end of string + stx CHARAD ; move input pointer past string + rts +LB24E cmpa #0xa8 ; NOT? + bne LB25F ; brif not + lda #0x5a ; precedence of unary NOT + jsr LB15A ; process the operand of NOT + jsr INTCNV ; convert to integer in D + coma ; do a bitwise complement + comb + jmp GIVABF ; resturn the result +LB25F inca ; is it a function token? + beq LB290 ; brif so +LB262 bsr LB26A ; only other legal thing must be a (expr) + jsr LB156 ; evaluate parentheticized expression +LB267 ldb #') ; force a ) + skip2 +LB26A ldb #'( ; force a ( + skip2 +LB26D ldb #', ; force a , +LB26F cmpb [CHARAD] ; does character match? + bne LB277 ; brif not + jmp GETNCH ; each the character and return the next +LB277 ldb #2*1 ; raise syntax error + jmp LAC46 +LB27C lda #0x7d ; unary negation precedence + jsr LB15A ; evaluate argument + jmp LBEE9 ; flip sign of FPA0 and return +LB284 jsr LB357 ; evaluate variable +LB287 stx FPA0+2 ; save descriptor address in FPA0 + lda VALTYP ; test variable type + bne LB222 ; brif string - we're done + jmp LBC14 ; copy FP number from (X) into FPA0 +LB290 jsr GETNCH ; get the actual token number + tfr a,b ; save it (for offsetting X) + lslb ; two bytes per jump table entry (and lose high bit) + jsr GETNCH ; eat the token byte + cmpb #2*19 ; is it a valid token for Color Basic? + bls LB29F ; brif so + jmp [COMVEC+18] ; transfer control to Extended Basic if not +LB29F pshs b ; save jump table offset + cmpb #2*14 ; does it expect a numeric argument? + blo LB2C7 ; brif so + cmpb #2*18 ; does it need no arguments? + bhs LB2C9 ; brif so + bsr LB26A ; force a ( + lda ,s ; get token value + cmpa #2*17 ; is it POINT? + bhs LB2C9 ; brif so + jsr LB156 ; evaluate first argument string + bsr LB26D ; force a comma + jsr LB146 ; TM error if string + puls a ; get token value + ldu FPA0+2 ; get string descriptor + pshs u,a ; now we save the first string argument and the token value + jsr LB70B ; evaluate first numeric argument + puls a ; get back token value + pshs b,a ; save second argument and token value + fcb 0x8e ; opcode of LDX immediate (skips two bytes) +LB2C7 bsr LB262 ; force a ( +LB2C9 puls b ; get offset + ldx COMVEC+8 ; get jump table pointer + abx ; add offset into table + jsr [,x] ; go process function + jmp LB143 ; make sure result is numeric +; operator OR +LB2D4 skip1lda ; set flag to nonzero to signal OR +; operator AND +LB2D5 clra ; set flag to zero to signal AND + sta TMPLOC ; save AND/OR flag + jsr INTCNV ; convert second argument to intenger + std CHARAC ; save it + jsr LBC4A ; move first argument to FPA0 + jsr INTCNV ; convert first argument to integer + tst TMPLOC ; is it AND or OR? + bne LB2ED ; brif OR + anda CHARAC ; do the bitwise AND + andb ENDCHR + bra LB2F1 ; finish up +LB2ED ora CHARAC ; do the bitwise OR + orb ENDCHR +LB2F1 jmp GIVABF ; return integer result +; relational comparision operators +LB2F4 jsr LB148 ; TM error if type mismatch + BNE LB309 ; brif we have a string comparison + lda FP1SGN ; pack FPA1 + ora #0x7f + anda FPA1 + sta FPA1 + ldx #FP1EXP ; point to packed FPA1 + jsr LBC96 ; compare FPA0 to FPA1 + bra LB33F ; handle truth comparison +LB309 clr VALTYP ; the result of a comparison is always a number + dec TRELFL ; remove the string flag from the comparison data + jsr LB657 ; get string details for second argument + stb STRDES ; save them in the temporary string descriptor + stx STRDES+2 + ldx FPA1+2 ; get pointer to first argument descriptor + jsr LB659 ; get string details for second argument + lda STRDES ; get length of second argument + pshs b ; save length of first argument + suba ,s+ ; now A is the difference in string lengths + beq LB328 ; brif string lengths are equal + lda #1 ; flag for second argument is longer than first + bcc LB328 ; brif second string is longer than first + ldb STRDES ; get length of second string (shorter) + nega ; invert default comparison result +LB328 sta FP0SGN ; save default truth flag + ldu STRDES+2 ; get pointer to start of second string + incb ; compensate for DECB +LB32D decb ; have we compared everything? + bne LB334 ; brif not + ldb FP0SGN ; get default truth value + bra LB33F ; decide comparison truth +LB334 lda ,x+ ; get byte from first argument + cmpa ,u+ ; compare with second argument + beq LB32D ; brif equal - keep comparing + ldb #0xff ; negative if first string is > second + bcc LB33F ; brif string A > string B + negb ; invert result +LB33F addb #1 ; convert to 0,1,2 + rolb ; shift left - now it's 4,2,1 for <, =, > + andb RELFLG ; keep only the truth we care about + beq LB348 ; brif no matching bits - it's false + ldb #0xff ; set true +LB348 jmp LBC7C ; convert result to FP and return it +; DIM command +LB34B jsr LB26D ; make sure there's a comma between variables +DIM ldb #1 ; flag that we're dimensioning + bsr LB35A ; go allocate the variable + jsr GETCCH ; are we done? + bne LB34B ; brif not + rts +; This routine parses a variable. For scalars, it will return a NULL string or 0 value number +; if it is called from "evaluate term". Otherwise, it allocates the variable. For arrays, it will +; allocate a default sized array if dimensioning is not underway and then attempt to look up +; the requested coordinates in that array. Otherwise, it will allocate an array based on the +; specified dimension values. +LB357 clrb ; flag that we're not setting up an array + jsr GETCCH +LB35A stb DIMFLG ; save dimensioning flag + sta VARNAM ; save first character of variable name + jsr GETCCH ; get input character (why? we already have it) + bsr LB3A2 ; set carry if not alpha + lbcs LB277 ; brif our variable doesn't start with a letter + clrb ; default second variable character to NUL + stb VALTYP ; set value type to numeric + jsr GETNCH ; get second character + bcs LB371 ; brif numeric - numbers are allowed + bsr LB3A2 ; set carry if not alpha + bcs LB37B ; brif not alpha +LB371 tfr a,b ; save set second character of variable name +LB373 jsr GETNCH ; get an input character + bcs LB373 ; brif numeric - still in variable name + bsr LB3A2 ; set carry if not alpha + bcc LB373 ; brif alpha - still in variable name +LB37B cmpa #'$ ; do we have the string sigil? + bne LB385 ; brif not + com VALTYP ; set value type to string + addb #0x80 ; set bit 7 of second variable character to indicate string + jsr GETNCH ; eat the sigil +LB385 stb VARNAM+1 ; save second variable name character + ora ARYDIS ; merge array disable flag (will set bit 7 of input character if no arrays) + suba #'( ; do we have a subscript? + lbeq LB404 ; brif so + clr ARYDIS ; disable the array disable flag - it's single use + ldx VARTAB ; point to the start of the variable table + ldd VARNAM ; get variable name +LB395 cmpx ARYTAB ; are we at the top of the variable table? + beq LB3AB ; brif so + cmpd ,x++ ; does the variable name match (and move pointer to variable data) + beq LB3DC ; brif so + leax 5,x ; move to next table entry + bra LB395 ; see if we have a match +; Set carry if not upper case alpha +LB3A2 cmpa #'A ; set C if less than A + bcs LB3AA ; brif less than A + suba #'Z+1 ; set C if greater than Z + suba #-('Z+1) +LB3AA rts +LB3AB ldx #ZERO ; point to empty location (NULL/0 value) + ldu ,s ; get caller address + cmpu #LB287 ; coming from "evaluate term"? + beq LB3DE ; brif so - don't allocate + ldd ARYEND ; get end of arrays + std V43 ; save as top of source block + addd #7 ; 7 bytes per scalar entry + std V41 ; save as top of destination block + ldx ARYTAB ; get bottom of arrays + stx V47 ; save as bottom of source block + jsr LAC1E ; move the arrays up to make a hole + ldx V41 ; get new top of arrays + stx ARYEND ; set new end of arrays + ldx V45 ; get bottom of destination block + stx ARYTAB ; set as new start of arrays + ldx V47 ; get old end of variables + ldd VARNAM ; get name of variable + std ,x++ ; set variable name and advance X to the value + clra ; zero out the variable value + clrb + std ,x + std 2,x + sta 4,x +LB3DC stx VARPTR ; save descriptor address of return value +LB3DE rts +; Various integer conversion routines +LB3DF fcb 0x90,0x80,0x00,0x00,0x00 ; FP constant -32768 +LB3E4 jsr GETNCH ; fetch input character +LB3E6 jsr LB141 ; evaluate numeric expression +LB3E9 lda FP0SGN ; get sign of value + bmi LB44A ; brif negative (raise FC error) +INTCNV jsr LB143 ; TM error if string + lda FP0EXP ; get exponent + cmpa #0x90 ; is it within the range for a 16 bit integer? + blo LB3FE ; brif smaller than 32768 + ldx #LB3DF ; point to -32678 constant + jsr LBC96 ; is FPA0 equal to -32768? + bne LB44A ; brif not - magnitude is too far negative +LB3FE jsr LBCC8 ; move binary point to the right of FPA0 and correct sign + ldd FPA0+2 ; get the resulting integer + rts +LB404 ldd DIMFLG ; get dimensioning flag and variable type + pshs b,a ; save them (to avoid issues while evaluating dimension values) + nop ; dead space caused by 1.2 revision + clrb ; reset dimension counter +LB40A ldx VARNAM ; get variable name + pshs x,b ; save dimension counter and variable name + bsr LB3E4 ; evaluate a dimension value (and skip either ( or ,) + puls b,x,y ; get variable name, dimension counter, and dimensioning/type flag + stx VARNAM ; restore variable name + ldu FPA0+2 ; get dimension size/index + pshs u,y ; save dimension size and dimensioning/type flag + incb ; bump dimension counter + jsr GETCCH ; get what's after the dimension count + cmpa #', ; do we have another dimension? + beq LB40A ; brif so - parse it + stb TMPLOC ; save dimension counter + jsr LB267 ; make sure we have a ) + puls a,b ; get back variable type and dimensioning flag + std DIMFLG ; restore variable type and dimensioning flag + ldx ARYTAB ; get start of arrays +LB42A cmpx ARYEND ; are we at the end of the array table + beq LB44F ; brif so + ldd VARNAM ; get variable name + cmpd ,x ; does it match? + beq LB43B ; brif so + ldd 2,x ; get length of this array + leax d,x ; move to next array + bra LB42A ; go check another entry +LB43B ldb #2*9 ; code for redimensioned array error + lda DIMFLG ; are we dimensioning? + bne LB44C ; brif so - raise error + ldb TMPLOC ; get number of dimensions given + cmpb 4,x ; does it match? + beq LB4A0 ; brif so +LB447 ldb #8*2 ; raise "bad subscript" + skip2 +LB44A ldb #4*2 ; raise "illegal function call" +LB44C jmp LAC46 ; raise error +LB44F ldd #5 ; 5 bytes per array entry + std COEFPT ; initialize array size to entry size + ldd VARNAM ; get variable name + std ,x ; set array name + ldb TMPLOC ; get dimension count + stb 4,x ; set dimension count + jsr LAC33 ; make sure we haven't overflowed memory + stx V41 ; save array descriptor address +LB461 ldb #11 ; default dimension value (zero-based, gives max index of 10) + clra ; zero extend (??? why not LDD above?) + tst DIMFLG ; are we dimensioning? + beq LB46D ; brif not + puls a,b ; get dimension size + addd #1 ; account for zero based indexing +LB46D std 5,x ; save dimension size + bsr LB4CE ; multiply by accumulated array size + std COEFPT ; save new array size + leax 2,x ; move to next dimension + dec TMPLOC ; have we done all dimensions? + bne LB461 ; brif not + stx TEMPTR ; save end of array descriptor (minus 5) + addd TEMPTR ; add total size of array to address of descriptor + lbcs LAC44 ; brif it overflows memory + tfr d,x ; save end of array for later + jsr LAC37 ; does array fit in memory? + subd #STKBUF-5 ; subtract out the "stack fudge factor" but add 5 to the result + std ARYEND ; save new end of arrays + clra ; set up for clearing +LB48C leax -1,x ; move back one + sta 5,x ; blank out a byte in the array data + cmpx TEMPTR ; have we reached the array header? + bne LB48C ; brif not + ldx V41 ; get address of start of descriptor + lda ARYEND ; get MSB of end of array back (B still has LSB) + subd V41 ; subtract start of descriptor + std 2,x ; save length of array in array header + lda DIMFLG ; are we dimensioning? + bne LB4CD ; brif so - we're done +LB4A0 ldb 4,x ; get number of dimensions + stb TMPLOC ; initialize counter + clra ; initialize accumulated offset + clrb +LB4A6 std COEFPT ; save accumulated offset + puls a,b ; get desired index + std FPA0+2 ; save it + cmpd 5,x ; is it in range for this dimension? + bhs LB4EB ; brif not + ldu COEFPT ; get accumulated offset + beq LB4B9 ; brif first dimension + bsr LB4CE ; multiply accumulated offset by dimension length + addd FPA0+2 ; add in offset into this dimension +LB4B9 leax 2,x ; move to next dimension in header + dec TMPLOC ; done all dimensions? + bne LB4A6 ; brif not + std ,--s ; save D for multiply by 5 (should be pshs d) + aslb ; times 2 + rola + aslb ; times 4 + rola + addd ,s++ ; times 5 + leax d,x ; add in offset from start of array data + leax 5,x ; offset to end of header + stx VARPTR ; save pointer to element data +LB4CD rts +; multiply 16 bit number at 5,x by the 16 bit number in COEFPT; return result in D; BS error if carry +LB4CE lda #16 ; 16 shifts to do a multiply + sta V45 ; save shift counter + ldd 5,x ; get multiplier + std BOTSTK ; save it + clra ; zero out product + clrb +LB4D8 aslb ; shift product left + rola + bcs LB4EB ; brif we have a carry + asl COEFPT+1 ; shift other factor left + rol COEFPT + bcc LB4E6 ; brif no carry - this bit position is 0 + addd BOTSTK ; add in multiplier at this bit position + bcs LB4EB ; brif carry - do an error +LB4E6 dec V45 ; have we done all 16 bits? + bne LB4D8 ; brif not + rts +LB4EB jmp LB447 ; raise a BS error +; MEM function +; BUG: this doesn't account for the STKBUF fudge factor used for memory availability checks +MEM tfr s,d ; get stack pointer where we can do math + subd ARYEND ; calculate number of bytes between the stack and the top of arrays + skip1 ; return result +; Convert unsigned value in B to FP +LB4F3 clra ; zero extend +; Convert signed value in D to FP +GIVABF clr VALTYP ; set value type to numeric + std FPA0 ; save value in FPA0 + ldb #0x90 ; exponent for top two bytes to be an integer + jmp LBC82 ; finish conversion to integer +; STR$ function +STR jsr LB143 ; make sure we have a number + ldu #STRBUF+2 ; convert FP number to string in temporary string buffer + jsr LBDDC + leas 2,s ; don't return to the function evaluator (which will do a numeric type check) + ldx #STRBUF+1 ; point to number string + bra LB518 ; to stash the string in string space and return to the "evaluate term" caller +; Reserve B bytes of string space. Return start in X and FRESPC +LB50D stx V4D ; save X somewhere in case the caller needs it +LB50F bsr LB56D ; allocate string space +LB511 stx STRDES+2 ; save pointer to allocated space in the temporary descriptor + stb STRDES ; save length in the temporary descriptor + rts +LB516 leax -1,x ; move pointer back one (to compensate for the increment below) +; Scan from X until either NUL or one of the string terminators is found +LB518 lda #'" ; set terminator to be string delimiter +LB51A sta CHARAC ; set both delimiters + sta ENDCHR +LB51E leax 1,x ; move to next character + stx RESSGN ; save start of string + stx STRDES+2 ; save start of string in the temporary string descriptor + ldb #-1 ; initialize length counter to -1 (compensate for initial INCB) +LB526 incb ; bump string length + lda ,x+ ; get character from string + beq LB537 ; brif end of line + cmpa CHARAC ; is it delimiter #1? + beq LB533 ; brif so + cmpa ENDCHR ; is it delimiter #2? + bne LB526 ; brif not - keep scanning +LB533 cmpa #'" ; string delimiter? + beq LB539 ; brif so - don't move pointer back +LB537 leax -1,x ; move pointer back (so we don't consume the delimiter) +LB539 stx COEFPT ; save end of string address + stb STRDES ; save string length + ldu RESSGN ; get start of string + cmpu #STRBUF+2 ; is it at the start of the string buffer? + bhi LB54C ; brif so - don't copy it to string space + bsr LB50D ; allocate string space + ldx RESSGN ; point to beginning of the string + jsr LB645 ; copy string data (B bytes) from (X) to (FRESPC) +; Put temporary string descriptor on the string stack +LB54C ldx TEMPPT ; get top of string stack + cmpx #CFNBUF ; is the string stack full? + bne LB558 ; brif not + ldb #15*2 ; code for "string formula too complex" +LB555 jmp LAC46 ; raise error +LB558 lda STRDES ; get string length + sta 0,x ; save it in the string stack descriptor + ldd STRDES+2 ; get string data pointer + std 2,x ; save in string stack descriptor + lda #0xff ; set value type to string + sta VALTYP + stx LASTPT ; set pointer to last used entry on the string stack + stx FPA0+2 ; set pointer to descriptor in the current evaluation value + leax 5,x ; advance string stack pointer + stx TEMPPT + rts +; Reserve B bytes in string space. If there isn't enough space, try compacting string space and +; then try the allocation again. If it still fails, raise OS error. +LB56D clr GARBFL ; flag that compaction not yet done +LB56F clra ; zero extend the length + pshs d ; save requested string length + ldd STRTAB ; get current bottom of strings + subd ,s+ ; calculate new bottom of strings and remove zero extension + cmpd FRETOP ; does the string fit? + blo LB585 ; brif not - try compaction + std STRTAB ; save new bottom of strings + ldx STRTAB ; get bottom of strings + leax 1,x ; now X points to the real start of the allocated space + stx FRESPC ; save the string pointer + puls b,pc ; restore length and return +LB585 ldb #2*13 ; code for out of string space + com GARBFL ; have we compacted string space yet? + beq LB555 ; brif so - raise error + bsr LB591 ; compact string space + puls b ; get back string length + bra LB56F ; go try allocation again +; Compact string space +; This is an O(n^2) algorithm. It first searches all extant strings for the highest address data pointer +; that hasn't already been moved into the freshly compacted string space. If then moves that string data +; up to the highest address it can go to. It repeats this process over and over until it finds no string +; that isn't already in the compacted space. While doing this, it has to search all strings on the string +; stack (this is why the string stack is needed - so we can track anonymous strings), all scalar string +; variables, and *every* entry in every string array. +LB591 ldx MEMSIZ ; get to of string space +LB593 stx STRTAB ; save top of uncompacted stringspace + clra ; zero out D and reset pointer to discovered variable to NULL + clrb + std V4B + ldx FRETOP ; point to bottom of string space + stx V47 ; save as lowest match address (match will be higher) + ldx #STRSTK ; point to start of string stack +LB5A0 cmpx TEMPPT ; are we at the top of the string stack? + beq LB5A8 ; brif so - done with the string stack + bsr LB5D8 ; check for string in uncompacted space (and advance pointer) + bra LB5A0 ; check another on the string stack +LB5A8 ldx VARTAB ; point to start of scalar variables +LB5AA cmpx ARYTAB ; end of scalars? + beq LB5B2 ; brif so + bsr LB5D2 ; check for string in uncompacted space and advance pointer + bra LB5AA ; check another variable +LB5B2 stx V41 ; save address of end of variables (address of first array) +LB5B4 ldx V41 ; get start of the next array +LB5B6 cmpx ARYEND ; end of arrays? + beq LB5EF ; brif so + ldd 2,x ; get length of array + addd V41 ; add to start of array + std V41 ; save address of next array + lda 1,x ; get second character of variable name + bpl LB5B4 ; brif numeric + ldb 4,x ; get number of dimensions + aslb ; two bytes per dimension size + addb #5 ; add in fixed overhead for array descriptor + abx ; now X points to first array element +LB5CA cmpx V41 ; at the start of the next array? + beq LB5B6 ; brif so - go handle another array + bsr LB5D8 ; check for string in uncompacted space (and advance pointer) + bra LB5CA ; process next array element +LB5D2 lda 1,x ; get second character of variable name + leax 2,x ; move to variable data + bpl LB5EC ; brif numeric +LB5D8 ldb ,x ; get length of string + beq LB5EC ; brif NULL - don't need to check data pointer + ldd 2,x ; get data pointer + cmpd STRTAB ; is it in compacted string space? + bhi LB5EC ; brif so + cmpd V47 ; is it better match than previous best? + bls LB5EC ; brif not + stx V4B ; save descriptor address of best match + std V47 ; save new best data pointer match +LB5EC leax 5,x ; move to next descriptor +LB5EE rts +LB5EF ldx V4B ; get descriptor address of the matched string + beq LB5EE ; brif we didn't find one - we're done + clra ; zero extend length + ldb ,x ; get string length + decb ; subtract one (we won't have a NULL string here) + addd V47 ; now D points to the address of the end of the string data + std V43 ; save as top address of move + ldx STRTAB ; set top of uncompacted space as destination + stx V41 + jsr LAC20 ; move string to top of uncompactedspace + ldx V4B ; point to string descriptor + ldd V45 ; get new data pointer address + std 2,x ; update descriptor + ldx V45 ; get bottom of copy destination + leax -1,x ; move back below it + jmp LB593 ; go search for another string to move (and set new bottom of string space) +; Concatenate two strings. We come here directly from the operator handler rather than via a JSR. +LB60F ldd FPA0+2 ; get string descriptor for the first string + pshs d ; save it + jsr LB223 ; evaluate a second string (concatenation is left associative) + jsr LB146 ; make sure we have a string + puls x ; get back first string descriptor + stx RESSGN ; save it + ldb ,x ; get length of first string + ldx FPA0+2 ; get pointer to second string + addb ,x ; add length of second string + bcc LB62A ; brif combined length is OK + ldb #2*14 ; raise string too long error + jmp LAC46 +LB62A jsr LB50D ; reserve room for new string + ldx RESSGN ; get descriptor address of the first string + ldb ,x ; get length of first string + bsr LB643 ; copy it to string space + ldx V4D ; get descriptor address of second string + bsr LB659 ; get string details for second string + bsr LB645 ; copy second string into new string space + ldx RESSGN ; get pointer to first string + bsr LB659 ; remove it from the string stack if possible + jsr LB54C ; put new string on the string stack + jmp LB168 ; return to expression evaluator +; Copy B bytes to space pointed to by FRESPC +LB643 ldx 2,x ; get source address from string descriptor +LB645 ldu FRESPC ; get destination address + incb ; compensate for decb + bra LB64E ; do the copy +LB64A lda ,x+ ; copy a byte + sta ,u+ +LB64E decb ; done yet? + bne LB64A ; brif not + stu FRESPC ; save destination pointer + rts +; Fetch details of string in FPA0+2 and remove from the string stack if possible +LB654 jsr LB146 ; make sure we have a string +LB657 ldx FPA0+2 ; get descriptor pointer +LB659 ldb ,x ; get length of string + bsr LB675 ; see if it's at the top of the string stack and remove it if so + bne LB672 ; brif not removed + ldx 5+2,x ; get start address of string just removed + leax -1,x ; move pointer down 1 + cmpx STRTAB ; is it at the bottom of string space? + bne LB66F ; brif not + pshs b ; save length + addd STRTAB ; add length to start of strings (A was cleared previously) + std STRTAB ; save new string space start (deallocated space for this string) + puls b ; get back string length +LB66F leax 1,x ; restore pointer to pointing at the actual string data + rts +LB672 ldx 2,x ; get data pointer for the string + rts +; Remove string pointed to by X from the string stack if it is at the top of the stack; return with +; A clear and Z set if string removed +LB675 cmpx LASTPT ; is it at the top of the string stack? + bne LB680 ; brif not - do nothing + stx TEMPPT ; save new top of stack + leax -5,x ; move the "last" pointer back as well + stx LASTPT + clra ; flag string removed +LB680 rts +; LEN function +LEN bsr LB686 ; get string details +LB683 jmp LB4F3 ; return unsigned length in B +LB686 bsr LB654 ; get string details and remove from string stack + clr VALTYP ; set value type to numeric + tstb ; set flags according to length + rts +; CHR$ function +CHR jsr LB70E ; get 8 bit unsigned integer to B +LB68F ldb #1 ; allocate a one byte string + jsr LB56D + lda FPA0+3 ; get character code + jsr LB511 ; save reserved string details in temp descriptor + sta ,x ; put character in string +LB69B leas 2,s ; don't go back to function handler - avoid numeric type check +LB69D jmp LB54C ; return temporary string on string stack +; ASC function +ASC bsr LB6A4 ; get first character of argument + bra LB683 ; return unsigned code in B +LB6A4 bsr LB686 ; fetch string details + beq LB706 ; brif NULL string + ldb ,x ; get character at start of string + rts +; LEFT$ function +LEFT bsr LB6F5 ; get arguments from the stack +LB6AD clra ; clear pointer offset (set to start of string) +LB6AE cmpb ,x ; are we asking for more characters than there are in the string? + bls LB6B5 ; brif not + ldb ,x ; only return the number that are in the string + clra ; force starting offset to be the start of the string +LB6B5 pshs b,a ; save offset and length + jsr LB50F ; reserve space in string space + ldx V4D ; point to original string descriptor + bsr LB659 ; get string details + puls b ; get string offset + abx ; now X points to the start of the data to copy + puls b ; get length of copy + jsr LB645 ; copy the data to the allocated space + bra LB69D ; return temp string on string stack +; RIGHT$ function +RIGHT bsr LB6F5 ; get arguments from stack + suba ,x ; subtract length of original string from desired length + nega ; now A is offset into old string where we start copying + bra LB6AE ; go handle everything else +; MID$ function +MID ldb #255 ; default length is the whole string + stb FPA0+3 ; save it + jsr GETCCH ; see what we have after offset + cmpa #') ; end of function? + beq LB6DE ; brif so - no length + jsr LB26D ; force a comma + bsr LB70B ; get length parameter +LB6DE bsr LB6F5 ; get string and offset parameters from the stack + beq LB706 ; brif we have a 0 offset requested (string offsets are 1-based) + clrb ; clear length counter + deca ; subtract one from position parameter (we work on 0-based, param is 1-based) + cmpa ,x ; is start greater than length of string? + bhs LB6B5 ; brif so - return NULL string + tfr a,b ; save absolute position parameter + subb ,x ; now B is postition less length + negb ; now B is amount of string to copy + cmpb FPA0+3 ; is it less than the length requested? + bls LB6B5 ; brif so + ldb FPA0+3 ; set length to the requested length + bra LB6B5 ; go finish up copying the substring +; Common routine for LEFT$, RIGHT$, MID$ - check for ) and fetch string data and first parameter +; from the stack. These were evaluated in the function evaluation handler. (It's not clear that doing +; it that way is actually beneficial. However, this is what the ROM designers did, so here we are.) +LB6F5 jsr LB267 ; make sure we have ) + ldu ,s ; get return address - we're going to mess with the stack + ldx 5,s ; get address of string descriptor + stx V4D ; save descriptor adddress + lda 4,s ; get first numeric parameter in both A and B + ldb 4,s + leas 7,s ; clean up stack + tfr u,pc ; return to original caller +LB706 jmp LB44A ; raise FC error +; Evaluate an unsigned 8 bit expression to B +LB709 jsr GETNCH ; move to next character +LB70B jsr LB141 ; evaluate a numeric expression +LB70E jsr LB3E9 ; convert to integer in D + tsta ; are we negative or > 255? + bne LB706 ; brif so - FC error + jmp GETCCH ; fetch current input character and return +; VAL function +VAL jsr LB686 ; get string details + lbeq LBA39 ; brif NULL string - return 0 + ldu CHARAD ; get input pointer so we can replace it later + stx CHARAD ; point interpreter at string data + abx ; calculate end address of the string + lda ,x ; get byte after the end of the string + pshs u,x,a ; save end of string address, input pointer, and character after end of string + clr ,x ; put a NUL after the string (stops the number interpreter) + jsr GETCCH ; get input character at start of string + jsr LBD12 ; evaluate numeric expression in string + puls a,x,u ; get back saved character and pointers + sta ,x ; restore byte after string + stu CHARAD ; restore interpeter's input pointer + rts +; Evaluate unsigned expression to BINVAL, then evaluate an unsigned expression to B +LB734 bsr LB73D ; evaluate expression + stx BINVAL ; save result +LB738 jsr LB26D ; make sure there's a comma + bra LB70B ; evaluate unsigned expression to B +; Evaluate unsigned expression in X +LB73D jsr LB141 ; evaluate numeric expression +LB740 lda FP0SGN ; is it negative? + bmi LB706 ; brif so + lda FP0EXP ; get exponent + cmpa #0x90 ; largest possible exponent for 16 bits + bhi LB706 ; brif too large + jsr LBCC8 ; move binary point to right of FPA0 + ldx FPA0+2 ; get resulting unsigned value + rts +; PEEK function +PEEK bsr LB740 ; get address to X + ldb ,x ; get the value at that address + jmp LB4F3 ; return B as unsigned value +; POKE function +POKE bsr LB734 ; evaluate address and byte value + ldx BINVAL ; get address + stb ,x ; put value there + rts +; LLIST command +LLIST ldb #-2 ; set output device to printer + stb DEVNUM + jsr GETCCH ; reset flags for input character and fall through to LIST +; LIST command +LIST pshs cc ; save zero flag (end of statement) + jsr LAF67 ; parse line number + jsr LAD01 ; find address of that line + stx LSTTXT ; save that address as the start of the list + puls cc ; get back ent of statement flag + beq LB784 ; brif end of line - list whole program + jsr GETCCH ; are we at the end of the line (one number)? + beq LB789 ; brif end of line + cmpa #0xac ; is it "-"? + bne LB783 ; brif not + jsr GETNCH ; eat the "-" + beq LB784 ; brif no second number - list to end of program + jsr LAF67 ; evaluate the second number + beq LB789 ; brif illegal number +LB783 rts +LB784 ldu #0xffff ; this will cause listing to do the entire program + stu BINVAL +LB789 leas 2,s ; don't return to the caller - we'll jump back to the main loop + ldx LSTTXT ; get address of line to list +LB78D jsr LB95C ; do a newline if needed + jsr LA549 ; do a break check + ldd ,x ; get address of next line + bne LB79F ; brif not end of program +LB797 jsr LA42D ; close output file + clr DEVNUM ; reset device to screen + jmp LAC73 ; go back to immediate mode +LB79F stx LSTTXT ; save new line address + ldd 2,x ; get line number of this line + cmpd BINVAL ; is it above the end line? + bhi LB797 ; brif so - return + jsr LBDCC ; display line number + jsr LB9AC ; put a space after it + ldx LSTTXT ; get line address + bsr LB7C2 ; detokenize the line + ldx [LSTTXT] ; get pointer to next line + ldu #LINBUF+1 ; point to start of detokenized line +LB7B9 lda ,u+ ; get byte from detokenized line + beq LB78D ; brif end of line + jsr LB9B1 ; output character + bra LB7B9 ; handle next character +; Detokenize a line from (X) to the line input buffer +LB7C2 jsr RVEC24 ; do the RAM hook dance + leax 4,x ; move past next line pointer and line number + ldy #LINBUF+1 ; point to line input buffer (destination) +LB7CB lda ,x+ ; get character from tokenized line + beq LB820 ; brif end of input + bmi LB7E6 ; brif it's a token + cmpa #': ; colon? + bne LB7E2 ; brif not + ldb ,x ; get what's after the colon + cmpb #0x84 ; ELSE? + beq LB7CB ; brif so - suppress the colon + cmpb #0x83 ; '? + beq LB7CB ; brif so - suppress the colon + skip2 +LB7E0 lda #'! ; placeholder for unknown token +LB7E2 bsr LB814 ; stow output character + bra LB7CB ; go process another input character +LB7E6 ldu #COMVEC-10 ; point to command interptation table + cmpa #0xff ; is it a function? + bne LB7F1 ; brif not + lda ,x+ ; get function token + leau 5,u ; shift to the function half of the interpretation tables +LB7F1 anda #0x7f ; remove token bias +LB7F3 leau 10,u ; move to next command/function table + tst ,u ; is this table active? + beq LB7E0 ; brif not - use place holder + suba ,u ; subtract number of tokens handled by this table entry + bpl LB7F3 ; brif this token isn't handled here + adda ,u ; undo extra subtraction + ldu 1,u ; get reserved word list for this table +LB801 deca ; are we at the right entry? + bmi LB80A ; brif so +LB804 tst ,u+ ; end of entry? + bpl LB804 ; brif not + bra LB801 ; see if we're there yet +LB80A lda ,u ; get character from wordlist + bsr LB814 ; put character in the buffer + tst ,u+ ; end of word? + bpl LB80A ; brif not + bra LB7CB ; go handle another input character +LB814 cmpy #LINBUF+LBUFMX ; is there room? + bhs LB820 ; brif not + anda #0x7f ; lose bit 7 + sta ,y+ ; save character in output + clr ,y ; make sure there's always a NUL terminator +LB820 rts +; Tokenize the line that the input pointer is pointing to; put result in the line input buffer; return +; length in D +LB821 jsr RVEC23 ; do the RAM hook dance + ldx CHARAD ; get input pointer + ldu #LINBUF ; set destination pointer +LB829 clr V43 ; clear alpha string flag + clr V44 ; clear DATA flag +LB82D lda ,x+ ; get input character + beq LB852 ; brif end of input + tst V43 ; are we handling an alphanumeric string? + beq LB844 ; brif not + jsr LB3A2 ; set carry if not alpha + bcc LB852 ; brif alpha + cmpa #'0 ; is it below the digits? + blo LB842 ; brif so + cmpa #'9 ; is it within the digits? + bls LB852 ; brif so +LB842 clr V43 ; flag that we're past the alphanumeric string +LB844 cmpa #0x20 ; space? + beq LB852 ; brif so - keep it + sta V42 ; save scan delimiter + cmpa #'" ; string delimiter? + beq LB886 ; brif so - copy until another " + tst V44 ; doing "DATA"? + beq LB86B ; brif not +LB852 sta ,u+ ; put character in output + beq LB85C ; brif end of input + cmpa #': ; colon? + beq LB829 ; brif so - reset DATA and alpha string flags +LB85A bra LB82D ; go process another input character +LB85C clr ,u+ ; put a double NUL at the end + clr ,u+ + tfr u,d ; calculate length of result (includes double NUL and an extra two bytes) + subd #LINHDR + ldx #LINBUF-1 ; point to one before the output + stx CHARAD ; set input pointer there + rts +LB86B cmpa #'? ; print abbreviation? + bne LB873 ; brif not + lda #0x87 ; token for PRINT + bra LB852 ; go stash it +LB873 cmpa #'' ; REM abbreviation? + bne LB88A ; brif not + ldd #0x3a83 ; colon plus ' token + std ,u++ ; put it in the output +LB87C clr V42 ; set delimiter to NUL +LB87E lda ,x+ ; get input + beq LB852 ; brif end of line + cmpa V42 ; at the delimiter? + beq LB852 ; brif so +LB886 sta ,u+ ; save in output + bra LB87E ; keep scanning for delimiter +LB88A cmpa #'0 ; is it below digits? + blo LB892 ; brif so + cmpa #';+1 ; is it digit, colon, or semicolon? + blo LB852 ; brif so +LB892 leax -1,x ; move input pointer back one (to point at this input character) + pshs u,x ; save input and output pointers + clr V41 ; set token type to 0 (command) + ldu #COMVEC-10 ; point to command interpretation table +LB89B clr V42 ; set token counter to 0 (0x80) +LB89D leau 10,u ; + lda ,u ; get number of reserved words + beq LB8D4 ; brif this table isn't active + ldy 1,u ; point to reserved words list +LB8A6 ldx ,s ; get input pointer +LB8A8 ldb ,y+ ; get character from reserved word table + subb ,x+ ; compare with input character + beq LB8A8 ; brif exact match + cmpb #0x80 ; brif it was the last character in word and exact match + bne LB8EA ; brif not + leas 2,s ; remove original input pointer from stack + puls u ; get back output pointer + orb V42 ; create token value (B has 0x80 from above) + lda V41 ; get token type + bne LB8C2 ; brif function + cmpb #0x84 ; is it ELSE? + bne LB8C6 ; brif not + lda #': ; silently add a colon before ELSE +LB8C2 std ,u++ ; put two byte token into output + bra LB85A ; go handle more input +LB8C6 stb ,u+ ; save single byte token + cmpb #0x86 ; DATA? + bne LB8CE ; brif not + inc V44 ; set DATA flag +LB8CE cmpb #0x82 ; REM? + beq LB87C ; brif so - skip over rest of line +LB8D2 bra LB85A ; go handle more input +LB8D4 ldu #COMVEC-5 ; point to interpretation table, function style +LB8D7 com V41 ; invert token flag + bne LB89B ; brif we haven't already done functions + puls x,u ; restore input and output pointers + lda ,x+ ; copy first character + sta ,u+ + jsr LB3A2 ; set C if not alpha + bcs LB8D2 ; brif not alpha - it isn't a variable + com V43 ; set alphanumeric string flag + bra LB8D2 ; process more input +LB8EA inc V42 ; bump token number + deca ; checked all in this table? + beq LB89D ; brif so + leay -1,y ; unconsume last compared character +LB8F1 ldb ,y+ ; end of entry? + bpl LB8F1 ; brif not + bra LB8A6 ; check next reserved word +; PRINT command +PRINT beq LB958 ; brif no argument - do a newline + bsr LB8FE ; process print options + clr DEVNUM ; reset output to screen + rts +LB8FE cmpa #'@ ; is it PRINT @? + bne LB907 ; brif not + jsr LA554 ; move cursor to correct location + bra LB911 ; handle some more +LB907 cmpa #'# ; device number specified? + bne LB918 ; brif not + jsr LA5A5 ; parse device number + jsr LA406 ; check for valid output file +LB911 jsr GETCCH ; get input character + beq LB958 ; brif nothing - do newline + jsr LB26D ; need comma after @ or # +LB918 jsr RVEC9 ; do the RAM hook boogaloo +LB91B beq LB965 ; brif end of input +LB91D cmpa #0xa4 ; TAB(? + beq LB97E ; brif so + cmpa #', ; comma (next tab field)? + beq LB966 ; brif so + cmpa #'; ; semicolon (do not advance print position) + beq LB997 ; brif so + jsr LB156 ; evaluate expression + lda VALTYP ; get type of value + pshs a ; save it + bne LB938 ; brif string + jsr LBDD9 ; convert FP number to string + jsr LB516 ; parse a string and put on string stack +LB938 bsr LB99F ; print string + puls b ; get back variable type + jsr LA35F ; set up print parameters + tst PRTDEV ; is it a display device? + beq LB949 ; brif so + bsr LB958 ; do a newline + jsr GETCCH ; get input + bra LB91B ; process more print stuff +LB949 tstb ; set flags on print position + bne LB954 ; brif not at start of line + jsr GETCCH ; get current input + cmpa #', ; comma? + beq LB966 ; skip to next tab field if so + bsr LB9AC ; send a space +LB954 jsr GETCCH ; get input character + bne LB91D ; brif not end of statement +LB958 lda #0x0d ; carriage return + bra LB9B1 ; send it to output +LB95C jsr LA35F ; set up print parameters +LB95F beq LB958 ; brif width is 0 + lda DEVPOS ; get line position + bne LB958 ; brif not at start of line +LB965 rts +LB966 jsr LA35F ; set up print parameters + beq LB975 ; brif line width is 0 + ldb DEVPOS ; get line position + cmpb DEVLCF ; at or past last comma field? + blo LB977 ; brif so + bsr LB958 ; move to next line + bra LB997 ; handle more stuff +LB975 ldb DEVPOS ; get line position +LB977 subb DEVCFW ; subtract a comma field width + bhs LB977 ; brif we don't have a remainder yet + negb ; now B is number of of spaces needed + bra LB98E ; go advance +LB97E jsr LB709 ; evaluate TAB distance + cmpa #') ; closing )? + lbne LB277 ; brif not + jsr LA35F ; set up print parameters + subb DEVPOS ; subtract print position from desired position + bls LB997 ; brif we're already past it +LB98E tst PRTDEV ; is it a display device? + bne LB997 ; brif not +LB992 bsr LB9AC ; output a space + decb ; done enough? + bne LB992 ; brif not +LB997 jsr GETNCH ; get input character + jmp LB91B ; process more items +; cpoy string from (X-1) to output +LB99C jsr LB518 ; parse the string +LB99F jsr LB657 ; get string details +LB9A2 incb ; compensate for decb +LB9A3 decb ; done all of the string? + beq LB965 ; brif so + lda ,x+ ; get character from string + bsr LB9B1 ; send to output + bra LB9A3 ; go do another character +LB9AC lda #0x20 ; space character + skip2 +LB9AF lda #'? ; question mark character +LB9B1 jmp PUTCHR ; output character +; The floating point math package and related functions and operations follow from here +; to the end of the Color Basic ROM area +LB9B4 ldx #LBEC0 ; point to FP constant 0.5 + bra LB9C2 ; add 0.5 to FPA0 +LB9B9 jsr LBB2F ; unpack FP data from (X) to FPA1 +; subtraction operator +LB9BC com FP0SGN ; invert sign of FPA0 (subtracting is adding the negative) + com RESSGN ; that also inverts the sign differential + bra LB9C5 ; go add the negative of FPA0 to FPA1 +LB9C2 jsr LBB2F ; unpack FP data from (X) to FPA1 +; addition operator +LB9C5 tstb ; check exponent of FPA0 + lbeq LBC4A ; copy FPA1 to FPA0 if FPA0 is 0 + ldx #FP1EXP ; point X to FPA1 (first operand) as the operand to denormalize +LB9CD tfr a,b ; put exponent of FPA1 into B + tstb ; is FPA1 0? + beq LBA3E ; brif exponent is 0 - no-op; adding 0 to FPA0 + subb FP0EXP ; get difference in exponents - number of bits to shift the smaller mantissa + beq LBA3F ; brif exponents are equal - no need to denormalize + blo LB9E2 ; brif FPA0 > FPA1 + sta FP0EXP ; replace result exponent with FPA1's (FPA1 is bigger) + lda FP1SGN ; also copy sign over + sta FP0SGN + ldx #FP0EXP ; point to FPA0 (we need to denormalize the smaller number) + negb ; invert the difference - this is the number of bits to shift the mantissa +LB9E2 cmpb #-8 ; do we have to shift by a whole byte? + ble LBA3F ; brif so start by shifting whole bytes to the right + clra ; clear overflow byte + lsr 1,x ; shift high bit of mantissa right (LSR will force a zero into the high bit) + jsr LBABA ; shift remainder of mantissa right -B times +LB9EC ldb RESSGN ; get the sign flag + bpl LB9FB ; brif signs are the same (we add the mantissas then) + com 1,x ; complement the mantissa and extra precision bytes + com 2,x + com 3,x + com 4,x + coma + adca #0 ; add one to A (COM sets C); this may cause a carry to enter the ADD below +LB9FB sta FPSBYT ; save extra precision byte + lda FPA0+3 ; add the main mantissa bytes (and propage carry from above) + adca FPA1+3 + sta FPA0+3 + lda FPA0+2 + adca FPA1+2 + sta FPA0+2 + lda FPA0+1 + adca FPA1+1 + sta FPA0+1 + lda FPA0 + adca FPA1 + sta FPA0 + tstb ; were signs the same? + bpl LBA5C ; brif so - number may have gotten bigger so normalize if needed +LBA18 bcs LBA1C ; brif we had a carry - result is positive?) + bsr LBA79 ; do a proper negation of FPA0 mantissa +LBA1C clrb ; clear temporary exponent accumulator +LBA1D lda FPA0 ; test high byte of mantissa + bne LBA4F ; brif not 0 - we need to do bit shifting + lda FPA0+1 ; shift left 8 bits + sta FPA0 + lda FPA0+2 + sta FPA0+1 + lda FPA0+3 + sta FPA0+2 + lda FPSBYT + sta FPA0+3 + clr FPSBYT + addb #8 ; account for 8 bits shifted + cmpb #5*8 ; shifted 5 bytes worth? + blt LBA1D ; brif not +LBA39 clra ; zero out exponent and sign - result is 0 +LBA3A sta FP0EXP ; set exponent and sign + sta FP0SGN +LBA3E rts +LBA3F bsr LBAAE ; shift FPA0 mantissa to the right + clrb ; clear carry + bra LB9EC ; get on with adding +LBA44 incb ; account for one bit shift + asl FPSBYT ; shift mantissa and extra precision left + rol FPA0+3 + rol FPA0+2 + rol FPA0+1 + rol FPA0 +LBA4F bpl LBA44 ; brif we haven't got a 1 in bit 7 + lda FP0EXP ; get exponent of result + pshs b ; subtract shift count from exponent + suba ,s+ + sta FP0EXP ; save adjusted exponent + bls LBA39 ; brif we underflowed - set result to 0 + skip2 +LBA5C bcs LBA66 ; brif mantissa overflowed + asl FPSBYT ; get bit 7 of expra precision to C (used for round off) + lda #0 ; set to 0 without affecting C + sta FPSBYT ; clear out extra precision bits + bra LBA72 ; go round off result +LBA66 inc FP0EXP ; bump exponent (for a right shift to bring carry in) + beq LBA92 ; brif we overflowed + ror FPA0 ; shift carry into mantissa, shift right + ror FPA0+1 + ror FPA0+2 + ror FPA0+3 +LBA72 bcc LBA78 ; brif no round-off needed + bsr LBA83 ; add one to mantissa + beq LBA66 ; brif carry - need to shift right again +LBA78 rts +LBA79 com FP0SGN ; invert sign of value +LBA7B com FPA0 ; first do a one's copmlement + com FPA0+1 + com FPA0+2 + com FPA0+3 +LBA83 ldx FPA0+2 ; add one to mantissa (after one's complement gives two's complement) + leax 1,x ; bump low word + stx FPA0+2 + bne LBA91 ; brif no carry from low word + ldx FPA0 ; bump high word + leax 1,x + stx FPA0 +LBA91 rts +LBA92 ldb #2*5 ; code for overflow + jmp LAC46 ; raise error +LBA97 ldx #FPA2-1 ; point to FPA2 +LBA9A lda 4,x ; shift mantissa right by 8 bits + sta FPSBYT + lda 3,x + sta 4,x + lda 2,x + sta 3,x + lda 1,x + sta 2,x + lda FPCARY ; and handle extra precision on the left + sta 1,x +LBAAE addb #8 ; account for 8 bits shifted + ble LBA9A ; brif more shifts needed + lda FPSBYT ; get sub byte (extra precision) + subb #8 ; undo the 8 added above + beq LBAC4 ; brif difference is 0 +LBAB8 asr 1,x ; shift mantissa and sub byte one bit (keep mantissa high bit set) +LBABA ror 2,x + ror 3,x + ror 4,x + rora + incb ; account for one shift + bne LBAB8 ; brif not enought shifts yet +LBAC4 rts +LBAC5 fcb 0x81,0x00,0x00,0x00,0x00 ; packed FP 1.0 +LBACA bsr LBB2F ; unpack FP value from (X) to FPA1 +; multiplication operator +LBACC beq LBB2E ; brif exponent of FPA0 is 0 (result is 0) + bsr LBB48 ; calculate exponent of product +LBAD0 lda #0 ; zero out mantissa of FPA2 + sta FPA2 + sta FPA2+1 + sta FPA2+2 + sta FPA2+3 + ldb FPA0+3 ; multiply FPA1 by LSB of FPA0 + bsr LBB00 + ldb FPSBYT ; save extra precision byte + stb VAE + ldb FPA0+2 + bsr LBB00 ; again for next byte of FPA0 + ldb FPSBYT + stb VAD + ldb FPA0+1 ; again for next byte of FPA0 + bsr LBB00 + ldb FPSBYT + stb VAC + ldb FPA0 ; and finally for the high byte + bsr LBB02 + ldb FPSBYT + stb VAB + jsr LBC0B ; copy mantissa from FPA2 to FPA0 (result) + jmp LBA1C ; normalize +LBB00 beq LBA97 ; brif multiplier is 0 - just shift, don't multiply +LBB02 coma ; set carry +LBB03 lda FPA2 ; get FPA2 MS byte + rorb ; data bit to carry; will be 0 when all shifts done + beq LBB2E ; brif 8 shifts done + bcc LBB20 ; brif data bit is 0 - no addition + lda FPA2+3 ; add mantissa of FPA1 and FPA2 + adda FPA1+3 + sta FPA2+3 + lda FPA2+2 + adca FPA1+2 + sta FPA2+2 + lda FPA2+1 + adca FPA1+1 + sta FPA2+1 + lda FPA2 + adca FPA1 +LBB20 rora ; shift carry into FPA2 + sta FPA2 + ror FPA2+1 + ror FPA2+2 + ror FPA2+3 + ror FPSBYT + clra ; clear carry + bra LBB03 +LBB2E rts +; Unpack FP value from (X) to FPA1 +LBB2F ldd 1,x ; copy mantissa (and sign) + sta FP1SGN ; save sign bit + ora #0x80 ; make sure mantissa has bit 7 set + std FPA1 + ldb FP1SGN ; get sign + eorb FP0SGN ; set if FPA0 sign differs + stb RESSGN + ldd 3,x ; copy remainder of mantissa + std FPA1+2 + lda ,x ; and exponent + sta FP1EXP + ldb FP0EXP ; fetch FPA0 exponent and set flags + rts +; Calculate eponent for product of FPA0 and FPA1 +LBB48 tsta ; is FPA1 zero? + beq LBB61 ; brif so + adda FP0EXP ; add to exponent of FPA0 (this is how scientific notation works) + rora ; set V if we *don't* have an overflow + rola + bvc LBB61 ; brif exponent too larger or small + adda #0x80 ; restore the bias + sta FP0EXP ; set result exponent + beq LBB63 ; brif 0 - clear FPA0 + lda RESSGN ; the result sign (negative if signs differ) is the result sign + sta FP0SGN ; so set it as such + rts +LBB5C lda FP0SGN ; get sign of FPA0 + coma ; invert sign + bra LBB63 ; zero sign and exponent +LBB61 leas 2,s ; don't go back to caller (mul/div) - return to previous caller +LBB63 lbpl LBA39 ; brif we underflowed - go zero things out +LBB67 jmp LBA92 ; raise overflow error +; fast multiply by 10 - leave result in FPA0 +LBB6A jsr LBC5F ; copy FPA0 to FPA1 (for addition later) + beq LBB7C ; brif exponent is 0 - it's a no-op then + adda #2 ; this gives "times 4" + bcs LBB67 ; raise overflow if required + clr RESSGN ; set result sign to "signs the same" + jsr LB9CD ; add FPA1 to FPA0 "times 5" + inc FP0EXP ; times 10 + beq LBB67 ; brif overflow +LBB7C rts +LBB7D fcb 0x84,0x20,0x00,0x00,0x00 ; packed FP constant 10.0 +; Divide by 10 +LBB82 jsr LBC5F ; move FPA0 to FPA1 + ldx #LBB7D ; point to constant 10 + clrb ; zero sign +LBB89 stb RESSGN ; result will be positive or zero + jsr LBC14 ; unpack constant 10 to FPA0 + skip2 ; fall through to division (divide FPA1 by 10) +LBB8F bsr LBB2F ; unpack FP number from (X) to FPA1 +; division operator +LBB91 beq LBC06 ; brif FPA0 is 0 - division by zero + neg FP0EXP ; get exponent of reciprocal of the divisor + bsr LBB48 ; calculate exponent of quotient + inc FP0EXP ; bump exponent (due to division algorithm below) + beq LBB67 ; brif overflow + ldx #FPA2 ; point to temporary storage location + ldb #4 ; do 5 bytes + stb TMPLOC ; save counter + ldb #1 ; shift counter and quotient byte +LBBA4 lda FPA0 ; compare mantissa of FPA0 to FPA1, set C if FPA1 less + cmpa FPA1 + bne LBBBD + lda FPA0+1 + cmpa FPA1+1 + bne LBBBD + lda FPA0+2 + cmpa FPA1+2 + bne LBBBD + lda FPA0+3 + cmpa FPA1+3 + bne LBBBD + coma ; set C if FPA0 = FPA1 (it "goes") +LBBBD tfr cc,a ; save "it goes" status + rolb ; rotate carry into quotient + bcc LBBCC ; brif carry clear - haven't done 8 shifts yet + stb ,x+ ; save quotient byte + dec TMPLOC ; done enough bytes? + bmi LBBFC ; brif done all 5 + beq LBBF8 ; brif last byte + ldb #1 ; reset shift counter and quotient byte +LBBCC tfr a,cc ; get back carry status + bcs LBBDE ; brif it "went" +LBBD0 asl FPA1+3 ; shift mantissa (dividend) left + rol FPA1+2 + rol FPA1+1 + rol FPA1 + bcs LBBBD ; brif carry - it "goes" so we have to bump quotient + bmi LBBA4 ; brif high order bit is set - compare mantissas + bra LBBBD ; otherwise, count a 0 bit and try next bit +LBBDE lda FPA1+3 ; subtract mantissa of FPA0 from mantissa of FPA1 + suba FPA0+3 + sta FPA1+3 + lda FPA1+2 + sbca FPA0+2 + sta FPA1+2 + lda FPA1+1 + sbca FPA0+1 + sta FPA1+1 + lda FPA1 + sbca FPA0 + sta FPA1 + bra LBBD0 ; go check for another go +LBBF8 ldb #0x40 ; only two bits in last byte (for rounding) + bra LBBCC ; go do the last byte +LBBFC rorb ; get low bits to bits 7,6 and C to bit 5 + rorb + rorb + stb FPSBYT ; save result extra precision + bsr LBC0B ; move FPA2 mantissa to FPA0 (result) + jmp LBA1C ; go normalize the result +LBC06 ldb #2*10 ; division by zero + jmp LAC46 ; raise error +; Copy mantissa of FPA2 to FPA0 +LBC0B ldx FPA2 ; copy high word + stx FPA0 + ldx FPA2+2 ; copy low word + stx FPA0+2 + rts +; unpack FP number at (X) to FPA0 +LBC14 pshs a ; save register + ldd 1,x ; get mantissa high word and sign + sta FP0SGN ; set sign + ora #0x80 ; make sure mantissa always has bit 7 set + std FPA0 + clr FPSBYT ; clear extra precision + ldb ,x ; get exponent + ldx 3,x ; copy mantissa low word + stx FPA0+2 + stb FP0EXP ; save exponent (and set flags) + puls a,pc ; restore register and return +LBC2A ldx #V45 ; point to FPA4 + bra LBC35 ; pack FPA0 there +LBC2F ldx #V40 ; point to FPA3 + skip2 ; fall through to pack FPA0 there +LBC33 ldx VARDES ; get variable descriptor pointer +; Pack FPA0 to (X) +LBC35 lda FP0EXP ; get exponent + sta ,x ; save it + lda FP0SGN ; get sign + ora #0x7f ; force set low bits - only keep sign in high bit + anda FPA0 ; merge in bits 6-0 of high byte of mantissa + sta 1,x ; save it + lda FPA0+1 ; copy next highest byte + sta 2,x + ldu FPA0+2 ; and the low word of the mantissa + stu 3,x + rts +; Copy FPA1 to FPA0; return with sign in A +LBC4A lda FP1SGN ; copy sign +LBC4C sta FP0SGN + ldx FP1EXP ; copy exponent, mantissa high byte + stx FP0EXP + clr FPSBYT ; clear extra precision + lda FPA1+1 ; copy mantissa second highest byte + sta FPA0+1 + lda FP0SGN ; set sign for return + ldx FPA1+2 ; copy low word of mantissa + stx FPA0+2 + rts +; Copy FPA0 to FPA1 +LBC5F ldd FP0EXP ; copy exponent and high byte of mantissa + std FP1EXP + ldx FPA0+1 ; copy middle bytes of mantissa + stx FPA1+1 + ldx FPA0+3 ; copy low byte of mantissa and sign + stx FPA1+3 + tsta ; set flags on exponent + rts +; check FPA0: return B = 0, if FPA0 is 0, 0xff if negative, and 0x01 if positive +LBC6D ldb FP0EXP ; get exponent + beq LBC79 ; brif 0 +LBC71 ldb FP0SGN ; get sign +LBC73 rolb ; get sign to C + ldb #0xff ; set for negative result + bcs LBC79 ; brif negative + negb ; set to 1 for positive +LBC79 rts +; SGN function +SGN bsr LBC6D ; get sign of FPA0 +LBC7C stb FPA0 ; save result + clr FPA0+1 ; clear next lower 8 bits + ldb #0x88 ; exponent if mantissa is 8 bit integer +LBC82 lda FPA0 ; get high bits of mantissa + suba #0x80 ; set C if mantissa was positive (will cause a negation if it was negative) +LBC86 stb FP0EXP ; set exponent + ldd ZERO ; clear out low word + std FPA0+2 + sta FPSBYT ; clear extra precision + sta FP0SGN ; set sign to positive + jmp LBA18 ; normalize the result +; ABS function +ABS clr FP0SGN ; force FPA0 to be positive (yes, it's that simple) + rts +; Compare packed FP number at (X) to FPA0 +; Return with B = -1, 0, 1 for FPA0 <, =, > (X) and flags set based on that +LBC96 ldb ,x ; get exponent of (X) + beq LBC6D ; brif (X) is 0 + ldb 1,x ; get MS byte of mantissa of (X) + eorb FP0SGN ; set bit 7 if signs of (X) and FPA0 differ + bmi LBC71 ; brif signs differ - no need to compare the magnitude +LBCA0 ldb FP0EXP ; compare exponents and brif different + cmpb ,x + bne LBCC3 + ldb 1,x ; compare mantissa (but we have to pack the FPA0 bits first + orb #0x7f ; keep only sign bit (note: signs are the same) + andb FPA0 ; merge in the mantissa bits from FPA0 + cmpb 1,x ; do the packed versions match? + bne LBCC3 ; brif not + ldb FPA0+1 ; compare second byte of mantissas + cmpb 2,x + bne LBCC3 + ldb FPA0+2 ; compare third byte of mantissas + cmpb 3,x + bne LBCC3 + ldb FPA0+3 ; compare low byte of mantissas, but use subtraction so B = 0 on match + subb 4,x + bne LBCC3 + rts ; return B = 0 if (X) = FPA0 +LBCC3 rorb ; shift carry to bit 7 (C set if FPA0 < (X)) + eorb FP0SGN ; invert the comparision sense if the signs are negative + bra LBC73 ; interpret comparison result +; Shift mantissa of FPA0 until the binary point is immediately to the right of the mantissa and set up the +; result as a two's complement value. +LBCC8 ldb FP0EXP ; get exponent of FPA0 + beq LBD09 ; brif FPA0 is zero - we don't have to do anything, just blank it + subb #0xa0 ; calculate number of shifts to get to the correct exponent (binary point to the right) + lda FP0SGN ; do we have a positive number? + bpl LBCD7 ; brif so + com FPCARY ; negate the mantissa and set extra inbound precision to the correct sign + jsr LBA7B +LBCD7 ldx #FP0EXP ; point to FPA0 + cmpb #-8 ; moving by whole bytes? + bgt LBCE4 ; brif not + jsr LBAAE ; do bit shifting + clr FPCARY ; clear carry in byte + rts +LBCE4 clr FPCARY ; clear the extra carry in precision + lda FP0SGN ; get sign of value + rola ; get sign to carry (so rotate repeats the sign) + ror FPA0 ; shift the first bit + jmp LBABA ; do the shifting dance +; INT function +INT ldb FP0EXP ; get exponent + cmpb #0xa0 ; is the number big enough that there can be no fractional part? + bhs LBD11 ; brif so - we don't have to do anything + bsr LBCC8 ; go shift binary point to the right of the mantissa + stb FPSBYT ; save extra precision bits + lda FP0SGN ; get original sign + stb FP0SGN ; force result to be positive + suba #0x80 ; set C if we had a positive result + lda #0xa0 ; set exponent to match denormalized result + sta FP0EXP + lda FPA0+3 ; save low byte + sta CHARAC + jmp LBA18 ; go normalize (this will correct for the two's complement representation of negatives) +LBD09 stb FPA0 ; replace mantissa of FPA0 with contents of B + stb FPA0+1 + stb FPA0+2 + stb FPA0+3 +LBD11 rts +; Convert ASCII string to FP +; BUG: no overflow is checked on the decimal exponent in exponential notation. +LBD12 ldx ZERO ; zero out FPA0 and temporaries + stx FP0SGN + stx FP0EXP + stx FPA0+1 + stx FPA0+2 + stx V47 + stx V45 + bcs LBD86 ; brif input character is numeric + jsr RVEC19 ; do the RAM hook dance + cmpa #'- ; regular negative sign + bne LBD2D ; brif not + com COEFCT ; invert sign + bra LBD31 ; process stuff after the sign +LBD2D cmpa #'+ ; regular plus? + bne LBD35 ; brif not +LBD31 jsr GETNCH ; get character after sign + bcs LBD86 ; brif numeric +LBD35 cmpa #'. ; decimal point? + beq LBD61 ; brif so + cmpa #'E ; scientific notation + bne LBD65 ; brif not + jsr GETNCH ; eat the "E" + bcs LBDA5 ; brif numeric + cmpa #0xac ; negative sign (token)? + beq LBD53 ; brif so + cmpa #'- ; regular negative? + beq LBD53 ; brif so + cmpa #0xab ; plus sign (token)? + beq LBD55 ; brif so + cmpa #'+ ; regular plus? + beq LBD55 + bra LBD59 ; brif no sign found +LBD53 com V48 ; set exponent sign to negative +LBD55 jsr GETNCH ; eat the sign + bcs LBDA5 ; brif numeric +LBD59 tst V48 ; is the exponent sign negatvie? + beq LBD65 ; brif not + neg V47 ; negate base 10 exponent + bra LBD65 +LBD61 com V46 ; toggle decimal point flag + bne LBD31 ; brif we haven't seen two decimal points +LBD65 lda V47 ; get base 10 exponent + suba V45 ; subtract number of places to the right + sta V47 ; we now have a complete decimal exponent + beq LBD7F ; brif we have no base 10 shifting required + bpl LBD78 ; brif positive exponent +LBD6F jsr LBB82 ; divide FPA0 by 10 (shift decimal point left) + inc V47 ; bump exponent + bne LBD6F ; brif we haven't reached 0 yet + bra LBD7F ; return result +LBD78 jsr LBB6A ; multiply by 10 + dec V47 ; downshift the exponent + bne LBD78 ; brif not at 0 yet +LBD7F lda COEFCT ; get desired sign + bpl LBD11 ; brif it will be positive - no need to do anything + jmp LBEE9 ; flip the sign of FPA0 +LBD86 ldb V45 ; get the decimal count + subb V46 ; (if decimal seen, will add one; otherwise it does nothing) + stb V45 + pshs a ; save new digit + jsr LBB6A ; multiply partial result by 10 + puls b ; get back digit + subb #'0 ; remove ASCII bias + bsr LBD99 ; add B to FPA0 + bra LBD31 ; go process another digit +LBD99 jsr LBC2F ; save FPA0 to FPA3 + jsr LBC7C ; convert B to FP number + ldx #V40 ; point to FPA3 + jmp LB9C2 ; add FPA3 and FPA0 +LBDA5 ldb V47 ; get exponent value + aslb ; times 2 + aslb ; times 4 + addb V47 ; times 5 + aslb ; times 10 + suba #'0 ; remove ASCII bias + pshs b ; save acculated result + adda ,s+ ; add new digit to accumulated result + sta V47 ; save new accumulated decimal exponent + bra LBD55 ; interpret another exponent character +LBDB6 fcb 0x9b,0x3e,0xbc,0x1f,0xfd ; packed FP: 99999999.9 +LBDBB fcb 0x9e,0x6e,0x6b,0x27,0xfd ; packed FP: 999999999 +LBDC0 fcb 0x9e,0x6e,0x6b,0x28,0x00 ; pakced FP: 1E9 +LBDC5 ldx #LABE8-1 ; point to "IN" message + bsr LBDD6 ; output the string + ldd CURLIN ; get basic line number +LBDCC std FPA0 ; save 16 bit unsigned integer + ldb #0x90 ; exponent for upper 16 bits of FPA0 to be an integer + coma ; set C (force normalization to treat as positive) + jsr LBC86 ; zero bottom half, save exponent, and normalize + bsr LBDD9 ; convert FP number to ASCII string +LBDD6 jmp LB99C ; output string +; Convert FP number to ASCII string +LBDD9 ldu #STRBUF+3 ; point to buffer address that will not cause string to go to string space +LBDDC lda #0x20 ; default sign is a space character + ldb FP0SGN ; get sign of value + bpl LBDE4 ; brif positive + lda #'- ; use negative sign +LBDE4 sta ,u+ ; save sign + stu COEFPT ; save output buffer pointer + sta FP0SGN ; save sign character + lda #'0 ; result is 0 if exponent is 0 + ldb FP0EXP ; get exponent + lbeq LBEB8 ; brif FPA0 is 0 + clra ; base 10 exponent is 0 for > 1 + cmpb #0x80 ; is number > 1? + bhi LBDFF ; brif so + ldx #LBDC0 ; point to 1E+09 + jsr LBACA ; shift decimal to the right by 9 spaces + lda #-9 ; account for shift +LBDFF sta V45 ; save base 10 exponent +LBE01 ldx #LBDBB ; point to 999999999 + jsr LBCA0 ; are we above that? + bgt LBE18 ; brif so +LBE09 ldx #LBDB6 ; point to 99999999.9 + jsr LBCA0 ; are we above that? + bgt LBE1F ; brif in range + jsr LBB6A ; multiply by 10 (we were small) + dec V45 ; account for shift + bra LBE09 ; see if we've come into range +LBE18 jsr LBB82 ; divide by 10 + inc V45 ; account for shift + bra LBE01 ; see if we've come into range +LBE1F jsr LB9B4 ; add 0.5 to FPA0 (rounding) + jsr LBCC8 ; do the integer dance + ldb #1 ; default decimal flag (force immediate decimal) + lda V45 ; get base 10 exponent + adda #10 ; account for "unormalized" number + bmi LBE36 ; brif number < 1.0 + cmpa #11 ; do we have more than 9 places? + bhs LBE36 ; brif so - do scientific notation + deca + tfr a,b + lda #2 ; force no scientific notation +LBE36 deca ; subtract wo without affecting carry + deca + sta V47 ; save exponent - 0 is do not display in scientific notation + stb V45 ; save number of places to left of decimal + bgt LBE4B ; brif >= 1 + ldu COEFPT ; point to string buffer + lda #'. ; put decimal + sta ,u+ + tstb ; is there anything to left of decimal? + beq LBE4B ; brif not + lda #'0 ; store a zero + sta ,u+ +LBE4B ldx #LBEC5 ; point to powers of 10 + ldb #0x80 ; set digit counter to 0x80 +LBE50 lda FPA0+3 ; add mantissa to power of 10 + adda 3,x + sta FPA0+3 + lda FPA0+2 + adca 2,x + sta FPA0+2 + lda FPA0+1 + adca 1,x + sta FPA0+1 + lda FPA0 + adca ,x + sta FPA0 + incb ; add one to digit counter + rorb ; put carry into bit 7 + rolb ; set V if carry and sign differ + bvc LBE50 ; brif positive mantissa or carry is 0 and negative mantissa + bcc LBE72 ; brif negative mantissa + subb #10+1 ; take 9's complement if adding mantissa + negb +LBE72 addb #'0-1 ; add ASCII bias + leax 4,x ; move to next power of 10 + tfr b,a ; save digit + anda #0x7f ; remove add/subtract flag + sta ,u+ ; put in output + dec V45 ; do we need a decimal yet? + bne LBE84 ; brif not + lda #'. ; put decimal + sta ,u+ +LBE84 comb ; toggle bit 7 (add/sub flag) + andb #0x80 ; only keep bit 7 + cmpx #LBEC5+9*4 ; done all places? + bne LBE50 ; brif not +LBE8C lda ,-u ; get last character + cmpa #'0 ; was it 0? + beq LBE8C ; brif so + cmpa #'. ; decimal? + bne LBE98 ; brif not + leau -1,u ; move past decimal if it isn't needed +LBE98 lda #'+ ; plus sign + ldb V47 ; get scientific notation exponent + beq LBEBA ; brif not scientific notation + bpl LBEA3 ; brif positive exponent + lda #'- ; negative sign for base 10 exponent + negb ; switch to positive exponent +LBEA3 sta 2,u ; put sign + lda #'E ; put "E" + sta 1,u + lda #'0-1 ; init to ASCII 0 (compensate for INC) +LBEAB inca ; bump digit + subb #10 ; have we hit the correct one yet? + bcc LBEAB ; brif not + addb #'9+1 ; convert units digit to ASCII + std 3,u ; put exponent in output + clr 5,u ; put trailing NUL + bra LBEBC ; go reset pointer +LBEB8 sta ,u ; store last character +LBEBA clr 1,u ; put NUL at the end +LBEBC ldx #STRBUF+3 ; point to start of string + rts +LBEC0 fcb 0x80,0x00,0x00,0x00,0x00 ; packed FP 0.5 +LBEC5 fqb -100000000 + fqb 10000000 + fqb -1000000 + fqb 100000 + fqb -10000 + fqb 1000 + fqb -100 + fqb 10 + fqb -1 +LBEE9 lda FP0EXP ; get exponent of FPA0 + beq LBEEF ; brif 0 - don't flip sign + com FP0SGN ; flip sign +LBEEF rts +; Expand a polynomial of the form +; AQ+BQ^3+CQ^5+DQ^7..... where Q is FPA0 and X points to a table +LBEF0 stx COEFPT ; save coefficient table pointer + jsr LBC2F ; copy FPA0 to FPA3 + bsr LBEFC ; multiply FPA3 by FPA0 + bsr LBF01 ; expand polynomial + ldx #V40 ; point to FPA3 +LBEFC jmp LBACA ; multiply FPA0 by FPA3 +LBEFF stx COEFPT ; save coefficient table counter +LBF01 jsr LBC2A ; move FPA0 to FPA4 + ldx COEFPT ; get the current coefficient + ldb ,x+ ; get the number of entries + stb COEFCT ; save as counter + stx COEFPT ; save new pointer +LBF0C bsr LBEFC ; multiply (X) and FPA0 + ldx COEFPT ; get this coefficient + leax 5,x ; move to next one + stx COEFPT ; save new pointer + jsr LB9C2 ; add (X) to FPA0 + ldx #V45 ; point X to FPA4 + dec COEFCT ; done all coefficients? + bne LBF0C ; brif more left + rts +; RND function +RND jsr LBC6D ; set flags on FPA0 + bmi LBF45 ; brif negative - set seed + beq LBF3B ; brif 0 - do random between 0 and 1 + bsr LBF38 ; convert to integer + jsr LBC2F ; save range value + bsr LBF3B ; get random number + ldx #V40 ; point to FPA3 + bsr LBEFC ; multply (X) by FPA0 + ldx #LBAC5 ; point to FP 1.0 + jsr LB9C2 ; add 1 to FPA0 +LBF38 jmp INT ; return integer value +LBF3B ldx RVSEED+1 ; move variable random number seed to FPA0 + stx FPA0 + ldx RVSEED+3 + stx FPA0+2 +LBF45 ldx RSEED ; move fixed seed to FPA1 + stx FPA1 + ldx RSEED+2 + stx FPA1+2 + jsr LBAD0 ; multiply them + ldd VAD ; get lowest order product bytes + addd #0x658b ; add a constant + std RVSEED+3 ; save it as new seed + std FPA0+2 ; save in result + ldd VAB ; get high order extra product bytes + adcb #0xb0 ; add upper bytes of constant + adca #5 + std RVSEED+1 ; save as new seed + std FPA0 ; save as result + clr FP0SGN ; set result to positive + lda #0x80 ; set exponent to 0 < FPA0 < 1 + sta FP0EXP + lda FPA2+2 ; get a byte from FPA2 + sta FPSBYT ; save as extra precision + jmp LBA1C ; go normalize FPA0 +RSEED fqb 0x40e64dab ; constant random number generator seed +; SIN function +SIN jsr LBC5F ; copy FPA0 to FPA1 + ldx #LBFBD ; point to 2*pi + ldb FP1SGN ; get sign of FPA1 + jsr LBB89 ; divide FPA0 by 2*pi + jsr LBC5F ; copy FPA0 to FPA1 + bsr LBF38 ; convert FPA0 to an integer + clr RESSGN ; set result to positive + lda FP1EXP ; get exponent of FPA1 + ldb FP0EXP ; get exponent of FPA0 + jsr LB9BC ; subtract FPA0 from FPA1 + ldx #LBFC2 ; point to FP 0.25 + jsr LB9B9 ; subtract FPA0 from 0.25 (pi/2) + lda FP0SGN ; get result sign + pshs a ; save it + bpl LBFA6 ; brif positive + jsr LB9B4 ; add 0.5 (pi) to FPA0 + lda FP0SGN ; get sign of result + bmi LBFA9 ; brif negative + com RELFLG ; if 3pi/2 >= arg >= pi/2 +LBFA6 jsr LBEE9 ; flip sign of FPA0 +LBFA9 ldx #LBFC2 ; point to 0.25 + jsr LB9C2 ; add 0.25 (pi/2) to FPA0 + puls a ; get original sign + tsta ; was it positive + bpl LBFB7 ; brif so + jsr LBEE9 ; flip result sign +LBFB7 ldx #LBFC7 ; point to series coefficients + jmp LBEF0 ; go calculate value +LBFBD fcb 0x83,0x49,0x0f,0xda,0xa2 ; 2*pi +LBFC2 fcb 0x7f,0x00,0x00,0x00,0x00 ; 0.25 +; modified taylor series SIN coefficients +LBFC7 fcb 6-1 ; six coefficients + fcb 0x84,0xe6,0x1a,0x2d,0x1b ; -((2pi)^11)/11! + fcb 0x86,0x28,0x07,0xfb,0xf8 ; ((2pi)^9)/9! + fcb 0x87,0x99,0x68,0x89,0x01 ; -((2pi)^7)/7! + fcb 0x87,0x23,0x35,0xdf,0xe1 ; ((2pi)^5)/5! + fcb 0x86,0xa5,0x5d,0xe7,0x28 ; -(2pi)^3)/3! + fcb 0x83,0x49,0x0f,0xda,0xa2 ; 2*pi +; these 12 bytes are unused + fcb 0xa1,0x54,0x46,0x8f,0x13,0x8f,0x52,0x43 + fcb 0x89,0xcd,0xa6,0x81 +; these are the hardware interrupt vectors (coco1/2 only) + fdb SW3VEC + fdb SW2VEC + fdb FRQVEC + fdb IRQVEC + fdb SWIVEC + fdb NMIVEC + fdb RESVEC diff -r 000000000000 -r 605ff82c4618 bas13.s --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bas13.s Sat Dec 08 19:57:01 2018 -0700 @@ -0,0 +1,4071 @@ + *pragma nolist + include defs.s + *pragma list +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; COLOR BASIC ROM +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + org BASIC +; This is the official set of ROM entry points. It is unfortunate that the generic input ("console in") routine is not exposed +; here. It is also unfortunate that no open and close file routines are exposed. This and other oversignts led to a lot of +; software either having to re-invent the wheel or calling directly into the ROM instead of via these official entry points. +POLCAT fdb KEYIN ; indirect jump, get a keystroke +CHROUT fdb PUTCHR ; indirect jump, output character +CSRDON fdb CASON ; indirect jump, turn cassette on and start reading +BLKIN fdb GETBLK ; indirect jump, read a block from tape +BLKOUT fdb SNDBLK ; indirect jump, write a block to tape +JOYIN fdb GETJOY ; indirect jump, read joystick axes +WRTLDR fdb WRLDR ; indirect jump, turn cassette on and write a leader +; Initialization code. +LA00E lds #LINBUF+LBUFMX+1 ; put the stack in the line input buffer which is a safe place for now + lda #0x37 ; enable the cartidge interrupt (to detect autostarting cartridges) + sta PIA1+3 + lda RSTFLG ; get warm start flag + cmpa #0x55 ; is it valid? + bne BACDST ; brif not - cold start + ldx RSTVEC ; get warm start routine pointer + lda ,x ; get first byte of the routine + cmpa #0x12 ; is it NOP? + bne BACDST ; brif not - the routine is invalid so do a cold start + jmp ,x ; transfer control to the warm start routine +; RESET/power on comes here +RESVEC leay LA00E,pcr ; point to warm start check code +LA02A ldx #PIA1 ; point to PIA1 - we're going to rely on the mirroring to reach PIA0 + clr -3,x ; set PIA0 DA to direction mode + clr -1,x ; set PIA0 DB to direction mode + clr -4,x ; set PIA0 DA to inputs + ldd #0xff34 + sta -2,x ; set PIA0 DB to outputs + stb -3,x ; set PIA0 DA to data mode + stb -1,x ; set PIA0 DB to data mode + clr 1,x ; set PIA1 DA to direction mode + clr 3,x ; set PIA1 DB to direction mode + deca + sta ,x ; set PIA1 DA bits 7-1 as output, 0 as input + lda #0xf8 ; set PIA1 DB bits 7-3 as output, 2-0 as input + sta 2,x + stb 1,x ; set PIA1 DA to data mode + stb 3,x ; set PIA1 DB to data mode + clr 2,x ; set VDG to alpha-numeric + ldb #2 ; make RS232 marking ("stop" bit) + stb ,x + ldu #SAMREG ; point to SAM register + ldb #16 ; 16 bits to clear +LA056 sta ,u++ ; clear a bit + decb ; done all? + bne LA056 ; brif not + sta SAMREG+9 ; put display at 0x400 + tfr b,dp ; set direct page to 0 + ldb #4 ; use as mask to check ram size input + clr -2,x ; strobe ram size low + bitb 2,x ; is the input set? + beq LA06E ; brif not + sta -5,u ; program SAM for 16Kx4 RAMs + sta -11,u ; the P bit is required to work with 16Kx4 RAMs + bra LA072 +LA06E nop + nop + sta -3,u ; program SAM for 64Kx1 RAMs +LA072 jmp ,y ; transfer control to startup routine +; Cold start jumps here +BACDST ldx #VIDRAM+1 ; point past the top of the first 1K of memory (for double predec below) +LA077 clr ,--x ; clear a byte (last will actually try clearing LSB of RESET vector in ROM) + leax 1,x ; move forward one byte (will set Z if we're done) + bne LA077 ; brif not donw yet + jsr LA928 ; clear the screen + clr ,x+ ; put the constant zero that lives before the program + stx TXTTAB ; set beginning of program storage +LA084 lda 2,x ; get value from memory + coma ; make it different + sta 2,x ; try putting different into memory + cmpa 2,x ; did it matcH? + bne LA093 ; brif not - we found the end of memory + leax 1,x ; move pointer forward + com 1,x ; restore the original memory contents + bra LA084 ; try another byte +LA093 stx TOPRAM ; save top of memory (one below actual top because we need a byte for VAL() to work) + stx MEMSIZ ; save top of string space + stx STRTAB ; set bottom of allocated string space + leax -200,x ; allocate 200 bytes of string space + stx FRETOP ; set top of actually free memory + tfr x,s ; put the stack there + ldx #LA10D ; point to variable initializer + ldu #CMPMID ; point to variables to initialize (first batch) + ldb #28 ; 28 bytes in first batch + jsr LA59A ; copy bytes to variables + ldu #IRQVEC ; point to variables to initialize (second batch) + ldb #30 ; 30 bytes this time + jsr LA59A ; copy bytes to variables + ldx -12,x ; get SN error address + stx 3,u ; set ECB's command handlers to error + stx 8,u + ldx #RVEC0 ; point to RAM vectors + ldd #0x394b ; write 75 RTS opcodes (25 RAM vectors) +LA0C0 sta ,x+ ; put an RTS + decb ; done? + bne LA0C0 ; brif not + sta LINHDR-1 ; make temporary line header data for line encoding have a nonzero next line pointer + jsr LAD19 ; do a "NEW" + ldx #'E*256+'X ; magic number to detect ECB ROM + cmpx EXBAS ; is there an ECB ROM? + lbeq EXBAS+2 ; brif so - launch it + andcc #0xaf ; start interrupts + ldx #LA147-1 ; point to sign on message + jsr LB99C ; print it out + ldx #BAWMST ; warm start routine address + stx RSTVEC ; set vector there + lda #0x55 ; warm start valid flag + sta RSTFLG ; mark warm start valid + bra LA0F3 ; go to direct mode +; Warm start entry point +BAWMST nop ; valid routine marker + clr DEVNUM ; reset output/input to screen + jsr LAD33 ; do a partial NEW + andcc #0xaf ; start interrupts + jsr LA928 ; clear the screen +LA0F3 jmp LAC73 ; go to direct mode +; FIRQ service routine - this handles starting autostart cartridges +BFRQSV tst PIA1+3 ; is it the cartridge interrupt? + bmi LA0FC ; brif so + rti +LA0FC jsr LA7D1 ; delay for a while + jsr LA7D1 ; delay for another while + leay 0 ; NOTE: the 0 is a placeholder, extended addressing is required + jmp BROMHK +; Variable initializers (second batch) + jmp BIRQSV ; IRQ handler + jmp BFRQSV ; FIRQ handler + jmp LB44A ; default USR() address + fcb 0x80,0x4f,0xc7,0x52,0x59 ; random seed + fcb 0xff ; capslock flag - default to upper case + fdb DEBDEL ; keyboard debounce delay (why is it a variable?) + jmp LB277 ; exponentiation handler vector + fcb 53 ; (command interpretation table) 53 commands + fdb LAA66 ; (command interpretation table) reserved words list (commands) + fdb LAB67 ; (command interpretation table) jump table (commands) + fcb 20 ; (command interpretation table) 20 functions + fdb LAB1A ; (command interpretation table) reserved words list (functions) + fdb LAA29 ; (command interpretation table) jump table (functions) +; This is the signon message. +LA147 fcc 'COLOR BASIC 1.3' + fcb 0x0d + fcc '(C) 1982 TANDY' + fcb 0 +; This is the "invalid colour" CLS easter egg text. Basically 11 wasted bytes +LA166 fcc 'MICROSOFT' + fcb 0x0d,0 +; Read a character from current device and mask off bit 7 (keep it as 7 bit ASCII) +LA171 bsr LA176 ; get character + anda #0x7f ; mask off high bit + rts +; Generic read routine. Reads a character from the device specified by DEVNUM. If no input was available, +; CINBFL will bet nonzero (on EOF). If input was available, CINBFL will be zero. Note that this routine +; has undefined results when called on an output only device. All registers except CC and A are preserved. +LA176 jsr RVEC4 ; do RAM hook + clr CINBFL ; flag data available + tst DEVNUM ; is it keyboard? + beq LA1B1 ; brif so - blink cursor and wait for key press + tst CINCTR ; is there anything in cassette input buffer? + bne LA186 ; brif so + com CINBFL ; flag EOF + rts +; Read character from cassette file +LA186 pshs u,y,x,b ; preserve registers + ldx CINPTR ; get input buffer pointer + lda ,x+ ; get character from buffer + pshs a ; save it for return + stx CINPTR ; save new input buffer pointer + dec CINCTR ; count character just consumed + bne LA197 ; brif buffer is not empty yet + jsr LA635 ; go read another block, if any, to refill the buffer +LA197 puls a,b,x,y,u,pc ; restore registers and return the character +; Blink the cursor. This might be better timed via an interrupt or something. +LA199 dec BLKCNT ; is it time to blink the cursor? + bne LA1AB ; brif not + ldb #11 ; reset blink timer + stb BLKCNT + ldx CURPOS ; get cursor position + lda ,x ; get character at the cursor + adda #0x10 ; move to next color + ora #0x8f ; make sure it's a grahpics block with all elements lit + sta ,x ; put new cursor block on screen +LA1AB ldx #DEBDEL ; we'll use the debounce delay for the cursor blink timer (10ms) +LA1AE jmp LA7D3 ; go count X down +; Blink cursor while waiting for a key press +LA1B1 pshs x,b ; save registers +LA1B3 bsr LA199 ; go do a cursor iteration + bsr KEYIN ; go read a key + beq LA1B3 ; brif no key pressed + ldb #0x60 ; VDG screen space character + stb [CURPOS] ; blank cursor out + puls b,x,pc ; restore registers and return +; This is the actual keyboard polling routine. Returns 0 if no new key is down. Compared to the 1.0 and 1.1 +; ROMs, this routine is quite a lot more compact and robust. +LA1C1 clr PIA0+2 ; strobe all columns + lda PIA0 ; get rows + coma ; bits set if keys down + lsla ; remove the comparator input + beq LA244 ; brif no keys down - don't actually poll the keyboard +KEYIN pshs u,x,b ; save registers + ldu #PIA0 ; point to keyboard PIA + ldx #KEYBUF ; point to state table + clra ; clear carry, set column to 0xff (no strobe) + deca ; (note: deca does not affect C) + pshs x,a ; save column counter and make a couple of holes for temporaries + sta 2,u ; set strobe to no columns +LA1D9 rol 2,u ; move to next column (C is 0 initially, 1 after) + bcc LA220 ; brif we shifted out a 0 - we've done 8 columns + inc 0,s ; bump column counter (first bump goes to 0) + bsr LA23A ; read row data + sta 1,s ; save key data (for debounce check and later saving) + eora ,x ; now bits set if key state changed + anda ,x ; now bits are only set if a key has been pressed + ldb 1,s ; get new key data + stb ,x+ ; save in state table + tsta ; was a key down? + beq LA1D9 ; brif not - do another (nothing above cleared C) + ldb 2,u ; get strobe data + stb 2,s ; save it for debounce check + ldb #0xf8 ; set up so B is 0 after first add +LA1F4 addb #8 ; add 8 for each row + lsra ; did we hit the right row? + bcc LA1F4 ; brif not + addb 0,s ; add in column number + beq LA245 ; brif @ + cmpb #26 ; letter? + bhi LA247 ; brif not + orb #0x40 ; bias into letter range + bsr LA22E ; check for SHIFT + ora CASFLG ; merge in capslock state + bne LA20C ; brif either capslock or SHIFT - keep upper case + orb #0x20 ; move to lower case +LA20C stb 0,s ; save ASCII value + ldx DEBVAL ; get debounce delay + bsr LA1AE ; do the 10ms debounce delay + ldb #0xff ; set strobe to none - only joystick buttons register now + bsr LA238 ; read keyboard + inca ; A now 0 if no buttons down + bne LA220 ; brif button down - return nothing since we have interference +LA21A ldb 2,s ; get column strobe data + bsr LA238 ; read row data + cmpa 1,s ; does it match original read? +LA220 puls a,x ; clean up stack and get return value + bne LA22B ; brif failed debounce or a joystick button down + cmpa #0x12 ; is it SHIFT-0? + bne LA22C ; brif not + com CASFLG ; swap capslock state +LA22B clra ; set no key down +LA22C puls b,x,u,pc ; restore registers and return +LA22E lda #0x7f ; column strobe for SHIFT + sta 2,u ; set column + lda ,u ; get row data + coma ; set if key down + anda #0x40 ; only keep SHIFT state + rts +LA238 stb 2,u ; save strobe data +LA23A lda ,u ; get row data + ora #0x80 ; mask off comparator so it doesn't interfere + tst 2,u ; are we on column 7? + bmi LA244 ; brif not + ora #0xc0 ; also mask off SHIFT +LA244 rts +LA245 ldb #51 ; scan code for @ +LA247 ldx #CONTAB-0x36 ; point to code table + cmpb #33 ; arrows, space, zero? + blo LA264 ; brif so + ldx #CONTAB-0x54 ; adjust to other half of table + cmpb #48 ; ENTER, CLEAR, BREAK, @? + bhs LA264 ; brif so + bsr LA22E ; read shift state + cmpb #43 ; is it a number, colon, semicolon? + bls LA25D ; brif so + eora #0x40 ; invert shift state for others +LA25D tsta ; shift down? + bne LA20C ; brif not - return result + addb #0x10 ; add in offset to shifted character + bra LA20C ; go return result +LA264 lslb ; two entries per key + bsr LA22E ; check SHIFT state + beq LA26A ; brif not shift + incb ; point to shifted entry +LA26A ldb b,x ; get actual key code + bra LA20C ; go return result +CONTAB fcb 0x5e,0x5f ; (^, _) + fcb 0x0a,0x5b ; (LF, [) + fcb 0x08,0x15 ; (BS, ^U) + fcb 0x09,0x5d ; (TAB, ]) + fcb 0x20,0x20 ; + fcb 0x30,0x12 ; <0> (0, ^R) + fcb 0x0d,0x0d ; (CR, CR) + fcb 0x0c,0x5c ; (FF, \) + fcb 0x03,0x03 ; (^C, ^C) + fcb 0x40,0x13 ; <@> (@, ^S) +; Generic output routine. +; Output character in A to the device specified by DEVNUM. All registers are preserved except CC. +; Sending output to a device that does not support output is undefined. +PUTCHR jsr RVEC3 ; call RAM hook + pshs b ; save B + ldb DEVNUM ; get desired device number + incb ; set flags (Z for -1, etc.) + puls b ; restore B + bmi LA2BF ; brif < -1 (line printer) + bne LA30A ; brif > -1 (screen) +; Write character to tape file + pshs x,b,a ; save registers + ldb FILSTA ; get file status + decb ; input file? + beq LA2A6 ; brif so + ldb CINCTR ; get character count + incb ; account for this character + bne LA29E ; brif buffer not full + bsr LA2A8 ; write previously full block to tape +LA29E ldx CINPTR ; get output buffer pointer + sta ,x+ ; put character in output + stx CINPTR ; save new buffer pointer + inc CINCTR ; account for this character +LA2A6 puls a,b,x,pc ; restore registers and return +; Write a block of data to tape. +LA2A8 ldb #1 ; data block type +LA2AA stb BLKTYP ; set block type + ldx #CASBUF ; point to output buffer + stx CBUFAD ; set buffer pointer + ldb CINCTR ; get number of bytes in the block + stb BLKLEN ; set length to write + pshs u,y,a ; save registers + jsr LA7E5 ; write a block to tape + puls a,y,u ; restore registers + jmp LA650 ; reset buffer pointers +; Send byte to line printer +LA2BF pshs x,b,a,cc ; save registers and interrupt status + orcc #0x50 ; disable interrupts +LA2C3 ldb PIA1+2 ; get RS232 status + lsrb ; get status to C + bcs LA2C3 ; brif busy - loop until not busy + bsr LA2FB ; set output to marking + clrb ; transmit one start bit + bsr LA2FD + ldb #8 ; counter for 8 bits +LA2D0 pshs b ; save bit count + clrb ; zero output bits + lsra ; bet output bit to C + rolb ; get output bit to correct bit for output byte + lslb + bsr LA2FD ; transmit bit + puls b ; get back bit counter + decb ; are we done yet? + bne LA2D0 ; brif not + bsr LA2FB ; send stop bit (marking) + puls cc,a ; restore interrupt status and output character + cmpa #0x0d ; carriage return? + beq LA2ED ; brif so + inc LPTPOS ; bump output position + ldb LPTPOS ; get new position + cmpb LPTWID ; end of line? + blo LA2F3 ; brif not +LA2ED clr LPTPOS ; reset position to start of line + bsr LA305 ; do carriage return delay + bsr LA305 +LA2F3 ldb PIA1+2 ; get RS232 status + lsrb ; get status to C + bcs LA2F3 ; brif still busy, keep waiting + puls b,x,pc ; restore registers and return +LA2FB ldb #2 ; set output to high (marking) +LA2FD stb PIA1 ; set RS232 output + bsr LA302 ; do baud delay (first iteration) then fall through for second +LA302 ldx LPTBTD ; get buard rate delay constant + skip2 +LA305 ldx LPTLND ; get carriage return delay constant + jmp LA7D3 ; count X down +; Output character to screen +LA30A pshs x,b,a ; save registers + ldx CURPOS ; get cursor pointer + cmpa #0x08 ; backspace? + bne LA31D ; brif not + cmpx #VIDRAM ; at top of screen? + beq LA35D ; brif so - it's a no-op + lda #0x60 ; VDG space character + sta ,-x ; put a space at previous location and move pointer back + bra LA344 ; save new cursor position and return +LA31D cmpa #0x0d ; carriage return? + bne LA32F ; brif not + ldx CURPOS ; get cursor pointer (why? we already have it) +LA323 lda #0x60 ; VDG space character + sta ,x+ ; put output space + tfr x,d ; see if we at a multiple of 32 now + bitb #0x1f + bne LA323 ; brif not + bra LA344 ; go check for scrolling +LA32F cmpa #0x20 ; control character? + blo LA35D ; brif so + tsta ; is it graphics block? + bmi LA342 ; brif so + cmpa #0x40 ; number or special? + blo LA340 ; brif so (flip "case" bit) + cmpa #0x60 ; upper case alpha? + blo LA342 ; brif so - keep it unmodified + anda #0xdf ; clear bit 5 (inverse video) +LA340 eora #0x40 ; flip inverse video bit +LA342 sta ,x+ ; output character +LA344 stx CURPOS ; save new cursor position + cmpx #VIDRAM+511 ; end of screen? + bls LA35D ; brif not + ldx #VIDRAM ; point to start of screen +LA34E ldd 32,x ; get two characters from next row + std ,x++ ; put them on this row + cmpx #VIDRAM+0x1e0 ; at start of last row on screen? + blo LA34E ; brif not + ldb #0x60 ; VDG space + jsr LA92D ; blank out last line (borrow CLS's loop) +LA35D puls a,b,x,pc ; restore registers and return +; Set up device parameters for output +LA35F jsr RVEC2 ; do the RAM hook dance + pshs x,b,a ; save registers + clr PRTDEV ; flag device as a screen + lda DEVNUM ; get devicenumber + beq LA373 ; brif screen + inca ; is it tape? + beq LA384 ; brif so + ldx LPTCFW ; get tab width and last tab stop for printer + ldd LPTWID ; get line width and current position for printer + bra LA37C ; set parameters +LA373 ldb CURPOS+1 ; get LSB of cursor position + andb #0x1f ; now we have the offset into the line + ldx #0x1010 ; 16 character tab, position 16 is last tab stop + lda #32 ; screen is 32 characters wide +LA37C stx DEVCFW ; save tab width and last tab stop for active device + stb DEVPOS ; save line position for current device + sta DEVWID ; save line width for current device + puls a,b,x,pc ; restore registers and return +LA384 com PRTDEV ; flag device as non-display + ldx #0x0100 ; tab width is 1, last tab field is 0 + clra ; line width is 0 + clrb ; character position on line is 0 + bra LA37C ; go set parameters +; This is the line input routine used for reading lines for Basic, both in immediate mode and for +; INPUT and LINE INPUT. Exit with C set if terminated by BREAK and C clear if terminated by ENTER. +; The actualy entry point is LA390. Note that this routine echoes to *all* devices. +LA38D jsr LA928 ; clear screen (CLEAR key handling) +LA390 jsr RVEC12 ; do the RAM hook dance + clr IKEYIM ; reset cached input character from BREAK check + ldx #LINBUF+1 ; point to line input buffer (input pointer) + ldb #1 ; Number of characters in line (we start at 1 so BS handling is easier) +LA39A jsr LA171 ; get an input character, only keep low 7 bits + tst CINBFL ; is it EOF? + bne LA3CC ; brif EOF + tst DEVNUM ; is it keyboard input? + bne LA3C8 ; brif not - don't do line editing + cmpa #0x0c ; form feed (CLEAR)? + beq LA38D ; brif so - clear screen and reset + cmpa #0x08 ; backspace? + bne LA3B4 ; brif not + decb ; move back one character + beq LA390 ; brif we were at the start of the line - reset and start again + leax -1,x ; move input pointer back + bra LA3E8 ; echo the backspace and continue +LA3B4 cmpa #0x15 ; SHIFT-LEFT (kill line)? + bne LA3C2 ; brif not +LA3B8 decb ; at start of line? + beq LA390 ; brif so - reset and restart + lda #0x08 ; echo a backspace + jsr PUTCHR + bra LA3B8 ; see if we've erased everything yet +LA3C2 cmpa #0x03 ; BREAK? + orcc #1 ; set C if it is (only need Z for the next test + beq LA3CD ; brif BREAK - exit +LA3C8 cmpa #0x0d ; ENTER (CR) + bne LA3D9 ; brif not +LA3CC clra ; clear carry (it might not be clear on EOF) +LA3CD pshs cc ; save ENTER/BREAK flag + jsr LB958 ; echo a carriage return + clr ,x ; make sure we have a NUL at the end of the buffer + ldx #LINBUF ; point to input buffer + puls cc,pc ; restore ENTER/BREAK flag and return +LA3D9 cmpa #0x20 ; control character? + blo LA39A ; brif so - skip it + cmpa #'z+1 ; above z? + bhs LA39A ; brif so - ignore it + cmpb #LBUFMX ; is the buffer full? + bhs LA39A ; brif so - ignore extra characters + sta ,x+ ; put character in the buffer + incb ; bump character count +LA3E8 jsr PUTCHR ; echo character + bra LA39A ; go handle next input character +; Check if DEVNUM Is valid for reading. Raise FM error if open but not for reading. NO error if not open. +LA3ED jsr RVEC5 ; do the RAM hook dance + lda DEVNUM ; get device number + beq LA415 ; brif keyboard - always valid + inca ; is it tape? + bne LA403 ; brif not + lda FILSTA ; get tape file status + bne LA400 ; brif file is open +LA3FB ldb #22*2 ; raise NO error + jmp LAC46 +LA400 deca ; is it in input mode? + beq LA415 ; brif so +LA403 jmp LA616 ; raise FM error +; Check if DEVNUM is valid for writing. Raise FM error if open but not for writing. NO error if not open. +LA406 jsr RVEC6 ; do the RAM hook dance + lda DEVNUM ; get device number + inca ; is it tape? + bne LA415 ; brif not + lda FILSTA ; get file status + beq LA3FB ; brif not open + deca ; is it open for reading? + beq LA403 ; brif so - bad mode +LA415 rts +; CLOSE command +CLOSE beq LA426 ; brif no file specified - close all files + jsr LA5A5 ; parse device number +LA41B bsr LA42D ; close specified file + jsr GETCCH ; is there more? + beq LA44B ; brif not + jsr LA5A2 ; check for comma and parse another device number + bra LA41B ; go close this one +; Close all files handler. +LA426 jsr RVEC7 ; Yup. The RAM hook dance. + lda #-1 ; start with tape file + sta DEVNUM +; Close file specified in DEVNUM. Note that this never fails. +LA42D jsr RVEC8 ; You know it. RAM hook. + lda DEVNUM ; get device we're closing + clr DEVNUM ; reset to screen/keyboard + inca ; is it tape? + bne LA44B ; brif not + lda FILSTA ; get file status + cmpa #2 ; is it output? + bne LA449 ; brif not + lda CINCTR ; is there anything waiting to be written? + beq LA444 ; brif not + jsr LA2A8 ; write final block of data +LA444 ldb #0xff ; write EOF block + jsr LA2AA +LA449 clr FILSTA ; mark tape file closed +LA44B rts +; CSAVE command +CSAVE jsr LA578 ; parse filename + jsr GETCCH ; see what we have after the file name + beq LA469 ; brif none + jsr LB26D ; make sure there's a comma + ldb #'A ; make sure there's an A after + jsr LB26F + bne LA44B ; brif not end of line + clra ; file type 0 (basic program) + jsr LA65C ; write out header block + lda #-1 ; set output to tape + sta DEVNUM + clra ; set Z so we list the whole program + jmp LIST ; go list the program to tape +LA469 clra ; file type 0 (basic program) + ldx ZERO ; set to binary file mode + jsr LA65F ; write header block + clr FILSTA ; close files + inc BLKTYP ; set block type to data + jsr WRLDR ; write out a leader + ldx TXTTAB ; point to start of program +LA478 stx CBUFAD ; set buffer location + lda #255 ; block size to 255 bytes (max size) + sta BLKLEN + ldd VARTAB ; get end of program + subd CBUFAD ; how much is left? + beq LA491 ; brif we have nothing left + cmpd #255 ; do we have a full block worth? + bhs LA48C ; brif so + stb BLKLEN ; save actual remainder as block length +LA48C jsr SNDBLK ; write a block out + bra LA478 ; go do another block +LA491 neg BLKTYP ; set block type to 0xff (EOF) + clr BLKLEN ; no data in EOF block + jmp LA7E7 ; write EOF, stop tape, and return +; CLOAD and CLOADM commands +CLOAD clr FILSTA ; close tape file + cmpa #'M ; is it ClOADM? + beq LA4FE ; brif so + leas 2,s ; clean up stack + jsr LA5C5 ; parse file name + jsr LA648 ; go find the file + tst CASBUF+10 ; is it binary? + beq LA4C8 ; brif so + lda CASBUF+9 ; is it ASCII? + beq LA4CD ; brif not + jsr LAD19 ; clear out existing program + lda #-1 ; set up for reading from tape + sta DEVNUM + inc FILSTA ; set tape file to input + jsr LA635 ; go read first block + jmp LAC7C ; go to immediate mode to read in the program +; This is the EOF handler for immediate mode. It's rather assinine for this to be located here. It is +; probably an artifact of a substantially different code layout prior to compacting the ROM to fit in +; 8K. +LA4BF jsr RVEC13 ; do the RAM hook dance + jsr LA42D ; close file + jmp LAC73 ; go back to immediate mode +LA4C8 lda CASBUF+8 ; get file type + beq LA4D0 ; brif basic program +LA4CD jmp LA616 ; raise FM error +LA4D0 jsr LAD19 ; erase existing program + jsr CASON ; start reading tape + ldx TXTTAB ; get start of program storage +LA4D8 stx CBUFAD ; set load address for block + ldd CBUFAD ; get start of block + inca ; bump by 256 + jsr LAC37 ; check if there's room for a maximum sized block of 255 + jsr GETBLK ; go read a block + bne LA4F8 ; brif there was an error during reading + lda BLKTYP ; get type of block read + beq LA4F8 ; brif header block - IO error + bpl LA4D8 ; brif data block - read another + stx VARTAB ; save new end of program + bsr LA53B ; stop tape + ldx #LABED-1 ; point to "OK" prompt + jsr LB99C ; show prompt + jmp LACE9 ; reset various things and return +LA4F8 jsr LAD19 ; clear out partial program load +LA4FB jmp LA619 ; raise IO error +; This is the CLOADM command +LA4FE jsr GETNCH ; eat the "M" + bsr LA578 ; parse file name + jsr LA648 ; go find the file +LA505 ldx ZERO ; default offset is 0 + jsr GETCCH ; see if there's something after the file name + beq LA511 ; brif no offset + jsr LB26D ; make sure there's a comma + jsr LB73D ; evaluate offset to X +LA511 lda CASBUF+8 ; get file mode + cmpa #2 ; M/L program? + bne LA4CD ; brif not - FM error + ldd CASBUF+11 ; get load address + leau D,x ; add in offset + stu EXECJP ; set EXEC default address + tst CASBUF+10 ; is it binary? + bne LA4CD ; brif not + ldd CASBUF+13 ; get load address + leax d,x ; add in offset + stx CBUFAD ; set buffer address for loading + jsr CASON ; start up tape +LA52E jsr GETBLK ; read a block + bne LA4FB ; brif error reading + stx CBUFAD ; save new load address + tst BLKTYP ; set flags on block type + beq LA4FB ; brif another header - IO error + bpl LA52E ; brif it was data - read more +LA53B jmp LA7E9 ; turn off tape and return +; The EXEC command +EXEC beq LA545 ; brif no argument - use default address + jsr LB73D ; evaluate EXEC address to X + stx EXECJP ; set new default EXEC address +LA545 jmp [EXECJP] ; transfer control to execution address +; Break check for LIST so it doesn't interrupt ASCII saves. Why is this here instead of with the rest of the break +; check logic or packaged up with LIST? +LA549 jsr RVEC11 ; do the RAM hook dance + lda DEVNUM ; get device number + inca ; is it tape? + beq LA5A1 ; brif so - don't do break check + jmp LADEB ; do the actual break check +; Evaluate an expression and make sure it is within the limits of the screen and sets the cursor position. +; This really should be located with the PRINT command. +LA554 jsr LB3E4 ; evaluate a positive expression to D + subd #511 ; is it within bounds? + lbhi LB44A ; brif not - error out + addd #VIDRAM+511 ; adjust to be within the screen (and undo the SUBD above) + std CURPOS ; set cursor position + rts +; INKEY$ function +INKEY lda IKEYIM ; was a key down during break check? + bne LA56B ; brif so + jsr KEYIN ; poll the keyboard +LA56B clr IKEYIM ; reset the break check cache + sta FPA0+3 ; store result for later return + lbne LB68F ; brif a key was down - return it as a string + sta STRDES ; set string length to 0 (no key down) + jmp LB69B ; return the NULL string +; Parse a filename +LA578 ldx #CFNBUF ; point to file name buffer + clr ,x+ ; zero out file name length + lda #0x20 ; space character to initialize file name +LA57F sta ,x+ ; put a space in the buffer + cmpx #CASBUF ; at end of file name? + bne LA57F ; brif not + jsr GETCCH ; get input character + beq LA5A1 ; brif no name present + jsr LB156 ; evaluate the file name expression + jsr LB654 ; point to start of the file name + ldu #CFNBUF ; point to file name buffer + stb ,u+ ; save string length + beq LA5A1 ; brif empty - we're done + skip2 +LA598 ldb #8 ; copy 8 bytes +; Move B bytes from (X) to (U) +LA59A lda ,x+ ; copy a byte + sta ,u+ + decb ; done yet? + bne LA59A ; brif not +LA5A1 rts +; Parse a device number and check validity +LA5A2 jsr LB26D ; check for comma and SN error if not +LA5A5 cmpa #'# ; do we have a #? + bne LA5AB ; brif not (it's optional) + jsr GETNCH ; munch the # +LA5AB jsr LB141 ; evaluate the expression +LA5AE jsr INTCNV ; convert it to an integer in D + rolb ; move sign of B into C + adca #0 ; add sign of B to A + bne LA61F ; brif A doesn't match the sign of B + rorb ; restore B (ADCA will have set C if B was negative) + stb DEVNUM ; set device number + jsr RVEC1 ; do the RAM hook dance + beq LA5C4 ; brif device number set to screen/keyboard (valid) + bpl LA61F ; brif not negative (not valid) + cmpb #-2 ; is it printer or tape? + blt LA61F ; brif not (not valid) +LA5C4 rts +; Read file name from the line and do an error if anything follows it +LA5C5 bsr LA578 ; parse file name + jsr GETCCH ; set flags on current character +LA5C9 beq LA5C4 ; brif nothing there - it's good + jmp LB277 ; raise SN error +; EOF functoin +EOF jsr RVEC14 ; do the RAM hook dance + lda DEVNUM ; get device number + pshs a ; save it (so we can restore it later) + bsr LA5AE ; check the device number (which is in FPA0) + jsr LA3ED ; check validity for reading +LA5DA clrb ; not EOF = 0 (FALSE) + lda DEVNUM ; get device number + beq LA5E4 ; brif keyboard - never EOF + tst CINCTR ; is there anything in the input buffer? + bne LA5E4 ; brif so - not EOF + comb ; set EOF flag to -1 (true) +LA5E4 puls a ; get back original device + sta DEVNUM ; restore it +LA5E8 sex ; sign extend result to 16 bits + jmp GIVABF ; go return the result +; SKIPF command +SKIPF bsr LA5C5 ; parse file name + bsr LA648 ; look for the file + jsr LA6D1 ; read the file + bne LA619 ; brif error reading file + rts +; OPEN command +OPEN jsr RVEC0 ; do the RAM hook dance + jsr LB156 ; get file status (input/output) + jsr LB6A4 ; get first character of status string + pshs b ; save status + bsr LA5A2 ; parse a comma then the device number + jsr LB26D ; make sure there's a comma + bsr LA5C5 ; parse the file name + lda DEVNUM ; get device number of the file + clr DEVNUM ; reset actual device to the screen + puls b ; get back status + cmpb #'I ; INPUT? + beq LA624 ; brif so - open a file for INPUT + cmpb #'O ; OUTPUT? + beq LA658 ; brif so - open a file for OUTPUT +LA616 ldb #21*2 ; raise FM error + skip2 +LA619 ldb #20*2 ; raise I/O error + skip2 +LA61C ldb #18*2 ; raise AO error + skip2 +LA61F ldb #19*2 ; raise DN error + jmp LAC46 +LA624 inca ; are we opening the tape? + bmi LA616 ; brif printer - FM error; printer can't be opened for READ + bne LA657 ; brif screen - screen is always open + bsr LA648 ; read header block + lda CASBUF+9 ; clear A if binary or machine language file + anda CASBUF+10 + beq LA616 ; bad file mode if not data file + inc FILSTA ; open file for input +LA635 jsr LA701 ; start tape, read block + bne LA619 ; brif error during read + tst BLKTYP ; check block type + beq LA619 ; brif header block - something's wrong + bmi LA657 ; brif EOF + lda BLKLEN ; get length of block + beq LA635 ; brif empty block - read another +LA644 sta CINCTR ; set buffer count + bra LA652 ; reset buffer pointer +LA648 tst FILSTA ; is the file open? + bne LA61C ; brif so - AO error + bsr LA681 ; search for file + bne LA619 ; brif error on read +LA650 clr CINCTR ; mark buffer empty +LA652 ldx #CASBUF ; set buffer pointer to start of buffer + stx CINPTR +LA657 rts +LA658 inca ; check for tape device + bne LA657 ; brif not tape (nothing doing - it's always open) + inca ; make file type 1 +LA65C ldx #0xffff ; ASCII and data mode +LA65F tst FILSTA ; is file open? + bne LA61C ; brif so - raise error + ldu #CASBUF ; point to tape buffer + stu CBUFAD ; set address of block to write + sta 8,u ; set file type + stx 9,u ; set ASCII flag and mode + ldx #CFNBUF+1 ; point to file name + jsr LA598 ; move file name to the tape buffer + clr BLKTYP ; set for header block + lda #15 ; 15 bytes in a header block + sta BLKLEN ; set block length + jsr LA7E5 ; write the block + lda #2 ; set file type to output + sta FILSTA + bra LA650 ; reset file pointers +; Search for correct cassette file name +LA681 ldx #CASBUF ; point to cassette buffer + stx CBUFAD ; set location to read blocks to +LA686 lda CURLIN ; are we in immediate mode? + inca + bne LA696 ; brif not + jsr LA928 ; clear screen + ldx CURPOS ; get start of screen (set after clear) + ldb #'S ; for "searching" + stb ,x++ ; put it on the screen + stx CURPOS ; save cursor position to be one past the search indicator +LA696 bsr LA701 ; read a block + orb BLKTYP ; merge error flag with block type + bne LA6D0 ; brif error or not header + ldx #CASBUF ; point to block just read + ldu #CFNBUF+1 ; point to the desired name + ldb #8 ; compare 8 characters + clr ,-s ; set flag to "match" +LA6A6 lda ,x+ ; get character from just read block + ldy CURLIN ; immediate mode? + leay 1,y + bne LA6B4 ; brif not + clr DEVNUM ; set output to screen + jsr PUTCHR ; display character +LA6B4 suba ,u+ ; subtract from desired file name (nonzero if no match) + ora ,s ; merge with match flag + sta ,s ; save new match flag (will be nonzero if any character differs) + decb ; done all characters? + bne LA6A6 ; brif not - do another + lda ,s+ ; get match flag (and set flags) + beq LA6CB ; brif we have a match + tst -9,u ; did we actually have a file name or will any file do? + beq LA6CB ; brif any file will do + bsr LA6D1 ; go read past the file + bne LA6D0 ; return on error + bra LA686 ; keep looking +LA6CB lda #'F ; for "found" + bsr LA6F8 ; put "F" on screen + clra ; set Z to indicat eno errors +LA6D0 rts +LA6D1 tst CASBUF+10 ; check type of file + bne LA6DF ; brif "blocked" file + jsr CASON ; turn on tape +LA6D9 bsr GETBLK ; read a block + bsr LA6E5 ; error or EOF? + bra LA6D9 ; read another block +LA6DF bsr LA701 ; read a single block + bsr LA6E5 ; error or EOF? + bra LA6DF ; read another block +LA6E5 bne LA6ED ; got error reading block + lda BLKTYP ; check block type + nega ; A is 0 now if EOF + bmi LA700 ; brif not end of file + deca ; clear error indicator +LA6ED sta CSRERR ; set error flag + leas 2,s ; don't return to original caller + bra LA705 ; turn off motor and return +LA6F3 lda VIDRAM ; get first char on screen + eora #0x40 ; flip case +LA6F8 ldb CURLIN ; immediate mode? + incb + bne LA700 ; brif not + sta VIDRAM ; save flipped case character +LA700 rts +; Read a single block from tape (for a "blocked" file) +LA701 bsr CASON ; start tape going + bsr GETBLK ; read block +LA705 jsr LA7E9 ; stop tape + ldb CSRERR ; get error status + rts +; Read a block from tape - this does the heavy lifting +GETBLK orcc #0x50 ; disable interrupts (timing is important) + bsr LA6F3 ; reverse video of upper left character in direct mode + ldx CBUFAD ; point to destination buffer + clra ; reset read byte +LA712 bsr LA755 ; read a bit + rora ; move bit into accumulator + cmpa #0x3c ; have we synched on the start of the block data yet? + bne LA712 ; brif not + bsr LA749 ; read block type + sta BLKTYP + bsr LA749 ; get block size + sta BLKLEN + adda BLKTYP ; accumulate checksum + sta CCKSUM ; save current checksum + lda BLKLEN ; get back count + sta CSRERR ; initialize counter; we use this since it will be ovewritten later anyway + beq LA73B ; brif empty block +LA72B bsr LA749 ; read a byte + sta ,x ; save in buffer + cmpa ,x+ ; make sure it wrote + bne LA744 ; brif error if it didn't match + adda CCKSUM ; accumulate checksum + sta CCKSUM + dec CSRERR ; read all bytes? + bne LA72B ; brif not +LA73B bsr LA749 ; read checksum from tape + suba CCKSUM ; does it match? + beq LA746 ; brif so + lda #1 ; checksum error flag + skip2 +LA744 lda #2 ; non-RAM error flag +LA746 sta CSRERR ; save error status + rts +LA749 lda #8 ; read 8 bits + sta CPULWD ; initialize counter +LA74D bsr LA755 ; read a bit + rora ; put it into accumulator + dec CPULWD ; got all 8 bits? + bne LA74D ; brif not + rts +LA755 bsr LA75D ; get time between transitions + ldb CPERTM ; get timer + decb + cmpb CMPMID ; set C if timer is below the transition point - high or 1; clear otherwise + rts +LA75D clr CPERTM ; reset timer + tst CBTPHA ; check which phase we synched on + bne LA773 ; brif HI-LO synch +LA763 bsr LA76C ; read input + bcs LA763 ; brif still high +LA767 bsr LA76C ; read input + bcc LA767 ; brif still low + rts +LA76C inc CPERTM ; bump timer + ldb PIA1 ; get input bit to C + rorb + rts +LA773 bsr LA76C ; read input + bcc LA773 ; brif still low +LA777 bsr LA76C ; read output + bcs LA777 ; brif still high + rts +; Start tape and look for sync bytes +CASON orcc #0x50 ; disable interrupts + bsr LA7CA ; turn on tape + clr CPULWD ; reset timer +LA782 bsr LA763 ; wait for low-high transition +LA784 bsr LA7AD ; wait for it to go low again + bhi LA797 ; brif in range for 1200 Hz +LA788 bsr LA7A7 ; wait for it to go high again + blo LA79B ; brif in range for 2400 Hz + dec CPULWD ; decrement counter (synched on low-high) + lda CPULWD ; get counter + cmpa #-96 ; have we seen 96 1-0-1-0 patterns (48 0x55s)? +LA792 bne LA782 ; brif not - wait some more + sta CBTPHA ; save phase we synched on + rts +LA797 bsr LA7A7 ; wait for it to go high again + bhi LA784 ; brif another 1200 Hz, 2 in a row, try again +LA79B bsr LA7AD ; wait for it to go low again + blo LA788 ; brif another 2400 Hz; go try again for high + inc CPULWD ; bump counter + lda CPULWD ; get counter + suba #96 ; set 0 if we've seen enought 0-1-0-1 patterns (0xaa) + bra LA792 ; set phase and return or keep waiting +LA7A7 clr CPERTM ; reset period timer + bsr LA767 ; wait for high + bra LA7B1 ; set flags on result +LA7AD clr CPERTM ; reset period timer + bsr LA777 ; wait for low +LA7B1 ldb CPERTM ; get period count + cmpb CMP0 ; is it too long for 1200Hz? + bhi LA7BA ; brif so - reset counts + cmpb CMP1 ; set C if 2400Hz, clear C if 1200 Hz + rts +LA7BA clr CPULWD ; reset sync counter (too slow or drop out) + rts +; MOTOR command +MOTOR tfr a,b ; save ON/OFF + jsr GETNCH ; eat the ON/OFF token + cmpb #0xaa ; OFF? + beq LA7E9 ; brif so - turn off tape + cmpb #0x88 ; ON? + jsr LA5C9 ; SN error if no match +; Turn on tape +LA7CA lda PIA1+1 ; get motor control value + ora #8 ; turn on bit 3 (starts motor) + bsr LA7F0 ; put it back (dumb but it saves a byte) +LA7D1 ldx ZERO ; maximum delay timer +LA7D3 leax -1,x ; count down + bne LA7D3 ; brif not at 0 yet + rts +; Write a synch leader to tape +WRLDR orcc #0x50 ; disable interrupts + bsr LA7CA ; turn on tape + ldx SYNCLN ; get count of 0x55s to write +LA7DE bsr LA828 ; write a 0x55 + leax -1,x ; done? + bne LA7DE ; brif not + rts +; Write sync bytes and a block, then stop tape +LA7E5 bsr WRLDR ; write sync +LA7E7 bsr SNDBLK ; write block +; Turn off tape +LA7E9 andcc #0xaf ; enable interrupts + lda PIA1+1 ; get control register + anda #0xf7 ; disable motor bit +LA7F0 sta PIA1+1 ; set motor enable bit + rts +; Write a block to tape. +SNDBLK orcc #0x50 ; disable interrupts + ldb BLKLEN ; get block size + stb CSRERR ; initialize character counter + lda BLKLEN ; initialize checksum + beq LA805 ; brif empty block + ldx CBUFAD ; point to tape buffer +LA800 adda ,x+ ; accumulate checksum + decb ; end of block data? + bne LA800 ; brif not +LA805 adda BLKTYP ; accumulate block type into checksum + sta CCKSUM ; save calculated checksum + ldx CBUFAD ; point to buffer + bsr LA828 ; send a 0x55 + lda #0x3c ; and then a 0x3c + bsr LA82A + lda BLKTYP ; send block type + bsr LA82A + lda BLKLEN ; send block size + bsr LA82A + tsta ; empty block? + beq LA824 ; brif so +LA81C lda ,x+ ; send character from block data + bsr LA82A + dec CSRERR ; are we done yet? + bne LA81C ; brif not +LA824 lda CCKSUM ; send checksum + bsr LA82A +LA828 lda #0x55 ; send a 0x55 +LA82A pshs a ; save output byte + ldb #1 ; initialize bit probe +LA82E lda CLSTSN ; get ending value of last cycle + sta PIA1 ; set DA + ldy #LA85C ; point to sine wave table + bitb ,s ; is bit set? + bne LA848 ; brif so - do high frequency +LA83B lda ,y+ ; get next sample (use all for low frequency) + cmpy #LA85C+36 ; end of table? + beq LA855 ; brif so + sta PIA1 ; set output sample + bra LA83B ; do another sample +LA848 lda ,y++ ; get next sample (use every other for high frequency) + cmpy #LA85C+36 ; end of table? + beq LA855 ; brif so + sta PIA1 ; send output sample + bra LA848 ; do another sample +LA855 sta CLSTSN ; save last sample that *would* have been sent + lslb ; shift mask to next bit + bcc LA82E ; brif not done all 8 bits + puls a,pc ; get back original character and return +; This is the sample table for the tape sine wave +LA85C fcb 0x82,0x92,0xaa,0xba,0xca,0xda + fcb 0xea,0xf2,0xfa,0xfa,0xfa,0xf2 + fcb 0xea,0xda,0xca,0xba,0xaa,0x92 + fcb 0x7a,0x6a,0x52,0x42,0x32,0x22 + fcb 0x12,0x0a,0x02,0x02,0x02,0x0a + fcb 0x12,0x22,0x32,0x42,0x52,0x6a +; SET command +SET bsr LA8C1 ; get absolute screen position of graphics block + pshs x ; save character location + jsr LB738 ; evaluate comma then expression in B + puls x ; get back character pointer + cmpb #8 ; valid colour? + bhi LA8D5 ; brif not + decb ; normalize colours + bmi LA895 ; brif colour 0 (use current colour) + lda #0x10 ; 16 patterns per colour + mul + bra LA89D ; go save the colour +LA895 ldb ,x ; get current value + bpl LA89C ; brif not grahpic + andb #0x70 ; keep only the colour + skip1 +LA89C clrb ; reset block to all black +LA89D pshs b ; save colour + bsr LA90D ; force a ) + lda ,x ; get current screen value + bmi LA8A6 ; brif graphic block already + clra ; force all pixels off +LA8A6 anda #0x0f ; keep only pixel data + ora GRBLOK ; set the desired pixel + ora ,s+ ; merge with desired colour +LA8AC ora #0x80 ; force it to be a graphic block + sta ,x ; put new block on screen + rts +; RESET command +RESET bsr LA8C1 ; get address of desired block + bsr LA90D ; force a ) + clra ; zero block (no pixels) + ldb ,x ; is it graphics? + bpl LA8AC ; brif not - just blank the block + com GRBLOK ; invert pixel data + andb GRBLOK ; turn off the desired pixel + stb ,x ; put new pixel data on screen + rts +; Parse SET/RESET/POINT coordinates except for closing ) +LA8C1 jsr LB26A ; make sure it starts with ( +LA8C4 jsr RVEC21 ; do the RAM hook dance + jsr LB70B ; get first coordinate + cmpb #63 ; valid horizontal coordinate + bhi LA8D5 ; brif out of range + pshs b ; save horizontal coordinate + jsr LB738 ; look for , followed by vertical coordinate + cmpb #31 ; in range for vertical? +LA8D5 bhi LA948 ; brif not + pshs b ; save vertical coordinate + lsrb ; divide by two (two blocks per row) + lda #32 ; 32 bytes per row + mul ; now we have the offset into video RAM + ldx #VIDRAM ; point to start of screen + leax d,x ; now X points to the correct character row + ldb 1,s ; get horizontal coordinate + lsrb ; divide by two (two per character cell) + abx ; now we're pointing to the correct character cell + puls a,b ; get back coordinates (vertical in A) + anda #1 ; keep only row offset of vertical + rorb ; get column offset of horizontal to C + rola ; now we have "row * 2 + col" in A + ldb #0x10 ; make a bit mask (one bit left of first pixel) +LA8EE lsrb ; move mask right + deca ; at the right pixel? + bpl LA8EE ; brif not + stb GRBLOK ; save graphics block mask + rts +; POINT function +POINT bsr LA8C4 ; evaluate coordinates + ldb #0xff ; default colour value is -1 (not graphics) + lda ,x ; get character + bpl LA90A ; brif not graphics + anda GRBLOK ; is desired pixel set? + beq LA909 ; brif not - return 0 for "black" + ldb ,x ; get graphics data + lsrb ; shift right 4 to get colour in low bits + lsrb + lsrb + lsrb + andb #7 ; lose the graphics block bias +LA909 incb ; shift colours into 1 to 8 range +LA90A jsr LA5E8 ; convert B to floating point +LA90D jmp LB267 ; make sure we have a ) and return +; CLS command +CLS jsr RVEC22 ; do the RAM hook dance +LA913 beq LA928 ; brif no colour - just do a basic screen clear + jsr LB70B ; evaluate colour number + cmpb #8 ; valid colour? + bhi LA937 ; brif not - do the easter egg + tstb ; color 0? + beq LA925 ; brif so + decb ; normalize to 0 based colour numbers + lda #0x10 ; 16 blocks per colour + mul ; now we have the base code for that colour + orb #0x0f ; set all pixels +LA925 orb #0x80 ; make it a graphics block + skip2 +LA928 ldb #0x60 ; VDG screen space character + ldx #VIDRAM ; point to start of screen +LA92D stx CURPOS ; set cursor position +LA92F stb ,x+ ; blank a character + cmpx #VIDRAM+511 ; end of screen? + bls LA92F ; brif not + rts +LA937 bsr LA928 ; clear te screen + ldx #LA166-1 ; point to the easter egg + jmp LB99C ; go display it +; Evaluate an expression to B, prefixed by a comma, and do FC error if 0 +LA93F jsr LB26D ; force a comma +LA942 jsr LB70B ; evaluate expression to B + tstb ; is it 0? + bne LA984 ; brif not - return +LA948 jmp LB44A ; raise FC error +; SOUND command +SOUND bsr LA942 ; evaluate frequency + stb SNDTON ; save it + bsr LA93F ; evaluate duration (after a comma) +LA951 lda #4 ; constant factor for duration (each increment is 1/15 of a second) + mul + std SNDDUR ; save length of sound (IRQ will count it down) + lda PIA0+3 ; enable 60 Hz interrupt + ora #1 + sta PIA0+3 + clr ARYDIS ; clear array disable flag for some reason + bsr LA9A2 ; connect DAC to MUX output + bsr LA976 ; turn on sound +LA964 bsr LA985 ; store mid range output value and delay + lda #0xfe ; store high value and delay + bsr LA987 + bsr LA985 ; store mid range value and delay + lda #2 ; store low value and delay + bsr LA987 + ldx SNDDUR ; has timer expired? + bne LA964 ; brif not, do another wave +; Disable sound output +LA974 clra ; bit 3 to 0 will disable output + skip2 +; Enable sound output +LA976 lda #8 ; bit 3 set to enable output + sta ,-s ; save desired value + lda PIA1+3 ; get control register value + anda #0xf7 ; reset value + ora ,s+ ; set to desired value + sta PIA1+3 ; set new sound output status +LA984 rts +LA985 lda #0x7e ; mid range value for DAC +LA987 sta PIA1 ; set DAC output value + lda SNDTON ; get frequency +LA98C inca ; increment it (gives shorter count with higher values, so higher frequencies work) + bne LA98C ; brif not done yet + rts +; AUDIO command +AUDIO tfr a,b ; save ON/OFF token + jsr GETNCH ; munch the ON/OFF token + cmpb #0xaa ; OFF? + beq LA974 ; brif so + subb #0x88 ; ON? + jsr LA5C9 ; do SN error if not + incb ; now B is 1 - cassette sound source + bsr LA9A2 ; set MUX input to tape + bra LA976 ; enable sound +; Set MUX source to value in B +LA9A2 ldu #PIA0+1 ; point to PIA0 control register A + bsr LA9A7 ; program bit 0 then fall through for bit 1 +LA9A7 lda ,u ; get control register value + anda #0xf7 ; reset mux control bit + asrb ; shift desired value to C + bcc LA9B0 ; brif this bit is clear + ora #8 ; set the bit +LA9B0 sta ,u++ ; set register value and move to next register + rts +; IRQ service routine +BIRQSV lda PIA0+3 ; check for VSYNC interrupt + bpl LA9C5 ; brif not - return. BUG: should clear HSYNC interrupt status first + lda PIA0+2 ; clear VSYNC interrupt status + ldx >SNDDUR ; are we counting down for SOUND? (force extended in case DP is modified) + beq LA9C5 ; brif not + leax -1,x ; count down one tick + stx >SNDDUR ; save new count (forced extended in case DP is modified) +LA9C5 rti +; JOYSTK function +JOYSTK jsr LB70E ; evaluate which joystick axis is desired + cmpb #3 ; valid axis? + lbhi LB44A ; brif not + tstb ; want axis 0? + bne LA9D4 ; brif not + bsr GETJOY ; read axis data if axis 0 +LA9D4 ldx #POTVAL ; point to axis values + ldb FPA0+3 ; get desired axis + ldb b,x ; get axis value + jmp LB4F3 ; return value +; Read all four joystick axes. Note that this routine will try 10 times to get a value that matches +; the value obtained during the *previous call to this routine*. Thus, if the axis value has changed, +; this routine will do the read *ten times* before just returning the last value. This is assininely +; slow and probably a bug since it seems more logical to look for two matching reads in a row. Note +; also that this routine should be using PSHS and PULS but it doesn't. +GETJOY bsr LA974 ; turn off sound + ldx #POTVAL+4 ; point to the end of the axis data (we'll work backwards) + ldb #3 ; start with axis 3 +LA9E5 lda #10 ; 10 tries to see if we match *the last call* to this routine + std ,--s ; save retry counter and axis number + bsr LA9A2 ; set MUX for the correct axis +LA9EB ldd #0x4080 ; set initial trial value to mid range and the next difference to add/subtract to half +LA9EE sta ,-s ; store the add/subtract value + orb #2 ; keep rs232 output marking + stb PIA1 ; set DAC output to the trial value + eorb #2 ; remove RS232 output value + lda PIA0 ; read the comparator + bmi LA9FF ; brif comparator output is high (DAC is lower than the axis value) + subb ,s ; subtract next bit value (split the difference toward 0) + skip2 +LA9FF addb ,s ; add next bit value (split the different toward infinity) + lda ,s+ ; get bit value back + lsra ; cut in half + cmpa #1 ; have we done that last value for the DAC? + bne LA9EE ; brif not + lsrb ; normalize the axis value + lsrb + cmpb -1,x ; does it match the read from the last call to this routine? + beq LAA12 ; brif so + dec ,s ; are we out of retries? + bne LA9EB ; brif not - try again +LAA12 stb ,-x ; save new value and move pointer back + ldd ,s++ ; get axis counter and clean up retry counter + decb ; move to next axis + bpl LA9E5 ; brif still more axes to do + rts +; This is the "bottom half" of the character fetching routines. +BROMHK cmpa #'9+1 ; is it >= colon? + bhs LAA28 ; brif so Z set if colon, C clear. + cmpa #0x20 ; space? + bne LAA24 ; brif not + jmp GETNCH ; move on to another character if space +LAA24 suba #'0 ; normalize ascii digit to 0-9; we already handled above digit 9 + suba #-'0 ; this will cause a carry for any value that was already positive +LAA28 rts +; Jump table for functions +LAA29 fdb SGN ; SGN 0x80 + fdb INT ; INT 0x81 + fdb ABS ; ABS 0x82 + fdb USRJMP ; USR 0x83 + fdb RND ; RND 0x84 + fdb SIN ; SIN 0x85 + fdb PEEK ; PEEK 0x86 + fdb LEN ; LEN 0x87 + fdb STR ; STR$ 0x88 + fdb VAL ; VAL 0x89 + fdb ASC ; ASC 0x8a + fdb CHR ; CHR$ 0x8b + fdb EOF ; EOF 0x8c + fdb JOYSTK ; JOYSTK 0x8d + fdb LEFT ; LEFT$ 0x8e + fdb RIGHT ; RIGHT$ 0x8f + fdb MID ; MID$ 0x90 + fdb POINT ; POINT 0x91 + fdb INKEY ; INKEY$ 0x92 + fdb MEM ; MEM 0x93 +; Operator precedence and jump table (binary ops except relational) +LAA51 fcb 0x79 ; + + fdb LB9C5 + fcb 0x79 ; - + fdb LB9BC + fcb 0x7b ; * + fdb LBACC + fcb 0x7b ; / + fdb LBB91 + fcb 0x7f ; ^ (exponentiation) + fdb EXPJMP + fcb 0x50 ; AND + fdb LB2D5 + fcb 0x46 ; OR + fdb LB2D4 +; Reserved words table for commands +LAA66 fcs 'FOR' ; 0x80 + fcs 'GO' ; 0x81 + fcs 'REM' ; 0x82 + fcs "'" ; 0x83 + fcs 'ELSE' ; 0x84 + fcs 'IF' ; 0x85 + fcs 'DATA' ; 0x86 + fcs 'PRINT' ; 0x87 + fcs 'ON' ; 0x88 + fcs 'INPUT' ; 0x89 + fcs 'END' ; 0x8a + fcs 'NEXT' ; 0x8b + fcs 'DIM' ; 0x8c + fcs 'READ' ; 0x8d + fcs 'RUN' ; 0x8e + fcs 'RESTORE' ; 0x8f + fcs 'RETURN' ; 0x90 + fcs 'STOP' ; 0x91 + fcs 'POKE' ; 0x92 + fcs 'CONT' ; 0x93 + fcs 'LIST' ; 0x94 + fcs 'CLEAR' ; 0x95 + fcs 'NEW' ; 0x96 + fcs 'CLOAD' ; 0x97 + fcs 'CSAVE' ; 0x98 + fcs 'OPEN' ; 0x99 + fcs 'CLOSE' ; 0x9a + fcs 'LLIST' ; 0x9b + fcs 'SET' ; 0x9c + fcs 'RESET' ; 0x9d + fcs 'CLS' ; 0x9e + fcs 'MOTOR' ; 0x9f + fcs 'SOUND' ; 0xa0 + fcs 'AUDIO' ; 0xa1 + fcs 'EXEC' ; 0xa2 + fcs 'SKIPF' ; 0xa3 + fcs 'TAB(' ; 0xa4 + fcs 'TO' ; 0xa5 + fcs 'SUB' ; 0xa6 + fcs 'THEN' ; 0xa7 + fcs 'NOT' ; 0xa8 + fcs 'STEP' ; 0xa9 + fcs 'OFF' ; 0xaa + fcs '+' ; 0xab + fcs '-' ; 0xac + fcs '*' ; 0xad + fcs '/' ; 0xae + fcs '^' ; 0xaf + fcs 'AND' ; 0xb0 + fcs 'OR' ; 0xb1 + fcs '>' ; 0xb2 + fcs '=' ; 0xb3 + fcs '<' ; 0xb4 +; Reserved word list for functions +LAB1A fcs 'SGN' ; 0x80 + fcs 'INT' ; 0x81 + fcs 'ABS' ; 0x82 + fcs 'USR' ; 0x83 + fcs 'RND' ; 0x84 + fcs 'SIN' ; 0x85 + fcs 'PEEK' ; 0x86 + fcs 'LEN' ; 0x87 + fcs 'STR$' ; 0x88 + fcs 'VAL' ; 0x89 + fcs 'ASC' ; 0x8a + fcs 'CHR$' ; 0x8b + fcs 'EOF' ; 0x8c + fcs 'JOYSTK' ; 0x8d + fcs 'LEFT$' ; 0x8e + fcs 'RIGHT$' ; 0x8f + fcs 'MID$' ; 0x90 + fcs 'POINT' ; 0x91 + fcs 'INKEY$' ; 0x92 + fcs 'MEM' ; 0x93 +; Jump table for commands +LAB67 fdb FOR ; 0x80 FOR + fdb GO ; 0x81 GO + fdb REM ; 0x82 REM + fdb REM ; 0x83 ' + fdb REM ; 0x84 ELSE + fdb IFTOK ; 0x85 IF + fdb DATA ; 0x86 DATA + fdb PRINT ; 0x87 PRINT + fdb ON ; 0x88 ON + fdb INPUT ; 0x89 INPUT + fdb ENDTOK ; 0x8a END + fdb NEXT ; 0x8b NEXT + fdb DIM ; 0x8c DIM + fdb READ ; 0x8d READ + fdb RUN ; 0x8e RUN + fdb RESTOR ; 0x8f RESTORE + fdb RETURN ; 0x90 RETURN + fdb STOP ; 0x91 STOP + fdb POKE ; 0x92 POKE + fdb CONT ; 0x93 CONT + fdb LIST ; 0x94 LIST + fdb CLEAR ; 0x95 CLEAR + fdb NEW ; 0x96 NEW + fdb CLOAD ; 0x97 CLOAD + fdb CSAVE ; 0x98 CSAVE + fdb OPEN ; 0x99 OPEN + fdb CLOSE ; 0x9a CLOSE + fdb LLIST ; 0x9b LLIST + fdb SET ; 0x9c SET + fdb RESET ; 0x9d RESET + fdb CLS ; 0x9e CLS + fdb MOTOR ; 0x9f MOTOR + fdb SOUND ; 0xa0 SOUND + fdb AUDIO ; 0xa1 AUDIO + fdb EXEC ; 0xa2 EXEC + fdb SKIPF ; 0xa3 SKIPF +; Error message table +LABAF fcc 'NF' ; 0 NEXT without FOR + fcc 'SN' ; 1 Syntax error + fcc 'RG' ; 2 RETURN without GOSUB + fcc 'OD' ; 3 Out of data + fcc 'FC' ; 4 Illegal function call + fcc 'OV' ; 5 Overflow + fcc 'OM' ; 6 Out of memory + fcc 'UL' ; 7 Undefined line number + fcc 'BS' ; 8 Bad subscript + fcc 'DD' ; 9 Redimensioned array + fcc '/0' ; 10 Division by 0 + fcc 'ID' ; 11 Illegal direct statement + fcc 'TM' ; 12 Type mismatch + fcc 'OS' ; 13 Out of string space + fcc 'LS' ; 14 String too long + fcc 'ST' ; 15 String formula too complex + fcc 'CN' ; 16 Can't continue + fcc 'FD' ; 17 Bad file data + fcc 'AO' ; 18 File already open + fcc 'DN' ; 19 Device number error + fcc 'IO' ; 20 Input/output error + fcc 'FM' ; 21 Bad file mode + fcc 'NO' ; 22 File not open + fcc 'IE' ; 23 Input past end of file + fcc 'DS' ; 24 Direct statement in file +LABE1 fcn ' ERROR' +LABE8 fcn ' IN ' +LABED fcb 0x0d +LABEE fcc 'OK' + fcb 0x0d,0x00 +LABF2 fcb 0x0d + fcn 'BREAK' +; Search stack for a FOR/NEXT stack frame. Stop search when something that isn't a FOR/NEXT +; stack frame is found. Enter with the index variable descriptor pointer in VARDES, or NULL +; for the first match. +; +; NOTE: this routine can be reworked to avoid needed two temporaries by taking advantage of the +; 6809's registers. This requires some minor tweaks where the routine is called. Further, the +; use of B is completely pointless and, even if B is going to be used, why is it reloaded on +; every loop? +LABF9 leax 4,s ; skip past our caller and the main command loop return address +LABFB ldb #18 ; each FOR/NEXT frame is 18 bytes + stx TEMPTR ; save current search pointer + lda ,x ; get first byte of this frame + suba #0x80 ; set to 0 if FOR/NEXT + bne LAC1A ; brif not FOR/NEXT (we hit the end of the stack for a GOSUB frame) + ldx 1,x ; get index variable descriptor + stx TMPTR1 ; save it + ldx VARDES ; get desired index descriptor + beq LAC16 ; brif NULL - we found something + cmpx TMPTR1 ; does this one match? + beq LAC1A ; brif so + ldx TEMPTR ; get back frame pointer + abx ; move to next entry + bra LABFB ; check next block of data +LAC16 ldx TMPTR1 ; get index variable of this frame + stx VARDES ; set it as the one found +LAC1A ldx TEMPTR ; get matching frame pointer + tsta ; set Z if FOR/NEXT + rts +; This is a block copy routine which copies from top to bottom. It's not clear that the use of +; this routine actually saves any ROM space compared to just implementing the copies directly +; once all the marshalling to set up the parameter variables is taken into account. +LAC1E bsr LAC37 ; check to see if stack collides with D +LAC20 ldu V41 ; point to destination + leau 1,u ; offset for pre-dec + ldx V43 ; point to source + leax 1,x ; offset for pre-dec +LAC28 lda ,-x ; get source byte + pshu a ; store at destination (sta ,-u would be less weird) + cmpx V47 ; at the bottom of the copy? + bne LAC28 ; brif not + stu V45 ; save final destination address +LAC32 rts +; Check for 2*B (0 <= B <= 127) bytes for free memory +LAC33 clra ; zero extend + aslb ; times 2 (loses bit 7 of B) + addd ARYEND ; add to top of used memory +LAC37 addd #STKBUF ; add a fudge factor for interpreter operation + bcs LAC44 ; brif >65535! + sts BOTSTK ; get current stack pointer + cmpd BOTSTK ; is our new address above that? + blo LAC32 ; brif not - no error +LAC44 ldb #6*2 ; raise OM error +; The error servicing routine +LAC46 jsr RVEC16 ; do the RAM hook dance (ON ERROR reserved hook) +LAC49 jsr RVEC17 ; do the RAM hook dance again + jsr LA7E9 ; turn off tape + jsr LA974 ; disable sound + jsr LAD33 ; reset stack, etc. + clr DEVNUM ; reset output to screen + jsr LB95C ; do a newline + jsr LB9AF ; send a ? + ldx #LABAF ; point to error table + abx ; offset to correct message + bsr LACA0 ; send a char from X + bsr LACA0 ; send another char from X + ldx #LABE1-1 ; point to "ERROR" message +LAC68 jsr LB99C ; print ERROR message (or BREAK) + lda CURLIN ; are we in immediate mode? + inca + beq LAC73 ; brif not - go to immediate mode + jsr LBDC5 ; print "IN ****" +; This is the immediate mode loop +LAC73 jsr LB95C ; do a newline if needed +LAC76 ldx #LABEE-1 ; point to prompt (without leading CR) + jsr LB99C ; show prompt +LAC7C jsr LA390 ; read an input line + ldu #0xffff ; flag immediate mode + stu CURLIN + bcs LAC7C ; brif we ended on BREAK - just go for another line + tst CINBFL ; EOF? + lbne LA4BF ; brif so + stx CHARAD ; save start of input line as input pointer + jsr GETNCH ; get character from input line + beq LAC7C ; brif no input + bcs LACA5 ; brif numeric - adding or removing a line number + ldb #2*24 ; code for "direct statement in file" + tst DEVNUM ; keyboard input? + bne LAC46 ; brif not - complain about direct statement + jsr LB821 ; go tokenize the input line + jmp LADC0 ; go execute the newly tokenized line +LACA0 lda ,x+ ; get character and advance pointer + jmp LB9B1 ; output it +LACA5 jsr LAF67 ; convert line number to binary + ldx BINVAL ; get converted number + stx LINHDR ; put it before the line we just read + jsr LB821 ; tokenize the input line + stb TMPLOC ; save line length + bsr LAD01 ; find where the line should be in the program + bcs LACC8 ; brif the line number isn't already present + ldd V47 ; get address where the line is in the program + subd ,x ; get the difference between here and the end of the line (negative) + addd VARTAB ; subtract line length from the end of the program + std VARTAB ; save new end of program address + ldu ,x ; get start of next line +LACC0 pulu a ; get source byte (lda ,u+ would be less weird) + sta ,x+ ; move it down + cmpx VARTAB ; have we moved everything yet? + bne LACC0 ; brif not +LACC8 lda LINBUF ; see if there is actually a line to input + beq LACE9 ; brif not - we just needed to remove the line + ldd VARTAB ; get current end of program + std V43 ; set as source pointer + addb TMPLOC ; add in the length of the new line + adca #0 + std V41 ; save destination pointer + jsr LAC1E ; make sure there's enough room and then make a hole for the new line + ldu #LINHDR-2 ; point to the line (well, 4 bytes before it, incl line number and fake next line pointer) +LACDD pulu a ; get byte from new line (lda ,u+ would be less weird) + sta ,x+ ; stow it + cmpx V45 ; at the end of the hole we just made? + bne LACDD ; brif not + ldx V41 ; get save new top of program address + stx VARTAB +LACE9 bsr LAD21 ; reset variables, etc. + bsr LACEF ; adjust next line pointers + bra LAC7C ; go read another input line +; Recompute next line pointers +LACEF ldx TXTTAB ; point to start of program +LACF1 ldd ,x ; get address of next line + beq LAD16 ; brif end of program + leau 4,x ; move past pointer and line number +LACF7 lda ,u+ ; are we at the end of the line? + bne LACF7 ; brif not + stu ,x ; save new next line pointer + ldx ,x ; point to next line + bra LACF1 ; process the next line +; Find a line in the program +LAD01 ldd BINVAL ; get desired line number + ldx TXTTAB ; point to start of program +LAD05 ldu ,x ; get address of next line + beq LAD12 ; brif end of program + cmpd 2,x ; do we have a match? + bls LAD14 ; brif our search number is <= the number here + ldx ,x ; move to next line + bra LAD05 ; check another line +LAD12 orcc #1 ; set C for not found +LAD14 stx V47 ; save address of matching line *or* line just after where it would have been +LAD16 rts +; NEW command +; This routine has multiple entry points used for various "levels" of NEW +NEW bne LAD14 ; brif there was input given; should be LAD16! +LAD19 ldx TXTTAB ; point to start of program + clr ,x+ ; blank out program (with NULL next line pointer) + clr ,x+ + stx VARTAB ; save end of program +LAD21 ldx TXTTAB ; get start of program + jsr LAEBB ; put input pointer there +LAD26 ldx MEMSIZ ; reset string space + stx STRTAB + jsr RESTOR ; reset DATA pointer + ldx VARTAB ; clear out scalars and arrays + stx ARYTAB + stx ARYEND +LAD33 ldx #STRSTK ; reset the string stack + stx TEMPPT + ldx ,s ; get return address (we're going to reset the stack) + lds FRETOP ; reset the stack to top of memory + clr ,-s ; put stopper so FOR/NEXT search will actually stop here + clr OLDPTR ; reset "CONT" state + clr OLDPTR+1 + clr ARYDIS ; un-disable arrays + jmp ,x ; return to original caller +; FOR command +FOR lda #0x80 ; disable array parsing + sta ARYDIS + jsr LET ; assign start value to index + jsr LABF9 ; search stack for matching FOR/NEXT frame + leas 2,s ; lose return address + bne LAD59 ; brif variable not already being used + ldx TEMPTR ; get address of matched data + leas b,x ; move stack pointer to the end of it (B is set to 18 in the stack search) +LAD59 ldb #9 ; is there room for 18 bytes in memory? + jsr LAC33 + jsr LAEE8 ; get address of the end of this statement in X + ldd CURLIN ; get line number + pshs x,b,a ; save next line address and current line number + ldb #0xa5 ; make sure we have TO + jsr LB26F + jsr LB143 ; make sure we have a numeric index + jsr LB141 ; evaluate terminal condition value + ldb FP0SGN ; pack FPA0 in place + orb #0x7f + andb FPA0 + stb FPA0 + ldy #LAD7F ; where to come back to + jmp LB1EA ; stash terminal condition on the stack +LAD7F ldx #LBAC5 ; point to FP 1.0 (default step) + jsr LBC14 ; unpack it to FPA0 + jsr GETCCH ; get character after the terminal + cmpa #0xa9 ; is it STEP? + bne LAD90 ; brif not + jsr GETNCH ; eat STEP + jsr LB141 ; evaluate step condition +LAD90 jsr LBC6D ; get "status" of FPA0 + jsr LB1E6 ; stash FPA0 on the stack (for step value) + ldd VARDES ; get variable descriptor pointer + pshs d ; put that on the stack too + lda #0x80 ; flag the frame as a FOR/NEXT frame + pshs a +; Main command interpretation loop +LAD9E jsr RVEC20 ; do the RAM hook dance + andcc #0xaf ; make sure interrupts are running + bsr LADEB ; check for BREAK/pause + ldx CHARAD ; get input pointer + stx TINPTR ; save input pointer for start of line + lda ,x+ ; get current input character + beq LADB4 ; brif end of line - move to another line + cmpa #': ; end of statement? + beq LADC0 ; brif so - keep processing +LADB1 jmp LB277 ; raise a syntax error +LADB4 lda ,x++ ; get MSB of next line pointer and skip past pointer + sta ENDFLG + beq LAE15 ; brif MSB of next line address is 0 (do END) + ldd ,x+ ; get line number but only advance one + std CURLIN ; set current line number + stx CHARAD ; set input pointer to one before line text +LADC0 jsr GETNCH ; move past statement separator or to first character in line + bsr LADC6 ; process a command + bra LAD9E ; handle next statement or line +LADC6 beq LAE40 ; return if end of statement + tsta ; is it a token? + lbpl LET ; brif not - do a LET + cmpa #0xa3 ; above SKIPF? + bhi LADDC ; brif so + ldx COMVEC+3 ; point to jump table + lsla ; two bytes per entry (loses the token bias) + tfr a,b ; put it in B for unsigned ABX + abx + jsr GETNCH ; move past token + jmp [,x] ; transfer control to the handler (which will return to the main loop) +LADDC cmpa #0xb4 ; is it a non-executable token? + bls LADB1 ; brif so + jmp [COMVEC+13] ; transfer control to ECB command handler +; RESTORE command +RESTOR ldx TXTTAB ; point to beginning of the program + leax -1,x ; move back one (to compensate for "GETNCH") +LADE8 stx DATPTR ; save as new data pointer + rts +; BREAK check +LADEB jsr LA1C1 ; read keyboard + beq LADFA ; brif no key down +LADF0 cmpa #3 ; BREAK? + beq STOP ; brif so - do a STOP + cmpa #0x13 ; pause (SHIFT-@)? + beq LADFB ; brif so + sta IKEYIM ; cache key for later INKEY$ so break check doesn't break INKEY$ +LADFA rts +LADFB jsr KEYIN ; read keyboard + beq LADFB ; brif no key down + bra LADF0 ; process pressed key in case BREAK or SHIFT-@ again +; END command +ENDTOK jsr LA426 ; close files + jsr GETCCH ; re-get input character + bra LAE0B +; STOP command +STOP orcc #1 ; flag "STOP" +LAE0B bne LAE40 ; brif not end of statement + ldx CHARAD ; save current input pointer + stx TINPTR +LAE11 ror ENDFLG ; save END/STOP flag (C) + leas 2,s ; lose return address +LAE15 ldx CURLIN ; get current input line (end of program comes here) + cmpx #0xffff ; immediate mode? + beq LAE22 ; brif so + stx OLDTXT ; save line where we stopped executing + ldx TINPTR ; get input pointer + stx OLDPTR ; save location where we stopped executing +LAE22 clr DEVNUM ; reset to screen/keyboard + ldx #LABF2-1 ; point to BREAK message + tst ENDFLG ; are we doing "BREAK"? + lbpl LAC73 ; brif not + jmp LAC68 ; go do the BREAK message and return to main loop +; CONT command +CONT bne LAE40 ; brif not end of statement + ldb #2*16 ; code for can't continue + ldx OLDPTR ; get saved execution pointer + lbeq LAC46 ; brif no saved pointer - raise CN error + stx CHARAD ; reset input pointer + ldx OLDTXT ; reset current line number + stx CURLIN +LAE40 rts +; CLEAR command +CLEAR beq LAE6F ; brif no argument + jsr LB3E6 ; evaluate string space size + pshs d ; save it + ldx MEMSIZ ; get memory size (top of memory) + jsr GETCCH ; is there anything after the string space size? + beq LAE5A ; brif not + jsr LB26D ; force a comma + jsr LB73D ; get top of memory value in X + leax -1,x ; move back one (top of cleared space) + cmpx TOPRAM ; is it within the memory available? + bhi LAE72 ; brif higher than top of memory - OM error +LAE5A tfr x,d ; so we can do math for checking memory usage + subd ,s++ ; subtract out string space value + bcs LAE72 ; brif less than 0 + tfr d,u ; U is bottom of cleared space + subd #STKBUF ; also account for slop space + bcs LAE72 ; brif less than 0 + subd VARTAB ; is there still room for the program? + blo LAE72 ; brif not + stu FRETOP ; set top of free memory + stx MEMSIZ ; set size of usable memory +LAE6F jmp LAD26 ; erase variables, etc. +LAE72 jmp LAC44 ; raise OM error +; RUN command +RUN jsr RVEC18 ; do the RAM hook dance + jsr LA426 ; close any open files + jsr GETCCH ; is there a line number + lbeq LAD21 ; brif no line number - start from beginning + jsr LAD26 ; clear variables, etc. + bra LAE9F ; "GOTO" the line number +; GO command (GOTO and GOSUB) +GO tfr a,b ; save TO/SUB +LAE88 jsr GETNCH ; eat the TO/SUB token + cmpb #0xa5 ; TO? + beq LAEA4 ; brif GOTO + cmpb #0xa6 ; SUB? + bne LAED7 ; brif not + ldb #3 ; room for 6 bytes? + jsr LAC33 + ldu CHARAD ; get input pointer + ldx CURLIN ; get line number + lda #0xa6 ; flag for GOSUB frame + pshs u,x,a ; set stack frame +LAE9F bsr LAEA4 ; do "GOTO" + jmp LAD9E ; go back to main loop +; Actual GOTO is here +LAEA4 jsr GETCCH ; get current input + jsr LAF67 ; convert number to binary + bsr LAEEB ; move input pointer to end of statement + leax 1,x ; point to start of next line + ldd BINVAL ; get desired line number + cmpd CURLIN ; is it beyond here? + bhi LAEB6 ; brif so + ldx TXTTAB ; start search at beginning of program +LAEB6 jsr LAD05 ; find line number + bcs LAED2 ; brif not found +LAEBB leax -1,x ; move to just before start of line + stx CHARAD ; reset input pointer +LAEBF rts +; RETURN command +RETURN bne LAEBF ; exit if argument given + lda #0xff ; set VARDES to an illegal value so we ignore FOR frames + sta VARDES + jsr LABF9 ; look for a GOSUB frame + tfr x,s ; reset stack + cmpa #0xa6-0x80 ; is it a GOSUB frame? + beq LAEDA ; brif so + ldb #2*2 ; code for RETURN without GOSUB + skip2 +LAED2 ldb #7*2 ; code for undefined line number + jmp LAC46 ; raise error +LAED7 jmp LB277 ; raise syntax error +LAEDA puls a,x,u ; get back saved line number and input pointer + stx CURLIN ; reset line number + stu CHARAD ; reset input pointer +; DATA command +DATA bsr LAEE8 ; move input pointer to end of statement + skip2 +; REM command (also ELSE) +REM bsr LAEEB ; move input pointer to end of line + stx CHARAD ; save new input pointer +LAEE7 rts +; Return end of statement (LAEE8) or line (AEEB) in X +LAEE8 ldb #': ; colon is statement terminator + skip1lda +LAEEB clrb ; make main terminator NUL + stb CHARAC ; save terminator + clrb ; end of line - always terminates + ldx CHARAD ; get input pointer +LAEF1 tfr b,a ; save secondary terminator + ldb CHARAC ; get main terminator + sta CHARAC ; save secondary +LAEF7 lda ,x ; get input character + beq LAEE7 ; brif end of line + pshs b ; save terminator + cmpa ,s+ ; does it match? + beq LAEE7 ; brif so - bail + leax 1,x ; move pointer ahead + cmpa #'" ; start of string? + beq LAEF1 ; brif so + inca ; functon token? + bne LAF0C ; brif not + leax 1,x ; skip second part of function token +LAF0C cmpa #0x85+1 ; IF? + bne LAEF7 ; brif not + inc IFCTR ; bump "IF" count + bra LAEF7 ; get check another input character +; IF command +IFTOK jsr LB141 ; evaluate condition + jsr GETCCH ; find out what's after the conditin + cmpa #0x81 ; GO? + beq LAF22 ; treat same as THEN + ldb #0xa7 ; make sure we have a THEN + jsr LB26F +LAF22 lda FP0EXP ; get true/false (false is 0) + bne LAF39 ; brif condition true + clr IFCTR ; reset IF counter +LAF28 bsr DATA ; skip over statement + tsta ; end of line? + beq LAEE7 ; brif so + jsr GETNCH ; get start of this statement + cmpa #0x84 ; ELSE? + bne LAF28 ; brif not + dec IFCTR ; is it a matching ELSE? + bpl LAF28 ; brif not - keep looking + jsr GETNCH ; eat the ELSE +LAF39 jsr GETCCH ; get current input + lbcs LAEA4 ; brif numeric - to a GOTO + jmp LADC6 ; let main loop interpret the next command +; ON command +ON jsr LB70B ; evaluate index expression +LAF45 ldb #0x81 ; make sure we have "GO" + jsr LB26F + pshs a ; save TO/SUB + cmpa #0xa6 ; SUB? + beq LAF54 ; brif so + cmpa #0xa5 ; TO? +LAF52 bne LAED7 ; brif not +LAF54 dec FPA0+3 ; are we at the right index? + bne LAF5D ; brif not + puls b ; get TO/SUB token + jmp LAE88 ; go do GOTO or GOSUB +LAF5D jsr GETNCH ; munch a character + bsr LAF67 ; parse line number + cmpa #', ; is there another line following? + beq LAF54 ; brif so - see if we're there yet + puls b,pc ; clean up TO/SUB token and return - we fell through +; Parse a line number +LAF67 ldx ZERO ; initialize line number accumulator to 0 + stx BINVAL +LAF6B bcc LAFCE ; brif not numeric + suba #'0 ; adjust to actual value of digit + sta CHARAC ; save digit + ldd BINVAL ; get accumulated number + cmpa #24 ; will this overflow? + bhi LAF52 ; brif so - raise syntax error + aslb ; times 2 + rola + aslb ; times 4 + rola + addd BINVAL ; times 5 + aslb ; times 10 + rola + addb CHARAC ; add in digit + adca #0 + std BINVAL ; save new accumulated number + jsr GETNCH ; fetch next character + bra LAF6B ; process next digit +; LET command (the LET keyword requires Extended Basic) +LET jsr LB357 ; evaluate destination variable + stx VARDES ; save descriptor pointer + ldb #0xb3 ; make sure we have = + jsr LB26F + lda VALTYP ; get destination variable type + pshs a ; save it for later + jsr LB156 ; evaluate the expression to assign + puls a ; get back original variable type + rora ; put type in C + jsr LB148 ; make sure the current result matches the type + lbeq LBC33 ; bri fnumeric - copy FPA0 to variable +LAFA4 ldx FPA0+2 ; point to descriptor of replacement string + ldd FRETOP ; get bottom of string space + cmpd 2,x ; is the string already in string space? + bhs LAFBE ; brif so + cmpx VARTAB ; is the descriptor in variable space? + blo LAFBE ; brif not +LAFB1 ldb ,x ; get length of string + jsr LB50D ; allocate space for this string + ldx V4D ; get descriptor pointer back + jsr LB643 ; copy string into string space + ldx #STRDES ; point to temporary string descriptor +LAFBE stx V4D ; save descriptor pointer + jsr LB675 ; remove string from string stack if appropriate + ldu V4D ; get back replacement descriptor + ldx VARDES ; get target descriptor + pulu a,b,y ; get string length (A) and data pointer (Y) + sta ,x ; save new length + sty 2,x ; save new pointer +LAFCE rts +; READ and INPUT commands. +LAFCF fcc '?REDO' ; The ?REDO message + fcb 0x0d,0x00 +LAFD6 ldb #2*17 ; bad file data code + tst DEVNUM ; are we reading from the keyboard? + beq LAFDF ; brif so +LAFDC jmp LAC46 ; raise the error +LAFDF lda INPFLG ; are we doing INPUT? + beq LAFEA ; brif so + ldx DATTXT ; get line number where the DATA statement happened + stx CURLIN ; set current line number to that so can report the correct location + jmp LB277 ; raise a syntax error on bad data +LAFEA ldx #LAFCF-1 ; show the ?REDO if we're doing INPUT + jsr LB99C + ldx TINPTR ;* reset input pointer to start of statement (this will cause the + stx CHARAD ;* INPUT statement to be re-executed + rts +INPUT ldb #11*2 ; code for illegal direct statement + ldx CURLIN ; are we in immediate mode? + leax 1,x + beq LAFDC ; brif so - raise ID error + bsr LB002 ; go do the INPUT thing + clr DEVNUM ; reset device to screen/keyboard + rts +LB002 cmpa #'# ; is there a device number? + bne LB00F ; brif not + jsr LA5A5 ; parse device number + jsr LA3ED ; make sure it's valid for input + jsr LB26D ; make sure we have a comma after the device number +LB00F cmpa #'" ; is there a prompt string? + bne LB01E ; brif not + jsr LB244 ; parse the prompt string + ldb #'; ; make sure we have a semicolon after the prompt + jsr LB26F + jsr LB99F ; print the prompt +LB01E ldx #LINBUF ; point to line input buffer + clr ,x ; NUL first byte to indicate no data + tst DEVNUM ; is it keyboard input? + bne LB049 ; brif not + bsr LB02F ; read a line from the keyboard + ldb #', ; put a comma at the start of the buffer + stb ,x + bra LB049 ; go process some input +LB02F jsr LB9AF ; send a ? + jsr LB9AC ; send a space +LB035 jsr LA390 ; read input from the keyboard + bcc LB03F ; brif not BREAK + leas 4,s ; clean up stack +LB03C jmp LAE11 ; go process BREAK +LB03F ldb #2*23 ; input past end of file error code + tst CINBFL ; was it EOF? + bne LAFDC ; brif so - raise the error + rts +READ ldx DATPTR ; fetch current DATA pointer + skip1lda ; set A to nonzero (for READ) +LB049 clra ; set A to zero (for INPUT) + sta INPFLG ; record whether we're doing READ or INPUT + stx DATTMP ; save current input location +LB04E jsr LB357 ; evaluate a variable (destination of data) + stx VARDES ; save descriptor + ldx CHARAD ; save interpreter input pointer + stx BINVAL + ldx DATTMP ; get data pointer + lda ,x ; is there anything to read? + bne LB069 ; brif so + lda INPFLG ; is it INPUT? + bne LB0B9 ; brif not + jsr RVEC10 ; do the RAM hook dance + jsr LB9AF ; send a ? (so subsequent lines get ??) + bsr LB02F ; go read an input line +LB069 stx CHARAD ; save data pointer + jsr GETNCH ; fetch next data character + ldb VALTYP ; do we want a number? + beq LB098 ; brif so + ldx CHARAD ; get input pointer + sta CHARAC ; save initial character as the delimiter + cmpa #'" ; do we have a string delimiter? + beq LB08B ; brif so - use " as both delimiters + leax -1,x ; back up input if we don't have a delimiter + clra ; set delimiter to NUL (end of line) + sta CHARAC + jsr LA35F ; set up print parameters + tst PRTDEV ; is it a file type device? + bne LB08B ; brif so - use two NULs + lda #': ; use colon as one delimiter + sta CHARAC + lda #', ; and use comma as the other +LB08B sta ENDCHR ; save second terminator + jsr LB51E ; parse out the string + jsr LB249 ; move input pointer past the string + jsr LAFA4 ; assign the string to the variable + bra LB09E ; go see if there's more to read +LB098 jsr LBD12 ; parse a numeric string + jsr LBC33 ; assign the numbe to the variable +LB09E jsr GETCCH ; get current input character + beq LB0A8 ; brif end of line + cmpa #', ; check for comma + lbne LAFD6 ; brif not - we have bad data +LB0A8 ldx CHARAD ; get current data pointer + stx DATTMP ; save the data pointer + ldx BINVAL ; restore the interpreter input pointer + stx CHARAD + jsr GETCCH ; get current input from program + beq LB0D5 ; brif end of statement + jsr LB26D ; make sure there's a comma between variables + bra LB04E ; go read another item +LB0B9 stx CHARAD ; reset input pointer + jsr LAEE8 ; search for end of statement + leax 1,x ; move past end of statement + tsta ; was it end of line? + bne LB0CD ; brif not + ldb #2*3 ; code for out of data + ldu ,x++ ; get pointer to next line + beq LB10A ; brif end of program - raise OD error + ldd ,x++ ; get line number + std DATTXT ; record it for raising errors in DATA statements +LB0CD lda ,x ; do we have a DATA statement? + cmpa #0x86 + bne LB0B9 ; brif not - keep scanning + bra LB069 ; go process the input +LB0D5 ldx DATTMP ; get data pointer + ldb INPFLG ; were we doing READ? + lbne LADE8 ; brif so - save DATA pointer + lda ,x ; is there something after the input in the input buffer? + beq LB0E7 ; brif not - we consumed everything + ldx #LB0E8-1 ; print the ?EXTRA IGNORED message + jmp LB99C +LB0E7 rts +LB0E8 fcc '?EXTRA IGNORED' + fcb 0x0d,0x00 +; NEXT command +NEXT bne LB0FE ; brif argument given + ldx ZERO ; set to NULL descriptor pointer + bra LB101 ; go process "any index will do" +LB0FE jsr LB357 ; evaluate the variable +LB101 stx VARDES ; save the index we're looking for + jsr LABF9 ; search the stack for the matching frame + beq LB10C ; brif we found a matching frame + ldb #0 ; code for NEXT without FOR +LB10A bra LB153 ; raise the error +LB10C tfr x,s ; reset the stack to the start of the stack frame + leax 3,x ; point to the STEP value + jsr LBC14 ; copy the value to FPA0 + lda 8,s ; get step direction + sta FP0SGN ; save as sign of FPA0 + ldx VARDES ; point to index variable + jsr LB9C2 ; add (X) to FPA0 (steps the index) + jsr LBC33 ; save new value to the index + leax 9,s ; point to terminal condition + jsr LBC96 ; compare the new index value with the terminal + subb 8,s ; set B=0 if we hit the terminal (or passed it with nonzero step) + beq LB134 ; brif loop complete + ldx 14,s ; restore line number and input pointer to start of loop + stx CURLIN + ldx 16,s + stx CHARAD +LB131 jmp LAD9E ; return to interpretation loop +LB134 leas 18,s ; remove the frame from the stack + jsr GETCCH ; get character after the index + cmpa #', ; do we have more indexes? + bne LB131 ; brif not + jsr GETNCH ; munch the comma + bsr LB0FE ; go process another value +; NOTE: despite the BSR on the preceding line, execution of the NEXT command will not fall +; through this point, nor will the stack grow without bound. The BSR is required to make sure +; the stack is aligned properly for the stack search for the subsequent index variable. +; +; The following is the expression evaluation system. It has various entry points including for type +; checking. This really consists of two co-routines, one for evaluating operators and one for individual +; terms. However, it does some rather confusing stack operations so it's a bit difficult to follow +; just how some of this works. +; +; Evaluate numeric expression +LB141 bsr LB156 ; evaluate an expression +; TM error if string +LB143 andcc #0xfe ; clear C to indicate we want a number + skip2keepc +; TM error if numeric +LB146 orcc #1 ; set C to indicate we want a string +; TM error if: C = 1 and number, OR C = 0 and string +LB148 tst VALTYP ; set flags on the current value to (doesn't change C) + bcs LB14F ; brif we want a string + bpl LB0E7 ; brif we have a number (we want a number) + skip2 +LB14F bmi LB0E7 ; brif we have a string (we want a string) +LB151 ldb #12*2 ; code for TM error +LB153 jmp LAC46 ; raise the error +; The general expression evaluation entry point +LB156 bsr LB1C6 ; back up input pointer to compensate for GETNCH below +LB158 clra ; set operator precedence to 0 (no previous operator) + skip2 +LB15A pshs b ; save relational operator flags + pshs a ; save previous operator precedence + ldb #1 ; make sure we aren't overflowing the stack + jsr LAC33 + jsr LB223 ; go evaluate the first term +LB166 clr TRELFL ; flag no relational operators seen +LB168 jsr GETCCH ; get input character +LB16A suba #0xb2 ; token for > (lowest relational operator) + blo LB181 ; brif below relational operators + cmpa #3 ; there are three relational operators, is it one? + bhs LB181 ; brif not + cmpa #1 ; set C if > + rola ; shift C into bit 0 (4: <, 2: =, 1: >) + eora TRELFL ; flip the bit for this operator + cmpa TRELFL ; did the result get lower? + blo LB1DF ; brif so - we have a duplicate so raise an error + sta TRELFL ; save new operator flags + jsr GETNCH ; munch the operator + bra LB16A ; go see if we have another one +LB181 ldb TRELFL ; do we have a relational comparison? + bne LB1B8 ; brif so + lbcc LB1F4 ; brif the token is above the relational operators + adda #7 ; put operators starting at 0 + bhs LB1F4 ; brif we're above 0 - it's an operator, Jim + adca VALTYP ; add carry, numeric flag, and modified token number + lbeq LB60F ; brif we have string and A is + - do concatenation + adca #-1 ; restore operator number + pshs a ; save operator number + asla ; times 2 + adda ,s+ ; and times 3 (3 bytes per entry) + ldx #LAA51 ; point to operator pecedence and jump table + leax a,x ; point to correct entry +LB19F puls a ; get precedence of previous operation + cmpa ,x ; is hit higher (or same) than the current one? + bhs LB1FA ; brif so - we need to process that operator + bsr LB143 ; TM error if we have a string +LB1A7 pshs a ; save previous operation precedence + bsr LB1D4 ; push operator handler address and FPA0 onto the stack + ldx RELPTR ; get pointer to arithmetic/logical table entry for last operation + puls a ; get back precedence + bne LB1CE ; brif we had a relational operation + tsta ; check precedence of previous operation + lbeq LB220 ; brif end of expression + bra LB203 ; go handle operation +LB1B8 asl VALTYP ; get type of value to C + rolb ; mix it in to bit 0 of relational flags + bsr LB1C6 ; back up input pointer + ldx #LB1CB ; point to relational operator precedence and handler + stb TRELFL ; save relational comparison flags + clr VALTYP ; result will be numeric + bra LB19F ; to process the operation +LB1C6 ldx CHARAD ; get input pointer + jmp LAEBB ; back it up one and put it back +LB1CB fcb 0x64 ; precedence of relational comparison + fdb LB2F4 ; handler address for relational comparison +LB1CE cmpa ,x ; is last done operation higher (or same) precedence? + bhs LB203 ; brif so - go process it + bra LB1A7 ; go push things on the stack and process this operation otherwise +LB1D4 ldd 1,x ; get address of operatorroutine + pshs d ; save it + bsr LB1E2 ; push FPA0 onto the stack + ldb TRELFL ; get back relational operator flags + lbra LB15A ; go evaluate another operation +LB1DF jmp LB277 ; raise a syntax error +LB1E2 ldb FP0SGN ; get sign of FPA0 + lda ,x ; get precedence of this operation +LB1E6 puls y ; get back original caller + pshs b ; save sign +LB1EA ldb FP0EXP ; get exponent + ldx FPA0 ; get mantissa + ldu FPA0+2 + pshs u,x,b ; stow FPA0 sign and mantissa + jmp ,y ; return to caller +LB1F4 ldx ZERO ; point to dummy value + lda ,s+ ; get precedence of previous operation (and set flags) + beq LB220 ; brif end of expression +LB1FA cmpa #0x64 ; relational operation? + beq LB201 ; brif so + jsr LB143 ; type mismatch if string +LB201 stx RELPTR ; save pointer to operator routine +LB203 puls b ; get relational flags + cmpa #0x5a ; NOT operation? + beq LB222 ; brif so (it was unary) + cmpa #0x7d ; unary negation? + beq LB222 ; brif so + lsrb ; shift value type flag out of relational flags + stb RELFLG ; save relational operator flag + puls a,x,u ; get FP value back + sta FP1EXP ; set exponent and mantissa in FPA1 + stx FPA1 + stu FPA1+2 + puls b ; and the sign + stb FP1SGN + eorb FP0SGN ; set RESSGN if the two operand signs differ + stb RESSGN +LB220 ldb FP0EXP ; get exponent of FPA0 +LB222 rts ; return or transfer control to operator handler routine +LB223 jsr RVEC15 ; do the RAM hook dance + clr VALTYP ; set type to numeric + jsr GETNCH ; get first character in the term + bcc LB22F ; brif not numeric +LB22C jmp LBD12 ; parse a number (and return) +LB22F jsr LB3A2 ; set carry if not alpha + bcc LB284 ; brif alpha character (variable) + cmpa #'. ; decimal point? + beq LB22C ; brif so - evaluate number + cmpa #0xac ; minus? + beq LB27C ; brif so - process unary negation + cmpa #0xab ; plus? + beq LB223 ; brif so - ignore unary "posation" + cmpa #'" ; string delimiter? + bne LB24E ; brif not +LB244 ldx CHARAD ; get input pointer + jsr LB518 ; go parse the string +LB249 ldx COEFPT ; get address of end of string + stx CHARAD ; move input pointer past string + rts +LB24E cmpa #0xa8 ; NOT? + bne LB25F ; brif not + lda #0x5a ; precedence of unary NOT + jsr LB15A ; process the operand of NOT + jsr INTCNV ; convert to integer in D + coma ; do a bitwise complement + comb + jmp GIVABF ; resturn the result +LB25F inca ; is it a function token? + beq LB290 ; brif so +LB262 bsr LB26A ; only other legal thing must be a (expr) + jsr LB156 ; evaluate parentheticized expression +LB267 ldb #') ; force a ) + skip2 +LB26A ldb #'( ; force a ( + skip2 +LB26D ldb #', ; force a , +LB26F cmpb [CHARAD] ; does character match? + bne LB277 ; brif not + jmp GETNCH ; each the character and return the next +LB277 ldb #2*1 ; raise syntax error + jmp LAC46 +LB27C lda #0x7d ; unary negation precedence + jsr LB15A ; evaluate argument + jmp LBEE9 ; flip sign of FPA0 and return +LB284 jsr LB357 ; evaluate variable +LB287 stx FPA0+2 ; save descriptor address in FPA0 + lda VALTYP ; test variable type + bne LB222 ; brif string - we're done + jmp LBC14 ; copy FP number from (X) into FPA0 +LB290 jsr GETNCH ; get the actual token number + tfr a,b ; save it (for offsetting X) + lslb ; two bytes per jump table entry (and lose high bit) + jsr GETNCH ; eat the token byte + cmpb #2*19 ; is it a valid token for Color Basic? + bls LB29F ; brif so + jmp [COMVEC+18] ; transfer control to Extended Basic if not +LB29F pshs b ; save jump table offset + cmpb #2*14 ; does it expect a numeric argument? + blo LB2C7 ; brif so + cmpb #2*18 ; does it need no arguments? + bhs LB2C9 ; brif so + bsr LB26A ; force a ( + lda ,s ; get token value + cmpa #2*17 ; is it POINT? + bhs LB2C9 ; brif so + jsr LB156 ; evaluate first argument string + bsr LB26D ; force a comma + jsr LB146 ; TM error if string + puls a ; get token value + ldu FPA0+2 ; get string descriptor + pshs u,a ; now we save the first string argument and the token value + jsr LB70B ; evaluate first numeric argument + puls a ; get back token value + pshs b,a ; save second argument and token value + fcb 0x8e ; opcode of LDX immediate (skips two bytes) +LB2C7 bsr LB262 ; force a ( +LB2C9 puls b ; get offset + ldx COMVEC+8 ; get jump table pointer + abx ; add offset into table + jsr [,x] ; go process function + jmp LB143 ; make sure result is numeric +; operator OR +LB2D4 skip1lda ; set flag to nonzero to signal OR +; operator AND +LB2D5 clra ; set flag to zero to signal AND + sta TMPLOC ; save AND/OR flag + jsr INTCNV ; convert second argument to intenger + std CHARAC ; save it + jsr LBC4A ; move first argument to FPA0 + jsr INTCNV ; convert first argument to integer + tst TMPLOC ; is it AND or OR? + bne LB2ED ; brif OR + anda CHARAC ; do the bitwise AND + andb ENDCHR + bra LB2F1 ; finish up +LB2ED ora CHARAC ; do the bitwise OR + orb ENDCHR +LB2F1 jmp GIVABF ; return integer result +; relational comparision operators +LB2F4 jsr LB148 ; TM error if type mismatch + BNE LB309 ; brif we have a string comparison + lda FP1SGN ; pack FPA1 + ora #0x7f + anda FPA1 + sta FPA1 + ldx #FP1EXP ; point to packed FPA1 + jsr LBC96 ; compare FPA0 to FPA1 + bra LB33F ; handle truth comparison +LB309 clr VALTYP ; the result of a comparison is always a number + dec TRELFL ; remove the string flag from the comparison data + jsr LB657 ; get string details for second argument + stb STRDES ; save them in the temporary string descriptor + stx STRDES+2 + ldx FPA1+2 ; get pointer to first argument descriptor + jsr LB659 ; get string details for second argument + lda STRDES ; get length of second argument + pshs b ; save length of first argument + suba ,s+ ; now A is the difference in string lengths + beq LB328 ; brif string lengths are equal + lda #1 ; flag for second argument is longer than first + bcc LB328 ; brif second string is longer than first + ldb STRDES ; get length of second string (shorter) + nega ; invert default comparison result +LB328 sta FP0SGN ; save default truth flag + ldu STRDES+2 ; get pointer to start of second string + incb ; compensate for DECB +LB32D decb ; have we compared everything? + bne LB334 ; brif not + ldb FP0SGN ; get default truth value + bra LB33F ; decide comparison truth +LB334 lda ,x+ ; get byte from first argument + cmpa ,u+ ; compare with second argument + beq LB32D ; brif equal - keep comparing + ldb #0xff ; negative if first string is > second + bcc LB33F ; brif string A > string B + negb ; invert result +LB33F addb #1 ; convert to 0,1,2 + rolb ; shift left - now it's 4,2,1 for <, =, > + andb RELFLG ; keep only the truth we care about + beq LB348 ; brif no matching bits - it's false + ldb #0xff ; set true +LB348 jmp LBC7C ; convert result to FP and return it +; DIM command +LB34B jsr LB26D ; make sure there's a comma between variables +DIM ldb #1 ; flag that we're dimensioning + bsr LB35A ; go allocate the variable + jsr GETCCH ; are we done? + bne LB34B ; brif not + rts +; This routine parses a variable. For scalars, it will return a NULL string or 0 value number +; if it is called from "evaluate term". Otherwise, it allocates the variable. For arrays, it will +; allocate a default sized array if dimensioning is not underway and then attempt to look up +; the requested coordinates in that array. Otherwise, it will allocate an array based on the +; specified dimension values. +LB357 clrb ; flag that we're not setting up an array + jsr GETCCH +LB35A stb DIMFLG ; save dimensioning flag + sta VARNAM ; save first character of variable name + jsr GETCCH ; get input character (why? we already have it) + bsr LB3A2 ; set carry if not alpha + lbcs LB277 ; brif our variable doesn't start with a letter + clrb ; default second variable character to NUL + stb VALTYP ; set value type to numeric + jsr GETNCH ; get second character + bcs LB371 ; brif numeric - numbers are allowed + bsr LB3A2 ; set carry if not alpha + bcs LB37B ; brif not alpha +LB371 tfr a,b ; save set second character of variable name +LB373 jsr GETNCH ; get an input character + bcs LB373 ; brif numeric - still in variable name + bsr LB3A2 ; set carry if not alpha + bcc LB373 ; brif alpha - still in variable name +LB37B cmpa #'$ ; do we have the string sigil? + bne LB385 ; brif not + com VALTYP ; set value type to string + addb #0x80 ; set bit 7 of second variable character to indicate string + jsr GETNCH ; eat the sigil +LB385 stb VARNAM+1 ; save second variable name character + ora ARYDIS ; merge array disable flag (will set bit 7 of input character if no arrays) + suba #'( ; do we have a subscript? + lbeq LB404 ; brif so + clr ARYDIS ; disable the array disable flag - it's single use + ldx VARTAB ; point to the start of the variable table + ldd VARNAM ; get variable name +LB395 cmpx ARYTAB ; are we at the top of the variable table? + beq LB3AB ; brif so + cmpd ,x++ ; does the variable name match (and move pointer to variable data) + beq LB3DC ; brif so + leax 5,x ; move to next table entry + bra LB395 ; see if we have a match +; Set carry if not upper case alpha +LB3A2 cmpa #'A ; set C if less than A + bcs LB3AA ; brif less than A + suba #'Z+1 ; set C if greater than Z + suba #-('Z+1) +LB3AA rts +LB3AB ldx #ZERO ; point to empty location (NULL/0 value) + ldu ,s ; get caller address + cmpu #LB287 ; coming from "evaluate term"? + beq LB3DE ; brif so - don't allocate + ldd ARYEND ; get end of arrays + std V43 ; save as top of source block + addd #7 ; 7 bytes per scalar entry + std V41 ; save as top of destination block + ldx ARYTAB ; get bottom of arrays + stx V47 ; save as bottom of source block + jsr LAC1E ; move the arrays up to make a hole + ldx V41 ; get new top of arrays + stx ARYEND ; set new end of arrays + ldx V45 ; get bottom of destination block + stx ARYTAB ; set as new start of arrays + ldx V47 ; get old end of variables + ldd VARNAM ; get name of variable + std ,x++ ; set variable name and advance X to the value + clra ; zero out the variable value + clrb + std ,x + std 2,x + sta 4,x +LB3DC stx VARPTR ; save descriptor address of return value +LB3DE rts +; Various integer conversion routines +LB3DF fcb 0x90,0x80,0x00,0x00,0x00 ; FP constant -32768 +LB3E4 jsr GETNCH ; fetch input character +LB3E6 jsr LB141 ; evaluate numeric expression +LB3E9 lda FP0SGN ; get sign of value + bmi LB44A ; brif negative (raise FC error) +INTCNV jsr LB143 ; TM error if string + lda FP0EXP ; get exponent + cmpa #0x90 ; is it within the range for a 16 bit integer? + blo LB3FE ; brif smaller than 32768 + ldx #LB3DF ; point to -32678 constant + jsr LBC96 ; is FPA0 equal to -32768? + bne LB44A ; brif not - magnitude is too far negative +LB3FE jsr LBCC8 ; move binary point to the right of FPA0 and correct sign + ldd FPA0+2 ; get the resulting integer + rts +LB404 ldd DIMFLG ; get dimensioning flag and variable type + pshs b,a ; save them (to avoid issues while evaluating dimension values) + nop ; dead space caused by 1.2 revision + clrb ; reset dimension counter +LB40A ldx VARNAM ; get variable name + pshs x,b ; save dimension counter and variable name + bsr LB3E4 ; evaluate a dimension value (and skip either ( or ,) + puls b,x,y ; get variable name, dimension counter, and dimensioning/type flag + stx VARNAM ; restore variable name + ldu FPA0+2 ; get dimension size/index + pshs u,y ; save dimension size and dimensioning/type flag + incb ; bump dimension counter + jsr GETCCH ; get what's after the dimension count + cmpa #', ; do we have another dimension? + beq LB40A ; brif so - parse it + stb TMPLOC ; save dimension counter + jsr LB267 ; make sure we have a ) + puls a,b ; get back variable type and dimensioning flag + std DIMFLG ; restore variable type and dimensioning flag + ldx ARYTAB ; get start of arrays +LB42A cmpx ARYEND ; are we at the end of the array table + beq LB44F ; brif so + ldd VARNAM ; get variable name + cmpd ,x ; does it match? + beq LB43B ; brif so + ldd 2,x ; get length of this array + leax d,x ; move to next array + bra LB42A ; go check another entry +LB43B ldb #2*9 ; code for redimensioned array error + lda DIMFLG ; are we dimensioning? + bne LB44C ; brif so - raise error + ldb TMPLOC ; get number of dimensions given + cmpb 4,x ; does it match? + beq LB4A0 ; brif so +LB447 ldb #8*2 ; raise "bad subscript" + skip2 +LB44A ldb #4*2 ; raise "illegal function call" +LB44C jmp LAC46 ; raise error +LB44F ldd #5 ; 5 bytes per array entry + std COEFPT ; initialize array size to entry size + ldd VARNAM ; get variable name + std ,x ; set array name + ldb TMPLOC ; get dimension count + stb 4,x ; set dimension count + jsr LAC33 ; make sure we haven't overflowed memory + stx V41 ; save array descriptor address +LB461 ldb #11 ; default dimension value (zero-based, gives max index of 10) + clra ; zero extend (??? why not LDD above?) + tst DIMFLG ; are we dimensioning? + beq LB46D ; brif not + puls a,b ; get dimension size + addd #1 ; account for zero based indexing +LB46D std 5,x ; save dimension size + bsr LB4CE ; multiply by accumulated array size + std COEFPT ; save new array size + leax 2,x ; move to next dimension + dec TMPLOC ; have we done all dimensions? + bne LB461 ; brif not + stx TEMPTR ; save end of array descriptor (minus 5) + addd TEMPTR ; add total size of array to address of descriptor + lbcs LAC44 ; brif it overflows memory + tfr d,x ; save end of array for later + jsr LAC37 ; does array fit in memory? + subd #STKBUF-5 ; subtract out the "stack fudge factor" but add 5 to the result + std ARYEND ; save new end of arrays + clra ; set up for clearing +LB48C leax -1,x ; move back one + sta 5,x ; blank out a byte in the array data + cmpx TEMPTR ; have we reached the array header? + bne LB48C ; brif not + ldx V41 ; get address of start of descriptor + lda ARYEND ; get MSB of end of array back (B still has LSB) + subd V41 ; subtract start of descriptor + std 2,x ; save length of array in array header + lda DIMFLG ; are we dimensioning? + bne LB4CD ; brif so - we're done +LB4A0 ldb 4,x ; get number of dimensions + stb TMPLOC ; initialize counter + clra ; initialize accumulated offset + clrb +LB4A6 std COEFPT ; save accumulated offset + puls a,b ; get desired index + std FPA0+2 ; save it + cmpd 5,x ; is it in range for this dimension? + bhs LB4EB ; brif not + ldu COEFPT ; get accumulated offset + beq LB4B9 ; brif first dimension + bsr LB4CE ; multiply accumulated offset by dimension length + addd FPA0+2 ; add in offset into this dimension +LB4B9 leax 2,x ; move to next dimension in header + dec TMPLOC ; done all dimensions? + bne LB4A6 ; brif not + std ,--s ; save D for multiply by 5 (should be pshs d) + aslb ; times 2 + rola + aslb ; times 4 + rola + addd ,s++ ; times 5 + leax d,x ; add in offset from start of array data + leax 5,x ; offset to end of header + stx VARPTR ; save pointer to element data +LB4CD rts +; multiply 16 bit number at 5,x by the 16 bit number in COEFPT; return result in D; BS error if carry +LB4CE lda #16 ; 16 shifts to do a multiply + sta V45 ; save shift counter + ldd 5,x ; get multiplier + std BOTSTK ; save it + clra ; zero out product + clrb +LB4D8 aslb ; shift product left + rola + bcs LB4EB ; brif we have a carry + asl COEFPT+1 ; shift other factor left + rol COEFPT + bcc LB4E6 ; brif no carry - this bit position is 0 + addd BOTSTK ; add in multiplier at this bit position + bcs LB4EB ; brif carry - do an error +LB4E6 dec V45 ; have we done all 16 bits? + bne LB4D8 ; brif not + rts +LB4EB jmp LB447 ; raise a BS error +; MEM function +; BUG: this doesn't account for the STKBUF fudge factor used for memory availability checks +MEM tfr s,d ; get stack pointer where we can do math + subd ARYEND ; calculate number of bytes between the stack and the top of arrays + skip1 ; return result +; Convert unsigned value in B to FP +LB4F3 clra ; zero extend +; Convert signed value in D to FP +GIVABF clr VALTYP ; set value type to numeric + std FPA0 ; save value in FPA0 + ldb #0x90 ; exponent for top two bytes to be an integer + jmp LBC82 ; finish conversion to integer +; STR$ function +STR jsr LB143 ; make sure we have a number + ldu #STRBUF+2 ; convert FP number to string in temporary string buffer + jsr LBDDC + leas 2,s ; don't return to the function evaluator (which will do a numeric type check) + ldx #STRBUF+1 ; point to number string + bra LB518 ; to stash the string in string space and return to the "evaluate term" caller +; Reserve B bytes of string space. Return start in X and FRESPC +LB50D stx V4D ; save X somewhere in case the caller needs it +LB50F bsr LB56D ; allocate string space +LB511 stx STRDES+2 ; save pointer to allocated space in the temporary descriptor + stb STRDES ; save length in the temporary descriptor + rts +LB516 leax -1,x ; move pointer back one (to compensate for the increment below) +; Scan from X until either NUL or one of the string terminators is found +LB518 lda #'" ; set terminator to be string delimiter +LB51A sta CHARAC ; set both delimiters + sta ENDCHR +LB51E leax 1,x ; move to next character + stx RESSGN ; save start of string + stx STRDES+2 ; save start of string in the temporary string descriptor + ldb #-1 ; initialize length counter to -1 (compensate for initial INCB) +LB526 incb ; bump string length + lda ,x+ ; get character from string + beq LB537 ; brif end of line + cmpa CHARAC ; is it delimiter #1? + beq LB533 ; brif so + cmpa ENDCHR ; is it delimiter #2? + bne LB526 ; brif not - keep scanning +LB533 cmpa #'" ; string delimiter? + beq LB539 ; brif so - don't move pointer back +LB537 leax -1,x ; move pointer back (so we don't consume the delimiter) +LB539 stx COEFPT ; save end of string address + stb STRDES ; save string length + ldu RESSGN ; get start of string + cmpu #STRBUF+2 ; is it at the start of the string buffer? + bhi LB54C ; brif so - don't copy it to string space + bsr LB50D ; allocate string space + ldx RESSGN ; point to beginning of the string + jsr LB645 ; copy string data (B bytes) from (X) to (FRESPC) +; Put temporary string descriptor on the string stack +LB54C ldx TEMPPT ; get top of string stack + cmpx #CFNBUF ; is the string stack full? + bne LB558 ; brif not + ldb #15*2 ; code for "string formula too complex" +LB555 jmp LAC46 ; raise error +LB558 lda STRDES ; get string length + sta 0,x ; save it in the string stack descriptor + ldd STRDES+2 ; get string data pointer + std 2,x ; save in string stack descriptor + lda #0xff ; set value type to string + sta VALTYP + stx LASTPT ; set pointer to last used entry on the string stack + stx FPA0+2 ; set pointer to descriptor in the current evaluation value + leax 5,x ; advance string stack pointer + stx TEMPPT + rts +; Reserve B bytes in string space. If there isn't enough space, try compacting string space and +; then try the allocation again. If it still fails, raise OS error. +LB56D clr GARBFL ; flag that compaction not yet done +LB56F clra ; zero extend the length + pshs d ; save requested string length + ldd STRTAB ; get current bottom of strings + subd ,s+ ; calculate new bottom of strings and remove zero extension + cmpd FRETOP ; does the string fit? + blo LB585 ; brif not - try compaction + std STRTAB ; save new bottom of strings + ldx STRTAB ; get bottom of strings + leax 1,x ; now X points to the real start of the allocated space + stx FRESPC ; save the string pointer + puls b,pc ; restore length and return +LB585 ldb #2*13 ; code for out of string space + com GARBFL ; have we compacted string space yet? + beq LB555 ; brif so - raise error + bsr LB591 ; compact string space + puls b ; get back string length + bra LB56F ; go try allocation again +; Compact string space +; This is an O(n^2) algorithm. It first searches all extant strings for the highest address data pointer +; that hasn't already been moved into the freshly compacted string space. If then moves that string data +; up to the highest address it can go to. It repeats this process over and over until it finds no string +; that isn't already in the compacted space. While doing this, it has to search all strings on the string +; stack (this is why the string stack is needed - so we can track anonymous strings), all scalar string +; variables, and *every* entry in every string array. +LB591 ldx MEMSIZ ; get to of string space +LB593 stx STRTAB ; save top of uncompacted stringspace + clra ; zero out D and reset pointer to discovered variable to NULL + clrb + std V4B + ldx FRETOP ; point to bottom of string space + stx V47 ; save as lowest match address (match will be higher) + ldx #STRSTK ; point to start of string stack +LB5A0 cmpx TEMPPT ; are we at the top of the string stack? + beq LB5A8 ; brif so - done with the string stack + bsr LB5D8 ; check for string in uncompacted space (and advance pointer) + bra LB5A0 ; check another on the string stack +LB5A8 ldx VARTAB ; point to start of scalar variables +LB5AA cmpx ARYTAB ; end of scalars? + beq LB5B2 ; brif so + bsr LB5D2 ; check for string in uncompacted space and advance pointer + bra LB5AA ; check another variable +LB5B2 stx V41 ; save address of end of variables (address of first array) +LB5B4 ldx V41 ; get start of the next array +LB5B6 cmpx ARYEND ; end of arrays? + beq LB5EF ; brif so + ldd 2,x ; get length of array + addd V41 ; add to start of array + std V41 ; save address of next array + lda 1,x ; get second character of variable name + bpl LB5B4 ; brif numeric + ldb 4,x ; get number of dimensions + aslb ; two bytes per dimension size + addb #5 ; add in fixed overhead for array descriptor + abx ; now X points to first array element +LB5CA cmpx V41 ; at the start of the next array? + beq LB5B6 ; brif so - go handle another array + bsr LB5D8 ; check for string in uncompacted space (and advance pointer) + bra LB5CA ; process next array element +LB5D2 lda 1,x ; get second character of variable name + leax 2,x ; move to variable data + bpl LB5EC ; brif numeric +LB5D8 ldb ,x ; get length of string + beq LB5EC ; brif NULL - don't need to check data pointer + ldd 2,x ; get data pointer + cmpd STRTAB ; is it in compacted string space? + bhi LB5EC ; brif so + cmpd V47 ; is it better match than previous best? + bls LB5EC ; brif not + stx V4B ; save descriptor address of best match + std V47 ; save new best data pointer match +LB5EC leax 5,x ; move to next descriptor +LB5EE rts +LB5EF ldx V4B ; get descriptor address of the matched string + beq LB5EE ; brif we didn't find one - we're done + clra ; zero extend length + ldb ,x ; get string length + decb ; subtract one (we won't have a NULL string here) + addd V47 ; now D points to the address of the end of the string data + std V43 ; save as top address of move + ldx STRTAB ; set top of uncompacted space as destination + stx V41 + jsr LAC20 ; move string to top of uncompactedspace + ldx V4B ; point to string descriptor + ldd V45 ; get new data pointer address + std 2,x ; update descriptor + ldx V45 ; get bottom of copy destination + leax -1,x ; move back below it + jmp LB593 ; go search for another string to move (and set new bottom of string space) +; Concatenate two strings. We come here directly from the operator handler rather than via a JSR. +LB60F ldd FPA0+2 ; get string descriptor for the first string + pshs d ; save it + jsr LB223 ; evaluate a second string (concatenation is left associative) + jsr LB146 ; make sure we have a string + puls x ; get back first string descriptor + stx RESSGN ; save it + ldb ,x ; get length of first string + ldx FPA0+2 ; get pointer to second string + addb ,x ; add length of second string + bcc LB62A ; brif combined length is OK + ldb #2*14 ; raise string too long error + jmp LAC46 +LB62A jsr LB50D ; reserve room for new string + ldx RESSGN ; get descriptor address of the first string + ldb ,x ; get length of first string + bsr LB643 ; copy it to string space + ldx V4D ; get descriptor address of second string + bsr LB659 ; get string details for second string + bsr LB645 ; copy second string into new string space + ldx RESSGN ; get pointer to first string + bsr LB659 ; remove it from the string stack if possible + jsr LB54C ; put new string on the string stack + jmp LB168 ; return to expression evaluator +; Copy B bytes to space pointed to by FRESPC +LB643 ldx 2,x ; get source address from string descriptor +LB645 ldu FRESPC ; get destination address + incb ; compensate for decb + bra LB64E ; do the copy +LB64A lda ,x+ ; copy a byte + sta ,u+ +LB64E decb ; done yet? + bne LB64A ; brif not + stu FRESPC ; save destination pointer + rts +; Fetch details of string in FPA0+2 and remove from the string stack if possible +LB654 jsr LB146 ; make sure we have a string +LB657 ldx FPA0+2 ; get descriptor pointer +LB659 ldb ,x ; get length of string + bsr LB675 ; see if it's at the top of the string stack and remove it if so + bne LB672 ; brif not removed + ldx 5+2,x ; get start address of string just removed + leax -1,x ; move pointer down 1 + cmpx STRTAB ; is it at the bottom of string space? + bne LB66F ; brif not + pshs b ; save length + addd STRTAB ; add length to start of strings (A was cleared previously) + std STRTAB ; save new string space start (deallocated space for this string) + puls b ; get back string length +LB66F leax 1,x ; restore pointer to pointing at the actual string data + rts +LB672 ldx 2,x ; get data pointer for the string + rts +; Remove string pointed to by X from the string stack if it is at the top of the stack; return with +; A clear and Z set if string removed +LB675 cmpx LASTPT ; is it at the top of the string stack? + bne LB680 ; brif not - do nothing + stx TEMPPT ; save new top of stack + leax -5,x ; move the "last" pointer back as well + stx LASTPT + clra ; flag string removed +LB680 rts +; LEN function +LEN bsr LB686 ; get string details +LB683 jmp LB4F3 ; return unsigned length in B +LB686 bsr LB654 ; get string details and remove from string stack + clr VALTYP ; set value type to numeric + tstb ; set flags according to length + rts +; CHR$ function +CHR jsr LB70E ; get 8 bit unsigned integer to B +LB68F ldb #1 ; allocate a one byte string + jsr LB56D + lda FPA0+3 ; get character code + jsr LB511 ; save reserved string details in temp descriptor + sta ,x ; put character in string +LB69B leas 2,s ; don't go back to function handler - avoid numeric type check +LB69D jmp LB54C ; return temporary string on string stack +; ASC function +ASC bsr LB6A4 ; get first character of argument + bra LB683 ; return unsigned code in B +LB6A4 bsr LB686 ; fetch string details + beq LB706 ; brif NULL string + ldb ,x ; get character at start of string + rts +; LEFT$ function +LEFT bsr LB6F5 ; get arguments from the stack +LB6AD clra ; clear pointer offset (set to start of string) +LB6AE cmpb ,x ; are we asking for more characters than there are in the string? + bls LB6B5 ; brif not + ldb ,x ; only return the number that are in the string + clra ; force starting offset to be the start of the string +LB6B5 pshs b,a ; save offset and length + jsr LB50F ; reserve space in string space + ldx V4D ; point to original string descriptor + bsr LB659 ; get string details + puls b ; get string offset + abx ; now X points to the start of the data to copy + puls b ; get length of copy + jsr LB645 ; copy the data to the allocated space + bra LB69D ; return temp string on string stack +; RIGHT$ function +RIGHT bsr LB6F5 ; get arguments from stack + suba ,x ; subtract length of original string from desired length + nega ; now A is offset into old string where we start copying + bra LB6AE ; go handle everything else +; MID$ function +MID ldb #255 ; default length is the whole string + stb FPA0+3 ; save it + jsr GETCCH ; see what we have after offset + cmpa #') ; end of function? + beq LB6DE ; brif so - no length + jsr LB26D ; force a comma + bsr LB70B ; get length parameter +LB6DE bsr LB6F5 ; get string and offset parameters from the stack + beq LB706 ; brif we have a 0 offset requested (string offsets are 1-based) + clrb ; clear length counter + deca ; subtract one from position parameter (we work on 0-based, param is 1-based) + cmpa ,x ; is start greater than length of string? + bhs LB6B5 ; brif so - return NULL string + tfr a,b ; save absolute position parameter + subb ,x ; now B is postition less length + negb ; now B is amount of string to copy + cmpb FPA0+3 ; is it less than the length requested? + bls LB6B5 ; brif so + ldb FPA0+3 ; set length to the requested length + bra LB6B5 ; go finish up copying the substring +; Common routine for LEFT$, RIGHT$, MID$ - check for ) and fetch string data and first parameter +; from the stack. These were evaluated in the function evaluation handler. (It's not clear that doing +; it that way is actually beneficial. However, this is what the ROM designers did, so here we are.) +LB6F5 jsr LB267 ; make sure we have ) + ldu ,s ; get return address - we're going to mess with the stack + ldx 5,s ; get address of string descriptor + stx V4D ; save descriptor adddress + lda 4,s ; get first numeric parameter in both A and B + ldb 4,s + leas 7,s ; clean up stack + tfr u,pc ; return to original caller +LB706 jmp LB44A ; raise FC error +; Evaluate an unsigned 8 bit expression to B +LB709 jsr GETNCH ; move to next character +LB70B jsr LB141 ; evaluate a numeric expression +LB70E jsr LB3E9 ; convert to integer in D + tsta ; are we negative or > 255? + bne LB706 ; brif so - FC error + jmp GETCCH ; fetch current input character and return +; VAL function +VAL jsr LB686 ; get string details + lbeq LBA39 ; brif NULL string - return 0 + ldu CHARAD ; get input pointer so we can replace it later + stx CHARAD ; point interpreter at string data + abx ; calculate end address of the string + lda ,x ; get byte after the end of the string + pshs u,x,a ; save end of string address, input pointer, and character after end of string + clr ,x ; put a NUL after the string (stops the number interpreter) + jsr GETCCH ; get input character at start of string + jsr LBD12 ; evaluate numeric expression in string + puls a,x,u ; get back saved character and pointers + sta ,x ; restore byte after string + stu CHARAD ; restore interpeter's input pointer + rts +; Evaluate unsigned expression to BINVAL, then evaluate an unsigned expression to B +LB734 bsr LB73D ; evaluate expression + stx BINVAL ; save result +LB738 jsr LB26D ; make sure there's a comma + bra LB70B ; evaluate unsigned expression to B +; Evaluate unsigned expression in X +LB73D jsr LB141 ; evaluate numeric expression +LB740 lda FP0SGN ; is it negative? + bmi LB706 ; brif so + lda FP0EXP ; get exponent + cmpa #0x90 ; largest possible exponent for 16 bits + bhi LB706 ; brif too large + jsr LBCC8 ; move binary point to right of FPA0 + ldx FPA0+2 ; get resulting unsigned value + rts +; PEEK function +PEEK bsr LB740 ; get address to X + ldb ,x ; get the value at that address + jmp LB4F3 ; return B as unsigned value +; POKE function +POKE bsr LB734 ; evaluate address and byte value + ldx BINVAL ; get address + stb ,x ; put value there + rts +; LLIST command +LLIST ldb #-2 ; set output device to printer + stb DEVNUM + jsr GETCCH ; reset flags for input character and fall through to LIST +; LIST command +LIST pshs cc ; save zero flag (end of statement) + jsr LAF67 ; parse line number + jsr LAD01 ; find address of that line + stx LSTTXT ; save that address as the start of the list + puls cc ; get back ent of statement flag + beq LB784 ; brif end of line - list whole program + jsr GETCCH ; are we at the end of the line (one number)? + beq LB789 ; brif end of line + cmpa #0xac ; is it "-"? + bne LB783 ; brif not + jsr GETNCH ; eat the "-" + beq LB784 ; brif no second number - list to end of program + jsr LAF67 ; evaluate the second number + beq LB789 ; brif illegal number +LB783 rts +LB784 ldu #0xffff ; this will cause listing to do the entire program + stu BINVAL +LB789 leas 2,s ; don't return to the caller - we'll jump back to the main loop + ldx LSTTXT ; get address of line to list +LB78D jsr LB95C ; do a newline if needed + jsr LA549 ; do a break check + ldd ,x ; get address of next line + bne LB79F ; brif not end of program +LB797 jsr LA42D ; close output file + clr DEVNUM ; reset device to screen + jmp LAC73 ; go back to immediate mode +LB79F stx LSTTXT ; save new line address + ldd 2,x ; get line number of this line + cmpd BINVAL ; is it above the end line? + bhi LB797 ; brif so - return + jsr LBDCC ; display line number + jsr LB9AC ; put a space after it + ldx LSTTXT ; get line address + bsr LB7C2 ; detokenize the line + ldx [LSTTXT] ; get pointer to next line + ldu #LINBUF+1 ; point to start of detokenized line +LB7B9 lda ,u+ ; get byte from detokenized line + beq LB78D ; brif end of line + jsr LB9B1 ; output character + bra LB7B9 ; handle next character +; Detokenize a line from (X) to the line input buffer +LB7C2 jsr RVEC24 ; do the RAM hook dance + leax 4,x ; move past next line pointer and line number + ldy #LINBUF+1 ; point to line input buffer (destination) +LB7CB lda ,x+ ; get character from tokenized line + beq LB820 ; brif end of input + bmi LB7E6 ; brif it's a token + cmpa #': ; colon? + bne LB7E2 ; brif not + ldb ,x ; get what's after the colon + cmpb #0x84 ; ELSE? + beq LB7CB ; brif so - suppress the colon + cmpb #0x83 ; '? + beq LB7CB ; brif so - suppress the colon + skip2 +LB7E0 lda #'! ; placeholder for unknown token +LB7E2 bsr LB814 ; stow output character + bra LB7CB ; go process another input character +LB7E6 ldu #COMVEC-10 ; point to command interptation table + cmpa #0xff ; is it a function? + bne LB7F1 ; brif not + lda ,x+ ; get function token + leau 5,u ; shift to the function half of the interpretation tables +LB7F1 anda #0x7f ; remove token bias +LB7F3 leau 10,u ; move to next command/function table + tst ,u ; is this table active? + beq LB7E0 ; brif not - use place holder + suba ,u ; subtract number of tokens handled by this table entry + bpl LB7F3 ; brif this token isn't handled here + adda ,u ; undo extra subtraction + ldu 1,u ; get reserved word list for this table +LB801 deca ; are we at the right entry? + bmi LB80A ; brif so +LB804 tst ,u+ ; end of entry? + bpl LB804 ; brif not + bra LB801 ; see if we're there yet +LB80A lda ,u ; get character from wordlist + bsr LB814 ; put character in the buffer + tst ,u+ ; end of word? + bpl LB80A ; brif not + bra LB7CB ; go handle another input character +LB814 cmpy #LINBUF+LBUFMX ; is there room? + bhs LB820 ; brif not + anda #0x7f ; lose bit 7 + sta ,y+ ; save character in output + clr ,y ; make sure there's always a NUL terminator +LB820 rts +; Tokenize the line that the input pointer is pointing to; put result in the line input buffer; return +; length in D +LB821 jsr RVEC23 ; do the RAM hook dance + ldx CHARAD ; get input pointer + ldu #LINBUF ; set destination pointer +LB829 clr V43 ; clear alpha string flag + clr V44 ; clear DATA flag +LB82D lda ,x+ ; get input character + beq LB852 ; brif end of input + tst V43 ; are we handling an alphanumeric string? + beq LB844 ; brif not + jsr LB3A2 ; set carry if not alpha + bcc LB852 ; brif alpha + cmpa #'0 ; is it below the digits? + blo LB842 ; brif so + cmpa #'9 ; is it within the digits? + bls LB852 ; brif so +LB842 clr V43 ; flag that we're past the alphanumeric string +LB844 cmpa #0x20 ; space? + beq LB852 ; brif so - keep it + sta V42 ; save scan delimiter + cmpa #'" ; string delimiter? + beq LB886 ; brif so - copy until another " + tst V44 ; doing "DATA"? + beq LB86B ; brif not +LB852 sta ,u+ ; put character in output + beq LB85C ; brif end of input + cmpa #': ; colon? + beq LB829 ; brif so - reset DATA and alpha string flags +LB85A bra LB82D ; go process another input character +LB85C clr ,u+ ; put a double NUL at the end + clr ,u+ + tfr u,d ; calculate length of result (includes double NUL and an extra two bytes) + subd #LINHDR + ldx #LINBUF-1 ; point to one before the output + stx CHARAD ; set input pointer there + rts +LB86B cmpa #'? ; print abbreviation? + bne LB873 ; brif not + lda #0x87 ; token for PRINT + bra LB852 ; go stash it +LB873 cmpa #'' ; REM abbreviation? + bne LB88A ; brif not + ldd #0x3a83 ; colon plus ' token + std ,u++ ; put it in the output +LB87C clr V42 ; set delimiter to NUL +LB87E lda ,x+ ; get input + beq LB852 ; brif end of line + cmpa V42 ; at the delimiter? + beq LB852 ; brif so +LB886 sta ,u+ ; save in output + bra LB87E ; keep scanning for delimiter +LB88A cmpa #'0 ; is it below digits? + blo LB892 ; brif so + cmpa #';+1 ; is it digit, colon, or semicolon? + blo LB852 ; brif so +LB892 leax -1,x ; move input pointer back one (to point at this input character) + pshs u,x ; save input and output pointers + clr V41 ; set token type to 0 (command) + ldu #COMVEC-10 ; point to command interpretation table +LB89B clr V42 ; set token counter to 0 (0x80) +LB89D leau 10,u ; + lda ,u ; get number of reserved words + beq LB8D4 ; brif this table isn't active + ldy 1,u ; point to reserved words list +LB8A6 ldx ,s ; get input pointer +LB8A8 ldb ,y+ ; get character from reserved word table + subb ,x+ ; compare with input character + beq LB8A8 ; brif exact match + cmpb #0x80 ; brif it was the last character in word and exact match + bne LB8EA ; brif not + leas 2,s ; remove original input pointer from stack + puls u ; get back output pointer + orb V42 ; create token value (B has 0x80 from above) + lda V41 ; get token type + bne LB8C2 ; brif function + cmpb #0x84 ; is it ELSE? + bne LB8C6 ; brif not + lda #': ; silently add a colon before ELSE +LB8C2 std ,u++ ; put two byte token into output + bra LB85A ; go handle more input +LB8C6 stb ,u+ ; save single byte token + cmpb #0x86 ; DATA? + bne LB8CE ; brif not + inc V44 ; set DATA flag +LB8CE cmpb #0x82 ; REM? + beq LB87C ; brif so - skip over rest of line +LB8D2 bra LB85A ; go handle more input +LB8D4 ldu #COMVEC-5 ; point to interpretation table, function style +LB8D7 com V41 ; invert token flag + bne LB89B ; brif we haven't already done functions + puls x,u ; restore input and output pointers + lda ,x+ ; copy first character + sta ,u+ + jsr LB3A2 ; set C if not alpha + bcs LB8D2 ; brif not alpha - it isn't a variable + com V43 ; set alphanumeric string flag + bra LB8D2 ; process more input +LB8EA inc V42 ; bump token number + deca ; checked all in this table? + beq LB89D ; brif so + leay -1,y ; unconsume last compared character +LB8F1 ldb ,y+ ; end of entry? + bpl LB8F1 ; brif not + bra LB8A6 ; check next reserved word +; PRINT command +PRINT beq LB958 ; brif no argument - do a newline + bsr LB8FE ; process print options + clr DEVNUM ; reset output to screen + rts +LB8FE cmpa #'@ ; is it PRINT @? + bne LB907 ; brif not + jsr LA554 ; move cursor to correct location + bra LB911 ; handle some more +LB907 cmpa #'# ; device number specified? + bne LB918 ; brif not + jsr LA5A5 ; parse device number + jsr LA406 ; check for valid output file +LB911 jsr GETCCH ; get input character + beq LB958 ; brif nothing - do newline + jsr LB26D ; need comma after @ or # +LB918 jsr RVEC9 ; do the RAM hook boogaloo +LB91B beq LB965 ; brif end of input +LB91D cmpa #0xa4 ; TAB(? + beq LB97E ; brif so + cmpa #', ; comma (next tab field)? + beq LB966 ; brif so + cmpa #'; ; semicolon (do not advance print position) + beq LB997 ; brif so + jsr LB156 ; evaluate expression + lda VALTYP ; get type of value + pshs a ; save it + bne LB938 ; brif string + jsr LBDD9 ; convert FP number to string + jsr LB516 ; parse a string and put on string stack +LB938 bsr LB99F ; print string + puls b ; get back variable type + jsr LA35F ; set up print parameters + tst PRTDEV ; is it a display device? + beq LB949 ; brif so + bsr LB958 ; do a newline + jsr GETCCH ; get input + bra LB91B ; process more print stuff +LB949 tstb ; set flags on print position + bne LB954 ; brif not at start of line + jsr GETCCH ; get current input + cmpa #', ; comma? + beq LB966 ; skip to next tab field if so + bsr LB9AC ; send a space +LB954 jsr GETCCH ; get input character + bne LB91D ; brif not end of statement +LB958 lda #0x0d ; carriage return + bra LB9B1 ; send it to output +LB95C jsr LA35F ; set up print parameters +LB95F beq LB958 ; brif width is 0 + lda DEVPOS ; get line position + bne LB958 ; brif not at start of line +LB965 rts +LB966 jsr LA35F ; set up print parameters + beq LB975 ; brif line width is 0 + ldb DEVPOS ; get line position + cmpb DEVLCF ; at or past last comma field? + blo LB977 ; brif so + bsr LB958 ; move to next line + bra LB997 ; handle more stuff +LB975 ldb DEVPOS ; get line position +LB977 subb DEVCFW ; subtract a comma field width + bhs LB977 ; brif we don't have a remainder yet + negb ; now B is number of of spaces needed + bra LB98E ; go advance +LB97E jsr LB709 ; evaluate TAB distance + cmpa #') ; closing )? + lbne LB277 ; brif not + jsr LA35F ; set up print parameters + subb DEVPOS ; subtract print position from desired position + bls LB997 ; brif we're already past it +LB98E tst PRTDEV ; is it a display device? + bne LB997 ; brif not +LB992 bsr LB9AC ; output a space + decb ; done enough? + bne LB992 ; brif not +LB997 jsr GETNCH ; get input character + jmp LB91B ; process more items +; cpoy string from (X-1) to output +LB99C jsr LB518 ; parse the string +LB99F jsr LB657 ; get string details +LB9A2 incb ; compensate for decb +LB9A3 decb ; done all of the string? + beq LB965 ; brif so + lda ,x+ ; get character from string + bsr LB9B1 ; send to output + bra LB9A3 ; go do another character +LB9AC lda #0x20 ; space character + skip2 +LB9AF lda #'? ; question mark character +LB9B1 jmp PUTCHR ; output character +; The floating point math package and related functions and operations follow from here +; to the end of the Color Basic ROM area +LB9B4 ldx #LBEC0 ; point to FP constant 0.5 + bra LB9C2 ; add 0.5 to FPA0 +LB9B9 jsr LBB2F ; unpack FP data from (X) to FPA1 +; subtraction operator +LB9BC com FP0SGN ; invert sign of FPA0 (subtracting is adding the negative) + com RESSGN ; that also inverts the sign differential + bra LB9C5 ; go add the negative of FPA0 to FPA1 +LB9C2 jsr LBB2F ; unpack FP data from (X) to FPA1 +; addition operator +LB9C5 tstb ; check exponent of FPA0 + lbeq LBC4A ; copy FPA1 to FPA0 if FPA0 is 0 + ldx #FP1EXP ; point X to FPA1 (first operand) as the operand to denormalize +LB9CD tfr a,b ; put exponent of FPA1 into B + tstb ; is FPA1 0? + beq LBA3E ; brif exponent is 0 - no-op; adding 0 to FPA0 + subb FP0EXP ; get difference in exponents - number of bits to shift the smaller mantissa + beq LBA3F ; brif exponents are equal - no need to denormalize + blo LB9E2 ; brif FPA0 > FPA1 + sta FP0EXP ; replace result exponent with FPA1's (FPA1 is bigger) + lda FP1SGN ; also copy sign over + sta FP0SGN + ldx #FP0EXP ; point to FPA0 (we need to denormalize the smaller number) + negb ; invert the difference - this is the number of bits to shift the mantissa +LB9E2 cmpb #-8 ; do we have to shift by a whole byte? + ble LBA3F ; brif so start by shifting whole bytes to the right + clra ; clear overflow byte + lsr 1,x ; shift high bit of mantissa right (LSR will force a zero into the high bit) + jsr LBABA ; shift remainder of mantissa right -B times +LB9EC ldb RESSGN ; get the sign flag + bpl LB9FB ; brif signs are the same (we add the mantissas then) + com 1,x ; complement the mantissa and extra precision bytes + com 2,x + com 3,x + com 4,x + coma + adca #0 ; add one to A (COM sets C); this may cause a carry to enter the ADD below +LB9FB sta FPSBYT ; save extra precision byte + lda FPA0+3 ; add the main mantissa bytes (and propage carry from above) + adca FPA1+3 + sta FPA0+3 + lda FPA0+2 + adca FPA1+2 + sta FPA0+2 + lda FPA0+1 + adca FPA1+1 + sta FPA0+1 + lda FPA0 + adca FPA1 + sta FPA0 + tstb ; were signs the same? + bpl LBA5C ; brif so - number may have gotten bigger so normalize if needed +LBA18 bcs LBA1C ; brif we had a carry - result is positive?) + bsr LBA79 ; do a proper negation of FPA0 mantissa +LBA1C clrb ; clear temporary exponent accumulator +LBA1D lda FPA0 ; test high byte of mantissa + bne LBA4F ; brif not 0 - we need to do bit shifting + lda FPA0+1 ; shift left 8 bits + sta FPA0 + lda FPA0+2 + sta FPA0+1 + lda FPA0+3 + sta FPA0+2 + lda FPSBYT + sta FPA0+3 + clr FPSBYT + addb #8 ; account for 8 bits shifted + cmpb #5*8 ; shifted 5 bytes worth? + blt LBA1D ; brif not +LBA39 clra ; zero out exponent and sign - result is 0 +LBA3A sta FP0EXP ; set exponent and sign + sta FP0SGN +LBA3E rts +LBA3F bsr LBAAE ; shift FPA0 mantissa to the right + clrb ; clear carry + bra LB9EC ; get on with adding +LBA44 incb ; account for one bit shift + asl FPSBYT ; shift mantissa and extra precision left + rol FPA0+3 + rol FPA0+2 + rol FPA0+1 + rol FPA0 +LBA4F bpl LBA44 ; brif we haven't got a 1 in bit 7 + lda FP0EXP ; get exponent of result + pshs b ; subtract shift count from exponent + suba ,s+ + sta FP0EXP ; save adjusted exponent + bls LBA39 ; brif we underflowed - set result to 0 + skip2 +LBA5C bcs LBA66 ; brif mantissa overflowed + asl FPSBYT ; get bit 7 of expra precision to C (used for round off) + lda #0 ; set to 0 without affecting C + sta FPSBYT ; clear out extra precision bits + bra LBA72 ; go round off result +LBA66 inc FP0EXP ; bump exponent (for a right shift to bring carry in) + beq LBA92 ; brif we overflowed + ror FPA0 ; shift carry into mantissa, shift right + ror FPA0+1 + ror FPA0+2 + ror FPA0+3 +LBA72 bcc LBA78 ; brif no round-off needed + bsr LBA83 ; add one to mantissa + beq LBA66 ; brif carry - need to shift right again +LBA78 rts +LBA79 com FP0SGN ; invert sign of value +LBA7B com FPA0 ; first do a one's copmlement + com FPA0+1 + com FPA0+2 + com FPA0+3 +LBA83 ldx FPA0+2 ; add one to mantissa (after one's complement gives two's complement) + leax 1,x ; bump low word + stx FPA0+2 + bne LBA91 ; brif no carry from low word + ldx FPA0 ; bump high word + leax 1,x + stx FPA0 +LBA91 rts +LBA92 ldb #2*5 ; code for overflow + jmp LAC46 ; raise error +LBA97 ldx #FPA2-1 ; point to FPA2 +LBA9A lda 4,x ; shift mantissa right by 8 bits + sta FPSBYT + lda 3,x + sta 4,x + lda 2,x + sta 3,x + lda 1,x + sta 2,x + lda FPCARY ; and handle extra precision on the left + sta 1,x +LBAAE addb #8 ; account for 8 bits shifted + ble LBA9A ; brif more shifts needed + lda FPSBYT ; get sub byte (extra precision) + subb #8 ; undo the 8 added above + beq LBAC4 ; brif difference is 0 +LBAB8 asr 1,x ; shift mantissa and sub byte one bit (keep mantissa high bit set) +LBABA ror 2,x + ror 3,x + ror 4,x + rora + incb ; account for one shift + bne LBAB8 ; brif not enought shifts yet +LBAC4 rts +LBAC5 fcb 0x81,0x00,0x00,0x00,0x00 ; packed FP 1.0 +LBACA bsr LBB2F ; unpack FP value from (X) to FPA1 +; multiplication operator +LBACC beq LBB2E ; brif exponent of FPA0 is 0 (result is 0) + bsr LBB48 ; calculate exponent of product +LBAD0 lda #0 ; zero out mantissa of FPA2 + sta FPA2 + sta FPA2+1 + sta FPA2+2 + sta FPA2+3 + ldb FPA0+3 ; multiply FPA1 by LSB of FPA0 + bsr LBB00 + ldb FPSBYT ; save extra precision byte + stb VAE + ldb FPA0+2 + bsr LBB00 ; again for next byte of FPA0 + ldb FPSBYT + stb VAD + ldb FPA0+1 ; again for next byte of FPA0 + bsr LBB00 + ldb FPSBYT + stb VAC + ldb FPA0 ; and finally for the high byte + bsr LBB02 + ldb FPSBYT + stb VAB + jsr LBC0B ; copy mantissa from FPA2 to FPA0 (result) + jmp LBA1C ; normalize +LBB00 beq LBA97 ; brif multiplier is 0 - just shift, don't multiply +LBB02 coma ; set carry +LBB03 lda FPA2 ; get FPA2 MS byte + rorb ; data bit to carry; will be 0 when all shifts done + beq LBB2E ; brif 8 shifts done + bcc LBB20 ; brif data bit is 0 - no addition + lda FPA2+3 ; add mantissa of FPA1 and FPA2 + adda FPA1+3 + sta FPA2+3 + lda FPA2+2 + adca FPA1+2 + sta FPA2+2 + lda FPA2+1 + adca FPA1+1 + sta FPA2+1 + lda FPA2 + adca FPA1 +LBB20 rora ; shift carry into FPA2 + sta FPA2 + ror FPA2+1 + ror FPA2+2 + ror FPA2+3 + ror FPSBYT + clra ; clear carry + bra LBB03 +LBB2E rts +; Unpack FP value from (X) to FPA1 +LBB2F ldd 1,x ; copy mantissa (and sign) + sta FP1SGN ; save sign bit + ora #0x80 ; make sure mantissa has bit 7 set + std FPA1 + ldb FP1SGN ; get sign + eorb FP0SGN ; set if FPA0 sign differs + stb RESSGN + ldd 3,x ; copy remainder of mantissa + std FPA1+2 + lda ,x ; and exponent + sta FP1EXP + ldb FP0EXP ; fetch FPA0 exponent and set flags + rts +; Calculate eponent for product of FPA0 and FPA1 +LBB48 tsta ; is FPA1 zero? + beq LBB61 ; brif so + adda FP0EXP ; add to exponent of FPA0 (this is how scientific notation works) + rora ; set V if we *don't* have an overflow + rola + bvc LBB61 ; brif exponent too larger or small + adda #0x80 ; restore the bias + sta FP0EXP ; set result exponent + beq LBB63 ; brif 0 - clear FPA0 + lda RESSGN ; the result sign (negative if signs differ) is the result sign + sta FP0SGN ; so set it as such + rts +LBB5C lda FP0SGN ; get sign of FPA0 + coma ; invert sign + bra LBB63 ; zero sign and exponent +LBB61 leas 2,s ; don't go back to caller (mul/div) - return to previous caller +LBB63 lbpl LBA39 ; brif we underflowed - go zero things out +LBB67 jmp LBA92 ; raise overflow error +; fast multiply by 10 - leave result in FPA0 +LBB6A jsr LBC5F ; copy FPA0 to FPA1 (for addition later) + beq LBB7C ; brif exponent is 0 - it's a no-op then + adda #2 ; this gives "times 4" + bcs LBB67 ; raise overflow if required + clr RESSGN ; set result sign to "signs the same" + jsr LB9CD ; add FPA1 to FPA0 "times 5" + inc FP0EXP ; times 10 + beq LBB67 ; brif overflow +LBB7C rts +LBB7D fcb 0x84,0x20,0x00,0x00,0x00 ; packed FP constant 10.0 +; Divide by 10 +LBB82 jsr LBC5F ; move FPA0 to FPA1 + ldx #LBB7D ; point to constant 10 + clrb ; zero sign +LBB89 stb RESSGN ; result will be positive or zero + jsr LBC14 ; unpack constant 10 to FPA0 + skip2 ; fall through to division (divide FPA1 by 10) +LBB8F bsr LBB2F ; unpack FP number from (X) to FPA1 +; division operator +LBB91 beq LBC06 ; brif FPA0 is 0 - division by zero + neg FP0EXP ; get exponent of reciprocal of the divisor + bsr LBB48 ; calculate exponent of quotient + inc FP0EXP ; bump exponent (due to division algorithm below) + beq LBB67 ; brif overflow + ldx #FPA2 ; point to temporary storage location + ldb #4 ; do 5 bytes + stb TMPLOC ; save counter + ldb #1 ; shift counter and quotient byte +LBBA4 lda FPA0 ; compare mantissa of FPA0 to FPA1, set C if FPA1 less + cmpa FPA1 + bne LBBBD + lda FPA0+1 + cmpa FPA1+1 + bne LBBBD + lda FPA0+2 + cmpa FPA1+2 + bne LBBBD + lda FPA0+3 + cmpa FPA1+3 + bne LBBBD + coma ; set C if FPA0 = FPA1 (it "goes") +LBBBD tfr cc,a ; save "it goes" status + rolb ; rotate carry into quotient + bcc LBBCC ; brif carry clear - haven't done 8 shifts yet + stb ,x+ ; save quotient byte + dec TMPLOC ; done enough bytes? + bmi LBBFC ; brif done all 5 + beq LBBF8 ; brif last byte + ldb #1 ; reset shift counter and quotient byte +LBBCC tfr a,cc ; get back carry status + bcs LBBDE ; brif it "went" +LBBD0 asl FPA1+3 ; shift mantissa (dividend) left + rol FPA1+2 + rol FPA1+1 + rol FPA1 + bcs LBBBD ; brif carry - it "goes" so we have to bump quotient + bmi LBBA4 ; brif high order bit is set - compare mantissas + bra LBBBD ; otherwise, count a 0 bit and try next bit +LBBDE lda FPA1+3 ; subtract mantissa of FPA0 from mantissa of FPA1 + suba FPA0+3 + sta FPA1+3 + lda FPA1+2 + sbca FPA0+2 + sta FPA1+2 + lda FPA1+1 + sbca FPA0+1 + sta FPA1+1 + lda FPA1 + sbca FPA0 + sta FPA1 + bra LBBD0 ; go check for another go +LBBF8 ldb #0x40 ; only two bits in last byte (for rounding) + bra LBBCC ; go do the last byte +LBBFC rorb ; get low bits to bits 7,6 and C to bit 5 + rorb + rorb + stb FPSBYT ; save result extra precision + bsr LBC0B ; move FPA2 mantissa to FPA0 (result) + jmp LBA1C ; go normalize the result +LBC06 ldb #2*10 ; division by zero + jmp LAC46 ; raise error +; Copy mantissa of FPA2 to FPA0 +LBC0B ldx FPA2 ; copy high word + stx FPA0 + ldx FPA2+2 ; copy low word + stx FPA0+2 + rts +; unpack FP number at (X) to FPA0 +LBC14 pshs a ; save register + ldd 1,x ; get mantissa high word and sign + sta FP0SGN ; set sign + ora #0x80 ; make sure mantissa always has bit 7 set + std FPA0 + clr FPSBYT ; clear extra precision + ldb ,x ; get exponent + ldx 3,x ; copy mantissa low word + stx FPA0+2 + stb FP0EXP ; save exponent (and set flags) + puls a,pc ; restore register and return +LBC2A ldx #V45 ; point to FPA4 + bra LBC35 ; pack FPA0 there +LBC2F ldx #V40 ; point to FPA3 + skip2 ; fall through to pack FPA0 there +LBC33 ldx VARDES ; get variable descriptor pointer +; Pack FPA0 to (X) +LBC35 lda FP0EXP ; get exponent + sta ,x ; save it + lda FP0SGN ; get sign + ora #0x7f ; force set low bits - only keep sign in high bit + anda FPA0 ; merge in bits 6-0 of high byte of mantissa + sta 1,x ; save it + lda FPA0+1 ; copy next highest byte + sta 2,x + ldu FPA0+2 ; and the low word of the mantissa + stu 3,x + rts +; Copy FPA1 to FPA0; return with sign in A +LBC4A lda FP1SGN ; copy sign +LBC4C sta FP0SGN + ldx FP1EXP ; copy exponent, mantissa high byte + stx FP0EXP + clr FPSBYT ; clear extra precision + lda FPA1+1 ; copy mantissa second highest byte + sta FPA0+1 + lda FP0SGN ; set sign for return + ldx FPA1+2 ; copy low word of mantissa + stx FPA0+2 + rts +; Copy FPA0 to FPA1 +LBC5F ldd FP0EXP ; copy exponent and high byte of mantissa + std FP1EXP + ldx FPA0+1 ; copy middle bytes of mantissa + stx FPA1+1 + ldx FPA0+3 ; copy low byte of mantissa and sign + stx FPA1+3 + tsta ; set flags on exponent + rts +; check FPA0: return B = 0, if FPA0 is 0, 0xff if negative, and 0x01 if positive +LBC6D ldb FP0EXP ; get exponent + beq LBC79 ; brif 0 +LBC71 ldb FP0SGN ; get sign +LBC73 rolb ; get sign to C + ldb #0xff ; set for negative result + bcs LBC79 ; brif negative + negb ; set to 1 for positive +LBC79 rts +; SGN function +SGN bsr LBC6D ; get sign of FPA0 +LBC7C stb FPA0 ; save result + clr FPA0+1 ; clear next lower 8 bits + ldb #0x88 ; exponent if mantissa is 8 bit integer +LBC82 lda FPA0 ; get high bits of mantissa + suba #0x80 ; set C if mantissa was positive (will cause a negation if it was negative) +LBC86 stb FP0EXP ; set exponent + ldd ZERO ; clear out low word + std FPA0+2 + sta FPSBYT ; clear extra precision + sta FP0SGN ; set sign to positive + jmp LBA18 ; normalize the result +; ABS function +ABS clr FP0SGN ; force FPA0 to be positive (yes, it's that simple) + rts +; Compare packed FP number at (X) to FPA0 +; Return with B = -1, 0, 1 for FPA0 <, =, > (X) and flags set based on that +LBC96 ldb ,x ; get exponent of (X) + beq LBC6D ; brif (X) is 0 + ldb 1,x ; get MS byte of mantissa of (X) + eorb FP0SGN ; set bit 7 if signs of (X) and FPA0 differ + bmi LBC71 ; brif signs differ - no need to compare the magnitude +LBCA0 ldb FP0EXP ; compare exponents and brif different + cmpb ,x + bne LBCC3 + ldb 1,x ; compare mantissa (but we have to pack the FPA0 bits first + orb #0x7f ; keep only sign bit (note: signs are the same) + andb FPA0 ; merge in the mantissa bits from FPA0 + cmpb 1,x ; do the packed versions match? + bne LBCC3 ; brif not + ldb FPA0+1 ; compare second byte of mantissas + cmpb 2,x + bne LBCC3 + ldb FPA0+2 ; compare third byte of mantissas + cmpb 3,x + bne LBCC3 + ldb FPA0+3 ; compare low byte of mantissas, but use subtraction so B = 0 on match + subb 4,x + bne LBCC3 + rts ; return B = 0 if (X) = FPA0 +LBCC3 rorb ; shift carry to bit 7 (C set if FPA0 < (X)) + eorb FP0SGN ; invert the comparision sense if the signs are negative + bra LBC73 ; interpret comparison result +; Shift mantissa of FPA0 until the binary point is immediately to the right of the mantissa and set up the +; result as a two's complement value. +LBCC8 ldb FP0EXP ; get exponent of FPA0 + beq LBD09 ; brif FPA0 is zero - we don't have to do anything, just blank it + subb #0xa0 ; calculate number of shifts to get to the correct exponent (binary point to the right) + lda FP0SGN ; do we have a positive number? + bpl LBCD7 ; brif so + com FPCARY ; negate the mantissa and set extra inbound precision to the correct sign + jsr LBA7B +LBCD7 ldx #FP0EXP ; point to FPA0 + cmpb #-8 ; moving by whole bytes? + bgt LBCE4 ; brif not + jsr LBAAE ; do bit shifting + clr FPCARY ; clear carry in byte + rts +LBCE4 clr FPCARY ; clear the extra carry in precision + lda FP0SGN ; get sign of value + rola ; get sign to carry (so rotate repeats the sign) + ror FPA0 ; shift the first bit + jmp LBABA ; do the shifting dance +; INT function +INT ldb FP0EXP ; get exponent + cmpb #0xa0 ; is the number big enough that there can be no fractional part? + bhs LBD11 ; brif so - we don't have to do anything + bsr LBCC8 ; go shift binary point to the right of the mantissa + stb FPSBYT ; save extra precision bits + lda FP0SGN ; get original sign + stb FP0SGN ; force result to be positive + suba #0x80 ; set C if we had a positive result + lda #0xa0 ; set exponent to match denormalized result + sta FP0EXP + lda FPA0+3 ; save low byte + sta CHARAC + jmp LBA18 ; go normalize (this will correct for the two's complement representation of negatives) +LBD09 stb FPA0 ; replace mantissa of FPA0 with contents of B + stb FPA0+1 + stb FPA0+2 + stb FPA0+3 +LBD11 rts +; Convert ASCII string to FP +; BUG: no overflow is checked on the decimal exponent in exponential notation. +LBD12 ldx ZERO ; zero out FPA0 and temporaries + stx FP0SGN + stx FP0EXP + stx FPA0+1 + stx FPA0+2 + stx V47 + stx V45 + bcs LBD86 ; brif input character is numeric + jsr RVEC19 ; do the RAM hook dance + cmpa #'- ; regular negative sign + bne LBD2D ; brif not + com COEFCT ; invert sign + bra LBD31 ; process stuff after the sign +LBD2D cmpa #'+ ; regular plus? + bne LBD35 ; brif not +LBD31 jsr GETNCH ; get character after sign + bcs LBD86 ; brif numeric +LBD35 cmpa #'. ; decimal point? + beq LBD61 ; brif so + cmpa #'E ; scientific notation + bne LBD65 ; brif not + jsr GETNCH ; eat the "E" + bcs LBDA5 ; brif numeric + cmpa #0xac ; negative sign (token)? + beq LBD53 ; brif so + cmpa #'- ; regular negative? + beq LBD53 ; brif so + cmpa #0xab ; plus sign (token)? + beq LBD55 ; brif so + cmpa #'+ ; regular plus? + beq LBD55 + bra LBD59 ; brif no sign found +LBD53 com V48 ; set exponent sign to negative +LBD55 jsr GETNCH ; eat the sign + bcs LBDA5 ; brif numeric +LBD59 tst V48 ; is the exponent sign negatvie? + beq LBD65 ; brif not + neg V47 ; negate base 10 exponent + bra LBD65 +LBD61 com V46 ; toggle decimal point flag + bne LBD31 ; brif we haven't seen two decimal points +LBD65 lda V47 ; get base 10 exponent + suba V45 ; subtract number of places to the right + sta V47 ; we now have a complete decimal exponent + beq LBD7F ; brif we have no base 10 shifting required + bpl LBD78 ; brif positive exponent +LBD6F jsr LBB82 ; divide FPA0 by 10 (shift decimal point left) + inc V47 ; bump exponent + bne LBD6F ; brif we haven't reached 0 yet + bra LBD7F ; return result +LBD78 jsr LBB6A ; multiply by 10 + dec V47 ; downshift the exponent + bne LBD78 ; brif not at 0 yet +LBD7F lda COEFCT ; get desired sign + bpl LBD11 ; brif it will be positive - no need to do anything + jmp LBEE9 ; flip the sign of FPA0 +LBD86 ldb V45 ; get the decimal count + subb V46 ; (if decimal seen, will add one; otherwise it does nothing) + stb V45 + pshs a ; save new digit + jsr LBB6A ; multiply partial result by 10 + puls b ; get back digit + subb #'0 ; remove ASCII bias + bsr LBD99 ; add B to FPA0 + bra LBD31 ; go process another digit +LBD99 jsr LBC2F ; save FPA0 to FPA3 + jsr LBC7C ; convert B to FP number + ldx #V40 ; point to FPA3 + jmp LB9C2 ; add FPA3 and FPA0 +LBDA5 ldb V47 ; get exponent value + aslb ; times 2 + aslb ; times 4 + addb V47 ; times 5 + aslb ; times 10 + suba #'0 ; remove ASCII bias + pshs b ; save acculated result + adda ,s+ ; add new digit to accumulated result + sta V47 ; save new accumulated decimal exponent + bra LBD55 ; interpret another exponent character +LBDB6 fcb 0x9b,0x3e,0xbc,0x1f,0xfd ; packed FP: 99999999.9 +LBDBB fcb 0x9e,0x6e,0x6b,0x27,0xfd ; packed FP: 999999999 +LBDC0 fcb 0x9e,0x6e,0x6b,0x28,0x00 ; pakced FP: 1E9 +LBDC5 ldx #LABE8-1 ; point to "IN" message + bsr LBDD6 ; output the string + ldd CURLIN ; get basic line number +LBDCC std FPA0 ; save 16 bit unsigned integer + ldb #0x90 ; exponent for upper 16 bits of FPA0 to be an integer + coma ; set C (force normalization to treat as positive) + jsr LBC86 ; zero bottom half, save exponent, and normalize + bsr LBDD9 ; convert FP number to ASCII string +LBDD6 jmp LB99C ; output string +; Convert FP number to ASCII string +LBDD9 ldu #STRBUF+3 ; point to buffer address that will not cause string to go to string space +LBDDC lda #0x20 ; default sign is a space character + ldb FP0SGN ; get sign of value + bpl LBDE4 ; brif positive + lda #'- ; use negative sign +LBDE4 sta ,u+ ; save sign + stu COEFPT ; save output buffer pointer + sta FP0SGN ; save sign character + lda #'0 ; result is 0 if exponent is 0 + ldb FP0EXP ; get exponent + lbeq LBEB8 ; brif FPA0 is 0 + clra ; base 10 exponent is 0 for > 1 + cmpb #0x80 ; is number > 1? + bhi LBDFF ; brif so + ldx #LBDC0 ; point to 1E+09 + jsr LBACA ; shift decimal to the right by 9 spaces + lda #-9 ; account for shift +LBDFF sta V45 ; save base 10 exponent +LBE01 ldx #LBDBB ; point to 999999999 + jsr LBCA0 ; are we above that? + bgt LBE18 ; brif so +LBE09 ldx #LBDB6 ; point to 99999999.9 + jsr LBCA0 ; are we above that? + bgt LBE1F ; brif in range + jsr LBB6A ; multiply by 10 (we were small) + dec V45 ; account for shift + bra LBE09 ; see if we've come into range +LBE18 jsr LBB82 ; divide by 10 + inc V45 ; account for shift + bra LBE01 ; see if we've come into range +LBE1F jsr LB9B4 ; add 0.5 to FPA0 (rounding) + jsr LBCC8 ; do the integer dance + ldb #1 ; default decimal flag (force immediate decimal) + lda V45 ; get base 10 exponent + adda #10 ; account for "unormalized" number + bmi LBE36 ; brif number < 1.0 + cmpa #11 ; do we have more than 9 places? + bhs LBE36 ; brif so - do scientific notation + deca + tfr a,b + lda #2 ; force no scientific notation +LBE36 deca ; subtract wo without affecting carry + deca + sta V47 ; save exponent - 0 is do not display in scientific notation + stb V45 ; save number of places to left of decimal + bgt LBE4B ; brif >= 1 + ldu COEFPT ; point to string buffer + lda #'. ; put decimal + sta ,u+ + tstb ; is there anything to left of decimal? + beq LBE4B ; brif not + lda #'0 ; store a zero + sta ,u+ +LBE4B ldx #LBEC5 ; point to powers of 10 + ldb #0x80 ; set digit counter to 0x80 +LBE50 lda FPA0+3 ; add mantissa to power of 10 + adda 3,x + sta FPA0+3 + lda FPA0+2 + adca 2,x + sta FPA0+2 + lda FPA0+1 + adca 1,x + sta FPA0+1 + lda FPA0 + adca ,x + sta FPA0 + incb ; add one to digit counter + rorb ; put carry into bit 7 + rolb ; set V if carry and sign differ + bvc LBE50 ; brif positive mantissa or carry is 0 and negative mantissa + bcc LBE72 ; brif negative mantissa + subb #10+1 ; take 9's complement if adding mantissa + negb +LBE72 addb #'0-1 ; add ASCII bias + leax 4,x ; move to next power of 10 + tfr b,a ; save digit + anda #0x7f ; remove add/subtract flag + sta ,u+ ; put in output + dec V45 ; do we need a decimal yet? + bne LBE84 ; brif not + lda #'. ; put decimal + sta ,u+ +LBE84 comb ; toggle bit 7 (add/sub flag) + andb #0x80 ; only keep bit 7 + cmpx #LBEC5+9*4 ; done all places? + bne LBE50 ; brif not +LBE8C lda ,-u ; get last character + cmpa #'0 ; was it 0? + beq LBE8C ; brif so + cmpa #'. ; decimal? + bne LBE98 ; brif not + leau -1,u ; move past decimal if it isn't needed +LBE98 lda #'+ ; plus sign + ldb V47 ; get scientific notation exponent + beq LBEBA ; brif not scientific notation + bpl LBEA3 ; brif positive exponent + lda #'- ; negative sign for base 10 exponent + negb ; switch to positive exponent +LBEA3 sta 2,u ; put sign + lda #'E ; put "E" + sta 1,u + lda #'0-1 ; init to ASCII 0 (compensate for INC) +LBEAB inca ; bump digit + subb #10 ; have we hit the correct one yet? + bcc LBEAB ; brif not + addb #'9+1 ; convert units digit to ASCII + std 3,u ; put exponent in output + clr 5,u ; put trailing NUL + bra LBEBC ; go reset pointer +LBEB8 sta ,u ; store last character +LBEBA clr 1,u ; put NUL at the end +LBEBC ldx #STRBUF+3 ; point to start of string + rts +LBEC0 fcb 0x80,0x00,0x00,0x00,0x00 ; packed FP 0.5 +LBEC5 fqb -100000000 + fqb 10000000 + fqb -1000000 + fqb 100000 + fqb -10000 + fqb 1000 + fqb -100 + fqb 10 + fqb -1 +LBEE9 lda FP0EXP ; get exponent of FPA0 + beq LBEEF ; brif 0 - don't flip sign + com FP0SGN ; flip sign +LBEEF rts +; Expand a polynomial of the form +; AQ+BQ^3+CQ^5+DQ^7..... where Q is FPA0 and X points to a table +LBEF0 stx COEFPT ; save coefficient table pointer + jsr LBC2F ; copy FPA0 to FPA3 + bsr LBEFC ; multiply FPA3 by FPA0 + bsr LBF01 ; expand polynomial + ldx #V40 ; point to FPA3 +LBEFC jmp LBACA ; multiply FPA0 by FPA3 +LBEFF stx COEFPT ; save coefficient table counter +LBF01 jsr LBC2A ; move FPA0 to FPA4 + ldx COEFPT ; get the current coefficient + ldb ,x+ ; get the number of entries + stb COEFCT ; save as counter + stx COEFPT ; save new pointer +LBF0C bsr LBEFC ; multiply (X) and FPA0 + ldx COEFPT ; get this coefficient + leax 5,x ; move to next one + stx COEFPT ; save new pointer + jsr LB9C2 ; add (X) to FPA0 + ldx #V45 ; point X to FPA4 + dec COEFCT ; done all coefficients? + bne LBF0C ; brif more left + rts +; RND function +RND jsr LBC6D ; set flags on FPA0 + bmi LBF45 ; brif negative - set seed + beq LBF3B ; brif 0 - do random between 0 and 1 + bsr LBF38 ; convert to integer + jsr LBC2F ; save range value + bsr LBF3B ; get random number + ldx #V40 ; point to FPA3 + bsr LBEFC ; multply (X) by FPA0 + ldx #LBAC5 ; point to FP 1.0 + jsr LB9C2 ; add 1 to FPA0 +LBF38 jmp INT ; return integer value +LBF3B ldx RVSEED+1 ; move variable random number seed to FPA0 + stx FPA0 + ldx RVSEED+3 + stx FPA0+2 +LBF45 ldx RSEED ; move fixed seed to FPA1 + stx FPA1 + ldx RSEED+2 + stx FPA1+2 + jsr LBAD0 ; multiply them + ldd VAD ; get lowest order product bytes + addd #0x658b ; add a constant + std RVSEED+3 ; save it as new seed + std FPA0+2 ; save in result + ldd VAB ; get high order extra product bytes + adcb #0xb0 ; add upper bytes of constant + adca #5 + std RVSEED+1 ; save as new seed + std FPA0 ; save as result + clr FP0SGN ; set result to positive + lda #0x80 ; set exponent to 0 < FPA0 < 1 + sta FP0EXP + lda FPA2+2 ; get a byte from FPA2 + sta FPSBYT ; save as extra precision + jmp LBA1C ; go normalize FPA0 +RSEED fqb 0x40e64dab ; constant random number generator seed +; SIN function +SIN jsr LBC5F ; copy FPA0 to FPA1 + ldx #LBFBD ; point to 2*pi + ldb FP1SGN ; get sign of FPA1 + jsr LBB89 ; divide FPA0 by 2*pi + jsr LBC5F ; copy FPA0 to FPA1 + bsr LBF38 ; convert FPA0 to an integer + clr RESSGN ; set result to positive + lda FP1EXP ; get exponent of FPA1 + ldb FP0EXP ; get exponent of FPA0 + jsr LB9BC ; subtract FPA0 from FPA1 + ldx #LBFC2 ; point to FP 0.25 + jsr LB9B9 ; subtract FPA0 from 0.25 (pi/2) + lda FP0SGN ; get result sign + pshs a ; save it + bpl LBFA6 ; brif positive + jsr LB9B4 ; add 0.5 (pi) to FPA0 + lda FP0SGN ; get sign of result + bmi LBFA9 ; brif negative + com RELFLG ; if 3pi/2 >= arg >= pi/2 +LBFA6 jsr LBEE9 ; flip sign of FPA0 +LBFA9 ldx #LBFC2 ; point to 0.25 + jsr LB9C2 ; add 0.25 (pi/2) to FPA0 + puls a ; get original sign + tsta ; was it positive + bpl LBFB7 ; brif so + jsr LBEE9 ; flip result sign +LBFB7 ldx #LBFC7 ; point to series coefficients + jmp LBEF0 ; go calculate value +LBFBD fcb 0x83,0x49,0x0f,0xda,0xa2 ; 2*pi +LBFC2 fcb 0x7f,0x00,0x00,0x00,0x00 ; 0.25 +; modified taylor series SIN coefficients +LBFC7 fcb 6-1 ; six coefficients + fcb 0x84,0xe6,0x1a,0x2d,0x1b ; -((2pi)^11)/11! + fcb 0x86,0x28,0x07,0xfb,0xf8 ; ((2pi)^9)/9! + fcb 0x87,0x99,0x68,0x89,0x01 ; -((2pi)^7)/7! + fcb 0x87,0x23,0x35,0xdf,0xe1 ; ((2pi)^5)/5! + fcb 0x86,0xa5,0x5d,0xe7,0x28 ; -(2pi)^3)/3! + fcb 0x83,0x49,0x0f,0xda,0xa2 ; 2*pi +; these 12 bytes are unused + fcb 0xa1,0x54,0x46,0x8f,0x13,0x8f,0x52,0x43 + fcb 0x89,0xcd,0xa6,0x81 +; these are the hardware interrupt vectors (coco1/2 only) + fdb SW3VEC + fdb SW2VEC + fdb FRQVEC + fdb IRQVEC + fdb SWIVEC + fdb NMIVEC + fdb RESVEC diff -r 000000000000 -r 605ff82c4618 defs.s --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/defs.s Sat Dec 08 19:57:01 2018 -0700 @@ -0,0 +1,477 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; First, some useful macro definitions to avoid most instances with FCB standing for actual code. +skip1 macro noexpand + fcb 0x21 ; opcode of BRN which causes next code byte to be skipped + endm +skip2 macro noexpand + fcb 0x8c ; opcode of CMPX immediate which will skip two bytes (but clobbers flags) + endm +skip2keepc macro noexpand + fcb 0x7d ; opcode of TST extended which will skip two bytes like above but preserves C + endm +skip1lda macro noexpand + fcb 0x86 ; opcode of LDA immediate; used to load A with nonzero and skip a CLRA, usually + endm +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Now various constants used in the ROMs. +STKBUF equ 58 ; extra room kept between the stack and variables during memory checks +DEBDEL equ 0x45e ; delay constant for debouncing the keyboard (10ms at standard clock speed) +LBUFMX equ 250 ; size of the line input buffer +MAXLIN equ 0xfa ; the maximum MS byte of a line number +DOSBUF equ 0x2600 ; address where DOS command loads track 24 +DIRLEN equ 32 ; size of a directory entry on disk +SECLEN equ 256 ; length of a disk sector +SECMAX equ 18 ; number of sectors per track +TRKLEN equ SECMAX*SECLEN ; number of bytes on a track +TRKMAX equ 35 ; number of tracks per disk +FATLEN equ 6+(TRKMAX-1)*2 ; size of memory copy of a file allocation table +GRANMX equ (TRKMAX-1)*2 ; number of granules per disk +FCBLEN equ SECLEN+25 ; size of a file control block +INPFIL equ 0x10 ; input file type +OUTFIL equ 0x20 ; output file type +RANFIL equ 0x40 ; random file type +ROWMAX equ 24 ; the number of rows on the hi-res text screen +HRESSCRN equ 0x2000 ; where the hi-res text screen starts in logical memory +HRESBUFF equ 0xc000 ; where the HGET/HPUT buffers are in logical memory +TMPSTACK equ 0xdfff ; temporary stack location when in secondary memory map +CURCHAR equ SPACE ; hi-res text screen cursor character +; HGET/HPUT buffer header structure +HB.ADDR equ 0 ; address of next buffer (2 bytes) +HB.NUM equ 2 ; number of this buffer (1 byte) +HB.SIZE equ 3 ; number of bytes in this buffer (2 bytes) +HB.LEN equ 5 ; number of bytes in a buffer header +; Video register definitions +; INIT0 +COCO equ 0x80 ; 1 = Coco2 compatible mode +MMUEN equ 0x40 ; 1 = MMU enabled +IEN equ 0x20 ; 1 = IRQ enabled +FEN equ 0x10 ; 1 = FIRQ enabled +MC3 equ 0x08 ; 1 = FExx constant page enabled +MC2 equ 0x04 ; 1 = Standard SCS operation +MC1 equ 0x02 ; ROM map control bit 1 +MC0 equ 0x01 ; ROM map control bit 0 +; Interrupt enable/status bits +TMR equ 0x20 ; TIMER +HBORD equ 0x10 ; horizontal border +VBORD equ 0x08 ; vertical border +EI2 equ 0x04 ; serial data +EI1 equ 0x02 ; keyboard +EI0 equ 0x01 ; cartridge port +; Memory block definitions +BLOCK6.0 equ 0x30 ; hi-res graphics screen +BLOCK6.1 equ 0x31 ; hi-res graphics screen +BLOCK6.2 equ 0x32 ; hi-res graphics screen +BLOCK6.3 equ 0x33 ; hi-res graphics screen +BLOCK6.4 equ 0x34 ; HGET/HPUT buffers +BLOCK6.5 equ 0x35 ; stack space for hi-res graphics operations +BLOCK6.6 equ 0x36 ; hi-res text screen +BLOCK6.7 equ 0x37 ; unused by Basic +BLOCK7.0 equ 0x38 ; standard 64K memory map +BLOCK7.1 equ 0x39 ; standard 64K memory map +BLOCK7.2 equ 0x3a ; standard 64K memory map +BLOCK7.3 equ 0x3b ; standard 64K memory map +BLOCK7.4 equ 0x3c ; standard 64K memory map +BLOCK7.5 equ 0x3d ; standard 64K memory map +BLOCK7.6 equ 0x3e ; standard 64K memory map +BLOCK7.7 equ 0x3f ; standard 64K memory map (constant page comes from this block) +; Disk Basic FAT data format. The memory image contains a six byte header followed by the cached copy +; of the FAT from the disk. The cached copy may be modified compared to the copy on disk. This is indicated +; by a nonzero of the "FAT1" byte. +FAT0 equ 0 ; active file counter for this image +FAT1 equ 1 ; data changed flag - nonzero means write is needed +FATCON equ 6 ; offset to start of FAT data +; Directory entry format. Entries are 32 bytes long but only the first 16 are used. +DIRNAM equ 0 ; file name (8 characters) +DIREXT equ 8 ; extention (3 characters) +DIRTYP equ 11 ; file type number +DIRASC equ 12 ; ASCII flag +DIRGRN equ 13 ; first granule number for the file +DIRLST equ 14 ; number of bytes used in last sector (two bytes are needed) +; File control block format. +FCBTYP equ 0 ; file type: 0x40=RANDOM, 0x20=WRITE, 0x10=READ +FCBDRV equ 1 ; drive number +FCBFGR equ 2 ; first granule in the file +FCBCGR equ 3 ; current granule being used +FCBSEC equ 4 ; current sector within granule +FCBCPT equ 5 ; input file - next char to be read; output: sector full flag (sequential) +FCBPOS equ 6 ; current print position (0 for random files) +FCBREC equ 7 ; current record number (random files) +FCBRLN equ 9 ; random file record length +FCBBUF equ 11 ; pointer to the start of this files buffer +FCBSOF equ 13 ; sector offset to current position in the record +FCBFLG equ 15 ; GET/PUT flag +FCBCFL equ 16 ; cache flag: nonzero is cache full (sequential) +FCBCDT equ 17 ; cache byte (sequential) +FCBDIR equ 18 ; directory entry number +FCBLST equ 19 ; number of bytes in last sector of the file +FCBGET equ 21 ; GET record counter (how many pulled from record) +FCBPUT equ 23 ; PUT record counter (where to put next byte in record) +FCBDFL equ 23 ; input file: data left flag (nonzero is no data) +FCBLFT equ 24 ; number of characters left in input file buffer +FCBCON equ 25 ; offset to data buffer +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; This is the memory map and variable definitions for the entire system (all levels of Basic) +; NOTE: unnamed variables (Vxx) are generally scratch variables used for various purpose. As such, no further description is +; provided for those. + org 0 + setdp 0 +ENDFLG rmb 1 ; STOP/END flag: bit 7 set means STOP, bit 7 clear means END +CHARAC rmb 1 ; terminator character for searches +ENDCHR rmb 1 ; terminator character for searches +TMPLOC rmb 1 ; scratch variable +IFCTR rmb 1 ; number of "IF" statements encountered while scanning a line +DIMFLG rmb 1 ; nonzero means dimensioning, zero means evaluating (when parsing array) +VALTYP rmb 1 ; the type of the current value when evaluating expressions +GARBFL rmb 1 ; flag for whether garbage collection has been done yet when allocating strings +ARYDIS rmb 1 ; nonzero means don't parse arrays when parsing variables +INPFLG rmb 1 ; are we running INPUT or READ - nonzero for INPUT +RELFLG rmb 1 ; which relational operators are currently in play +TEMPPT rmb 2 ; pinter to the top of the string stack +LASTPT rmb 2 ; pointer to the last entry on the string stack +TEMPTR rmb 2 ; scratch pointer +TMPTR1 rmb 2 ; scratch pointer +FPA2 rmb 4 ; floating point accumulator, mantissa only +BOTSTK rmb 2 ; bottom of stack during last check; also a scratch variable +TXTTAB rmb 2 ; pointer to the beginning of the program +VARTAB rmb 2 ; pointer to the beginning of the scalar variable table (end of program) +ARYTAB rmb 2 ; pointer to the beginning of the array table (end of scalars) +ARYEND rmb 2 ; pointer to the end of the array table (start of free memory) +FRETOP rmb 2 ; top of free memory (start of string space) +STRTAB rmb 2 ; start of allocates string space +FRESPC rmb 2 ; pointer to newly allocated string space +MEMSIZ rmb 2 ; top of memory (and string space) allowed for the interpreter to size +OLDTXT rmb 2 ; save line number during STOP +BINVAL rmb 2 ; binary value of line number during parsing +OLDPTR rmb 2 ; saving input pointer during STOP +TINPTR rmb 2 ; temporary input pointer storage +DATTXT rmb 2 ; line number where we're looking for DATA +DATPTR rmb 2 ; input pointer where we're looking for DATA +DATTMP rmb 2 ; scratch pointer for INPUT and READ +VARNAM rmb 2 ; storage for variable name during parsing +VARPTR rmb 2 ; pointer to variable descriptor +VARDES rmb 2 ; pointer to variable descriptor +RELPTR rmb 2 ; pointer to relational operator processing routine +TRELFL rmb 1 ; temporary relational operator flags +V40 rmb 1 +V41 rmb 1 +V42 rmb 1 +V43 rmb 1 +V44 rmb 1 +V45 rmb 1 +V46 rmb 1 +V47 rmb 1 +V48 rmb 2 +V4A rmb 1 +V4B rmb 2 +V4D rmb 2 +FP0EXP rmb 1 ; floating point accumulator #0 exponent +FPA0 rmb 4 ; floating point accumulator #0 mantissa +FP0SGN rmb 1 ; floating point accumulator #0 sign +COEFCT rmb 1 ; polynomial expansion coefficient counter +STRDES rmb 5 ; temporary string descriptor +FPCARY rmb 1 ; floating point carry byte +FP1EXP rmb 1 ; floating point accumulator #1 exponent +FPA1 rmb 4 ; floating point accumulator #1 mantissa +FP1SGN rmb 1 ; floating point accumulator #1 sign +RESSGN rmb 1 ; pre-calculated result sign +FPSBYT rmb 1 ; floating point extra precision underflow byte +COEFPT rmb 2 ; polynomial expansion coefficient pointer +LSTTXT rmb 2 ; current line pointer during LIST +CURLIN rmb 2 ; line number of currently executing statement (0xffff for direct) +DEVCFW rmb 1 ; width of tab field for current device +DEVLCF rmb 1 ; column of last tab field for current device +DEVPOS rmb 1 ; current output column for current device +DEVWID rmb 1 ; number of characters per line for current device +PRTDEV rmb 1 ; print device flag: 0xff for non-display device (tape) +DEVNUM rmb 1 ; current I/O device/file number (signed) +CINBFL rmb 1 ; EOF flag for console in (0xff for EOF) +RSTFLG rmb 1 ; warm start enable flag: 0x55 for enabled +RSTVEC rmb 2 ; pointer to warm start routine (must start with NOP) +TOPRAM rmb 2 ; the actual top of memory + rmb 2 ; *unused* +FILSTA rmb 1 ; status of tape file (0=closed, 1=input, 2=output) +CINCTR rmb 1 ; number of characters in buffer +CINPTR rmb 2 ; pointer to current buffer location +BLKTYP rmb 1 ; cassette block type +BLKLEN rmb 1 ; length of cassette block +CBUFAD rmb 2 ; pointer to data buffer for cassette I/O +CCKSUM rmb 1 ; cassette checksum +CSRERR rmb 1 ; cassette error flag/charactercount +CPULWD rmb 1 ; pulse width counter +CPERTM rmb 1 ; bit counter +CBTPHA rmb 1 ; sync phase flag +CLSTSN rmb 1 ; last sine table entry +GRBLOK rmb 1 ; graphic block value for SET/RESET/POINT +IKEYIM rmb 1 ; cached key read during BREAK check +CURPOS rmb 2 ; screen cursor position +ZERO rmb 2 ; always kept as 0 +SNDTON rmb 1 ; tone value for SOUND +SNDDUR rmb 2 ; duration value for SOUND +CMPMID rmb 1 ; 1200/2400 Hz partition (why is this a variable?) +CMP0 rmb 1 ; upper limit of 1200 Hz period (why is this a variable?) +CMP1 rmb 1 ; upper limit of 2400 Hz period (why is this a variable?) +SYNCLN rmb 2 ; number of 0x55s for a cassette leader (why is this a variable?) +BLKCNT rmb 1 ; cursor blink delay counter +LPTBTD rmb 2 ; serial baud rate delay count +LPTLND rmb 2 ; delay for waiting for carriage return +LPTCFW rmb 1 ; tab field width for printer +LPTLCF rmb 1 ; last tab field position for printer +LPTWID rmb 1 ; width of printer line +LPTPOS rmb 1 ; current character position for printer +EXECJP rmb 2 ; default jump address for EXEC +GETNCH rmb 6 ; fetch next character for interpretation (INC CHARAD+1\BNE GETCCH\INC CHARAD) +GETCCH rmb 1 ; re-fetch the current character for interpretation (opcode of LDA extended) +CHARAD rmb 2 ; current input pointer + rmb 3 ; (JMP BROMHK) +VAB rmb 1 +VAC rmb 1 +VAD rmb 1 +VAE rmb 1 +TRCFLG rmb 1 ; is TRACE enabled? nonzero is on +USRADR rmb 2 ; address of the start of USR vectors +FORCOL rmb 1 ; bitmap foreground colour +BAKCOL rmb 1 ; bitmap background colour +WCOLOR rmb 1 ; bitmap working colour +ALLCOL rmb 1 ; byte with all pixels set to WCOLOR +PMODE rmb 1 ; which PMODE is active +ENDGRP rmb 2 ; end of current bitmap graphics page +HORBYT rmb 1 ; number of bytes per bitmap row +BEGGRP rmb 2 ; start of current bitmap graphics page +GRPRAM rmb 1 ; MSB of start of bitmap graphics +HORBEG rmb 2 ; horizontal starting coordinate +VERBEG rmb 2 ; vertical starting coordinate +CSSVAL rmb 1 ; SCREEN's colour set value +SETFLG rmb 1 ; doing PSET or PRESET? (nonzero = PSET) +HOREND rmb 2 ; horizontal ending coordinate +VEREND rmb 2 ; vertical ending coordinate +HORDEF rmb 2 ; default horizontal coordinate +VERDEF rmb 2 ; default vertical coordinate +VCB rmb 2 +VCD rmb 2 +VCF rmb 2 +VD1 rmb 2 +VD3 rmb 1 +VD4 rmb 1 +VD5 rmb 1 +VD6 rmb 1 +VD7 rmb 1 +VD8 rmb 1 +VD9 rmb 1 +VDA rmb 1 +CHGFLG rmb 1 ; flag indicating if graphic data changed +TMPSTK rmb 2 ; temporary stack pointer during PAINT +OCTAVE rmb 1 ; PLAY octave +VOLHI rmb 1 ; PLAY high volume value +VOLLOW rmb 1 ; PLAY low volumnevalue +NOTELN rmb 1 ; PLAY notelength +TEMPO rmb 1 ; PLAY tempo +PLYTMR rmb 2 ; PLAY timer +DOTVAL rmb 1 ; PLAY dotted note timer scale factor +HRMODE equ * ; Coco3 graphics mode +DLBAUD rmb 1 ; DLOAD BAUD constant +HRWIDTH equ * ; Coco3 text mode +TIMOUT rmb 1 ; DLOAD timeout constant +ANGLE rmb 1 ; DRAW angle +SCALE rmb 1 ; DRAW scale +DCOPC rmb 1 ; DSKCON operation code +DCDRV rmb 1 ; DSKCON drive number +DCTRK rmb 1 ; DSKCON track number +DSEC rmb 1 ; DSKCON sector number +DCBPT rmb 2 ; DSKCON buffer pointer +DCSTA rmb 1 ; DSKCON status +FCBTMP rmb 2 ; temp file control block pointer + rmb 13 ; *unused* +SW3VEC rmb 3 ; SWI3 vector +SW2VEC rmb 3 ; SWI2 vector +SWIVEC rmb 3 ; SWI vector +NMIVEC rmb 3 ; NMI vector +IRQVEC rmb 3 ; IRQ vector +FRQVEC rmb 3 ; FIRQ vector +TIMVAL equ * ; Extended Basic TIMER value (2 bytes only) +USRJMP rmb 3 ; jump address for USR function +RVSEED rmb 5 ; floating point random number seed +CASFLG rmb 1 ; capslock state for keyboard +DEBVAL rmb 2 ; keyboar debounce delay (why is this a variable?) +EXPJMP rmb 3 ; exponentiation handler +COMVEC rmb 10 ; Color Basic's command table + rmb 10 ; Extended Basic's command table + rmb 10 ; Disk Basic's command table +USR0 rmb 20 ; ECB USR jump addresses (extra user command table and unused for Disk Basic) +KEYBUF rmb 8 ; keyboard state table +POTVAL rmb 4 ; joystick axis values (left vert, left horiz, right vert, right horiz) +RVEC0 rmb 3 ; RAM hook: OPEN +RVEC1 rmb 3 ; RAM hook: device number check +RVEC2 rmb 3 ; RAM hook: set print parameters +RVEC3 rmb 3 ; RAM hook: console out +RVEC4 rmb 3 ; RAM hook: console in +RVEC5 rmb 3 ; RAM hook: input device number check +RVEC6 rmb 3 ; RAM hook: output device number check +RVEC7 rmb 3 ; RAM hook: close all files +RVEC8 rmb 3 ; RAM hook: close current file +RVEC9 rmb 3 ; RAM hook: PRINT +RVEC10 rmb 3 ; RAM hook: INPUT +RVEC11 rmb 3 ; RAM hook: BREAK check +RVEC12 rmb 3 ; RAM hook: line input +RVEC13 rmb 3 ; RAM hook: terminating line input +RVEC14 rmb 3 ; RAM hook: EOF +RVEC15 rmb 3 ; RAM hook: expression evaluation +RVEC16 rmb 3 ; RAM hook: ERROR handler (on error goto) +RVEC17 rmb 3 ; RAM hook: ERROR handler (regular handling) +RVEC18 rmb 3 ; RAM hook: RUN +RVEC19 rmb 3 ; RAM hook: parse number +RVEC20 rmb 3 ; RAM hook: main interpretation loop +RVEC21 rmb 3 ; RAM hook: SET/RESET/POINT +RVEC22 rmb 3 ; RAM hook: CLS/ECB secondary/RENUM token/GET/PUT +RVEC23 rmb 3 ; RAM hook: crunch line +RVEC24 rmb 3 ; RAM hook: uncrunch line +STRSTK rmb 8*5 ; string stack +CFNBUF rmb 9 ; cassette file name buffer +CASBUF rmb 256 ; cassette I/O buffer +LINHDR rmb 2 ; header for line input (holds line number when editing program) +LINBUF rmb LBUFMX+1 ; line input buffer +STRBUF rmb 41 ; temporary string buffer +VIDRAM rmb 0x200 ; VDG text screen +DBUF0 rmb SECLEN ; Main disk I/O buffer +DBUF1 rmb SECLEN ; Secondary disk I/O buffer (used for VERIFY) +FATBL0 rmb FATLEN ; File allocation table cache for drive 0 +FATBL1 rmb FATLEN ; File allocation table cache for drive 1 +FATBL2 rmb FATLEN ; File allocation table cache for drive 2 +FATBL3 rmb FATLEN ; File allocation table cache for drive 3 +FCBV1 rmb 16*2 ; FCB pointers +RNBFAD rmb 2 ; start of free random file buffer area +FCBADR rmb 2 ; start of file control blocks +DNAMBF rmb 8 ; disk file name buffer +DEXTBF rmb 3 ; disk file name extension buffer +DFLTYP rmb 1 ; disk file type (0=Basic, 1=Data, 2=M/L) +DASCFL rmb 1 ; disk file ASCII flag (0=binary, 0xff=ASCII) +DRUNFL rmb 1 ; RUN flag (bit 1 set then RUN, bit 0 set then close files) +DEFDRV rmb 1 ; default drive number +FCBACT rmb 1 ; number of active file control blocks +DRESFL rmb 1 ; reset flag - will cause all FCBs to be shut down +DLODFL rmb 1 ; load flag - cause a "NEW" on error +DMRGFL rmb 1 ; merge flag: nonzero means we're merging +DUSRVC rmb 20 ; Disk Basic USR addresses +V973 rmb 1 +V974 rmb 2 +V976 rmb 1 +V977 rmb 1 +V978 rmb 2 +WFATVL rmb 2 ; number of granules allocated to trigger a FAT cache write +DFFLEN rmb 2 ; direct access file recrod length +DR0TRK rmb 4 ; current track number for each drive +NMIFLG rmb 1 ; nonzero means NMI vectoring is enabled +DNMIVC rmb 2 ; address to vector to in NMI +RDYTMR rmb 1 ; number of ticks before turning off drive motors +DRGRAM rmb 1 ; cached value of write-only disk control register +DVERFL rmb 1 ; nonzero means write verifies on +ATTCTR rmb 1 ; read/write attempt counter +DFLBUF equ * ; start of random file area, FCBs, etc. + + + org 0x8000 +EXBAS rmb 0x2000 ; Extended Basic ROM area +BASIC rmb 0x2000 ; Color Basic ROM area +ROMPAK equ * ; external ROM address +DOSBAS rmb 0x2000 ; Disk Basic ROM area + rmb 0x1e00 ; Super Extended Basic ROM area +; These are the variables used by SECB on the "constant FExx page" which is unaffected by the MMU +; when the FExx bit in INIT0 is set. +H.CRSLOC rmb 2 ; current cursor location pointer (hi-res) +H.CURSX rmb 1 ; current X coordinate of the cursor (hi-res) +H.CURSY rmb 1 ; current Y coordinate of the cursor (hi-res) +H.COLUMN rmb 1 ; the number of columns on the screen (hi-res) +H.ROW rmb 1 ; the number of rows on the screen (hi-res) +H.DISPEN rmb 2 ; pointer to the end of the screen (hi-res) +H.CRSATT rmb 1 ; current cursor attributes (hi-res) + rmb 1 ; *unused* +H.FCOLOR rmb 1 ; foreground colour (hi-res) +H.BCOLOR rmb 1 ; background colour (hi-res) +H.ONBRK rmb 2 ; ON BRK GOTO line number +H.ONERR rmb 2 ; ON ERR GOTO line number +H.ERROR rmb 1 ; most recent error number encountered (-1 for no error) +H.ONERRS rmb 2 ; line number where ON ERR GOTO was encountered +H.ERLINE rmb 2 ; line number where error actually occurred +H.ONBRKS rmb 2 ; line number where ON BRK GOTO was encountered +H.ERRBRK rmb 1 ; flag for whether we're processing an error or a break event +H.PCOUNT rmb 1 ; number of characters in the HPRINT buffer +H.PBUF rmb 80 ; HPRINT line buffer (max 80 characters per line) + rmb 132 ; *unused* +; This is the set of primary interrupt vectors on the Coco3. These are here so that systems other +; than Basic do not have to rely on low memory remaining constant. Under Basic, these simply bounce +; to the traditional vectors starting at 0x100. +INT.FLAG rmb 1 ; interrupt vector validity flag: 0x55 for valid +INT.JUMP equ * +INT.SWI3 rmb 3 ; SWI3 bounce vector +INT.SWI2 rmb 3 ; SWI2 bounce vector +INT.FIRQ rmb 3 ; FIRQ bounce vector +INT.IRQ rmb 3 ; IRQ bounce vector +INT.SWI rmb 3 ; SWI bounce vector +INT.NMI rmb 3 ; NMI bounce vector +; This is that start of the I/O page. Everything above here is hardware defined. +PIA0 rmb 4 ; PIA0 (keyboard/joystick) + rmb 28 ; PIA0 images (due to sloppy-ass decode) +DA equ * ; DAC address +PIA1 rmb 4 ; PIA1 (misc hardware) + rmb 28 ; PIA1 images (due to sloppy-ass decode) +DSKREG rmb 1 ; Disk control register + rmb 7 ; images of DSKREG due to (sloppy-ass decode) +FDCREG rmb 1 ; FDC command/status register + rmb 1 ; FDC track register + rmb 1 ; FDC sector register + rmb 1 ; FDC data register + rmb 4 ; mirror of FDCREG + rmb 16 ; unused SCS area + rmb 32 ; misc hardware area + rmb 16 ; *coco3 reserved* +INIT0 rmb 1 ; GIME initialization register 0 +INIT1 rmb 1 ; GIME initialization register 1 +IRQENR rmb 1 ; GIME IRQ enable/status register +FIRQENR rmb 1 ; GIME FIRQ enable/status register +V.TIMER rmb 2 ; GIME timer setting + rmb 2 ; *reserved* +VIDEOMOD rmb 1 ; GIME video mode register +VIDEORES rmb 1 ; GIME video resolution register +V.BORDER rmb 1 ; GIME border colour register + rmb 1 ; *reserved* +V.SCROLL rmb 1 ; GIME vertical scroll register +V.OFFSET rmb 2 ; GIME vertical offset (screen address) register +H.OFFSET rmb 1 ; GIME horizontal offset register +MMUREG rmb 16 ; GIME MMU registers (two tasks of 8) +PALETREG rmb 16 ; GIME palette registers +SAMREG equ * ; the SAM registers +V0CLR rmb 1 ; SAM video mode bits +V0SET rmb 1 +V1CLR rmb 1 +V1SET rmb 1 +V2CLR rmb 1 +V2SET rmb 1 +F0CLR rmb 1 ; SAM screen address bits +F0SET rmb 1 +F1CLR rmb 1 +F1SET rmb 1 +F2CLR rmb 1 +F2SET rmb 1 +F3CLR rmb 1 +F3SET rmb 1 +F4CLR rmb 1 +F4SET rmb 1 +F5CLR rmb 1 +F5SET rmb 1 +F6CLR rmb 1 +F6SET rmb 1 + rmb 2 ; *reserved* +R0CLR rmb 1 ; SAM R0 bit (address dependent speedup) +R0SET rmb 1 +R1CLR rmb 1 ; SAM R1 bit (full speedup/coco3 speedup) +R1SET rmb 1 + rmb 4 ; *reserved* +TYCLR rmb 1 ; force ROM mode +TYSET rmb 1 ; set RAM mode + rmb 18 ; *MPU reserved* +SWI3 rmb 2 ; MPU SWI3 vector +SWI2 rmb 2 ; MPU SWI2 vector +FIRQ rmb 2 ; MPU FIRQ vector +IRQ rmb 2 ; MPU IRQ vector +SWI rmb 2 ; MPU SWI vector +NMI rmb 2 ; MPU NMI vector +RESETV rmb 2 ; MPU reset vector diff -r 000000000000 -r 605ff82c4618 disk.s --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/disk.s Sat Dec 08 19:57:01 2018 -0700 @@ -0,0 +1,3566 @@ + *pragma nolist + include defs.s +; Various references to Color Basic and Extended Color Basic which are listed here to allow this ROM to be built independently. +XBWMST equ 0x80c0 +L813C equ 0x813c +L8168 equ 0x8168 +XVEC3 equ 0x8273 +XVEC8 equ 0x8286 +XVEC18 equ 0x829c +L8311 equ 0x8311 +L8316 equ 0x8316 +L836C equ 0x836c +L8748 equ 0x8748 +L880E equ 0x880e +XVEC15 equ 0x8846 +XVEC17 equ 0x88f0 +L8955 equ 0x8955 +L8C1B equ 0x8c1b +XVEC4 equ 0x8cf1 +XVEC9 equ 0x8e90 +L95AC equ 0x95ac +L962E equ 0x962e +L9650 equ 0x9650 +L96CB equ 0x96cb +L96EC equ 0x96ec +L975F equ 0x975f +L9FB5 equ 0x9fb5 +LA0E2 equ 0xa0e2 +BAWMST equ 0xa0e8 +LA171 equ 0xa171 +LA176 equ 0xa176 +PUTCHR equ 0xa282 +LA35F equ 0xa35f +LA37C equ 0xa37c +LA3ED equ 0xa3ed +LA3FB equ 0xa3fb +LA406 equ 0xa406 +LA426 equ 0xa426 +LA429 equ 0xa429 +LA42D equ 0xa42d +LA549 equ 0xa549 +LA59A equ 0xa59a +LA5A2 equ 0xa5a2 +LA5A5 equ 0xa5a5 +LA5AE equ 0xa5ae +LA5C7 equ 0xa5c7 +LA5DA equ 0xa5da +LA5E4 equ 0xa5e4 +LA603 equ 0xa603 +LA616 equ 0xa616 +LA61C equ 0xa61c +LA61F equ 0xa61f +LA928 equ 0xa928 +LA951 equ 0xa951 +LA7D1 equ 0xa7d1 +LA7E9 equ 0xa7e9 +LA974 equ 0xa974 +LAC37 equ 0xac37 +LAC44 equ 0xac44 +LAC46 equ 0xac46 +LAC60 equ 0xac60 +LAC73 equ 0xac73 +LAC7C equ 0xac7c +LACEF equ 0xacef +LAD19 equ 0xad19 +LAD21 equ 0xad21 +LAD33 equ 0xad33 +LAD9E equ 0xad9e +LADC6 equ 0xadc6 +LADD4 equ 0xadd4 +LADEB equ 0xadeb +LAE15 equ 0xae15 +LAF9A equ 0xaf9a +LAFA4 equ 0xafa4 +LAFB1 equ 0xafb1 +LB00C equ 0xb00c +LB01E equ 0xb01e +LB069 equ 0xb069 +LB143 equ 0xb143 +LB146 equ 0xb146 +LB148 equ 0xb148 +LB156 equ 0xb156 +LB244 equ 0xb244 +LB262 equ 0xb262 +LB166 equ 0xb166 +SYNCOMMA equ 0xb26d +LB26F equ 0xb26f +LB277 equ 0xb277 +LB2CE equ 0xb2ce +LB357 equ 0xb357 +LB3E6 equ 0xb3e6 +LB44A equ 0xb44a +LB4F3 equ 0xb4f3 +GIVABF equ 0xb4f4 +LB50F equ 0xb50f +LB516 equ 0xb516 +LB654 equ 0xb654 +LB657 equ 0xb657 +LB659 equ 0xb659 +LB69B equ 0xb69b +LB6A4 equ 0xb6a4 +EVALEXPB equ 0xb70b +LB70E equ 0xb70e +LB738 equ 0xb738 +LB73D equ 0xb73d +LIST equ 0xb764 +LB958 equ 0xb958 +LB95C equ 0xb95c +STRINOUT equ 0xb99c +LB99F equ 0xb99f +LB9A2 equ 0xb9a2 +LB9AC equ 0xb9ac +LB9AF equ 0xb9af +LB9C5 equ 0xb9c5 +LBB91 equ 0xbb91 +LBC14 equ 0xbc14 +LBC33 equ 0xbc33 +LBC35 equ 0xbc35 +LBC5F equ 0xbc5f +INT equ 0xbcee +LBDCC equ 0xbdcc +LBDD9 equ 0xbdd9 + pragma noexpandcond + *pragma list +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; DISK EXTENDED COLOR BASIC +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ifeq DISKVER +DHITOK equ 0xe0 ; highest command token +CYEAR equ '1 ; ones digit of copyright year +MINORVER equ '0 + else +DHITOK equ 0xe1 ; highest command token +CYEAR equ '2 ; ones digit for copyright year +MINORVER equ '1 + endc + org DOSBAS + fcc 'DK' ; magic number Extended Basic uses to identify the presence of a Disk ROM +LC002 bra LC00C ; entry point - jump around vector table +DCNVEC fdb DSKCON ; DSKCON indirect entry point +DSKVAR fdb DCOPC ; address of DSKCON parameter block + ifeq DISKVER-1 +DSINIT fdb DOSINI ; vector to an initialization routine, the utility of which is unclear +DOSVEC fdb DOSCOM ; pointer to the actual location of the DOS command + endc +LC00C ldx #DBUF0 ; point to start of Disk Basic memory +LC00F clr ,x+ ; clear a byte + cmpx #DFLBUF ; at end of statically allocated Disk Basic memory? + bne LC00F ; brif not + ldx #LC109 ; point to initializer for the command interpretation table + ldu #COMVEC+20 ; point to command interpretation table location for Disk Basic + ldb #10 ; ten bytes in each table entry + jsr LA59A ; initialize interpretation table + ldd #LB277 ; syntax error address + std 3,u ; set command handler for "user" table to error + std 8,u ; set function handler for "user" table to error + clr ,u ; mark user table with 0 command keywords + clr 5,u ; mark user table with 0 function keywords + ldd #DXCVEC ; intercept Extended Basic's command handler + std COMVEC+13 + ldd #DXIVEC ; intercept Extended Basic's function handler + std COMVEC+18 + ldu #RVEC0 ; point to first RAM vector +LC03B lda #0x7e ; op code of JMP extended + sta RVEC22 ; set first byte of GET/PUT vector to JMP + sta ,u+ ; set this vector to be a JMP instruction + ldd ,x++ ; get vector destination + std ,u++ ; set the vector destination + cmpx #LC139 ; done the whole list? + bne LC03B ; brif not + ldx #DVEC22 ; install handler for GET/PUT vector + stx RVEC22+1 + ifeq DISKVER-1 + ldx #DVEC20 ; install replacement interpretation loop handler + stx RVEC20+1 + endc + ldx #DUSRVC ; relocate USR vector table + stx USRADR + ldu #LB44A ; default USR address is illegal function call + ldb #10 ; there are 10 vectors +LC061 stu ,x++ ; set a vector + decb ; done all? + bne LC061 ; brif not + ldx #DNMISV ; install NMI handler + stx NMIVEC+1 + lda #0x7e ; make it a JMP + sta NMIVEC + ldx #DIRQSV ; install IRQ handler (ECB and CB already made it a JMP) + stx IRQVEC+1 + lda #19 ; initialze the number of changes needed to trigger a FAT write + sta WFATVL + clr FATBL0 ; mark all four FAT images as having no open files + clr FATBL1 + clr FATBL2 + clr FATBL3 + ldx #DFLBUF ; point to the start of the disk "heap" + stx RNBFAD ; set start of random file buffers as the end of the allocated space + leax 0x100,x ; allocate 256 bytes for random file buffers by default + stx FCBADR ; save start of FCBs (and end of random file buffers) + leax 1,x ; leave an empty byte before FCBs (why?) + stx FCBV1 ; set address of FCB 1 + clr < end? + lblo LB44A ; brif so - throw an error + jsr L836C ; evaluate execution address + jsr LA5C7 ; make sure there's nothing more + ldd #0x200 ; file type 2 (M/L) and binary + std DFLTYP + jsr LCA04 ; open file for output + clra ; write preamble header + bsr LCFB5 + ldd 2,s ; get end address + subd 4,s ; subtract start - gets length + addd #1 ; length is inclusive + tfr d,y ; set data counter + bsr LCFB3 ; output the length of the block + ldd 4,s ; output the load address + bsr LCFB3 + ldx 4,s ; point to start of block +LCF9B lda ,x+ ; grab output byte + jsr LCC24 ; write it + leay -1,y ; done yet? + bne LCF9B ; brif not + lda #0xff ; write header for postamble + bsr LCFB5 + clra ; write dummy 16 bits (would be block length) + clrb + bsr LCFB3 + puls d,x,y ; get back execution address and clean stack + bsr LCFB3 ; write out execution address + jmp LA42D ; close file +LCFB3 bsr LCFB5 ; write MSB of word; fall through for LSB +LCFB5 jsr LCC24 ; write byte out + exg a,b ; swap bytes in word + rts +LCFBB ldx #BINEXT ; set default extension to "BIN" + jmp LC938 ; parse filename +; LOADM command (comes from LOAD command) +LCFC1 jsr GETNCH ; eat the "M" + bsr LCFBB ; parse filename + jsr LCA07 ; open file for input + ldd DFLTYP ; get type of file + subd #0x200 ; is it M/L and binary? + lbne LA616 ; bad file mode if not + ldx ZERO ; set default offset value + jsr GETCCH ; is there a load offset? + beq LCFDE ; brif not + jsr SYNCOMMA ; insist on a comma + jsr LB73D ; evaluate offset to X +LCFDE stx VD3 ; save offset + jsr LA5C7 ; insist on end of statement +LCFE3 jsr LCDBC ; read "amble" header + pshs a ; save "amble" type + bsr LD013 ; read block length + tfr d,y ; save it (in counter) + bsr LD013 ; read block address + addd VD3 ; add load offset + std EXECJP ; set execution address + tfr d,x ; put it also in a pointer + lda ,s+ ; set flags on "amble" type + lbne LA42D ; brif postamble - stop reading +LCFFA jsr LC5C4 ; read a byte + ldb CINBFL ; EOF? + beq LD004 ; brif not + jmp LC352 ; raise "IE" error +LD004 sta ,x ; save byte in memory + cmpa ,x+ ; does the byte match? + beq LD00D ; brif so + jmp LD709 ; bad store - throw I/O error +LD00D leay -1,y ; done whole block? + bne LCFFA ; brif not + bra LCFE3 ; process another "amble" +LD013 bsr LD015 ; read MSB then fall through for LSB +LD015 jsr LCDBC ; read a byte from the file + exg a,b ; swap bytes in word + rts +; RENAME command +RENAME ldx CHARAD ; get input pointer + pshs x ; save it + bsr LD056 ; evaluate source file name + lda DCDRV ; save drive for first file + pshs a + bsr LD051 ; evaluate TO and destination file + puls a ; get back original drive number + cmpa DCDRV ; does it match? + lbne LB44A ; brif not - we can't rename across drives + bsr LD059 ; make sure new file does not already exist + puls x ; get back input pointer + stx CHARAD + bsr LD056 ; re-evaluate source file + jsr LC68C ; find the source file + jsr LC6E5 ; throw error if not found + bsr LD051 ; evaluate destination file name + ldx #DNAMBF ; point to destinatoin file name + ldu V974 ; point to directory entry + ldb #11 ; 8 characters file name and 3 characters extension + jsr LA59A ; replace file name + ldb #3 ; operation code for write + stb DCOPC + jmp LD6F2 ; write updated directory sector +LD051 ldb #0xa5 ; insist on "TO" + jsr LB26F +LD056 jmp LC935 ; evaluate file name with default blank extension +LD059 jsr LC68C ; find file + ldb #33*2 ; code for already exists + tst V973 ; does it exist? + lbne LAC46 ; brif so - raise error + rts +; WRITE command +WRITE lbeq LB958 ; do CR if end of statement + bsr LD06F ; write an item list + clr DEVNUM ; reset device to screen +LD06E rts +LD06F cmpa #'# ; device number? + bne LD082 ; brif not + jsr LA5A5 ; evaluate device number + jsr LA406 ; check for output + jsr GETCCH ; end of statement? + lbeq LB958 ; brif so - do newline +LD07F jsr SYNCOMMA ; need a comma after device +LD082 jsr LB156 ; evaluate expression + lda VALTYP ; number or string? + bne LD0A7 ; brif string + jsr LBDD9 ; convert number to strng + jsr LB516 ; stash it as the current value + jsr LB99F ; output the string +LD092 jsr GETCCH ; is there more? + lbeq LB958 ; brif not - do newline + lda #', ; comma for non-tape separator + jsr LA35F ; set print parameters + tst PRTDEV ; tape? + beq LD0A3 ; brif not + lda #0x0d ; carriage return for tape separator +LD0A3 bsr LD0B9 ; send separator + bra LD07F ; process another item +LD0A7 bsr LD0B0 ; print leading string delimiter + jsr LB99F ; print string + bsr LD0B0 ; print ending string delimiter + bra LD092 ; handle separator or next item +LD0B0 jsr LA35F ; set print parameters + tst PRTDEV ; tape? + bne LD06E ; brif so + lda #'" ; string delimiter +LD0B9 jmp PUTCHR ; send character out +; FIELD command +FIELD jsr LC82E ; evaluate device number and make sure it's a random file + clra ; clear total field length counter + clrb + pshs x,b,a ; save field length and FCB pointer +LD0C3 jsr GETCCH ; are we at the end of the statement? + bne LD0C9 ; brif not + puls a,b,x,pc ; clean up stack and return +LD0C9 jsr LB738 ; evaluate comma and expression + pshs x,b ; save field length (B) and make a hole for a temp + clra ; zero extend length + addd 3,s ; add in current accumulated size + bcs LD0DA ; brif we overflowed + ldx 5,s ; point to FCB + cmpd FCBRLN,x ; have we overflowed the record? + bls LD0DF ; brif not +LD0DA ldb #34*2 ; code for field overflow + jmp LAC46 ; raise error +LD0DF ldu 3,s ; get old accumulated length + std 3,s ; save new accumulated length + ldd FCBBUF,x ; get start of random buffer + leau d,u ; now U is the start of the field in the buffer + stu 1,s ; save it so we can set string descriptor + ldb #0xff ; insist on two byte "AS" token + jsr LB26F + ldb #0xa7 + jsr LB26F + jsr LB357 ; evaluate variable + jsr LB146 ; error if it's a number + puls b,u ; get back field length and buffer address + stb ,x ; put those in the string descriptor + stu 2,x + bra LD0C3 ; look for another entry +; RSET command +RSET skip1lda ; set nonzero for right justify and fall through +; LSET command +LSET clra ; set zero to flag left justify + pshs a ; save justification type + jsr LB357 ; evaluate variable + jsr LB146 ; make sure it's a string + pshs x ; save descriptor address + ldx 2,x ; get address of string data + cmpx #DFLBUF ; is it below random buffer area? + blo LD119 ; brif below - throw error + cmpx FCBADR ; is it above the random buffer area? + blo LD11E ; brif not - it's good +LD119 ldb #2*35 ; code for "set to non-fielded string" + jmp LAC46 ; raise error +LD11E ldb #0xb3 ; insist on = + jsr LB26F + jsr L8748 ; evaluate string expression; return details + puls y ; get back destination descriptor pointer + lda ,y ; get length of field string + beq LD15A ; brif empty destination + pshs b ; save length of new string + ldb #0x20 ; pad characteris space + ldu 2,y ; point to destination string +LD132 stb ,u+ ; blank out string + deca ; done yet? + bne LD132 ; brif not + ldb ,s+ ; get length of data string + beq LD15A ; brif that one is NULL + cmpb ,y ; is data string smaller than destination? + blo LD143 ; brif not + ldb ,y ; get length of destination string as the copy length + clr ,s ; we'll do left justify if string is longer +LD143 ldu 2,y ; get address of the destination + tst ,s+ ; left or right justify? + beq LD157 ; brif left set + pshs b ; save number of bytes to move into field string + clra ; get negation of that in D (do -DLEN+FLEN) + negb + sbca #0 + addb ,y ; then add string length of destination to it + adca #0 + leau d,u ; now U points to destination offset + puls b ; get back destination size +LD157 jmp LA59A ; copy data and return +LD15A puls a,pc ; clean up stack and return +; FILES command +FILES jsr L95AC ; reset video display + ldd FCBADR ; get current size of buffer space + subd #DFLBUF + pshs d ; save it as the default size + ldb FCBACT ; get number of available disk files + pshs b ; save it as the default number + jsr GETCCH ; get input character + cmpa #', ; is numer of files specified? + beq LD181 ; brif not + jsr EVALEXPB ; evaluate the number of files wanted + cmpb #15 ; we only allow 15 + lbhi LB44A ; raise error if too many + stb ,s ; save number of FCBs to allocate + jsr GETCCH ; see what's after the number + beq LD189 ; brif no buffer size specified +LD181 jsr SYNCOMMA ; insist on a comma + jsr LB3E6 ; evaluate buffer size + ifeq DISKVER + addd #1 + endc + std 1,s ; save buffer size +LD189 jsr DVEC7 ; close all disk files + ldb ,s ; get number of files + pshs b ; initialize the file number count + ldd #DFLBUF ; point to start of random buffers + addd 2,s ; add in size requested for buffer space + bcs LD208 ; raise error if we carry + std 2,s ; save start of FCBs +LD199 addd #FCBLEN ; add size of one FCB + bcs LD208 ; brif we overflowed + dec ,s ; added on for all files? + bpl LD199 ; brif not yet (BPL so we set up the system one as well) + tstb ; are we at an even page boundary? + ifeq DISKVER + beq LD1AF ; brif so + else + beq LD1A8 ; brif so + endc + inca ; round up + beq LD208 ; brif we overflowed + ifeq DISKVER-1 +LD1A8 bita #1 ; on an even 512 byte boundary? + beq LD1AF ; brif so + inca ; round up + beq LD208 ; brif we overflowed + endc +LD1AF sta ,s ; save MSB of graphics memory + ldd VARTAB ; get end of program + suba GRPRAM ; subtract out start of graphics (D is now size of program and graphics) + adda ,s ; add in the new start of graphics + bcs LD208 ; brif overflow + tfr d,x ; save new top of program + inca ; add some extra room for the stack + beq LD208 ; brif overflow + cmpd FRETOP ; does it fit in memory? + bhs LD208 ; brif not + deca ; restore actual top of program + subd VARTAB ; get difference compared to the original top of program + addd TXTTAB ; add that to the program start + tfr d,y ; now Y is the new start of the program + lda ,s ; get back start of graphics memory + suba GRPRAM ; this gives difference between old graphics start and new grahpics start + tfr a,b ; we need this twice + adda BEGGRP ; set new start of active page + sta BEGGRP + addb ENDGRP ; set new end of active page + stb ENDGRP + puls a,b,u ; get back graphics start, number of files, and start of buffers + sta GRPRAM ; set graphics memory start + stb FCBACT ; set number of active FCBs + stu FCBADR ; set start of FCBs + ifeq DISKVER-1 + lda CURLIN ; in immediate mode? + inca + beq LD1EF ; brif so + tfr y,d ; calculate how much the program is moving + subd TXTTAB + addd CHARAD ; adjust input pointer for the change + std CHARAD + endc +LD1EF ldu VARTAB ; get old end of program + stx VARTAB ; save new end of program + cmpu VARTAB ; which way did it move? + bhi LD20B ; brif it moved up +LD1F8 lda ,-u ; move a byte down + sta ,-x + cmpu TXTTAB ; at start? + bne LD1F8 ; brif not + sty TXTTAB ; set new start of program + clr -1,y ; make sure there's a NUL before the program + bra LD21B ; clean up +LD208 jmp LAC44 ; raise OM error +LD20B ldu TXTTAB ; get old start of program + sty TXTTAB ; save new start of program + clr -1,y ; make sure there's a NUL before the program +LD212 lda ,u+ ; move a byte up + sta ,y+ + cmpy VARTAB ; at end yet? + bne LD212 ; brif not +LD21B ldu #FCBV1 ; point to FCB pointers + ldx FCBADR ; point to start of FCBs + clrb ; file counter +LD222 stx ,u++ ; set FCB pointer + clr <= 1.0 and the other if it is < 1.0 +ATN lda FP0SGN ; get sign of argument + pshs a ; save it + bpl L83B8 ; brif positive + bsr L83DC ; flip sign of argument +L83B8 lda FP0EXP ; get exponent + pshs a ; save it + cmpa #0x81 ; exponent for 1.0 + blo L83C5 ; brif less - value is less than 1.0 + ldx #LBAC5 ; point to FP constant 1.0 + bsr L83A3 ; calculate reciprocal +L83C5 ldx #L83E0 ; point to polynomical coefficients + jsr LBEF0 ; expand polynomial + puls a ; get exponent of argument + cmpa #0x81 ; did we do a reciprocal calculation? + blo L83D7 ; brif not + ldx #L83AB ; subtract result from pi/2 if we did + jsr LB9B9 +L83D7 puls a ; get sign of original + tsta ; was it positive? + bpl L83DF ; brif so +L83DC jmp LBEE9 ; flip sign of result +L83DF rts +; Chebyshev modified taylor series coefficients for inverse tangent. Note that these diverge quite significantly +; from the standard taylor series after 1/9. The standard coefficients are 1/1,-1/3, 1/5, -1/7, 1/9, -1/11, and +; so on. These modified coefficients yield reasonable accuracy for the precision available, presumably with +; fewer coefficients. +L83E0 fcb 11 ; 12 coefficients + fcb 0x76,0xb3,0x83,0xbd,0xd3 ; -0.000684793912 + fcb 0x79,0x1e,0xf4,0xa6,0xf5 ; 0.00485094216 + fcb 0x7b,0x83,0xfc,0xb0,0x10 ; -0.0161117018 + fcb 0x7c,0x0c,0x1f,0x67,0xca ; 0.0342096381 + fcb 0x7c,0xde,0x53,0xcb,0xc1 ; -0.0542791328 + fcb 0x7d,0x14,0x64,0x70,0x4c ; 0.0724571965 + fcb 0x7d,0xb7,0xea,0x51,0x7a ; -0.0898023954 + fcb 0x7d,0x63,0x30,0x88,0x7e ; 0.110932413 + fcb 0x7e,0x92,0x44,0x99,0x3a ; -0.142839808 + fcb 0x7e,0x4c,0xcc,0x91,0xc7 ; 0.199999121 + fcb 0x7f,0xaa,0xaa,0xaa,0x13 ; -0.333333316 + fcb 0x81,0x00,0x00,0x00,0x00 ; 1.0 +; Chebyshev modified taylor series coefficients for the ln(x)/ln(2) (base 2 log of x) +L841D fcb 3 ; four coefficients + fcb 0x7f,0x5e,0x56,0xcb,0x79 ; 0.434255942 from (2/7)/ln(2) + fcb 0x80,0x13,0x9b,0x0b,0x64 ; 0.576584541 from (2/5)/ln(2) + fcb 0x80,0x76,0x38,0x93,0x16 ; 0.961800759 from (2/3)/ln(2) + fcb 0x82,0x38,0xaa,0x3b,0x20 ; 2.88539007 from 2/ln(2) +L8432 fcb 0x80,0x35,0x04,0xf3,0x34 ; 1/sqrt(2) +L8437 fcb 0x81,0x35,0x04,0xf3,0x34 ; sqrt(2) +L843C fcb 0x80,0x80,0x00,0x00,0x00 ; -0.5 +L8441 fcb 0x80,0x31,0x72,0x17,0xf8 ; ln(2) +; LOG function (natural log, ln) +; FP representation is of the form A*2^B. Thus, the log routine determines the value of +; ln(A*2^B). +; +; ln(A*2^B) = ln(A) + ln(2^B) = ln(A) + B*ln(2) = (ln(A)/ln(2) + B)*ln(2), OR: +; (log2(A) + B)*ln(2) +; Now if we write A as A*(sqrt(2)/sqrt(2)), we haven't changed anything so: +; (log2(A*sqrt(2)/sqrt(2)) + B)*ln(2) = (log2(A*sqrt(2)) - log2(sqrt(2)) + B) * ln(2) +; which gives: (-1/2 + log2(A*sqrt(2)) + B) * ln(2) +; +; Everything except log2(A*sqrt(2)) is either constant or trivial. +; +; What the actual code below feeds into the modified taylor series is actually: +; 1 - (sqrt(2)/(A + sqrt(2)) which algebra reveals to be (A*sqrt(2)-1)/(A*sqrt(2)+1) +; +; Thus, the code is using (A*sqrt(2)-1)/(A*sqrt(2)+1) instead of A*sqrt(2) as one would +; expect from the identities. However, the modified coefficients in the series above +; could be correcting for that or the introduced error was deemed acceptable. +; NOTE: this routine does NOT return 0 for LOG(1) +LOG jsr LBC6D ; get status of FPA0 + lble LB44A ; brif <= 0 - logarithms don't exist in that case + ldx #L8432 ; point to 1/sqrt(2) + lda FP0EXP ; get exponent of argument + suba #0x80 ; remove bias + pshs a ; save it for later + lda #0x80 ; force exponent to 0 (this turns FPA0 into A in the above description) + sta FP0EXP + jsr LB9C2 ; add 1/sqrt(2) to A + ldx #L8437 ; point to sqrt(2) + jsr LBB8F ; divide that by FPA0 + ldx #LBAC5 ; point to 1.0 + jsr LB9B9 ; subtract result from 1.0: Now FPA0 is 1-(sqrt(2)/(A+1/sqrt(2))) + ldx #L841D ; point to coefficients + jsr LBEF0 ; expand polynomial (calculate base 2 log of modified argument) + ldx #L843C ; point to -0.5 + jsr LB9C2 ; add result + puls b ; get original exponent back + jsr LBD99 ; add B to FPA0 + ldx #L8441 ; point to ln(2) + jmp LBACA ; multiply by ln(2) which gives us the result in base e +; SQR function (square root) - returns the principle root (positive) +SQR jsr LBC5F ; move argument to FPA1 (first argument for exponentiation) + ldx #LBEC0 ; point to 0.5 (exponent for square root) + jsr LBC14 ; set up second argument to exponentiation (the exponent) +; Exponentiation operator +; This is calculated as A^x = e^(x*ln(A)) where A is in FPA1 and x is in FPA0 +L8489 beq EXP ; do "EXP" if exponent is 0 (this will catch 0^0) + tsta ; check that the base is not 0 + bne L8491 ; brif base is not 0 + jmp LBA3A ; 0^(nonzero) is 0 +L8491 ldx #V4A ; save exponent (to FPA5) + jsr LBC35 + clrb ; result sign will default to positive + lda FP1SGN ; check if base is positive + bpl L84AC ; brif so + jsr INT ; convert exponent to integer + ldx #V4A ; point to original expoent + lda FP1SGN ; get sign of FPA1 + jsr LBCA0 ; compare original exponent with truncated one + bne L84AC ; brif not equal + coma ; flip sign + ldb CHARAC ; get LS byte of integer exponent (result sign flag) +L84AC jsr LBC4C ; copy FPA1 (base) to FPA0 (A = sign) + pshs b ; save result sign + jsr LOG ; get natural log of the base + ldx #V4A ; multiply the log by the exponent + jsr LBACA + bsr EXP ; now raise e to the resulting power + puls a ; get result sign + rora ; brif it was negative + lbcs LBEE9 ; brif negative - flip sign + rts +L84C4 fcb 0x81,0x38,0xaa,0x3b,0x29 ; 1.44269504 (correction factor for exponential function) +; Chebyshev modified taylor series coefficients for e^x +L84C9 fcb 7 ; eight coefficients + fcb 0x71,0x34,0x58,0x3e,0x56 ; 1/(7!*(CF^7)) + fcb 0x74,0x16,0x7e,0xb3,0x1b ; 1/(6!*(CF^6)) + fcb 0x77,0x2f,0xee,0xe3,0x85 ; 1/(5!*(CF^5)) + fcb 0x7a,0x1d,0x84,0x1c,0x2a ; 1/(4!*(CF^4)) + fcb 0x7c,0x63,0x59,0x58,0x0a ; 1/(3!*(CF^3)) + fcb 0x7e,0x75,0xfd,0xe7,0xc6 ; 1/(2!*(CF^2)) + fcb 0x80,0x31,0x72,0x18,0x10 ; 1/(1!*(CF^1)) + fcb 0x81,0x00,0x00,0x00,0x00 ; 1 +; EXP function (e^x) +EXP ldx #L84C4 ; point to correction factor + jsr LBACA ; multiply it + jsr LBC2F ; save corrected argument to FPA3 + lda FP0EXP ; get exponent of FPA0 + cmpa #0x88 ; is it too big? + blo L8504 ; brif not +L8501 jmp LBB5C ; to 0 (underflow) or overflow error +L8504 jsr INT ; convert argument to an integer + lda CHARAC ; get ls byte of integer + adda #0x81 ; was argument 127? if so, the OV error; adds bias + beq L8501 + deca ; adjust for the extra +1 above + pshs a ; save integer exponent + ldx #V40 ; get fractional part of argument + jsr LB9B9 + ldx #L84C9 ; point to coefficients + jsr LBEFF ; evaluate polynomial on the fractional part + clr RESSGN ; force result to be positive + puls a ; get back original exponent + jsr LBB48 ; add original exponent to the fractional result + rts +; FIX function (truncate/round toward 0) (NOTE: INT() always rounds down so INT(-0.5) gives -1 but FIX(-0.5) gives 0) +FIX jsr LBC6D ; get status of argument + bmi L852C ; brif negative +L8529 jmp INT ; do regular "int" if positive +L852C com FP0SGN ; flip the sign + bsr L8529 ; do "INT" on this + jmp LBEE9 ; flip the sign back +; EDIT command +EDIT jsr L89AE ; get line number + leas 2,s ; we're not going to return to the main loop +L8538 lda #1 ; "LIST" flag + sta VD8 ; set to list the line + jsr LAD01 ; find line number + lbcs LAED2 ; brif line wasn't found + jsr LB7C2 ; go unpack the line into the buffer + tfr y,d ; calculate the actual length of the line + subd #LINBUF+2 + stb VD7 ; save line length (it will only be 8 bits) +L854D ldd BINVAL ; get the line number + jsr LBDCC ; display it + jsr LB9AC ; put a space after it + ldx #LINBUF+1 ; point to iput uffer + ldb VD8 ; are we listing? + bne L8581 ; brif so +L855C clrb ; reset digit accumulator +L855D jsr L8687 ; get a keypress + jsr L90AA ; set carry if not numeric + bcs L8570 ; brif not a number + suba #'0 ; remove ASCII bias + pshs a ; save digit value + lda #10 ; multiply accumulator by 10 + mul + addb ,s+ ; add in new digit + bra L855D ; go check for another digit +L8570 subb #1 ; this converts 0 to 1, but *only* 0 to 1 + adcb #1 + cmpa #'A ; abort? + bne L857D ; brif not + jsr LB958 ; to a CR + bra L8538 ; restart EDIT process +L857D cmpa #'L ; list? + bne L858C ; brif not +L8581 bsr L85B4 ; list the line + clr VD8 ; reset to "not listing" + jsr LB958 ; do a CR + bra L854D ; start editing +L858A leas 2,s ; lose return address +L858C cmpa #0x0d ; ENTER? + bne L859D ; brif not + bsr L85B4 ; echo out the line +L8592 jsr LB958 ; do a CR + ldx #LINBUF+1 ; reset input pointer to start of buffer + stx CHARAD + jmp LACA8 ; join immediate mode to replace the line in the program +L859D cmpa #'E ; exit? + beq L8592 ; brif so - end edit with no echo + cmpa #'Q ; quit? + bne L85AB ; brif not + jsr LB958 ; do a CR + jmp LAC73 ; go to immediate mode with no fanfare - no changes saved +L85AB bsr L85AF ; go do commands + bra L855C ; go handle another command +L85AF cmpa #0x20 ; space? + bne L85C3 ; brif not + skip2 +L85B4 ldb #LBUFMX-1 ; display up to a whole line +L85B6 lda ,x ; get buffer chracter + beq L85C2 ; brif end of line + jsr PUTCHR ; output character + leax 1,x ; move to next character + decb ; done? + bne L85B6 ; brif not +L85C2 rts +L85C3 cmpa #'D ; delete? + bne L860F ; brif not +L85C7 tst ,x ; end of line? + beq L85C2 ; brif so - can't delete + bsr L85D1 ; remove a character + decb ; done all requested? + bne L85C7 ; brif not + rts +L85D1 dec VD7 ; account for character being removed + leay -1,x ; set pointer and compensate for increment below +L85D5 leay 1,y ; move to next character + lda 1,y ; get next character + sta ,y ; move it forward + bne L85D5 ; brif we didn't hit the end of the buffer + rts +L85DE cmpa #'I ; insert? + beq L85F5 ; brif so + cmpa #'X ; extend? + beq L85F3 ; brif so + cmpa #'H ; "hack"? + bne L8646 ; brif not + clr ,x ; mark current location as end of line + tfr x,d ; calculate new line length + subd #LINBUF+2 + stb VD7 ; save new length +L85F3 bsr L85B4 ; display the line +L85F5 jsr L8687 ; read a character + cmpa #0x0d ; ENTER? + beq L858A ; brif so - terminate entry + cmpa #0x1b ; ESC? + beq L8625 ; brif so - back to command mode + cmpa #0x08 ; backspace? + bne L8626 ; brif no + cmpx #LINBUF+1 ; are we at the start of the buffer? + beq L85F5 ; brif so - it's a no-op + bsr L8650 ; move pointer back one, do a BS + bsr L85D1 ; remove character from the buffer + bra L85F5 ; go handle more input +L860F cmpa #'C ; change? + bne L85DE ; brif not +L8613 tst ,x ; is there something to change? + beq L8625 ; brif not + jsr L8687 ; get a key stroke + bcs L861E ; brif valid key + bra L8613 ; try again if invalid key +L861E sta ,x+ ; put new character in the buffer + bsr L8659 ; echo it + decb ; changed number requested? + bne L8613 ; brif not +L8625 rts +L8626 ldb VD7 ; get length of line + cmpb #LBUFMX-1 ; at maximum line length? + bne L862E ; brif not + bra L85F5 ; process another input character +L862E pshs x ; save input pointer +L8630 tst ,x+ ; are we at the end of the line? + bne L8630 ; brif not +L8634 ldb ,-x ; get character before current pointer, move back + stb 1,x ; move it forward + cmpx ,s ; at the original buffer pointer? + bne L8634 ; brif not + leas 2,s ; remove saved buffer pointer + sta ,x+ ; save input character in newly made hole + bsr L8659 ; echo it + inc VD7 ; bump line length counter + bra L85F5 ; go handle more stuff +L8646 cmpa #0x08 ; backspace? + bne L865C ; brif not +L864A bsr L8650 ; move pointer back, echo BS + decb ; done enough of them? + bne L864A ; brif not + rts +L8650 cmpx #LINBUF+1 ; at start of buffer? + beq L8625 ; brif so + leax -1,x ; move pointer back + lda #0x08 ; character to echo - BS +L8659 jmp PUTCHR ; echo character to screen +L865C cmpa #'K ; "kill"? + beq L8665 ; brif so + suba #'S ; search? + beq L8665 ; brif so + rts +L8665 pshs a ; save kill/search flag + bsr L8687 ; read target + pshs a ; save search character +L866B lda ,x ; get current character in buffer + beq L8685 ; brif end of line - nothing more to search + tst 1,s ; is it KILL? + bne L8679 ; brif so + bsr L8659 ; echo the character + leax 1,x ; move ahead + bra L867C ; check next character +L8679 jsr L85D1 ; remove character from buffer +L867C lda ,x ; get character in buffer + cmpa ,s ; are we at the target? + bne L866B ; brif not + decb ; have we found enough of them? + bne L866B ; brif not +L8685 puls y,pc ; clean up stack and return to main EDIT routine +L8687 jsr LA171 ; get input from the generic input handler (will show the cursor) + cmpa #0x7f ; graphics (or DEL)? + bhs L8687 ; brif so - ignore it + cmpa #0x5f ; SHIFT-UP? + bne L8694 ; brif not + lda #0x1b ; replace with ESC +L8694 cmpa #0x0d ; carriage return? + beq L86A6 ; brif so (C=0) + cmpa #0x1b ; ESC + beq L86A6 ; brif so (C=0) + cmpa #0x08 ; backspace? + beq L86A6 ; brif so (C=0) + cmpa #32 ; control code? + blo L8687 ; brif control code - try again + orcc #1 ; set C for "valid" (printable) character +L86A6 rts +; TRON and TROFF commands +TRON skip1lda ; load flag with nonzero for trace enabled (and skip next) +TROFF clra ; clear flag for trace disabled + sta TRCFLG ; save trace status + rts +; POS function +POS lda DEVNUM ; get original device number + pshs a ; save it for later + jsr LA5AE ; fetch device number + jsr LA406 ; check for open file + jsr LA35F ; set up print parameters + ldb DEVPOS ; get current line position for the device + jmp LA5E4 ; return position in B as unsigned +; VARPTR function +VARPTRTOK jsr LB26A ; make sure we have ( + ldd ARYEND ; get address of end of arrays + pshs d ; save it + jsr LB357 ; parse variable descriptor + jsr LB267 ; make sure there is a ) + puls d ; get original end of arrays + exg x,d ; swap original end of arrays and the discovered variable pointer + cmpx ARYEND ; did array end move (variable created?) + bne L8724 ; brif so (FC error) + jmp GIVABF ; return the pointer (NOTE: as signed) +; MID$() assignment; note that this cannot make the string longer or shorter and if the replacement is shorter +; than the specified size, only the number of characters actually in the replacement will be used. +L86D6 jsr GETNCH ; eat the MID$ token + jsr LB26A ; force ( + jsr LB357 ; evaluate the variable + pshs x ; save variable descriptor + ldd 2,x ; point to start of original string + cmpd FRETOP ; is it in string space? + bls L86EB ; brif not + subd MEMSIZ ; is it still in string space (top end)? + bls L86FD ; brif so +L86EB ldb ,x ; get length of original string + jsr LB56D ; allocate space in string space + pshs x ; save pointer to string space + ldx 2,s ; get to original string descriptor + jsr LB643 ; move the string into string space + puls x,u ; get new string address and string descriptor + stx 2,u ; save new data address for the string + pshs u ; save descriptor address again +L86FD jsr LB738 ; evaluate ",start" + pshs b ; save start offset + tstb ; is start 0? + beq L8724 ; brif so - strings offsets are 1-based + ldb #255 ; default use the entire string + cmpa #') ; end of parameters? + beq L870E ; brif so + jsr LB738 ; evaluate ",length" +L870E pshs b ; save length + jsr LB267 ; make sure we have a ) + ldb #0xb3 ; make sure we have = + jsr LB26F + bsr L8748 ; evaluate replacement string + tfr x,u ; save replacement string address + ldx 2,s ; get original string descriptor + lda ,x ; get length of original string + suba 1,s ; subtract start position + bhs L8727 ; brif within the string - insert replacement +L8724 jmp LB44A ; raise illegal function call +L8727 inca ; A is now number of characters to the right of the position parameter + cmpa ,s ; compare to length desired + bhs L872E ; brif new length fits + sta ,s ; only use as much of the length as will fit +L872E lda 1,s ; get position offset + exg a,b ; swap replacement length and position + ldx 2,x ; point to original string address + decb ; we work with 0-based offsets + abx ; now X points to start of replacement + tsta ; replacing 0? + beq L8746 ; brif so - done + cmpa ,s ; is replacement shorter than the hole? + bls L873F ; brif so + lda ,s ; use copy the maximum number specified +L873F tfr a,b ; save number to move in B + exg u,x ; swap pointers so they are right for the routine + jsr LA59A ; copy string data +L8746 puls a,b,x,pc ; clean up stack and return +L8748 jsr LB156 ; evaluate expression + jmp LB654 ; make sure it's a string and return string details +; STRING$ function +STRING jsr LB26A ; make sure we have ( + jsr EVALEXPB ; evaluate repeat count (error if > 255) + pshs b ; save repeat count + jsr SYNCOMMA ; make sure there's a comma + jsr LB156 ; evaluate the thing to repeat + jsr LB267 ; make sure we have a ) + lda VALTYP ; is it string? + bne L8768 ; brif so + jsr LB70E ; get 8 bit character code + bra L876B ; use that +L8768 jsr LB6A4 ; get first character of string +L876B pshs b ; save repeat character + ldb 1,s ; get repeat count + jsr LB50F ; reserve space for the string + puls a,b ; get character and repeat count + beq L877B ; brif NULL string +L8776 sta ,x+ ; put character into string + decb ; put enough? + bne L8776 ; brif not +L877B jmp LB69B ; return the newly created string +; INSTR function +INSTR jsr LB26A ; evaluate ( + jsr LB156 ; evaluate first argument + ldb #1 ; default start position is 1 (start of string) + pshs b ; save start position + lda VALTYP ; get type of first argument + bne L879C ; brif string - use default starting position + jsr LB70E ; convert first argument into string offset + stb ,s ; save offset + beq L8724 ; brif starting at 0 - not allowed + jsr SYNCOMMA ; make sure there's a comma + jsr LB156 ; evaluate the search string + jsr LB146 ; make sure it *is* a string +L879C ldx FPA0+2 ; get search string descriptor + pshs x ; save it + jsr SYNCOMMA ; make sure we have a comma + jsr L8748 ; evalute the target string + pshs x,b ; save address and length of target string + jsr LB267 ; make sure we have a ) + ldx 3,s ; get search string address + jsr LB659 ; get string details + pshs b ; save search string length + cmpb 6,s ; compare length of search string to the start + blo L87D9 ; brif start position is beyond the search string - return 0 + lda 1,s ; get length of target string + beq L87D6 ; brif targetstring is NULL - match will be immediate + ldb 6,s ; get start position + decb ; zero-base it + abx ; now X points to the start position for the search +L87BE leay ,x ; point to start of search + ldu 2,s ; get target string pointer + ldb 1,s ; get targetlength + lda ,s ; get length of serach + suba 6,s ; see how much is left in searh + inca ; add one for "inclusivity" + cmpa 1,s ; do we have less than the target string? + blo L87D9 ; brif so - we obviously won't match +L87CD lda ,x+ ; compare a byte + cmpa ,u+ + bne L87DF ; brif no match + decb ; compared all of target? + bne L87CD ; brif not +L87D6 ldb 6,s ; get position where we matched + skip1 +L87D9 clrb ; flag no match + leas 7,s ; clean up stack + jmp LB4F3 ; return unsigned B +L87DF inc 6,s ; bump start position + leax 1,y ; move starting pointer + bra L87BE ; see if we match now +; Number parsing handler +XVEC19 cmpa #'& ; do we have & (hex or octal)? + bne L8845 ; brif not + leas 2,s ; we won't return to the original invoker +L87EB clr FPA0+2 ; clear bottom of FPA0 for 16 bit value + clr FPA0+3 + ldx #FPA0+2 ; point to accumulator + jsr GETNCH ; eat the & + cmpa #'O ; octal? + beq L880A ; brif so + cmpa #'H ; hex? + beq L881F ; brif so + jsr GETCCH ; reset flags on input + bra L880C ; go process octal (default) +L8800 cmpa #'8 ; is it a valid octal character? + lbhi LB277 ; brif not (BUG: should be LBHS or above be #'7) + ldb #3 ; base 8 multiplier + bsr L8834 ; add digit to accumulator +L880A jsr GETNCH ; get input character +L880C bcs L8800 ; brif numeric +L880E clr FPA0 ; clear upper bytes of FPA0 + clr FPA0+1 + clr VALTYP ; result is numeric + clr FPSBYT ; clear out any extra precision + clr FP0SGN ; make it positive + ldb #0xa0 ; exponent for integer aligned to right of FPA0 + stb FP0EXP + jmp LBA1C ; go normalize the result and return +L881F jsr GETNCH ; get input character + bcs L882E ; brif digit + jsr LB3A2 ; set carry if not alpha +L8826 bcs L880E ; brif not alpha + cmpa #'G ; is it valid HEX digit? + bhs L880E ; brif not + suba #7 ; normalize A-F to be just above 0-9 +L882E ldb #4 ; four bits per digit + bsr L8834 ; add digit to accumlator + bra L881F ; process another digit +L8834 asl 1,x ; shift accumulator one bit left + rol ,x + lbcs LBA92 ; brif too big - overflow + decb ; done enough bit shifts? + bne L8834 ; brif not +L883F suba #'0 ; remove ASCII bias + adda 1,x ; merge digit into accumlator (this cannot cause carry) + sta 1,x +L8845 rts +; Expression evaluation handler +XVEC15 puls u ; get back return address + clr VALTYP ; set result to numeric + ldx CHARAD ; save input pointer + jsr GETNCH ; get the input character + cmpa #'& ; HEX or OCTAL? + beq L87EB ; brif so + cmpa #0xcc ; FN? + beq L88B4 ; brif so - do "FNx()" + cmpa #0xff ; function token? + bne L8862 ; brif not + jsr GETNCH ; get function token value + cmpa #0x83 ; USR? + lbeq L892C ; brif so - short circuit Color Basic's USR handler +L8862 stx CHARAD ; restore input pointer + jmp ,u ; return to mainline code +L8866 ldx CURLIN ; are we in immediate mode? + leax 1,x + bne L8845 ; brif not - we're good + ldb #2*11 ; code for illegal direct statement +L886E jmp LAC46 ; raise error +; DEF command (DEF FN, DEF USR) +DEF ldx [CHARAD] ; get two input characters + cmpx #0xff83 ; USR? + lbeq L890F ; brif so - do DEF USR + bsr L88A1 ; get descriptor address for FN variable + bsr L8866 ; disallow DEF FN in immediate mode + jsr LB26A ; make sure we have ( + ldb #0x80 ; disallow arrays as arguments + stb ARYDIS + jsr LB357 ; evaluate variable + bsr L88B1 ; make sure it's numeric + jsr LB267 ; make sure we have ) + ldb #0xb3 ; make sure we have = + jsr LB26F + ldx V4B ; get variable descriptor address + ldd CHARAD ; get input pointer + std ,x ; save address of the actual function code in variable descriptor + ldd VARPTR ; get descriptor address of argument + std 2,x ; save argument descriptor address + jmp DATA ; move to the end of this statement +L88A1 ldb #0xcc ; make sure we have FN + jsr LB26F + ldb #0x80 ; disable array lookup + stb ARYDIS + ora #0x80 ; set bit 7 of first character (to indicate FN variable) + jsr LB35C ; find the variable + stx V4B ; save descriptor pointer +L88B1 jmp LB143 ; make sure we have a numeric variable +; Evaluate an FN call +L88B4 bsr L88A1 ; parse the FNx bit and get the descriptor + pshs x ; save descriptor + jsr LB262 ; evaluate parameter + bsr L88B1 ; make sure it's a number + puls u ; get FN descriptor + ldb #2*25 ; code for undefined function + ldx 2,u ; point to argument variable descriptor + beq L886E ; brif nothing doing there (if it was just created, this will be NULL) + ldy CHARAD ; save current input pointer + ldu ,u ; point to start of FN definition + stu CHARAD ; put input pointer there + lda 4,x ; save original value of argument and save it with current input, and variable pointers + pshs a + ldd ,x + ldu 2,x + pshs u,y,x,d + jsr LBC35 ; set argument variable to the argument +L88D9 jsr LB141 ; go evaluate the FN expression + puls d,x,y,u ; get back variable pointers, input pointer, and original variable value + std ,x + stu 2,x + puls a + sta 4,x + jsr GETCCH ; test end of FN formula + lbne LB277 ; brif not end of statement - problem with the function + sty CHARAD ; restore input pointer +L88EF rts +; Error handler +XVEC17 cmpb #2*25 ; is it a valid Extended Basic error code? + blo L88EF ; brif not - return to mainline + jsr LA7E9 ; turn off tape + jsr LA974 ; turn off sound + jsr LAD33 ; clean up stack and other bits + clr DEVNUM ; reset output to screen + jsr LB95C ; do a newline if needed + jsr LB9AF ; do a ? + ldx #L890B-25*2 ; point to error message table + jmp LAC60 ; go display error message +; Extended Basic error codes. Yes, NE is defined here even though it is only actually documented in the +; Disk Basic documentation. It is here for the use of DLOAD. +L890B fcc 'UF' ; 25 undefined function call + fcc 'NE' ; 26 File not found +; DEF USR +L890F jsr GETNCH ; eat the USR token + bsr L891C ; get pointer to USR call + pshs x ; save FN exec address location + bsr L8944 ; calculate execution address + puls u ; get FN address pointer + stx ,u ; save new address + rts +L891C clrb ; default routine number is 0 + jsr GETNCH ; fetch the call number + bcc L8927 ; brif not a number + suba #'0 ; remove ASCII bias + tfr a,b ; save it in the right place + jsr GETNCH ; eat the call number +L8927 ldx USRADR ; get start address of USR jump table + aslb ; two bytes per address + abx ; now X points to the right entry + rts +; Evaluate a USR call +L892C bsr L891C ; find the correct routine address location + ldx ,x ; get routine address + pshs x ; save it + jsr LB262 ; evaluate argument + ldx #FP0EXP ; point to FPA0 (argument value) + lda VALTYP ; is it string? + beq L8943 ; brif not + jsr LB657 ; fetch string details (removes it from the string stack) + ldx FPA0+2 ; get string descriptor pointer + lda VALTYP ; set flags for the value type +L8943 rts ; call the routine and return to mainline code +L8944 ldb #0xb3 ; check for "=" + jsr LB26F + jmp LB73D ; evaluate integer expression to X and return +; Extended Basic IRQ handler +XIRQSV lda PIA0+3 ; is it VSYNC interrupt? + bmi L8952 ; brif so + rti ; really should clear the HSYNC interrupt here +L8952 lda PIA0+2 ; clear VSYNC interrupt + ldx TIMVAL ; increment the TIMER value + leax 1,x + stx TIMVAL + jmp L9C3E ; check for other stuff +; TIMER= +L8960 jsr GETNCH ; eat the TIMER token + bsr L8944 ; evaluate =nnnn to X + stx TIMVAL ; set the timer + rts +; TIMER function +TIMER ldx TIMVAL ; get timer value + stx FPA0+2 ; set it in FPA0 + jmp L880E ; return as positive 16 bit value +; DEL command +DEL lbeq LB44A ; raise error if no argument (probably to avoid accidentally deleting line 0) + jsr LAF67 ; parse line number + jsr LAD01 ; find line + stx VD3 ; save address of line + jsr GETCCH ; is there something more? + beq L8990 ; brif not + cmpa #0xac ; dash? + bne L89BF ; brif not - error out + jsr GETNCH ; each the - + beq L898C ; brif no ending line - use default line number + bsr L89AE ; parse second line number and save in BINVAL + bra L8990 ; do the deletion +L898C lda #0xff ; set to maximum line number + sta BINVAL +L8990 ldu VD3 ; point end to start + skip2 +L8993 ldu ,u ; point to start of next line + ldd ,u ; check for end of program + beq L899F ; brif end of program + ldd 2,u ; get line number + subd BINVAL ; is it in range? + bls L8993 ; brif so +L899F ldx VD3 ; get starting line address + bsr L89B8 ; close up gap + jsr LAD21 ; reset input pointer and erase variables + ldx VD3 ; get start of program after the deletion + jsr LACF1 ; recompute netl ine pointers + jmp LAC73 ; return to immediate mode +L89AE jsr LAF67 ; parse a line number + jmp LA5C7 ; make sure there's nothing more +L89B4 lda ,u+ ; copy a byte + sta ,x+ +L89B8 cmpu VARTAB ; end of program? + bne L89B4 ; brif not + stx VARTAB ; save new start of variables/end of program +L89BF rts +; LINE INPUT +L89C0 jsr L8866 ; raise error if in immediate mode + jsr GETNCH ; eat the "INPUT" token + cmpa #'# ; device number? + bne L89D2 ; brif not + jsr LA5A5 ; parse device number + jsr LA3ED ; make sure it's valid for input + jsr SYNCOMMA ; make sure there's a comma after the device number +L89D2 cmpa #'" ; is there a prompt? + bne L89E1 ; brif not + jsr LB244 ; parse the string + ldb #'; ; make sure there's a semicolon after the prompt + jsr LB26F + jsr LB99F ; go actually display the prompt +L89E1 leas -2,s ; make a hole on the stack (so number of return adddresses is right) + jsr LB035 ; read an input line from current device + leas 2,s ; clean up stack + clr DEVNUM ; reset to screen/keyboard + jsr LB357 ; parse a variable + stx VARDES ; save pointer to it + jsr LB146 ; make sure it's a string + ldx #LINBUF ; point to input buffer + clra ; make sure we terminate on NUL only + jsr LB51A ; parse string and store it in string space + jmp LAFA4 ; go assign the string to its final resting place +; RENUM command +L89FC jsr LAF67 ; read a line number + ldx BINVAL ; get value + rts +L8A02 ldx VD1 ; get current old number being renumbered +L8A04 stx BINVAL ; save number being searched for + jmp LAD01 ; go find line number +RENUM jsr LAD26 ; erase variables + ldd #10 ; default line number interval and start + std VD5 ; set starting line number + std VCF ; set number interval + clrb ; now D is 0 + std VD1 ; save default start for renumbering + jsr GETCCH ; are there any arguments + bcc L8A20 ; brif not numeric + bsr L89FC ; fetch line number + stx VD5 ; save line beginning number + jsr GETCCH ; get input character +L8A20 beq L8A3D ; brif end of line + jsr SYNCOMMA ; make sure we have a comma + bcc L8A2D ; brif next isn't numeric + bsr L89FC ; fetch starting line number + stx VD1 ; save the number where we start working + jsr GETCCH ; fetch input character +L8A2D beq L8A3D ; brif end of line + jsr SYNCOMMA ; make sure we have a comma + bcc L8A3A ; brif we don't have a number + bsr L89FC ; parse the number + stx VCF ; save interval + beq L8A83 ; brif we ave a zero interval +L8A3A jsr LA5C7 ; raise error if more stuff +L8A3D bsr L8A02 ; get address of old number to process + stx VD3 ; save address + ldx VD5 ; get the next renumbered line to use + bsr L8A04 ; find that line + cmpx VD3 ; is it before the previous one? + blo L8A83 ; brif so - raise error + bsr L8A67 ; make sure renumbered line numbers will be in range + jsr L8ADD ; convert line numbers to "expanded" binary + jsr LACEF ; recalculate next line pointers + bsr L8A02 ; get address of first line to renumber + stx VD3 ; save it + bsr L8A91 ; make sure line numbers exist + bsr L8A68 ; renumber the actual lines + bsr L8A91 ; update line numbers in program text + jsr L8B7B ; convert packed binary line numbers to text + jsr LAD26 ; erase variables, reset stack, etc. + jsr LACEF ; recalculate next line pointers + jmp LAC73 ; bounce back to immediate mode +L8A67 skip1lda ; set line number flag to nonzero (skip next instruction) +L8A68 clra ; set line number flag to zero (insert new numbers) + sta VD8 ; save line number flag + ldx VD3 ; get address of line being renumbered + ldd VD5 ; get the current renumbering number + bsr L8A86 ; return if end of program +L8A71 tst VD8 ; test line number flag + bne L8A77 ; brif not adding new numbers + std 2,x ; set new number +L8A77 ldx ,x ; point to next line + bsr L8A86 ; return if end of program + addd VCF ; add interval to current number + bcs L8A83 ; brif we overflowed - bad line number + cmpa #MAXLIN ; maximum legal number? + blo L8A71 ; brif so - do another +L8A83 jmp LB44A ; raise FC error +L8A86 pshs d ; save D (we're going to clobber it) + ldd ,x ; get next line pointer + puls d ; unclobber D + bne L8A90 ; brif not end of program + leas 2,s ; return to caller's caller +L8A90 rts +L8A91 ldx TXTTAB ; get start of program + leax -1,x ; move pointer back one (compensate for leax 1,x below) +L8A95 leax 1,x ; move to next line + bsr L8A86 ; return if end of program +L8A99 leax 3,x ; move past next line address and line number, go one before line +L8A9B leax 1,x ; move to next character + lda ,x ; check input character + beq L8A95 ; brif end of line + stx TEMPTR ; save current pointer + deca ; is it start of packed numeric line number? + beq L8AB2 ; brif so + deca ; does line exist? + beq L8AD3 ; brif line number exists + deca ; not part of something to process? + bne L8A9B ; brif so +L8AAC lda #3 ; set 1st byte to 3 to indicate line number not existing + sta ,x+ + bra L8A99 ; go process another +L8AB2 ldd 1,x ; get MSB of line number + dec 2,x ; is MS byte zero? + beq L8AB9 ; brif not + clra ; set MS byte to 0 +L8AB9 ldb 3,x ; get LSB of line number + dec 4,x ; is it zero? + beq L8AC0 ; brif not + clrb ; clear byte +L8AC0 std 1,x ; save binary number + std BINVAL ; save trial number + jsr LAD01 ; find the line number +L8AC7 ldx TEMPTR ; get start of packed line + bcs L8AAC ; brif line number not found + ldd V47 ; get address of line number + inc ,x+ ; bump first byte to 2 (exists if checking) or 1 if inserting + std ,x ; save address of correct number + bra L8A99 ; go process more +L8AD3 clr ,x ; clear carry and first byte + ldx 1,x ; point to address of correct line + ldx 2,x ; get correct line number + stx V47 ; save it + bra L8AC7 ; insert into line +L8ADD ldx TXTTAB ; get beginning of program + bra L8AE5 +L8AE1 ldx CHARAD ; get input pointer + leax 1,x ; move it forward +L8AE5 bsr L8A86 ; return if end of program + leax 2,x ; move past line address +L8AE9 leax 1,x ; move forward +L8AEB stx CHARAD ; save input pointer +L8AED jsr GETNCH ; get an input character +L8AEF tsta ; is it actual 0? + beq L8AE1 ; brif end of line + bpl L8AED ; brif not a token + ldx CHARAD ; get input pointer + cmpa #0xff ; function? + beq L8AE9 ; brif so - ignore it (and following byte) + jsr RVEC22 ; do the RAM hook thing + cmpa #0xa7 ; THEN? + beq L8B13 ; brif so + cmpa #0x84 ; ELSE? + beq L8B13 ; brif so + cmpa #0x81 ; GO(TO|SUB)? + bne L8AED ; brif not - we don't have a line number + jsr GETNCH ; get TO/SUB + cmpa #0xa5 ; GOTO? + beq L8B13 ; brif so + cmpa #0xa6 ; GOSUB? + bne L8AEB ; brif not +L8B13 jsr GETNCH ; fetch character after token + bcs L8B1B ; brif numeric (line number) +L8B17 jsr GETCCH ; set flags on input character + bra L8AEF ; keep checking for line numbers +L8B1B ldx CHARAD ; get input pointer + pshs x ; save it + jsr LAF67 ; parse line number + ldx CHARAD ; get input pointer after line +L8B24 lda ,-x ; get character before pointer + jsr L90AA ; set C if numeric + bcs L8B24 ; brif not numeric + leax 1,x ; move pointer up + tfr x,d ; calculate size of line number + subb 1,s + subb #5 ; make sure at least 5 bytes + beq L8B55 ; brif exactly 5 bytes - no change + blo L8B41 ; brif less than 5 bytes + leau ,x ; move remainder of program backward + negb ; negate extra number of bytes (to subtract from X) + leax b,x ; now X is the correct position to move program to + jsr L89B8 ; shift program backward + bra L8B55 +L8B41 stx V47 ; save end of line number space (end of copy) + ldx VARTAB ; get end of program + stx V43 ; set source pointer + negb ; get positive difference + leax b,x ; now X is the top of the destination block + stx V41 ; set copy destination + stx VARTAB ; save new end of program + jsr LAC1E ; make sure enough room and make a hole in the program + ldx V45 ; get end address of destination block + stx CHARAD ; set input there +L8B55 puls x ; get starting address of the line number + lda #1 ; set "new number" flag + sta ,x + sta 2,x + sta 4,x + ldb BINVAL ; get MS byte of line number + bne L8B67 ; brif it is not zero + ldb #1 ; set to 1 if MSB is 0 + inc 2,x ; flag MSB as 0 +L8B67 stb 1,x ; set MSB of line number + ldb BINVAL+1 ; get LSB of number + bne L8B71 ; brif nonzero + ldb #1 ; set to 1 if LSB is 0 + inc 4,x ; flag LSB as 0 +L8B71 stb 3,x ; save LSB of line number + jsr GETCCH ; get input character + cmpa #', ; multiple line numbers? (ON GOTO, ON GOSUB) + beq L8B13 ; brif so - process another + bra L8B17 ; go look for more line numbers +L8B7B ldx TXTTAB ; point to start of program + leax -1,x ; move back (compensate for inc below) +L8B7F leax 1,x ; move forward + ldd 2,x ; get this line number + std CURLIN ; save it (for error message) + jsr L8A86 ; return if end of program + leax 3,x ; skip address and line number, stay one before line text +L8B8A leax 1,x ; move to next character +L8B8C lda ,x ; get input character + beq L8B7F ; brif end of line + deca ; valid line new line number? + beq L8BAE ; brif so + suba #2 ; undefined line? + bne L8B8A ; brif not + pshs x ; save line number pointer + ldx #L8BD9-1 ; show UL message + jsr STRINOUT + ldx ,s ; get input pointer + ldd 1,x ; get undefined line number + jsr LBDCC ; display line number + jsr LBDC5 ; print out "IN XXXX" + jsr LB958 ; do a newline + puls x ; get input pointer back +L8BAE pshs x ; save input pointer + ldd 1,x ; get binary value of line number + std FPA0+2 ; save it in FPA0 + jsr L880E ; adjust FPA0 as integer + jsr LBDD9 ; convert to text string + puls u ; get previous input pointer address + ldb #5 ; each expanded line uses 5 bytes +L8BBE leax 1,x ; move pointer forward (in string number) past sign + lda ,x ; do we have a digit? + beq L8BC9 ; brif not - end of number + decb ; mark a byte consumed + sta ,u+ ; put digit in program + bra L8BBE ; copy another digit +L8BC9 leax ,u ; point to address at end of text number + tstb ; did number fill whole space? + beq L8B8C ; brif so - move on + leay ,u ; save end of number pointer + leau b,u ; point to the end of the original expanded number + jsr L89B8 ; close up gap in program + leax ,y ; get end of line number pointer back + bra L8B8C ; go process more +L8BD9 fcn 'UL ' +; HEX$ function +HEXDOL jsr LB740 ; convert argument to positive integer + ldx #STRBUF+2 ; point to string buffer + ldb #4 ; convert 4 nibbles +L8BE5 pshs b ; save nibble counter + clrb ; clear digit accumulator + lda #4 ; do 4 shifts +L8BEA asl FPA0+3 ; shift a bit off the left of the value into low bits of B + rol FPA0+2 + rolb + deca ; done all shifts? + bne L8BEA ; brif not + tstb ; do we have a nonzero digit? + bne L8BFF ; brif so + lda ,s ; is it last digit? + deca + beq L8BFF ; brif so - keep the 0 + cmpx #STRBUF+2 ; is it a middle zero? + beq L8C0B ; brif not +L8BFF addb #'0 ; add ASCII bias + cmpb #'9 ; above 9? + bls L8C07 ; brif not + addb #7 ; adjust into alpha range +L8C07 stb ,x+ ; save digit in output + clr ,x ; make sure we have a NUL term +L8C0B puls b ; get back nibble counter + decb ; done all? + bne L8BE5 ; brif not + leas 2,s ; don't return mainline (we're returning a string) + ldx #STRBUF+1 ; point to start of converted number + jmp LB518 ; save string in string space, etc., and return it +; DLOAD command +DLOAD jsr LA429 ; close files +L8C1B clr ,-s ; save default token (not DLOADM) + cmpa #'M ; is it DLOADM? + bne L8C25 ; brif not + sta ,s ; save the "M" + jsr GETNCH ; eat the "M" +L8C25 jsr LA578 ; parse the file name + jsr GETCCH ; get character after file name + beq L8C44 ; brif end of line + jsr SYNCOMMA ; make sure we have a comma + cmpa #', ; do we have 2 commas? + beq L8C44 ; brif so - use default baud rate + jsr EVALEXPB ; evaluate baud rate +L8C36 lda #44*4 ; delay for 300 baud + tstb ; was argument 0? + beq L8C42 ; brif so - 300 baud + lda #44 ; constant for 1200 baud + decb ; was it 1? + lbne LB44A ; raise error if not +L8C42 sta DLBAUD ; save baud rate constant +L8C44 jsr L8CD0 ; transmit file name and read in file status + pshs a ; save register + lda #-3 ; set input to DLOAD + sta DEVNUM + puls a ; restore register + tst ,s+ ; is it DLOADM? + bne L8C85 ; brif so + jsr LA5C7 ; check for end of line - error if not + tstb ; ASCII? + beq L8C5F ; brif not - do error + jsr LAD19 ; clear out program + jmp LAC7C ; go read program +L8C5F jmp LA616 ; raise bad file mode +; CLOADM patch for Extended Basic +L8C62 jsr GETNCH ; get character after CLOAD + cmpa #'M ; CLOADM? + lbne CLOAD ; brif not - Color Basic's CLOAD can handle it + clr FILSTA ; close tape file + jsr GETNCH ; eat the "M" + jsr LA578 ; parse file name + jsr LA648 ; find the file + tst CASBUF+10 ; is it a chunked file? + lbeq LA505 ; brif not - Color Basic's CLOADM can handle it + ldu CASBUF+8 ; get file type and ASCII flag + dec DEVNUM ; set source device to tape + jsr LA635 ; go read the first block + tfr u,d ; put type and ASCII flag somewhere more useful +; NOTE: DLOADM comes here to do the final processing +L8C85 subd #0x200 ; is it binary and "machine language"? + bne L8C5F ; brif not - raise an error + ldx ZERO ; default load offset + jsr GETCCH ; is there any offset? + beq L8C96 ; brif not + jsr SYNCOMMA ; make sure there's a comma + jsr LB73D ; evaluate offset in X +L8C96 stx VD3 ; save offset + jsr LA5C7 ; raise error if more stuff follows +L8C9B bsr L8CC6 ; get type of "amble" + pshs a ; save it + bsr L8CBF ; read in block length + tfr d,y ; save it + bsr L8CBF ; read in load address + addd VD3 ; add in offset + std EXECJP ; save it as the execution address + tfr d,x ; put load address in a pointer + lda ,s+ ; get "amble" type + lbne LA42D ; brif postamble - close file +L8CB1 bsr L8CC6 ; read a data byte + sta ,x ; save in memory + cmpa ,x+ ; did it actually save? + bne L8CCD ; brif not RAM - raise error + leay -1,y ; done yet? + bne L8CB1 ; brif not + bra L8C9B ; look for another "amble" +L8CBF bsr L8CC1 ; read a character to B +L8CC1 bsr L8CC6 ; read character to A + exg a,b ; swap character with previously read one +L8CC5 rts +L8CC6 jsr LA176 ; read a character from input + tst CINBFL ; EOF? + beq L8CC5 ; brif not +L8CCD jmp LA619 ; raise I/O error if EOF +L8CD0 bsr L8D14 ; transmit file name + pshs b,a ; save file status + inca ; was file found? + beq L8CDD ; brif not + ldu ZERO ; zero U - first block + bsr L8CE4 ; read block + puls a,b,pc ; restore status and return +L8CDD ldb #2*26 ; code for NE error + jmp LAC46 ; raise error +L8CE2 ldu CBUFAD ; get block number +L8CE4 leax 1,u ; bump block number + stx CBUFAD ; save new block number + ldx #CASBUF ; use cassette buffer + jsr L8D7C ; read a block + jmp LA644 ; reset input buffer pointers +; Generic input handler for Extended Basic +XVEC4 lda DEVNUM ; get device number + cmpa #-3 ; DLOAD? + bne L8D01 ; brif not + leas 2,s ; don't return to mainline code + clr CINBFL ; reset EOF flag to not EOF + tst CINCTR ; anything available? + bne L8D02 ; brif so - fetch one + com CINBFL ; flag EOF +L8D01 rts +L8D02 pshs u,y,x,b ; save registers + ldx CINPTR ; get buffer pointer + lda ,x+ ; get character from buffer + pshs a ; save it for return + stx CINPTR ; save new input pointer + dec CINCTR ; account for byte removed from buffer + bne L8D12 ; brif buffer not empty + bsr L8CE2 ; go read a block +L8D12 puls a,b,x,y,u,pc ; restore registers, return value, and return +L8D14 clra ; clear attempt counter + pshs x,b,a ; make a hole for variables + leay ,s ; set up frame pointer + bra L8D1D ; go read block +L8D1B bsr L8D48 ; bump attempt counter +L8D1D lda #0x8a ; send file request control code + bsr L8D58 + bne L8D1B ; brif no echo or error + ldx #CFNBUF+1 ; point to file name +L8D26 lda ,x+ ; get file name characater + jsr L8E04 ; send it + cmpx #CFNBUF+9 ; end of file name? + bne L8D26 ; brif not + bsr L8D62 ; output check byte and look for response + bne L8D1B ; transmit name again if not ack + bsr L8D72 ; get file type (0xff is not found) + bne L8D1B ; brif error + sta 2,y ; save file type + bsr L8D72 ; read ASCII flag + bne L8D1B ; brif error + sta 3,y ; save ASCII flag + bsr L8D6B ; read check byte + bne L8D1B ; brif error + leas 2,s ; lose attempt counter and check byte + puls a,b,pc ; return file type and ascii flag +L8D48 inc ,y ; bump attempt counter + lda ,y ; get new count + cmpa #5 ; done 5 times? + blo L8D6A ; brif not + lda #0xbc ; send abort code + jsr L8E0C + jmp LA619 ; raise an I/O error +L8D58 pshs a ; save compare character + bsr L8DB8 ; send character + bne L8D60 ; brif read error + cmpa ,s ; does it match? (set Z if good) +L8D60 puls a,pc ; restore character and return +L8D62 lda 1,y ; get XOR check byte + bsr L8DB8 ; send it and read + bne L8D6A ; brif read error + cmpa #0xc8 ; is it ack? (set Z if so) +L8D6A rts +L8D6B bsr L8D72 ; read character from rs232 + bne L8D6A ; brif error + lda 1,y ; get check byte + rts +L8D72 bsr L8DBC ; read a character from rs232 + pshs a,cc ; save result (and flags) + eora 1,y ; accumulate xor checksum + sta 1,y + puls cc,a,pc ; restore byte, flags, and return +L8D7C clra ; reset attempt counter + pshs u,y,x,b,a ; make a stack frame + asl 7,s ; split block number into two 7 bit chuncks + rol 6,s + lsr 7,s + leay ,s ; set up frame pointer + bra L8D8B +L8D89 bsr L8D48 ; bump attempt counter +L8D8B lda #0x97 ; send block request code + bsr L8D58 + bne L8D89 ; brif error + lda 6,y ; send out block number (high bits first) + bsr L8E04 + lda 7,y + bsr L8E04 + bsr L8D62 ; send check byte and get ack + bne L8D89 ; brif error + bsr L8D72 ; read block size + bne L8D89 ; brif read error + sta 4,y ; save character count + ldx 2,y ; get buffer pointer + ldb #128 ; length of data block +L8DA7 bsr L8D72 ; read a data byte + bne L8D89 ; brif error + sta ,x+ ; save byte in buffer + decb ; done a whole block? + bne L8DA7 ; brif not + bsr L8D6B ; read check byte + bne L8D89 ; brif error + leas 4,s ; lose attempt counter, check byte, and buffer pointer + puls a,b,x,pc ; return with character count in A, clean rest of stack +L8DB8 clr 1,y ; clear check byte + bsr L8E0C ; output character +L8DBC clra ; clear attempt counter + pshs x,b,cc ; save registers and interrupt status + orcc #0x50 ; disable interrupts + lda TIMOUT ; get timout delay (variable) + ldx ZERO ; get constant timeout value +L8DC5 bsr L8DE6 ; get RS232 status + bcc L8DC5 ; brif "space" - waiting for "mark" +L8DC9 bsr L8DE6 ; get RS232 status + bcs L8DC9 ; brif "mark" - waiting for "space" (start bit) + bsr L8DF9 ; delay for half of bit time + ldb #1 ; set bit probe + pshs b ; save it + clra ; reset data byte +L8DD4 bsr L8DF7 ; wait one bit time + ldb PIA1+2 ; get input bit to carry + rorb + bcc L8DDE ; brif "space" (0) + ora ,s ; merge bit probe in +L8DDE asl ,s ; shift bit probe over + bcc L8DD4 ; brif we haven't done 8 bits + leas 1,s ; remove bit probe + puls cc,b,x,pc ; restore interrupts, registers, and return +L8DE6 ldb PIA1+2 ; get RS232 value + rorb ; put in C + leax 1,x ; bump timeout + bne L8DF6 ; brif nonzero + deca ; did the number of waits expire? + bne L8DF6 ; brif not + leas 2,s ; don't return - we timed out + puls cc,b,x ; restore interrupts and registers + inca ; clear Z (A was zero above) +L8DF6 rts +L8DF7 bsr L8DF9 ; do first half of bit delay then fall through for second +L8DF9 pshs a ; save register + lda DLBAUD ; get baud rate constant +L8DFD brn L8DFD ; do nothing - delay + deca ; time expired? + bne L8DFD ; brif not + puls a,pc ; restore register and return +L8E04 pshs a ; save character to send + eora 1,y ; accumulate chechsum + sta 1,y + puls a ; get character back +L8E0C pshs b,a,cc ; save registers and interrupt status + orcc #0x50 ; disable interrupts + bsr L8DF7 ; do a bit delay + bsr L8DF7 ; do another bit delay + clr PIA1 ; set output to space (start bit) + bsr L8DF7 ; do a bit delay + ldb #1 ; bit probe start at LSB + pshs b ; save bitprobe +L8E1D lda 2,s ; get output byte + anda ,s ; see what our current bit is + beq L8E25 ; brif output is 0 + lda #2 ; set output to "marking" +L8E25 sta PIA1 ; send bit + bsr L8DF7 ; do a bit delay + asl ,s ; shift bit probe + bcc L8E1D ; brif not last bit + lda #2 ; set output to marking ("stop" bit) + sta PIA1 + leas 1,s ; lose bit probe + puls cc,a,b,pc ; restore registers, interrupts, and return +; PRINT USING +; This is a massive block of code for something that has limited utility. It is *almost* flexible enough to +; be truly useful outside of a narrow range of use cases. Indeed, this comes in at about 1/8 of the total +; Extended Color Basic ROM. +; +; This uses several variables: +; VD5: pointer to format string descriptor +; VD7: next print item flag +; VD8: right digit counter +; VD9: left digit counter (or length of string argument) +; VDA: status byte (bits as follows): +; 6: force comma +; 5: force leading * +; 4: floating $ +; 3: pre-sign +; 2: post-sign +; 0: scientific notation +L8E37 lda #1 ; set length to use to 1 + sta VD9 +L8E3B decb ; consume character from format string + jsr L8FD8 ; show error flag if flags set + jsr GETCCH ; get input character + lbeq L8ED8 ; brif end of line - bail + stb VD3 ; save remaining string length + jsr LB156 ; evaluate the argument + jsr LB146 ; error if numeric + ldx FPA0+2 ; get descriptor for argument + stx V4D ; save it for later + ldb VD9 ; get length counter to use + jsr LB6AD ; get B bytes of string space (do a LEFT$) + jsr LB99F ; print the formatted string + ldx FPA0+2 ; get formatted string descriptor + ldb VD9 ; get requested length + subb ,x ; see if we have any left over +L8E5F decb ; have we got the right width? + lbmi L8FB3 ; brif so - go process more + jsr LB9AC ; output a space + bra L8E5F ; go see if we're done yet +L8E69 stb VD3 ; save current format string counter and pointer + stx TEMPTR + lda #2 ; initial spaces count = 2 (for the two %s) + sta VD9 ; save length counter +L8E71 lda ,x ; get character in string + cmpa #'% ; is it the end of the sequence? + beq L8E3B ; brif so - display requested part of the strign + cmpa #0x20 ; space? + bne L8E82 ; brif not + inc VD9 ; bump spaces count + leax 1,x ; move format pointer forward + decb ; consume character + bne L8E71 ; brif not end of format string +L8E82 ldx TEMPTR ; restore format string pointer + ldb VD3 ; get back format string length + lda #'% ; show % as debugging aid +L8E88 jsr L8FD8 ; send error indicator if flags set + jsr PUTCHR ; output character + bra L8EB9 ; go process more format string +; PRINT extension for USING +XVEC9 cmpa #0xcd ; USING? + beq L8E95 ; brif so + rts ; return to mainline code +; This is the main entry point for PRINT USING +L8E95 leas 2,s ; don't return to the mainline code + jsr LB158 ; evaluate the format string + jsr LB146 ; error if numeric + ldb #'; ; make sure there's a ; after the string + jsr LB26F + ldx FPA0+2 ; get format string descriptor + stx VD5 ; save it for later + bra L8EAE ; process format string +L8EA8 lda VD7 ; is there a print item? + beq L8EB4 ; brif not + ldx VD5 ; get back format string descriptor +L8EAE clr VD7 ; reset next print item flag + ldb ,x ; get length of format string + bne L8EB7 ; brif string is non-null +L8EB4 jmp LB44A ; raise FC error +L8EB7 ldx 2,x ; point to start of string +L8EB9 clr VDA ; clear status (new item) +L8EBB clr VD9 ; clear left digit counter + lda ,x+ ; get character from format string + cmpa #'! ; ! (use first character of string)? + lbeq L8E37 ; brif so + cmpa #'# ; digit? + beq L8F24 ; brif so - handle numeric + decb ; consume format character + bne L8EE2 ; brif not done + jsr L8FD8 ; show a "+" if any flags set - indicates error at end of string + jsr PUTCHR ; output format string character +L8ED2 jsr GETCCH ; get current input character + bne L8EA8 ; brif not end of statement + lda VD7 ; get next item flag +L8ED8 bne L8EDD ; brif more print items + jsr LB958 ; do newline +L8EDD ldx VD5 ; point to format string descriptor + jmp LB659 ; remove from string stack, etc., if appropriate (and return) +L8EE2 cmpa #'+ ; is it + (pre-sign)? + bne L8EEF ; brif not + jsr L8FD8 ; send a "+" if flags set + lda #8 ; flag for pre-sign + sta VDA ; set flags + bra L8EBB ; go interpret some more stuff +L8EEF cmpa #'. ; decimal? + beq L8F41 ; brif so - numeric + cmpa #'% ; % (show string)? + lbeq L8E69 ; brif so + cmpa ,x ; do we have two identical characters? +L8EFB bne L8E88 ; brif not - invalid format character + cmpa #'$ ; double $? + beq L8F1A ; brif so - floating $ + cmpa #'* ; double *? + bne L8EFB ; brif not + lda VDA ; get status byte + ora #0x20 ; enable * padding + sta VDA + cmpb #2 ; is $$ the last two? + blo L8F20 ; brif so + lda 1,x ; is it $ after? + cmpa #'$ + bne L8F20 ; brif not + decb ; consume the "$" + leax 1,x + inc VD9 ; add to digit counter * pad + $ counter +L8F1A lda VDA ; indicate floating $ + ora #0x10 + sta VDA +L8F20 leax 1,x ; consume the second format character + inc VD9 ; add one more left place +L8F24 clr VD8 ; clear right digit counter +L8F26 inc VD9 ; bump left digit counter + decb ; consume character + beq L8F74 ; brif end of string + lda ,x+ ; get next format character + cmpa #'. ; decimal? + beq L8F4F ; brif so + cmpa #'# ; digit? + beq L8F26 ; brif so + cmpa #', ; comma flag? + bne L8F5A ; brif not + lda VDA ; set commas flag + ora #0x40 + sta VDA + bra L8F26 ; handle more characters to left of decimal +L8F41 lda ,x ; get character after . + cmpa #'# ; digit? + lbne L8E88 ; brif not - invalid + lda #1 ; set right digit counter to 1 (for the .) + sta VD8 + leax 1,x ; consume the . +L8F4F inc VD8 ; add one to right digit counter + decb ; consume character + beq L8F74 ; brif end of format string + lda ,x+ ; get another format character + cmpa #'# ; digit? + beq L8F4F ; brif so +L8F5A cmpa #0x5e ; up arrow? + bne L8F74 ; brif not + cmpa ,x ; two of them? + bne L8F74 ; brif not + cmpa 1,x ; three of them? + bne L8F74 ; brif not + cmpa 2,x ; four of them? + bne L8F74 ; brif not + cmpb #4 ; string actually has the characters? + blo L8F74 ; brif not + subb #4 ; consome them + leax 4,x + inc VDA ; set scientific notation bit +L8F74 leax -1,x ; back up input pointer + inc VD9 ; add one digit for pre-sign force + lda VDA ; is it pre-sign? + bita #8 + bne L8F96 ; brif so + dec VD9 ; undo pre-sign adjustment + tstb ; end of string? + beq L8F96 ; brif so + lda ,x ; get next character + suba #'- ; post sign force? + beq L8F8F ; brif so + cmpa #'+-'- ; plus? + bne L8F96 ; brif not + lda #8 ; trailing + is a pre-sign force +L8F8F ora #4 ; add in post sign flag + ora VDA ; merge with flags + sta VDA + decb ; consume character +L8F96 jsr GETCCH ; do we have an argument + lbeq L8ED8 ; brif not + stb VD3 ; save format string length + jsr LB141 ; evluate numeric expression + lda VD9 ; get left digit counter + adda VD8 ; add in right digit counter + cmpa #17 ; is it more than 16 digits + decimal? + lbhi LB44A ; brif so - this is a problem + jsr L8FE5 ; format value according to settings + leax -1,x ; move buffer pointer back + jsr STRINOUT ; display formatted number string +L8FB3 clr VD7 ; reset next print item flag + jsr GETCCH ; get current input character + beq L8FC6 ; brif end of statement + sta VD7 ; set next print flag to nonzero + cmpa #'; ; list separator ;? + beq L8FC4 ; brif so + jsr SYNCOMMA ; require a comma between if not ; + bra L8FC6 ; process next item +L8FC4 jsr GETNCH ; munch the semicolon +L8FC6 ldx VD5 ; get format string descriptor + ldb ,x ; get length of string + subb VD3 ; subtract amount left after last item + ldx 2,x ; point to string address + abx ; move pointer to correct spot + ldb VD3 ; get remaining string length + lbne L8EB9 ; if we have more, interpret from there + jmp L8ED2 ; re-interpret from start if we hit the end +L8FD8 pshs a ; save character + lda #'+ ; "error" flag character + tst VDA ; did we have some flags set? + beq L8FE3 ; brif not + jsr PUTCHR ; output error flag +L8FE3 puls a,pc ; restore character and return +L8FE5 ldu #STRBUF+4 ; point to string buffer + ldb #0x20 ; blank space + lda VDA ; get flags + bita #8 ; pre-sign? + beq L8FF2 ; brif not + ldb #'+ ; plus sign +L8FF2 tst FP0SGN ; get sign of value + bpl L8FFA ; brif positive + clr FP0SGN ; make number positive (for later) + ldb #'- ; negative sign +L8FFA stb ,u+ ; put sign in buffer + ldb #'0 ; put a zero there + stb ,u+ + anda #1 ; check scientific notation force + lbne L910D ; brif so + ldx #LBDC0 ; point to FP 1E+9 + jsr LBCA0 ; is it less? + bmi L9023 ; brif so + jsr LBDD9 ; convert FP number to string (we're doing scientific notation) +L9011 lda ,x+ ; advance pointer to end of string + bne L9011 +L9015 lda ,-x ; make a hole at the start + sta 1,x + cmpx #STRBUF+3 ; done yet? + bne L9015 ; brif not + lda #'% ; put "overflow" flag at start + sta ,x + rts +L9023 lda FP0EXP ; get exponent of value + sta V47 ; save it + beq L902C ; brif value is 0 + jsr L91CD ; convert to number with 9 significant figures to left of decimal +L902C lda V47 ; get base 10 exponent offset + lbmi L90B3 ; brif < 100,000,000 + nega ; get negative difference + adda VD9 ; add to number of left digits + suba #9 ; account for the 9 we actually have + jsr L90EA ; put leading zeroes in buffer + jsr L9263 ; initialize the decimal point and comma counters + jsr L9202 ; convert FPA0 to decimal ASCII in buffer + lda V47 ; get base 10 exponent + jsr L9281 ; put that many zeroes in buffer, stop at decimal point + lda V47 ; get base 10 exponent + jsr L9249 ; check for decimal + lda VD8 ; get right digit counter + bne L9050 ; brif we want stuff after decimal + leau -1,u ; delete decimal if not needed +L9050 deca ; subtract one place (for decimal) + jsr L90EA ; put zeroes in buffer (trailing) +L9054 jsr L9185 ; insert * padding, floating $, and post-sign + tsta ; was there a post sign? + beq L9060 ; brif not + cmpb #'* ; was first character a *? + beq L9060 ; brif so + stb ,u+ ; store the post sign +L9060 clr ,u ; make srue it's NUL terminated + ldx #STRBUF+3 ; point to start of buffer +L9065 leax 1,x ; move to next character + stx TEMPTR ; save it for later + lda VARPTR+1 ; get address of decimal point + suba TEMPTR+1 ; subtract out actual digits left of decimal + suba VD9 ; subtract out required left digits + beq L90A9 ; brif no padding needed + lda ,x ; get current character + cmpa #0x20 ; space? + beq L9065 ; brif so - advance pointer + cmpa #'* ; *? + beq L9065 ; brif so - advance pointer + clra ; zero on stack is end of data ponter +L907C pshs a ; save character on stack + lda ,x+ ; get next character + cmpa #'- ; minus? + beq L907C ; brif so + cmpa #'+ ; plus? + beq L907C ; brif so + cmpa #'$ ; $? + beq L907C ; brif so + cmpa #'0 ; zero? + bne L909E ; brif not + lda 1,x ; get character after 0 + bsr L90AA ; clear carry if number + bcs L909E ; brif not number +L9096 puls a ; get character off stack + sta ,-x ; put it back in string buffer + bne L9096 ; brif not - restore another + bra L9065 ; keep cleaning up buffer +L909E puls a ; get the character on the stack + tsta ; is it NUL? + bne L909E ; brif not + ldx TEMPTR ; get string buffer start pointer + lda #'% ; put error flag in front + sta ,-x +L90A9 rts +L90AA cmpa #'0 ; zero? + blo L90B2 ; brif not + suba #'9+1 ; set C if > "9" + suba #-('9+1) +L90B2 rts +L90B3 lda VD8 ; get right digit counter + beq L90B8 ; brif not right digits + deca ; account for decimal point +L90B8 adda V47 ; add base 10 exponent offset + bmi L90BD ; if >= 0, no shifts are required + clra ; force shift counter to 0 +L90BD pshs a ; save shift counter +L90BF bpl L90CB ; brif positive count + pshs a ; save shift counter + jsr LBB82 ; divide FPA0 by 10 (shift 1 digit to the right) + puls a ; get shift counter back + inca ; account for the shift + bra L90BF ; see if we're done yet +L90CB lda V47 ; get base 10 exponent offset + suba ,s+ ; account for adjustment + sta V47 ; save new exponent offset + adda #9 ; account for significant places + bmi L90EE ; brif we don't need zeroes to left + lda VD9 ; get left decimal counter + suba #9 ; account for significant figures + suba V47 ; subtract exponent offset + bsr L90EA ; output leading zeroes + jsr L9263 ; initialize decimal and comma counters + bra L90FF ; process remainder of digits +L90E2 pshs a ; save zero counter + lda #'0 ; insert a 0 + sta ,u+ + puls a ; get back counter +L90EA deca ; do we need more zeroes? + bpl L90E2 ; brif so + rts +L90EE lda VD9 ; get left digit counter + bsr L90EA ; put that many zeroes in + jsr L924D ; put decimal in buffer + lda #-9 ; figure out filler zeroes + suba V47 + bsr L90EA ; output required leader zeroes + clr V45 ; clear decimal pointer counter + clr VD7 ; clear comma counter +L90FF jsr L9202 ; decode FPA0 to decimal string + lda VD8 ; get right digit counter + bne L9108 ; brif there are right digits + ldu VARPTR ; point to decimal location of decimal +L9108 adda V47 ; add base 10 exponent + lbra L9050 ; add in leading astrisks, etc. +L910D lda FP0EXP ; get exponent of FPA0 + pshs a ; save it + beq L9116 ; brif 0 + jsr L91CD ; convert to number with 9 figures +L9116 lda VD8 ; get right digit counter + beq L911B ; brif no right digits + deca ; account for decimal point +L911B adda VD9 ; get left digit counter + clr STRBUF+3 ; use buffer byte as temporary storage + ldb VDA ; get status flags + andb #4 ; post-sign? + bne L9129 ; brif so + com STRBUF+3 ; flip byte if no post sign +L9129 adda STRBUF+3 ; subtract 1 if no post sign + suba #9 ; account for significant figures + pshs a ; save shift counter +L9130 bpl L913C ; brif no more shifts needed + pshs a ; save counter + jsr LBB82 ; divide by 10 (shift right one) + puls a ; get back counter + inca ; account for the shift + bra L9130 ; see if we need more +L913C lda ,s ; get original shift count + bmi L9141 ; brif shifting happened + clra ; flag for no shifting +L9141 nega ; calculate position of decimal (negate shift count, add left digit and post sign if needed) + adda VD9 ; add left digit counter + inca ; and post sign + adda STRBUF+3 + sta V45 ; save decimal counter + clr VD7 ; clear comma counter + jsr L9202 ; convert to decimal string + puls a ; get shift counter + jsr L9281 ; put the needed zeroes in + lda VD8 ; get right digit counter + bne L915A ; brif we want some + leau -1,u ; remove te decimal point +L915A ldb ,s+ ; get original exponent + beq L9167 ; brif it was 0 + ldb V47 ; get base 10 exponent + addb #9 ; account for significant figures + subb VD9 ; remove left digit count + subb STRBUF+3 ; add one if post sign +L9167 lda #'+ ; positive sign + tstb ; is base 10 exponent positive? + bpl L916F ; brif so + lda #'- ; negative sign + negb ; flip exponent +L916F sta 1,u ; put exponent sign + lda #'E ; put "E" and advance output pointer + sta ,u++ + lda #'0-1 ; initialize digit accumulator +L9177 inca ; bump digit + subb #12 ; are we at the right digit? + bcc L9177 ; brif not + addb #'0+12 ; add ASCII bias and undo extra subtraction + std ,u++ ; save exponent in buffer + clr ,u ; clear final byte in buffer + jmp L9054 ; insert *, $, etc. +L9185 ldx #STRBUF+4 ; point to start of result + ldb ,x ; get sign + pshs b ; save it + lda #0x20 ; default pad with spaces + ldb VDA ; get flags + bitb #0x20 ; padding with *? + puls b + beq L919E ; brif no padding + lda #'* ; pad with * + cmpb #0x20 ; do we have a blank? (positive) + bne L919E ; brif not + tfr a,b ; use pad character +L919E pshs b ; save first character +L91A0 sta ,x+ ; store padding + ldb ,x ; get next character + beq L91B6 ; brif end of string + cmpb #'E ; exponent? + beq L91B6 ; brif so - treat as 0 + cmpb #'0 ; zero? + beq L91A0 ; brif so - pad it + cmpb #', ; leading comma? + beq L91A0 ; brif so - pad it + cmpb #'. ; decimal? + bne L91BA ; brif so - don't put a 0 before it +L91B6 lda #'0 ; put a zero before + sta ,-x +L91BA lda VDA ; get status byte + bita #0x10 ; floating $? + beq L91C4 ; brif not + ldb #'$ ; stuff a $ in + stb ,-x +L91C4 anda #4 ; pre-sgn? + puls b ; get back first character + bne L91CC ; brif not + stb ,-x ; save leading character (sign) +L91CC rts +L91CD pshs u ; save buffer pointer + clra ; initial exponent offset is 0 +L91D0 sta V47 ; save exponent offset + ldb FP0EXP ; get actual exponent + cmpb #0x80 ; is value >= 1.0? + bhi L91E9 ; brif so + ldx #LBDC0 ; point to FP number 1E9 + jsr LBACA ; multiply by 1000000000 + lda V47 ; account for 9 shifts + suba #9 + bra L91D0 ; brif not there yet +L91E4 jsr LBB82 ; divide by 10 + inc V47 ; account for shift +L91E9 ldx #LBDBB ; point to 999999999 + jsr LBCA0 ; compare it + bgt L91E4 ; brif not in range yet +L91F1 ldx #LBDB6 ; point to 99999999.9 + jsr LBCA0 ; compare + bgt L9200 ; brif in range + jsr LBB6A ; multiply by 10 + dec V47 ; account for shift + bra L91F1 ; see if we're in range yet +L9200 puls u,pc ; restore buffer pointer and return +L9202 pshs u ; save buffer pointer + jsr LB9B4 ; add .5 (round off) + jsr LBCC8 ; convert to integer format + puls u ; restore buffer pointer + ldx #LBEC5 ; point to 32 bit powers of 10 (alternating signs) + ldb #0x80 ; intitial digit counter is 0 with 0x80 bias +L9211 bsr L9249 ; check for comma +L9213 lda FPA0+3 ; add a power of 10 + adda 3,x + sta FPA0+3 + lda FPA0+2 + adca 2,x + sta FPA0+2 + lda FPA0+1 + adca 1,x + sta FPA0+1 + lda FPA0 + adca ,x + sta FPA0 + incb ; add one to digit counter + rorb ; set V if carry and sign differ + rolb + bvc L9213 ; brif we haven't "wrapped" + bcc L9235 ; brif subtracting + subb #10+1 ; take 9's complement if adding + negb +L9235 addb #'0-1 ; add in ASCII bias + leax 4,x ; move to next power + tfr b,a ; save digit + anda #0x7f ; mask off subtract flag + sta ,u+ ; save digit + comb ; toggle add/subtract + andb #0x80 + cmpx #LBEE9 ; done all places? + bne L9211 ; brif not + clr ,u ; but NUL at end +L9249 dec V45 ; at decimal? + bne L9256 ; brif not +L924D stu VARPTR ; save decimal point pointer + lda #'. ; insert decimal + sta ,u+ + clr VD7 ; clear comma counter + rts +L9256 dec VD7 ; do we need a comma? + bne L9262 ; brif not + lda #3 ; reset comma counter + sta VD7 + lda #', ; insert comma + sta ,u+ +L9262 rts +L9263 lda V47 ; get base 10 exponent offset + adda #10 ; account for significant figures + sta V45 ; save decimal counter + inca ; add one for decimal point +L926A suba #3 ; divide by 3, leave remainder in A + bcc L926A + adda #5 ; renormalize to range 1-3 + sta VD7 ; save comma counter + lda VDA ; get status + anda #0x40 ; commas wanted? + bne L927A ; brif not + sta VD7 ; clear comma counter +L927A rts +L927B pshs a ; save zeroes counter + bsr L9249 ; check for decimal + puls a ; get back counter +L9281 deca ; need a zero? + bmi L928E ; brif not + pshs a ; save counter + lda #'0 ; put a zero + sta ,u+ + lda ,s+ ; get back counter and set flags + bne L927B ; brif not done enough +L928E rts +; From here to the end of the Extended Basic ROM is the PMODE graphics system and related +; infrastructure with the exception of the PLAY command which shares some of its machinery +; with the DRAW command. +; +; Fetch screen address calculation routine address for the selected graphics mode +L928F ldu #L929C ; point to normalization routine jump table + lda PMODE ; get graphics mode + asla ; two bytes per address + ldu a,u ; get routine address + rts +; Normalize VERBEG/HORBEG coordinates and return screen address in X and pixel mask in A. +L9298 bsr L928F ; fetch normalization routine pointer + jmp ,u ; transfer control to it +L929C fdb L92A6 ; PMODE 0 + fdb L92C2 ; PMODE 1 + fdb L92A6 ; PMODE 2 + fdb L92C2 ; PMODE 3 + fdb L92A6 ; PMODE 4 +; Two colour mode address calculatoin +L92A6 pshs u,b ; savce registers + ldb HORBYT ; get number of bytes in each graphics row + lda VERBEG+1 ; get vertical coordinate + mul + addd BEGGRP ; now D is the absolute address of the start of the row + tfr d,x ; get address to the return location + ldb HORBEG+1 ; get horizontal coordinate + lsrb ; divide by 8 (8 pixels per byte in 2 colour mode) + lsrb + lsrb + abx ; now X is the address of the actual pixel byte + lda HORBEG+1 ; get horizontal coordinate + anda #7 ; keep only the low 3 bits which contain the pixel number + ldu #L92DD ; point to pixel mask lookup + lda a,u ; get pixel mask + puls b,u,pc ; restore registers and return result +; four colour address calculation +L92C2 pshs u,b ; save registers + ldb HORBYT ; get bytes per graphics row + lda VERBEG+1 ; get vertical coordinate + mul + addd BEGGRP ; now D is the address of the start of the row + tfr d,x ; put it in returnlocatin + ldb HORBEG+1 ; get horizontal coordinate + lsrb ; divide by 4 (four colour modes have four pixels per byte) + lsrb + abx ; now X points to the screen byte + lda HORBEG+1 ; get horizontal coordinate + anda #3 ; keep low two bits for pixel number + ldu #L92E5 ; point to four colour pixel masks + lda a,u ; get pixel mask + puls b,u,pc ; restore registers and return result +L92DD fcb 0x80,0x40,0x20,0x10 ; two colour pixel masks + fcb 0x08,0x04,0x02,0x01 +L92E5 fcb 0xc0,0x30,0x0c,0x03 ; four colour pixel masks +; Move X down one graphics row +L92E9 ldb HORBYT ; get bytes per row + abx ; add to screen address + rts +; Move one pixel right in 2 colour mode +L92ED lsra ; move pixel mask right + bcc L92F3 ; brif same byte + rora ; move pixel mask to left of byte + leax 1,x ; move to next byte +L92F3 rts +; Move one pixel right in 4 colour mode +L92F4 lsra ; shift mask half a pixel right + bcc L92ED ; brif not past end of byte - shift one more + lda #0xc0 ; set mask on left of byte + leax 1,x ; move to next byte + rts +; Evaluate two expressions (coordinates). Put first in HORBEG and second in VERBEG. +L92FC jsr LB734 ; evaluate two expressions - first in BINVAL, second in B + ldy #HORBEG ; point to storage location +L9303 cmpb #192 ; is vertical outside range? + blo L9309 ; brif not + ldb #191 ; max it at bottom of screen +L9309 clra ; zero extend vertical coordinate + std 2,y ; save vertical coordinate + ldd BINVAL ; get horizontal coordinate + cmpd #256 ; in range? + blo L9317 ; brif so + ldd #255 ; max it out to right side of screen +L9317 std ,y ; save horizontal coordinate + rts +; Normalize coordinates for proper PMODE +L931A jsr L92FC ; parse coordinates +L931D ldu #HORBEG ; point to start coordinates +L9320 lda PMODE ; get graphics mode + cmpa #2 ; is it pmode 0 or 1? + bhs L932C ; brif not + ldd 2,u ; get vertical coordinate + lsra ; divide it by two + rorb + std 2,u ; save it back +L932C lda PMODE ; get graphics mode + cmpa #4 ; pmode 4? + bhs L9338 ; brif so + ldd ,u ; cut horizontal coordinate in half + lsra + rorb + std ,u ; save new coordinate +L9338 rts +; PPOINT function +PPOINT jsr L93B2 ; evaluate two expressions (coordinates) + jsr L931D ; normalize coordinates + jsr L9298 ; get screen address + anda ,x ; get colour value of desired screen coordinate + ldb PMODE ; get graphics mode + rorb ; is it a two colour m ode? + bcc L935B ; brif so +L9349 cmpa #4 ; is it on rightmost bits? + blo L9351 ; brif not + rora ; shift right + rora + bra L9349 ; see if we're there yet +L9351 inca ; colour numbers start at 1 + asla ; add in colour set (0 or 8) + adda CSSVAL + lsra ; get colour in range of 0 to 8 +L9356 tfr a,b ; put result to B + jmp LB4F3 ; return B as FP number +L935B tsta ; is pixel on? + beq L9356 ; brif not, return 0 (off) + clra ; set colour number to "1" + bra L9351 ; make it 1 or 5 and return +; PSET command +PSET lda #1 ; PSET flag + bra L9366 ; go turn on the pixel +; PRESET command +PRESET clra ; PRESET flag +L9366 sta SETFLG ; store whether we're setting or resetting + jsr LB26A ; enforce ( + jsr L931A ; evaluate coordinates + jsr L9581 ; evaluate colour + jsr LB267 ; enforce ) + jsr L9298 ; get address of pixel +L9377 ldb ,x ; get screen data + pshs b ; save it + tfr a,b ; duplicate pixel mask + coma ; invert mask + anda ,x ; turn off screen pixel + andb ALLCOL ; adjust pixel mask to be the current colour + pshs b ; merge pixel data into the screen data + ora ,s+ + sta ,x ; put it on screen + suba ,s+ ; nonzero if screen data changed + ora CHGFLG ; propagate change flag + sta CHGFLG + rts +; Evaluate two sets of coordinates separated by a minus sign. First at HORBEG,VERBEG and +; second at HOREND,VEREND. If no first set, use HORDEF,VERDEF. +L938F ldx HORDEF ; set default start coords + stx HORBEG + ldx VERDEF + stx VERBEG + cmpa #0xac ; do we start with a -? + beq L939E ; brif no starting coordinates + jsr L93B2 ; parse coordinates +L939E ldb #0xac ; make sure we have a - + jsr LB26F + jsr LB26A ; require a ( + jsr LB734 ; evaluate two expressions + ldy #HOREND ; point to storage location + jsr L9303 ; process coordinates + bra L93B8 ; finish up with a ) +L93B2 jsr LB26A ; make sure there's a ( + jsr L92FC ; evaluate coordinates +L93B8 jmp LB267 ; force a ) +; LINE command +LINE cmpa #0x89 ; is it LINE INPUT? + lbeq L89C0 ; brif so - go handle it + cmpa #'( ; starting coord? + beq L93CE ; brif so + cmpa #0xac ; leading -? + beq L93CE ; brif so + ldb #'@ ; if it isn't the above, make sure it's @ + jsr LB26F +L93CE jsr L938F ; parse coordinates + ldx HOREND ; set ending coordinates as the defaults + stx HORDEF + ldx VEREND + stx VERDEF + jsr SYNCOMMA ; make sure we have a comma + cmpa #0xbe ; PRESET? + beq L93E9 ; brif so + cmpa #0xbd ; PSET? + lbne LB277 ; brif not + ldb #01 ; PSET flag + skip1lda ; skip byte and set A nonzero +L93E9 clrb ; PRESET flag + pshs b ; save PSET/PRESET flag + jsr GETNCH ; eat the PSET/PRESET + jsr L9420 ; normalize coordinates + puls b ; get back PSET/PRESET flag + stb SETFLG ; flag which we're doing + jsr L959A ; set colour byte + jsr GETCCH ; get next bit + lbeq L94A1 ; brif no box option + jsr SYNCOMMA ; make sure it's comma + ldb #'B ; make sure "B" for "box" + jsr LB26F + bne L9429 ; brif something follows the B + bsr L9444 ; draw horizontal line + bsr L946E ; draw vertical line + ldx HORBEG ; save horizontal coordinate + pshs x ; save it + ldx HOREND ; switch in horizontal end + stx HORBEG + bsr L946E ; draw vertical line + puls x ; get back original start + stx HORBEG ; put it back + ldx VEREND ; do the same dance with the vertical end + stx VERBEG + bra L9444 ; draw horizontal line +L9420 jsr L931D ; normalize the start coordinates + ldu #HOREND ; point to end coords + jmp L9320 ; normalize those coordinates +L9429 ldb #'F ; make sure we have "BF" for "filled box" + jsr LB26F + bra L9434 ; fill the box +L9430 leax -1,x ; move vertical coordinate up one +L9432 stx VERBEG ; save new vertical coordinate +L9434 jsr L9444 ; draw a horizontal line + ldx VERBEG ; are we at the end of the box? + cmpx VEREND + beq L9443 ; brif so + bcc L9430 ; brif we're moving up the screen + leax 1,x ; move down the screen + bra L9432 ; go draw another line +L9443 rts +; Draw a horizontal line from HORBEG to HOREND at VERBEG using color mask in ALLCOL +L9444 ldx HORBEG ; get starting horizontal coordinate + pshs x ; save it + jsr L971D ; get absolute value of HOREND-HORBEG + bcc L9451 ; brif end is > start + ldx HOREND ; copy end coordinate to start it is smaller + stx HORBEG +L9451 tfr d,y ; save difference - it's a pixel count + leay 1,y ; coordinates are inclusive + jsr L9298 ; get screen position of start coord + puls u ; restore original start coordinate + stu HORBEG + bsr L9494 ; point to routine to move pizel pointers to right +L945E sta VD7 ; save pixel mask + jsr L9377 ; turn on pixel + lda VD7 ; get pixel mask back + jsr ,u ; move one pixel right + leay -1,y ; turned on enough pixels yet? + bne L945E ; brif not +L946B rts +L946C puls b,a ; clean up stack +L946E ldd VERBEG ; save original vertical start coordinate + pshs b,a + jsr L9710 ; get vertical difference + bcc L947B ; brif end coordinate > start + ldx VEREND ; swap in end coordinate if not + stx VERBEG +L947B tfr d,y ; save number of pixels to set + leay 1,y ; the coordinates are inclusive + jsr L9298 ; get screen pointer + puls u ; restore start coordinate + stu VERBEG + bsr L949D ; point to routine to move down one row + bra L945E ; draw vertical line +; Point to routine which will move one pixel right +L948A fdb L92ED ; PMODE 0 + fdb L92F4 ; PMODE 1 + fdb L92ED ; PMODE 2 + fdb L92F4 ; PMODE 3 + fdb L92ED ; PMODE 4 +L9494 ldu #L948A ; point to jump table + ldb PMODE ; get graphics mode + aslb ; two bytes per address + ldu b,u ; get jump address + rts +; Point to routine to move down one row +L949D ldu #L92E9 ; point to "move down one row" routien + rts +; Draw a line from HORBEG,VERBEG to HOREND,VEREND +L94A1 ldy #L950D ; point to increase vertical coord + jsr L9710 ; calculate difference + lbeq L9444 ; brif none - draw a horizontal line + bcc L94B2 ; brif vertical end is > vertical start + ldy #L951B ; point to decrease vertical coord +L94B2 pshs d ; save vertical difference + ldu #L9506 ; point to increase horizontal coord + jsr L971D ; get difference + beq L946C ; brif none - draw a vertical line + bcc L94C1 ; brif horizontal end > horizontal start + ldu #L9514 ; point to decrease hoizontal coord +L94C1 cmpd ,s ; compare vert and horiz differences + puls x ; get X difference + bcc L94CC ; brif horiz diff > vert diff + exg u,y ; swap change routine pointers + exg d,x ; swap differences +L94CC pshs u,d ; save larger difference and routine + pshs d ; save larger difference + lsra ; divide by two + rorb + bcs L94DD ; brif odd number + cmpu #L950D+1 ; increase or decrease? + blo L94DD ; brif increase + subd #1 ; back up one +L94DD pshs x,b,a ; save smallest difference and initial middle offset + jsr L928F ; point to proper coordinate to screen conversion routine +L94E2 jsr ,u ; convert coordinates to screen address + jsr L9377 ; turn on a pixel + ldx 6,s ; get distnace counter + beq L9502 ; brif line is completely drawn + leax -1,x ; account for one pixel drawn + stx 6,s ; save new counter + jsr [8,s] ; increment/decrement larger delta + ldd ,s ; get the minor coordinate increment counter + addd 2,s ; add the smallest difference + std ,s ; save new minor coordinate incrementcounter + subd 4,s ; subtractout the largest difference + bcs L94E2 ; brif not greater - draw another pixel + std ,s ; save new minor coordinate increment + jsr ,y ; adjust minor coordinate + bra L94E2 ; go draw another pixel +L9502 puls x ; clean up stack and return + puls a,b,x,y,u,pc +L9506 ldx HORBEG ; bump horizontal coordinate + leax 1,x + stx HORBEG + rts +L950D ldx VERBEG ; bump vertical coordinate + leax 1,x + stx VERBEG + rts +L9514 ldx HORBEG ; decrement horizontal coordinate + leax -1,x + stx HORBEG + rts +L951B ldx VERBEG ; decrement vertical coordinate + leax -1,x + stx VERBEG + rts +; Get normalized maximum coordinate values in VD3 and VD5 +L9522 ldu #VD3 ; point to temp storage + ldx #255 ; set maximum horizontal + stx ,u + ldx #191 ; set maximum vertical + stx 2,u + jmp L9320 ; normalize them +; PCLS command +PCLS beq L9542 ; clear to background colour if no argument + bsr L955A ; evaluate colour +L9536 lda #0x55 ; consider each byte as 4 groups of 2 bit sub-nibbles + mul ; now colour is in all four sub-pixels + ldx BEGGRP ; get start of graphics screen +L953B stb ,x+ ; set byte to proper colour + cmpx ENDGRP ; at end of graphics page? + bne L953B ; brif not + rts +L9542 ldb BAKCOL ; get background colour + bra L9536 ; do the clearing dance +; COLOR command +COLOR cmpa #', ; check for comma + beq L9552 ; brif no foreground colour + bsr L955A ; evaluate first colour + stb FORCOL ; set foreground colour + jsr GETCCH ; is there a background colour? + beq L9559 ; brif not +L9552 jsr SYNCOMMA ; make sure we have a comma + bsr L955A ; evaluate background colour argument + stb BAKCOL ; set background colour +L9559 rts +; Evaluate a colour agument and convert to proper code based on graphics mode +L955A jsr EVALEXPB ; evaluate colour code +L955D cmpb #9 ; is it in range of 0-8? + lbhs LB44A ; brif not - raise error + clra ; CSS value for first colour set + cmpb #5 ; is it first or second colour set? + blo L956C ; brif first colour set + lda #8 ; flag second colour set + subb #4 ; adjust into basic range +L956C pshs a ; save CSS value + lda PMODE ; get graphics mode + rora ; 4 colour or 2? + bcc L957B ; brif 2 colour + tstb ; was it 0? + bne L9578 ; brif not +L9576 ldb #4 ; if so, make it 4 +L9578 decb ; convert to zero based +L9579 puls a,pc ; get back CSS value and return +L957B rorb ; is colour number odd? + bcs L9576 ; brif so - force all bits set colour + clrb ; force colour 0 if not + bra L9579 +; Set all pixel byte and active colour +L9581 jsr L959A ; set colour byte + jsr GETCCH ; is there something to evaluate? + beq L9598 ; brif not + cmpa #') ; )? + beq L9598 ; brif so + jsr SYNCOMMA ; force comma + cmpa #', ; another comma? + beq L9598 ; brif so + jsr L955A ; evaluate expression and return colour + bsr L95A2 ; save colour and pixel byte +L9598 jmp GETCCH ; re-fetch input character and return +L959A ldb FORCOL ; use foreground colour by default + tst SETFLG ; doing PRESET? + bne L95A2 ; brif not + ldb BAKCOL ; default to background colour +L95A2 stb WCOLOR ; save working colour + lda #0x55 ; consider a byte as 4 pixels + mul ; now all pixels are set to the same bit pattern + stb ALLCOL ; set all pixels byte + rts +L95AA bne L95CF ; brif graphics mode +L95AC pshs x,b,a ; save registers + ldx #SAMREG+8 ; point to middle of control register + sta 10,x ; reset display page to 0x400 + sta 8,x + sta 6,x + sta 4,x + sta 2,x + sta 1,x + sta -2,x + sta -4,x ; reset to alpha mode + sta -6,x + sta -8,x + lda PIA1+2 ; set VDG to alpha mode, colour set 0 + anda #7 + sta PIA1+2 + puls a,b,x,pc ;restore registers and return +L95CF pshs x,b,a ; save registers + lda PMODE ; get graphics mode + adda #3 ; offset to 3-7 (we don't use the bottom 3 modes) + ldb #0x10 ; shift to high 4 bits + mul + orb #0x80 ; set to graphics mode + orb CSSVAL ; set the desired colour set + lda PIA1+2 ; get get original PIA values + anda #7 ; mask off VDG control + pshs a ; merge with new VDG control + orb ,s+ + stb PIA1+2 ; set new VDG mode + lda BEGGRP ; get start of graphics page + lsra ; divide by two - pages are on 512 byte boundaries + jsr L960F ; set SAM control register + lda PMODE ; get graphics mode + adda #3 ; shift to VDG values + cmpa #7 ; PMODE 4? + bne L95F7 ; brif not + deca ; treat PMODE 4 the same as PMODE 3 +L95F7 bsr L95FB ; program SAM's VDG bits + puls a,b,x,pc ; restore registers and return +L95FB ldb #3 ; set 3 bits in register + ldx #SAMREG ; point to VDG control bits +L9600 rora ; get bit to set + bcc L9607 ; brif we need to clear the bit + sta 1,x ; set the bit + bra L9609 +L9607 sta ,x ; clear the bit +L9609 leax 2,x ; move to next bit + decb ; done all bits? + bne L9600 ; brif not + rts +L960F ldb #7 ; 7 screen address bits + ldx #SAMREG+6 ; point to screen address bits in SAM + bra L9600 ; go program SAM bits +L9616 lda PIA1+2 ; get VDG bits + anda #0xf7 ; keep everything but CSS bit + ora CSSVAL ; set correct CSS bit + sta PIA1+2 ; set desired CSS + rts +; PMODE command +PMODETOK cmpa #', ; is first argument missing? + beq L9650 ; brif so + jsr EVALEXPB ; evaluate PMODE number + cmpb #5 ; valid (0-4)? + bhs L966D ; brif not + lda #6 ; get start of graphics memory +L962E sta BEGGRP ; set start of graphics page + aslb ; multiply mode by two (table has two bytes per entry) + ldu #L9706+1 ; point to lookup table + adda b,u ; add in number of 256 byte pages used for graphics screen + cmpa TXTTAB ; does it fit? + bhi L966D ; brif not + sta ENDGRP ; save end of graphics + leau -1,u ; point to bytes per horizontal row + lda b,u ; get bytes per row + sta HORBYT ; set it + lsrb ; restore PMODE value + stb PMODE ; set graphics mode + clra ; set background colour to 0 + sta BAKCOL + lda #3 ; set foreground colour to maximum (3) + sta FORCOL + jsr GETCCH ; is there a starting page number? + beq L966C ; brif not +L9650 jsr LB738 ; evaluate an expression following a comma + tstb ; page 0? + beq L966D ; brif so - not valid + decb ; zero-base it + lda #6 ; each graphics page is 6*256 + mul + addb GRPRAM ; add to start of graphics memory + pshs b ; save start of screen memory + addb ENDGRP ; add current and address + subb BEGGRP ; subtract current start (adds size of screen) + cmpb TXTTAB ; does it fit? + bhi L966D ; brif not + stb ENDGRP ; save new end of graphics + puls b ; get back start of graphics + stb BEGGRP ; set start of graphics +L966C rts +L966D jmp LB44A ; raise FC error +; SCREEN command +SCREEN cmpa #', ; is there a mode? + beq L967F ; brif no mode + jsr EVALEXPB ; get mode argument + tstb ; set Z if alpha + jsr L95AA ; set SAM/VDG for graphics mode + jsr GETCCH ; is there a second argument? + beq L966C ; brif not +L967F jsr LB738 ; evaluate , + tstb ; colour set 0? + beq L9687 ; brif so + ldb #8 ; flag for colour set 1 +L9687 stb CSSVAL ; set colour set + bra L9616 ; set up VDG +; PCLEAR command +PCLEAR jsr EVALEXPB ; evaulate number of pages requested + tstb ; 0? + beq L966D ; brif zero - not allowed + cmpb #9 ; more than 8? + bhs L966D ; brif so - not allowed + lda #6 ; there are 6 "pages" per graphics page + mul ; now B is the number of pages to reserve + addb GRPRAM ; add to start of graphics memory + tfr b,a ; now A is the MSB of the start of free memory + ldb #1 ; program memory always starts one above + tfr d,y ; save pointer to program memory + cmpd ENDGRP ; are we trying to deallocate the current graphics page? + lblo LB44A ; brif so + subd TXTTAB ; subtract out current start of basic program + addd VARTAB ; add in end of program - now D is new top of program + tfr d,x ; save new end of program + addd #200 ; make some extra space (for stack) + subd FRETOP ; see if new top of program fits + bhs L966D ; brif there isn't enough space + ldu VARTAB ; get end of program + stx VARTAB ; save new end of program + cmpu VARTAB ; is old end higher? + bhs L96D4 ; brif so +L96BD lda ,-u ; copy a byte upward + sta ,-x + cmpu TXTTAB ; at beginning? + bne L96BD ; brif not + sty TXTTAB ; save new start of program + clr -1,y ; there must always be a NUL before the program +L96CB jsr LACEF ; re-assign basic program addresses + jsr LAD26 ; reset variables and stack + jmp LAD9E ; return to interpretation loop +L96D4 ldu TXTTAB ; get start of program + sty TXTTAB ; save new start of program + clr -1,y ; there must be a NUL at the start of the program +L96DB lda ,u+ ; move a byte downward + sta ,y+ + cmpy VARTAB ; at the top of the program? + bne L96DB ; brif not + bra L96CB ; finish up +; Graphics initialization routine - this really should be up at the start of the ROM with the +; rest of the initialization code. +L96E6 ldb #0x1e ; set start of program to 0x1e00 ("PCLEAR 4") + stb TXTTAB + lda #6 ; graphics memory starts immediately after the screen +L96EC sta GRPRAM ; set graphics memory start + sta BEGGRP ; set start of current graphics page + clra ; set PMODE to 0 + sta PMODE + lda #16 ; 16 bytes per graphics row + sta HORBYT + lda #3 ; set foreground colour to 3 + sta FORCOL + lda #0x0c ; set ending graphics page (for PMODE 0) + sta ENDGRP + ldx TXTTAB ; get start of program + clr -1,x ; make sure there's a NUL before it +L9703 jmp LAD19 ; do a "NEW" +; PMODE data table (bytes per row and number of 256 byte pages required for a screen) +L9706 fcb 16,6 ; PMODE 0 + fcb 32,12 ; PMODE 1 + fcb 16,12 ; PMODE 2 + fcb 32,24 ; PMODE 3 + fcb 32,24 ; PMODE 4 +; Calculate absolute value of vertical coordinate difference +L9710 ldd VEREND ; get ending address + subd VERBEG ; get difference +L9714 bcc L9751 ; brif we didn't carry + pshs cc ; save status (C set if start > end) + jsr L9DC3 ; negate the difference to be positive + puls cc,pc ; restore C and return +; Calculate absolute value of horizontal coordinate difference +L971D ldd HOREND ; get end coordinate + subd HORBEG ; calculate difference + bra L9714 ; turn into absolute value +; PCOPY command +PCOPY bsr L973F ; fetch address of the source page + pshs d ; save address + ldb #0xa5 ; make sure we have TO + jsr LB26F + bsr L973F ; fetch address of the second page + puls x ; get back source + tfr d,u ; put destination into a pointer + ldy #0x300 ; 0x300 words to copy +L9736 ldd ,x++ ; copy a word + std ,u++ + leay -1,y ; done? + bne L9736 ; brif not + rts +L973F jsr EVALEXPB ; evaluate page number + tstb ; zero? + beq L9752 ; brif invalid page number +; BUG: this should be deferred until after the address is calculated at which point it should +; be bhs instead of bhi. There should also be a check to make sure the page number is less than +; or equal to 8 above so we don't have to test for overflows below. + cmpb TXTTAB ; is page number higher than start of program (BUG!) + bhi L9752 ; brif so - error + decb ; zero-base the page number + lda #6 ; 6 "pages" per graphics page + mul ; now we have proper number of "pages" for the offset + addb GRPRAM ; add start of graphics memory + exg a,b ; put MSB into A, 0 into B. +L9751 rts +L9752 jmp LB44A ; raise illegal function call +; GET command +GET clrb ; GET flag + bra L975A ; go on to the main body +PUT ldb #1 ; PUT flag +L975A stb VD8 ; save GET/PUT flag + jsr RVEC22 ; do the RAM hook dance (so Disk Basic can do its thing) +L975F cmpa #'@ ; @ before coordinates? + bne L9765 ; brif not + jsr GETNCH ; eat the @ +L9765 jsr L938F ; evaluate start/end coordinates + jsr SYNCOMMA ; make sure we have a comma + jsr L98CC ; get pointer to array + tfr X,D ; save descriptor pointer + ldu ,x ; get offset to next descriptor + leau -2,u ; move back to array name + leau d,u ; point to end of array + stu VD1 ; save end of data + leax 2,x ; point to number of dimensions + ldb ,x ; get dimension count + aslb ; two bytes per dimension size + abx ; now X points to start of data + stx VCF ; save start of array data + lda VALTYP ; is it numeric + bne L9752 ; brif not + clr VD4 ; set default graphic action to PSET + jsr GETCCH ; get input character + beq L97B7 ; brif no action flag + com VD4 ; flag action enabled + jsr SYNCOMMA ; make sure there's a comma + tst VD8 ; PUT? + bne L979A ; brif so + ldb #'G ; check for full graphics option + jsr LB26F + bra L97CA ; handle the rest of the process +L979A ldb #5 ; 5 legal actions for PUT + ldx #L9839 ; point to action table +L979F ldu ,x++ ; get "clear bit" action routine + ldy ,x++ ; get "set bit" action routine + cmpa ,x+ ; does token match? + beq L97AE ; brif so + decb ; checked all? + bne L979F ; brif not + jmp LB277 ; raise error +L97AE sty VD5 ; save set bit action address + stu VD9 ; save clear bit action address + jsr GETNCH ; munch the acton token + bra L97CA ; handle rest of process +L97B7 ldb #0xf8 ; mask for bottom three bits + lda PMODE ; get graphics mode + rora ; odd number mode? + bcc L97C0 ; brif even + ldb #0xfc ; bottom 2 bits mask +L97C0 tfr b,a ; save mask + andb HORBEG+1 ; round down the start address + stb HORBEG+1 + anda HOREND+1 ; round down end address + sta HOREND+1 +L97CA jsr L971D ; get horizontal size + bcc L97D3 ; brif end > start + ldx HOREND ; switch end in for start + stx HORBEG +L97D3 std HOREND ; save size + jsr L9710 ; calculate vertical size + bcc L97DE ; brif end > start + ldx VEREND ; swap in vertical end for the start + stx VERBEG +L97DE std VEREND ; save vertical size + lda PMODE ; get graphics mode + rora ; even? + ldd HOREND ; get difference + bcc L97EB ; brif even (2 colour) + addd HOREND ; add in size (double it) + std HOREND ; save adjusted end size +L97EB jsr L9420 ; normalize differences + ldd HOREND ; get end coord + ldx VEREND ; get end size + leax 1,x ; make vertical size inclusive + stx VEREND ; save it back + tst VD4 ; got "G" or GET action + bne L9852 ; brif given + lsra ; we're going for whole bytes here + rorb + lsra + rorb + lsra + rorb + addd #1 ; make it inclusive + std HOREND ; save new coordinate + jsr L9298 ; convert to screen address +L9808 ldb HOREND+1 ; get horizontal size + pshs x ; save screen position +L980C tst VD8 ; get/put flag + beq L9831 ; brif get + bsr L9823 ; bump array data pointer + lda ,u ; copy data from array to screen + sta ,x+ +L9816 decb ; are we done the row? + bne L980C ; brif not + puls x ; get screen address + jsr L92E9 ; move to next row + dec VEREND+1 ; done? + bne L9808 ; brif not +L9822 rts +L9823 ldu VCF ; get array data location + leau 1,u ; bump it + stu VCF ; save new array data location + cmpu VD1 ; did we hit the end of the array? + bne L9822 ; brif not +L982E jmp LB44A ; raise function call error +L9831 lda ,x+ ; get data from screen + bsr L9823 ; bump array data pointer + sta ,u ; put data in array + bra L9816 ; do the loopy thing +; PUT actions +L9839 fdb L9894,L989B ; PSET + fcb 0xbd + fdb L989B,L9894 ; PRESET + fcb 0xbe + fdb L98B1,L989B ; OR + fcb 0xb1 + fdb L9894,L98B1 ; AND + fcb 0xb0 + fdb L98A1,L98A1 ; NOT + fcb 0xa8 +L9852 addd #1 ; add to horiz difference + std HOREND ; save it + lda VD8 ; PUT? + bne L9864 ; brif so + ldu VD1 ; get end of array +L985D sta ,-u ; zero out a byte + cmpu VCF ; done? + bhi L985D ; brif not +L9864 jsr L9298 ; get screen address + ldb PMODE ; get graphics mode + rorb ; even? + bcc L986E ; brif so + anda #0xaa ; use as pixel mask for 4 colour mode +L986E ldb #1 ; set bit probe + ldy VCF ; point to start of array data +L9873 pshs x,a ; save screen address + ldu HOREND ; get horizontal size +L9877 pshs u,a ; save horizontal size and pixel mask + lsrb ; move bit probe right + bcc L9884 ; brif we didn't fall off a byte + rorb ; shift carry back in on the left + leay 1,y ; move ahead a byte in the array + cmpy VD1 ; end of array data? + beq L982E ; raise error if so +L9884 tst VD8 ; PUT? + beq L98A7 ; brif not + bitb ,y ; test bit in array + beq L9890 ; brif not set + jmp [VD5] ; do action routine for bit set +L9890 jmp [VD9] ; do action routine for bit clear +L9894 coma ; invert mask + anda ,x ; read screen data and reset the desired bit + sta ,x ; save on screen + bra L98B1 +L989B ora ,x ; merge pixel mask with screen data (turn on bit) + sta ,x ; save on screen + bra L98B1 +L98A1 eora ,x ; invert the pixel in the screen data + sta ,x ; save on screen + bra L98B1 +L98A7 bita ,x ; is the bit set? + beq L98B1 ; brif not - do nothing + tfr b,a ; get bit probe + ora ,y ; turn on proper bit in data + sta ,y +L98B1 puls a,u ; get back array address + jsr L92ED ; move screen address to the right + leau -1,u ; account for consumed pixel + cmpu ZERO ; done yet? + bne L9877 ; brif not + ldx 1,s ; get start of row back + lda HORBYT ; get number of bytes per row + leax a,x ; move ahead one line + puls a ; get back screen pixel mask + leas 2,s ; lose the screen pointer + dec VEREND+1 ; done all rows? + bne L9873 ; brif not + rts +L98CC jsr LB357 ; evaluate a variable + ldb ,-x ; get variable name + lda ,-x + tfr d,u ; save it + ldx ARYTAB ; get start of arrays +L98D7 cmpx ARYEND ; end of arrays? + lbeq LB44A ; brif not found + cmpu ,x ; correct variable? + beq L98E8 ; brif so + ldd 2,x ; get array size + leax d,x ; move to next array + bra L98D7 ; check this array +L98E8 leax 2,x ; move pointer to the array header + rts ; obviously this rts is not needed +L98EB rts +; PAINT command +PAINT cmpa #'@ ; do we have @ before coords? + bne L98F2 ; brif not + jsr GETNCH ; eat the @ +L98F2 jsr L93B2 ; evaluate coordinates + jsr L931D ; normalize coordinates + lda #1 ; PSET flag (use working colour) + sta SETFLG + jsr L9581 ; parse colour and set working colour, etc. + ldd WCOLOR ; get working colour and all pixels byte + pshs d ; save them + jsr GETCCH ; is there anything else? + beq L990A ; brif not + jsr L9581 ; evaluate border colour +L990A lda ALLCOL ; get border colour all pixel byte + sta VD8 ; save border colour pixel byte + puls d ; get back working colour details + std WCOLOR + clra ; store a "stop" frame on the stack so PAINT knows when it's finished unwinding + pshs u,x,b,a + jsr L9522 ; set up starting coordinates + jsr L928F ; point to pixel mask routine + stu VD9 ; save pixel mask routine + jsr L99DF ; paint from current horizontal coordinate to zero (left) + beq L9931 ; brif hit border immediately + jsr L99CB ; paint from current horizontal coordinate upward (right) + lda #1 ; set direction to "down" + sta VD7 + jsr L99BA ; save "down" frame + neg VD7 ; set direction to "up" + jsr L99BA ; save "up" frame +L9931 sts TMPSTK ; save stack pointer +L9934 tst CHGFLG ; did the paint change anything? + bne L993B ; brif so + lds TMPSTK ; get back stack pointer +L993B puls a,b,x,u ; get frame from stack + clr CHGFLG ; mark nothing changed + sts TMPSTK ; save stack pointer + leax 1,x ; move start coordinate right + stx HORBEG ; save new coordinate + stu VD1 ; save length of line + sta VD7 ; save up/down flag + beq L98EB ; did we hit the "stop" frame? + bmi L9954 ; brif negative going (up)? + incb ; bump vertical coordinate + cmpb VD6 ; at end? + bls L9958 ; brif not + clrb ; set vertical to 0 (wrap around) +L9954 tstb ; did we wrap? + beq L9934 ; do another block if so + decb ; move up a row +L9958 stb VERBEG+1 ; save vertical coordinate + jsr L99DF ; paint from horizontal to 0 + beq L996E ; brif we hit the border immediately + cmpd #3 ; less than 3 pixels? + blo L9969 ; brif so + leax -2,x ; move two pixels left + bsr L99A1 ; save paint block on the stack +L9969 jsr L99CB ; continue painting to the right +L996C bsr L99BA ; save paint data frame +L996E coma ; complement length of line just painted and add to length of line + comb +L9970 addd VD1 ; save difference between this line and parent line + std VD1 + ble L998C ; brif parent line is shorter + jsr L9506 ; bump horizontal coordinate + jsr L9A12 ; see if we bounced into the border + bne L9983 ; brif not border + ldd #-1 ; move left + bra L9970 ; keep looking +L9983 jsr L9514 ; move horizontally left + bsr L99C6 ; save horizontal coordinate + bsr L99E8 ; paint right + bra L996C ; save paint block and do more +L998C jsr L9506 ; bump horizontal coordinate + leax d,x ; point to right end of parent line + stx HORBEG ; set as curent coordinate + coma ; get amount we extend past parent line + comb + subd #1 + ble L999E ; brif doesn't extend + tfr d,x ; save length of line + bsr L99A1 ; save paint frame +L999E jmp L9934 +L99A1 std VCB ; save number of pixels painted + puls y ; get return address + ldd HORBEG ; get horizontal coordinate + pshs x,b,a ; save horizontal coordinate and pointer + lda VD7 ; get up/down flag + nega ; reverse it +L99AC ldb VERBEG+1 ; get vertical coordainte + pshs b,a ; save vertical coord and up/down flag + pshs y ; put return address back + ldb #2 ; make sure we haven't overflowed memory + jsr LAC33 + ldd VCB ; get line length back + rts +L99BA std VCB ; save length of painted line + puls y ; get return address + ldd HOREND ; get start coord + pshs x,b,a ; save horizontal start and length + lda VD7 ; get up/down flag + bra L99AC ; finish up with the stack +L99C6 ldx HORBEG ; save current horizontal coord and save it + stx HOREND + rts +L99CB std VCD ; save number of pixels painted + ldy HOREND ; get last horizontal start + bsr L99C6 ; save current coordinate + sty HORBEG ; save coordinate + bsr L99E8 ; paint a line + ldx VCD ; get number painted + leax d,x ; add to the number painted going the other way + addd #1 ; now D is length of line + rts +L99DF jsr L99C6 ; put starting coordinate in end + ldy #L9514 ; decrement horizontal coordinate address + bra L99EE ; go paint line +L99E8 ldy #L9506 ; increment horizontal coordinate address + jsr ,y ; bump coordinate +L99EE ldu ZERO ; initialize pixel count + ldx HORBEG ; get starting coordinate +L99F2 bmi L9A0B ; brif out of range for hoizontal coordinate + cmpx VD3 ; at end? + bhi L9A0B ; brif right of max + pshs u,y ; save counter and inc/dec routine pointer + bsr L9A12 ; at border? + beq L9A09 ; brif so + jsr L9377 ; set pixel to paint colour + puls y,u ; restore counter and inc/dec/pointer + leau 1,u ; bump number of painted pixels + jsr ,y ; inc/dec screen address + bra L99F2 ; go do another pixel +L9A09 puls y,u ; get back counter and inc/dec routine +L9A0B tfr u,d ; save count in D + tfr d,x ; and in X + subd ZERO ; set flags on D (smaller/faster than cmpd ZERO) + rts +L9A12 jsr [VD9] ; get the screen address + tfr a,b ; save pixel mask + andb VD8 ; set pixel to border colour + pshs b,a ; save mask and border + anda ,x ; mask current pixel into A + cmpa 1,s ; does it match border? Z=1 if so + puls a,b,pc ; restore mask, border pixel, and return +; PLAY command +; This is here mixed in with the graphics package because it shares some machinery with DRAW. +PLAY ldx ZERO ; default values for note length, etc. + ldb #1 + pshs x,b ; save default values + jsr LB156 ; evaluate argument + clrb ; enable DA and sound output + jsr LA9A2 + jsr LA976 +L9A32 jsr LB654 ; fetch PLAY string details + bra L9A39 ; go evaluate the string +L9A37 puls b,x ; get back play string details +L9A39 stb VD8 ; save length of string + beq L9A37 ; brif end of string + stx VD9 ; save start of string + lbeq LA974 ; brif NULL string - disable sound and return +L9A43 tst VD8 ; have anything left? + beq L9A37 ; brif not + jsr L9B98 ; get command character + cmpa #'; ; command separator? + beq L9A43 ; brif so - ignore it + cmpa #'' ; '? + beq L9A43 ; brif so - ignore it + cmpa #'X ; execuate sub string? + lbeq L9C0A ; brif so - handle it + bsr L9A5C ; handle other commands + bra L9A43 ; look for more stuff +L9A5C cmpa #'O ; octave? + bne L9A6D ; brif not + ldb OCTAVE ; get current octave + incb ; 1-base it + bsr L9AC0 ; get value if present + decb ; zero-base it + cmpb #4 ; valid octave? + bhi L9ACD ; raise error if not + stb OCTAVE ; save new octave + rts +L9A6D cmpa #'V ; volume? + bne L9A8B ; brif not + ldb VOLHI ; get current high volume limit + lsrb ; shift 2 bits right (DA is 6 bits in high bits) + lsrb + subb #31 ; subtract out mid value offset + bsr L9AC0 ; read argument + cmpb #31 ; maximum range is 31 + bhi L9ACD ; brif out of range + aslb ; adjust back in range + aslb + pshs b ; save new volume + ldd #0x7e7e ; midrange value for both high and low + adda ,s ; add new volume to high limit + subb ,s+ ; subtract volume from low limit + std VOLHI ; save new volume limits (sets high and low amplitudes) + rts +L9A8B cmpa #'L ; note length? + bne L9AB2 ; brif not + ldb NOTELN ; get current length + bsr L9AC0 ; read parameter + tstb ; resulting length 0? + beq L9ACD ; brif so - problem + stb NOTELN ; save new length + clr DOTVAL ; reset note timer scale factor +L9A9A bsr L9A9F ; check for dot + bcc L9A9A ; brif there was one + rts +L9A9F tst VD8 ; check length + beq L9AAD ; brif zero + jsr L9B98 ; get command character + cmpa #'. ; dot? + beq L9AAF ; brif so + jsr L9BE2 ; move input back and bump length +L9AAD coma ; set C to indicate nothing found + rts +L9AAF inc DOTVAL ; bump number of dots + rts +L9AB2 cmpa #'T ; tempo? + bne L9AC3 ; brif not + ldb TEMPO ; get current tempo + bsr L9AC0 ; parse tempo argument + tstb ; 0? + beq L9ACD ; brif so - invalid + stb TEMPO ; save new tempo + rts +L9AC0 jmp L9BAC ; evaluate various operators +L9AC3 cmpa #'P ; pause? + bne L9AEB ; brif not + jsr L9CCB ; evaluate parameter + tstb ; is the pause number 0? + bne L9AD0 ; brif not +L9ACD jmp LB44A ; raise FC error +L9AD0 lda DOTVAL ; save current volume and note scale + ldx VOLHI + pshs x,a + lda #0x7e ; drop DA to mid range + sta VOLHI + sta VOLLOW + clr DOTVAL + bsr L9AE7 ; go play a "silence" + puls a,x ; restore volume and note scale + sta DOTVAL + stx VOLHI + rts +L9AE7 clr ,-s ; set not number 0 + bra L9B2B ; go play it +L9AEB cmpa #'N ; N for "note"? + bne L9AF2 ; brif not - it's optional + jsr L9B98 ; skip the "N" +L9AF2 cmpa #'A ; is it a valid note? + blo L9AFA ; brif not + cmpa #'G ; is it above the note range? + bls L9AFF ; brif not - valid note +L9AFA jsr L9BBE ; evaluate a number + bra L9B22 ; process note value +L9AFF suba #'A ; normalize note number to 0 + ldx #L9C5B ; point to note number lookup table + ldb a,x ; get not number + tst VD8 ; any command characters left? + beq L9B22 ; brif not + jsr L9B98 ; get character + cmpa #'# ; sharp? + beq L9B15 ; brif so + cmpa #'+ ; also sharp? + bne L9B18 ; brif not +L9B15 incb ; add one half tone + bra L9B22 +L9B18 cmpa #'- ; flat? + bne L9B1F ; brif not + decb ; subtract one half tone + bra L9B22 +L9B1F jsr L9BE2 ; back up command pointer +L9B22 decb ; adjust note number (zero base it) + cmpb #11 ; is it valid? + bhi L9ACD ; raise error if not + pshs b ; save note value + ldb NOTELN ; get note length +L9B2B lda TEMPO ; get tempo value + mul ; calculate note duration + std VD5 ; save duration + leau 1,s ; point to where the stack goes after we're done + lda OCTAVE ; get current octave + cmpa #1 ; 0 or 1? + bhi L9B64 ; brif not + ldx #L9C62 ; point to delay table + ldb #2*12 ; 24 bytes per octave + mul ; now we have the base address + abx ; now X points to the octave base + puls b ; get back note value + aslb ; two bytes per delay + abx ; now we're pointing to the delay + leay ,x ; save pointer to note value + bsr L9B8C ; calculate note timer value + std PLYTMR ; set timer for note playing (IRQ will count this down) +L9B49 bsr L9B57 ; set to mid range and delay + lda VOLHI ; get high value + bsr L9B5A ; set to high value and delay + bsr L9B57 ; set to mid range and delay + lda VOLLOW ; get low value + bsr L9B5A ; set to low value and delay + bra L9B49 ; do it again (IRQ will break the loop) +L9B57 lda #0x7e ; mid value for DA with RS232 marking + nop ; a delay to fine tune frequencies +L9B5A sta PIA1 ; set DA + ldx ,y ; get delay value +L9B5F leax -1,x ; count down + bne L9B5F ; brif not done yet + rts +L9B64 ldx #L9C92-2*12 ; point to delay table for octaves 2+ + ldb #12 ; 12 bytes per octave + mul ; now we have the offset to the desired octave + abx ; now we point to the start of the octave + puls b ; get back note value + abx ; now we point to the delay value + bsr L9B8C ; calculate timer value + std PLYTMR ; set play timer (IRQ counts this down) +L9B72 bsr L9B80 ; send mid value and delay + lda VOLHI ; get high value + bsr L9B83 ; send high value and delay + bsr L9B80 ; send low value and delay + lda VOLLOW ; get low value + bsr L9B83 ; send low value and delay + bra L9B72 ; do it again (IRQ will break the loop) +L9B80 lda #0x7e ; mid range value with RS232 marking + nop ; fine tuning delay +L9B83 sta PIA1 ; set DA + lda ,x ; get delay value +L9B88 deca ; count down + bne L9B88 ; brif not done + rts +L9B8C ldb #0xff ; base timer value + lda DOTVAL ; get number of dots + beq L9B97 ; use default value if 0 + adda #2 ; add in constant timer factor + mul ; multiply scale by base + lsra ; divide by two - each increment will increase note timer by 128 + rorb +L9B97 rts +L9B98 pshs x ; save register +L9B9A tst VD8 ; do we have anything left? + beq L9BEB ; brif not - raise error + ldx VD9 ; get parsing address + lda ,x+ ; get character + stx VD9 ; save pointer + dec VD8 ; account for character consumed + cmpa #0x20 ; space? + beq L9B9A ; brif so - skip it + puls x,pc ; restore register and return +L9BAC bsr L9B98 ; get character + cmpa #'+ ; add one? + beq L9BEE ; brif so + cmpa #'- ; subtract one? + beq L9BF2 ; brif so + cmpa #'> ; double? + beq L9BFC ; brif so + cmpa #'< ; halve? + beq L9BF7 ; brif so +L9BBE cmpa #'= ; variable equate? + beq L9C01 ; brif so + jsr L90AA ; clear carry if numeric + bcs L9BEB ; brif not numeric + clrb ; initialize value to 0 +L9BC8 suba #'0 ; remove ASCII bias + sta VD7 ; save digit + lda #10 ; make room for digit + mul + tsta ; did we overflow 8 bits? + bne L9BEB ; brif so + addb VD7 ; add in digit + bcs L9BEB ; brif that overflowed + tst VD8 ; more digits? + beq L9BF1 ; brif not + jsr L9B98 ; get character + jsr L90AA ; clear carry if numeric + bcc L9BC8 ; brif another digit +L9BE2 inc VD8 ; unaccount for character just read + ldx VD9 ; move pointer back + leax -1,x + stx VD9 + rts +L9BEB jmp LB44A ; raise FC error +L9BEE incb ; bump param + beq L9BEB ; brif overflow +L9BF1 rts +L9BF2 tstb ; already zero? + beq L9BEB ; brif so - underflow + decb ; decrease parameter + rts +L9BF7 tstb ; already at 0? + beq L9BEB ; brif so - raise error + lsrb ; halve it + rts +L9BFC tstb ; will it overflow? + bmi L9BEB ; brif so + aslb ; double it + rts +L9C01 pshs u,y ; save registers + bsr L9C1B ; interpret command string as a variable + jsr LB70E ; convert it to an 8 bit number + puls y,u,pc ; restore registers and return +L9C0A jsr L9C1B ; evaluate expression in command string + ldb #2 ; room for 4 bytes? + jsr LAC33 + ldb VD8 ; get the command length and pointer + ldx VD9 + pshs x,b ; save them + jmp L9A32 ; go process the sub string +L9C1B ldx VD9 ; get command pointer + pshs x ; save it + jsr L9B98 ; get input character + jsr LB3A2 ; set carry if not alpha + bcs L9BEB ; brif not a variable reference +L9C27 jsr L9B98 ; get command character + cmpa #'; ; semicolon? + bne L9C27 ; keep scanning if not + puls x ; get back start of variable string + ldu CHARAD ; get current interpreter input pointer + pshs u ; save it + stx CHARAD ; point interpreter at command string + jsr LB284 ; evaluate expression as string + puls x ; restore interpeter input pointer + stx CHARAD + rts +; The bit of Extended Basic's IRQ routine that handles PLAY. Note that everything after +; clr PLYTMR+1 could be replaced with a simple lds 8,s followed by rts. +L9C3E clra ; make sure DP is set to 0 + tfr a,dp + ldd PLYTMR ; is PLAY running? + lbeq LA9BB ; brif not - transfer control on the Color Basic's routine + subd VD5 ; subtract out the interval + std PLYTMR ; save new timer value + bhi L9C5A ; brif it isn't <= 0 + clr PLYTMR ; disable the timer + clr PLYTMR+1 + puls a ; get saved CC + lds 7,s ; set stack to saved U value + anda #0x7f ; clear E flag (to return minimal state) + pshs a ; set fake "FIRQ" stack frame +L9C5A rti +L9C5B fcb 10,12,1,3,5,6,8 ; table of half tone numbers for notes A-G +L9C62 fdb 0x01a8,0x0190,0x017a,0x0164 ; delays for octave 1 + fdb 0x0150,0x013d,0x012b,0x011a + fdb 0x010a,0x00fb,0x00ed,0x00df + fdb 0x00d3,0x00c7,0x00bb,0x00b1 ; delays for octave 2 + fdb 0x00a6,0x009d,0x0094,0x008b + fdb 0x0083,0x007c,0x0075,0x006e +L9C92 fcb 0xa6,0x9c,0x93,0x8b ; delays for octave 3 + fcb 0x83,0x7b,0x74,0x6d + fcb 0x67,0x61,0x5b,0x56 + fcb 0x51,0x4c,0x47,0x43 ; delays for octave 4 + fcb 0x3f,0x3b,0x37,0x34 + fcb 0x31,0x2e,0x2b,0x28 + fcb 0x26,0x23,0x21,0x1f ; delays for octave 5 + fcb 0x1d,0x1b,0x19,0x18 + fcb 0x16,0x14,0x13,0x12 +; DRAW command +DRAW ldx ZERO ; create an empty "DRAW" frame + ldb #1 + pshs x,b + stb SETFLG ; set to "PSET" mode + stx VD5 ; clear update and draw flag + jsr L959A ; set active colour byte + jsr LB156 ; evaluate command string +L9CC6 jsr LB654 ; fetch command string details + bra L9CD3 ; interpret the command string +L9CCB jsr L9B98 ; fetch command character + jmp L9BBE ; evaluate a number +L9CD1 puls b,x ; get previously saved command string +L9CD3 stb VD8 ; save length counter + beq L9CD1 ; brif end of string + stx VD9 ; save pointer + lbeq L9DC7 ; brif overall end of command +L9CDD tst VD8 ; are we at the end of the string? + beq L9CD1 ; brif so - return to previous string + jsr L9B98 ; get command character + cmpa #'; ; semicolon? + beq L9CDD ; brif so - ignore it + cmpa #'' ; '? + beq L9CDD ; brif so - ignore that too + cmpa #'N ; update position toggle? + bne L9CF4 ; brif not + com VD5 ; toggle update position flag + bra L9CDD ; get on for another command +L9CF4 cmpa #'B ; blank flag? + bne L9CFC ; brif not + com VD6 ; toggle blank flag + bra L9CDD ; get on for another command +L9CFC cmpa #'X ; substring? + lbeq L9D98 ; brif so - execute command + cmpa #'M ; move draw position? + lbeq L9E32 ; brif so + pshs a ; save command character + ldb #1 ; default value if no number follows + tst VD8 ; is there something there? + beq L9D21 ; brif not + jsr L9B98 ; get character + jsr LB3A2 ; set C if not alpha + pshs cc ; save alpha state + jsr L9BE2 ; move back pointer + puls cc ; get back alpha flag + bcc L9D21 ; brif it's alpha + bsr L9CCB ; evaluate a number +L9D21 puls a ; get command back + cmpa #'C ; color change? + beq L9D4F ; brif so + cmpa #'A ; angle? + beq L9D59 ; brif so + cmpa #'S ; scale? + beq L9D61 ; brif so + cmpa #'U ; up? + beq L9D8F ; brif so + cmpa #'D ; down? + beq L9D8C ; brif so + cmpa #'L ; left? + beq L9D87 ; brif so + cmpa #'R ; right? + beq L9D82 ; brif so + suba #'E ; normalize the half cardinals to 0 + beq L9D72 ; brif E (45°) + deca ; F (135°?) + beq L9D6D ; brif so + deca ; G (225°?) + beq L9D7B ; brif so + deca ; H (315°?) + beq L9D69 ; brif so +L9D4C jmp LB44A ; raise FC error +L9D4F jsr L955D ; adjust colour for PMODE + stb FORCOL ; save new foreground colour + jsr L959A ; set up working colour and all pixels byte +L9D57 bra L9CDD ; go process another command +L9D59 cmpb #4 ; only 3 angles are valid + bhs L9D4C ; brif not valid + stb ANGLE ; save new angle + bra L9D57 ; go process another command +L9D61 cmpb #63 ; only 64 scale values are possible + bhs L9D4C ; brif out of range + stb SCALE ; save new scale factor + bra L9D57 ; go process another command +L9D69 clra ; make horizontal negative + bsr L9DC4 + skip1 +L9D6D clra ; keep horizontal distance positive + tfr d,x ; make horizontal distance and vertical distance the same + bra L9DCB ; go do the draw thing +L9D72 clra ; zero extend horizontal distance + tfr d,x ; set it as vertical + bsr L9DC4 ; negate horizontal distance + exg d,x ; swap directions (vertical is now negative) + bra L9DCB ; go do the draw thing +L9D7B clra ; zero extend horizontal distance + tfr d,x ; copy horizontal to vertical + bsr L9DC4 ; negate horizontal + bra L9DCB ; go do the drawing thing +L9D82 clra ; zero extend horizontal distance +L9DB3 ldx ZERO ; no vertical distance + bra L9DCB ; go do the drawing things +L9D87 clra ; zero extend horizontal + bsr L9DC4 ; negate horizontal + bra L9DB3 ; zero out vertical and do the drawing thing +L9D8C clra ; zero extend distance + bra L9D92 ; make the distance vertical and zero out horizontal +L9D8F clra ; zero extend distance + bsr L9DC4 ; negate distance +L9D92 ldx ZERO ; zero out vertical distance + exg x,d ; swap vertical and horizontal + bra L9DCB ; go do the drawing thing +L9D98 jsr L9C1B ; evaluate substring expression + ldb #2 ; is there enough room for the state? + jsr LAC33 + ldb VD8 ; save current command string state + ldx VD9 + pshs x,b + jmp L9CC6 ; go evaluate the sub string +L9DA9 ldb SCALE ; get scale factor + beq L9DC8 ; brif zero - default to full size + clra ; zero extend + exg d,x ; put distance somewhere useful + sta ,-s ; save MS of distance + bpl L9DB6 ; brif positive distance + bsr L9DC3 ; negate the distance +L9DB6 jsr L9FB5 ; multiply D and X + tfr u,d ; save ms bytes in D + lsra ; divide by 2 + rorb +L9DBD lsra ; ...divide by 4 + rorb + tst ,s+ ; negative distance? + bpl L9DC7 ; brif it was positive +L9DC3 nega ; negate D +L9DC4 negb + sbca #0 +L9DC7 rts +L9DC8 tfr x,d ; copy unchanged sitance to D + rts +L9DCB pshs b,a ; save horizontal distance + bsr L9DA9 ; apply scale factor to vertical + puls x ; get horizontal distance + pshs b,a ; save scaled vertical + bsr L9DA9 ; apply scale to horizontal + puls x ; get back vertical distance + ldy ANGLE ; get draw angle and scale + pshs y ; save them +L9DDC tst ,s ; is there an angle? + beq L9DE8 ; brif no angle + exg x,d ; swap distances + bsr L9DC3 ; negate D + dec ,s ; account for one tick around the rotation + bra L9DDC ; see if we're there yet +L9DE8 puls y ; get angle and scale back + ldu ZERO ; default end position (horizontal) is 0 + addd HORDEF ; add default horizontal to horizontal distance + bmi L9DF2 ; brif we went negative + tfr d,u ; save calculated end coordindate +L9DF2 tfr x,d ; get vertical distance somewhere useful + ldx ZERO ; default vertical end is 0 + addd VERDEF ; add distance to default vertical start + bmi L9DFC ; brif negative - use 0 + tfr d,x ; save calculated end coordinate +L9DFC cmpu #256 ; is horizontal in range? + blo L9E05 ; brif su + ldu #255 ; maximize it +L9E05 cmpx #192 ; is vertical in range? + blo L9E0D ; brif so + ldx #191 ; maximize it +L9E0D ldd HORDEF ; set starting coordinates for the line + std HORBEG + ldd VERDEF + std VERBEG + stx VEREND ; set end coordinates + stu HOREND + tst VD5 ; are we updating position? + bne L9E21 ; brif not + stx VERDEF ; update default coordinates + stu HORDEF +L9E21 jsr L9420 ; normalize coordindates + tst VD6 ; are we drawing something? + bne L9E2B ; brif not + jsr L94A1 ; draw the line +L9E2B clr VD5 ; reset draw and update flags + clr VD6 + jmp L9CDD ; do another command +L9E32 jsr L9B98 ; get a command character + pshs a ; save it + jsr L9E5E ; evaluate horizontal distance + pshs b,a ; save it + jsr L9B98 ; get character + cmpa #', ; comma between coordinates? + lbne L9D4C ; brif not - raise error + jsr L9E5B ; evaluate vertical distance + tfr d,x ; save vertical distance + puls u ; get horizontal distance + puls a ; get back first command character + cmpa #'+ ; was it + at start? + beq L9E56 ; brif +; treat values as positive + cmpa #'- ; was it -? + bne L9DFC ; brif not - treat it as absolute +L9E56 tfr u,d ; put horizontal distance somewhere useful + jmp L9DCB ; move draw position (relative) +L9E5B jsr L9B98 ; get input character +L9E5E cmpa #'+ ; leading +? + beq L9E69 ; brif so + cmpa #'- ; leading -? + beq L9E6A ; brif so - negative + jsr L9BE2 ; move pointer back one +L9E69 clra ; 0 for +, nonzero for - +L9E6A pshs a ; save sign flag + jsr L9CCB ; evaluate number + puls a ; get sign flag + tsta ; negative? + beq L9E78 ; brif not + clra ; zero extend and negate + negb + sbca #0 +L9E78 rts +; Table of sines and cosines for CIRCLE +L9E79 fdb 0x0000,0x0001 ; subarc 0 + fdb 0xfec5,0x1919 ; subarc 1 + fdb 0xfb16,0x31f2 ; subarc 2 + fdb 0xf4fb,0x4a51 ; subarc 3 + fdb 0xec84,0x61f9 ; subarc 4 + fdb 0xe1c7,0x78ae ; subarc 5 + fdb 0xd4dc,0x8e3b ; subarc 6 + fdb 0xc5e5,0xa269 ; subarc 7 + fdb 0xb506,0xb506 ; subarc 8 +; CIRCLE command +; The circle is drawn as a 64 sided polygon (64 LINE commands essentially) +CIRCLE cmpa #'@ ; is there an @ before coordinates? + bne L9EA3 ; brif not + jsr GETNCH ; eat the @ +L9EA3 jsr L9522 ; get max coordinates for screen + jsr L93B2 ; parse coordinates for circle centre + jsr L931D ; normalize the start coordinates + ldx ,u ; get horizontal coordinate + stx VCB ; save it + ldx 2,u ; get vertical coordinate + stx VCD ; saveit + jsr SYNCOMMA ; make sure we have a comma + jsr LB73D ; evaluate radius expression + ldu #VCF ; point to temp storage + stx ,u ; save radius + jsr L9320 ; normalize radius + lda #1 ; default to PSET + sta SETFLG + jsr L9581 ; evaluate the colour expression + ldx #0x100 ; height/width default value + jsr GETCCH ; is there a ratio? + beq L9EDF ; brif not + jsr SYNCOMMA ; make sure we have a comma + jsr LB141 ; evaluate the ratio + lda FP0EXP ; multiply ratio by 256 + adda #8 + sta FP0EXP + jsr LB740 ; evaluate ratio to X (fraction part in LSB) +L9EDF lda PMODE ; get graphics mode + bita #2 ; is it even? + beq L9EE9 ; brif so + tfr x,d ; double the ratio + leax d,x +L9EE9 stx VD1 ; save height/width ratio + ldb #1 ; set the SET flag to PSET + stb SETFLG + stb VD8 ; set first time flag (set to 0 after arc drawn) + jsr L9FE2 ; evaluate circle starting point (octant, subarc) + pshs b,a ; save startpoint + jsr L9FE2 ; evaluate circle end point (octant, subarc) + std VD9 ; save endp oint + puls a,b +L9EFD pshs b,a ; save current circle position + ldx HOREND ; move end coordinates to start coordinates + stx HORBEG + ldx VEREND + stx VERBEG + ldu #L9E79+2 ; point to sine/cosine table + anda #1 ; even octant? + beq L9F11 ; brif so + negb ; convert 0-7 to 8-1 for odd octants + addb #8 +L9F11 aslb ; four bytes per table entry + aslb + leau b,u ; point to correct table entry + pshs u ; save sine/cosine table entry pointer + jsr L9FA7 ; calculate horizontal offset + puls u ; get back table entry pointer + leau -2,u ; move to cosine entry + pshs x ; save horizontal offset + jsr L9FA7 ; calculate vertical offset + puls y ; put horizontal in Y + lda ,s ; get octant number + anda #3 ; is it 0 or 4? + beq L9F31 ; brif so + cmpa #3 ; is it 3 or 7? + beq L9F31 ; brif so + exg x,y ; swap horizontal and vertical +L9F31 stx HOREND ; save horizontal offset + tfr y,x ; put vertical offset in X + ldd VD1 ; get height/width ratio + jsr L9FB5 ; multiply vertical by h/w ratio + tfr y,d ; save the product to D + tsta ; did it overflow? + lbne LB44A ; brif so + stb VEREND ; save vertical coordinate MSB + tfr u,d ; get LSW of product + sta VEREND+1 ; save LSB of integer part (we have 8 bits of fraction in the ratio) + lda ,s ; get octant + cmpa #2 ; is it 0 or 1? + blo L9F5B ; brif so + cmpa #6 ; is it 6 or 7? + bhs L9F5B ; brif so + ldd VCB ; get horizontal centre + subd HOREND ; subtract horizontal displacement + bcc L9F68 ; brif we didn't overflow the screen + clra ; zero out coordinate if we overflowed the screen + clrb + bra L9F68 +L9F5B ldd VCB ; get horizontal coordinate of the centre + addd HOREND ; add displacement + bcs L9F66 ; brif overlod + cmpd VD3 ; larger than max horizontal coord? + blo L9F68 ; brif not +L9F66 ldd VD3 ; maximize the coordinate +L9F68 std HOREND ; save horizontal ending coordainte + lda ,s ; get octant + cmpa #4 ; is it 0-3? + blo L9F7A ; brif so + ldd VCD ; get vertical coordinate of centre + subd VEREND ; subtract displacement + bcc L9F87 ; brif we didn't overflow the screen + clra ; minimize to top of screen + clrb + bra L9F87 +L9F7A ldd VCD ; get vertical centre coordinate + addd VEREND ; add displacement + bcs L9F85 ; brif we overflowed the screen + cmpd VD5 ; did we go past max coordinate? + blo L9F87 ; brif not +L9F85 ldd VD5 ; maximize the coordinate +L9F87 std VEREND ; save end coordinate + tst VD8 ; check first time flag + bne L9F8F ; do not draw if first time through (it was setting start coord) + bsr L9FDF ; draw the line +L9F8F puls a,b ; get arc number and sub arc + lsr VD8 ; get first time flag value (and clear it!) + bcs L9F9A ; do not check for end point after drawing for first coordinate + cmpd VD9 ; at end point? + beq L9FA6 ; brif drawing finished +L9F9A incb ; bump arc counter + cmpb #8 ; done 8 arcs? + bne L9FA3 ; brif not + inca ; bump octant + clrb ; reset subarc number + anda #7 ; make sure octant number stays in 0-7 range +L9FA3 jmp L9EFD ; go do another arc +L9FA6 rts +L9FA7 ldx VCF ; get radius + ldd ,u ; get sine/cosine table entry + beq L9FB4 ; brif 0 - offset = radius + subd #1 ; adjust values to correct range + bsr L9FB5 ; multiply radius by sine/cosine + tfr y,x ; resturn result in X +L9FB4 rts +L9FB5 pshs u,y,x,b,a ; save registers and reserve temp space + clr 4,s ; reset overflow byte (YH) + lda 3,s ; calcuate B*XL + mul + std 6,s ; put in "U" + ldd 1,s ; calculate B*XH + mul + addb 6,s ; accumluate with previous product + adca #0 + std 5,s ; save in YL,UH + ldb ,s ; calculate A*XL + lda 3,s + mul + addd 5,s ; accumulate with previous partical product + std 5,s ; save in YL,UH + bcc L9FD4 ; brif no carry + inc 4,s ; bump YH for carry +L9FD4 lda ,s ; calculate A*XH + ldb 2,s + mul + addd 4,s ; accumulate with previous partial product + std 4,s ; save in Y (we can't have a carry here) + puls a,b,x,y,u,pc ; restore multiplicands and return results +L9FDF jmp L94A1 ; go draw a line +L9FE2 clrb ; default arc number (0) + jsr GETCCH ; is there something there for a value? + beq L9FF8 ; brif not + jsr SYNCOMMA ; evaluate , + expression + jsr LB141 + lda FP0EXP ; multiply by 64 + adda #6 + sta FP0EXP + jsr LB70E ; get integer value of circle fraction + andb #0x3f ; max value of 63 +L9FF8 tfr b,a ; save arc value in A to calculate octant + andb #7 ; calculate subarc + lsra ; calculate octant + lsra + lsra + rts diff -r 000000000000 -r 605ff82c4618 exbas11.s --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exbas11.s Sat Dec 08 19:57:01 2018 -0700 @@ -0,0 +1,4039 @@ + *pragma nolist + include defs.s +; These are the entry points in the Color Basic ROM which are used the the Extended Basic ROM. +; They are included here in order to keep the Extended Basic ROM separate from the Color Basic +; ROM. +BAWMST EQU 0xA0E8 +CLOAD EQU 0xA498 +CSAVE EQU 0xA44C +DATA EQU 0xAEE0 +EVALEXPB EQU 0xB70B +GIVABF EQU 0xB4F4 +INT EQU 0xBCEE +LA0E2 EQU 0xA0E2 +LA171 EQU 0xA171 +LA176 EQU 0xA176 +LA35F EQU 0xA35F +LA3ED EQU 0xA3ED +LA406 EQU 0xA406 +LA429 EQU 0xA429 +LA42D EQU 0xA42D +LA444 EQU 0xA444 +LA491 EQU 0xA491 +LA505 EQU 0xA505 +LA578 EQU 0xA578 +LA59A EQU 0xA59A +LA5A5 EQU 0xA5A5 +LA5AE EQU 0xA5AE +LA5C7 EQU 0xA5C7 +LA5E4 EQU 0xA5E4 +LA616 EQU 0xA616 +LA619 EQU 0xA619 +LA635 EQU 0xA635 +LA644 EQU 0xA644 +LA648 EQU 0xA648 +LA65F EQU 0xA65F +LA7E9 EQU 0xA7E9 +LA974 EQU 0xA974 +LA976 EQU 0xA976 +LA9A2 EQU 0xA9A2 +LA9BB EQU 0xA9BB +LAC1E EQU 0xAC1E +LAC33 EQU 0xAC33 +LAC46 EQU 0xAC46 +LAC60 EQU 0xAC60 +LAC73 EQU 0xAC73 +LAC7C EQU 0xAC7C +LAC9D EQU 0xAC9D +LACA8 EQU 0xACA8 +LACEF EQU 0xACEF +LACF1 EQU 0xACF1 +LAD01 EQU 0xAD01 +LAD19 EQU 0xAD19 +LAD21 EQU 0xAD21 +LAD26 EQU 0xAD26 +LAD33 EQU 0xAD33 +LAD9E EQU 0xAD9E +LADC6 EQU 0xADC6 +LADD4 EQU 0xADD4 +LADEB EQU 0xADEB +LAE15 EQU 0xAE15 +LAED2 EQU 0xAED2 +LAF67 EQU 0xAF67 +LAFA4 EQU 0xAFA4 +LB035 EQU 0xB035 +LB141 EQU 0xB141 +LB143 EQU 0xB143 +LB146 EQU 0xB146 +LB156 EQU 0xB156 +LB158 EQU 0xB158 +LB244 EQU 0xB244 +LB262 EQU 0xB262 +LB267 EQU 0xB267 +LB26A EQU 0xB26A +LB26F EQU 0xB26F +LB277 EQU 0xB277 +LB284 EQU 0xB284 +LB2CE EQU 0xB2CE +LB357 EQU 0xB357 +LB35C EQU 0xB35C +LB3A2 EQU 0xB3A2 +LB44A EQU 0xB44A +LB4F3 EQU 0xB4F3 +LB50F EQU 0xB50F +LB518 EQU 0xB518 +LB51A EQU 0xB51A +LB56D EQU 0xB56D +LB643 EQU 0xB643 +LB654 EQU 0xB654 +LB657 EQU 0xB657 +LB659 EQU 0xB659 +LB69B EQU 0xB69B +LB6A4 EQU 0xB6A4 +LB6AD EQU 0xB6AD +LB70E EQU 0xB70E +LB734 EQU 0xB734 +LB738 EQU 0xB738 +LB73D EQU 0xB73D +LB740 EQU 0xB740 +LB7C2 EQU 0xB7C2 +LB958 EQU 0xB958 +LB95C EQU 0xB95C +LB99F EQU 0xB99F +LB9AC EQU 0xB9AC +LB9AF EQU 0xB9AF +LB9B4 EQU 0xB9B4 +LB9B9 EQU 0xB9B9 +LB9C2 EQU 0xB9C2 +LBA1C EQU 0xBA1C +LBA3A EQU 0xBA3A +LBA92 EQU 0xBA92 +LBAC5 EQU 0xBAC5 +LBACA EQU 0xBACA +LBB48 EQU 0xBB48 +LBB5C EQU 0xBB5C +LBB6A EQU 0xBB6A +LBB82 EQU 0xBB82 +LBB8F EQU 0xBB8F +LBC14 EQU 0xBC14 +LBC2F EQU 0xBC2F +LBC35 EQU 0xBC35 +LBC4C EQU 0xBC4C +LBC5F EQU 0xBC5F +LBC6D EQU 0xBC6D +LBCA0 EQU 0xBCA0 +LBCC8 EQU 0xBCC8 +LBD99 EQU 0xBD99 +LBDB6 EQU 0xBDB6 +LBDBB EQU 0xBDBB +LBDC0 EQU 0xBDC0 +LBDC5 EQU 0xBDC5 +LBDCC EQU 0xBDCC +LBDD9 EQU 0xBDD9 +LBEC0 EQU 0xBEC0 +LBEC5 EQU 0xBEC5 +LBEE9 EQU 0xBEE9 +LBEF0 EQU 0xBEF0 +LBEFF EQU 0xBEFF +LBFA6 EQU 0xBFA6 +LET EQU 0xAF89 +PUTCHR EQU 0xA282 +SIN EQU 0xBF78 +SNDBLK EQU 0xA7F4 +STRINOUT EQU 0xB99C +SYNCOMMA EQU 0xB26D +WRLDR EQU 0xA7D8 + *pragma list +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; EXTENDED COLOR BASIC ROM +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + org EXBAS + fcc 'EX' ; magic number that Color Basic uses to identify the presence of Extended Basic +L8002 ldx #L80DE ; point to command interpretation table information + ldu #COMVEC+10 ; point to command interpretation table location + ldb #10 ; 10 bytes to move + jsr LA59A ; copy command interpretation table + ldx #LB277 ; initialize Disk Basic's entries to error + stx 3,u + stx 8,u + ldx #XIRQSV ; set up IRQ service routine + stx IRQVEC+1 + ldx ZERO ; reset the TIMER value + stx TIMVAL + jsr XVEC18 ; do a bunch of initialization + ldd #0x2c05 ; initialize DLOAD baud rate constant and timeout + std DLBAUD + ldx #USR0 ; set up pointer to USR routine addresses + stx USRADR + ldu #LB44A ; set up USR routine addresses to "FC error" + ldb #10 ; there are 10 routines +L8031 stu ,x++ ; set a routine to FC error + decb ; done all? + bne L8031 ; brif not + lda #0x7e ; op code of JMP extended (for RAM hook intialization) + sta RVEC20 ; command interpretation loop + ldx #XVEC20 + stx RVEC20+1 + sta RVEC15 ; expression evaluation + ldx #XVEC15 + stx RVEC15+1 + sta RVEC19 ; number parsing + ldx #XVEC19 + stx RVEC19+1 + sta RVEC9 ; PRINT + ldx #XVEC9 + stx RVEC9+1 + sta RVEC17 ; error handler + ldx #XVEC17 + stx RVEC17+1 + sta RVEC4 ; generic input + ldx #XVEC4 + stx RVEC4+1 + sta RVEC3 ; generic output + ldx #XVEC3 + stx RVEC3+1 + sta RVEC8 ; close file + ldx #XVEC8 + stx RVEC8+1 + sta RVEC23 ; tokenize line + ldx #XVEC23 + stx RVEC23+1 + sta RVEC18 ; RUN + ldx #XVEC18 + stx RVEC18+1 + sta EXPJMP ; exponentiation + ldx #L8489 + stx EXPJMP+1 + jsr L96E6 ; initialize graphics stuff + lda PIA0+3 ; enable 60Hz interrupt + ora #1 + sta PIA0+3 + ldx #'D*256+'K ; magic number for a Disk Basic ROM + cmpx DOSBAS ; do we have a Disk Basic ROM? + lbeq DOSBAS+2 ; brif so - launch it + andcc #0xaf ; enable interrupts + ldx #L80E8-1 ; show sign on message + jsr STRINOUT + ldx #XBWMST ; install warm start handler + stx RSTVEC + jmp LA0E2 ; set up warm start flag and launch immediate mode +; Extended Basic warm start code +XBWMST nop ; flag to mark routine as valid + clr PLYTMR ; cancel any PLAY command in progress + clr PLYTMR+1 + lda PIA0+3 ; enable 60Hz interrupt + ora #1 + sta PIA0+3 + jmp BAWMST ; let Color Basic's warm start process run +; This code is to fix the famous PCLEAR bug. It replaces dead code in the 1.0 ROM. This patch corrects +; the input pointer so that it points to the correct place after the program has been relocated by +; PCLEAR instead of continuing with something that, in the best case, is a syntax error. +L80D0 lda CURLIN ; immediate mode? + inca + beq L80DD ; brif so + tfr y,d ; save offset to D + subd TXTTAB ; see how far into the program we are + addd CHARAD ; now adjust the input pointer based on that + std CHARAD ; save corrected input pointer +L80DD rts +L80DE fcb 25 ; 25 Extended Basic commands + fdb L8183 ; reserved word table (commands) + fdb L813C ; interpretation handler (commands) + fcb 14 ; 14 Extended Basic functions + fdb L821E ; reserved word table (functions) + fdb L8168 ; function handler +L80E8 fcc 'EXTENDED COLOR BASIC 1.1' + fcb 0x0d + fcc 'COPYRIGHT (C) 1982 BY TANDY' + fcb 0x0d + fcc 'UNDER LICENSE FROM MICROSOFT' + fcb 0x0d,0x0d,0x00 +; Extended Basic command interpretation loop +L813C cmpa #0xcb ; is it an Extended Basic command? + bhi L8148 ; brif not + ldx #L81F0 ; point to dispatch table + suba #0xb5 ; normalize the token number so 0 is the first entry + jmp LADD4 ; go transfer control to the command +L8148 cmpa #0xff ; is it a function token? + beq L8154 ; brif so - for MID$()=, TIMER= + cmpa #0xcd ; is it a token for a keyword that isn't a command? + bls L8165 ; brif so - error for USING and FN + jmp [COMVEC+23] ; transfer control to Disk Basic if it is present +L8154 jsr GETNCH ; get token after the function flag + cmpa #0x90 ; MID$? + lbeq L86D6 ; brif so (substring replacement) + cmpa #0x9f ; TIMER? + lbeq L8960 ; brif so - TIMER setting + jsr RVEC22 ; do a RAM hook in case something wants to extend this +L8165 jmp LB277 ; we have nothing valid here +; Function handler +L8168 cmpb #2*33 ; is it a valid Extended Basic function? + bls L8170 ; brif so + jmp [COMVEC+28] ; transfer control to Disk Basic if it is present +L8170 subb #2*20 ; normalize Extended Basic functions to 0 + cmpb #2*8 ; Above HEX$? + bhi L817D ; brif so - we don't pre-evaluate an argument + pshs b ; save token value + jsr LB262 ; evaluate the function parameter + puls b ; get back token value +L817D ldx #L8257 ; point to dispatch table + jmp LB2CE ; go transfer control to the function +; Reserved words (commands) +L8183 fcs 'DEL' ; 0xb5 + fcs 'EDIT' ; 0xb6 + fcs 'TRON' ; 0xb7 + fcs 'TROFF' ; 0xb8 + fcs 'DEF' ; 0xb9 + fcs 'LET' ; 0xba + fcs 'LINE' ; 0xbb + fcs 'PCLS' ; 0xbc + fcs 'PSET' ; 0xbd + fcs 'PRESET' ; 0xbe + fcs 'SCREEN' ; 0xbf + fcs 'PCLEAR' ; 0xc0 + fcs 'COLOR' ; 0xc1 + fcs 'CIRCLE' ; 0xc2 + fcs 'PAINT' ; 0xc3 + fcs 'GET' ; 0xc4 + fcs 'PUT' ; 0xc5 + fcs 'DRAW' ; 0xc6 + fcs 'PCOPY' ; 0xc7 + fcs 'PMODE' ; 0xc8 + fcs 'PLAY' ; 0xc9 + fcs 'DLOAD' ; 0xca + fcs 'RENUM' ; 0xcb + fcs 'FN' ; 0xcc + fcs 'USING' ; 0xcd +; Dispatch table (commands) +L81F0 fdb DEL ; 0xb5 DEL + fdb EDIT ; 0xb6 EDIT + fdb TRON ; 0xb7 TRON + fdb TROFF ; 0xb8 TROFF + fdb DEF ; 0xb9 DEF + fdb LET ; 0xba LET (note: implemented by Color Basic!) + fdb LINE ; 0xbb LINE + fdb PCLS ; 0xbc PCLS + fdb PSET ; 0xbd PSET + fdb PRESET ; 0xbe PRESET + fdb SCREEN ; 0xbf SCREEN + fdb PCLEAR ; 0xc0 PCLEAR + fdb COLOR ; 0xc1 COLOR + fdb CIRCLE ; 0xc2 CIRCLE + fdb PAINT ; 0xc3 PAINT + fdb GET ; 0xc4 GET + fdb PUT ; 0xc5 PUT + fdb DRAW ; 0xc6 DRAW + fdb PCOPY ; 0xc7 PCOPY + fdb PMODETOK ; 0xc8 PMODE + fdb PLAY ; 0xc9 PLAY + fdb DLOAD ; 0xca DLOAD + fdb RENUM ; 0xcb RENUM +; Reserved words (functions) +L821E fcs 'ATN' ; 0x94 + fcs 'COS' ; 0x95 + fcs 'TAN' ; 0x96 + fcs 'EXP' ; 0x97 + fcs 'FIX' ; 0x98 + fcs 'LOG' ; 0x99 + fcs 'POS' ; 0x9a + fcs 'SQR' ; 0x9b + fcs 'HEX$' ; 0x9c + fcs 'VARPTR' ; 0x9d + fcs 'INSTR' ; 0x9e + fcs 'TIMER' ; 0x9f + fcs 'PPOINT' ; 0xa0 + fcs 'STRING$' ; 0xa1 +; Dispatch table (functions) +L8257 fdb ATN ; 0x94 ATN + fdb COS ; 0x95 COS + fdb TAN ; 0x96 TAN + fdb EXP ; 0x97 EXP + fdb FIX ; 0x98 FIX + fdb LOG ; 0x99 LOG + fdb POS ; 0x9a POS + fdb SQR ; 0x9b SQR + fdb HEXDOL ; 0x9c HEX$ + fdb VARPTRTOK ; 0x9d VARPTR + fdb INSTR ; 0x9e INSTR + fdb TIMER ; 0x9f TIMER + fdb PPOINT ; 0xa0 PPOINT + fdb STRING ; 0xa1 STRING$ +; Generic output handler +XVEC3 tst DEVNUM ; screen? + lbeq L95AC ; brif so - force text screen active + pshs b ; save register + ldb DEVNUM ; get output device + cmpb #-3 ; check for DLOAD + puls b ; restore register + bne L8285 ; brif not DLOAD + leas 2,s ; bail out of output handler if DLOAD +L8285 rts +; Close file handler. This corrects a bug in Color Basic 1.0 which didn't handle writing the +; end of file block correctly. That bug is fixed in Color Basic 1.1 so this isn't required +; if a recent enough version of Color Basic is installed. +XVEC8 lda DEVNUM ; get device number + inca ; is it tape? + bne L8285 ; brif not - we aren't going to mess with it + lda FILSTA ; get tape file status + cmpa #2 ; output file? + bne L8285 ; brif not + lda CINCTR ; is there anything waiting to be written out? + bne L8285 ; brif so - mainline code will handle it properly + clr DEVNUM ; reset output to screen + leas 2,s ; don't return to mainline code + jmp LA444 ; write EOF block +; RUN handler - sets up some Extended Basic stuff to defaults at program start +XVEC18 ldd #0xba42 ; initialize PLAY volume + std VOLHI + lda #2 ; set PLAY tempo to 2, PLAY octave to 3 + sta TEMPO + sta OCTAVE + asla ; set default note length to 5 + sta NOTELN + clr DOTVAL ; don't do any note length extension + ldd ZERO ; initialize DRAW angle and scale to default 1 + std ANGLE + ldb #128 ; initialize horizontal and vertical default coordinates to the middle of the screen + std HORDEF + ldb #96 + std VERDEF + rts +; Command interpretation loop handler; we need to intercept this to implement TRON/TROFF +XVEC20 leas 2,s ; don't return to the mainline code +L82BB andcc #0xaf ; make sure interrupts are running + jsr LADEB ; do a BREAK/pause check + ldx CHARAD ; save input pointer + stx TINPTR + lda ,x+ ; get current input character + beq L82CF ; brif end of line + cmpa #': ; statement separator? + beq L82F1 ; brif so + jmp LB277 ; raise error we got here with extra junk +L82CF lda ,x++ ; get first byte of next line address + sta ENDFLG ; use it to set "END" flag to "END" + bne L82D8 ; brif not end of program + jmp LAE15 ; go do the "END" +L82D8 ldd ,x+ ; get line number of next line (and leave pointer one before line text) + std CURLIN ; set current line number + stx CHARAD ; save input pointer + lda TRCFLG ; are we tracing? + beq L82F1 ; brif not + lda #'[ ; show opening marker for TRON line number + jsr PUTCHR + lda CURLIN ; restore MSB of line number + jsr LBDCC ; show line number + lda #'] ; show closing marker for TRON line number + jsr PUTCHR +L82F1 jsr GETNCH ; get the start of the statement + tfr cc,b ; save status flags + cmpa #0x98 ; is it CSAVE? + beq L8316 ; brif so - go to Extended Basic patch (adds CSAVEM) + cmpa #0x97 ; is it CLOAD? + beq L8311 ; brif so - go to Extended Basic patch (adds multi-origin binaries) + tfr b,cc ; restore character status + jsr LADC6 ; go process command + bra L82BB ; restart interpretation loop +; Tokenizaton handler. This is actually a hack to intercept CLOAD and CSAVE during immediate mode by causing the +; tokenization routine to return to the interpretation loop above instead of the mainline interpretation loop. This +; is necessary because the first command encountered on a line in immediate mode is executed BEFORE the interpretation +; loop RAM hook is called. This patch doesn't actually affect tokenization itself at all. +XVEC23 ldx 2,s ; get return address of caller to the tokenizer + cmpx #LAC9D ; is it coming from immediate mode prior to executing the line? + bne L8310 ; brif not + ldx #L82F1 ; force return to Extended Basic's main loop patch above + stx 2,s +L8310 rts +; These two patches are set up this way so that control can be transferred back to the original Color Basic +; implementations if the Extended Basic addons are not triggered. +L8311 jsr L8C62 ; transfer control to Extended Basic's CLOAD handler + bra L82BB ; go do another command +L8316 bsr L831A ; go do Extended Basic's CSAVE handler + bra L82BB ; go do another command +; Extended Basic's CSAVE handler which implements CSAVEM (which Color Basic does not have) +L831A jsr GETNCH ; get character after CSAVE + cmpa #'M ; is it CSAVEM? + lbne CSAVE ; brif not - Color Basic can handle this + jsr GETNCH ; eat the "M" + jsr LA578 ; parse file name + bsr L836C ; get start address + stx CASBUF+13 ; save it in file header + bsr L836C ; get end address + cmpx 2,s ; compare to start address + lblo LB44A ; brif end address is before the start address + bsr L836C ; get execution address + stx CASBUF+11 ; put in file header + jsr GETCCH ; are we at the end of the commmand? + bne L8310 ; brif not + lda #2 ; file type to machine language + ldx ZERO ; set to binary and single block + jsr LA65F ; write header + clr FILSTA ; mark any open tape file closed + inc BLKTYP ; set block type to data + jsr WRLDR ; write a data leader + ldx 4,s ; get starting address +L834D stx CBUFAD ; set start of data address + lda #255 ; try a full length block by default + sta BLKLEN + ldd 2,s ; get ending address + subd CBUFAD ; see how much is left + bhs L835E ; brif we have more to write + leas 6,s ; clean up stack + jmp LA491 ; write EOF block +L835E cmpd #255 ; do we have a full block left? + bhs L8367 ; brif so + incb ; set block size to remainder + stb BLKLEN +L8367 jsr SNDBLK ; write a data block + bra L834D ; go see if we have more to write +L836C jsr SYNCOMMA ; make sure we have a comma + jsr LB73D ; evaluate unsigned expression to X + ldu ,s ; get return address + stx ,s ; save result on stack + tfr u,pc ; return to caller +; COS function +COS ldx #L83AB ; point to PI/2 constant + jsr LB9C2 ; add to argument ( cos(x) = sin((pi/2)+x) ) +L837E jmp SIN ; now calculate sin((pi/2)+x) +; TAN function. This is determined by the identity TAN(X) = SIN(X)/COS(X) +TAN jsr LBC2F ; save FPA0 in FPA3 + clr RELFLG ; reset quadrant flag + bsr L837E ; calculate SIN(x) + ldx #V4A ; save result in FPA5 + jsr LBC35 + ldx #V40 ; get back original argument + jsr LBC14 + clr FP0SGN ; force result positive + lda RELFLG ; get quadrant flag + bsr L83A6 ; calculate COS(x) + tst FP0EXP ; did we get 0 for COS(x) + lbeq LBA92 ; brif so - overflow + ldx #V4A ; point to sin(x) +L83A3 jmp LBB8F ; divide the sin(x) value by the cos(x) value +L83A6 pshs a ; save sign flag + jmp LBFA6 ; expand polynomial +L83AB fcb 0x81,0x49,0x0f,0xda,0xa2 ; pi/2 constant +; ATN function (inverse tangent). There are two calculation streams used to improve precision. +; One if the parameter is >= 1.0 and the other if it is < 1.0 +ATN lda FP0SGN ; get sign of argument + pshs a ; save it + bpl L83B8 ; brif positive + bsr L83DC ; flip sign of argument +L83B8 lda FP0EXP ; get exponent + pshs a ; save it + cmpa #0x81 ; exponent for 1.0 + blo L83C5 ; brif less - value is less than 1.0 + ldx #LBAC5 ; point to FP constant 1.0 + bsr L83A3 ; calculate reciprocal +L83C5 ldx #L83E0 ; point to polynomical coefficients + jsr LBEF0 ; expand polynomial + puls a ; get exponent of argument + cmpa #0x81 ; did we do a reciprocal calculation? + blo L83D7 ; brif not + ldx #L83AB ; subtract result from pi/2 if we did + jsr LB9B9 +L83D7 puls a ; get sign of original + tsta ; was it positive? + bpl L83DF ; brif so +L83DC jmp LBEE9 ; flip sign of result +L83DF rts +; Chebyshev modified taylor series coefficients for inverse tangent. Note that these diverge quite significantly +; from the standard taylor series after 1/9. The standard coefficients are 1/1,-1/3, 1/5, -1/7, 1/9, -1/11, and +; so on. These modified coefficients yield reasonable accuracy for the precision available, presumably with +; fewer coefficients. +L83E0 fcb 11 ; 12 coefficients + fcb 0x76,0xb3,0x83,0xbd,0xd3 ; -0.000684793912 + fcb 0x79,0x1e,0xf4,0xa6,0xf5 ; 0.00485094216 + fcb 0x7b,0x83,0xfc,0xb0,0x10 ; -0.0161117018 + fcb 0x7c,0x0c,0x1f,0x67,0xca ; 0.0342096381 + fcb 0x7c,0xde,0x53,0xcb,0xc1 ; -0.0542791328 + fcb 0x7d,0x14,0x64,0x70,0x4c ; 0.0724571965 + fcb 0x7d,0xb7,0xea,0x51,0x7a ; -0.0898023954 + fcb 0x7d,0x63,0x30,0x88,0x7e ; 0.110932413 + fcb 0x7e,0x92,0x44,0x99,0x3a ; -0.142839808 + fcb 0x7e,0x4c,0xcc,0x91,0xc7 ; 0.199999121 + fcb 0x7f,0xaa,0xaa,0xaa,0x13 ; -0.333333316 + fcb 0x81,0x00,0x00,0x00,0x00 ; 1.0 +; Chebyshev modified taylor series coefficients for the ln(x)/ln(2) (base 2 log of x) +L841D fcb 3 ; four coefficients + fcb 0x7f,0x5e,0x56,0xcb,0x79 ; 0.434255942 from (2/7)/ln(2) + fcb 0x80,0x13,0x9b,0x0b,0x64 ; 0.576584541 from (2/5)/ln(2) + fcb 0x80,0x76,0x38,0x93,0x16 ; 0.961800759 from (2/3)/ln(2) + fcb 0x82,0x38,0xaa,0x3b,0x20 ; 2.88539007 from 2/ln(2) +L8432 fcb 0x80,0x35,0x04,0xf3,0x34 ; 1/sqrt(2) +L8437 fcb 0x81,0x35,0x04,0xf3,0x34 ; sqrt(2) +L843C fcb 0x80,0x80,0x00,0x00,0x00 ; -0.5 +L8441 fcb 0x80,0x31,0x72,0x17,0xf8 ; ln(2) +; LOG function (natural log, ln) +; FP representation is of the form A*2^B. Thus, the log routine determines the value of +; ln(A*2^B). +; +; ln(A*2^B) = ln(A) + ln(2^B) = ln(A) + B*ln(2) = (ln(A)/ln(2) + B)*ln(2), OR: +; (log2(A) + B)*ln(2) +; Now if we write A as A*(sqrt(2)/sqrt(2)), we haven't changed anything so: +; (log2(A*sqrt(2)/sqrt(2)) + B)*ln(2) = (log2(A*sqrt(2)) - log2(sqrt(2)) + B) * ln(2) +; which gives: (-1/2 + log2(A*sqrt(2)) + B) * ln(2) +; +; Everything except log2(A*sqrt(2)) is either constant or trivial. +; +; What the actual code below feeds into the modified taylor series is actually: +; 1 - (sqrt(2)/(A + sqrt(2)) which algebra reveals to be (A*sqrt(2)-1)/(A*sqrt(2)+1) +; +; Thus, the code is using (A*sqrt(2)-1)/(A*sqrt(2)+1) instead of A*sqrt(2) as one would +; expect from the identities. However, the modified coefficients in the series above +; could be correcting for that or the introduced error was deemed acceptable. +; NOTE: this routine does NOT return 0 for LOG(1) +LOG jsr LBC6D ; get status of FPA0 + lble LB44A ; brif <= 0 - logarithms don't exist in that case + ldx #L8432 ; point to 1/sqrt(2) + lda FP0EXP ; get exponent of argument + suba #0x80 ; remove bias + pshs a ; save it for later + lda #0x80 ; force exponent to 0 (this turns FPA0 into A in the above description) + sta FP0EXP + jsr LB9C2 ; add 1/sqrt(2) to A + ldx #L8437 ; point to sqrt(2) + jsr LBB8F ; divide that by FPA0 + ldx #LBAC5 ; point to 1.0 + jsr LB9B9 ; subtract result from 1.0: Now FPA0 is 1-(sqrt(2)/(A+1/sqrt(2))) + ldx #L841D ; point to coefficients + jsr LBEF0 ; expand polynomial (calculate base 2 log of modified argument) + ldx #L843C ; point to -0.5 + jsr LB9C2 ; add result + puls b ; get original exponent back + jsr LBD99 ; add B to FPA0 + ldx #L8441 ; point to ln(2) + jmp LBACA ; multiply by ln(2) which gives us the result in base e +; SQR function (square root) - returns the principle root (positive) +SQR jsr LBC5F ; move argument to FPA1 (first argument for exponentiation) + ldx #LBEC0 ; point to 0.5 (exponent for square root) + jsr LBC14 ; set up second argument to exponentiation (the exponent) +; Exponentiation operator +; This is calculated as A^x = e^(x*ln(A)) where A is in FPA1 and x is in FPA0 +L8489 beq EXP ; do "EXP" if exponent is 0 (this will catch 0^0) + tsta ; check that the base is not 0 + bne L8491 ; brif base is not 0 + jmp LBA3A ; 0^(nonzero) is 0 +L8491 ldx #V4A ; save exponent (to FPA5) + jsr LBC35 + clrb ; result sign will default to positive + lda FP1SGN ; check if base is positive + bpl L84AC ; brif so + jsr INT ; convert exponent to integer + ldx #V4A ; point to original expoent + lda FP1SGN ; get sign of FPA1 + jsr LBCA0 ; compare original exponent with truncated one + bne L84AC ; brif not equal + coma ; flip sign + ldb CHARAC ; get LS byte of integer exponent (result sign flag) +L84AC jsr LBC4C ; copy FPA1 (base) to FPA0 (A = sign) + pshs b ; save result sign + jsr LOG ; get natural log of the base + ldx #V4A ; multiply the log by the exponent + jsr LBACA + bsr EXP ; now raise e to the resulting power + puls a ; get result sign + rora ; brif it was negative + lbcs LBEE9 ; brif negative - flip sign + rts +L84C4 fcb 0x81,0x38,0xaa,0x3b,0x29 ; 1.44269504 (correction factor for exponential function) +; Chebyshev modified taylor series coefficients for e^x +L84C9 fcb 7 ; eight coefficients + fcb 0x71,0x34,0x58,0x3e,0x56 ; 1/(7!*(CF^7)) + fcb 0x74,0x16,0x7e,0xb3,0x1b ; 1/(6!*(CF^6)) + fcb 0x77,0x2f,0xee,0xe3,0x85 ; 1/(5!*(CF^5)) + fcb 0x7a,0x1d,0x84,0x1c,0x2a ; 1/(4!*(CF^4)) + fcb 0x7c,0x63,0x59,0x58,0x0a ; 1/(3!*(CF^3)) + fcb 0x7e,0x75,0xfd,0xe7,0xc6 ; 1/(2!*(CF^2)) + fcb 0x80,0x31,0x72,0x18,0x10 ; 1/(1!*(CF^1)) + fcb 0x81,0x00,0x00,0x00,0x00 ; 1 +; EXP function (e^x) +EXP ldx #L84C4 ; point to correction factor + jsr LBACA ; multiply it + jsr LBC2F ; save corrected argument to FPA3 + lda FP0EXP ; get exponent of FPA0 + cmpa #0x88 ; is it too big? + blo L8504 ; brif not +L8501 jmp LBB5C ; to 0 (underflow) or overflow error +L8504 jsr INT ; convert argument to an integer + lda CHARAC ; get ls byte of integer + adda #0x81 ; was argument 127? if so, the OV error; adds bias + beq L8501 + deca ; adjust for the extra +1 above + pshs a ; save integer exponent + ldx #V40 ; get fractional part of argument + jsr LB9B9 + ldx #L84C9 ; point to coefficients + jsr LBEFF ; evaluate polynomial on the fractional part + clr RESSGN ; force result to be positive + puls a ; get back original exponent + jsr LBB48 ; add original exponent to the fractional result + rts +; FIX function (truncate/round toward 0) (NOTE: INT() always rounds down so INT(-0.5) gives -1 but FIX(-0.5) gives 0) +FIX jsr LBC6D ; get status of argument + bmi L852C ; brif negative +L8529 jmp INT ; do regular "int" if positive +L852C com FP0SGN ; flip the sign + bsr L8529 ; do "INT" on this + jmp LBEE9 ; flip the sign back +; EDIT command +EDIT jsr L89AE ; get line number + leas 2,s ; we're not going to return to the main loop +L8538 lda #1 ; "LIST" flag + sta VD8 ; set to list the line + jsr LAD01 ; find line number + lbcs LAED2 ; brif line wasn't found + jsr LB7C2 ; go unpack the line into the buffer + tfr y,d ; calculate the actual length of the line + subd #LINBUF+2 + stb VD7 ; save line length (it will only be 8 bits) +L854D ldd BINVAL ; get the line number + jsr LBDCC ; display it + jsr LB9AC ; put a space after it + ldx #LINBUF+1 ; point to iput uffer + ldb VD8 ; are we listing? + bne L8581 ; brif so +L855C clrb ; reset digit accumulator +L855D jsr L8687 ; get a keypress + jsr L90AA ; set carry if not numeric + bcs L8570 ; brif not a number + suba #'0 ; remove ASCII bias + pshs a ; save digit value + lda #10 ; multiply accumulator by 10 + mul + addb ,s+ ; add in new digit + bra L855D ; go check for another digit +L8570 subb #1 ; this converts 0 to 1, but *only* 0 to 1 + adcb #1 + cmpa #'A ; abort? + bne L857D ; brif not + jsr LB958 ; to a CR + bra L8538 ; restart EDIT process +L857D cmpa #'L ; list? + bne L858C ; brif not +L8581 bsr L85B4 ; list the line + clr VD8 ; reset to "not listing" + jsr LB958 ; do a CR + bra L854D ; start editing +L858A leas 2,s ; lose return address +L858C cmpa #0x0d ; ENTER? + bne L859D ; brif not + bsr L85B4 ; echo out the line +L8592 jsr LB958 ; do a CR + ldx #LINBUF+1 ; reset input pointer to start of buffer + stx CHARAD + jmp LACA8 ; join immediate mode to replace the line in the program +L859D cmpa #'E ; exit? + beq L8592 ; brif so - end edit with no echo + cmpa #'Q ; quit? + bne L85AB ; brif not + jsr LB958 ; do a CR + jmp LAC73 ; go to immediate mode with no fanfare - no changes saved +L85AB bsr L85AF ; go do commands + bra L855C ; go handle another command +L85AF cmpa #0x20 ; space? + bne L85C3 ; brif not + skip2 +L85B4 ldb #LBUFMX-1 ; display up to a whole line +L85B6 lda ,x ; get buffer chracter + beq L85C2 ; brif end of line + jsr PUTCHR ; output character + leax 1,x ; move to next character + decb ; done? + bne L85B6 ; brif not +L85C2 rts +L85C3 cmpa #'D ; delete? + bne L860F ; brif not +L85C7 tst ,x ; end of line? + beq L85C2 ; brif so - can't delete + bsr L85D1 ; remove a character + decb ; done all requested? + bne L85C7 ; brif not + rts +L85D1 dec VD7 ; account for character being removed + leay -1,x ; set pointer and compensate for increment below +L85D5 leay 1,y ; move to next character + lda 1,y ; get next character + sta ,y ; move it forward + bne L85D5 ; brif we didn't hit the end of the buffer + rts +L85DE cmpa #'I ; insert? + beq L85F5 ; brif so + cmpa #'X ; extend? + beq L85F3 ; brif so + cmpa #'H ; "hack"? + bne L8646 ; brif not + clr ,x ; mark current location as end of line + tfr x,d ; calculate new line length + subd #LINBUF+2 + stb VD7 ; save new length +L85F3 bsr L85B4 ; display the line +L85F5 jsr L8687 ; read a character + cmpa #0x0d ; ENTER? + beq L858A ; brif so - terminate entry + cmpa #0x1b ; ESC? + beq L8625 ; brif so - back to command mode + cmpa #0x08 ; backspace? + bne L8626 ; brif no + cmpx #LINBUF+1 ; are we at the start of the buffer? + beq L85F5 ; brif so - it's a no-op + bsr L8650 ; move pointer back one, do a BS + bsr L85D1 ; remove character from the buffer + bra L85F5 ; go handle more input +L860F cmpa #'C ; change? + bne L85DE ; brif not +L8613 tst ,x ; is there something to change? + beq L8625 ; brif not + jsr L8687 ; get a key stroke + bcs L861E ; brif valid key + bra L8613 ; try again if invalid key +L861E sta ,x+ ; put new character in the buffer + bsr L8659 ; echo it + decb ; changed number requested? + bne L8613 ; brif not +L8625 rts +L8626 ldb VD7 ; get length of line + cmpb #LBUFMX-1 ; at maximum line length? + bne L862E ; brif not + bra L85F5 ; process another input character +L862E pshs x ; save input pointer +L8630 tst ,x+ ; are we at the end of the line? + bne L8630 ; brif not +L8634 ldb ,-x ; get character before current pointer, move back + stb 1,x ; move it forward + cmpx ,s ; at the original buffer pointer? + bne L8634 ; brif not + leas 2,s ; remove saved buffer pointer + sta ,x+ ; save input character in newly made hole + bsr L8659 ; echo it + inc VD7 ; bump line length counter + bra L85F5 ; go handle more stuff +L8646 cmpa #0x08 ; backspace? + bne L865C ; brif not +L864A bsr L8650 ; move pointer back, echo BS + decb ; done enough of them? + bne L864A ; brif not + rts +L8650 cmpx #LINBUF+1 ; at start of buffer? + beq L8625 ; brif so + leax -1,x ; move pointer back + lda #0x08 ; character to echo - BS +L8659 jmp PUTCHR ; echo character to screen +L865C cmpa #'K ; "kill"? + beq L8665 ; brif so + suba #'S ; search? + beq L8665 ; brif so + rts +L8665 pshs a ; save kill/search flag + bsr L8687 ; read target + pshs a ; save search character +L866B lda ,x ; get current character in buffer + beq L8685 ; brif end of line - nothing more to search + tst 1,s ; is it KILL? + bne L8679 ; brif so + bsr L8659 ; echo the character + leax 1,x ; move ahead + bra L867C ; check next character +L8679 jsr L85D1 ; remove character from buffer +L867C lda ,x ; get character in buffer + cmpa ,s ; are we at the target? + bne L866B ; brif not + decb ; have we found enough of them? + bne L866B ; brif not +L8685 puls y,pc ; clean up stack and return to main EDIT routine +L8687 jsr LA171 ; get input from the generic input handler (will show the cursor) + cmpa #0x7f ; graphics (or DEL)? + bhs L8687 ; brif so - ignore it + cmpa #0x5f ; SHIFT-UP? + bne L8694 ; brif not + lda #0x1b ; replace with ESC +L8694 cmpa #0x0d ; carriage return? + beq L86A6 ; brif so (C=0) + cmpa #0x1b ; ESC + beq L86A6 ; brif so (C=0) + cmpa #0x08 ; backspace? + beq L86A6 ; brif so (C=0) + cmpa #32 ; control code? + blo L8687 ; brif control code - try again + orcc #1 ; set C for "valid" (printable) character +L86A6 rts +; TRON and TROFF commands +TRON skip1lda ; load flag with nonzero for trace enabled (and skip next) +TROFF clra ; clear flag for trace disabled + sta TRCFLG ; save trace status + rts +; POS function +POS lda DEVNUM ; get original device number + pshs a ; save it for later + jsr LA5AE ; fetch device number + jsr LA406 ; check for open file + jsr LA35F ; set up print parameters + ldb DEVPOS ; get current line position for the device + jmp LA5E4 ; return position in B as unsigned +; VARPTR function +VARPTRTOK jsr LB26A ; make sure we have ( + ldd ARYEND ; get address of end of arrays + pshs d ; save it + jsr LB357 ; parse variable descriptor + jsr LB267 ; make sure there is a ) + puls d ; get original end of arrays + exg x,d ; swap original end of arrays and the discovered variable pointer + cmpx ARYEND ; did array end move (variable created?) + bne L8724 ; brif so (FC error) + jmp GIVABF ; return the pointer (NOTE: as signed) +; MID$() assignment; note that this cannot make the string longer or shorter and if the replacement is shorter +; than the specified size, only the number of characters actually in the replacement will be used. +L86D6 jsr GETNCH ; eat the MID$ token + jsr LB26A ; force ( + jsr LB357 ; evaluate the variable + pshs x ; save variable descriptor + ldd 2,x ; point to start of original string + cmpd FRETOP ; is it in string space? + bls L86EB ; brif not + subd MEMSIZ ; is it still in string space (top end)? + bls L86FD ; brif so +L86EB ldb ,x ; get length of original string + jsr LB56D ; allocate space in string space + pshs x ; save pointer to string space + ldx 2,s ; get to original string descriptor + jsr LB643 ; move the string into string space + puls x,u ; get new string address and string descriptor + stx 2,u ; save new data address for the string + pshs u ; save descriptor address again +L86FD jsr LB738 ; evaluate ",start" + pshs b ; save start offset + tstb ; is start 0? + beq L8724 ; brif so - strings offsets are 1-based + ldb #255 ; default use the entire string + cmpa #') ; end of parameters? + beq L870E ; brif so + jsr LB738 ; evaluate ",length" +L870E pshs b ; save length + jsr LB267 ; make sure we have a ) + ldb #0xb3 ; make sure we have = + jsr LB26F + bsr L8748 ; evaluate replacement string + tfr x,u ; save replacement string address + ldx 2,s ; get original string descriptor + lda ,x ; get length of original string + suba 1,s ; subtract start position + bhs L8727 ; brif within the string - insert replacement +L8724 jmp LB44A ; raise illegal function call +L8727 inca ; A is now number of characters to the right of the position parameter + cmpa ,s ; compare to length desired + bhs L872E ; brif new length fits + sta ,s ; only use as much of the length as will fit +L872E lda 1,s ; get position offset + exg a,b ; swap replacement length and position + ldx 2,x ; point to original string address + decb ; we work with 0-based offsets + abx ; now X points to start of replacement + tsta ; replacing 0? + beq L8746 ; brif so - done + cmpa ,s ; is replacement shorter than the hole? + bls L873F ; brif so + lda ,s ; use copy the maximum number specified +L873F tfr a,b ; save number to move in B + exg u,x ; swap pointers so they are right for the routine + jsr LA59A ; copy string data +L8746 puls a,b,x,pc ; clean up stack and return +L8748 jsr LB156 ; evaluate expression + jmp LB654 ; make sure it's a string and return string details +; STRING$ function +STRING jsr LB26A ; make sure we have ( + jsr EVALEXPB ; evaluate repeat count (error if > 255) + pshs b ; save repeat count + jsr SYNCOMMA ; make sure there's a comma + jsr LB156 ; evaluate the thing to repeat + jsr LB267 ; make sure we have a ) + lda VALTYP ; is it string? + bne L8768 ; brif so + jsr LB70E ; get 8 bit character code + bra L876B ; use that +L8768 jsr LB6A4 ; get first character of string +L876B pshs b ; save repeat character + ldb 1,s ; get repeat count + jsr LB50F ; reserve space for the string + puls a,b ; get character and repeat count + beq L877B ; brif NULL string +L8776 sta ,x+ ; put character into string + decb ; put enough? + bne L8776 ; brif not +L877B jmp LB69B ; return the newly created string +; INSTR function +INSTR jsr LB26A ; evaluate ( + jsr LB156 ; evaluate first argument + ldb #1 ; default start position is 1 (start of string) + pshs b ; save start position + lda VALTYP ; get type of first argument + bne L879C ; brif string - use default starting position + jsr LB70E ; convert first argument into string offset + stb ,s ; save offset + beq L8724 ; brif starting at 0 - not allowed + jsr SYNCOMMA ; make sure there's a comma + jsr LB156 ; evaluate the search string + jsr LB146 ; make sure it *is* a string +L879C ldx FPA0+2 ; get search string descriptor + pshs x ; save it + jsr SYNCOMMA ; make sure we have a comma + jsr L8748 ; evalute the target string + pshs x,b ; save address and length of target string + jsr LB267 ; make sure we have a ) + ldx 3,s ; get search string address + jsr LB659 ; get string details + pshs b ; save search string length + cmpb 6,s ; compare length of search string to the start + blo L87D9 ; brif start position is beyond the search string - return 0 + lda 1,s ; get length of target string + beq L87D6 ; brif targetstring is NULL - match will be immediate + ldb 6,s ; get start position + decb ; zero-base it + abx ; now X points to the start position for the search +L87BE leay ,x ; point to start of search + ldu 2,s ; get target string pointer + ldb 1,s ; get targetlength + lda ,s ; get length of serach + suba 6,s ; see how much is left in searh + inca ; add one for "inclusivity" + cmpa 1,s ; do we have less than the target string? + blo L87D9 ; brif so - we obviously won't match +L87CD lda ,x+ ; compare a byte + cmpa ,u+ + bne L87DF ; brif no match + decb ; compared all of target? + bne L87CD ; brif not +L87D6 ldb 6,s ; get position where we matched + skip1 +L87D9 clrb ; flag no match + leas 7,s ; clean up stack + jmp LB4F3 ; return unsigned B +L87DF inc 6,s ; bump start position + leax 1,y ; move starting pointer + bra L87BE ; see if we match now +; Number parsing handler +XVEC19 cmpa #'& ; do we have & (hex or octal)? + bne L8845 ; brif not + leas 2,s ; we won't return to the original invoker +L87EB clr FPA0+2 ; clear bottom of FPA0 for 16 bit value + clr FPA0+3 + ldx #FPA0+2 ; point to accumulator + jsr GETNCH ; eat the & + cmpa #'O ; octal? + beq L880A ; brif so + cmpa #'H ; hex? + beq L881F ; brif so + jsr GETCCH ; reset flags on input + bra L880C ; go process octal (default) +L8800 cmpa #'8 ; is it a valid octal character? + lbhi LB277 ; brif not (BUG: should be LBHS or above be #'7) + ldb #3 ; base 8 multiplier + bsr L8834 ; add digit to accumulator +L880A jsr GETNCH ; get input character +L880C bcs L8800 ; brif numeric +L880E clr FPA0 ; clear upper bytes of FPA0 + clr FPA0+1 + clr VALTYP ; result is numeric + clr FPSBYT ; clear out any extra precision + clr FP0SGN ; make it positive + ldb #0xa0 ; exponent for integer aligned to right of FPA0 + stb FP0EXP + jmp LBA1C ; go normalize the result and return +L881F jsr GETNCH ; get input character + bcs L882E ; brif digit + jsr LB3A2 ; set carry if not alpha +L8826 bcs L880E ; brif not alpha + cmpa #'G ; is it valid HEX digit? + bhs L880E ; brif not + suba #7 ; normalize A-F to be just above 0-9 +L882E ldb #4 ; four bits per digit + bsr L8834 ; add digit to accumlator + bra L881F ; process another digit +L8834 asl 1,x ; shift accumulator one bit left + rol ,x + lbcs LBA92 ; brif too big - overflow + decb ; done enough bit shifts? + bne L8834 ; brif not +L883F suba #'0 ; remove ASCII bias + adda 1,x ; merge digit into accumlator (this cannot cause carry) + sta 1,x +L8845 rts +; Expression evaluation handler +XVEC15 puls u ; get back return address + clr VALTYP ; set result to numeric + ldx CHARAD ; save input pointer + jsr GETNCH ; get the input character + cmpa #'& ; HEX or OCTAL? + beq L87EB ; brif so + cmpa #0xcc ; FN? + beq L88B4 ; brif so - do "FNx()" + cmpa #0xff ; function token? + bne L8862 ; brif not + jsr GETNCH ; get function token value + cmpa #0x83 ; USR? + lbeq L892C ; brif so - short circuit Color Basic's USR handler +L8862 stx CHARAD ; restore input pointer + jmp ,u ; return to mainline code +L8866 ldx CURLIN ; are we in immediate mode? + leax 1,x + bne L8845 ; brif not - we're good + ldb #2*11 ; code for illegal direct statement +L886E jmp LAC46 ; raise error +; DEF command (DEF FN, DEF USR) +DEF ldx [CHARAD] ; get two input characters + cmpx #0xff83 ; USR? + lbeq L890F ; brif so - do DEF USR + bsr L88A1 ; get descriptor address for FN variable + bsr L8866 ; disallow DEF FN in immediate mode + jsr LB26A ; make sure we have ( + ldb #0x80 ; disallow arrays as arguments + stb ARYDIS + jsr LB357 ; evaluate variable + bsr L88B1 ; make sure it's numeric + jsr LB267 ; make sure we have ) + ldb #0xb3 ; make sure we have = + jsr LB26F + ldx V4B ; get variable descriptor address + ldd CHARAD ; get input pointer + std ,x ; save address of the actual function code in variable descriptor + ldd VARPTR ; get descriptor address of argument + std 2,x ; save argument descriptor address + jmp DATA ; move to the end of this statement +L88A1 ldb #0xcc ; make sure we have FN + jsr LB26F + ldb #0x80 ; disable array lookup + stb ARYDIS + ora #0x80 ; set bit 7 of first character (to indicate FN variable) + jsr LB35C ; find the variable + stx V4B ; save descriptor pointer +L88B1 jmp LB143 ; make sure we have a numeric variable +; Evaluate an FN call +L88B4 bsr L88A1 ; parse the FNx bit and get the descriptor + pshs x ; save descriptor + jsr LB262 ; evaluate parameter + bsr L88B1 ; make sure it's a number + puls u ; get FN descriptor + ldb #2*25 ; code for undefined function + ldx 2,u ; point to argument variable descriptor + beq L886E ; brif nothing doing there (if it was just created, this will be NULL) + ldy CHARAD ; save current input pointer + ldu ,u ; point to start of FN definition + stu CHARAD ; put input pointer there + lda 4,x ; save original value of argument and save it with current input, and variable pointers + pshs a + ldd ,x + ldu 2,x + pshs u,y,x,d + jsr LBC35 ; set argument variable to the argument +L88D9 jsr LB141 ; go evaluate the FN expression + puls d,x,y,u ; get back variable pointers, input pointer, and original variable value + std ,x + stu 2,x + puls a + sta 4,x + jsr GETCCH ; test end of FN formula + lbne LB277 ; brif not end of statement - problem with the function + sty CHARAD ; restore input pointer +L88EF rts +; Error handler +XVEC17 cmpb #2*25 ; is it a valid Extended Basic error code? + blo L88EF ; brif not - return to mainline + jsr LA7E9 ; turn off tape + jsr LA974 ; turn off sound + jsr LAD33 ; clean up stack and other bits + clr DEVNUM ; reset output to screen + jsr LB95C ; do a newline if needed + jsr LB9AF ; do a ? + ldx #L890B-25*2 ; point to error message table + jmp LAC60 ; go display error message +; Extended Basic error codes. Yes, NE is defined here even though it is only actually documented in the +; Disk Basic documentation. It is here for the use of DLOAD. +L890B fcc 'UF' ; 25 undefined function call + fcc 'NE' ; 26 File not found +; DEF USR +L890F jsr GETNCH ; eat the USR token + bsr L891C ; get pointer to USR call + pshs x ; save FN exec address location + bsr L8944 ; calculate execution address + puls u ; get FN address pointer + stx ,u ; save new address + rts +L891C clrb ; default routine number is 0 + jsr GETNCH ; fetch the call number + bcc L8927 ; brif not a number + suba #'0 ; remove ASCII bias + tfr a,b ; save it in the right place + jsr GETNCH ; eat the call number +L8927 ldx USRADR ; get start address of USR jump table + aslb ; two bytes per address + abx ; now X points to the right entry + rts +; Evaluate a USR call +L892C bsr L891C ; find the correct routine address location + ldx ,x ; get routine address + pshs x ; save it + jsr LB262 ; evaluate argument + ldx #FP0EXP ; point to FPA0 (argument value) + lda VALTYP ; is it string? + beq L8943 ; brif not + jsr LB657 ; fetch string details (removes it from the string stack) + ldx FPA0+2 ; get string descriptor pointer + lda VALTYP ; set flags for the value type +L8943 rts ; call the routine and return to mainline code +L8944 ldb #0xb3 ; check for "=" + jsr LB26F + jmp LB73D ; evaluate integer expression to X and return +; Extended Basic IRQ handler +XIRQSV lda PIA0+3 ; is it VSYNC interrupt? + bmi L8952 ; brif so + rti ; really should clear the HSYNC interrupt here +L8952 lda PIA0+2 ; clear VSYNC interrupt + ldx TIMVAL ; increment the TIMER value + leax 1,x + stx TIMVAL + jmp L9C3E ; check for other stuff +; TIMER= +L8960 jsr GETNCH ; eat the TIMER token + bsr L8944 ; evaluate =nnnn to X + stx TIMVAL ; set the timer + rts +; TIMER function +TIMER ldx TIMVAL ; get timer value + stx FPA0+2 ; set it in FPA0 + jmp L880E ; return as positive 16 bit value +; DEL command +DEL lbeq LB44A ; raise error if no argument (probably to avoid accidentally deleting line 0) + jsr LAF67 ; parse line number + jsr LAD01 ; find line + stx VD3 ; save address of line + jsr GETCCH ; is there something more? + beq L8990 ; brif not + cmpa #0xac ; dash? + bne L89BF ; brif not - error out + jsr GETNCH ; each the - + beq L898C ; brif no ending line - use default line number + bsr L89AE ; parse second line number and save in BINVAL + bra L8990 ; do the deletion +L898C lda #0xff ; set to maximum line number + sta BINVAL +L8990 ldu VD3 ; point end to start + skip2 +L8993 ldu ,u ; point to start of next line + ldd ,u ; check for end of program + beq L899F ; brif end of program + ldd 2,u ; get line number + subd BINVAL ; is it in range? + bls L8993 ; brif so +L899F ldx VD3 ; get starting line address + bsr L89B8 ; close up gap + jsr LAD21 ; reset input pointer and erase variables + ldx VD3 ; get start of program after the deletion + jsr LACF1 ; recompute netl ine pointers + jmp LAC73 ; return to immediate mode +L89AE jsr LAF67 ; parse a line number + jmp LA5C7 ; make sure there's nothing more +L89B4 lda ,u+ ; copy a byte + sta ,x+ +L89B8 cmpu VARTAB ; end of program? + bne L89B4 ; brif not + stx VARTAB ; save new start of variables/end of program +L89BF rts +; LINE INPUT +L89C0 jsr L8866 ; raise error if in immediate mode + jsr GETNCH ; eat the "INPUT" token + cmpa #'# ; device number? + bne L89D2 ; brif not + jsr LA5A5 ; parse device number + jsr LA3ED ; make sure it's valid for input + jsr SYNCOMMA ; make sure there's a comma after the device number +L89D2 cmpa #'" ; is there a prompt? + bne L89E1 ; brif not + jsr LB244 ; parse the string + ldb #'; ; make sure there's a semicolon after the prompt + jsr LB26F + jsr LB99F ; go actually display the prompt +L89E1 leas -2,s ; make a hole on the stack (so number of return adddresses is right) + jsr LB035 ; read an input line from current device + leas 2,s ; clean up stack + clr DEVNUM ; reset to screen/keyboard + jsr LB357 ; parse a variable + stx VARDES ; save pointer to it + jsr LB146 ; make sure it's a string + ldx #LINBUF ; point to input buffer + clra ; make sure we terminate on NUL only + jsr LB51A ; parse string and store it in string space + jmp LAFA4 ; go assign the string to its final resting place +; RENUM command +L89FC jsr LAF67 ; read a line number + ldx BINVAL ; get value + rts +L8A02 ldx VD1 ; get current old number being renumbered +L8A04 stx BINVAL ; save number being searched for + jmp LAD01 ; go find line number +RENUM jsr LAD26 ; erase variables + ldd #10 ; default line number interval and start + std VD5 ; set starting line number + std VCF ; set number interval + clrb ; now D is 0 + std VD1 ; save default start for renumbering + jsr GETCCH ; are there any arguments + bcc L8A20 ; brif not numeric + bsr L89FC ; fetch line number + stx VD5 ; save line beginning number + jsr GETCCH ; get input character +L8A20 beq L8A3D ; brif end of line + jsr SYNCOMMA ; make sure we have a comma + bcc L8A2D ; brif next isn't numeric + bsr L89FC ; fetch starting line number + stx VD1 ; save the number where we start working + jsr GETCCH ; fetch input character +L8A2D beq L8A3D ; brif end of line + jsr SYNCOMMA ; make sure we have a comma + bcc L8A3A ; brif we don't have a number + bsr L89FC ; parse the number + stx VCF ; save interval + beq L8A83 ; brif we ave a zero interval +L8A3A jsr LA5C7 ; raise error if more stuff +L8A3D bsr L8A02 ; get address of old number to process + stx VD3 ; save address + ldx VD5 ; get the next renumbered line to use + bsr L8A04 ; find that line + cmpx VD3 ; is it before the previous one? + blo L8A83 ; brif so - raise error + bsr L8A67 ; make sure renumbered line numbers will be in range + jsr L8ADD ; convert line numbers to "expanded" binary + jsr LACEF ; recalculate next line pointers + bsr L8A02 ; get address of first line to renumber + stx VD3 ; save it + bsr L8A91 ; make sure line numbers exist + bsr L8A68 ; renumber the actual lines + bsr L8A91 ; update line numbers in program text + jsr L8B7B ; convert packed binary line numbers to text + jsr LAD26 ; erase variables, reset stack, etc. + jsr LACEF ; recalculate next line pointers + jmp LAC73 ; bounce back to immediate mode +L8A67 skip1lda ; set line number flag to nonzero (skip next instruction) +L8A68 clra ; set line number flag to zero (insert new numbers) + sta VD8 ; save line number flag + ldx VD3 ; get address of line being renumbered + ldd VD5 ; get the current renumbering number + bsr L8A86 ; return if end of program +L8A71 tst VD8 ; test line number flag + bne L8A77 ; brif not adding new numbers + std 2,x ; set new number +L8A77 ldx ,x ; point to next line + bsr L8A86 ; return if end of program + addd VCF ; add interval to current number + bcs L8A83 ; brif we overflowed - bad line number + cmpa #MAXLIN ; maximum legal number? + blo L8A71 ; brif so - do another +L8A83 jmp LB44A ; raise FC error +L8A86 pshs d ; save D (we're going to clobber it) + ldd ,x ; get next line pointer + puls d ; unclobber D + bne L8A90 ; brif not end of program + leas 2,s ; return to caller's caller +L8A90 rts +L8A91 ldx TXTTAB ; get start of program + leax -1,x ; move pointer back one (compensate for leax 1,x below) +L8A95 leax 1,x ; move to next line + bsr L8A86 ; return if end of program +L8A99 leax 3,x ; move past next line address and line number, go one before line +L8A9B leax 1,x ; move to next character + lda ,x ; check input character + beq L8A95 ; brif end of line + stx TEMPTR ; save current pointer + deca ; is it start of packed numeric line number? + beq L8AB2 ; brif so + deca ; does line exist? + beq L8AD3 ; brif line number exists + deca ; not part of something to process? + bne L8A9B ; brif so +L8AAC lda #3 ; set 1st byte to 3 to indicate line number not existing + sta ,x+ + bra L8A99 ; go process another +L8AB2 ldd 1,x ; get MSB of line number + dec 2,x ; is MS byte zero? + beq L8AB9 ; brif not + clra ; set MS byte to 0 +L8AB9 ldb 3,x ; get LSB of line number + dec 4,x ; is it zero? + beq L8AC0 ; brif not + clrb ; clear byte +L8AC0 std 1,x ; save binary number + std BINVAL ; save trial number + jsr LAD01 ; find the line number +L8AC7 ldx TEMPTR ; get start of packed line + bcs L8AAC ; brif line number not found + ldd V47 ; get address of line number + inc ,x+ ; bump first byte to 2 (exists if checking) or 1 if inserting + std ,x ; save address of correct number + bra L8A99 ; go process more +L8AD3 clr ,x ; clear carry and first byte + ldx 1,x ; point to address of correct line + ldx 2,x ; get correct line number + stx V47 ; save it + bra L8AC7 ; insert into line +L8ADD ldx TXTTAB ; get beginning of program + bra L8AE5 +L8AE1 ldx CHARAD ; get input pointer + leax 1,x ; move it forward +L8AE5 bsr L8A86 ; return if end of program + leax 2,x ; move past line address +L8AE9 leax 1,x ; move forward +L8AEB stx CHARAD ; save input pointer +L8AED jsr GETNCH ; get an input character +L8AEF tsta ; is it actual 0? + beq L8AE1 ; brif end of line + bpl L8AED ; brif not a token + ldx CHARAD ; get input pointer + cmpa #0xff ; function? + beq L8AE9 ; brif so - ignore it (and following byte) + jsr RVEC22 ; do the RAM hook thing + cmpa #0xa7 ; THEN? + beq L8B13 ; brif so + cmpa #0x84 ; ELSE? + beq L8B13 ; brif so + cmpa #0x81 ; GO(TO|SUB)? + bne L8AED ; brif not - we don't have a line number + jsr GETNCH ; get TO/SUB + cmpa #0xa5 ; GOTO? + beq L8B13 ; brif so + cmpa #0xa6 ; GOSUB? + bne L8AEB ; brif not +L8B13 jsr GETNCH ; fetch character after token + bcs L8B1B ; brif numeric (line number) +L8B17 jsr GETCCH ; set flags on input character + bra L8AEF ; keep checking for line numbers +L8B1B ldx CHARAD ; get input pointer + pshs x ; save it + jsr LAF67 ; parse line number + ldx CHARAD ; get input pointer after line +L8B24 lda ,-x ; get character before pointer + jsr L90AA ; set C if numeric + bcs L8B24 ; brif not numeric + leax 1,x ; move pointer up + tfr x,d ; calculate size of line number + subb 1,s + subb #5 ; make sure at least 5 bytes + beq L8B55 ; brif exactly 5 bytes - no change + blo L8B41 ; brif less than 5 bytes + leau ,x ; move remainder of program backward + negb ; negate extra number of bytes (to subtract from X) + leax b,x ; now X is the correct position to move program to + jsr L89B8 ; shift program backward + bra L8B55 +L8B41 stx V47 ; save end of line number space (end of copy) + ldx VARTAB ; get end of program + stx V43 ; set source pointer + negb ; get positive difference + leax b,x ; now X is the top of the destination block + stx V41 ; set copy destination + stx VARTAB ; save new end of program + jsr LAC1E ; make sure enough room and make a hole in the program + ldx V45 ; get end address of destination block + stx CHARAD ; set input there +L8B55 puls x ; get starting address of the line number + lda #1 ; set "new number" flag + sta ,x + sta 2,x + sta 4,x + ldb BINVAL ; get MS byte of line number + bne L8B67 ; brif it is not zero + ldb #1 ; set to 1 if MSB is 0 + inc 2,x ; flag MSB as 0 +L8B67 stb 1,x ; set MSB of line number + ldb BINVAL+1 ; get LSB of number + bne L8B71 ; brif nonzero + ldb #1 ; set to 1 if LSB is 0 + inc 4,x ; flag LSB as 0 +L8B71 stb 3,x ; save LSB of line number + jsr GETCCH ; get input character + cmpa #', ; multiple line numbers? (ON GOTO, ON GOSUB) + beq L8B13 ; brif so - process another + bra L8B17 ; go look for more line numbers +L8B7B ldx TXTTAB ; point to start of program + leax -1,x ; move back (compensate for inc below) +L8B7F leax 1,x ; move forward + ldd 2,x ; get this line number + std CURLIN ; save it (for error message) + jsr L8A86 ; return if end of program + leax 3,x ; skip address and line number, stay one before line text +L8B8A leax 1,x ; move to next character +L8B8C lda ,x ; get input character + beq L8B7F ; brif end of line + deca ; valid line new line number? + beq L8BAE ; brif so + suba #2 ; undefined line? + bne L8B8A ; brif not + pshs x ; save line number pointer + ldx #L8BD9-1 ; show UL message + jsr STRINOUT + ldx ,s ; get input pointer + ldd 1,x ; get undefined line number + jsr LBDCC ; display line number + jsr LBDC5 ; print out "IN XXXX" + jsr LB958 ; do a newline + puls x ; get input pointer back +L8BAE pshs x ; save input pointer + ldd 1,x ; get binary value of line number + std FPA0+2 ; save it in FPA0 + jsr L880E ; adjust FPA0 as integer + jsr LBDD9 ; convert to text string + puls u ; get previous input pointer address + ldb #5 ; each expanded line uses 5 bytes +L8BBE leax 1,x ; move pointer forward (in string number) past sign + lda ,x ; do we have a digit? + beq L8BC9 ; brif not - end of number + decb ; mark a byte consumed + sta ,u+ ; put digit in program + bra L8BBE ; copy another digit +L8BC9 leax ,u ; point to address at end of text number + tstb ; did number fill whole space? + beq L8B8C ; brif so - move on + leay ,u ; save end of number pointer + leau b,u ; point to the end of the original expanded number + jsr L89B8 ; close up gap in program + leax ,y ; get end of line number pointer back + bra L8B8C ; go process more +L8BD9 fcn 'UL ' +; HEX$ function +HEXDOL jsr LB740 ; convert argument to positive integer + ldx #STRBUF+2 ; point to string buffer + ldb #4 ; convert 4 nibbles +L8BE5 pshs b ; save nibble counter + clrb ; clear digit accumulator + lda #4 ; do 4 shifts +L8BEA asl FPA0+3 ; shift a bit off the left of the value into low bits of B + rol FPA0+2 + rolb + deca ; done all shifts? + bne L8BEA ; brif not + tstb ; do we have a nonzero digit? + bne L8BFF ; brif so + lda ,s ; is it last digit? + deca + beq L8BFF ; brif so - keep the 0 + cmpx #STRBUF+2 ; is it a middle zero? + beq L8C0B ; brif not +L8BFF addb #'0 ; add ASCII bias + cmpb #'9 ; above 9? + bls L8C07 ; brif not + addb #7 ; adjust into alpha range +L8C07 stb ,x+ ; save digit in output + clr ,x ; make sure we have a NUL term +L8C0B puls b ; get back nibble counter + decb ; done all? + bne L8BE5 ; brif not + leas 2,s ; don't return mainline (we're returning a string) + ldx #STRBUF+1 ; point to start of converted number + jmp LB518 ; save string in string space, etc., and return it +; DLOAD command +DLOAD jsr LA429 ; close files +L8C1B jsr GETCCH ; get back input character + suba #'M ; is it DLOADM? + pshs a ; save DLOADM flag + bne L8C25 ; brif DLOAD + jsr GETNCH ; eat the "M" +L8C25 jsr LA578 ; parse the file name + jsr GETCCH ; get character after file name + beq L8C44 ; brif end of line + jsr SYNCOMMA ; make sure we have a comma + cmpa #', ; do we have 2 commas? + beq L8C44 ; brif so - use default baud rate + jsr EVALEXPB ; evaluate baud rate +L8C36 lda #44*4 ; delay for 300 baud + tstb ; was argument 0? + beq L8C42 ; brif so - 300 baud + lda #44 ; constant for 1200 baud + decb ; was it 1? + lbne LB44A ; raise error if not +L8C42 sta DLBAUD ; save baud rate constant +L8C44 jsr L8CD0 ; transmit file name and read in file status + pshs a ; save register + lda #-3 ; set input to DLOAD + sta DEVNUM + puls a ; restore register + tst ,s+ ; is it DLOADM? + beq L8C85 ; brif so + jsr LA5C7 ; check for end of line - error if not + tstb ; ASCII? + beq L8C5F ; brif not - do error + jsr LAD19 ; clear out program + jmp LAC7C ; go read program +L8C5F jmp LA616 ; raise bad file mode +; CLOADM patch for Extended Basic +L8C62 jsr GETNCH ; get character after CLOAD + cmpa #'M ; CLOADM? + lbne CLOAD ; brif not - Color Basic's CLOAD can handle it + clr FILSTA ; close tape file + jsr GETNCH ; eat the "M" + jsr LA578 ; parse file name + jsr LA648 ; find the file + tst CASBUF+10 ; is it a chunked file? + lbeq LA505 ; brif not - Color Basic's CLOADM can handle it + ldu CASBUF+8 ; get file type and ASCII flag + dec DEVNUM ; set source device to tape + jsr LA635 ; go read the first block + tfr u,d ; put type and ASCII flag somewhere more useful +; NOTE: DLOADM comes here to do the final processing +L8C85 subd #0x200 ; is it binary and "machine language"? + bne L8C5F ; brif not - raise an error + ldx ZERO ; default load offset + jsr GETCCH ; is there any offset? + beq L8C96 ; brif not + jsr SYNCOMMA ; make sure there's a comma + jsr LB73D ; evaluate offset in X +L8C96 stx VD3 ; save offset + jsr LA5C7 ; raise error if more stuff follows +L8C9B bsr L8CC6 ; get type of "amble" + pshs a ; save it + bsr L8CBF ; read in block length + tfr d,y ; save it + bsr L8CBF ; read in load address + addd VD3 ; add in offset + std EXECJP ; save it as the execution address + tfr d,x ; put load address in a pointer + lda ,s+ ; get "amble" type + lbne LA42D ; brif postamble - close file +L8CB1 bsr L8CC6 ; read a data byte + sta ,x ; save in memory + cmpa ,x+ ; did it actually save? + bne L8CCD ; brif not RAM - raise error + leay -1,y ; done yet? + bne L8CB1 ; brif not + bra L8C9B ; look for another "amble" +L8CBF bsr L8CC1 ; read a character to B +L8CC1 bsr L8CC6 ; read character to A + exg a,b ; swap character with previously read one +L8CC5 rts +L8CC6 jsr LA176 ; read a character from input + tst CINBFL ; EOF? + beq L8CC5 ; brif not +L8CCD jmp LA619 ; raise I/O error if EOF +L8CD0 bsr L8D14 ; transmit file name + pshs b,a ; save file status + inca ; was file found? + beq L8CDD ; brif not + ldu ZERO ; zero U - first block + bsr L8CE4 ; read block + puls a,b,pc ; restore status and return +L8CDD ldb #2*26 ; code for NE error + jmp LAC46 ; raise error +L8CE2 ldu CBUFAD ; get block number +L8CE4 leax 1,u ; bump block number + stx CBUFAD ; save new block number + ldx #CASBUF ; use cassette buffer + jsr L8D7C ; read a block + jmp LA644 ; reset input buffer pointers +; Generic input handler for Extended Basic +XVEC4 lda DEVNUM ; get device number + cmpa #-3 ; DLOAD? + bne L8D01 ; brif not + leas 2,s ; don't return to mainline code + clr CINBFL ; reset EOF flag to not EOF + tst CINCTR ; anything available? + bne L8D02 ; brif so - fetch one + com CINBFL ; flag EOF +L8D01 rts +L8D02 pshs u,y,x,b ; save registers + ldx CINPTR ; get buffer pointer + lda ,x+ ; get character from buffer + pshs a ; save it for return + stx CINPTR ; save new input pointer + dec CINCTR ; account for byte removed from buffer + bne L8D12 ; brif buffer not empty + bsr L8CE2 ; go read a block +L8D12 puls a,b,x,y,u,pc ; restore registers, return value, and return +L8D14 clra ; clear attempt counter + pshs x,b,a ; make a hole for variables + leay ,s ; set up frame pointer + bra L8D1D ; go read block +L8D1B bsr L8D48 ; bump attempt counter +L8D1D lda #0x8a ; send file request control code + bsr L8D58 + bne L8D1B ; brif no echo or error + ldx #CFNBUF+1 ; point to file name +L8D26 lda ,x+ ; get file name characater + jsr L8E04 ; send it + cmpx #CFNBUF+9 ; end of file name? + bne L8D26 ; brif not + bsr L8D62 ; output check byte and look for response + bne L8D1B ; transmit name again if not ack + bsr L8D72 ; get file type (0xff is not found) + bne L8D1B ; brif error + sta 2,y ; save file type + bsr L8D72 ; read ASCII flag + bne L8D1B ; brif error + sta 3,y ; save ASCII flag + bsr L8D6B ; read check byte + bne L8D1B ; brif error + leas 2,s ; lose attempt counter and check byte + puls a,b,pc ; return file type and ascii flag +L8D48 inc ,y ; bump attempt counter + lda ,y ; get new count + cmpa #5 ; done 5 times? + blo L8D6A ; brif not + lda #0xbc ; send abort code + jsr L8E0C + jmp LA619 ; raise an I/O error +L8D58 pshs a ; save compare character + bsr L8DB8 ; send character + bne L8D60 ; brif read error + cmpa ,s ; does it match? (set Z if good) +L8D60 puls a,pc ; restore character and return +L8D62 lda 1,y ; get XOR check byte + bsr L8DB8 ; send it and read + bne L8D6A ; brif read error + cmpa #0xc8 ; is it ack? (set Z if so) +L8D6A rts +L8D6B bsr L8D72 ; read character from rs232 + bne L8D6A ; brif error + lda 1,y ; get check byte + rts +L8D72 bsr L8DBC ; read a character from rs232 + pshs a,cc ; save result (and flags) + eora 1,y ; accumulate xor checksum + sta 1,y + puls cc,a,pc ; restore byte, flags, and return +L8D7C clra ; reset attempt counter + pshs u,y,x,b,a ; make a stack frame + asl 7,s ; split block number into two 7 bit chuncks + rol 6,s + lsr 7,s + leay ,s ; set up frame pointer + bra L8D8B +L8D89 bsr L8D48 ; bump attempt counter +L8D8B lda #0x97 ; send block request code + bsr L8D58 + bne L8D89 ; brif error + lda 6,y ; send out block number (high bits first) + bsr L8E04 + lda 7,y + bsr L8E04 + bsr L8D62 ; send check byte and get ack + bne L8D89 ; brif error + bsr L8D72 ; read block size + bne L8D89 ; brif read error + sta 4,y ; save character count + ldx 2,y ; get buffer pointer + ldb #128 ; length of data block +L8DA7 bsr L8D72 ; read a data byte + bne L8D89 ; brif error + sta ,x+ ; save byte in buffer + decb ; done a whole block? + bne L8DA7 ; brif not + bsr L8D6B ; read check byte + bne L8D89 ; brif error + leas 4,s ; lose attempt counter, check byte, and buffer pointer + puls a,b,x,pc ; return with character count in A, clean rest of stack +L8DB8 clr 1,y ; clear check byte + bsr L8E0C ; output character +L8DBC clra ; clear attempt counter + pshs x,b,cc ; save registers and interrupt status + orcc #0x50 ; disable interrupts + lda TIMOUT ; get timout delay (variable) + ldx ZERO ; get constant timeout value +L8DC5 bsr L8DE6 ; get RS232 status + bcc L8DC5 ; brif "space" - waiting for "mark" +L8DC9 bsr L8DE6 ; get RS232 status + bcs L8DC9 ; brif "mark" - waiting for "space" (start bit) + bsr L8DF9 ; delay for half of bit time + ldb #1 ; set bit probe + pshs b ; save it + clra ; reset data byte +L8DD4 bsr L8DF7 ; wait one bit time + ldb PIA1+2 ; get input bit to carry + rorb + bcc L8DDE ; brif "space" (0) + ora ,s ; merge bit probe in +L8DDE asl ,s ; shift bit probe over + bcc L8DD4 ; brif we haven't done 8 bits + leas 1,s ; remove bit probe + puls cc,b,x,pc ; restore interrupts, registers, and return +L8DE6 ldb PIA1+2 ; get RS232 value + rorb ; put in C + leax 1,x ; bump timeout + bne L8DF6 ; brif nonzero + deca ; did the number of waits expire? + bne L8DF6 ; brif not + leas 2,s ; don't return - we timed out + puls cc,b,x ; restore interrupts and registers + inca ; clear Z (A was zero above) +L8DF6 rts +L8DF7 bsr L8DF9 ; do first half of bit delay then fall through for second +L8DF9 pshs a ; save register + lda DLBAUD ; get baud rate constant +L8DFD brn L8DFD ; do nothing - delay + deca ; time expired? + bne L8DFD ; brif not + puls a,pc ; restore register and return +L8E04 pshs a ; save character to send + eora 1,y ; accumulate chechsum + sta 1,y + puls a ; get character back +L8E0C pshs b,a,cc ; save registers and interrupt status + orcc #0x50 ; disable interrupts + bsr L8DF7 ; do a bit delay + bsr L8DF7 ; do another bit delay + clr PIA1 ; set output to space (start bit) + bsr L8DF7 ; do a bit delay + ldb #1 ; bit probe start at LSB + pshs b ; save bitprobe +L8E1D lda 2,s ; get output byte + anda ,s ; see what our current bit is + beq L8E25 ; brif output is 0 + lda #2 ; set output to "marking" +L8E25 sta PIA1 ; send bit + bsr L8DF7 ; do a bit delay + asl ,s ; shift bit probe + bcc L8E1D ; brif not last bit + lda #2 ; set output to marking ("stop" bit) + sta PIA1 + leas 1,s ; lose bit probe + puls cc,a,b,pc ; restore registers, interrupts, and return +; PRINT USING +; This is a massive block of code for something that has limited utility. It is *almost* flexible enough to +; be truly useful outside of a narrow range of use cases. Indeed, this comes in at about 1/8 of the total +; Extended Color Basic ROM. +; +; This uses several variables: +; VD5: pointer to format string descriptor +; VD7: next print item flag +; VD8: right digit counter +; VD9: left digit counter (or length of string argument) +; VDA: status byte (bits as follows): +; 6: force comma +; 5: force leading * +; 4: floating $ +; 3: pre-sign +; 2: post-sign +; 0: scientific notation +L8E37 lda #1 ; set length to use to 1 + sta VD9 +L8E3B decb ; consume character from format string + jsr L8FD8 ; show error flag if flags set + jsr GETCCH ; get input character + lbeq L8ED8 ; brif end of line - bail + stb VD3 ; save remaining string length + jsr LB156 ; evaluate the argument + jsr LB146 ; error if numeric + ldx FPA0+2 ; get descriptor for argument + stx V4D ; save it for later + ldb VD9 ; get length counter to use + jsr LB6AD ; get B bytes of string space (do a LEFT$) + jsr LB99F ; print the formatted string + ldx FPA0+2 ; get formatted string descriptor + ldb VD9 ; get requested length + subb ,x ; see if we have any left over +L8E5F decb ; have we got the right width? + lbmi L8FB3 ; brif so - go process more + jsr LB9AC ; output a space + bra L8E5F ; go see if we're done yet +L8E69 stb VD3 ; save current format string counter and pointer + stx TEMPTR + lda #2 ; initial spaces count = 2 (for the two %s) + sta VD9 ; save length counter +L8E71 lda ,x ; get character in string + cmpa #'% ; is it the end of the sequence? + beq L8E3B ; brif so - display requested part of the strign + cmpa #0x20 ; space? + bne L8E82 ; brif not + inc VD9 ; bump spaces count + leax 1,x ; move format pointer forward + decb ; consume character + bne L8E71 ; brif not end of format string +L8E82 ldx TEMPTR ; restore format string pointer + ldb VD3 ; get back format string length + lda #'% ; show % as debugging aid +L8E88 jsr L8FD8 ; send error indicator if flags set + jsr PUTCHR ; output character + bra L8EB9 ; go process more format string +; PRINT extension for USING +XVEC9 cmpa #0xcd ; USING? + beq L8E95 ; brif so + rts ; return to mainline code +; This is the main entry point for PRINT USING +L8E95 leas 2,s ; don't return to the mainline code + jsr LB158 ; evaluate the format string + jsr LB146 ; error if numeric + ldb #'; ; make sure there's a ; after the string + jsr LB26F + ldx FPA0+2 ; get format string descriptor + stx VD5 ; save it for later + bra L8EAE ; process format string +L8EA8 lda VD7 ; is there a print item? + beq L8EB4 ; brif not + ldx VD5 ; get back format string descriptor +L8EAE clr VD7 ; reset next print item flag + ldb ,x ; get length of format string + bne L8EB7 ; brif string is non-null +L8EB4 jmp LB44A ; raise FC error +L8EB7 ldx 2,x ; point to start of string +L8EB9 clr VDA ; clear status (new item) +L8EBB clr VD9 ; clear left digit counter + lda ,x+ ; get character from format string + cmpa #'! ; ! (use first character of string)? + lbeq L8E37 ; brif so + cmpa #'# ; digit? + beq L8F24 ; brif so - handle numeric + decb ; consume format character + bne L8EE2 ; brif not done + jsr L8FD8 ; show a "+" if any flags set - indicates error at end of string + jsr PUTCHR ; output format string character +L8ED2 jsr GETCCH ; get current input character + bne L8EA8 ; brif not end of statement + lda VD7 ; get next item flag +L8ED8 bne L8EDD ; brif more print items + jsr LB958 ; do newline +L8EDD ldx VD5 ; point to format string descriptor + jmp LB659 ; remove from string stack, etc., if appropriate (and return) +L8EE2 cmpa #'+ ; is it + (pre-sign)? + bne L8EEF ; brif not + jsr L8FD8 ; send a "+" if flags set + lda #8 ; flag for pre-sign + sta VDA ; set flags + bra L8EBB ; go interpret some more stuff +L8EEF cmpa #'. ; decimal? + beq L8F41 ; brif so - numeric + cmpa #'% ; % (show string)? + lbeq L8E69 ; brif so + cmpa ,x ; do we have two identical characters? +L8EFB bne L8E88 ; brif not - invalid format character + cmpa #'$ ; double $? + beq L8F1A ; brif so - floating $ + cmpa #'* ; double *? + bne L8EFB ; brif not + lda VDA ; get status byte + ora #0x20 ; enable * padding + sta VDA + cmpb #2 ; is $$ the last two? + blo L8F20 ; brif so + lda 1,x ; is it $ after? + cmpa #'$ + bne L8F20 ; brif not + decb ; consume the "$" + leax 1,x + inc VD9 ; add to digit counter * pad + $ counter +L8F1A lda VDA ; indicate floating $ + ora #0x10 + sta VDA +L8F20 leax 1,x ; consume the second format character + inc VD9 ; add one more left place +L8F24 clr VD8 ; clear right digit counter +L8F26 inc VD9 ; bump left digit counter + decb ; consume character + beq L8F74 ; brif end of string + lda ,x+ ; get next format character + cmpa #'. ; decimal? + beq L8F4F ; brif so + cmpa #'# ; digit? + beq L8F26 ; brif so + cmpa #', ; comma flag? + bne L8F5A ; brif not + lda VDA ; set commas flag + ora #0x40 + sta VDA + bra L8F26 ; handle more characters to left of decimal +L8F41 lda ,x ; get character after . + cmpa #'# ; digit? + lbne L8E88 ; brif not - invalid + lda #1 ; set right digit counter to 1 (for the .) + sta VD8 + leax 1,x ; consume the . +L8F4F inc VD8 ; add one to right digit counter + decb ; consume character + beq L8F74 ; brif end of format string + lda ,x+ ; get another format character + cmpa #'# ; digit? + beq L8F4F ; brif so +L8F5A cmpa #0x5e ; up arrow? + bne L8F74 ; brif not + cmpa ,x ; two of them? + bne L8F74 ; brif not + cmpa 1,x ; three of them? + bne L8F74 ; brif not + cmpa 2,x ; four of them? + bne L8F74 ; brif not + cmpb #4 ; string actually has the characters? + blo L8F74 ; brif not + subb #4 ; consome them + leax 4,x + inc VDA ; set scientific notation bit +L8F74 leax -1,x ; back up input pointer + inc VD9 ; add one digit for pre-sign force + lda VDA ; is it pre-sign? + bita #8 + bne L8F96 ; brif so + dec VD9 ; undo pre-sign adjustment + tstb ; end of string? + beq L8F96 ; brif so + lda ,x ; get next character + suba #'- ; post sign force? + beq L8F8F ; brif so + cmpa #'+-'- ; plus? + bne L8F96 ; brif not + lda #8 ; trailing + is a pre-sign force +L8F8F ora #4 ; add in post sign flag + ora VDA ; merge with flags + sta VDA + decb ; consume character +L8F96 jsr GETCCH ; do we have an argument + lbeq L8ED8 ; brif not + stb VD3 ; save format string length + jsr LB141 ; evluate numeric expression + lda VD9 ; get left digit counter + adda VD8 ; add in right digit counter + cmpa #17 ; is it more than 16 digits + decimal? + lbhi LB44A ; brif so - this is a problem + jsr L8FE5 ; format value according to settings + leax -1,x ; move buffer pointer back + jsr STRINOUT ; display formatted number string +L8FB3 clr VD7 ; reset next print item flag + jsr GETCCH ; get current input character + beq L8FC6 ; brif end of statement + sta VD7 ; set next print flag to nonzero + cmpa #'; ; list separator ;? + beq L8FC4 ; brif so + jsr SYNCOMMA ; require a comma between if not ; + bra L8FC6 ; process next item +L8FC4 jsr GETNCH ; munch the semicolon +L8FC6 ldx VD5 ; get format string descriptor + ldb ,x ; get length of string + subb VD3 ; subtract amount left after last item + ldx 2,x ; point to string address + abx ; move pointer to correct spot + ldb VD3 ; get remaining string length + lbne L8EB9 ; if we have more, interpret from there + jmp L8ED2 ; re-interpret from start if we hit the end +L8FD8 pshs a ; save character + lda #'+ ; "error" flag character + tst VDA ; did we have some flags set? + beq L8FE3 ; brif not + jsr PUTCHR ; output error flag +L8FE3 puls a,pc ; restore character and return +L8FE5 ldu #STRBUF+4 ; point to string buffer + ldb #0x20 ; blank space + lda VDA ; get flags + bita #8 ; pre-sign? + beq L8FF2 ; brif not + ldb #'+ ; plus sign +L8FF2 tst FP0SGN ; get sign of value + bpl L8FFA ; brif positive + clr FP0SGN ; make number positive (for later) + ldb #'- ; negative sign +L8FFA stb ,u+ ; put sign in buffer + ldb #'0 ; put a zero there + stb ,u+ + anda #1 ; check scientific notation force + lbne L910D ; brif so + ldx #LBDC0 ; point to FP 1E+9 + jsr LBCA0 ; is it less? + bmi L9023 ; brif so + jsr LBDD9 ; convert FP number to string (we're doing scientific notation) +L9011 lda ,x+ ; advance pointer to end of string + bne L9011 +L9015 lda ,-x ; make a hole at the start + sta 1,x + cmpx #STRBUF+3 ; done yet? + bne L9015 ; brif not + lda #'% ; put "overflow" flag at start + sta ,x + rts +L9023 lda FP0EXP ; get exponent of value + sta V47 ; save it + beq L902C ; brif value is 0 + jsr L91CD ; convert to number with 9 significant figures to left of decimal +L902C lda V47 ; get base 10 exponent offset + lbmi L90B3 ; brif < 100,000,000 + nega ; get negative difference + adda VD9 ; add to number of left digits + suba #9 ; account for the 9 we actually have + jsr L90EA ; put leading zeroes in buffer + jsr L9263 ; initialize the decimal point and comma counters + jsr L9202 ; convert FPA0 to decimal ASCII in buffer + lda V47 ; get base 10 exponent + jsr L9281 ; put that many zeroes in buffer, stop at decimal point + lda V47 ; get base 10 exponent + jsr L9249 ; check for decimal + lda VD8 ; get right digit counter + bne L9050 ; brif we want stuff after decimal + leau -1,u ; delete decimal if not needed +L9050 deca ; subtract one place (for decimal) + jsr L90EA ; put zeroes in buffer (trailing) +L9054 jsr L9185 ; insert * padding, floating $, and post-sign + tsta ; was there a post sign? + beq L9060 ; brif not + cmpb #'* ; was first character a *? + beq L9060 ; brif so + stb ,u+ ; store the post sign +L9060 clr ,u ; make srue it's NUL terminated + ldx #STRBUF+3 ; point to start of buffer +L9065 leax 1,x ; move to next character + stx TEMPTR ; save it for later + lda VARPTR+1 ; get address of decimal point + suba TEMPTR+1 ; subtract out actual digits left of decimal + suba VD9 ; subtract out required left digits + beq L90A9 ; brif no padding needed + lda ,x ; get current character + cmpa #0x20 ; space? + beq L9065 ; brif so - advance pointer + cmpa #'* ; *? + beq L9065 ; brif so - advance pointer + clra ; zero on stack is end of data ponter +L907C pshs a ; save character on stack + lda ,x+ ; get next character + cmpa #'- ; minus? + beq L907C ; brif so + cmpa #'+ ; plus? + beq L907C ; brif so + cmpa #'$ ; $? + beq L907C ; brif so + cmpa #'0 ; zero? + bne L909E ; brif not + lda 1,x ; get character after 0 + bsr L90AA ; clear carry if number + bcs L909E ; brif not number +L9096 puls a ; get character off stack + sta ,-x ; put it back in string buffer + bne L9096 ; brif not - restore another + bra L9065 ; keep cleaning up buffer +L909E puls a ; get the character on the stack + tsta ; is it NUL? + bne L909E ; brif not + ldx TEMPTR ; get string buffer start pointer + lda #'% ; put error flag in front + sta ,-x +L90A9 rts +L90AA cmpa #'0 ; zero? + blo L90B2 ; brif not + suba #'9+1 ; set C if > "9" + suba #-('9+1) +L90B2 rts +L90B3 lda VD8 ; get right digit counter + beq L90B8 ; brif not right digits + deca ; account for decimal point +L90B8 adda V47 ; add base 10 exponent offset + bmi L90BD ; if >= 0, no shifts are required + clra ; force shift counter to 0 +L90BD pshs a ; save shift counter +L90BF bpl L90CB ; brif positive count + pshs a ; save shift counter + jsr LBB82 ; divide FPA0 by 10 (shift 1 digit to the right) + puls a ; get shift counter back + inca ; account for the shift + bra L90BF ; see if we're done yet +L90CB lda V47 ; get base 10 exponent offset + suba ,s+ ; account for adjustment + sta V47 ; save new exponent offset + adda #9 ; account for significant places + bmi L90EE ; brif we don't need zeroes to left + lda VD9 ; get left decimal counter + suba #9 ; account for significant figures + suba V47 ; subtract exponent offset + bsr L90EA ; output leading zeroes + jsr L9263 ; initialize decimal and comma counters + bra L90FF ; process remainder of digits +L90E2 pshs a ; save zero counter + lda #'0 ; insert a 0 + sta ,u+ + puls a ; get back counter +L90EA deca ; do we need more zeroes? + bpl L90E2 ; brif so + rts +L90EE lda VD9 ; get left digit counter + bsr L90EA ; put that many zeroes in + jsr L924D ; put decimal in buffer + lda #-9 ; figure out filler zeroes + suba V47 + bsr L90EA ; output required leader zeroes + clr V45 ; clear decimal pointer counter + clr VD7 ; clear comma counter +L90FF jsr L9202 ; decode FPA0 to decimal string + lda VD8 ; get right digit counter + bne L9108 ; brif there are right digits + ldu VARPTR ; point to decimal location of decimal +L9108 adda V47 ; add base 10 exponent + lbra L9050 ; add in leading astrisks, etc. +L910D lda FP0EXP ; get exponent of FPA0 + pshs a ; save it + beq L9116 ; brif 0 + jsr L91CD ; convert to number with 9 figures +L9116 lda VD8 ; get right digit counter + beq L911B ; brif no right digits + deca ; account for decimal point +L911B adda VD9 ; get left digit counter + clr STRBUF+3 ; use buffer byte as temporary storage + ldb VDA ; get status flags + andb #4 ; post-sign? + bne L9129 ; brif so + com STRBUF+3 ; flip byte if no post sign +L9129 adda STRBUF+3 ; subtract 1 if no post sign + suba #9 ; account for significant figures + pshs a ; save shift counter +L9130 bpl L913C ; brif no more shifts needed + pshs a ; save counter + jsr LBB82 ; divide by 10 (shift right one) + puls a ; get back counter + inca ; account for the shift + bra L9130 ; see if we need more +L913C lda ,s ; get original shift count + bmi L9141 ; brif shifting happened + clra ; flag for no shifting +L9141 nega ; calculate position of decimal (negate shift count, add left digit and post sign if needed) + adda VD9 ; add left digit counter + inca ; and post sign + adda STRBUF+3 + sta V45 ; save decimal counter + clr VD7 ; clear comma counter + jsr L9202 ; convert to decimal string + puls a ; get shift counter + jsr L9281 ; put the needed zeroes in + lda VD8 ; get right digit counter + bne L915A ; brif we want some + leau -1,u ; remove te decimal point +L915A ldb ,s+ ; get original exponent + beq L9167 ; brif it was 0 + ldb V47 ; get base 10 exponent + addb #9 ; account for significant figures + subb VD9 ; remove left digit count + subb STRBUF+3 ; add one if post sign +L9167 lda #'+ ; positive sign + tstb ; is base 10 exponent positive? + bpl L916F ; brif so + lda #'- ; negative sign + negb ; flip exponent +L916F sta 1,u ; put exponent sign + lda #'E ; put "E" and advance output pointer + sta ,u++ + lda #'0-1 ; initialize digit accumulator +L9177 inca ; bump digit + subb #10 ; are we at the right digit? + bcc L9177 ; brif not + addb #'0+10 ; add ASCII bias and undo extra subtraction + std ,u++ ; save exponent in buffer + clr ,u ; clear final byte in buffer + jmp L9054 ; insert *, $, etc. +L9185 ldx #STRBUF+4 ; point to start of result + ldb ,x ; get sign + pshs b ; save it + lda #0x20 ; default pad with spaces + ldb VDA ; get flags + bitb #0x20 ; padding with *? + puls b + beq L919E ; brif no padding + lda #'* ; pad with * + cmpb #0x20 ; do we have a blank? (positive) + bne L919E ; brif not + tfr a,b ; use pad character +L919E pshs b ; save first character +L91A0 sta ,x+ ; store padding + ldb ,x ; get next character + beq L91B6 ; brif end of string + cmpb #'E ; exponent? + beq L91B6 ; brif so - treat as 0 + cmpb #'0 ; zero? + beq L91A0 ; brif so - pad it + cmpb #', ; leading comma? + beq L91A0 ; brif so - pad it + cmpb #'. ; decimal? + bne L91BA ; brif so - don't put a 0 before it +L91B6 lda #'0 ; put a zero before + sta ,-x +L91BA lda VDA ; get status byte + bita #0x10 ; floating $? + beq L91C4 ; brif not + ldb #'$ ; stuff a $ in + stb ,-x +L91C4 anda #4 ; pre-sgn? + puls b ; get back first character + bne L91CC ; brif not + stb ,-x ; save leading character (sign) +L91CC rts +L91CD pshs u ; save buffer pointer + clra ; initial exponent offset is 0 +L91D0 sta V47 ; save exponent offset + ldb FP0EXP ; get actual exponent + cmpb #0x80 ; is value >= 1.0? + bhi L91E9 ; brif so + ldx #LBDC0 ; point to FP number 1E9 + jsr LBACA ; multiply by 1000000000 + lda V47 ; account for 9 shifts + suba #9 + bra L91D0 ; brif not there yet +L91E4 jsr LBB82 ; divide by 10 + inc V47 ; account for shift +L91E9 ldx #LBDBB ; point to 999999999 + jsr LBCA0 ; compare it + bgt L91E4 ; brif not in range yet +L91F1 ldx #LBDB6 ; point to 99999999.9 + jsr LBCA0 ; compare + bgt L9200 ; brif in range + jsr LBB6A ; multiply by 10 + dec V47 ; account for shift + bra L91F1 ; see if we're in range yet +L9200 puls u,pc ; restore buffer pointer and return +L9202 pshs u ; save buffer pointer + jsr LB9B4 ; add .5 (round off) + jsr LBCC8 ; convert to integer format + puls u ; restore buffer pointer + ldx #LBEC5 ; point to 32 bit powers of 10 (alternating signs) + ldb #0x80 ; intitial digit counter is 0 with 0x80 bias +L9211 bsr L9249 ; check for comma +L9213 lda FPA0+3 ; add a power of 10 + adda 3,x + sta FPA0+3 + lda FPA0+2 + adca 2,x + sta FPA0+2 + lda FPA0+1 + adca 1,x + sta FPA0+1 + lda FPA0 + adca ,x + sta FPA0 + incb ; add one to digit counter + rorb ; set V if carry and sign differ + rolb + bvc L9213 ; brif we haven't "wrapped" + bcc L9235 ; brif subtracting + subb #10+1 ; take 9's complement if adding + negb +L9235 addb #'0-1 ; add in ASCII bias + leax 4,x ; move to next power + tfr b,a ; save digit + anda #0x7f ; mask off subtract flag + sta ,u+ ; save digit + comb ; toggle add/subtract + andb #0x80 + cmpx #LBEE9 ; done all places? + bne L9211 ; brif not + clr ,u ; but NUL at end +L9249 dec V45 ; at decimal? + bne L9256 ; brif not +L924D stu VARPTR ; save decimal point pointer + lda #'. ; insert decimal + sta ,u+ + clr VD7 ; clear comma counter + rts +L9256 dec VD7 ; do we need a comma? + bne L9262 ; brif not + lda #3 ; reset comma counter + sta VD7 + lda #', ; insert comma + sta ,u+ +L9262 rts +L9263 lda V47 ; get base 10 exponent offset + adda #10 ; account for significant figures + sta V45 ; save decimal counter + inca ; add one for decimal point +L926A suba #3 ; divide by 3, leave remainder in A + bcc L926A + adda #5 ; renormalize to range 1-3 + sta VD7 ; save comma counter + lda VDA ; get status + anda #0x40 ; commas wanted? + bne L927A ; brif not + sta VD7 ; clear comma counter +L927A rts +L927B pshs a ; save zeroes counter + bsr L9249 ; check for decimal + puls a ; get back counter +L9281 deca ; need a zero? + bmi L928E ; brif not + pshs a ; save counter + lda #'0 ; put a zero + sta ,u+ + lda ,s+ ; get back counter and set flags + bne L927B ; brif not done enough +L928E rts +; From here to the end of the Extended Basic ROM is the PMODE graphics system and related +; infrastructure with the exception of the PLAY command which shares some of its machinery +; with the DRAW command. +; +; Fetch screen address calculation routine address for the selected graphics mode +L928F ldu #L929C ; point to normalization routine jump table + lda PMODE ; get graphics mode + asla ; two bytes per address + ldu a,u ; get routine address + rts +; Normalize VERBEG/HORBEG coordinates and return screen address in X and pixel mask in A. +L9298 bsr L928F ; fetch normalization routine pointer + jmp ,u ; transfer control to it +L929C fdb L92A6 ; PMODE 0 + fdb L92C2 ; PMODE 1 + fdb L92A6 ; PMODE 2 + fdb L92C2 ; PMODE 3 + fdb L92A6 ; PMODE 4 +; Two colour mode address calculatoin +L92A6 pshs u,b ; savce registers + ldb HORBYT ; get number of bytes in each graphics row + lda VERBEG+1 ; get vertical coordinate + mul + addd BEGGRP ; now D is the absolute address of the start of the row + tfr d,x ; get address to the return location + ldb HORBEG+1 ; get horizontal coordinate + lsrb ; divide by 8 (8 pixels per byte in 2 colour mode) + lsrb + lsrb + abx ; now X is the address of the actual pixel byte + lda HORBEG+1 ; get horizontal coordinate + anda #7 ; keep only the low 3 bits which contain the pixel number + ldu #L92DD ; point to pixel mask lookup + lda a,u ; get pixel mask + puls b,u,pc ; restore registers and return result +; four colour address calculation +L92C2 pshs u,b ; save registers + ldb HORBYT ; get bytes per graphics row + lda VERBEG+1 ; get vertical coordinate + mul + addd BEGGRP ; now D is the address of the start of the row + tfr d,x ; put it in returnlocatin + ldb HORBEG+1 ; get horizontal coordinate + lsrb ; divide by 4 (four colour modes have four pixels per byte) + lsrb + abx ; now X points to the screen byte + lda HORBEG+1 ; get horizontal coordinate + anda #3 ; keep low two bits for pixel number + ldu #L92E5 ; point to four colour pixel masks + lda a,u ; get pixel mask + puls b,u,pc ; restore registers and return result +L92DD fcb 0x80,0x40,0x20,0x10 ; two colour pixel masks + fcb 0x08,0x04,0x02,0x01 +L92E5 fcb 0xc0,0x30,0x0c,0x03 ; four colour pixel masks +; Move X down one graphics row +L92E9 ldb HORBYT ; get bytes per row + abx ; add to screen address + rts +; Move one pixel right in 2 colour mode +L92ED lsra ; move pixel mask right + bcc L92F3 ; brif same byte + rora ; move pixel mask to left of byte + leax 1,x ; move to next byte +L92F3 rts +; Move one pixel right in 4 colour mode +L92F4 lsra ; shift mask half a pixel right + bcc L92ED ; brif not past end of byte - shift one more + lda #0xc0 ; set mask on left of byte + leax 1,x ; move to next byte + rts +; Evaluate two expressions (coordinates). Put first in HORBEG and second in VERBEG. +L92FC jsr LB734 ; evaluate two expressions - first in BINVAL, second in B + ldy #HORBEG ; point to storage location +L9303 cmpb #192 ; is vertical outside range? + blo L9309 ; brif not + ldb #191 ; max it at bottom of screen +L9309 clra ; zero extend vertical coordinate + std 2,y ; save vertical coordinate + ldd BINVAL ; get horizontal coordinate + cmpd #256 ; in range? + blo L9317 ; brif so + ldd #255 ; max it out to right side of screen +L9317 std ,y ; save horizontal coordinate + rts +; Normalize coordinates for proper PMODE +L931A jsr L92FC ; parse coordinates +L931D ldu #HORBEG ; point to start coordinates +L9320 lda PMODE ; get graphics mode + cmpa #2 ; is it pmode 0 or 1? + bhs L932C ; brif not + ldd 2,u ; get vertical coordinate + lsra ; divide it by two + rorb + std 2,u ; save it back +L932C lda PMODE ; get graphics mode + cmpa #4 ; pmode 4? + bhs L9338 ; brif so + ldd ,u ; cut horizontal coordinate in half + lsra + rorb + std ,u ; save new coordinate +L9338 rts +; PPOINT function +PPOINT jsr L93B2 ; evaluate two expressions (coordinates) + jsr L931D ; normalize coordinates + jsr L9298 ; get screen address + anda ,x ; get colour value of desired screen coordinate + ldb PMODE ; get graphics mode + rorb ; is it a two colour m ode? + bcc L935B ; brif so +L9349 cmpa #4 ; is it on rightmost bits? + blo L9351 ; brif not + rora ; shift right + rora + bra L9349 ; see if we're there yet +L9351 inca ; colour numbers start at 1 + asla ; add in colour set (0 or 8) + adda CSSVAL + lsra ; get colour in range of 0 to 8 +L9356 tfr a,b ; put result to B + jmp LB4F3 ; return B as FP number +L935B tsta ; is pixel on? + beq L9356 ; brif not, return 0 (off) + clra ; set colour number to "1" + bra L9351 ; make it 1 or 5 and return +; PSET command +PSET lda #1 ; PSET flag + bra L9366 ; go turn on the pixel +; PRESET command +PRESET clra ; PRESET flag +L9366 sta SETFLG ; store whether we're setting or resetting + jsr LB26A ; enforce ( + jsr L931A ; evaluate coordinates + jsr L9581 ; evaluate colour + jsr LB267 ; enforce ) + jsr L9298 ; get address of pixel +L9377 ldb ,x ; get screen data + pshs b ; save it + tfr a,b ; duplicate pixel mask + coma ; invert mask + anda ,x ; turn off screen pixel + andb ALLCOL ; adjust pixel mask to be the current colour + pshs b ; merge pixel data into the screen data + ora ,s+ + sta ,x ; put it on screen + suba ,s+ ; nonzero if screen data changed + ora CHGFLG ; propagate change flag + sta CHGFLG + rts +; Evaluate two sets of coordinates separated by a minus sign. First at HORBEG,VERBEG and +; second at HOREND,VEREND. If no first set, use HORDEF,VERDEF. +L938F ldx HORDEF ; set default start coords + stx HORBEG + ldx VERDEF + stx VERBEG + cmpa #0xac ; do we start with a -? + beq L939E ; brif no starting coordinates + jsr L93B2 ; parse coordinates +L939E ldb #0xac ; make sure we have a - + jsr LB26F + jsr LB26A ; require a ( + jsr LB734 ; evaluate two expressions + ldy #HOREND ; point to storage location + jsr L9303 ; process coordinates + bra L93B8 ; finish up with a ) +L93B2 jsr LB26A ; make sure there's a ( + jsr L92FC ; evaluate coordinates +L93B8 jmp LB267 ; force a ) +; LINE command +LINE cmpa #0x89 ; is it LINE INPUT? + lbeq L89C0 ; brif so - go handle it + cmpa #'( ; starting coord? + beq L93CE ; brif so + cmpa #0xac ; leading -? + beq L93CE ; brif so + ldb #'@ ; if it isn't the above, make sure it's @ + jsr LB26F +L93CE jsr L938F ; parse coordinates + ldx HOREND ; set ending coordinates as the defaults + stx HORDEF + ldx VEREND + stx VERDEF + jsr SYNCOMMA ; make sure we have a comma + cmpa #0xbe ; PRESET? + beq L93E9 ; brif so + cmpa #0xbd ; PSET? + lbne LB277 ; brif not + ldb #01 ; PSET flag + skip1lda ; skip byte and set A nonzero +L93E9 clrb ; PRESET flag + pshs b ; save PSET/PRESET flag + jsr GETNCH ; eat the PSET/PRESET + jsr L9420 ; normalize coordinates + puls b ; get back PSET/PRESET flag + stb SETFLG ; flag which we're doing + jsr L959A ; set colour byte + jsr GETCCH ; get next bit + lbeq L94A1 ; brif no box option + jsr SYNCOMMA ; make sure it's comma + ldb #'B ; make sure "B" for "box" + jsr LB26F + bne L9429 ; brif something follows the B + bsr L9444 ; draw horizontal line + bsr L946E ; draw vertical line + ldx HORBEG ; save horizontal coordinate + pshs x ; save it + ldx HOREND ; switch in horizontal end + stx HORBEG + bsr L946E ; draw vertical line + puls x ; get back original start + stx HORBEG ; put it back + ldx VEREND ; do the same dance with the vertical end + stx VERBEG + bra L9444 ; draw horizontal line +L9420 jsr L931D ; normalize the start coordinates + ldu #HOREND ; point to end coords + jmp L9320 ; normalize those coordinates +L9429 ldb #'F ; make sure we have "BF" for "filled box" + jsr LB26F + bra L9434 ; fill the box +L9430 leax -1,x ; move vertical coordinate up one +L9432 stx VERBEG ; save new vertical coordinate +L9434 jsr L9444 ; draw a horizontal line + ldx VERBEG ; are we at the end of the box? + cmpx VEREND + beq L9443 ; brif so + bcc L9430 ; brif we're moving up the screen + leax 1,x ; move down the screen + bra L9432 ; go draw another line +L9443 rts +; Draw a horizontal line from HORBEG to HOREND at VERBEG using color mask in ALLCOL +L9444 ldx HORBEG ; get starting horizontal coordinate + pshs x ; save it + jsr L971D ; get absolute value of HOREND-HORBEG + bcc L9451 ; brif end is > start + ldx HOREND ; copy end coordinate to start it is smaller + stx HORBEG +L9451 tfr d,y ; save difference - it's a pixel count + leay 1,y ; coordinates are inclusive + jsr L9298 ; get screen position of start coord + puls u ; restore original start coordinate + stu HORBEG + bsr L9494 ; point to routine to move pizel pointers to right +L945E sta VD7 ; save pixel mask + jsr L9377 ; turn on pixel + lda VD7 ; get pixel mask back + jsr ,u ; move one pixel right + leay -1,y ; turned on enough pixels yet? + bne L945E ; brif not +L946B rts +L946C puls b,a ; clean up stack +L946E ldd VERBEG ; save original vertical start coordinate + pshs b,a + jsr L9710 ; get vertical difference + bcc L947B ; brif end coordinate > start + ldx VEREND ; swap in end coordinate if not + stx VERBEG +L947B tfr d,y ; save number of pixels to set + leay 1,y ; the coordinates are inclusive + jsr L9298 ; get screen pointer + puls u ; restore start coordinate + stu VERBEG + bsr L949D ; point to routine to move down one row + bra L945E ; draw vertical line +; Point to routine which will move one pixel right +L948A fdb L92ED ; PMODE 0 + fdb L92F4 ; PMODE 1 + fdb L92ED ; PMODE 2 + fdb L92F4 ; PMODE 3 + fdb L92ED ; PMODE 4 +L9494 ldu #L948A ; point to jump table + ldb PMODE ; get graphics mode + aslb ; two bytes per address + ldu b,u ; get jump address + rts +; Point to routine to move down one row +L949D ldu #L92E9 ; point to "move down one row" routien + rts +; Draw a line from HORBEG,VERBEG to HOREND,VEREND +L94A1 ldy #L950D ; point to increase vertical coord + jsr L9710 ; calculate difference + lbeq L9444 ; brif none - draw a horizontal line + bcc L94B2 ; brif vertical end is > vertical start + ldy #L951B ; point to decrease vertical coord +L94B2 pshs d ; save vertical difference + ldu #L9506 ; point to increase horizontal coord + jsr L971D ; get difference + beq L946C ; brif none - draw a vertical line + bcc L94C1 ; brif horizontal end > horizontal start + ldu #L9514 ; point to decrease hoizontal coord +L94C1 cmpd ,s ; compare vert and horiz differences + puls x ; get X difference + bcc L94CC ; brif horiz diff > vert diff + exg u,y ; swap change routine pointers + exg d,x ; swap differences +L94CC pshs u,d ; save larger difference and routine + pshs d ; save larger difference + lsra ; divide by two + rorb + bcs L94DD ; brif odd number + cmpu #L950D+1 ; increase or decrease? + blo L94DD ; brif increase + subd #1 ; back up one +L94DD pshs x,b,a ; save smallest difference and initial middle offset + jsr L928F ; point to proper coordinate to screen conversion routine +L94E2 jsr ,u ; convert coordinates to screen address + jsr L9377 ; turn on a pixel + ldx 6,s ; get distnace counter + beq L9502 ; brif line is completely drawn + leax -1,x ; account for one pixel drawn + stx 6,s ; save new counter + jsr [8,s] ; increment/decrement larger delta + ldd ,s ; get the minor coordinate increment counter + addd 2,s ; add the smallest difference + std ,s ; save new minor coordinate incrementcounter + subd 4,s ; subtractout the largest difference + bcs L94E2 ; brif not greater - draw another pixel + std ,s ; save new minor coordinate increment + jsr ,y ; adjust minor coordinate + bra L94E2 ; go draw another pixel +L9502 puls x ; clean up stack and return + puls a,b,x,y,u,pc +L9506 ldx HORBEG ; bump horizontal coordinate + leax 1,x + stx HORBEG + rts +L950D ldx VERBEG ; bump vertical coordinate + leax 1,x + stx VERBEG + rts +L9514 ldx HORBEG ; decrement horizontal coordinate + leax -1,x + stx HORBEG + rts +L951B ldx VERBEG ; decrement vertical coordinate + leax -1,x + stx VERBEG + rts +; Get normalized maximum coordinate values in VD3 and VD5 +L9522 ldu #VD3 ; point to temp storage + ldx #255 ; set maximum horizontal + stx ,u + ldx #191 ; set maximum vertical + stx 2,u + jmp L9320 ; normalize them +; PCLS command +PCLS beq L9542 ; clear to background colour if no argument + bsr L955A ; evaluate colour +L9536 lda #0x55 ; consider each byte as 4 groups of 2 bit sub-nibbles + mul ; now colour is in all four sub-pixels + ldx BEGGRP ; get start of graphics screen +L953B stb ,x+ ; set byte to proper colour + cmpx ENDGRP ; at end of graphics page? + bne L953B ; brif not + rts +L9542 ldb BAKCOL ; get background colour + bra L9536 ; do the clearing dance +; COLOR command +COLOR cmpa #', ; check for comma + beq L9552 ; brif no foreground colour + bsr L955A ; evaluate first colour + stb FORCOL ; set foreground colour + jsr GETCCH ; is there a background colour? + beq L9559 ; brif not +L9552 jsr SYNCOMMA ; make sure we have a comma + bsr L955A ; evaluate background colour argument + stb BAKCOL ; set background colour +L9559 rts +; Evaluate a colour agument and convert to proper code based on graphics mode +L955A jsr EVALEXPB ; evaluate colour code +L955D cmpb #9 ; is it in range of 0-8? + lbhs LB44A ; brif not - raise error + clra ; CSS value for first colour set + cmpb #5 ; is it first or second colour set? + blo L956C ; brif first colour set + lda #8 ; flag second colour set + subb #4 ; adjust into basic range +L956C pshs a ; save CSS value + lda PMODE ; get graphics mode + rora ; 4 colour or 2? + bcc L957B ; brif 2 colour + tstb ; was it 0? + bne L9578 ; brif not +L9576 ldb #4 ; if so, make it 4 +L9578 decb ; convert to zero based +L9579 puls a,pc ; get back CSS value and return +L957B rorb ; is colour number odd? + bcs L9576 ; brif so - force all bits set colour + clrb ; force colour 0 if not + bra L9579 +; Set all pixel byte and active colour +L9581 jsr L959A ; set colour byte + jsr GETCCH ; is there something to evaluate? + beq L9598 ; brif not + cmpa #') ; )? + beq L9598 ; brif so + jsr SYNCOMMA ; force comma + cmpa #', ; another comma? + beq L9598 ; brif so + jsr L955A ; evaluate expression and return colour + bsr L95A2 ; save colour and pixel byte +L9598 jmp GETCCH ; re-fetch input character and return +L959A ldb FORCOL ; use foreground colour by default + tst SETFLG ; doing PRESET? + bne L95A2 ; brif not + ldb BAKCOL ; default to background colour +L95A2 stb WCOLOR ; save working colour + lda #0x55 ; consider a byte as 4 pixels + mul ; now all pixels are set to the same bit pattern + stb ALLCOL ; set all pixels byte + rts +L95AA bne L95CF ; brif graphics mode +L95AC pshs x,b,a ; save registers + ldx #SAMREG+8 ; point to middle of control register + sta 10,x ; reset display page to 0x400 + sta 8,x + sta 6,x + sta 4,x + sta 2,x + sta 1,x + sta -2,x + sta -4,x ; reset to alpha mode + sta -6,x + sta -8,x + lda PIA1+2 ; set VDG to alpha mode, colour set 0 + anda #7 + sta PIA1+2 + puls a,b,x,pc ;restore registers and return +L95CF pshs x,b,a ; save registers + lda PMODE ; get graphics mode + adda #3 ; offset to 3-7 (we don't use the bottom 3 modes) + ldb #0x10 ; shift to high 4 bits + mul + orb #0x80 ; set to graphics mode + orb CSSVAL ; set the desired colour set + lda PIA1+2 ; get get original PIA values + anda #7 ; mask off VDG control + pshs a ; merge with new VDG control + orb ,s+ + stb PIA1+2 ; set new VDG mode + lda BEGGRP ; get start of graphics page + lsra ; divide by two - pages are on 512 byte boundaries + jsr L960F ; set SAM control register + lda PMODE ; get graphics mode + adda #3 ; shift to VDG values + cmpa #7 ; PMODE 4? + bne L95F7 ; brif not + deca ; treat PMODE 4 the same as PMODE 3 +L95F7 bsr L95FB ; program SAM's VDG bits + puls a,b,x,pc ; restore registers and return +L95FB ldb #3 ; set 3 bits in register + ldx #SAMREG ; point to VDG control bits +L9600 rora ; get bit to set + bcc L9607 ; brif we need to clear the bit + sta 1,x ; set the bit + bra L9609 +L9607 sta ,x ; clear the bit +L9609 leax 2,x ; move to next bit + decb ; done all bits? + bne L9600 ; brif not + rts +L960F ldb #7 ; 7 screen address bits + ldx #SAMREG+6 ; point to screen address bits in SAM + bra L9600 ; go program SAM bits +L9616 lda PIA1+2 ; get VDG bits + anda #0xf7 ; keep everything but CSS bit + ora CSSVAL ; set correct CSS bit + sta PIA1+2 ; set desired CSS + rts +; PMODE command +PMODETOK cmpa #', ; is first argument missing? + beq L9650 ; brif so + jsr EVALEXPB ; evaluate PMODE number + cmpb #5 ; valid (0-4)? + bhs L966D ; brif not + lda GRPRAM ; get start of graphics memory +L962E sta BEGGRP ; set start of graphics page + aslb ; multiply mode by two (table has two bytes per entry) + ldu #L9706+1 ; point to lookup table + adda b,u ; add in number of 256 byte pages used for graphics screen + cmpa TXTTAB ; does it fit? + bhi L966D ; brif not + sta ENDGRP ; save end of graphics + leau -1,u ; point to bytes per horizontal row + lda b,u ; get bytes per row + sta HORBYT ; set it + lsrb ; restore PMODE value + stb PMODE ; set graphics mode + clra ; set background colour to 0 + sta BAKCOL + lda #3 ; set foreground colour to maximum (3) + sta FORCOL + jsr GETCCH ; is there a starting page number? + beq L966C ; brif not +L9650 jsr LB738 ; evaluate an expression following a comma + tstb ; page 0? + beq L966D ; brif so - not valid + decb ; zero-base it + lda #6 ; each graphics page is 6*256 + mul + addb GRPRAM ; add to start of graphics memory + pshs b ; save start of screen memory + addb ENDGRP ; add current and address + subb BEGGRP ; subtract current start (adds size of screen) + cmpb TXTTAB ; does it fit? + bhi L966D ; brif not + stb ENDGRP ; save new end of graphics + puls b ; get back start of graphics + stb BEGGRP ; set start of graphics +L966C rts +L966D jmp LB44A ; raise FC error +; SCREEN command +SCREEN cmpa #', ; is there a mode? + beq L967F ; brif no mode + jsr EVALEXPB ; get mode argument + tstb ; set Z if alpha + jsr L95AA ; set SAM/VDG for graphics mode + jsr GETCCH ; is there a second argument? + beq L966C ; brif not +L967F jsr LB738 ; evaluate , + tstb ; colour set 0? + beq L9687 ; brif so + ldb #8 ; flag for colour set 1 +L9687 stb CSSVAL ; set colour set + bra L9616 ; set up VDG +; PCLEAR command +PCLEAR jsr EVALEXPB ; evaulate number of pages requested + tstb ; 0? + beq L966D ; brif zero - not allowed + cmpb #9 ; more than 8? + bhs L966D ; brif so - not allowed + lda #6 ; there are 6 "pages" per graphics page + mul ; now B is the number of pages to reserve + addb GRPRAM ; add to start of graphics memory + tfr b,a ; now A is the MSB of the start of free memory + ldb #1 ; program memory always starts one above + tfr d,y ; save pointer to program memory + cmpd ENDGRP ; are we trying to deallocate the current graphics page? + blo L966D ; brif so (note that this prevents PCLEAR 0 anyway) + subd TXTTAB ; subtract out current start of basic program + addd VARTAB ; add in end of program - now D is new top of program + tfr d,x ; save new end of program + inca ; make some extra space (for stack) + subd FRETOP ; see if new top of program fits + bhs L966D ; brif there isn't enough space + jsr L80D0 ; adjust input pointer + nop ; space filler for 1.1 patch (the JSR above) + ldu VARTAB ; get end of program + stx VARTAB ; save new end of program + cmpu VARTAB ; is old end higher? + bhs L96D4 ; brif so +L96BD lda ,-u ; copy a byte upward + sta ,-x + cmpu TXTTAB ; at beginning? + bne L96BD ; brif not + sty TXTTAB ; save new start of program + clr -1,y ; there must always be a NUL before the program +L96CB jsr LACEF ; re-assign basic program addresses + jsr LAD26 ; reset variables and stack + jmp LAD9E ; return to interpretation loop +L96D4 ldu TXTTAB ; get start of program + sty TXTTAB ; save new start of program + clr -1,y ; there must be a NUL at the start of the program +L96DB lda ,u+ ; move a byte downward + sta ,y+ + cmpy VARTAB ; at the top of the program? + bne L96DB ; brif not + bra L96CB ; finish up +; Graphics initialization routine - this really should be up at the start of the ROM with the +; rest of the initialization code. +L96E6 ldb #0x1e ; set start of program to 0x1e00 ("PCLEAR 4") + stb TXTTAB + lda #6 ; graphics memory starts immediately after the screen +L96EC sta GRPRAM ; set graphics memory start + sta BEGGRP ; set start of current graphics page + clra ; set PMODE to 0 + sta PMODE + lda #16 ; 16 bytes per graphics row + sta HORBYT + lda #3 ; set foreground colour to 3 + sta FORCOL + lda #0x0c ; set ending graphics page (for PMODE 0) + sta ENDGRP + ldx TXTTAB ; get start of program + clr -1,x ; make sure there's a NUL before it +L9703 jmp LAD19 ; do a "NEW" +; PMODE data table (bytes per row and number of 256 byte pages required for a screen) +L9706 fcb 16,6 ; PMODE 0 + fcb 32,12 ; PMODE 1 + fcb 16,12 ; PMODE 2 + fcb 32,24 ; PMODE 3 + fcb 32,24 ; PMODE 4 +; Calculate absolute value of vertical coordinate difference +L9710 ldd VEREND ; get ending address + subd VERBEG ; get difference +L9714 bcc L9751 ; brif we didn't carry + pshs cc ; save status (C set if start > end) + jsr L9DC3 ; negate the difference to be positive + puls cc,pc ; restore C and return +; Calculate absolute value of horizontal coordinate difference +L971D ldd HOREND ; get end coordinate + subd HORBEG ; calculate difference + bra L9714 ; turn into absolute value +; PCOPY command +PCOPY bsr L973F ; fetch address of the source page + pshs d ; save address + ldb #0xa5 ; make sure we have TO + jsr LB26F + bsr L973F ; fetch address of the second page + puls x ; get back source + tfr d,u ; put destination into a pointer + ldy #0x300 ; 0x300 words to copy +L9736 ldd ,x++ ; copy a word + std ,u++ + leay -1,y ; done? + bne L9736 ; brif not + rts +L973F jsr EVALEXPB ; evaluate page number + tstb ; zero? + beq L9752 ; brif invalid page number +; BUG: this should be deferred until after the address is calculated at which point it should +; be bhs instead of bhi. There should also be a check to make sure the page number is less than +; or equal to 8 above so we don't have to test for overflows below. + cmpb TXTTAB ; is page number higher than start of program (BUG!) + bhi L9752 ; brif so - error + decb ; zero-base the page number + lda #6 ; 6 "pages" per graphics page + mul ; now we have proper number of "pages" for the offset + addb GRPRAM ; add start of graphics memory + exg a,b ; put MSB into A, 0 into B. +L9751 rts +L9752 jmp LB44A ; raise illegal function call +; GET command +GET clrb ; GET flag + bra L975A ; go on to the main body +PUT ldb #1 ; PUT flag +L975A stb VD8 ; save GET/PUT flag + jsr RVEC22 ; do the RAM hook dance (so Disk Basic can do its thing) +L975F cmpa #'@ ; @ before coordinates? + bne L9765 ; brif not + jsr GETNCH ; eat the @ +L9765 jsr L938F ; evaluate start/end coordinates + jsr SYNCOMMA ; make sure we have a comma + jsr L98CC ; get pointer to array + tfr X,D ; save descriptor pointer + ldu ,x ; get offset to next descriptor + leau -2,u ; move back to array name + leau d,u ; point to end of array + stu VD1 ; save end of data + leax 2,x ; point to number of dimensions + ldb ,x ; get dimension count + aslb ; two bytes per dimension size + abx ; now X points to start of data + stx VCF ; save start of array data + lda VALTYP ; is it numeric + bne L9752 ; brif not + clr VD4 ; set default graphic action to PSET + jsr GETCCH ; get input character + beq L97B7 ; brif no action flag + com VD4 ; flag action enabled + jsr SYNCOMMA ; make sure there's a comma + tst VD8 ; PUT? + bne L979A ; brif so + ldb #'G ; check for full graphics option + jsr LB26F + bra L97CA ; handle the rest of the process +L979A ldb #5 ; 5 legal actions for PUT + ldx #L9839 ; point to action table +L979F ldu ,x++ ; get "clear bit" action routine + ldy ,x++ ; get "set bit" action routine + cmpa ,x+ ; does token match? + beq L97AE ; brif so + decb ; checked all? + bne L979F ; brif not + jmp LB277 ; raise error +L97AE sty VD5 ; save set bit action address + stu VD9 ; save clear bit action address + jsr GETNCH ; munch the acton token + bra L97CA ; handle rest of process +L97B7 ldb #0xf8 ; mask for bottom three bits + lda PMODE ; get graphics mode + rora ; odd number mode? + bcc L97C0 ; brif even + ldb #0xfc ; bottom 2 bits mask +L97C0 tfr b,a ; save mask + andb HORBEG+1 ; round down the start address + stb HORBEG+1 + anda HOREND+1 ; round down end address + sta HOREND+1 +L97CA jsr L971D ; get horizontal size + bcc L97D3 ; brif end > start + ldx HOREND ; switch end in for start + stx HORBEG +L97D3 std HOREND ; save size + jsr L9710 ; calculate vertical size + bcc L97DE ; brif end > start + ldx VEREND ; swap in vertical end for the start + stx VERBEG +L97DE std VEREND ; save vertical size + lda PMODE ; get graphics mode + rora ; even? + ldd HOREND ; get difference + bcc L97EB ; brif even (2 colour) + addd HOREND ; add in size (double it) + std HOREND ; save adjusted end size +L97EB jsr L9420 ; normalize differences + ldd HOREND ; get end coord + ldx VEREND ; get end size + leax 1,x ; make vertical size inclusive + stx VEREND ; save it back + tst VD4 ; got "G" or GET action + bne L9852 ; brif given + lsra ; we're going for whole bytes here + rorb + lsra + rorb + lsra + rorb + addd #1 ; make it inclusive + std HOREND ; save new coordinate + jsr L9298 ; convert to screen address +L9808 ldb HOREND+1 ; get horizontal size + pshs x ; save screen position +L980C tst VD8 ; get/put flag + beq L9831 ; brif get + bsr L9823 ; bump array data pointer + lda ,u ; copy data from array to screen + sta ,x+ +L9816 decb ; are we done the row? + bne L980C ; brif not + puls x ; get screen address + jsr L92E9 ; move to next row + dec VEREND+1 ; done? + bne L9808 ; brif not +L9822 rts +L9823 ldu VCF ; get array data location + leau 1,u ; bump it + stu VCF ; save new array data location + cmpu VD1 ; did we hit the end of the array? + bne L9822 ; brif not +L982E jmp LB44A ; raise function call error +L9831 lda ,x+ ; get data from screen + bsr L9823 ; bump array data pointer + sta ,u ; put data in array + bra L9816 ; do the loopy thing +; PUT actions +L9839 fdb L9894,L989B ; PSET + fcb 0xbd + fdb L989B,L9894 ; PRESET + fcb 0xbe + fdb L98B1,L989B ; OR + fcb 0xb1 + fdb L9894,L98B1 ; AND + fcb 0xb0 + fdb L98A1,L98A1 ; NOT + fcb 0xa8 +L9852 addd #1 ; add to horiz difference + std HOREND ; save it + lda VD8 ; PUT? + bne L9864 ; brif so + ldu VD1 ; get end of array +L985D sta ,-u ; zero out a byte + cmpu VCF ; done? + bhi L985D ; brif not +L9864 jsr L9298 ; get screen address + ldb PMODE ; get graphics mode + rorb ; even? + bcc L986E ; brif so + anda #0xaa ; use as pixel mask for 4 colour mode +L986E ldb #1 ; set bit probe + ldy VCF ; point to start of array data +L9873 pshs x,a ; save screen address + ldu HOREND ; get horizontal size +L9877 pshs u,a ; save horizontal size and pixel mask + lsrb ; move bit probe right + bcc L9884 ; brif we didn't fall off a byte + rorb ; shift carry back in on the left + leay 1,y ; move ahead a byte in the array + cmpy VD1 ; end of array data? + beq L982E ; raise error if so +L9884 tst VD8 ; PUT? + beq L98A7 ; brif not + bitb ,y ; test bit in array + beq L9890 ; brif not set + jmp [VD5] ; do action routine for bit set +L9890 jmp [VD9] ; do action routine for bit clear +L9894 coma ; invert mask + anda ,x ; read screen data and reset the desired bit + sta ,x ; save on screen + bra L98B1 +L989B ora ,x ; merge pixel mask with screen data (turn on bit) + sta ,x ; save on screen + bra L98B1 +L98A1 eora ,x ; invert the pixel in the screen data + sta ,x ; save on screen + bra L98B1 +L98A7 bita ,x ; is the bit set? + beq L98B1 ; brif not - do nothing + tfr b,a ; get bit probe + ora ,y ; turn on proper bit in data + sta ,y +L98B1 puls a,u ; get back array address + jsr L92ED ; move screen address to the right + leau -1,u ; account for consumed pixel + cmpu ZERO ; done yet? + bne L9877 ; brif not + ldx 1,s ; get start of row back + lda HORBYT ; get number of bytes per row + leax a,x ; move ahead one line + puls a ; get back screen pixel mask + leas 2,s ; lose the screen pointer + dec VEREND+1 ; done all rows? + bne L9873 ; brif not + rts +L98CC jsr LB357 ; evaluate a variable + ldb ,-x ; get variable name + lda ,-x + tfr d,u ; save it + ldx ARYTAB ; get start of arrays +L98D7 cmpx ARYEND ; end of arrays? + lbeq LB44A ; brif not found + cmpu ,x ; correct variable? + beq L98E8 ; brif so + ldd 2,x ; get array size + leax d,x ; move to next array + bra L98D7 ; check this array +L98E8 leax 2,x ; move pointer to the array header + rts ; obviously this rts is not needed +L98EB rts +; PAINT command +PAINT cmpa #'@ ; do we have @ before coords? + bne L98F2 ; brif not + jsr GETNCH ; eat the @ +L98F2 jsr L93B2 ; evaluate coordinates + jsr L931D ; normalize coordinates + lda #1 ; PSET flag (use working colour) + sta SETFLG + jsr L9581 ; parse colour and set working colour, etc. + ldd WCOLOR ; get working colour and all pixels byte + pshs d ; save them + jsr GETCCH ; is there anything else? + beq L990A ; brif not + jsr L9581 ; evaluate border colour +L990A lda ALLCOL ; get border colour all pixel byte + sta VD8 ; save border colour pixel byte + puls d ; get back working colour details + std WCOLOR + clra ; store a "stop" frame on the stack so PAINT knows when it's finished unwinding + pshs u,x,b,a + jsr L9522 ; set up starting coordinates + jsr L928F ; point to pixel mask routine + stu VD9 ; save pixel mask routine + jsr L99DF ; paint from current horizontal coordinate to zero (left) + beq L9931 ; brif hit border immediately + jsr L99CB ; paint from current horizontal coordinate upward (right) + lda #1 ; set direction to "down" + sta VD7 + jsr L99BA ; save "down" frame + neg VD7 ; set direction to "up" + jsr L99BA ; save "up" frame +L9931 sts TMPSTK ; save stack pointer +L9934 tst CHGFLG ; did the paint change anything? + bne L993B ; brif so + lds TMPSTK ; get back stack pointer +L993B puls a,b,x,u ; get frame from stack + clr CHGFLG ; mark nothing changed + sts TMPSTK ; save stack pointer + leax 1,x ; move start coordinate right + stx HORBEG ; save new coordinate + stu VD1 ; save length of line + sta VD7 ; save up/down flag + beq L98EB ; did we hit the "stop" frame? + bmi L9954 ; brif negative going (up)? + incb ; bump vertical coordinate + cmpb VD6 ; at end? + bls L9958 ; brif not + clrb ; set vertical to 0 (wrap around) +L9954 tstb ; did we wrap? + beq L9934 ; do another block if so + decb ; move up a row +L9958 stb VERBEG+1 ; save vertical coordinate + jsr L99DF ; paint from horizontal to 0 + beq L996E ; brif we hit the border immediately + cmpd #3 ; less than 3 pixels? + blo L9969 ; brif so + leax -2,x ; move two pixels left + bsr L99A1 ; save paint block on the stack +L9969 jsr L99CB ; continue painting to the right +L996C bsr L99BA ; save paint data frame +L996E coma ; complement length of line just painted and add to length of line + comb +L9970 addd VD1 ; save difference between this line and parent line + std VD1 + ble L998C ; brif parent line is shorter + jsr L9506 ; bump horizontal coordinate + jsr L9A12 ; see if we bounced into the border + bne L9983 ; brif not border + ldd #-1 ; move left + bra L9970 ; keep looking +L9983 jsr L9514 ; move horizontally left + bsr L99C6 ; save horizontal coordinate + bsr L99E8 ; paint right + bra L996C ; save paint block and do more +L998C jsr L9506 ; bump horizontal coordinate + leax d,x ; point to right end of parent line + stx HORBEG ; set as curent coordinate + coma ; get amount we extend past parent line + comb + subd #1 + ble L999E ; brif doesn't extend + tfr d,x ; save length of line + bsr L99A1 ; save paint frame +L999E jmp L9934 +L99A1 std VCB ; save number of pixels painted + puls y ; get return address + ldd HORBEG ; get horizontal coordinate + pshs x,b,a ; save horizontal coordinate and pointer + lda VD7 ; get up/down flag + nega ; reverse it +L99AC ldb VERBEG+1 ; get vertical coordainte + pshs b,a ; save vertical coord and up/down flag + pshs y ; put return address back + ldb #2 ; make sure we haven't overflowed memory + jsr LAC33 + ldd VCB ; get line length back + rts +L99BA std VCB ; save length of painted line + puls y ; get return address + ldd HOREND ; get start coord + pshs x,b,a ; save horizontal start and length + lda VD7 ; get up/down flag + bra L99AC ; finish up with the stack +L99C6 ldx HORBEG ; save current horizontal coord and save it + stx HOREND + rts +L99CB std VCD ; save number of pixels painted + ldy HOREND ; get last horizontal start + bsr L99C6 ; save current coordinate + sty HORBEG ; save coordinate + bsr L99E8 ; paint a line + ldx VCD ; get number painted + leax d,x ; add to the number painted going the other way + addd #1 ; now D is length of line + rts +L99DF jsr L99C6 ; put starting coordinate in end + ldy #L9514 ; decrement horizontal coordinate address + bra L99EE ; go paint line +L99E8 ldy #L9506 ; increment horizontal coordinate address + jsr ,y ; bump coordinate +L99EE ldu ZERO ; initialize pixel count + ldx HORBEG ; get starting coordinate +L99F2 bmi L9A0B ; brif out of range for hoizontal coordinate + cmpx VD3 ; at end? + bhi L9A0B ; brif right of max + pshs u,y ; save counter and inc/dec routine pointer + bsr L9A12 ; at border? + beq L9A09 ; brif so + jsr L9377 ; set pixel to paint colour + puls y,u ; restore counter and inc/dec/pointer + leau 1,u ; bump number of painted pixels + jsr ,y ; inc/dec screen address + bra L99F2 ; go do another pixel +L9A09 puls y,u ; get back counter and inc/dec routine +L9A0B tfr u,d ; save count in D + tfr d,x ; and in X + subd ZERO ; set flags on D (smaller/faster than cmpd ZERO) + rts +L9A12 jsr [VD9] ; get the screen address + tfr a,b ; save pixel mask + andb VD8 ; set pixel to border colour + pshs b,a ; save mask and border + anda ,x ; mask current pixel into A + cmpa 1,s ; does it match border? Z=1 if so + puls a,b,pc ; restore mask, border pixel, and return +; PLAY command +; This is here mixed in with the graphics package because it shares some machinery with DRAW. +PLAY ldx ZERO ; default values for note length, etc. + ldb #1 + pshs x,b ; save default values + jsr LB156 ; evaluate argument + clrb ; enable DA and sound output + jsr LA9A2 + jsr LA976 +L9A32 jsr LB654 ; fetch PLAY string details + bra L9A39 ; go evaluate the string +L9A37 puls b,x ; get back play string details +L9A39 stb VD8 ; save length of string + beq L9A37 ; brif end of string + stx VD9 ; save start of string + lbeq LA974 ; brif NULL string - disable sound and return +L9A43 tst VD8 ; have anything left? + beq L9A37 ; brif not + jsr L9B98 ; get command character + cmpa #'; ; command separator? + beq L9A43 ; brif so - ignore it + cmpa #'' ; '? + beq L9A43 ; brif so - ignore it + cmpa #'X ; execuate sub string? + lbeq L9C0A ; brif so - handle it + bsr L9A5C ; handle other commands + bra L9A43 ; look for more stuff +L9A5C cmpa #'O ; octave? + bne L9A6D ; brif not + ldb OCTAVE ; get current octave + incb ; 1-base it + bsr L9AC0 ; get value if present + decb ; zero-base it + cmpb #4 ; valid octave? + bhi L9ACD ; raise error if not + stb OCTAVE ; save new octave + rts +L9A6D cmpa #'V ; volume? + bne L9A8B ; brif not + ldb VOLHI ; get current high volume limit + lsrb ; shift 2 bits right (DA is 6 bits in high bits) + lsrb + subb #31 ; subtract out mid value offset + bsr L9AC0 ; read argument + cmpb #31 ; maximum range is 31 + bhi L9ACD ; brif out of range + aslb ; adjust back in range + aslb + pshs b ; save new volume + ldd #0x7e7e ; midrange value for both high and low + adda ,s ; add new volume to high limit + subb ,s+ ; subtract volume from low limit + std VOLHI ; save new volume limits (sets high and low amplitudes) + rts +L9A8B cmpa #'L ; note length? + bne L9AB2 ; brif not + ldb NOTELN ; get current length + bsr L9AC0 ; read parameter + tstb ; resulting length 0? + beq L9ACD ; brif so - problem + stb NOTELN ; save new length + clr DOTVAL ; reset note timer scale factor +L9A9A bsr L9A9F ; check for dot + bcc L9A9A ; brif there was one + rts +L9A9F tst VD8 ; check length + beq L9AAD ; brif zero + jsr L9B98 ; get command character + cmpa #'. ; dot? + beq L9AAF ; brif so + jsr L9BE2 ; move input back and bump length +L9AAD coma ; set C to indicate nothing found + rts +L9AAF inc DOTVAL ; bump number of dots + rts +L9AB2 cmpa #'T ; tempo? + bne L9AC3 ; brif not + ldb TEMPO ; get current tempo + bsr L9AC0 ; parse tempo argument + tstb ; 0? + beq L9ACD ; brif so - invalid + stb TEMPO ; save new tempo + rts +L9AC0 jmp L9BAC ; evaluate various operators +L9AC3 cmpa #'P ; pause? + bne L9AEB ; brif not + jsr L9CCB ; evaluate parameter + tstb ; is the pause number 0? + bne L9AD0 ; brif not +L9ACD jmp LB44A ; raise FC error +L9AD0 lda DOTVAL ; save current volume and note scale + ldx VOLHI + pshs x,a + lda #0x7e ; drop DA to mid range + sta VOLHI + sta VOLLOW + clr DOTVAL + bsr L9AE7 ; go play a "silence" + puls a,x ; restore volume and note scale + sta DOTVAL + stx VOLHI + rts +L9AE7 clr ,-s ; set not number 0 + bra L9B2B ; go play it +L9AEB cmpa #'N ; N for "note"? + bne L9AF2 ; brif not - it's optional + jsr L9B98 ; skip the "N" +L9AF2 cmpa #'A ; is it a valid note? + blo L9AFA ; brif not + cmpa #'G ; is it above the note range? + bls L9AFF ; brif not - valid note +L9AFA jsr L9BBE ; evaluate a number + bra L9B22 ; process note value +L9AFF suba #'A ; normalize note number to 0 + ldx #L9C5B ; point to note number lookup table + ldb a,x ; get not number + tst VD8 ; any command characters left? + beq L9B22 ; brif not + jsr L9B98 ; get character + cmpa #'# ; sharp? + beq L9B15 ; brif so + cmpa #'+ ; also sharp? + bne L9B18 ; brif not +L9B15 incb ; add one half tone + bra L9B22 +L9B18 cmpa #'- ; flat? + bne L9B1F ; brif not + decb ; subtract one half tone + bra L9B22 +L9B1F jsr L9BE2 ; back up command pointer +L9B22 decb ; adjust note number (zero base it) + cmpb #11 ; is it valid? + bhi L9ACD ; raise error if not + pshs b ; save note value + ldb NOTELN ; get note length +L9B2B lda TEMPO ; get tempo value + mul ; calculate note duration + std VD5 ; save duration + leau 1,s ; point to where the stack goes after we're done + lda OCTAVE ; get current octave + cmpa #1 ; 0 or 1? + bhi L9B64 ; brif not + ldx #L9C62 ; point to delay table + ldb #2*12 ; 24 bytes per octave + mul ; now we have the base address + abx ; now X points to the octave base + puls b ; get back note value + aslb ; two bytes per delay + abx ; now we're pointing to the delay + leay ,x ; save pointer to note value + bsr L9B8C ; calculate note timer value + std PLYTMR ; set timer for note playing (IRQ will count this down) +L9B49 bsr L9B57 ; set to mid range and delay + lda VOLHI ; get high value + bsr L9B5A ; set to high value and delay + bsr L9B57 ; set to mid range and delay + lda VOLLOW ; get low value + bsr L9B5A ; set to low value and delay + bra L9B49 ; do it again (IRQ will break the loop) +L9B57 lda #0x7e ; mid value for DA with RS232 marking + nop ; a delay to fine tune frequencies +L9B5A sta PIA1 ; set DA + ldx ,y ; get delay value +L9B5F leax -1,x ; count down + bne L9B5F ; brif not done yet + rts +L9B64 ldx #L9C92-2*12 ; point to delay table for octaves 2+ + ldb #12 ; 12 bytes per octave + mul ; now we have the offset to the desired octave + abx ; now we point to the start of the octave + puls b ; get back note value + abx ; now we point to the delay value + bsr L9B8C ; calculate timer value + std PLYTMR ; set play timer (IRQ counts this down) +L9B72 bsr L9B80 ; send mid value and delay + lda VOLHI ; get high value + bsr L9B83 ; send high value and delay + bsr L9B80 ; send low value and delay + lda VOLLOW ; get low value + bsr L9B83 ; send low value and delay + bra L9B72 ; do it again (IRQ will break the loop) +L9B80 lda #0x7e ; mid range value with RS232 marking + nop ; fine tuning delay +L9B83 sta PIA1 ; set DA + lda ,x ; get delay value +L9B88 deca ; count down + bne L9B88 ; brif not done + rts +L9B8C ldb #0xff ; base timer value + lda DOTVAL ; get number of dots + beq L9B97 ; use default value if 0 + adda #2 ; add in constant timer factor + mul ; multiply scale by base + lsra ; divide by two - each increment will increase note timer by 128 + rorb +L9B97 rts +L9B98 pshs x ; save register +L9B9A tst VD8 ; do we have anything left? + beq L9BEB ; brif not - raise error + ldx VD9 ; get parsing address + lda ,x+ ; get character + stx VD9 ; save pointer + dec VD8 ; account for character consumed + cmpa #0x20 ; space? + beq L9B9A ; brif so - skip it + puls x,pc ; restore register and return +L9BAC bsr L9B98 ; get character + cmpa #'+ ; add one? + beq L9BEE ; brif so + cmpa #'- ; subtract one? + beq L9BF2 ; brif so + cmpa #'> ; double? + beq L9BFC ; brif so + cmpa #'< ; halve? + beq L9BF7 ; brif so +L9BBE cmpa #'= ; variable equate? + beq L9C01 ; brif so + jsr L90AA ; clear carry if numeric + bcs L9BEB ; brif not numeric + clrb ; initialize value to 0 +L9BC8 suba #'0 ; remove ASCII bias + sta VD7 ; save digit + lda #10 ; make room for digit + mul + tsta ; did we overflow 8 bits? + bne L9BEB ; brif so + addb VD7 ; add in digit + bcs L9BEB ; brif that overflowed + tst VD8 ; more digits? + beq L9BF1 ; brif not + jsr L9B98 ; get character + jsr L90AA ; clear carry if numeric + bcc L9BC8 ; brif another digit +L9BE2 inc VD8 ; unaccount for character just read + ldx VD9 ; move pointer back + leax -1,x + stx VD9 + rts +L9BEB jmp LB44A ; raise FC error +L9BEE incb ; bump param + beq L9BEB ; brif overflow +L9BF1 rts +L9BF2 tstb ; already zero? + beq L9BEB ; brif so - underflow + decb ; decrease parameter + rts +L9BF7 tstb ; already at 0? + beq L9BEB ; brif so - raise error + lsrb ; halve it + rts +L9BFC tstb ; will it overflow? + bmi L9BEB ; brif so + aslb ; double it + rts +L9C01 pshs u,y ; save registers + bsr L9C1B ; interpret command string as a variable + jsr LB70E ; convert it to an 8 bit number + puls y,u,pc ; restore registers and return +L9C0A jsr L9C1B ; evaluate expression in command string + ldb #2 ; room for 4 bytes? + jsr LAC33 + ldb VD8 ; get the command length and pointer + ldx VD9 + pshs x,b ; save them + jmp L9A32 ; go process the sub string +L9C1B ldx VD9 ; get command pointer + pshs x ; save it + jsr L9B98 ; get input character + jsr LB3A2 ; set carry if not alpha + bcs L9BEB ; brif not a variable reference +L9C27 jsr L9B98 ; get command character + cmpa #'; ; semicolon? + bne L9C27 ; keep scanning if not + puls x ; get back start of variable string + ldu CHARAD ; get current interpreter input pointer + pshs u ; save it + stx CHARAD ; point interpreter at command string + jsr LB284 ; evaluate expression as string + puls x ; restore interpeter input pointer + stx CHARAD + rts +; The bit of Extended Basic's IRQ routine that handles PLAY. Note that everything after +; clr PLYTMR+1 could be replaced with a simple lds 8,s followed by rts. +L9C3E clra ; make sure DP is set to 0 + tfr a,dp + ldd PLYTMR ; is PLAY running? + lbeq LA9BB ; brif not - transfer control on the Color Basic's routine + subd VD5 ; subtract out the interval + std PLYTMR ; save new timer value + bhi L9C5A ; brif it isn't <= 0 + clr PLYTMR ; disable the timer + clr PLYTMR+1 + puls a ; get saved CC + lds 7,s ; set stack to saved U value + anda #0x7f ; clear E flag (to return minimal state) + pshs a ; set fake "FIRQ" stack frame +L9C5A rti +L9C5B fcb 10,12,1,3,5,6,8 ; table of half tone numbers for notes A-G +L9C62 fdb 0x01a8,0x0190,0x017a,0x0164 ; delays for octave 1 + fdb 0x0150,0x013d,0x012b,0x011a + fdb 0x010a,0x00fb,0x00ed,0x00df + fdb 0x00d3,0x00c7,0x00bb,0x00b1 ; delays for octave 2 + fdb 0x00a6,0x009d,0x0094,0x008b + fdb 0x0083,0x007c,0x0075,0x006e +L9C92 fcb 0xa6,0x9c,0x93,0x8b ; delays for octave 3 + fcb 0x83,0x7b,0x74,0x6d + fcb 0x67,0x61,0x5b,0x56 + fcb 0x51,0x4c,0x47,0x43 ; delays for octave 4 + fcb 0x3f,0x3b,0x37,0x34 + fcb 0x31,0x2e,0x2b,0x28 + fcb 0x26,0x23,0x21,0x1f ; delays for octave 5 + fcb 0x1d,0x1b,0x19,0x18 + fcb 0x16,0x14,0x13,0x12 +; DRAW command +DRAW ldx ZERO ; create an empty "DRAW" frame + ldb #1 + pshs x,b + stb SETFLG ; set to "PSET" mode + stx VD5 ; clear update and draw flag + jsr L959A ; set active colour byte + jsr LB156 ; evaluate command string +L9CC6 jsr LB654 ; fetch command string details + bra L9CD3 ; interpret the command string +L9CCB jsr L9B98 ; fetch command character + jmp L9BBE ; evaluate a number +L9CD1 puls b,x ; get previously saved command string +L9CD3 stb VD8 ; save length counter + beq L9CD1 ; brif end of string + stx VD9 ; save pointer + lbeq L9DC7 ; brif overall end of command +L9CDD tst VD8 ; are we at the end of the string? + beq L9CD1 ; brif so - return to previous string + jsr L9B98 ; get command character + cmpa #'; ; semicolon? + beq L9CDD ; brif so - ignore it + cmpa #'' ; '? + beq L9CDD ; brif so - ignore that too + cmpa #'N ; update position toggle? + bne L9CF4 ; brif not + com VD5 ; toggle update position flag + bra L9CDD ; get on for another command +L9CF4 cmpa #'B ; blank flag? + bne L9CFC ; brif not + com VD6 ; toggle blank flag + bra L9CDD ; get on for another command +L9CFC cmpa #'X ; substring? + lbeq L9D98 ; brif so - execute command + cmpa #'M ; move draw position? + lbeq L9E32 ; brif so + pshs a ; save command character + ldb #1 ; default value if no number follows + tst VD8 ; is there something there? + beq L9D21 ; brif not + jsr L9B98 ; get character + jsr LB3A2 ; set C if not alpha + pshs cc ; save alpha state + jsr L9BE2 ; move back pointer + puls cc ; get back alpha flag + bcc L9D21 ; brif it's alpha + bsr L9CCB ; evaluate a number +L9D21 puls a ; get command back + cmpa #'C ; color change? + beq L9D4F ; brif so + cmpa #'A ; angle? + beq L9D59 ; brif so + cmpa #'S ; scale? + beq L9D61 ; brif so + cmpa #'U ; up? + beq L9D8F ; brif so + cmpa #'D ; down? + beq L9D8C ; brif so + cmpa #'L ; left? + beq L9D87 ; brif so + cmpa #'R ; right? + beq L9D82 ; brif so + suba #'E ; normalize the half cardinals to 0 + beq L9D72 ; brif E (45°) + deca ; F (135°?) + beq L9D6D ; brif so + deca ; G (225°?) + beq L9D7B ; brif so + deca ; H (315°?) + beq L9D69 ; brif so +L9D4C jmp LB44A ; raise FC error +L9D4F jsr L955D ; adjust colour for PMODE + stb FORCOL ; save new foreground colour + jsr L959A ; set up working colour and all pixels byte +L9D57 bra L9CDD ; go process another command +L9D59 cmpb #4 ; only 3 angles are valid + bhs L9D4C ; brif not valid + stb ANGLE ; save new angle + bra L9D57 ; go process another command +L9D61 cmpb #63 ; only 64 scale values are possible + bhs L9D4C ; brif out of range + stb SCALE ; save new scale factor + bra L9D57 ; go process another command +L9D69 clra ; make horizontal negative + bsr L9DC4 + skip1 +L9D6D clra ; keep horizontal distance positive + tfr d,x ; make horizontal distance and vertical distance the same + bra L9DCB ; go do the draw thing +L9D72 clra ; zero extend horizontal distance + tfr d,x ; set it as vertical + bsr L9DC4 ; negate horizontal distance + exg d,x ; swap directions (vertical is now negative) + bra L9DCB ; go do the draw thing +L9D7B clra ; zero extend horizontal distance + tfr d,x ; copy horizontal to vertical + bsr L9DC4 ; negate horizontal + bra L9DCB ; go do the drawing thing +L9D82 clra ; zero extend horizontal distance +L9DB3 ldx ZERO ; no vertical distance + bra L9DCB ; go do the drawing things +L9D87 clra ; zero extend horizontal + bsr L9DC4 ; negate horizontal + bra L9DB3 ; zero out vertical and do the drawing thing +L9D8C clra ; zero extend distance + bra L9D92 ; make the distance vertical and zero out horizontal +L9D8F clra ; zero extend distance + bsr L9DC4 ; negate distance +L9D92 ldx ZERO ; zero out vertical distance + exg x,d ; swap vertical and horizontal + bra L9DCB ; go do the drawing thing +L9D98 jsr L9C1B ; evaluate substring expression + ldb #2 ; is there enough room for the state? + jsr LAC33 + ldb VD8 ; save current command string state + ldx VD9 + pshs x,b + jmp L9CC6 ; go evaluate the sub string +L9DA9 ldb SCALE ; get scale factor + beq L9DC8 ; brif zero - default to full size + clra ; zero extend + exg d,x ; put distance somewhere useful + sta ,-s ; save MS of distance + bpl L9DB6 ; brif positive distance + bsr L9DC3 ; negate the distance +L9DB6 jsr L9FB5 ; multiply D and X + tfr u,d ; save ms bytes in D + lsra ; divide by 2 + rorb +L9DBD lsra ; ...divide by 4 + rorb + tst ,s+ ; negative distance? + bpl L9DC7 ; brif it was positive +L9DC3 nega ; negate D +L9DC4 negb + sbca #0 +L9DC7 rts +L9DC8 tfr x,d ; copy unchanged sitance to D + rts +L9DCB pshs b,a ; save horizontal distance + bsr L9DA9 ; apply scale factor to vertical + puls x ; get horizontal distance + pshs b,a ; save scaled vertical + bsr L9DA9 ; apply scale to horizontal + puls x ; get back vertical distance + ldy ANGLE ; get draw angle and scale + pshs y ; save them +L9DDC tst ,s ; is there an angle? + beq L9DE8 ; brif no angle + exg x,d ; swap distances + bsr L9DC3 ; negate D + dec ,s ; account for one tick around the rotation + bra L9DDC ; see if we're there yet +L9DE8 puls y ; get angle and scale back + ldu ZERO ; default end position (horizontal) is 0 + addd HORDEF ; add default horizontal to horizontal distance + bmi L9DF2 ; brif we went negative + tfr d,u ; save calculated end coordindate +L9DF2 tfr x,d ; get vertical distance somewhere useful + ldx ZERO ; default vertical end is 0 + addd VERDEF ; add distance to default vertical start + bmi L9DFC ; brif negative - use 0 + tfr d,x ; save calculated end coordinate +L9DFC cmpu #256 ; is horizontal in range? + blo L9E05 ; brif su + ldu #255 ; maximize it +L9E05 cmpx #192 ; is vertical in range? + blo L9E0D ; brif so + ldx #191 ; maximize it +L9E0D ldd HORDEF ; set starting coordinates for the line + std HORBEG + ldd VERDEF + std VERBEG + stx VEREND ; set end coordinates + stu HOREND + tst VD5 ; are we updating position? + bne L9E21 ; brif not + stx VERDEF ; update default coordinates + stu HORDEF +L9E21 jsr L9420 ; normalize coordindates + tst VD6 ; are we drawing something? + bne L9E2B ; brif not + jsr L94A1 ; draw the line +L9E2B clr VD5 ; reset draw and update flags + clr VD6 + jmp L9CDD ; do another command +L9E32 jsr L9B98 ; get a command character + pshs a ; save it + jsr L9E5E ; evaluate horizontal distance + pshs b,a ; save it + jsr L9B98 ; get character + cmpa #', ; comma between coordinates? + lbne L9D4C ; brif not - raise error + jsr L9E5B ; evaluate vertical distance + tfr d,x ; save vertical distance + puls u ; get horizontal distance + puls a ; get back first command character + cmpa #'+ ; was it + at start? + beq L9E56 ; brif +; treat values as positive + cmpa #'- ; was it -? + bne L9DFC ; brif not - treat it as absolute +L9E56 tfr u,d ; put horizontal distance somewhere useful + jmp L9DCB ; move draw position (relative) +L9E5B jsr L9B98 ; get input character +L9E5E cmpa #'+ ; leading +? + beq L9E69 ; brif so + cmpa #'- ; leading -? + beq L9E6A ; brif so - negative + jsr L9BE2 ; move pointer back one +L9E69 clra ; 0 for +, nonzero for - +L9E6A pshs a ; save sign flag + jsr L9CCB ; evaluate number + puls a ; get sign flag + tsta ; negative? + beq L9E78 ; brif not + clra ; zero extend and negate + negb + sbca #0 +L9E78 rts +; Table of sines and cosines for CIRCLE +L9E79 fdb 0x0000,0x0001 ; subarc 0 + fdb 0xfec5,0x1919 ; subarc 1 + fdb 0xfb16,0x31f2 ; subarc 2 + fdb 0xf4fb,0x4a51 ; subarc 3 + fdb 0xec84,0x61f9 ; subarc 4 + fdb 0xe1c7,0x78ae ; subarc 5 + fdb 0xd4dc,0x8e3b ; subarc 6 + fdb 0xc5e5,0xa269 ; subarc 7 + fdb 0xb506,0xb506 ; subarc 8 +; CIRCLE command +; The circle is drawn as a 64 sided polygon (64 LINE commands essentially) +CIRCLE cmpa #'@ ; is there an @ before coordinates? + bne L9EA3 ; brif not + jsr GETNCH ; eat the @ +L9EA3 jsr L9522 ; get max coordinates for screen + jsr L93B2 ; parse coordinates for circle centre + jsr L931D ; normalize the start coordinates + ldx ,u ; get horizontal coordinate + stx VCB ; save it + ldx 2,u ; get vertical coordinate + stx VCD ; saveit + jsr SYNCOMMA ; make sure we have a comma + jsr LB73D ; evaluate radius expression + ldu #VCF ; point to temp storage + stx ,u ; save radius + jsr L9320 ; normalize radius + lda #1 ; default to PSET + sta SETFLG + jsr L9581 ; evaluate the colour expression + ldx #0x100 ; height/width default value + jsr GETCCH ; is there a ratio? + beq L9EDF ; brif not + jsr SYNCOMMA ; make sure we have a comma + jsr LB141 ; evaluate the ratio + lda FP0EXP ; multiply ratio by 256 + adda #8 + sta FP0EXP + jsr LB740 ; evaluate ratio to X (fraction part in LSB) +L9EDF lda PMODE ; get graphics mode + bita #2 ; is it even? + beq L9EE9 ; brif so + tfr x,d ; double the ratio + leax d,x +L9EE9 stx VD1 ; save height/width ratio + ldb #1 ; set the SET flag to PSET + stb SETFLG + stb VD8 ; set first time flag (set to 0 after arc drawn) + jsr L9FE2 ; evaluate circle starting point (octant, subarc) + pshs b,a ; save startpoint + jsr L9FE2 ; evaluate circle end point (octant, subarc) + std VD9 ; save endp oint + puls a,b +L9EFD pshs b,a ; save current circle position + ldx HOREND ; move end coordinates to start coordinates + stx HORBEG + ldx VEREND + stx VERBEG + ldu #L9E79+2 ; point to sine/cosine table + anda #1 ; even octant? + beq L9F11 ; brif so + negb ; convert 0-7 to 8-1 for odd octants + addb #8 +L9F11 aslb ; four bytes per table entry + aslb + leau b,u ; point to correct table entry + pshs u ; save sine/cosine table entry pointer + jsr L9FA7 ; calculate horizontal offset + puls u ; get back table entry pointer + leau -2,u ; move to cosine entry + pshs x ; save horizontal offset + jsr L9FA7 ; calculate vertical offset + puls y ; put horizontal in Y + lda ,s ; get octant number + anda #3 ; is it 0 or 4? + beq L9F31 ; brif so + cmpa #3 ; is it 3 or 7? + beq L9F31 ; brif so + exg x,y ; swap horizontal and vertical +L9F31 stx HOREND ; save horizontal offset + tfr y,x ; put vertical offset in X + ldd VD1 ; get height/width ratio + jsr L9FB5 ; multiply vertical by h/w ratio + tfr y,d ; save the product to D + tsta ; did it overflow? + lbne LB44A ; brif so + stb VEREND ; save vertical coordinate MSB + tfr u,d ; get LSW of product + sta VEREND+1 ; save LSB of integer part (we have 8 bits of fraction in the ratio) + lda ,s ; get octant + cmpa #2 ; is it 0 or 1? + blo L9F5B ; brif so + cmpa #6 ; is it 6 or 7? + bhs L9F5B ; brif so + ldd VCB ; get horizontal centre + subd HOREND ; subtract horizontal displacement + bcc L9F68 ; brif we didn't overflow the screen + clra ; zero out coordinate if we overflowed the screen + clrb + bra L9F68 +L9F5B ldd VCB ; get horizontal coordinate of the centre + addd HOREND ; add displacement + bcs L9F66 ; brif overlod + cmpd VD3 ; larger than max horizontal coord? + blo L9F68 ; brif not +L9F66 ldd VD3 ; maximize the coordinate +L9F68 std HOREND ; save horizontal ending coordainte + lda ,s ; get octant + cmpa #4 ; is it 0-3? + blo L9F7A ; brif so + ldd VCD ; get vertical coordinate of centre + subd VEREND ; subtract displacement + bcc L9F87 ; brif we didn't overflow the screen + clra ; minimize to top of screen + clrb + bra L9F87 +L9F7A ldd VCD ; get vertical centre coordinate + addd VEREND ; add displacement + bcs L9F85 ; brif we overflowed the screen + cmpd VD5 ; did we go past max coordinate? + blo L9F87 ; brif not +L9F85 ldd VD5 ; maximize the coordinate +L9F87 std VEREND ; save end coordinate + tst VD8 ; check first time flag + bne L9F8F ; do not draw if first time through (it was setting start coord) + bsr L9FDF ; draw the line +L9F8F puls a,b ; get arc number and sub arc + lsr VD8 ; get first time flag value (and clear it!) + bcs L9F9A ; do not check for end point after drawing for first coordinate + cmpd VD9 ; at end point? + beq L9FA6 ; brif drawing finished +L9F9A incb ; bump arc counter + cmpb #8 ; done 8 arcs? + bne L9FA3 ; brif not + inca ; bump octant + clrb ; reset subarc number + anda #7 ; make sure octant number stays in 0-7 range +L9FA3 jmp L9EFD ; go do another arc +L9FA6 rts +L9FA7 ldx VCF ; get radius + ldd ,u ; get sine/cosine table entry + beq L9FB4 ; brif 0 - offset = radius + subd #1 ; adjust values to correct range + bsr L9FB5 ; multiply radius by sine/cosine + tfr y,x ; resturn result in X +L9FB4 rts +L9FB5 pshs u,y,x,b,a ; save registers and reserve temp space + clr 4,s ; reset overflow byte (YH) + lda 3,s ; calcuate B*XL + mul + std 6,s ; put in "U" + ldd 1,s ; calculate B*XH + mul + addb 6,s ; accumluate with previous product + adca #0 + std 5,s ; save in YL,UH + ldb ,s ; calculate A*XL + lda 3,s + mul + addd 5,s ; accumulate with previous partical product + std 5,s ; save in YL,UH + bcc L9FD4 ; brif no carry + inc 4,s ; bump YH for carry +L9FD4 lda ,s ; calculate A*XH + ldb 2,s + mul + addd 4,s ; accumulate with previous partial product + std 4,s ; save in Y (we can't have a carry here) + puls a,b,x,y,u,pc ; restore multiplicands and return results +L9FDF jmp L94A1 ; go draw a line +L9FE2 clrb ; default arc number (0) + jsr GETCCH ; is there something there for a value? + beq L9FF8 ; brif not + jsr SYNCOMMA ; evaluate , + expression + jsr LB141 + lda FP0EXP ; multiply by 64 + adda #6 + sta FP0EXP + jsr LB70E ; get integer value of circle fraction + andb #0x3f ; max value of 63 +L9FF8 tfr b,a ; save arc value in A to calculate octant + andb #7 ; calculate subarc + lsra ; calculate octant + lsra + lsra + rts diff -r 000000000000 -r 605ff82c4618 secb.s --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/secb.s Sat Dec 08 19:57:01 2018 -0700 @@ -0,0 +1,11797 @@ + *pragma nolist + include defs.s +DC0DC equ 0xC0DC ; needed for Disk Basic path jump backs + *pragma list +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; EXTENDED COLOR BASIC ROM area +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + org EXBAS + fcc 'EX' ; magic number that Color Basic uses to identify the presence of Extended Basic +L8002 ldx #L80DE ; point to command interpretation table information + ldu #COMVEC+10 ; point to command interpretation table location + ldb #10 ; 10 bytes to move + jsr LA59A ; copy command interpretation table + ldx #LB277 ; initialize Disk Basic's entries to error + stx 3,u + stx 8,u + ldx #XIRQSV ; set up IRQ service routine + stx IRQVEC+1 + ldx ZERO ; reset the TIMER value + stx TIMVAL + jsr XVEC18 ; do a bunch of initialization + ldd #0x2c05 ; initialize DLOAD baud rate constant and timeout + std DLBAUD + ldx #USR0 ; set up pointer to USR routine addresses + stx USRADR + ldu #LB44A ; set up USR routine addresses to "FC error" + ldb #10 ; there are 10 routines +L8031 stu ,x++ ; set a routine to FC error + decb ; done all? + bne L8031 ; brif not + lda #0x7e ; op code of JMP extended (for RAM hook intialization) + sta RVEC20 ; command interpretation loop + ldx #XVEC20 + stx RVEC20+1 + sta RVEC15 ; expression evaluation + ldx #XVEC15 + stx RVEC15+1 + sta RVEC19 ; number parsing + ldx #XVEC19 + stx RVEC19+1 + sta RVEC9 ; PRINT + ldx #XVEC9 + stx RVEC9+1 + sta RVEC17 ; error handler + ldx #XVEC17 + stx RVEC17+1 + sta RVEC4 ; generic input + ldx #XVEC4 + stx RVEC4+1 + sta RVEC3 ; generic output + ldx #XVEC3 + stx RVEC3+1 + sta RVEC8 ; close file + ldx #XVEC8 + stx RVEC8+1 + sta RVEC23 ; tokenize line + ldx #XVEC23 + stx RVEC23+1 + sta RVEC18 ; RUN + ldx #XVEC18 + stx RVEC18+1 + sta EXPJMP ; exponentiation + ldx #L8489 + stx EXPJMP+1 + jsr L96E6 ; initialize graphics stuff + lda PIA0+3 ; enable 60Hz interrupt + ora #1 + sta PIA0+3 + ldx #'D*256+'K ; magic number for a Disk Basic ROM + cmpx DOSBAS ; do we have a Disk Basic ROM? + lbeq DOSBAS+2 ; brif so - launch it + andcc #0xaf ; enable interrupts +L80B2 ldx #L80E8-1 ; show sign on message + jsr STRINOUT +L80B8 ldx #XBWMST ; install warm start handler + stx RSTVEC + jmp LA0E2 ; set up warm start flag and launch immediate mode +; Extended Basic warm start code +XBWMST fcb 0xff ; mark routine as invalid so that ROMs are always copied to RAM on RESET + clr PLYTMR ; cancel any PLAY command in progress + clr PLYTMR+1 + lda PIA0+3 ; enable 60Hz interrupt + ora #1 + sta PIA0+3 + jmp BAWMST ; let Color Basic's warm start process run +; This code is to fix the famous PCLEAR bug. It replaces dead code in the 1.0 ROM. This patch corrects +; the input pointer so that it points to the correct place after the program has been relocated by +; PCLEAR instead of continuing with something that, in the best case, is a syntax error. +L80D0 lda CURLIN ; immediate mode? + inca + beq L80DD ; brif so + tfr y,d ; save offset to D + subd TXTTAB ; see how far into the program we are + addd CHARAD ; now adjust the input pointer based on that + std CHARAD ; save corrected input pointer +L80DD rts +L80DE fcb 25 ; 25 Extended Basic commands + fdb L8183 ; reserved word table (commands) + fdb L813C ; interpretation handler (commands) + fcb 14 ; 14 Extended Basic functions + fdb L821E ; reserved word table (functions) + fdb L8168 ; function handler +L80E8 fcc 'EXTENDED COLOR BASIC 2.0' + fcb 0x0d + fcc 'COPR. 1982, 1986 BY TANDY ' + fcb 0x0d + fcc 'UNDER LICENSE FROM MICROSOFT' + fcb 0x0d,0x0d,0x00 +; Extended Basic command interpretation loop +L813C cmpa #0xcb ; is it an Extended Basic command? + bhi L8148 ; brif not + ldx #L81F0 ; point to dispatch table + suba #0xb5 ; normalize the token number so 0 is the first entry + jmp LADD4 ; go transfer control to the command +L8148 cmpa #0xff ; is it a function token? + beq L8154 ; brif so - for MID$()=, TIMER= + cmpa #0xcd ; is it a token for a keyword that isn't a command? + bls L8165 ; brif so - error for USING and FN +L8150 jmp [COMVEC+23] ; transfer control to Disk Basic if it is present +L8154 jsr GETNCH ; get token after the function flag + cmpa #0x90 ; MID$? + lbeq L86D6 ; brif so (substring replacement) + cmpa #0x9f ; TIMER? + lbeq L8960 ; brif so - TIMER setting + jsr RVEC22 ; do a RAM hook in case something wants to extend this +L8165 jmp LB277 ; we have nothing valid here +; Function handler +L8168 cmpb #2*33 ; is it a valid Extended Basic function? + bls L8170 ; brif so +L816C jmp [COMVEC+28] ; transfer control to Disk Basic if it is present +L8170 subb #2*20 ; normalize Extended Basic functions to 0 + cmpb #2*8 ; Above HEX$? + bhi L817D ; brif so - we don't pre-evaluate an argument + pshs b ; save token value + jsr LB262 ; evaluate the function parameter + puls b ; get back token value +L817D ldx #L8257 ; point to dispatch table + jmp LB2CE ; go transfer control to the function +; Reserved words (commands) +L8183 fcs 'DEL' ; 0xb5 + fcs 'EDIT' ; 0xb6 + fcs 'TRON' ; 0xb7 + fcs 'TROFF' ; 0xb8 + fcs 'DEF' ; 0xb9 + fcs 'LET' ; 0xba + fcs 'LINE' ; 0xbb + fcs 'PCLS' ; 0xbc + fcs 'PSET' ; 0xbd + fcs 'PRESET' ; 0xbe + fcs 'SCREEN' ; 0xbf + fcs 'PCLEAR' ; 0xc0 + fcs 'COLOR' ; 0xc1 + fcs 'CIRCLE' ; 0xc2 + fcs 'PAINT' ; 0xc3 + fcs 'GET' ; 0xc4 + fcs 'PUT' ; 0xc5 + fcs 'DRAW' ; 0xc6 + fcs 'PCOPY' ; 0xc7 + fcs 'PMODE' ; 0xc8 + fcs 'PLAY' ; 0xc9 + fcs 'DLOAD' ; 0xca + fcs 'RENUM' ; 0xcb + fcs 'FN' ; 0xcc + fcs 'USING' ; 0xcd +; Dispatch table (commands) +L81F0 fdb DEL ; 0xb5 DEL + fdb EDIT ; 0xb6 EDIT + fdb TRON ; 0xb7 TRON + fdb TROFF ; 0xb8 TROFF + fdb DEF ; 0xb9 DEF + fdb LET ; 0xba LET (note: implemented by Color Basic!) + fdb LINE ; 0xbb LINE + fdb PCLS ; 0xbc PCLS + fdb PSET ; 0xbd PSET + fdb PRESET ; 0xbe PRESET + fdb SCREEN ; 0xbf SCREEN + fdb PCLEAR ; 0xc0 PCLEAR + fdb COLOR ; 0xc1 COLOR + fdb CIRCLE ; 0xc2 CIRCLE + fdb PAINT ; 0xc3 PAINT + fdb GET ; 0xc4 GET + fdb PUT ; 0xc5 PUT + fdb DRAW ; 0xc6 DRAW + fdb PCOPY ; 0xc7 PCOPY + fdb PMODETOK ; 0xc8 PMODE + fdb PLAY ; 0xc9 PLAY + fdb DLOAD ; 0xca DLOAD + fdb RENUM ; 0xcb RENUM +; Reserved words (functions) +L821E fcs 'ATN' ; 0x94 + fcs 'COS' ; 0x95 + fcs 'TAN' ; 0x96 + fcs 'EXP' ; 0x97 + fcs 'FIX' ; 0x98 + fcs 'LOG' ; 0x99 + fcs 'POS' ; 0x9a + fcs 'SQR' ; 0x9b + fcs 'HEX$' ; 0x9c + fcs 'VARPTR' ; 0x9d + fcs 'INSTR' ; 0x9e + fcs 'TIMER' ; 0x9f + fcs 'PPOINT' ; 0xa0 + fcs 'STRING$' ; 0xa1 +; Dispatch table (functions) +L8257 fdb ATN ; 0x94 ATN + fdb COS ; 0x95 COS + fdb TAN ; 0x96 TAN + fdb EXP ; 0x97 EXP + fdb FIX ; 0x98 FIX + fdb LOG ; 0x99 LOG + fdb POS ; 0x9a POS + fdb SQR ; 0x9b SQR + fdb HEXDOL ; 0x9c HEX$ + fdb VARPTRTOK ; 0x9d VARPTR + fdb INSTR ; 0x9e INSTR + fdb TIMER ; 0x9f TIMER + fdb PPOINT ; 0xa0 PPOINT + fdb STRING ; 0xa1 STRING$ +; Generic output handler +XVEC3 tst DEVNUM ; screen? + lbeq L95AC ; brif so - force text screen active + pshs b ; save register + ldb DEVNUM ; get output device + cmpb #-3 ; check for DLOAD + puls b ; restore register + bne L8285 ; brif not DLOAD + leas 2,s ; bail out of output handler if DLOAD +L8285 rts +; Close file handler. This corrects a bug in Color Basic 1.0 which didn't handle writing the +; end of file block correctly. That bug is fixed in Color Basic 1.1 so this isn't required +; if a recent enough version of Color Basic is installed. +XVEC8 lda DEVNUM ; get device number + inca ; is it tape? + bne L8285 ; brif not - we aren't going to mess with it + lda FILSTA ; get tape file status + cmpa #2 ; output file? + bne L8285 ; brif not + lda CINCTR ; is there anything waiting to be written out? + bne L8285 ; brif so - mainline code will handle it properly + clr DEVNUM ; reset output to screen + leas 2,s ; don't return to mainline code + jmp LA444 ; write EOF block +; RUN handler - sets up some Extended Basic stuff to defaults at program start +XVEC18 ldd #0xba42 ; initialize PLAY volume + std VOLHI + lda #2 ; set PLAY tempo to 2, PLAY octave to 3 + sta TEMPO + sta OCTAVE + asla ; set default note length to 5 + sta NOTELN + clr DOTVAL ; don't do any note length extension + ldd ZERO ; initialize DRAW angle and scale to default 1 + std ANGLE + ldb #128 ; initialize horizontal and vertical default coordinates to the middle of the screen + std HORDEF + ldb #96 + std VERDEF + rts +; Command interpretation loop handler; we need to intercept this to implement TRON/TROFF +XVEC20 leas 2,s ; don't return to the mainline code +L82BB andcc #0xaf ; make sure interrupts are running + jsr LADEB ; do a BREAK/pause check + ldx CHARAD ; save input pointer + stx TINPTR + lda ,x+ ; get current input character + beq L82CF ; brif end of line + cmpa #': ; statement separator? + beq L82F1 ; brif so + jmp LB277 ; raise error we got here with extra junk +L82CF lda ,x++ ; get first byte of next line address + sta ENDFLG ; use it to set "END" flag to "END" + bne L82D8 ; brif not end of program + jmp LAE15 ; go do the "END" +L82D8 ldd ,x+ ; get line number of next line (and leave pointer one before line text) + std CURLIN ; set current line number + stx CHARAD ; save input pointer + lda TRCFLG ; are we tracing? + beq L82F1 ; brif not + lda #'[ ; show opening marker for TRON line number + jsr PUTCHR + lda CURLIN ; restore MSB of line number + jsr LBDCC ; show line number + lda #'] ; show closing marker for TRON line number + jsr PUTCHR +L82F1 jsr GETNCH ; get the start of the statement + tfr cc,b ; save status flags + cmpa #0x98 ; is it CSAVE? + beq L8316 ; brif so - go to Extended Basic patch (adds CSAVEM) + cmpa #0x97 ; is it CLOAD? + beq L8311 ; brif so - go to Extended Basic patch (adds multi-origin binaries) + tfr b,cc ; restore character status + jsr LADC6 ; go process command + bra L82BB ; restart interpretation loop +; Tokenizaton handler. This is actually a hack to intercept CLOAD and CSAVE during immediate mode by causing the +; tokenization routine to return to the interpretation loop above instead of the mainline interpretation loop. This +; is necessary because the first command encountered on a line in immediate mode is executed BEFORE the interpretation +; loop RAM hook is called. This patch doesn't actually affect tokenization itself at all. +XVEC23 ldx 2,s ; get return address of caller to the tokenizer + cmpx #LAC9D ; is it coming from immediate mode prior to executing the line? + bne L8310 ; brif not + ldx #L82F1 ; force return to Extended Basic's main loop patch above + stx 2,s +L8310 rts +; These two patches are set up this way so that control can be transferred back to the original Color Basic +; implementations if the Extended Basic addons are not triggered. +L8311 jsr L8C62 ; transfer control to Extended Basic's CLOAD handler + bra L82BB ; go do another command +L8316 bsr L831A ; go do Extended Basic's CSAVE handler + bra L82BB ; go do another command +; Extended Basic's CSAVE handler which implements CSAVEM (which Color Basic does not have) +L831A jsr GETNCH ; get character after CSAVE + cmpa #'M ; is it CSAVEM? + lbne CSAVE ; brif not - Color Basic can handle this + jsr GETNCH ; eat the "M" + jsr LA578 ; parse file name + bsr L836C ; get start address + stx CASBUF+13 ; save it in file header + bsr L836C ; get end address + cmpx 2,s ; compare to start address + lblo LB44A ; brif end address is before the start address + bsr L836C ; get execution address + stx CASBUF+11 ; put in file header + jsr GETCCH ; are we at the end of the commmand? + bne L8310 ; brif not + lda #2 ; file type to machine language + ldx ZERO ; set to binary and single block + jsr LA65F ; write header + clr FILSTA ; mark any open tape file closed + inc BLKTYP ; set block type to data + jsr WRLDR ; write a data leader + ldx 4,s ; get starting address +L834D stx CBUFAD ; set start of data address + lda #255 ; try a full length block by default + sta BLKLEN + ldd 2,s ; get ending address + subd CBUFAD ; see how much is left + bhs L835E ; brif we have more to write + leas 6,s ; clean up stack + jmp LA491 ; write EOF block +L835E cmpd #255 ; do we have a full block left? + bhs L8367 ; brif so + incb ; set block size to remainder + stb BLKLEN +L8367 jsr SNDBLK ; write a data block + bra L834D ; go see if we have more to write +L836C jsr SYNCOMMA ; make sure we have a comma + jsr LB73D ; evaluate unsigned expression to X + ldu ,s ; get return address + stx ,s ; save result on stack + tfr u,pc ; return to caller +; COS function +COS ldx #L83AB ; point to PI/2 constant + jsr LB9C2 ; add to argument ( cos(x) = sin((pi/2)+x) ) +L837E jmp SIN ; now calculate sin((pi/2)+x) +; TAN function. This is determined by the identity TAN(X) = SIN(X)/COS(X) +TAN jsr LBC2F ; save FPA0 in FPA3 + clr RELFLG ; reset quadrant flag + bsr L837E ; calculate SIN(x) + ldx #V4A ; save result in FPA5 + jsr LBC35 + ldx #V40 ; get back original argument + jsr LBC14 + clr FP0SGN ; force result positive + lda RELFLG ; get quadrant flag + bsr L83A6 ; calculate COS(x) + tst FP0EXP ; did we get 0 for COS(x) + lbeq LBA92 ; brif so - overflow + ldx #V4A ; point to sin(x) +L83A3 jmp LBB8F ; divide the sin(x) value by the cos(x) value +L83A6 pshs a ; save sign flag + jmp LBFA6 ; expand polynomial +L83AB fcb 0x81,0x49,0x0f,0xda,0xa2 ; pi/2 constant +; ATN function (inverse tangent). There are two calculation streams used to improve precision. +; One if the parameter is >= 1.0 and the other if it is < 1.0 +ATN lda FP0SGN ; get sign of argument + pshs a ; save it + bpl L83B8 ; brif positive + bsr L83DC ; flip sign of argument +L83B8 lda FP0EXP ; get exponent + pshs a ; save it + cmpa #0x81 ; exponent for 1.0 + blo L83C5 ; brif less - value is less than 1.0 + ldx #LBAC5 ; point to FP constant 1.0 + bsr L83A3 ; calculate reciprocal +L83C5 ldx #L83E0 ; point to polynomical coefficients + jsr LBEF0 ; expand polynomial + puls a ; get exponent of argument + cmpa #0x81 ; did we do a reciprocal calculation? + blo L83D7 ; brif not + ldx #L83AB ; subtract result from pi/2 if we did + jsr LB9B9 +L83D7 puls a ; get sign of original + tsta ; was it positive? + bpl L83DF ; brif so +L83DC jmp LBEE9 ; flip sign of result +L83DF rts +; Chebyshev modified taylor series coefficients for inverse tangent. Note that these diverge quite significantly +; from the standard taylor series after 1/9. The standard coefficients are 1/1,-1/3, 1/5, -1/7, 1/9, -1/11, and +; so on. These modified coefficients yield reasonable accuracy for the precision available, presumably with +; fewer coefficients. +L83E0 fcb 11 ; 12 coefficients + fcb 0x76,0xb3,0x83,0xbd,0xd3 ; -0.000684793912 + fcb 0x79,0x1e,0xf4,0xa6,0xf5 ; 0.00485094216 + fcb 0x7b,0x83,0xfc,0xb0,0x10 ; -0.0161117018 + fcb 0x7c,0x0c,0x1f,0x67,0xca ; 0.0342096381 + fcb 0x7c,0xde,0x53,0xcb,0xc1 ; -0.0542791328 + fcb 0x7d,0x14,0x64,0x70,0x4c ; 0.0724571965 + fcb 0x7d,0xb7,0xea,0x51,0x7a ; -0.0898023954 + fcb 0x7d,0x63,0x30,0x88,0x7e ; 0.110932413 + fcb 0x7e,0x92,0x44,0x99,0x3a ; -0.142839808 + fcb 0x7e,0x4c,0xcc,0x91,0xc7 ; 0.199999121 + fcb 0x7f,0xaa,0xaa,0xaa,0x13 ; -0.333333316 + fcb 0x81,0x00,0x00,0x00,0x00 ; 1.0 +; Chebyshev modified taylor series coefficients for the ln(x)/ln(2) (base 2 log of x) +L841D fcb 3 ; four coefficients + fcb 0x7f,0x5e,0x56,0xcb,0x79 ; 0.434255942 from (2/7)/ln(2) + fcb 0x80,0x13,0x9b,0x0b,0x64 ; 0.576584541 from (2/5)/ln(2) + fcb 0x80,0x76,0x38,0x93,0x16 ; 0.961800759 from (2/3)/ln(2) + fcb 0x82,0x38,0xaa,0x3b,0x20 ; 2.88539007 from 2/ln(2) +L8432 fcb 0x80,0x35,0x04,0xf3,0x34 ; 1/sqrt(2) +L8437 fcb 0x81,0x35,0x04,0xf3,0x34 ; sqrt(2) +L843C fcb 0x80,0x80,0x00,0x00,0x00 ; -0.5 +L8441 fcb 0x80,0x31,0x72,0x17,0xf8 ; ln(2) +; LOG function (natural log, ln) +; FP representation is of the form A*2^B. Thus, the log routine determines the value of +; ln(A*2^B). +; +; ln(A*2^B) = ln(A) + ln(2^B) = ln(A) + B*ln(2) = (ln(A)/ln(2) + B)*ln(2), OR: +; (log2(A) + B)*ln(2) +; Now if we write A as A*(sqrt(2)/sqrt(2)), we haven't changed anything so: +; (log2(A*sqrt(2)/sqrt(2)) + B)*ln(2) = (log2(A*sqrt(2)) - log2(sqrt(2)) + B) * ln(2) +; which gives: (-1/2 + log2(A*sqrt(2)) + B) * ln(2) +; +; Everything except log2(A*sqrt(2)) is either constant or trivial. +; +; What the actual code below feeds into the modified taylor series is actually: +; 1 - (sqrt(2)/(A + sqrt(2)) which algebra reveals to be (A*sqrt(2)-1)/(A*sqrt(2)+1) +; +; Thus, the code is using (A*sqrt(2)-1)/(A*sqrt(2)+1) instead of A*sqrt(2) as one would +; expect from the identities. However, the modified coefficients in the series above +; could be correcting for that or the introduced error was deemed acceptable. +; NOTE: this routine does NOT return 0 for LOG(1) +LOG jsr LBC6D ; get status of FPA0 + lble LB44A ; brif <= 0 - logarithms don't exist in that case + ldx #L8432 ; point to 1/sqrt(2) + lda FP0EXP ; get exponent of argument + suba #0x80 ; remove bias + pshs a ; save it for later + lda #0x80 ; force exponent to 0 (this turns FPA0 into A in the above description) + sta FP0EXP + jsr LB9C2 ; add 1/sqrt(2) to A + ldx #L8437 ; point to sqrt(2) + jsr LBB8F ; divide that by FPA0 + ldx #LBAC5 ; point to 1.0 + jsr LB9B9 ; subtract result from 1.0: Now FPA0 is 1-(sqrt(2)/(A+1/sqrt(2))) + ldx #L841D ; point to coefficients + jsr LBEF0 ; expand polynomial (calculate base 2 log of modified argument) + ldx #L843C ; point to -0.5 + jsr LB9C2 ; add result + puls b ; get original exponent back + jsr LBD99 ; add B to FPA0 + ldx #L8441 ; point to ln(2) + jmp LBACA ; multiply by ln(2) which gives us the result in base e +; SQR function (square root) - returns the principle root (positive) +SQR jsr LBC5F ; move argument to FPA1 (first argument for exponentiation) + ldx #LBEC0 ; point to 0.5 (exponent for square root) + jsr LBC14 ; set up second argument to exponentiation (the exponent) +; Exponentiation operator +; This is calculated as A^x = e^(x*ln(A)) where A is in FPA1 and x is in FPA0 +L8489 beq EXP ; do "EXP" if exponent is 0 (this will catch 0^0) + tsta ; check that the base is not 0 + bne L8491 ; brif base is not 0 + jmp LBA3A ; 0^(nonzero) is 0 +L8491 ldx #V4A ; save exponent (to FPA5) + jsr LBC35 + clrb ; result sign will default to positive + lda FP1SGN ; check if base is positive + bpl L84AC ; brif so + jsr INT ; convert exponent to integer + ldx #V4A ; point to original expoent + lda FP1SGN ; get sign of FPA1 + jsr LBCA0 ; compare original exponent with truncated one + bne L84AC ; brif not equal + coma ; flip sign + ldb CHARAC ; get LS byte of integer exponent (result sign flag) +L84AC jsr LBC4C ; copy FPA1 (base) to FPA0 (A = sign) + pshs b ; save result sign + jsr LOG ; get natural log of the base + ldx #V4A ; multiply the log by the exponent + jsr LBACA + bsr EXP ; now raise e to the resulting power + puls a ; get result sign + rora ; brif it was negative + lbcs LBEE9 ; brif negative - flip sign + rts +L84C4 fcb 0x81,0x38,0xaa,0x3b,0x29 ; 1.44269504 (correction factor for exponential function) +; Chebyshev modified taylor series coefficients for e^x +L84C9 fcb 7 ; eight coefficients + fcb 0x71,0x34,0x58,0x3e,0x56 ; 1/(7!*(CF^7)) + fcb 0x74,0x16,0x7e,0xb3,0x1b ; 1/(6!*(CF^6)) + fcb 0x77,0x2f,0xee,0xe3,0x85 ; 1/(5!*(CF^5)) + fcb 0x7a,0x1d,0x84,0x1c,0x2a ; 1/(4!*(CF^4)) + fcb 0x7c,0x63,0x59,0x58,0x0a ; 1/(3!*(CF^3)) + fcb 0x7e,0x75,0xfd,0xe7,0xc6 ; 1/(2!*(CF^2)) + fcb 0x80,0x31,0x72,0x18,0x10 ; 1/(1!*(CF^1)) + fcb 0x81,0x00,0x00,0x00,0x00 ; 1 +; EXP function (e^x) +EXP ldx #L84C4 ; point to correction factor + jsr LBACA ; multiply it + jsr LBC2F ; save corrected argument to FPA3 + lda FP0EXP ; get exponent of FPA0 + cmpa #0x88 ; is it too big? + blo L8504 ; brif not +L8501 jmp LBB5C ; to 0 (underflow) or overflow error +L8504 jsr INT ; convert argument to an integer + lda CHARAC ; get ls byte of integer + adda #0x81 ; was argument 127? if so, the OV error; adds bias + beq L8501 + deca ; adjust for the extra +1 above + pshs a ; save integer exponent + ldx #V40 ; get fractional part of argument + jsr LB9B9 + ldx #L84C9 ; point to coefficients + jsr LBEFF ; evaluate polynomial on the fractional part + clr RESSGN ; force result to be positive + puls a ; get back original exponent + jsr LBB48 ; add original exponent to the fractional result + rts +; FIX function (truncate/round toward 0) (NOTE: INT() always rounds down so INT(-0.5) gives -1 but FIX(-0.5) gives 0) +FIX jsr LBC6D ; get status of argument + bmi L852C ; brif negative +L8529 jmp INT ; do regular "int" if positive +L852C com FP0SGN ; flip the sign + bsr L8529 ; do "INT" on this + jmp LBEE9 ; flip the sign back +; EDIT command +EDIT jsr L89AE ; get line number + leas 2,s ; we're not going to return to the main loop +L8538 lda #1 ; "LIST" flag + sta VD8 ; set to list the line + jsr LAD01 ; find line number + lbcs LAED2 ; brif line wasn't found + jsr LB7C2 ; go unpack the line into the buffer + tfr y,d ; calculate the actual length of the line + subd #LINBUF+2 + stb VD7 ; save line length (it will only be 8 bits) +L854D ldd BINVAL ; get the line number + jsr LBDCC ; display it + jsr LB9AC ; put a space after it + ldx #LINBUF+1 ; point to iput uffer + ldb VD8 ; are we listing? + bne L8581 ; brif so +L855C clrb ; reset digit accumulator +L855D jsr L8687 ; get a keypress + jsr L90AA ; set carry if not numeric + bcs L8570 ; brif not a number + suba #'0 ; remove ASCII bias + pshs a ; save digit value + lda #10 ; multiply accumulator by 10 + mul + addb ,s+ ; add in new digit + bra L855D ; go check for another digit +L8570 subb #1 ; this converts 0 to 1, but *only* 0 to 1 + adcb #1 + cmpa #'A ; abort? + bne L857D ; brif not + jsr LB958 ; to a CR + bra L8538 ; restart EDIT process +L857D cmpa #'L ; list? + bne L858C ; brif not +L8581 bsr L85B4 ; list the line + clr VD8 ; reset to "not listing" + jsr LB958 ; do a CR + bra L854D ; start editing +L858A leas 2,s ; lose return address +L858C cmpa #0x0d ; ENTER? + bne L859D ; brif not + bsr L85B4 ; echo out the line +L8592 jsr LB958 ; do a CR + ldx #LINBUF+1 ; reset input pointer to start of buffer + stx CHARAD + jmp LACA8 ; join immediate mode to replace the line in the program +L859D cmpa #'E ; exit? + beq L8592 ; brif so - end edit with no echo + cmpa #'Q ; quit? + bne L85AB ; brif not + jsr LB958 ; do a CR + jmp LAC73 ; go to immediate mode with no fanfare - no changes saved +L85AB bsr L85AF ; go do commands + bra L855C ; go handle another command +L85AF cmpa #0x20 ; space? + bne L85C3 ; brif not + skip2 +L85B4 ldb #LBUFMX-1 ; display up to a whole line +L85B6 lda ,x ; get buffer chracter + beq L85C2 ; brif end of line + jsr PUTCHR ; output character + leax 1,x ; move to next character + decb ; done? + bne L85B6 ; brif not +L85C2 rts +L85C3 cmpa #'D ; delete? + bne L860F ; brif not +L85C7 tst ,x ; end of line? + beq L85C2 ; brif so - can't delete + bsr L85D1 ; remove a character + decb ; done all requested? + bne L85C7 ; brif not + rts +L85D1 dec VD7 ; account for character being removed + leay -1,x ; set pointer and compensate for increment below +L85D5 leay 1,y ; move to next character + lda 1,y ; get next character + sta ,y ; move it forward + bne L85D5 ; brif we didn't hit the end of the buffer + rts +L85DE cmpa #'I ; insert? + beq L85F5 ; brif so + cmpa #'X ; extend? + beq L85F3 ; brif so + cmpa #'H ; "hack"? + bne L8646 ; brif not + clr ,x ; mark current location as end of line + tfr x,d ; calculate new line length + subd #LINBUF+2 + stb VD7 ; save new length +L85F3 bsr L85B4 ; display the line +L85F5 jsr L8687 ; read a character + cmpa #0x0d ; ENTER? + beq L858A ; brif so - terminate entry + cmpa #0x1b ; ESC? + beq L8625 ; brif so - back to command mode + cmpa #0x08 ; backspace? + bne L8626 ; brif no + cmpx #LINBUF+1 ; are we at the start of the buffer? + beq L85F5 ; brif so - it's a no-op + bsr L8650 ; move pointer back one, do a BS + bsr L85D1 ; remove character from the buffer + bra L85F5 ; go handle more input +L860F cmpa #'C ; change? + bne L85DE ; brif not +L8613 tst ,x ; is there something to change? + beq L8625 ; brif not + jsr L8687 ; get a key stroke + bcs L861E ; brif valid key + bra L8613 ; try again if invalid key +L861E sta ,x+ ; put new character in the buffer + bsr L8659 ; echo it + decb ; changed number requested? + bne L8613 ; brif not +L8625 rts +L8626 ldb VD7 ; get length of line + cmpb #LBUFMX-1 ; at maximum line length? + bne L862E ; brif not + bra L85F5 ; process another input character +L862E pshs x ; save input pointer +L8630 tst ,x+ ; are we at the end of the line? + bne L8630 ; brif not +L8634 ldb ,-x ; get character before current pointer, move back + stb 1,x ; move it forward + cmpx ,s ; at the original buffer pointer? + bne L8634 ; brif not + leas 2,s ; remove saved buffer pointer + sta ,x+ ; save input character in newly made hole + bsr L8659 ; echo it + inc VD7 ; bump line length counter + bra L85F5 ; go handle more stuff +L8646 cmpa #0x08 ; backspace? + bne L865C ; brif not +L864A bsr L8650 ; move pointer back, echo BS + decb ; done enough of them? + bne L864A ; brif not + rts +L8650 cmpx #LINBUF+1 ; at start of buffer? + beq L8625 ; brif so + leax -1,x ; move pointer back + lda #0x08 ; character to echo - BS +L8659 jmp PUTCHR ; echo character to screen +L865C cmpa #'K ; "kill"? + beq L8665 ; brif so + suba #'S ; search? + beq L8665 ; brif so + rts +L8665 pshs a ; save kill/search flag + bsr L8687 ; read target + pshs a ; save search character +L866B lda ,x ; get current character in buffer + beq L8685 ; brif end of line - nothing more to search + tst 1,s ; is it KILL? + bne L8679 ; brif so + bsr L8659 ; echo the character + leax 1,x ; move ahead + bra L867C ; check next character +L8679 jsr L85D1 ; remove character from buffer +L867C lda ,x ; get character in buffer + cmpa ,s ; are we at the target? + bne L866B ; brif not + decb ; have we found enough of them? + bne L866B ; brif not +L8685 puls y,pc ; clean up stack and return to main EDIT routine +L8687 jsr LA171 ; get input from the generic input handler (will show the cursor) + cmpa #0x7f ; graphics (or DEL)? + bhs L8687 ; brif so - ignore it + cmpa #0x5f ; SHIFT-UP? + bne L8694 ; brif not + lda #0x1b ; replace with ESC +L8694 cmpa #0x0d ; carriage return? + beq L86A6 ; brif so (C=0) + cmpa #0x1b ; ESC + beq L86A6 ; brif so (C=0) + cmpa #0x08 ; backspace? + beq L86A6 ; brif so (C=0) + cmpa #32 ; control code? + blo L8687 ; brif control code - try again + orcc #1 ; set C for "valid" (printable) character +L86A6 rts +; TRON and TROFF commands +TRON skip1lda ; load flag with nonzero for trace enabled (and skip next) +TROFF clra ; clear flag for trace disabled + sta TRCFLG ; save trace status + rts +; POS function +POS lda DEVNUM ; get original device number + pshs a ; save it for later + jsr LA5AE ; fetch device number + jsr LA406 ; check for open file + jsr LA35F ; set up print parameters + ldb DEVPOS ; get current line position for the device + jmp LA5E4 ; return position in B as unsigned +; VARPTR function +VARPTRTOK jsr LB26A ; make sure we have ( + ldd ARYEND ; get address of end of arrays + pshs d ; save it + jsr LB357 ; parse variable descriptor + jsr LB267 ; make sure there is a ) + puls d ; get original end of arrays + exg x,d ; swap original end of arrays and the discovered variable pointer + cmpx ARYEND ; did array end move (variable created?) + bne L8724 ; brif so (FC error) + jmp GIVABF ; return the pointer (NOTE: as signed) +; MID$() assignment; note that this cannot make the string longer or shorter and if the replacement is shorter +; than the specified size, only the number of characters actually in the replacement will be used. +L86D6 jsr GETNCH ; eat the MID$ token + jsr LB26A ; force ( + jsr LB357 ; evaluate the variable + pshs x ; save variable descriptor + ldd 2,x ; point to start of original string + cmpd FRETOP ; is it in string space? + bls L86EB ; brif not + subd MEMSIZ ; is it still in string space (top end)? + bls L86FD ; brif so +L86EB ldb ,x ; get length of original string + jsr LB56D ; allocate space in string space + pshs x ; save pointer to string space + ldx 2,s ; get to original string descriptor + jsr LB643 ; move the string into string space + puls x,u ; get new string address and string descriptor + stx 2,u ; save new data address for the string + pshs u ; save descriptor address again +L86FD jsr LB738 ; evaluate ",start" + pshs b ; save start offset + tstb ; is start 0? + beq L8724 ; brif so - strings offsets are 1-based + ldb #255 ; default use the entire string + cmpa #') ; end of parameters? + beq L870E ; brif so + jsr LB738 ; evaluate ",length" +L870E pshs b ; save length + jsr LB267 ; make sure we have a ) + ldb #0xb3 ; make sure we have = + jsr LB26F + bsr L8748 ; evaluate replacement string + tfr x,u ; save replacement string address + ldx 2,s ; get original string descriptor + lda ,x ; get length of original string + suba 1,s ; subtract start position + bhs L8727 ; brif within the string - insert replacement +L8724 jmp LB44A ; raise illegal function call +L8727 inca ; A is now number of characters to the right of the position parameter + cmpa ,s ; compare to length desired + bhs L872E ; brif new length fits + sta ,s ; only use as much of the length as will fit +L872E lda 1,s ; get position offset + exg a,b ; swap replacement length and position + ldx 2,x ; point to original string address + decb ; we work with 0-based offsets + abx ; now X points to start of replacement + tsta ; replacing 0? + beq L8746 ; brif so - done + cmpa ,s ; is replacement shorter than the hole? + bls L873F ; brif so + lda ,s ; use copy the maximum number specified +L873F tfr a,b ; save number to move in B + exg u,x ; swap pointers so they are right for the routine + jsr LA59A ; copy string data +L8746 puls a,b,x,pc ; clean up stack and return +L8748 jsr LB156 ; evaluate expression + jmp LB654 ; make sure it's a string and return string details +; STRING$ function +STRING jsr LB26A ; make sure we have ( + jsr EVALEXPB ; evaluate repeat count (error if > 255) + pshs b ; save repeat count + jsr SYNCOMMA ; make sure there's a comma + jsr LB156 ; evaluate the thing to repeat + jsr LB267 ; make sure we have a ) + lda VALTYP ; is it string? + bne L8768 ; brif so + jsr LB70E ; get 8 bit character code + bra L876B ; use that +L8768 jsr LB6A4 ; get first character of string +L876B pshs b ; save repeat character + ldb 1,s ; get repeat count + jsr LB50F ; reserve space for the string + puls a,b ; get character and repeat count + beq L877B ; brif NULL string +L8776 sta ,x+ ; put character into string + decb ; put enough? + bne L8776 ; brif not +L877B jmp LB69B ; return the newly created string +; INSTR function +INSTR jsr LB26A ; evaluate ( + jsr LB156 ; evaluate first argument + ldb #1 ; default start position is 1 (start of string) + pshs b ; save start position + lda VALTYP ; get type of first argument + bne L879C ; brif string - use default starting position + jsr LB70E ; convert first argument into string offset + stb ,s ; save offset + beq L8724 ; brif starting at 0 - not allowed + jsr SYNCOMMA ; make sure there's a comma + jsr LB156 ; evaluate the search string + jsr LB146 ; make sure it *is* a string +L879C ldx FPA0+2 ; get search string descriptor + pshs x ; save it + jsr SYNCOMMA ; make sure we have a comma + jsr L8748 ; evalute the target string + pshs x,b ; save address and length of target string + jsr LB267 ; make sure we have a ) + ldx 3,s ; get search string address + jsr LB659 ; get string details + pshs b ; save search string length + cmpb 6,s ; compare length of search string to the start + blo L87D9 ; brif start position is beyond the search string - return 0 + lda 1,s ; get length of target string + beq L87D6 ; brif targetstring is NULL - match will be immediate + ldb 6,s ; get start position + decb ; zero-base it + abx ; now X points to the start position for the search +L87BE leay ,x ; point to start of search + ldu 2,s ; get target string pointer + ldb 1,s ; get targetlength + lda ,s ; get length of serach + suba 6,s ; see how much is left in searh + inca ; add one for "inclusivity" + cmpa 1,s ; do we have less than the target string? + blo L87D9 ; brif so - we obviously won't match +L87CD lda ,x+ ; compare a byte + cmpa ,u+ + bne L87DF ; brif no match + decb ; compared all of target? + bne L87CD ; brif not +L87D6 ldb 6,s ; get position where we matched + skip1 +L87D9 clrb ; flag no match + leas 7,s ; clean up stack + jmp LB4F3 ; return unsigned B +L87DF inc 6,s ; bump start position + leax 1,y ; move starting pointer + bra L87BE ; see if we match now +; Number parsing handler +XVEC19 cmpa #'& ; do we have & (hex or octal)? +L87E7 bne L8845 ; brif not + leas 2,s ; we won't return to the original invoker +L87EB clr FPA0+2 ; clear bottom of FPA0 for 16 bit value + clr FPA0+3 + ldx #FPA0+2 ; point to accumulator + jsr GETNCH ; eat the & + cmpa #'O ; octal? + beq L880A ; brif so + cmpa #'H ; hex? + beq L881F ; brif so + jsr GETCCH ; reset flags on input + bra L880C ; go process octal (default) +L8800 cmpa #'8 ; is it a valid octal character? + lbhi LB277 ; brif not (BUG: should be LBHS or above be #'7) + ldb #3 ; base 8 multiplier + bsr L8834 ; add digit to accumulator +L880A jsr GETNCH ; get input character +L880C bcs L8800 ; brif numeric +L880E clr FPA0 ; clear upper bytes of FPA0 + clr FPA0+1 + clr VALTYP ; result is numeric + clr FPSBYT ; clear out any extra precision + clr FP0SGN ; make it positive + ldb #0xa0 ; exponent for integer aligned to right of FPA0 + stb FP0EXP + jmp LBA1C ; go normalize the result and return +L881F jsr GETNCH ; get input character + bcs L882E ; brif digit + jsr LB3A2 ; set carry if not alpha +L8826 bcs L880E ; brif not alpha + cmpa #'G ; is it valid HEX digit? + bhs L880E ; brif not + suba #7 ; normalize A-F to be just above 0-9 +L882E ldb #4 ; four bits per digit + bsr L8834 ; add digit to accumlator + bra L881F ; process another digit +L8834 asl 1,x ; shift accumulator one bit left + rol ,x + lbcs LBA92 ; brif too big - overflow + decb ; done enough bit shifts? + bne L8834 ; brif not +L883F suba #'0 ; remove ASCII bias + adda 1,x ; merge digit into accumlator (this cannot cause carry) + sta 1,x +L8845 rts +; Expression evaluation handler +XVEC15 puls u ; get back return address + clr VALTYP ; set result to numeric + ldx CHARAD ; save input pointer + jsr GETNCH ; get the input character + cmpa #'& ; HEX or OCTAL? + beq L87EB ; brif so + cmpa #0xcc ; FN? + beq L88B4 ; brif so - do "FNx()" + cmpa #0xff ; function token? + bne L8862 ; brif not + jsr GETNCH ; get function token value + cmpa #0x83 ; USR? + lbeq L892C ; brif so - short circuit Color Basic's USR handler +L8862 stx CHARAD ; restore input pointer + jmp ,u ; return to mainline code +L8866 ldx CURLIN ; are we in immediate mode? + leax 1,x +L886A bne L8845 ; brif not - we're good + ldb #2*11 ; code for illegal direct statement +L886E jmp LAC46 ; raise error +; DEF command (DEF FN, DEF USR) +DEF ldx [CHARAD] ; get two input characters + cmpx #0xff83 ; USR? + lbeq L890F ; brif so - do DEF USR + bsr L88A1 ; get descriptor address for FN variable + bsr L8866 ; disallow DEF FN in immediate mode + jsr LB26A ; make sure we have ( + ldb #0x80 ; disallow arrays as arguments + stb ARYDIS + jsr LB357 ; evaluate variable + bsr L88B1 ; make sure it's numeric + jsr LB267 ; make sure we have ) + ldb #0xb3 ; make sure we have = + jsr LB26F + ldx V4B ; get variable descriptor address + ldd CHARAD ; get input pointer + std ,x ; save address of the actual function code in variable descriptor + ldd VARPTR ; get descriptor address of argument + std 2,x ; save argument descriptor address + jmp DATA ; move to the end of this statement +L88A1 ldb #0xcc ; make sure we have FN + jsr LB26F + ldb #0x80 ; disable array lookup + stb ARYDIS + ora #0x80 ; set bit 7 of first character (to indicate FN variable) + jsr LB35C ; find the variable + stx V4B ; save descriptor pointer +L88B1 jmp LB143 ; make sure we have a numeric variable +; Evaluate an FN call +L88B4 bsr L88A1 ; parse the FNx bit and get the descriptor + pshs x ; save descriptor + jsr LB262 ; evaluate parameter + bsr L88B1 ; make sure it's a number + puls u ; get FN descriptor + ldb #2*25 ; code for undefined function + ldx 2,u ; point to argument variable descriptor + beq L886E ; brif nothing doing there (if it was just created, this will be NULL) + ldy CHARAD ; save current input pointer + ldu ,u ; point to start of FN definition + stu CHARAD ; put input pointer there + lda 4,x ; save original value of argument and save it with current input, and variable pointers + pshs a + ldd ,x + ldu 2,x + pshs u,y,x,d + jsr LBC35 ; set argument variable to the argument +L88D9 jsr LB141 ; go evaluate the FN expression + puls d,x,y,u ; get back variable pointers, input pointer, and original variable value + std ,x + stu 2,x + puls a + sta 4,x + jsr GETCCH ; test end of FN formula + lbne LB277 ; brif not end of statement - problem with the function + sty CHARAD ; restore input pointer +L88EF rts +; Error handler +XVEC17 cmpb #2*25 ; is it a valid Extended Basic error code? + blo L88EF ; brif not - return to mainline + jsr LA7E9 ; turn off tape + jsr LA974 ; turn off sound + jsr LAD33 ; clean up stack and other bits + clr DEVNUM ; reset output to screen + jsr LB95C ; do a newline if needed + jsr LB9AF ; do a ? + ldx #L890B-25*2 ; point to error message table + jmp LAC60 ; go display error message +; Extended Basic error codes. Yes, NE is defined here even though it is only actually documented in the +; Disk Basic documentation. It is here for the use of DLOAD. +L890B fcc 'UF' ; 25 undefined function call + fcc 'NE' ; 26 File not found +; DEF USR +L890F jsr GETNCH ; eat the USR token + bsr L891C ; get pointer to USR call + pshs x ; save FN exec address location + bsr L8944 ; calculate execution address + puls u ; get FN address pointer + stx ,u ; save new address + rts +L891C clrb ; default routine number is 0 + jsr GETNCH ; fetch the call number + bcc L8927 ; brif not a number + suba #'0 ; remove ASCII bias + tfr a,b ; save it in the right place + jsr GETNCH ; eat the call number +L8927 ldx USRADR ; get start address of USR jump table + aslb ; two bytes per address + abx ; now X points to the right entry + rts +; Evaluate a USR call +L892C bsr L891C ; find the correct routine address location + ldx ,x ; get routine address + pshs x ; save it + jsr LB262 ; evaluate argument + ldx #FP0EXP ; point to FPA0 (argument value) + lda VALTYP ; is it string? + beq L8943 ; brif not + jsr LB657 ; fetch string details (removes it from the string stack) + ldx FPA0+2 ; get string descriptor pointer + lda VALTYP ; set flags for the value type +L8943 rts ; call the routine and return to mainline code +L8944 ldb #0xb3 ; check for "=" + jsr LB26F + jmp LB73D ; evaluate integer expression to X and return +; Extended Basic IRQ handler +XIRQSV lda PIA0+3 ; is it VSYNC interrupt? + bmi L8952 ; brif so + rti ; really should clear the HSYNC interrupt here +L8952 lda PIA0+2 ; clear VSYNC interrupt + ldx TIMVAL ; increment the TIMER value + leax 1,x + stx TIMVAL + jmp L9C3E ; check for other stuff +; TIMER= +L8960 jsr GETNCH ; eat the TIMER token + bsr L8944 ; evaluate =nnnn to X + stx TIMVAL ; set the timer + rts +; TIMER function +TIMER ldx TIMVAL ; get timer value + stx FPA0+2 ; set it in FPA0 + jmp L880E ; return as positive 16 bit value +; DEL command +DEL lbeq LB44A ; raise error if no argument (probably to avoid accidentally deleting line 0) + jsr LAF67 ; parse line number + jsr LAD01 ; find line + stx VD3 ; save address of line + jsr GETCCH ; is there something more? + beq L8990 ; brif not + cmpa #0xac ; dash? + bne L89BF ; brif not - error out + jsr GETNCH ; each the - + beq L898C ; brif no ending line - use default line number + bsr L89AE ; parse second line number and save in BINVAL + bra L8990 ; do the deletion +L898C lda #0xff ; set to maximum line number + sta BINVAL +L8990 ldu VD3 ; point end to start + skip2 +L8993 ldu ,u ; point to start of next line + ldd ,u ; check for end of program + beq L899F ; brif end of program + ldd 2,u ; get line number + subd BINVAL ; is it in range? + bls L8993 ; brif so +L899F ldx VD3 ; get starting line address + bsr L89B8 ; close up gap + jsr LAD21 ; reset input pointer and erase variables + ldx VD3 ; get start of program after the deletion + jsr LACF1 ; recompute netl ine pointers + jmp LAC73 ; return to immediate mode +L89AE jsr LAF67 ; parse a line number + jmp LA5C7 ; make sure there's nothing more +L89B4 lda ,u+ ; copy a byte + sta ,x+ +L89B8 cmpu VARTAB ; end of program? + bne L89B4 ; brif not + stx VARTAB ; save new start of variables/end of program +L89BF rts +; LINE INPUT +L89C0 jsr L8866 ; raise error if in immediate mode + jsr GETNCH ; eat the "INPUT" token + cmpa #'# ; device number? + bne L89D2 ; brif not + jsr LA5A5 ; parse device number + jsr LA3ED ; make sure it's valid for input + jsr SYNCOMMA ; make sure there's a comma after the device number +L89D2 cmpa #'" ; is there a prompt? + bne L89E1 ; brif not + jsr LB244 ; parse the string + ldb #'; ; make sure there's a semicolon after the prompt + jsr LB26F + jsr LB99F ; go actually display the prompt +L89E1 leas -2,s ; make a hole on the stack (so number of return adddresses is right) + jsr LB035 ; read an input line from current device + leas 2,s ; clean up stack + clr DEVNUM ; reset to screen/keyboard + jsr LB357 ; parse a variable + stx VARDES ; save pointer to it + jsr LB146 ; make sure it's a string + ldx #LINBUF ; point to input buffer + clra ; make sure we terminate on NUL only + jsr LB51A ; parse string and store it in string space + jmp LAFA4 ; go assign the string to its final resting place +; RENUM command +L89FC jsr LAF67 ; read a line number + ldx BINVAL ; get value + rts +L8A02 ldx VD1 ; get current old number being renumbered +L8A04 stx BINVAL ; save number being searched for + jmp LAD01 ; go find line number +RENUM jsr LAD26 ; erase variables + ldd #10 ; default line number interval and start + std VD5 ; set starting line number + std VCF ; set number interval + clrb ; now D is 0 + std VD1 ; save default start for renumbering + jsr GETCCH ; are there any arguments + bcc L8A20 ; brif not numeric + bsr L89FC ; fetch line number + stx VD5 ; save line beginning number + jsr GETCCH ; get input character +L8A20 beq L8A3D ; brif end of line + jsr SYNCOMMA ; make sure we have a comma + bcc L8A2D ; brif next isn't numeric + bsr L89FC ; fetch starting line number + stx VD1 ; save the number where we start working + jsr GETCCH ; fetch input character +L8A2D beq L8A3D ; brif end of line + jsr SYNCOMMA ; make sure we have a comma + bcc L8A3A ; brif we don't have a number + bsr L89FC ; parse the number + stx VCF ; save interval + beq L8A83 ; brif we ave a zero interval +L8A3A jsr LA5C7 ; raise error if more stuff +L8A3D bsr L8A02 ; get address of old number to process + stx VD3 ; save address + ldx VD5 ; get the next renumbered line to use + bsr L8A04 ; find that line + cmpx VD3 ; is it before the previous one? + blo L8A83 ; brif so - raise error + bsr L8A67 ; make sure renumbered line numbers will be in range + jsr L8ADD ; convert line numbers to "expanded" binary + jsr LACEF ; recalculate next line pointers + bsr L8A02 ; get address of first line to renumber + stx VD3 ; save it + bsr L8A91 ; make sure line numbers exist + bsr L8A68 ; renumber the actual lines + bsr L8A91 ; update line numbers in program text + jsr L8B7B ; convert packed binary line numbers to text + jsr LAD26 ; erase variables, reset stack, etc. + jsr LACEF ; recalculate next line pointers + jmp LAC73 ; bounce back to immediate mode +L8A67 skip1lda ; set line number flag to nonzero (skip next instruction) +L8A68 clra ; set line number flag to zero (insert new numbers) + sta VD8 ; save line number flag + ldx VD3 ; get address of line being renumbered + ldd VD5 ; get the current renumbering number + bsr L8A86 ; return if end of program +L8A71 tst VD8 ; test line number flag + bne L8A77 ; brif not adding new numbers + std 2,x ; set new number +L8A77 ldx ,x ; point to next line + bsr L8A86 ; return if end of program + addd VCF ; add interval to current number + bcs L8A83 ; brif we overflowed - bad line number + cmpa #MAXLIN ; maximum legal number? + blo L8A71 ; brif so - do another +L8A83 jmp LB44A ; raise FC error +L8A86 pshs d ; save D (we're going to clobber it) + ldd ,x ; get next line pointer + puls d ; unclobber D + bne L8A90 ; brif not end of program + leas 2,s ; return to caller's caller +L8A90 rts +L8A91 ldx TXTTAB ; get start of program + leax -1,x ; move pointer back one (compensate for leax 1,x below) +L8A95 leax 1,x ; move to next line + bsr L8A86 ; return if end of program +L8A99 leax 3,x ; move past next line address and line number, go one before line +L8A9B leax 1,x ; move to next character + lda ,x ; check input character + beq L8A95 ; brif end of line + stx TEMPTR ; save current pointer + deca ; is it start of packed numeric line number? + beq L8AB2 ; brif so + deca ; does line exist? + beq L8AD3 ; brif line number exists + deca ; not part of something to process? + bne L8A9B ; brif so +L8AAC lda #3 ; set 1st byte to 3 to indicate line number not existing + sta ,x+ + bra L8A99 ; go process another +L8AB2 ldd 1,x ; get MSB of line number + dec 2,x ; is MS byte zero? + beq L8AB9 ; brif not + clra ; set MS byte to 0 +L8AB9 ldb 3,x ; get LSB of line number + dec 4,x ; is it zero? + beq L8AC0 ; brif not + clrb ; clear byte +L8AC0 std 1,x ; save binary number + std BINVAL ; save trial number + jsr LAD01 ; find the line number +L8AC7 ldx TEMPTR ; get start of packed line + bcs L8AAC ; brif line number not found + ldd V47 ; get address of line number + inc ,x+ ; bump first byte to 2 (exists if checking) or 1 if inserting + std ,x ; save address of correct number + bra L8A99 ; go process more +L8AD3 clr ,x ; clear carry and first byte + ldx 1,x ; point to address of correct line + ldx 2,x ; get correct line number + stx V47 ; save it + bra L8AC7 ; insert into line +L8ADD ldx TXTTAB ; get beginning of program + bra L8AE5 +L8AE1 ldx CHARAD ; get input pointer + leax 1,x ; move it forward +L8AE5 bsr L8A86 ; return if end of program + leax 2,x ; move past line address +L8AE9 leax 1,x ; move forward +L8AEB stx CHARAD ; save input pointer +L8AED jsr GETNCH ; get an input character +L8AEF tsta ; is it actual 0? + beq L8AE1 ; brif end of line + bpl L8AED ; brif not a token + ldx CHARAD ; get input pointer + cmpa #0xff ; function? + beq L8AE9 ; brif so - ignore it (and following byte) + jsr RVEC22 ; do the RAM hook thing + cmpa #0xa7 ; THEN? + beq L8B13 ; brif so + cmpa #0x84 ; ELSE? + beq L8B13 ; brif so + cmpa #0x81 ; GO(TO|SUB)? + bne L8AED ; brif not - we don't have a line number + jsr GETNCH ; get TO/SUB + cmpa #0xa5 ; GOTO? + beq L8B13 ; brif so + cmpa #0xa6 ; GOSUB? + bne L8AEB ; brif not +L8B13 jsr GETNCH ; fetch character after token + bcs L8B1B ; brif numeric (line number) +L8B17 jsr GETCCH ; set flags on input character + bra L8AEF ; keep checking for line numbers +L8B1B ldx CHARAD ; get input pointer + pshs x ; save it + jsr LAF67 ; parse line number + ldx CHARAD ; get input pointer after line +L8B24 lda ,-x ; get character before pointer + jsr L90AA ; set C if numeric + bcs L8B24 ; brif not numeric + leax 1,x ; move pointer up + tfr x,d ; calculate size of line number + subb 1,s + subb #5 ; make sure at least 5 bytes + beq L8B55 ; brif exactly 5 bytes - no change + blo L8B41 ; brif less than 5 bytes + leau ,x ; move remainder of program backward + negb ; negate extra number of bytes (to subtract from X) + leax b,x ; now X is the correct position to move program to + jsr L89B8 ; shift program backward + bra L8B55 +L8B41 stx V47 ; save end of line number space (end of copy) + ldx VARTAB ; get end of program + stx V43 ; set source pointer + negb ; get positive difference + leax b,x ; now X is the top of the destination block + stx V41 ; set copy destination + stx VARTAB ; save new end of program + jsr LAC1E ; make sure enough room and make a hole in the program + ldx V45 ; get end address of destination block + stx CHARAD ; set input there +L8B55 puls x ; get starting address of the line number + lda #1 ; set "new number" flag + sta ,x + sta 2,x + sta 4,x + ldb BINVAL ; get MS byte of line number + bne L8B67 ; brif it is not zero + ldb #1 ; set to 1 if MSB is 0 + inc 2,x ; flag MSB as 0 +L8B67 stb 1,x ; set MSB of line number + ldb BINVAL+1 ; get LSB of number + bne L8B71 ; brif nonzero + ldb #1 ; set to 1 if LSB is 0 + inc 4,x ; flag LSB as 0 +L8B71 stb 3,x ; save LSB of line number + jsr GETCCH ; get input character + cmpa #', ; multiple line numbers? (ON GOTO, ON GOSUB) + beq L8B13 ; brif so - process another + bra L8B17 ; go look for more line numbers +L8B7B ldx TXTTAB ; point to start of program + leax -1,x ; move back (compensate for inc below) +L8B7F leax 1,x ; move forward + ldd 2,x ; get this line number + std CURLIN ; save it (for error message) + jsr L8A86 ; return if end of program + leax 3,x ; skip address and line number, stay one before line text +L8B8A leax 1,x ; move to next character +L8B8C lda ,x ; get input character + beq L8B7F ; brif end of line + deca ; valid line new line number? + beq L8BAE ; brif so + suba #2 ; undefined line? + bne L8B8A ; brif not + pshs x ; save line number pointer + ldx #L8BD9-1 ; show UL message + jsr STRINOUT + ldx ,s ; get input pointer + ldd 1,x ; get undefined line number + jsr LBDCC ; display line number + jsr LBDC5 ; print out "IN XXXX" + jsr LB958 ; do a newline + puls x ; get input pointer back +L8BAE pshs x ; save input pointer + ldd 1,x ; get binary value of line number + std FPA0+2 ; save it in FPA0 + jsr L880E ; adjust FPA0 as integer + jsr LBDD9 ; convert to text string + puls u ; get previous input pointer address + ldb #5 ; each expanded line uses 5 bytes +L8BBE leax 1,x ; move pointer forward (in string number) past sign + lda ,x ; do we have a digit? + beq L8BC9 ; brif not - end of number + decb ; mark a byte consumed + sta ,u+ ; put digit in program + bra L8BBE ; copy another digit +L8BC9 leax ,u ; point to address at end of text number + tstb ; did number fill whole space? + beq L8B8C ; brif so - move on + leay ,u ; save end of number pointer + leau b,u ; point to the end of the original expanded number + jsr L89B8 ; close up gap in program + leax ,y ; get end of line number pointer back + bra L8B8C ; go process more +L8BD9 fcn 'UL ' +; HEX$ function +HEXDOL jsr LB740 ; convert argument to positive integer + ldx #STRBUF+2 ; point to string buffer + ldb #4 ; convert 4 nibbles +L8BE5 pshs b ; save nibble counter + clrb ; clear digit accumulator + lda #4 ; do 4 shifts +L8BEA asl FPA0+3 ; shift a bit off the left of the value into low bits of B + rol FPA0+2 + rolb + deca ; done all shifts? + bne L8BEA ; brif not + tstb ; do we have a nonzero digit? + bne L8BFF ; brif so + lda ,s ; is it last digit? + deca + beq L8BFF ; brif so - keep the 0 + cmpx #STRBUF+2 ; is it a middle zero? + beq L8C0B ; brif not +L8BFF addb #'0 ; add ASCII bias + cmpb #'9 ; above 9? + bls L8C07 ; brif not + addb #7 ; adjust into alpha range +L8C07 stb ,x+ ; save digit in output + clr ,x ; make sure we have a NUL term +L8C0B puls b ; get back nibble counter + decb ; done all? + bne L8BE5 ; brif not + leas 2,s ; don't return mainline (we're returning a string) + ldx #STRBUF+1 ; point to start of converted number + jmp LB518 ; save string in string space, etc., and return it +; DLOAD command; this is eliminated on the Coco3. It was basically useless anyway so it was +; a good candidate to overwrite for some extra code space in the lower 16K of the internal +; ROM. Now, DLOAD functions as another entry into the RESET sequence. +DLOAD jsr LA429 ; close tape file +L8C1B orcc #0x50 ; make sure interrupts are disabled + lda #MC3+MC1 ; disable MMU, 32K internal ROM, not "coco" compatible + sta INIT0 + clr TYCLR ; go to ROM mapping mode + jmp SC000 ; transfer control to the "hidden" init code +L8C28 clr INT.FLAG ; set the interrupt flag to not valid + clr PIA1+3 ; disable cartridge interrupt +L8C2E lda #COCO+MMUEN+MC3+MC2 ; enable SCS, 16K split, MMU, COCO mode + sta INIT0 + clr TYCLR ; go to ROM mode +L8C36 rts +L8C37 pshs a,b,x ; save registers + ldx CURPOS ; get cursor position + ldb HRWIDTH ; hi-res mode? + lbne ALINK22 ; brif so + ldb 1,s ; restore B + jmp LA30E ; go back to mainline code +L8C46 pshs cc ; save Z + tst HRWIDTH ; hi-res? + beq L8C4F ; brif not + jmp ALINK23 ; go to hi-res handler +L8C4F puls cc ; get back Z + jmp LA913 ; go back to mainline code + nop + fcb 0xc7 ; junk byte (too few NOPs above) +; This returns to the remainder of the original ECB 1.1 DLOAD code + tstb ; ASCII? + beq L8C5F ; brif not - do error + jsr LAD19 ; clear out program + jmp LAC7C ; go read program +L8C5F jmp LA616 ; raise bad file mode +; CLOADM patch for Extended Basic +L8C62 jsr GETNCH ; get character after CLOAD + cmpa #'M ; CLOADM? + lbne CLOAD ; brif not - Color Basic's CLOAD can handle it + clr FILSTA ; close tape file + jsr GETNCH ; eat the "M" + jsr LA578 ; parse file name + jsr LA648 ; find the file + tst CASBUF+10 ; is it a chunked file? + lbeq LA505 ; brif not - Color Basic's CLOADM can handle it + ldu CASBUF+8 ; get file type and ASCII flag + dec DEVNUM ; set source device to tape + jsr LA635 ; go read the first block + tfr u,d ; put type and ASCII flag somewhere more useful +; NOTE: DLOADM comes here to do the final processing +L8C85 subd #0x200 ; is it binary and "machine language"? + bne L8C5F ; brif not - raise an error + ldx ZERO ; default load offset + jsr GETCCH ; is there any offset? + beq L8C96 ; brif not + jsr SYNCOMMA ; make sure there's a comma + jsr LB73D ; evaluate offset in X +L8C96 stx VD3 ; save offset + jsr LA5C7 ; raise error if more stuff follows +L8C9B bsr L8CC6 ; get type of "amble" + pshs a ; save it + bsr L8CBF ; read in block length + tfr d,y ; save it + bsr L8CBF ; read in load address + addd VD3 ; add in offset + std EXECJP ; save it as the execution address + tfr d,x ; put load address in a pointer + lda ,s+ ; get "amble" type + lbne LA42D ; brif postamble - close file +L8CB1 bsr L8CC6 ; read a data byte + sta ,x ; save in memory + cmpa ,x+ ; did it actually save? + bne L8CCD ; brif not RAM - raise error + leay -1,y ; done yet? + bne L8CB1 ; brif not + bra L8C9B ; look for another "amble" +L8CBF bsr L8CC1 ; read a character to B +L8CC1 bsr L8CC6 ; read character to A + exg a,b ; swap character with previously read one +L8CC5 rts +L8CC6 jsr LA176 ; read a character from input + tst CINBFL ; EOF? + beq L8CC5 ; brif not +L8CCD jmp LA619 ; raise I/O error if EOF +L8CD0 bsr L8D14 ; transmit file name + pshs b,a ; save file status + inca ; was file found? + beq L8CDD ; brif not + ldu ZERO ; zero U - first block + bsr L8CE4 ; read block + puls a,b,pc ; restore status and return +L8CDD ldb #2*26 ; code for NE error + jmp LAC46 ; raise error +L8CE2 ldu CBUFAD ; get block number +L8CE4 leax 1,u ; bump block number + stx CBUFAD ; save new block number + ldx #CASBUF ; use cassette buffer + jsr L8D7C ; read a block + jmp LA644 ; reset input buffer pointers +; Generic input handler for Extended Basic +XVEC4 lda DEVNUM ; get device number + cmpa #-3 ; DLOAD? + bne L8D01 ; brif not + leas 2,s ; don't return to mainline code + clr CINBFL ; reset EOF flag to not EOF + tst CINCTR ; anything available? + bne L8D02 ; brif so - fetch one + com CINBFL ; flag EOF +L8D01 rts +L8D02 pshs u,y,x,b ; save registers + ldx CINPTR ; get buffer pointer + lda ,x+ ; get character from buffer + pshs a ; save it for return + stx CINPTR ; save new input pointer + dec CINCTR ; account for byte removed from buffer + bne L8D12 ; brif buffer not empty + bsr L8CE2 ; go read a block +L8D12 puls a,b,x,y,u,pc ; restore registers, return value, and return +L8D14 clra ; clear attempt counter + pshs x,b,a ; make a hole for variables + leay ,s ; set up frame pointer + bra L8D1D ; go read block +L8D1B bsr L8D48 ; bump attempt counter +L8D1D lda #0x8a ; send file request control code + bsr L8D58 + bne L8D1B ; brif no echo or error + ldx #CFNBUF+1 ; point to file name +L8D26 lda ,x+ ; get file name characater + jsr L8E04 ; send it + cmpx #CFNBUF+9 ; end of file name? + bne L8D26 ; brif not + bsr L8D62 ; output check byte and look for response + bne L8D1B ; transmit name again if not ack + bsr L8D72 ; get file type (0xff is not found) + bne L8D1B ; brif error + sta 2,y ; save file type + bsr L8D72 ; read ASCII flag + bne L8D1B ; brif error + sta 3,y ; save ASCII flag + bsr L8D6B ; read check byte + bne L8D1B ; brif error + leas 2,s ; lose attempt counter and check byte + puls a,b,pc ; return file type and ascii flag +L8D48 inc ,y ; bump attempt counter + lda ,y ; get new count + cmpa #5 ; done 5 times? + blo L8D6A ; brif not + lda #0xbc ; send abort code + jsr L8E0C + jmp LA619 ; raise an I/O error +L8D58 pshs a ; save compare character + bsr L8DB8 ; send character + bne L8D60 ; brif read error + cmpa ,s ; does it match? (set Z if good) +L8D60 puls a,pc ; restore character and return +L8D62 lda 1,y ; get XOR check byte + bsr L8DB8 ; send it and read + bne L8D6A ; brif read error + cmpa #0xc8 ; is it ack? (set Z if so) +L8D6A rts +L8D6B bsr L8D72 ; read character from rs232 + bne L8D6A ; brif error + lda 1,y ; get check byte + rts +L8D72 bsr L8DBC ; read a character from rs232 + pshs a,cc ; save result (and flags) + eora 1,y ; accumulate xor checksum + sta 1,y + puls cc,a,pc ; restore byte, flags, and return +L8D7C clra ; reset attempt counter + pshs u,y,x,b,a ; make a stack frame + asl 7,s ; split block number into two 7 bit chuncks + rol 6,s + lsr 7,s + leay ,s ; set up frame pointer + bra L8D8B +L8D89 bsr L8D48 ; bump attempt counter +L8D8B lda #0x97 ; send block request code + bsr L8D58 + bne L8D89 ; brif error + lda 6,y ; send out block number (high bits first) + bsr L8E04 + lda 7,y + bsr L8E04 + bsr L8D62 ; send check byte and get ack + bne L8D89 ; brif error + bsr L8D72 ; read block size + bne L8D89 ; brif read error + sta 4,y ; save character count + ldx 2,y ; get buffer pointer + ldb #128 ; length of data block +L8DA7 bsr L8D72 ; read a data byte + bne L8D89 ; brif error + sta ,x+ ; save byte in buffer + decb ; done a whole block? + bne L8DA7 ; brif not + bsr L8D6B ; read check byte + bne L8D89 ; brif error + leas 4,s ; lose attempt counter, check byte, and buffer pointer + puls a,b,x,pc ; return with character count in A, clean rest of stack +L8DB8 clr 1,y ; clear check byte + bsr L8E0C ; output character +L8DBC clra ; clear attempt counter + pshs x,b,cc ; save registers and interrupt status + orcc #0x50 ; disable interrupts + lda TIMOUT ; get timout delay (variable) + ldx ZERO ; get constant timeout value +L8DC5 bsr L8DE6 ; get RS232 status + bcc L8DC5 ; brif "space" - waiting for "mark" +L8DC9 bsr L8DE6 ; get RS232 status + bcs L8DC9 ; brif "mark" - waiting for "space" (start bit) + bsr L8DF9 ; delay for half of bit time + ldb #1 ; set bit probe + pshs b ; save it + clra ; reset data byte +L8DD4 bsr L8DF7 ; wait one bit time + ldb PIA1+2 ; get input bit to carry + rorb + bcc L8DDE ; brif "space" (0) + ora ,s ; merge bit probe in +L8DDE asl ,s ; shift bit probe over + bcc L8DD4 ; brif we haven't done 8 bits + leas 1,s ; remove bit probe + puls cc,b,x,pc ; restore interrupts, registers, and return +L8DE6 ldb PIA1+2 ; get RS232 value + rorb ; put in C + leax 1,x ; bump timeout + bne L8DF6 ; brif nonzero + deca ; did the number of waits expire? + bne L8DF6 ; brif not + leas 2,s ; don't return - we timed out + puls cc,b,x ; restore interrupts and registers + inca ; clear Z (A was zero above) +L8DF6 rts +L8DF7 bsr L8DF9 ; do first half of bit delay then fall through for second +L8DF9 pshs a ; save register + lda DLBAUD ; get baud rate constant +L8DFD brn L8DFD ; do nothing - delay + deca ; time expired? + bne L8DFD ; brif not + puls a,pc ; restore register and return +L8E04 pshs a ; save character to send + eora 1,y ; accumulate chechsum + sta 1,y + puls a ; get character back +L8E0C pshs b,a,cc ; save registers and interrupt status + orcc #0x50 ; disable interrupts + bsr L8DF7 ; do a bit delay + bsr L8DF7 ; do another bit delay + clr PIA1 ; set output to space (start bit) + bsr L8DF7 ; do a bit delay + ldb #1 ; bit probe start at LSB + pshs b ; save bitprobe +L8E1D lda 2,s ; get output byte + anda ,s ; see what our current bit is + beq L8E25 ; brif output is 0 + lda #2 ; set output to "marking" +L8E25 sta PIA1 ; send bit + bsr L8DF7 ; do a bit delay + asl ,s ; shift bit probe + bcc L8E1D ; brif not last bit + lda #2 ; set output to marking ("stop" bit) + sta PIA1 + leas 1,s ; lose bit probe + puls cc,a,b,pc ; restore registers, interrupts, and return +; PRINT USING +; This is a massive block of code for something that has limited utility. It is *almost* flexible enough to +; be truly useful outside of a narrow range of use cases. Indeed, this comes in at about 1/8 of the total +; Extended Color Basic ROM. +; +; This uses several variables: +; VD5: pointer to format string descriptor +; VD7: next print item flag +; VD8: right digit counter +; VD9: left digit counter (or length of string argument) +; VDA: status byte (bits as follows): +; 6: force comma +; 5: force leading * +; 4: floating $ +; 3: pre-sign +; 2: post-sign +; 0: scientific notation +L8E37 lda #1 ; set length to use to 1 + sta VD9 +L8E3B decb ; consume character from format string + jsr L8FD8 ; show error flag if flags set + jsr GETCCH ; get input character + lbeq L8ED8 ; brif end of line - bail + stb VD3 ; save remaining string length + jsr LB156 ; evaluate the argument + jsr LB146 ; error if numeric + ldx FPA0+2 ; get descriptor for argument + stx V4D ; save it for later + ldb VD9 ; get length counter to use + jsr LB6AD ; get B bytes of string space (do a LEFT$) + jsr LB99F ; print the formatted string + ldx FPA0+2 ; get formatted string descriptor + ldb VD9 ; get requested length + subb ,x ; see if we have any left over +L8E5F decb ; have we got the right width? + lbmi L8FB3 ; brif so - go process more + jsr LB9AC ; output a space + bra L8E5F ; go see if we're done yet +L8E69 stb VD3 ; save current format string counter and pointer + stx TEMPTR + lda #2 ; initial spaces count = 2 (for the two %s) + sta VD9 ; save length counter +L8E71 lda ,x ; get character in string + cmpa #'% ; is it the end of the sequence? + beq L8E3B ; brif so - display requested part of the strign + cmpa #0x20 ; space? + bne L8E82 ; brif not + inc VD9 ; bump spaces count + leax 1,x ; move format pointer forward + decb ; consume character + bne L8E71 ; brif not end of format string +L8E82 ldx TEMPTR ; restore format string pointer + ldb VD3 ; get back format string length + lda #'% ; show % as debugging aid +L8E88 jsr L8FD8 ; send error indicator if flags set + jsr PUTCHR ; output character + bra L8EB9 ; go process more format string +; PRINT extension for USING +XVEC9 cmpa #0xcd ; USING? + beq L8E95 ; brif so + rts ; return to mainline code +; This is the main entry point for PRINT USING +L8E95 leas 2,s ; don't return to the mainline code + jsr LB158 ; evaluate the format string + jsr LB146 ; error if numeric + ldb #'; ; make sure there's a ; after the string + jsr LB26F + ldx FPA0+2 ; get format string descriptor + stx VD5 ; save it for later + bra L8EAE ; process format string +L8EA8 lda VD7 ; is there a print item? + beq L8EB4 ; brif not + ldx VD5 ; get back format string descriptor +L8EAE clr VD7 ; reset next print item flag + ldb ,x ; get length of format string + bne L8EB7 ; brif string is non-null +L8EB4 jmp LB44A ; raise FC error +L8EB7 ldx 2,x ; point to start of string +L8EB9 clr VDA ; clear status (new item) +L8EBB clr VD9 ; clear left digit counter + lda ,x+ ; get character from format string + cmpa #'! ; ! (use first character of string)? + lbeq L8E37 ; brif so + cmpa #'# ; digit? + beq L8F24 ; brif so - handle numeric + decb ; consume format character + bne L8EE2 ; brif not done + jsr L8FD8 ; show a "+" if any flags set - indicates error at end of string + jsr PUTCHR ; output format string character +L8ED2 jsr GETCCH ; get current input character + bne L8EA8 ; brif not end of statement + lda VD7 ; get next item flag +L8ED8 bne L8EDD ; brif more print items + jsr LB958 ; do newline +L8EDD ldx VD5 ; point to format string descriptor + jmp LB659 ; remove from string stack, etc., if appropriate (and return) +L8EE2 cmpa #'+ ; is it + (pre-sign)? + bne L8EEF ; brif not + jsr L8FD8 ; send a "+" if flags set + lda #8 ; flag for pre-sign + sta VDA ; set flags + bra L8EBB ; go interpret some more stuff +L8EEF cmpa #'. ; decimal? + beq L8F41 ; brif so - numeric + cmpa #'% ; % (show string)? + lbeq L8E69 ; brif so + cmpa ,x ; do we have two identical characters? +L8EFB bne L8E88 ; brif not - invalid format character + cmpa #'$ ; double $? + beq L8F1A ; brif so - floating $ + cmpa #'* ; double *? + bne L8EFB ; brif not + lda VDA ; get status byte + ora #0x20 ; enable * padding + sta VDA + cmpb #2 ; is $$ the last two? + blo L8F20 ; brif so + lda 1,x ; is it $ after? + cmpa #'$ + bne L8F20 ; brif not + decb ; consume the "$" + leax 1,x + inc VD9 ; add to digit counter * pad + $ counter +L8F1A lda VDA ; indicate floating $ + ora #0x10 + sta VDA +L8F20 leax 1,x ; consume the second format character + inc VD9 ; add one more left place +L8F24 clr VD8 ; clear right digit counter +L8F26 inc VD9 ; bump left digit counter + decb ; consume character + beq L8F74 ; brif end of string + lda ,x+ ; get next format character + cmpa #'. ; decimal? + beq L8F4F ; brif so + cmpa #'# ; digit? + beq L8F26 ; brif so + cmpa #', ; comma flag? + bne L8F5A ; brif not + lda VDA ; set commas flag + ora #0x40 + sta VDA + bra L8F26 ; handle more characters to left of decimal +L8F41 lda ,x ; get character after . + cmpa #'# ; digit? + lbne L8E88 ; brif not - invalid + lda #1 ; set right digit counter to 1 (for the .) + sta VD8 + leax 1,x ; consume the . +L8F4F inc VD8 ; add one to right digit counter + decb ; consume character + beq L8F74 ; brif end of format string + lda ,x+ ; get another format character + cmpa #'# ; digit? + beq L8F4F ; brif so +L8F5A cmpa #0x5e ; up arrow? + bne L8F74 ; brif not + cmpa ,x ; two of them? + bne L8F74 ; brif not + cmpa 1,x ; three of them? + bne L8F74 ; brif not + cmpa 2,x ; four of them? + bne L8F74 ; brif not + cmpb #4 ; string actually has the characters? + blo L8F74 ; brif not + subb #4 ; consome them + leax 4,x + inc VDA ; set scientific notation bit +L8F74 leax -1,x ; back up input pointer + inc VD9 ; add one digit for pre-sign force + lda VDA ; is it pre-sign? + bita #8 + bne L8F96 ; brif so + dec VD9 ; undo pre-sign adjustment + tstb ; end of string? + beq L8F96 ; brif so + lda ,x ; get next character + suba #'- ; post sign force? + beq L8F8F ; brif so + cmpa #'+-'- ; plus? + bne L8F96 ; brif not + lda #8 ; trailing + is a pre-sign force +L8F8F ora #4 ; add in post sign flag + ora VDA ; merge with flags + sta VDA + decb ; consume character +L8F96 jsr GETCCH ; do we have an argument + lbeq L8ED8 ; brif not + stb VD3 ; save format string length + jsr LB141 ; evluate numeric expression + lda VD9 ; get left digit counter + adda VD8 ; add in right digit counter + cmpa #17 ; is it more than 16 digits + decimal? + lbhi LB44A ; brif so - this is a problem + jsr L8FE5 ; format value according to settings + leax -1,x ; move buffer pointer back + jsr STRINOUT ; display formatted number string +L8FB3 clr VD7 ; reset next print item flag + jsr GETCCH ; get current input character + beq L8FC6 ; brif end of statement + sta VD7 ; set next print flag to nonzero + cmpa #'; ; list separator ;? + beq L8FC4 ; brif so + jsr SYNCOMMA ; require a comma between if not ; + bra L8FC6 ; process next item +L8FC4 jsr GETNCH ; munch the semicolon +L8FC6 ldx VD5 ; get format string descriptor + ldb ,x ; get length of string + subb VD3 ; subtract amount left after last item + ldx 2,x ; point to string address + abx ; move pointer to correct spot + ldb VD3 ; get remaining string length + lbne L8EB9 ; if we have more, interpret from there + jmp L8ED2 ; re-interpret from start if we hit the end +L8FD8 pshs a ; save character + lda #'+ ; "error" flag character + tst VDA ; did we have some flags set? + beq L8FE3 ; brif not + jsr PUTCHR ; output error flag +L8FE3 puls a,pc ; restore character and return +L8FE5 ldu #STRBUF+4 ; point to string buffer + ldb #0x20 ; blank space + lda VDA ; get flags + bita #8 ; pre-sign? + beq L8FF2 ; brif not + ldb #'+ ; plus sign +L8FF2 tst FP0SGN ; get sign of value + bpl L8FFA ; brif positive + clr FP0SGN ; make number positive (for later) + ldb #'- ; negative sign +L8FFA stb ,u+ ; put sign in buffer + ldb #'0 ; put a zero there + stb ,u+ + anda #1 ; check scientific notation force + lbne L910D ; brif so + ldx #LBDC0 ; point to FP 1E+9 + jsr LBCA0 ; is it less? + bmi L9023 ; brif so + jsr LBDD9 ; convert FP number to string (we're doing scientific notation) +L9011 lda ,x+ ; advance pointer to end of string + bne L9011 +L9015 lda ,-x ; make a hole at the start + sta 1,x + cmpx #STRBUF+3 ; done yet? + bne L9015 ; brif not + lda #'% ; put "overflow" flag at start + sta ,x + rts +L9023 lda FP0EXP ; get exponent of value + sta V47 ; save it + beq L902C ; brif value is 0 + jsr L91CD ; convert to number with 9 significant figures to left of decimal +L902C lda V47 ; get base 10 exponent offset + lbmi L90B3 ; brif < 100,000,000 + nega ; get negative difference + adda VD9 ; add to number of left digits + suba #9 ; account for the 9 we actually have + jsr L90EA ; put leading zeroes in buffer + jsr L9263 ; initialize the decimal point and comma counters + jsr L9202 ; convert FPA0 to decimal ASCII in buffer + lda V47 ; get base 10 exponent + jsr L9281 ; put that many zeroes in buffer, stop at decimal point + lda V47 ; get base 10 exponent + jsr L9249 ; check for decimal + lda VD8 ; get right digit counter + bne L9050 ; brif we want stuff after decimal + leau -1,u ; delete decimal if not needed +L9050 deca ; subtract one place (for decimal) + jsr L90EA ; put zeroes in buffer (trailing) +L9054 jsr L9185 ; insert * padding, floating $, and post-sign + tsta ; was there a post sign? + beq L9060 ; brif not + cmpb #'* ; was first character a *? + beq L9060 ; brif so + stb ,u+ ; store the post sign +L9060 clr ,u ; make srue it's NUL terminated + ldx #STRBUF+3 ; point to start of buffer +L9065 leax 1,x ; move to next character + stx TEMPTR ; save it for later + lda VARPTR+1 ; get address of decimal point + suba TEMPTR+1 ; subtract out actual digits left of decimal + suba VD9 ; subtract out required left digits + beq L90A9 ; brif no padding needed + lda ,x ; get current character + cmpa #0x20 ; space? + beq L9065 ; brif so - advance pointer + cmpa #'* ; *? + beq L9065 ; brif so - advance pointer + clra ; zero on stack is end of data ponter +L907C pshs a ; save character on stack + lda ,x+ ; get next character + cmpa #'- ; minus? + beq L907C ; brif so + cmpa #'+ ; plus? + beq L907C ; brif so + cmpa #'$ ; $? + beq L907C ; brif so + cmpa #'0 ; zero? + bne L909E ; brif not + lda 1,x ; get character after 0 + bsr L90AA ; clear carry if number + bcs L909E ; brif not number +L9096 puls a ; get character off stack + sta ,-x ; put it back in string buffer + bne L9096 ; brif not - restore another + bra L9065 ; keep cleaning up buffer +L909E puls a ; get the character on the stack + tsta ; is it NUL? + bne L909E ; brif not + ldx TEMPTR ; get string buffer start pointer + lda #'% ; put error flag in front + sta ,-x +L90A9 rts +L90AA cmpa #'0 ; zero? + blo L90B2 ; brif not + suba #'9+1 ; set C if > "9" + suba #-('9+1) +L90B2 rts +L90B3 lda VD8 ; get right digit counter + beq L90B8 ; brif not right digits + deca ; account for decimal point +L90B8 adda V47 ; add base 10 exponent offset + bmi L90BD ; if >= 0, no shifts are required + clra ; force shift counter to 0 +L90BD pshs a ; save shift counter +L90BF bpl L90CB ; brif positive count + pshs a ; save shift counter + jsr LBB82 ; divide FPA0 by 10 (shift 1 digit to the right) + puls a ; get shift counter back + inca ; account for the shift + bra L90BF ; see if we're done yet +L90CB lda V47 ; get base 10 exponent offset + suba ,s+ ; account for adjustment + sta V47 ; save new exponent offset + adda #9 ; account for significant places + bmi L90EE ; brif we don't need zeroes to left + lda VD9 ; get left decimal counter + suba #9 ; account for significant figures + suba V47 ; subtract exponent offset + bsr L90EA ; output leading zeroes + jsr L9263 ; initialize decimal and comma counters + bra L90FF ; process remainder of digits +L90E2 pshs a ; save zero counter + lda #'0 ; insert a 0 + sta ,u+ + puls a ; get back counter +L90EA deca ; do we need more zeroes? + bpl L90E2 ; brif so + rts +L90EE lda VD9 ; get left digit counter + bsr L90EA ; put that many zeroes in + jsr L924D ; put decimal in buffer + lda #-9 ; figure out filler zeroes + suba V47 + bsr L90EA ; output required leader zeroes + clr V45 ; clear decimal pointer counter + clr VD7 ; clear comma counter +L90FF jsr L9202 ; decode FPA0 to decimal string + lda VD8 ; get right digit counter + bne L9108 ; brif there are right digits + ldu VARPTR ; point to decimal location of decimal +L9108 adda V47 ; add base 10 exponent + lbra L9050 ; add in leading astrisks, etc. +L910D lda FP0EXP ; get exponent of FPA0 + pshs a ; save it + beq L9116 ; brif 0 + jsr L91CD ; convert to number with 9 figures +L9116 lda VD8 ; get right digit counter + beq L911B ; brif no right digits + deca ; account for decimal point +L911B adda VD9 ; get left digit counter + clr STRBUF+3 ; use buffer byte as temporary storage + ldb VDA ; get status flags + andb #4 ; post-sign? + bne L9129 ; brif so + com STRBUF+3 ; flip byte if no post sign +L9129 adda STRBUF+3 ; subtract 1 if no post sign + suba #9 ; account for significant figures + pshs a ; save shift counter +L9130 bpl L913C ; brif no more shifts needed + pshs a ; save counter + jsr LBB82 ; divide by 10 (shift right one) + puls a ; get back counter + inca ; account for the shift + bra L9130 ; see if we need more +L913C lda ,s ; get original shift count + bmi L9141 ; brif shifting happened + clra ; flag for no shifting +L9141 nega ; calculate position of decimal (negate shift count, add left digit and post sign if needed) + adda VD9 ; add left digit counter + inca ; and post sign + adda STRBUF+3 + sta V45 ; save decimal counter + clr VD7 ; clear comma counter + jsr L9202 ; convert to decimal string + puls a ; get shift counter + jsr L9281 ; put the needed zeroes in + lda VD8 ; get right digit counter + bne L915A ; brif we want some + leau -1,u ; remove te decimal point +L915A ldb ,s+ ; get original exponent + beq L9167 ; brif it was 0 + ldb V47 ; get base 10 exponent + addb #9 ; account for significant figures + subb VD9 ; remove left digit count + subb STRBUF+3 ; add one if post sign +L9167 lda #'+ ; positive sign + tstb ; is base 10 exponent positive? + bpl L916F ; brif so + lda #'- ; negative sign + negb ; flip exponent +L916F sta 1,u ; put exponent sign + lda #'E ; put "E" and advance output pointer + sta ,u++ + lda #'0-1 ; initialize digit accumulator +L9177 inca ; bump digit + subb #10 ; are we at the right digit? + bcc L9177 ; brif not + addb #'0+10 ; add ASCII bias and undo extra subtraction + std ,u++ ; save exponent in buffer + clr ,u ; clear final byte in buffer + jmp L9054 ; insert *, $, etc. +L9185 ldx #STRBUF+4 ; point to start of result + ldb ,x ; get sign + pshs b ; save it + lda #0x20 ; default pad with spaces + ldb VDA ; get flags + bitb #0x20 ; padding with *? + puls b + beq L919E ; brif no padding + lda #'* ; pad with * + cmpb #0x20 ; do we have a blank? (positive) + bne L919E ; brif not + tfr a,b ; use pad character +L919E pshs b ; save first character +L91A0 sta ,x+ ; store padding + ldb ,x ; get next character + beq L91B6 ; brif end of string + cmpb #'E ; exponent? + beq L91B6 ; brif so - treat as 0 + cmpb #'0 ; zero? + beq L91A0 ; brif so - pad it + cmpb #', ; leading comma? + beq L91A0 ; brif so - pad it + cmpb #'. ; decimal? + bne L91BA ; brif so - don't put a 0 before it +L91B6 lda #'0 ; put a zero before + sta ,-x +L91BA lda VDA ; get status byte + bita #0x10 ; floating $? + beq L91C4 ; brif not + ldb #'$ ; stuff a $ in + stb ,-x +L91C4 anda #4 ; pre-sgn? + puls b ; get back first character + bne L91CC ; brif not + stb ,-x ; save leading character (sign) +L91CC rts +L91CD pshs u ; save buffer pointer + clra ; initial exponent offset is 0 +L91D0 sta V47 ; save exponent offset + ldb FP0EXP ; get actual exponent + cmpb #0x80 ; is value >= 1.0? + bhi L91E9 ; brif so + ldx #LBDC0 ; point to FP number 1E9 + jsr LBACA ; multiply by 1000000000 + lda V47 ; account for 9 shifts + suba #9 + bra L91D0 ; brif not there yet +L91E4 jsr LBB82 ; divide by 10 + inc V47 ; account for shift +L91E9 ldx #LBDBB ; point to 999999999 + jsr LBCA0 ; compare it + bgt L91E4 ; brif not in range yet +L91F1 ldx #LBDB6 ; point to 99999999.9 + jsr LBCA0 ; compare + bgt L9200 ; brif in range + jsr LBB6A ; multiply by 10 + dec V47 ; account for shift + bra L91F1 ; see if we're in range yet +L9200 puls u,pc ; restore buffer pointer and return +L9202 pshs u ; save buffer pointer + jsr LB9B4 ; add .5 (round off) + jsr LBCC8 ; convert to integer format + puls u ; restore buffer pointer + ldx #LBEC5 ; point to 32 bit powers of 10 (alternating signs) + ldb #0x80 ; intitial digit counter is 0 with 0x80 bias +L9211 bsr L9249 ; check for comma +L9213 lda FPA0+3 ; add a power of 10 + adda 3,x + sta FPA0+3 + lda FPA0+2 + adca 2,x + sta FPA0+2 + lda FPA0+1 + adca 1,x + sta FPA0+1 + lda FPA0 + adca ,x + sta FPA0 + incb ; add one to digit counter + rorb ; set V if carry and sign differ + rolb + bvc L9213 ; brif we haven't "wrapped" + bcc L9235 ; brif subtracting + subb #10+1 ; take 9's complement if adding + negb +L9235 addb #'0-1 ; add in ASCII bias + leax 4,x ; move to next power + tfr b,a ; save digit + anda #0x7f ; mask off subtract flag + sta ,u+ ; save digit + comb ; toggle add/subtract + andb #0x80 + cmpx #LBEE9 ; done all places? + bne L9211 ; brif not + clr ,u ; but NUL at end +L9249 dec V45 ; at decimal? + bne L9256 ; brif not +L924D stu VARPTR ; save decimal point pointer + lda #'. ; insert decimal + sta ,u+ + clr VD7 ; clear comma counter + rts +L9256 dec VD7 ; do we need a comma? + bne L9262 ; brif not + lda #3 ; reset comma counter + sta VD7 + lda #', ; insert comma + sta ,u+ +L9262 rts +L9263 lda V47 ; get base 10 exponent offset + adda #10 ; account for significant figures + sta V45 ; save decimal counter + inca ; add one for decimal point +L926A suba #3 ; divide by 3, leave remainder in A + bcc L926A + adda #5 ; renormalize to range 1-3 + sta VD7 ; save comma counter + lda VDA ; get status + anda #0x40 ; commas wanted? + bne L927A ; brif not + sta VD7 ; clear comma counter +L927A rts +L927B pshs a ; save zeroes counter + bsr L9249 ; check for decimal + puls a ; get back counter +L9281 deca ; need a zero? + bmi L928E ; brif not + pshs a ; save counter + lda #'0 ; put a zero + sta ,u+ + lda ,s+ ; get back counter and set flags + bne L927B ; brif not done enough +L928E rts +; From here to the end of the Extended Basic ROM is the PMODE graphics system and related +; infrastructure with the exception of the PLAY command which shares some of its machinery +; with the DRAW command. +; +; Fetch screen address calculation routine address for the selected graphics mode +L928F ldu #L929C ; point to normalization routine jump table + lda PMODE ; get graphics mode + asla ; two bytes per address + ldu a,u ; get routine address + rts +; Normalize VERBEG/HORBEG coordinates and return screen address in X and pixel mask in A. +L9298 bsr L928F ; fetch normalization routine pointer + jmp ,u ; transfer control to it +L929C fdb L92A6 ; PMODE 0 + fdb L92C2 ; PMODE 1 + fdb L92A6 ; PMODE 2 + fdb L92C2 ; PMODE 3 + fdb L92A6 ; PMODE 4 +; Two colour mode address calculatoin +L92A6 pshs u,b ; savce registers + ldb HORBYT ; get number of bytes in each graphics row + lda VERBEG+1 ; get vertical coordinate + mul + addd BEGGRP ; now D is the absolute address of the start of the row + tfr d,x ; get address to the return location + ldb HORBEG+1 ; get horizontal coordinate + lsrb ; divide by 8 (8 pixels per byte in 2 colour mode) + lsrb + lsrb + abx ; now X is the address of the actual pixel byte + lda HORBEG+1 ; get horizontal coordinate + anda #7 ; keep only the low 3 bits which contain the pixel number + ldu #L92DD ; point to pixel mask lookup + lda a,u ; get pixel mask + puls b,u,pc ; restore registers and return result +; four colour address calculation +L92C2 pshs u,b ; save registers + ldb HORBYT ; get bytes per graphics row + lda VERBEG+1 ; get vertical coordinate + mul + addd BEGGRP ; now D is the address of the start of the row + tfr d,x ; put it in returnlocatin + ldb HORBEG+1 ; get horizontal coordinate + lsrb ; divide by 4 (four colour modes have four pixels per byte) + lsrb + abx ; now X points to the screen byte + lda HORBEG+1 ; get horizontal coordinate + anda #3 ; keep low two bits for pixel number + ldu #L92E5 ; point to four colour pixel masks + lda a,u ; get pixel mask + puls b,u,pc ; restore registers and return result +L92DD fcb 0x80,0x40,0x20,0x10 ; two colour pixel masks + fcb 0x08,0x04,0x02,0x01 +L92E5 fcb 0xc0,0x30,0x0c,0x03 ; four colour pixel masks +; Move X down one graphics row +L92E9 ldb HORBYT ; get bytes per row + abx ; add to screen address + rts +; Move one pixel right in 2 colour mode +L92ED lsra ; move pixel mask right + bcc L92F3 ; brif same byte + rora ; move pixel mask to left of byte + leax 1,x ; move to next byte +L92F3 rts +; Move one pixel right in 4 colour mode +L92F4 lsra ; shift mask half a pixel right + bcc L92ED ; brif not past end of byte - shift one more + lda #0xc0 ; set mask on left of byte + leax 1,x ; move to next byte + rts +; Evaluate two expressions (coordinates). Put first in HORBEG and second in VERBEG. +L92FC jsr LB734 ; evaluate two expressions - first in BINVAL, second in B + ldy #HORBEG ; point to storage location +L9303 cmpb #192 ; is vertical outside range? + blo L9309 ; brif not + ldb #191 ; max it at bottom of screen +L9309 clra ; zero extend vertical coordinate + std 2,y ; save vertical coordinate + ldd BINVAL ; get horizontal coordinate + cmpd #256 ; in range? + blo L9317 ; brif so + ldd #255 ; max it out to right side of screen +L9317 std ,y ; save horizontal coordinate + rts +; Normalize coordinates for proper PMODE +L931A jsr L92FC ; parse coordinates +L931D ldu #HORBEG ; point to start coordinates +L9320 lda PMODE ; get graphics mode + cmpa #2 ; is it pmode 0 or 1? + bhs L932C ; brif not + ldd 2,u ; get vertical coordinate + lsra ; divide it by two + rorb + std 2,u ; save it back +L932C lda PMODE ; get graphics mode + cmpa #4 ; pmode 4? + bhs L9338 ; brif so + ldd ,u ; cut horizontal coordinate in half + lsra + rorb + std ,u ; save new coordinate +L9338 rts +; PPOINT function +PPOINT jsr L93B2 ; evaluate two expressions (coordinates) + jsr L931D ; normalize coordinates + jsr L9298 ; get screen address + anda ,x ; get colour value of desired screen coordinate + ldb PMODE ; get graphics mode + rorb ; is it a two colour m ode? + bcc L935B ; brif so +L9349 cmpa #4 ; is it on rightmost bits? + blo L9351 ; brif not + rora ; shift right + rora + bra L9349 ; see if we're there yet +L9351 inca ; colour numbers start at 1 + asla ; add in colour set (0 or 8) + adda CSSVAL + lsra ; get colour in range of 0 to 8 +L9356 tfr a,b ; put result to B + jmp LB4F3 ; return B as FP number +L935B tsta ; is pixel on? + beq L9356 ; brif not, return 0 (off) + clra ; set colour number to "1" + bra L9351 ; make it 1 or 5 and return +; PSET command +PSET lda #1 ; PSET flag + bra L9366 ; go turn on the pixel +; PRESET command +PRESET clra ; PRESET flag +L9366 sta SETFLG ; store whether we're setting or resetting + jsr LB26A ; enforce ( + jsr L931A ; evaluate coordinates + jsr L9581 ; evaluate colour + jsr LB267 ; enforce ) + jsr L9298 ; get address of pixel +L9377 ldb ,x ; get screen data + pshs b ; save it + tfr a,b ; duplicate pixel mask + coma ; invert mask + anda ,x ; turn off screen pixel + andb ALLCOL ; adjust pixel mask to be the current colour + pshs b ; merge pixel data into the screen data + ora ,s+ + sta ,x ; put it on screen + suba ,s+ ; nonzero if screen data changed + ora CHGFLG ; propagate change flag + sta CHGFLG + rts +; Evaluate two sets of coordinates separated by a minus sign. First at HORBEG,VERBEG and +; second at HOREND,VEREND. If no first set, use HORDEF,VERDEF. +L938F ldx HORDEF ; set default start coords + stx HORBEG + ldx VERDEF + stx VERBEG + cmpa #0xac ; do we start with a -? + beq L939E ; brif no starting coordinates + jsr L93B2 ; parse coordinates +L939E ldb #0xac ; make sure we have a - + jsr LB26F + jsr LB26A ; require a ( + jsr LB734 ; evaluate two expressions + ldy #HOREND ; point to storage location + jsr L9303 ; process coordinates + bra L93B8 ; finish up with a ) +L93B2 jsr LB26A ; make sure there's a ( + jsr L92FC ; evaluate coordinates +L93B8 jmp LB267 ; force a ) +; LINE command +LINE cmpa #0x89 ; is it LINE INPUT? + lbeq L89C0 ; brif so - go handle it + cmpa #'( ; starting coord? + beq L93CE ; brif so + cmpa #0xac ; leading -? + beq L93CE ; brif so + ldb #'@ ; if it isn't the above, make sure it's @ + jsr LB26F +L93CE jsr L938F ; parse coordinates + ldx HOREND ; set ending coordinates as the defaults + stx HORDEF + ldx VEREND + stx VERDEF + jsr SYNCOMMA ; make sure we have a comma + cmpa #0xbe ; PRESET? + beq L93E9 ; brif so + cmpa #0xbd ; PSET? + lbne LB277 ; brif not + ldb #01 ; PSET flag + skip1lda ; skip byte and set A nonzero +L93E9 clrb ; PRESET flag + pshs b ; save PSET/PRESET flag + jsr GETNCH ; eat the PSET/PRESET + jsr L9420 ; normalize coordinates + puls b ; get back PSET/PRESET flag + stb SETFLG ; flag which we're doing + jsr L959A ; set colour byte + jsr GETCCH ; get next bit + lbeq L94A1 ; brif no box option + jsr SYNCOMMA ; make sure it's comma + ldb #'B ; make sure "B" for "box" + jsr LB26F + bne L9429 ; brif something follows the B + bsr L9444 ; draw horizontal line + bsr L946E ; draw vertical line + ldx HORBEG ; save horizontal coordinate + pshs x ; save it + ldx HOREND ; switch in horizontal end + stx HORBEG + bsr L946E ; draw vertical line + puls x ; get back original start + stx HORBEG ; put it back + ldx VEREND ; do the same dance with the vertical end + stx VERBEG + bra L9444 ; draw horizontal line +L9420 jsr L931D ; normalize the start coordinates + ldu #HOREND ; point to end coords + jmp L9320 ; normalize those coordinates +L9429 ldb #'F ; make sure we have "BF" for "filled box" + jsr LB26F + bra L9434 ; fill the box +L9430 leax -1,x ; move vertical coordinate up one +L9432 stx VERBEG ; save new vertical coordinate +L9434 jsr L9444 ; draw a horizontal line + ldx VERBEG ; are we at the end of the box? + cmpx VEREND + beq L9443 ; brif so + bcc L9430 ; brif we're moving up the screen + leax 1,x ; move down the screen + bra L9432 ; go draw another line +L9443 rts +; Draw a horizontal line from HORBEG to HOREND at VERBEG using color mask in ALLCOL +L9444 ldx HORBEG ; get starting horizontal coordinate + pshs x ; save it + jsr L971D ; get absolute value of HOREND-HORBEG + bcc L9451 ; brif end is > start + ldx HOREND ; copy end coordinate to start it is smaller + stx HORBEG +L9451 tfr d,y ; save difference - it's a pixel count + leay 1,y ; coordinates are inclusive + jsr L9298 ; get screen position of start coord + puls u ; restore original start coordinate + stu HORBEG + bsr L9494 ; point to routine to move pizel pointers to right +L945E sta VD7 ; save pixel mask + jsr L9377 ; turn on pixel + lda VD7 ; get pixel mask back + jsr ,u ; move one pixel right + leay -1,y ; turned on enough pixels yet? + bne L945E ; brif not +L946B rts +L946C puls b,a ; clean up stack +L946E ldd VERBEG ; save original vertical start coordinate + pshs b,a + jsr L9710 ; get vertical difference + bcc L947B ; brif end coordinate > start + ldx VEREND ; swap in end coordinate if not + stx VERBEG +L947B tfr d,y ; save number of pixels to set + leay 1,y ; the coordinates are inclusive + jsr L9298 ; get screen pointer + puls u ; restore start coordinate + stu VERBEG + bsr L949D ; point to routine to move down one row + bra L945E ; draw vertical line +; Point to routine which will move one pixel right +L948A fdb L92ED ; PMODE 0 + fdb L92F4 ; PMODE 1 + fdb L92ED ; PMODE 2 + fdb L92F4 ; PMODE 3 + fdb L92ED ; PMODE 4 +L9494 ldu #L948A ; point to jump table + ldb PMODE ; get graphics mode + aslb ; two bytes per address + ldu b,u ; get jump address + rts +; Point to routine to move down one row +L949D ldu #L92E9 ; point to "move down one row" routien + rts +; Draw a line from HORBEG,VERBEG to HOREND,VEREND +L94A1 ldy #L950D ; point to increase vertical coord + jsr L9710 ; calculate difference + lbeq L9444 ; brif none - draw a horizontal line + bcc L94B2 ; brif vertical end is > vertical start + ldy #L951B ; point to decrease vertical coord +L94B2 pshs d ; save vertical difference + ldu #L9506 ; point to increase horizontal coord + jsr L971D ; get difference + beq L946C ; brif none - draw a vertical line + bcc L94C1 ; brif horizontal end > horizontal start + ldu #L9514 ; point to decrease hoizontal coord +L94C1 cmpd ,s ; compare vert and horiz differences + puls x ; get X difference + bcc L94CC ; brif horiz diff > vert diff + exg u,y ; swap change routine pointers + exg d,x ; swap differences +L94CC pshs u,d ; save larger difference and routine + pshs d ; save larger difference + lsra ; divide by two + rorb + bcs L94DD ; brif odd number + cmpu #L950D+1 ; increase or decrease? + blo L94DD ; brif increase + subd #1 ; back up one +L94DD pshs x,b,a ; save smallest difference and initial middle offset + jsr L928F ; point to proper coordinate to screen conversion routine +L94E2 jsr ,u ; convert coordinates to screen address + jsr L9377 ; turn on a pixel + ldx 6,s ; get distnace counter + beq L9502 ; brif line is completely drawn + leax -1,x ; account for one pixel drawn + stx 6,s ; save new counter + jsr [8,s] ; increment/decrement larger delta + ldd ,s ; get the minor coordinate increment counter + addd 2,s ; add the smallest difference + std ,s ; save new minor coordinate incrementcounter + subd 4,s ; subtractout the largest difference + bcs L94E2 ; brif not greater - draw another pixel + std ,s ; save new minor coordinate increment + jsr ,y ; adjust minor coordinate + bra L94E2 ; go draw another pixel +L9502 puls x ; clean up stack and return + puls a,b,x,y,u,pc +L9506 ldx HORBEG ; bump horizontal coordinate + leax 1,x + stx HORBEG + rts +L950D ldx VERBEG ; bump vertical coordinate + leax 1,x + stx VERBEG + rts +L9514 ldx HORBEG ; decrement horizontal coordinate + leax -1,x + stx HORBEG + rts +L951B ldx VERBEG ; decrement vertical coordinate + leax -1,x + stx VERBEG + rts +; Get normalized maximum coordinate values in VD3 and VD5 +L9522 ldu #VD3 ; point to temp storage + ldx #255 ; set maximum horizontal + stx ,u + ldx #191 ; set maximum vertical + stx 2,u + jmp L9320 ; normalize them +; PCLS command +PCLS beq L9542 ; clear to background colour if no argument + bsr L955A ; evaluate colour +L9536 lda #0x55 ; consider each byte as 4 groups of 2 bit sub-nibbles + mul ; now colour is in all four sub-pixels + ldx BEGGRP ; get start of graphics screen +L953B stb ,x+ ; set byte to proper colour + cmpx ENDGRP ; at end of graphics page? + bne L953B ; brif not + rts +L9542 ldb BAKCOL ; get background colour + bra L9536 ; do the clearing dance +; COLOR command +COLOR cmpa #', ; check for comma + beq L9552 ; brif no foreground colour + bsr L955A ; evaluate first colour + stb FORCOL ; set foreground colour + jsr GETCCH ; is there a background colour? + beq L9559 ; brif not +L9552 jsr SYNCOMMA ; make sure we have a comma + bsr L955A ; evaluate background colour argument + stb BAKCOL ; set background colour +L9559 rts +; Evaluate a colour agument and convert to proper code based on graphics mode +L955A jsr EVALEXPB ; evaluate colour code +L955D cmpb #9 ; is it in range of 0-8? + lbhs LB44A ; brif not - raise error + clra ; CSS value for first colour set + cmpb #5 ; is it first or second colour set? + blo L956C ; brif first colour set + lda #8 ; flag second colour set + subb #4 ; adjust into basic range +L956C pshs a ; save CSS value + lda PMODE ; get graphics mode + rora ; 4 colour or 2? + bcc L957B ; brif 2 colour + tstb ; was it 0? + bne L9578 ; brif not +L9576 ldb #4 ; if so, make it 4 +L9578 decb ; convert to zero based +L9579 puls a,pc ; get back CSS value and return +L957B rorb ; is colour number odd? + bcs L9576 ; brif so - force all bits set colour + clrb ; force colour 0 if not + bra L9579 +; Set all pixel byte and active colour +L9581 jsr L959A ; set colour byte + jsr GETCCH ; is there something to evaluate? + beq L9598 ; brif not + cmpa #') ; )? + beq L9598 ; brif so + jsr SYNCOMMA ; force comma + cmpa #', ; another comma? + beq L9598 ; brif so + jsr L955A ; evaluate expression and return colour + bsr L95A2 ; save colour and pixel byte +L9598 jmp GETCCH ; re-fetch input character and return +L959A ldb FORCOL ; use foreground colour by default + tst SETFLG ; doing PRESET? + bne L95A2 ; brif not + ldb BAKCOL ; default to background colour +L95A2 stb WCOLOR ; save working colour + lda #0x55 ; consider a byte as 4 pixels + mul ; now all pixels are set to the same bit pattern + stb ALLCOL ; set all pixels byte + rts +L95AA bne L95CF ; brif graphics mode +L95AC pshs x,b,a ; save registers + ldx #SAMREG+8 ; point to middle of control register + sta 10,x ; reset display page to 0x400 + sta 8,x + sta 6,x + sta 4,x + sta 2,x + sta 1,x + sta -2,x + sta -4,x ; reset to alpha mode + sta -6,x + sta -8,x + lda PIA1+2 ; set VDG to alpha mode, colour set 0 + anda #7 + sta PIA1+2 + puls a,b,x,pc ;restore registers and return +L95CF pshs x,b,a ; save registers + lda PMODE ; get graphics mode + adda #3 ; offset to 3-7 (we don't use the bottom 3 modes) + ldb #0x10 ; shift to high 4 bits + mul + orb #0x80 ; set to graphics mode + orb CSSVAL ; set the desired colour set + lda PIA1+2 ; get get original PIA values + anda #7 ; mask off VDG control + pshs a ; merge with new VDG control + orb ,s+ + stb PIA1+2 ; set new VDG mode + lda BEGGRP ; get start of graphics page + lsra ; divide by two - pages are on 512 byte boundaries + jsr L960F ; set SAM control register + lda PMODE ; get graphics mode + adda #3 ; shift to VDG values + cmpa #7 ; PMODE 4? + bne L95F7 ; brif not + deca ; treat PMODE 4 the same as PMODE 3 +L95F7 bsr L95FB ; program SAM's VDG bits + puls a,b,x,pc ; restore registers and return +L95FB ldb #3 ; set 3 bits in register + ldx #SAMREG ; point to VDG control bits +L9600 rora ; get bit to set + bcc L9607 ; brif we need to clear the bit + sta 1,x ; set the bit + bra L9609 +L9607 sta ,x ; clear the bit +L9609 leax 2,x ; move to next bit + decb ; done all bits? + bne L9600 ; brif not + rts +L960F ldb #7 ; 7 screen address bits + ldx #SAMREG+6 ; point to screen address bits in SAM + bra L9600 ; go program SAM bits +L9616 lda PIA1+2 ; get VDG bits + anda #0xf7 ; keep everything but CSS bit + ora CSSVAL ; set correct CSS bit + sta PIA1+2 ; set desired CSS + rts +; PMODE command +PMODETOK cmpa #', ; is first argument missing? + beq L9650 ; brif so + jsr EVALEXPB ; evaluate PMODE number + cmpb #5 ; valid (0-4)? + bhs L966D ; brif not + lda GRPRAM ; get start of graphics memory +L962E sta BEGGRP ; set start of graphics page + aslb ; multiply mode by two (table has two bytes per entry) + ldu #L9706+1 ; point to lookup table + adda b,u ; add in number of 256 byte pages used for graphics screen + cmpa TXTTAB ; does it fit? + bhi L966D ; brif not + sta ENDGRP ; save end of graphics + leau -1,u ; point to bytes per horizontal row + lda b,u ; get bytes per row + sta HORBYT ; set it + lsrb ; restore PMODE value + stb PMODE ; set graphics mode + clra ; set background colour to 0 + sta BAKCOL + lda #3 ; set foreground colour to maximum (3) + sta FORCOL + jsr GETCCH ; is there a starting page number? + beq L966C ; brif not +L9650 jsr LB738 ; evaluate an expression following a comma + tstb ; page 0? + beq L966D ; brif so - not valid + decb ; zero-base it + lda #6 ; each graphics page is 6*256 + mul + addb GRPRAM ; add to start of graphics memory + pshs b ; save start of screen memory + addb ENDGRP ; add current and address + subb BEGGRP ; subtract current start (adds size of screen) + cmpb TXTTAB ; does it fit? + bhi L966D ; brif not + stb ENDGRP ; save new end of graphics + puls b ; get back start of graphics + stb BEGGRP ; set start of graphics +L966C rts +L966D jmp LB44A ; raise FC error +; SCREEN command +SCREEN cmpa #', ; is there a mode? + beq L967F ; brif no mode + jsr EVALEXPB ; get mode argument + tstb ; set Z if alpha + jsr L95AA ; set SAM/VDG for graphics mode + jsr GETCCH ; is there a second argument? + beq L966C ; brif not +L967F jsr LB738 ; evaluate , + tstb ; colour set 0? + beq L9687 ; brif so + ldb #8 ; flag for colour set 1 +L9687 stb CSSVAL ; set colour set + bra L9616 ; set up VDG +; PCLEAR command +PCLEAR jsr EVALEXPB ; evaulate number of pages requested + tstb ; 0? + beq L966D ; brif zero - not allowed + cmpb #9 ; more than 8? + bhs L966D ; brif so - not allowed + lda #6 ; there are 6 "pages" per graphics page + mul ; now B is the number of pages to reserve + addb GRPRAM ; add to start of graphics memory + tfr b,a ; now A is the MSB of the start of free memory + ldb #1 ; program memory always starts one above + tfr d,y ; save pointer to program memory + cmpd ENDGRP ; are we trying to deallocate the current graphics page? + blo L966D ; brif so (note that this prevents PCLEAR 0 anyway) + subd TXTTAB ; subtract out current start of basic program + addd VARTAB ; add in end of program - now D is new top of program + tfr d,x ; save new end of program + inca ; make some extra space (for stack) + subd FRETOP ; see if new top of program fits + bhs L966D ; brif there isn't enough space + jsr L80D0 ; adjust input pointer + nop ; space filler for 1.1 patch (the JSR above) + ldu VARTAB ; get end of program + stx VARTAB ; save new end of program + cmpu VARTAB ; is old end higher? + bhs L96D4 ; brif so +L96BD lda ,-u ; copy a byte upward + sta ,-x + cmpu TXTTAB ; at beginning? + bne L96BD ; brif not + sty TXTTAB ; save new start of program + clr -1,y ; there must always be a NUL before the program +L96CB jsr LACEF ; re-assign basic program addresses + jsr LAD26 ; reset variables and stack + jmp LAD9E ; return to interpretation loop +L96D4 ldu TXTTAB ; get start of program + sty TXTTAB ; save new start of program + clr -1,y ; there must be a NUL at the start of the program +L96DB lda ,u+ ; move a byte downward + sta ,y+ + cmpy VARTAB ; at the top of the program? + bne L96DB ; brif not + bra L96CB ; finish up +; Graphics initialization routine - this really should be up at the start of the ROM with the +; rest of the initialization code. +L96E6 ldb #0x1e ; set start of program to 0x1e00 ("PCLEAR 4") + stb TXTTAB + lda #6 ; graphics memory starts immediately after the screen +L96EC sta GRPRAM ; set graphics memory start + sta BEGGRP ; set start of current graphics page + clra ; set PMODE to 0 + sta PMODE + lda #16 ; 16 bytes per graphics row + sta HORBYT + lda #3 ; set foreground colour to 3 + sta FORCOL + lda #0x0c ; set ending graphics page (for PMODE 0) + sta ENDGRP + ldx TXTTAB ; get start of program + clr -1,x ; make sure there's a NUL before it +L9703 jmp LAD19 ; do a "NEW" +; PMODE data table (bytes per row and number of 256 byte pages required for a screen) +L9706 fcb 16,6 ; PMODE 0 + fcb 32,12 ; PMODE 1 + fcb 16,12 ; PMODE 2 + fcb 32,24 ; PMODE 3 + fcb 32,24 ; PMODE 4 +; Calculate absolute value of vertical coordinate difference +L9710 ldd VEREND ; get ending address + subd VERBEG ; get difference +L9714 bcc L9751 ; brif we didn't carry + pshs cc ; save status (C set if start > end) + jsr L9DC3 ; negate the difference to be positive + puls cc,pc ; restore C and return +; Calculate absolute value of horizontal coordinate difference +L971D ldd HOREND ; get end coordinate + subd HORBEG ; calculate difference + bra L9714 ; turn into absolute value +; PCOPY command +PCOPY bsr L973F ; fetch address of the source page + pshs d ; save address + ldb #0xa5 ; make sure we have TO + jsr LB26F + bsr L973F ; fetch address of the second page + puls x ; get back source + tfr d,u ; put destination into a pointer + ldy #0x300 ; 0x300 words to copy +L9736 ldd ,x++ ; copy a word + std ,u++ + leay -1,y ; done? + bne L9736 ; brif not + rts +L973F jsr EVALEXPB ; evaluate page number + tstb ; zero? + beq L9752 ; brif invalid page number +; BUG: this should be deferred until after the address is calculated at which point it should +; be bhs instead of bhi. There should also be a check to make sure the page number is less than +; or equal to 8 above so we don't have to test for overflows below. + cmpb TXTTAB ; is page number higher than start of program (BUG!) + bhi L9752 ; brif so - error + decb ; zero-base the page number + lda #6 ; 6 "pages" per graphics page + mul ; now we have proper number of "pages" for the offset + addb GRPRAM ; add start of graphics memory + exg a,b ; put MSB into A, 0 into B. +L9751 rts +L9752 jmp LB44A ; raise illegal function call +; GET command +GET clrb ; GET flag + bra L975A ; go on to the main body +PUT ldb #1 ; PUT flag +L975A stb VD8 ; save GET/PUT flag + jsr RVEC22 ; do the RAM hook dance (so Disk Basic can do its thing) +L975F cmpa #'@ ; @ before coordinates? + bne L9765 ; brif not + jsr GETNCH ; eat the @ +L9765 jsr L938F ; evaluate start/end coordinates + jsr SYNCOMMA ; make sure we have a comma + jsr L98CC ; get pointer to array + tfr X,D ; save descriptor pointer + ldu ,x ; get offset to next descriptor + leau -2,u ; move back to array name + leau d,u ; point to end of array + stu VD1 ; save end of data + leax 2,x ; point to number of dimensions + ldb ,x ; get dimension count + aslb ; two bytes per dimension size + abx ; now X points to start of data + stx VCF ; save start of array data + lda VALTYP ; is it numeric + bne L9752 ; brif not + clr VD4 ; set default graphic action to PSET + jsr GETCCH ; get input character + beq L97B7 ; brif no action flag + com VD4 ; flag action enabled + jsr SYNCOMMA ; make sure there's a comma + tst VD8 ; PUT? + bne L979A ; brif so + ldb #'G ; check for full graphics option + jsr LB26F + bra L97CA ; handle the rest of the process +L979A ldb #5 ; 5 legal actions for PUT + ldx #L9839 ; point to action table +L979F ldu ,x++ ; get "clear bit" action routine + ldy ,x++ ; get "set bit" action routine + cmpa ,x+ ; does token match? + beq L97AE ; brif so + decb ; checked all? + bne L979F ; brif not + jmp LB277 ; raise error +L97AE sty VD5 ; save set bit action address + stu VD9 ; save clear bit action address + jsr GETNCH ; munch the acton token + bra L97CA ; handle rest of process +L97B7 ldb #0xf8 ; mask for bottom three bits + lda PMODE ; get graphics mode + rora ; odd number mode? + bcc L97C0 ; brif even + ldb #0xfc ; bottom 2 bits mask +L97C0 tfr b,a ; save mask + andb HORBEG+1 ; round down the start address + stb HORBEG+1 + anda HOREND+1 ; round down end address + sta HOREND+1 +L97CA jsr L971D ; get horizontal size + bcc L97D3 ; brif end > start + ldx HOREND ; switch end in for start + stx HORBEG +L97D3 std HOREND ; save size + jsr L9710 ; calculate vertical size + bcc L97DE ; brif end > start + ldx VEREND ; swap in vertical end for the start + stx VERBEG +L97DE std VEREND ; save vertical size + lda PMODE ; get graphics mode + rora ; even? + ldd HOREND ; get difference + bcc L97EB ; brif even (2 colour) + addd HOREND ; add in size (double it) + std HOREND ; save adjusted end size +L97EB jsr L9420 ; normalize differences + ldd HOREND ; get end coord + ldx VEREND ; get end size + leax 1,x ; make vertical size inclusive + stx VEREND ; save it back + tst VD4 ; got "G" or GET action + bne L9852 ; brif given + lsra ; we're going for whole bytes here + rorb + lsra + rorb + lsra + rorb + addd #1 ; make it inclusive + std HOREND ; save new coordinate + jsr L9298 ; convert to screen address +L9808 ldb HOREND+1 ; get horizontal size + pshs x ; save screen position +L980C tst VD8 ; get/put flag + beq L9831 ; brif get + bsr L9823 ; bump array data pointer + lda ,u ; copy data from array to screen + sta ,x+ +L9816 decb ; are we done the row? + bne L980C ; brif not + puls x ; get screen address + jsr L92E9 ; move to next row + dec VEREND+1 ; done? + bne L9808 ; brif not +L9822 rts +L9823 ldu VCF ; get array data location + leau 1,u ; bump it + stu VCF ; save new array data location + cmpu VD1 ; did we hit the end of the array? + bne L9822 ; brif not +L982E jmp LB44A ; raise function call error +L9831 lda ,x+ ; get data from screen + bsr L9823 ; bump array data pointer + sta ,u ; put data in array + bra L9816 ; do the loopy thing +; PUT actions +L9839 fdb L9894,L989B ; PSET + fcb 0xbd + fdb L989B,L9894 ; PRESET + fcb 0xbe + fdb L98B1,L989B ; OR + fcb 0xb1 + fdb L9894,L98B1 ; AND + fcb 0xb0 + fdb L98A1,L98A1 ; NOT + fcb 0xa8 +L9852 addd #1 ; add to horiz difference + std HOREND ; save it + lda VD8 ; PUT? + bne L9864 ; brif so + ldu VD1 ; get end of array +L985D sta ,-u ; zero out a byte + cmpu VCF ; done? + bhi L985D ; brif not +L9864 jsr L9298 ; get screen address + ldb PMODE ; get graphics mode + rorb ; even? + bcc L986E ; brif so + anda #0xaa ; use as pixel mask for 4 colour mode +L986E ldb #1 ; set bit probe + ldy VCF ; point to start of array data +L9873 pshs x,a ; save screen address + ldu HOREND ; get horizontal size +L9877 pshs u,a ; save horizontal size and pixel mask + lsrb ; move bit probe right + bcc L9884 ; brif we didn't fall off a byte + rorb ; shift carry back in on the left + leay 1,y ; move ahead a byte in the array + cmpy VD1 ; end of array data? + beq L982E ; raise error if so +L9884 tst VD8 ; PUT? + beq L98A7 ; brif not + bitb ,y ; test bit in array + beq L9890 ; brif not set + jmp [VD5] ; do action routine for bit set +L9890 jmp [VD9] ; do action routine for bit clear +L9894 coma ; invert mask + anda ,x ; read screen data and reset the desired bit + sta ,x ; save on screen + bra L98B1 +L989B ora ,x ; merge pixel mask with screen data (turn on bit) + sta ,x ; save on screen + bra L98B1 +L98A1 eora ,x ; invert the pixel in the screen data + sta ,x ; save on screen + bra L98B1 +L98A7 bita ,x ; is the bit set? + beq L98B1 ; brif not - do nothing + tfr b,a ; get bit probe + ora ,y ; turn on proper bit in data + sta ,y +L98B1 puls a,u ; get back array address + jsr L92ED ; move screen address to the right + leau -1,u ; account for consumed pixel + cmpu ZERO ; done yet? + bne L9877 ; brif not + ldx 1,s ; get start of row back + lda HORBYT ; get number of bytes per row + leax a,x ; move ahead one line + puls a ; get back screen pixel mask + leas 2,s ; lose the screen pointer + dec VEREND+1 ; done all rows? + bne L9873 ; brif not + rts +L98CC jsr LB357 ; evaluate a variable + ldb ,-x ; get variable name + lda ,-x + tfr d,u ; save it + ldx ARYTAB ; get start of arrays +L98D7 cmpx ARYEND ; end of arrays? + lbeq LB44A ; brif not found + cmpu ,x ; correct variable? + beq L98E8 ; brif so + ldd 2,x ; get array size + leax d,x ; move to next array + bra L98D7 ; check this array +L98E8 leax 2,x ; move pointer to the array header + rts ; obviously this rts is not needed +L98EB rts +; PAINT command +PAINT cmpa #'@ ; do we have @ before coords? + bne L98F2 ; brif not + jsr GETNCH ; eat the @ +L98F2 jsr L93B2 ; evaluate coordinates + jsr L931D ; normalize coordinates + lda #1 ; PSET flag (use working colour) + sta SETFLG + jsr L9581 ; parse colour and set working colour, etc. + ldd WCOLOR ; get working colour and all pixels byte + pshs d ; save them + jsr GETCCH ; is there anything else? + beq L990A ; brif not + jsr L9581 ; evaluate border colour +L990A lda ALLCOL ; get border colour all pixel byte + sta VD8 ; save border colour pixel byte + puls d ; get back working colour details + std WCOLOR + clra ; store a "stop" frame on the stack so PAINT knows when it's finished unwinding + pshs u,x,b,a + jsr L9522 ; set up starting coordinates + jsr L928F ; point to pixel mask routine + stu VD9 ; save pixel mask routine + jsr L99DF ; paint from current horizontal coordinate to zero (left) + beq L9931 ; brif hit border immediately + jsr L99CB ; paint from current horizontal coordinate upward (right) + lda #1 ; set direction to "down" + sta VD7 + jsr L99BA ; save "down" frame + neg VD7 ; set direction to "up" + jsr L99BA ; save "up" frame +L9931 sts TMPSTK ; save stack pointer +L9934 tst CHGFLG ; did the paint change anything? + bne L993B ; brif so + lds TMPSTK ; get back stack pointer +L993B puls a,b,x,u ; get frame from stack + clr CHGFLG ; mark nothing changed + sts TMPSTK ; save stack pointer + leax 1,x ; move start coordinate right + stx HORBEG ; save new coordinate + stu VD1 ; save length of line + sta VD7 ; save up/down flag + beq L98EB ; did we hit the "stop" frame? + bmi L9954 ; brif negative going (up)? + incb ; bump vertical coordinate + cmpb VD6 ; at end? + bls L9958 ; brif not + clrb ; set vertical to 0 (wrap around) +L9954 tstb ; did we wrap? + beq L9934 ; do another block if so + decb ; move up a row +L9958 stb VERBEG+1 ; save vertical coordinate + jsr L99DF ; paint from horizontal to 0 + beq L996E ; brif we hit the border immediately + cmpd #3 ; less than 3 pixels? + blo L9969 ; brif so + leax -2,x ; move two pixels left + bsr L99A1 ; save paint block on the stack +L9969 jsr L99CB ; continue painting to the right +L996C bsr L99BA ; save paint data frame +L996E coma ; complement length of line just painted and add to length of line + comb +L9970 addd VD1 ; save difference between this line and parent line + std VD1 + ble L998C ; brif parent line is shorter + jsr L9506 ; bump horizontal coordinate + jsr L9A12 ; see if we bounced into the border + bne L9983 ; brif not border + ldd #-1 ; move left + bra L9970 ; keep looking +L9983 jsr L9514 ; move horizontally left + bsr L99C6 ; save horizontal coordinate + bsr L99E8 ; paint right + bra L996C ; save paint block and do more +L998C jsr L9506 ; bump horizontal coordinate + leax d,x ; point to right end of parent line + stx HORBEG ; set as curent coordinate + coma ; get amount we extend past parent line + comb + subd #1 + ble L999E ; brif doesn't extend + tfr d,x ; save length of line + bsr L99A1 ; save paint frame +L999E jmp L9934 +L99A1 std VCB ; save number of pixels painted + puls y ; get return address + ldd HORBEG ; get horizontal coordinate + pshs x,b,a ; save horizontal coordinate and pointer + lda VD7 ; get up/down flag + nega ; reverse it +L99AC ldb VERBEG+1 ; get vertical coordainte + pshs b,a ; save vertical coord and up/down flag + pshs y ; put return address back + ldb #2 ; make sure we haven't overflowed memory + jsr LAC33 + ldd VCB ; get line length back + rts +L99BA std VCB ; save length of painted line + puls y ; get return address + ldd HOREND ; get start coord + pshs x,b,a ; save horizontal start and length + lda VD7 ; get up/down flag + bra L99AC ; finish up with the stack +L99C6 ldx HORBEG ; save current horizontal coord and save it + stx HOREND + rts +L99CB std VCD ; save number of pixels painted + ldy HOREND ; get last horizontal start + bsr L99C6 ; save current coordinate + sty HORBEG ; save coordinate + bsr L99E8 ; paint a line + ldx VCD ; get number painted + leax d,x ; add to the number painted going the other way + addd #1 ; now D is length of line + rts +L99DF jsr L99C6 ; put starting coordinate in end + ldy #L9514 ; decrement horizontal coordinate address + bra L99EE ; go paint line +L99E8 ldy #L9506 ; increment horizontal coordinate address + jsr ,y ; bump coordinate +L99EE ldu ZERO ; initialize pixel count + ldx HORBEG ; get starting coordinate +L99F2 bmi L9A0B ; brif out of range for hoizontal coordinate + cmpx VD3 ; at end? + bhi L9A0B ; brif right of max + pshs u,y ; save counter and inc/dec routine pointer + bsr L9A12 ; at border? + beq L9A09 ; brif so + jsr L9377 ; set pixel to paint colour + puls y,u ; restore counter and inc/dec/pointer + leau 1,u ; bump number of painted pixels + jsr ,y ; inc/dec screen address + bra L99F2 ; go do another pixel +L9A09 puls y,u ; get back counter and inc/dec routine +L9A0B tfr u,d ; save count in D + tfr d,x ; and in X + subd ZERO ; set flags on D (smaller/faster than cmpd ZERO) + rts +L9A12 jsr [VD9] ; get the screen address + tfr a,b ; save pixel mask + andb VD8 ; set pixel to border colour + pshs b,a ; save mask and border + anda ,x ; mask current pixel into A + cmpa 1,s ; does it match border? Z=1 if so + puls a,b,pc ; restore mask, border pixel, and return +; PLAY command +; This is here mixed in with the graphics package because it shares some machinery with DRAW. +PLAY ldx ZERO ; default values for note length, etc. + ldb #1 + pshs x,b ; save default values + jsr LB156 ; evaluate argument + clrb ; enable DA and sound output + jsr LA9A2 + jsr LA976 +L9A32 jsr LB654 ; fetch PLAY string details + bra L9A39 ; go evaluate the string +L9A37 puls b,x ; get back play string details +L9A39 stb VD8 ; save length of string + beq L9A37 ; brif end of string + stx VD9 ; save start of string + lbeq LA974 ; brif NULL string - disable sound and return +L9A43 tst VD8 ; have anything left? + beq L9A37 ; brif not + jsr L9B98 ; get command character + cmpa #'; ; command separator? + beq L9A43 ; brif so - ignore it + cmpa #'' ; '? + beq L9A43 ; brif so - ignore it + cmpa #'X ; execuate sub string? + lbeq L9C0A ; brif so - handle it + bsr L9A5C ; handle other commands + bra L9A43 ; look for more stuff +L9A5C cmpa #'O ; octave? + bne L9A6D ; brif not + ldb OCTAVE ; get current octave + incb ; 1-base it + bsr L9AC0 ; get value if present + decb ; zero-base it + cmpb #4 ; valid octave? + bhi L9ACD ; raise error if not + stb OCTAVE ; save new octave + rts +L9A6D cmpa #'V ; volume? + bne L9A8B ; brif not + ldb VOLHI ; get current high volume limit + lsrb ; shift 2 bits right (DA is 6 bits in high bits) + lsrb + subb #31 ; subtract out mid value offset + bsr L9AC0 ; read argument + cmpb #31 ; maximum range is 31 + bhi L9ACD ; brif out of range + aslb ; adjust back in range + aslb + pshs b ; save new volume + ldd #0x7e7e ; midrange value for both high and low + adda ,s ; add new volume to high limit + subb ,s+ ; subtract volume from low limit + std VOLHI ; save new volume limits (sets high and low amplitudes) + rts +L9A8B cmpa #'L ; note length? + bne L9AB2 ; brif not + ldb NOTELN ; get current length + bsr L9AC0 ; read parameter + tstb ; resulting length 0? + beq L9ACD ; brif so - problem + stb NOTELN ; save new length + clr DOTVAL ; reset note timer scale factor +L9A9A bsr L9A9F ; check for dot + bcc L9A9A ; brif there was one + rts +L9A9F tst VD8 ; check length + beq L9AAD ; brif zero + jsr L9B98 ; get command character + cmpa #'. ; dot? + beq L9AAF ; brif so + jsr L9BE2 ; move input back and bump length +L9AAD coma ; set C to indicate nothing found + rts +L9AAF inc DOTVAL ; bump number of dots + rts +L9AB2 cmpa #'T ; tempo? + bne L9AC3 ; brif not + ldb TEMPO ; get current tempo + bsr L9AC0 ; parse tempo argument + tstb ; 0? + beq L9ACD ; brif so - invalid + stb TEMPO ; save new tempo + rts +L9AC0 jmp L9BAC ; evaluate various operators +L9AC3 cmpa #'P ; pause? + bne L9AEB ; brif not + jsr L9CCB ; evaluate parameter + tstb ; is the pause number 0? + bne L9AD0 ; brif not +L9ACD jmp LB44A ; raise FC error +L9AD0 lda DOTVAL ; save current volume and note scale + ldx VOLHI + pshs x,a + lda #0x7e ; drop DA to mid range + sta VOLHI + sta VOLLOW + clr DOTVAL + bsr L9AE7 ; go play a "silence" + puls a,x ; restore volume and note scale + sta DOTVAL + stx VOLHI + rts +L9AE7 clr ,-s ; set not number 0 + bra L9B2B ; go play it +L9AEB cmpa #'N ; N for "note"? + bne L9AF2 ; brif not - it's optional + jsr L9B98 ; skip the "N" +L9AF2 cmpa #'A ; is it a valid note? + blo L9AFA ; brif not + cmpa #'G ; is it above the note range? + bls L9AFF ; brif not - valid note +L9AFA jsr L9BBE ; evaluate a number + bra L9B22 ; process note value +L9AFF suba #'A ; normalize note number to 0 + ldx #L9C5B ; point to note number lookup table + ldb a,x ; get not number + tst VD8 ; any command characters left? + beq L9B22 ; brif not + jsr L9B98 ; get character + cmpa #'# ; sharp? + beq L9B15 ; brif so + cmpa #'+ ; also sharp? + bne L9B18 ; brif not +L9B15 incb ; add one half tone + bra L9B22 +L9B18 cmpa #'- ; flat? + bne L9B1F ; brif not + decb ; subtract one half tone + bra L9B22 +L9B1F jsr L9BE2 ; back up command pointer +L9B22 decb ; adjust note number (zero base it) + cmpb #11 ; is it valid? + bhi L9ACD ; raise error if not + pshs b ; save note value + ldb NOTELN ; get note length +L9B2B lda TEMPO ; get tempo value + mul ; calculate note duration + std VD5 ; save duration + leau 1,s ; point to where the stack goes after we're done + lda OCTAVE ; get current octave + cmpa #1 ; 0 or 1? + bhi L9B64 ; brif not + ldx #L9C62 ; point to delay table + ldb #2*12 ; 24 bytes per octave + mul ; now we have the base address + abx ; now X points to the octave base + puls b ; get back note value + aslb ; two bytes per delay + abx ; now we're pointing to the delay + leay ,x ; save pointer to note value + bsr L9B8C ; calculate note timer value + std PLYTMR ; set timer for note playing (IRQ will count this down) +L9B49 bsr L9B57 ; set to mid range and delay + lda VOLHI ; get high value + bsr L9B5A ; set to high value and delay + bsr L9B57 ; set to mid range and delay + lda VOLLOW ; get low value + bsr L9B5A ; set to low value and delay + bra L9B49 ; do it again (IRQ will break the loop) +L9B57 lda #0x7e ; mid value for DA with RS232 marking + nop ; a delay to fine tune frequencies +L9B5A sta PIA1 ; set DA + ldx ,y ; get delay value +L9B5F leax -1,x ; count down + bne L9B5F ; brif not done yet + rts +L9B64 ldx #L9C92-2*12 ; point to delay table for octaves 2+ + ldb #12 ; 12 bytes per octave + mul ; now we have the offset to the desired octave + abx ; now we point to the start of the octave + puls b ; get back note value + abx ; now we point to the delay value + bsr L9B8C ; calculate timer value + std PLYTMR ; set play timer (IRQ counts this down) +L9B72 bsr L9B80 ; send mid value and delay + lda VOLHI ; get high value + bsr L9B83 ; send high value and delay + bsr L9B80 ; send low value and delay + lda VOLLOW ; get low value + bsr L9B83 ; send low value and delay + bra L9B72 ; do it again (IRQ will break the loop) +L9B80 lda #0x7e ; mid range value with RS232 marking + nop ; fine tuning delay +L9B83 sta PIA1 ; set DA + lda ,x ; get delay value +L9B88 deca ; count down + bne L9B88 ; brif not done + rts +L9B8C ldb #0xff ; base timer value + lda DOTVAL ; get number of dots + beq L9B97 ; use default value if 0 + adda #2 ; add in constant timer factor + mul ; multiply scale by base + lsra ; divide by two - each increment will increase note timer by 128 + rorb +L9B97 rts +L9B98 pshs x ; save register +L9B9A tst VD8 ; do we have anything left? + beq L9BEB ; brif not - raise error + ldx VD9 ; get parsing address + lda ,x+ ; get character + stx VD9 ; save pointer + dec VD8 ; account for character consumed + cmpa #0x20 ; space? + beq L9B9A ; brif so - skip it + puls x,pc ; restore register and return +L9BAC bsr L9B98 ; get character + cmpa #'+ ; add one? + beq L9BEE ; brif so + cmpa #'- ; subtract one? + beq L9BF2 ; brif so + cmpa #'> ; double? + beq L9BFC ; brif so + cmpa #'< ; halve? + beq L9BF7 ; brif so +L9BBE cmpa #'= ; variable equate? + beq L9C01 ; brif so + jsr L90AA ; clear carry if numeric + bcs L9BEB ; brif not numeric + clrb ; initialize value to 0 +L9BC8 suba #'0 ; remove ASCII bias + sta VD7 ; save digit + lda #10 ; make room for digit + mul + tsta ; did we overflow 8 bits? + bne L9BEB ; brif so + addb VD7 ; add in digit + bcs L9BEB ; brif that overflowed + tst VD8 ; more digits? + beq L9BF1 ; brif not + jsr L9B98 ; get character + jsr L90AA ; clear carry if numeric + bcc L9BC8 ; brif another digit +L9BE2 inc VD8 ; unaccount for character just read + ldx VD9 ; move pointer back + leax -1,x + stx VD9 + rts +L9BEB jmp LB44A ; raise FC error +L9BEE incb ; bump param + beq L9BEB ; brif overflow +L9BF1 rts +L9BF2 tstb ; already zero? + beq L9BEB ; brif so - underflow + decb ; decrease parameter + rts +L9BF7 tstb ; already at 0? + beq L9BEB ; brif so - raise error + lsrb ; halve it + rts +L9BFC tstb ; will it overflow? + bmi L9BEB ; brif so + aslb ; double it + rts +L9C01 pshs u,y ; save registers + bsr L9C1B ; interpret command string as a variable + jsr LB70E ; convert it to an 8 bit number + puls y,u,pc ; restore registers and return +L9C0A jsr L9C1B ; evaluate expression in command string + ldb #2 ; room for 4 bytes? + jsr LAC33 + ldb VD8 ; get the command length and pointer + ldx VD9 + pshs x,b ; save them + jmp L9A32 ; go process the sub string +L9C1B ldx VD9 ; get command pointer + pshs x ; save it + jsr L9B98 ; get input character + jsr LB3A2 ; set carry if not alpha + bcs L9BEB ; brif not a variable reference +L9C27 jsr L9B98 ; get command character + cmpa #'; ; semicolon? + bne L9C27 ; keep scanning if not + puls x ; get back start of variable string + ldu CHARAD ; get current interpreter input pointer + pshs u ; save it + stx CHARAD ; point interpreter at command string + jsr LB284 ; evaluate expression as string + puls x ; restore interpeter input pointer + stx CHARAD + rts +; The bit of Extended Basic's IRQ routine that handles PLAY. Note that everything after +; clr PLYTMR+1 could be replaced with a simple lds 8,s followed by rts. +L9C3E clra ; make sure DP is set to 0 + tfr a,dp + ldd PLYTMR ; is PLAY running? + lbeq LA9BB ; brif not - transfer control on the Color Basic's routine + subd VD5 ; subtract out the interval + std PLYTMR ; save new timer value + bhi L9C5A ; brif it isn't <= 0 + clr PLYTMR ; disable the timer + clr PLYTMR+1 + puls a ; get saved CC + lds 7,s ; set stack to saved U value + anda #0x7f ; clear E flag (to return minimal state) + pshs a ; set fake "FIRQ" stack frame +L9C5A rti +L9C5B fcb 10,12,1,3,5,6,8 ; table of half tone numbers for notes A-G +L9C62 fdb 0x01a8,0x0190,0x017a,0x0164 ; delays for octave 1 + fdb 0x0150,0x013d,0x012b,0x011a + fdb 0x010a,0x00fb,0x00ed,0x00df + fdb 0x00d3,0x00c7,0x00bb,0x00b1 ; delays for octave 2 + fdb 0x00a6,0x009d,0x0094,0x008b + fdb 0x0083,0x007c,0x0075,0x006e +L9C92 fcb 0xa6,0x9c,0x93,0x8b ; delays for octave 3 + fcb 0x83,0x7b,0x74,0x6d + fcb 0x67,0x61,0x5b,0x56 + fcb 0x51,0x4c,0x47,0x43 ; delays for octave 4 + fcb 0x3f,0x3b,0x37,0x34 + fcb 0x31,0x2e,0x2b,0x28 + fcb 0x26,0x23,0x21,0x1f ; delays for octave 5 + fcb 0x1d,0x1b,0x19,0x18 + fcb 0x16,0x14,0x13,0x12 +; DRAW command +DRAW ldx ZERO ; create an empty "DRAW" frame + ldb #1 + pshs x,b + stb SETFLG ; set to "PSET" mode + stx VD5 ; clear update and draw flag + jsr L959A ; set active colour byte + jsr LB156 ; evaluate command string +L9CC6 jsr LB654 ; fetch command string details + bra L9CD3 ; interpret the command string +L9CCB jsr L9B98 ; fetch command character + jmp L9BBE ; evaluate a number +L9CD1 puls b,x ; get previously saved command string +L9CD3 stb VD8 ; save length counter + beq L9CD1 ; brif end of string + stx VD9 ; save pointer + lbeq L9DC7 ; brif overall end of command +L9CDD tst VD8 ; are we at the end of the string? + beq L9CD1 ; brif so - return to previous string + jsr L9B98 ; get command character + cmpa #'; ; semicolon? + beq L9CDD ; brif so - ignore it + cmpa #'' ; '? + beq L9CDD ; brif so - ignore that too + cmpa #'N ; update position toggle? + bne L9CF4 ; brif not + com VD5 ; toggle update position flag + bra L9CDD ; get on for another command +L9CF4 cmpa #'B ; blank flag? + bne L9CFC ; brif not + com VD6 ; toggle blank flag + bra L9CDD ; get on for another command +L9CFC cmpa #'X ; substring? + lbeq L9D98 ; brif so - execute command + cmpa #'M ; move draw position? + lbeq L9E32 ; brif so + pshs a ; save command character + ldb #1 ; default value if no number follows + tst VD8 ; is there something there? + beq L9D21 ; brif not + jsr L9B98 ; get character + jsr LB3A2 ; set C if not alpha + pshs cc ; save alpha state + jsr L9BE2 ; move back pointer + puls cc ; get back alpha flag + bcc L9D21 ; brif it's alpha + bsr L9CCB ; evaluate a number +L9D21 puls a ; get command back + cmpa #'C ; color change? + beq L9D4F ; brif so + cmpa #'A ; angle? + beq L9D59 ; brif so + cmpa #'S ; scale? + beq L9D61 ; brif so + cmpa #'U ; up? + beq L9D8F ; brif so + cmpa #'D ; down? + beq L9D8C ; brif so + cmpa #'L ; left? + beq L9D87 ; brif so + cmpa #'R ; right? + beq L9D82 ; brif so + suba #'E ; normalize the half cardinals to 0 + beq L9D72 ; brif E (45°) + deca ; F (135°?) + beq L9D6D ; brif so + deca ; G (225°?) + beq L9D7B ; brif so + deca ; H (315°?) + beq L9D69 ; brif so +L9D4C jmp LB44A ; raise FC error +L9D4F jsr L955D ; adjust colour for PMODE + stb FORCOL ; save new foreground colour + jsr L959A ; set up working colour and all pixels byte +L9D57 bra L9CDD ; go process another command +L9D59 cmpb #4 ; only 3 angles are valid + bhs L9D4C ; brif not valid + stb ANGLE ; save new angle + bra L9D57 ; go process another command +L9D61 cmpb #63 ; only 64 scale values are possible + bhs L9D4C ; brif out of range + stb SCALE ; save new scale factor + bra L9D57 ; go process another command +L9D69 clra ; make horizontal negative + bsr L9DC4 + skip1 +L9D6D clra ; keep horizontal distance positive + tfr d,x ; make horizontal distance and vertical distance the same + bra L9DCB ; go do the draw thing +L9D72 clra ; zero extend horizontal distance + tfr d,x ; set it as vertical + bsr L9DC4 ; negate horizontal distance + exg d,x ; swap directions (vertical is now negative) + bra L9DCB ; go do the draw thing +L9D7B clra ; zero extend horizontal distance + tfr d,x ; copy horizontal to vertical + bsr L9DC4 ; negate horizontal + bra L9DCB ; go do the drawing thing +L9D82 clra ; zero extend horizontal distance +L9DB3 ldx ZERO ; no vertical distance + bra L9DCB ; go do the drawing things +L9D87 clra ; zero extend horizontal + bsr L9DC4 ; negate horizontal + bra L9DB3 ; zero out vertical and do the drawing thing +L9D8C clra ; zero extend distance + bra L9D92 ; make the distance vertical and zero out horizontal +L9D8F clra ; zero extend distance + bsr L9DC4 ; negate distance +L9D92 ldx ZERO ; zero out vertical distance + exg x,d ; swap vertical and horizontal + bra L9DCB ; go do the drawing thing +L9D98 jsr L9C1B ; evaluate substring expression + ldb #2 ; is there enough room for the state? + jsr LAC33 + ldb VD8 ; save current command string state + ldx VD9 + pshs x,b + jmp L9CC6 ; go evaluate the sub string +L9DA9 ldb SCALE ; get scale factor + beq L9DC8 ; brif zero - default to full size + clra ; zero extend + exg d,x ; put distance somewhere useful + sta ,-s ; save MS of distance + bpl L9DB6 ; brif positive distance + bsr L9DC3 ; negate the distance +L9DB6 jsr L9FB5 ; multiply D and X + tfr u,d ; save ms bytes in D + lsra ; divide by 2 + rorb +L9DBD lsra ; ...divide by 4 + rorb + tst ,s+ ; negative distance? + bpl L9DC7 ; brif it was positive +L9DC3 nega ; negate D +L9DC4 negb + sbca #0 +L9DC7 rts +L9DC8 tfr x,d ; copy unchanged sitance to D + rts +L9DCB pshs b,a ; save horizontal distance + bsr L9DA9 ; apply scale factor to vertical + puls x ; get horizontal distance + pshs b,a ; save scaled vertical + bsr L9DA9 ; apply scale to horizontal + puls x ; get back vertical distance + ldy ANGLE ; get draw angle and scale + pshs y ; save them +L9DDC tst ,s ; is there an angle? + beq L9DE8 ; brif no angle + exg x,d ; swap distances + bsr L9DC3 ; negate D + dec ,s ; account for one tick around the rotation + bra L9DDC ; see if we're there yet +L9DE8 puls y ; get angle and scale back + ldu ZERO ; default end position (horizontal) is 0 + addd HORDEF ; add default horizontal to horizontal distance + bmi L9DF2 ; brif we went negative + tfr d,u ; save calculated end coordindate +L9DF2 tfr x,d ; get vertical distance somewhere useful + ldx ZERO ; default vertical end is 0 + addd VERDEF ; add distance to default vertical start + bmi L9DFC ; brif negative - use 0 + tfr d,x ; save calculated end coordinate +L9DFC cmpu #256 ; is horizontal in range? + blo L9E05 ; brif su + ldu #255 ; maximize it +L9E05 cmpx #192 ; is vertical in range? + blo L9E0D ; brif so + ldx #191 ; maximize it +L9E0D ldd HORDEF ; set starting coordinates for the line + std HORBEG + ldd VERDEF + std VERBEG + stx VEREND ; set end coordinates + stu HOREND + tst VD5 ; are we updating position? + bne L9E21 ; brif not + stx VERDEF ; update default coordinates + stu HORDEF +L9E21 jsr L9420 ; normalize coordindates + tst VD6 ; are we drawing something? + bne L9E2B ; brif not + jsr L94A1 ; draw the line +L9E2B clr VD5 ; reset draw and update flags + clr VD6 + jmp L9CDD ; do another command +L9E32 jsr L9B98 ; get a command character + pshs a ; save it + jsr L9E5E ; evaluate horizontal distance + pshs b,a ; save it + jsr L9B98 ; get character + cmpa #', ; comma between coordinates? + lbne L9D4C ; brif not - raise error + jsr L9E5B ; evaluate vertical distance + tfr d,x ; save vertical distance + puls u ; get horizontal distance + puls a ; get back first command character + cmpa #'+ ; was it + at start? + beq L9E56 ; brif +; treat values as positive + cmpa #'- ; was it -? + bne L9DFC ; brif not - treat it as absolute +L9E56 tfr u,d ; put horizontal distance somewhere useful + jmp L9DCB ; move draw position (relative) +L9E5B jsr L9B98 ; get input character +L9E5E cmpa #'+ ; leading +? + beq L9E69 ; brif so + cmpa #'- ; leading -? + beq L9E6A ; brif so - negative + jsr L9BE2 ; move pointer back one +L9E69 clra ; 0 for +, nonzero for - +L9E6A pshs a ; save sign flag + jsr L9CCB ; evaluate number + puls a ; get sign flag + tsta ; negative? + beq L9E78 ; brif not + clra ; zero extend and negate + negb + sbca #0 +L9E78 rts +; Table of sines and cosines for CIRCLE +L9E79 fdb 0x0000,0x0001 ; subarc 0 + fdb 0xfec5,0x1919 ; subarc 1 + fdb 0xfb16,0x31f2 ; subarc 2 + fdb 0xf4fb,0x4a51 ; subarc 3 + fdb 0xec84,0x61f9 ; subarc 4 + fdb 0xe1c7,0x78ae ; subarc 5 + fdb 0xd4dc,0x8e3b ; subarc 6 + fdb 0xc5e5,0xa269 ; subarc 7 + fdb 0xb506,0xb506 ; subarc 8 +; CIRCLE command +; The circle is drawn as a 64 sided polygon (64 LINE commands essentially) +CIRCLE cmpa #'@ ; is there an @ before coordinates? + bne L9EA3 ; brif not + jsr GETNCH ; eat the @ +L9EA3 jsr L9522 ; get max coordinates for screen + jsr L93B2 ; parse coordinates for circle centre + jsr L931D ; normalize the start coordinates + ldx ,u ; get horizontal coordinate + stx VCB ; save it + ldx 2,u ; get vertical coordinate + stx VCD ; saveit + jsr SYNCOMMA ; make sure we have a comma + jsr LB73D ; evaluate radius expression + ldu #VCF ; point to temp storage + stx ,u ; save radius + jsr L9320 ; normalize radius + lda #1 ; default to PSET + sta SETFLG + jsr L9581 ; evaluate the colour expression + ldx #0x100 ; height/width default value + jsr GETCCH ; is there a ratio? + beq L9EDF ; brif not + jsr SYNCOMMA ; make sure we have a comma + jsr LB141 ; evaluate the ratio + lda FP0EXP ; multiply ratio by 256 + adda #8 + sta FP0EXP + jsr LB740 ; evaluate ratio to X (fraction part in LSB) +L9EDF lda PMODE ; get graphics mode + bita #2 ; is it even? + beq L9EE9 ; brif so + tfr x,d ; double the ratio + leax d,x +L9EE9 stx VD1 ; save height/width ratio + ldb #1 ; set the SET flag to PSET + stb SETFLG + stb VD8 ; set first time flag (set to 0 after arc drawn) + jsr L9FE2 ; evaluate circle starting point (octant, subarc) + pshs b,a ; save startpoint + jsr L9FE2 ; evaluate circle end point (octant, subarc) + std VD9 ; save endp oint + puls a,b +L9EFD pshs b,a ; save current circle position + ldx HOREND ; move end coordinates to start coordinates + stx HORBEG + ldx VEREND + stx VERBEG + ldu #L9E79+2 ; point to sine/cosine table + anda #1 ; even octant? + beq L9F11 ; brif so + negb ; convert 0-7 to 8-1 for odd octants + addb #8 +L9F11 aslb ; four bytes per table entry + aslb + leau b,u ; point to correct table entry + pshs u ; save sine/cosine table entry pointer + jsr L9FA7 ; calculate horizontal offset + puls u ; get back table entry pointer + leau -2,u ; move to cosine entry + pshs x ; save horizontal offset + jsr L9FA7 ; calculate vertical offset + puls y ; put horizontal in Y + lda ,s ; get octant number + anda #3 ; is it 0 or 4? + beq L9F31 ; brif so + cmpa #3 ; is it 3 or 7? + beq L9F31 ; brif so + exg x,y ; swap horizontal and vertical +L9F31 stx HOREND ; save horizontal offset + tfr y,x ; put vertical offset in X + ldd VD1 ; get height/width ratio + jsr L9FB5 ; multiply vertical by h/w ratio + tfr y,d ; save the product to D + tsta ; did it overflow? + lbne LB44A ; brif so + stb VEREND ; save vertical coordinate MSB + tfr u,d ; get LSW of product + sta VEREND+1 ; save LSB of integer part (we have 8 bits of fraction in the ratio) + lda ,s ; get octant + cmpa #2 ; is it 0 or 1? + blo L9F5B ; brif so + cmpa #6 ; is it 6 or 7? + bhs L9F5B ; brif so + ldd VCB ; get horizontal centre + subd HOREND ; subtract horizontal displacement + bcc L9F68 ; brif we didn't overflow the screen + clra ; zero out coordinate if we overflowed the screen + clrb + bra L9F68 +L9F5B ldd VCB ; get horizontal coordinate of the centre + addd HOREND ; add displacement + bcs L9F66 ; brif overlod + cmpd VD3 ; larger than max horizontal coord? + blo L9F68 ; brif not +L9F66 ldd VD3 ; maximize the coordinate +L9F68 std HOREND ; save horizontal ending coordainte + lda ,s ; get octant + cmpa #4 ; is it 0-3? + blo L9F7A ; brif so + ldd VCD ; get vertical coordinate of centre + subd VEREND ; subtract displacement + bcc L9F87 ; brif we didn't overflow the screen + clra ; minimize to top of screen + clrb + bra L9F87 +L9F7A ldd VCD ; get vertical centre coordinate + addd VEREND ; add displacement + bcs L9F85 ; brif we overflowed the screen + cmpd VD5 ; did we go past max coordinate? + blo L9F87 ; brif not +L9F85 ldd VD5 ; maximize the coordinate +L9F87 std VEREND ; save end coordinate + tst VD8 ; check first time flag + bne L9F8F ; do not draw if first time through (it was setting start coord) + bsr L9FDF ; draw the line +L9F8F puls a,b ; get arc number and sub arc + lsr VD8 ; get first time flag value (and clear it!) + bcs L9F9A ; do not check for end point after drawing for first coordinate + cmpd VD9 ; at end point? + beq L9FA6 ; brif drawing finished +L9F9A incb ; bump arc counter + cmpb #8 ; done 8 arcs? + bne L9FA3 ; brif not + inca ; bump octant + clrb ; reset subarc number + anda #7 ; make sure octant number stays in 0-7 range +L9FA3 jmp L9EFD ; go do another arc +L9FA6 rts +L9FA7 ldx VCF ; get radius + ldd ,u ; get sine/cosine table entry + beq L9FB4 ; brif 0 - offset = radius + subd #1 ; adjust values to correct range + bsr L9FB5 ; multiply radius by sine/cosine + tfr y,x ; resturn result in X +L9FB4 rts +L9FB5 pshs u,y,x,b,a ; save registers and reserve temp space + clr 4,s ; reset overflow byte (YH) + lda 3,s ; calcuate B*XL + mul + std 6,s ; put in "U" + ldd 1,s ; calculate B*XH + mul + addb 6,s ; accumluate with previous product + adca #0 + std 5,s ; save in YL,UH + ldb ,s ; calculate A*XL + lda 3,s + mul + addd 5,s ; accumulate with previous partical product + std 5,s ; save in YL,UH + bcc L9FD4 ; brif no carry + inc 4,s ; bump YH for carry +L9FD4 lda ,s ; calculate A*XH + ldb 2,s + mul + addd 4,s ; accumulate with previous partial product + std 4,s ; save in Y (we can't have a carry here) + puls a,b,x,y,u,pc ; restore multiplicands and return results +L9FDF jmp L94A1 ; go draw a line +L9FE2 clrb ; default arc number (0) + jsr GETCCH ; is there something there for a value? + beq L9FF8 ; brif not + jsr SYNCOMMA ; evaluate , + expression + jsr LB141 + lda FP0EXP ; multiply by 64 + adda #6 + sta FP0EXP + jsr LB70E ; get integer value of circle fraction + andb #0x3f ; max value of 63 +L9FF8 tfr b,a ; save arc value in A to calculate octant + andb #7 ; calculate subarc + lsra ; calculate octant + lsra + lsra + rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; COLOR BASIC ROM area +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; This is the official set of ROM entry points. It is unfortunate that the generic input ("console in") routine is not exposed +; here. It is also unfortunate that no open and close file routines are exposed. This and other oversignts led to a lot of +; software either having to re-invent the wheel or calling directly into the ROM instead of via these official entry points. +POLCAT fdb KEYIN ; indirect jump, get a keystroke +CHROUT fdb PUTCHR ; indirect jump, output character +CSRDON fdb CASON ; indirect jump, turn cassette on and start reading +BLKIN fdb GETBLK ; indirect jump, read a block from tape +BLKOUT fdb SNDBLK ; indirect jump, write a block to tape +JOYIN fdb GETJOY ; indirect jump, read joystick axes +WRTLDR fdb WRLDR ; indirect jump, turn cassette on and write a leader +; Initialization code. +LA00E lds #LINBUF+LBUFMX+1 ; put the stack in the line input buffer which is a safe place for now + lda #0x37 ; enable the cartidge interrupt (to detect autostarting cartridges) + sta PIA1+3 + lda RSTFLG ; get warm start flag + cmpa #0x55 ; is it valid? + bne BACDST ; brif not - cold start + ldx RSTVEC ; get warm start routine pointer + lda ,x ; get first byte of the routine + cmpa #0x12 ; is it NOP? + bne BACDST ; brif not - the routine is invalid so do a cold start + jmp ,x ; transfer control to the warm start routine +; RESET/power on comes here +RESVEC leay LA00E,pcr ; point to warm start check code +LA02A lda #0x3a ; restore MMU block in 0x4000-0x5fff block + sta MMUREG+2 + ldx #PIA1 ; point to PIA1 + ldd #0xff34 ; set up for initializing PIAs + clr 1,x ; set PIA1 DA to direction mode + clr 3,x ; set PIA1 DB to direction mode + deca + sta ,x ; set PIA1 DA bits 7-1 as output, 0 as input + lda #0xf8 ; set PIA1 DB bits 7-3 as output, 2-0 as input + sta 2,x + stb 1,x ; set PIA1 DA to data mode + stb 3,x ; set PIA1 DB to data mode + clr 2,x ; set VDG to alpha-numeric + lda #2 ; set RS232 to marking + sta ,x + lda #0xff + ldx #PIA0 ; point to PIA0 + clr 1,x ; set PIA0 DA to direction mode + clr 3,x ; set PIA0 DB to direction mode + clr ,x ; set PIA0 DA to input + sta 2,x ; set PIA0 DB to output + stb 1,x ; set PIA0 DA to direction mode + stb 3,x ; set PIA0 DB to direction mode + jmp LA072 ; continue initializing +LA05E jsr L8C2E ; map ROM pack + jmp 0xc000 ; transfer control to ROM pack +; Left over initialization code from Color Basic 1.2 follows + bitb 2,x ; check RAMSZ input + beq LA072 ; brif set for 4K RAMs + clr -2,x ; set strobe low + bitb 2,x ; check input + beq LA070 ; brif set for 64K rams + leau -2,u ; adjust pointer to set SAM for 16K RAMs +LA070 sta -3,u ; program SAM for either 16K or 64K RAMs +LA072 jmp ,y ; transfer control to startup routine +; Cold start jumps here +BACDST ldx #VIDRAM+1 ; point past the top of the first 1K of memory (for double predec below) +LA077 clr ,--x ; clear a byte (last will actually try clearing LSB of RESET vector in ROM) + leax 1,x ; move forward one byte (will set Z if we're done) + bne LA077 ; brif not donw yet + jsr LA928 ; clear the screen + clr ,x+ ; put the constant zero that lives before the program + stx TXTTAB ; set beginning of program storage +LA084 ldx #0x7fff ; set to of available RAM to just below the "ROM" area + bra LA093 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop +LA093 stx TOPRAM ; save top of memory (one below actual top because we need a byte for VAL() to work) + stx MEMSIZ ; save top of string space + stx STRTAB ; set bottom of allocated string space + leax -200,x ; allocate 200 bytes of string space + stx FRETOP ; set top of actually free memory + tfr x,s ; put the stack there + ldx #LA10D ; point to variable initializer + ldu #CMPMID ; point to variables to initialize (first batch) + ldb #28 ; 28 bytes in first batch + jsr LA59A ; copy bytes to variables + ldu #IRQVEC ; point to variables to initialize (second batch) + ldb #30 ; 30 bytes this time + jsr LA59A ; copy bytes to variables + ldx -12,x ; get SN error address + stx 3,u ; set ECB's command handlers to error + stx 8,u + ldx #RVEC0 ; point to RAM vectors + ldd #0x394b ; write 75 RTS opcodes (25 RAM vectors) +LA0C0 sta ,x+ ; put an RTS + decb ; done? + bne LA0C0 ; brif not + sta LINHDR-1 ; make temporary line header data for line encoding have a nonzero next line pointer + jsr LAD19 ; do a "NEW" + jmp L8002 ; transfer control to ECB's initialization routine +LA0CE pshs b,x ; save registers + tst HRWIDTH ; is it VDG mode? + lbne ALINK24 ; brif not +LA0D6 jsr LA199 ; do a "cursor" + jsr KEYIN ; read a key + beq LA0D6 ; brif no key +LA0DE jmp LA1B9 ; return to mainline + fcb 0x72 ; left-over from code replacement above +LA0E2 lda #0x55 ; warm start valid flag + sta RSTFLG ; mark warm start valid + bra LA0F3 ; go to direct mode +; Warm start entry point +BAWMST nop ; valid routine marker + clr DEVNUM ; reset output/input to screen + jsr LAD33 ; do a partial NEW + andcc #0xaf ; start interrupts + jsr CLS ; clear the screen +LA0F3 jmp LAC73 ; go to direct mode +; FIRQ service routine - this handles starting autostart cartridges +BFRQSV tst PIA1+3 ; is it the cartridge interrupt? + bmi LA0FC ; brif so + rti +LA0FC jsr L8C28 ; map cartridge + jsr LA7D1 ; delay for another while + leay 0 ; NOTE: the 0 is a placeholder, extended addressing is required + jmp BROMHK +; Variable initializers (second batch) + jmp BIRQSV ; IRQ handler + jmp BFRQSV ; FIRQ handler + jmp LB44A ; default USR() address + fcb 0x80,0x4f,0xc7,0x52,0x59 ; random seed + fcb 0xff ; capslock flag - default to upper case + fdb DEBDEL ; keyboard debounce delay (why is it a variable?) + jmp LB277 ; exponentiation handler vector + fcb 53 ; (command interpretation table) 53 commands + fdb LAA66 ; (command interpretation table) reserved words list (commands) + fdb LAB67 ; (command interpretation table) jump table (commands) + fcb 20 ; (command interpretation table) 20 functions + fdb LAB1A ; (command interpretation table) reserved words list (functions) + fdb LAA29 ; (command interpretation table) jump table (functions) +; This is the signon message. +LA147 fcc 'COLOR BASIC 1.2' + fcb 0x0d + fcc '(C) 1982 TANDY' + fcb 0 +; This is the "invalid colour" CLS easter egg text. Basically 11 wasted bytes +LA166 fcc 'MICROSOFT' + fcb 0x0d,0 +; Read a character from current device and mask off bit 7 (keep it as 7 bit ASCII) +LA171 bsr LA176 ; get character + anda #0x7f ; mask off high bit + rts +; Generic read routine. Reads a character from the device specified by DEVNUM. If no input was available, +; CINBFL will bet nonzero (on EOF). If input was available, CINBFL will be zero. Note that this routine +; has undefined results when called on an output only device. All registers except CC and A are preserved. +LA176 jsr RVEC4 ; do RAM hook + clr CINBFL ; flag data available + tst DEVNUM ; is it keyboard? + beq LA1B1 ; brif so - blink cursor and wait for key press + tst CINCTR ; is there anything in cassette input buffer? + bne LA186 ; brif so + com CINBFL ; flag EOF + rts +; Read character from cassette file +LA186 pshs u,y,x,b ; preserve registers + ldx CINPTR ; get input buffer pointer + lda ,x+ ; get character from buffer + pshs a ; save it for return + stx CINPTR ; save new input buffer pointer + dec CINCTR ; count character just consumed + bne LA197 ; brif buffer is not empty yet + jsr LA635 ; go read another block, if any, to refill the buffer +LA197 puls a,b,x,y,u,pc ; restore registers and return the character +; Blink the cursor. This might be better timed via an interrupt or something. +LA199 dec BLKCNT ; is it time to blink the cursor? + bne LA1AB ; brif not + ldb #11 ; reset blink timer + stb BLKCNT + ldx CURPOS ; get cursor position + lda ,x ; get character at the cursor + adda #0x10 ; move to next color + ora #0x8f ; make sure it's a grahpics block with all elements lit + sta ,x ; put new cursor block on screen +LA1AB ldx #DEBDEL ; we'll use the debounce delay for the cursor blink timer (10ms) +LA1AE jmp LA7D3 ; go count X down +; Blink cursor while waiting for a key press +LA1B1 pshs x,b ; save registers +LA1B3 bsr LA199 ; go do a cursor iteration + bsr KEYIN ; go read a key + beq LA1B3 ; brif no key pressed +LA1B9 ldb #0x60 ; VDG screen space character + stb [CURPOS] ; blank cursor out + puls b,x,pc ; restore registers and return +; This is the actual keyboard polling routine. Returns 0 if no new key is down. Compared to the 1.0 and 1.1 +; ROMs, this routine is quite a lot more compact and robust. +LA1C1 jmp KEYIN ; transfer control to actual keyboard scan + rts ;* this actually removes a check to see if any keys are actually down + rts ;* which is unfortunate because it makes programs run slower. + rts + rts + rts + rts + rts +KEYIN pshs u,x,b ; save registers + ldu #PIA0 ; point to keyboard PIA + ldx #KEYBUF ; point to state table + clra ; clear carry, set column to 0xff (no strobe) + deca ; (note: deca does not affect C) + pshs x,a ; save column counter and make a couple of holes for temporaries + sta 2,u ; set strobe to no columns +LA1D9 rol 2,u ; move to next column (C is 0 initially, 1 after) + bcc LA220 ; brif we shifted out a 0 - we've done 8 columns + inc 0,s ; bump column counter (first bump goes to 0) + bsr LA23A ; read row data + sta 1,s ; save key data (for debounce check and later saving) + eora ,x ; now bits set if key state changed + anda ,x ; now bits are only set if a key has been pressed + ldb 1,s ; get new key data + stb ,x+ ; save in state table + tsta ; was a key down? + beq LA1D9 ; brif not - do another (nothing above cleared C) + ldb 2,u ; get strobe data + stb 2,s ; save it for debounce check + ldb #0xf8 ; set up so B is 0 after first add +LA1F4 addb #8 ; add 8 for each row + lsra ; did we hit the right row? + bcc LA1F4 ; brif not + addb 0,s ; add in column number + beq LA245 ; brif @ + cmpb #26 ; letter? + bhi LA247 ; brif not + orb #0x40 ; bias into letter range + bsr LA22E ; check for SHIFT + ora CASFLG ; merge in capslock state + bne LA20C ; brif either capslock or SHIFT - keep upper case + orb #0x20 ; move to lower case +LA20C stb 0,s ; save ASCII value + ldx DEBVAL ; get debounce delay + bsr LA1AE ; do the 10ms debounce delay + ldb #0xff ; set strobe to none - only joystick buttons register now + bsr LA238 ; read keyboard + inca ; A now 0 if no buttons down + bne LA220 ; brif button down - return nothing since we have interference +LA21A ldb 2,s ; get column strobe data + bsr LA238 ; read row data + cmpa 1,s ; does it match original read? +LA220 puls a,x ; clean up stack and get return value + bne LA22B ; brif failed debounce or a joystick button down + cmpa #0x12 ; is it SHIFT-0? + bne LA22C ; brif not + com CASFLG ; swap capslock state +LA22B clra ; set no key down +LA22C puls b,x,u,pc ; restore registers and return +LA22E lda #0x7f ; column strobe for SHIFT + sta 2,u ; set column + lda ,u ; get row data + coma ; set if key down + anda #0x40 ; only keep SHIFT state + rts +LA238 stb 2,u ; save strobe data +LA23A lda ,u ; get row data + ora #0x80 ; mask off comparator so it doesn't interfere + tst 2,u ; are we on column 7? + bmi LA244 ; brif not + ora #0xc0 ; also mask off SHIFT +LA244 rts +LA245 ldb #51 ; scan code for @ +LA247 ldx #CONTAB-0x36 ; point to code table + cmpb #33 ; arrows, space, zero? + blo LA264 ; brif so + ldx #CONTAB-0x54 ; adjust to other half of table + cmpb #48 ; ENTER, CLEAR, BREAK, @? + bhs LA264 ; brif so + bsr LA22E ; read shift state + cmpb #43 ; is it a number, colon, semicolon? + bls LA25D ; brif so + eora #0x40 ; invert shift state for others +LA25D tsta ; shift down? + bne LA20C ; brif not - return result + addb #0x10 ; add in offset to shifted character + bra LA20C ; go return result +LA264 lslb ; two entries per key + bsr LA22E ; check SHIFT state + beq LA26A ; brif not shift + incb ; point to shifted entry +LA26A ldb b,x ; get actual key code + bra LA20C ; go return result +CONTAB fcb 0x5e,0x5f ; (^, _) + fcb 0x0a,0x5b ; (LF, [) + fcb 0x08,0x15 ; (BS, ^U) + fcb 0x09,0x5d ; (TAB, ]) + fcb 0x20,0x20 ; + fcb 0x30,0x12 ; <0> (0, ^R) + fcb 0x0d,0x0d ; (CR, CR) + fcb 0x0c,0x5c ; (FF, \) + fcb 0x03,0x03 ; (^C, ^C) + fcb 0x40,0x13 ; <@> (@, ^S) +; Generic output routine. +; Output character in A to the device specified by DEVNUM. All registers are preserved except CC. +; Sending output to a device that does not support output is undefined. +PUTCHR jsr RVEC3 ; call RAM hook + pshs b ; save B + ldb DEVNUM ; get desired device number + incb ; set flags (Z for -1, etc.) + puls b ; restore B + bmi LA2BF ; brif < -1 (line printer) + bne LA30A ; brif > -1 (screen) +; Write character to tape file + pshs x,b,a ; save registers + ldb FILSTA ; get file status + decb ; input file? + beq LA2A6 ; brif so + ldb CINCTR ; get character count + incb ; account for this character + bne LA29E ; brif buffer not full + bsr LA2A8 ; write previously full block to tape +LA29E ldx CINPTR ; get output buffer pointer + sta ,x+ ; put character in output + stx CINPTR ; save new buffer pointer + inc CINCTR ; account for this character +LA2A6 puls a,b,x,pc ; restore registers and return +; Write a block of data to tape. +LA2A8 ldb #1 ; data block type +LA2AA stb BLKTYP ; set block type + ldx #CASBUF ; point to output buffer + stx CBUFAD ; set buffer pointer + ldb CINCTR ; get number of bytes in the block + stb BLKLEN ; set length to write + pshs u,y,a ; save registers + jsr LA7E5 ; write a block to tape + puls a,y,u ; restore registers + jmp LA650 ; reset buffer pointers +; Send byte to line printer +LA2BF pshs x,b,a,cc ; save registers and interrupt status + orcc #0x50 ; disable interrupts +LA2C3 ldb PIA1+2 ; get RS232 status + lsrb ; get status to C + bcs LA2C3 ; brif busy - loop until not busy + bsr LA2FB ; set output to marking + clrb ; transmit one start bit + bsr LA2FD + ldb #8 ; counter for 8 bits +LA2D0 pshs b ; save bit count + clrb ; zero output bits + lsra ; bet output bit to C + rolb ; get output bit to correct bit for output byte + lslb + bsr LA2FD ; transmit bit + puls b ; get back bit counter + decb ; are we done yet? + bne LA2D0 ; brif not + bsr LA2FB ; send stop bit (marking) + puls cc,a ; restore interrupt status and output character + cmpa #0x0d ; carriage return? + beq LA2ED ; brif so + inc LPTPOS ; bump output position + ldb LPTPOS ; get new position + cmpb LPTWID ; end of line? + blo LA2F3 ; brif not +LA2ED clr LPTPOS ; reset position to start of line + bsr LA305 ; do carriage return delay + bsr LA305 +LA2F3 ldb PIA1+2 ; get RS232 status + lsrb ; get status to C + bcs LA2F3 ; brif still busy, keep waiting + puls b,x,pc ; restore registers and return +LA2FB ldb #2 ; set output to high (marking) +LA2FD stb PIA1 ; set RS232 output + bsr LA302 ; do baud delay (first iteration) then fall through for second +LA302 ldx LPTBTD ; get buard rate delay constant + skip2 +LA305 ldx LPTLND ; get carriage return delay constant + jmp LA7D3 ; count X down +; Output character to screen +LA30A pshs x,b,a ; save registers + ldx CURPOS ; get cursor pointer +LA30E cmpa #0x08 ; backspace? + bne LA31D ; brif not + cmpx #VIDRAM ; at top of screen? + beq LA35D ; brif so - it's a no-op + lda #0x60 ; VDG space character + sta ,-x ; put a space at previous location and move pointer back + bra LA344 ; save new cursor position and return +LA31D cmpa #0x0d ; carriage return? + bne LA32F ; brif not + ldx CURPOS ; get cursor pointer (why? we already have it) +LA323 lda #0x60 ; VDG space character + sta ,x+ ; put output space + tfr x,d ; see if we at a multiple of 32 now + bitb #0x1f + bne LA323 ; brif not + bra LA344 ; go check for scrolling +LA32F cmpa #0x20 ; control character? + blo LA35D ; brif so + tsta ; is it graphics block? + bmi LA342 ; brif so + cmpa #0x40 ; number or special? + blo LA340 ; brif so (flip "case" bit) + cmpa #0x60 ; upper case alpha? + blo LA342 ; brif so - keep it unmodified + anda #0xdf ; clear bit 5 (inverse video) +LA340 eora #0x40 ; flip inverse video bit +LA342 sta ,x+ ; output character +LA344 stx CURPOS ; save new cursor position + cmpx #VIDRAM+511 ; end of screen? + bls LA35D ; brif not + ldx #VIDRAM ; point to start of screen +LA34E ldd 32,x ; get two characters from next row + std ,x++ ; put them on this row + cmpx #VIDRAM+0x1e0 ; at start of last row on screen? + blo LA34E ; brif not + ldb #0x60 ; VDG space + jsr LA92D ; blank out last line (borrow CLS's loop) +LA35D puls a,b,x,pc ; restore registers and return +; Set up device parameters for output +LA35F jsr RVEC2 ; do the RAM hook dance + pshs x,b,a ; save registers + clr PRTDEV ; flag device as a screen + lda DEVNUM ; get devicenumber + beq LA373 ; brif screen + inca ; is it tape? + beq LA384 ; brif so + ldx LPTCFW ; get tab width and last tab stop for printer + ldd LPTWID ; get line width and current position for printer + bra LA37C ; set parameters +LA373 ldb CURPOS+1 ; get LSB of cursor position + andb #0x1f ; now we have the offset into the line + ldx #0x1010 ; 16 character tab, position 16 is last tab stop + lda #32 ; screen is 32 characters wide +LA37C stx DEVCFW ; save tab width and last tab stop for active device + stb DEVPOS ; save line position for current device + sta DEVWID ; save line width for current device + puls a,b,x,pc ; restore registers and return +LA384 com PRTDEV ; flag device as non-display + ldx #0x0100 ; tab width is 1, last tab field is 0 + clra ; line width is 0 + clrb ; character position on line is 0 + bra LA37C ; go set parameters +; This is the line input routine used for reading lines for Basic, both in immediate mode and for +; INPUT and LINE INPUT. Exit with C set if terminated by BREAK and C clear if terminated by ENTER. +; The actualy entry point is LA390. Note that this routine echoes to *all* devices. +LA38D jsr LA928 ; clear screen (CLEAR key handling) +LA390 jsr RVEC12 ; do the RAM hook dance + clr IKEYIM ; reset cached input character from BREAK check + ldx #LINBUF+1 ; point to line input buffer (input pointer) + ldb #1 ; Number of characters in line (we start at 1 so BS handling is easier) +LA39A jsr LA171 ; get an input character, only keep low 7 bits + tst CINBFL ; is it EOF? + bne LA3CC ; brif EOF + tst DEVNUM ; is it keyboard input? + bne LA3C8 ; brif not - don't do line editing + cmpa #0x0c ; form feed (CLEAR)? + beq LA38D ; brif so - clear screen and reset + cmpa #0x08 ; backspace? + bne LA3B4 ; brif not + decb ; move back one character + beq LA390 ; brif we were at the start of the line - reset and start again + leax -1,x ; move input pointer back + bra LA3E8 ; echo the backspace and continue +LA3B4 cmpa #0x15 ; SHIFT-LEFT (kill line)? + bne LA3C2 ; brif not +LA3B8 decb ; at start of line? + beq LA390 ; brif so - reset and restart + lda #0x08 ; echo a backspace + jsr PUTCHR + bra LA3B8 ; see if we've erased everything yet +LA3C2 cmpa #0x03 ; BREAK? + orcc #1 ; set C if it is (only need Z for the next test +LA3C6 beq LA3CD ; brif BREAK - exit +LA3C8 cmpa #0x0d ; ENTER (CR) + bne LA3D9 ; brif not +LA3CC clra ; clear carry (it might not be clear on EOF) +LA3CD pshs cc ; save ENTER/BREAK flag + jsr LB958 ; echo a carriage return + clr ,x ; make sure we have a NUL at the end of the buffer + ldx #LINBUF ; point to input buffer + puls cc,pc ; restore ENTER/BREAK flag and return +LA3D9 cmpa #0x20 ; control character? + blo LA39A ; brif so - skip it + cmpa #'z+1 ; above z? + bhs LA39A ; brif so - ignore it + cmpb #LBUFMX ; is the buffer full? + bhs LA39A ; brif so - ignore extra characters + sta ,x+ ; put character in the buffer + incb ; bump character count +LA3E8 jsr PUTCHR ; echo character + bra LA39A ; go handle next input character +; Check if DEVNUM Is valid for reading. Raise FM error if open but not for reading. NO error if not open. +LA3ED jsr RVEC5 ; do the RAM hook dance + lda DEVNUM ; get device number + beq LA415 ; brif keyboard - always valid + inca ; is it tape? + bne LA403 ; brif not + lda FILSTA ; get tape file status + bne LA400 ; brif file is open +LA3FB ldb #22*2 ; raise NO error + jmp LAC46 +LA400 deca ; is it in input mode? + beq LA415 ; brif so +LA403 jmp LA616 ; raise FM error +; Check if DEVNUM is valid for writing. Raise FM error if open but not for writing. NO error if not open. +LA406 jsr RVEC6 ; do the RAM hook dance + lda DEVNUM ; get device number + inca ; is it tape? + bne LA415 ; brif not + lda FILSTA ; get file status + beq LA3FB ; brif not open + deca ; is it open for reading? + beq LA403 ; brif so - bad mode +LA415 rts +; CLOSE command +CLOSE beq LA426 ; brif no file specified - close all files + jsr LA5A5 ; parse device number +LA41B bsr LA42D ; close specified file + jsr GETCCH ; is there more? + beq LA44B ; brif not + jsr LA5A2 ; check for comma and parse another device number + bra LA41B ; go close this one +; Close all files handler. +LA426 jsr RVEC7 ; Yup. The RAM hook dance. +LA429 lda #-1 ; start with tape file + sta DEVNUM +; Close file specified in DEVNUM. Note that this never fails. +LA42D jsr RVEC8 ; You know it. RAM hook. + lda DEVNUM ; get device we're closing + clr DEVNUM ; reset to screen/keyboard + inca ; is it tape? + bne LA44B ; brif not + lda FILSTA ; get file status + cmpa #2 ; is it output? + bne LA449 ; brif not + lda CINCTR ; is there anything waiting to be written? + beq LA444 ; brif not + jsr LA2A8 ; write final block of data +LA444 ldb #0xff ; write EOF block + jsr LA2AA +LA449 clr FILSTA ; mark tape file closed +LA44B rts +; CSAVE command +CSAVE jsr LA578 ; parse filename + jsr GETCCH ; see what we have after the file name + beq LA469 ; brif none + jsr SYNCOMMA ; make sure there's a comma + ldb #'A ; make sure there's an A after + jsr LB26F + bne LA44B ; brif not end of line + clra ; file type 0 (basic program) + jsr LA65C ; write out header block + lda #-1 ; set output to tape + sta DEVNUM + clra ; set Z so we list the whole program + jmp LIST ; go list the program to tape +LA469 clra ; file type 0 (basic program) + ldx ZERO ; set to binary file mode + jsr LA65F ; write header block + clr FILSTA ; close files + inc BLKTYP ; set block type to data + jsr WRLDR ; write out a leader + ldx TXTTAB ; point to start of program +LA478 stx CBUFAD ; set buffer location + lda #255 ; block size to 255 bytes (max size) + sta BLKLEN + ldd VARTAB ; get end of program + subd CBUFAD ; how much is left? + beq LA491 ; brif we have nothing left + cmpd #255 ; do we have a full block worth? + bhs LA48C ; brif so + stb BLKLEN ; save actual remainder as block length +LA48C jsr SNDBLK ; write a block out + bra LA478 ; go do another block +LA491 neg BLKTYP ; set block type to 0xff (EOF) + clr BLKLEN ; no data in EOF block + jmp LA7E7 ; write EOF, stop tape, and return +; CLOAD and CLOADM commands +CLOAD clr FILSTA ; close tape file + cmpa #'M ; is it ClOADM? + beq LA4FE ; brif so + leas 2,s ; clean up stack + jsr LA5C5 ; parse file name + jsr LA648 ; go find the file + tst CASBUF+10 ; is it binary? + beq LA4C8 ; brif so + lda CASBUF+9 ; is it ASCII? + beq LA4CD ; brif not + jsr LAD19 ; clear out existing program + lda #-1 ; set up for reading from tape + sta DEVNUM + inc FILSTA ; set tape file to input + jsr LA635 ; go read first block + jmp LAC7C ; go to immediate mode to read in the program +; This is the EOF handler for immediate mode. It's rather assinine for this to be located here. It is +; probably an artifact of a substantially different code layout prior to compacting the ROM to fit in +; 8K. +LA4BF jsr RVEC13 ; do the RAM hook dance + jsr LA42D ; close file + jmp LAC73 ; go back to immediate mode +LA4C8 lda CASBUF+8 ; get file type + beq LA4D0 ; brif basic program +LA4CD jmp LA616 ; raise FM error +LA4D0 jsr LAD19 ; erase existing program + jsr CASON ; start reading tape + ldx TXTTAB ; get start of program storage +LA4D8 stx CBUFAD ; set load address for block + ldd CBUFAD ; get start of block + inca ; bump by 256 + jsr LAC37 ; check if there's room for a maximum sized block of 255 + jsr GETBLK ; go read a block + bne LA4F8 ; brif there was an error during reading + lda BLKTYP ; get type of block read + beq LA4F8 ; brif header block - IO error + bpl LA4D8 ; brif data block - read another + stx VARTAB ; save new end of program + bsr LA53B ; stop tape + ldx #LABED-1 ; point to "OK" prompt + jsr STRINOUT ; show prompt + jmp LACE9 ; reset various things and return +LA4F8 jsr LAD19 ; clear out partial program load +LA4FB jmp LA619 ; raise IO error +; This is the CLOADM command +LA4FE jsr GETNCH ; eat the "M" + bsr LA578 ; parse file name + jsr LA648 ; go find the file +LA505 ldx ZERO ; default offset is 0 + jsr GETCCH ; see if there's something after the file name + beq LA511 ; brif no offset + jsr SYNCOMMA ; make sure there's a comma + jsr LB73D ; evaluate offset to X +LA511 lda CASBUF+8 ; get file mode + cmpa #2 ; M/L program? + bne LA4CD ; brif not - FM error + ldd CASBUF+11 ; get load address + leau D,x ; add in offset + stu EXECJP ; set EXEC default address + tst CASBUF+10 ; is it binary? + bne LA4CD ; brif not + ldd CASBUF+13 ; get load address + leax d,x ; add in offset + stx CBUFAD ; set buffer address for loading + jsr CASON ; start up tape +LA52E jsr GETBLK ; read a block + bne LA4FB ; brif error reading + stx CBUFAD ; save new load address + tst BLKTYP ; set flags on block type + beq LA4FB ; brif another header - IO error + bpl LA52E ; brif it was data - read more +LA53B jmp LA7E9 ; turn off tape and return +; The EXEC command +EXEC beq LA545 ; brif no argument - use default address + jsr LB73D ; evaluate EXEC address to X + stx EXECJP ; set new default EXEC address +LA545 jmp [EXECJP] ; transfer control to execution address +; Break check for LIST so it doesn't interrupt ASCII saves. Why is this here instead of with the rest of the break +; check logic or packaged up with LIST? +LA549 jsr RVEC11 ; do the RAM hook dance + lda DEVNUM ; get device number + inca ; is it tape? + beq LA5A1 ; brif so - don't do break check + jmp LADEB ; do the actual break check +; Evaluate an expression and make sure it is within the limits of the screen and sets the cursor position. +; This really should be located with the PRINT command. +LA554 jsr LB3E4 ; evaluate a positive expression to D + subd #511 ; is it within bounds? + lbhi LB44A ; brif not - error out + addd #VIDRAM+511 ; adjust to be within the screen (and undo the SUBD above) + std CURPOS ; set cursor position + rts +; INKEY$ function +INKEY lda IKEYIM ; was a key down during break check? + bne LA56B ; brif so + jsr KEYIN ; poll the keyboard +LA56B clr IKEYIM ; reset the break check cache + sta FPA0+3 ; store result for later return + lbne LB68F ; brif a key was down - return it as a string + sta STRDES ; set string length to 0 (no key down) + jmp LB69B ; return the NULL string +; Parse a filename +LA578 ldx #CFNBUF ; point to file name buffer + clr ,x+ ; zero out file name length + lda #0x20 ; space character to initialize file name +LA57F sta ,x+ ; put a space in the buffer + cmpx #CASBUF ; at end of file name? + bne LA57F ; brif not + jsr GETCCH ; get input character + beq LA5A1 ; brif no name present + jsr LB156 ; evaluate the file name expression + jsr LB654 ; point to start of the file name + ldu #CFNBUF ; point to file name buffer + stb ,u+ ; save string length + beq LA5A1 ; brif empty - we're done + skip2 +LA598 ldb #8 ; copy 8 bytes +; Move B bytes from (X) to (U) +LA59A lda ,x+ ; copy a byte + sta ,u+ + decb ; done yet? + bne LA59A ; brif not +LA5A1 rts +; Parse a device number and check validity +LA5A2 jsr SYNCOMMA ; check for comma and SN error if not +LA5A5 cmpa #'# ; do we have a #? + bne LA5AB ; brif not (it's optional) + jsr GETNCH ; munch the # +LA5AB jsr LB141 ; evaluate the expression +LA5AE jsr INTCNV ; convert it to an integer in D + rolb ; move sign of B into C + adca #0 ; add sign of B to A + bne LA61F ; brif A doesn't match the sign of B + rorb ; restore B (ADCA will have set C if B was negative) + stb DEVNUM ; set device number + jsr RVEC1 ; do the RAM hook dance + beq LA5C4 ; brif device number set to screen/keyboard (valid) + bpl LA61F ; brif not negative (not valid) + cmpb #-2 ; is it printer or tape? + blt LA61F ; brif not (not valid) +LA5C4 rts +; Read file name from the line and do an error if anything follows it +LA5C5 bsr LA578 ; parse file name +LA5C7 jsr GETCCH ; set flags on current character +LA5C9 beq LA5C4 ; brif nothing there - it's good + jmp LB277 ; raise SN error +; EOF function +EOF jsr RVEC14 ; do the RAM hook dance + lda DEVNUM ; get device number + pshs a ; save it (so we can restore it later) + bsr LA5AE ; check the device number (which is in FPA0) + jsr LA3ED ; check validity for reading +LA5DA clrb ; not EOF = 0 (FALSE) + lda DEVNUM ; get device number + beq LA5E4 ; brif keyboard - never EOF + tst CINCTR ; is there anything in the input buffer? + bne LA5E4 ; brif so - not EOF + comb ; set EOF flag to -1 (true) +LA5E4 puls a ; get back original device + sta DEVNUM ; restore it +LA5E8 sex ; sign extend result to 16 bits + jmp GIVABF ; go return the result +; SKIPF command +SKIPF bsr LA5C5 ; parse file name + bsr LA648 ; look for the file + jsr LA6D1 ; read the file + bne LA619 ; brif error reading file + rts +; OPEN command +OPEN jsr RVEC0 ; do the RAM hook dance + jsr LB156 ; get file status (input/output) + jsr LB6A4 ; get first character of status string + pshs b ; save status + bsr LA5A2 ; parse a comma then the device number + jsr SYNCOMMA ; make sure there's a comma + bsr LA5C5 ; parse the file name + lda DEVNUM ; get device number of the file + clr DEVNUM ; reset actual device to the screen + puls b ; get back status + cmpb #'I ; INPUT? + beq LA624 ; brif so - open a file for INPUT + cmpb #'O ; OUTPUT? + beq LA658 ; brif so - open a file for OUTPUT +LA616 ldb #21*2 ; raise FM error + skip2 +LA619 ldb #20*2 ; raise I/O error + skip2 +LA61C ldb #18*2 ; raise AO error + skip2 +LA61F ldb #19*2 ; raise DN error + jmp LAC46 +LA624 inca ; are we opening the tape? + bmi LA616 ; brif printer - FM error; printer can't be opened for READ + bne LA657 ; brif screen - screen is always open + bsr LA648 ; read header block + lda CASBUF+9 ; clear A if binary or machine language file + anda CASBUF+10 + beq LA616 ; bad file mode if not data file + inc FILSTA ; open file for input +LA635 jsr LA701 ; start tape, read block + bne LA619 ; brif error during read + tst BLKTYP ; check block type + beq LA619 ; brif header block - something's wrong + bmi LA657 ; brif EOF + lda BLKLEN ; get length of block + beq LA635 ; brif empty block - read another +LA644 sta CINCTR ; set buffer count + bra LA652 ; reset buffer pointer +LA648 tst FILSTA ; is the file open? + bne LA61C ; brif so - AO error + bsr LA681 ; search for file + bne LA619 ; brif error on read +LA650 clr CINCTR ; mark buffer empty +LA652 ldx #CASBUF ; set buffer pointer to start of buffer + stx CINPTR +LA657 rts +LA658 inca ; check for tape device + bne LA657 ; brif not tape (nothing doing - it's always open) + inca ; make file type 1 +LA65C ldx #0xffff ; ASCII and data mode +LA65F tst FILSTA ; is file open? + bne LA61C ; brif so - raise error + ldu #CASBUF ; point to tape buffer + stu CBUFAD ; set address of block to write + sta 8,u ; set file type + stx 9,u ; set ASCII flag and mode + ldx #CFNBUF+1 ; point to file name + jsr LA598 ; move file name to the tape buffer + clr BLKTYP ; set for header block + lda #15 ; 15 bytes in a header block + sta BLKLEN ; set block length + jsr LA7E5 ; write the block + lda #2 ; set file type to output + sta FILSTA + bra LA650 ; reset file pointers +; Search for correct cassette file name +LA681 ldx #CASBUF ; point to cassette buffer + stx CBUFAD ; set location to read blocks to +LA686 lda CURLIN ; are we in immediate mode? + inca + bne LA696 ; brif not + jsr LA928 ; clear screen + ldx CURPOS ; get start of screen (set after clear) + ldb #'S ; for "searching" + stb ,x++ ; put it on the screen + stx CURPOS ; save cursor position to be one past the search indicator +LA696 bsr LA701 ; read a block + orb BLKTYP ; merge error flag with block type + bne LA6D0 ; brif error or not header + ldx #CASBUF ; point to block just read + ldu #CFNBUF+1 ; point to the desired name + ldb #8 ; compare 8 characters + clr ,-s ; set flag to "match" +LA6A6 lda ,x+ ; get character from just read block + ldy CURLIN ; immediate mode? + leay 1,y + bne LA6B4 ; brif not + clr DEVNUM ; set output to screen + jsr PUTCHR ; display character +LA6B4 suba ,u+ ; subtract from desired file name (nonzero if no match) + ora ,s ; merge with match flag + sta ,s ; save new match flag (will be nonzero if any character differs) + decb ; done all characters? + bne LA6A6 ; brif not - do another + lda ,s+ ; get match flag (and set flags) + beq LA6CB ; brif we have a match + tst -9,u ; did we actually have a file name or will any file do? + beq LA6CB ; brif any file will do + bsr LA6D1 ; go read past the file + bne LA6D0 ; return on error + bra LA686 ; keep looking +LA6CB lda #'F ; for "found" + bsr LA6F8 ; put "F" on screen + clra ; set Z to indicat eno errors +LA6D0 rts +LA6D1 tst CASBUF+10 ; check type of file + bne LA6DF ; brif "blocked" file + jsr CASON ; turn on tape +LA6D9 bsr GETBLK ; read a block + bsr LA6E5 ; error or EOF? + bra LA6D9 ; read another block +LA6DF bsr LA701 ; read a single block + bsr LA6E5 ; error or EOF? + bra LA6DF ; read another block +LA6E5 bne LA6ED ; got error reading block + lda BLKTYP ; check block type + nega ; A is 0 now if EOF + bmi LA700 ; brif not end of file + deca ; clear error indicator +LA6ED sta CSRERR ; set error flag + leas 2,s ; don't return to original caller + bra LA705 ; turn off motor and return +LA6F3 lda VIDRAM ; get first char on screen + eora #0x40 ; flip case +LA6F8 ldb CURLIN ; immediate mode? + incb + bne LA700 ; brif not + sta VIDRAM ; save flipped case character +LA700 rts +; Read a single block from tape (for a "blocked" file) +LA701 bsr CASON ; start tape going + bsr GETBLK ; read block +LA705 jsr LA7E9 ; stop tape + ldb CSRERR ; get error status + rts +; Read a block from tape - this does the heavy lifting +GETBLK orcc #0x50 ; disable interrupts (timing is important) + bsr LA6F3 ; reverse video of upper left character in direct mode + ldx CBUFAD ; point to destination buffer + clra ; reset read byte +LA712 bsr LA755 ; read a bit + rora ; move bit into accumulator + cmpa #0x3c ; have we synched on the start of the block data yet? + bne LA712 ; brif not + bsr LA749 ; read block type + sta BLKTYP + bsr LA749 ; get block size + sta BLKLEN + adda BLKTYP ; accumulate checksum + sta CCKSUM ; save current checksum + lda BLKLEN ; get back count + sta CSRERR ; initialize counter; we use this since it will be ovewritten later anyway + beq LA73B ; brif empty block +LA72B bsr LA749 ; read a byte + sta ,x ; save in buffer + cmpa ,x+ ; make sure it wrote + bne LA744 ; brif error if it didn't match + adda CCKSUM ; accumulate checksum + sta CCKSUM + dec CSRERR ; read all bytes? + bne LA72B ; brif not +LA73B bsr LA749 ; read checksum from tape + suba CCKSUM ; does it match? + beq LA746 ; brif so + lda #1 ; checksum error flag + skip2 +LA744 lda #2 ; non-RAM error flag +LA746 sta CSRERR ; save error status + rts +LA749 lda #8 ; read 8 bits + sta CPULWD ; initialize counter +LA74D bsr LA755 ; read a bit + rora ; put it into accumulator + dec CPULWD ; got all 8 bits? + bne LA74D ; brif not + rts +LA755 bsr LA75D ; get time between transitions + ldb CPERTM ; get timer + decb + cmpb CMPMID ; set C if timer is below the transition point - high or 1; clear otherwise + rts +LA75D clr CPERTM ; reset timer + tst CBTPHA ; check which phase we synched on + bne LA773 ; brif HI-LO synch +LA763 bsr LA76C ; read input + bcs LA763 ; brif still high +LA767 bsr LA76C ; read input + bcc LA767 ; brif still low + rts +LA76C inc CPERTM ; bump timer + ldb PIA1 ; get input bit to C + rorb + rts +LA773 bsr LA76C ; read input + bcc LA773 ; brif still low +LA777 bsr LA76C ; read output + bcs LA777 ; brif still high + rts +; Start tape and look for sync bytes +CASON orcc #0x50 ; disable interrupts + bsr LA7CA ; turn on tape + clr CPULWD ; reset timer +LA782 bsr LA763 ; wait for low-high transition +LA784 bsr LA7AD ; wait for it to go low again + bhi LA797 ; brif in range for 1200 Hz +LA788 bsr LA7A7 ; wait for it to go high again + blo LA79B ; brif in range for 2400 Hz + dec CPULWD ; decrement counter (synched on low-high) + lda CPULWD ; get counter + cmpa #-96 ; have we seen 96 1-0-1-0 patterns (48 0x55s)? +LA792 bne LA782 ; brif not - wait some more + sta CBTPHA ; save phase we synched on + rts +LA797 bsr LA7A7 ; wait for it to go high again + bhi LA784 ; brif another 1200 Hz, 2 in a row, try again +LA79B bsr LA7AD ; wait for it to go low again + blo LA788 ; brif another 2400 Hz; go try again for high + inc CPULWD ; bump counter + lda CPULWD ; get counter + suba #96 ; set 0 if we've seen enought 0-1-0-1 patterns (0xaa) + bra LA792 ; set phase and return or keep waiting +LA7A7 clr CPERTM ; reset period timer + bsr LA767 ; wait for high + bra LA7B1 ; set flags on result +LA7AD clr CPERTM ; reset period timer + bsr LA777 ; wait for low +LA7B1 ldb CPERTM ; get period count + cmpb CMP0 ; is it too long for 1200Hz? + bhi LA7BA ; brif so - reset counts + cmpb CMP1 ; set C if 2400Hz, clear C if 1200 Hz + rts +LA7BA clr CPULWD ; reset sync counter (too slow or drop out) + rts +; MOTOR command +MOTOR tfr a,b ; save ON/OFF + jsr GETNCH ; eat the ON/OFF token + cmpb #0xaa ; OFF? + beq LA7E9 ; brif so - turn off tape + cmpb #0x88 ; ON? + jsr LA5C9 ; SN error if no match +; Turn on tape +LA7CA lda PIA1+1 ; get motor control value + ora #8 ; turn on bit 3 (starts motor) + bsr LA7F0 ; put it back (dumb but it saves a byte) +LA7D1 ldx ZERO ; maximum delay timer +LA7D3 leax -1,x ; count down + bne LA7D3 ; brif not at 0 yet + rts +; Write a synch leader to tape +WRLDR orcc #0x50 ; disable interrupts + bsr LA7CA ; turn on tape + ldx SYNCLN ; get count of 0x55s to write +LA7DE bsr LA828 ; write a 0x55 + leax -1,x ; done? + bne LA7DE ; brif not + rts +; Write sync bytes and a block, then stop tape +LA7E5 bsr WRLDR ; write sync +LA7E7 bsr SNDBLK ; write block +; Turn off tape +LA7E9 andcc #0xaf ; enable interrupts + lda PIA1+1 ; get control register + anda #0xf7 ; disable motor bit +LA7F0 sta PIA1+1 ; set motor enable bit + rts +; Write a block to tape. +SNDBLK orcc #0x50 ; disable interrupts + ldb BLKLEN ; get block size + stb CSRERR ; initialize character counter + lda BLKLEN ; initialize checksum + beq LA805 ; brif empty block + ldx CBUFAD ; point to tape buffer +LA800 adda ,x+ ; accumulate checksum + decb ; end of block data? + bne LA800 ; brif not +LA805 adda BLKTYP ; accumulate block type into checksum + sta CCKSUM ; save calculated checksum + ldx CBUFAD ; point to buffer + bsr LA828 ; send a 0x55 + lda #0x3c ; and then a 0x3c + bsr LA82A + lda BLKTYP ; send block type + bsr LA82A + lda BLKLEN ; send block size + bsr LA82A + tsta ; empty block? + beq LA824 ; brif so +LA81C lda ,x+ ; send character from block data + bsr LA82A + dec CSRERR ; are we done yet? + bne LA81C ; brif not +LA824 lda CCKSUM ; send checksum + bsr LA82A +LA828 lda #0x55 ; send a 0x55 +LA82A pshs a ; save output byte + ldb #1 ; initialize bit probe +LA82E lda CLSTSN ; get ending value of last cycle + sta PIA1 ; set DA + ldy #LA85C ; point to sine wave table + bitb ,s ; is bit set? + bne LA848 ; brif so - do high frequency +LA83B lda ,y+ ; get next sample (use all for low frequency) + cmpy #LA85C+36 ; end of table? + beq LA855 ; brif so + sta PIA1 ; set output sample + bra LA83B ; do another sample +LA848 lda ,y++ ; get next sample (use every other for high frequency) + cmpy #LA85C+36 ; end of table? + beq LA855 ; brif so + sta PIA1 ; send output sample + bra LA848 ; do another sample +LA855 sta CLSTSN ; save last sample that *would* have been sent + lslb ; shift mask to next bit + bcc LA82E ; brif not done all 8 bits + puls a,pc ; get back original character and return +; This is the sample table for the tape sine wave +LA85C fcb 0x82,0x92,0xaa,0xba,0xca,0xda + fcb 0xea,0xf2,0xfa,0xfa,0xfa,0xf2 + fcb 0xea,0xda,0xca,0xba,0xaa,0x92 + fcb 0x7a,0x6a,0x52,0x42,0x32,0x22 + fcb 0x12,0x0a,0x02,0x02,0x02,0x0a + fcb 0x12,0x22,0x32,0x42,0x52,0x6a +; SET command +SET bsr LA8C1 ; get absolute screen position of graphics block + pshs x ; save character location + jsr LB738 ; evaluate comma then expression in B + puls x ; get back character pointer + cmpb #8 ; valid colour? + bhi LA8D5 ; brif not + decb ; normalize colours + bmi LA895 ; brif colour 0 (use current colour) + lda #0x10 ; 16 patterns per colour + mul + bra LA89D ; go save the colour +LA895 ldb ,x ; get current value + bpl LA89C ; brif not grahpic + andb #0x70 ; keep only the colour + skip1 +LA89C clrb ; reset block to all black +LA89D pshs b ; save colour + bsr LA90D ; force a ) + lda ,x ; get current screen value + bmi LA8A6 ; brif graphic block already + clra ; force all pixels off +LA8A6 anda #0x0f ; keep only pixel data + ora GRBLOK ; set the desired pixel + ora ,s+ ; merge with desired colour +LA8AC ora #0x80 ; force it to be a graphic block + sta ,x ; put new block on screen + rts +; RESET command +RESET bsr LA8C1 ; get address of desired block + bsr LA90D ; force a ) + clra ; zero block (no pixels) + ldb ,x ; is it graphics? + bpl LA8AC ; brif not - just blank the block + com GRBLOK ; invert pixel data + andb GRBLOK ; turn off the desired pixel + stb ,x ; put new pixel data on screen + rts +; Parse SET/RESET/POINT coordinates except for closing ) +LA8C1 jsr LB26A ; make sure it starts with ( +LA8C4 jsr RVEC21 ; do the RAM hook dance + jsr EVALEXPB ; get first coordinate + cmpb #63 ; valid horizontal coordinate + bhi LA8D5 ; brif out of range + pshs b ; save horizontal coordinate + jsr LB738 ; look for , followed by vertical coordinate + cmpb #31 ; in range for vertical? +LA8D5 bhi LA948 ; brif not + pshs b ; save vertical coordinate + lsrb ; divide by two (two blocks per row) + lda #32 ; 32 bytes per row + mul ; now we have the offset into video RAM + ldx #VIDRAM ; point to start of screen + leax d,x ; now X points to the correct character row + ldb 1,s ; get horizontal coordinate + lsrb ; divide by two (two per character cell) + abx ; now we're pointing to the correct character cell + puls a,b ; get back coordinates (vertical in A) + anda #1 ; keep only row offset of vertical + rorb ; get column offset of horizontal to C + rola ; now we have "row * 2 + col" in A + ldb #0x10 ; make a bit mask (one bit left of first pixel) +LA8EE lsrb ; move mask right + deca ; at the right pixel? + bpl LA8EE ; brif not + stb GRBLOK ; save graphics block mask + rts +; POINT function +POINT bsr LA8C4 ; evaluate coordinates + ldb #0xff ; default colour value is -1 (not graphics) + lda ,x ; get character + bpl LA90A ; brif not graphics + anda GRBLOK ; is desired pixel set? + beq LA909 ; brif not - return 0 for "black" + ldb ,x ; get graphics data + lsrb ; shift right 4 to get colour in low bits + lsrb + lsrb + lsrb + andb #7 ; lose the graphics block bias +LA909 incb ; shift colours into 1 to 8 range +LA90A jsr LA5E8 ; convert B to floating point +LA90D jmp LB267 ; make sure we have a ) and return +; CLS command +CLS jsr RVEC22 ; do the RAM hook dance +LA913 beq LA928 ; brif no colour - just do a basic screen clear + jsr EVALEXPB ; evaluate colour number + cmpb #8 ; valid colour? + bhi LA937 ; brif not - do the easter egg + tstb ; color 0? + beq LA925 ; brif so + decb ; normalize to 0 based colour numbers + lda #0x10 ; 16 blocks per colour + mul ; now we have the base code for that colour + orb #0x0f ; set all pixels +LA925 orb #0x80 ; make it a graphics block + skip2 +LA928 ldb #0x60 ; VDG screen space character + ldx #VIDRAM ; point to start of screen +LA92D stx CURPOS ; set cursor position +LA92F stb ,x+ ; blank a character + cmpx #VIDRAM+511 ; end of screen? + bls LA92F ; brif not + rts +LA937 bsr LA928 ; clear te screen + ldx #LA166-1 ; point to the easter egg + jmp STRINOUT ; go display it +; Evaluate an expression to B, prefixed by a comma, and do FC error if 0 +LA93F jsr SYNCOMMA ; force a comma +LA942 jsr EVALEXPB ; evaluate expression to B + tstb ; is it 0? + bne LA984 ; brif not - return +LA948 jmp LB44A ; raise FC error +; SOUND command +SOUND bsr LA942 ; evaluate frequency + stb SNDTON ; save it + bsr LA93F ; evaluate duration (after a comma) +LA951 lda #4 ; constant factor for duration (each increment is 1/15 of a second) + mul + std SNDDUR ; save length of sound (IRQ will count it down) + lda PIA0+3 ; enable 60 Hz interrupt + ora #1 + sta PIA0+3 + clr ARYDIS ; clear array disable flag for some reason + bsr LA9A2 ; connect DAC to MUX output + bsr LA976 ; turn on sound +LA964 bsr LA985 ; store mid range output value and delay + lda #0xfe ; store high value and delay + bsr LA987 + bsr LA985 ; store mid range value and delay + lda #2 ; store low value and delay + bsr LA987 + ldx SNDDUR ; has timer expired? + bne LA964 ; brif not, do another wave +; Disable sound output +LA974 clra ; bit 3 to 0 will disable output + skip2 +; Enable sound output +LA976 lda #8 ; bit 3 set to enable output + sta ,-s ; save desired value + lda PIA1+3 ; get control register value + anda #0xf7 ; reset value + ora ,s+ ; set to desired value + sta PIA1+3 ; set new sound output status +LA984 rts +LA985 lda #0x7e ; mid range value for DAC +LA987 sta PIA1 ; set DAC output value + lda SNDTON ; get frequency +LA98C inca ; increment it (gives shorter count with higher values, so higher frequencies work) + bne LA98C ; brif not done yet + rts +; AUDIO command +AUDIO tfr a,b ; save ON/OFF token + jsr GETNCH ; munch the ON/OFF token + cmpb #0xaa ; OFF? + beq LA974 ; brif so + subb #0x88 ; ON? + jsr LA5C9 ; do SN error if not + incb ; now B is 1 - cassette sound source + bsr LA9A2 ; set MUX input to tape + bra LA976 ; enable sound +; Set MUX source to value in B +LA9A2 ldu #PIA0+1 ; point to PIA0 control register A + bsr LA9A7 ; program bit 0 then fall through for bit 1 +LA9A7 lda ,u ; get control register value + anda #0xf7 ; reset mux control bit + asrb ; shift desired value to C + bcc LA9B0 ; brif this bit is clear + ora #8 ; set the bit +LA9B0 sta ,u++ ; set register value and move to next register + rts +; IRQ service routine +BIRQSV lda PIA0+3 ; check for VSYNC interrupt + bpl LA9C5 ; brif not - return. BUG: should clear HSYNC interrupt status first + lda PIA0+2 ; clear VSYNC interrupt status +LA9BB ldx >SNDDUR ; are we counting down for SOUND? (force extended in case DP is modified) + beq LA9C5 ; brif not + leax -1,x ; count down one tick + stx >SNDDUR ; save new count (forced extended in case DP is modified) +LA9C5 rti +; JOYSTK function +JOYSTK jsr LB70E ; evaluate which joystick axis is desired + cmpb #3 ; valid axis? + lbhi LB44A ; brif not + tstb ; want axis 0? + bne LA9D4 ; brif not + bsr GETJOY ; read axis data if axis 0 +LA9D4 ldx #POTVAL ; point to axis values + ldb FPA0+3 ; get desired axis + ldb b,x ; get axis value + jmp LB4F3 ; return value +; Read all four joystick axes. Note that this routine will try 10 times to get a value that matches +; the value obtained during the *previous call to this routine*. Thus, if the axis value has changed, +; this routine will do the read *ten times* before just returning the last value. This is assininely +; slow and probably a bug since it seems more logical to look for two matching reads in a row. Note +; also that this routine should be using PSHS and PULS but it doesn't. +GETJOY bsr LA974 ; turn off sound + ldx #POTVAL+4 ; point to the end of the axis data (we'll work backwards) + ldb #3 ; start with axis 3 +LA9E5 lda #10 ; 10 tries to see if we match *the last call* to this routine + std ,--s ; save retry counter and axis number + bsr LA9A2 ; set MUX for the correct axis +LA9EB ldd #0x4080 ; set initial trial value to mid range and the next difference to add/subtract to half +LA9EE sta ,-s ; store the add/subtract value + orb #2 ; keep rs232 output marking + stb PIA1 ; set DAC output to the trial value + eorb #2 ; remove RS232 output value + lda PIA0 ; read the comparator + bmi LA9FF ; brif comparator output is high (DAC is lower than the axis value) + subb ,s ; subtract next bit value (split the difference toward 0) + skip2 +LA9FF addb ,s ; add next bit value (split the different toward infinity) + lda ,s+ ; get bit value back + lsra ; cut in half + cmpa #1 ; have we done that last value for the DAC? + bne LA9EE ; brif not + lsrb ; normalize the axis value + lsrb + cmpb -1,x ; does it match the read from the last call to this routine? + beq LAA12 ; brif so + dec ,s ; are we out of retries? + bne LA9EB ; brif not - try again +LAA12 stb ,-x ; save new value and move pointer back + ldd ,s++ ; get axis counter and clean up retry counter + decb ; move to next axis + bpl LA9E5 ; brif still more axes to do + rts +; This is the "bottom half" of the character fetching routines. +BROMHK cmpa #'9+1 ; is it >= colon? + bhs LAA28 ; brif so Z set if colon, C clear. + cmpa #0x20 ; space? + bne LAA24 ; brif not + jmp GETNCH ; move on to another character if space +LAA24 suba #'0 ; normalize ascii digit to 0-9; we already handled above digit 9 + suba #-'0 ; this will cause a carry for any value that was already positive +LAA28 rts +; Jump table for functions +LAA29 fdb SGN ; SGN 0x80 + fdb INT ; INT 0x81 + fdb ABS ; ABS 0x82 + fdb USRJMP ; USR 0x83 + fdb RND ; RND 0x84 + fdb SIN ; SIN 0x85 + fdb PEEK ; PEEK 0x86 + fdb LEN ; LEN 0x87 + fdb STR ; STR$ 0x88 + fdb VAL ; VAL 0x89 + fdb ASC ; ASC 0x8a + fdb CHR ; CHR$ 0x8b + fdb EOF ; EOF 0x8c + fdb JOYSTK ; JOYSTK 0x8d + fdb LEFT ; LEFT$ 0x8e + fdb RIGHT ; RIGHT$ 0x8f + fdb MID ; MID$ 0x90 + fdb POINT ; POINT 0x91 + fdb INKEY ; INKEY$ 0x92 + fdb MEM ; MEM 0x93 +; Operator precedence and jump table (binary ops except relational) +LAA51 fcb 0x79 ; + + fdb LB9C5 + fcb 0x79 ; - + fdb LB9BC + fcb 0x7b ; * + fdb LBACC + fcb 0x7b ; / + fdb LBB91 + fcb 0x7f ; ^ (exponentiation) + fdb EXPJMP + fcb 0x50 ; AND + fdb LB2D5 + fcb 0x46 ; OR + fdb LB2D4 +; Reserved words table for commands +LAA66 fcs 'FOR' ; 0x80 + fcs 'GO' ; 0x81 + fcs 'REM' ; 0x82 + fcs "'" ; 0x83 + fcs 'ELSE' ; 0x84 + fcs 'IF' ; 0x85 + fcs 'DATA' ; 0x86 + fcs 'PRINT' ; 0x87 + fcs 'ON' ; 0x88 + fcs 'INPUT' ; 0x89 + fcs 'END' ; 0x8a + fcs 'NEXT' ; 0x8b + fcs 'DIM' ; 0x8c + fcs 'READ' ; 0x8d + fcs 'RUN' ; 0x8e + fcs 'RESTORE' ; 0x8f + fcs 'RETURN' ; 0x90 + fcs 'STOP' ; 0x91 + fcs 'POKE' ; 0x92 + fcs 'CONT' ; 0x93 + fcs 'LIST' ; 0x94 + fcs 'CLEAR' ; 0x95 + fcs 'NEW' ; 0x96 + fcs 'CLOAD' ; 0x97 + fcs 'CSAVE' ; 0x98 + fcs 'OPEN' ; 0x99 + fcs 'CLOSE' ; 0x9a + fcs 'LLIST' ; 0x9b + fcs 'SET' ; 0x9c + fcs 'RESET' ; 0x9d + fcs 'CLS' ; 0x9e + fcs 'MOTOR' ; 0x9f + fcs 'SOUND' ; 0xa0 + fcs 'AUDIO' ; 0xa1 + fcs 'EXEC' ; 0xa2 + fcs 'SKIPF' ; 0xa3 + fcs 'TAB(' ; 0xa4 + fcs 'TO' ; 0xa5 + fcs 'SUB' ; 0xa6 + fcs 'THEN' ; 0xa7 + fcs 'NOT' ; 0xa8 + fcs 'STEP' ; 0xa9 + fcs 'OFF' ; 0xaa + fcs '+' ; 0xab + fcs '-' ; 0xac + fcs '*' ; 0xad + fcs '/' ; 0xae + fcs '^' ; 0xaf + fcs 'AND' ; 0xb0 + fcs 'OR' ; 0xb1 + fcs '>' ; 0xb2 + fcs '=' ; 0xb3 + fcs '<' ; 0xb4 +; Reserved word list for functions +LAB1A fcs 'SGN' ; 0x80 + fcs 'INT' ; 0x81 + fcs 'ABS' ; 0x82 + fcs 'USR' ; 0x83 + fcs 'RND' ; 0x84 + fcs 'SIN' ; 0x85 + fcs 'PEEK' ; 0x86 + fcs 'LEN' ; 0x87 + fcs 'STR$' ; 0x88 + fcs 'VAL' ; 0x89 + fcs 'ASC' ; 0x8a + fcs 'CHR$' ; 0x8b + fcs 'EOF' ; 0x8c + fcs 'JOYSTK' ; 0x8d + fcs 'LEFT$' ; 0x8e + fcs 'RIGHT$' ; 0x8f + fcs 'MID$' ; 0x90 + fcs 'POINT' ; 0x91 + fcs 'INKEY$' ; 0x92 + fcs 'MEM' ; 0x93 +; Jump table for commands +LAB67 fdb FOR ; 0x80 FOR + fdb GO ; 0x81 GO + fdb REM ; 0x82 REM + fdb REM ; 0x83 ' + fdb REM ; 0x84 ELSE + fdb IFTOK ; 0x85 IF + fdb DATA ; 0x86 DATA + fdb PRINT ; 0x87 PRINT + fdb ON ; 0x88 ON + fdb INPUT ; 0x89 INPUT + fdb ENDTOK ; 0x8a END + fdb NEXT ; 0x8b NEXT + fdb DIM ; 0x8c DIM + fdb READ ; 0x8d READ + fdb RUN ; 0x8e RUN + fdb RESTOR ; 0x8f RESTORE + fdb RETURN ; 0x90 RETURN + fdb STOP ; 0x91 STOP + fdb POKE ; 0x92 POKE + fdb CONT ; 0x93 CONT + fdb LIST ; 0x94 LIST + fdb CLEAR ; 0x95 CLEAR + fdb NEW ; 0x96 NEW + fdb CLOAD ; 0x97 CLOAD + fdb CSAVE ; 0x98 CSAVE + fdb OPEN ; 0x99 OPEN + fdb CLOSE ; 0x9a CLOSE + fdb LLIST ; 0x9b LLIST + fdb SET ; 0x9c SET + fdb RESET ; 0x9d RESET + fdb CLS ; 0x9e CLS + fdb MOTOR ; 0x9f MOTOR + fdb SOUND ; 0xa0 SOUND + fdb AUDIO ; 0xa1 AUDIO + fdb EXEC ; 0xa2 EXEC + fdb SKIPF ; 0xa3 SKIPF +; Error message table +LABAF fcc 'NF' ; 0 NEXT without FOR + fcc 'SN' ; 1 Syntax error + fcc 'RG' ; 2 RETURN without GOSUB + fcc 'OD' ; 3 Out of data + fcc 'FC' ; 4 Illegal function call + fcc 'OV' ; 5 Overflow + fcc 'OM' ; 6 Out of memory + fcc 'UL' ; 7 Undefined line number + fcc 'BS' ; 8 Bad subscript + fcc 'DD' ; 9 Redimensioned array + fcc '/0' ; 10 Division by 0 + fcc 'ID' ; 11 Illegal direct statement + fcc 'TM' ; 12 Type mismatch + fcc 'OS' ; 13 Out of string space + fcc 'LS' ; 14 String too long + fcc 'ST' ; 15 String formula too complex + fcc 'CN' ; 16 Can't continue + fcc 'FD' ; 17 Bad file data + fcc 'AO' ; 18 File already open + fcc 'DN' ; 19 Device number error + fcc 'IO' ; 20 Input/output error + fcc 'FM' ; 21 Bad file mode + fcc 'NO' ; 22 File not open + fcc 'IE' ; 23 Input past end of file + fcc 'DS' ; 24 Direct statement in file +LABE1 fcn ' ERROR' +LABE8 fcn ' IN ' +LABED fcb 0x0d +LABEE fcc 'OK' + fcb 0x0d,0x00 +LABF2 fcb 0x0d + fcn 'BREAK' +; Search stack for a FOR/NEXT stack frame. Stop search when something that isn't a FOR/NEXT +; stack frame is found. Enter with the index variable descriptor pointer in VARDES, or NULL +; for the first match. +; +; NOTE: this routine can be reworked to avoid needed two temporaries by taking advantage of the +; 6809's registers. This requires some minor tweaks where the routine is called. Further, the +; use of B is completely pointless and, even if B is going to be used, why is it reloaded on +; every loop? +LABF9 leax 4,s ; skip past our caller and the main command loop return address +LABFB ldb #18 ; each FOR/NEXT frame is 18 bytes + stx TEMPTR ; save current search pointer + lda ,x ; get first byte of this frame + suba #0x80 ; set to 0 if FOR/NEXT + bne LAC1A ; brif not FOR/NEXT (we hit the end of the stack for a GOSUB frame) + ldx 1,x ; get index variable descriptor + stx TMPTR1 ; save it + ldx VARDES ; get desired index descriptor + beq LAC16 ; brif NULL - we found something + cmpx TMPTR1 ; does this one match? + beq LAC1A ; brif so + ldx TEMPTR ; get back frame pointer + abx ; move to next entry + bra LABFB ; check next block of data +LAC16 ldx TMPTR1 ; get index variable of this frame + stx VARDES ; set it as the one found +LAC1A ldx TEMPTR ; get matching frame pointer + tsta ; set Z if FOR/NEXT + rts +; This is a block copy routine which copies from top to bottom. It's not clear that the use of +; this routine actually saves any ROM space compared to just implementing the copies directly +; once all the marshalling to set up the parameter variables is taken into account. +LAC1E bsr LAC37 ; check to see if stack collides with D +LAC20 ldu V41 ; point to destination + leau 1,u ; offset for pre-dec + ldx V43 ; point to source + leax 1,x ; offset for pre-dec +LAC28 lda ,-x ; get source byte + pshu a ; store at destination (sta ,-u would be less weird) + cmpx V47 ; at the bottom of the copy? + bne LAC28 ; brif not + stu V45 ; save final destination address +LAC32 rts +; Check for 2*B (0 <= B <= 127) bytes for free memory +LAC33 clra ; zero extend + aslb ; times 2 (loses bit 7 of B) + addd ARYEND ; add to top of used memory +LAC37 addd #STKBUF ; add a fudge factor for interpreter operation + bcs LAC44 ; brif >65535! + sts BOTSTK ; get current stack pointer + cmpd BOTSTK ; is our new address above that? + blo LAC32 ; brif not - no error +LAC44 ldb #6*2 ; raise OM error +; The error servicing routine +LAC46 jsr RVEC16 ; do the RAM hook dance (ON ERROR reserved hook) +LAC49 jsr RVEC17 ; do the RAM hook dance again + jsr LA7E9 ; turn off tape + jsr LA974 ; disable sound + jsr LAD33 ; reset stack, etc. + clr DEVNUM ; reset output to screen + jsr LB95C ; do a newline + jsr LB9AF ; send a ? + ldx #LABAF ; point to error table +LAC60 abx ; offset to correct message + bsr LACA0 ; send a char from X + bsr LACA0 ; send another char from X +LAC65 ldx #LABE1-1 ; point to "ERROR" message +LAC68 jsr STRINOUT ; print ERROR message (or BREAK) + lda CURLIN ; are we in immediate mode? + inca + beq LAC73 ; brif not - go to immediate mode + jsr LBDC5 ; print "IN ****" +; This is the immediate mode loop +LAC73 jsr LB95C ; do a newline if needed +LAC76 ldx #LABEE-1 ; point to prompt (without leading CR) + jsr STRINOUT ; show prompt +LAC7C jsr LA390 ; read an input line + ldu #0xffff ; flag immediate mode + stu CURLIN + bcs LAC7C ; brif we ended on BREAK - just go for another line + tst CINBFL ; EOF? + lbne LA4BF ; brif so + stx CHARAD ; save start of input line as input pointer + jsr GETNCH ; get character from input line + beq LAC7C ; brif no input + bcs LACA5 ; brif numeric - adding or removing a line number + ldb #2*24 ; code for "direct statement in file" + tst DEVNUM ; keyboard input? + bne LAC46 ; brif not - complain about direct statement + jsr LB821 ; go tokenize the input line +LAC9D jmp LADC0 ; go execute the newly tokenized line +LACA0 lda ,x+ ; get character and advance pointer + jmp LB9B1 ; output it +LACA5 jsr LAF67 ; convert line number to binary +LACA8 ldx BINVAL ; get converted number + stx LINHDR ; put it before the line we just read + jsr LB821 ; tokenize the input line + stb TMPLOC ; save line length + bsr LAD01 ; find where the line should be in the program + bcs LACC8 ; brif the line number isn't already present + ldd V47 ; get address where the line is in the program + subd ,x ; get the difference between here and the end of the line (negative) + addd VARTAB ; subtract line length from the end of the program + std VARTAB ; save new end of program address + ldu ,x ; get start of next line +LACC0 pulu a ; get source byte (lda ,u+ would be less weird) + sta ,x+ ; move it down + cmpx VARTAB ; have we moved everything yet? + bne LACC0 ; brif not +LACC8 lda LINBUF ; see if there is actually a line to input + beq LACE9 ; brif not - we just needed to remove the line + ldd VARTAB ; get current end of program + std V43 ; set as source pointer + addb TMPLOC ; add in the length of the new line + adca #0 + std V41 ; save destination pointer + jsr LAC1E ; make sure there's enough room and then make a hole for the new line + ldu #LINHDR-2 ; point to the line (well, 4 bytes before it, incl line number and fake next line pointer) +LACDD pulu a ; get byte from new line (lda ,u+ would be less weird) + sta ,x+ ; stow it + cmpx V45 ; at the end of the hole we just made? + bne LACDD ; brif not + ldx V41 ; get save new top of program address + stx VARTAB +LACE9 bsr LAD21 ; reset variables, etc. + bsr LACEF ; adjust next line pointers + bra LAC7C ; go read another input line +; Recompute next line pointers +LACEF ldx TXTTAB ; point to start of program +LACF1 ldd ,x ; get address of next line + beq LAD16 ; brif end of program + leau 4,x ; move past pointer and line number +LACF7 lda ,u+ ; are we at the end of the line? + bne LACF7 ; brif not + stu ,x ; save new next line pointer + ldx ,x ; point to next line + bra LACF1 ; process the next line +; Find a line in the program +LAD01 ldd BINVAL ; get desired line number + ldx TXTTAB ; point to start of program +LAD05 ldu ,x ; get address of next line + beq LAD12 ; brif end of program + cmpd 2,x ; do we have a match? + bls LAD14 ; brif our search number is <= the number here + ldx ,x ; move to next line + bra LAD05 ; check another line +LAD12 orcc #1 ; set C for not found +LAD14 stx V47 ; save address of matching line *or* line just after where it would have been +LAD16 rts +; NEW command +; This routine has multiple entry points used for various "levels" of NEW +NEW bne LAD14 ; brif there was input given; should be LAD16! +LAD19 ldx TXTTAB ; point to start of program + clr ,x+ ; blank out program (with NULL next line pointer) + clr ,x+ + stx VARTAB ; save end of program +LAD21 ldx TXTTAB ; get start of program + jsr LAEBB ; put input pointer there +LAD26 ldx MEMSIZ ; reset string space + stx STRTAB + jsr RESTOR ; reset DATA pointer + ldx VARTAB ; clear out scalars and arrays + stx ARYTAB + stx ARYEND +LAD33 ldx #STRSTK ; reset the string stack + stx TEMPPT + ldx ,s ; get return address (we're going to reset the stack) + lds FRETOP ; reset the stack to top of memory + clr ,-s ; put stopper so FOR/NEXT search will actually stop here +LAD3F clr OLDPTR ; reset "CONT" state + clr OLDPTR+1 +LAD43 clr ARYDIS ; un-disable arrays + jmp ,x ; return to original caller +; FOR command +FOR lda #0x80 ; disable array parsing + sta ARYDIS + jsr LET ; assign start value to index + jsr LABF9 ; search stack for matching FOR/NEXT frame + leas 2,s ; lose return address + bne LAD59 ; brif variable not already being used + ldx TEMPTR ; get address of matched data + leas b,x ; move stack pointer to the end of it (B is set to 18 in the stack search) +LAD59 ldb #9 ; is there room for 18 bytes in memory? + jsr LAC33 + jsr LAEE8 ; get address of the end of this statement in X + ldd CURLIN ; get line number + pshs x,b,a ; save next line address and current line number + ldb #0xa5 ; make sure we have TO + jsr LB26F + jsr LB143 ; make sure we have a numeric index + jsr LB141 ; evaluate terminal condition value + ldb FP0SGN ; pack FPA0 in place + orb #0x7f + andb FPA0 + stb FPA0 + ldy #LAD7F ; where to come back to + jmp LB1EA ; stash terminal condition on the stack +LAD7F ldx #LBAC5 ; point to FP 1.0 (default step) + jsr LBC14 ; unpack it to FPA0 + jsr GETCCH ; get character after the terminal + cmpa #0xa9 ; is it STEP? + bne LAD90 ; brif not + jsr GETNCH ; eat STEP + jsr LB141 ; evaluate step condition +LAD90 jsr LBC6D ; get "status" of FPA0 + jsr LB1E6 ; stash FPA0 on the stack (for step value) + ldd VARDES ; get variable descriptor pointer + pshs d ; put that on the stack too + lda #0x80 ; flag the frame as a FOR/NEXT frame + pshs a +; Main command interpretation loop +LAD9E jsr RVEC20 ; do the RAM hook dance + andcc #0xaf ; make sure interrupts are running + bsr LADEB ; check for BREAK/pause + ldx CHARAD ; get input pointer + stx TINPTR ; save input pointer for start of line + lda ,x+ ; get current input character + beq LADB4 ; brif end of line - move to another line + cmpa #': ; end of statement? + beq LADC0 ; brif so - keep processing +LADB1 jmp LB277 ; raise a syntax error +LADB4 lda ,x++ ; get MSB of next line pointer and skip past pointer + sta ENDFLG + beq LAE15 ; brif MSB of next line address is 0 (do END) + ldd ,x+ ; get line number but only advance one + std CURLIN ; set current line number + stx CHARAD ; set input pointer to one before line text +LADC0 jsr GETNCH ; move past statement separator or to first character in line + bsr LADC6 ; process a command +LADC4 bra LAD9E ; handle next statement or line +LADC6 beq LAE40 ; return if end of statement + tsta ; is it a token? + lbpl LET ; brif not - do a LET + cmpa #0xa3 ; above SKIPF? + bhi LADDC ; brif so + ldx COMVEC+3 ; point to jump table +LADD4 lsla ; two bytes per entry (loses the token bias) + tfr a,b ; put it in B for unsigned ABX + abx + jsr GETNCH ; move past token + jmp [,x] ; transfer control to the handler (which will return to the main loop) +LADDC cmpa #0xb4 ; is it a non-executable token? + bls LADB1 ; brif so + jmp [COMVEC+13] ; transfer control to ECB command handler +; RESTORE command +RESTOR ldx TXTTAB ; point to beginning of the program + leax -1,x ; move back one (to compensate for "GETNCH") +LADE8 stx DATPTR ; save as new data pointer + rts +; BREAK check +LADEB jsr LA1C1 ; read keyboard + beq LADFA ; brif no key down +LADF0 cmpa #3 ; BREAK? + beq STOP ; brif so - do a STOP +LADF4 cmpa #0x13 ; pause (SHIFT-@)? + beq LADFB ; brif so + sta IKEYIM ; cache key for later INKEY$ so break check doesn't break INKEY$ +LADFA rts +LADFB jsr KEYIN ; read keyboard + beq LADFB ; brif no key down + bra LADF0 ; process pressed key in case BREAK or SHIFT-@ again +; END command +ENDTOK jsr LA426 ; close files + jsr GETCCH ; re-get input character + bra LAE0B +; STOP command +STOP orcc #1 ; flag "STOP" +LAE0B bne LAE40 ; brif not end of statement + ldx CHARAD ; save current input pointer + stx TINPTR +LAE11 ror ENDFLG ; save END/STOP flag (C) + leas 2,s ; lose return address +LAE15 ldx CURLIN ; get current input line (end of program comes here) + cmpx #0xffff ; immediate mode? + beq LAE22 ; brif so + stx OLDTXT ; save line where we stopped executing + ldx TINPTR ; get input pointer + stx OLDPTR ; save location where we stopped executing +LAE22 clr DEVNUM ; reset to screen/keyboard + ldx #LABF2-1 ; point to BREAK message + tst ENDFLG ; are we doing "BREAK"? + lbpl LAC73 ; brif not + jmp LAC68 ; go do the BREAK message and return to main loop +; CONT command +CONT bne LAE40 ; brif not end of statement + ldb #2*16 ; code for can't continue + ldx OLDPTR ; get saved execution pointer + lbeq LAC46 ; brif no saved pointer - raise CN error + stx CHARAD ; reset input pointer + ldx OLDTXT ; reset current line number + stx CURLIN +LAE40 rts +; CLEAR command +CLEAR beq LAE6F ; brif no argument + jsr LB3E6 ; evaluate string space size + pshs d ; save it + ldx MEMSIZ ; get memory size (top of memory) + jsr GETCCH ; is there anything after the string space size? + beq LAE5A ; brif not + jsr SYNCOMMA ; force a comma + jsr LB73D ; get top of memory value in X + leax -1,x ; move back one (top of cleared space) + cmpx TOPRAM ; is it within the memory available? + bhi LAE72 ; brif higher than top of memory - OM error +LAE5A tfr x,d ; so we can do math for checking memory usage + subd ,s++ ; subtract out string space value + bcs LAE72 ; brif less than 0 + tfr d,u ; U is bottom of cleared space + subd #STKBUF ; also account for slop space + bcs LAE72 ; brif less than 0 + subd VARTAB ; is there still room for the program? + blo LAE72 ; brif not + stu FRETOP ; set top of free memory + stx MEMSIZ ; set size of usable memory +LAE6F jmp LAD26 ; erase variables, etc. +LAE72 jmp LAC44 ; raise OM error +; RUN command +RUN jsr RVEC18 ; do the RAM hook dance + jsr LA426 ; close any open files + jsr GETCCH ; is there a line number + lbeq LAD21 ; brif no line number - start from beginning + jsr LAD26 ; clear variables, etc. + bra LAE9F ; "GOTO" the line number +; GO command (GOTO and GOSUB) +GO tfr a,b ; save TO/SUB +LAE88 jsr GETNCH ; eat the TO/SUB token + cmpb #0xa5 ; TO? + beq LAEA4 ; brif GOTO + cmpb #0xa6 ; SUB? + bne LAED7 ; brif not + ldb #3 ; room for 6 bytes? + jsr LAC33 + ldu CHARAD ; get input pointer + ldx CURLIN ; get line number + lda #0xa6 ; flag for GOSUB frame + pshs u,x,a ; set stack frame +LAE9F bsr LAEA4 ; do "GOTO" + jmp LAD9E ; go back to main loop +; Actual GOTO is here +LAEA4 jsr GETCCH ; get current input + jsr LAF67 ; convert number to binary + bsr LAEEB ; move input pointer to end of statement + leax 1,x ; point to start of next line + ldd BINVAL ; get desired line number + cmpd CURLIN ; is it beyond here? + bhi LAEB6 ; brif so + ldx TXTTAB ; start search at beginning of program +LAEB6 jsr LAD05 ; find line number + bcs LAED2 ; brif not found +LAEBB leax -1,x ; move to just before start of line + stx CHARAD ; reset input pointer +LAEBF rts +; RETURN command +RETURN bne LAEBF ; exit if argument given + lda #0xff ; set VARDES to an illegal value so we ignore FOR frames + sta VARDES + jsr LABF9 ; look for a GOSUB frame + tfr x,s ; reset stack + cmpa #0xa6-0x80 ; is it a GOSUB frame? + beq LAEDA ; brif so + ldb #2*2 ; code for RETURN without GOSUB + skip2 +LAED2 ldb #7*2 ; code for undefined line number + jmp LAC46 ; raise error +LAED7 jmp LB277 ; raise syntax error +LAEDA puls a,x,u ; get back saved line number and input pointer + stx CURLIN ; reset line number + stu CHARAD ; reset input pointer +; DATA command +DATA bsr LAEE8 ; move input pointer to end of statement + skip2 +; REM command (also ELSE) +REM bsr LAEEB ; move input pointer to end of line + stx CHARAD ; save new input pointer +LAEE7 rts +; Return end of statement (LAEE8) or line (AEEB) in X +LAEE8 ldb #': ; colon is statement terminator + skip1lda +LAEEB clrb ; make main terminator NUL + stb CHARAC ; save terminator + clrb ; end of line - always terminates + ldx CHARAD ; get input pointer +LAEF1 tfr b,a ; save secondary terminator + ldb CHARAC ; get main terminator + sta CHARAC ; save secondary +LAEF7 lda ,x ; get input character + beq LAEE7 ; brif end of line + pshs b ; save terminator + cmpa ,s+ ; does it match? + beq LAEE7 ; brif so - bail + leax 1,x ; move pointer ahead + cmpa #'" ; start of string? + beq LAEF1 ; brif so + inca ; functon token? + bne LAF0C ; brif not + leax 1,x ; skip second part of function token +LAF0C cmpa #0x85+1 ; IF? + bne LAEF7 ; brif not + inc IFCTR ; bump "IF" count + bra LAEF7 ; get check another input character +; IF command +IFTOK jsr LB141 ; evaluate condition + jsr GETCCH ; find out what's after the conditin + cmpa #0x81 ; GO? + beq LAF22 ; treat same as THEN + ldb #0xa7 ; make sure we have a THEN + jsr LB26F +LAF22 lda FP0EXP ; get true/false (false is 0) + bne LAF39 ; brif condition true + clr IFCTR ; reset IF counter +LAF28 bsr DATA ; skip over statement + tsta ; end of line? + beq LAEE7 ; brif so + jsr GETNCH ; get start of this statement + cmpa #0x84 ; ELSE? + bne LAF28 ; brif not + dec IFCTR ; is it a matching ELSE? + bpl LAF28 ; brif not - keep looking + jsr GETNCH ; eat the ELSE +LAF39 jsr GETCCH ; get current input + lbcs LAEA4 ; brif numeric - to a GOTO + jmp LADC6 ; let main loop interpret the next command +; ON command +ON jsr EVALEXPB ; evaluate index expression +LAF45 ldb #0x81 ; make sure we have "GO" + jsr LB26F + pshs a ; save TO/SUB + cmpa #0xa6 ; SUB? + beq LAF54 ; brif so + cmpa #0xa5 ; TO? +LAF52 bne LAED7 ; brif not +LAF54 dec FPA0+3 ; are we at the right index? + bne LAF5D ; brif not + puls b ; get TO/SUB token + jmp LAE88 ; go do GOTO or GOSUB +LAF5D jsr GETNCH ; munch a character + bsr LAF67 ; parse line number + cmpa #', ; is there another line following? + beq LAF54 ; brif so - see if we're there yet + puls b,pc ; clean up TO/SUB token and return - we fell through +; Parse a line number +LAF67 ldx ZERO ; initialize line number accumulator to 0 + stx BINVAL +LAF6B bcc LAFCE ; brif not numeric + suba #'0 ; adjust to actual value of digit + sta CHARAC ; save digit + ldd BINVAL ; get accumulated number + cmpa #24 ; will this overflow? + bhi LAF52 ; brif so - raise syntax error + aslb ; times 2 + rola + aslb ; times 4 + rola + addd BINVAL ; times 5 + aslb ; times 10 + rola + addb CHARAC ; add in digit + adca #0 + std BINVAL ; save new accumulated number + jsr GETNCH ; fetch next character + bra LAF6B ; process next digit +; LET command (the LET keyword requires Extended Basic) +LET jsr LB357 ; evaluate destination variable + stx VARDES ; save descriptor pointer + ldb #0xb3 ; make sure we have = + jsr LB26F + lda VALTYP ; get destination variable type + pshs a ; save it for later + jsr LB156 ; evaluate the expression to assign + puls a ; get back original variable type + rora ; put type in C + jsr LB148 ; make sure the current result matches the type + lbeq LBC33 ; bri fnumeric - copy FPA0 to variable +LAFA4 ldx FPA0+2 ; point to descriptor of replacement string + ldd FRETOP ; get bottom of string space + cmpd 2,x ; is the string already in string space? + bhs LAFBE ; brif so + cmpx VARTAB ; is the descriptor in variable space? + blo LAFBE ; brif not +LAFB1 ldb ,x ; get length of string + jsr LB50D ; allocate space for this string + ldx V4D ; get descriptor pointer back + jsr LB643 ; copy string into string space + ldx #STRDES ; point to temporary string descriptor +LAFBE stx V4D ; save descriptor pointer + jsr LB675 ; remove string from string stack if appropriate + ldu V4D ; get back replacement descriptor + ldx VARDES ; get target descriptor + pulu a,b,y ; get string length (A) and data pointer (Y) + sta ,x ; save new length + sty 2,x ; save new pointer +LAFCE rts +; READ and INPUT commands. +LAFCF fcc '?REDO' ; The ?REDO message + fcb 0x0d,0x00 +LAFD6 ldb #2*17 ; bad file data code + tst DEVNUM ; are we reading from the keyboard? + beq LAFDF ; brif so +LAFDC jmp LAC46 ; raise the error +LAFDF lda INPFLG ; are we doing INPUT? + beq LAFEA ; brif so + ldx DATTXT ; get line number where the DATA statement happened + stx CURLIN ; set current line number to that so can report the correct location + jmp LB277 ; raise a syntax error on bad data +LAFEA ldx #LAFCF-1 ; show the ?REDO if we're doing INPUT + jsr STRINOUT + ldx TINPTR ;* reset input pointer to start of statement (this will cause the + stx CHARAD ;* INPUT statement to be re-executed + rts +INPUT ldb #11*2 ; code for illegal direct statement + ldx CURLIN ; are we in immediate mode? + leax 1,x + beq LAFDC ; brif so - raise ID error + bsr LB002 ; go do the INPUT thing + clr DEVNUM ; reset device to screen/keyboard + rts +LB002 cmpa #'# ; is there a device number? + bne LB00F ; brif not + jsr LA5A5 ; parse device number + jsr LA3ED ; make sure it's valid for input + jsr SYNCOMMA ; make sure we have a comma after the device number +LB00F cmpa #'" ; is there a prompt string? + bne LB01E ; brif not + jsr LB244 ; parse the prompt string + ldb #'; ; make sure we have a semicolon after the prompt + jsr LB26F + jsr LB99F ; print the prompt +LB01E ldx #LINBUF ; point to line input buffer + clr ,x ; NUL first byte to indicate no data + tst DEVNUM ; is it keyboard input? + bne LB049 ; brif not + bsr LB02F ; read a line from the keyboard + ldb #', ; put a comma at the start of the buffer + stb ,x + bra LB049 ; go process some input +LB02F jsr LB9AF ; send a ? + jsr LB9AC ; send a space +LB035 jsr LA390 ; read input from the keyboard + bcc LB03F ; brif not BREAK + leas 4,s ; clean up stack +LB03C jmp LAE11 ; go process BREAK +LB03F ldb #2*23 ; input past end of file error code + tst CINBFL ; was it EOF? + bne LAFDC ; brif so - raise the error + rts +READ ldx DATPTR ; fetch current DATA pointer + skip1lda ; set A to nonzero (for READ) +LB049 clra ; set A to zero (for INPUT) + sta INPFLG ; record whether we're doing READ or INPUT + stx DATTMP ; save current input location +LB04E jsr LB357 ; evaluate a variable (destination of data) + stx VARDES ; save descriptor + ldx CHARAD ; save interpreter input pointer + stx BINVAL + ldx DATTMP ; get data pointer + lda ,x ; is there anything to read? + bne LB069 ; brif so + lda INPFLG ; is it INPUT? + bne LB0B9 ; brif not + jsr RVEC10 ; do the RAM hook dance + jsr LB9AF ; send a ? (so subsequent lines get ??) + bsr LB02F ; go read an input line +LB069 stx CHARAD ; save data pointer + jsr GETNCH ; fetch next data character + ldb VALTYP ; do we want a number? + beq LB098 ; brif so + ldx CHARAD ; get input pointer + sta CHARAC ; save initial character as the delimiter + cmpa #'" ; do we have a string delimiter? + beq LB08B ; brif so - use " as both delimiters + leax -1,x ; back up input if we don't have a delimiter + clra ; set delimiter to NUL (end of line) + sta CHARAC + jsr LA35F ; set up print parameters + tst PRTDEV ; is it a file type device? + bne LB08B ; brif so - use two NULs + lda #': ; use colon as one delimiter + sta CHARAC + lda #', ; and use comma as the other +LB08B sta ENDCHR ; save second terminator + jsr LB51E ; parse out the string + jsr LB249 ; move input pointer past the string + jsr LAFA4 ; assign the string to the variable + bra LB09E ; go see if there's more to read +LB098 jsr LBD12 ; parse a numeric string + jsr LBC33 ; assign the numbe to the variable +LB09E jsr GETCCH ; get current input character + beq LB0A8 ; brif end of line + cmpa #', ; check for comma + lbne LAFD6 ; brif not - we have bad data +LB0A8 ldx CHARAD ; get current data pointer + stx DATTMP ; save the data pointer + ldx BINVAL ; restore the interpreter input pointer + stx CHARAD + jsr GETCCH ; get current input from program + beq LB0D5 ; brif end of statement + jsr SYNCOMMA ; make sure there's a comma between variables + bra LB04E ; go read another item +LB0B9 stx CHARAD ; reset input pointer + jsr LAEE8 ; search for end of statement + leax 1,x ; move past end of statement + tsta ; was it end of line? + bne LB0CD ; brif not + ldb #2*3 ; code for out of data + ldu ,x++ ; get pointer to next line + beq LB10A ; brif end of program - raise OD error + ldd ,x++ ; get line number + std DATTXT ; record it for raising errors in DATA statements +LB0CD lda ,x ; do we have a DATA statement? + cmpa #0x86 + bne LB0B9 ; brif not - keep scanning + bra LB069 ; go process the input +LB0D5 ldx DATTMP ; get data pointer + ldb INPFLG ; were we doing READ? + lbne LADE8 ; brif so - save DATA pointer + lda ,x ; is there something after the input in the input buffer? + beq LB0E7 ; brif not - we consumed everything + ldx #LB0E8-1 ; print the ?EXTRA IGNORED message + jmp STRINOUT +LB0E7 rts +LB0E8 fcc '?EXTRA IGNORED' + fcb 0x0d,0x00 +; NEXT command +NEXT bne LB0FE ; brif argument given + ldx ZERO ; set to NULL descriptor pointer + bra LB101 ; go process "any index will do" +LB0FE jsr LB357 ; evaluate the variable +LB101 stx VARDES ; save the index we're looking for + jsr LABF9 ; search the stack for the matching frame + beq LB10C ; brif we found a matching frame + ldb #0 ; code for NEXT without FOR +LB10A bra LB153 ; raise the error +LB10C tfr x,s ; reset the stack to the start of the stack frame + leax 3,x ; point to the STEP value + jsr LBC14 ; copy the value to FPA0 + lda 8,s ; get step direction + sta FP0SGN ; save as sign of FPA0 + ldx VARDES ; point to index variable + jsr LB9C2 ; add (X) to FPA0 (steps the index) + jsr LBC33 ; save new value to the index + leax 9,s ; point to terminal condition + jsr LBC96 ; compare the new index value with the terminal + subb 8,s ; set B=0 if we hit the terminal (or passed it with nonzero step) + beq LB134 ; brif loop complete + ldx 14,s ; restore line number and input pointer to start of loop + stx CURLIN + ldx 16,s + stx CHARAD +LB131 jmp LAD9E ; return to interpretation loop +LB134 leas 18,s ; remove the frame from the stack + jsr GETCCH ; get character after the index + cmpa #', ; do we have more indexes? + bne LB131 ; brif not + jsr GETNCH ; munch the comma + bsr LB0FE ; go process another value +; NOTE: despite the BSR on the preceding line, execution of the NEXT command will not fall +; through this point, nor will the stack grow without bound. The BSR is required to make sure +; the stack is aligned properly for the stack search for the subsequent index variable. +; +; The following is the expression evaluation system. It has various entry points including for type +; checking. This really consists of two co-routines, one for evaluating operators and one for individual +; terms. However, it does some rather confusing stack operations so it's a bit difficult to follow +; just how some of this works. +; +; Evaluate numeric expression +LB141 bsr LB156 ; evaluate an expression +; TM error if string +LB143 andcc #0xfe ; clear C to indicate we want a number + skip2keepc +; TM error if numeric +LB146 orcc #1 ; set C to indicate we want a string +; TM error if: C = 1 and number, OR C = 0 and string +LB148 tst VALTYP ; set flags on the current value to (doesn't change C) + bcs LB14F ; brif we want a string + bpl LB0E7 ; brif we have a number (we want a number) + skip2 +LB14F bmi LB0E7 ; brif we have a string (we want a string) +LB151 ldb #12*2 ; code for TM error +LB153 jmp LAC46 ; raise the error +; The general expression evaluation entry point +LB156 bsr LB1C6 ; back up input pointer to compensate for GETNCH below +LB158 clra ; set operator precedence to 0 (no previous operator) + skip2 +LB15A pshs b ; save relational operator flags + pshs a ; save previous operator precedence + ldb #1 ; make sure we aren't overflowing the stack + jsr LAC33 + jsr LB223 ; go evaluate the first term +LB166 clr TRELFL ; flag no relational operators seen +LB168 jsr GETCCH ; get input character +LB16A suba #0xb2 ; token for > (lowest relational operator) + blo LB181 ; brif below relational operators + cmpa #3 ; there are three relational operators, is it one? + bhs LB181 ; brif not + cmpa #1 ; set C if > + rola ; shift C into bit 0 (4: <, 2: =, 1: >) + eora TRELFL ; flip the bit for this operator + cmpa TRELFL ; did the result get lower? + blo LB1DF ; brif so - we have a duplicate so raise an error + sta TRELFL ; save new operator flags + jsr GETNCH ; munch the operator + bra LB16A ; go see if we have another one +LB181 ldb TRELFL ; do we have a relational comparison? + bne LB1B8 ; brif so + lbcc LB1F4 ; brif the token is above the relational operators + adda #7 ; put operators starting at 0 + bhs LB1F4 ; brif we're above 0 - it's an operator, Jim + adca VALTYP ; add carry, numeric flag, and modified token number + lbeq LB60F ; brif we have string and A is + - do concatenation + adca #-1 ; restore operator number + pshs a ; save operator number + asla ; times 2 + adda ,s+ ; and times 3 (3 bytes per entry) + ldx #LAA51 ; point to operator pecedence and jump table + leax a,x ; point to correct entry +LB19F puls a ; get precedence of previous operation + cmpa ,x ; is hit higher (or same) than the current one? + bhs LB1FA ; brif so - we need to process that operator + bsr LB143 ; TM error if we have a string +LB1A7 pshs a ; save previous operation precedence + bsr LB1D4 ; push operator handler address and FPA0 onto the stack + ldx RELPTR ; get pointer to arithmetic/logical table entry for last operation + puls a ; get back precedence + bne LB1CE ; brif we had a relational operation + tsta ; check precedence of previous operation + lbeq LB220 ; brif end of expression + bra LB203 ; go handle operation +LB1B8 asl VALTYP ; get type of value to C + rolb ; mix it in to bit 0 of relational flags + bsr LB1C6 ; back up input pointer + ldx #LB1CB ; point to relational operator precedence and handler + stb TRELFL ; save relational comparison flags + clr VALTYP ; result will be numeric + bra LB19F ; to process the operation +LB1C6 ldx CHARAD ; get input pointer + jmp LAEBB ; back it up one and put it back +LB1CB fcb 0x64 ; precedence of relational comparison + fdb LB2F4 ; handler address for relational comparison +LB1CE cmpa ,x ; is last done operation higher (or same) precedence? + bhs LB203 ; brif so - go process it + bra LB1A7 ; go push things on the stack and process this operation otherwise +LB1D4 ldd 1,x ; get address of operatorroutine + pshs d ; save it + bsr LB1E2 ; push FPA0 onto the stack + ldb TRELFL ; get back relational operator flags + lbra LB15A ; go evaluate another operation +LB1DF jmp LB277 ; raise a syntax error +LB1E2 ldb FP0SGN ; get sign of FPA0 + lda ,x ; get precedence of this operation +LB1E6 puls y ; get back original caller + pshs b ; save sign +LB1EA ldb FP0EXP ; get exponent + ldx FPA0 ; get mantissa + ldu FPA0+2 + pshs u,x,b ; stow FPA0 sign and mantissa + jmp ,y ; return to caller +LB1F4 ldx ZERO ; point to dummy value + lda ,s+ ; get precedence of previous operation (and set flags) + beq LB220 ; brif end of expression +LB1FA cmpa #0x64 ; relational operation? + beq LB201 ; brif so + jsr LB143 ; type mismatch if string +LB201 stx RELPTR ; save pointer to operator routine +LB203 puls b ; get relational flags + cmpa #0x5a ; NOT operation? + beq LB222 ; brif so (it was unary) + cmpa #0x7d ; unary negation? + beq LB222 ; brif so + lsrb ; shift value type flag out of relational flags + stb RELFLG ; save relational operator flag + puls a,x,u ; get FP value back + sta FP1EXP ; set exponent and mantissa in FPA1 + stx FPA1 + stu FPA1+2 + puls b ; and the sign + stb FP1SGN + eorb FP0SGN ; set RESSGN if the two operand signs differ + stb RESSGN +LB220 ldb FP0EXP ; get exponent of FPA0 +LB222 rts ; return or transfer control to operator handler routine +LB223 jsr RVEC15 ; do the RAM hook dance + clr VALTYP ; set type to numeric + jsr GETNCH ; get first character in the term + bcc LB22F ; brif not numeric +LB22C jmp LBD12 ; parse a number (and return) +LB22F jsr LB3A2 ; set carry if not alpha + bcc LB284 ; brif alpha character (variable) + cmpa #'. ; decimal point? + beq LB22C ; brif so - evaluate number + cmpa #0xac ; minus? + beq LB27C ; brif so - process unary negation + cmpa #0xab ; plus? + beq LB223 ; brif so - ignore unary "posation" + cmpa #'" ; string delimiter? + bne LB24E ; brif not +LB244 ldx CHARAD ; get input pointer + jsr LB518 ; go parse the string +LB249 ldx COEFPT ; get address of end of string + stx CHARAD ; move input pointer past string + rts +LB24E cmpa #0xa8 ; NOT? + bne LB25F ; brif not + lda #0x5a ; precedence of unary NOT + jsr LB15A ; process the operand of NOT + jsr INTCNV ; convert to integer in D + coma ; do a bitwise complement + comb + jmp GIVABF ; resturn the result +LB25F inca ; is it a function token? + beq LB290 ; brif so +LB262 bsr LB26A ; only other legal thing must be a (expr) + jsr LB156 ; evaluate parentheticized expression +LB267 ldb #') ; force a ) + skip2 +LB26A ldb #'( ; force a ( + skip2 +SYNCOMMA ldb #', ; force a , +LB26F cmpb [CHARAD] ; does character match? + bne LB277 ; brif not + jmp GETNCH ; each the character and return the next +LB277 ldb #2*1 ; raise syntax error + jmp LAC46 +LB27C lda #0x7d ; unary negation precedence + jsr LB15A ; evaluate argument + jmp LBEE9 ; flip sign of FPA0 and return +LB284 jsr LB357 ; evaluate variable +LB287 stx FPA0+2 ; save descriptor address in FPA0 + lda VALTYP ; test variable type + bne LB222 ; brif string - we're done + jmp LBC14 ; copy FP number from (X) into FPA0 +LB290 jsr GETNCH ; get the actual token number + tfr a,b ; save it (for offsetting X) + lslb ; two bytes per jump table entry (and lose high bit) + jsr GETNCH ; eat the token byte + cmpb #2*19 ; is it a valid token for Color Basic? + bls LB29F ; brif so + jmp [COMVEC+18] ; transfer control to Extended Basic if not +LB29F pshs b ; save jump table offset + cmpb #2*14 ; does it expect a numeric argument? + blo LB2C7 ; brif so + cmpb #2*18 ; does it need no arguments? + bhs LB2C9 ; brif so + bsr LB26A ; force a ( + lda ,s ; get token value + cmpa #2*17 ; is it POINT? + bhs LB2C9 ; brif so + jsr LB156 ; evaluate first argument string + bsr SYNCOMMA ; force a comma + jsr LB146 ; TM error if string + puls a ; get token value + ldu FPA0+2 ; get string descriptor + pshs u,a ; now we save the first string argument and the token value + jsr EVALEXPB ; evaluate first numeric argument + puls a ; get back token value + pshs b,a ; save second argument and token value + fcb 0x8e ; opcode of LDX immediate (skips two bytes) +LB2C7 bsr LB262 ; force a ( +LB2C9 puls b ; get offset + ldx COMVEC+8 ; get jump table pointer +LB2CE abx ; add offset into table + jsr [,x] ; go process function + jmp LB143 ; make sure result is numeric +; operator OR +LB2D4 skip1lda ; set flag to nonzero to signal OR +; operator AND +LB2D5 clra ; set flag to zero to signal AND + sta TMPLOC ; save AND/OR flag + jsr INTCNV ; convert second argument to intenger + std CHARAC ; save it + jsr LBC4A ; move first argument to FPA0 + jsr INTCNV ; convert first argument to integer + tst TMPLOC ; is it AND or OR? + bne LB2ED ; brif OR + anda CHARAC ; do the bitwise AND + andb ENDCHR + bra LB2F1 ; finish up +LB2ED ora CHARAC ; do the bitwise OR + orb ENDCHR +LB2F1 jmp GIVABF ; return integer result +; relational comparision operators +LB2F4 jsr LB148 ; TM error if type mismatch + BNE LB309 ; brif we have a string comparison + lda FP1SGN ; pack FPA1 + ora #0x7f + anda FPA1 + sta FPA1 + ldx #FP1EXP ; point to packed FPA1 + jsr LBC96 ; compare FPA0 to FPA1 + bra LB33F ; handle truth comparison +LB309 clr VALTYP ; the result of a comparison is always a number + dec TRELFL ; remove the string flag from the comparison data + jsr LB657 ; get string details for second argument + stb STRDES ; save them in the temporary string descriptor + stx STRDES+2 + ldx FPA1+2 ; get pointer to first argument descriptor + jsr LB659 ; get string details for second argument + lda STRDES ; get length of second argument + pshs b ; save length of first argument + suba ,s+ ; now A is the difference in string lengths + beq LB328 ; brif string lengths are equal + lda #1 ; flag for second argument is longer than first + bcc LB328 ; brif second string is longer than first + ldb STRDES ; get length of second string (shorter) + nega ; invert default comparison result +LB328 sta FP0SGN ; save default truth flag + ldu STRDES+2 ; get pointer to start of second string + incb ; compensate for DECB +LB32D decb ; have we compared everything? + bne LB334 ; brif not + ldb FP0SGN ; get default truth value + bra LB33F ; decide comparison truth +LB334 lda ,x+ ; get byte from first argument + cmpa ,u+ ; compare with second argument + beq LB32D ; brif equal - keep comparing + ldb #0xff ; negative if first string is > second + bcc LB33F ; brif string A > string B + negb ; invert result +LB33F addb #1 ; convert to 0,1,2 + rolb ; shift left - now it's 4,2,1 for <, =, > + andb RELFLG ; keep only the truth we care about + beq LB348 ; brif no matching bits - it's false + ldb #0xff ; set true +LB348 jmp LBC7C ; convert result to FP and return it +; DIM command +LB34B jsr SYNCOMMA ; make sure there's a comma between variables +DIM ldb #1 ; flag that we're dimensioning + bsr LB35A ; go allocate the variable + jsr GETCCH ; are we done? + bne LB34B ; brif not + rts +; This routine parses a variable. For scalars, it will return a NULL string or 0 value number +; if it is called from "evaluate term". Otherwise, it allocates the variable. For arrays, it will +; allocate a default sized array if dimensioning is not underway and then attempt to look up +; the requested coordinates in that array. Otherwise, it will allocate an array based on the +; specified dimension values. +LB357 clrb ; flag that we're not setting up an array + jsr GETCCH +LB35A stb DIMFLG ; save dimensioning flag +LB35C sta VARNAM ; save first character of variable name + jsr GETCCH ; get input character (why? we already have it) + bsr LB3A2 ; set carry if not alpha + lbcs LB277 ; brif our variable doesn't start with a letter + clrb ; default second variable character to NUL + stb VALTYP ; set value type to numeric + jsr GETNCH ; get second character + bcs LB371 ; brif numeric - numbers are allowed + bsr LB3A2 ; set carry if not alpha + bcs LB37B ; brif not alpha +LB371 tfr a,b ; save set second character of variable name +LB373 jsr GETNCH ; get an input character + bcs LB373 ; brif numeric - still in variable name + bsr LB3A2 ; set carry if not alpha + bcc LB373 ; brif alpha - still in variable name +LB37B cmpa #'$ ; do we have the string sigil? + bne LB385 ; brif not + com VALTYP ; set value type to string + addb #0x80 ; set bit 7 of second variable character to indicate string + jsr GETNCH ; eat the sigil +LB385 stb VARNAM+1 ; save second variable name character + ora ARYDIS ; merge array disable flag (will set bit 7 of input character if no arrays) + suba #'( ; do we have a subscript? + lbeq LB404 ; brif so + clr ARYDIS ; disable the array disable flag - it's single use + ldx VARTAB ; point to the start of the variable table + ldd VARNAM ; get variable name +LB395 cmpx ARYTAB ; are we at the top of the variable table? + beq LB3AB ; brif so + cmpd ,x++ ; does the variable name match (and move pointer to variable data) + beq LB3DC ; brif so + leax 5,x ; move to next table entry + bra LB395 ; see if we have a match +; Set carry if not upper case alpha +LB3A2 cmpa #'A ; set C if less than A + bcs LB3AA ; brif less than A + suba #'Z+1 ; set C if greater than Z + suba #-('Z+1) +LB3AA rts +LB3AB ldx #ZERO ; point to empty location (NULL/0 value) + ldu ,s ; get caller address + cmpu #LB287 ; coming from "evaluate term"? + beq LB3DE ; brif so - don't allocate + ldd ARYEND ; get end of arrays + std V43 ; save as top of source block + addd #7 ; 7 bytes per scalar entry + std V41 ; save as top of destination block + ldx ARYTAB ; get bottom of arrays + stx V47 ; save as bottom of source block + jsr LAC1E ; move the arrays up to make a hole + ldx V41 ; get new top of arrays + stx ARYEND ; set new end of arrays + ldx V45 ; get bottom of destination block + stx ARYTAB ; set as new start of arrays + ldx V47 ; get old end of variables + ldd VARNAM ; get name of variable + std ,x++ ; set variable name and advance X to the value + clra ; zero out the variable value + clrb + std ,x + std 2,x + sta 4,x +LB3DC stx VARPTR ; save descriptor address of return value +LB3DE rts +; Various integer conversion routines +LB3DF fcb 0x90,0x80,0x00,0x00,0x00 ; FP constant -32768 +LB3E4 jsr GETNCH ; fetch input character +LB3E6 jsr LB141 ; evaluate numeric expression +LB3E9 lda FP0SGN ; get sign of value + bmi LB44A ; brif negative (raise FC error) +INTCNV jsr LB143 ; TM error if string + lda FP0EXP ; get exponent + cmpa #0x90 ; is it within the range for a 16 bit integer? + blo LB3FE ; brif smaller than 32768 + ldx #LB3DF ; point to -32678 constant + jsr LBC96 ; is FPA0 equal to -32768? + bne LB44A ; brif not - magnitude is too far negative +LB3FE jsr LBCC8 ; move binary point to the right of FPA0 and correct sign + ldd FPA0+2 ; get the resulting integer + rts +LB404 ldd DIMFLG ; get dimensioning flag and variable type + pshs b,a ; save them (to avoid issues while evaluating dimension values) + nop ; dead space caused by 1.2 revision + clrb ; reset dimension counter +LB40A ldx VARNAM ; get variable name + pshs x,b ; save dimension counter and variable name + bsr LB3E4 ; evaluate a dimension value (and skip either ( or ,) + puls b,x,y ; get variable name, dimension counter, and dimensioning/type flag + stx VARNAM ; restore variable name + ldu FPA0+2 ; get dimension size/index + pshs u,y ; save dimension size and dimensioning/type flag + incb ; bump dimension counter + jsr GETCCH ; get what's after the dimension count + cmpa #', ; do we have another dimension? + beq LB40A ; brif so - parse it + stb TMPLOC ; save dimension counter + jsr LB267 ; make sure we have a ) + puls a,b ; get back variable type and dimensioning flag + std DIMFLG ; restore variable type and dimensioning flag + ldx ARYTAB ; get start of arrays +LB42A cmpx ARYEND ; are we at the end of the array table + beq LB44F ; brif so + ldd VARNAM ; get variable name + cmpd ,x ; does it match? + beq LB43B ; brif so + ldd 2,x ; get length of this array + leax d,x ; move to next array + bra LB42A ; go check another entry +LB43B ldb #2*9 ; code for redimensioned array error + lda DIMFLG ; are we dimensioning? + bne LB44C ; brif so - raise error + ldb TMPLOC ; get number of dimensions given + cmpb 4,x ; does it match? + beq LB4A0 ; brif so +LB447 ldb #8*2 ; raise "bad subscript" + skip2 +LB44A ldb #4*2 ; raise "illegal function call" +LB44C jmp LAC46 ; raise error +LB44F ldd #5 ; 5 bytes per array entry + std COEFPT ; initialize array size to entry size + ldd VARNAM ; get variable name + std ,x ; set array name + ldb TMPLOC ; get dimension count + stb 4,x ; set dimension count + jsr LAC33 ; make sure we haven't overflowed memory + stx V41 ; save array descriptor address +LB461 ldb #11 ; default dimension value (zero-based, gives max index of 10) + clra ; zero extend (??? why not LDD above?) + tst DIMFLG ; are we dimensioning? + beq LB46D ; brif not + puls a,b ; get dimension size + addd #1 ; account for zero based indexing +LB46D std 5,x ; save dimension size + bsr LB4CE ; multiply by accumulated array size + std COEFPT ; save new array size + leax 2,x ; move to next dimension + dec TMPLOC ; have we done all dimensions? + bne LB461 ; brif not + stx TEMPTR ; save end of array descriptor (minus 5) + addd TEMPTR ; add total size of array to address of descriptor + lbcs LAC44 ; brif it overflows memory + tfr d,x ; save end of array for later + jsr LAC37 ; does array fit in memory? + subd #STKBUF-5 ; subtract out the "stack fudge factor" but add 5 to the result + std ARYEND ; save new end of arrays + clra ; set up for clearing +LB48C leax -1,x ; move back one + sta 5,x ; blank out a byte in the array data + cmpx TEMPTR ; have we reached the array header? + bne LB48C ; brif not + ldx V41 ; get address of start of descriptor + lda ARYEND ; get MSB of end of array back (B still has LSB) + subd V41 ; subtract start of descriptor + std 2,x ; save length of array in array header + lda DIMFLG ; are we dimensioning? + bne LB4CD ; brif so - we're done +LB4A0 ldb 4,x ; get number of dimensions + stb TMPLOC ; initialize counter + clra ; initialize accumulated offset + clrb +LB4A6 std COEFPT ; save accumulated offset + puls a,b ; get desired index + std FPA0+2 ; save it + cmpd 5,x ; is it in range for this dimension? + bhs LB4EB ; brif not + ldu COEFPT ; get accumulated offset + beq LB4B9 ; brif first dimension + bsr LB4CE ; multiply accumulated offset by dimension length + addd FPA0+2 ; add in offset into this dimension +LB4B9 leax 2,x ; move to next dimension in header + dec TMPLOC ; done all dimensions? + bne LB4A6 ; brif not + std ,--s ; save D for multiply by 5 (should be pshs d) + aslb ; times 2 + rola + aslb ; times 4 + rola + addd ,s++ ; times 5 + leax d,x ; add in offset from start of array data + leax 5,x ; offset to end of header + stx VARPTR ; save pointer to element data +LB4CD rts +; multiply 16 bit number at 5,x by the 16 bit number in COEFPT; return result in D; BS error if carry +LB4CE lda #16 ; 16 shifts to do a multiply + sta V45 ; save shift counter + ldd 5,x ; get multiplier + std BOTSTK ; save it + clra ; zero out product + clrb +LB4D8 aslb ; shift product left + rola + bcs LB4EB ; brif we have a carry + asl COEFPT+1 ; shift other factor left + rol COEFPT + bcc LB4E6 ; brif no carry - this bit position is 0 + addd BOTSTK ; add in multiplier at this bit position + bcs LB4EB ; brif carry - do an error +LB4E6 dec V45 ; have we done all 16 bits? + bne LB4D8 ; brif not + rts +LB4EB jmp LB447 ; raise a BS error +; MEM function +; BUG: this doesn't account for the STKBUF fudge factor used for memory availability checks +MEM tfr s,d ; get stack pointer where we can do math + subd ARYEND ; calculate number of bytes between the stack and the top of arrays + skip1 ; return result +; Convert unsigned value in B to FP +LB4F3 clra ; zero extend +; Convert signed value in D to FP +GIVABF clr VALTYP ; set value type to numeric + std FPA0 ; save value in FPA0 + ldb #0x90 ; exponent for top two bytes to be an integer + jmp LBC82 ; finish conversion to integer +; STR$ function +STR jsr LB143 ; make sure we have a number + ldu #STRBUF+2 ; convert FP number to string in temporary string buffer + jsr LBDDC + leas 2,s ; don't return to the function evaluator (which will do a numeric type check) + ldx #STRBUF+1 ; point to number string + bra LB518 ; to stash the string in string space and return to the "evaluate term" caller +; Reserve B bytes of string space. Return start in X and FRESPC +LB50D stx V4D ; save X somewhere in case the caller needs it +LB50F bsr LB56D ; allocate string space +LB511 stx STRDES+2 ; save pointer to allocated space in the temporary descriptor + stb STRDES ; save length in the temporary descriptor + rts +LB516 leax -1,x ; move pointer back one (to compensate for the increment below) +; Scan from X until either NUL or one of the string terminators is found +LB518 lda #'" ; set terminator to be string delimiter +LB51A sta CHARAC ; set both delimiters + sta ENDCHR +LB51E leax 1,x ; move to next character + stx RESSGN ; save start of string + stx STRDES+2 ; save start of string in the temporary string descriptor + ldb #-1 ; initialize length counter to -1 (compensate for initial INCB) +LB526 incb ; bump string length + lda ,x+ ; get character from string + beq LB537 ; brif end of line + cmpa CHARAC ; is it delimiter #1? + beq LB533 ; brif so + cmpa ENDCHR ; is it delimiter #2? + bne LB526 ; brif not - keep scanning +LB533 cmpa #'" ; string delimiter? + beq LB539 ; brif so - don't move pointer back +LB537 leax -1,x ; move pointer back (so we don't consume the delimiter) +LB539 stx COEFPT ; save end of string address + stb STRDES ; save string length + ldu RESSGN ; get start of string + cmpu #STRBUF+2 ; is it at the start of the string buffer? + bhi LB54C ; brif so - don't copy it to string space + bsr LB50D ; allocate string space + ldx RESSGN ; point to beginning of the string + jsr LB645 ; copy string data (B bytes) from (X) to (FRESPC) +; Put temporary string descriptor on the string stack +LB54C ldx TEMPPT ; get top of string stack + cmpx #CFNBUF ; is the string stack full? + bne LB558 ; brif not + ldb #15*2 ; code for "string formula too complex" +LB555 jmp LAC46 ; raise error +LB558 lda STRDES ; get string length + sta 0,x ; save it in the string stack descriptor + ldd STRDES+2 ; get string data pointer + std 2,x ; save in string stack descriptor + lda #0xff ; set value type to string + sta VALTYP + stx LASTPT ; set pointer to last used entry on the string stack + stx FPA0+2 ; set pointer to descriptor in the current evaluation value + leax 5,x ; advance string stack pointer + stx TEMPPT + rts +; Reserve B bytes in string space. If there isn't enough space, try compacting string space and +; then try the allocation again. If it still fails, raise OS error. +LB56D clr GARBFL ; flag that compaction not yet done +LB56F clra ; zero extend the length + pshs d ; save requested string length + ldd STRTAB ; get current bottom of strings + subd ,s+ ; calculate new bottom of strings and remove zero extension + cmpd FRETOP ; does the string fit? + blo LB585 ; brif not - try compaction + std STRTAB ; save new bottom of strings + ldx STRTAB ; get bottom of strings + leax 1,x ; now X points to the real start of the allocated space + stx FRESPC ; save the string pointer + puls b,pc ; restore length and return +LB585 ldb #2*13 ; code for out of string space + com GARBFL ; have we compacted string space yet? + beq LB555 ; brif so - raise error + bsr LB591 ; compact string space + puls b ; get back string length + bra LB56F ; go try allocation again +; Compact string space +; This is an O(n^2) algorithm. It first searches all extant strings for the highest address data pointer +; that hasn't already been moved into the freshly compacted string space. If then moves that string data +; up to the highest address it can go to. It repeats this process over and over until it finds no string +; that isn't already in the compacted space. While doing this, it has to search all strings on the string +; stack (this is why the string stack is needed - so we can track anonymous strings), all scalar string +; variables, and *every* entry in every string array. +LB591 ldx MEMSIZ ; get to of string space +LB593 stx STRTAB ; save top of uncompacted stringspace + clra ; zero out D and reset pointer to discovered variable to NULL + clrb + std V4B + ldx FRETOP ; point to bottom of string space + stx V47 ; save as lowest match address (match will be higher) + ldx #STRSTK ; point to start of string stack +LB5A0 cmpx TEMPPT ; are we at the top of the string stack? + beq LB5A8 ; brif so - done with the string stack + bsr LB5D8 ; check for string in uncompacted space (and advance pointer) + bra LB5A0 ; check another on the string stack +LB5A8 ldx VARTAB ; point to start of scalar variables +LB5AA cmpx ARYTAB ; end of scalars? + beq LB5B2 ; brif so + bsr LB5D2 ; check for string in uncompacted space and advance pointer + bra LB5AA ; check another variable +LB5B2 stx V41 ; save address of end of variables (address of first array) +LB5B4 ldx V41 ; get start of the next array +LB5B6 cmpx ARYEND ; end of arrays? + beq LB5EF ; brif so + ldd 2,x ; get length of array + addd V41 ; add to start of array + std V41 ; save address of next array + lda 1,x ; get second character of variable name + bpl LB5B4 ; brif numeric + ldb 4,x ; get number of dimensions + aslb ; two bytes per dimension size + addb #5 ; add in fixed overhead for array descriptor + abx ; now X points to first array element +LB5CA cmpx V41 ; at the start of the next array? + beq LB5B6 ; brif so - go handle another array + bsr LB5D8 ; check for string in uncompacted space (and advance pointer) + bra LB5CA ; process next array element +LB5D2 lda 1,x ; get second character of variable name + leax 2,x ; move to variable data + bpl LB5EC ; brif numeric +LB5D8 ldb ,x ; get length of string + beq LB5EC ; brif NULL - don't need to check data pointer + ldd 2,x ; get data pointer + cmpd STRTAB ; is it in compacted string space? + bhi LB5EC ; brif so + cmpd V47 ; is it better match than previous best? + bls LB5EC ; brif not + stx V4B ; save descriptor address of best match + std V47 ; save new best data pointer match +LB5EC leax 5,x ; move to next descriptor +LB5EE rts +LB5EF ldx V4B ; get descriptor address of the matched string + beq LB5EE ; brif we didn't find one - we're done + clra ; zero extend length + ldb ,x ; get string length + decb ; subtract one (we won't have a NULL string here) + addd V47 ; now D points to the address of the end of the string data + std V43 ; save as top address of move + ldx STRTAB ; set top of uncompacted space as destination + stx V41 + jsr LAC20 ; move string to top of uncompactedspace + ldx V4B ; point to string descriptor + ldd V45 ; get new data pointer address + std 2,x ; update descriptor + ldx V45 ; get bottom of copy destination + leax -1,x ; move back below it + jmp LB593 ; go search for another string to move (and set new bottom of string space) +; Concatenate two strings. We come here directly from the operator handler rather than via a JSR. +LB60F ldd FPA0+2 ; get string descriptor for the first string + pshs d ; save it + jsr LB223 ; evaluate a second string (concatenation is left associative) + jsr LB146 ; make sure we have a string + puls x ; get back first string descriptor + stx RESSGN ; save it + ldb ,x ; get length of first string + ldx FPA0+2 ; get pointer to second string + addb ,x ; add length of second string + bcc LB62A ; brif combined length is OK + ldb #2*14 ; raise string too long error + jmp LAC46 +LB62A jsr LB50D ; reserve room for new string + ldx RESSGN ; get descriptor address of the first string + ldb ,x ; get length of first string + bsr LB643 ; copy it to string space + ldx V4D ; get descriptor address of second string + bsr LB659 ; get string details for second string + bsr LB645 ; copy second string into new string space + ldx RESSGN ; get pointer to first string + bsr LB659 ; remove it from the string stack if possible + jsr LB54C ; put new string on the string stack + jmp LB168 ; return to expression evaluator +; Copy B bytes to space pointed to by FRESPC +LB643 ldx 2,x ; get source address from string descriptor +LB645 ldu FRESPC ; get destination address + incb ; compensate for decb + bra LB64E ; do the copy +LB64A lda ,x+ ; copy a byte + sta ,u+ +LB64E decb ; done yet? + bne LB64A ; brif not + stu FRESPC ; save destination pointer + rts +; Fetch details of string in FPA0+2 and remove from the string stack if possible +LB654 jsr LB146 ; make sure we have a string +LB657 ldx FPA0+2 ; get descriptor pointer +LB659 ldb ,x ; get length of string + bsr LB675 ; see if it's at the top of the string stack and remove it if so + bne LB672 ; brif not removed + ldx 5+2,x ; get start address of string just removed + leax -1,x ; move pointer down 1 + cmpx STRTAB ; is it at the bottom of string space? + bne LB66F ; brif not + pshs b ; save length + addd STRTAB ; add length to start of strings (A was cleared previously) + std STRTAB ; save new string space start (deallocated space for this string) + puls b ; get back string length +LB66F leax 1,x ; restore pointer to pointing at the actual string data + rts +LB672 ldx 2,x ; get data pointer for the string + rts +; Remove string pointed to by X from the string stack if it is at the top of the stack; return with +; A clear and Z set if string removed +LB675 cmpx LASTPT ; is it at the top of the string stack? + bne LB680 ; brif not - do nothing + stx TEMPPT ; save new top of stack + leax -5,x ; move the "last" pointer back as well + stx LASTPT + clra ; flag string removed +LB680 rts +; LEN function +LEN bsr LB686 ; get string details +LB683 jmp LB4F3 ; return unsigned length in B +LB686 bsr LB654 ; get string details and remove from string stack + clr VALTYP ; set value type to numeric + tstb ; set flags according to length + rts +; CHR$ function +CHR jsr LB70E ; get 8 bit unsigned integer to B +LB68F ldb #1 ; allocate a one byte string + jsr LB56D + lda FPA0+3 ; get character code + jsr LB511 ; save reserved string details in temp descriptor + sta ,x ; put character in string +LB69B leas 2,s ; don't go back to function handler - avoid numeric type check +LB69D jmp LB54C ; return temporary string on string stack +; ASC function +ASC bsr LB6A4 ; get first character of argument + bra LB683 ; return unsigned code in B +LB6A4 bsr LB686 ; fetch string details + beq LB706 ; brif NULL string + ldb ,x ; get character at start of string + rts +; LEFT$ function +LEFT bsr LB6F5 ; get arguments from the stack +LB6AD clra ; clear pointer offset (set to start of string) +LB6AE cmpb ,x ; are we asking for more characters than there are in the string? + bls LB6B5 ; brif not + ldb ,x ; only return the number that are in the string + clra ; force starting offset to be the start of the string +LB6B5 pshs b,a ; save offset and length + jsr LB50F ; reserve space in string space + ldx V4D ; point to original string descriptor + bsr LB659 ; get string details + puls b ; get string offset + abx ; now X points to the start of the data to copy + puls b ; get length of copy + jsr LB645 ; copy the data to the allocated space + bra LB69D ; return temp string on string stack +; RIGHT$ function +RIGHT bsr LB6F5 ; get arguments from stack + suba ,x ; subtract length of original string from desired length + nega ; now A is offset into old string where we start copying + bra LB6AE ; go handle everything else +; MID$ function +MID ldb #255 ; default length is the whole string + stb FPA0+3 ; save it + jsr GETCCH ; see what we have after offset + cmpa #') ; end of function? + beq LB6DE ; brif so - no length + jsr SYNCOMMA ; force a comma + bsr EVALEXPB ; get length parameter +LB6DE bsr LB6F5 ; get string and offset parameters from the stack + beq LB706 ; brif we have a 0 offset requested (string offsets are 1-based) + clrb ; clear length counter + deca ; subtract one from position parameter (we work on 0-based, param is 1-based) + cmpa ,x ; is start greater than length of string? + bhs LB6B5 ; brif so - return NULL string + tfr a,b ; save absolute position parameter + subb ,x ; now B is postition less length + negb ; now B is amount of string to copy + cmpb FPA0+3 ; is it less than the length requested? + bls LB6B5 ; brif so + ldb FPA0+3 ; set length to the requested length + bra LB6B5 ; go finish up copying the substring +; Common routine for LEFT$, RIGHT$, MID$ - check for ) and fetch string data and first parameter +; from the stack. These were evaluated in the function evaluation handler. (It's not clear that doing +; it that way is actually beneficial. However, this is what the ROM designers did, so here we are.) +LB6F5 jsr LB267 ; make sure we have ) + ldu ,s ; get return address - we're going to mess with the stack + ldx 5,s ; get address of string descriptor + stx V4D ; save descriptor adddress + lda 4,s ; get first numeric parameter in both A and B + ldb 4,s + leas 7,s ; clean up stack + tfr u,pc ; return to original caller +LB706 jmp LB44A ; raise FC error +; Evaluate an unsigned 8 bit expression to B +LB709 jsr GETNCH ; move to next character +EVALEXPB jsr LB141 ; evaluate a numeric expression +LB70E jsr LB3E9 ; convert to integer in D + tsta ; are we negative or > 255? + bne LB706 ; brif so - FC error + jmp GETCCH ; fetch current input character and return +; VAL function +VAL jsr LB686 ; get string details + lbeq LBA39 ; brif NULL string - return 0 + ldu CHARAD ; get input pointer so we can replace it later + stx CHARAD ; point interpreter at string data + abx ; calculate end address of the string + lda ,x ; get byte after the end of the string + pshs u,x,a ; save end of string address, input pointer, and character after end of string + clr ,x ; put a NUL after the string (stops the number interpreter) + jsr GETCCH ; get input character at start of string + jsr LBD12 ; evaluate numeric expression in string + puls a,x,u ; get back saved character and pointers + sta ,x ; restore byte after string + stu CHARAD ; restore interpeter's input pointer + rts +; Evaluate unsigned expression to BINVAL, then evaluate an unsigned expression to B +LB734 bsr LB73D ; evaluate expression + stx BINVAL ; save result +LB738 jsr SYNCOMMA ; make sure there's a comma + bra EVALEXPB ; evaluate unsigned expression to B +; Evaluate unsigned expression in X +LB73D jsr LB141 ; evaluate numeric expression +LB740 lda FP0SGN ; is it negative? + bmi LB706 ; brif so + lda FP0EXP ; get exponent + cmpa #0x90 ; largest possible exponent for 16 bits + bhi LB706 ; brif too large + jsr LBCC8 ; move binary point to right of FPA0 + ldx FPA0+2 ; get resulting unsigned value + rts +; PEEK function +PEEK bsr LB740 ; get address to X + ldb ,x ; get the value at that address + jmp LB4F3 ; return B as unsigned value +; POKE function +POKE bsr LB734 ; evaluate address and byte value + ldx BINVAL ; get address + stb ,x ; put value there + rts +; LLIST command +LLIST ldb #-2 ; set output device to printer + stb DEVNUM + jsr GETCCH ; reset flags for input character and fall through to LIST +; LIST command +LIST pshs cc ; save zero flag (end of statement) + jsr LAF67 ; parse line number + jsr LAD01 ; find address of that line + stx LSTTXT ; save that address as the start of the list + puls cc ; get back ent of statement flag + beq LB784 ; brif end of line - list whole program + jsr GETCCH ; are we at the end of the line (one number)? + beq LB789 ; brif end of line + cmpa #0xac ; is it "-"? + bne LB783 ; brif not + jsr GETNCH ; eat the "-" + beq LB784 ; brif no second number - list to end of program + jsr LAF67 ; evaluate the second number + beq LB789 ; brif illegal number +LB783 rts +LB784 ldu #0xffff ; this will cause listing to do the entire program + stu BINVAL +LB789 leas 2,s ; don't return to the caller - we'll jump back to the main loop + ldx LSTTXT ; get address of line to list +LB78D jsr LB95C ; do a newline if needed + jsr LA549 ; do a break check + ldd ,x ; get address of next line + bne LB79F ; brif not end of program +LB797 jsr LA42D ; close output file + clr DEVNUM ; reset device to screen + jmp LAC73 ; go back to immediate mode +LB79F stx LSTTXT ; save new line address + ldd 2,x ; get line number of this line + cmpd BINVAL ; is it above the end line? + bhi LB797 ; brif so - return + jsr LBDCC ; display line number + jsr LB9AC ; put a space after it + ldx LSTTXT ; get line address + bsr LB7C2 ; detokenize the line + ldx [LSTTXT] ; get pointer to next line + ldu #LINBUF+1 ; point to start of detokenized line +LB7B9 lda ,u+ ; get byte from detokenized line + beq LB78D ; brif end of line + jsr LB9B1 ; output character + bra LB7B9 ; handle next character +; Detokenize a line from (X) to the line input buffer +LB7C2 jsr RVEC24 ; do the RAM hook dance + leax 4,x ; move past next line pointer and line number + ldy #LINBUF+1 ; point to line input buffer (destination) +LB7CB lda ,x+ ; get character from tokenized line + beq LB820 ; brif end of input + bmi LB7E6 ; brif it's a token + cmpa #': ; colon? + bne LB7E2 ; brif not + ldb ,x ; get what's after the colon + cmpb #0x84 ; ELSE? + beq LB7CB ; brif so - suppress the colon + cmpb #0x83 ; '? + beq LB7CB ; brif so - suppress the colon + skip2 +LB7E0 lda #'! ; placeholder for unknown token +LB7E2 bsr LB814 ; stow output character + bra LB7CB ; go process another input character +LB7E6 ldu #COMVEC-10 ; point to command interptation table + cmpa #0xff ; is it a function? + bne LB7F1 ; brif not + lda ,x+ ; get function token + leau 5,u ; shift to the function half of the interpretation tables +LB7F1 anda #0x7f ; remove token bias +LB7F3 leau 10,u ; move to next command/function table + tst ,u ; is this table active? + beq LB7E0 ; brif not - use place holder +LB7F9 suba ,u ; subtract number of tokens handled by this table entry + bpl LB7F3 ; brif this token isn't handled here + adda ,u ; undo extra subtraction + ldu 1,u ; get reserved word list for this table +LB801 deca ; are we at the right entry? + bmi LB80A ; brif so +LB804 tst ,u+ ; end of entry? + bpl LB804 ; brif not + bra LB801 ; see if we're there yet +LB80A lda ,u ; get character from wordlist + bsr LB814 ; put character in the buffer + tst ,u+ ; end of word? + bpl LB80A ; brif not + bra LB7CB ; go handle another input character +LB814 cmpy #LINBUF+LBUFMX ; is there room? + bhs LB820 ; brif not + anda #0x7f ; lose bit 7 + sta ,y+ ; save character in output + clr ,y ; make sure there's always a NUL terminator +LB820 rts +; Tokenize the line that the input pointer is pointing to; put result in the line input buffer; return +; length in D +LB821 jsr RVEC23 ; do the RAM hook dance + ldx CHARAD ; get input pointer + ldu #LINBUF ; set destination pointer +LB829 clr V43 ; clear alpha string flag + clr V44 ; clear DATA flag +LB82D lda ,x+ ; get input character + beq LB852 ; brif end of input + tst V43 ; are we handling an alphanumeric string? + beq LB844 ; brif not + jsr LB3A2 ; set carry if not alpha + bcc LB852 ; brif alpha + cmpa #'0 ; is it below the digits? + blo LB842 ; brif so + cmpa #'9 ; is it within the digits? + bls LB852 ; brif so +LB842 clr V43 ; flag that we're past the alphanumeric string +LB844 cmpa #0x20 ; space? + beq LB852 ; brif so - keep it + sta V42 ; save scan delimiter + cmpa #'" ; string delimiter? + beq LB886 ; brif so - copy until another " + tst V44 ; doing "DATA"? + beq LB86B ; brif not +LB852 sta ,u+ ; put character in output + beq LB85C ; brif end of input + cmpa #': ; colon? + beq LB829 ; brif so - reset DATA and alpha string flags +LB85A bra LB82D ; go process another input character +LB85C clr ,u+ ; put a double NUL at the end + clr ,u+ + tfr u,d ; calculate length of result (includes double NUL and an extra two bytes) + subd #LINHDR + ldx #LINBUF-1 ; point to one before the output + stx CHARAD ; set input pointer there + rts +LB86B cmpa #'? ; print abbreviation? + bne LB873 ; brif not + lda #0x87 ; token for PRINT + bra LB852 ; go stash it +LB873 cmpa #'' ; REM abbreviation? + bne LB88A ; brif not + ldd #0x3a83 ; colon plus ' token + std ,u++ ; put it in the output +LB87C clr V42 ; set delimiter to NUL +LB87E lda ,x+ ; get input + beq LB852 ; brif end of line + cmpa V42 ; at the delimiter? + beq LB852 ; brif so +LB886 sta ,u+ ; save in output + bra LB87E ; keep scanning for delimiter +LB88A cmpa #'0 ; is it below digits? + blo LB892 ; brif so + cmpa #';+1 ; is it digit, colon, or semicolon? + blo LB852 ; brif so +LB892 leax -1,x ; move input pointer back one (to point at this input character) + pshs u,x ; save input and output pointers + clr V41 ; set token type to 0 (command) + ldu #COMVEC-10 ; point to command interpretation table +LB89B clr V42 ; set token counter to 0 (0x80) +LB89D leau 10,u ; + lda ,u ; get number of reserved words + beq LB8D4 ; brif this table isn't active + ldy 1,u ; point to reserved words list +LB8A6 ldx ,s ; get input pointer +LB8A8 ldb ,y+ ; get character from reserved word table + subb ,x+ ; compare with input character + beq LB8A8 ; brif exact match + cmpb #0x80 ; brif it was the last character in word and exact match + bne LB8EA ; brif not + leas 2,s ; remove original input pointer from stack + puls u ; get back output pointer + orb V42 ; create token value (B has 0x80 from above) + lda V41 ; get token type + bne LB8C2 ; brif function + cmpb #0x84 ; is it ELSE? + bne LB8C6 ; brif not + lda #': ; silently add a colon before ELSE +LB8C2 std ,u++ ; put two byte token into output + bra LB85A ; go handle more input +LB8C6 stb ,u+ ; save single byte token + cmpb #0x86 ; DATA? + bne LB8CE ; brif not + inc V44 ; set DATA flag +LB8CE cmpb #0x82 ; REM? + beq LB87C ; brif so - skip over rest of line +LB8D2 bra LB85A ; go handle more input +LB8D4 ldu #COMVEC-5 ; point to interpretation table, function style +LB8D7 com V41 ; invert token flag + bne LB89B ; brif we haven't already done functions + puls x,u ; restore input and output pointers + lda ,x+ ; copy first character + sta ,u+ + jsr LB3A2 ; set C if not alpha + bcs LB8D2 ; brif not alpha - it isn't a variable + com V43 ; set alphanumeric string flag + bra LB8D2 ; process more input +LB8EA inc V42 ; bump token number + deca ; checked all in this table? + beq LB89D ; brif so + leay -1,y ; unconsume last compared character +LB8F1 ldb ,y+ ; end of entry? + bpl LB8F1 ; brif not + bra LB8A6 ; check next reserved word +; PRINT command +PRINT beq LB958 ; brif no argument - do a newline + bsr LB8FE ; process print options + clr DEVNUM ; reset output to screen + rts +LB8FE cmpa #'@ ; is it PRINT @? + bne LB907 ; brif not +LB902 jsr LA554 ; move cursor to correct location +LB905 bra LB911 ; handle some more +LB907 cmpa #'# ; device number specified? + bne LB918 ; brif not + jsr LA5A5 ; parse device number + jsr LA406 ; check for valid output file +LB911 jsr GETCCH ; get input character + beq LB958 ; brif nothing - do newline + jsr SYNCOMMA ; need comma after @ or # +LB918 jsr RVEC9 ; do the RAM hook boogaloo +LB91B beq LB965 ; brif end of input +LB91D cmpa #0xa4 ; TAB(? + beq LB97E ; brif so + cmpa #', ; comma (next tab field)? + beq LB966 ; brif so + cmpa #'; ; semicolon (do not advance print position) + beq LB997 ; brif so + jsr LB156 ; evaluate expression + lda VALTYP ; get type of value + pshs a ; save it + bne LB938 ; brif string + jsr LBDD9 ; convert FP number to string + jsr LB516 ; parse a string and put on string stack +LB938 bsr LB99F ; print string + puls b ; get back variable type + jsr LA35F ; set up print parameters + tst PRTDEV ; is it a display device? + beq LB949 ; brif so + bsr LB958 ; do a newline + jsr GETCCH ; get input + bra LB91B ; process more print stuff +LB949 tstb ; set flags on print position + bne LB954 ; brif not at start of line + jsr GETCCH ; get current input + cmpa #', ; comma? + beq LB966 ; skip to next tab field if so + bsr LB9AC ; send a space +LB954 jsr GETCCH ; get input character + bne LB91D ; brif not end of statement +LB958 lda #0x0d ; carriage return + bra LB9B1 ; send it to output +LB95C jsr LA35F ; set up print parameters +LB95F beq LB958 ; brif width is 0 + lda DEVPOS ; get line position + bne LB958 ; brif not at start of line +LB965 rts +LB966 jsr LA35F ; set up print parameters + beq LB975 ; brif line width is 0 + ldb DEVPOS ; get line position + cmpb DEVLCF ; at or past last comma field? + blo LB977 ; brif so + bsr LB958 ; move to next line + bra LB997 ; handle more stuff +LB975 ldb DEVPOS ; get line position +LB977 subb DEVCFW ; subtract a comma field width + bhs LB977 ; brif we don't have a remainder yet + negb ; now B is number of of spaces needed + bra LB98E ; go advance +LB97E jsr LB709 ; evaluate TAB distance + cmpa #') ; closing )? + lbne LB277 ; brif not + jsr LA35F ; set up print parameters + subb DEVPOS ; subtract print position from desired position + bls LB997 ; brif we're already past it +LB98E tst PRTDEV ; is it a display device? + bne LB997 ; brif not +LB992 bsr LB9AC ; output a space + decb ; done enough? + bne LB992 ; brif not +LB997 jsr GETNCH ; get input character + jmp LB91B ; process more items +; cpoy string from (X-1) to output +STRINOUT jsr LB518 ; parse the string +LB99F jsr LB657 ; get string details +LB9A2 incb ; compensate for decb +LB9A3 decb ; done all of the string? + beq LB965 ; brif so + lda ,x+ ; get character from string + bsr LB9B1 ; send to output + bra LB9A3 ; go do another character +LB9AC lda #0x20 ; space character + skip2 +LB9AF lda #'? ; question mark character +LB9B1 jmp PUTCHR ; output character +; The floating point math package and related functions and operations follow from here +; to the end of the Color Basic ROM area +LB9B4 ldx #LBEC0 ; point to FP constant 0.5 + bra LB9C2 ; add 0.5 to FPA0 +LB9B9 jsr LBB2F ; unpack FP data from (X) to FPA1 +; subtraction operator +LB9BC com FP0SGN ; invert sign of FPA0 (subtracting is adding the negative) + com RESSGN ; that also inverts the sign differential + bra LB9C5 ; go add the negative of FPA0 to FPA1 +LB9C2 jsr LBB2F ; unpack FP data from (X) to FPA1 +; addition operator +LB9C5 tstb ; check exponent of FPA0 + lbeq LBC4A ; copy FPA1 to FPA0 if FPA0 is 0 + ldx #FP1EXP ; point X to FPA1 (first operand) as the operand to denormalize +LB9CD tfr a,b ; put exponent of FPA1 into B + tstb ; is FPA1 0? + beq LBA3E ; brif exponent is 0 - no-op; adding 0 to FPA0 + subb FP0EXP ; get difference in exponents - number of bits to shift the smaller mantissa + beq LBA3F ; brif exponents are equal - no need to denormalize + blo LB9E2 ; brif FPA0 > FPA1 + sta FP0EXP ; replace result exponent with FPA1's (FPA1 is bigger) + lda FP1SGN ; also copy sign over + sta FP0SGN + ldx #FP0EXP ; point to FPA0 (we need to denormalize the smaller number) + negb ; invert the difference - this is the number of bits to shift the mantissa +LB9E2 cmpb #-8 ; do we have to shift by a whole byte? + ble LBA3F ; brif so start by shifting whole bytes to the right + clra ; clear overflow byte + lsr 1,x ; shift high bit of mantissa right (LSR will force a zero into the high bit) + jsr LBABA ; shift remainder of mantissa right -B times +LB9EC ldb RESSGN ; get the sign flag + bpl LB9FB ; brif signs are the same (we add the mantissas then) + com 1,x ; complement the mantissa and extra precision bytes + com 2,x + com 3,x + com 4,x + coma + adca #0 ; add one to A (COM sets C); this may cause a carry to enter the ADD below +LB9FB sta FPSBYT ; save extra precision byte + lda FPA0+3 ; add the main mantissa bytes (and propage carry from above) + adca FPA1+3 + sta FPA0+3 + lda FPA0+2 + adca FPA1+2 + sta FPA0+2 + lda FPA0+1 + adca FPA1+1 + sta FPA0+1 + lda FPA0 + adca FPA1 + sta FPA0 + tstb ; were signs the same? + bpl LBA5C ; brif so - number may have gotten bigger so normalize if needed +LBA18 bcs LBA1C ; brif we had a carry - result is positive?) + bsr LBA79 ; do a proper negation of FPA0 mantissa +LBA1C clrb ; clear temporary exponent accumulator +LBA1D lda FPA0 ; test high byte of mantissa + bne LBA4F ; brif not 0 - we need to do bit shifting + lda FPA0+1 ; shift left 8 bits + sta FPA0 + lda FPA0+2 + sta FPA0+1 + lda FPA0+3 + sta FPA0+2 + lda FPSBYT + sta FPA0+3 + clr FPSBYT + addb #8 ; account for 8 bits shifted + cmpb #5*8 ; shifted 5 bytes worth? + blt LBA1D ; brif not +LBA39 clra ; zero out exponent and sign - result is 0 +LBA3A sta FP0EXP ; set exponent and sign + sta FP0SGN +LBA3E rts +LBA3F bsr LBAAE ; shift FPA0 mantissa to the right + clrb ; clear carry + bra LB9EC ; get on with adding +LBA44 incb ; account for one bit shift + asl FPSBYT ; shift mantissa and extra precision left + rol FPA0+3 + rol FPA0+2 + rol FPA0+1 + rol FPA0 +LBA4F bpl LBA44 ; brif we haven't got a 1 in bit 7 + lda FP0EXP ; get exponent of result + pshs b ; subtract shift count from exponent + suba ,s+ + sta FP0EXP ; save adjusted exponent + bls LBA39 ; brif we underflowed - set result to 0 + skip2 +LBA5C bcs LBA66 ; brif mantissa overflowed + asl FPSBYT ; get bit 7 of expra precision to C (used for round off) + lda #0 ; set to 0 without affecting C + sta FPSBYT ; clear out extra precision bits + bra LBA72 ; go round off result +LBA66 inc FP0EXP ; bump exponent (for a right shift to bring carry in) + beq LBA92 ; brif we overflowed + ror FPA0 ; shift carry into mantissa, shift right + ror FPA0+1 + ror FPA0+2 + ror FPA0+3 +LBA72 bcc LBA78 ; brif no round-off needed + bsr LBA83 ; add one to mantissa + beq LBA66 ; brif carry - need to shift right again +LBA78 rts +LBA79 com FP0SGN ; invert sign of value +LBA7B com FPA0 ; first do a one's copmlement + com FPA0+1 + com FPA0+2 + com FPA0+3 +LBA83 ldx FPA0+2 ; add one to mantissa (after one's complement gives two's complement) + leax 1,x ; bump low word + stx FPA0+2 + bne LBA91 ; brif no carry from low word + ldx FPA0 ; bump high word + leax 1,x + stx FPA0 +LBA91 rts +LBA92 ldb #2*5 ; code for overflow + jmp LAC46 ; raise error +LBA97 ldx #FPA2-1 ; point to FPA2 +LBA9A lda 4,x ; shift mantissa right by 8 bits + sta FPSBYT + lda 3,x + sta 4,x + lda 2,x + sta 3,x + lda 1,x + sta 2,x + lda FPCARY ; and handle extra precision on the left + sta 1,x +LBAAE addb #8 ; account for 8 bits shifted + ble LBA9A ; brif more shifts needed + lda FPSBYT ; get sub byte (extra precision) + subb #8 ; undo the 8 added above + beq LBAC4 ; brif difference is 0 +LBAB8 asr 1,x ; shift mantissa and sub byte one bit (keep mantissa high bit set) +LBABA ror 2,x + ror 3,x + ror 4,x + rora + incb ; account for one shift + bne LBAB8 ; brif not enought shifts yet +LBAC4 rts +LBAC5 fcb 0x81,0x00,0x00,0x00,0x00 ; packed FP 1.0 +LBACA bsr LBB2F ; unpack FP value from (X) to FPA1 +; multiplication operator +LBACC beq LBB2E ; brif exponent of FPA0 is 0 (result is 0) + bsr LBB48 ; calculate exponent of product +LBAD0 lda #0 ; zero out mantissa of FPA2 + sta FPA2 + sta FPA2+1 + sta FPA2+2 + sta FPA2+3 + ldb FPA0+3 ; multiply FPA1 by LSB of FPA0 + bsr LBB00 + ldb FPSBYT ; save extra precision byte + stb VAE + ldb FPA0+2 + bsr LBB00 ; again for next byte of FPA0 + ldb FPSBYT + stb VAD + ldb FPA0+1 ; again for next byte of FPA0 + bsr LBB00 + ldb FPSBYT + stb VAC + ldb FPA0 ; and finally for the high byte + bsr LBB02 + ldb FPSBYT + stb VAB + jsr LBC0B ; copy mantissa from FPA2 to FPA0 (result) + jmp LBA1C ; normalize +LBB00 beq LBA97 ; brif multiplier is 0 - just shift, don't multiply +LBB02 coma ; set carry +LBB03 lda FPA2 ; get FPA2 MS byte + rorb ; data bit to carry; will be 0 when all shifts done + beq LBB2E ; brif 8 shifts done + bcc LBB20 ; brif data bit is 0 - no addition + lda FPA2+3 ; add mantissa of FPA1 and FPA2 + adda FPA1+3 + sta FPA2+3 + lda FPA2+2 + adca FPA1+2 + sta FPA2+2 + lda FPA2+1 + adca FPA1+1 + sta FPA2+1 + lda FPA2 + adca FPA1 +LBB20 rora ; shift carry into FPA2 + sta FPA2 + ror FPA2+1 + ror FPA2+2 + ror FPA2+3 + ror FPSBYT + clra ; clear carry + bra LBB03 +LBB2E rts +; Unpack FP value from (X) to FPA1 +LBB2F ldd 1,x ; copy mantissa (and sign) + sta FP1SGN ; save sign bit + ora #0x80 ; make sure mantissa has bit 7 set + std FPA1 + ldb FP1SGN ; get sign + eorb FP0SGN ; set if FPA0 sign differs + stb RESSGN + ldd 3,x ; copy remainder of mantissa + std FPA1+2 + lda ,x ; and exponent + sta FP1EXP + ldb FP0EXP ; fetch FPA0 exponent and set flags + rts +; Calculate eponent for product of FPA0 and FPA1 +LBB48 tsta ; is FPA1 zero? + beq LBB61 ; brif so + adda FP0EXP ; add to exponent of FPA0 (this is how scientific notation works) + rora ; set V if we *don't* have an overflow + rola + bvc LBB61 ; brif exponent too larger or small + adda #0x80 ; restore the bias + sta FP0EXP ; set result exponent + beq LBB63 ; brif 0 - clear FPA0 + lda RESSGN ; the result sign (negative if signs differ) is the result sign + sta FP0SGN ; so set it as such + rts +LBB5C lda FP0SGN ; get sign of FPA0 + coma ; invert sign + bra LBB63 ; zero sign and exponent +LBB61 leas 2,s ; don't go back to caller (mul/div) - return to previous caller +LBB63 lbpl LBA39 ; brif we underflowed - go zero things out +LBB67 jmp LBA92 ; raise overflow error +; fast multiply by 10 - leave result in FPA0 +LBB6A jsr LBC5F ; copy FPA0 to FPA1 (for addition later) + beq LBB7C ; brif exponent is 0 - it's a no-op then + adda #2 ; this gives "times 4" + bcs LBB67 ; raise overflow if required + clr RESSGN ; set result sign to "signs the same" + jsr LB9CD ; add FPA1 to FPA0 "times 5" + inc FP0EXP ; times 10 + beq LBB67 ; brif overflow +LBB7C rts +LBB7D fcb 0x84,0x20,0x00,0x00,0x00 ; packed FP constant 10.0 +; Divide by 10 +LBB82 jsr LBC5F ; move FPA0 to FPA1 + ldx #LBB7D ; point to constant 10 + clrb ; zero sign +LBB89 stb RESSGN ; result will be positive or zero + jsr LBC14 ; unpack constant 10 to FPA0 + skip2 ; fall through to division (divide FPA1 by 10) +LBB8F bsr LBB2F ; unpack FP number from (X) to FPA1 +; division operator +LBB91 beq LBC06 ; brif FPA0 is 0 - division by zero + neg FP0EXP ; get exponent of reciprocal of the divisor + bsr LBB48 ; calculate exponent of quotient + inc FP0EXP ; bump exponent (due to division algorithm below) + beq LBB67 ; brif overflow + ldx #FPA2 ; point to temporary storage location + ldb #4 ; do 5 bytes + stb TMPLOC ; save counter + ldb #1 ; shift counter and quotient byte +LBBA4 lda FPA0 ; compare mantissa of FPA0 to FPA1, set C if FPA1 less + cmpa FPA1 + bne LBBBD + lda FPA0+1 + cmpa FPA1+1 + bne LBBBD + lda FPA0+2 + cmpa FPA1+2 + bne LBBBD + lda FPA0+3 + cmpa FPA1+3 + bne LBBBD + coma ; set C if FPA0 = FPA1 (it "goes") +LBBBD tfr cc,a ; save "it goes" status + rolb ; rotate carry into quotient + bcc LBBCC ; brif carry clear - haven't done 8 shifts yet + stb ,x+ ; save quotient byte + dec TMPLOC ; done enough bytes? + bmi LBBFC ; brif done all 5 + beq LBBF8 ; brif last byte + ldb #1 ; reset shift counter and quotient byte +LBBCC tfr a,cc ; get back carry status + bcs LBBDE ; brif it "went" +LBBD0 asl FPA1+3 ; shift mantissa (dividend) left + rol FPA1+2 + rol FPA1+1 + rol FPA1 + bcs LBBBD ; brif carry - it "goes" so we have to bump quotient + bmi LBBA4 ; brif high order bit is set - compare mantissas + bra LBBBD ; otherwise, count a 0 bit and try next bit +LBBDE lda FPA1+3 ; subtract mantissa of FPA0 from mantissa of FPA1 + suba FPA0+3 + sta FPA1+3 + lda FPA1+2 + sbca FPA0+2 + sta FPA1+2 + lda FPA1+1 + sbca FPA0+1 + sta FPA1+1 + lda FPA1 + sbca FPA0 + sta FPA1 + bra LBBD0 ; go check for another go +LBBF8 ldb #0x40 ; only two bits in last byte (for rounding) + bra LBBCC ; go do the last byte +LBBFC rorb ; get low bits to bits 7,6 and C to bit 5 + rorb + rorb + stb FPSBYT ; save result extra precision + bsr LBC0B ; move FPA2 mantissa to FPA0 (result) + jmp LBA1C ; go normalize the result +LBC06 ldb #2*10 ; division by zero + jmp LAC46 ; raise error +; Copy mantissa of FPA2 to FPA0 +LBC0B ldx FPA2 ; copy high word + stx FPA0 + ldx FPA2+2 ; copy low word + stx FPA0+2 + rts +; unpack FP number at (X) to FPA0 +LBC14 pshs a ; save register + ldd 1,x ; get mantissa high word and sign + sta FP0SGN ; set sign + ora #0x80 ; make sure mantissa always has bit 7 set + std FPA0 + clr FPSBYT ; clear extra precision + ldb ,x ; get exponent + ldx 3,x ; copy mantissa low word + stx FPA0+2 + stb FP0EXP ; save exponent (and set flags) + puls a,pc ; restore register and return +LBC2A ldx #V45 ; point to FPA4 + bra LBC35 ; pack FPA0 there +LBC2F ldx #V40 ; point to FPA3 + skip2 ; fall through to pack FPA0 there +LBC33 ldx VARDES ; get variable descriptor pointer +; Pack FPA0 to (X) +LBC35 lda FP0EXP ; get exponent + sta ,x ; save it + lda FP0SGN ; get sign + ora #0x7f ; force set low bits - only keep sign in high bit + anda FPA0 ; merge in bits 6-0 of high byte of mantissa + sta 1,x ; save it + lda FPA0+1 ; copy next highest byte + sta 2,x + ldu FPA0+2 ; and the low word of the mantissa + stu 3,x + rts +; Copy FPA1 to FPA0; return with sign in A +LBC4A lda FP1SGN ; copy sign +LBC4C sta FP0SGN + ldx FP1EXP ; copy exponent, mantissa high byte + stx FP0EXP + clr FPSBYT ; clear extra precision + lda FPA1+1 ; copy mantissa second highest byte + sta FPA0+1 + lda FP0SGN ; set sign for return + ldx FPA1+2 ; copy low word of mantissa + stx FPA0+2 + rts +; Copy FPA0 to FPA1 +LBC5F ldd FP0EXP ; copy exponent and high byte of mantissa + std FP1EXP + ldx FPA0+1 ; copy middle bytes of mantissa + stx FPA1+1 + ldx FPA0+3 ; copy low byte of mantissa and sign + stx FPA1+3 + tsta ; set flags on exponent + rts +; check FPA0: return B = 0, if FPA0 is 0, 0xff if negative, and 0x01 if positive +LBC6D ldb FP0EXP ; get exponent + beq LBC79 ; brif 0 +LBC71 ldb FP0SGN ; get sign +LBC73 rolb ; get sign to C + ldb #0xff ; set for negative result + bcs LBC79 ; brif negative + negb ; set to 1 for positive +LBC79 rts +; SGN function +SGN bsr LBC6D ; get sign of FPA0 +LBC7C stb FPA0 ; save result + clr FPA0+1 ; clear next lower 8 bits + ldb #0x88 ; exponent if mantissa is 8 bit integer +LBC82 lda FPA0 ; get high bits of mantissa + suba #0x80 ; set C if mantissa was positive (will cause a negation if it was negative) +LBC86 stb FP0EXP ; set exponent + ldd ZERO ; clear out low word + std FPA0+2 + sta FPSBYT ; clear extra precision + sta FP0SGN ; set sign to positive + jmp LBA18 ; normalize the result +; ABS function +ABS clr FP0SGN ; force FPA0 to be positive (yes, it's that simple) + rts +; Compare packed FP number at (X) to FPA0 +; Return with B = -1, 0, 1 for FPA0 <, =, > (X) and flags set based on that +LBC96 ldb ,x ; get exponent of (X) + beq LBC6D ; brif (X) is 0 + ldb 1,x ; get MS byte of mantissa of (X) + eorb FP0SGN ; set bit 7 if signs of (X) and FPA0 differ + bmi LBC71 ; brif signs differ - no need to compare the magnitude +LBCA0 ldb FP0EXP ; compare exponents and brif different + cmpb ,x + bne LBCC3 + ldb 1,x ; compare mantissa (but we have to pack the FPA0 bits first + orb #0x7f ; keep only sign bit (note: signs are the same) + andb FPA0 ; merge in the mantissa bits from FPA0 + cmpb 1,x ; do the packed versions match? + bne LBCC3 ; brif not + ldb FPA0+1 ; compare second byte of mantissas + cmpb 2,x + bne LBCC3 + ldb FPA0+2 ; compare third byte of mantissas + cmpb 3,x + bne LBCC3 + ldb FPA0+3 ; compare low byte of mantissas, but use subtraction so B = 0 on match + subb 4,x + bne LBCC3 + rts ; return B = 0 if (X) = FPA0 +LBCC3 rorb ; shift carry to bit 7 (C set if FPA0 < (X)) + eorb FP0SGN ; invert the comparision sense if the signs are negative + bra LBC73 ; interpret comparison result +; Shift mantissa of FPA0 until the binary point is immediately to the right of the mantissa and set up the +; result as a two's complement value. +LBCC8 ldb FP0EXP ; get exponent of FPA0 + beq LBD09 ; brif FPA0 is zero - we don't have to do anything, just blank it + subb #0xa0 ; calculate number of shifts to get to the correct exponent (binary point to the right) + lda FP0SGN ; do we have a positive number? + bpl LBCD7 ; brif so + com FPCARY ; negate the mantissa and set extra inbound precision to the correct sign + jsr LBA7B +LBCD7 ldx #FP0EXP ; point to FPA0 + cmpb #-8 ; moving by whole bytes? + bgt LBCE4 ; brif not + jsr LBAAE ; do bit shifting + clr FPCARY ; clear carry in byte + rts +LBCE4 clr FPCARY ; clear the extra carry in precision + lda FP0SGN ; get sign of value + rola ; get sign to carry (so rotate repeats the sign) + ror FPA0 ; shift the first bit + jmp LBABA ; do the shifting dance +; INT function +INT ldb FP0EXP ; get exponent + cmpb #0xa0 ; is the number big enough that there can be no fractional part? + bhs LBD11 ; brif so - we don't have to do anything + bsr LBCC8 ; go shift binary point to the right of the mantissa + stb FPSBYT ; save extra precision bits + lda FP0SGN ; get original sign + stb FP0SGN ; force result to be positive + suba #0x80 ; set C if we had a positive result + lda #0xa0 ; set exponent to match denormalized result + sta FP0EXP + lda FPA0+3 ; save low byte + sta CHARAC + jmp LBA18 ; go normalize (this will correct for the two's complement representation of negatives) +LBD09 stb FPA0 ; replace mantissa of FPA0 with contents of B + stb FPA0+1 + stb FPA0+2 + stb FPA0+3 +LBD11 rts +; Convert ASCII string to FP +; BUG: no overflow is checked on the decimal exponent in exponential notation. +LBD12 ldx ZERO ; zero out FPA0 and temporaries + stx FP0SGN + stx FP0EXP + stx FPA0+1 + stx FPA0+2 + stx V47 + stx V45 + bcs LBD86 ; brif input character is numeric + jsr RVEC19 ; do the RAM hook dance + cmpa #'- ; regular negative sign + bne LBD2D ; brif not + com COEFCT ; invert sign + bra LBD31 ; process stuff after the sign +LBD2D cmpa #'+ ; regular plus? + bne LBD35 ; brif not +LBD31 jsr GETNCH ; get character after sign + bcs LBD86 ; brif numeric +LBD35 cmpa #'. ; decimal point? + beq LBD61 ; brif so + cmpa #'E ; scientific notation + bne LBD65 ; brif not + jsr GETNCH ; eat the "E" + bcs LBDA5 ; brif numeric + cmpa #0xac ; negative sign (token)? + beq LBD53 ; brif so + cmpa #'- ; regular negative? + beq LBD53 ; brif so + cmpa #0xab ; plus sign (token)? + beq LBD55 ; brif so + cmpa #'+ ; regular plus? + beq LBD55 + bra LBD59 ; brif no sign found +LBD53 com V48 ; set exponent sign to negative +LBD55 jsr GETNCH ; eat the sign + bcs LBDA5 ; brif numeric +LBD59 tst V48 ; is the exponent sign negatvie? + beq LBD65 ; brif not + neg V47 ; negate base 10 exponent + bra LBD65 +LBD61 com V46 ; toggle decimal point flag + bne LBD31 ; brif we haven't seen two decimal points +LBD65 lda V47 ; get base 10 exponent + suba V45 ; subtract number of places to the right + sta V47 ; we now have a complete decimal exponent + beq LBD7F ; brif we have no base 10 shifting required + bpl LBD78 ; brif positive exponent +LBD6F jsr LBB82 ; divide FPA0 by 10 (shift decimal point left) + inc V47 ; bump exponent + bne LBD6F ; brif we haven't reached 0 yet + bra LBD7F ; return result +LBD78 jsr LBB6A ; multiply by 10 + dec V47 ; downshift the exponent + bne LBD78 ; brif not at 0 yet +LBD7F lda COEFCT ; get desired sign + bpl LBD11 ; brif it will be positive - no need to do anything + jmp LBEE9 ; flip the sign of FPA0 +LBD86 ldb V45 ; get the decimal count + subb V46 ; (if decimal seen, will add one; otherwise it does nothing) + stb V45 + pshs a ; save new digit + jsr LBB6A ; multiply partial result by 10 + puls b ; get back digit + subb #'0 ; remove ASCII bias + bsr LBD99 ; add B to FPA0 + bra LBD31 ; go process another digit +LBD99 jsr LBC2F ; save FPA0 to FPA3 + jsr LBC7C ; convert B to FP number + ldx #V40 ; point to FPA3 + jmp LB9C2 ; add FPA3 and FPA0 +LBDA5 ldb V47 ; get exponent value + aslb ; times 2 + aslb ; times 4 + addb V47 ; times 5 + aslb ; times 10 + suba #'0 ; remove ASCII bias + pshs b ; save acculated result + adda ,s+ ; add new digit to accumulated result + sta V47 ; save new accumulated decimal exponent + bra LBD55 ; interpret another exponent character +LBDB6 fcb 0x9b,0x3e,0xbc,0x1f,0xfd ; packed FP: 99999999.9 +LBDBB fcb 0x9e,0x6e,0x6b,0x27,0xfd ; packed FP: 999999999 +LBDC0 fcb 0x9e,0x6e,0x6b,0x28,0x00 ; pakced FP: 1E9 +LBDC5 ldx #LABE8-1 ; point to "IN" message + bsr LBDD6 ; output the string + ldd CURLIN ; get basic line number +LBDCC std FPA0 ; save 16 bit unsigned integer + ldb #0x90 ; exponent for upper 16 bits of FPA0 to be an integer + coma ; set C (force normalization to treat as positive) + jsr LBC86 ; zero bottom half, save exponent, and normalize + bsr LBDD9 ; convert FP number to ASCII string +LBDD6 jmp STRINOUT ; output string +; Convert FP number to ASCII string +LBDD9 ldu #STRBUF+3 ; point to buffer address that will not cause string to go to string space +LBDDC lda #0x20 ; default sign is a space character + ldb FP0SGN ; get sign of value + bpl LBDE4 ; brif positive + lda #'- ; use negative sign +LBDE4 sta ,u+ ; save sign + stu COEFPT ; save output buffer pointer + sta FP0SGN ; save sign character + lda #'0 ; result is 0 if exponent is 0 + ldb FP0EXP ; get exponent + lbeq LBEB8 ; brif FPA0 is 0 + clra ; base 10 exponent is 0 for > 1 + cmpb #0x80 ; is number > 1? + bhi LBDFF ; brif so + ldx #LBDC0 ; point to 1E+09 + jsr LBACA ; shift decimal to the right by 9 spaces + lda #-9 ; account for shift +LBDFF sta V45 ; save base 10 exponent +LBE01 ldx #LBDBB ; point to 999999999 + jsr LBCA0 ; are we above that? + bgt LBE18 ; brif so +LBE09 ldx #LBDB6 ; point to 99999999.9 + jsr LBCA0 ; are we above that? + bgt LBE1F ; brif in range + jsr LBB6A ; multiply by 10 (we were small) + dec V45 ; account for shift + bra LBE09 ; see if we've come into range +LBE18 jsr LBB82 ; divide by 10 + inc V45 ; account for shift + bra LBE01 ; see if we've come into range +LBE1F jsr LB9B4 ; add 0.5 to FPA0 (rounding) + jsr LBCC8 ; do the integer dance + ldb #1 ; default decimal flag (force immediate decimal) + lda V45 ; get base 10 exponent + adda #10 ; account for "unormalized" number + bmi LBE36 ; brif number < 1.0 + cmpa #11 ; do we have more than 9 places? + bhs LBE36 ; brif so - do scientific notation + deca + tfr a,b + lda #2 ; force no scientific notation +LBE36 deca ; subtract wo without affecting carry + deca + sta V47 ; save exponent - 0 is do not display in scientific notation + stb V45 ; save number of places to left of decimal + bgt LBE4B ; brif >= 1 + ldu COEFPT ; point to string buffer + lda #'. ; put decimal + sta ,u+ + tstb ; is there anything to left of decimal? + beq LBE4B ; brif not + lda #'0 ; store a zero + sta ,u+ +LBE4B ldx #LBEC5 ; point to powers of 10 + ldb #0x80 ; set digit counter to 0x80 +LBE50 lda FPA0+3 ; add mantissa to power of 10 + adda 3,x + sta FPA0+3 + lda FPA0+2 + adca 2,x + sta FPA0+2 + lda FPA0+1 + adca 1,x + sta FPA0+1 + lda FPA0 + adca ,x + sta FPA0 + incb ; add one to digit counter + rorb ; put carry into bit 7 + rolb ; set V if carry and sign differ + bvc LBE50 ; brif positive mantissa or carry is 0 and negative mantissa + bcc LBE72 ; brif negative mantissa + subb #10+1 ; take 9's complement if adding mantissa + negb +LBE72 addb #'0-1 ; add ASCII bias + leax 4,x ; move to next power of 10 + tfr b,a ; save digit + anda #0x7f ; remove add/subtract flag + sta ,u+ ; put in output + dec V45 ; do we need a decimal yet? + bne LBE84 ; brif not + lda #'. ; put decimal + sta ,u+ +LBE84 comb ; toggle bit 7 (add/sub flag) + andb #0x80 ; only keep bit 7 + cmpx #LBEC5+9*4 ; done all places? + bne LBE50 ; brif not +LBE8C lda ,-u ; get last character + cmpa #'0 ; was it 0? + beq LBE8C ; brif so + cmpa #'. ; decimal? + bne LBE98 ; brif not + leau -1,u ; move past decimal if it isn't needed +LBE98 lda #'+ ; plus sign + ldb V47 ; get scientific notation exponent + beq LBEBA ; brif not scientific notation + bpl LBEA3 ; brif positive exponent + lda #'- ; negative sign for base 10 exponent + negb ; switch to positive exponent +LBEA3 sta 2,u ; put sign + lda #'E ; put "E" + sta 1,u + lda #'0-1 ; init to ASCII 0 (compensate for INC) +LBEAB inca ; bump digit + subb #10 ; have we hit the correct one yet? + bcc LBEAB ; brif not + addb #'9+1 ; convert units digit to ASCII + std 3,u ; put exponent in output + clr 5,u ; put trailing NUL + bra LBEBC ; go reset pointer +LBEB8 sta ,u ; store last character +LBEBA clr 1,u ; put NUL at the end +LBEBC ldx #STRBUF+3 ; point to start of string + rts +LBEC0 fcb 0x80,0x00,0x00,0x00,0x00 ; packed FP 0.5 +LBEC5 fqb -100000000 + fqb 10000000 + fqb -1000000 + fqb 100000 + fqb -10000 + fqb 1000 + fqb -100 + fqb 10 + fqb -1 +LBEE9 lda FP0EXP ; get exponent of FPA0 + beq LBEEF ; brif 0 - don't flip sign + com FP0SGN ; flip sign +LBEEF rts +; Expand a polynomial of the form +; AQ+BQ^3+CQ^5+DQ^7..... where Q is FPA0 and X points to a table +LBEF0 stx COEFPT ; save coefficient table pointer + jsr LBC2F ; copy FPA0 to FPA3 + bsr LBEFC ; multiply FPA3 by FPA0 + bsr LBF01 ; expand polynomial + ldx #V40 ; point to FPA3 +LBEFC jmp LBACA ; multiply FPA0 by FPA3 +LBEFF stx COEFPT ; save coefficient table counter +LBF01 jsr LBC2A ; move FPA0 to FPA4 + ldx COEFPT ; get the current coefficient + ldb ,x+ ; get the number of entries + stb COEFCT ; save as counter + stx COEFPT ; save new pointer +LBF0C bsr LBEFC ; multiply (X) and FPA0 + ldx COEFPT ; get this coefficient + leax 5,x ; move to next one + stx COEFPT ; save new pointer + jsr LB9C2 ; add (X) to FPA0 + ldx #V45 ; point X to FPA4 + dec COEFCT ; done all coefficients? + bne LBF0C ; brif more left + rts +; RND function +RND jsr LBC6D ; set flags on FPA0 + bmi LBF45 ; brif negative - set seed + beq LBF3B ; brif 0 - do random between 0 and 1 + bsr LBF38 ; convert to integer + jsr LBC2F ; save range value + bsr LBF3B ; get random number + ldx #V40 ; point to FPA3 + bsr LBEFC ; multply (X) by FPA0 + ldx #LBAC5 ; point to FP 1.0 + jsr LB9C2 ; add 1 to FPA0 +LBF38 jmp INT ; return integer value +LBF3B ldx RVSEED+1 ; move variable random number seed to FPA0 + stx FPA0 + ldx RVSEED+3 + stx FPA0+2 +LBF45 ldx RSEED ; move fixed seed to FPA1 + stx FPA1 + ldx RSEED+2 + stx FPA1+2 + jsr LBAD0 ; multiply them + ldd VAD ; get lowest order product bytes + addd #0x658b ; add a constant + std RVSEED+3 ; save it as new seed + std FPA0+2 ; save in result + ldd VAB ; get high order extra product bytes + adcb #0xb0 ; add upper bytes of constant + adca #5 + std RVSEED+1 ; save as new seed + std FPA0 ; save as result + clr FP0SGN ; set result to positive + lda #0x80 ; set exponent to 0 < FPA0 < 1 + sta FP0EXP + lda FPA2+2 ; get a byte from FPA2 + sta FPSBYT ; save as extra precision + jmp LBA1C ; go normalize FPA0 +RSEED fqb 0x40e64dab ; constant random number generator seed +; SIN function +SIN jsr LBC5F ; copy FPA0 to FPA1 + ldx #LBFBD ; point to 2*pi + ldb FP1SGN ; get sign of FPA1 + jsr LBB89 ; divide FPA0 by 2*pi + jsr LBC5F ; copy FPA0 to FPA1 + bsr LBF38 ; convert FPA0 to an integer + clr RESSGN ; set result to positive + lda FP1EXP ; get exponent of FPA1 + ldb FP0EXP ; get exponent of FPA0 + jsr LB9BC ; subtract FPA0 from FPA1 + ldx #LBFC2 ; point to FP 0.25 + jsr LB9B9 ; subtract FPA0 from 0.25 (pi/2) + lda FP0SGN ; get result sign + pshs a ; save it + bpl LBFA6 ; brif positive + jsr LB9B4 ; add 0.5 (pi) to FPA0 + lda FP0SGN ; get sign of result + bmi LBFA9 ; brif negative + com RELFLG ; if 3pi/2 >= arg >= pi/2 +LBFA6 jsr LBEE9 ; flip sign of FPA0 +LBFA9 ldx #LBFC2 ; point to 0.25 + jsr LB9C2 ; add 0.25 (pi/2) to FPA0 + puls a ; get original sign + tsta ; was it positive + bpl LBFB7 ; brif so + jsr LBEE9 ; flip result sign +LBFB7 ldx #LBFC7 ; point to series coefficients + jmp LBEF0 ; go calculate value +LBFBD fcb 0x83,0x49,0x0f,0xda,0xa2 ; 2*pi +LBFC2 fcb 0x7f,0x00,0x00,0x00,0x00 ; 0.25 +; modified taylor series SIN coefficients +LBFC7 fcb 6-1 ; six coefficients + fcb 0x84,0xe6,0x1a,0x2d,0x1b ; -((2pi)^11)/11! + fcb 0x86,0x28,0x07,0xfb,0xf8 ; ((2pi)^9)/9! + fcb 0x87,0x99,0x68,0x89,0x01 ; -((2pi)^7)/7! + fcb 0x87,0x23,0x35,0xdf,0xe1 ; ((2pi)^5)/5! + fcb 0x86,0xa5,0x5d,0xe7,0x28 ; -(2pi)^3)/3! + fcb 0x83,0x49,0x0f,0xda,0xa2 ; 2*pi +; these 12 bytes are unused + fcb 0xa1,0x54,0x46,0x8f,0x13,0x8f,0x52,0x43 + fcb 0x89,0xcd,0xa6,0x81 +; these are a copy of the interrupt vectors that live at the top of the ROM. It's not clear +; why these vectors have been modified since they are not actually used. + fdb INT.SWI3 ; SWI3 + fdb INT.SWI2 ; SWI2 + fdb INT.FIRQ ; FIRQ + fdb INT.IRQ ; IRQ + fdb INT.SWI ; SWI + fdb INT.NMI ; NMI + fdb L8C1B ; RESET +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Coco3 internal ROM, upper 32K +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; This is the initialization code specific to the Coco3. This handles copying the ROMs to RAM and adding various patches in. +; This sequence of code demonstrates clearly that the creators of the Coco3 additions were rushed and didn't have a clear +; understanding of the Coco3 hardware or how Color Basic works. There is evidence of last minute adjustments along with code +; that serves no purpose but which is still present. +; +; There is also a major bug. The F1 for burst phase invert enable is clearly supposed to be enabled for the HSCREEN graphics +; modes. However, the code that enables it actually patches the wrong byte in the graphics mode initializers. Instead of enabling +; the burst phase invert bit in FF98, it actually enables the FIRQ enable bit in FF90. +SC000 orcc #0x50 ; make sure interrupts are disabled + lds #0x5eff ; put the stack somewhere + lda #0x12 ; nuclear green colour + ldb #16 ; 16 palette registers + ldx #PALETREG ; point to palette registers +SC00D sta ,x+ ; set a palette register to green + decb ; done? + bne SC00D ; brif not + ldx #MMUREG ; point to MMU registers + leay MMUIMAGE,pcr ; point to MMU initializer + ldb #16 ; there are 16 MMU registers +SC01B lda ,y+ ; copy an MMU initializer + sta ,x+ + decb ; done all? + bne SC01B ; brif not + lda #COCO+MMUEN+MC3+MC2+MC1 ; enable coco compatible, mmu, SCS, FExx, and 32K internal + sta INIT0 + leax BEGMOVE,pcr ; point to start of relocated initialization code + ldy #0x4000 ; point to RAM address where it goes +SC02F ldd ,x++ ; copy four bytes + ldu ,x++ + std ,y++ + stu ,y++ + cmpx #ENDMOVE ; copied everything? + blo SC02F ; brif not + jmp 0x4000 ; transfer control to code in RAM +; The rest runs from RAM. This allows it to mess with the ROM mapping (for the ROM/RAM copy). Unfortunately, +; this clobbers an 8K memory block *before* it determines that it isn't going to copy ROM to RAM which is +; somewhat problematic for things that intercept a warm start. +BEGMOVE leas -1,s ; make a hole on the stack + nop ; space fillers; probably something removed at the eleventh hour + nop + nop + nop + nop + lda #0xff ; set GIME timer to maximum value and start it counting + sta V.TIMER + sta V.TIMER+1 + leax VIDIMAGE,pcr ; point to video mode initializer + ldy #VIDEOMOD ; point to video mode registers +SC056 lda ,x+ ; copy a byte + sta ,y+ + cmpy #MMUREG ; done? + bne SC056 ; brif not + ldx #PIA1 ; point to PIA1 + ldd #0xff34 ; set up for initializing PIAs + clr 1,x ; set PIA1 DA to direction mode + clr 3,x ; set PIA1 DB to direction mode + deca + sta ,x ; set PIA1 DA bits 7-1 as output, 0 as input + lda #0xf8 ; set PIA1 DB bits 7-3 as output, 2-0 as input + sta 2,x + stb 1,x ; set PIA1 DA to data mode + stb 3,x ; set PIA1 DB to data mode + clr 2,x ; set VDG to alpha-numeric + lda #2 ; set RS232 to marking + sta ,x + lda #0xff + ldx #PIA0 ; point to PIA0 + clr 1,x ; set PIA0 DA to direction mode + clr 3,x ; set PIA0 DB to direction mode + clr ,x ; set PIA0 DA to input + sta 2,x ; set PIA0 DB to output + stb 1,x ; set PIA0 DA to direction mode + stb 3,x ; set PIA0 DB to direction mode + ldb #12 ; there are 12 SAM bits to reset + ldu #SAMREG ; point to SAM register +SC091 sta ,u++ ; clear a bit + decb ; done all? + bne SC091 ; brif not + sta SAMREG+9 ; put VDG display at 0x400 + tfr b,dp ; set direct page to 0 + clr 2,x ; strobe all keyboard columns (pointless) + sta -3,u ; select RAM page 1 (also pointless) + ldx #PIA0 ; point to PIA0 (unneeded - already points there) + ldb #0xdf ; column strobe for F1 + stb 2,x ; strobe for F1 + lda ,x ; get row data + coma ; set nonzero if F1 down + anda #0x40 + sta ,s ; save F1 state for later + ldy #2 ; check for two keys +SC0B1 asrb ; shift strobe (why not just shift directly in the PIA?) + stb 2,x ; strobe new column + lda ,x ; get row data + coma ; set nonzero if CTRL or ALT is down + anda #0x40 + beq SC0C2 ; brif not - we don't have C-A-RESET + leay -1,y ; done both? + bne SC0B1 ; brif not + lbra SC1F0 ; go do easter egg picture if C-A-RESET +SC0C2 lda #COCO+MMUEN+MC3+MC1 ; turn off standard SCS (why?) + sta INIT0 +; This checks if we have a valid warm start routine. If there is one, we don't do a ROM/RAM copy. This and +; everything above could just as easily have been done from ROM. + lda INT.FLAG ; are the bounce vectors valid? + cmpa #0x55 + bne SC0F6 ; brif not - copy ROM to RAM + lda RSTFLG ; is the DP reset vector marked valid? + cmpa #0x55 + bne NOWARM ; brif not + ldx RSTVEC ; does the vector point to NOP? + lda ,x + cmpa #0x12 + lbeq SC18C ; brif so - don't do ROM/RAM copy +NOWARM clr MMUREG ; put bottom memory block in logical block 0 (replaces DP) + lda RSTFLG ; does this give us a valid reset vector? + cmpa #0x55 + bne SC0F1 ; brif not + ldx RSTVEC ; does this routine point to a NOP? + lda ,x + cmpa #0x12 + lbeq SC18C ; brif so - don't do ROM/RAM copy and keep modified memory map +SC0F1 lda #BLOCK7.0 ; restore memory map + sta MMUREG +SC0F6 ldx #DOSBAS ; point to the end of Color Basic + ldy #EXBAS ; point to start of Extended Basic + lbsr SC1AA ; copy them to RAM + leay PATCHTAB,pcr ; point to patch table + lda ,y+ ; get number of patches to be made +SC106 pshs a ; save patch counter + ldx ,y++ ; get address to patch + ldb ,y+ ; get number of bytes in the patch +SC10C lda ,y+ ; copy a byte + sta ,x+ + decb ; done all in this patch? + bne SC10C + puls a ; get back patch counter + deca ; done all patches? + bne SC106 ; brif not + clr TYCLR ; got back to ROM mode + lda #COCO+MMUEN+MC3 ; set up for 16K split mode + sta INIT0 + ldd DOSBAS ; is there a Disk Basic ROM signature? + cmpa #'D ; (note that this should just be CMPD) + bne SC137 + cmpb #'K + bne SC137 + ldx #SUPERVAR ; point to end of Disk Basic ROM + ldy #DOSBAS ; point to start of Disk Basic ROM + bsr SC1AA ; copy it to RAM + lbsr SC322 ; add patches to Disk Basic +SC137 clr TYCLR ; go back to ROM mode + lda #COCO+MMUEN+MC3+MC1 ; set 32K internal mocde + sta INIT0 + ldx #H.CRSLOC ; point to end of the Coco3 additions + ldy #SUPERVAR ; point to start of the Coco3 additions + bsr SC1AA ; copy it to RAM + lbsr SC1DE ; set up an easter egg + leay INTIMAGE,pcr ; point to bounce vector initializer + ldx #INT.FLAG ; point to bounce vectors + ldb #19 ; 19 bytes in bounce vectors + lbsr MOVE.XY ; initialize the bounce vectors + clr TYSET ; enable RAM mode (the ROM/RAM copy already did this) + tst ,s ; was F1 pressed? + beq SC180 ; brif not + ldx #IM.TEXT ; point to text mode initializers + ldb #3 ; there are three sets of them + leax 1,x ; move past the FF90 initializer +SC165 lda ,x ; get video mode initializer + ora #0x20 ; enable burst phase invert + sta ,x ; update initializer + leax 9,x ; move to next mode + decb ; done all of them? + bne SC165 ; brif not + ldb #2 ; two graphics mode initalizers + ldx #IM.GRAPH ; point to graphics mode initializers (should be +1; we're actually enabling GIME FIRQ) +SC175 lda ,x ; get initializer + ora #0x20 ; enable burst phase invert (or it would if X pointed to the right place) + sta ,x ; save modified initializer + leax 9,x ; move to next set + decb ; done all of them? + bne SC175 ; brif not +SC180 ldx #VIDRAM ; point to start of VDG text screen + lda #0x60 ; VDG space character +SC185 sta ,x+ ; blank a character + cmpx #VIDRAM+512 ; end of screen? + blo SC185 ; brif not +SC18C lda #COCO+MMUEN+MC3+MC2+MC1 ; turn the SCS back on + sta INIT0 + tst ,s ; F1? + beq SC19A ; brif not + lda #0x20 ; enable burst phase invert + sta VIDEOMOD +SC19A ldx #PALETREG ; point to palette registers + leay PALIMAGE,PCR ; point to palette initializer + ldb #16 ; do 16 palette registers + bsr MOVE.XY ; initialize palette + leas 1,s ; clean up stack (not much point since it will be reset anyway) + jmp RESVEC ; transfer control to the original Color Basic initialization routine +SC1AA stx 0x5f02 ; save end copy address + sts 0x5f00 ; save stack +SC1B1 clr TYCLR ; go to ROM mode + ldd ,y ; grab 8 bytes + ldx 2,y + ldu 4,y + lds 6,y + clr TYSET ; go to RAM mode + std ,y ; save the 8 bytes + stx 2,y + stu 4,y + sts 6,y + leay 8,y ; move pointer forward + cmpy 0x5f02 ; done yet? + blo SC1B1 ; brif not + lds 0x5f00 ; restore stack pointer + rts +MOVE.XY lda ,y+ ; copy a byte + sta ,x+ + decb ; done all? + bne MOVE.XY ; brif not + rts +SC1DE ldx #AUTHORMS ; point to author name easter egg + leay SC30D,pcr ; point to encoded names + ldb #21 ; 21 bytes in names +SC1E7 lda ,y+ ; get encoded byte + coma ; decode (wow. one's complement encoding.) + sta ,x+ ; put in copied ROM + decb ; done all? + bne SC1E7 ; brif not + rts +SC1F0 clra ; set up to mark things as invalid + sta INT.FLAG ; mark bounce vectors invalid + sta RSTFLG ; mark reset vector invalid + sta TYCLR ; go to ROM mode + ldb #9 ; foreground colour for image + stb PALETREG+10 + ldb #63 ; white background + stb PALETREG+11 + ldx #AUTHPIC ; point to author picture data + ldy #0xe00 ; put picture at 0xe00 in memory +SC20A ldd ,x++ ; copy four bytes + ldu ,x++ + std ,y++ + stu ,y++ + cmpx #AUTHPICe ; at end of picture data? + blo SC20A ; brif not + lda #0xf9 ; 256x192, CSS0 VDG mode + sta PIA1+2 + clra ; this instruction is useless + ldx #SAMREG ; point to SAM register + sta ,x ; set SAM address to 0xe00 and video mode to 256x192 + sta 3,x + sta 5,x + sta 7,x + sta 9,x + sta 11,x +WAITLOOP bra WAITLOOP ; freeze the system +VIDIMAGE fcb 0x00,0x00,0x00,0x00,0x0f,0xe0,0x00,0x00 +PALIMAGE fcb 18,36,11,7,63,31,9,38,0,18,0,63,0,18,0,38 +MMUIMAGE fcb BLOCK7.0,BLOCK7.1,BLOCK6.4,BLOCK7.3 + fcb BLOCK7.4,BLOCK7.5,BLOCK7.6,BLOCK7.7 + fcb BLOCK7.0,BLOCK6.0,BLOCK6.1,BLOCK6.2 + fcb BLOCK6.3,BLOCK7.5,BLOCK6.5,BLOCK7.7 +PATCHTAB fcb 27 ; 27 patches to install +; Patch #1: enable warm start routine after ROM/RAM copy +patch1 fdb XBWMST + fcb patch2-*-1 + nop +; Patch #2: intercept tokenization routine +patch2 fdb LB8D4 + fcb patch3-*-1 + jmp ALINK2 +; Patch #3: intercept detokenization routine +patch3 fdb LB7F3 + fcb patch4-*-1 + jmp ALINK3 +; Patch #4: intercept Extended Basic's command interpretation handler +patch4 fdb L8150 + fcb patch5-*-1 + jmp ALINK4 + nop +; Patch #5: intercept Extended Basic's function handler +patch5 fdb L816C + fcb patch6-*-1 + jmp ALINK5 + nop +; Patch #6 through patch #10 - extend &H and &O to allow 24 bit values +patch6 fdb L8834 + fcb patch7-*-1 + jmp ALINK6A + clr FPA0+1 + clr FPA0+2 + clr FPA0+3 + bra *-78 + clr FPA0 + bra *-47 + jmp ALINK6B +patch7 fdb L87EB + fcb patch8-*-1 + bra *+76 + nop + rts + ldx #FPA0+1 +patch8 fdb L880C + fcb patch9-*-1 + bra *+55 +patch9 fdb L8826 + fcb patch10-*-1 + bcs *+25 +patch10 fdb L87E7 + fcb patch11-*-1 + bne *+7 +; Patch #11 is needed because the above removed an RTS used by this routine +patch11 fdb L886A + fcb patch12-*-1 + bne *-124 +; Patch #12 - intercept signon message display +patch12 fdb L80B2 + fcb patch13-*-1 + jmp ALINK12 +; Patch #13 - remove one carriage return from signon message +patch13 fdb L80E8+82 + fcb patch14-*-1 + fcb 0 +; Patch #14 - extend Extended Basic's graphics initialization routine +patch14 fdb L9703 + fcb patch15-*-1 + jmp ALINK14 +; Patch #15 - intercept break check +patch15 fdb LADF0 + fcb patch16-*-1 + jmp ALINK15 + nop +; Patch #16 - intercept break check when handling "line input" +patch16 fdb LA3C2 + fcb patch17-*-1 + jmp ALINK16 + nop +; Patch #17 - cause INPUT to respond to ON BRK +patch17 fdb LB03C+1 + fcb patch18-*-1 + fdb ALINK17 +; Patch #18 - intercept ON command +patch18 fdb ON + fcb patch19-*-1 + jmp ALINK18 +; Patch #19 - add on extra stuff to end of NEW +patch19 fdb LAD3F + fcb patch20-*-1 + jmp ALINK19 + nop +; Patch #20 - intercept error handler +patch20 fdb LAC46 + fcb patch21-*-1 + jmp ALINK20 +; Patch #21 - intercept immediate mode loop +patch21 fdb LAC73 + fcb patch22-*-1 + jmp ALINK21 +; Patch #22 - intercept character to screen routine +patch22 fdb LA30A + fcb patch23-*-1 + jmp L8C37 +; Patch #23 - intercept CLS +patch23 fdb CLS + fcb patch24-*-1 + jmp L8C46 +; Patch #24 - intercept waiting for keypress with cursor routine +patch24 fdb LA1B1 + fcb patch25-*-1 + jmp LA0CE + nop + nop + nop + nop + nop +; Patch #25 - intercept PRINT @ +patch25 fdb LB902 + fcb patch26-*-1 + jmp ALINK25 +; Patch #26 - intercept conditional newline routine +patch26 fdb LB95C + fcb patch27-*-1 + jmp ALINK26 +; Patch #27 - intercept CLEAR handling in line input routine +patch27 fdb LA38D + fcb patch27e-*-1 + jmp ALINK27 +patch27e equ * +; Names of the authors in one's complemented ASCII +SC30D fcb 0xab,0xd1,0xb7,0x9e,0x8d,0x8d,0x96,0x8c + fcb 0xdf,0xd9,0xdf,0xab,0xd1,0xba,0x9e,0x8d + fcb 0x93,0x9a,0x8c,0xf2,0xff +SC322 lda DOSBAS+4 ; get MSB of DSKCON vector + cmpa #0xd6 ; is it 0xd6? + bne SC334 ; brif not - we have Disk Basic 1.1 + ldx #0xc0c6 ; point to patch address in Disk Basic 1.0 + leay SC355,pcr ; point to patch for Disk Basic 1.0 + ldb ,y+ ; get number of bytes to patch + bra SC349 ; go patch it +SC334 ldx #0xC8B4 ; point to Disk Basic 1.1 keyboard check (in the interpretation loop handler) + lda #0x12 ; NOP opcode + ldb #11 ; clobber 11 bytes (which check for a key down before calling the break check) +SC33B sta ,x+ ; put a NOP + decb ; done? + bne SC33B ; brif not + ldx #0xc0d9 ; point to the Disk Basic 1.1 patch address + leay SC351,pcr ; point to patch for Disk Basic 1.1 + ldb ,y+ ; get number of bytes in patch +SC349 lda ,y+ ; put a byte from the patch + sta ,x+ + decb ; done yet? + bne SC349 ; brif not + rts +; Copyright message patch for Disk Basic 1.1 +SC351 fcb SC355-*-1 + jmp ALINK29 +; Copyright message patch for Disk Basic 1.0 +SC355 fcb SC355e-*-1 + jmp ALINK28 +SC355e equ * +; This is the initializer for the bounce vector table. It sets up to transfer control to Color Basic's +; interrupt vectors at 0x100. These really should be JMP instead of LBRA, if only because JMP is faster. +INTIMAGE fcb 0x55 ; valid vector table flag + lbra (INTIMAGE+1)-(INT.JUMP)+SW3VEC ; SWI3 + lbra (INTIMAGE+1)-(INT.JUMP)+SW2VEC ; SWI2 + lbra (INTIMAGE+1)-(INT.JUMP)+FRQVEC ; FIRQ + lbra (INTIMAGE+1)-(INT.JUMP)+IRQVEC ; IRQ + lbra (INTIMAGE+1)-(INT.JUMP)+SWIVEC ; SWI + lbra (INTIMAGE+1)-(INT.JUMP)+NMIVEC ; NMI +ENDMOVE equ * +; Unused bytes + fcb 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0x00,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0x00,0x18,0x00,0x0E,0x00 +; This is the "pmode 4" author picture easter egg +AUTHPIC fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFB,0xEE,0xEF,0xFB,0xFF,0xBB,0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xBB + fcb 0xBB,0xBB,0xBF,0xBB,0xBB,0xFF,0xBF,0xFF,0xFE,0xEF,0xFF,0xFF,0xFF,0xEF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xFF,0xBB,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBB,0xBF,0xBB,0xFF,0xBB,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0xEE,0xEE,0xEE,0xEF,0xFF,0xFE,0xFF,0xFE,0xEE,0xEE,0xFE,0xEE + fcb 0xEE,0xEE,0xEF,0xEE,0xEE,0xEE,0xEE,0xEE,0xFE,0xEE,0xEE,0xEE,0xEE,0xEE,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xFF,0xBB,0xBB,0xBB,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xBB,0xBB,0xBB,0xBB,0xBB,0x7F,0xFF + fcb 0xFF,0xF6,0xAA,0xAE,0xAA,0xAE,0xAA,0xEA,0xBB,0xBB,0xFB,0xFF,0xBB,0xFF,0xBF,0xBF + fcb 0xFF,0xFB,0xBF,0xBB,0xBB,0xBB,0xBB,0xBB,0xBA,0xEA,0xAA,0xEE,0xAE,0xEE,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xBF,0xBB,0xBB,0xBB,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xBB,0xBB,0xBB,0xBB,0xBB,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEF,0xFE,0xEF,0xFF,0xFF,0xFF + fcb 0xFF,0xFE,0xEF,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBB,0xBB,0xBB,0xBB,0xBB,0x7F,0xFF + fcb 0xFF,0xF6,0xAA,0xEE,0xAA,0xAA,0xAA,0xEA,0xAB,0xBB,0xBB,0xFF,0xBB,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFB,0xBF,0xFF,0xFF,0xBB,0xBB,0xEA,0xAE,0xAA,0xAA,0xAF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xBB,0xBF,0xFF,0xFB,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xBB,0xBB,0xBB,0xBB,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0xEE,0xEF,0xEF,0xEE,0xEE,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEE,0xEE,0xFF,0xEF,0xEE,0xEE,0xEF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBB,0xBB,0xBB,0x7F,0xFF + fcb 0xFF,0xF7,0xEA,0xAA,0xBB,0xBB,0xBB,0xBB,0xBB,0xFB,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xFB,0xFB,0xBB,0xBB,0xBA,0xAA,0xAA,0xEF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xBB,0xBB,0xBB,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0xEE,0xFE,0xEE,0xEE,0xEE,0xEE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xEE,0xEF,0xFE,0xEE,0xEE,0xEE,0xEF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xBB,0xFB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xBB,0xBB,0xBB,0x7F,0xFF + fcb 0xFF,0xF6,0xEA,0xAB,0xAA,0xAB,0xBB,0xBB,0xBF,0xBF,0xFF,0xFF,0xFF,0x80,0x00,0x7F + fcb 0xFF,0xFF,0xC0,0x00,0x3F,0xFF,0xFF,0xBB,0xBB,0xBF,0xAA,0xAA,0xAA,0xEF,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xBB,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBB,0xBB,0xBB,0xBF,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0xEE,0xEF,0xEE,0xFF,0xFF,0xFF,0xEE,0xFF,0xFF,0x80,0x00,0x3F + fcb 0xFF,0xFF,0x80,0x00,0x3F,0xFF,0xFF,0xEE,0xFE,0xEE,0xEE,0xEE,0xEE,0xEF,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xBB,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xBB,0xBB,0xBB,0xBF,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xAB,0xAE,0xEB,0xBB,0xBF,0xFB,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0x1F + fcb 0xFF,0xFF,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xBB,0xBB,0xAA,0xAA,0xBE,0xAB,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xBF,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFB,0xBB,0xBB,0xBF,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0xEE,0xEE,0xEE,0xEF,0xFE,0xEF,0xFF,0xFF,0xFF,0x80,0x00,0x0F + fcb 0xFF,0xFE,0x00,0x00,0x3F,0xFF,0xFF,0xEF,0xEF,0xEE,0xEE,0xEE,0xEE,0xEF,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xBF,0xFB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0xFF,0xBB,0xBF,0x7F,0xFF + fcb 0xFF,0xF6,0xEA,0xAB,0xBA,0xAB,0xBB,0xFB,0xFF,0xBF,0xFF,0xFF,0xFF,0x80,0x00,0x07 + fcb 0xFF,0xFC,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xFB,0xAA,0xFF,0xFE,0xEA,0xAB,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xBF,0xFB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBB,0xBF,0xFF,0xBB,0xBF,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0xFF,0xFE,0xEE,0xEE,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0x03 + fcb 0xFF,0xF8,0x00,0x00,0x3F,0xFF,0xFF,0xFE,0xEE,0xEE,0xFF,0xFE,0xEE,0xEF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0xBB,0xBF,0xFF,0xBB,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xAA,0xFB,0xBB,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0x01 + fcb 0xFF,0xF0,0x00,0x00,0x3F,0xFF,0xFF,0xBB,0xBB,0xAE,0xBB,0xBE,0xAF,0xBB,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBB,0xFF,0xFF,0xBB,0xFF,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0x80,0x08,0x00 + fcb 0xFF,0xE0,0x02,0x00,0x3F,0xFF,0xFF,0xFE,0xEF,0xEE,0xFF,0xEE,0xEF,0xEF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xBB,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xAA,0xBB,0xBB,0xFB,0xBB,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0x80,0x0C,0x00 + fcb 0x7F,0xC0,0x06,0x00,0x3F,0xFF,0xFF,0xFF,0xBB,0xFF,0xFF,0xBF,0xEB,0xBB,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x0E,0x00 + fcb 0x3F,0x80,0x0E,0x00,0x3F,0xFF,0xFF,0xFE,0xFF,0xFF,0xFE,0xEE,0xEE,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF3,0xEA,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x0F,0x00 + fcb 0x1F,0x00,0x1E,0x00,0x3F,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xBF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xEE,0xEE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x0F,0x80 + fcb 0x0E,0x00,0x3E,0x00,0x3F,0xFF,0xFF,0xEE,0xFF,0xFF,0xFF,0xFE,0xEF,0xEF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF,0x7F,0xFF + fcb 0xFF,0xF6,0xEA,0xBB,0xBB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x0F,0xC0 + fcb 0x00,0x00,0x7E,0x00,0x3F,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xBA,0xEF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBB,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x0F,0xE0 + fcb 0x00,0x00,0xFE,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFE,0xEF,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBB,0x7F,0xFF + fcb 0xFF,0xF6,0xEB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x0F,0xC0 + fcb 0x00,0x00,0x3E,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xEB,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00 + fcb 0x00,0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBA,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEF,0xFF,0xFF,0xFF,0xC0,0x1F,0xFF,0xFF,0xFF,0xFF,0x80,0x0C,0x00 + fcb 0x00,0x00,0x1E,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEE,0xEB,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xFF,0xFF,0xFF,0xFC,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xAA,0x7F,0xFF + fcb 0xFF,0xF6,0xAB,0xBB,0xFF,0xFF,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0x80,0x0C,0x00 + fcb 0x00,0x00,0x06,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0x79,0x9F,0xFB,0xAB,0x7F,0xFF + fcb 0xFF,0xF7,0xBB,0xFF,0xFF,0xF8,0x00,0x00,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00 + fcb 0x00,0x38,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x03,0xFE,0xEE,0x7F,0xFF + fcb 0xFF,0xF6,0xEF,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x4F,0xFF,0xFF,0xFF,0x80,0x00,0x00 + fcb 0x01,0x7C,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0x00,0xFA,0xBB,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x01,0xFF,0xFF,0xF0,0x00,0x00,0x00 + fcb 0x03,0xFE,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x7E,0xEE,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x80,0x00,0x00 + fcb 0x0F,0xFF,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x3B,0xBB,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0xFF,0xFF,0x00,0x00 + fcb 0x2F,0xFF,0x00,0x7F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x0E,0xEE,0x7F,0xFF + fcb 0xFF,0xF6,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0xFF,0x80,0x00,0x01 + fcb 0xFF,0xFF,0x80,0x00,0x3F,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x07,0xBB,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0xF0,0x00,0x00,0x03 + fcb 0xFF,0xFF,0x40,0x00,0x03,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x03,0xEE,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xFF,0xF0,0x00,0x00,0x00,0x01,0x00,0x1F,0xFF,0xFF,0x80,0x01,0x07 + fcb 0xFF,0xFF,0xA0,0x00,0x3F,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x00,0x01,0xBB,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x01,0x80,0x1F,0xFF,0xFF,0xFE,0x0E,0x2F + fcb 0xFF,0xFF,0xC0,0x1F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0xEE,0x7F,0xFF + fcb 0xFF,0xF6,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x65,0xE0,0x0F,0xFF,0xFF,0x80,0x0C,0x1F + fcb 0xFF,0xFF,0xE0,0x00,0x3F,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x3B,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xC0,0x00,0x00,0x0B,0xFF,0xF0,0x0F,0xFF,0xFC,0x00,0x18,0x0F + fcb 0xF8,0x00,0x60,0x00,0x07,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x0E,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xFF,0xC0,0x00,0x01,0xFF,0xFF,0xF8,0x0F,0xFF,0xFF,0x80,0x00,0x01 + fcb 0xF8,0x00,0x20,0x00,0x3F,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x1B,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0x80,0x00,0x03,0xFF,0xFF,0xFC,0x0F,0xFF,0xFF,0xFC,0x03,0x03 + fcb 0xFF,0x7F,0xF0,0x03,0xFF,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x0E,0x7F,0xFF + fcb 0xFF,0xF7,0xFE,0xFF,0x80,0x01,0x5D,0xFF,0xFF,0xFC,0x0F,0xFF,0xFF,0xC0,0x07,0xF1 + fcb 0xFC,0x07,0xFC,0x00,0x3F,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x03,0x7F,0xFF + fcb 0xFF,0xF7,0xBF,0xFF,0x00,0x02,0xEF,0xFF,0xFF,0xFC,0x0F,0xFF,0xFF,0x00,0x0E,0x00 + fcb 0x7C,0x00,0xF4,0x00,0x0C,0x7F,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,0x0E,0x7F,0xFF + fcb 0xFF,0xF2,0xAB,0xFF,0x00,0x05,0xFF,0xFF,0xFF,0xFE,0x0F,0xFF,0xFF,0xF8,0x1C,0x00 + fcb 0xFC,0x01,0xF8,0x01,0xFB,0xBF,0xFF,0xF8,0x00,0x00,0x0F,0xE1,0x80,0x0B,0x7F,0xFF + fcb 0xFF,0xF7,0xBF,0xFF,0x00,0x03,0xFF,0xFF,0xFF,0xFE,0x0F,0xFF,0xFF,0xF8,0x1F,0xC0 + fcb 0xFE,0x3F,0xFC,0x01,0xF6,0xDF,0xFF,0xF8,0x00,0x3F,0xFF,0xFF,0x80,0x0E,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0x00,0x07,0xFF,0xFF,0xFF,0xFE,0x0F,0xFE,0xFF,0xFC,0x1F,0xF1 + fcb 0xFF,0x9F,0xF8,0x37,0xF5,0xDF,0xFF,0xF8,0x00,0xFF,0xFF,0xFF,0x80,0x0B,0x7F,0xFF + fcb 0xFF,0xF7,0xBF,0xFF,0x00,0x07,0xFF,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xF8,0x0F,0xEF + fcb 0x7F,0xFF,0xFC,0x03,0xF6,0xDF,0xFF,0xF8,0x7F,0xFF,0xFF,0xFF,0x80,0x06,0x7F,0xFF + fcb 0xFF,0xF6,0xFF,0xBF,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0x07,0xFB,0xBB,0xFD,0xDF,0xFF + fcb 0xEF,0xFF,0xF9,0x7F,0xFB,0xBF,0xFF,0xFC,0x7F,0xFF,0xFF,0xFF,0xC0,0x0B,0x7F,0xFF + fcb 0xFF,0xF7,0xBF,0xFE,0x00,0x03,0xFF,0xFF,0xFF,0xF8,0x07,0xFF,0xFF,0xFF,0x9F,0xFF + fcb 0xFF,0xFF,0xFF,0xBF,0xFC,0x7F,0xFF,0xFC,0x75,0x0F,0xFF,0xCF,0xC0,0x0E,0x7F,0xFF + fcb 0xFF,0xF6,0xFF,0xFE,0x00,0x07,0xF5,0x47,0xFF,0xE0,0x07,0xFF,0xEF,0xFF,0xCF,0xFD + fcb 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0xFC,0x00,0x00,0xE0,0x07,0xC0,0x0F,0x7F,0xFF + fcb 0xFF,0xF7,0xBF,0xFF,0x00,0x0F,0xF8,0x07,0xFF,0xDC,0x07,0xFF,0xFF,0xFF,0xCF,0xFB + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x03,0xE0,0x0D,0x7F,0xFF + fcb 0xFF,0xF6,0xFF,0xFE,0x00,0x1F,0xD1,0x1F,0xFF,0xEF,0x07,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xF1,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x7B,0xF0,0x0B,0x7F,0xFF + fcb 0xFF,0xF3,0xBF,0xFE,0x00,0x1F,0xFF,0xEF,0xFE,0x81,0x07,0xFF,0xFF,0xFF,0x7F,0xF8 + fcb 0x40,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x60,0x01,0xFC,0x1D,0xF0,0x0D,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xFF,0x00,0x1F,0xFF,0x01,0xFE,0x01,0x87,0xFE,0xEE,0xFF,0xFF,0xF0 + fcb 0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xE0,0x03,0xF0,0x67,0xF0,0x17,0x7F,0xFF + fcb 0xFF,0xF3,0xBF,0xFE,0x00,0x1F,0xF8,0x01,0xFC,0x03,0x87,0xFF,0xFF,0xFF,0xFF,0xC0 + fcb 0x00,0x0F,0xFC,0x3F,0xFF,0xFF,0xFF,0xFF,0xC0,0x03,0xF0,0x33,0xF0,0x5F,0x7F,0xFF + fcb 0xFF,0xF6,0xAF,0xFE,0x00,0x1F,0xFF,0xF0,0xFE,0x0F,0x87,0xFF,0xBF,0xFF,0xFF,0x80 + fcb 0xFF,0x07,0xF8,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0x03,0xFA,0xFF,0xF1,0xBB,0x7F,0xFF + fcb 0xFF,0xF3,0xBF,0xBF,0x00,0x1F,0xFF,0xFE,0xFE,0x9B,0x87,0xFF,0xFF,0xFF,0xFF,0xBD + fcb 0x80,0xF3,0xF8,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0x17,0xFF,0xFF,0xF4,0xEE,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xFF,0x00,0x1F,0xFF,0xFF,0xFF,0x03,0x87,0xEF,0xEE,0xEF,0xFF,0x00 + fcb 0x00,0x07,0xF8,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0x77,0xFF,0xFF,0xFE,0xBB,0x7F,0xFF + fcb 0xFF,0xF3,0xBF,0xBF,0x00,0x0F,0xFF,0xEF,0xF8,0x83,0x87,0xFF,0xFF,0xFF,0xFF,0xBE + fcb 0xBF,0xFF,0xF8,0x7F,0xFF,0xFF,0xFF,0xFF,0xFD,0xB3,0xFF,0xFF,0xFD,0xEE,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEF,0x00,0x0F,0xFF,0xF8,0x7F,0xF7,0x8F,0xFF,0xFB,0xBF,0xFF,0xFF + fcb 0xFF,0xF7,0xFC,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0xBB,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xBB,0x00,0x0F,0xFF,0xF3,0x7F,0x0F,0x8F,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xBF,0xE3,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0xEE,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEF,0x00,0x0F,0xFE,0xF7,0xFF,0xCF,0x8E,0xFF,0xFF,0xEF,0xFF,0xFF + fcb 0x17,0xF7,0xFD,0xEF,0xEF,0xFE,0xFF,0xFF,0xFF,0x9F,0xFF,0xFF,0xFF,0xBB,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xBB,0x00,0x07,0xFF,0xFE,0xFF,0xC1,0x9F,0xFF,0xFF,0xFF,0xFF,0xFE + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0xF7,0xFF,0xFE,0xEE,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0x00,0x03,0xFF,0xC4,0x1C,0x03,0x9B,0xFB,0xAF,0xBF,0xFF,0xFF + fcb 0xFF,0xFF,0xFE,0xFB,0xFB,0xBB,0xFF,0xBF,0xFE,0x02,0xAB,0xFF,0xF8,0xFB,0x7F,0xFF + fcb 0xFF,0xF3,0xBF,0xBA,0x04,0x03,0xFA,0x00,0x00,0x01,0x9F,0xFF,0xBB,0xFF,0xFB,0xFF + fcb 0xFF,0xFF,0xFB,0xBF,0xFF,0xFB,0xFE,0xEF,0xFC,0x00,0x05,0xFF,0xF9,0xEE,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEC,0x06,0x01,0xFC,0xA2,0x00,0x03,0x9F,0xEE,0xEE,0xEF,0xFE,0xFF + fcb 0xFF,0xFF,0xFE,0xFF,0xFE,0xEE,0xEF,0xBF,0xFE,0x00,0x00,0x3F,0xFB,0xBB,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xB0,0x00,0x01,0xFA,0x10,0x00,0x03,0x9F,0xFB,0xBB,0xFF,0xFB,0xFF + fcb 0xFF,0xFF,0xFB,0xBF,0xFF,0xFB,0xBE,0xEF,0xFC,0x0F,0x80,0x1F,0xF6,0xEE,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0x00,0x00,0x68,0x5F,0xFE,0x03,0x1B,0xAE,0xAE,0xFB,0xEE,0xFD + fcb 0xFF,0xFF,0xFE,0xAB,0xBB,0xEA,0xFF,0xBF,0xF8,0x0B,0xFC,0x1F,0xFF,0xBB,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0xBA,0x00,0x00,0x7B,0x0F,0xFE,0x00,0x1F,0xBB,0xBB,0xFF,0xFB,0xFF + fcb 0xFF,0xFF,0xFB,0xBB,0xFF,0xBB,0xBE,0xEF,0xFC,0x07,0xFC,0x5F,0xF6,0xEE,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0xEE,0x00,0x00,0x14,0x40,0x3C,0x00,0x0E,0xEE,0xEE,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFE,0xEE,0xEE,0xEE,0xEF,0xBF,0xFE,0x00,0x61,0xFF,0xFF,0xBB,0x7F,0xFF + fcb 0xFF,0xF3,0xBB,0x80,0x00,0x00,0x00,0x7E,0x00,0x00,0x3F,0xBB,0xFF,0xFF,0xBF,0xFF + fcb 0xFF,0xFF,0xFF,0xBB,0xFF,0xBB,0xBE,0xEF,0xFF,0xE0,0x07,0xFF,0xFF,0xEE,0x7F,0xFF + fcb 0xFF,0xF6,0xEE,0x00,0x00,0x00,0x00,0x3F,0xEE,0x80,0x2E,0xAB,0xBF,0xBE,0xFC,0xFF + fcb 0xFF,0xFF,0xFE,0xEB,0xBB,0xAA,0xBB,0xBF,0xEF,0xFA,0xFF,0xFF,0xF8,0x0B,0x7F,0xFF + fcb 0xFF,0xF3,0xBA,0x00,0x00,0xC0,0x00,0x0F,0xFE,0x00,0x3B,0xBF,0xFF,0xFB,0xB1,0xFF + fcb 0xFF,0xFF,0xF3,0xBB,0xFF,0xBB,0xAE,0xEF,0xBF,0xD9,0x7F,0xFF,0xF8,0x02,0x7F,0xFF + fcb 0xFF,0xF6,0xEC,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0xEE,0xEE,0xEF,0xEE,0xE1,0xFF + fcb 0xFF,0xFF,0xF0,0xEE,0xEE,0xEE,0xEB,0xBF,0xEF,0x8E,0x3F,0xFF,0xF8,0x00,0x7F,0xFF + fcb 0xFF,0xF3,0xBA,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0xBB,0xBB,0xBF,0xBB,0xB1,0xFF + fcb 0xFF,0xFF,0xE0,0x3B,0xFF,0xBB,0xEE,0xFF,0xBB,0x00,0x5F,0xFF,0xF8,0x00,0x7F,0xFF + fcb 0xFF,0xF6,0xE0,0x04,0x00,0x78,0x00,0x00,0x00,0x01,0xEE,0xAA,0xAE,0xEA,0xC0,0x7F + fcb 0xFF,0xFF,0x80,0x0B,0xFF,0xEF,0xAB,0xFF,0xEF,0xC2,0x3F,0xFF,0xFF,0xE0,0x7F,0xFF + fcb 0xFF,0xF3,0x80,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x3B,0xBB,0xBB,0xBB,0x00,0x3F + fcb 0xFF,0xFF,0x00,0x03,0xFF,0xFE,0xEE,0xEF,0xBB,0xC0,0x7F,0xFF,0xFE,0xF8,0x7F,0xFF + fcb 0xFF,0xF6,0x60,0xC0,0x00,0x7F,0x00,0x00,0x00,0x00,0x2E,0xEE,0xEE,0xEC,0x00,0x01 + fcb 0x3F,0xFC,0x00,0x00,0x6E,0xEF,0xFF,0xFB,0xEE,0xE2,0x3F,0xFF,0xFE,0x78,0x7F,0xFF + fcb 0xFF,0xF0,0x98,0x60,0x00,0x3F,0x80,0x00,0x00,0x00,0x03,0xBB,0xBB,0xB8,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x01,0xBB,0xBB,0xBA,0xBB,0x5F,0x9F,0xFF,0xFC,0xF8,0x7F,0xFF + fcb 0xFF,0xF6,0x00,0x30,0x00,0x0F,0xC0,0x00,0x00,0x00,0x00,0x6A,0xEE,0xA0,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFA,0xEE,0x83,0xFF,0xFF,0xFC,0xF8,0x7F,0xFF + fcb 0xFF,0xF4,0x00,0x00,0x00,0x07,0xE0,0x00,0x00,0x00,0x05,0x13,0xBB,0x80,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x2E,0xEE,0xEA,0x9B,0xA3,0xFF,0xFF,0xFD,0xF0,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x01,0xF0,0x00,0x00,0x00,0x00,0x0E,0xEE,0x80,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x2F,0xFE,0xAA,0x0E,0xED,0xFF,0xFF,0xF9,0xF0,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x03,0xBB,0x80,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x0A,0xBA,0xA8,0x0B,0xFB,0x7F,0xFF,0xF3,0xF0,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x00,0x02,0x8A,0x80,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x02,0xFE,0xA0,0x06,0xFE,0xFF,0xFF,0x07,0xF0,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x07,0x03,0x80,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x02,0xAA,0x80,0x07,0xF9,0xFF,0xFC,0x07,0xF4,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x0F,0x14,0x0E,0x00,0x00,0x02,0x80,0x80,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0xAA,0x80,0xBF,0xFF,0xBF,0xF0,0x0F,0xF6,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x03,0xFF,0xFC,0x00,0x00,0x03,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0xEA,0x0F,0xFE,0x7C,0x77,0x80,0x1F,0xF6,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x01,0xFF,0xF8,0x00,0x00,0x03,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x7F,0xFC,0xFE,0x80,0x00,0x1F,0xF7,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x03,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x37,0x3C,0xFE,0xC0,0x00,0xBF,0xF7,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x07,0x7F,0xE0,0x00,0x00,0xF3,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x3E,0xFF,0x80,0x01,0xFF,0xF7,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0xA3,0xFF,0xC0,0x00,0x00,0x63,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFD,0xEE,0x80,0x07,0xFF,0xF7,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x63,0xFF,0xC0,0x00,0x00,0x73,0x00,0x00,0x00,0x00 + fcb 0xFF,0x00,0x00,0x00,0x00,0x00,0x08,0x1F,0x78,0xF7,0xE8,0x9F,0xFF,0xFC,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x77,0xBF,0x80,0x00,0x00,0x33,0x00,0x00,0x00,0x3F + fcb 0xBB,0xE0,0x00,0x00,0x00,0x00,0x00,0x1F,0xF8,0x3B,0x55,0x7F,0x80,0x70,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x01,0xFF,0x00,0x00,0x00,0x3B,0x00,0x00,0x00,0xFF + fcb 0x3B,0xFC,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0x1D,0x8E,0xE7,0x82,0xF8,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x30,0x00,0x00,0x01,0xFE + fcb 0xF9,0x9F,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xC1,0x17,0xFF,0xFF,0xC0,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0xF8 + fcb 0xFF,0x3F,0xE0,0x00,0x04,0x00,0x00,0x0F,0xBF,0xC2,0xEF,0xFF,0xFF,0x80,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x1C,0x00,0x00,0x08,0xFB + fcb 0xFF,0x37,0xE0,0x00,0x00,0x00,0x00,0x0F,0xBF,0xC1,0x1F,0xFF,0xFF,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x1D,0x07 + fcb 0xFF,0xE3,0x68,0x00,0x00,0x00,0x00,0x07,0x7F,0xC7,0x3F,0xFF,0xFE,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F + fcb 0xFF,0xFB,0xF6,0x00,0x00,0x00,0x00,0x07,0x7F,0xE1,0x7F,0xFF,0xFC,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x01,0xC6,0x7F + fcb 0xFF,0xFF,0xE8,0x00,0x00,0x00,0x00,0x07,0x0F,0xF3,0x8F,0x97,0xFC,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xB1,0xEF + fcb 0xFF,0xEF,0xB7,0x00,0x00,0x00,0x00,0x07,0x83,0xE1,0xC6,0x00,0x78,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0xDF + fcb 0xFF,0xD1,0xA2,0x00,0x00,0x40,0x00,0x03,0x00,0xF1,0xFF,0x14,0x20,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0xFF + fcb 0xFF,0xFF,0xDF,0x00,0x00,0x00,0x00,0x03,0x06,0x70,0x7C,0x00,0xC0,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF + fcb 0xFF,0xFF,0xE7,0xE0,0x00,0x00,0x00,0x03,0x87,0xF1,0xF8,0x03,0x80,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0xAF + fcb 0xFF,0xFF,0xF7,0x00,0x00,0x00,0x00,0x03,0x8F,0xFB,0xF8,0x20,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xBF + fcb 0xFF,0xFF,0xBF,0xE0,0x00,0x00,0x00,0xF3,0x8D,0xFF,0xF8,0x5F,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0xFF + fcb 0xFF,0xFF,0x4F,0xF0,0x00,0x00,0x01,0xE3,0x87,0xFF,0xFC,0x6E,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0D,0xF7 + fcb 0xBF,0xFF,0xEF,0xE8,0x00,0x00,0x00,0x01,0x1F,0xFF,0xFF,0xFE,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0xFF + fcb 0x9F,0xFF,0xFD,0xF8,0x00,0x00,0x00,0x01,0xBF,0xFF,0xFF,0xFF,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF + fcb 0xFF,0xFF,0x8F,0xE8,0x00,0x00,0x00,0x01,0xBF,0xFF,0xFF,0xFC,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF + fcb 0xFF,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x01,0xBF,0xFF,0xFF,0xF8,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF + fcb 0xFF,0x9F,0xFF,0xF8,0x00,0x00,0x00,0x01,0x3F,0xFF,0xFF,0xF0,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0x07,0xFF + fcb 0x1F,0xFF,0xF5,0xFC,0x00,0x00,0x00,0x01,0x3F,0xFF,0xFF,0xE0,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFC + fcb 0x3F,0xFF,0xBF,0xF8,0x00,0x00,0x00,0x01,0x3F,0x85,0xFF,0xC0,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFF + fcb 0xF7,0xFF,0xDF,0xF8,0x00,0x00,0x00,0x00,0x1D,0x2A,0x07,0x80,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF + fcb 0xFF,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x01,0x06,0xFF,0x00,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x1F,0xFF + fcb 0xFF,0xFF,0xDF,0xF8,0x00,0x00,0x00,0x01,0x17,0x3D,0x95,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0x80,0x00,0x07,0xFF + fcb 0xFF,0xFF,0xEF,0xF8,0x00,0x00,0x00,0x00,0x0E,0xEF,0xDA,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFF + fcb 0xFF,0xFF,0xF7,0xF0,0x00,0x00,0x00,0x00,0x15,0xF7,0x80,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFF + fcb 0xFF,0xFF,0xED,0xE0,0x00,0x00,0x00,0x00,0x0F,0xBF,0xEC,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF + fcb 0xFF,0xFF,0xDC,0xE0,0x00,0x00,0x00,0x00,0x17,0x7F,0xD8,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF + fcb 0xFF,0xFF,0xBC,0xC0,0x00,0x00,0x00,0x00,0x3F,0xFF,0xF8,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFF + fcb 0xFF,0xFF,0xDE,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0xF8,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFF + fcb 0xFF,0xFF,0xFF,0x80,0x00,0x00,0x00,0x00,0x7F,0xFF,0xF8,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF + fcb 0xFF,0xFB,0xFF,0x80,0x00,0x00,0x00,0x00,0x7F,0xFF,0xF0,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xDF + fcb 0xFE,0xC1,0xF2,0x00,0x00,0x00,0x00,0x18,0x7F,0xFF,0xF0,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83 + fcb 0x00,0x1F,0xCC,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0xF0,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00 + fcb 0x80,0x3F,0x98,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0xF0,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE2 + fcb 0x46,0x3E,0x60,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0xE0,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80 + fcb 0x33,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x60,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xA0 + fcb 0x73,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x3A,0x80,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0 + fcb 0x08,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0xF6,0x00,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xB6,0x00,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0xED,0x80,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x2C,0x00,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0xB0,0x80,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0x00,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x7F,0xFF,0x80,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x7F,0xFF,0xC0,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0x80,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0x80,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0x80,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0x3C,0xFF,0xF3,0x3F,0xFF,0xCF,0x9F,0xFF,0xFF,0xFF,0x03,0xFF + fcb 0xCC,0xFF,0xFF,0xF3,0xFF,0xFF,0xF8,0x1F,0xFE,0x0F,0xFF,0x8F,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0x18,0xFF,0xF3,0x3F,0xFF,0xCF,0xFF,0xFF,0xFF,0xFF,0xCF,0xFF + fcb 0xCC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x7F,0xFE,0x7F,0xFF,0xCF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0x00,0xFF,0xF3,0x31,0x9E,0x49,0x10,0xE1,0xFF,0xFF,0xCF,0xFF + fcb 0xCC,0xC6,0x08,0x23,0x0F,0xFF,0xFE,0x7F,0xFE,0x7C,0x60,0xCC,0x70,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0x24,0xFF,0xF0,0x3C,0xC0,0xC3,0x92,0x4F,0xFF,0xFF,0xCF,0xFF + fcb 0xC0,0xF2,0x38,0xF2,0x7F,0xFF,0xFE,0x7F,0xFE,0x1F,0x23,0xC9,0x27,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0x3C,0xFF,0xF3,0x30,0xC0,0xC7,0x92,0x63,0xFF,0xFF,0xCF,0xFF + fcb 0xCC,0xC2,0x79,0xF3,0x1F,0xFF,0xFE,0x7F,0xFE,0x7C,0x27,0xC8,0x31,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0x3C,0xFF,0xF3,0x24,0xE1,0xC3,0x92,0x79,0xFF,0xFF,0xCF,0xFF + fcb 0xCC,0x92,0x79,0xF3,0xCF,0xFF,0xFE,0x7F,0xFE,0x79,0x27,0xC9,0xFC,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0x3C,0x9F,0xF3,0x30,0xE1,0xC9,0x92,0x43,0xFF,0xFF,0xCE,0x7F + fcb 0xCC,0xC2,0x79,0xF2,0x1F,0xFF,0xFE,0x73,0xFE,0x0C,0x27,0xCC,0x61,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF + fcb 0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +AUTHPICe equ * +; Unused + fcb 0xFF,0x00,0x00,0xA0,0x27,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xEF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF + fcb 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x40,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; The actual Super Extended Basic (SECB) extensions start here. +; +; Note that many routines in this area feature a "lbrn 0" instruction. This appears to be intended as a placeholder to allow +; patching into the routines similar to the RAM hooks in Color Basic except using direct overwiting of the instruction. It's +; completely pointless and probably illustrates that the writers of this code didn't really think through what they were doing. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +SUPERVAR fdb HRMODE ; address of direct page variables unique to SECB +PRGTEXT fdb SETTEXT ; set video registers for text mode (indirect) +PRGGRAPH fdb SETGRAPH ; set video registers for graphics mode (indirect) +PRGMMU fdb SETMMU ; set MMU registers to their "default" (indirect) +GETTEXT fdb SELTEXT ; put hi-res text screen in logical block 1 (indirect) +GETBLOK0 fdb SELBLOK0 ; put block in B in logical block 0 (indirect) +GETTASK0 fdb SELTASK0 ; re-select MMU task 0 (indirect) +GETTAKS1 fdb SELTASK1 ; select MMU task 1(indirect) + jmp LA05E ; execute non-self starting ROM +SPARE0 fdb 0 ; undefined +SPARE1 fdb 0 ; undefined +SPARE2 fdb 0 ; undefined +; Set up video registers for the selected text screen. Given that the sets of video mode initializers are contiguous in +; memory, this would probably be better done with a simple sequence of MUL and ABX. +SETTEXT pshs y,x,a ; save registers + lbrn 0 + ldx #IM.TEXT ; point to 32 column video mode registers + lda HRWIDTH ; get text mode + beq SETVIDEO ; brif 32 column + ldx #SE03B ; point to 40 column mode data + cmpa #1 ; is it 40 column? + beq SETVIDEO ; brif so + ldx #SE044 ; assume 80 column + bra SETVIDEO ; program video registers +; 32 column (VDG) initializer +IM.TEXT fcb COCO+MMUEN+MC3+MC2 ; INIT0 (COCO bit enables VDG modes) + fcb 0x00 ; VIDEOMOD (unused for VDG modes) + fcb 0x00 ; VIDEORES (unused for VDG modes) + fcb 0x00 ; V.BORDER (black) + fcb 0x00 ; filler for reserved + fcb 0x0f ; V.SCROLL - this value is needed to show a proper 12 lines per text row + fdb 0xe000 ; V.OFFSET - SAM offsets operate in 0x7xxx + fcb 0x00 ; H.OFFSET - no horizontal offset +; 40 column screen +SE03B fcb MMUEN+MC3+MC2 ; INIT0 (disable VDG modes) + fcb 0x03 ; VIDEOMOD - 8 lines per row + fcb 0x05 ; VIDEORES - 40 columns, attributes enabled, 192 lines per field + fcb 0x12 ; V.BORDER - nuclear green + fcb 0x00 ; filler for reserved + fcb 0x00 ; V.SCROLL - no offset + fdb 0xd800 ; V.OFFSET - screen at 0x6c000 + fcb 0x00 ; H.OFFSET - no offset +; 80 column screen +SE044 fcb MMUEN+MC3+MC2 ; INIT0 (disable VDG modes) + fcb 0x03 ; VIDEOMOD - 8 lines per row + fcb 0x15 ; VIDEORES - 80 columns, attributes enabled, 192 lines per field + fcb 0x12 ; V.BORDER - nuclear green + fcb 0x00 ; filler for reserved + fcb 0x00 ; V.SCROLL - no offset + fdb 0xd800 ; V.OFFSET - screen at 0x6c000 + fcb 0x00 ; H.OFFSET - no offset +; Set up video registers for the selected "HSCREEN" mode. Note that the two code paths for the resolution widths +; are unneeded. +SETGRAPH pshs y,x,a ; save registers + lbrn 0 + ldx #IM.GRAPH ; point to graphics initlializer + ldy #RESTABLE ; point to resolution bytes table + lda HRMODE ; get graphics mode + cmpa #2 ; is it a 640 pixel mode? + bls SE063 ; brif not + ldx #SE079 ; point to 640 pixel registers +SE063 suba #1 ; normalize mode numbers to start at 0 + lda a,y ; get resolution setting for this mode + sta 2,x ; put it in the graphics mode initializer + jmp SETVIDEO ; go set up video registers +RESTABLE fcb 0x15 ; 320x192, 4 colours + fcb 0x1e ; 320x192, 16 colours + fcb 0x14 ; 640x192, 2 colours + fcb 0x1d ; 640x192, 4 colours +; 320x192 graphics modes +IM.GRAPH fcb MMUEN+MC3+MC2 ; INIT0 (disable VDG modes) + fcb 0x80 ; VIDEOMOD - enable bit plane, one line per row + fcb 0x00 ; VIDEORES - placeholder + fcb 0x00 ; V.BORDER - black + fcb 0x00 ; filler for reserved + fcb 0x00 ; V.SCROLL - no offset + fdb 0xc000 ; V.OFFSET - screen at 0x60000 + fcb 0x00 ; H.SCROLL - no offset +; 640x192 graphics modes (observe that these are identical to the 320x192 ones above) +SE079 fcb MMUEN+MC3+MC2 ; INITo (disable VDG modes) + fcb 0x80 ; VIDEOMOD - enable bit plane, one line per row + fcb 0x00 ; VIDEORES - placeholder + fcb 0x00 ; V.BORDER - black + fcb 0x00 ; filler for reserved + fcb 0x00 ; V.SCROLL - no offset + fdb 0xc000 ; V.OFFSET - screen at 0x60000 + fcb 0x00 ; H.SCROLL - no offset +; Program video registers and INIT0 from (X); enter with A,X,Y pre-saved +SETVIDEO lda ,x+ ; set INIT0 + sta INIT0 + ldy #VIDEOMOD ; point to start of video mode registers +SE08B lda ,x+ ; set a register + sta ,y+ + cmpy #MMUREG ; done all of them? + blo SE08B ; brif not + puls a,x,y,pc ; restore registers and return +; Set MMU registers to their default values. All 16 of them. +SETMMU pshs y,x,b,a ; save registers + leax IM.MMU,pcr ; point to MMU initializer + bsr SE0F1 ; program MMU registers + puls a,b,x,y,pc ; restore registers and return +; Set logical block 0 to the physical block in B. This is embarassingly inefficient since it sets +; *all 16* MMU registers to change one of them. All the faffing about with pointers and the call +; to set the MMU registers is pointless. It could be done with a single STB. +SELBLOK0 pshs y,x,b,a ; save registers + leax IM.MMU,pcr ; point to MMU initializer + pshs x ; save it for later + stb ,x ; set desired block in initializer + bsr SE0F1 ; program *all 16* MMU registers (stupid) + ldb #BLOCK7.0 ; get correct block number for logical block 0 + puls x ; get back pointer to the initializer + stb ,x ; restore initializer value + puls a,b,x,y,pc ; restore registers and return +; Put hi-res text screen in logical address space block 1. This is embarassingly ineffecient since +; it sets *all 16* MMU registers to change only one. All the faffing about with pointers and the +; call to set the MMU registers is pointless. It could be done with a singel LD/ST sequence. +SELTEXT pshs y,x,b,a ; save registers + leax IM.MMU,pcr ; point to MMU initializer + pshs x ; save pointer for later + ldb #BLOCK6.6 ; get block number for text screen + stb 1,x ; put in logical block 1 of initializer + bsr SE0F1 ; program *all 16* MMU registers (stupid) + puls x ; get back pointer + ldb #BLOCK7.1 ; get proper block for logical block 1 + stb 1,x ; put it back in the initializer + puls a,b,x,y,pc ; restore registers and return +; Get block 6.4 (HBUFF buffers) to logical block 6 of task 1. This is embarassingly inefficient +; since it sets *all 16* MMU registers to change only one. All the faffing about with pointers and +; the call to set the MMU registers ispointless. It could be done with a single LD/ST sequence. +SE0CB pshs y,x,b,a ; save registers + leax IM.MMU,pcr ; point to MMU initializer + pshs x ; save pointer for later + ldb #BLOCK6.4 ; get block for the HBUFF buffers + stb 14,x ; put in logical block 6, task 1 + bsr SE0F1 ; program *all 16* MMU registers (stupid) + puls x ; get back pointer + ldb #BLOCK6.5 ; get default block for logical block 6 of task 1 + stb 14,x ; put it back in the initializer + puls a,b,x,y,pc ; restore registers and return +; MMU initializer +IM.MMU fcb BLOCK7.0,BLOCK7.1,BLOCK7.2,BLOCK7.3 ; task 0: map 0x70000-0x7ffff + fcb BLOCK7.4,BLOCK7.5,BLOCK7.6,BLOCK7.7 + fcb BLOCK7.0,BLOCK6.0,BLOCK6.1,BLOCK6.2 ; task 1: put hires gfx at 0x2000 and a stack block at 0xc000 + fcb BLOCK6.3,BLOCK7.5,BLOCK6.5,BLOCK7.7 +; Set all 16 MMU registers from the initializer pointed to by X. +SE0F1 ldy #MMUREG ; point to MMU registers + ldb #16 ; there are 16 to set +SE0F7 lda ,x+ ; set a register + sta ,y+ + decb ; done all? + bne SE0F7 ; brif not + rts +; Select task register 0 as the active MMU set. Enter with stack in a temporary location which +; holds the original stack pointer. Note that the return could be done simply as jmp [V42] instead +; of pushing the return address onto the stack. Interrupts will be enabled on the way out. +SELTASK0 std V40 ; temp save D + ldd ,s ; get return address + std V42 ; save it + ldd 2,s ; get original stack pointer + std V44 ; save it + clrb ; reset INIT1 to task 0 (and slow timer), could just be CLR INIT1 + stb INIT1 + lds V44 ; restore original stack pointer + ldd V42 ; get back return address + pshs d ; set return address on stack + ldd V40 ; get back original D + andcc #0xaf ; re-enable interrupts + rts +; Select task register 1 as the active MMU set. Exit with interrupts disabled and the original +; stack pointer saved at the top of the temporary stack. Note that jmp [V42] could be used to +; return instead of pushing the return address back on the stack. +SELTASK1 orcc #0x50 ; disable interrupts + std V40 ; temp save D + puls d ; get return address + std V42 ; save it + sts V44 ; save stack pointer + ldb #1 ; set to enable task 1, slow timer input + stb INIT1 + lds #TMPSTACK ; point to temporary stack location (top of the C000-Dfff range) + ldd V44 ; get old stack pointer + pshs d ; stash it + ldd V42 ; put original return address back + pshs d + ldd V40 ; restore original D + rts +; Tokenziation patch +ALINK2 tst V41 ; is it a function token? + bne SE152 ; brif so + lda V42 ; get token value + cmpa #0x62 ; have we reached the first SECB token? + bls SE148 ; brif not + ldu #COMVEC-5 ; point to function table and go again + jmp LB8D7 ; re-enter mainline code +SE148 lda #0x62 ; force tokens to start at the correct number (above Disk Basic) + ldu #EBCOMTAB-10 ; point to SECB command table +SE14D sta V42 ; set token counter to SECB values + jmp LB89D ; re-enter mainstream still in command mode +SE152 lda V42 ; get token number + cmpa #0x29 ; have we run through SECB functions yet? + bls SE15B ; brif not + jmp LB8D7 ; re-enter mainline code (end of processing) +SE15B lda #0x29 ; force token into SECB range (this leaves one unused) + ldu #EBCOMTAB-5 ; point to SECB function table + bra SE14D ; go transfer control back to mainline with new settings +EBCOMTAB fcb 23 ; number of keywords (commands) + fdb COMDIC20 ; keyword table (commands) + fdb ALINK4 ; interpretation handler (commands) + fcb 5 ; number of keywords (functions) + fdb FUNDIC20 ; keyword table (functions) + fdb ALINK5 ; interpretation handler (functions) + fcb 0x00,0x00,0x00,0x00,0x00,0x00 ; marker for no further tables +; Detokenization patch. This routine has a bug. It freezes if an unknown token is encountered instead +; of using the placeholder. +ALINK3 leau 10,u ; move to next table + tst ,u ; valid table? + lbne LB7F9 ; brif so - re-enter mainline code + leax -1,x ; get token number + lda ,x+ + anda #0x7f ; remove token bias + cmpa #0x62 ; SECB command? + blo SE18B ; brif not + suba #0x62 ; zero-base SECB token number + ldu #EBCOMTAB-10 ; point to command table + bra ALINK3 ; go try again +SE18B suba #0x29 ; zero-base function token + ldu #EBCOMTAB-5 ; point to SECB function table + bra ALINK3 ; go try again +; Command interpretation patch +ALINK4 cmpa #0xe2 ; is it within the SECB range? + blo SE19A ; brif not (below) + cmpa #0xf8 ; is it above range? + bls SE19E ; brif not - we have a SECB command +SE19A jmp [COMVEC+23] ; transfer control onward to Disk Basic +SE19E suba #0xe2 ; normalize SECB commands to 0 + ldx #COMDIS20 ; point to jump table for SECB commands + jmp LADD4 ; go dispatch command +; Function processing patch +ALINK5 cmpb #0x29*2 ; is it an SECB function? + blo SE1AE ; brif not (below) + cmpb #0x2d*2 ; is it still an SECB function? + bls SE1B2 ; brif so +SE1AE jmp [COMVEC+28] ; transfer control to Disk Basic +SE1B2 subb #0x29*2 ; normalize SECB functions to 0 + cmpb #2*2 ; do we need to parse a parameter? + bhs SE1BF ; brif not + pshs b ; save token offset + jsr LB262 ; parse parenthetical expression + puls b ; get back token offset +SE1BF ldx #FUNDIS20 ; point to jump table for SECB functions + jmp LB2CE ; go dispatch function call +; Keyword table (commands) for SECB +COMDIC20 fcs 'WIDTH' ; 0xe2 + fcs 'PALETTE' ; 0xe3 + fcs 'HSCREEN' ; 0xe4 + fcs 'LPOKE' ; 0xe5 + fcs 'HCLS' ; 0xe6 + fcs 'HCOLOR' ; 0xe7 + fcs 'HPAINT' ; 0xe8 + fcs 'HCIRCLE' ; 0xe9 + fcs 'HLINE' ; 0xea + fcs 'HGET' ; 0xeb + fcs 'HPUT' ; 0xec + fcs 'HBUFF' ; 0xed + fcs 'HPRINT' ; 0xee + fcs 'ERR' ; 0xef + fcs 'BRK' ; 0xf0 + fcs 'LOCATE' ; 0xf1 + fcs 'HSTAT' ; 0xf2 + fcs 'HSET' ; 0xf3 + fcs 'HRESET' ; 0xf4 + fcs 'HDRAW' ; 0xf5 + fcs 'CMP' ; 0xf6 + fcs 'RGB' ; 0xf7 + fcs 'ATTR' ; 0xf8 +; Jump table for SECB commands +COMDIS20 fdb WIDTH ; 0xe2 WIDTH + fdb PALETTE ; 0xe3 PALETTE + fdb HSCREEN ; 0xe4 HSCREEN + fdb LPOKE ; 0xe5 LPOKE + fdb HCLS ; 0xe6 HCLS + fdb HCOLOR ; 0xe7 HCOLOR + fdb HPAINT ; 0xe8 HPAINT + fdb HCIRCLE ; 0xe9 HCIRCLE + fdb HLINE ; 0xea HLINE + fdb HGET ; 0xeb HGET + fdb HPUT ; 0xec HPUT + fdb HBUFF ; 0xed HBUFF + fdb HPRINT ; 0xee HPRINT + fdb ERR ; 0xef ERR (should be LB277) + fdb BRK ; 0xf0 BRK (should be LB277) + fdb LOCATE ; 0xf1 LOCATE + fdb HSTAT ; 0xf2 HSTAT + fdb HSET ; 0xf3 HSET + fdb HRESET ; 0xf4 HRESET + fdb HDRAW ; 0xf5 HDRAW + fdb CMP ; 0xf6 CMP + fdb RGB ; 0xf7 RGB + fdb ATTR ; 0xf8 ATTR +; Keyword table for SECB functions +FUNDIC20 fcs 'LPEEK' ; 0xa9 + fcs 'BUTTON' ; 0xaa + fcs 'HPOINT' ; 0xab + fcs 'ERNO' ; 0xac + fcs 'ERLIN' ; 0xad +; Jump table for SECB functions +FUNDIS20 fdb LPEEK ; 0xa9 LPEEK + fdb BUTTON ; 0xaa BUTTON + fdb HPOINT ; 0xab HPOINT + fdb ERNO ; 0xac ERNO + fdb ERLIN ; 0xad ERLIN +; Signon message patch +ALINK12 ldx #L80E8-1 ; point to ECB's message + jsr STRINOUT ; display it + ldx #MWAREMS-1 ; point to Microware string + jsr STRINOUT ; display it + jmp L80B8 ; return to mainline code +; Signon message patch for Disk Basic 1.0 +ALINK28 ldx #DISK20MS-1 ; point to modified message + jmp DC0DC-19 ; return to mainline code +; Signon message patch for Disk Basic 1.1 +ALINK29 ldx #DISK21MS-1 ; point to modified message + jmp DC0DC ; return to mainline code +DISK20MS fcc 'DISK EXTENDED COLOR BASIC 2.0' + fcb 0x0d + fcc 'COPR. 1981, 1986 BY TANDY' + fcb 0x0d + fcc 'UNDER LICENSE FROM MICROSOFT' + fcb 0x0d +MWAREMS fcc 'AND MICROWARE SYSTEMS CORP.' + fcb 0x0d,0x0d,0x00 +DISK21MS fcc 'DISK EXTENDED COLOR BASIC 2.1' + fcb 0x0d + fcc 'COPR. 1982, 1986 BY TANDY' + fcb 0x0d + fcc 'UNDER LICENSE FROM MICROSOFT' + fcb 0x0d + fcc 'AND MICROWARE SYSTEMS CORP.' + fcb 0x0d,0x0d,0x00 +; Extended Basic extra initialization patch +ALINK14 clra ; set up to clear things + clrb + lbrn 0 + stb H.CRSATT ; reset cursor attributes + std HRMODE ; reset to VDG screen and no HSCREEN graphics + std H.ONBRK ; reset ON BRK destination + std H.ONERR ; reset ON ERR destinatin + sta H.BCOLOR ; default HSCREEN background to 0 + lda #1 ; default HSCREEN foreground to 1 + sta H.FCOLOR + lda #BLOCK6.4 ; map the HGET/HPUT buffers + sta MMUREG + ldd #0xffff ; mark as empty + std 0 + lda #BLOCK7.0 ; restore memory map + sta MMUREG + jmp LAD19 ; go finish initializing (NEW) +; ON command patch +ALINK18 cmpa #0xef ; ERR? + beq ERR ; brif so + cmpa #0xf0 ; BRK? + beq BRK ; brif so + jsr EVALEXPB ; evaluate the ON index argument + jmp LAF45 ; return to mainline code +SE3C2 jsr GETNCH ; eat the ERR/BRK token + cmpa #0x81 ; GO? + bne SE3CF ; brif not + jsr GETNCH ; eat the GO + cmpa #0xa5 ; TO? + bne SE3CF ; brif not + rts +SE3CF leas 2,s ; clean up stack (not needed) + jmp LB277 ; raise syntax error +; ERR jumps here if used as a command +; NOTE: you can do ERR GOTO (where is a single character that doesn't prevent GOTO from being tokenized +ERR bsr SE3C2 ; check for GOTO + jsr GETNCH ; eat the "TO" + jsr LAF67 ; evaluate destination line number + ldd BINVAL ; get line number + std H.ONERR ; set error destination + ldd CURLIN ; get current line number + std H.ONERRS ; save line number where ON ERR was executed + rts +; BRK jump shere if used as a command. +; Same note as for ERR applies. +BRK bsr SE3C2 ; check for GOTO + jsr GETNCH ; eat the "TO" + jsr LAF67 ; evaluate destination line number + ldd BINVAL ; get line number + std H.ONBRK ; set break destination + ldd CURLIN ; get current line number + std H.ONBRKS ; save line number where ON BRK was executed + rts +; Patches for &H parsing +ALINK6A lsl 2,x ; multiply accumulator by 2 + rol 1,x + rol ,x + lbcs LBA92 ; brif we overflowed + decb ; done enough shifts? + bne ALINK6A ; brif not + suba #'0 ; remove ASCII bias + adda 2,x ; add digit to accumulator (this cannot cause carry) + sta 2,x + rts +ALINK6B lbcs L8800 ; brif numeric + jmp L883F ; return to mainline +; Line input patch +ALINK16 cmpa #3 ; is it BREAK? + orcc #1 ; set C for BREAK + bne SE426 ; brif not BREAK + pshs a,cc ; save character and break status + lda HRMODE ; is it graphics mode? + beq SE424 ; brif not + clr HRMODE ; disable graphics mode + jsr SETTEXT +SE424 puls cc,a ; get back BREAK status and character +SE426 jmp LA3C6 ; return to mainline +; Break check patch +ALINK15 cmpa #3 ; BREAK? + beq SE430 ; brif so + jmp LADF4 ; re-enter mainline +SE430 lda #1 ; BREAK flag + sta H.ERRBRK + lda CURLIN ; immediate mode? + inca + beq SE43F ; brif so + ldd H.ONBRK ; is ON BRK active? + bne SE449 ; brif so +SE43F lda HRMODE ; graphics m ode? + beq SE446 ; brif not + jsr SETTEXT ; set text mode +SE446 jmp STOP ; go handle BREAK +SE449 std BINVAL ; set destination line number + tst H.ERRBRK ; error or break? + bne SE458 ; brif break + lds FRETOP ; reset stack pointer + ldd #LADC4 ; return to main loop + pshs d +SE458 jsr LAEEB ; move to end of line + leax 1,x ; move past line terminator + ldd BINVAL ; get desired line number + cmpd CURLIN ; is it here or later? + bhi SE466 ; brif so + ldx TXTTAB ; start search at beginning +SE466 jsr LAD05 ; find program line + lbcs SE51E ; brif not found + jmp LAEBB ; reset input pointer and return to main loop +; Error handling patch +ALINK20 clr H.ERRBRK ; flag error handling + lda CURLIN ; immediate mode? + inca + beq SE47D ; brif so + ldx H.ONERR ; is ON ERR in effect + bne SE4B3 ; brif so +SE47D pshs a ; save register + lda HRMODE ; set flags on graphics mode + puls a ; get back A + beq SE488 ; brif not graphics mode + jsr SETTEXT ; force text mode +SE488 cmpb #38*2 ; HG error? + bne SE49F ; brif not + jsr LB95C ; do newline + jsr LB9AF ; do ? + leax BAS20ERR,pcr ; point to error string +SE496 jsr LACA0 ; display two character error message + jsr LACA0 + jmp LAC65 ; return to mainline code +SE49F cmpb #39*2 ; HP error? + bne SE4B0 ; brif not + jsr LB95C ; do newline + jsr LB9AF ; do ? + leax BAS20ERR+2,pcr ; point to error string + jmp SE496 ; go finish up +SE4B0 jmp LAC49 ; return to mainline error handler +SE4B3 stb H.ERROR ; save error number + pshs b ; save error number + ldd CURLIN ; get current line number + std H.ERLINE ; save line number where error occurred + puls b ; get back error number + cmpb #3*2 ; OD error? + bne SE4C7 ; brif not + ldd BINVAL ; restore input pointer + std CHARAD +SE4C7 tfr x,d ; save error destination line + lbra SE449 ; go transfer control to error handler +BAS20ERR fcc 'HR' ; 38 Hi resolution graphics error + fcc 'HP' ; 39 Hi resolutuion print error +; NEW handling patch +ALINK19 pshs d ; save D + clra ; set up to clear things + clrb + std OLDPTR ; reset CONT address + std H.ONBRK ; reset ON BRK line + std H.ONERR ; reset ON ERR line + std H.ERLINE ; reset error source line + lda #0xff ; set error number to -1 + sta H.ERROR + puls d ; restore d + jmp LAD43 ; return to mainline +; ERNO function +ERNO clra ; zero extend error number + ldb H.ERROR ; get error number + cmpb #0xff ; real? + bne SE4F4 ; brif so + sex ; return "-1" + bra SE4FA +SE4F4 cmpb #0xf1 ; error number 0xf1? + bne SE4F9 ; brif not + comb ; turn it back into UL error +SE4F9 asrb ; error numbers are pre-multiplied by 2 - undo that +SE4FA jmp GIVABF ; return error number +; ERLIN function +ERLIN ldd H.ERLINE ; get the line number where the error occurred + bra SE4FA ; return it - BUG: will treat lines above 32767 as negative +; Immediate mode patch +ALINK21 jsr SETTEXT ; force text mode + jsr LB95C ; do line feed if needed + orcc #0x50 ; disable interrupts + lda #BLOCK6.4 ; map HGET/HPUT buffers + sta MMUREG + ldd #0xffff ; mark buffers empty + std 0 + lda #BLOCK7.0 ; restore memory map + sta MMUREG + andcc #0xaf ; re-enable interrupts + jmp LAC76 ; return to mainline +; Handle undefined line in ON ERR or ON BRK +SE51E tst H.ERRBRK ; break? + beq SE528 ; brif not + ldd H.ONBRKS ; get line number where ON BRK is + bra SE52B +SE528 ldd H.ONERRS ; get line number where ON ERR is +SE52B std CURLIN ; reset the current line number there + ldb #7*2 ; undefined line number + jmp LAC49 ; raise error (bypass ON ERR check) +; INPUT patch +ALINK17 ldd H.ONBRK ; is ON BRK operating? + lbeq LAE11 ; brif not + pshs d ; save destination line + lda #1 ; set BREAK flag + sta H.ERRBRK + puls d ; get destination line + lbra SE449 ; go handle ON BRK in INPUT +; LPOKE command +LPOKE jsr LB141 ; evaluate numeric expression (address) + lbrn 0 + bsr SE58E ; convert to extended address + cmpb #BLOCK7.7 ; valid block number? + lbhi LB44A ; brif not + pshs x,b ; save block and offset + jsr SYNCOMMA ; require a comma + jsr EVALEXPB ; evaluate value to put in memory + tfr b,a ; save value in A + puls b,x ; get back block and offset + cmpb #BLOCK7.7 ; valid block (we already tested this!!) + lbhi LB44A ; brif not + orcc #0x50 ; clobber interrupts + lbsr SELBLOK0 ; map the block (by writing *all 16* MMU registers) + sta ,x ; save byte in memory + lbsr SETMMU ; unmap block (by writing *all 16* MMU registers) + andcc #0xaf ; re-enable interrupts + rts +; LPEEK function +LPEEK bsr SE58E ; convert to block and offset + lbrn 0 + cmpb #BLOCK7.7 ; valid block? + lbhi LB44A ; brif not + orcc #0x50 ; clobber interrupts + lbsr SELBLOK0 ; map the block (by writing *all 16* MMU registers) + ldb ,x ; get memory contents + lbsr SETMMU ; restore map (by writing *all 16* MMU registers) + andcc #0xaf ; re-enable interrupts + jmp LB4F3 ; return B as unsigned +SE58E pshs a ; save register + lda FP0EXP ; is it in range for 0x80000? + cmpa #0x93 + bls SE59A ; brif so + ldb #BLOCK7.7+1 ; return illegal block number + bra SE5AF +SE59A jsr LBCC8 ; shift binary point to right of mantissa + ldd FPA0+2 ; get low bits + anda #0x1f ; mask off block number bits + tfr d,x ; now X has the block offset + ldd FPA0+1 ; get high bits + asra ;* shift block number to the right of B; note that + rorb ;* asr *should* be lsr but it works here because of + asra ;* the maximum range of the value + rorb + asra + rorb + asra + rorb + asra + rorb +SE5AF puls a,pc ; restore registers and return +; BUTTON command +BUTTON jsr INTCNV ; get button number + lbrn 0 + cmpb #3 ; button number in range? + lbhi LB44A ; raise error if not + tfr b,a ; save button number + clrb ; set B to 0xff (strobe no keyboard columns) + comb + ldx #PIA0 ; point to PIA0 + stb 2,x ; strobe nothing + ldb ,x ; get button data + cmpb #0x0f ; buttons are on bottom four rows + beq SE5EA ; brif no buttons down + leax SE5D5,pcr ; point to button mask routines + asla ; four bytes per button routine + asla + jmp a,x ; jump to appropriate routine +SE5D5 andb #1 ; keep button 1, right joystick + bra SE5E3 + andb #4 ; keep button #1, left joystick + bra SE5E3 + andb #2 ; keep button #2, right joystick + bra SE5E3 + andb #8 ; keep button #2, left joystick +SE5E3 bne SE5EA ; brif button was not down + ldd #1 ; return nonzero if button down + bra SE5EC ; return result +SE5EA clra ; return zero if not down + clrb +SE5EC jsr GIVABF ; return result + rts +; PALETTE command +PALETTE cmpa #0xf7 ; RGB? + lbrn 0 + bne SE600 ; brif not + jsr GETNCH ; munch the RGB +SE5FA leax IM.RGB,pcr ; point to RGB palette initializer + bra SE634 ; go set palette registers +SE600 cmpa #0xf6 ; CMP? + bne SE60C ; brif not + jsr GETNCH ; eat the CMP +SE606 leax IM.CMP,pcr ; point to CMP palette initializer + bra SE634 ; go set palette registers +SE60C jsr SE7B2 ; evaluate two expressions + ldx #PALETREG ; point to palette registers + ldy #IM.PALET ; point to palette register images + lda BINVAL+1 ; get palette number + cmpa #16 ; valid entry? + lbhs LB44A ; brif not 0-15 + leax a,x ; offset the pointers to the right entries + leay a,y + ldb VERBEG+1 ; get colour number + cmpb #63 ; valid? + bls SE62A ; brif so + ldb #63 ; maximize to 63 (white) +SE62A orcc #0x50 ; disable interrupts + sync ; synchronize to VSYNC + stb ,x ; set palette register + stb ,y ; record it in image + andcc #0xaf ; restore interrupts + rts +SE634 pshs x ; save source pointer + ldy #IM.PALET ; point to palette register live image + bsr SE648 ; copy source to the live image + puls x ; get source back + ldy #PALETREG ; point to palette registers + orcc #0x50 ; disable interrupts + sync ; synchronize to VSYNC + bsr SE648 ; copy the colour values into the palette registers + rts +SE648 ldb #16-1 ; BUG: should be 16 - this doesn't set register #15 +SE64A lda ,x+ ; set a register + sta ,y+ + decb ; done all? + bne SE64A ; brif not + andcc #0xaf ; re-enable interrupts + rts +IM.CMP fcb 18,36,11,7,63,31,9,38 ; palette values for CMP + fcb 0,18,0,63,0,18,0,38 +IM.RGB fcb 18,54,9,36,63,27,45,38 ; palette values for RGB + fcb 0,18,0,63,0,18,0,38 +; CMP and RGB commands just jump to the relevant implementations above +RGB bra SE5FA +CMP bra SE606 +IM.PALET fcb 18,36,11,7,63,31,9,38 ; live palette images + fcb 0,18,0,63,0,18,0,38 +; HSCREEN command +HSCREEN cmpa #0 ; end of line? BUG: won't work if colon terminates the command + lbrn 0 + bne SE693 ; brif not end of line + clrb ; default to HSCREEN 0 - turn off graphics + bra SE69C +SE693 jsr EVALEXPB ; evaluate HSCREEN argument + cmpb #4 ; only 4 HSCREEN modes + lbhi LB44A ; brif out of range +SE69C stb HRMODE ; set graphics mode + cmpb #0 ; HSCREEN 0? + bne SE6A5 ; brif not + jmp SETTEXT ; set text mode (disable graphics) +SE6A5 stb HRMODE ; set graphics mode (we already did!) + ldx #SE6CB ; point to bytes/row table + subb #1 ; normalize mode to 0 + lda b,x ; get bytes per row value + sta HORBYT ; set it + cmpb #1 ; is it 1 or 2? + bgt SE6B9 ; brif not + ldd #160 ; default coordinate for middle of 320 screen + bra SE6BC +SE6B9 ldd #320 ; default coordainte for middle of 640 screen +SE6BC std HORDEF ; set default horizontal coordinate + ldd #96 ; set default vertical coordinate to middle + std VERDEF + ldb H.BCOLOR ; get background colour + bsr CLRHIRES ; clear hi-res graphics screen + jmp SETGRAPH ; set up to display the screen +SE6CB fcb 80,160,80,160 ; bytes per row values for HSCREEN 1 through 4 +; HCLS command +HCLS bne SE6D6 ; brif not end of statement + ldb H.BCOLOR ; get background colour as default + bra CLRHIRES ; go clear screen +SE6D6 bsr SE70E ; evaluate colour number +CLRHIRES tst HRMODE ; graphics mode? + beq SE6EF ; brif not + bsr PIXELFIL ; get all pixels set byte + jsr SELTASK1 ; swap screen in + ldx #HRESSCRN ; point to start of screen +SE6E4 stb ,x+ ; set a byte worth of pixels + cmpx #HRESSCRN+0x8000 ; end of graphics memory? + bne SE6E4 ; brif not + jsr SELTASK0 ; restore memory map + rts +SE6EF ldb #38*2 ; code for HR error + jmp LAC46 ; raise error +; HCOLOR command +HCOLOR cmpa #', ; was a foreground colour given? + lbrn 0 + beq SE705 ; brif not + bsr SE70E ; evaluate colour number + stb H.FCOLOR ; save foreground colour + jsr GETCCH ; is there something after the foreground? + beq SE70D ; brif not +SE705 jsr SYNCOMMA ; insist on a comma + bsr SE70E ; evaluate colour number + stb H.BCOLOR ; set background colour +SE70D rts +; Evaluate a colour number and make sure it's between 0 and 15 inclusive +SE70E jsr EVALEXPB ; evaluate colour +SE711 cmpb #16 ; is it in range? + lbhs LB44A ; brif not + rts +SE718 jsr SE731 ; set working colour and pixel bytes to default + jsr GETCCH ; is there a colour number? + beq SE72F ; brif not + cmpa #') ; )? + beq SE72F ; brif so - no colour + jsr SYNCOMMA ; insist on a comma + cmpa #', ; another comma? + beq SE72F ; brif so - colour not specified + jsr SE70E ; evaluate colour + bsr SE73B ; set working colour and pixel bytes +SE72F jmp GETCCH ; get current character and return +SE731 ldb H.FCOLOR ; get foreground colour + tst SETFLG ; doing set? + bne SE73B ; brif so + ldb H.BCOLOR ; use background colour if doing reset +SE73B stb WCOLOR ; save working colour + bsr PIXELFIL ; get all pixel byte + stb ALLCOL ; save all pixel byte + rts +; Return B with all pixels set to colour number in B +PIXELFIL pshs x ; save registers + lda HRMODE ; get graphics mode + suba #1 ; normalize mode numbers to start at 0 + ldx #SE759 ; point to colour masks + andb a,x ; now B has only the relevant low bits of colour number + lda HRMODE ; get graphics mode + suba #1 ; normalize mode numbers to start at 0 + ldx #SE75D ; point to multiplier table + lda a,x ; get multplier + mul ; now B has all pixels set + puls x,pc ; restore registers and return +SE759 fcb 0x03,0x0f,0x01,0x03 ; colour masks to keep only necessary low bits +SE75D fcb 0x55,0x11,0xff,0x55 ; multipliers to duplicate colour value across all pixels +; HSET command +HSET lda #1 ; HSET flag + bra SE76A +; HRESET command +HRESET clra ; HRESET flag + lbrn 0 +SE76A tst HRMODE ; are we in a graphics mode? + beq SE6EF ; brif not - raise error + sta SETFLG ; save our set/reset state + jsr LB26A ; insist on ( + jsr SE7AA ; evaluate coordindates + tst SETFLG ; resetting? + bne SE77F ; brif so + jsr SE731 ; set working colour and pixel byte + bra SE782 +SE77F jsr SE718 ; evaluate colour number if present +SE782 jsr LB267 ; insist on a ) + jsr HCALPOS ; fetch screen pointer address and pixel mask +SE788 jsr SELTASK1 ; map the screen + jsr SE792 ; set or reset the pixel + jsr SELTASK0 ; unmap the screen + rts +SE792 ldb ,x ; get byte on screen + pshs b ; save it + tfr a,b ; duplicate mask + coma ; invert the mask for clearing the screen data + anda ,x ; reset the pixel + andb ALLCOL ; set pixel mask to correct colour + pshs b ; merge pixel colour into screen data + ora ,s+ + sta ,x ; put modified data on screen + suba ,s+ ; nonzero if the screen changed + ora CHGFLG ; merge with existing change flag + sta CHGFLG + rts +SE7AA jsr SE7B2 ; evaluate coordinates +SE7AD ldu #HORBEG ; point to horizontal coordinates +SE7B0 rts ; dummy "normalization" routine + rts ; pointles RTS +; Evaluate two expressions (usually coordinates) +SE7B2 jsr LB734 ; evaluate two expressions, first in BINVAL, second in B + ldy #HORBEG ; point to horizontal coordinates +SE7B9 cmpb #192 ; in range vertically? + blo SE7BF ; brif so + ldb #191 ; set to maximum coordinate +SE7BF clra ; zero extend vertical + std 2,y ; set vertical coordinate + lda HRMODE ; get graphics mode + cmpa #2 ; is it 1 or 2? + bgt SE7CD ; brif not + ldd #319 ; maximum coordinate for modes 1 and 2 + bra SE7D0 +SE7CD ldd #639 ; maximum coordindate for modes 3 and 4 +SE7D0 cmpd BINVAL ; is our max less than the specified one? + blo SE7D7 ; brif so - keep max + ldd BINVAL ; use specified coordinate +SE7D7 std ,y ; save horizontal coordinate + rts +; Calculate pixel mask and memory address for pixel +HCALPOS bsr SE7E6 ; point to correct routine for current mode + jmp ,u ; execute it +CALTABLE fdb G2BITPIX ; HSCREEN 1 + fdb G4BITPIX ; HSCREEN 2 + fdb G1BITPIX ; HSCREEN 3 + fdb G2BITPIX ; HSCREEN 4 +SE7E6 ldu #CALTABLE ; point to routine table + lda HRMODE ; get graphicsmode + suba #1 ; zero-base it + asla ; two bytes per address + ldu a,u ; get routine address + rts +PIX1MASK fcb 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01 ; pixel masks for 1 bpp +PIX2MASK fcb 0xc0,0x30,0x0c,0x03 ; pxiel masks for 2 bpp +PIX4MASK fcb 0xf0,0x0f ; pixel masks for 4 bpp +G1BITPIX pshs u,b ; save registers + ldb HORBYT ; get bytes per row + lda VERBEG+1 ; get vergical coord + mul ; now D is the offset to the start of the row + addd #HRESSCRN ; add in start of screen in memory + tfr d,x ; put it in a pointer + ldd HORBEG ; get horiztonal coordindate + lsra ; 8 pixels per byte do divide by 8 + rorb + lsra + rorb + lsra + rorb + leax d,x ; offset to correct byte row + lda HORBEG+1 ; get pixel number + anda #7 ; keep only byte offset + ldu #PIX1MASK ; point to 1 bpp masks + lda a,u ; get pixel mask + puls b,u,pc ; restore registers and return +G2BITPIX pshs u,b ; save registers + ldb HORBYT ; get number of bytes per row + lda VERBEG+1 ; get horizontal coordinate + mul ; now D is the offset to the start of the row + addd #HRESSCRN ; add in memory offset to the screen + tfr d,x ; put that in a pointer + ldd HORBEG ; get horizontal coordinate + lsra ; 4 pixels per byte so divide by 4 + rorb + lsra + rorb + leax d,x ; now X points to the correct memory byte + lda HORBEG+1 ; get horizontal coordinate + anda #3 ; keep only the pixel number in the byte + ldu #PIX2MASK ; point to 2 bpp pixel masks + lda a,u ; get mask for this pixel + puls b,u,pc ; restore registers and return +G4BITPIX pshs u,b ; save registers + ldb HORBYT ; get bytes per row + lda VERBEG+1 ; get vertical coordinate + mul ; now D is the offset to the start of the row + addd #HRESSCRN ; add in memory address of start of screen + tfr d,x ; put it in a pointer + ldd HORBEG ; get horizontal coordinate + lsra ; divide by 2 - only 2 pixels per byte + rorb + leax d,x ; now X points to the memory address of the pixel + lda HORBEG+1 ; get horiztonal coordinate + anda #1 ; keep offset into byte + ldu #PIX4MASK ; point to 4 bpp pixel masks + lda a,u ; get pixel mask + puls b,u,pc ; restore registers and return +; HPOINT function +HPOINT tst HRMODE ; is there a graphics mode? + lbeq SE6EF ; brif not - raise error + jsr LB26A ; insist on ( + jsr SE7AA ; evaluate coordinates + jsr LB267 ; insist on ) + jsr SELTASK1 ; map the screen + jsr HCALPOS ; get screen pointer + tfr a,b ; save mask + andb ,x ; get pixel data +SE875 lsra ; is the pixel aligned right? + bcs SE87B ; brif so + lsrb ; shift right + bra SE875 ; see if it's aligned yet +SE87B jsr LB4F3 ; return colour number + jsr SELTASK0 ; restore memory map + rts +; HLINE command +HLINE tst HRMODE ; is there a graphics mode active? + lbeq SE6EF ; brif not - raise error + lbrn 0 + cmpa #'( ; is there (? + beq SE899 ; brif so - we have start coords + cmpa #0xac ; -? + beq SE899 ; brif no start given + ldb #'@ ; make sure it's @ if not + jsr LB26F +SE899 jsr SE9E1 ; get start/end coords + ldx HOREND ; put end in the defaults + stx HORDEF + ldx VEREND + stx VERDEF + jsr SYNCOMMA ; make sure comma + cmpa #0xbe ; PRESET? + beq SE8B4 ; brif so + cmpa #0xbd ; PSET? + lbne LB277 ; brif not + ldb #1 ; set flag + skip1lda +SE8B4 clrb ; reset flag + pshs b ; save set/reset flag + jsr GETNCH ; eat the PSET/PRESET token + jsr SEA0D ; normalize start/end + puls b ; get back set/reset flag + stb SETFLG ; save set/reset flag + jsr SE731 ; set active colour byte + jsr GETCCH ; is there more? + lbeq SE94E ; brif not - no box + jsr SYNCOMMA ; insist on a comma + ldb #'B ; insist on a B + jsr LB26F + bne SE8EB ; brif something after B + bsr SE906 ; draw horizontal line (top) + bsr SE931 ; draw vertical line (left) + ldx HORBEG ; save horizontal start + pshs x + ldx HOREND ; set up to draw vertical line (right) + stx HORBEG + bsr SE931 ; draw vertical line (right) + puls x ; restore start coord + stx HORBEG + ldx VEREND ; set up to draw horizontal line (bottom) + stx VERBEG + bra SE906 ; draw horizontal line (bottom) and return +SE8EB ldb #'F ; insist on F + jsr LB26F + bra SE8F6 ; draw a filled box +SE8F2 leax -1,x ; move vertical coordinate up one +SE8F4 stx VERBEG ; save new vertical coordinate +SE8F6 jsr SE906 ; draw horizontal line + ldx VERBEG ; get current coordinate + cmpx VEREND ; above or below end? + beq SE905 ; brif done + bhs SE8F2 ; brif below - move up + leax 1,x ; move down (we're above) + bra SE8F4 ; draw another line +SE905 rts +SE906 ldx HORBEG ; get starting coordinate + pshs x ; save it + jsr SE9DB ; get absolute horizontal difference + bcc SE913 ; brif end > start + ldx HOREND ; get ending coordinate + stx HORBEG ; save as starting position +SE913 tfr d,y ; save difference (pixel count) + leay 1,y ; bump it (coords are inclusive) + jsr HCALPOS ; calculate pixel address + puls u ; get start coordinate + stu HORBEG ; restore it + lbsr SEA16 ; point to routine to move pixel pointer right +SE921 sta VD7 ; save pixel mask + jsr SE788 ; turn on pixel + lda VD7 ; get back pixel mask + jsr ,u ; move one pixel right + leay -1,y ; done all pixels? + bne SE921 ; brif not + rts +SE92F puls d ; clean up stack +SE931 ldd VERBEG ; get vertical start + pshs d ; save it + jsr SE9CD ; calculate absolute vertical difference + bcc SE93E ; brif end > start + ldx VEREND ; swap coordinate + stx VERBEG +SE93E tfr d,y ; save difference (pixel count) + leay 1,y ; coordinates are inclusive + jsr HCALPOS ; get screen pointer + puls u ; get original start coord + stu VERBEG ; restore it + lbsr SEA21 ; get routine to move down one row + bra SE921 ; draw vertical line +SE94E ldy #SE9B8 ; point to vertical increment routine + jsr SE9CD ; calculate absolute vertical difference + beq SE906 ; draw horizontal if difference is 0 + bcc SE95D ; brif vertical end > vertical start + ldy #SE9C6 ; point to decrement vertical routine +SE95D pshs b,a ; save vertical difference + ldu #SE9B1 ; point to horitzontal increment routine + jsr SE9DB ; calculate absolute horizontal difference + beq SE92F ; draw vertical line if difference is 0 + bcc SE96C ; brif horizontal end > horizontal start + ldu #SE9BF ; point to decrement horizontal routine +SE96C cmpd ,s ; compare horiztonal difference with vertical difference + puls x ; get vertical difference back + bhs SE977 ; brif horizontal difference is greater + exg u,y ; swap major/minor directions + exg d,x +SE977 pshs u,d ; save larger difference and incr/decr routine + pshs d ; save larger difference + lsra ; divide larger difference by 2 + rorb + bcs SE988 ; brif odd + cmpu #SE9B8+1 ; inc or dec? + blo SE988 ; brif inc + subd #1 ; move back of dec (round down) +SE988 pshs x,d ; save smaller difference and inc/dec + jsr SE7E6 ; point to screen address routine +SE98D jsr ,u ; convert coordinates to screen address + jsr SE788 ; turn on pxiel + ldx 6,s ; done all? + beq SE9AD ; brif so + leax -1,x ; account for pixel just drawn + stx 6,s + jsr [8,s] ; bump coordinate + ldd ,s ; get minor coordinate increment counter + addd 2,s ; add to minor coordinate + std ,s ; save new minor increment + subd 4,s ; subtract largest difference + bcs SE98D ; brif result not bigger than largest difference + std ,s ; save new minor increment + jsr ,y ; inc/dec minor + bra SE98D ; draw another pixel +SE9AD puls x ; clean up stack + puls d,x,y,u,pc ; clean up stack and return +SE9B1 ldx HORBEG ; bump horizontal coord + leax 1,x + stx HORBEG + rts +SE9B8 ldx VERBEG ; bump vertical coord + leax 1,x + stx VERBEG + rts +SE9BF ldx HORBEG ; reduce horizontal coord + leax -1,x + stx HORBEG + rts +SE9C6 ldx VERBEG ; reduce vertical coord + leax -1,x + stx VERBEG +SE9CC rts +SE9CD ldd VEREND ; get vertical end + subd VERBEG ; get subtract start +SE9D1 bcc SE9CC ; brif end > start + pshs cc ; save flag for which is > + nega ; negate difference + negb + sbca #0 + puls cc,pc ; restore status andreturn +SE9DB ldd HOREND ; get horizontal end coord + subd HORBEG ; subtract start coord + bra SE9D1 ; handle going negative +; Evaluate two sets of coordinates +SE9E1 ldx HORDEF ; set start to default + stx HORBEG + ldx VERDEF + stx VERBEG + cmpa #0xac ; -? + beq SE9F0 ; brif so - use default start + jsr SEA04 ; evaluate coordinate pair +SE9F0 ldb #0xac ; insist on - + jsr LB26F + jsr LB26A ; insist on ( + jsr LB734 ; evaluate two expressions (X, B) + ldy #HOREND ; point to end coords + jsr SE7B9 ; validate end coords + bra SEA0A ; handle rest of evaluation +SEA04 jsr LB26A ; insist on ( + jsr SE7B2 ; evaluate coordinates with range check +SEA0A jmp LB267 ; insist on ) +SEA0D jsr SE7AD ; "normalize" start + ldu #HOREND ; point to end coords + jmp SE7B0 ; "normalize" end +; Point U to routine to move pixel to right +SEA16 ldu #SEA25 ; point to jump table + ldb HRMODE ; get graphics mode + subb #1 ; zero-base it + aslb ; two bytes per entry + ldu b,u ; get routine address + rts +SEA21 ldu #SEA45 ; point to routine to move down one row + rts +SEA25 fdb SEA34 ; HSCREEN 1 right + fdb SEA3D ; HSCREEN 2 right + fdb SEA2D ; HSCREEN 3 right + fdb SEA34 ; HSCREEN 4 right +SEA2D lsra ; move pixel mask right + bcc SEA33 ; brif not changing bytes + rora ; shift mask back around to left + leax 1,x ; move byte forward +SEA33 rts +SEA34 lsra ; move one pixel right + lsra + bcc SEA33 ; brif same byte + lda #0xc0 ; reset pixel mask + leax 1,x ; move to next byte + rts +SEA3D coma ; flip pixels + cmpa #0xf0 ; did we move to a new byte? + bne SEA44 ; brif not + leax 1,x ; move to next byte +SEA44 rts +SEA45 ldb HORBYT ; get number of bytes per row + abx ; move ahead that many + rts +; HCIRCLE command +HCIRCLE tst HRMODE ; graphics mode? + lbeq SE6EF ; brif not - raise error + lbrn 0 + cmpa #'@ ; is there @ before coords? + bne SEA59 ; brif not + jsr GETNCH ; eat the @ +SEA59 jsr SEB60 ; get max coords for video mode + jsr SEA04 ; parse centre coords + jsr SE7AD ; normalize coordinates (ha ha) + ldx ,u ; get horizontal coordinate + stx VCB ; save it + ldx 2,u ; get vertical coordinate + stx VCD ; save it + jsr SYNCOMMA ; insist on a comma + jsr LB73D ; evaluate expression into X (radius) + ldu #VCF ; point to temp storage area + stx ,u ; save radius + jsr SE7B0 ; normalize - pointless + lda #1 ; put into "set" mode + sta SETFLG + jsr SE718 ; evaluate colour + ldx #0x100 ; default H/W ratio (1:1) + jsr GETCCH ; is the an HW ratio? + beq SEA95 ; brif not + jsr SYNCOMMA ; insist on comma + jsr LB141 ; evaluate HW ratio + lda FP0EXP ; multiply by 256 + adda #8 + sta FP0EXP + jsr LB740 ; fetch HW ratio to X (with a fixed 8 bit fraction part) +SEA95 lda HRMODE ; get graphics mode + cmpa #2 ; is it a 320 mode? + bhi SEA9F ; brif not + tfr x,d ; double HW ratio for 320 modes + leax d,x +SEA9F stx VD1 ; save H/W ratio + ldb #1 ; go into SET mode + stb SETFLG + stb VD8 ; flag for "first arc" + jsr SEB7B ; evaluate start point (octant, subarc) + pshs d ; save start point + jsr SEB7B ; evaluate end point + std VD9 ; save end point + puls d ; get back start point +SEAB3 pshs d ; save current circle position + ldx HOREND ; switch previous end coords in as the start + stx HORBEG + ldx VEREND + stx VERBEG + ldu #CIRCDATA+2 ; point to sines/cosines table + anda #1 ; is it an even octant? + beq SEAC7 ; brif so + negb ; swap arc order for odd octants + addb #8 +SEAC7 aslb ; four bytes per table entry + aslb + leau b,u ; now U points to the correct entry + pshs u ; save table entry + jsr SEBBD ; calculate horizontal offset + puls u ; get back table pointer + leau -2,u ; move to other entry + pshs x ; save horizontal offset + jsr SEBBD ; calculaute vertical offset + puls y ; get back horizontal offset + lda ,s ; get octant number + anda #3 ; is it 0 or 4? + beq SEAE7 ; brif so + cmpa #3 ; is it 3 or 7? + beq SEAE7 ; brif so + exg x,y ; swap horizontal and vertical otherwise +SEAE7 stx HOREND ; save horizontal offset + tfr y,d ; divide offset by 2 + lsra + rorb + ldx VD1 ; get H/W ratio + jsr SEBCB ; multiply offset by ratio + tfr y,d ; did MSB (bits 23-16) end up nonzero? + tsta ; brif so - outside 16 bit range + lbne LB44A ; brif so - raise error + stb VEREND ; save vertical offset MSB + tfr u,d ; get low bytes of result + sta VEREND+1 ; save LSB (lose fractional part) + lda ,s ; get octant number + cmpa #2 ; 0 or 1? + blo SEB13 ; brif so + cmpa #6 ; 6 or h? + bhs SEB13 ; brif so + ldd VCB ; get horizontal center + subd HOREND ; subtract offset (going left) + bcc SEB20 ; brif we didn't go negative + clra ; minimize to 0 + clrb + bra SEB20 +SEB13 ldd VCB ; get horizontal centre + addd HOREND ; add offset + bcs SEB1E ; brif we overflowed + cmpd VD3 ; did we overflow screen size? + blo SEB20 ; brif not +SEB1E ldd VD3 ; maximize horizontal coordinate +SEB20 std HOREND ; save new horizontal coordinate + lda ,s ; get octantnumber + cmpa #4 ; is it 0-3 (bottom half)? + blo SEB32 ; brif so + ldd VCD ; get vertical centre + subd VEREND ; subtract offset + bcc SEB3F ; brif we didn't run past 0 + clra ; minimize to 0 + clrb + bra SEB3F +SEB32 ldd VCD ; get vertical centre + addd VEREND ; add offset + bcs SEB3D ; brif we overflowed + cmpd VD5 ; did we go past end of screen? + blo SEB3F ; brif not +SEB3D ldd VD5 ; maximize to screen size +SEB3F std VEREND ; save new vertical coord + tst VD8 ; was this the first coordinate? + bne SEB48 ; brif so - don't draw a line + lbsr SE94E ; draw the subarc line +SEB48 puls d ; get octant and arc + lsr VD8 ; test if first point, and clear flag + bcs SEB53 ; brif first coord + cmpd VD9 ; at end of circle? + beq SEB5F ; brif so +SEB53 incb ; bump arc counter + cmpb #8 ; done 8 subarcs? + bne SEB5C ; brif not + inca ; bump octant + clrb ; reset arc counter + anda #7 ; wrap octant number if needed +SEB5C jmp SEAB3 ; move on with the next arc +SEB5F rts +SEB60 ldu #VD3 ; point to storage area + ldx #639 ; set max horizontal coord for 640 mode + stx ,u + lda HRMODE ; get graphics mode + cmpa #2 ; is it a 640 mode? + bgt SEB73 ; brif so + ldx #319 ; set max horzontalcoord for 320 mode + stx ,u +SEB73 ldx #191 ; all modes have a 191 vertical max + stx 2,u + jmp SE7B0 ; "normalize" coords +SEB7B clrb ; default circle start/end to 0 + jsr GETCCH ; is there a fraction? + beq SEB91 ; brif not + jsr SYNCOMMA ; insist on a comma + jsr LB141 ; evaluate circle fraction + lda FP0EXP ; multiply by 64 (calculate # of 64ths) + adda #6 + sta FP0EXP + jsr LB70E ; fetch result as 8 bits unsigned + andb #0x3f ; keep only the fraction part +SEB91 tfr b,a ; copy fraction to A (for octant) + andb #7 ; keep only arc number in B + lsra ; shift octant number to right of A + lsra + lsra + rts +CIRCDATA fdb 0x0000,0x0001 ; subarc 0 + fdb 0xfec5,0x1919 ; subarc 1 + fdb 0xfb16,0x31f2 ; subarc 2 + fdb 0xf4fb,0x4a51 ; subarc 3 + fdb 0xec84,0x61f9 ; subarc 4 + fdb 0xe1c7,0x78ae ; subarc 5 + fdb 0xd4dc,0x8e3b ; subarc 6 + fdb 0xc5e5,0xa269 ; subarc 7 + fdb 0xb506,0xb506 ; subarc 8 +SEBBD ldx VCF ; get radius + ldd ,u ; get sin/cos value + beq SEBCA ; brif 0 - just use radius + subd #1 ; subtract 1 + bsr SEBCB ; do the multiplication dance + tfr y,x ; save result to X +SEBCA rts +SEBCB pshs u,y,x,b,a ; save registers and reserve storage + clr 4,s ; clear high bits + lda 3,s ; B*XL + mul + std 6,s ; save in partical result + ldd 1,s ; A*XH + mul + addb 6,s ; add to partial product + adca #0 + std 5,s + ldb ,s ; A*XL + lda 3,s + mul + addd 5,s ; add to partial product + std 5,s + bcc SEBEA + inc 4,s +SEBEA lda ,s ; A*XH + ldb 2,s + mul + addd 4,s ; add to partial product + std 4,s ; save final product bits + puls a,b,x,y,u,pc ; save factors, retrieve result, and return +; HPAINT command +HPAINT tst HRMODE ; do we have a grahpics mode? + lbeq SE6EF ; brif not - raise error + lbrn 0 + cmpa #'@ ; is there @ before the coords? + bne SEC05 ; brif not + jsr GETNCH ; eat the @ +SEC05 jsr SEA04 ; insist on ( + jsr SE7AD ; evaluate the coordinates + lda #1 ; set up for "setting" + sta SETFLG + jsr SE718 ; evaluate colour code + ldd WCOLOR ; get working colour and all pixels byte + pshs d ; save them for later + jsr GETCCH ; do we have a border colour? + beq SEC1D ; brif not - use default + jsr SE718 ; evaluate border colour +SEC1D lda ALLCOL ; get border colour pixel byte + sta VD8 ; save it + puls d ; get paint colour details + std WCOLOR ; restore them + jsr SELTASK1 ; map the graphics screen + clra ;* add a terminator block to the top of the stack + pshs u,x,b,a ;* which is how HPAINT knows it's done + jsr SEB60 ; get maximum coordinate values + jsr SE7E6 ; point U to routine that selects a pixel + stu VD9 ; save pixel selection routine address + jsr SECBE ; paint from current coord to the left + beq SEC47 ; brif no painting done - we started on the border + jsr SED01 ; paint to the right + lda #1 ; set up a record to paint down the screen + sta VD7 + jsr SED2E + neg VD7 ; set up a record to paint up the screen + jsr SED2E +SEC47 sts TMPSTK ; save stack pointer +SEC4A tst CHGFLG ; see if a pixel changed + bne SEC51 ; brif so + lds TMPSTK ; get stack pointer back +SEC51 puls a,b,x,u ; get data for the next line to handle + clr CHGFLG ; flag nothing changed yet + sts TMPSTK ; save new stack address + leax 1,x ; add one to the start position + STX HORBEG ; set it as the starting position + stu VD1 ; save length of parent line + sta VD7 ; save up/down flag + beq SECBA ; brif up/down is 0 - end marker + bmi SEC6A ; brif we're going up the screen + incb ; bump vertical coord + cmpb VD6 ; at end of screen? + bls SEC6E ; brif not + clrb ; wrap around - this will cause us to bail below +SEC6A tstb ; is coord 0? + beq SEC4A ; brif so - don't go upward + decb ; move upward on the screen +SEC6E stb VERBEG+1 ; save new vertical coordinate + jsr SECBE ; paint to the left + beq SEC86 ; brif no pixels changed + cmpd #3 ; less than 3 painted? + blo SEC80 ; brif so - no need to check for paintable data + leax -2,x ; move coord left two + jsr SED15 ; save block of paint data in the other direction (vertically) +SEC80 jsr SED01 ; paint to the right +SEC83 jsr SED2E ; save a block of paint data in the same direction +SEC86 coma ; invert number of pixels painted (but "less 1") + comb +SEC88 addd VD1 ; add to length of parent line + std VD1 ; now we have the new parent line length + ble SECA5 ; brif parent line was shorter + jsr SE9B1 ; bump horizontal + jsr SECF1 ; check for border + bne SEC9B ; brif not + ldd #-1 ; count down + bra SEC88 ; keep looking +SEC9B jsr SE9BF ; move left + jsr SED3A ; save horizontal coord + bsr SECC7 ; paint to the right + bra SEC83 ; save paint block and keep going +SECA5 jsr SE9B1 ; bump horizontal coord + leax d,x ; point to right of end of parent line + stx HORBEG ; set as start coord + coma ; negate pixel count (and subtract 2?) + comb + subd #1 + ble SECB7 ; brif line doesn't extend past right of parent + tfr d,x ; save portion of line to the right as length + bsr SED15 ; save block of paint data +SECB7 jmp SEC4A ; go process more paint blocks +SECBA jsr SELTASK0 ; unmap screen + rts +SECBE jsr SED3A ; point starting coord in end + ldy #SE9BF ; point to dec horizontal + bra SECCD ; paint line +SECC7 ldy #SE9B1 ; point to incr horizontal coord + jsr ,y ; skip first - already done +SECCD ldu ZERO ; initial pixel counter to 0 + ldx HORBEG ; get starting coord +SECD1 bmi SECEA ; brif off the left side + cmpx VD3 ; at max value? + bhi SECEA ; brif off right side + pshs u,y ; save counter and inc/dec p ointer + bsr SECF1 ; check for border + beq SECE8 ; brif so - we're done + jsr SE792 ; set pixel + puls y,u ; restore count and inc/dec routine + leau 1,u ; bump count + jsr ,y ; adjust coord + bra SECD1 ; go do another pixel +SECE8 puls y,u ; get back counter and inc/dec pointer +SECEA tfr u,d ; save paint counter in D and X + tfr d,x + subd ZERO ; set flags on counter + rts +SECF1 jsr [VD9] ; get address of pixel + tfr a,b ; duplicate mask + andb VD8 ; get pixel colour mask for the pixel + pshs b,a ; save masks + anda ,x ; merge in with pixel data on screen + cmpa 1,s ; does it match? (Z set if so) + puls a,b,pc ; restore masks and return +SED01 std VCD ; save pixel count + ldy HOREND ; get last horizontal coord + bsr SED3A ; save current coord + sty HORBEG ; start painting to right from the previous end + bsr SECC7 ; go paint rightward + ldx VCD ; get previous pixel count + leax d,x ; now we have a total count for this line + addd #1 ; bump it by one? + rts +SED15 std VCB ; save painted pixel count + puls y ; get return address + ldd HORBEG ; get start coord + pshs x,d ; save start coord and line length + lda VD7 ; get direction + nega ; invert it +SED20 ldb VERBEG+1 ; get vertical coordinate + pshs b,a ; save direction and vertical coord + pshs y ; put return address back + ldb #6 ; make sure we didn't overflow the stack + jsr SED3F + ldd VCB ; get line length back + rts +SED2E std VCB ; save line length + puls y ; get return address + ldd HOREND ; get horizontal start + pshs x,d ; save line length and horizontal coord + lda VD7 ; get direction flag + bra SED20 ; finish saving frame +SED3A ldx HORBEG ; get start coord + stx HOREND ; save it as end coord + rts +SED3F negb ; subtract B bytes from S + leas b,s + cmps #TMPSTACK-(0x2000+14) ; does it overflow? (14 extra is from the unused vectors at the top of the CB ROM area) + lblo SED4E ; raise OM error if we did + negb ; restore stack pointer + leas b,s + rts +SED4E lds #TMPSTACK-2 ; reset stack (since we overflowed it) + jsr SELTASK0 ; restore default memory map + jmp LAC44 ; raise OM error +; HBUFF command +HBUFF jsr LB73D ; evaluate buffer number to X + lbrn 0 + cmpx #255 ; valid? + lbhi LB44A ; brif not + stx VD1 ; save buffer number + beq SED72 ; don't get size if buffer 0 select + jsr SYNCOMMA ; insist on a comma + jsr LB73D ; evaluate size to X + stx VD3 ; save buffer size +SED72 jsr SE0CB ; map the buffers + jsr SELTASK1 + ldd VD1 ; get buffer number + tstb ; is it zero (not needed!) + bne SED85 ; brif not + ldd #0xffff ; clear all buffers + std HRESBUFF + bra SEDBD ; reset memory map and return +SED85 ldy #HRESBUFF ; point to buffers + ldd ,y ; get address of next block + cmpd #0xffff ; empty buffer space? + bne SED95 ; brif not + bsr SEDC4 ; check for room in buffer space + bra SEDB0 ; set up buffer +SED95 ldb VD1+1 ; get buffer number +SED97 cmpb 2,y ; is this buffer the same number? + beq SEDD2 ; brif so - throw error + ldu ,y ; get address of next buffer + beq SEDA3 ; brif last buffer + tfr u,y ; move on to next buffer + bra SED97 ; see if we have a matching number here +SEDA3 tfr y,u ; save start address to U + ldd 3,y ; get size of last buffer + leay 5,y ; move past header + leay d,y ; move past buffer data + bsr SEDC4 ; check for enough room + sty ,u ; save pointer to the new buffer in previous header +SEDB0 ldd #0 ; mark this as the last buffer + std ,y + ldb VD1+1 ; set buffer number + stb 2,y + ldd VD3 ; set buffer size + std 3,y +SEDBD jsr SELTASK0 ; restore memory map + jsr SETMMU + rts +SEDC4 tfr y,x ; point X to the start of the buffer data + leax 5,x + ldd VD3 ; get length requested + leax d,x ; point to end of new buffer + cmpx #HRESBUFF+0x1f00 ; does it fit? + bhi SEDD6 ; brif not + rts +SEDD2 ldb #9*2 ; code for redim array + bra SEDD8 +SEDD6 ldb #6*2 ; code for out of memory +SEDD8 lds #TMPSTACK-2 ; reset stack + jsr SELTASK0 ; restore memory map + jsr SETMMU + jmp LAC46 ; raise error +; HGET command +HGET ldx #SEEC0 ; point to HGET movement routine + stx VD5 ; save it + clrb ; flag for "GET" + bra SEDF4 ; get on with things +; HPUT command +HPUT ldx #SEEEF ; point to HPUT movement routine + stx VD5 ; save it + ldb #1 ; flag for "PUT" +SEDF4 tst HRMODE ; check for graphics + lbeq SE6EF ; brif not - raise error + lbrn 0 + stb VD8 ; save GET/PUT flag + cmpa #'@ ; is there @ before coords? + bne SEE06 ; brif not + jsr GETNCH ; eat the @ +SEE06 jsr SE9E1 ; evaluate box bounds + jsr SYNCOMMA ; insist on a comma + jsr EVALEXPB ; evaluate buffer number + stb VD3 ; save buffer number + clr VD4 ; default action to none + jsr GETCCH ; is there an action flag? + beq SEE38 ; brif not + com VD4 ; flag for action flag specified + jsr SYNCOMMA ; insist on a comma + tst VD8 ; is it GET? + bne SEE23 ; brif not + lbra LB277 ; raise error +SEE23 ldb #5 ; 5 possible actions + ldx #SEEE0 ; point to action routine table address +SEE28 ldu ,x++ ; get routine address + cmpa ,x+ ; does the action match? + beq SEE34 ; brif so + decb ; checked all of them? + bne SEE28 ; brif not + jmp LB277 ; raise error +SEE34 stu VD5 ; save action address + jsr GETNCH ; eat the action token +SEE38 jsr SE0CB ; map the buffers and screen + jsr SELTASK1 + ldb VD3 ; get buffer number + jsr SEF18 ; find the correct buffer's data + ldd HORBEG ; get horizontal start + cmpd HOREND ; is it less than end? + ble SEE50 ; brif so + ldx HOREND ; swap start/end horizontal coords + stx HORBEG + std HOREND +SEE50 ldd VERBEG ; get vertical start + cmpd VEREND ; less that end? + ble SEE5D ; brif so + ldx VEREND ; swap vertical coords + stx VERBEG + std VEREND +SEE5D lda HRMODE ; get graphics mode + ldb #0xf8 ; round off mask for mode 3 (1 bpp) + cmpa #3 ; is it mode 3 (1 bpp) + beq SEE6D ; brif so + ldb #0xfc ; mask for mode 1 or 4 (2 bpp) + cmpa #2 ; is it mode 2? + bne SEE6D ; brif not - it's mode 1 or 4 + ldb #0xfe ; round off mask for mode 2 (4 bpp) +SEE6D tfr b,a ; save round off in A and B - we need it twice + anda HORBEG+1 ; round off horizontal start + sta HORBEG+1 + andb HOREND+1 ; round of horizontal end coord + stb HOREND+1 + jsr SE9DB ; calculate horizontal difference + std HOREND ; save it + jsr SE9CD ; calculate vertial difference + addd #1 ; make it inclusive + std VEREND ; save it + lda HRMODE ; get graphics mode + cmpa #2 ; HSCREEN 2? + beq SEE96 ; divide pixel count by 2 for byte count + cmpa #3 ; HSCREEN 3? + bne SEE92 ; brif not - divide by 4 (HSCREEN 1, 4) + lsr HOREND ; divide by 8 (falls through to by 4) + ror HOREND+1 +SEE92 lsr HOREND ; divide by 4 (falls through to divide by 2) + ror HOREND+1 +SEE96 lsr HOREND ; divide by 2 + ror HOREND+1 + ldd HOREND ; get byte count + addd #1 ; make it inclusive of the end + std HOREND + jsr HCALPOS ; get pointer to screen location + ldy VD5 ; point to action routine address +SEEA7 ldb HOREND+1 ; get LS byte of byte count + pshs x ; save line start pointer +SEEAB jsr ,y ; perform movement action + decb ; done all bytes? + bne SEEAB ; brif not + puls x ; get back line start + jsr SEA45 ; move down one line + dec VEREND+1 ; done all rows? + bne SEEA7 ; brif not + jsr SELTASK0 ; restore memory map + jsr SETMMU + rts +SEEC0 lda ,x+ ; get a byte from screen + bsr SEEC7 ; point to proper buffer location + sta ,u ; save it + rts +SEEC7 ldu VCF ; get buffer pointer + leau 1,u ; move to next byte + stu VCF ; save new pointer + cmpu VD1 ; did we run past the end of the buffer? + bhi SEED3 ; brif so - raise error + rts +SEED3 lds #TMPSTACK-2 ; reset stack + jsr SELTASK0 ; restore memory map + jsr SETMMU + jmp LB44A ; raise FC error +SEEE0 fdb SEEEF ; PSET action routine + fcb 0xbd ; PSET token + fdb SEEF6 ; PRESET action routine + fcb 0xbe ; PRESET token + fdb SEF07 ; OR action routine + fcb 0xb1 ; OR token + fdb SEEFE ; AND action routine + fcb 0xb0 ; AND token + fdb SEF10 ; NOT action routine + fcb 0xa8 ; NOT token +SEEEF bsr SEEC7 ; point to buffer location + lda ,u ; get byte from buffer + sta ,x+ ; put it on screen + rts +SEEF6 bsr SEEC7 ; point to buffer location + lda ,u ; get byte + coma ; invert it + sta ,x+ ; put it on screen + rts +SEEFE bsr SEEC7 ; point to buffer location + lda ,u ; get byte from buffer + anda ,x ; "AND" with screen data + sta ,x+ ; put it on screen + rts +SEF07 bsr SEEC7 ; point to buffer location + lda ,u ; get byte from buffer + ora ,x ; "OR" with screen data + sta ,x+ ; put on screen + rts +SEF10 bsr SEEC7 ; point to buffer address + lda ,x ; get byte from screen (BUG: should be ,u to get from buffer) + coma ; invert data + sta ,x+ ; save on screen + rts +SEF18 ldy #HRESBUFF ; point to start of buffers + lda ,y ; are there any buffers? + cmpa #0xff + bne SEF2C ; brif so + jmp SEED3 ; raise error if no buffers +SEF25 ldy ,y ; point to next buffer + lbeq SEED3 ; brif end of buffers - raise error +SEF2C cmpb 2,y ; is this the desired buffer? + bne SEF25 ; brif not + ldd 3,y ; get size of buffer + leay 4,y ; point to start of data (less one for "pre-inc" on use + sty VCF ; save buffer pointer + leay 1,y ; point to actual data start + leay d,y ; calculate address of end of buffer + sty VD1 ; save end address + rts +; HPRINT command +HPRINT tst HRMODE ; graphics mode? + lbeq SE6EF ; brif not - raise error + lbrn 0 + jsr LB26A ; insist on ( + jsr SE7B2 ; evaluate coordinates + jsr LB267 ; insist on ) + jsr SYNCOMMA ; insist on comma + jsr LB156 ; evaluate print string + tst VALTYP ; is it string? + bne SEF62 ; brif not numeric (should be BMI) + jsr LBDD9 ; convert number to string + jsr LB516 ; save string in string space and all that jazz +SEF62 jsr LB657 ; fetch string details + stb H.PCOUNT ; save length in print count + ldy #H.PBUF ; point to temporary string buffer +SEF6C decb ; have we processed the whole string? + bmi SEF75 ; brif so (or if the string length was > 128) + lda ,x+ ; copy a character from the string into the buffer + sta ,y+ + bra SEF6C ; see if we're done yet +SEF75 lda HRMODE ; get graphics mode + ldb #40 ; 40 characters on a 320 line + cmpa #3 ; is it mode 1 or 2? + blo SEF7F ; brif so + ldb #80 ; 80 characters on a 640 line +SEF7F clra ; zero extend line size + subd HORBEG ; subtract first position from line length + bmi SF001 ; brif we're printing off the side of the screen + cmpb H.PCOUNT ; is the print count larger than characters left? + bhi SEF8E ; brif not + stb H.PCOUNT ; save remaining screen positions as print count + beq SF001 ; brif nothing to print +SEF8E lda #ROWMAX-1 ; get highest row number + cmpa VERBEG+1 ; are we in range? + bge SEF96 ; brif so + sta VERBEG+1 ; force bottom row if not in range +SEF96 jsr SF08C ; calculate actual pixel coordinates + jsr HCALPOS ; get screen pointer + ldy #H.PBUF ; point to string data + ldb H.PCOUNT ; get number of characters to print +SEFA3 lda ,y ; get character to print + anda #0x7f ; lose bit 7 (character set repeats) + suba #0x20 ; lose the "control" characters - no glyphs for those codes + bpl SEFAD ; brif it was not a control character + lda #0 ; use a space if it was +SEFAD sta ,y+ ; put glyph number into the buffer + decb ; processed all of them? + bgt SEFA3 ; brif not + lda HRMODE ; get graphics mode + deca ; zero-base it + asla ; two bytes per display routine + ldy #SF002 ; point to display routine table + ldy a,y ; point to display routine + sty VD1 ; save it + lda #8 ; 8 rows per character + sta VD3 ; temp save row counter + ldy #H.PBUF ; point to print buffer + ldu #SF09D ; point to FONT data + ldb H.FCOLOR ; get foreground colour + jsr PIXELFIL ; get an all pixel byte + stb ALLCOL ; save it + jsr SELTASK1 ; map the screen + lda H.PCOUNT ; get character count to display +SEFD9 pshs y,x,a ; save buffer pointer, character count, and screen address +SEFDB ldb ,y+ ; get character from buffer + clra ; zero extend it + aslb ;* 8 bytes per character entry (don't need rola after first + aslb ;* because characters are only 7 bits + rola + aslb + rola + lda d,u ; get font data for this row + jsr [VD1] ; display it + dec H.PCOUNT ; done all characters on this row? + bgt SEFDB ; brif not + puls a,x,y ; get back character count, buffer pointer, and screen address + dec VD3 ; have we done all the rows? + beq SEFFE ; brif so + sta H.PCOUNT ; restore print count + leau 1,u ; move one row down the font data + jsr SEA45 ; move one row down the screen + bra SEFD9 ; go do another row of pixels +SEFFE jsr SELTASK0 ; restore memory map +SF001 rts +SF002 fdb SF01A ; HSCREEN 1 (2 bpp) + fdb SF045 ; HSCREEN 2 (4 bpp) + fdb SF00A ; HSCREEN 3 (1 bpp) + fdb SF01A ; HSCREEN 4 (2 bpp) +SF00A pshs a ; save font data + coma ; invert it + anda ,x ; merge with screen - turns off pixels in the character + sta ,x ; save it back on the screen + puls a ; get back font data + anda ALLCOL ; merge with colour data + ora ,x ; merge with screen to fill hole created above + sta ,x+ ; save it on screen + rts +SF01A pshs y ; save buffer pointer + ldy #SF035 ; point to 2 bpp pixel masks + tfr a,b ; copy character data (need two bytes per character) + lsra ; use the upper 4 bits in first byte + lsra + lsra + lsra + lda a,y ; get pixel mask for all 16 possibilities for upper 4 bits + jsr SF00A ; shove it on screen + andb #0x0f ; lose upper bits for low half + lda b,y ; get pixel mask for this pixel combination + jsr SF00A ; shove that on screen too + puls y ; restore buffer pointer + rts +SF035 fcb 0x00,0x03,0x0c,0x0f ; combined pixel masks for 16 possibilities for a 2 bpp byte + fcb 0x30,0x33,0x3c,0x3f + fcb 0xc0,0xc3,0xcc,0xcf + fcb 0xf0,0xf3,0xfc,0xff +SF045 pshs y,a ; save buffer pointer and font data + ldy #SF06C ; point to 16 colour masks + lsra ; fetch high 4 bits + lsra + lsra + lsra + asla ; two bytes per mask (this is NOT redundant - this and above clears bit 0) + ldd a,y ; get two byte mask for these four bits + jsr SF00A ; show upper 2 pixels + tfr b,a ; show lower 2 pixels + jsr SF00A + puls a ; get back font data + anda #0x0f ; lost upper bits + asla ; two bytes per mask + ldd a,y ; get mask data + jsr SF00A ; show upper 2 pixels + tfr b,a ; show lower 2 pixels + jsr SF00A + puls y ; restore buffer pointer + rts +SF06C fdb 0x0000,0x000f,0x00f0,0x00ff ; combined pixel masks for 16 possibilities for a 4 bpp double byte + fdb 0x0f00,0x0f0f,0x0ff0,0x0fff + fdb 0xf000,0xf00f,0xf0f0,0xf0ff + fdb 0xff00,0xff0f,0xfff0,0xffff +SF08C ldd HORBEG ; get horizontal character cell coordinate + aslb ; times 8 - 8x8 font data; note first shift can't cause carry with max 79 for column number + aslb + rola + aslb + rola + std HORBEG ; save actual horizontal pixel position of print position + lda VERBEG+1 ; get vertical character cell coordinate + asla ; times 8 - 8x8 font data + asla + asla + sta VERBEG+1 ; save actual vertical pixel position of print position + rts +; This is the HPRINT font, which is basically equivalent to the hardware font in the GIME for character codes +; 0x20 through 0x7f. It does not include the extra characters in the 0x00-0x1f range of the hardware character +; set. However, glyphs for those are actually included in the ROM above the end of the actual code. +SF09D fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; SPACE + fcb 0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x00 ; ! + fcb 0x28,0x28,0x28,0x00,0x00,0x00,0x00,0x00 ; " + fcb 0x28,0x28,0x7C,0x28,0x7C,0x28,0x28,0x00 ; # + fcb 0x10,0x3C,0x50,0x38,0x14,0x78,0x10,0x00 ; $ + fcb 0x60,0x64,0x08,0x10,0x20,0x4C,0x0C,0x00 ; % + fcb 0x20,0x50,0x50,0x20,0x54,0x48,0x34,0x00 ; & + fcb 0x10,0x10,0x20,0x00,0x00,0x00,0x00,0x00 ; ' + fcb 0x08,0x10,0x20,0x20,0x20,0x10,0x08,0x00 ; ( + fcb 0x20,0x10,0x08,0x08,0x08,0x10,0x20,0x00 ; ) + fcb 0x00,0x10,0x54,0x38,0x38,0x54,0x10,0x00 ; * + fcb 0x00,0x10,0x10,0x7C,0x10,0x10,0x00,0x00 ; + + fcb 0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x20 ; , + fcb 0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00 ; - + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00 ; . + fcb 0x00,0x04,0x08,0x10,0x20,0x40,0x00,0x00 ; / + fcb 0x38,0x44,0x4C,0x54,0x64,0x44,0x38,0x00 ; 0 + fcb 0x10,0x30,0x10,0x10,0x10,0x10,0x38,0x00 ; 1 + fcb 0x38,0x44,0x04,0x38,0x40,0x40,0x7C,0x00 ; 2 + fcb 0x38,0x44,0x04,0x08,0x04,0x44,0x38,0x00 ; 3 + fcb 0x08,0x18,0x28,0x48,0x7C,0x08,0x08,0x00 ; 4 + fcb 0x7C,0x40,0x78,0x04,0x04,0x44,0x38,0x00 ; 5 + fcb 0x38,0x40,0x40,0x78,0x44,0x44,0x38,0x00 ; 6 + fcb 0x7C,0x04,0x08,0x10,0x20,0x40,0x40,0x00 ; 7 + fcb 0x38,0x44,0x44,0x38,0x44,0x44,0x38,0x00 ; 8 + fcb 0x38,0x44,0x44,0x38,0x04,0x04,0x38,0x00 ; 9 + fcb 0x00,0x00,0x10,0x00,0x00,0x10,0x00,0x00 ; : + fcb 0x00,0x00,0x10,0x00,0x00,0x10,0x10,0x20 ; ; + fcb 0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x00 ; < + fcb 0x00,0x00,0x7C,0x00,0x7C,0x00,0x00,0x00 ; = + fcb 0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x00 ; > + fcb 0x38,0x44,0x04,0x08,0x10,0x00,0x10,0x00 ; ? + fcb 0x38,0x44,0x04,0x34,0x4C,0x4C,0x38,0x00 ; @ + fcb 0x10,0x28,0x44,0x44,0x7C,0x44,0x44,0x00 ; A + fcb 0x78,0x24,0x24,0x38,0x24,0x24,0x78,0x00 ; B + fcb 0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x00 ; C + fcb 0x78,0x24,0x24,0x24,0x24,0x24,0x78,0x00 ; D + fcb 0x7C,0x40,0x40,0x70,0x40,0x40,0x7C,0x00 ; E + fcb 0x7C,0x40,0x40,0x70,0x40,0x40,0x40,0x00 ; F + fcb 0x38,0x44,0x40,0x40,0x4C,0x44,0x38,0x00 ; G + fcb 0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x00 ; H + fcb 0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00 ; I + fcb 0x04,0x04,0x04,0x04,0x04,0x44,0x38,0x00 ; J + fcb 0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00 ; K + fcb 0x40,0x40,0x40,0x40,0x40,0x40,0x7C,0x00 ; L + fcb 0x44,0x6C,0x54,0x54,0x44,0x44,0x44,0x00 ; M + fcb 0x44,0x44,0x64,0x54,0x4C,0x44,0x44,0x00 ; N + fcb 0x38,0x44,0x44,0x44,0x44,0x44,0x38,0x00 ; O + fcb 0x78,0x44,0x44,0x78,0x40,0x40,0x40,0x00 ; P + fcb 0x38,0x44,0x44,0x44,0x54,0x48,0x34,0x00 ; Q + fcb 0x78,0x44,0x44,0x78,0x50,0x48,0x44,0x00 ; R + fcb 0x38,0x44,0x40,0x38,0x04,0x44,0x38,0x00 ; S + fcb 0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x00 ; T + fcb 0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00 ; U + fcb 0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x00 ; V + fcb 0x44,0x44,0x44,0x44,0x54,0x6C,0x44,0x00 ; W + fcb 0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x00 ; X + fcb 0x44,0x44,0x28,0x10,0x10,0x10,0x10,0x00 ; Y + fcb 0x7C,0x04,0x08,0x10,0x20,0x40,0x7C,0x00 ; Z + fcb 0x38,0x20,0x20,0x20,0x20,0x20,0x38,0x00 ; ] + fcb 0x00,0x40,0x20,0x10,0x08,0x04,0x00,0x00 ; \ + fcb 0x38,0x08,0x08,0x08,0x08,0x08,0x38,0x00 ; [ + fcb 0x10,0x38,0x54,0x10,0x10,0x10,0x10,0x00 ; UP ARROW + fcb 0x00,0x10,0x20,0x7C,0x20,0x10,0x00,0x00 ; LEFT ARROW + fcb 0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00 ; ^ + fcb 0x00,0x00,0x38,0x04,0x3C,0x44,0x3C,0x00 ; a + fcb 0x40,0x40,0x58,0x64,0x44,0x64,0x58,0x00 ; b + fcb 0x00,0x00,0x38,0x44,0x40,0x44,0x38,0x00 ; c + fcb 0x04,0x04,0x34,0x4C,0x44,0x4C,0x34,0x00 ; d + fcb 0x00,0x00,0x38,0x44,0x7C,0x40,0x38,0x00 ; e + fcb 0x08,0x14,0x10,0x38,0x10,0x10,0x10,0x00 ; f + fcb 0x00,0x00,0x34,0x4C,0x4C,0x34,0x04,0x38 ; g + fcb 0x40,0x40,0x58,0x64,0x44,0x44,0x44,0x00 ; h + fcb 0x00,0x10,0x00,0x30,0x10,0x10,0x38,0x00 ; i + fcb 0x00,0x04,0x00,0x04,0x04,0x04,0x44,0x38 ; j + fcb 0x40,0x40,0x48,0x50,0x60,0x50,0x48,0x00 ; k + fcb 0x30,0x10,0x10,0x10,0x10,0x10,0x38,0x00 ; l + fcb 0x00,0x00,0x68,0x54,0x54,0x54,0x54,0x00 ; m + fcb 0x00,0x00,0x58,0x64,0x44,0x44,0x44,0x00 ; n + fcb 0x00,0x00,0x38,0x44,0x44,0x44,0x38,0x00 ; o + fcb 0x00,0x00,0x78,0x44,0x44,0x78,0x40,0x40 ; p + fcb 0x00,0x00,0x3C,0x44,0x44,0x3C,0x04,0x04 ; q + fcb 0x00,0x00,0x58,0x64,0x40,0x40,0x40,0x00 ; r + fcb 0x00,0x00,0x3C,0x40,0x38,0x04,0x78,0x00 ; s + fcb 0x20,0x20,0x70,0x20,0x20,0x24,0x18,0x00 ; t + fcb 0x00,0x00,0x44,0x44,0x44,0x4C,0x34,0x00 ; u + fcb 0x00,0x00,0x44,0x44,0x44,0x28,0x10,0x00 ; v + fcb 0x00,0x00,0x44,0x54,0x54,0x28,0x28,0x00 ; w + fcb 0x00,0x00,0x44,0x28,0x10,0x28,0x44,0x00 ; x + fcb 0x00,0x00,0x44,0x44,0x44,0x3C,0x04,0x38 ; y + fcb 0x00,0x00,0x7C,0x08,0x10,0x20,0x7C,0x00 ; z + fcb 0x08,0x10,0x10,0x20,0x10,0x10,0x08,0x00 ; { + fcb 0x10,0x10,0x10,0x00,0x10,0x10,0x10,0x00 ; | + fcb 0x20,0x10,0x10,0x08,0x10,0x10,0x20,0x00 ; } + fcb 0x20,0x54,0x08,0x00,0x00,0x00,0x00,0x00 ; ~ + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00 ; _ +; HDRAW command +HDRAW tst HRMODE ; grahics mode? + lbeq SE6EF ; brif not + lbrn 0 + ldx #0 ; set empty string for "end of DRAW" + ldb #1 + pshs x,b + stb SETFLG ; set up for "set" mode + stx VD5 ; clear update and draw flags + jsr SE731 ; set up color byte + jsr LB156 ; evaluate command string +SF3B8 jsr LB654 ; fetch command string details + bra SF3C5 ; interpret command string +SF3BD jsr SF591 ; fetch command character + jmp SF5A7 ; evaluate number +SF3C3 puls b,x ; get "caller" command string details +SF3C5 stb VD8 ; save string pointer + beq SF3C3 ; brif end of string - try another + stx VD9 ; set string data pointer + lbeq SF4D0 ; brif we hit the top of the stack +SF3CF tst VD8 ; is there anything left? + beq SF3C3 ; brif not + jsr SF591 ; get command character + cmpa #'; ; separator? + beq SF3CF ; brif so - ignore it + cmpa #'' ; '? + beq SF3CF ; brif so - ignore that too + cmpa #'N ; update toggle? + bne SF3E6 ; brif not + com VD5 ; toggle the "update" flag (if set, return to original position after) + bra SF3CF ; process more +SF3E6 cmpa #'B ; blank modifier? + bne SF3EE ; brif not + com VD6 ; toggle "draw" flag - 0 = draw, nonzero = don't draw + bra SF3CF ; process more +SF3EE cmpa #'X ; substring call? + lbeq SF4A1 ; brif so - process it + cmpa #'M ; M (move)? + lbeq SF54C ; brif so - process "move" + pshs a ; save command character + ldb #1 ; default count if no number follows + clr VD3 ; clear MS byte of count + stb VD4 ; save LS byte of count + tst VD8 ; is there anything left? + beq SF417 ; brif not + jsr SF591 ; get command character + jsr LB3A2 ; set C if not alpha + pshs cc ; save alpha flag + jsr SF5F2 ; back up command pointer + puls cc ; get back alpha flag + bcc SF417 ; brif command is alpha + bsr SF3BD ; evaluate decimal string +SF417 puls a ; get command back + cmpa #'C ; C (colour)? + beq SF445 ; brif so + cmpa #'A ; A (angle)? + beq SF451 ; brif so + cmpa #'S ; S (scale)? + beq SF45C ; brif so + cmpa #'U ; U (up)? + beq SF496 ; brif so + cmpa #'D ; D (down)? + beq SF492 ; brif so + cmpa #'L ; L (left)? + beq SF48C ; brif so + cmpa #'R ; R (right)? + beq SF485 ; brif so + suba #'E ; shift E,F,G,H to be 0-3 + beq SF473 ; brif E (UR) + deca ; F (DR) + beq SF46D ; brif so + deca ; G (DL) + beq SF47D ; brif so + deca ; H (UL) + beq SF467 ; brif so + jmp LB44A ; raise error if unrecognized command +SF445 jsr SE711 ; adjust colour code for graphics mode + stb H.FCOLOR ; set new foreground + jsr SE731 ; set up colour byte + lbra SF3CF ; handle another command +SF451 cmpb #4 ; only 4 angles valid + lbhs LB44A ; brif invalid angle + stb ANGLE ; save draw angle + lbra SF3CF ; go handle another command +SF45C cmpb #63 ; only 0-62 are valid scale factors + lbhs LB44A ; brif invalid scale + stb SCALE ; set scale factor + lbra SF3CF ; process another command +SF467 lda VD3 ; get count MSB + bsr NEGACCD ; negate horizontal difference (go left) + bra SF46F ; go the same distance up +SF46D lda VD3 ; get count MSB +SF46F tfr d,x ; go same distance right as down + bra SF4D4 ; go handle movement/drawing +SF473 lda VD3 ; get MSB of count + tfr d,x ; going same distance on both axes + bsr NEGACCD ; negate the vertical distance + exg d,x ; put vertical in X, horizontal in D + bra SF4D4 ; go handle drawing and moving +SF47D lda VD3 ; get MSB of count + tfr d,x ; go same distance on both axes + bsr NEGACCD ; go left horizontally (and down vertically) + bra SF4D4 ; go handle drawing and moving +SF485 lda VD3 ; get MSB of difference (going right) +SF487 ldx #0 ; no vertical movement + bra SF4D4 ; handle drawing/moving +SF48C lda VD3 ; get MSB of count + bsr NEGACCD ; negate because going left + bra SF487 ; set no vertical difference, handle drawing/moving +SF492 lda VD3 ; get MSB of count + bra SF49A ; go make horizontal difference 0, use positive distance for down +SF496 lda VD3 ; get MSB of count + bsr NEGACCD ; use negative distance for up +SF49A ldx #0 ; use 0 horizontal distance + exg x,d ; put horizontal and vertical in the right places + bra SF4D4 ; go move/draw +SF4A1 jsr SF611 ; interpret command as a variable + ldb #2 ; see if we're about to run out of memory + jsr LAC33 + ldb VD8 ; get remaining characters in current command string + ldx VD9 ; get current command string pointer + pshs x,b ; save the stack frame + jmp SF3B8 ; go evaluate the string +SF4B2 ldb SCALE ; get scaling factor + beq SF4D1 ; brif none - use full scale + clra ; zero extend scale + exg d,x ; put distance in D, save scale factor + sta ,-s ; save MSB of distance and set flags on sign + bpl SF4BF ; brif positive distance + bsr NEGACCD ; make it positive if negative +SF4BF jsr SEBCB ; multiply D and X + tfr U,D ; save LSW in D + lsra ; divide by 4 + rorb + lsra + rorb + tst ,s+ ; was original positive? + bpl SF4D0 ; brif so +NEGACCD nega ; negate D + negb + sbca #0 +SF4D0 rts +SF4D1 tfr x,d ; keep unmodified distance + rts +SF4D4 pshs d ; save horizontal distance + bsr SF4B2 ; apply scale factor to vertical distance + puls x ; get back horizontal distance + pshs d ; save scaled vertical distance + bsr SF4B2 ; apply scale to horizontal distance + puls x ; get back the vertical distance + ldy ANGLE ; get draw angle (using Y to avoid clobbering D) + pshs y ; save it +SF4E5 tst ,s ; check angle + beq SF4F1 ; brif no angle + exg x,d ;* swap horizontal and vertical distances then negate new horizontal + bsr NEGACCD ;* distance, which rotates 90° counterclockwise + dec ,s ; count down the angle + bra SF4E5 ; see if we have rotated enough times +SF4F1 puls y ; clean up stack + ldu #0 ; default end position to 0 + addd HORDEF ; add distance to current draw position + bmi SF4FC ; brif we went negative - use minimal 0 + tfr d,u ; use calculated draw coordinate +SF4FC tfr x,d ; fetch vertical distance for calculation + ldx #0 ; default end position to 0 + addd VERDEF ; add distance to draw position + bmi SF507 ; brif we went negative - use minimal 0 + tfr d,x ; use calculated coordinate +SF507 cmpu #640 ; is it out of range completely? + blo SF510 ; brif not + ldu #639 ; maximize to right edge of screen +SF510 lda HRMODE ; get graphics mode + cmpa #2 ; is it a 320 mode? + bgt SF51F ; brif not + cmpu #320 ; out of range for 320 mode? + blo SF51F ; brif not + ldu #319 ; maximize to right edge of screen +SF51F cmpx #192 ; out of range vertically? + blo SF527 ; brif not + ldx #191 ; maximize to bottom of screen +SF527 ldd HORDEF ; set start position to current draw position + std HORBEG + ldd VERDEF + std VERBEG + stx VEREND ; set calculated position as end position + stu HOREND + tst VD5 ; are we going to update draw position? + bne SF53B ; brif not + stx VERDEF ; set new draw position + stu HORDEF +SF53B jsr SEA0D ; "normalize" coordinates + tst VD6 ; are we doing to draw a line? + bne SF545 ; brif not + jsr SE94E ; draw a line +SF545 clr VD5 ; reset the "update" flag + clr VD6 ; reset the "draw" flag + jmp SF3CF ; go handle another command +SF54C jsr SF591 ; get input character + pshs a ; save it + jsr SF578 ; evaluate horizontal distance + pshs d ; save it + jsr SF591 ; get a character + cmpa #', ; is it a comma separator? + lbne LB44A ; brif not - raise error + jsr SF575 ; evaluate the vertical distance + tfr d,x ; save vertical distance + puls u ; get horizontal distance + puls a ; get first command character + cmpa #'+ ; +? + beq SF570 ; treat coordinates as relative displacements + cmpa #'- ; -? + bne SF507 ; brif neither + or -; treat as absolute coordinates +SF570 tfr u,d ; put horizontal distance in D + jmp SF4D4 ; treat distances as offsets +SF575 jsr SF591 ; get character +SF578 cmpa #'+ ; +? + beq SF583 ; brif so - do positive + cmpa #'- ; -? + beq SF584 ; brif so - do negative + jsr SF5F2 ; back up input pointer +SF583 clra ; flag positive +SF584 pshs a ; save sign flag + jsr SF3BD ; evaluate decimal number + tst ,s+ ; is it positive? + beq SF590 ; brif so + negb ; negate the value - BUG: should be JSR NEGACCD; this code sequence doesn't work + sbca #0 +SF590 rts +SF591 pshs x ; save register +SF593 tst VD8 ; is there anything to fetch? + lbeq LB44A ; brif not - raise error + ldx VD9 ; get command pointer + lda ,x+ ; get command character + stx VD9 ; save updated pointer + dec VD8 ; account for character consumed + cmpa #0x20 ; space? + beq SF593 ; brif so - skip it + puls x,pc ; restore register and return +SF5A7 cmpa #'= ; is it variable equate? + bne SF5B6 ; brif not + pshs u,y ; save registers + bsr SF611 ; interpret variable in command string + jsr LB3E9 ; convert to integer in D + std VD3 ; save as count + puls y,u,pc ; restore registers and return +SF5B6 jsr SF608 ; clear carry if numeric + lbcs LB44A ; bail if not numeric + clr VD3 ; initialize count to 0 + clr VD4 +SF5C1 suba #'0 ; remove ASCII bias + sta VD7 ; save digit value + ldd VD3 ; get accumulated value + bsr SF5FD ; multiply by 10 + addb VD7 ; add digit value + adca #0 ; propagate carry + std VD3 ; save accumulated count value + lda HRMODE ; get graphics mode + cmpa #2 ; is it a 640 mode? + bgt SF5DA ; brif so + ldd #319 ; get max for 320 mode + bra SF5DD +SF5DA ldd #639 ; get max for 640 mode +SF5DD cmpd VD3 ; is the value in range for a horizontal coordinate? + lblt LB44A ; brif not + ldd VD3 ; get accumulated value + tst VD8 ; is there anything more to parse? + beq SF5FA ; brif not + jsr SF591 ; get a character + jsr SF608 ; set C if not digit + bcc SF5C1 ; brif digit - add to accumulated value +SF5F2 inc VD8 ; account for character being unfetched + ldx VD9 ; move command pointer back + leax -1,x + stx VD9 +SF5FA ldd VD3 ; get accumulated value + rts +SF5FD aslb ; times 2 + rola + pshs d ; save 2D + aslb ; times 4 + rola + aslb ; times 8 + rola + addd ,s++ ; 8D+2D=10D + rts +SF608 cmpa #'0 ; is it less than ASCII 0? + blo SF610 ; brif so - sets C + suba #'9+1 ; set C if > ASCII 9 + suba #-('9+1) +SF610 rts +SF611 ldx VD9 ; get command pointer + pshs x ; save it + jsr SF591 ; get command character + jsr LB3A2 ; set C if not alpha + lbcs LB44A ; brif not variable name +SF61F jsr SF591 ; get command character + cmpa #'; ; is it end of variable string? + bne SF61F ; brif not + puls x ; get back start of variable + ldu CHARAD ; save interpreter input pointer + pshs u + stx CHARAD ; save command string pointer as interpeter input + jsr LB284 ; evaluate variable + puls x ; restore interpreter input pointer + stx CHARAD + rts +; WIDTH command +WIDTH clr HRMODE ; turn off graphics + lbrn 0 + cmpa #0 ; end of line? (BUG: should do a BEQ before the CLR above) + beq SF64F ; brif so - raise error if no argument (won't trigger on :) + jsr EVALEXPB ; evaluate width argument + cmpb #32 ; 32 columns? + beq COL32 ; brif so + cmpb #40 ; 40 columns? + beq COL40 ; brif so + cmpb #80 ; 80 columns? + beq COL80 ; brif so +SF64F jmp LB44A ; raise FC error +COL32 clra ; set text mode to 32 columns + sta HRWIDTH + jsr LA928 ; clear screen + lbsr SETTEXT ; set up display for 32 column screen + rts +COL40 lda #1 ; mode number for 40 columns + sta HRWIDTH ; set text screen mode + lbsr SF772 ; map text screen + lda #40 ; set up scren size in character cells + ldb #ROWMAX + std H.COLUMN + ldd #HRESSCRN+40*ROWMAX*2 ; set end address of screen +SF66D std H.DISPEN ; save end address + bsr SF68C ; clear the screen + lbsr SF778 ; unmap text screen + lbsr SETTEXT ; set up display for the text screen + rts +COL80 lda #2 ; mode number for 80 columns + sta HRWIDTH ; set text screen mode + lbsr SF772 ; map the screen + lda #80 ; set up screen size in character cells + ldb #ROWMAX + std H.COLUMN + ldd #HRESSCRN+80*ROWMAX*2 ; set end address of screen + bra SF66D ; set up rest of parameters +SF68C ldx #HRESSCRN ; set cursor address to top left corner + lbrn 0 + stx H.CRSLOC + lda #0x20 ; use space to clear screen + ldb H.CRSATT ; get current attributes +SF69B std ,x++ ; blank a character cell + cmpx H.DISPEN ; end of screen? + blo SF69B ; brif not + ldx #HRESSCRN ; reset to top of screen + clra ; reset cursor coordinates to 0,0 + sta H.CURSX + sta H.CURSY + rts +; CLS patch entered from the other patch in the ECB area +ALINK23 puls cc ; restore zero flag + lbrn 0 + beq SF6E0 ; brif no arguments + jsr EVALEXPB ; get colour number + tstb ; 0? + beq SF6E0 ; brif so - treat as no arguments + cmpb #8 ; valid colour? + bhi SF6E7 ; brif not - do the easter egg or the other easter egg + decb ; zero-base the colour + leay IM.PALET,pcr ; point to current palette settings + lda b,y ; get the real colour + sta V.BORDER ; set border colour + lbsr SF766 ; set border colour in GIME initializers + stb H.CRSATT ; set attributes to foreground 0, background as selected, no blink or underline + lda #0x20 ; get space character + lbsr SF772 ; map screen + ldx #HRESSCRN ; get address of start of screen + stx H.CRSLOC ; put cursor there + bsr SF69B ; clear screen +SF6DC lbsr SF778 ; unmap screen + rts +SF6E0 lbsr SF772 ; map screen + bsr SF68C ; clear screen + bra SF6DC ; ummap screen and return +SF6E7 clr H.CRSATT ; reset attributes to colours 0,0, no blink or underline + lda IM.PALET ; get colour in register 0 + sta V.BORDER ; set border colour + bsr SF766 ; reset border colour in GIME initializers + cmpb #100 ; is it CLS 100? +SF6F4 beq SF730 ; brif so - do the easter egg + bsr SF772 ; map the screen + bsr SF68C ; clear screen + bsr SF778 ; unmap screen + ldx #MICROMS-1 ; point to Microware commercial + jmp STRINOUT ; display it +MICROMS fcc 'Microware Systems Corp.' + fcb 0x0d,0x00 +AUTHORMS fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; the ROM/RAM copy sets this to the actual easter egg text + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + fcb 0x00,0x00,0x00,0x00,0x00 +SF730 bsr SF772 ; map the screen + lbsr SF68C ; clear it + bsr SF778 ; unmap the screen + ldx #AUTHORMS-1 ; point to the easter egg + jsr STRINOUT ; display it + pshs x ; save X for some reason + leax >SF6F4,pcr ; point to start of easter egg code + lda #0x12 ; NOP opcode + sta ,x+ ; blank out the branch that brings us here + sta ,x + leax >AUTHORMS,pcr ; point to author message +SF74D sta ,x+ ; blank out character in string or this code + cmpx #SF74D ; end of string or display code? + blo SF74D ; brif not + puls x ; restore X + rts +; Line input routine patch for handling CLEAR +ALINK27 tst HRWIDTH ; is it 40/80 column screen? + bne SF761 ; brif so + jsr LA928 ; clear 32 column screen +SF75E jmp LA390 ; go make to mainline +SF761 lbsr SF6E0 ; clear the screen + bra SF75E ; return to mainline +SF766 pshs y ; save register + leay SE03B,pcr ; point to text initializers + sta 3,y ; set border in 40 column initializer + sta 12,y ; set border in 80 column initializer + puls y,pc ; restore registers and return +SF772 orcc #0x50 ; clobber interrupts + lbsr SELTEXT ; map the text screen (by setting *all 16* MMU registers) + rts +SF778 lbsr SETMMU ; unmap text screen (by setting *all 16* MMU registers) + andcc #0xaf ; restart interrupts + rts +; The driver for putting characters on the 40 and 80 column screen is here, modulo the WIDTH command and clear +; screen routines above. +; +; There are several major problems with this driver: +; +; * Cursor handling is overly complicated. Instead of doing what Color Basic does and only show the cursor +; when waiting for input in the generic input routine, this driver displays it almost all of the time and, thus, +; has all manner of code for managing the cursor that would not be needed otherwise. +; * The system goes out of its way to set up the screen height and width values. Then, it doesn't bother using +; them consistently, especially during the screen scrolling routine. Indeed, the screen scrolling routine could +; be made completely general purpose by using two pointers, say U for the destination and X for the source. Then +; the column count could be used to decide the line width (to calculate the offset betwen U and X) and then the +; row count could be used to set the row number after the scrolling is done. This would remove any hard coded +; offsets or screen size assumptions. +; * The routines for mapping and unmapping the text screen are inexcusably slow. Only one MMU register needs to +; be changed in either routine. However, instead of doing that, the routines use an inefficient routine that +; sets *all 16* MMU registers (both tasks!). This reduces screen output speed so much that a fast reader can +; actually keep up with the output going full speed. Simply replacing these two routines with simpler ones that +; do not do that dumbassery gets performance on par with the 32 column VDG screen. +; +; Along with the above, some of the code is far more convoluted than it needs to be, but that is relatively +; benign compared to everything else. +; +; Blink cursor patch +ALINK24 bsr SF787 ; blink the cursor + jsr KEYIN ; get keypress + beq ALINK24 ; brif no key pressed + puls b,x,pc ; return to caller +SF787 dec BLKCNT ; time to blink cursor? + bne SF7A8 ; brif not + ldb #11 ; reset blink counter + stb BLKCNT + bsr SF772 ; map screen + ldx H.CRSLOC ; get cursor pointer + lda 1,x ; get current attributes + bita #0x40 ; is underline on? + beq SF79F ; brif not - enable it + lda H.CRSATT ; use current attributes if it is + bra SF7A4 +SF79F lda H.CRSATT ; get current attributes + ora #0x40 ; turn on underline +SF7A4 sta 1,x ; save new attributes + bsr SF778 ; unmap screen +SF7A8 ldx #DEBDEL ; do a delay + jmp LA7D3 +; Put character on screen patch +ALINK22 bsr SF772 ; map the screen + lbrn 0 + ldx H.CRSLOC ; get cursor location + cmpa #0x08 ; backspace? + bne SF7C4 ; brif not + cmpx #HRESSCRN ; at start of screen? + beq SF7DE ; brif so - do nothing + bsr SF7E2 ; do a backspace + bra SF7DE ; finish up +SF7C4 cmpa #0x0d ; carriage return? + bne SF7CC ; brif not + bsr SF827 ; do a carriage return + bra SF7D7 ; finish up with scroll check +SF7CC cmpa #0x20 ; is it a control code? + blo SF7DE ; brif so - do nothing + ldb H.CRSATT ; get current attributes + std ,x ; put character on screen + bsr SF807 ; move cursor forward +SF7D7 cmpx H.DISPEN ; end of screen? + blo SF7DE ; brif not + bsr SF854 ; scroll screen +SF7DE bsr SF778 ; unmap the screen + puls a,b,x,pc ; restore registers and return +SF7E2 pshs b,a ; save registers + lda #0x20 ; space character + ldb H.CRSATT ; get attributes + std ,x ; turns off cursor at this position and blanks it + orb #0x40 ; turn on underline (we'll put a cursor in the previous position) + std -2,x ; put blank and cursor back one + leax -2,x ; move pointer back + stx H.CRSLOC ; save new cursor pointer + ldd H.CURSX ; get coordinates + deca ; move horizontal back + bpl SF802 ; brif we didn't wrap + decb ; move vertical back + stb H.CURSY ; save it + lda H.COLUMN ; get screen width + deca ; coordinates are zero-based so now we have the max horizontal coord +SF802 sta H.CURSX ; save new horizontal position + puls a,b,pc ; restore registers and return +SF807 pshs a,b ; save registers + lda #0x20 ; we'll blank a character for the cursor + ldb H.CRSATT ; get attributes + orb #0x40 ; force underline for cursor + leax 2,x ; move pointer forward + std ,x ; put blank and cursor on screen + stx H.CRSLOC ; save new cursor position + ldd H.CURSX ; get coordinates + inca ; move right + cmpa H.COLUMN ; did we hit the edge? + blo SF802 ; brif not - save new horizontal coordinate and return + incb ; bump line + stb H.CURSY ; save new line + clra ; reset to left side of screen + bra SF802 ; save new horizontal coordinate and return +SF827 pshs a,b ; save registers + lda #0x20 ; get space character + ldb H.CRSATT ; get attributes +SF82E std ,x++ ; blank a character + pshs a ; save character + lda H.CURSX ; get horizontal position + inca ; bump it + sta H.CURSX ; save new position + cmpa H.COLUMN ; edge of screen? + puls a ; restore character + blo SF82E ; brif not end of line yet + stx H.CRSLOC ; save cursor location + clr H.CURSX ; reset to left edge + inc H.CURSY ; bump row + lda #0x20 ; space character + ldb H.CRSATT ; get attributes + orb #0x40 ; turn on underline + std ,x ; put a cursor on screen + puls a,b,pc ; restore registers and return +SF854 pshs a,b ; save registers + ldx #HRESSCRN ; point to start of screen + lda H.COLUMN ; get screen width + cmpa #40 ; is it 40 columns? + bne SF86E ; brif not - do 80 column scroll +SF860 ldd 2*40,x ; get character cell from one line down + std ,x++ ; move it here + cmpx #HRESSCRN+(ROWMAX-1)*40*2 ; at start of last row? + blo SF860 ; brif not +SF86A bsr SF87B ; fill last row with spaces + puls a,b,pc ; restore registers and return +SF86E ldd 80*2,x ; get a character cell from next row + std ,x++ ; put it here + cmpx #HRESSCRN+(ROWMAX-1)*80*2 ; at start of last row? + blo SF86E ; brif not + bra SF86A ; blank out last row and finish up +SF87B clr H.CURSX ; reset column to 0 + lda #ROWMAX-1 ; reset row number to bottom of screen + sta H.CURSY + lda #0x20 ; get space character + ldb H.CRSATT ; get attributes + pshs x ; save pointer to start of row +SF88A std ,x++ ; blank a character + cmpx H.DISPEN ; at end of screen? + bne SF88A ; brif not + clr H.CURSX ; reset horizontal position to margin + puls x ; get start of line pointer + lda #0x20 ; space haracter + ldb H.CRSATT ; get attributes + orb #0x40 ; turn on underline + std ,x ; put a bleeping cursor at start of line + stx H.CRSLOC ; set cursor position + rts +; Conditional newline patch. Note that this maps and unmaps the text screen in 40/80 column mode +; but that is completely unneeded to just test the X coordinate. +ALINK26 tst DEVNUM ; is it screen? + bne SF8AB ; brif not + tst HRWIDTH ; VDG screen? + bne SF8B1 ; brif not +SF8AB jsr LA35F ; set up print parameters + jmp LB95F ; re-enter mainline code +SF8B1 lbsr SF772 ; map screen + tst H.CURSX ; at left margin? + pshs cc ; save Z flag + lbsr SF778 ; unmap screen + puls cc ; get back Z flag + lbne LB958 ; brif not at left margine - do CR + rts +; PRINT @ patch +ALINK25 tst HRWIDTH ; VDG screen? + bne SF8CD ; brif not - raise error + jsr LA554 ; move cursor to specified position + jmp LB905 ; return to mainline code +SF8CD ldb #39*2 ; code for HP error + jmp LAC46 ; raise error +; LOCATE command +; The parameter checking here could simply use the H.COLUMN and H.ROW variables and it would +; be loads simpler. Also, if the dumbassery with the cursor wasn't a thing, this routine wouldn't +; need to mess with mapping the screen or screwing around with the cursor. +LOCATE ldb HRWIDTH ; is it 40/80 column screen? + lbrn 0 + beq SF8CD ; brif not - raise error + pshs b ; save screen mode + jsr SE7B2 ; evaluate coordinates + lda BINVAL+1 ; get X coordinate + puls b ; get back screen mode + cmpb #1 ; is it 40 column screen? + bne SF8EB ; brif not + cmpa #40 ; in range for 40 columns? + bra SF8ED +SF8EB cmpa #80 ; in range for 80 columns? +SF8ED lbhs LB44A ; brif not - raise error + ldb VERBEG+1 ; get Y coordinate + cmpb #ROWMAX ; is it in range? + bhs SF8ED ; brif not - raise error + pshs d ; save new coordinates + lbsr SF772 ; map screen + std H.CURSX ; set screen coordinates + ldx H.CRSLOC ; get pointer to old position + lda H.CRSATT ; replace attributes with current ones + sta 1,x + lda H.COLUMN ; get number of columns (why not use this above?) + asla ; two bytes per character cell + mul ; now D is offset to start of row + ldx #HRESSCRN ; get start of screen + leax d,x ; now X points to the start of the line + puls a,b ; get back column and row numbers + asla ; two bytes per character cell + tfr a,b ; need this in B since we'll overflow singed 8 bits + abx ; offset to correct cursor position + lda H.CRSATT ; get attributes + ora #0x40 ; enable underline + sta 1,x ; enable cursor + stx H.CRSLOC ; save new cursor pointer + lbsr SF778 ; unmap screen + rts +; HSTAT command +HSTAT tst HRWIDTH ; is it 40/80 column screen? + lbrn 0 + beq SF8CD ; brif not - raise error + lbsr SF772 ; map the screen + ldx H.CRSLOC ; get cursor pointer + ldd ,x ; get character and attributes + std VCB ; save them + ldd H.CURSX ; get screen coordinates + std VCD ; save them + lbsr SF778 ; unmap screen + jsr LB357 ; evaluate variable for character + stx VARDES ; saveit + jsr SYNCOMMA ; insist on a comma + ldb #1 ; make a single character string + jsr LB56D + lda VCB ; get character on screen + jsr LB511 ; get string details + sta ,x ; save character in string + jsr LB54C ; put string on string stack + ldx VARDES ; point to variable descriptor + tst -1,x ; is it a string? (should have checked after evaluating instead) + lbpl LB151 ; do type mismatch if number + ldy FPA0+2 ; point to destination string descriptor + ldb #5 ; copy 5 bytes from newly created string into variable +SF963 lda ,y+ ; copy byte + sta ,x+ + decb ; done all? + bne SF963 ; brif not + LDX TEMPPT ; point to new string descriptor + leax -5,x ; BUG: should just call LB675 to remove string from string stack + stx TEMPPT + jsr LB357 ; evaluate a variable (for X coord) + stx VARDES ; save pointer to it + jsr SYNCOMMA ; insist on a comma after it + clra ; zero extend attributes + ldb VCB+1 ; get attribute byte + jsr GIVABF ; convert to float + ldx VARDES ; point to variable + tst -1,x ; test if numeric (should have tested VALTYP above) + lbmi LB151 ; TM error if not number + jsr LBC35 ; pack FPA0 to variable + jsr LB357 ; evaluate another variable + stx VARDES ; save it + jsr SYNCOMMA ; insist on a comma + clra ; zero extend the X coordinate + ldb VCD ; get X coordinate + jsr GIVABF ; turn into a FP number + ldx VARDES ; get variable + tst -1,x ; is it a number (should have tested VALTYP above) + LBMI LB151 ; brif not - TM error + jsr LBC35 ; pack FPA0 to variable + jsr LB357 ; evaluate another variable + stx VARDES ; save it + clra ; zero extend Y coordinate + ldb VCD+1 ; get Y coordinate + jsr GIVABF ; turn into a FP number + ldx VARDES ; get variable descriptor back + tst -1,x ; is it a number (should have tested VALTYP above) + lbmi LB151 ; brif not - TM error + jsr LBC35 ; pack FPA0 to variable + rts +; ATTR command +ATTR jsr EVALEXPB ; evaluate foreground colour + lbrn 0 + cmpb #8 ; there are 8 valid colours (0-7) + lbhs LB44A ; brif out of range - raise error + aslb ; shift over to bits 5,4,3 + aslb + aslb + pshs b ; save partial attribute byte + jsr GETCCH ; fetch current character (useless call) + jsr SYNCOMMA ; insist on comma + jsr EVALEXPB ; evaluate background colour + cmpb #8 ; is it valid (0-7)? + lbhs LB44A ; brif not - raise error + orb ,s ; merge with partial attribute byte + leas 1,s ; clean up stack (could use ,s+ above) + andb #0x3f ; make sure we have zeros in bit 7,6 - unneeded + pshs b ; save colour attributes + jsr GETCCH ; is there mode? +SF9E3 beq SFA06 ; brif no more flags + jsr SYNCOMMA ; insist on a comma + cmpa #'B ; B (blink)? + bne SF9F6 ; brif not + puls b ; set blink bit in accumulated attributes + orb #0x80 + pshs b + jsr GETNCH ; eat the flag + bra SF9E3 ; look for another flag +SF9F6 cmpa #'U ; U (underline)? + lbne LB44A ; invalid flag - raise error + puls b ; get accumulated attributes and set underline bit + orb #0x40 + pshs b + jsr GETNCH ; eat the flag + bra SF9E3 ; look for another flag +SFA06 puls b ; get new attributes + stb H.CRSATT ; set them as default + rts + fcb 0x00,0x00,0x00,0x00 ; unused bytes +; These are extra glyphs that should be part of the HPRINT font but aren't. + fcb 0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x10 ; Ç + fcb 0x44,0x00,0x44,0x44,0x44,0x4C,0x34,0x00 ; ü + fcb 0x08,0x10,0x38,0x44,0x7C,0x40,0x38,0x00 ; é + fcb 0x10,0x28,0x38,0x04,0x3C,0x44,0x3C,0x00 ; â + fcb 0x28,0x00,0x38,0x04,0x3C,0x44,0x3C,0x00 ; ä + fcb 0x20,0x10,0x38,0x04,0x3C,0x44,0x3C,0x00 ; à + fcb 0x10,0x00,0x38,0x04,0x3C,0x44,0x3C,0x00 ; ȧ (or å maybe?) + fcb 0x00,0x00,0x38,0x44,0x40,0x44,0x38,0x10 ; ç + fcb 0x10,0x28,0x38,0x44,0x7C,0x40,0x38,0x00 ; ê + fcb 0x28,0x00,0x38,0x44,0x7C,0x40,0x38,0x00 ; ë + fcb 0x20,0x10,0x38,0x44,0x7C,0x40,0x38,0x00 ; è + fcb 0x28,0x00,0x30,0x10,0x10,0x10,0x38,0x00 ; ï + fcb 0x10,0x28,0x00,0x30,0x10,0x10,0x38,0x00 ; î + fcb 0x00,0x18,0x24,0x38,0x24,0x24,0x38,0x40 ; ẞ + fcb 0x44,0x10,0x28,0x44,0x7C,0x44,0x44,0x00 ; Ä + fcb 0x10,0x10,0x28,0x44,0x7C,0x44,0x44,0x00 ; Ȧ (or Å maybe?) + fcb 0x08,0x10,0x38,0x44,0x44,0x44,0x38,0x00 ; ó + fcb 0x00,0x00,0x68,0x14,0x3C,0x50,0x3C,0x00 ; æ + fcb 0x3C,0x50,0x50,0x78,0x50,0x50,0x5C,0x00 ; Æ + fcb 0x10,0x28,0x38,0x44,0x44,0x44,0x38,0x00 ; ô + fcb 0x28,0x00,0x38,0x44,0x44,0x44,0x38,0x00 ; ö + fcb 0x00,0x00,0x38,0x4C,0x54,0x64,0x38,0x00 ; ø + fcb 0x10,0x28,0x00,0x44,0x44,0x4C,0x34,0x00 ; û + fcb 0x20,0x10,0x44,0x44,0x44,0x4C,0x34,0x00 ; ù + fcb 0x38,0x4C,0x54,0x54,0x54,0x64,0x38,0x00 ; Ø + fcb 0x44,0x38,0x44,0x44,0x44,0x44,0x38,0x00 ; Ö + fcb 0x28,0x44,0x44,0x44,0x44,0x44,0x38,0x00 ; Ü + fcb 0x38,0x40,0x38,0x44,0x38,0x04,0x38,0x00 ; § + fcb 0x08,0x14,0x10,0x38,0x10,0x50,0x3C,0x00 ; £ + fcb 0x10,0x10,0x7C,0x10,0x10,0x00,0x7C,0x00 ; ± + fcb 0x10,0x28,0x10,0x00,0x00,0x00,0x00,0x00 ; ° + fcb 0x08,0x14,0x10,0x38,0x10,0x10,0x20,0x40 ; ſ (long s) +; These are some extra symbol glyphs + fcb 0x00,0x10,0x18,0x1C,0x1C,0x18,0x10,0x00 ; solid right pointing triangle + fcb 0x00,0x08,0x18,0x38,0x38,0x18,0x08,0x00 ; solid left pointing triangle + fcb 0x00,0x00,0x00,0x7E,0x3C,0x18,0x00,0x00 ; solid down pointing triangle + fcb 0x00,0x00,0x18,0x3C,0x7E,0x00,0x00,0x00 ; solid up pointing triangle + fcb 0x00,0xFF,0x00,0xFF,0xFF,0x00,0xFF,0x00 ; three horizontal lines with middle one double thick + fcb 0x00,0x00,0x30,0x3C,0x14,0x1C,0x00,0x00 ; solid square on top left of open square + fcb 0x00,0x7E,0x42,0x5A,0x5A,0x42,0x7E,0x00 ; solid box inside larger box + fcb 0x00,0x7E,0x7E,0x00,0x00,0x7E,0x7E,0x00 ; thick equals sign + fcb 0x00,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x00 ; solid vertical rectangle + fcb 0x00,0x00,0x7E,0x7E,0x7E,0x7E,0x00,0x00 ; solid horizontal rectangle + fcb 0x00,0x7E,0x24,0x18,0x18,0x24,0x7E,0x00 ; hour glass + fcb 0x00,0x7F,0x00,0x7F,0x7F,0x00,0x7F,0x00 ; left end three horizontal lines with middle one double thick + fcb 0x00,0xFE,0x00,0xFE,0xFE,0x00,0xFE,0x00 ; right end three horizontal lines with middle one double thick +; The above 45 glyphs are duplicated below + fcb 0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x10 + fcb 0x44,0x00,0x44,0x44,0x44,0x4C,0x34,0x00 + fcb 0x08,0x10,0x38,0x44,0x7C,0x40,0x38,0x00 + fcb 0x10,0x28,0x38,0x04,0x3C,0x44,0x3C,0x00 + fcb 0x28,0x00,0x38,0x04,0x3C,0x44,0x3C,0x00 + fcb 0x20,0x10,0x38,0x04,0x3C,0x44,0x3C,0x00 + fcb 0x10,0x00,0x38,0x04,0x3C,0x44,0x3C,0x00 + fcb 0x00,0x00,0x38,0x44,0x40,0x44,0x38,0x10 + fcb 0x10,0x28,0x38,0x44,0x7C,0x40,0x38,0x00 + fcb 0x28,0x00,0x38,0x44,0x7C,0x40,0x38,0x00 + fcb 0x20,0x10,0x38,0x44,0x7C,0x40,0x38,0x00 + fcb 0x28,0x00,0x30,0x10,0x10,0x10,0x38,0x00 + fcb 0x10,0x28,0x00,0x30,0x10,0x10,0x38,0x00 + fcb 0x00,0x18,0x24,0x38,0x24,0x24,0x38,0x40 + fcb 0x44,0x10,0x28,0x44,0x7C,0x44,0x44,0x00 + fcb 0x10,0x10,0x28,0x44,0x7C,0x44,0x44,0x00 + fcb 0x08,0x10,0x38,0x44,0x44,0x44,0x38,0x00 + fcb 0x00,0x00,0x68,0x14,0x3C,0x50,0x3C,0x00 + fcb 0x3C,0x50,0x50,0x78,0x50,0x50,0x5C,0x00 + fcb 0x10,0x28,0x38,0x44,0x44,0x44,0x38,0x00 + fcb 0x28,0x00,0x38,0x44,0x44,0x44,0x38,0x00 + fcb 0x00,0x00,0x38,0x4C,0x54,0x64,0x38,0x00 + fcb 0x10,0x28,0x00,0x44,0x44,0x4C,0x34,0x00 + fcb 0x20,0x10,0x44,0x44,0x44,0x4C,0x34,0x00 + fcb 0x38,0x4C,0x54,0x54,0x54,0x64,0x38,0x00 + fcb 0x44,0x38,0x44,0x44,0x44,0x44,0x38,0x00 + fcb 0x28,0x44,0x44,0x44,0x44,0x44,0x38,0x00 + fcb 0x38,0x40,0x38,0x44,0x38,0x04,0x38,0x00 + fcb 0x08,0x14,0x10,0x38,0x10,0x50,0x3C,0x00 + fcb 0x10,0x10,0x7C,0x10,0x10,0x00,0x7C,0x00 + fcb 0x10,0x28,0x10,0x00,0x00,0x00,0x00,0x00 + fcb 0x08,0x14,0x10,0x38,0x10,0x10,0x20,0x40 + fcb 0x00,0x10,0x18,0x1C,0x1C,0x18,0x10,0x00 + fcb 0x00,0x08,0x18,0x38,0x38,0x18,0x08,0x00 + fcb 0x00,0x00,0x00,0x7E,0x3C,0x18,0x00,0x00 + fcb 0x00,0x00,0x18,0x3C,0x7E,0x00,0x00,0x00 + fcb 0x00,0xFF,0x00,0xFF,0xFF,0x00,0xFF,0x00 + fcb 0x00,0x00,0x30,0x3C,0x14,0x1C,0x00,0x00 + fcb 0x00,0x7E,0x42,0x5A,0x5A,0x42,0x7E,0x00 + fcb 0x00,0x7E,0x7E,0x00,0x00,0x7E,0x7E,0x00 + fcb 0x00,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x00 + fcb 0x00,0x00,0x7E,0x7E,0x7E,0x7E,0x00,0x00 + fcb 0x00,0x7E,0x24,0x18,0x18,0x24,0x7E,0x00 + fcb 0x00,0x7F,0x00,0x7F,0x7F,0x00,0x7F,0x00 + fcb 0x00,0xFE,0x00,0xFE,0xFE,0x00,0xFE,0x00 +; The glyphs above repeat one more time here but the set is incomplete + fcb 0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x10 + fcb 0x44,0x00,0x44,0x44,0x44,0x4C,0x34,0x00 + fcb 0x08,0x10,0x38,0x44,0x7C,0x40,0x38,0x00 + fcb 0x10,0x28,0x38,0x04,0x3C,0x44,0x3C,0x00 + fcb 0x28,0x00,0x38,0x04,0x3C,0x44,0x3C,0x00 + fcb 0x20,0x10,0x38,0x04,0x3C,0x44,0x3C,0x00 + fcb 0x10,0x00,0x38,0x04,0x3C,0x44,0x3C,0x00 + fcb 0x00,0x00,0x38,0x44,0x40,0x44,0x38,0x10 + fcb 0x10,0x28,0x38,0x44,0x7C,0x40,0x38,0x00 + fcb 0x28,0x00,0x38,0x44,0x7C,0x40,0x38,0x00 + fcb 0x20,0x10,0x38,0x44,0x7C,0x40,0x38,0x00 + fcb 0x28,0x00,0x30,0x10,0x10,0x10,0x38,0x00 + fcb 0x10,0x28,0x00,0x30,0x10,0x10,0x38,0x00 + fcb 0x00,0x18,0x24,0x38,0x24,0x24,0x38,0x40 + fcb 0x44,0x10,0x28,0x44,0x7C,0x44,0x44,0x00 + fcb 0x10,0x10,0x28,0x44,0x7C,0x44,0x44,0x00 + fcb 0x08,0x10,0x38,0x44,0x44,0x44,0x38,0x00 + fcb 0x00,0x00,0x68,0x14,0x3C,0x50,0x3C,0x00 + fcb 0x3C,0x50,0x50,0x78,0x50,0x50,0x5C,0x00 + fcb 0x10,0x28,0x38,0x44,0x44,0x44,0x38,0x00 + fcb 0x28,0x00,0x38,0x44,0x44,0x44,0x38,0x00 + fcb 0x00,0x00,0x38,0x4C,0x54,0x64,0x38,0x00 + fcb 0x10,0x28,0x00,0x44,0x44,0x4C,0x34,0x00 + fcb 0x20,0x10,0x44,0x44,0x44,0x4C,0x34,0x00 + fcb 0x38,0x4C,0x54,0x54,0x54,0x64,0x38,0x00 + fcb 0x44,0x38,0x44,0x44,0x44,0x44,0x38,0x00 + fcb 0x28,0x44,0x44,0x44,0x44,0x44,0x38,0x00 + fcb 0x38,0x40,0x38,0x44,0x38,0x04,0x38,0x00 + fcb 0x08,0x14,0x10,0x38,0x10,0x50,0x3C,0x00 + fcb 0x10,0x10,0x7C,0x10,0x10,0x00,0x7C,0x00 + fcb 0x10,0x28,0x10,0x00,0x00,0x00,0x00,0x00 + fcb 0x08,0x14,0x10,0x38,0x10,0x10,0x20,0x40 + fcb 0x00,0x10,0x18,0x1C,0x1C,0x18,0x10,0x00 + fcb 0x00,0x08,0x18,0x38,0x38,0x18,0x08,0x00 ; final duplicated glyph: left pointing solid triangle + fcb 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; junk unused (or blank space) + fcb 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF ; junk unused (or solid block) +; This is where the constant page (FExx) would start. It's just garbage in the ROM. + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 +; This is where the I/O page would start. It's just garbage in the ROM until the interrupt vectors. + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 + fill 0x00,8 + fill 0xff,8 +; These are the actual CPU interrupt vectors + fdb 0x0000 ; would be the 6309 illegal instruction trap + fdb INT.SWI3 ; SWI3 bounce vector address + fdb INT.SWI2 ; SWI2 bounce vector address + fdb INT.FIRQ ; FIRQ bounce vector address + fdb INT.IRQ ; IRQ bounce vector address + fdb INT.SWI ; SWI bounce vector address + fdb INT.NMI ; NMI bounce vector address + fdb L8C1B ; this is where execution starts on RESET or power on