; Astro BASIC (2000 Baud Version) ; (C) July 1978 Bally Mfg. ; (C) Dec 1980 Revised ; ; Written by Jay Fenton ; ; Source-code Retyped by Adam Trionfo ; ; Revisions: ; * March 2, 2004 - 1.0 ; - 100% Finished - HVGLIB.H 2.6 (or above) required ; - Matches perfectly with AstroBASIC cartridge ; - First assemble with no errors ; * February 29, 2004 - .02 ; - Finish typing in source-code ; - Start converting to standard Z-80 mnemonics ; * February 14, 2004 - .01 ; - Begin typing in source-code ; ; To assemble this Z-80 source code using ZMAC: ; ; zmac -d -o -x ; ; For example, assemble this Astrocade Z-80 ROM file: ; ; zmac -i -m -o astrobas.bin -x astrobas.lst astrobas.asm ; ; Part Two of Disassembly of the file "ASTROBAS.BIN" (C)1978&1980 ; Programmed and Commented by: Jay Fenton for Bally Manufacturing ; ; CPU Type: Z80 - for Bally Home Video Game / Home Library System ; ; Created with dZ80 1.50 and a whole lot of hand editing/copying! ; Re-assembles 100% correctly with: zmac -i -m AstroBAS.ASM ; ; Dis-assembled and pasted-up by: Richard C. Degler, From Scratch ; Beginning on or about Monday, 21 of April 2008 at 07:42 PM ; ; Renamed some labels and added a very few comments in Mixed Case ; ; Source of Jay Fenton's comments is "astrobas.asm" by Adam Trionfo ; retyped from a wrapped-around paper listing with page headings of: ; TDL Z80 CP/M DISC ASSEMBLER VERSION 2.21 [overwritten by Page #] ; .MAIN. - ; Note: The only text in "bally_basic_souce_pages_01-32.pdf" was: ; > Bally BASIC Interpreter ; > ; > © July 1978 Bally Mfg. ; > © December 1980 Revised ; > ; > Written by Jay Fenton ; and: ; > This page left blank for double-sided printing purposes. ; ; Part Three - More Update of the file "AstroBAS.BIN" (C)1978&1980 ; Programmed by: Jay Fenton for Bally Manufacturing [see Part Two] ; ; Jay Fenton's comments from "New Features/Differences in Basics" ; added as unindented Mixed Case text preceding revelant routines. ; ; Exact starting date unknown, sometime between Parts Two and Four. ; ; Part Four - Assembly Code for the file "AtroBASIC (listing).ASM" ; Programmed by: Jay Fenton for Bally Manufacturing [see Part Two] ; ; Reverse-source code found by Brett Bilbrey - "Bally Basic (1979)" ; Loose Leaf paper listing Scanned and saved as PDF by Adam Trionfo ; moved to Bally Alley Yahoo! user group dated Friday, May 15, 2009 ; ; Comments (ONLY) then retyped by: Richard C. Degler, From Scratch ; Beginning on Memorial Day Eve Sunday, 24 of May 2009 at 02:24 PM ; ; To match the style (and only where useful) these are marked by ;* ; Author Unknown, but no LC decenders listing had page headings of: ; ; CROMEMCO CDOS Z80 ASSEMBLER version 02.15 PAGE 0001 ; Bally Tiny Basic 8/25/79 ; [without leading semi-colons used here] and a program identifier: ; ; 0002 ;************************************************ ; 0003 ;* * ; 0004 ;* Bally Tiny Basic * ; 0005 ;* * ; 0006 ;* 8/25/79 * ; 0007 ;* * ; 0008 ;************************************************ ; 0009 ; ; ; [End of Header] ; ; Here then is ... ; *************************** ; * BALLY BASIC INTERPRETER * ; * * ; * (C) JULY 1978 BALLY MFG * ; * (C) DEC 1980 REVISED * ; * * ; * WRITTEN BY: JAY FENTON * ; * * ; * BALLY BASIC IS BASED ON * ; * PALO ALTO TINY BASIC BY * ; * LICHEN WANG * ; * * ; *************************** ; TINY BASIC INTERPRETER [with updated 2000 baud tape interface] ; NOLIST INCLUDE "HVGLIB.H" ; HOME VIDEO GAME LIBRARY ; ; MACROS TOKEN MACRO TINDX, TGOTO DB TINDX DEFF TGOTO ENDM ITEM MACRO STRANG, JUMPTO ; Quoted Character DB 'STRANG' DEFF JUMPTO ENDM DEFF MACRO WORDY ;* Jump table entry DB (WORDY >> 8) | $80 DB WORDY & $FF ENDM TSTC MACRO CAT, DOG ; Quoted Character RST 1 DB 'CAT' ;* Char. to check DB (DOG - $)-1 ;* Jump bias if no match ENDM TSTCC MACRO CAT1, DOG1 RST 1 DB CAT1 ;* Char. to check DB (DOG1 - $)-1 ;* Jump bias if no match ENDM ; New Features ; ------------ ; ; note from Jay Fenton on Bally/Astro BASIC differences: ; I believe you will be excited and amazed by these improvements. ; ; among others: The memory layout has been modified to leave the ; system RAM used by HVGSYS alone. This allows HVGSYS routines ; such as the music interpreter to be exploited. ; ; Defines HLTPORT EQU $15 ; KEYPAD KEY WITH HALT ONIT ; CR EQU $0D RUBOUT EQU $1F COMMA EQU $2C EDKEY EQU $66 NLLN EQU $67 ; ; EQUATES FOR RESTART INSTRUCTIONS RSTEXP EQU $10 ; EXPR - Get EXPRession restart ;* Evaluate expression RSTLDE EQU $18 ; LDE - LoaD A, from (DE) [was OUTC in bb and cb] RSTIGN EQU $20 ; IGNBLK - IGNore BLanKs ;* Get next non-blank from (DE) RSTPAR EQU $28 ; PARN - Expression in PAReNthesis ;* Get value in () RSTFIN EQU $30 ; FINISH - FINISH ;* cr. or ; otherwise, WHAT? ; BOTROM EQU FIRSTC ; $2000 ; ; Scratchpad area: BOTSCR EQU $4E20 ; BOTtom of SCReen [from $4000 to $4E18 + 2*4 for ??] TXTUNF EQU BOTSCR ;* "End of Basic Pgm" address [plus 2 is $FF00 ??] VARBGN EQU $4E22 ; One-letter variables (two bytes each) DEVVAR EQU $4E56 ; Two-letter DEVICES VARIABLES thusly: DEVCL0 EQU DEVVAR ; BC = Background Color 0 (default 7) DEVCL1 EQU $4E58 ; FC = Foreground Color 1 (default 0) DEVTEM EQU $4E5A ; NT = TEMPO Note Timer (default 2) VDMX EQU $4E5C ; CX = VDM X COORDINATE (default -77) VDMY EQU $4E5E ; CY = VDM Y COORDINATE (default 40) OLDXY EQU $4E60 ; XY = PREVIOUS COORDINATES FROM VECTOR DRAW ; Note: 12 new Two-letter device variables for AstroBasic (except for ReMain) DEVMO EQU $4E62 ; MO = Master OSC (center octave = 71) DEVOA EQU $4E64 ; TA = Tone OSCillator A DEVOB EQU $4E66 ; TB = Tone OSC B DEVOC EQU $4E68 ; TC = Tone OSC C DEVVD EQU $4E6A ; VR = Vibrato DEPTH aka "Range" = 0 - 63 DEVVR EQU $4E6C ; VF = Vibrato RATE aka "Frequency" 0 - 3 DEVVC EQU $4E6E ; VC = Volume C DEVNM EQU $4E70 ; NM = Noise Mode if 1, Vibrato if 0 DEVVA EQU $4E72 ; VA = Volume A DEVVB EQU $4E74 ; VB = Volume B DEVNV EQU $4E76 ; NV = Noise Volume REMAIN EQU $4E78 ; RM = REMAINder FROM LAST DIVIDE SCRMOD EQU $4E7A ; SM = Scroll Mode ; VDMNLF EQU $4E7C ; VDM NEW LINE FLAG KEYTMR EQU $4E7D ; KEYBOARD SCAN TIMER ;* Key release timer (60 Hz) MUZTMR EQU $4E7E ; MUSIC NOTE TIMER ;* Time remaining on current note NEWTMR EQU $4E7F ; NEW MUSIC TIMER VALUE ;* Time for next note MUZMO EQU $4E80 ; MASTER OSC FOR DICK ;* Master sound divider value MUZTON EQU $4E81 ; TONE VALUE ;* Next note to output SHARPF EQU $4E82 ; SHARP-FLAT ; KEYTRK EQU $4E83 ; KEYpad debounce TRacKer ;* Last calc. input LINEND EQU $4E84 EDFLG EQU $4E86 ; line EDit FLaG PIXVAL EQU EDFLG ; PIXel VALue TO DRAW WITH EDPTR EQU $4E87 ; line EDit PoinTeR MNMX EQU EDPTR ; MiN - MaX DELTAS FOR VECTOR DRAW INCRO EQU $4E89 ; COORDINATE INCRements FOR VECTOR DRAW NLLNLN EQU $4E8B ; AUTO LINE # STUFF NLLNCT EQU $4E8D NLLNZS EQU $4E8E ; AUTO LINE NUMBER Zero Suppress FLAG OLDLN EQU $4E8F ; PREVIOUS LINE # TYPED (used for GO+10) ; CHECKER EQU $4E91 ; PLACE FOR CHECKSUM HKVECT EQU $4E92 ; 14 bytes initialized from HOOKER : HKLPINT EQU HKVECT ; JP ($4E93) for Light Pen INTerrupt HKINT EQU $4E95 ; JP ($4E96) for Screen INTerrupt CHKIO EQU $4E98 ; JP ($4E99) for CHaracter Key IOput OUTCH EQU $4E9B ; JP ($4E9C) for OUTput CHaracter STACKP EQU $4E9E ; STACK toP reset on halt or stop ; ALTFON EQU $4EA0 ; 7 byte ALTernate FONt descriptor ;* Character Spec Table ; ?? EQU $4EA6 ;* [+6 ??] Indexes VRBL storage area OLDCUR EQU $4EA7 ; OLD CURsor CURRNT EQU $4EA9 ; CURReNT line STKGOS EQU $4EAB ; STacK for GOSub STKINP EQU $4EAD ; ?? VARNXT EQU STKINP ;* "NEXT" VRBL address LOPVAR EQU $4EAF ; FOR VARiable ;* "FOR" VRBL address LOPINC EQU $4EB1 ; FOR STEP ;* "STEP" VRBL address LOPLMT EQU $4EB3 ; FOR TO ;* "TO" VRBL address LOPLN EQU $4EB5 ; LOoP Line Number ;* Line # of current FOR loop LOPPT EQU $4EB7 ; 3 bytes LOoP PoinTer ;* Current FOR line text pointer ; XQTBUF EQU $4EBA ; eXeCUTE BUFFER aka BUFFER EQU XQTBUF ; line input BUFFER ;* Keyboard input buffer BUFEND EQU $4F22 ; BUFfer END (104 Characters) ; ?? unused block of 48 bytes ?? STKLMT EQU $4F6A ; STacK LiMiT (128 bytes available) TOPSCR EQU DURAT ; $4FEA used for Top of Stack STACK EQU TOPSCR ; ... and loads into STACKP vector ; BOTRAM EQU $A000 ; [Denotes $4000 but shared with screen image] TXT EQU BOTRAM ; [was TEXT] Text Array Area [flagged as negative ??] DFTLMT EQU TXT + $070C ; [Last interlaced address is $A70C] ; [use final two routines to read/write bit-shared data from screen+$6000 ?? [counts??]] ; ; (the @(x) array runs up from TXTUNF to, and *(x) array backwards from) DFTLMT ; No check is performed to prevent *( ) from overstoring @( ) [new in AB] ; LIST ORG BOTROM JP BEGIN ; ** AUTOSTART CASSETTE ** PIXTBL: DB $80 ;* Masks for picture data (PX) DB $20 DB $08 DB $02 ; TRANSFER VECTORS TO RESTART ROUTINES JP TSTCH ; * RST $08 JP EXPR ; * RST RSTEXP ;* Evaluate expression JP LDE ; * RST RSTLDE [was OUTC ; * RST RSTOUT in bb and cb ??] JP IGNBLK ; * RST RSTIGN ;* Get next non-blank from (DE) JP PARN ; * RST RSTPAR ;* Get value of () or storage adrs POP AF ; * RST RSTFIN ;* cr. or ; otherwise, WHAT? JP FINISH ; Text String WHAT: DB 'WHAT?',CR ; note: Light pen interrupts are always enabled. The ; light pen interrupt routine is initialized as null, i.e. ; re-enable interrupts and return. Therefore the coordinates of ; the most recent light pen hit are available from &(15) and &(14). ; As stated in the HACKERS GUIDE, the coordinates may be off by a ; constant amount due to time delays in the light pen circuits. ; The addition or subtraction of a fudge factor may be needed. ; ; INITIAL VECTOR ITEMS - points to HKVECT block in RAM, change vectors there DW HKLPINT ; (ITAB & $FFF0) HooK Light Pen INTerrupt vector ITAB: DW HKINT ; default HooK INTerrupt vector ; INITIAL VALUES FOR PARAMETER VECTOR ;* Following is moved to DEVVAR at $4E56 INIDEV: DW $0007 ; BACKGROUND COLOR ;* BC preset-White DW $0000 ; FOREGROUND COLOR ;* FC preset-Black DW $0002 ; MUSIC TEMPO ;* NT preset DW $FFB3 ; (-77) VDM X COORDINATE ;* CX preset DW $0028 ; (40) VDM Y COORDINATE ;* CY preset ; BIT BANGER GOODIES FOLLOW: BANGIN EQU $3C00 ; BIT BANGER READ PORT BANG1 EQU $3800 ; BIT BANG CODE TO WRITE A ONE BANG0 EQU $3C00 ; BIT BANG CODE TO WRITE A ZERO ; WASTEIT MACRO TIME, ?LAB ; was WASTE[TIME,%LAB]= originally LD A,TIME ?LAB DEC A JR NZ,?LAB ENDM ; :PRINT COMMAND ; IF VARIABLE NAME, BLOCKSIZE GIVEN, WE WILL WRITE OUT ; THE SPECIFIED BLOCK RATHER THAN THE PROGRAM STORAGE AREA ; ; The :PRINT command causes the stored program, the ; screen image, the values in @( ) and *( ) arrays, and the ; values of all the variables to be written out. This process ; will take between 10 and 20 seconds. As only one jack is ; provided on the BASIC ROM cassette, it is necessary for the ; user to manually connect the audio cable to the MIC jack of ; the cassette recorder. ; When writing blocks from *( ) we must cite the LAST ; word in the block rather than the first. This is because *( ) ; runs backwards. ; The recording will consist of a 3 second leader ; tone, then the data block, followed by a 1/2 second trailer ; tone. The tone frequencies are derived from software timing ; loops accurate to plus or minus one microsecond. ; TOUTPU: CALL IGNATNL ; ANY ARGS? do IGNore blanks, 0 AT NewLine or ; JR Z,YYSPGM ; JUMP TO SAVE PGM IF SO CALL TSTVFF ; ELSE GET START ADDR ;* Get adrs. of VRBL PUSH HL ; SAVE THAT TSTCC COMMA,BADSAV ; CHECK FOR COMMA RST RSTEXP ; GET BLOCK SIZE ;* Evaluate expression ADD HL,HL ; CONVERT TO BYTES EX DE,HL EX (SP),HL ; PUSH DE ON STACK EX DE,HL ; DE=START, HL=SIZE JR YYOUTB ; JUMP TO OUTPUTER YYSPGM: LD HL,CHECKER - $4000 PUSH DE LD DE,$4000 ; SAVE PROGRAM TO TAPE ; YYOUTB: SYSSUK EMUSIC DI CALL LEADER ; WRITE OUT SOME LEADER LD A,$A5 CALL OUTBYA ; do OUTput BYte in A to 2000-baud tape OBL: RST RSTLDE ; does LD A,(DE) [from TeXT if necessary] CALL OUTBYA ; TWEEDLE IT OUT INC DE ; BUMP BLOCK PTR DEC HL ; DECREMENT BLOCK SIZE LD A,H ; LOOP END YET? OR L JR NZ,OBL RST RSTLDE ; does LD A,(DE) [from TeXT if necessary] LD A,B ; OUTPUT CHECKSUM CPL ; COMPLEMENT FOR LATER TEST CALL OUTBYA ; do OUTput BYte in A to 2000-baud tape LD B,$02 RST RSTLDE ; does LD A,(DE) [from TeXT if necessary] EX (SP),HL EX (SP),HL CALL LEADR1 ; PUT OUT TRAILER POP DE EI RST RSTFIN ; BYE BYE ;* cr. or ; otherwise, WHAT? BADSAV: JP QWHAT ; :LIST command ; ; The :LIST command has been modified to perform a ; verify function. It no longer LISTS the data on tape, as the ; data rate is far too high. Now :LIST will scan a digital ; recording and verify that the checksum is correct. If ; incorrect, a question mark is printed just before the ; command returns. This allows one to check recording ; integrity without damaging anything. ; If problems arise, check the playback level on your ; cassette recorder. We have found that maximum volume ; settings work best. It is recommended that AC power be used ; if possible to minimize speed fluctuations. ; TVLIST: PUSH DE CALL TVLLNK ; do Prepare for tape input POP DE RST RSTFIN ;* cr. or ; otherwise, WHAT? ; SPECIAL ENTRY TO LOAD COMBINED SCREEN AND PGM ; ; :INPUT COMMAND ; IF VARIABLE ADDRESS IS GIVEN, WE WILL INPUT ; THE BLOCK INTO THE SPECIFIED AREA, OTHERWISE ; WE HANDLE IT LIKE A PROGRAM ; ; To load programs, use :INPUT. This will retrieve the ; program, the screen image, the arrays and variables from ; audio tape. It is necessary to 'cue up' the audio tape on ; the three second leaden tone, and switch the cable over to ; EARPHONE before loading. ; The accuracy of the load is verified using a ; checksum. If a checksum error is detected, a question mark ; will be typed out just before :INPUT returns. It is possible ; that a checksum error could damage the program or system ; variables in such a way as to 'bomb' the computer. ; ; Optional specifies where the load is to ; begin. Note that the size of the block was determined when ; it was written out. The block read back will be one byte ; longer than the one written out. The extra byte is the ; checksum byte. Therefore, allow one word of 'slop' when ; reading into an array. ; TINPUT: CALL IGNATNL ; AND ARGS? do IGNore blanks, 0 AT NewLine or ; LD HL,$4000 CALL NZ,TSTVFF ; GET VAR ADDR ;* Get adrs. of VRBL PUSH DE CALL INBLK ; do INput a BLocK from 2000-baud tape XXELOD: POP DE RST RSTFIN ;* cr. or ; otherwise, WHAT? ; :RUN COMMAND - LOADS BOOTSTRAP INTO RAM ; AND JUMPS TO IT ; ; The :RUN command is provided for loading machine ; language programs. The load will begin at the top of the ; screen ($4000 or 16484). When the load is completed, ; control will be transferred to this first address. The block ; loaded is limited in size [to 128 ??] only by the need to avoid ; 'stomping' on the stack area. ; TLOAD: LD HL,$4000 ; HL=SCREEN TOP PUSH HL ; SUBROUTINE TO INPUT A BLOCK, HL=STORE ADDR ; FIRST - AN ENTRY TO REVEAL FEEDBACK AREA INBLK: CALL SENWAI ; do SENtinel WAIt ; LOOP TO GRAB CHARS AND STORE EM ZZCHRL: CALL INCHAR ; do Read CHARacter from 2000-baud tape JR Z,ZZEOT CALL STHL ; STore A by HL [interlaced into TeXT if necessary] INC HL JR ZZCHRL ;* Prepare for tape input ; TVLLNK: CALL SENWAI ; do SENtinel WAIt ZZKIL: CALL INCHAR ; do Read CHARacter from 2000-baud tape JR NZ,ZZKIL ZZEOT: DEC HL EI INC D ; SHOULD HAVE BEEN FF RET Z OUTCHQ: LD A,'?' JP OUTCH ; to Output Question Mark SENWAI: SYSSUK EMUSIC DI SENW: CALL INCHAR ; WAIT FOR THE SENTINEL JR Z,SENW CP $A5 JR NZ,SENW LD D,A RET ; INCHAR CLOBBERS A, BC, DE ; NZ IF NO TIMEOUT, Z IF TIMEOUT, CHAR INC ; INCHAR: LD A,(BANGIN+2) AND $01 LD E,A ; PRIME THE PUMP LD BC,$0810 ; 8 BITS, 10=TIMEOUT FACTOR SBW: CALL INBIT ; WAIT FOR START BIT JR NC,GETL ; GOT IT DEC C ; TIMEOUT? JR NZ,SBW ; NOT YET RET ; Z SET GETL: CALL INBIT CALL C,INBIT ; GET ANOTHER 1 RR C DJNZ GETL ; GET 8 BITS LD A,C ; UPDATE CHECKSUM ADD A,D LD D,A DEC B ; SET NONZERO LD A,C ; RETURN VALUE RET ; RETURN ; CHECK FOR ABORT LOAD KEY INBIT: IN A,(HLTPORT) RRCA JP C,INIT0 ; do INITialize interrupts to 0 LD A,(BANGIN) XOR E ; CHECK FOR CHANGE RRCA JR NC,INBIT ; NO - WAIT WASTEIT $17 LD A,(BANGIN+1) AND $01 CP E LD E,A RET Z SCF RET ; OUTBYT CLOBBERS A, BC OUTBYA: LD C,A ; GET CHAR FROM A ADD A,B ; ADD CHECKSUM ACCUM LD B,A ; AND SAVE PUSH BC CALL WRZERO ; WRITE START BIT WASTEIT $0E ; VERY TIME SENSITIVE LD B,$08 ; WRITE 8 DATA, 1 STOP WRL: SCF RR C ; GET BIT, INSERT 1 FOR STOP JR C,WR1 ; IF ONE, WRITE ONE CALL WRZERO ; ELSE WRITE ZERO WASTEIT $0C JR NXT NXT: JR WRE ; LOOP COUNTER WR1: CALL WRONE ; WRITE ONE-BIT WASTEIT $20 WRE: DJNZ WRL ; TILL 8 BITS DONE JR SEX SEX: POP BC CALL WRONE ; WRITE A ONE BIT FOR STOP WASTEIT $18 RET ; ; LEADER CLOBEBERS BC AND A LEADER: LD B,$0F ; APPROX 3 SECS LEADR2: WASTEIT $20 NOP LEADR1: CALL WRONE ; LEADER IS ALL ONES DEC BC LD A,B OR C JR NZ,LEADR2 WASTEIT $1D RET ; ; WRONE WRITES ONE HALF CYCLE OF ONE-BIT (1/1200 SEC) ; WRONE: LD A,(BANG1) ; CHANGE ITS STATE WASTEIT $24 ; WAIT SOME, THEN FALL INTO... LD A,(BANG0) RET ; ; WRZERO WRITES ONE HALF CYCLE OF ZERO BIT (1/2400 SEC) ; WRZERO: LD A,(BANG1) WASTEIT $11 NOP NOP LD A,(BANG0) RET ; CKHLDE and COMP [should be down below CHKSGN & CHGSGN ??] ; CKHLDE: LD A,H ; *** CKHLDE *** XOR D ; SAME SIGN? JP P,COMP ; YES, COMPARE ;* Jp if same sign EX DE,HL ; NO, XCH AND COMPARE ;* Exchange and fall into ... ;* COMPare HL and DE ;* Z if HL = DE ;* C if HL < DE COMP: LD A,H ; *** COMP *** CP D ; COMPARE HL WITH DE RET NZ ; RETURN CORRECT C AND LD A,L ; ZFLAGS CP E ; BUT OLD A IS LOST RET ; Text Strings continued HOW: DB 'HOW?',CR SORRY: DB 'SORRY',CR ; TABLE GIVING JUMP TO ADDRESS FOR COMMANDS TOKJT: DW LISTCOM ; 68 - 'LIST' COMmand routine DW CLRSCR ; 69 - 'CLEAR' DW RUN ; 6A - 'RUN' DW NEXT ; 6B - 'NEXT' DW LINEDR ; 6C - 'LINE' DW IFF ; 6D - 'IF' DW GOTO ; 6E - 'GOTO' DW GOSUB ; 6F - 'GOSUB' DW RETURN ; 70 - 'RETURN' DW BOXDRW ; 71 - 'BOX' DW FOR ; 72 - 'FOR' DW INPUT ; 73 - 'INPUT' DW PRINT ; 74 - 'PRINT' ; INITIAL HOOK VECTOR ITEMS ; ; EXPERIMENTERS note: HOOK VECTORS have been installed in RAM ; to allow the user to gain control at several strategic places. ; The hook vectors are jump instructions which reside in RAM, ; which then branches to the appropriate routine. Hooks are ; provided for the following functions: 1) Light Pen Interrupt, ; 2) Screen Interrupt, 3) Character Read, 4) Character Print. ; HOOKER: JP LPINT ; Copied to HKVECT JP TBIINT JP XCHKIO JP XOUTCH DW STACK ; set to DURAT as default ; TABLE GIVING ASCII CHARS FOR TOKENS with last character marked negative TOKTXT: DB 'LIS' DB 'T' + $80 ; ($68) "LIST" Text DB 'CLEA' DB 'R' + $80 ; ($69) "CLEAR" DB 'RU' DB 'N' + $80 ; ($6A) "RUN" DB 'NEX' DB 'T' + $80 ; ($6B) "NEXT" DB 'LIN' DB 'E' + $80 ; ($6C) "LINE" DB 'I' DB 'F' + $80 ; ($6D) "IF" DB 'GOT' DB 'O' + $80 ; ($6E) "GOTO" DB 'GOSU' DB 'B' + $80 ; ($6F) "GOSUB" DB 'RETUR' DB 'N' + $80 ; ($70) "RETURN" DB 'BO' DB 'X' + $80 ; ($71) "BOX" DB 'FO' DB 'R' + $80 ; ($72) "FOR" DB 'INPU' DB 'T' + $80 ; ($73) "INPUT" DB 'PRIN' DB 'T' + $80 ; ($74) "PRINT" DB 'STE' DB 'P' + $80 ; ($75) "STEP" DB 'RN' DB 'D' + $80 ; ($76) "RND" DB 'T' DB 'O' + $80 ; ($77) "TO" ; DEVICE VARIABLE TABLE /* Special 2-letter variables ; THIS TABLE IS IN INVERSE ORDER OF APPEARENCE IN MEMORY PARNUM EQU 19 ; [was only 7 in bb] DEVLST: ; [has first character minus $40 for ?? why??] DB 'S'-'@' DB 'M' ; "SM" = Scroll Mode [New for AB] DB 'R'-'@' DB 'M' ; "RM" = ReMainder from last divide DB 'N'-'@' DB 'V' ; "NV" = Noise Volume [New for AB] DB 'V'-'@' DB 'B' ; "VB" = Volume B [New for AB] DB 'V'-'@' DB 'A' ; "VA" = Volume A [New for AB] DB 'N'-'@' DB 'M' ; "NM" = Noise Mode [New for AB] DB 'V'-'@' DB 'C' ; "VC" = Volume C [New for AB] DB 'V'-'@' DB 'F' ; "VF" = Vibrato Frequency [New for AB] DB 'V'-'@' DB 'R' ; "VR" = Vibrato Range [New for AB] DB 'T'-'@' DB 'C' ; "TC" = Tone C [New for AB] DB 'T'-'@' DB 'B' ; "TB" = Tone B [New for AB] DB 'T'-'@' DB 'A' ; "TA" = Tone A [New for AB] DB 'M'-'@' DB 'O' ; "MO" = Master Oscillator [New for AB] DB 'X'-'@' DB 'Y' ; "XY" = Location from last draw DB 'C'-'@' DB 'Y' ; "CY" = Character print Y DB 'C'-'@' DB 'X' ; "CX" = Character print X DB 'N'-'@' DB 'T' ; "NT" = Note Tempo DB 'F'-'@' DB 'C' ; "FC" = Foreground Color DB 'B'-'@' DB 'C' ; "BC" = Background Color ; TINY BASIC INTERRUPT ROUTINE ;* NORMAL INTERRUPT PROCESSOR ; TBIINT: PUSH AF ; SAVE REGISTERS PUSH BC PUSH HL ; DEAL WITH KEYBOARD SCAN TIMER LD HL,KEYTMR LD A,(HL) ;* Get key release timer AND A JR Z,TBIN0 ;* Jp if already zero DEC (HL) ; Count KEYTMR down to 0 TBIN0: INC HL ; point to MUZTMR ; HAS MUSIC TIMER COUNTED DOWN? LD A,(HL) AND A JR Z,TBIN1 ; YEP - PLAY NEXT NOTE DEC (HL) ; ELSE DECREMENT IT JR NZ,TBIN3 ; JUMP IF NOT NOW ZERO XOR A LD (DEVOA),A ; Silence A Stacato JR TBIN2 ; MUSIC TIMER IS AT ZERO - ARE NEW PARAMETERS READY? TBIN1: INC HL ; STEP TO NEW TIMER VALUE OR (HL) ; IS IT NON ZERO? JR Z,TBIN3 ; JUMP IF NOT LD (HL),$00 ; SAY WE GOT IT JP M,TBIN3 ; IF MINUS UPDATE NOTHING DEC HL ; ELSE SET OFFICIAL TIMER LD (HL),A INC HL INC HL LD A,(HL) ; SET NEW MASTER LD (DEVMO),A LD (HL),OA2 ; Restore Center Octave = 71 INC HL LD A,(HL) ; AND NEW TONE LD (DEVOA),A AND A ; REST WANTED? JR Z,TBIN3 ; YES - JUMP AROUND VOLUME UPDATE LD A,$0F TBIN2: LD (DEVVA),A ; SET COLOR REGISTERS TO VALUES IN PARAMETER VARS %0 AND %1 TBIN3: LD A,(DEVCL0) ;* Get BC OUT (COL0L),A OUT (COL1L),A LD A,(DEVCL1) ;* Get FC OUT (COL2L),A OUT (COL3L),A ; UPDATE THE MUSIC PROCESSOR [new for ab] LD A,(DEVTEM) ; Note Timer < 0 ? RLCA JR C,INTDON LD BC,$0410 ; &(16) thru &(19) LD HL,DEVMO ; Start with Master Oscillator LP1: LD A,(HL) OUT (C),A INC HL INC HL INC C DJNZ LP1 ; Then TA, TB, TC LD B,(HL) ; B=VD aka VR INC HL INC HL LD A,(HL) ; A=VR aka VF RRCA RRCA XOR B AND $C0 XOR B OUT (C),A ; &(20) = "VFVRange" INC HL INC HL LD B,(HL) ; B=VOLC INC HL INC HL LD A,(HL) ; A=NM RLCA RLCA RLCA RLCA XOR B AND $30 XOR B OUT (VOLC),A ; &(21) = "00NMVCcc" INC HL INC HL LD B,(HL) ; VA INC HL INC HL LD A,(HL) ; VB RLCA RLCA RLCA RLCA XOR B AND $F0 XOR B OUT (VOLAB),A ; &(22) = "VBbbVAaa" INC HL INC HL LD A,(HL) ; GET NOISE VOLUME OUT (VOLN),A ; &(23) = "NVvalues" ; DONE - RESTORE REGISTERS AND GO BACK INTDON: POP HL POP BC POP AF ;* Light pen interrupt processor [was 300baud read in bb, now just...] LPINT: EI RET ; COMMAND TO SILENCE MUSIC PORTS - but only if NT < 0 ; SILENCE: PUSH DE SYSSUK FILL ; Clear TA through NV DW DEVOA DW $0014 DB $00 POP DE RST RSTFIN ;* cr. or ; otherwise, WHAT? ; ROUTINE TO MOVE PROGRAM LINE FROM PGM STORAGE AREA ; INTO EXECUTION BUFFER ; ; ENHANCED EXECUTION SPEED ; ; Internal changes have been made to speed up the ; execution of BASIC programs. The speedup ranges from 50% to ; 100% depending on the program. In general, long programs ; with long statements will benefit the most. The speedup is ; implemented by transferring the text of each line from pixel ; shared storage into the line input buffer before execution. ; This allows most 'searching' functions expedited access to ; the line. ; This has forced the introduction of an ; incompatibility with the original version of BASIC. The use ; of GOSUB statements in immediate mode (in a line without a ; line number) is now illegal. ; EXPAND: LD HL,(CURRNT) LD BC,(OLDCUR) AND A SBC HL,BC RET Z EXPMAN: LD HL,(CURRNT) BIT 7,H ; IN LINE BUFFER ALREADY? RET Z ; YES - KICKOUT LD (OLDCUR),HL INC HL INC HL ADD HL,HL LD BC,XQTBUF EXP1: LD A,(HL) RLCA INC HL XOR (HL) AND $AA XOR (HL) LD (BC),A INC HL INC BC CP $0D JR NZ,EXP1 SCF RR H RR L LD (LINEND),HL RET ; SUBROUTINE TO RETURN ZERO STATUS IF CHARACTER IN A IS NL ; OR ';' [was ZONATNL in bb, IGNore restart added for ab] ; IGNATNL: RST RSTIGN ; IGNORE ANY BLANKS ;* Get next non-blank from (DE) CP ';' ; CHECK FOR CONTINUATION RET Z CP CR ; AND FOR CR RET ; FUNCTION TO RETURN STATE OF ADDRESSED PIXEL ; IE... PIX(X,Y)= 1 IF PIXEL IS 1, 0 IF 0 ; ;* PX - Check screen dot ;* PX(,) ; PIXFUN: TSTC '(',PIXDUD ;* Open = Char. to check, Jump if no match PUSH BC RST RSTEXP ;* Evaluate expression PUSH HL ;* Save value of TSTCC COMMA,PIXDUD ;* Comma = Char. to check, Jump if no match RST RSTEXP ;* Evaluate expression TSTC ')',PIXDUD ;* Close = Char. to check, Jump if no match POP BC ;* X value PUSH DE ; SAVE PTR ;* Save text pointer LD D,L ;* D = Y LD E,C ;* E = X CALL R2ABS ;* Get odd crt adrs to A EX DE,HL ;* and adrs to DE SYSSUK INDEXB ; INDEX BYTE (SYSTEM SUBROUTINE) DW PIXTBL ; HL + A [not loaded!] to HL LD A,(DE) ; GET BYTE FROM SCREEN ;* Get CRT dots AND (HL) ; MASK OFF NONSENSE ;* AND with mask LD H,$00 LD L,H POP DE POP BC ;* No dot RET Z INC HL RET ;* Dot on ; SUBROUTINE TO GET VARIABLE MAKING SURE IT IS ONE ; TSTVFF: CALL TSTV ;* Get VRBL storage address RET NC ; GO BACK IF GOOD ;* Was a variable ; ELSE FALL INTO... PIXDUD: JP QWHAT ; CLEAR COMMAND ; CLRSCR: CALL CLRENT ; do RESET VDM GOODIES then CLeaR ENTire screen LD HL,$0000 ; and clear OLD XY [separate ??] LD (OLDXY),HL RST RSTFIN ;* cr. or ; otherwise, WHAT? ; BOX DRAW ROUTINE ;* - Draw a box on the screen ;* BOX(,,,,) ; BOXDRW: RST RSTEXP ; GET X ;* Evaluate expression PUSH HL ;* Save starting x TSTCC COMMA,BOXDUD ;* Comma = Char. to check, Jump if no match RST RSTEXP ; GET Y ;* Evaluate expression PUSH HL ;* Save starting y TSTCC COMMA,BOXDUD ;* Comma = Char. to check, Jump if no match RST RSTEXP ; Get Expr., Range 0-255 [was 1-255 in bb] LD A,L PUSH AF ;* Save xsize TSTCC COMMA,BOXDUD ;* Comma = Char. to check, Jump if no match RST RSTEXP ; Get Expr., Range 0-255 [was 1-255 in bb] LD B,L PUSH BC ;* Save ysize TSTCC COMMA,BOXDUD ;* Comma = Char. to check, Jump if no match RST RSTEXP ;* Get box type ;* Evaluate expression PUSH DE ;* Save text pointer POP IX ;* in IX POP BC ; RESTORE YS ;* Get ysize to B POP AF ; AND XS LD C,A ;* Get xsize to C LD A,L ; PRESERVE FLAG ;* Get box type to A POP HL LD D,L ;* Get start y to D POP HL LD E,L ;* Get start x to E LD L,A ; NOW WE HAVE: B=YS, C=XS, D=Y, E=X, L=FLAG ; LIMIT CHECK Y LD H,B SRL H LD A,D CALL SABS ; do Set to ABSoulte value ADD A,H CP $2D JR NC,BOXNDR LD A,B ; DIVIDE SIZE AGAIN DEC A ; THIS TIME WITH PRESUB SRL A ADD A,D LD D,A ; AND X LD H,C SRL H LD A,E CALL SABS ; do Set to ABSoulte value ADD A,H CP $51 JR NC,BOXNDR LD A,E SUB H LD E,A ; DIDDLE WITH FLAG BYTE LD A,L AND $03 ; MODULO 4 JR Z,BOXNDR ; SKIP DRAW IF ZERO SUB $02 ; ELSE SUBTRACT 2 FOR MASK BOXDR1: PUSH AF CALL R2ABS ; do Relative TO ABSoulte conversion ; HL = ABS ADDR, A = SA, B=YS, C=XS OUT (MAGIC),A POP AF CALL BOXPUT ; do draw a BOX PUT on screen BOXNDR: PUSH IX POP DE RST RSTFIN ;* cr. or ; otherwise, WHAT? BOXDUD: JP QWHAT ; Set A to ABSoulute value ; SABS: AND A RET P CPL INC A RET ; SUBROUTINE TO DRAW A BOX ON SCREEN ; ; The BOX command has been fixed to eliminate a ; problem with the off-screen detection logic. BOX will still ; refuse to draw a box that is partially or totally off-screen. ; The bug used to cause a bug in the scrolling function where ; stripes from 'nowhere' would propagate up the screen. This ; was exploited by some as a 'feature' to make video art. ; Sorry. ; BOXPUT: LD E,A LD A,C ; D = X / 4 RRCA RRCA AND $3F INC A LD D,A ; PAINT FULL BOX STRIPES MPT1: DEC D JR Z,MPT2 LD A,10101010B CALL STRIPE ; do draw a STRIPE on screen JR MPT1 MPT2: LD A,C AND $03 INC A LD C,A XOR A MPT3: DEC C JR Z,MPT4 RRCA RRCA OR 10000000B JR MPT3 MPT4: CALL STRIPE ; do draw a STRIPE on screen XOR A ; FALL INTO... ; SUBROUTINE TO PAINT A STRIPE ; STRIPE: PUSH HL PUSH BC LD (WASTER),A LD A,(WASTER + $4000) LD C,A STRP1: LD A,E CP $01 JR NZ,STRP2 LD A,(HL) XOR C STRP2: XOR (HL) AND C XOR (HL) LD (HL),A LD A,L ADD A,BYTEPL LD L,A LD A,H ADC A,$00 LD H,A DJNZ STRP1 POP BC POP HL INC HL RET ; LINE DRAWER ;* - Draw a line on the screen ; ; The LINE command now performs rudimentary clipping. ; The onscreen portion of the line is drawn. Note that only ; the low order 8 bits of a coordinate are examined by LINE. ; Thus 256 is equivalent to 0 and so on. ; ;* LINE(,,) ; LINEDR: RST RSTEXP ;* Evaluate expression LD A,L PUSH AF ;* Save starting x TSTCC COMMA,LINEDX ;* Comma = Char. to check, Jump if no match RST RSTEXP ;* Evaluate expression LD A,L PUSH AF ;* Save starting y TSTCC COMMA,LINEDX ;* Comma = Char. to check, Jump if no match RST RSTEXP ;* Evaluate expression LD C,L PUSH DE POP IX LD DE,(OLDXY) ;* Current xy [Y is high order byte, X is lower byte] POP AF LD H,A POP AF LD L,A LD (OLDXY),HL ; SET NEW LAST PLACE ;* Update xy ; DIDDLE WITH FLAG BYTE LD A,C AND $03 JR Z,LINED1 SUB $02 LD (PIXVAL),A ; SET PIXVAL CALL DVECT ; do Draw line VECTor LINED1: PUSH IX POP DE RST RSTFIN ;* cr. or ; otherwise, WHAT? LINEDX: JP QHOW ; LARRY LIVERMORE'S VECTOR DRAWING ALGORITHM ; H=Y1, L=X1, D=Y2, E=X2 ; DVECT: PUSH DE LD B,L LD C,E CALL CDELTA ; do Compute DELTA for X LD E,B LD L,C LD B,H LD C,D CALL CDELTA ; do Compute DELTA for Y LD H,C LD D,B ; NOW WE HAVE: H=SGN(DY), L=SGN(DX) ; D=ABS(DY), E=ANS(DX) LD (INCRO),HL ; DECIDE WHICH DELTA IS LARGER ; CALL BIGGER MX, SMALLER MN LD C,$00 LD A,D CP E JR C,VECT1 LD D,E LD E,A INC C VECT1: LD A,D ; MX TO A SRL A LD B,A EX DE,HL LD (MNMX),HL POP DE LD A,L INC A ; MAKE SURE LAST PIXEL WRITTEN ; THE INFAMOUS PIXEL PAINTING LOOP VECT2: PUSH AF CALL R2ACLP ; do Relative To Absolute with CLiPping [new for ab] JR NC,VECT2A PUSH BC PUSH HL LD C,A LD B,$00 LD HL,PIXTBL ADD HL,BC LD B,(HL) POP HL LD A,(PIXVAL) CP $01 JR NZ,VECT9 LD A,(HL) XOR B VECT9: XOR (HL) AND B XOR (HL) LD (HL),A POP BC ; INCREMENT COORDINATES VECT2A: LD HL,(MNMX) LD A,B ADD A,H CP B ; DID WRAP AROUND UNIVERSE? JR C,FUZZ CP L JR C,VECT4 FUZZ: SUB L LD B,A LD HL,(INCRO) LD A,D ADD A,H LD D,A VECT3: LD A,E ADD A,L LD E,A JR VECT5 VECT4: LD B,A LD HL,(INCRO) LD A,C RRCA JR NC,VECT3 LD A,D ADD A,H LD D,A ; END OF LOOP VECT5: POP AF DEC A JR NZ,VECT2 RET ; SUBROUTINE TO LOAD HL WITH VDM COORDINATES ; FROM DEVICE VARIABLES ; LDVDMC: PUSH AF LD A,(VDMY) CPL ADD A,$29 CP $51 ; OUT OF RANGE? JR C,LDVDM1 ; NO XOR A LDVDM1: LD H,A LD A,(VDMX) ; DIDDLE WITH X ADD A,$4D CP $9D JR C,LDVDM2 XOR A LDVDM2: LD L,A POP AF RET ; SUBROUTINE TO STORE HL INTO VDM COORDINATE DEVICE VARIABLES ; STVDMC: PUSH HL LD A,H SUB $29 CPL LD L,A CALL SGNEXT ; do SiGN EXTend from L into H LD (VDMY),HL ;* Update CY POP HL LD A,L SUB $4D LD L,A CALL SGNEXT ; do SiGN EXTend from L into H LD (VDMX),HL ;* Update CX RET ; SUBROUTINE TO COMPUTE DELTA AND INCREMENT FOR TWO COORDINATES ; CDELTA: PUSH HL PUSH DE LD L,C CALL SGNEXT ; do SiGN EXTend from L into H EX DE,HL LD L,B CALL SGNEXT ; do SiGN EXTend from L into H XOR A SBC HL,DE ; COMPUTE SGN(DELTA) AND ABS(DELTA) OR H JR Z,CDELT1 LD C,A LD A,L CPL INC A LD B,A JR CDELT3 CDELT1: OR L ; POS CASE 0? JR Z,CDELT2 LD A,$01 CDELT2: LD B,L LD C,A CDELT3: POP DE POP HL RET ; RELATIVE TO ABSULUTE CONVERSION WITH CLIPPING ; R2ACLP: LD A,E ADD A,$50 ; X was -80 up to 79 CP $A0 ; IN RANGE 0-159 - CX FOR OK RET NC LD A,D ADD A,$2C ; Y was -44 up to 43 CP $58 ; IN RANGE 0-87 - CY FOR OK RET NC ; ... ; RELATIVE TO ABSOLUTE CONVERSTION ; R2ABS: PUSH DE ;* Save XY LD A,D ;* Get y CPL ;* Reverse sense ADD A,$2C ;* Make it 0-87 LD D,A ;* Put it back LD A,E ADD A,$50 ;* Same for x [0-159] LD E,A XOR A SYSTEM RELAB1 ;* Get CRT adrs to DE EX DE,HL POP DE ;* Regain original XY SCF RET ; KB - FUNCTION TO RETURN NEXT CHARACTER FROM KEYBOARD ; GETKB: PUSH BC PUSH DE CALL CHKIO ; do Read a character from keyboard POP DE KBLNKX: POP BC LD L,A ;* HL is output register LD H,$00 RET ; DEVICE VARIABLE TO OUTPUT TO REFERENCED IO PORT ;* OUTPUT &()= ; PUTIO: RST RSTPAR ; GET PORT # ;* Get value of () or storage adrs TSTC '=',PUTCD2 ; GET EQUALS PUSH HL ; SAVE PORT # RST RSTEXP ; EVALUATE EXPRESSION FOLLOWING LD A,L ; A=VALUE TO OUTPUT POP HL ; RESTORE PORT # PUSH BC LD B,H LD C,L OUT (C),A ; IT 1 POP BC RST RSTFIN ; GO HOME ;* cr. or ; otherwise, WHAT? ; FUNCTION TO RETURN VALUE OF A GIVEN IO PORT ;* INPUT =&() ; IOFUN: RST RSTPAR ;* Get value of () or storage adrs PUSH BC ; GET PORT NUMBA LD B,H LD C,L IN A,(C) JR KBLNKX ; DEVICE VARIABLE TO PLAY NOTE WITHOUT PRINTING [via MU=] ; PUTMU: TSTC '=',PUTCD2 RST RSTEXP ;* Evaluate expression LD A,L CALL PNOTE ; do Play NOTE tone for key in A RST RSTFIN ;* cr. or ; otherwise, WHAT? ; DEVICE VARIABLE TO OUTPUT CHARACTER ON VDM [via TV=] ; PUTCD: TSTC '=',PUTCD2 RST RSTEXP ;* Evaluate expression LD A,L CALL OUTCH ; do OUTput CHaracter in A RST RSTFIN ;* cr. or ; otherwise, WHAT? PUTCD2: JP QWHAT ; ROUTINE TO TRANSFER CONTROL TO ASSEMBLY LANGUAGE SUBROUTINE ; ; The CALL command transfers control to an assembly ; Language subroutine. This routine should terminate by ; executing a RET instruction. Register DE contains a pointer ; to the line being interpreted, if needed it should be saved ; then restored before returning to BASIC. Stupid example: ; 500 CALL 0 ; Self destruct reset! ; ;* CALL ; DOCALL: LD HL,BBRET ; PUSH RETURN ADDR ON STACK PUSH HL RST RSTEXP ; GET ADDRESS ;* Evaluate expression JP (HL) ; AND JUMP TO IT ; ** TINY BASIC EXECUTION STARTS HERE ** ; CLEAR WHOLE KIT AND KABOOBLE ; ; ;*********************** ;* Cold Start * ;*********************** ; BEGIN: XOR A OUT (MAGIC),A ; Set up port $0C LD H,A ;* Clear $4000-$4FFF LD L,A LD (HL),A ; MAKE SURE SHIFTER FLUSHED BEGIN1: LD (HL),A INC HL BIT 4,H JR Z,BEGIN1 LD SP,SYSRAM SYSTEM INTPC ;* Start multiple Subr. DO SETOUT DB $B0 ;* Display height DB 00101100B ;* Border [$2C or 44] color 0, Left DB $18 ;* Normal and _Masked_ Lite Pen interrupts ; [*** This is what breaks MESS v.0.124 and above - try $0C for Unmasked LPI ??] ; INITIALIZE DEVICE VARIABLES DO MOVE ; MOVE BYTES (SYSTEM ROUTINE) DW DEVVAR DW $000A ;* Move 10 bytes from DW INIDEV ;* $2024 to $4E56 DO MOVE ; MOVE BYTES (SYSTEM ROUTINE) DW ALTFON DW $0007 ;* Move 7 bytes from DW FNTSYS ;* $0206 to $4EA0 DO MOVE ; MOVE BYTES (SYSTEM ROUTINE) DW HKVECT DW $000E DW HOOKER DO SETW ; STORE WORD (SYSTEM ROUTINE) DW $06A0 DW ALTFON ;* Char spacing = 6, table base [still] = $A0 DO SETW ; STORE WORD (SYSTEM ROUTINE) DW TXT + 4 DW TXTUNF ;* LD (TXTUNF),$A004 [in virtual TeXT area] DO SETW ; STORE WORD (SYSTEM ROUTINE) [commented TeXT + 2 ??] DW $5555 ; [TeXT + 1 init to $FF (or -1) End of Program marker] DW $4002 ; LD (NORMEM + 2),$55 and LD (NORMEM + 3),$55 [why ??] DONT XINTC ;* End multiple Subr. ;* Initialize interrupts ; INIT0: DI IM 2 LD A,ITAB >> 8 ;* Interrupt Page LD I,A LD A,ITAB & $FF ; Set TBIINT as Interrupt OUT (INFBK),A ;* Interrupt vector LD A,$C8 ;* Interrupt every 200 lines OUT (INLIN),A ; [actually on every line 200 !] EI INIT: CALL CRLF ; Prints NULL,cr [instead of a string] TELL: ; ; DIRECT COMMAND - TEXT COLLECTOR ; ; STOP command ; ; The STOP command halts the program. It is typed in ; as four discrete keystrokes. ; STOP: ; note: The STACK TOP is reset on halt or stop from STACKP ; instead of a fixed location. This allows some stack space ; to be borrowed for assembly language routines. [new for AB] ; RSTART: LD HL,(STACKP) ; DURAT precludes Counter Timers use LD SP,HL LD HL,XXST1 + 1 LD (CURRNT),HL XXST1: LD HL,$0000 LD (OLDCUR),HL LD (LOPVAR),HL LD (STKGOS),HL XXST2: LD A,'>' CALL GETLN ; do GET a LiNe PUSH DE LD DE,BUFFER CALL TSTNUM ;* Get possible line number RST RSTIGN ;* Get next non-blank from (DE) LD A,H OR L POP BC ;* BC = end of line JR Z,EXEC0 ;* Direct mode if no line # LD (OLDLN),HL DEC DE ;* Put binary line number in LD A,H ;* front of first significant LD (DE),A ;* character of line in input DEC DE ;* buffer LD A,L LD (DE),A PUSH BC PUSH DE LD A,C SUB E PUSH AF ;* A = # bytes in line CALL FNDLN ;* Find this line in SAVE area PUSH DE ;* DE = adrs JR NZ,XXST3 ;* Jp if not found; insert PUSH DE ;* Found, delete it CALL FNDNXT ;* Find next line POP BC ;* Start adrs of line to delete LD HL,(TXTUNF) ;* End of all text CALL MVUP ;* Move up to delete LD H,B ;* Save new text end address LD L,C LD (TXTUNF),HL XXST3: POP BC ;* Adrs of where to insert LD HL,(TXTUNF) ;* Text end address POP AF ;* Length of new line PUSH HL ;* Save text last adrs. CP $03 ;* Length = 3, delete only JR Z,RSTART ADD A,L ;* Compute new text end LD E,A ;* to DE LD A,$00 ADC A,H LD D,A LD HL,DFTLMT ;* Last possible address EX DE,HL CALL COMP ; do COMPare HL and DE JP NC,QSORRY ;* SORRY if no room LD (TXTUNF),HL ;* Save new text end POP DE ;* Old text end CALL MVDOWN ; do MoVe a block DOWN POP DE ;* New line start POP HL ;* end CALL MVUP ;* Move line to SAVE JR XXST2 ;* Get next line ; DIRECT AND EXEC ;* Direct Execute ; EXEC0: RST RSTIGN ; GET FIRST NONBLANK ;* Get next non-blank from (DE) PUSH DE ; SAVE POINTER SUB $68 ; IS SHE A TOKEN? [bb did CP $68 ??] ;* Is this a word? JR C,EXEC0A ; NO CP $0D ;* Yes, sure? [bb did CP ($68 + $0D) for $75 ??] JR NC,EXEC0A ;* No, assume implied LET ; WE FOUND A TOKEN - LOOKUP IN TABLE AND JUMP TO IT RLCA LD E,A LD D,$00 LD HL,TOKJT ;* Jump table [ab did (token - $68) already] ADD HL,DE LD E,(HL) INC HL LD D,(HL) EX DE,HL POP DE INC DE JP (HL) ;* Go to proper routine ; NOT A TOKEN - A VARIABLE PERHAPS? EXEC0A: CALL TSTV ; TEST FOR VARIABLE ;* Get VRBL storage address JR C,EXEC0B ; NO - SEARCH 1 TSTC '=',EXEC0B POP BC ; THROW OUT OLD PTR CALL SETV1 ; ASSIGNMENT 1 BBRET: RST RSTFIN ;* cr. or ; otherwise, WHAT? EXEC0B: POP DE LD HL,TAB2-1 EXEC: RST RSTIGN ; EXEC ;* Get next non-blank from (DE) PUSH DE ; SAVE POINTER EX1: LD A,(DE) ; ZAPPED LDE [instead of using RST RSTLDE ??] INC DE INC HL CP (HL) JR Z,EX1 LD A,$7F DEC DE CP (HL) JR C,EX5 EX2: INC HL CP (HL) JR NC,EX2 INC HL POP DE JR EXEC EX5: LD A,(HL) ; LOAD HL WITH THE JUMP INC HL ; ADDRESS FROM TABLE LD L,(HL) AND $7F LD H,A POP AF JP (HL) ; IF AND REM ; ;* . Comment line IFF: RST RSTEXP ;* Evaluate expression LD A,H OR L JR NZ,RUNSML ;* Jp if true REM: RUNNXL: LD DE,(LINEND) ; Point to LINe END JR RUNX1 ;* And run next one ; RUN routine(s) ; RUN: LD DE,TXT ; RUn NeXt One RUNX1: LD HL,$0000 ;* RST30 Jumps here on cr CALL FNDLP ;* Find next line [starting at Pointer] JP C,RSTART ;* Passed end; quit ; RUN The goSub Line RUNTSL: LD (CURRNT),DE ;* Set CURRNT to line address IN A,($14) LD L,A IN A,($15) ; Pressing PAUSE or LIST during execution AND L ; will pause or trace! CP $20 CALL Z,PRTLNS ; do PRinT Line Number, space, then String OK: CALL EXPAND ; Load program line into eXeCuTe BUFfer [ab only] LD DE,XQTBUF ; Back to start of line [no line number ??] ; RUN SaMe Line ;* RST30 Jps to RUNSML on ; RUNSML: CALL WHATSU ; CHECK FOR INTERRUPT KEY JR EXEC0 ;* Continue Same line execution ; GOTO routine ; GOTO: RST RSTEXP ;* Evaluate expression PUSH DE ;* Save for error routine CALL FNDLN ;* Find target line JP NZ,AHOW ;* No such line POP AF ;* Clear stack JR RUNTSL ; LIST AND PRINT ; NEW - IMPROVED LIST COMMAND ; LETS YOU PUT IT IN A PROGRAM ; ;* LIST [][,] LISTCOM: LD HL,$0000 ; ASSUME AT EOL ;* Preset to list from beginning CALL IGNATNL ; do IGNore blanks, set zero AT NewLine or ; JR Z,LS3 ;* Jp if ; or cr CP ',' ; LEADING COMMA? ;* Jp if only # lines specified JR Z,LS3 ; YEP - SKIP FIRST EXPR GET ; NOT AT FIRST - GET FIRST EXPR LS2: RST RSTEXP ;* Evaluate expression LS3: PUSH HL ;* Save line # for start LD HL,$FFFF ;* Set for max number of lines TSTCC COMMA,LS4 ;* Comma = Char. to check, Jump if no match RST RSTEXP ;* Get number of lines ;* Evaluate expression LS4: PUSH DE POP IY EX (SP),HL CALL FNDLN ; do FiND HL Line Number in TeXT LS5: JR C,LSQUIT EX (SP),HL LD A,H OR L JR Z,LSQUIT DEC HL EX (SP),HL CALL PRTLNS ; do PRinT Line Number, space, then String CALL WHATSU ; do Check for PAUSE or ABORT keys CALL FNDLP ; do FiND Linenumber starting at Pointer JR LS5 LSQUIT: PUSH IY POP DE RST RSTFIN ;* cr. or ; otherwise, WHAT? ; PRINT routine ; PRINT: LD C,$08 ; C=# OF SPACES /* Default number of spaces TSTCC $3B,PR1 ; IF NULL LIST & ";" ;* Jump if no match CALL CRLF ; GIVE CR-LF AND JR RUNSML ; CONTINUE SAME LINE PR1: TSTCC CR,PR6 ; IF NULL LIST (CR) ;* Jump if no match CALL CRLF ; ALSO GIVE CR-LF AND JP IMCHEK ; GO TO NEXT LINE IF POSSIBLE PR2: TSTC '#',PR4 ; ELSE IS IT FORMAT? ;* Jump if no match PR3: RST RSTEXP ; YES, EVALUATE EXPR. LD C,L ; AND SAVE IT IN C [ab not limited to 6 bits ??] JR PR5 ; LOOK FOR MORE TO PRINT PR4: CALL QTSTG ; OR IS IT A STRING? JR PR9 ; IF NOT, MUST BE EXPR. PR5: TSTCC COMMA,PR8 ; IF COMMA, GO FIND NEXT ;* Jump if no match PR6: TSTCC COMMA,PR7 ;* Comma = Char. to check, Jump if no match CALL SPOUTCH ; do SPace OUTput CHar. JR PR6 PR7: CALL FIN ; IN THE LIST. JR PR2 ; LIST CONTINUES PR8: CALL CRLF ; LIST ENDS RST RSTFIN ;* cr. or ; otherwise, WHAT? PR9: RST RSTEXP ; EVALUATE THE EXPR PUSH BC CALL PRTNUM ; PRINT THE VALUE POP BC JR PR5 ; Next value [not another PRINT THE VALUE] ; *************************************************************** ; * ; * *** GOSUB *** & *** RETURN *** ; * ; * 'GOSUB EXPR;' OR 'GOSUB EXPR(CR)' IS LIKE THE 'GOTO' COMMAND, ; * EXCEPT THAT THE CURRENT TEXT POINTER, STACK POINTER ETC. ARE SAVED ; * SO THAT EXECUTION CAN BE CONTINUED AFTER THE SUBROUTINE 'RETURN'. ; * IN ORDER THAT 'GOSUB' CAN BE NESTED (AND EVEN RECURSIVE), THE SAVE ; * AREA MUST BE STACKED. THE STACK POINTER IS SAVED IN 'STKGOS'. THE ; * OLD 'STKGOS' IS SAVED IN THE STACK. IF WE ARE IN THE MAIN ROUTINE, ; * 'STKGOS' IS ZERO (THIS WAS DONE BY THE "MAIN" SECTION OF CODE). ; * BUT WE STILL SAVE IT AS A FLAG FOR NO FURTHER 'RETURN'S. ; * ; * 'RETURN(CR)' UNDOES EVERYTHING THAT 'GOSUB' DID, AND THUS RETURN THE ; * EXECUTION TO THE COMMAND AFTER THE MOST RECENT 'GOSUB'. IF 'STKDOS' ; * IS ZERO, IT INDICATES THAT WE NEVER HAD A 'GOSUB' AND IS THUS AN ; * ERROR ; GOSUB: LD A,(CURRNT+1) ; DISALLOW FROM COMMAND RLCA JP NC,QHOW CALL PUSHA ; SAVE THE CURRENT "FOR" ;* Save current "FOR" params RST RSTEXP ; PARAMETERS ;* Evaluate expression PUSH DE ; AND TEXT POINTER ;* Save text pointer CALL FNDLN ; FIND THE TARGET LINE ;* Find target line JP NZ,AHOW ; NOT THERE. SAY "HOW?" ;* Not present; 'HOW?' LD HL,(CURRNT) ; SAVE OLD ;* Save everything PUSH HL ; 'CURRENT' OLD 'STKGOS' LD HL,(STKGOS) PUSH HL LD HL,$0000 ; AND LOAD NEW ONES LD (LOPVAR),HL ;* And load it up ADD HL,SP LD (STKGOS),HL JP RUNTSL ; THEN RUN THAT LINE RETURN: LD HL,(STKGOS) ; OLD STACK POINTER ;* Old stack pointer LD A,H ; 0 MEANS NOT EXIST OR L JP Z,QWHAT ; SO, WE SAY: "WHAT?" ;* RETURN with no GOSUB LD SP,HL ; ELSE, RESTORE IT ;* Restore old SP RESTO: POP HL LD (STKGOS),HL ; AND THE OLD 'STKGOS' ;* And old STKGOS POP HL LD (CURRNT),HL ; AND THE OLD 'CURRNT' ;* And old CURRNT POP DE ;* Get old text pointer CALL POPA ;* Restore old "FOR" params CALL EXPAND ; Load program line into eXeCuTe BUFfer [ab only] RST RSTFIN ;* cr. or ; otherwise, WHAT? ; ******************************************* ; * ; * *** FOR *** & NEXT *** ; * ; * 'FOR' HAS TWO FORMS: 'FOR VAR=EXP1 TO EXP2 STEP EXP3' AND ; * 'FOR VAR=EXP1 TO EXP2'. THE SECOND FORM MEANS THE SAME THING ; * AS THE FIRST FORM WITH EXP3=1, (I.E. WITH A STEP OF +1) TBI ; * WILL FIND THE VARIABLE VAR. AND SET ITS VALUE TO THE CURRENT ; * VALUE OF EXP1. IT ALSO EVALUATES EXP2 AND EXP3 AND SAVES ALL ; * THESE TOGETHER WITH THE TEXT POINTER ETC. IN THE 'FOR' SAVE AREA ; * WHICH CONSISTS OF 'LOPVAR', 'LOPINC', 'LOPLMT', 'LOPLN', AND 'LOPPT'. ; * IF THERE IS ALREADY SOMETHING IN THE SAVE AREA (INDICATED BY A ; * NON-ZERO 'LOPVAR'), THEN THE OLD SAVE AREA IS SAVED IN THE STACK ; * BEFORE THE NEW ONE OVERWRITES IT. TBI WILL THEN DIG IN THE STACK ; * AND FIND OUT IF THIS SAME VARIABLE WAS USED IN ANOTHER CURRENTLY ; * ACTIVE 'FOR' LOOP. IF THAT IS THE CASE, THEN THE OLD 'FOR' LOOP IS ; * DEACTIVATED. (PURGED FROM THE STACK..) ; * ; * 'NEXT VAR' SERVES AS THE LOGICAL (NOT NECESSARILY PHYSICAL) END OF ; * THE 'FOR' LOOP. THE CONTROL VARIABLE VAR. IS CHECKED WITH THE ; * 'LOPVAR'. IF THEY ARE NOT THE SAME, TBI DIGS IN THE STACK TO FIND ; * THE RIGHT ONE AND PURGES ALL THOSE THAT DID NOT MATCH. EITHER WAY, ; * TBI THEN ADDS THE 'STEP' TO THAT VARIABLE AND CHECKS THE RESULT WITH ; * THE LIMIT. IF IT IS WITHIN THE LIMIT, CONTROL LOOPS BACK TO THE ; * COMMAND FOLLOWING THE 'FOR'. IF OUTSIDE THE LIMIT, THE SAVE AREA IS ; * PURGED AND EXECUTION CONTINUES. ; FOR: CALL PUSHA ; SAVE THE OLD SAVE AREA ;* Save old "FOR" params CALL SETVAL ; SET THE CONTROL VAR. ;* Set control VRBL DEC HL ; HL IS ITS ADDRESS LD (LOPVAR),HL ; SAVE THAT TSTCC $77,FR1A ; TO? - LOOK FOR WORD "TO" ;* "TO" (don't need one) FR1: RST RSTEXP ; EVALUATE THE LIMIT FR1A: LD (LOPLMT),HL ; SAVE THAT ;* Save limit LD HL,$0001 ;* Preset step of 1 TSTCC $75,FR4 ; STEP? ;* "STEP" (don't need one), Jump if no match RST RSTEXP ;* Evaluate expression FR4: LD (LOPINC),HL ; SAVE THAT TOO ;* Save increment LD HL,(CURRNT) ; SAVE CURRENT LINE # LD (LOPLN),HL ; AND TEXT POINTER ;* Save current line number EX DE,HL LD (LOPPT),HL ;* And current text pointer LD BC,$000A ; DIG INTO STACK TO ;* Dig into stack to find LD HL,(LOPVAR) ; FIND 'LOPVAR' ;* LOPVAR from last EX DE,HL LD H,B LD L,B ; HL=0 NOW ;* HL = 0 ADD HL,SP ; HERE IS THE STACK JR FR6 FR5: ADD HL,BC ; EACH LEVEL IS 10 DEEP ;* Each level is 10 deeper FR6: LD A,(HL) ; GET THAT OLD 'LOPVAR' ;* Get old LOPVAR INC HL OR (HL) JR Z,FR7 ; 0 SAYS NO MORE IN IT ;* Jp if no more LD A,(HL) DEC HL CP D ; SAME AS THIS ONE? ;* Same as this one? JR NZ,FR5 LD A,(HL) ; THE OTHER HALF? ;* Other half also? XOR E JR NZ,FR5 EX DE,HL ; YES, FOUND ONE ;* Yes, found one LD H,A LD L,A ADD HL,SP ; TRY TO MOVE SP ;* Try to move SP LD B,H LD C,L LD HL,$000A ADD HL,DE CALL MVDOWN ; AND PURGE 10 WORDS ;* Purge 10 words LD SP,HL ; IN THE STACK ;* In the stack FR7: LD HL,(LOPPT) ; JOB DONE, RESTORE DE ;* Job done EX DE,HL RST RSTFIN ; AND CONTINUE ;* cr. or ; otherwise, WHAT? NEXT: CALL TSTV ; GET ADDRESS OF VAR. ;* Get VRBL storage address JP C,QWHAT ; NO VARIABLE, "WHAT?" ;* None, 'WHAT?' LD (VARNXT),HL ; YES, SAVE IT ;* Save its address NX1: PUSH DE ; SAVE TEXT POINTER ;* Save text pointer EX DE,HL LD HL,(LOPVAR) ; GET VAR. IN 'FOR' ;* Get VRBL in FOR LD A,H OR L ; 0 SAYS NEVER HAD ONE JP Z,AWHAT ; SO WE ASK: "WHAT?" ;* Never had one CALL COMP ; ELSE WE CHECK THEM ;* Check them for match JR Z,NX2 ; OK, THEY AGREE ;* Jp if agree POP DE ; NO, LET'S SEE ;* No agree, Purge current CALL POPA ; PURGE CURRENT LOOP ;* loop and pop one level LD HL,(VARNXT) ; AND POP ONE LEVEL JR NX1 ; GO CHECK AGAIN ;* Try again NX2: EX DE,HL ; COME HERE WHEN AGREED ;* ;Get value of VRBL to DE RST RSTLDE ; DE=VALUE OF VAR. [load from TeXT if necessary] LD L,A INC DE RST RSTLDE ; does LD A,(DE) [from TeXT if necessary] LD H,A EX DE,HL LD HL,(LOPINC) ;* Get increment PUSH HL LD A,H XOR D ; S=SIGN OF DIFFER LD A,D ; A=SIGN OF DE ADD HL,DE ; ADD ONE STEP ;* Add one step JP M,NX3 ; CANNOT OVERFLOW XOR H ; MAY OVERFLOW JP M,NX5 ; AND IT DID NX3: EX DE,HL LD HL,(LOPVAR) ; PUT IT BACK CALL STDEHL ; do STore DE word by HL [into TeXT if necessary] LD HL,(LOPLMT) ; HL=LIMIT POP AF ; OLD HL RLCA ; EXAMINE SIGN BIT JR NC,NX4 ; IF POS SKIP EX DE,HL ;* Step > 0 EX DE,HL ;* Step < 0 NX4: CALL CKHLDE ; COMPARE WITH LIMIT ;* Compare with limit POP DE ; RESTORE TEST POINTER ;* Restore text pointer JR C,NX6 ; OUTSIDE LIMIT ;* Outside limit LD HL,(LOPLN) ; WITHIN LIMIT, GO ;* Within limit LD (CURRNT),HL ; BACK TO THE SAVED ;* Put LOPLN in CURRNT LD HL,(LOPPT) ; 'CURRNT' AND TEXT ;* and LOPPT EX DE,HL ; POINTER CALL EXPAND ; Load program line into eXeCuTe BUFfer [ab only] RST RSTFIN ;* cr. or ; otherwise, WHAT? NX5: POP HL ; OVERFLOW , PURGE ; RESTO LINKS IN HERE NXXX: POP DE ; GARBAGE IN STACK NX6: CALL POPA ; PURGE THIS LOOP RST RSTFIN ;* cr. or ; otherwise, WHAT? ; ********************************************************* ; * ; * *** IF *** INPUT *** & LET (& DEFLT) **** ; * ; * 'IF' IS FOLLOWED BY AN EXPR. AS A CONDITION AND ONE OR MORE COMMANDS ; * (INCLUDING OTHER 'IF'S) SEPARATED BY SEMI-COLONS. NOTE THAT THE ; * WORD 'THEN' IS NOT USED. TBI EVALUATES THE EXPR. IF IT IS NON-ZERO, ; * EXECUTION CONTINUES. IF THE EXPR. IS ZERO, THE COMMANDS THAT ; * FOLLOW ARE IGNORED AND EXECUTION CONTINUES AT THE NEXT LINE. ; * ; * 'INPUT' COMMAND IS LIKE THE 'PRINT' COMMAND, AND IS FOLLOWED BY A ; * LIST OF ITEMS. IF THE ITEM IS A STRING IN A SINGLE OR DOUBLE QUOTES, ; * OR IS AN UP-ARROW, IT HAS THE SAME EFFECT AS IN 'PRINT'. IF AN ITEM ; * IS A VARIABLE, THIS VARIABLE NAME IS PRINTED OUT FOLLOWED BY A ; * COLON. THEN TBI WAITS FOR AN EXPR. TO BE TYPED IN. THE VARIABLE IS ; * THEN SET TO THE VALUE OF THIS EXPR. IF THE VARIABLE IS PROCEDED BY ; * A STRING (AGAIN IN SINGLE OR DOUBLE QUOTES), THE STRING WILL BE ; * PRINTED FOLLOWED BY A COLON. TBI THEN WAITS FOR INPUT EXPR. AND ; * SETS THE VARIABLE TO THE VALUE OF THE EXPR. ; * ; * IF THE INPUT EXPR. IS INVALID, TBI WILL PRINT "WHAT?", "HOW?", OR ; * "SORRY" AND REPRINT THE PROMPT AND REDO THE INPUT. THE EXECUTION ; * WILL NOT TERMINATE UNLESS YOU TYPE CONTROL-C. THIS IS HANDLED IN ; * 'INPERR'. ; INPERR: LD HL,(STKINP) ; *** INPERR *** LD SP,HL ; RESTORE OLD SP POP HL ; AND OLD 'CURRNT' LD (CURRNT),HL POP DE ; AND OLD TEXT POINTER POP DE ; REDO INPUT CALL EXPMAN ; EXPAND THAT LINE OUT INPUT EQU $ IP1: PUSH DE ; SAVE IN CASE OF ERROR CALL QTSTG ; IS NEXT ITEM A STRING? JR IP8 ; NO IP2: CALL TSTV ; YES, BUT FOLLOWED BY A ;* Get VRBL storage address JR C,IP5 ; M6IVARIABLE? NO. IP3: CALL IP12 LD DE,BUFFER ; POINTS TO BUFFER RST RSTEXP ; EVALUATE INPUT POP DE ; OK, GET OLD HL EX DE,HL CALL STDEHL ; do STore DE word by HL [into TeXT if necessary] IP4: POP HL ; GET OLD 'CURRNT' LD (CURRNT),HL POP DE ; AND OLD TEXT POINTER CALL EXPMAN IP5: POP AF ; PURGE JUNK IN STACK IP6: TSTCC COMMA,IP7 ; IS NEXT CH. ','? JR INPUT ; YES, MORE ITEMS. IP7: RST RSTFIN ;* cr. or ; otherwise, WHAT? IP8: PUSH DE ; SAVE FOR 'PRTSTG' CALL TSTV ; MUST BE VARIABLE NOT ;* Get VRBL storage address JP C,QWHAT ; "WHAT?" IT IS NOT? IP11: LD B,E POP DE CALL PRTCHS ; PRINT THOSE AS PROMPT JR IP3 ; YES, INPUT VARIABLE IP12: POP BC ; RETURN ADDRESS PUSH DE ; SAVE TEXT POINTER ;* Save in case of error EX DE,HL LD HL,(CURRNT) ; ALSO SAVE 'CURRNT' PUSH HL LD HL,IP1 ; A NEGATIVE NUMBER LD (CURRNT),HL ; AS A FLAG LD HL,$0000 ; SAVE SP TOO ADD HL,SP LD (STKINP),HL PUSH DE ; OLD HL PUSH BC LD A,' ' ;* Space after VRBL JP GETLN ; AND GET A LINE ; ********************************************* ; * ; * *** EXPR *** ; * ; * 'EXPR' EVALUATES ARITHMETICAL OR LOGICAL EXPRESSIONS. ; * ::= ; * ; * WHERE IS ONE OF THE OPERATORS IN TAB6 AND THE RESULT ; * OF THESE OPERATIONS IS 1 IF TRUE AND 0 IF FALSE. ; * ; * ::=(+ OR -)(+ OR -)(...) ; * WHERE () ARE OPTIONAL AND (...) ARE OPTIONAL REPEATS. ; * ; * ::=<(<* OR />)(...) ; * ; * ::= ; * ; * () ; * ; * IS RECURSIVE SO THAT VARIABLE '@' CAN HAVE AN ; * AS INDEX, FUNCTIONS CAN HAVE AN AS ARGUMENTS, AND ; * CAN BE AN IN PARENTHESES. ; EXPR: CALL EXPR1 ; *** EXPR *** PUSH HL ; SAVE VALUE LD HL,TAB6 - 1 ; LOOKUP REL.OP. JP EXEC ; GO DO IT. XPR1: CALL XPR8 ; REL.OP. ">=" RET C ; NO, RETURN HL=0 LD L,A ; YES, RETURN HL=1 RET XPR2: CALL XPR8 ; REL.OP. "#" RET Z ; FALSE, RETURN HL=0 LD L,A ; TRUE, RETURN HL=1 RET XPR3: CALL XPR8 ; REL.OP. ">" RET Z ; FALSE RET C LD L,A ; TRUE, RETURN HL=1 RET XPR4: CALL XPR8 ; REL.OP "<=" LD L,A ; SET HL=1 RET Z ; REL. TRUE, RETURN RET C ; ELSE SET HL=0 LD L,H RET XPR5: CALL XPR8 ; REL.OP. "=" RET NZ ; FALSE, RETURN HL=0 LD L,A ; ELSE SET HL=1 RET XPR6: CALL XPR8 ; REL.OP. "<" RET NC ; FALSE, RETURN HL=0 LD L,A ; ELSE SET HL=1 RET XPR7: POP HL ; NOT REL.OP. ;* - end of list 3 RET ; RETURN HL= XPR8: LD A,C ; SUBROUTINE FOR ALL POP HL ; REL.OP.'S POP BC PUSH HL ; REVERSE TOP OF STACK PUSH BC LD C,A CALL EXPR1 ; SET 2ND EX DE,HL ; VALUE IN DE NOW EX (SP),HL ; 1ST IN HL CALL CKHLDE ; COMPARE 1ST WITH 2ND POP DE ; RESTORE TEXT POINTER LD HL,$0000 ; SET HL=0, A=1 LD A,$01 RET EXPR1: TSTC '-',XP11 ; NEGATIVE SIGN? LD HL,$0000 ; YES, FAKE "0-" JR XP16 ; TREAT LIKE SUBTRACT ;* Fake "0-x" XP11: TSTC '+',XP12 ; POSITIVE SIGN? IGNORE ;* Ignore leading + XP12: CALL EXPR2 ; 1ST XP13: TSTC '+',XP15 ; ADD? PUSH HL ; YES, SAVE VALUE CALL EXPR2 ; GET 2ND XP14: EX DE,HL ; 2ND IN DE EX (SP),HL ; 1ST IN HL LD A,H ; COMPARE SIGN XOR D LD A,D ADD HL,DE POP DE ; RESTORE TEST POINTER JP M,XP13 ; 1ST 2ND SIGN DIFFER ;* Jp if signs different XOR H ; 1ST 2ND SIGN EQUAL ;* Signs alike JP P,XP13 ; SO IS RESULT JP QHOW ; ELSE WE HAVE OVERFLOWN XP15: TSTC '-',XPR9 ; SUBTRACT? XP16: PUSH HL ; YES, SAVE 1ST CALL EXPR2 ; GET 2ND CALL CHGSGN ; NEGATE JR XP14 ; AND ADD THEM EXPR2: CALL EXPR3 ; GET 1ST XP21: TSTCC $62,XP24 ; MULTIPLY? PUSH HL ; YES, SAVE 1ST CALL EXPR3 ; AND GET 2ND LD B,$00 ; CLEAR B FOR SIGN CALL CHKSGN ; CHECK SIGN EX (SP),HL ; 1ST IN HL CALL CHKSGN ; CHECK SIGN OF 1ST EX DE,HL ;* 2nd in DE EX (SP),HL ;* Get back 1st LD A,H ; IS HL > 255? OR A JR Z,XP22 ; NO ;* Jp if HL <255 LD A,D ; YES, HOW ABOUT DE OR D EX DE,HL ; PUT SMALLER IN DE JP NZ,AHOW ; ALSO >, WILL OVERFLOW ;* Jp if DE >255 (will overflow) XP22: LD A,L ; THIS IS DUMB LD HL,$0000 ; CLEAR RESULT OR A ; ADD AND COUNT NERDXX: JR Z,XP25 ;* Done XP23: ADD HL,DE ;* Continue multiply JP C,AHOW ; OVERFLOW DEC A JR NERDXX ; FINISHED XP24: TSTCC $63,XPR9 ; DIVIDE? ;* Jump if no match PUSH HL ; YES, SAVE 1ST CALL EXPR3 ; AND GET 2ND ONE LD B,$00 ; CLEAR B FOR SIGN CALL CHKSGN ; CHECK SIGN OF 2ND EX (SP),HL ; GET 1ST IN HL CALL CHKSGN ; CHECK SIGN OF 1ST EX DE,HL EX (SP),HL EX DE,HL LD A,D ; DIVIDE BY 0? OR E JP Z,AHOW ; SAY "HOW?" PUSH BC ; ELSE, SAVE SIGN CALL DIVIDE ; USE SUBROUTINE POP DE ; SIGN STUFF TO DE PUSH BC ; SAVE DIVIDE RESULT BIT 7,D ; WAS SIGN SET? CALL NZ,CHGSGN ; YEP - CHANGE LD (REMAIN),HL ; STUFF IT POP HL ; RESULT IN HL LD B,D ; COPY OVER SIGN STUFF LD C,E XP25: POP DE ; GET TEXT POINTER BACK LD A,H ; HL MUST BE + OR A JP M,QHOW ; ELSE IT IS OVERFLOW LD A,B OR A CALL M,CHGSGN ; CHANGE SIGN IF NEEDED JR XP21 ; LOOK FOR MORE TERMS EXPR3: LD HL,TAB3 - 1 ; FIND FUNCTION IN TAB3 JP EXEC ; AND GO DO IT NOTF: CALL TSTV ; NO, NOT A FUNCTION ;* End of list 2 JR C,XP32 ; NOR A VARIABLE ;* Is it a variable? No EX DE,HL ;* Yes, a variable RST RSTLDE ; does LD A,(DE) [from TeXT if necessary] PUSH AF INC DE RST RSTLDE ; does LD A,(DE) [from TeXT if necessary] EX DE,HL LD H,A ;* Get VRBL value to HL POP AF LD L,A RET XP32: CALL TSTNUM ; OR IS IT A NUMBER? LD A,B ; # OF DIGIT OR A RET NZ ; OK ; SINGLE CHAR STRING CONSTANT? TSTC '"',PARN ; HAVE WE GOT QUOTES? ;* Get one byte ASCII input LD A,(DE) ; NAILED RSTLDE [instead of using RST RSTLDE ??] LD L,A ; FAILED TSTNUM SET H TO ZERO INC DE TSTC '"',XPRO ; ERROR IF NO TRAILING RET ; ***** ; * ; PARN: TSTC '(',XPRO ; NO DIGIT, MUST BE PARNP: RST RSTEXP ; "(EXPR)" ;* Evaluate expression TSTC ')',XPRO XPR9: RET XPRO: JP QWHAT ; ELSE SAY: "WHAT?" ; RND( ) command ; ; RND(0) has been defined to mean: return a random ; number between -32768 and 32767. This saved ROM space. ; RND: RST RSTPAR ; *** RND(EXOR) *** ;* Get value of () or storage adrs LD A,H ; EXPR MUST BE + OR A JP M,QHOW ;* Bad if - [and not if 0 anymore] PUSH DE ; SAVE BOTH EX DE,HL ; DE = RANGE XOR A SYSTEM RANGED ;* Get Random Number LD L,A XOR A SYSTEM RANGED ;* Get Random Number LD H,A ; HL = RANDOM # PUSH BC LD A,D OR E CALL NZ,DIVIDE ; RND(N)=MOD(M,N)+1 POP BC POP DE INC HL RET ; ABS( ) function ; ; The absolute value function is available. It is ; typed in as 3 discrete keystrokes: "A", "B", and "S". ; ABS: RST RSTPAR ; *** ABS(EXPR) *** ;* Get value of () or storage adrs DEC DE CALL CHKSGN ; CHECK SIGN INC DE RET SIZE: LD HL,(TXTUNF) ; *** SIZE *** PUSH DE ; GET THE NUMBER OF EX DE,HL ; FREE BYTES BETWEEN 'TXTUNF' LD HL,DFTLMT ; AND 'TXTLMT' AND A SBC HL,DE POP DE RET ; FUNCTION TO SENSE DIAL VALUE ; The value ranges from 0 (full clockwise) ; to 255 (full counterclockwise). ; GETPOT: LD A,$1B CALL CHKRNG ; GET DATA CPL SUB $80 LD L,A ; FALL INTO... ; SIGN EXTEND SUBROUTINE SGNEXT: LD H,$00 LD A,L AND A RET P DEC H RET ; FUNCTION TO SENSE STATE OF TRIGGER ; GETTRG: CALL CHKRN1 ; do CHecK RaNge is 1-4 AND $10 RET Z INC L RET ; FUNCTIONS TO RETURN JOYSTICK VALUE ; THESE FUNCTIONS RETURN EITHER +1, 0, OR -1, DEPENDING ; ON JOYSTICK STATE ; GETJX: CALL CHKRN1 ; PARM IN RANGE? RRCA RRCA RRCA JR C,GETJY3 RRCA JR C,GETJY1 RET ; ENTRY FOR Y JOYSTICK VALUE GETJY: CALL CHKRN1 ; do CHecK RaNge is 1-4 RRCA JR NC,GETJY2 GETJY1: INC HL RET GETJY2: RRCA RET NC GETJY3: DEC HL RET ; SUBROUTINE TO GET PARAMETER BETWEEN 1 AND 4 ; CHKRN1: LD A,$0F CHKRNG: PUSH BC PUSH AF RST RSTPAR ;* Get value of () or storage adrs POP AF ADD A,L LD C,A IN A,(C) POP BC LD HL,$0000 RET ; ********************************************** ; * ; * *** DIVIDE *** SUBDE *** CHKSGN *** CHGSGN *** & CKHLDE *** ; * ; * 'DIVIDE' DIVIDES HL BY DE, RESULT IN BC, REMAINDER IN HL ; * ; * 'SUBDE' SUBTRACTS DE FROM HL ; * ; * 'CHKSGN' CHECKS SIGN OF HL. IF +,NO CHANGE. IF -,CHANGE SIGN AND ; * FLIP SIGN OF B. ; * ; * 'CHKSGN' CHANGES SIGN OF HL AND B UNCONDITIONALLY. ; * ; * 'CKHLDE' CHECKS SIGN OF HL AND DE. IF DIFFERENT, HL AND DE ARE ; * INTERCHANGED. IF SAME SIGN, NOT INTERCHANGED. EITHER CASE, HL DE ; * ARE THEN COMPARED TO SET THE FLAGS. [moved up with COMP routine] ; DIVIDE: PUSH HL ; *** DIVIDE *** LD L,H ; DIVIDE H BY DE LD H,$00 CALL DV1 LD B,C ; SAVE RESULT IN B LD A,L ; (REMAINDER + L)/DE POP HL LD H,A DV1: LD C,$FF ; RESULT IN C [initialize to -1] DV2: INC C ; DUMB ROUTINE AND A SBC HL,DE JR NC,DV2 ADD HL,DE RET CHKSGN: LD A,H ; *** CHKSGN *** ;* Check sign of HL OR A ; CHECK SIGN OF HL RET P ; IF -, CHANGE SIGN ;* Leave if + CHGSGN: LD A,H ; *** CHGSGN *** ;* Change sign of HL OR L RET Z ;* +0 stays the same LD A,H ;* Change sign PUSH AF CPL ; CHANGE SIGN OF HL LD H,A LD A,L CPL LD L,A INC HL POP AF LD A,B ; AND ALSO FLIP B ;* Also flip sign of B XOR $80 LD B,A RET ; *************************************************** ; * ; * *** SETVAL *** FIN *** ENDCHK *** & ERROR (& FRIENDS)*** ; * ; * 'SETVAL' EXPECTS A VARIABLE, FOLLOWED BY AN EQUAL SIGN AND THEN AN ; * EXPR. IT EVALUATES THE EXPR. AND SETS THE VARIABLE TO THAT VALUE. ; * ; * 'FIN' CHECKS THE END OF A COMMAND. IF IT ENDED WITH ";", EXECUTION ; * CONTINUES. IF IT ENDED WITH A CR, IT FINDS THE NEXT LINE AND ; * CONTINUES FROM THERE. ; * ; * 'ENDCHK' CHECKS IF A COMMAND IS ENDED WITH CR. THIS IS REQUIRED IN ; * CERTAIN COMMANDS. (GOTO, RETURN, AND STOP ETC.) [ab just uses 'FINISH' ??] ; * ; * 'ERROR' PRINTS THE STRING POINTED BY DE (AND ENDS WITH CR). IT THEN ; * PRINTS THE LINE POINTED BY 'CURRNT' WITH A "?" INSERTED AT WHERE THE ; * OLD TEXT POINTER (SHOULD BE ON TOP OF THE STACK) POINTS TO. ; * EXECUTION OF TB IS STOPPED AND TBI IS RESTARTED. HOWEVER, IF ; * 'CURRNT'=> ZERO (INDICATING A DIRECT COMMAND), THE DIRECT COMMAND ; * IS NOT PRINTED. AND IF 'CURRNT'=>NEGATIVE # (INDICATING 'INPUT' ; * COMMAND, THE INPUT LINE IS NOT PRINTED AND EXECUTION IS NOT ; * TERMINATED BUT CONTINUED AT 'INPERR'. ; * ; * RELATED TO 'ERROR' ARE THE FOLLOWING: 'QWHAT' SAVES TEXT POINTED IN ; * STACK AND GETS MESSAGE "WHAT?". 'AWHAT' JUST GETS MESSAGE "WHAT?" ; * AND JUMPS TO 'ERROR'. 'QSORRY' AND 'ASORRY' DO SAME KIND OF THING. ; * 'QHOW' AND 'AHOW' IN THE ZERO PAGE SECTION ALSO DO THIS. ; SETVAL: CALL TSTVFF ; *** SETVAL *** ;* Get adrs. of VRBL TSTC '=',QWHAT ; "WHAT?" NO VARIABLE ;* Jump if no match SETV1: PUSH HL ; SAVE ADDRESS OF VAR. RST RSTEXP ; EVALUATE EXPR. EX DE,HL ;* Value now in [?? not use BC in ab ??] EX (SP),HL ;* Get address CALL STDEHL ;* Place [double] into VRBL [into TeXT if necessary] POP DE RET FINISH: CALL FIN ; CHECK END OF COMMAND ;* RST30 after POP AF JR QWHAT ; PRINT "WHAT?" IF WRONG FIN: TSTCC $3B,FI1 ; *** FIN *** ;* Semicolon, Jump if no match POP AF ; ";", PURGE RET ADDR. JP RUNSML ; CONTINUE SAME LINE FI1: TSTCC CR,FI2 ; NOT ";", IS IT CR? ;* $0D, Jump if no match POP AF ; PURGE RETURN ADDRESS IMCHEK: LD A,(CURRNT + 1) RLCA JP NC,RSTART JP RUNNXL ; RUN NEXT LINE QWHAT: PUSH DE ; *** QWHAT *** AWHAT: LD DE,WHAT ; *** AWHAT *** ERROR: CALL CRLF ; *** ERROR *** CALL PRTSTG ; PRINT ERROR MESSAGE LD HL,(CURRNT) ; GET CURRENT LINE # ;* Current line pointer PUSH HL ;* Save it EX DE,HL ; CHECK THE VALUE RST RSTLDE ;* Get character in text LD H,A INC DE RST RSTLDE ; does LD A,(DE) [from TeXT if necessary] OR H EX DE,HL POP DE ;* Set DE to line # JP Z,TELL ; IF ZERO, JUST RESTART ;* If 0, just restart EX DE,HL ; IF NEGATIVE ;* Get first digit RST RSTLDE ; does LD A,(DE) [from TeXT if necessary] EX DE,HL OR A JP M,INPERR ; REDO INPUT ;* If negative, redo input CALL PRTLN ; ELSE PRINT THE LINE Number then Space POP HL ; HL=ERROR ADDR LD BC,XQTBUF AND A SBC HL,BC ADD HL,DE LD B,L CALL PRTCHS CALL OUTCHQ ;* do Output $3F Question Mark CALL PRTSTG ; LINE JP TELL ; THEN RESTART QSORRY: PUSH DE ; *** QSORRY *** ASORRY: LD DE,SORRY ; *** ASORRY *** JR ERROR ; ****************************************** ; * ; * *** FNDLN (& FRIENDS) *** ; * ; * 'FNDLN' FINDS A LINE WITH A GIVEN LINE # (IN HL) IN THE TEXT SAVE ; * AREA. DE IS USED AS THE TEXT POINTER. IF THE LINE IS FOUND, DE ; * WILL POINT TO THE BEGINNING OF THAT LINE (I.E., THE LOW BYTE OF THE ; * LINE #), AND FLAGS ARE NC & Z. IF THAT LINE IS NOT THERE AND A LINE ; * WITH A HIGHER LINE # IS FOUND, DE POINTS TO THERE AND FLAGS ARE NC & ; * NZ. IF WE REACHED THE END OF TEXT SAVE AREA AND CANNOT FIND THE ; * LINE, FLAGS ARE C & NZ. 'FNDLN' WILL INITIALIZE DE TO THE BEGINNING ; * OF THE TEXT SAVE AREA TO START THE SEARCH. SOME OTHER ENTRIES OF ; * THIS ROUTINE WILL NOT INITIALIZE DE AND DO THE SEARCH. 'FNDLP' ; * WILL START WITH DE AND SEARCH FOR THE LINE #. 'FNDNXT' WILL BUMP DE ; * BY 2, FIND A CR AND THEN START SEARCH. 'FNDSKP' USES DE TO FIND A ; * CR, AND THEN STARTS SEARCH. ; FNDLN: LD A,H ; *** FNDLN *** OR A ; CHECK SIGN OF HL JP M,QHOW ; IT CANNOT BE - LD DE,TXT ; INIT. TEXT POINTER FNDLP: INC DE ; IS EDT MARK? RST RSTLDE ; does LD A,(DE) [from TeXT if necessary] LD C,A DEC DE ADD A,A RET C RST RSTLDE ; C,NZ PASSED END [load from TeXT if necessary] SUB L ; WE DID NOT, GET BYTE 1 LD B,A ; IS THIS THE LINE? INC DE ; COMPARE LOW ORDER LD A,C ; GET BYTE 2 SBC A,H ; COMPARE HIGH ORDER JR C,FL1 ; NO, NOT THERE YET DEC DE ; ELSE WE EITHER FOUND OR B ; IT, OR IT IS NOT THERE FI2: RET ; NC,Z: FOUND; NC,NZ: NO FNDNXT: INC DE ; FIND NEXT LINE FL1: INC DE ; JUST PASSED BYTE ; FASTER FNDSKP FNDSKP: EX DE,HL ADD HL,HL ; CONVERT TO 'NORMAL' FS01: LD A,(HL) ; GET NEXT BYTE ;* Try to find cr RLCA ; COMBINE WITH FOLLOWING FELLA INC HL XOR (HL) ; TO MAKE THE REAL DATA AND $AA XOR (HL) INC HL CP CR ; HIT A CR YET? JR NZ,FS01 ; NO SIR EEE SCF ; WE GOT IT RR H ; NORMALIZE OUR POINTER RR L EX DE,HL JR FNDLP ; REENTER FIND LOOP ; SUBROUTINE TO GRAB AND VERIFY SUBSCRIPT ; GETSUB: POP IY ; STICK RETURN INTO IY INC DE ; SKIP DA NAME RST RSTPAR ; GET THE PARM ;* Get value of () or storage adrs ADD HL,HL ; CONVERTETH TO BYTES JP C,QHOW ; REJECT ABSURD VALUES PUSH DE ; SAVE SCAN PTR EX DE,HL CALL SIZE ; CHECK FOR VALID SUBSCRIPT CALL COMP ; do COMPare HL and DE JR C,ASORRY ; APOLOGIZE FOR RANGE ERR JP (IY) ; GO HOME ; *** TSTV *** ;* Get VRBL storage address ; TSTV: RST RSTIGN ;* Get next non-blank from (DE) CP '%' ; PEEK-POKE? JR Z,TSTV0 CP '*' ; BACKWARDS ARRAY? JR Z,STARR ; YEP - JUMP TO IT SUB '@' ; TEST VARIABLES RET C ; C: NOT A VARIABLE JR NZ,TV1 ; NOT "@" ARRAY CALL GETSUB ; [new for ab ??] ; LD HL,(TXTUNF) DEC HL DEC HL ADD HL,DE POP DE RET ; PROCESS THE BACKWARDS ARRAY [new for ab, why GETSUB broken out ??] STARR: CALL GETSUB LD HL,DFTLMT ; SUBTRACT INDEX FROM END SBC HL,DE POP DE XOR A ; NO CY SHIT RET ; does %(ADDR) PEEK-POKE CALL here ; TSTV0: INC DE RST RSTPAR ; GET ADDR ;* Get value of () or storage adrs XOR A ; CLEAR CY RET ; AND GO BACK TV1: CP $1B ; NOT @, IS IT A TO Z CCF ; IF NOT RETURN C FLAG RET C INC DE ; IF A THROUGH Z ; IS SECOND CHARACTER ALSO ALPHA? LD L,A ; SAVE FIRST ONE LD A,(DE) ; ZAPPED RSTLDE [instead of using RST RSTLDE ??] CP 'A' JR C,DEVV4 ; IF NOT IN RANGE A-Z CP 'Z' + 1 JR NC,DEVV4 ; THEN SEARCH PUSH BC PUSH DE LD H,A ; SECOND CHAR TO H LD B,PARNUM ; B - ITERATION CTR LD DE,DEVLST ; DE - SEARCH TABLE DEVV1: LD A,(DE) ; GET FIRST ENTRY INC DE CP L LD A,(DE) INC DE JR NZ,DEVV2 CP H JR NZ,DEVV2 ; MATCH FOUND - FIGURE OUT LOOKUP INDEX LD A,B ADD A,$1A LD L,A POP DE INC DE ; BUMP CHAR PTR JR DEVV3 ; MISMATCH - LOOP BACK IF POSS DEVV2: DJNZ DEVV1 ; NOT POSSIBLE - RETURN NOT A VAR POP DE POP BC DEC DE ; BACKUP TO CHAR START SCF ; SET CARRY RET DEVV3: POP BC DEVV4: LD A,L LD HL,VARBGN - 2 RLCA ADD A,L LD L,A LD A,$00 ADC A,H LD H,A RET ; **************************************** ; * ; * *** TSTCH *** TSTNUM *** ; * ; * 'TSTCH' IS USED TO TEST THE NEXT NON-BLANK CHARACTER IN THE TEXT ; * (POINTED BY DE) AGAINST THE CHARACTER THAT FOLLOWS THE CALL. IF ; * THEY DO NOT MATCH, N BYTES OF CODE WILL BE SKIPPED OVER, WHERE N IS ; * BETWEEN 0 & 255 AND IS STORED IN THE SECOND BYTE FOLLOWING THE CALL ; * ; * 'TSTNUM' IS USED TO CHECK WHETHER THE TEXT (POINTED BY DE) IS A ; * NUMBER. IF A NUMBER IS FOUND, B WILL BE NON-ZERO AND HL WILL ; * CONTAIN THE VALUE (IN BINARY) OF THE NUMBER, ELSE B AND HL ARE 0. ; ;**************************************** ;* RST8 TSTCH * ;**************************************** ; TSTCH: EX (SP),HL ; *** TSTCH *** ;* Get (caller+1) RST RSTIGN ; IGNORE LEADING BLANKS ;* Get next non-blank from (DE) CP (HL) ; AND TEST THE CHARACTER ;* Same? INC HL ; COMPARE THE BYTE THAT ;* Next location JR Z,TC1 ; FOLLOWS THE CALL INTS. ;* Match! PUSH BC ; WITH TEXT (DE->) LD C,(HL) ; IF NOT =, ADD THE 2ND ;* Get # of bytes to skip LD B,$00 ; BYTE THAT FOLLOWS THE ADD HL,BC ; CALL TO THE OLD PC ;* Increment past POP BC ; I.E., DO A RELATIVE DEC DE ; JUMP IF NOT = ;* Stay stuck on non-match TC1: INC DE ; IF =, SKIP THOSE BYTES ;* Next char. INC HL ; AND CONTINUE ;* Bump past # bytes EX (SP),HL ;* Put return back on stack RET TSTNUM: LD HL,$0000 ; *** TSTNUM *** LD B,H ; TEST IF THE TEXT IS RST RSTIGN ; A NUMBER ;* Get next non-blank from (DE) TN1: CP '0' ; IF NOT, RETURN 0 IN RET C ; B AND HL CP ':' ; IF NUMBERS, CONVERT RET NC ; TO BINARY IN HL AND LD A,$F0 ; SET B TO # OF DIGITS AND H ; IF H>255, THERE IS NO JR NZ,QHOW ; ROOM FOR NEXT DIGIT INC B ; B COUNTS # OF DIGITS PUSH BC LD B,H ; HL=10*HL+(NEW DIGIT) LD C,L ADD HL,HL ; WHERE 10* IS DONE BY ADD HL,HL ; SHIFT AND ADD ADD HL,BC ADD HL,HL LD A,(DE) ; AND (DIGIT) IS FROM INC DE ; STRIPPING THE ASCII AND $0F ; CODE ADD A,L LD L,A LD A,$00 ADC A,H LD H,A POP BC LD A,(DE) ; DO THIS DIGIT AFTER JP P,TN1 ; DIGIT. S SAYS OVERFLOW QHOW: PUSH DE ; *** ERROR: "HOW?" *** AHOW: LD DE,HOW JP ERROR ; * *** MVUP *** MVDOWN *** POPA *** AND PUSHA *** ; * ; * 'MVUP' MOVES A BLOCK UP FROM WHERE DE-> TO WHERE BC-> ; * UNTIL DE=HL ; * ; * 'MVDOWN' MOVES A BLOCK DOWN FROM WHERE DE-> TO WHERE HL-> ; * UNTIL DE=BC ; * ; * 'POPA' RESTORES THE 'FOR' LOOP VARIABLE SAVE AREA FROM THE STACK ; * ; * 'PUSHA' STACKS THE 'FOR' LOOP VARIABLE SAVE AREA INTO THE STACK ; MVUP: CALL COMP ; *** MVUP *** ; do COMPare HL and DE RET Z ; DE=HL, RETURN RST RSTLDE ; GET ONE BYTE [load from TeXT if necessary] PUSH HL ; SHOVEL REGS LD H,B LD L,C CALL STHL ; MOVE IT [interlaced into TeXT if necessary] POP HL INC DE ; INCREASE BOTH POINTERS INC BC JR MVUP ; UNTIL DONE MVDOWN: LD A,B ; *** MVDOWN *** SUB D ; TEST IF DE = BC JR NZ,MD1 ; NO, GO MOVE LD A,C ; MAYBE, OTHER BYTE SUB E RET Z ; YES, RETURN MD1: DEC DE ; ELSE MOVE A BYTE DEC HL ; BUT FIRST DECREASE RST RSTLDE ; BOTH PTRS AND THEN [load from TeXT if necessary] CALL STHL ; DO IT [interlaced into TeXT if necessary] JR MVDOWN ; LOOP BACK POPA: POP BC ; BC = RETURN ADDR. POP HL ; RESTORE LOPVAR, BUT LD (LOPVAR),HL ; =0 MEANS NO MORE LD A,H OR L JR Z,PP1 ; YEP, GO RETURN POP HL ; NO, RESTORE OTHERS LD (LOPINC),HL POP HL LD (LOPLMT),HL POP HL LD (LOPLN),HL POP HL LD (LOPPT),HL PP1: PUSH BC ; BC = RETURN ADDR. RET PUSHA: LD HL,-STKLMT ; *** PUSHA *** [ab omitted CALL CHGSGN ??] POP BC ; BC = RETURN ADDR. ADD HL,SP ; IS STACK NEAR THE TOP? JP NC,QSORRY ; YES - SORRY FOR THAT LD HL,(LOPVAR) ; ELSE SAVE LOOP VAR.S LD A,H ; BUT IF LOPVAR IS 0 OR L ; THAT WILL BE ALL JR Z,PU1 LD HL,(LOPPT) ; ELSE MORE TO SAVE PUSH HL LD HL,(LOPLN) PUSH HL LD HL,(LOPLMT) PUSH HL LD HL,(LOPINC) PUSH HL LD HL,(LOPVAR) PU1: PUSH HL PUSH BC ; BC = RETURN ADDR. RET ; * *** PRTSTG *** QTSTG *** PRTNUM *** PRTLN *** AND PRTLNS *** ; * ; * 'PRTSTG' PRINTS A STRING POINTED AT BY DE. IT STOPS ; * PRINTING AND RETURNS TO CALLER WHEN EITHER A CR IS ; * PRINTED OR WHEN THE NEXT BYTE IS ZERO. REG. A AND B ; * ARE CHANGED. REG. DE POINTS TO WHAT FOLLOWS THE CR ; * OR TO THE ZERO ; * ; * 'QTSTG' LOOKS FOR SINGLE QUOTE, OR DOUBLE QUOTE. IF ; * EITHER IT PRINTS THE STRING UNTIL MATCHING UNQUOTE ; * AND RETURNS 2 BYTES LATE. ; * ; * 'PRTNUM' PRINTS THE NUMBER HL. LEADING BLANKS ARE ADDED ; * IF NEEDED TO PAD THE NUMBER OF SPACES TO THE NUMBER IN C. ; * HOWEVER, IF THE NUMBER OF DIGITS IS LARGER THAN THE # IN C, ; * ALL DIGITS ARE PRINTED ANYWAY. NEGATIVE SIGN IS ALSO ; * PRINTED AND INCLUDED IN COUNT. POSITIVE SIGN IS NOT. ; * ; * 'PRTLN' FINDS A SAVED LINE, PRINTS THE LINE # AND A SPACE. ; * ; * [new for AB, 'PRTLNS' CALLS 'PRTLN' THEN FALLS INTO 'PRTSTG'] ; PRTLNS: CALL PRTLN ; do PRinT Line Number, then ... PRTSTG: SUB A ; *** PRTSTG *** PS1: LD B,A PS2: RST RSTLDE ; GET A CHARACTER [load from TeXT if necessary] INC DE ; BUMP POINTER CP B ; SAME AS OLD A RET Z ; YES, RETURN CALL OUTCH ; ELSE PRINT IT ;* Output Char. in A CP CR ; WAS IT A CR? JR NZ,PS2 ; NO - NEXT RET QTSTG: RST RSTLDE ; does LD A,(DE) [from TeXT if necessary] INC DE ; BUMP PAST CP '"' JR Z,QT1 ; IF DOUBLE QUOTE-PRINT IT CP $27 ; OR IF SINGLE JR Z,QT1 ; LIKEWIZE DEC DE RET QT1: CALL PS1 ; PRINT UNTIL ANOTHER QT2: CP CR ; WAS LAST ONE A CR? POP HL ; RETURN ADDRESS JP Z,IMCHEK ; WAS CR, END OF THIS INC HL ; SKIP 2 BYTES, THEN RET INC HL JP (HL) PRTCHS: LD A,E CP B RET Z RST RSTLDE ; does LD A,(DE) [from TeXT if necessary] CALL OUTCH ; do OUTput CHaracter in A INC DE JR PRTCHS PRTNUM EQU $ ; *** PRTNUM *** PN3: LD B,$00 ; B=SIGN CALL CHKSGN ; CHECK SIGN JP P,PN4 ; NO SIGN LD B,'-' ; B=SIGN DEC C ; '-' TAKES SPACE PN4: PUSH DE LD DE,$000A PUSH DE DEC C PUSH BC PN5: CALL DIVIDE ; DIV HL BY 10 LD A,B ; RESULT 0 OR C JR Z,PN6 ; YES, WE GOT ALL EX (SP),HL ; NO SAVE REMAINDER DEC L ; AND COUNT SPACE PUSH HL ; HL IS OLD BC LD H,B ; MOVE RESULT TO BC LD L,C JR PN5 ; AND DIV BY 10 PN6: POP BC ; WE GOT ALL DIGITS IN PN7: DEC C ; THE STACK BIT 7,C ; IF SPACE COUNT NEG JR NZ,PN8 ; NO LEADING BLANKS CALL SPOUTCH ; SPACE OUTCH ;* Output a Space JR PN7 ; MORE? PN8: LD A,B ; PRINT SIGN OR A CALL NZ,OUTCH ; MAYBE - OR NULL LD E,L ; LAST REMAINDER IN E PN9: LD A,E ; CHECK DIGIT IN E CP $0A ; 10 IS FLAG FOR NO MORE POP DE RET Z ; IF SO RETURN ADD A,'0' CALL OUTCH ; AND PRINT THE DIGIT ;* Output Char. in A JR PN9 ; GO BACK FOR MORE PRTLN: RST RSTLDE ; *** PRTLN *** [load from TeXT if necessary] LD L,A ; LOW ORDER LINE # INC DE RST RSTLDE ; HIGH ORDER [load from TeXT if necessary] LD H,A INC DE LD C,$02 ; PRINT 2 DIGIT [minimum] LINE # CALL PRTNUM ; do PRinT NUMber in HL SPOUTCH: ; SPace OUTput CHaracter routine LD A,' ' ; FOLLOWED BY BLANK JP OUTCH ; Output Space ; Tables for some COMMANDS, FUNCTIONS, and RELATIVE OPERATIONS TAB2: ITEM 'TV',PUTCD ; DIRECT-STATEMENT ITEM 'MU',PUTMU ITEM '&',PUTIO ITEM 'CALL',DOCALL ITEM '.',REM ; [Note: ITEM '$',DOLLAR for Calculator routine has been deleted] DB ':' TOKEN $74,TOUTPU ; on :PRINT DB ':' TOKEN $73,TINPUT ; on :INPUT DB ':' TOKEN $6A,TLOAD ; on :RUN DB ':' TOKEN $68,TVLIST ; on :LIST ; [Note: DB ':' ; TOKEN $70,TCLOSE on :RETURN to close off tape input port ; DB '*' ; TOKEN $74,POUTPU ... and on *PRINT have been deleted, ; Crafty hackers are now referred to the section explaining EXPERIMENTER ; SUPPORT FEATURES for hints on how to add a custom character print routine. ; TOKEN $60,SILENCE ; on Down-Arrow [new for ab] ITEM 'STOP',STOP DEFF FINISH ;* End list 1 ; ;* FUNCTIONS ; TAB3: TOKEN $76,RND ; Input FUNCTIONS ITEM 'KN',GETPOT ITEM 'TR',GETTRG ITEM 'JX',GETJX ITEM 'JY',GETJY ITEM 'KP',GETKB ITEM 'PX',PIXFUN ITEM '&',IOFUN ITEM 'ABS',ABS ITEM 'SZ',SIZE DEFF NOTF ;* End list 2 TAB6: ITEM '>=',XPR1 ; RELATION OPS ITEM '#',XPR2 ITEM '>',XPR3 ITEM '=',XPR5 ITEM '<=',XPR4 ITEM '<',XPR6 DEFF XPR7 RANEND EQU $ ;* End list 3 ; more routines here ; EDITOR FEATURE ; ; An editing feature has been added. Pressing the PAUSE ; key will retrieve a character from a statement in program ; storage. This feature is used as follows: ; 1) Type the line number of the line you wish to ; correct. ; 2) Press the PAUSE key once to recall the next ; character from the line in storage. Repeated pressing of ; PAUSE will scan across the stored line. ; 3) A character drawn by PAUSE looks to the computer ; just like a character entered directly from the keypad. This ; means ERASE can be used to discard unwanted characters from ; the stored line. ; 4) Additions can be entered at any time, intermixed ; as desired. ; 5) If PAUSE runs off the end of the stored line, it ; will have the same effect as pressing the GO key. The key ; sequence WORDS-SPACE will also activate this feature. An ; earlier version of Improved BASIC worked using this ; sequence. ; GLED: LD A,(EDFLG) ; *** Graphic Line EDitor *** AND A JR Z,GLEDA LD DE,BUFFER CALL TSTNUM ; do TeST for NUMber CALL FNDLN ; do FiND HL Line Number in TeXT LD A,'?' RET NZ INC DE CALL GLEDB XOR A LD (EDFLG),A GLEDA: LD DE,(EDPTR) RST RSTLDE ; does LD A,(DE) [from TeXT if necessary] GLEDB: INC DE LD (EDPTR),DE RET ;* Output A and input into BUFFER GETLN: LD DE,BUFFER ; *** GET a LiNe [redone for ab] *** LD (EDFLG),A GL1: CALL OUTCH ; PROMPT OR ECHO ;* Output Char. in A GL2: PUSH BC PUSH HL PUSH DE ; PLACE UP CURSOR BLOCK LD C,$AA CALL CURSE ; do Draw CURSor ; RETURN CHAR FROM NEXT LINE # GL2A: LD HL,NLLNCT LD A,(HL) ; SENSE FLAG AND A JR Z,GL2C DEC (HL) ; FIRST TIME THRU? CP $05 JR NZ,GL2B ; JUMP IF NOT ; GET PREVIOUS LINE # AND BUMP IT LD HL,(OLDLN) LD DE,$000A ADD HL,DE RES 7,H ; ALLOW NEG GL2J: LD (NLLNLN),HL ; MOVE TO WORKING RAM CELL ; COMPUTE DIVISION SUBTRACTOR GL2B: SYSSUK INDEXW ;* Get $2F0F+(A*2) DW TBLDIV - 2 ;* to DE LD HL,(NLLNLN) LD B,$00 GL2E: AND A SBC HL,DE JP M,GL2F INC B JR GL2E GL2F: ADD HL,DE LD (NLLNLN),HL LD HL,NLLNZS LD A,B AND A JR NZ,GL2G LD A,(HL) AND A JR Z,GL2A ; YES - JUMP BACK XOR A GL2G: ADD A,'0' ; MAKE ASCII LD (HL),A ; SET NONZERO FLAG JR GL2D ; NOTHIN FANCY GL2C: CALL CHKIO ; GET NORMAL CHARACTER from keyboard POP DE LD (DE),A ; STUFF CHAR AS DELIMITER PUSH DE CP EDKEY CALL Z,GLED GL2D: POP DE POP HL POP BC GL3: LD (DE),A ; [label not needed] CP RUBOUT JR NZ,GL4 ;* Jp if not ERASE LD A,E CP BUFFER & $FF JR Z,GL2 ;* Jp if at beginning DEC DE LD A,(DE) CP $68 ; TOKEN TO RUB OUT? JR NC,TOKIN CALL PNOTE ; do Play NOTE tone for key in A LD A,RUBOUT JR GL1 TOKIN: PUSH DE CALL TOKEPT ; do TOKEn PoinTer returned in HL TOKER: LD A,(HL) PUSH HL AND $7F CALL PNOTE ; do Play NOTE tone for key in A LD A,RUBOUT CALL VDM ; do Virtual Display Monitor output POP HL LD A,(HL) INC HL RLCA JR NC,TOKER TOKEQ: LD A,RUBOUT ; [label not needed] CALL OUTCH ; ECHO ONE RUBOUT CHAR ;* Output Char. in A POP DE GL9: JP GL2 GL4: XOR CR JR Z,GL5 LD A,E CP BUFEND & $FF JR Z,GL9 LD A,(DE) INC DE JP GL1 GL5: INC DE INC DE DEC A LD (DE),A DEC DE CRLF: LD A,CR ; Carriage Return (and Line Feed) JP OUTCH ; to OUTput CHaracter in A ; SUBROUTINE TO SIMULATE A CHARACTER DISPLAY IN THE ARCADE ; FRAME BUFFER. THE SIMULATED VDM [Virtual Display Monitor] ; HAS DIMENSIONS 26 CHARS BY 11 LINES. THE CHARACTER GRAPHICS ; ARE 5 X 7 IN A 6 X 8 FRAME. ALTERNATE FONT IS USED TO GET THIS. ; THE 64 UPPER CASE ASCII CHARACTERS ARE DISPLAYED BY THIS ; HANDLER. THE ASCII CONTROL CHARACTERS CARRIAGE RETURN AND ; RUBOUT ARE ALSO PROCESSED BY THIS HANDLER. CR CAUSES ; THE DISPLAY TO GO TO THE NEXT LINE OF THE DISPLAY, WITH ; SCROLL UP IF NECESSARY. RUBOUT CAUSES THE CURSOR TO MOVE ; BACKWARDS ONE CHARACTER POSITION. ; CHARACTER TO DISPLAY IS IN A. THE ALTERNATE REGISTER SET ; IS USED. ; XOUTCH: PUSH AF ; [Vectored to by a CALL OUTCH into RAM at ; $4E9B of HVECT, initialized to JP XOUTCH] EXX CALL VDM ; do Virtual Display Monitor output POP AF EXX RET ; SOME FUNNY GUYS ENTER HERE VDM: CP CR JR Z,VDMOCR CP RUBOUT ; TRANSLATE TRASH TO ? JR Z,VDM1 JR C,FILT1 CP $78 JR C,FILT2 FILT1: LD A,'?' FILT2: CP $68 ; TOKEN TO PRINT? JR NC,TOKEP ; JUMP IF SO ; PLAY NOTE FOR THIS CHAR CALL PNOTE ; do Play NOTE tone for key in A ; NON NEW LINE CHAR - UNWRITE OLD CURSOR VDM1: CALL UCURSE ; do Undraw CURSor CALL LDVDMC ; do LoaD VDM Coordinates CP RUBOUT ; WAS THAT RUBOUT? JR NZ,VDM3 ; JUMP IF NOT ; RUBOUT ENTERED - SO RUB OUT LD A,L ; GET X AND A ; IS X =0? JR Z,VDM2 ; YES - JUMP SUB $06 ; NO - BACKUP X LD L,A JR VDMDN1 ; AND JOIN STORE BACK VDM2: LD L,$96 LD A,H SUB $08 LD H,A JR VDMDN1 ; NEW LINE CHAR - DID WE JUST WRAP AROUND VDMOCR: LD A,(VDMNLF) ; CHECK OLD GLORY AND A JR NZ,VDMDON ; YES - SKIP DIDDLING CALL UCURSE ; NO - UNWRITE CURSOR CALL NEWLIN ; GO TO NEXT LINE JR VDMDON ; AND QUIT ; NORMAL CHARACTER ENTERED - DISPLAY IT VDM3: LD D,H ; COORDINATES TO DE LD E,L OR $80 ; ALT FONT THE CHAR LD C,011000B ; OR WRITE THE CHAR LD IX,ALTFON ; USING ALTERNATE CHAR FONT SYSTEM CHRDIS ; IT ;* Display Character LD A,L ; ADVANCE X POINTER ;* Skip a space ADD A,$06 LD L,A CP $9C ; END OF LINE? JR NZ,VDMDN1 ; NO - JUMP CALL NEWLIN ; YES - NEW 1 LINE LD A,$01 ; AND SET NEW LINE FORCED FLAG JR VDMDN2 VDMDN1: CALL STVDMC ; do STore VDM Coordinates VDMDON: XOR A ; CLEAR NEW LINE FORCED FLAG VDMDN2: LD (VDMNLF),A RET ; ROUTINE TO DISPLAY A TOKEN IN FULL FORM ; TOKEP: CALL TOKEPT ; do TOKEn PoinTer returned in HL TOKEP1: LD A,(HL) AND $7F PUSH HL CALL VDM ; do Virtual Display Monitor output POP HL LD A,(HL) INC HL RLCA JR NC,TOKEP1 LD A,' ' ; PUT SPACE AFTER TOKEN JP VDM ; AND GO HOME via VDM output ; SUBROUTINE TO UNWRITE THE CURSOR ; UCURSE: LD C,$00 JR CURSE ; do Draw CURSor in BC ; SUBROUTINE TO DISPLAY NEW LINE ; NEWLIN: CALL LDVDMC ; do LoaD VDM Coordinates ; IS SCROLL UP NEEDED? LD L,$00 LD A,H CP $50 JR NZ,NEWL1 ; JUMP IF NOT NEEDED ; ; A variable called SM (SCROLL MODE) has been added to ; allow more user control of the scrolling process which ; occurs when printing reaches the bottom of the screen. ; SM=0 Scrolls conventionally [as in BB]. ; SM=1 Suppresses scrolling, cursor stays at bottom ; SM=2 Holds cursor at screen bottom, clears after ; each CR ; SM=3 Clears the screen and resets cursor to ; screen top. ; SM=4 AUTO-PAUSE, release by pressing any key. ; After release the screen will clear and ; printing will resume from screen top. ; ; SCROLL UP IS NEEDED CALL STVDMC ; do STore VDM Coordinates ; WHAT MODE SHALL WE USE? LD A,(SCRMOD) DEC A RET Z ; 1 - NO SCROLL DEC A LD HL,$4C80 JR Z,CLRLP ; 2 - to CLeaR LooP [just last line] DEC A JR Z,CLRENT ; 3 - to CLeaR ENTire screen DEC A JR Z,CLRFRZ ; 4 - to CLeaR after FReeZe until keydown LD HL,$4DC0 SCRL9: LD A,(HL) ; else - NORMAL SCROLL AND 01010101B ;* Save screen bits LD (HL),A ;* Make them good INC HL ;* Move them up LD A,L CP $20 JR NZ,SCRL9 LD B,$04 SCRLP: PUSH BC LD HL,NORMEM LD DE,NORMEM + $0050 LD BC,$C00E SCRUP: LD A,(DE) XOR (HL) AND 10101010B XOR (HL) LD (HL),A INC HL INC DE DJNZ SCRUP DEC C JR NZ,SCRUP POP BC DJNZ SCRLP RET NEWL1: ADD A,$08 LD H,A JP STVDMC ; to STore VDM Coordinates ; Clear Screen subroutines [by SM variable, new for ab] CLRFRZ: CALL KEYSCN ; FReeZe until ANY KEY DOWN JR Z,CLRFRZ ; RESET VDM GOODIES then CLeaR ENTire screen CLRENT: PUSH DE SYSSUK MOVE ; MOVE BYTES (SYSTEM ROUTINE) DW VDMX DW $0004 ; 4 bytes VDM X, VDM Y DW INIDEV + 6 ; INIDEV Part 2 to $4E5C POP DE LD HL,$4000 CLRLP: LD A,(HL) ; [also used for last line only] AND 01010101B LD (HL),A INC HL LD A,H CP $4E JR NZ,CLRLP RET ; SUBROUTINE TO PAINT CURSOR ; C = DATA TO PAINT 00 OR AA ; CURSE: PUSH AF ;* Get CY/CX to HL CALL LDVDMC ; do LoaD VDM Coordinates PCURS1: EX DE,HL XOR A SYSTEM RELAB1 ;* Subroutine 58 OUT (MAGIC),A EX DE,HL LD A,C LD BC,$0806 CALL BOXPUT ; do draw a BOX PUT on screen POP AF RET ; NEW KEYBOARD HANDLER ; WITH SHIFT KEY ROLLOVER ; XCHKIO: CALL KEYSCN ; MAKE SURE PREVIOUS KEY RELEASE ;* Get keyboard data JR NZ,XCHKIO ;* Wait till no key change ; AWAIT DEBOUNCE TIMER COUNTDOWN CHKIO0: LD HL,KEYTMR LD (HL),$06 ; SET IT ;* Set key release timer LOOPER: LD A,(HL) AND A JR NZ,LOOPER ;* Wait for timer to expire ; SAVE BACKGROUND COLOR LD A,(DEVCL0) ;* Get BC PUSH AF ; ASSUME FIRST LEVEL KEYCODE LD HL,FIRSTL GETK1: PUSH HL ; SAVE TABLE PTR ; SCAN ONLY FOR SHIFT KEYS LD HL,KTBL4 LD DE,$FFEB ; (-21) ** SIZE OF LOOKUP TABLE ;* 20 keys+color LD BC,$0414 GETK2: IN A,(C) ; INPUT FROM PORT ;* Get shift key AND $20 ; SHIFT KEY DOWN? JR NZ,GETK3 ; JUMP IF YEP ;* Got one ADD HL,DE ; ELSE TO NEXT TABLE ;* Back up to previous table INC C ; AND PORT ;* Next port DJNZ GETK2 ;* Do all 4 ports ; NO SHIFT KEY IS DOWN - USE WHATEVER WE HAD BEFORE POP HL ;* Get current no-shift address JR GETK5 ; A SHIFT KEY IS DOWN - SAME OLD STORY? GETK3: POP DE ; DISCARD OLD BELIEFS ;* Clear stack LD A,(HL) ; SET NEW COLOR ;* Get BC LD (DEVCL0),A GETK4: INC HL ; SKIP COLOR BYTE ;* Point to keys ; NOW SCAN FOR ANY 'NORMAL' KEY DEPRESSION GETK5: CALL KEYSCN ;* Get input ;* Not there, allow different shift key JR Z,GETK1 ; JUMP IF NO KEY DOWN ; WE GOT ONE - CONVERT TO ASCII DEC A ; BY TABLE LOOKUP ;* Back to keys 0-23 LD C,A LD B,$00 ADD HL,BC ;* Point to conversion POP AF ; RESTORE COLOR ;* Get original BC LD (DEVCL0),A ;* Reset it LD A,(HL) ; GET CODE ;* Get key conversion AND A ; A HLT PERCHANCE? JR Z,INIJMP ; YEP - RESET ;* Restart if no code CP $01 ; AN ERROR? JR Z,XCHKIO ; YEP - GO DOIT AGAIN ;* Meaningless key ; GOOD KEY... CHKI02: RLCA ; set C to bit 7 value [label no longer needed] CALL C,WCLICK ; do Click if flagged (GO,ERASE,+,-,0,x,divide) LD A,(HL) ;* Regain converted key AND $7F CP NLLN ;* Was it GO+10 ? RET NZ LD HL,$0005 ;* Yes LD (NLLNCT),HL ; SET FLAG AND ZERO SUPPRESS LD A,CR ; PASS BACK CR AS FIRST CHAR ;* Return a CR RET ; NEW CLICK ROUTINE ; WCLICK: LD A,G0 ; $FD or 253 (not "GO") LD (MUZTON),A LD A,(DEVTEM) ;* Get note time DEC A RET M ; Silence if Tempo = 0 LD A,$01 ;* 1 beat delay LD (NEWTMR),A ; Short Click RET ; SUBROUTINE TO CHECK FOR HLT KEY WHILE PGM RUNNING ;* Check for PAUSE or ABORT ; WHATSU: PUSH BC PUSH DE CALL KEYSCN ; GET KEY CODE ;* Get calculator input SUB $02 ; FREEZE? ;* Was it PAUSE? JR Z,FRZKEY ;* Yes DEC A JR Z,INIJMP ;* Jp if HALT JR FRZGBK ; ELSE GO BACK TO CALLER ;* Else leave ;* PAUSE: wait for another key FRZKEY: CALL KEYSCN ; SCAN FOR NONZERO KEY TO REL JR Z,FRZKEY CP $03 ; HLT NAILED? ;* Was it another HALT INIJMP: JP Z,INIT ;* Yes, restart FRZGBK: POP DE POP BC RET ; SUBROUTINE TO SCAN TINY BASIC KEYBOARD ;* KEYPAD INPUT (all but shift) ; KEYSCN: LD BC,$0414 ; B = CNT, C = PORT # LD DE,KEYTRK ; DE = KEYBOARD MEMORY XOR A SYSTEM RANGED ;* Random number KYSCN1: IN A,(C) ; LOOK AT COLUMN ;* Raw key number AND $1F ; ISOLATE THE RELEVANT JR NZ,KYSCN2 ; JUMP IF BITS HIGH ;* Got one INC C ; BUMP PORT # ;* Next port DJNZ KYSCN1 ;* Do all input ports XOR A ; SET ZERO STATUS LD (DE),A ; NOTHIN - SAY ZIP ;* Set last key = 0 RET ;* and leave ; DEPRESSION FOUND - JUMP UP AND DOWN KYSCN2: DEC B LD C,$00 ; COME UP WITH BIT # KYSCN4: RRCA ; SHIFT BIT OVER JR C,KYSCN3 ; JUMP IF THE ONE INC C ; ELSE COUNT UP JR KYSCN4 ; AND TRY AGAIN ; FOUND BIT - ASSEMBLE KEYCODE KYSCN3: LD A,C ; BIT # TO A RLCA ; * 4 RLCA OR B ; COMBINE WITH COL # INC A ;* Keys 1-24 LD B,A LD A,(DE) XOR B LD A,B ;* Save new value RET Z ; QUIT IF THE SAME ;* Leave if same as last time LD (DE),A ; ELSE UPDATE TRACKER RET ;* and leave ; SUBROUTINE TO PLAY A NOTE ;* NOTE OUTPUT ; PNOTE: PUSH HL PUSH DE PUSH AF LD H,A ; WAIT FOR PREVIOUS PARAMETERS TO BE EATEN PRWAIT: LD A,(NEWTMR) AND A JR NZ,PRWAIT ; LOOP LD A,H XOR ' ' JR Z,TSTOR ; Tone STORe a Zero for Space LD A,H CP $63 JR Z,PNOTDV ; Down an octave on Divide sign CP $62 JR Z,PNOTML ; Up an octave on Multiply sign CP '+' JR Z,PNOTPL ; Sharp next note +1 through +7 CP '-' JR Z,PNOTMN ; Flat next note -1 through -7 CP CR JR Z,PNOTCL ; on GO SUB '0' JR Z,PNOTZ ; to Play NOTe Zero DEC A CP $07 JR C,ANSW ; Play Notes 1 thru 7 LD A,$6C ; Play Notes 8, 9, & etc. SUB H JR TSTOR ; 52 down through 9 for Divide ANSW: LD HL,SHARPF ; *** for Dick AiNSWorth *** ADD A,(HL) ; + 7 if Sharp, + 14 if Flat SYSSUK INDEXB ; INDEX BYTE (SYSTEM SUBROUTINE) DW DICKY ; HL + A [not loaded!] to HL TSTOR: LD (MUZTON),A ; Tone STORe routine ;* Save next note LD A,(DEVTEM) LD (NEWTMR),A PNOTCL: XOR A ; Clear SHARPF ;* Set for normal notes PSHARP: LD (SHARPF),A LINKB: POP AF POP DE POP HL RET PNOTDV: LD A,OA1 ;* Set down an octave DB $11 ; does a LD DE,$233E ;* Skips 2 bytes PNOTML: DB $3E ; does a LD A,OA3 ;* Set up an octave DB $23 LD (MUZMO),A ; save as Master Oscillator ;* Set master sound divider JR LINKB PNOTPL: LD A,$07 ;* Set for sharps DB $11 ; does a LD DE,$0E3E ;* Skips 2 bytes PNOTMN: DB $3E ; does a LD A,14 ;* Set for flats DB $0E JR PSHARP ; save as SHARPF PNOTZ: LD HL,MUZTMR ; Play NOTe Zero routine LD A,(DEVTEM) AND A JP M,LINKB ; exit if NT < 0 DI ADD A,(HL) ; add NT to MUZTMR ;* Increment note timer by one NT LD (HL),A EI JR LINKB ; LINK Back ; SUBROUTINE TO POINT AT A TOKEN ; TOKEPT: LD HL,TOKTXT ; POINT AT TEXT LIST SUB $68 JOKEP1: RET Z ; QUIT IF POINTING AT EM JOKEP2: BIT 7,(HL) ; MOVE PAST NEXT WORD INC HL JR Z,JOKEP2 DEC A JR JOKEP1 ; LOOP BACK AND CHECK ; DICKS MUSIC SYSTEM NOTE LOOKUP TABLE DICKY: ; for Keys 1 to 7 only ;* Normal note table DB C2,D2,E2,F2,G2,A2,B2 ; Sharps ;* Sharp note table DB CS2,DS2,F2,FS2,GS2,AS2,C3 ; Flats ;* Flat note table DB B1,CS2,DS2,E2,FS2,GS2,AS2 TBLDIV: DW $0001 ; 1 DW $000A ; 10 DW $0064 ; 100 DW $03E8 ; 1000 DW $2710 ; 10000 IGNBLK: LD A,(DE) ; *** IGNBLK *** ;* RST20 [not RST#0] CP ' ' ; IGNORE BLANKS RET NZ ; IN TEXT (WHERE DE->) INC DE ; AND RETURN THE FIRST JR IGNBLK ; NON-BLANK CHAR. IN A ; TABLE OF FIST LEVEL KEYCODES ;* CHARACTER TABLE, NO SHIFT KEYS ; ; Also used as Note to Play, new for ab ; Does a short Click if flagged negative ; FIRSTL: DB CR + $80 ; Go DB EDKEY ; Pause DB $00 ; Halt DB $63 + $80 ; Divide DB '7' DB '8' DB '9' DB $62 + $80 ; Multiply DB '4' DB '5' DB '6' DB '-' + $80 DB '1' DB '2' DB '3' DB '+' + $80 DB ' ' DB '0' + $80 DB RUBOUT + $80 ; Erase DB '=' ; FIRST SHIFT KEY ;* PORT 17 SHIFT KEY (GREEN BACKGROUND) KTBL1: DB $A7 ; FIRST SHIFT KEY COLOR = Green DB CR + $80 ; (Go) DB EDKEY ; (Pause) DB $00 ; (Halt) DB $01 ; [bb was EDKEY] DB 'A' DB 'D' DB 'G' DB 'J' DB 'M' DB 'P' DB 'S' DB 'V' DB 'Y' DB $5F ; Left-Arrow DB $5E ; Up-Arrow DB '&' DB '$' DB '<' DB '(' DB '#' ; SECOND SHIFT KEY ;* PORT 16 SHIFT KEY (RED BACKGROUND) KTBL2: DB $5F ; SECOND SHIFT KEY COLOR = Red DB CR + $80 ; (Go) DB $2F ; Slash DB $00 ; (Halt) DB $5B ; Left Bracket DB 'B' DB 'E' DB 'H' DB 'K' DB 'N' DB 'Q' DB 'T' DB 'W' DB 'Z' DB $27 ; Apostrophe DB '.' DB '@' DB ',' DB $22 ; Quotes DB ';' DB '%' ; TABLE THE THIRD ;* PORT 15 SHIFT KEY (BLUE BACKGROUND) KTBL3: DB $0F ; THIRD SHIFT KEY COLOR = Blue DB CR + $80 ; (Go) DB $5C ; Backslash DB $00 ; (Halt) DB $5D ; Right Bracket DB 'C' DB 'F' DB 'I' DB 'L' DB 'O' DB 'R' DB 'U' DB 'X' DB '!' DB $61 ; Right Arrow DB $60 ; Down Arrow DB '*' DB '?' DB '>' DB ')' DB ':' ; TOKEN KEY ;* PORT 14 SHIFT KEY (WORDS) KTBL4: DB $77 ; WORDS KEY COLOR = Gold DB NLLN ; GO+10 DB EDKEY ; (Pause) DB $6A ; RUN token DB $68 ; LIST token DB $72 ; FOR token DB $77 ; TO token DB $75 ; STEP token DB $6B ; NEXT token DB $6F ; GOSUB token DB $70 ; RETURN token DB $76 ; RND token DB $6D ; IF token DB $69 ; CLEAR token DB $6C ; LINE token DB $71 ; BOX token DB $6E ; GOTO token DB EDKEY DB $73 ; INPUT token DB $01 ; [bb was EDKEY] DB $74 ; PRINT token ; More Routines here ; SUBROUTINE TO LD A,(DE) FROM SCREEN TEXT MEMORY IF NECESSARY ; ;* EXTRACT BYTE (DE) FROM SCRATCHPAD ;* OR ;* EXTRACT BYTE (DE*2) FROM HIDDEN SCREEN AREA ; ; [new for ab, use] RST RSTLDE (RST $18) ; LDE: BIT 7,D JR Z,LDE1 ;* Jp if normal scratchpad EX DE,HL ADD HL,HL ;* Double the phoney number LD A,(HL) ;* First get bits 7,5,3,1 RLCA INC HL XOR (HL) AND 10101010B XOR (HL) ;* Then bits 6,4,2,0 SCF RR H ;* Byte value to A [different for ab ??] RR L EX DE,HL ;* Don't mess up his pointer RET LDE1: LD A,(DE) ;* Extract normal data RET ; DOUBLE STORE a word in DE into memory pointed to by HL Routine ; STDEHL: LD A,E CALL STHL ; STore A by HL [interlaced into TeXT if necessary] INC HL LD A,D ; THEN FALL INTO... ; STORE a byte INTO memory pointed to by HL Routine ; ;* DEPOSIT BYTE AT (HL) INTO SCRATCHPAD ;* OR ;* DEPOSIT BYTE AT (HL*2) INTO HIDDEN SCREEN AREA ; STHL: BIT 7,H JR Z,STHL1 ;* Jp if normal deposit PUSH BC LD C,A ADD HL,HL ;* Double his phoney number RRCA ;* Make bits 7,5,3,1 as XOR (HL) ;* 6,4,2,0 and get screen AND 01010101B ;* Save screen bits XOR (HL) ;* and restore mine LD (HL),A ;* Then place them away INC HL LD A,C XOR (HL) ;* With screen bits AND 01010101B XOR (HL) ;* Combined with mine LD (HL),A ;* For the screen area SCF RR H RR L LD A,C POP BC RET ; SUBROUTINE TO STORE a byte using LD (HL),A STHL1: LD (HL),A ;* Store normal data RET ; END OF BALLY BASIC INTERPRETER (AKA ASTROBASIC)