From df1a8df463d3c8b78a9a61de1b5d24004e258c4b Mon Sep 17 00:00:00 2001 From: dimitrit Date: Fri, 29 Oct 2021 22:47:24 +0100 Subject: [PATCH 1/2] Update TastyBasic --- Source/BuildShared.cmd | 1 + Source/Clean.cmd | 2 +- Source/HBIOS/Makefile | 5 +- Source/HBIOS/std.asm | 2 +- Source/HBIOS/tastybasic.asm | 1905 ---------------------- Source/Images/d_cpm22.txt | 1 + Source/Images/d_zsdos.txt | 1 + Source/Makefile | 1 + Source/TastyBasic/Build.cmd | 4 + Source/TastyBasic/Clean.cmd | 4 + Source/TastyBasic/LICENSE | 674 ++++++++ Source/TastyBasic/Makefile | 9 + Source/TastyBasic/README.md | 163 ++ Source/TastyBasic/examples/BATNUM.BAS | 86 + Source/TastyBasic/examples/DUMP.BAS | 16 + Source/TastyBasic/examples/README.md | 25 + Source/TastyBasic/examples/REVERSE.BAS | 74 + Source/TastyBasic/examples/SBCRTC.BAS | 30 + Source/TastyBasic/examples/TICTAC.BAS | 117 ++ Source/TastyBasic/examples/bitsum.asm | 48 + Source/TastyBasic/examples/bitsum.tba | Bin 0 -> 384 bytes Source/TastyBasic/examples/tictac.tba | Bin 0 -> 2816 bytes Source/TastyBasic/src/Build.cmd | 13 + Source/TastyBasic/src/Clean.cmd | 11 + Source/TastyBasic/src/Makefile | 32 + Source/TastyBasic/src/cpmio.asm | 207 +++ Source/TastyBasic/src/romwbwio.asm | 100 ++ Source/TastyBasic/src/tastybasic.asm | 2017 ++++++++++++++++++++++++ Source/TastyBasic/src/zemuio.asm | 54 + 29 files changed, 3694 insertions(+), 1908 deletions(-) delete mode 100644 Source/HBIOS/tastybasic.asm create mode 100644 Source/TastyBasic/Build.cmd create mode 100644 Source/TastyBasic/Clean.cmd create mode 100644 Source/TastyBasic/LICENSE create mode 100644 Source/TastyBasic/Makefile create mode 100644 Source/TastyBasic/README.md create mode 100644 Source/TastyBasic/examples/BATNUM.BAS create mode 100644 Source/TastyBasic/examples/DUMP.BAS create mode 100644 Source/TastyBasic/examples/README.md create mode 100644 Source/TastyBasic/examples/REVERSE.BAS create mode 100644 Source/TastyBasic/examples/SBCRTC.BAS create mode 100644 Source/TastyBasic/examples/TICTAC.BAS create mode 100644 Source/TastyBasic/examples/bitsum.asm create mode 100644 Source/TastyBasic/examples/bitsum.tba create mode 100644 Source/TastyBasic/examples/tictac.tba create mode 100644 Source/TastyBasic/src/Build.cmd create mode 100644 Source/TastyBasic/src/Clean.cmd create mode 100644 Source/TastyBasic/src/Makefile create mode 100644 Source/TastyBasic/src/cpmio.asm create mode 100644 Source/TastyBasic/src/romwbwio.asm create mode 100644 Source/TastyBasic/src/tastybasic.asm create mode 100644 Source/TastyBasic/src/zemuio.asm diff --git a/Source/BuildShared.cmd b/Source/BuildShared.cmd index df961d93..ea9a7b10 100644 --- a/Source/BuildShared.cmd +++ b/Source/BuildShared.cmd @@ -11,5 +11,6 @@ pushd CPM3 && call Build || exit /b & popd pushd ZPM3 && call Build || exit /b & popd pushd Apps && call Build || exit /b & popd pushd Forth && call Build || exit /b & popd +pushd TastyBasic && call Build || exit /b & popd pushd Fonts && call Build || exit /b & popd pushd RomDsk && call Build || exit /b & popd diff --git a/Source/Clean.cmd b/Source/Clean.cmd index 3507fcb2..1af0deff 100644 --- a/Source/Clean.cmd +++ b/Source/Clean.cmd @@ -11,6 +11,7 @@ pushd CBIOS && call Clean.cmd & popd pushd CPM3 && call Clean.cmd & popd pushd ZPM3 && call Clean.cmd & popd pushd Forth && call Clean.cmd & popd +pushd TastyBasic && call Clean & popd pushd Fonts && call Clean.cmd & popd pushd BPBIOS && call Clean.cmd & popd pushd HBIOS && call Clean.cmd & popd @@ -18,4 +19,3 @@ pushd Images && call Clean & popd pushd Prop && call Clean & popd pushd RomDsk && call Clean & popd pushd Doc && call Clean & popd - diff --git a/Source/HBIOS/Makefile b/Source/HBIOS/Makefile index cc7a94be..b1dc4abf 100644 --- a/Source/HBIOS/Makefile +++ b/Source/HBIOS/Makefile @@ -70,13 +70,16 @@ $(ROMNAME).rom $(ROMNAME).com $(ROMNAME).img &: $(ROMDEPS) cat hbios_app.bin osimg_small.bin > $(ROMNAME).com ; \ fi -prereq: $(FONTS) camel80.bin +prereq: $(FONTS) camel80.bin tastybasic.bin font%.asm: cp ../Fonts/$@ . camel80.bin: cp ../Forth/$@ . + +tastybasic.bin: + cp ../TastyBasic/src/$@ . hbios_rom.bin: hbios.asm build.inc $(DEPS) $(TASM) -dROMBOOT hbios.asm hbios_rom.bin hbios_rom.lst diff --git a/Source/HBIOS/std.asm b/Source/HBIOS/std.asm index 82a3be91..5630860c 100644 --- a/Source/HBIOS/std.asm +++ b/Source/HBIOS/std.asm @@ -592,7 +592,7 @@ BAS_END .EQU BAS_LOC + BAS_SIZ BAS_IMGLOC .EQU FTH_IMGLOC + FTH_SIZ TBC_LOC .EQU $0A00 ; TASTYBASIC -TBC_SIZ .EQU $0900 +TBC_SIZ .EQU $0A00 TBC_END .EQU TBC_LOC + TBC_SIZ TBC_IMGLOC .EQU BAS_IMGLOC + BAS_SIZ diff --git a/Source/HBIOS/tastybasic.asm b/Source/HBIOS/tastybasic.asm deleted file mode 100644 index 75162e67..00000000 --- a/Source/HBIOS/tastybasic.asm +++ /dev/null @@ -1,1905 +0,0 @@ - -; ----------------------------------------------------------------------------- -; Copyright 2018 Dimitri Theulings -; -; This file is part of Tasty Basic. -; -; Tasty Basic is free software: you can redistribute it and/or modify -; it under the terms of the GNU General Public License as published by -; the Free Software Foundation, either version 3 of the License, or -; (at your option) any later version. -; -; Tasty Basic is distributed in the hope that it will be useful, -; but WITHOUT ANY WARRANTY; without even the implied warranty of -; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -; GNU General Public License for more details. -; -; You should have received a copy of the GNU General Public License -; along with Tasty Basic. If not, see . -; ----------------------------------------------------------------------------- -; Tasty Basic is derived from earlier works by Li-Chen Wang, Peter Rauskolb, -; and Doug Gabbard. Refer to the source code repository for details -; . -; ----------------------------------------------------------------------------- - -#define dwa(addr) .db (addr >> 8) + 080h\ .db addr & 0ffh - -ctrlc .equ 03h -bs .equ 08h -lf .equ 0ah -cr .equ 0dh -ctrlo .equ 0fh -ctrlu .equ 15h - -#ifdef ZEMU ; Z80 Emulator -tty_data .equ 7ch -tty_status .equ 7dh -rx_full .equ 1 -tx_empty .equ 0 -TBC_LOC .equ 0 -#else ; RomWBW -#include "std.asm" -#endif - - .org TBC_LOC -start: - ld sp,stack ; ** Cold Start ** - ld a,0ffh - jp init -testc: - ex (sp),hl ; ** TestC ** - call skipspace ; ignore spaces - cp (hl) ; test character - inc hl ; compare the byte that follows the - jr z,tc1 ; call instruction with the text pointer - push bc - ld c,(hl) ; if not equal, ad the seond byte - ld b, 0h ; that follows the call to the old pc - add hl,bc - pop bc - dec de -tc1: - inc de ; if equal, skip those bytes - inc hl ; and continue - ex (sp),hl - ret - -skipspace: - ld a,(de) ; ** SkipSpace ** - cp ' ' ; ignore spaces - ret nz ; in text (where de points) - inc de ; and return the first non-blank - jp skipspace ; character in A - -expr: - call expr2 ; ** Expr ** - push hl ; evaluate expression - jp expr1 - -comp: - ld a,h ; ** Compare ** - cp d ; compare hl with de - ret nz ; return c and z flags - ld a,l ; old a is lost - cp e - ret - -finish: - pop af ; ** Finish ** - call fin ; check end of command - jp qwhat - -;************************************************************* -; -; *** REM *** IF *** INPUT *** & LET (& DEFLT) *** -; -; 'REM' CAN BE FOLLOWED BY ANYTHING AND IS IGNORED BY TBI. -; TBI TREATS IT LIKE AN 'IF' WITH A FALSE CONDITION. -; -; 'IF' IS FOLLOWED BY AN EXPR. AS A CONDITION AND ONE OR MORE -; COMMANDS (INCLUDING OTHER 'IF'S) SEPERATED 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 FOLLOWS 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 SINGLE OR -; DOUBLE QUOTES, OR IS A BACK-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 SET 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'. -; -; 'LET' IS FOLLOWED BY A LIST OF ITEMS SEPERATED BY COMMAS. -; EACH ITEM CONSISTS OF A VARIABLE, AN EQUAL SIGN, AND AN EXPR. -; TBI EVALUATES THE EXPR. AND SET THE VARIABLE TO THAT VALUE. -; TBI WILL ALSO HANDLE 'LET' COMMAND WITHOUT THE WORD 'LET'. -; THIS IS DONE BY 'DEFLT'. -;************************************************************* -rem: - ld hl,0000h ; ** Rem ** - jr if1 ; this is like 'IF 0' -iff: - call expr ; ** If ** -if1: - ld a,h ; is the expr = 0? - or l - jp nz,runsml ; no, continue - call findskip ; yes, skip rest of line - jp nc,runtsl ; and run the next line - jp rstart ; if no, restart -inputerror: - ld hl,(stkinp) ; ** InputError ** - ld sp,hl ; restore old sp and old current - pop hl - ld (current),hl - pop de ; and old text pointer - pop de ; redo current -input: - push de ; ** Input ** - call qtstg ; is next item a string? - jp ip2 ; no - call testvar ; yes and followed by a variable? - jp c,ip4 ; no - jp ip3 ; yes, input variable -ip2: - push de ; save for printstr - call testvar ; must be variable - jp c,qwhat ; no, what? - ld a,(de) ; prepare for printstr - ld c,a - sub a - ld (de),a - pop de - call printstr ; print string as prompt - ld a,c ; restore text - dec de - ld (de),a -ip3: - push de ; save text pointer - ex de,hl - ld hl,(current) ; also save current - push hl - ld hl,input - ld (current),hl - ld hl,0000h - add hl,sp - ld (stkinp),hl - push de - ld a,':' - call getline - ld de,buffer - call expr - nop - nop - nop - pop de - ex de,hl - ld (hl),e - inc hl - ld (hl),d - pop hl - ld (current),hl - pop de -ip4: - pop af ; purge stack - call testc ; is next character ','? - .db ',' - .db ip5-$-1 - jr input ; yes, more items -ip5: - call finish -deflt: - ld a,(de) ; ** DEFLT ** - cp cr ; empty line is fine - jr z,lt1 ; else it's 'LET' -let: - call setval ; ** Let ** - call testc ; set value to var - .db ',' - .db lt1-$-1 - jr let ; item by item -lt1: - call finish - -;************************************************************* -; -; *** PEEK *** POKE *** IN *** & OUT *** -; -; 'PEEK()' RETURNS THE VALUE OF THE BYTE AT THE GIVEN -; ADDRESS. -; 'POKE ,' SETS BYTE AT ADDRESS TO -; VALUE -; -;************************************************************* -peek: - call parn ; ** Peek(expr) ** - ld a,h ; expression must be positive - or a - jp m,qhow - ld a,(hl) - ld h,0 - ld l,a - ret -poke: - call expr ; ** Poke ** - ld a,h ; address must be positive - or a - jp m,qhow - push hl - call testc ; is next char a comma? - .db ',' - .db pk1-$-1 ; what, no? - call expr ; get value to store - ld a,0 ; is it > 255? - cp h - jp z,pk2 ; no, all good - pop hl - jp m,qhow -pk2: - ld a,l ; save value - pop hl - ld (hl),a - call finish -pk1: - pop hl - jp qwhat -usrexec: - call parn ; ** Usr(expr) ** - push de - ex de,hl - ld hl,ue1 - push hl - ld ix,(usrptr) - jp (ix) -ue1: - ex de,hl - pop de - ret -;************************************************************* -; -; *** EXPR *** -; -; 'EXPR' EVALUATES ARITHMETICAL OR LOGICAL EXPRESSIONS. -; :: -; -; WHERE IS ONE OF THE OPERATORS IN TAB8 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 PARANTHESE. -;************************************************************* - -expr1: - ld hl,tab8-1 ; look up rel.op - jp exec ; go do it -xp11: - call xp18 ; rel.op.'>=' - ret c ; no, return hl=0 - ld l,a ; yes, return hl=1 - ret -xp12: - call xp18 ; rel.op.'#' - ret z ; no, return hl=0 - ld l,a ; yes, return hl=1 - ret -xp13: - call xp18 ; rel.op.'>' - ret z ; no - ret c ; also, no - ld l,a ; yes, return hl=1 - ret -xp14: - call xp18 ; rel.op.'<=' - ld l,a ; set hl=1 - ret z ; yes, return hl=1 - ret c - ld l,h ; else set hl=0 - ret -xp15: - call xp18 ; rel.op.'=' - ret nz ; no, return hl=0 - ld l,a ; else hl=1 - ret -xp16: - call xp18 ; rel.op.'<' - ret nc ; no, return hl=0 - ld l,a ; else hl=1 - ret -xp17: - pop hl ; not rel.op - ret ; return hl= -xp18: - ld a,c ; routine for all rel.ops - pop hl - pop bc - push hl - push bc ; reverse top of stack - ld c,a - call expr2 ; get second - ex de,hl ; value now in de - ex (sp),hl ; first in hl - call ckhlde ; compare them - pop de ; restore text pointer - ld hl,0000h ; set hl=0, a=1 - ld a,1 - ret -expr2: - call testc ; is it minus sign? - .db '-' - .db xp21-$-1 - ld hl,0000h ; yes, fake 0 - - jr xp26 ; treat like subtract -xp21: - call testc ; is it plus sign? - .db '+' - .db xp22-$-1 -xp22: - call expr3 ; first -xp23: - call testc ; addition? - .db '+' - .db xp25-$-1 - push hl ; yes, save value - call expr3 ; get second -xp24: - 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 text pointer - jp m,xp23 ; first and second sign differ - xor h ; first and second sign are equal - jp p,xp23 ; so is the result - jp qhow ; else we have overflow -xp25: - call testc ; subtract? - .db '-' - .db xp42-$-1 -xp26: - push hl ; yes, save first - call expr3 ; get second - call changesign ; negate - jr xp24 ; and add them -expr3: - call expr4 ; get first expr4 -xp31: - call testc ; multiply? - .db '*' - .db xp34-$-1 - push hl ; yes, save first and get second - call expr4 ; - ld b,0 ; clear b for sign - call checksign - ex (sp),hl ; first in hl - call checksign ; check sign of first - ex de,hl - ex (sp),hl - ld a,h ; is hl > 255? - or a - jr z,xp32 ; no - ld a,d ; yes, what about de - or d - ex de,hl - jp nz,ahow -xp32: - ld a,l - ld hl,0000h - or a - jr z,xp35 -xp33: - add hl,de - jp c,ahow - dec a - jr nz,xp33 - jr xp35 -xp34: - call testc ; divide - .db '/' - .db xp42-$-1 - push hl ; yes, save first - call expr4 ; and get the second one - ld b,0h ; clear b for sign - call checksign ; check sign of the second - ex (sp),hl ; get the first in hl - call checksign ; check sign of first - ex de,hl - ex (sp),hl - ex de,hl - ld a,d ; divide by 0? - or e - jp z,ahow ; err...how? - push bc ; else save sign - call divide - ld h,b - ld l,c - pop bc ; retrieve sign -xp35: - pop de ; and text pointer - ld a,h ; hl must be positive - or a - jp m,qhow ; else it's overflow - ld a,b - or a - call m,changesign ; change sign if needed - jp xp31 ; look for more terms -expr4: - ld hl,tab4-1 ; find function in tab4 - jp exec ; and execute it -xp40: - call testvar - jr c,xp41 ; nor a variable - ld a,(hl) - inc hl - ld h,(hl) ; value in hl - ld l,a - ret -xp41: - call testnum ; or is it a number - ld a,b ; number of digits - or a - ret nz ; ok - -parn: - call testc - .db '(' - .db xp43-$-1 - call expr ; "(expr)" - call testc - .db ')' - .db xp43-$-1 -xp42: - ret -xp43: - jp qwhat ; what? -rnd: - call parn ; ** Rnd(expr) ** - ld a,h ; expression must be positive - or a - jp m,qhow - or l ; and non-zero - jp z,qhow - push de ; save de and hl - push hl - ld hl,(rndptr) ; get memory as random number - ld de,LST_ROM - call comp - jr c,ra1 ; wrap around if last - ld hl,start -ra1: - ld e,(hl) - inc hl - ld d,(hl) - ld (rndptr),hl - pop hl - ex de,hl - push bc - call divide ; rnd(n)=mod(m,n)+1 - pop bc - pop de - inc hl - ret -abs: - call parn ; ** Abs (expr) ** - dec de - call checksign - inc de - ret -size: - ld hl,(textunfilled) ; ** Size ** - push de ; get the number of free bytes between - ex de,hl ; and varbegin - ld hl,varbegin - call subde - pop de - ret -clrvars: - ld hl,(textunfilled) ; ** ClearVars** - push de ; get the number of bytes available - ex de,hl ; for variable storge - ld hl,varend - call subde - ld b,h ; and save in bc - ld c,l - ld hl,(textunfilled) ; clear the first byte - ld d,h - ld e,l - inc de - ld (hl),0h - ldir ; and repeat for all the others - pop de - ret - -;************************************************************* -; -; *** DIVIDE *** SUBDE *** CHKSGN *** CHGSGN *** & CKHLDE *** -; -; 'DIVIDE' DIVIDES HL BY DE, RESULT IN BC, REMAINDER IN HL -; -; 'SUBDE' SUBSTRACTS DE FROM HL -; -; 'CHKSGN' CHECKS SIGN OF HL. IF +, NO CHANGE. IF -, CHANGE -; SIGN AND FLIP SIGN OF B. -; -; 'CHGSGN' CHECKS SIGN N 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. -;************************************************************* -divide: - push hl ; ** Divide ** - ld l,h ; divide h by de - ld h,0h - call dv1 - ld b,c ; save result in b - ld a,l ; (remainder + l) / de - pop hl - ld h,a -dv1: - ld c,0ffh ; result in c -dv2: - inc c ; dumb routine - call subde ; divide using subtract and count - jr nc,dv2 - add hl,de - ret -subde: - ld a,l ; ** subde ** - sub e ; subtract de from hl - ld l,a - ld a,h - sbc a,d - ld h,a - ret - -checksign: - ld a,h ; ** CheckSign ** - or a ; check sign of hl - ret p -changesign: - ld a,h ; ** ChangeSign ** - or l ; check if hl is zero - jp nz,cs1 ; no, try to change sign - ret ; yes, return -cs1: - ld a,h ; change sign of hl - push af - cpl - ld h,a - ld a,l - cpl - ld l,a - inc hl - pop af - xor h - jp p,qhow - ld a,b ; and also flip b - xor 80h - ld b,a - ret -ckhlde: - ld a,h ; same sign? - xor d ; yes, compare - jp p,ck1 ; no, exchange and compare - ex de,hl -ck1: - call comp - ret - -;************************************************************* -; -; *** SETVAL *** FIN *** ENDCHK *** & ERROR (& FRIENDS) *** -; -; "SETVAL" EXPECTS A VARIABLE, FOLLOWED BY AN EQUAL SIGN AND -; THEN AN EXPR. IT EVALUATES THE EXPR. AND SET 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 CONTINUE FROM THERE. -; -; "ENDCHK" CHECKS IF A COMMAND IS ENDED WITH CR. THIS IS -; REQUIRED IN CERTAIN COMMANDS. (GOTO, RETURN, AND STOP ETC.) -; -; "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 POINTER IN STACK AND GET MESSAGE "WHAT?" -; 'AWHAT' JUST GET MESSAGE "WHAT?" AND JUMP TO 'ERROR'. -; 'QSORRY' AND 'ASORRY' DO SAME KIND OF THING. -; 'AHOW' AND 'AHOW' IN THE ZERO PAGE SECTION ALSO DO THIS. -;************************************************************* -setval: - call testvar ; ** SetVal ** - jp c,qwhat ; no variable - push hl ; save address of var - call testc ; do we have =? - .db '=' - .db sv1-$-1 - call expr ; evaluate expression - ld b,h ; value is in bc now - ld c,l - pop hl ; get address - ld (hl),c ; save value - inc hl - ld (hl),b - ret -sv1: - jp qwhat -fin: - call testc ; test for ':' - .db ':' - .db fi1 - $ - 1 - pop af ; yes, purge return address - jp runsml ; continue on same line -fi1: - call testc ; not ':', is it cr - .db cr - .db fi2 - $ - 1 - pop af ; yes, purge return address - jp runnxl ; run next line -fi2: - ret ; else return to caller -endchk: - call skipspace ; ** EndChk ** - cp cr ; ends with cr? - ret z ; ok, otherwise say 'what?' -qwhat: - push de ; ** QWhat ** -awhat: - ld de,what ; ** AWhat ** -handleerror: - sub a ; ** Error ** - call printstr ; print error message - pop de - ld a,(de) ; save the character - push af ; at where old de points - sub a ; and put a 0 (zero) there - ld (de),a - ld hl,(current) ; get the current line number - push hl - ld a,(hl) ; check the value - inc hl - or (hl) - pop de - jp z,rstart ; if zero, just rerstart - ld a,(hl) ; if negative - or a - jp m,inputerror ; then redo input - call printline ; else print the line - dec de ; up to where the 0 is - pop af ; restore the character - ld (de),a - ld a,'?' ; print a ? - call outc - sub a ; and the rest of the line - call printstr - jp rstart -qsorry: - push de ; ** Sorry ** -asorry: - ld de,sorry - jr handleerror - -;************************************************************* -; -; *** GETLN *** FNDLN (& FRIENDS) *** -; -; 'GETLN' READS A INPUT LINE INTO 'BUFFER'. IT FIRST PROMPT -; THE CHARACTER IN A (GIVEN BY THE CALLER), THEN IT FILLS -; THE BUFFER AND ECHOS. IT IGNORES LF'S AND NULLS, BUT STILL -; ECHOS THEM BACK. RUB-OUT IS USED TO CAUSE IT TO DELETE -; THE LAST CHARACTER (IF THERE IS ONE), AND ALT-MOD IS USED TO -; CAUSE IT TO DELETE THE WHOLE LINE AND START IT ALL OVER. -; CR SIGNALS THE END OF A LINE, AND CAUSE 'GETLN' TO RETURN. -; -; '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. -; 'FNDLNP' WILL START WITH DE AND SEARCH FOR THE LINE #. -; 'FNDNXT' WILL BUMP DE BY 2, FIND A CR AND THEN START SEARCH. -; 'FNDSKP' USE DE TO FIND A CR, AND THEN START SEARCH. -;************************************************************* -getline: - call outc ; ** GetLine ** - ld de,buffer ; prompt and initalise pointer -gl1: - call chkio ; check keyboard - jr z,gl1 ; no input, so wait - cp bs ; erase last character? - jr z,gl3 ; yes - call outc ; echo character - cp lf ; ignore lf - jr z,gl1 - or a ; ignore null - jr z,gl1 - cp ctrlu ; erase the whole line? - jr z,gl4 ; yes - ld (de),a ; save the input - inc de ; and increment pointer - cp cr ; was it cr? - ret z ; yes, end of line - ld a,e ; any free space left? - cp bufend & 0ffh - jr nz,gl1 ; yes, get next char -gl3: - ld a,e ; delete last character - cp buffer & 0ffh ; if there are any? - jr z,gl4 ; no, redo whole line - dec de ; yes, back pointer - ld a,08h ; and echo a backspace - call outc - jr gl1 ; and get next character -gl4: - call crlf ; redo entire line - ld a,'>' - jr getline -findline: - ld a,h ; ** FindLine ** - or a ; check the sign of hl - jp m,qhow ; it cannot be negative - ld de,textbegin ; initialise the text pointer -findlineptr: -fl1: - push hl ; save line number - ld hl,(textunfilled) ; check if we passed end - dec hl - call comp - pop hl ; retrieve line number - ret c ; c,nz passed end - ld a,(de) ; we didn't; get first byte - sub l ; is this the line? - ld b,a ; compare low order - inc de - ld a,(de) ; get second byte - sbc a,h ; compare high order - jr c,fl2 ; no, not there yet - dec de ; else we either found it - or b ; or it's not there - ret ; nc,z:found; nc,nz:no -findnext: - inc de ; find next line -fl2: - inc de ; just passed first and second byte -findskip: - ld a,(de) ; ** FindSkip ** - cp cr ; try to find cr - jr nz,fl2 ; keep looking - inc de ; found cr, skip over - jr fl1 ; check if end of text - -;************************************************************* -; -; *** PRTSTG *** QTSTG *** PRTNUM *** & PRTLN *** -; -; 'PRTSTG' PRINTS A STRING POINTED BY DE. IT STOPS PRINTING -; AND RETURNS TO CALLER WHEN EITHER A CR IS PRINTED OR WHEN -; THE NEXT BYTE IS THE SAME AS WHAT WAS IN A (GIVEN BY THE -; CALLER). OLD A IS STORED IN B, OLD B IS LOST. -; -; 'QTSTG' LOOKS FOR A BACK-ARROW, SINGLE QUOTE, OR DOUBLE -; QUOTE. IF NONE OF THESE, RETURN TO CALLER. IF BACK-ARROW, -; OUTPUT A CR WITHOUT A LF. IF SINGLE OR DOUBLE QUOTE, PRINT -; THE STRING IN THE QUOTE AND DEMANDS A MATCHING UNQUOTE. -; AFTER THE PRINTING THE NEXT 3 BYTES OF THE CALLER IS SKIPPED -; OVER (USUALLY A JUMP INSTRUCTION. -; -; 'PRTNUM' PRINTS THE NUMBER IN 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 COUNTED IN, POSITIVE SIGN IS NOT. -; -; 'PRTLN' PRINTS A SAVED TEXT LINE WITH LINE # AND ALL. -;************************************************************* -printstr: - ld b,a -ps1: - ld a,(de) ; get a character - inc de ; bump pointer - cp b ; same as old A? - ret z ; yes, return - call outc ; no, show character - cp cr ; was it a cr? - jr nz,ps1 ; no, next character - ret ; yes, returns -qtstg: - call testc ; ** Qtstg ** - .db 22h ; is it a double quote - .db qt3-$-1 - ld a,22h -qt1: - call printstr ; print until another - cp cr - pop hl - jp z,runnxl -qt2: - inc hl ; skip 3 bytes on return - inc hl - inc hl - jp (hl) ; return -qt3: - call testc ; is it a single quote - .db 27h - .db qt4-$-1 - ld a,27h - jr qt1 -qt4: - call testc ; is it back-arrow - .db '_' - .db qt5-$-1 - ld a,8dh ; yes, cr without lf - call outc - call outc - pop hl ; return address - jr qt2 -qt5: - ret ; none of the above - -printnum: - ld b,0h ; ** PrintNum ** - call checksign ; check sign - jp p,pn1 ; no sign - ld b,'-' - dec c -pn1: - push de ; save - ld de, 000ah ; decimal - push de ; save as flag - dec c ; c=spaces - push bc ; save sign & space -pn2: - call divide ; divide hl by 10 - ld a,b ; result 0? - or c - jr z,pn3 ; yes, we got all - ex (sp),hl ; no, save remainder - dec l ; and count space - push hl ; hl is old bc - ld h,b ; moved result to bc - ld l,c - jr pn2 ; and divide by 10 -pn3: - pop bc ; we got all digits -pn4: - dec c - ld a,c ; look at space count - or a - jp m,pn5 ; no leading spaces - ld a,' ' ; print a leading space - call outc - jr pn4 ; any more? -pn5: - ld a,b ; print sign - or a - call nz,outc - ld e,l ; last remainder in e -pn6: - ld a,e ; check digit in e - cp lf ; lf is flag for no more - pop de - ret z ; if yes, return - add a,30h ; else convert to ascii - call outc ; and print the digit - jr pn6 ; next digit -printhex: - ld c,h ; ** PrintHex ** - call ph1 ; first hex byte -printhex8: - ld c,l ; then second -ph1: - ld a,c ; get left nibble into position - rra - rra - rra - rra - call ph2 ; and turn into hex digit - ld a,c ; then convert right nibble -ph2: - and 0fh ; mask right nibble - add a,90h ; and convert to ascii character - daa - adc a,40h - daa - call outc ; print character - ret -printline: - ld a,(de) ; ** PrintLine ** - ld l,a ; low order line number - inc de - ld a,(de) ; high order - ld h,a - inc de - ld c,04h ; print 4 digit line number - call printnum - ld a,' ' ; followed by a space - call outc - sub a ; and the the rest - call printstr - ret - -;************************************************************* -; -; *** MVUP *** MVDOWN *** POPA *** & 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 ** - ret z ; de = hl, return - ld a,(de) ; get one byte - ld (bc),a ; then copy it - inc de ; increase both pointers - inc bc - jr mvup ; until done -mvdown: - ld a,b ; ** mvdown ** - sub d ; check if de = bc - jp 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 both pointers - ld a,(de) ; and then do it - ld (hl),a - jr mvdown ; loop back -popa: - pop bc ; bc = return address - pop hl ; restore loopvar - ld (loopvar),hl - ld a,h - or l - jr z,pp1 ; all done, so return - pop hl - ld (loopinc),hl - pop hl - ld (looplmt),hl - pop hl - ld (loopln),hl - pop hl - ld (loopptr),hl -pp1: - push bc ; bc = return address - ret -pusha: - ld hl,stacklimit ; ** PushA ** - call changesign - pop bc ; bc = return address - add hl,sp ; is stack near the top? - jp nc,qsorry ; yes, sorry - ld hl,(loopvar) ; else save loop variables - ld a,h - or l - jr z,pu1 ; only when loopvar not 0 - ld hl,(loopptr) - push hl - ld hl,(loopln) - push hl - ld hl,(looplmt) - push hl - ld hl,(loopinc) - push hl - ld hl,(loopvar) -pu1: - push hl - push bc ; bc = return address - ret - -testvar: - call skipspace ; ** testvar ** - sub '@' ; test variables - ret c ; not a variable - jr nz,tv1 ; not @ array - inc de ; is is the @ array - call parn ; @ should be followed by (expr) - add hl,hl ; as its index - jr c,qhow ; is index too big? - push de ; will it override text? - ex de,hl - call size ; find the size of free - call comp - jp c,asorry ; yes, sorry - ld hl,varbegin ; no, get address of @(expr) and - call subde ; put it in hl - pop de - ret -tv1: - cp 1bh ; not @, is it A to Z - ccf - ret c - inc de ; if A trhough Z - ld hl,varbegin ; calculate address of that variable - rlca ; and return it in hl - add a,l ; with the c flag cleared - ld l,a - ld a,0 - adc a,h - ld h,a - ret - -testnum: - ld hl,0000h ; ** TestNum ** - ld b,h ; test if the text is a number - call skipspace -tn1: - cp '0' ; if not,return 0 in b and hl - ret c - cp ':' ; if a digit, convert to binary in - ret nc ; b and hl - ld a,0f0h ; set b to number of digits - and h ; if h>255, there is no room for - jr nz,qhow ; next digit - inc b ; b counts number of digits - push bc - ld b,h ; hl=10*hl+(new digit) - ld c,l - add hl,hl ; where 10* is done by shift and add - add hl,hl - add hl,bc - add hl,hl - ld a,(de) ; and (digit) is by stripping the - inc de ; ascii code - and 0fh - add a,l - ld l,a - ld a,0 - adc a,h - ld h,a - pop bc - ld a,(de) - jp p,tn1 -qhow: - push de ; ** Error How? ** -ahow: - ld de,how - jp handleerror - -msg1 .db "TASTY BASIC",cr -msg2 .db " BYTES FREE",cr -how .db "HOW?",cr -ok .db "OK",cr -what .db "WHAT?",cr -sorry .db "SORRY",cr - -;************************************************************* -; -; *** MAIN *** -; -; THIS IS THE MAIN LOOP THAT COLLECTS THE TINY BASIC PROGRAM -; AND STORES IT IN THE MEMORY. -; -; AT START, IT PRINTS OUT "(CR)OK(CR)", AND INITIALIZES THE -; STACK AND SOME OTHER INTERNAL VARIABLES. THEN IT PROMPTS -; ">" AND READS A LINE. IF THE LINE STARTS WITH A NON-ZERO -; NUMBER, THIS NUMBER IS THE LINE NUMBER. THE LINE NUMBER -; (IN 16 BIT BINARY) AND THE REST OF THE LINE (INCLUDING CR) -; IS STORED IN THE MEMORY. IF A LINE WITH THE SAME LINE -; NUMBER IS ALREADY THERE, IT IS REPLACED BY THE NEW ONE. IF -; THE REST OF THE LINE CONSISTS OF A CR ONLY, IT IS NOT STORED -; AND ANY EXISTING LINE WITH THE SAME LINE NUMBER IS DELETED. -; -; AFTER A LINE IS INSERTED, REPLACED, OR DELETED, THE PROGRAM -; LOOPS BACK AND ASKS FOR ANOTHER LINE. THIS LOOP WILL BE -; TERMINATED WHEN IT READS A LINE WITH ZERO OR NO LINE -; NUMBER; AND CONTROL IS TRANSFERED TO "DIRECT". -; -; TINY BASIC PROGRAM SAVE AREA STARTS AT THE MEMORY LOCATION -; LABELED "TXTBGN" AND ENDS AT "TXTEND". WE ALWAYS FILL THIS -; AREA STARTING AT "TXTBGN", THE UNFILLED PORTION IS POINTED -; BY THE CONTENT OF A MEMORY LOCATION LABELED "TXTUNF". -; -; THE MEMORY LOCATION "CURRNT" POINTS TO THE LINE NUMBER -; THAT IS CURRENTLY BEING INTERPRETED. WHILE WE ARE IN -; THIS LOOP OR WHILE WE ARE INTERPRETING A DIRECT COMMAND -; (SEE NEXT SECTION). "CURRNT" SHOULD POINT TO A 0. -;************************************************************* -rstart: - ld sp,stack -st1: - call crlf - sub a ; a=0 - ld de,ok ; print ok - call printstr - ld hl,st2 + 1 ; literal zero - ld (current),hl ; reset current line pointer -st2: - ld hl,0000h - ld (loopvar),hl - ld (stkgos),hl -st3: - ld a,'>' ; initialise prompt - call getline - push de ; de points to end of line - ld de,buffer ; point de to beginning of line - call testnum ; check if it is a number - call skipspace - ld a,h ; hl = value of the number, or - or l ; 0 if no number was found - pop bc ; bc points to end of line - jp z,direct - dec de ; back up de and save the value of - ld a,h ; the value of the line number there - ld (de),a - dec de - ld a,l - ld (de),a - push bc ; bc,de point to begin,end - push de - ld a,c - sub e - - push af ; a = number of bytes in line - call findline ; find this line in save area - push de ; de points to save area - jr nz,st4 ; nz: line not found - push de ; z: found, delete it - call findnext ; find next line - ; de -> next line - pop bc ; bc -> line to be deleted - ld hl,(textunfilled) ; hl -> unfilled text area - call mvup ; move up to delete - ld h,b ; txtunf -> unfilled area - ld l,c - ld (textunfilled),hl -st4: - pop bc ; get ready to insert - ld hl,(textunfilled) ; but first check if the length - pop af ; of new line is 3 (line# and cr) - push hl - cp 3h ; if so, do not insert - jr z,rstart ; must clear the stack - add a,l ; calculate new txtunf - ld l,a - ld a,0 - adc a,h - ld h,a ; hl -> new unfilled area - ld de,textend ; check to see if there is space - call comp - jp nc,qsorry ; no, sorry - ld (textunfilled),hl ; ok, update textunfilled - pop de ; de -> old unfilled area - call mvdown - pop de ; de,hl -> begin,end - pop hl - call mvup ; copy new line to save area - jr st3 - -;************************************************************* -; -; WHAT FOLLOWS IS THE CODE TO EXECUTE DIRECT AND STATEMENT -; COMMANDS. CONTROL IS TRANSFERED TO THESE POINTS VIA THE -; COMMAND TABLE LOOKUP CODE OF 'DIRECT' AND 'EXEC' IN LAST -; SECTION. AFTER THE COMMAND IS EXECUTED, CONTROL IS -; TRANSFERED TO OTHERS SECTIONS AS FOLLOWS: -; -; FOR 'LIST', 'NEW', AND 'STOP': GO BACK TO 'RSTART' -; FOR 'RUN': GO EXECUTE THE FIRST STORED LINE IF ANY, ELSE -; GO BACK TO 'RSTART'. -; FOR 'GOTO' AND 'GOSUB': GO EXECUTE THE TARGET LINE. -; FOR 'RETURN' AND 'NEXT': GO BACK TO SAVED RETURN LINE. -; FOR ALL OTHERS: IF 'CURRENT' -> 0, GO TO 'RSTART', ELSE -; GO EXECUTE NEXT COMMAND. (THIS IS DONE IN 'FINISH'.) -;************************************************************* -; -; *** NEW *** CLEAR *** STOP *** RUN (& FRIENDS) *** GOTO *** -; -; 'NEW(CR)' SETS 'TXTUNF' TO POINT TO 'TXTBGN' -; -; 'CLEAR(CR)' CLEARS ALL VARIABLES -; -; 'END(CR)' GOES BACK TO 'RSTART' -; -; 'RUN(CR)' FINDS THE FIRST STORED LINE, STORE ITS ADDRESS (IN -; 'CURRENT'), AND START EXECUTE IT. NOTE THAT ONLY THOSE -; COMMANDS IN TAB2 ARE LEGAL FOR STORED PROGRAM. -; -; THERE ARE 3 MORE ENTRIES IN 'RUN': -; 'RUNNXL' FINDS NEXT LINE, STORES ITS ADDR. AND EXECUTES IT. -; 'RUNTSL' STORES THE ADDRESS OF THIS LINE AND EXECUTES IT. -; 'RUNSML' CONTINUES THE EXECUTION ON SAME LINE. -; -; 'GOTO EXPR(CR)' EVALUATES THE EXPRESSION, FIND THE TARGET -; LINE, AND JUMP TO 'RUNTSL' TO DO IT. -;************************************************************* -#ifndef ZEMU -bye: - call endchk ; ** Reboot ** - ;LD A,BID_BOOT ; BOOT BANK - ;LD HL,0 ; ADDRESS ZERO - ;CALL HB_BNKCALL ; DOES NOT RETURN - LD B,BF_SYSRESET ; SYSTEM RESTART - LD C,BF_SYSRES_WARM ; WARM START - CALL $FFF0 ; CALL HBIOS - HALT -#endif -new: - call endchk ; ** New ** - ld hl,textbegin - ld (textunfilled),hl -clear: - call clrvars ; ** Clear ** - jp rstart -endd: - call endchk ; ** End ** - jp rstart -run: - call endchk ; ** Run ** - ld de,textbegin -runnxl: - ld hl,0h ; ** Run Next Line ** - call findlineptr - jp c,rstart -runtsl: - ex de,hl ; ** Run Tsl - ld (current),hl ; set current -> line # - ex de,hl - inc de - inc de -runsml: - call chkio ; ** Run Same Line ** - ld hl, tab2-1 ; find the command in table 2 - jp exec ; and execute it -goto: - call expr - push de ; save for error routine - call endchk ; must find a cr - call findline ; find the target line - jp nz, ahow ; no such line # - pop af ; clear the pushed de - jr runtsl ; go do it - -;************************************************************* -; -; *** LIST *** & PRINT *** -; -; LIST HAS TWO FORMS: -; 'LIST(CR)' LISTS ALL SAVED LINES -; 'LIST #(CR)' START LIST AT THIS LINE # -; YOU CAN STOP THE LISTING BY CONTROL C KEY -; -; PRINT COMMAND IS 'PRINT ....;' OR 'PRINT ....(CR)' -; WHERE '....' IS A LIST OF EXPRESIONS, FORMATS, BACK- -; ARROWS, AND STRINGS. THESE ITEMS ARE SEPERATED BY COMMAS. -; -; A FORMAT IS A POUND SIGN FOLLOWED BY A NUMBER. IT CONTROLS -; THE NUMBER OF SPACES THE VALUE OF A EXPRESION IS GOING TO -; BE PRINTED. IT STAYS EFFECTIVE FOR THE REST OF THE PRINT -; COMMAND UNLESS CHANGED BY ANOTHER FORMAT. IF NO FORMAT IS -; SPECIFIED, 6 POSITIONS WILL BE USED. -; -; A STRING IS QUOTED IN A PAIR OF SINGLE QUOTES OR A PAIR OF -; DOUBLE QUOTES. -; -; A BACK-ARROW MEANS GENERATE A (CR) WITHOUT (LF) -; -; A (CRLF) IS GENERATED AFTER THE ENTIRE LIST HAS BEEN -; PRINTED OR IF THE LIST IS A NULL LIST. HOWEVER IF THE LIST -; ENDED WITH A COMMA, NO (CRLF) IS GENERATED. -;************************************************************* -list: - call testnum ; check if there is a number - call endchk ; if no number we get a 0 - call findline ; find this or next line -ls1: - jp c,rstart - call printline ; print the line - call chkio ; stop on ctrl-c - call findlineptr ; find the next line - jr ls1 ; and loop back - -print: - ld c,6 ; c = number of spaces - call testc ; is it a semicolon? - .db ':' - .db pr2-$-1 - call crlf - jr runsml -pr2: - call testc ; is it a cr? - .db cr - .db pr0-$-1 - call crlf - jr runnxl -pr0: - call testc ; is it format? - .db '#' - .db pr1-$-1 - call expr - ld c,l - jr pr3 -pr1: - call testc ; is it a dollar? - .db '$' - .db pr4-$-1 - call expr - ld c,l - call testc ; do we have a comma? - .db ',' - .db pr6-$-1 - push bc - call expr - pop bc - ld a,8 ; 8 bits? - cp c - jp nz,pr9 ; no, try 16 - call printhex8 ; yes, print a single hex byte - jp pr3 -pr9: - ld a,10h ; 16 bits? - cp c - jp nz,qhow ; no, show error message - call printhex ; yes, print two hex bytes - jp pr3 -pr4: - call qtstg ; is it a string? - jr pr8 -pr3: - call testc ; is it a comma? - .db ',' - .db pr6-$-1 - call fin - jr pr0 -pr6: - call crlf ; list ends - call finish -pr8: - call expr ; evaluate the expression - push bc - call printnum - pop bc - jr pr3 - -;************************************************************* -; -; *** GOSUB *** & RETURN *** -; -; 'GOSUB EXPR;' OR 'GOSUB EXPR (CR)' IS LIKE THE 'GOTO' -; COMMAND, EXCEPT THAT THE CURRENT TEXT POINTER, STACK POINTER -; ETC. ARE SAVE 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 THE CODE), -; BUT WE STILL SAVE IT AS A FLAG FOR NO FURTHER 'RETURN'S. -; -; 'RETURN(CR)' UNDOS EVERYTHING THAT 'GOSUB' DID, AND THUS -; RETURN THE EXECUTION TO THE COMMAND AFTER THE MOST RECENT -; 'GOSUB'. IF 'STKGOS' IS ZERO, IT INDICATES THAT WE -; NEVER HAD A 'GOSUB' AND IS THUS AN ERROR. -;************************************************************* -gosub: - call pusha ; ** Gosub ** - call expr ; save the current "FOR" params - push de ; and text pointer - call findline ; find the target line - jp nz,ahow ; how? because it doesn't exist - ld hl,(current) ; found it, save old 'current' - push hl - ld hl,(stkgos) ; and 'stkgos' - push hl - ld hl,0000h ; and load new ones - ld (loopvar),hl - add hl,sp - ld (stkgos),hl - jp runtsl ; and run the line -return: - call endchk ; there must be a cr - ld hl,(stkgos) ; check old stack pointer - ld a,h ; - or l - jp z,what ; what? not found - ld sp,hl ; otherwise restore it - pop hl - ld (stkgos),hl - pop hl - ld (current),hl ; and old 'current' - pop de ; and old text pointer - call popa ; and old 'FOR' params - call finish ; and we're back - -;************************************************************* -; -; *** 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 SAVE 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 SOME- -; THING IN THE SAVE AREA (THIS IS 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 NECESSARILLY 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 CHECK 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 old save area - call setval ; set the control variable - dec hl ; its address is hl - ld (loopvar),hl ; save that - ld hl,tab5-1 ; use 'exec' to find 'TO' - jp exec -fr1: - call expr ; evaluate the limit - ld (looplmt),hl ; and save it - ld hl,tab6-1 ; use 'exec' to find 'STEP' - jp exec -fr2: - call expr ; found 'STEP' - jr fr4 -fr3: - ld hl,0001h ; no 'STEP' so set to 1 -fr4: - ld (loopinc),hl ; and save that too -fr5: - ld hl,(current) ; save current line number - ld (loopln),hl - ex de,hl ; and text pointer - ld (loopptr),hl - ld bc,0ah ; dig into stack to find loopvar - ld hl,(loopvar) - ex de,hl - ld h,b - ld l,b - add hl,sp - .db 3eh -fr7: - add hl,bc - ld a,(hl) - inc hl - or (hl) - jr z,fr8 - ld a,(hl) - dec hl - cp d - jr nz,fr7 - ld a,(hl) - cp e - jr nz,fr7 - ex de,hl - ld hl,0000h - add hl,sp - ld b,h - ld c,l - ld hl,000ah - add hl,de - call mvdown - ld sp,hl -fr8: - ld hl,(loopptr) ; all done - ex de,hl - call finish -next: - call testvar ; get address of variable - jp c,qwhat ; what, no variable - ld (varnext),hl ; yes, save it -nx0: - push de ; save the text pointer - ex de,hl - ld hl,(loopvar) ; get the variable in 'FOR' - ld a,h - or l ; if 0, there never was one - jp z,awhat - call comp ; else check them - jr z,nx3 ; yes, they agree - pop de ; no, complete current loop - call popa - ld hl,(varnext) ; and pop one level - jr nx0 ; go check again -nx3: - ld e,(hl) - inc hl - ld d,(hl) ; de = value of variable - ld hl,(loopinc) - push hl - ld a,h - xor d - ld a,d - add hl,de - jp m,nx4 - xor h - jp m,nx5 -nx4: - ex de,hl - ld hl,(loopvar) - ld (hl),e - inc hl - ld (hl),d - ld hl,(looplmt) - pop af - or a - jp p,nx1 ; step > 0 - ex de,hl ; step < 0 -nx1: - call ckhlde ; compare with limit - pop de ; restore the text pointer - jr c,nx2 ; over the limit - ld hl,(loopln) ; within the limit - ld (current),hl - ld hl,(loopptr) - ex de,hl - call finish -nx5: - pop hl - pop de -nx2: - call popa ; purge this loop - call finish ; - -init: - ld hl,start ; initialise random pointer - ld (rndptr),hl - ld hl,usrfunc ; initialise user defined function - ld (usrptr),hl - ld a,0c3h - ld (usrfunc),a ; initiase default USR() behaviour - ld hl,qhow ; (i.e. HOW? error) - ld (usrfunc+1),hl - ld hl,textbegin ; initialise text area pointers - ld (textunfilled),hl - ld (ocsw),a ; enable output control switch - call clrvars ; clear variables - call crlf - ld de,msg1 ; output welcome message - call printstr - call crlf - call size ; output free size message - call printnum - ld de,msg2 - call printstr - jp rstart - -chkio: - call haschar ; check if character available - ret z ; no, return - call getchar ; get the character - push bc ; is it a lf? - ld b,a - sub lf - jr z,io1 ; yes, ignore an return - ld a,b ; no, restore a and bc - pop bc - cp ctrlo ; is it ctrl-o? - jr nz,io2 ; no, done - ld a,(ocsw) ; toggle output control switch - cpl - ld (ocsw),a - jr chkio ; get next character -io1: - ld a,0h ; clear - or a ; set the z-flag - pop bc ; restore bc - ret ; return with z set -io2: - cp 60h ; is it lower case? - jp c,io3 ; no - and 0dfh ; yes, make upper case -io3: - cp ctrlc ; is it ctrl-c? - ret nz ; no - jp rstart ; yes, restart tasty basic -crlf: - ld a,cr -outc: - push af - ld a,(ocsw) ; check output control switch - or a - jr nz,oc1 ; output is enabled - pop af ; output is disabled - ret ; so return -oc1: - pop af - call putchar - cp cr ; was it a cr? - ret nz ; no, return - ld a,lf ; send a lf - call outc - ld a,cr ; restore register - ret ; and return -putchar: -#ifdef ZEMU - call uart_tx_ready ; see if transmit is available - out (tty_data),a ; and send it - ret -uart_tx_ready: - push af -uart_tx_ready_loop: - in a,(tty_status) - bit tx_empty,a - jp z,uart_tx_ready_loop - pop af -#else - PUSH AF - PUSH BC - PUSH DE - PUSH HL - ; OUTPUT CHARACTER TO CONSOLE VIA HBIOS - LD E,A ; OUTPUT CHAR TO E - LD C,CIO_CONSOLE ; CONSOLE UNIT TO C - LD B,BF_CIOOUT ; HBIOS FUNC: OUTPUT CHAR - RST 08 ; HBIOS OUTPUTS CHARACTER - POP HL - POP DE - POP BC - POP AF -#endif - ret -haschar: -#ifdef ZEMU - in a,(tty_status) ; check if character available - bit rx_full,a -#else - PUSH BC - PUSH DE - PUSH HL - ; GET CONSOLE INPUT STATUS VIA HBIOS - LD C,CIO_CONSOLE ; CONSOLE UNIT TO C - LD B,BF_CIOIST ; HBIOS FUNC: INPUT STATUS - RST 08 ; HBIOS RETURNS STATUS IN A - POP HL - POP DE - POP BC -#endif - ret - -getchar: -#ifdef ZEMU - in a,(tty_data) ; get the character -#else - PUSH BC - PUSH DE - PUSH HL - ; INPUT CHARACTER FROM CONSOLE VIA HBIOS - LD C,CIO_CONSOLE ; CONSOLE UNIT TO C - LD B,BF_CIOIN ; HBIOS FUNC: INPUT CHAR - RST 08 ; HBIOS READS CHARACTDR - LD A,E ; MOVE CHARACTER TO A FOR RETURN - ; RESTORE REGISTERS (AF IS OUTPUT) - POP HL - POP DE - POP BC -#endif - ret - -;************************************************************* -; -; *** TABLES *** DIRECT *** & EXEC *** -; -; THIS SECTION OF THE CODE TESTS A STRING AGAINST A TABLE. -; WHEN A MATCH IS FOUND, CONTROL IS TRANSFERED TO THE SECTION -; OF CODE ACCORDING TO THE TABLE. -; -; AT 'EXEC', DE SHOULD POINT TO THE STRING AND HL SHOULD POINT -; TO THE TABLE-1. AT 'DIRECT', DE SHOULD POINT TO THE STRING. -; HL WILL BE SET UP TO POINT TO TAB1-1, WHICH IS THE TABLE OF -; ALL DIRECT AND STATEMENT COMMANDS. -; -; A '.' IN THE STRING WILL TERMINATE THE TEST AND THE PARTIAL -; MATCH WILL BE CONSIDERED AS A MATCH. E.G., 'P.', 'PR.', -; 'PRI.', 'PRIN.', OR 'PRINT' WILL ALL MATCH 'PRINT'. -; -; THE TABLE CONSISTS OF ANY NUMBER OF ITEMS. EACH ITEM -; IS A STRING OF CHARACTERS WITH BIT 7 SET TO 0 AND -; A JUMP ADDRESS STORED HI-LOW WITH BIT 7 OF THE HIGH -; BYTE SET TO 1. -; -; END OF TABLE IS AN ITEM WITH A JUMP ADDRESS ONLY. IF THE -; STRING DOES NOT MATCH ANY OF THE OTHER ITEMS, IT WILL -; MATCH THIS NULL ITEM AS DEFAULT. -;************************************************************* -tab1: ; direct commands - .db "LIST" - dwa(list) - .db "RUN" - dwa(run) - .db "NEW" - dwa(new) - .db "CLEAR" - dwa(clear) -#ifndef ZEMU - .db "BYE" - dwa(bye) -#endif -tab2: ; direct/statement - .db "NEXT" - dwa(next) - .db "LET" - dwa(let) - .db "IF" - dwa(iff) - .db "GOTO" - dwa(goto) - .db "GOSUB" - dwa(gosub) - .db "RETURN" - dwa(return) - .db "REM" - dwa(rem) - .db "FOR" - dwa(for) - .db "INPUT" - dwa(input) - .db "PRINT" - dwa(print) - .db "POKE" - dwa(poke) - .db "END" - dwa(endd) - dwa(deflt) -tab4: ; functions - .db "PEEK" - dwa(peek) - .db "RND" - dwa(rnd) - .db "ABS" - dwa(abs) - .db "USR" - dwa(usrexec) - .db "SIZE" - dwa(size) - dwa(xp40) -tab5: ; 'TO' in 'FOR' - .db "TO" - dwa(fr1) -tab6: ; 'STEP' in 'FOR' - .db "STEP" - dwa(fr2) - dwa(fr3) -tab8: ; relational operators - .db ">=" - dwa(xp11) - .db "#" - dwa(xp12) - .db ">" - dwa(xp13) - .db "=" - dwa(xp15) - .db "<=" - dwa(xp14) - .db "<" - dwa(xp16) - dwa(xp17) - -direct: - ld hl,tab1-1 ; ** Direct ** -exec: - call skipspace ; ** Exec ** - push de -ex1: - ld a,(de) - inc de - cp '.' - jr z,ex3 - inc hl - cp (hl) - jr z,ex1 - ld a,7fh - dec de - cp (hl) - jr c,ex5 -ex2: - inc hl - cp (hl) - jr nc,ex2 - inc hl - pop de - jr exec -ex3: - ld a,7fh -ex4: - inc hl - cp (hl) - jr nc,ex4 -ex5: - ld a,(hl) - inc hl - ld l,(hl) - and 7fh - ld h,a - pop af - jp (hl) - -;------------------------------------------------------------------------------- -LST_ROM: ; all the above _can_ be in rom - ; all following *must* be in ram - .org (TBC_LOC + 09feh) -usrptr: .ds 2 ; -> user defined function area - .org (TBC_LOC + 0a00h) -usrfunc .ds 2 ; start of user defined function area - .org (TBC_LOC + 0c00h) ; start of state -ocsw .ds 1 ; output control switch -current .ds 2 ; points to current line -stkgos .ds 2 ; saves sp in 'GOSUB' -varnext .ds 2 ; temp storage -stkinp .ds 2 ; save sp in 'INPUT' -loopvar .ds 2 ; 'FOR' loop save area -loopinc .ds 2 ; loop increment -looplmt .ds 2 ; loop limit -loopln .ds 2 ; loop line number -loopptr .ds 2 ; loop text pointer -rndptr .ds 2 ; random number pointer -textunfilled .ds 2 ; -> unfilled text area -textbegin .ds 2 ; start of text save area - .org (TBC_LOC + 07dffh) -textend .ds 0 ; end of text area -varbegin .ds 55 ; variable @(0) -varend .ds 0 ; end of variable area -buffer .ds 72 ; input buffer -bufend .ds 1 -stacklimit .ds 1 - .org (TBC_LOC + 07fffh) -stack .equ $ - -#ifndef ZEMU -SLACK .EQU (TBC_END - LST_ROM) - .FILL SLACK,'t' - - .ECHO "TASTYBASIC space remaining: " - .ECHO SLACK - .ECHO " bytes.\n" -#endif - .end diff --git a/Source/Images/d_cpm22.txt b/Source/Images/d_cpm22.txt index 8e4245a9..0be74e3d 100644 --- a/Source/Images/d_cpm22.txt +++ b/Source/Images/d_cpm22.txt @@ -17,6 +17,7 @@ d_cpm22/ReadMe.txt 0: ../../Binary/Apps/syscopy.com 0: ../../Binary/Apps/sysgen.com 0: ../../Binary/Apps/talk.com 0: +../../Binary/Apps/tbasic.com 0: ../../Binary/Apps/timer.com 0: ../../Binary/Apps/tune.com 0: ../../Binary/Apps/xm.com 0: diff --git a/Source/Images/d_zsdos.txt b/Source/Images/d_zsdos.txt index a691c5df..72106517 100644 --- a/Source/Images/d_zsdos.txt +++ b/Source/Images/d_zsdos.txt @@ -30,6 +30,7 @@ d_cpm22/u0/XSUB.COM 0: ../../Binary/Apps/syscopy.com 0: ../../Binary/Apps/sysgen.com 0: ../../Binary/Apps/talk.com 0: +../../Binary/Apps/tbasic.com 0: ../../Binary/Apps/timer.com 0: ../../Binary/Apps/tune.com 0: ../../Binary/Apps/xm.com 0: diff --git a/Source/Makefile b/Source/Makefile index 056fbcb8..d0dc7de1 100644 --- a/Source/Makefile +++ b/Source/Makefile @@ -6,6 +6,7 @@ SUBDIRS += Prop SUBDIRS += Apps SUBDIRS += CBIOS SUBDIRS += Forth +SUBDIRS += TastyBasic SUBDIRS += Fonts SUBDIRS += CPM22 ZCPR ZCPR-DJ ZSDOS CPM3 ZPM3 #SUBDIRS += BPBIOS diff --git a/Source/TastyBasic/Build.cmd b/Source/TastyBasic/Build.cmd new file mode 100644 index 00000000..d8138be3 --- /dev/null +++ b/Source/TastyBasic/Build.cmd @@ -0,0 +1,4 @@ +@echo off +setlocal + +pushd src && call Build || exit /b & popd diff --git a/Source/TastyBasic/Clean.cmd b/Source/TastyBasic/Clean.cmd new file mode 100644 index 00000000..ffdfa66c --- /dev/null +++ b/Source/TastyBasic/Clean.cmd @@ -0,0 +1,4 @@ +@echo off +setlocal + +pushd src && call Clean & popd diff --git a/Source/TastyBasic/LICENSE b/Source/TastyBasic/LICENSE new file mode 100644 index 00000000..3877ae0a --- /dev/null +++ b/Source/TastyBasic/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Source/TastyBasic/Makefile b/Source/TastyBasic/Makefile new file mode 100644 index 00000000..267ad065 --- /dev/null +++ b/Source/TastyBasic/Makefile @@ -0,0 +1,9 @@ +SUBDIRS := src +TARGETS := all clobber clean + +$(TARGETS): $(SUBDIRS) + +$(SUBDIRS): + $(MAKE) -C $@ $(MAKECMDGOALS) + +.PHONY: $(TARGETS) $(SUBDIRS) diff --git a/Source/TastyBasic/README.md b/Source/TastyBasic/README.md new file mode 100644 index 00000000..07e4a38b --- /dev/null +++ b/Source/TastyBasic/README.md @@ -0,0 +1,163 @@ +# Tasty Basic + +## Introduction +Tasty Basic is a basic interpreter for CP/M and RomWBW ([Warthen, 2021](##References)), based on +the Z80 port of Palo Alto Tiny Basic ([Gabbard, 2017; Rauskolb, 1976; Wang, 1976](##References)). + +## Tasty Basic Language +The Tasty Basic language is based on Palo Alto Tiny Basic, as described in the December 1976 +issue of Interface Age ([Rauskolb, 1976](##References)). As such, Tasty Basic shares many of the +same limitations as Palo Alto Basic. All numbers are integers and must be less than or +equal to 32767, and Tasty Basic supports only 26 variables denoted by letters A through Z. + +In addition to Tiny Basic's `ABS(n)`, `RND(n)` and `SIZE` functions, Tasty Basic also provides +statements and functions to read and write memory locations, and allows interaction with I/O ports. + +### Statements +Tasty Basic provides two statements to write to memory and I/O ports: + +`POKE m,n` Writes the value _n_ to address location _m_ + +`OUT m,n` Sends the value n to I/O port _m_ + +Additionally there are statements to define and read constant values: + +`DATA m[,n[,...]]` Used to store constant values in the program code. Each DATA statement can define one or more numeric constants separated by commas. `DATA` statements may appear anywhere in the program. + +`READ m` Reads the next available data value and assigns it to variable _m_, starting with the first item in the first `DATA` statement. + +`RESTORE` Resets the `READ` pointer to the first item of the data list, allowing `DATA` values to be re-read. + +#### CP/M Specific Statements +The CP/M version includes two additional statements that allow Tasty Basic programs to be saved +to, and loaded from, disk: + +`LOAD "filename"` Loads the Tasty Basic (`.TBA`) file with the given _filename_ from the current disk drive into memory. Any existing programs and variables are cleared before the program is loaded. + +`SAVE "filename"` Persists the program currently in memory in a file with the given _filename_ on the current disk drive. + +Refer to [Tasty Basic files](examples/README.md) for details of the `.TBA` file format. + +### Functions +Tasty Basic provides the following functions to read from and write to memory locations and I/O ports: + +`IN(m)` Returns the byte value read from I/O port _m_ + +`PEEK(m)` Returns the byte value of address location _m_ + +`USR(i)` Accepts a numeric expression _i_ , calls a user-defined machine language routine, and returns the resulting value. + +### User defined machine language routines +The `USR(i)` function enables interaction with user defined machine routines. +The entry point for these routines is specified using a platform specific vector +pointing to a default location as shown below. User defined code may be +placed elsewhere in memory by updating the vector values. +The value _i_ is passed to the routine in the `DE` register, which must also +contain the result on return. + +| Platform | Vector location | Default value | +| --- | --- | --- | +| CP/M | $0BFE/$0BFF | $0C00 | +| RomWBW | $13FE/$13FF | $1400 | + +### Example +The following example shows the bit summation for a given value: + +``` + 0000 #IFDEF CPM + 0C00 .ORG $0C00 ; ie. 3072 dec + 0C00~ #ELSE + 0C00~ .ORG $1400 ; ie. 5120 dec + 0C00 #ENDIF + 0C00 + 0C00 06 00 LD B,0 + 0C02 7A LD A,D + 0C03 CD 0E 0C CALL COUNT + 0C06 7B LD A,E + 0C07 CD 0E 0C CALL COUNT + 0C0A 58 LD E,B + 0C0B 16 00 LD D,0 + 0C0D C9 RET + 0C0E COUNT: + 0C0E B7 OR A + 0C0F C8 RET Z + 0C10 CB 47 BIT 0,A + 0C12 28 01 JR Z,NEXT + 0C14 04 INC B + 0C15 NEXT: + 0C15 CB 3F SRL A + 0C17 18 F5 JR COUNT + 0C19 + 0C19 .END +``` + +``` +10 REM -- CP/M VERSION +20 REM -- SEE EXAMPLES DIRECTORY FOR OTHER PLATFORMS +30 FOR I=0 TO 24 +40 READ A +50 POKE 3072+I,A +60 NEXT I +70 INPUT P +80 LET Q=USR(P) +90 PRINT "THE BIT SUMMATION OF ",#5,P," IS ",#2,Q +100 GOTO 70 +110 DATA 6,0,122,205,14,12,123,205,14,12,88,22,0,201 +120 DATA 183,200,203,71,40,1,4,203,63,24,245 +``` + +Note that the Tasty Basic program above is CP/M specific. Examples for other platforms can be found +in the `examples` directory. + +## Building Tasty Basic +Building Tasty Basic requires the `uz80as` Z80 assembler v1.12 or later ([Giner, 2021](##References)). +Alternatively, Windows users can use TASM (Telemark Assembler) ([Anderson, 1998](##References)). + +### RomWBW version +Tasty Basic is part of the SBCv2 RomWBW distribution. Please refer to the +[RomWBW github repository](https://github.com/wwarthen/RomWBW) for details. + +### CP/M version +The CP/M version of Tasty Basic can be built using the `-dCPM` flag: + +```uz80as -dCPM tastybasic.asm tbasic.com``` + +The resulting `tbasic.com` command file can be run in CP/M. For example: + +``` +B>TBASIC ↵ + +CP/M TASTY BASIC +28902 BYTES FREE + +OK +>10 PRINT "HELLO WORLD ", ↵ +>RUN ↵ +HELLO WORLD + +OK +>BYE ↵ + +B> +``` + +## Example BASIC programs + +A small number of example Tasty Basic programs are included in the `examples` directory. +Most of these programs are from _BASIC COMPUTER GAMES_ ([Ahl, 1978](##References)), and +have been modified as required to make them work with Tasty Basic. + +## License +In line with Wang's (1976) original Tiny Basic source listing and later derived works +by Rauskolb (1976) and Gabbard (2017), Tasty Basic is licensed under GPL v3. +For license details refer to the enclosed [LICENSE](../master/LICENSE) file. + +## References +Ahl, D. H. (Ed.).(1978). _BASIC COMPUTER GAMES_. New York, NY: Workman Publishing +Anderson, T. N. (1998). _The Telemark Assembler (TASM) User's Manual, Version 3.1._ Issaquah, WA: Squak Valley Software +b1ackmai1er (2018). _SBC V2_. Retrieved October 6, 2018, from [https://www.retrobrewcomputers.org/doku.php?id=boards:sbc:sbc_v2:start](https://www.retrobrewcomputers.org/doku.php?id=boards:sbc:sbc_v2:start) +Gabbard, D. (2017, October 10). _TinyBASIC for the z80 – TinyBASIC 2.0g._ Retrieved September 29, 2108, from [http://retrodepot.net/?p=274](http://retrodepot.net/?p=274) +Giner, J. (2021, August 1). _Micro Z80 assembler - uz80as._ Retrieved September 19, 2021, from [https://jorgicor.niobe.org/uz80as/](https://jorgicor.niobe.org/uz80as/) +Rauskolb, P. (1976, December). _DR. WANG'S PALO ALTO TINY BASIC._ Interface Age, (2)1, 92-108. Retrieved from [https://archive.org/stream/InterfaceAge197612/Interface%20Age%201976-12#page/n93/mode/1up](https://archive.org/stream/InterfaceAge197612/Interface%20Age%201976-12#page/n93/mode/1up) +Wang, L-C. (1976). _Palo Alto Tiny BASIC._ In J. C. Warren Jr. (Ed.), _Dr. Dobb's Journal of COMPUTER Calisthenics & Orthodontia_ (pp. 129-142). Menlo Park, CA: People's Computer Company +Warthen, W. (2021). _RomWBW, Z80/Z180 System Software._ Retrieved Octover 5, 2021, from [https://github.com/wwarthen/RomWBW](https://github.com/wwarthen/RomWBW) \ No newline at end of file diff --git a/Source/TastyBasic/examples/BATNUM.BAS b/Source/TastyBasic/examples/BATNUM.BAS new file mode 100644 index 00000000..13a3fdb8 --- /dev/null +++ b/Source/TastyBasic/examples/BATNUM.BAS @@ -0,0 +1,86 @@ +10 PRINT "BATNUM" +20 PRINT "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +30 PRINT +110 PRINT "THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE" +120 PRINT "COMPUTER IS YOUR OPPONENT." +130 PRINT +140 PRINT "THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU" +150 PRINT "AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE." +160 PRINT "WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR" +170 PRINT "NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS." +180 PRINT "DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME." +190 PRINT "ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING." +200 PRINT +210 GOTO 330 +220 FOR I=1 TO 10 +230 PRINT +240 NEXT I +330 INPUT "ENTER PILE SIZE"N +340 IF N<0 GOTO 1080 +350 IF N>=1 GOTO 390 +360 GOTO 330 +390 INPUT "ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST"M +410 IF M=1 GOTO 430 +420 IF M#2 GOTO 390 +430 INPUT "ENTER MIN PER TURN"A,"AND MAX"B +450 IF A>B GOTO 430 +460 IF A<1 GOTO 430 +490 INPUT "ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST"S +500 PRINT +510 IF S=1 GOTO 530 +520 IF S#2 GOTO 490 +530 C=A+B +540 IF S=2 GOTO 570 +550 GOSUB 600 +560 IF W=1 GOTO 220 +570 GOSUB 810 +580 IF W=1 GOTO 220 +590 GOTO 550 +600 Q=N +610 IF M=1 GOTO 630 +620 Q=Q-1 +630 IF M=1 GOTO 680 +640 IF N>A GOTO 720 +650 W=1 +660 PRINT "COMPUTER TAKES",N," AND LOSES." +670 RETURN +680 IF N>B GOTO 720 +690 W=1 +700 PRINT "COMPUTER TAKES",N," AND WINS." +710 RETURN +720 P=Q-C*(Q/C) +730 IF P>=A GOTO 750 +740 P=A +750 IF P<=B GOTO 770 +760 P=B +770 N=N-P +780 PRINT "COMPUTER TAKES",P," AND LEAVES",N +790 W=0 +800 RETURN +810 PRINT +820 INPUT "YOUR MOVE"P +830 IF P#0 GOTO 880 +840 PRINT "I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT." +850 W=1 +860 RETURN +880 IF P>=A GOTO 910 +890 IF P=N GOTO 960 +900 GOTO 920 +910 IF P<=B GOTO 940 +920 PRINT "ILLEGAL MOVE, REENTER IT!" +930 GOTO 820 +940 N=N-P +950 IF N#0 GOTO 1030 +960 IF M=1 GOTO 1000 +970 PRINT "TOUGH LUCK, YOU LOSE." +980 W=1 +990 RETURN +1000 PRINT "CONGRATULATIONS, YOU WIN." +1010 W=1 +1020 RETURN +1030 IF N>=0 GOTO 1060 +1040 N=N+P +1050 GOTO 920 +1060 W=0 +1070 RETURN +1080 END diff --git a/Source/TastyBasic/examples/DUMP.BAS b/Source/TastyBasic/examples/DUMP.BAS new file mode 100644 index 00000000..db91aca3 --- /dev/null +++ b/Source/TastyBasic/examples/DUMP.BAS @@ -0,0 +1,16 @@ +10 INPUT "ADDRESS"A +20 FOR I=0 TO 15 +30 P=A+I*16 +40 PRINT $16,P, +50 FOR J=0 TO 15 +60 PRINT " ",$8,PEEK(P+J), +70 NEXT J +80 PRINT +90 NEXT I +100 INPUT ""Q +110 IF Q<0 GOTO 160 +120 A=A+256 +130 IF Q=0 GOTO 20 +140 A=Q +150 GOTO 20 +160 END diff --git a/Source/TastyBasic/examples/README.md b/Source/TastyBasic/examples/README.md new file mode 100644 index 00000000..270e33a0 --- /dev/null +++ b/Source/TastyBasic/examples/README.md @@ -0,0 +1,25 @@ +# Tasty Basic Files + +## Introduction +The CP/M version of Tasty Basic allows programs to be saved to and loaded from disk. This +document describes the Tasty Basic `.TBA` file format. + +## .TBA File format +Tasty Basic `.TBA` files are direct reflections of Tasty Basic programs as held in memory. Thus, +each line of code starts with a 16 bit, LSB first, line number and ends with a carriage return +character (0xD). An EOF marker (0x1A) indicates the end of the file. Any trailing NUL characters +(0x0) are ignored. + +### Example +Following is an example Tasty Basic program: +``` +10 PRINT "HELLO WORLD" +20 GOTO 10 +``` +And its `.TBA` file representation: +``` +0A 00 50 52 49 4E 54 20 22 48 45 4C 4C 4F 20 57 +4F 52 4C 44 22 0D 14 00 47 4F 54 4F 20 31 30 0D +1A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + diff --git a/Source/TastyBasic/examples/REVERSE.BAS b/Source/TastyBasic/examples/REVERSE.BAS new file mode 100644 index 00000000..e8c235c7 --- /dev/null +++ b/Source/TastyBasic/examples/REVERSE.BAS @@ -0,0 +1,74 @@ +10 PRINT "REVERSE" +30 PRINT "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +100 PRINT "REVERSE -- A GAME OF SKILL" +140 REM *** N=NUMBER OF NUMBERS +150 N=9 +160 INPUT "DO YOU WANT THE RULES (0=NO,1=YES)"A +180 IF A=0 GOTO 210 +190 GOSUB 710 +200 REM *** MAKE A RANDOM LIST @(1) TO @(N) +210 @(1)=RND(N-1)+1 +220 FOR K=2 TO N +230 @(K)=RND(N) +240 FOR J=1 TO K-1 +250 IF @(K)=@(J) GOTO 230 +260 NEXT J +270 NEXT K +280 REM *** PRINT ORIGINAL LIST AND START GAME +290 PRINT +300 PRINT "HERE WE GO ... THE LIST IS:" +310 T=0 +320 GOSUB 610 +330 INPUT "HOW MANY SHALL I REVERSE"R +350 IF R=0 GOTO 520 +360 IF R<=N GOTO 390 +370 PRINT "OOPS! TOO MANY! I CAN REVERSE AT MOST ",#2,N +380 GOTO 330 +390 T=T+1 +400 REM *** REVERSE R NUMBERS AND PRINT NEW LIST +410 FOR K=1 TO R/2 +420 Z=@(K) +430 @(K)=@(R-K+1) +440 @(R-K+1)=Z +450 NEXT K +460 GOSUB 610 +470 REM *** CHECK FOR A WIN +480 FOR K=1 TO N +490 IF @(K)#K GOTO 330 +500 NEXT K +510 PRINT "YOU WON IT IN",T," MOVES!!!" +520 PRINT +530 INPUT "TRY AGAIN (1=YES, 0=NO)"A +550 IF A=1 GOTO 210 +560 PRINT +565 PRINT "O.K. HOPE YOU HAD FUN!!" +570 GOTO 999 +600 REM *** SUBROUTINE TO PRINT LIST +610 PRINT +620 FOR K=1 TO N +630 PRINT #2,@(K), +640 NEXT K +650 PRINT +660 RETURN +700 REM *** SUBROUTINE TO PRINT THE RULES +710 PRINT "THIS IS THE GAME OF 'REVERSE'. TO WIN, ALL YOU HAVE" +720 PRINT "TO DO IS ARRANGE A LIST OF NUMBERS (1 THROUGH ",#2,N,")" +730 PRINT "IN NUMERICAL ORDER FROM LEFT TO RIGHT. TO MOVE, YOU" +740 PRINT "TELL ME HOW MANY NUMBERS (COUNTING FROM THE LEFT) TO" +750 PRINT "REVERSE. FOR EXAMPLE, IF THE CURRENT LIST IS:" +755 PRINT +760 PRINT " 2 3 4 5 1 6 7 8 9" +765 PRINT +770 PRINT "AND YOU REVERSE 4, THE RESULT WILL BE:" +775 PRINT +780 PRINT " 5 4 3 2 1 6 7 8 9" +785 PRINT +790 PRINT "NOW IF YOU REVERSE 5, YOU WIN!" +795 PRINT +800 PRINT " 1 2 3 4 5 6 7 8 9" +805 PRINT +810 PRINT "NO DOUBT YOU WILL LIKE THIS GAME, BUT" +820 PRINT "IF YOU WANT TO QUIT, REVERSE 0 (ZERO)." +830 PRINT +840 RETURN +999 END diff --git a/Source/TastyBasic/examples/SBCRTC.BAS b/Source/TastyBasic/examples/SBCRTC.BAS new file mode 100644 index 00000000..ba8bc2c4 --- /dev/null +++ b/Source/TastyBasic/examples/SBCRTC.BAS @@ -0,0 +1,30 @@ +100 REM -- ADJUST USR FUNCTION POINTER +110 POKE 5118,6 +120 POKE 5119,20 +200 REM -- RTC BUFFER AT 5120-5125 +210 FOR I=0 TO 14 +220 READ A +230 POKE 5126+I,A +240 NEXT I +250 DATA 6,32,123,178,40,1,4,33,0,20,207,17,0,0,201 +300 INPUT "GET OR SET TIME (GET=0,SET=1)"P +310 IF P=0 GOTO 400 +320 A=5120:INPUT "YEAR"Q:GOSUB 500 +330 A=5121:INPUT "MONTH"Q:GOSUB 500 +340 A=5122:INPUT "DAY"Q:GOSUB 500 +350 A=5123:INPUT "HOURS"Q:GOSUB 500 +360 A=5124:INPUT "MINS"Q:GOSUB 500 +370 A=5125:INPUT "SECS"Q:GOSUB 500 +400 Q=USR(P) +410 PRINT $8,PEEK(5120),"-", +420 PRINT $8,PEEK(5121),"-", +430 PRINT $8,PEEK(5122)," ", +440 PRINT $8,PEEK(5123),":", +450 PRINT $8,PEEK(5124),":", +460 PRINT $8,PEEK(5125) +470 GOTO 300 +500 T=Q/10 +510 U=Q-10*T +520 B=T*16+U +530 POKE A,B +540 RETURN diff --git a/Source/TastyBasic/examples/TICTAC.BAS b/Source/TastyBasic/examples/TICTAC.BAS new file mode 100644 index 00000000..e6c9258a --- /dev/null +++ b/Source/TastyBasic/examples/TICTAC.BAS @@ -0,0 +1,117 @@ +2 PRINT "TIC-TAC-TOE" +4 PRINT "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +6 PRINT +8 PRINT "THE BOARD IS NUMBERED:" +10 PRINT " 1 2 3" +12 PRINT " 4 5 6" +14 PRINT " 7 8 9" +16 PRINT +20 FOR I=1 TO 9:@(I)=0:NEXT I +50 INPUT"DO YOU WANT 'X' OR 'O' (X=1,O=0)"C +55 IF C=1 GOTO 475 +60 P=0,Q=1 +100 G=-1,H=1:IF @(5)#0 GOTO 103 +102 @(5)=-1:GOTO 195 +103 IF @(5)#1 GOTO 106 +104 IF @(1)#0 GOTO 110 +105 @(1)=-1:GOTO 195 +106 IF (@(2)=1)*(@(1)=0) GOTO 181 +107 IF (@(4)=1)*(@(1)=0) GOTO 181 +108 IF (@(6)=1)*(@(9)=0) GOTO 189 +109 IF (@(8)=1)*(@(9)=0) GOTO 189 +110 IF G=1 GOTO 112 +111 GOTO 118 +112 J=3*(M-1)/3+1 +113 IF J=M LET K=1 +114 IF J+1=M LET K=2 +115 IF J+2=M LET K=3 +116 GOTO 120 +118 FOR J=1 TO 7 STEP 3:FOR K=1 TO 3 +120 IF @(J)#G GOTO 130 +122 IF @(J+2)#G GOTO 135 +126 IF @(J+1)#0 GOTO 150 +128 @(J+1)=-1:GOTO 195 +130 IF @(J)=H GOTO 150 +131 IF @(J+2)#G GOTO 150 +132 IF @(J+1)#G GOTO 150 +133 @(J)=-1:GOTO 195 +135 IF @(J+2)#0 GOTO 150 +136 IF @(J+1)#G GOTO 150 +138 @(J+2)=-1:GOTO 195 +150 IF @(K)#G GOTO 160 +152 IF @(K+6)#G GOTO 165 +156 IF @(K+3)#0 GOTO 170 +158 @(K+3)=-1:GOTO 195 +160 IF @(K)=H GOTO 170 +161 IF @(K+6)#G GOTO 170 +162 IF @(K+3)#G GOTO 170 +163 @(K)=-1:GOTO 195 +165 IF @(K+6)#0 GOTO 170 +166 IF @(K+3)#G GOTO 170 +168 @(K+6)=-1:GOTO 195 +170 GOTO 450 +171 IF (@(3)=G)*(@(7)=0) GOTO 187 +172 IF (@(9)=G)*(@(1)=0) GOTO 181 +173 IF (@(7)=G)*(@(3)=0) GOTO 183 +174 IF (@(9)=0)*(@(1)=G) GOTO 189 +175 IF G=-1 LET G=1,H=-1:GOTO 110 +176 IF (@(9)=1)*(@(3)=0) GOTO 182 +177 FOR I=2 TO 9:IF @(I)#0 GOTO 179 +178 @(I)=-1:GOTO 195 +179 NEXT I +181 @(1)=-1:GOTO 195 +182 IF @(1)=1 GOTO 177 +183 @(3)=-1:GOTO 195 +187 @(7)=-1:GOTO 195 +189 @(9)=-1 +195 PRINT"THE COMPUTER MOVES TO..." +202 GOSUB 1000 +205 GOTO 500 +450 IF G=1 GOTO 465 +455 IF (J=7)*(K=3) GOTO 465 +460 NEXT K:NEXT J +465 IF @(5)=G GOTO 171 +467 GOTO 175 +475 P=1,Q=0 +500 INPUT"WHERE DO YOU MOVE"M +502 IF M=0 PRINT"THANKS FOR THE GAME.":GOTO 2000 +503 IF M>9 GOTO 506 +505 IF @(M)=0 GOTO 510 +506 PRINT"THAT SQUARE IS OCCUPIED.":GOTO 500 +510 G=1,@(M)=1 +520 GOSUB 1000 +530 GOTO 100 +1000 FOR I=1 TO 9:PRINT" ",:IF @(I)#-1 GOTO 1014 +1011 IF Q=1 PRINT "X ", +1012 IF Q=0 PRINT "O ", +1013 GOTO 1020 +1014 IF @(I)#0 GOTO 1016 +1015 PRINT" ",:GOTO 1020 +1016 IF P=1 PRINT "X ", +1017 IF P=0 PRINT "O ", +1020 IF (I#3)*(I#6) GOTO 1050 +1030 PRINT"":PRINT"---+---+---" +1040 GOTO 1080 +1050 IF I=9 GOTO 1080 +1060 PRINT"!", +1080 NEXT I:PRINT +1095 FOR I=1 TO 7 STEP 3 +1100 IF @(I)#@(I+1) GOTO 1115 +1105 IF @(I)#@(I+2) GOTO 1115 +1110 IF @(I)=-1 GOTO 1350 +1112 IF @(I)=1 GOTO 1200 +1115 NEXT I:FOR I=1 TO 3:IF @(I)#@(I+3) GOTO 1150 +1130 IF @(I)#@(I+6) GOTO 1150 +1135 IF @(I)=-1 GOTO 1350 +1137 IF @(I)=1 GOTO 1200 +1150 NEXT I:FOR I=1 TO 9:IF @(I)=0 GOTO 1155 +1152 NEXT I:GOTO 1400 +1155 IF @(5)#G GOTO 1170 +1160 IF (@(1)=G)*(@(9)=G) GOTO 1180 +1165 IF (@(3)=G)*(@(7)=G) GOTO 1180 +1170 RETURN +1180 IF G=-1 GOTO 1350 +1200 PRINT"YOU BEAT ME!! GOOD GAME.":GOTO 2000 +1350 PRINT"I WIN, TURKEY!!!":GOTO 2000 +1400 PRINT"IT'S A DRAW. THANK YOU." +2000 END diff --git a/Source/TastyBasic/examples/bitsum.asm b/Source/TastyBasic/examples/bitsum.asm new file mode 100644 index 00000000..bcb8cfa4 --- /dev/null +++ b/Source/TastyBasic/examples/bitsum.asm @@ -0,0 +1,48 @@ +; ----------------------------------------------------------------------------- +; Copyright 2021 Dimitri Theulings +; +; This file is part of Tasty Basic. +; +; Tasty Basic is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; Tasty Basic is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with Tasty Basic. If not, see . +; ----------------------------------------------------------------------------- +; Tasty Basic is derived from earlier works by Li-Chen Wang, Peter Rauskolb, +; and Doug Gabbard. Refer to the source code repository for details +; . +; ----------------------------------------------------------------------------- + +#IFDEF CPM + .ORG $0C00 ; ie. 3072 dec +#ELSE + .ORG $1400 ; ie. 5120 dec +#ENDIF + + LD B,0 + LD A,D + CALL COUNT + LD A,E + CALL COUNT + LD E,B + LD D,0 + RET +COUNT: + OR A + RET Z + BIT 0,A + JR Z,NEXT + INC B +NEXT: + SRL A + JR COUNT + + .END \ No newline at end of file diff --git a/Source/TastyBasic/examples/bitsum.tba b/Source/TastyBasic/examples/bitsum.tba new file mode 100644 index 0000000000000000000000000000000000000000..83b3046ef7ecda68263989e5a724702030da6c4e GIT binary patch literal 384 zcmb_Wu};J=49$$>jfnwSWjTaTBx!r?zzyM=9uZBGOHy<&Gc)t~xF94x0L#OB&-Q!1 z5XpMnZt)QBJ-*nKU6|qWSIyR+jBt&qi}Ov3Py-xvIS53C8tfig*OqCn+sMPfsd|W zUc5JjKf^F$MK>))s?g;XYdXQZI0jDbBz%a$6oWgGq_ie2TT%@}?)newd*Z2NMZxFK biR#ydRjR2|)UW|H-)zSNMokM>;=k()w7Ws+ literal 0 HcmV?d00001 diff --git a/Source/TastyBasic/examples/tictac.tba b/Source/TastyBasic/examples/tictac.tba new file mode 100644 index 0000000000000000000000000000000000000000..a88534d24c2929bc1bd8db3dbdc136ce3817ef25 GIT binary patch literal 2816 zcmc&$OOG2x5Vo@>M2JENamuMZi?nvUUb<&Ieu+WrwI|-OV~=+{Uhj%4;t?JpfKbFC ziU^1Zi3)ekTrgebx;}lUW>$`;3jkC<>-=I3KUGu|H=6Kbrdc z79CbWbi@S5+dkV0J5i4XQ#PKBw*1KNwJo~II55GO&6s1+%_&r4%w?=@(Jd*|V64el z%c5HqM7QUThY<@r!Qzm$+MA`I?D6*4--}s5b}ks>s8%mz`{9huJE(4LZ;c^hEnH)z zJx{EM9xq#6YUG0BtP7fc2%=iUrMq$ykFW1|g6_@ry{cH>_Cy;_n=t<3Jb9ySdz=68J2AjtIggI?_si2dW>d+ZUuvr?ESNpmR z4yUIzSg|v(OJ@z7>9fn}9Qr({9O(2jhS%HHyzo442r|5YpEWRe%_#ssCwZ6e?|I`1 z0y8G?ykOvEf)|p5;jXju^Rn_CE>hJwy`sTNJp;RR(ZJ3kU4zrB;2l<+W!~2eZ(1-s zUpE99Uche{7`&!nz;8<4W!AThCkRZz@O<0A%LMNv2g7h?3w~G4Lk)d;PljRYarAyN z{*CPTHt2nA$5bo(f%W)6+YzShAX==CLmw{UxmmA%x?U~%NZRGTCnJqLz}}}v#l~J* z#H;mO;afypKZ1Ji`coWpV`Iaj?{Edv*%lL=bNWHuhl{UY=aYUI zVXV9Ktwc%#uYvXrJxBMh2Fv&Pq>Q1`-vRw3Jri|ICrHrGd8HAd(q#qeLYWmx@b?uR1+Bay9pvz&d&Whb zuCC~0GTXxW59y5lE~U7pz~vNwNw5^GI=IkawQiPyV;c__6t~q@f2-AMMSt)NF6eAc zPDcyi2t3_&!2#W&V~ePwSx^J6XsBPvG|PxSosb+!dH4mf*8LK~rJW^{oi*(m6Xsk; zI5_!U3L6yDLHC$*Wb!-d70I+_o{^Q)Nkg(KO=)MO>69@or#+p**|cZ>BW=s1HB*U< zE}b);sq0eQV3QT2A9bS@D@(=xRGMeUFk(p2aK99}9!emslDbWwUH zlBbf&_FdAKC*Pa4@Z!ftez6Glu=nqcJAQ*2j)2VryadZH=zgT7YY6kuJ@kW2#qocY1@8=(z!CXH8 literal 0 HcmV?d00001 diff --git a/Source/TastyBasic/src/Build.cmd b/Source/TastyBasic/src/Build.cmd new file mode 100644 index 00000000..5c0bc0ec --- /dev/null +++ b/Source/TastyBasic/src/Build.cmd @@ -0,0 +1,13 @@ +@echo off +setlocal + +set TOOLS=..\..\..\Tools + +set PATH=%TOOLS%\tasm32;%PATH% + +set TASMTABS=%TOOLS%\tasm32 + +tasm -80 -dROMWBW tastybasic.asm tastybasic.bin tastybasic.bin.lst +tasm -80 -dCPM tastybasic.asm tastybasic.com tastybasic.com.lst + +copy /b /v tastybasic.com ..\..\..\Binary\Apps\tbasic.com diff --git a/Source/TastyBasic/src/Clean.cmd b/Source/TastyBasic/src/Clean.cmd new file mode 100644 index 00000000..9d2d37a1 --- /dev/null +++ b/Source/TastyBasic/src/Clean.cmd @@ -0,0 +1,11 @@ +@echo off +setlocal + +if exist *.bin del *.bin +if exist *.lst del *.lst +if exist *.prn del *.prn +if exist *.hex del *.hex +if exist *.rel del *.rel +if exist *.sym del *.sym + +if exist ..\..\..\Binary\Apps\tbasic.com del ..\..\..\Binary\Apps\tbasic.com diff --git a/Source/TastyBasic/src/Makefile b/Source/TastyBasic/src/Makefile new file mode 100644 index 00000000..226bc02a --- /dev/null +++ b/Source/TastyBasic/src/Makefile @@ -0,0 +1,32 @@ +UNAME := $(shell uname) +VER := $(shell git describe --tags --abbrev=0) +APPDIR := $(wildcard ../../../Binary/Apps) +BINDIR := ../../../Tools/$(UNAME) + +ROMIMAGE := tastybasic.bin +ROMDEPS := tastybasic.asm romwbwio.asm +CPMCMD := tastybasic.com +CPMDEPS := tastybasic.asm cpmio.asm +CPMAPP := $(APPDIR)/tbasic.com +CPMIMAGE := tastybasic.img + +export PATH := $(BINDIR):${PATH} + +all: $(ROMIMAGE) $(CPMCMD) | $(APPDIR) + +clean clobber: + @rm -f *.lst *.img *.com *.bin $(CPMAPP) +$(ROMIMAGE): $(ROMDEPS) + @uz80as -dROMWBW -d"VERSION \"$(VER)\"" tastybasic.asm tastybasic.bin tastybasic.bin.lst + +$(CPMIMAGE): $(CPMCMD) + @mkfs.cpm -f wbw_fd144 tastybasic.img + @cpmcp -f wbw_fd144 tastybasic.img tastybasic.com 0:tbasic.com + +$(CPMCMD): $(CPMDEPS) + @uz80as -dCPM -d"VERSION \"$(VER)\"" tastybasic.asm tastybasic.com tastybasic.com.lst + +$(APPDIR): $(CPMCMD) + @cat $(CPMCMD) > $(CPMAPP) + +.PHONY: clean clobber $(APPDIR) diff --git a/Source/TastyBasic/src/cpmio.asm b/Source/TastyBasic/src/cpmio.asm new file mode 100644 index 00000000..fdef0bef --- /dev/null +++ b/Source/TastyBasic/src/cpmio.asm @@ -0,0 +1,207 @@ +; ----------------------------------------------------------------------------- +; Copyright 2021 Dimitri Theulings +; +; This file is part of Tasty Basic. +; +; Tasty Basic is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; Tasty Basic is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with Tasty Basic. If not, see . +; ----------------------------------------------------------------------------- +; Tasty Basic is derived from earlier works by Li-Chen Wang, Peter Rauskolb, +; and Doug Gabbard. Refer to the source code repository for details +; . +; ----------------------------------------------------------------------------- + +USRPTR_OFFSET .equ 0afeh +INTERNAL_OFFSET .equ 0c00h +TEXTEND_OFFSET .equ 07cffh +STACK_OFFSET .equ 07effh + +BDOS .equ 05h ; standard cp/m entry +DCONIO .equ 06h ; direct console I/O +INPREQ .equ 0ffh ; console input request +TERMCPM .equ 0 +OPENF .equ 0fh ; file open +CLOSEF .equ 10h ; file close +DELETEF .equ 13h ; file delete +READF .equ 14h ; read file record +WRITEF .equ 15h ; write file record +MAKEF .equ 16h ; make new file +SETDMA .equ 1ah ; set DMA address +EOF .equ 1ah ; EOF marker +DMAOFF .equ 1ah ; set DMA address pointer +FCB .equ 5ch ; file control block address +DMA .equ 80h ; disk buffer address +BUFSIZE .equ 80h ; disk buffer size + +; FILE CONTROL BLOCK DEFINITIONS +FCBDN .equ FCB+0 ; disk name +FCBFN .equ FCB+1 ; file name +FCBFT .equ FCB+9 ; disk file type (3 chars) +FCBRL .equ FCB+12 ; file's current reel number +FCBRC .equ FCB+15 ; file's record count (0 to 128) +FCBCR .equ FCB+32 ; current (next) record +FCBLN .equ FCB+33 ; FCB length +FTYPE .db "TBA" ; tasty basic file type + +haschar: + push bc + push de + ld c,DCONIO ; direct console i/o + ld e,INPREQ ; input request + call BDOS ; any chr typed? + pop de ; if yes, (a)<--char + pop bc ; else (a)<--00h (ignore chr) + or a + ret +; +putchar: + push bc + push de + push af + push hl + ld c,DCONIO ; direct console i/o + ld e,a ; output char (a) + call BDOS + pop hl + pop af + pop de + pop bc + ret +load: + ld hl,textbegin ; ** load ** + ld (textunfilled),hl ; clear program text area + call clrvars ; and variables + call fname ; get filename + call fopen ; and open file for reading + ld de,DMA + ld c,SETDMA ; point dma to default + call BDOS + +lo1: + ld de,FCB ; and read record + ld c,READF + call BDOS + or a ; are we at EOF? + jr nz,lo3 ; yes, all done + ld b,BUFSIZE ; no, copy from io buffer + ld de,DMA ; to text buffer + ld hl,(textunfilled) +lo2: + ld a,(de) ; get char from buffer + cp 1ah ; is it EOF? + jr z,lo3 ; yes, all done + ld (hl),a ; copy char to text area + inc hl ; and update pointers + inc de + ld (textunfilled),hl + dec b ; end of record? + jr z,lo1 ; yes, so try next record + jr lo2 ; no, copy next char +lo3: + jp rstart +save: + call fname ; ** save ** + ld de,textbegin ; check there is a program + ld hl,(textunfilled) ; in memory + sbc hl,de + jr nz,sa1 ; yes, try to save it + jp qhow ; no, nothing to be done +sa1: + call fdel ; remove any existing file + call fmake ; open new file for writing + ld de,textbegin ; initialise text ptr +sa2: + push de ; save current text ptr + ld hl,(textunfilled) + ld (hl),EOF ; set EOF marker + sbc hl,de ; are we done? + jr c,sa4 + ld c,SETDMA ; point dma to text + call BDOS + ld de,FCB ; write record + ld c,WRITEF + call BDOS + or a ; all good? + jr z,sa3 ; yes, try next + jp qsorry ; no, something bad happened +sa3: + pop hl ; update text ptr + ld de,BUFSIZE + add hl,de + ex de,hl + jr sa2 +sa4: + call fclose ; and close file jp rstart + jp rstart +fname: + call testc ; check filename + .db 22h ; is first char a double quote + .db fn4-$-1 ; no, so fail + ld hl,FCBFN ; start configuring fcb + ld b,22h + ld c,8 ; max filename length +fn1: + ld a,(de) + inc de ; bump pointer + cp b ; double quote? + jr z,fn2 + ld (hl),a ; copy into fcb + inc hl + dec c ; check filename length + jp z,qhow ; too long + jr fn1 +fn2: + call endchk + ld a,20h ; clear any remaining chars + ld (hl),a ; in filename + inc hl + dec c + jr nz,fn2 + ld b,3 ; set file type + ld hl,FTYPE + ld de,FCBFT +fn3: + ld a,(hl) + ld (de),a + inc hl + inc de + dec b + jr nz,fn3 + xor a + ld (FCBCR),a ; clear current record + ret +fn4: + jp qwhat +fopen: + ld de,FCB ; open file + ld c,OPENF + jr fexec +fclose: + ld de,FCB ; close file + ld c,CLOSEF + jr fexec +fmake: + ld de,FCB ; create new file + ld c,MAKEF +fexec: + call BDOS + inc a ; did operation fail? + ret nz ; no, all good + jp qhow ; something bad happened +fdel: + ld de,FCB ; delete file + ld c,DELETEF + jp BDOS ; ignore any errors +bye: + ld c,TERMCPM ; does not return! + jp BDOS \ No newline at end of file diff --git a/Source/TastyBasic/src/romwbwio.asm b/Source/TastyBasic/src/romwbwio.asm new file mode 100644 index 00000000..0f4a5cbd --- /dev/null +++ b/Source/TastyBasic/src/romwbwio.asm @@ -0,0 +1,100 @@ + +; ----------------------------------------------------------------------------- +; Copyright 2021 Dimitri Theulings +; +; This file is part of Tasty Basic. +; +; Tasty Basic is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; Tasty Basic is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with Tasty Basic. If not, see . +; ----------------------------------------------------------------------------- +; Tasty Basic is derived from earlier works by Li-Chen Wang, Peter Rauskolb, +; and Doug Gabbard. Refer to the source code repository for details +; . +; ----------------------------------------------------------------------------- + +CIODEV_CONSOLE .equ 0d0h +BF_CIOIN .equ 00h ; character input +BF_CIOOUT .equ 01h ; character output +BF_CIOIST .equ 02h ; character input status +BF_SYSRESET .equ 0f0h ; restart system +BF_SYSRES_WARM .equ 01h ; warm start + +;************************************************************* +; +; THE FOLLOWING NEED MUST BE SYNCED WITH STD.ASM SO ROMLDR +; KNOWS WHERE THIS EXECUTES AT +; +;************************************************************* +; +#ifndef PLATFORM +TBC_LOC .equ $0a00 +#endif +TBC_SIZ .equ $0a00 +TBC_END .equ TBC_LOC + TBC_SIZ +; +;************************************************************* + +USRPTR_OFFSET .equ 09feh +INTERNAL_OFFSET .equ 0c00h +TEXTEND_OFFSET .equ 07dffh +STACK_OFFSET .equ 07fffh + +bye: + call endchk ; ** Reboot ** + ld b,BF_SYSRESET ; system restart + ld c,BF_SYSRES_WARM ; warm start + jp 0fff0h ; does not return! +putchar: + push af + push bc + push de + push hl + ; output character to console via hbios + ld e,a ; output char to e + ld c,CIODEV_CONSOLE ; console unit to c + ld b,BF_CIOOUT ; hbios func: output char + rst 08h ; hbios outputs character + + pop hl + pop de + pop bc + pop af + ret +haschar: + push bc + push de + push hl + ; get console input status via hbios + ld c,CIODEV_CONSOLE ; console unit to c + ld b,BF_CIOIST ; hbios func: input status + rst 08h ; hbios returns status in a + + pop hl + pop de + pop bc + ret + +getchar: + push bc + push de + push hl + ; input character from console via hbios + ld c,CIODEV_CONSOLE ; console unit to c + ld b,BF_CIOIN ; hbios func: input char + rst 08h ; hbios reads charactdr + ld a,e ; move character to a for return + ; restore registers (af is output) + pop hl + pop de + pop bc + ret diff --git a/Source/TastyBasic/src/tastybasic.asm b/Source/TastyBasic/src/tastybasic.asm new file mode 100644 index 00000000..e564c3c8 --- /dev/null +++ b/Source/TastyBasic/src/tastybasic.asm @@ -0,0 +1,2017 @@ + +; ----------------------------------------------------------------------------- +; Copyright 2018 Dimitri Theulings +; +; This file is part of Tasty Basic. +; +; Tasty Basic is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; Tasty Basic is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with Tasty Basic. If not, see . +; ----------------------------------------------------------------------------- +; Tasty Basic is derived from earlier works by Li-Chen Wang, Peter Rauskolb, +; and Doug Gabbard. Refer to the source code repository for details +; . +; ----------------------------------------------------------------------------- + +#define dwa(addr) .db (addr >> 8) + 080h\ .db addr & 0ffh + +ctrlc .equ 03h +bs .equ 08h +lf .equ 0ah +cr .equ 0dh +ctrlo .equ 0fh +ctrlu .equ 15h + +#ifdef CPM +#define PLATFORM "CP/M" +TBC_LOC .equ 0100h +#endif + +#ifdef ROMWBW +#define PLATFORM "ROMWBW" +TBC_LOC .equ 0a00h +#endif + +#ifndef PLATFORM +TBC_LOC .equ 0 +#endif + + .org TBC_LOC +start: + ld sp,stack ; ** Cold Start ** + ld a,0ffh + jp init +testc: + ex (sp),hl ; ** TestC ** + call skipspace ; ignore spaces + cp (hl) ; test character + inc hl ; compare the byte that follows the + jr z,tc1 ; call instruction with the text pointer + push bc + ld c,(hl) ; if not equal, ad the seond byte + ld b, 0h ; that follows the call to the old pc + add hl,bc + pop bc + dec de +tc1: + inc de ; if equal, skip those bytes + inc hl ; and continue + ex (sp),hl + ret + +skipspace: + ld a,(de) ; ** SkipSpace ** + cp ' ' ; ignore spaces + ret nz ; in text (where de points) + inc de ; and return the first non-blank + jp skipspace ; character in A + +expr: + call expr2 ; ** Expr ** + push hl ; evaluate expression + jp expr1 + +comp: + ld a,h ; ** Compare ** + cp d ; compare hl with de + ret nz ; return c and z flags + ld a,l ; old a is lost + cp e + ret + +finish: + pop af ; ** Finish ** + call fin ; check end of command + jp qwhat + +;************************************************************* +; +; ** REM ** IF ** INPUT ** & LET (& DEFLT) ** DATA ** READ ** +; +; 'REM' CAN BE FOLLOWED BY ANYTHING AND IS IGNORED BY TBI. +; TBI TREATS IT LIKE AN 'IF' WITH A FALSE CONDITION. +; +; 'IF' IS FOLLOWED BY AN EXPR. AS A CONDITION AND ONE OR MORE +; COMMANDS (INCLUDING OTHER 'IF'S) SEPERATED 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 FOLLOWS 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 SINGLE OR +; DOUBLE QUOTES, OR IS A BACK-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 SET 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'. +; +; 'LET' IS FOLLOWED BY A LIST OF ITEMS SEPERATED BY COMMAS. +; EACH ITEM CONSISTS OF A VARIABLE, AN EQUAL SIGN, AND AN EXPR. +; TBI EVALUATES THE EXPR. AND SET THE VARIABLE TO THAT VALUE. +; TBI WILL ALSO HANDLE 'LET' COMMAND WITHOUT THE WORD 'LET'. +; THIS IS DONE BY 'DEFLT'. +; +; 'DATA' ALLOWS CONSTANT VALUES TO BE STORED IN CODE. TREATED +; AS A REMARK ('REM') WHEN PROGRAM IS EXECUTED. +; +; 'READ' ASSIGNS THE NEXT AVAILABLE DATA VALUE TO A VARIABLE. +;************************************************************* +rem: +data: + ld hl,0 ; ** Rem ** Data ** + jr if1 ; this is like 'IF 0' +iff: + call expr ; ** If ** +if1: + ld a,h ; is the expr = 0? + or l + jp nz,runsml ; no, continue + call findskip ; yes, skip rest of line + jp nc,runtsl ; and run the next line + jp rstart ; if no, restart +inputerror: + ld hl,(stkinp) ; ** InputError ** + ld sp,hl ; restore old sp and old current + pop hl + ld (current),hl + pop de ; and old text pointer + pop de ; redo current +input: + push de ; ** Input ** + call qtstg ; is next item a string? + jp ip2 ; no + call testvar ; yes and followed by a variable? + jp c,ip4 ; no + jp ip3 ; yes, input variable +ip2: + push de ; save for printstr + call testvar ; must be variable + jp c,qwhat ; no, what? + ld a,(de) ; prepare for printstr + ld c,a + sub a + ld (de),a + pop de + call printstr ; print string as prompt + ld a,c ; restore text + dec de + ld (de),a +ip3: + push de ; save text pointer + ex de,hl + ld hl,(current) ; also save current + push hl + ld hl,input + ld (current),hl + ld hl,0 + add hl,sp + ld (stkinp),hl + push de + ld a,':' + call getline + ld de,buffer + call expr + nop + nop + nop + pop de + ex de,hl + ld (hl),e + inc hl + ld (hl),d + pop hl + ld (current),hl + pop de +ip4: + pop af ; purge stack + call testc ; is next character ','? + .db ',' + .db ip5-$-1 + jr input ; yes, more items +ip5: + call finish +deflt: + ld a,(de) ; ** DEFLT ** + cp cr ; empty line is fine + jr z,lt1 ; else it's 'LET' +let: + call setval ; ** Let ** + call testc ; set value to var + .db ',' + .db lt1-$-1 + jr let ; item by item +lt1: + call finish +restore: call rstreadptr + call finish +rstreadptr: + ld hl,0 + ld (readptr),hl + ret +read: + push de ; ** Read ** + ld hl,(readptr) ; has read pointer been initialised? + ld a,h + or a + jr nz,rd2 ; yes, find next data value + call findline ; no, find first line + jr nc,rd1 ; found first line + pop de ; nothing found, so how? + jp qhow +rd1: + call finddata + jr rd4 +rd2: + ex de,hl + call skipspace ; skip over spaces + call testc ; have we hit a comma? + .db ',' + .db rd3-$-1 + jr rd5 +rd3: + call nextdata +rd4: + jr z,rd5 ; found a data statement + pop de + jp qhow ; nothing found, so how to read? + +rd5: + ld (readptr),de ; update read pointer + pop de + call testvar + jp c,qwhat ; no variable + push hl ; save address of variable + push de ; and text pointer + ld de,(readptr) ; point to next data value + call parsenum ; parse the constant + jr nc, rd6 + pop de ; spmething bad happened when + jp qhow ; parsing the number +rd6: + ld (readptr),de ; update read pointer + pop de ; and restore text pointer + ld b,h ; move value to bc + ld c,l + pop hl ; get address of variable + ld (hl),c ; assign value + inc hl + ld (hl),b + + call testc ; do we have more variables? + .db ',' + .db rd7-$-1 + jr read ; yes, read next +rd7: + call finish ; all done +finddata: + inc de ; skip over line no. + inc de + call skipspace + ld hl,datastmt + ld b,4 +fd1: + ld a,(de) + cp (hl) + jp nz,nextdata ; not what we're looking for + dec b ; are we done comparing + jr z,fd2 ; yes + inc de + inc hl + jr fd1 +fd2: + inc de ; first char past statement + ret ; nc,z:found; nc,nz:no + +nextdata: + ld hl,0 + call findskip ; find the next line + jr nc,finddata ; and try there + or 1 ; no more lines + ret ; nc,nz: not found! + +;************************************************************* +; +; *** PEEK *** POKE *** IN *** & OUT *** +; +; 'PEEK()' RETURNS THE VALUE OF THE BYTE AT THE GIVEN +; ADDRESS. +; 'POKE ,' SETS BYTE AT ADDRESS TO +; VALUE +; 'IN(,' WRITES VALUE TO PORT . +; +;************************************************************* +peek: + call parn ; ** Peek(expr) ** + ld a,h ; expression must be positive + or a + jp m,qhow + ld a,(hl) ; peek address + ld h,0 + ld l,a + ret +inp: + call parn ; ** In(expr) ** + ld a,0 ; is port > 255? + cp h + jp nz,qhow ; yes, so not a valid port + ld c,l + in l,(c) ; read port + ld h,0 + ret +poke: + call expr ; ** Poke ** + ld a,h ; address must be positive + or a + jp m,qhow + push hl + call testc ; is next char a comma? + .db ',' + .db ot1-$-1 ; what, no? + call expr ; get value to store + ld a,0 ; is it > 255? + cp h + jp z,pk1 ; no, all good + pop hl + jp qhow +pk1: + ld a,l ; save value + pop hl + ld (hl),a + call finish +outp: + call expr ; ** Out ** + ld a,0 ; is port > 255? + cp h + jp nz,qhow ; yes, so not a valid port + push hl + call testc ; is next char a comma? + .db ',' + .db ot1-$-1 ; what, no? + call expr ; get value to write + ld a,0 ; is it > 255? + cp h + jp z,ot2 ; no, all good + pop hl + jp qhow +ot2: + ld a,l ; output value + pop hl + ld c,l + out (c),a + call finish +ot1: + pop hl + jp qwhat +usrexec: + call parn ; ** Usr(expr) ** + push de + ex de,hl + ld hl,ue1 + push hl + ld ix,(usrptr) + jp (ix) +ue1: + ex de,hl + pop de + ret +;************************************************************* +; +; *** EXPR *** +; +; 'EXPR' EVALUATES ARITHMETICAL OR LOGICAL EXPRESSIONS. +; :: +; +; WHERE IS ONE OF THE OPERATORS IN TAB8 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 PARANTHESE. +;************************************************************* + +expr1: + ld hl,tab8-1 ; look up rel.op + jp exec ; go do it +xp11: + call xp18 ; rel.op.'>=' + ret c ; no, return hl=0 + ld l,a ; yes, return hl=1 + ret +xp12: + call xp18 ; rel.op.'#' + ret z ; no, return hl=0 + ld l,a ; yes, return hl=1 + ret +xp13: + call xp18 ; rel.op.'>' + ret z ; no + ret c ; also, no + ld l,a ; yes, return hl=1 + ret +xp14: + call xp18 ; rel.op.'<=' + ld l,a ; set hl=1 + ret z ; yes, return hl=1 + ret c + ld l,h ; else set hl=0 + ret +xp15: + call xp18 ; rel.op.'=' + ret nz ; no, return hl=0 + ld l,a ; else hl=1 + ret +xp16: + call xp18 ; rel.op.'<' + ret nc ; no, return hl=0 + ld l,a ; else hl=1 + ret +xp17: + pop hl ; not rel.op + ret ; return hl= +xp18: + ld a,c ; routine for all rel.ops + pop hl + pop bc + push hl + push bc ; reverse top of stack + ld c,a + call expr2 ; get second + ex de,hl ; value now in de + ex (sp),hl ; first in hl + call ckhlde ; compare them + pop de ; restore text pointer + ld hl,0 ; set hl=0, a=1 + ld a,1 + ret +expr2: + call testc ; is it minus sign? + .db '-' + .db xp21-$-1 + ld hl,0 ; yes, fake 0 - + jr xp26 ; treat like subtract +xp21: + call testc ; is it plus sign? + .db '+' + .db xp22-$-1 +xp22: + call expr3 ; first +xp23: + call testc ; addition? + .db '+' + .db xp25-$-1 + push hl ; yes, save value + call expr3 ; get second +xp24: + 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 text pointer + jp m,xp23 ; first and second sign differ + xor h ; first and second sign are equal + jp p,xp23 ; so is the result + jp qhow ; else we have overflow +xp25: + call testc ; subtract? + .db '-' + .db xp42-$-1 +xp26: + push hl ; yes, save first + call expr3 ; get second + call changesign ; negate + jr xp24 ; and add them +expr3: + call expr4 ; get first expr4 +xp31: + call testc ; multiply? + .db '*' + .db xp34-$-1 + push hl ; yes, save first and get second + call expr4 ; + ld b,0 ; clear b for sign + call checksign + ex (sp),hl ; first in hl + call checksign ; check sign of first + ex de,hl + ex (sp),hl + ld a,h ; is hl > 255? + or a + jr z,xp32 ; no + ld a,d ; yes, what about de + or d + ex de,hl + jp nz,ahow +xp32: + ld a,l + ld hl,0 + or a + jr z,xp35 +xp33: + add hl,de + jp c,ahow + dec a + jr nz,xp33 + jr xp35 +xp34: + call testc ; divide + .db '/' + .db xp42-$-1 + push hl ; yes, save first + call expr4 ; and get the second one + ld b,0h ; clear b for sign + call checksign ; check sign of the second + ex (sp),hl ; get the first in hl + call checksign ; check sign of first + ex de,hl + ex (sp),hl + ex de,hl + ld a,d ; divide by 0? + or e + jp z,ahow ; err...how? + push bc ; else save sign + call divide + ld h,b + ld l,c + pop bc ; retrieve sign +xp35: + pop de ; and text pointer + ld a,h ; hl must be positive + or a + jp m,qhow ; else it's overflow + ld a,b + or a + call m,changesign ; change sign if needed + jp xp31 ; look for more terms +expr4: + ld hl,tab4-1 ; find function in tab4 + jp exec ; and execute it +xp40: + call testvar + jr c,xp41 ; nor a variable + ld a,(hl) + inc hl + ld h,(hl) ; value in hl + ld l,a + ret +xp41: + call testnum ; or is it a number + ld a,b ; number of digits + or a + ret nz ; ok + +parn: + call testc + .db '(' + .db xp43-$-1 + call expr ; "(expr)" + call testc + .db ')' + .db xp43-$-1 +xp42: + ret +xp43: + jp qwhat ; what? +rnd: + call parn ; ** Rnd(expr) ** + ld a,h ; expression must be positive + or a + jp m,qhow + or l ; and non-zero + jp z,qhow + push de ; save de and hl + push hl + ld hl,(rndptr) ; get memory as random number + ld de,LST_ROM + call comp + jr c,ra1 ; wrap around if last + ld hl,start +ra1: + ld e,(hl) + inc hl + ld d,(hl) + ld (rndptr),hl + pop hl + ex de,hl + push bc + call divide ; rnd(n)=mod(m,n)+1 + pop bc + pop de + inc hl + ret +abs: + call parn ; ** Abs (expr) ** + dec de + call checksign + inc de + ret +size: + ld hl,(textunfilled) ; ** Size ** + push de ; get the number of free bytes between + ex de,hl ; and varbegin + ld hl,varbegin + call subde + pop de + ret +clrvars: + ld hl,(textunfilled) ; ** ClearVars** + push de ; get the number of bytes available + ex de,hl ; for variable storge + ld hl,varend + call subde + ld b,h ; and save in bc + ld c,l + ld hl,(textunfilled) ; clear the first byte + ld d,h + ld e,l + inc de + ld (hl),0h + ldir ; and repeat for all the others + pop de + ret + +;************************************************************* +; +; *** DIVIDE *** SUBDE *** CHKSGN *** CHGSGN *** & CKHLDE *** +; +; 'DIVIDE' DIVIDES HL BY DE, RESULT IN BC, REMAINDER IN HL +; +; 'SUBDE' SUBSTRACTS DE FROM HL +; +; 'CHKSGN' CHECKS SIGN OF HL. IF +, NO CHANGE. IF -, CHANGE +; SIGN AND FLIP SIGN OF B. +; +; 'CHGSGN' CHECKS SIGN N 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. +;************************************************************* +divide: + push hl ; ** Divide ** + ld l,h ; divide h by de + ld h,0h + call dv1 + ld b,c ; save result in b + ld a,l ; (remainder + l) / de + pop hl + ld h,a +dv1: + ld c,0ffh ; result in c +dv2: + inc c ; dumb routine + call subde ; divide using subtract and count + jr nc,dv2 + add hl,de + ret +subde: + ld a,l ; ** subde ** + sub e ; subtract de from hl + ld l,a + ld a,h + sbc a,d + ld h,a + ret + +checksign: + ld a,h ; ** CheckSign ** + or a ; check sign of hl + ret p +changesign: + ld a,h ; ** ChangeSign ** + or l ; check if hl is zero + jp nz,cs1 ; no, try to change sign + ret ; yes, return +cs1: + ld a,h ; change sign of hl + push af + cpl + ld h,a + ld a,l + cpl + ld l,a + inc hl + pop af + xor h + jp p,qhow + ld a,b ; and also flip b + xor 80h + ld b,a + ret +ckhlde: + ld a,h ; same sign? + xor d ; yes, compare + jp p,ck1 ; no, exchange and compare + ex de,hl +ck1: + call comp + ret + +;************************************************************* +; +; *** SETVAL *** FIN *** ENDCHK *** & ERROR (& FRIENDS) *** +; +; "SETVAL" EXPECTS A VARIABLE, FOLLOWED BY AN EQUAL SIGN AND +; THEN AN EXPR. IT EVALUATES THE EXPR. AND SET 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 CONTINUE FROM THERE. +; +; "ENDCHK" CHECKS IF A COMMAND IS ENDED WITH CR. THIS IS +; REQUIRED IN CERTAIN COMMANDS. (GOTO, RETURN, AND STOP ETC.) +; +; "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 POINTER IN STACK AND GET MESSAGE "WHAT?" +; 'AWHAT' JUST GET MESSAGE "WHAT?" AND JUMP TO 'ERROR'. +; 'QSORRY' AND 'ASORRY' DO SAME KIND OF THING. +; 'AHOW' AND 'AHOW' IN THE ZERO PAGE SECTION ALSO DO THIS. +;************************************************************* +setval: + call testvar ; ** SetVal ** + jp c,qwhat ; no variable + push hl ; save address of var + call testc ; do we have =? + .db '=' + .db sv1-$-1 + call expr ; evaluate expression + ld b,h ; value is in bc now + ld c,l + pop hl ; get address + ld (hl),c ; save value + inc hl + ld (hl),b + ret +sv1: + jp qwhat +fin: + call testc ; test for ':' + .db ':' + .db fi1 - $ - 1 + pop af ; yes, purge return address + jp runsml ; continue on same line +fi1: + call testc ; not ':', is it cr + .db cr + .db fi2 - $ - 1 + pop af ; yes, purge return address + jp runnxl ; run next line +fi2: + ret ; else return to caller +endchk: + call skipspace ; ** EndChk ** + cp cr ; ends with cr? + ret z ; ok, otherwise say 'what?' +qwhat: + push de ; ** QWhat ** +awhat: + ld de,what ; ** AWhat ** +handleerror: + sub a ; ** Error ** + call printstr ; print error message + pop de + ld a,(de) ; save the character + push af ; at where old de points + sub a ; and put a 0 (zero) there + ld (de),a + ld hl,(current) ; get the current line number + push hl + ld a,(hl) ; check the value + inc hl + or (hl) + pop de + jp z,rstart ; if zero, just rerstart + ld a,(hl) ; if negative + or a + jp m,inputerror ; then redo input + call printline ; else print the line + dec de ; up to where the 0 is + pop af ; restore the character + ld (de),a + ld a,'?' ; print a ? + call outc + sub a ; and the rest of the line + call printstr + jp rstart +qsorry: + push de ; ** Sorry ** +asorry: + ld de,sorry + jr handleerror + +;************************************************************* +; +; *** GETLN *** FNDLN (& FRIENDS) *** +; +; 'GETLN' READS A INPUT LINE INTO 'BUFFER'. IT FIRST PROMPT +; THE CHARACTER IN A (GIVEN BY THE CALLER), THEN IT FILLS +; THE BUFFER AND ECHOS. IT IGNORES LF'S AND NULLS, BUT STILL +; ECHOS THEM BACK. RUB-OUT IS USED TO CAUSE IT TO DELETE +; THE LAST CHARACTER (IF THERE IS ONE), AND ALT-MOD IS USED TO +; CAUSE IT TO DELETE THE WHOLE LINE AND START IT ALL OVER. +; CR SIGNALS THE END OF A LINE, AND CAUSE 'GETLN' TO RETURN. +; +; '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. +; 'FNDLNP' WILL START WITH DE AND SEARCH FOR THE LINE #. +; 'FNDNXT' WILL BUMP DE BY 2, FIND A CR AND THEN START SEARCH. +; 'FNDSKP' USE DE TO FIND A CR, AND THEN START SEARCH. +;************************************************************* +getline: + call outc ; ** GetLine ** + ld de,buffer ; prompt and initalise pointer +gl1: + call chkio ; check keyboard + jr z,gl1 ; no input, so wait + cp bs ; erase last character? + jr z,gl3 ; yes + call outc ; echo character + cp lf ; ignore lf + jr z,gl1 + or a ; ignore null + jr z,gl1 + cp ctrlu ; erase the whole line? + jr z,gl4 ; yes + ld (de),a ; save the input + inc de ; and increment pointer + cp cr ; was it cr? + ret z ; yes, end of line + ld a,e ; any free space left? + cp bufend & 0ffh + jr nz,gl1 ; yes, get next char +gl3: + ld a,e ; delete last character + cp buffer & 0ffh ; if there are any? + jr z,gl4 ; no, redo whole line + dec de ; yes, back pointer + ld a,08h ; and echo a backspace + call outc + jr gl1 ; and get next character +gl4: + call crlf ; redo entire line + ld a,'>' + jr getline +findline: + ld a,h ; ** FindLine ** + or a ; check the sign of hl + jp m,qhow ; it cannot be negative + ld de,textbegin ; initialise the text pointer +findlineptr: +fl1: + push hl ; save line number + ld hl,(textunfilled) ; check if we passed end + dec hl + call comp + pop hl ; retrieve line number + ret c ; c,nz passed end + ld a,(de) ; we didn't; get first byte + sub l ; is this the line? + ld b,a ; compare low order + inc de + ld a,(de) ; get second byte + sbc a,h ; compare high order + jr c,fl2 ; no, not there yet + dec de ; else we either found it + or b ; or it's not there + ret ; nc,z:found; nc,nz:no +findnext: + inc de ; find next line +fl2: + inc de ; just passed first and second byte +findskip: + ld a,(de) ; ** FindSkip ** + cp cr ; try to find cr + jr nz,fl2 ; keep looking + inc de ; found cr, skip over + jr fl1 ; check if end of text + +;************************************************************* +; +; *** PRTSTG *** QTSTG *** PRTNUM *** & PRTLN *** +; +; 'PRTSTG' PRINTS A STRING POINTED BY DE. IT STOPS PRINTING +; AND RETURNS TO CALLER WHEN EITHER A CR IS PRINTED OR WHEN +; THE NEXT BYTE IS THE SAME AS WHAT WAS IN A (GIVEN BY THE +; CALLER). OLD A IS STORED IN B, OLD B IS LOST. +; +; 'QTSTG' LOOKS FOR A BACK-ARROW, SINGLE QUOTE, OR DOUBLE +; QUOTE. IF NONE OF THESE, RETURN TO CALLER. IF BACK-ARROW, +; OUTPUT A CR WITHOUT A LF. IF SINGLE OR DOUBLE QUOTE, PRINT +; THE STRING IN THE QUOTE AND DEMANDS A MATCHING UNQUOTE. +; AFTER THE PRINTING THE NEXT 3 BYTES OF THE CALLER IS SKIPPED +; OVER (USUALLY A JUMP INSTRUCTION. +; +; 'PRTNUM' PRINTS THE NUMBER IN 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 COUNTED IN, POSITIVE SIGN IS NOT. +; +; 'PRTLN' PRINTS A SAVED TEXT LINE WITH LINE # AND ALL. +;************************************************************* +printstr: + ld b,a +ps1: + ld a,(de) ; get a character + inc de ; bump pointer + cp b ; same as old A? + ret z ; yes, return + call outc ; no, show character + cp cr ; was it a cr? + jr nz,ps1 ; no, next character + ret ; yes, returns +qtstg: + call testc ; ** Qtstg ** + .db 22h ; is it a double quote + .db qt3-$-1 + ld a,22h +qt1: + call printstr ; print until another + cp cr + pop hl + jp z,runnxl +qt2: + inc hl ; skip 3 bytes on return + inc hl + inc hl + jp (hl) ; return +qt3: + call testc ; is it a single quote + .db 27h + .db qt4-$-1 + ld a,27h + jr qt1 +qt4: + call testc ; is it back-arrow + .db '_' + .db qt5-$-1 + ld a,8dh ; yes, cr without lf + call outc + call outc + pop hl ; return address + jr qt2 +qt5: + ret ; none of the above + +printnum: + ld b,0h ; ** PrintNum ** + call checksign ; check sign + jp p,pn1 ; no sign + ld b,'-' + dec c +pn1: + push de ; save + ld de, 000ah ; decimal + push de ; save as flag + dec c ; c=spaces + push bc ; save sign & space +pn2: + call divide ; divide hl by 10 + ld a,b ; result 0? + or c + jr z,pn3 ; yes, we got all + ex (sp),hl ; no, save remainder + dec l ; and count space + push hl ; hl is old bc + ld h,b ; moved result to bc + ld l,c + jr pn2 ; and divide by 10 +pn3: + pop bc ; we got all digits +pn4: + dec c + ld a,c ; look at space count + or a + jp m,pn5 ; no leading spaces + ld a,' ' ; print a leading space + call outc + jr pn4 ; any more? +pn5: + ld a,b ; print sign + or a + call nz,outc + ld e,l ; last remainder in e +pn6: + ld a,e ; check digit in e + cp lf ; lf is flag for no more + pop de + ret z ; if yes, return + add a,30h ; else convert to ascii + call outc ; and print the digit + jr pn6 ; next digit +printhex: + ld c,h ; ** PrintHex ** + call ph1 ; first hex byte +printhex8: + ld c,l ; then second +ph1: + ld a,c ; get left nibble into position + rra + rra + rra + rra + call ph2 ; and turn into hex digit + ld a,c ; then convert right nibble +ph2: + and 0fh ; mask right nibble + add a,90h ; and convert to ascii character + daa + adc a,40h + daa + call outc ; print character + ret +printline: + ld a,(de) ; ** PrintLine ** + ld l,a ; low order line number + inc de + ld a,(de) ; high order + ld h,a + inc de + ld c,04h ; print 4 digit line number + call printnum + ld a,' ' ; followed by a space + call outc + sub a ; and the the rest + call printstr + ret + +;************************************************************* +; +; *** MVUP *** MVDOWN *** POPA *** & 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 ** + ret z ; de = hl, return + ld a,(de) ; get one byte + ld (bc),a ; then copy it + inc de ; increase both pointers + inc bc + jr mvup ; until done +mvdown: + ld a,b ; ** mvdown ** + sub d ; check if de = bc + jp 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 both pointers + ld a,(de) ; and then do it + ld (hl),a + jr mvdown ; loop back +popa: + pop bc ; bc = return address + pop hl ; restore loopvar + ld (loopvar),hl + ld a,h + or l + jr z,pp1 ; all done, so return + pop hl + ld (loopinc),hl + pop hl + ld (looplmt),hl + pop hl + ld (loopln),hl + pop hl + ld (loopptr),hl +pp1: + push bc ; bc = return address + ret +pusha: + ld hl,stacklimit ; ** PushA ** + call changesign + pop bc ; bc = return address + add hl,sp ; is stack near the top? + jp nc,qsorry ; yes, sorry + ld hl,(loopvar) ; else save loop variables + ld a,h + or l + jr z,pu1 ; only when loopvar not 0 + ld hl,(loopptr) + push hl + ld hl,(loopln) + push hl + ld hl,(looplmt) + push hl + ld hl,(loopinc) + push hl + ld hl,(loopvar) +pu1: + push hl + push bc ; bc = return address + ret + +testvar: + call skipspace ; ** testvar ** + sub '@' ; test variables + ret c ; not a variable + jr nz,tv1 ; not @ array + inc de ; is is the @ array + call parn ; @ should be followed by (expr) + add hl,hl ; as its index + jr c,qhow ; is index too big? + push de ; will it override text? + ex de,hl + call size ; find the size of free + call comp + jp c,asorry ; yes, sorry + ld hl,varbegin ; no, get address of @(expr) and + call subde ; put it in hl + pop de + ret +tv1: + cp 1bh ; not @, is it A to Z + ccf + ret c + inc de ; if A through Z + ld hl,varbegin ; calculate address of that variable + rlca ; and return it in hl + add a,l ; with the c flag cleared + ld l,a + ld a,0 + adc a,h + ld h,a + ret + +testnum: + call parsenum ; ** TestNum ** + ret nc ; if not a number, return nc and 0 in b and hl + jr qhow ; carry set, so overflowed +parsenum: + ld hl,0 ; try to parse text as a number + ld b,h ; if not a number, return 0 in b and hl + call skipspace +tn1: + cp '0' + jr nc,tn2 + ccf ; reset carry + ret +tn2: + cp ':' ; if a digit, convert to binary in + ret nc ; b and hl + ld a,0f0h ; set b to number of digits + and h ; if h>255, there is no room for + jr z,tn3 ; next digit, so set carry + scf + ret +tn3: + inc b ; b counts number of digits + push bc + ld b,h ; hl=10*hl+(new digit) + ld c,l + add hl,hl ; where 10* is done by shift and add + add hl,hl + add hl,bc + add hl,hl + ld a,(de) ; and (digit) is by stripping the + inc de ; ascii code + and 0fh + add a,l + ld l,a + ld a,0 + adc a,h + ld h,a + pop bc + ld a,(de) + jp p,tn1 + scf + ret +qhow: + push de ; ** Error How? ** +ahow: + ld de,how + jp handleerror + +welcome +#ifdef PLATFORM + .db PLATFORM," " +#endif + .db "TASTY BASIC" +#ifdef VERSION + .db " (",VERSION,")" +#endif + .db cr +free .db " BYTES FREE",cr +how .db "HOW?",cr +ok .db "OK",cr +what .db "WHAT?",cr +sorry .db "SORRY",cr + +;************************************************************* +; +; *** MAIN *** +; +; THIS IS THE MAIN LOOP THAT COLLECTS THE TINY BASIC PROGRAM +; AND STORES IT IN THE MEMORY. +; +; AT START, IT PRINTS OUT "(CR)OK(CR)", AND INITIALIZES THE +; STACK AND SOME OTHER INTERNAL VARIABLES. THEN IT PROMPTS +; ">" AND READS A LINE. IF THE LINE STARTS WITH A NON-ZERO +; NUMBER, THIS NUMBER IS THE LINE NUMBER. THE LINE NUMBER +; (IN 16 BIT BINARY) AND THE REST OF THE LINE (INCLUDING CR) +; IS STORED IN THE MEMORY. IF A LINE WITH THE SAME LINE +; NUMBER IS ALREADY THERE, IT IS REPLACED BY THE NEW ONE. IF +; THE REST OF THE LINE CONSISTS OF A CR ONLY, IT IS NOT STORED +; AND ANY EXISTING LINE WITH THE SAME LINE NUMBER IS DELETED. +; +; AFTER A LINE IS INSERTED, REPLACED, OR DELETED, THE PROGRAM +; LOOPS BACK AND ASKS FOR ANOTHER LINE. THIS LOOP WILL BE +; TERMINATED WHEN IT READS A LINE WITH ZERO OR NO LINE +; NUMBER; AND CONTROL IS TRANSFERED TO "DIRECT". +; +; TINY BASIC PROGRAM SAVE AREA STARTS AT THE MEMORY LOCATION +; LABELED "TXTBGN" AND ENDS AT "TXTEND". WE ALWAYS FILL THIS +; AREA STARTING AT "TXTBGN", THE UNFILLED PORTION IS POINTED +; BY THE CONTENT OF A MEMORY LOCATION LABELED "TXTUNF". +; +; THE MEMORY LOCATION "CURRNT" POINTS TO THE LINE NUMBER +; THAT IS CURRENTLY BEING INTERPRETED. WHILE WE ARE IN +; THIS LOOP OR WHILE WE ARE INTERPRETING A DIRECT COMMAND +; (SEE NEXT SECTION). "CURRNT" SHOULD POINT TO A 0. +;************************************************************* +rstart: + ld sp,stack +st1: + call crlf + sub a ; a=0 + ld de,ok ; print ok + call printstr + ld hl,st2 + 1 ; literal zero + ld (current),hl ; reset current line pointer +st2: + ld hl,0 + ld (loopvar),hl + ld (stkgos),hl +st3: + ld a,'>' ; initialise prompt + call getline + push de ; de points to end of line + ld de,buffer ; point de to beginning of line + call testnum ; check if it is a number + call skipspace + ld a,h ; hl = value of the number, or + or l ; 0 if no number was found + pop bc ; bc points to end of line + jp z,direct + dec de ; back up de and save the value of + ld a,h ; the value of the line number there + ld (de),a + dec de + ld a,l + ld (de),a + push bc ; bc,de point to begin,end + push de + ld a,c + sub e + + push af ; a = number of bytes in line + call findline ; find this line in save area + push de ; de points to save area + jr nz,st4 ; nz: line not found + push de ; z: found, delete it + call findnext ; find next line + ; de -> next line + pop bc ; bc -> line to be deleted + ld hl,(textunfilled) ; hl -> unfilled text area + call mvup ; move up to delete + ld h,b ; txtunf -> unfilled area + ld l,c + ld (textunfilled),hl +st4: + pop bc ; get ready to insert + ld hl,(textunfilled) ; but first check if the length + pop af ; of new line is 3 (line# and cr) + push hl + cp 3h ; if so, do not insert + jr z,rstart ; must clear the stack + add a,l ; calculate new txtunf + ld l,a + ld a,0 + adc a,h + ld h,a ; hl -> new unfilled area + ld de,textend ; check to see if there is space + call comp + jp nc,qsorry ; no, sorry + ld (textunfilled),hl ; ok, update textunfilled + pop de ; de -> old unfilled area + call mvdown + pop de ; de,hl -> begin,end + pop hl + call mvup ; copy new line to save area + jr st3 + +;************************************************************* +; +; WHAT FOLLOWS IS THE CODE TO EXECUTE DIRECT AND STATEMENT +; COMMANDS. CONTROL IS TRANSFERED TO THESE POINTS VIA THE +; COMMAND TABLE LOOKUP CODE OF 'DIRECT' AND 'EXEC' IN LAST +; SECTION. AFTER THE COMMAND IS EXECUTED, CONTROL IS +; TRANSFERED TO OTHERS SECTIONS AS FOLLOWS: +; +; FOR 'LIST', 'NEW', AND 'STOP': GO BACK TO 'RSTART' +; FOR 'RUN': GO EXECUTE THE FIRST STORED LINE IF ANY, ELSE +; GO BACK TO 'RSTART'. +; FOR 'GOTO' AND 'GOSUB': GO EXECUTE THE TARGET LINE. +; FOR 'RETURN' AND 'NEXT': GO BACK TO SAVED RETURN LINE. +; FOR ALL OTHERS: IF 'CURRENT' -> 0, GO TO 'RSTART', ELSE +; GO EXECUTE NEXT COMMAND. (THIS IS DONE IN 'FINISH'.) +;************************************************************* +; +; *** NEW *** CLEAR *** STOP *** RUN (& FRIENDS) *** GOTO *** +; +; 'NEW(CR)' SETS 'TXTUNF' TO POINT TO 'TXTBGN' +; +; 'CLEAR(CR)' CLEARS ALL VARIABLES +; +; 'END(CR)' GOES BACK TO 'RSTART' +; +; 'RUN(CR)' FINDS THE FIRST STORED LINE, STORE ITS ADDRESS (IN +; 'CURRENT'), AND START EXECUTE IT. NOTE THAT ONLY THOSE +; COMMANDS IN TAB2 ARE LEGAL FOR STORED PROGRAM. +; +; THERE ARE 3 MORE ENTRIES IN 'RUN': +; 'RUNNXL' FINDS NEXT LINE, STORES ITS ADDR. AND EXECUTES IT. +; 'RUNTSL' STORES THE ADDRESS OF THIS LINE AND EXECUTES IT. +; 'RUNSML' CONTINUES THE EXECUTION ON SAME LINE. +; +; 'GOTO EXPR(CR)' EVALUATES THE EXPRESSION, FIND THE TARGET +; LINE, AND JUMP TO 'RUNTSL' TO DO IT. +;************************************************************* +new: + call endchk ; ** New ** + ld hl,textbegin + ld (textunfilled),hl +clear: + call clrvars ; ** Clear ** + jp rstart +endd: + call endchk ; ** End ** + jp rstart +run: + call endchk ; ** Run ** + call rstreadptr + ld de,textbegin +runnxl: + ld hl,0h ; ** Run Next Line ** + call findlineptr + jp c,rstart +runtsl: + ex de,hl ; ** Run Tsl + ld (current),hl ; set current -> line # + ex de,hl + inc de + inc de +runsml: + call chkio ; ** Run Same Line ** + ld hl, tab2-1 ; find the command in table 2 + jp exec ; and execute it +goto: + call expr + push de ; save for error routine + call endchk ; must find a cr + call findline ; find the target line + jp nz, ahow ; no such line # + pop af ; clear the pushed de + jr runtsl ; go do it + +;************************************************************* +; +; *** LIST *** & PRINT *** +; +; LIST HAS TWO FORMS: +; 'LIST(CR)' LISTS ALL SAVED LINES +; 'LIST #(CR)' START LIST AT THIS LINE # +; YOU CAN STOP THE LISTING BY CONTROL C KEY +; +; PRINT COMMAND IS 'PRINT ....;' OR 'PRINT ....(CR)' +; WHERE '....' IS A LIST OF EXPRESIONS, FORMATS, BACK- +; ARROWS, AND STRINGS. THESE ITEMS ARE SEPERATED BY COMMAS. +; +; A FORMAT IS A POUND SIGN FOLLOWED BY A NUMBER. IT CONTROLS +; THE NUMBER OF SPACES THE VALUE OF A EXPRESION IS GOING TO +; BE PRINTED. IT STAYS EFFECTIVE FOR THE REST OF THE PRINT +; COMMAND UNLESS CHANGED BY ANOTHER FORMAT. IF NO FORMAT IS +; SPECIFIED, 6 POSITIONS WILL BE USED. +; +; A STRING IS QUOTED IN A PAIR OF SINGLE QUOTES OR A PAIR OF +; DOUBLE QUOTES. +; +; A BACK-ARROW MEANS GENERATE A (CR) WITHOUT (LF) +; +; A (CRLF) IS GENERATED AFTER THE ENTIRE LIST HAS BEEN +; PRINTED OR IF THE LIST IS A NULL LIST. HOWEVER IF THE LIST +; ENDED WITH A COMMA, NO (CRLF) IS GENERATED. +;************************************************************* +list: + call testnum ; check if there is a number + call endchk ; if no number we get a 0 + call findline ; find this or next line +ls1: + jp c,rstart + call printline ; print the line + call chkio ; stop on ctrl-c + call findlineptr ; find the next line + jr ls1 ; and loop back + +print: + ld c,6 ; c = number of spaces + call testc ; is it a semicolon? + .db ';' + .db pr2-$-1 + call crlf + jr runsml +pr2: + call testc ; is it a cr? + .db cr + .db pr0-$-1 + call crlf + jr runnxl +pr0: + call testc ; is it format? + .db '#' + .db pr1-$-1 + call expr + ld c,l + jr pr3 +pr1: + call testc ; is it a dollar? + .db '$' + .db pr4-$-1 + call expr + ld c,l + call testc ; do we have a comma? + .db ',' + .db pr6-$-1 + push bc + call expr + pop bc + ld a,8 ; 8 bits? + cp c + jp nz,pr9 ; no, try 16 + call printhex8 ; yes, print a single hex byte + jp pr3 +pr9: + ld a,10h ; 16 bits? + cp c + jp nz,qhow ; no, show error message + call printhex ; yes, print two hex bytes + jp pr3 +pr4: + call qtstg ; is it a string? + jp pr8 +pr3: + call testc ; is it a comma? + .db ',' + .db pr6-$-1 + call fin + jr pr0 +pr6: + call crlf ; list ends + call finish +pr8: + call expr ; evaluate the expression + push bc + call printnum + pop bc + jr pr3 + +;************************************************************* +; +; *** GOSUB *** & RETURN *** +; +; 'GOSUB EXPR;' OR 'GOSUB EXPR (CR)' IS LIKE THE 'GOTO' +; COMMAND, EXCEPT THAT THE CURRENT TEXT POINTER, STACK POINTER +; ETC. ARE SAVE 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 THE CODE), +; BUT WE STILL SAVE IT AS A FLAG FOR NO FURTHER 'RETURN'S. +; +; 'RETURN(CR)' UNDOS EVERYTHING THAT 'GOSUB' DID, AND THUS +; RETURN THE EXECUTION TO THE COMMAND AFTER THE MOST RECENT +; 'GOSUB'. IF 'STKGOS' IS ZERO, IT INDICATES THAT WE +; NEVER HAD A 'GOSUB' AND IS THUS AN ERROR. +;************************************************************* +gosub: + call pusha ; ** Gosub ** + call expr ; save the current "FOR" params + push de ; and text pointer + call findline ; find the target line + jp nz,ahow ; how? because it doesn't exist + ld hl,(current) ; found it, save old 'current' + push hl + ld hl,(stkgos) ; and 'stkgos' + push hl + ld hl,0 ; and load new ones + ld (loopvar),hl + add hl,sp + ld (stkgos),hl + jp runtsl ; and run the line +return: + call endchk ; there must be a cr + ld hl,(stkgos) ; check old stack pointer + ld a,h ; + or l + jp z,what ; what? not found + ld sp,hl ; otherwise restore it + pop hl + ld (stkgos),hl + pop hl + ld (current),hl ; and old 'current' + pop de ; and old text pointer + call popa ; and old 'FOR' params + call finish ; and we're back + +;************************************************************* +; +; *** 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 SAVE 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 SOME- +; THING IN THE SAVE AREA (THIS IS 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 NECESSARILLY 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 CHECK 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 old save area + call setval ; set the control variable + dec hl ; its address is hl + ld (loopvar),hl ; save that + ld hl,tab5-1 ; use 'exec' to find 'TO' + jp exec +fr1: + call expr ; evaluate the limit + ld (looplmt),hl ; and save it + ld hl,tab6-1 ; use 'exec' to find 'STEP' + jp exec +fr2: + call expr ; found 'STEP' + jr fr4 +fr3: + ld hl,0001h ; no 'STEP' so set to 1 +fr4: + ld (loopinc),hl ; and save that too +fr5: + ld hl,(current) ; save current line number + ld (loopln),hl + ex de,hl ; and text pointer + ld (loopptr),hl + ld bc,0ah ; dig into stack to find loopvar + ld hl,(loopvar) + ex de,hl + ld h,b + ld l,b + add hl,sp + .db 3eh +fr7: + add hl,bc + ld a,(hl) + inc hl + or (hl) + jr z,fr8 + ld a,(hl) + dec hl + cp d + jr nz,fr7 + ld a,(hl) + cp e + jr nz,fr7 + ex de,hl + ld hl,0 + add hl,sp + ld b,h + ld c,l + ld hl,0ah + add hl,de + call mvdown + ld sp,hl +fr8: + ld hl,(loopptr) ; all done + ex de,hl + call finish +next: + call testvar ; get address of variable + jp c,qwhat ; what, no variable + ld (varnext),hl ; yes, save it +nx0: + push de ; save the text pointer + ex de,hl + ld hl,(loopvar) ; get the variable in 'FOR' + ld a,h + or l ; if 0, there never was one + jp z,awhat + call comp ; else check them + jr z,nx3 ; yes, they agree + pop de ; no, complete current loop + call popa + ld hl,(varnext) ; and pop one level + jr nx0 ; go check again +nx3: + ld e,(hl) + inc hl + ld d,(hl) ; de = value of variable + ld hl,(loopinc) + push hl + ld a,h + xor d + ld a,d + add hl,de + jp m,nx4 + xor h + jp m,nx5 +nx4: + ex de,hl + ld hl,(loopvar) + ld (hl),e + inc hl + ld (hl),d + ld hl,(looplmt) + pop af + or a + jp p,nx1 ; step > 0 + ex de,hl ; step < 0 +nx1: + call ckhlde ; compare with limit + pop de ; restore the text pointer + jr c,nx2 ; over the limit + ld hl,(loopln) ; within the limit + ld (current),hl + ld hl,(loopptr) + ex de,hl + call finish +nx5: + pop hl + pop de +nx2: + call popa ; purge this loop + call finish ; + +init: + ld hl,start ; initialise random pointer + ld (rndptr),hl + ld hl,usrfunc ; initialise usr func pointer + ld (usrptr),hl + ld a,0c9h ; initialise usr func (RET) + ld (usrfunc),a + ld hl,textbegin ; initialise text area pointers + ld (textunfilled),hl + ld (ocsw),a ; enable output control switch + call clrvars ; clear variables + call crlf + ld de,welcome ; output welcome message + call printstr + call crlf + call size ; output free size message + call printnum + ld de,free + call printstr + jp rstart + + +;************************************************************* + +#ifdef ROMWBW +#include "romwbwio.asm" +#endif + +#ifdef CPM +#include "cpmio.asm" +#endif + +#ifndef PLATFORM +#include "zemuio.asm" +#endif + +;************************************************************* +chkio: + call haschar ; check if character available + ret z ; no, return +#ifndef CPM + call getchar ; get the character +#endif + push bc ; is it a lf? + ld b,a + sub lf + jr z,io1 ; yes, ignore a return + ld a,b ; no, restore a and bc + pop bc + cp ctrlo ; is it ctrl-o? + jr nz,io2 ; no, done + ld a,(ocsw) ; toggle output control switch + cpl + ld (ocsw),a + jr chkio ; get next character +io1: + ld a,0h ; clear + or a ; set the z-flag + pop bc ; restore bc + ret ; return with z set +io2: + cp 60h ; is it lower case? + jp c,io3 ; no + and 0dfh ; yes, make upper case +io3: + cp ctrlc ; is it ctrl-c? + ret nz ; no + jp rstart ; yes, restart tasty basic +crlf: + ld a,cr +outc: + push af + ld a,(ocsw) ; check output control switch + or a + jr nz,oc1 ; output is enabled + pop af ; output is disabled + ret ; so return +oc1: + pop af + call putchar + cp cr ; was it a cr? + ret nz ; no, return + ld a,lf ; send a lf + call outc + ld a,cr ; restore register + ret ; and return + +;************************************************************* +; +; *** TABLES *** DIRECT *** & EXEC *** +; +; THIS SECTION OF THE CODE TESTS A STRING AGAINST A TABLE. +; WHEN A MATCH IS FOUND, CONTROL IS TRANSFERED TO THE SECTION +; OF CODE ACCORDING TO THE TABLE. +; +; AT 'EXEC', DE SHOULD POINT TO THE STRING AND HL SHOULD POINT +; TO THE TABLE-1. AT 'DIRECT', DE SHOULD POINT TO THE STRING. +; HL WILL BE SET UP TO POINT TO TAB1-1, WHICH IS THE TABLE OF +; ALL DIRECT AND STATEMENT COMMANDS. +; +; A '.' IN THE STRING WILL TERMINATE THE TEST AND THE PARTIAL +; MATCH WILL BE CONSIDERED AS A MATCH. E.G., 'P.', 'PR.', +; 'PRI.', 'PRIN.', OR 'PRINT' WILL ALL MATCH 'PRINT'. +; +; THE TABLE CONSISTS OF ANY NUMBER OF ITEMS. EACH ITEM +; IS A STRING OF CHARACTERS WITH BIT 7 SET TO 0 AND +; A JUMP ADDRESS STORED HI-LOW WITH BIT 7 OF THE HIGH +; BYTE SET TO 1. +; +; END OF TABLE IS AN ITEM WITH A JUMP ADDRESS ONLY. IF THE +; STRING DOES NOT MATCH ANY OF THE OTHER ITEMS, IT WILL +; MATCH THIS NULL ITEM AS DEFAULT. +;************************************************************* +tab1: ; direct commands + .db "LIST" + dwa(list) + .db "RUN" + dwa(run) + .db "NEW" + dwa(new) + .db "CLEAR" + dwa(clear) +#ifdef PLATFORM + .db "BYE" + dwa(bye) +#endif +tab2: ; direct/statements + .db "NEXT" + dwa(next) + .db "LET" + dwa(let) + .db "IF" + dwa(iff) + .db "GOTO" + dwa(goto) + .db "GOSUB" + dwa(gosub) + .db "RETURN" + dwa(return) + .db "REM" + dwa(rem) + .db "FOR" + dwa(for) + .db "INPUT" + dwa(input) + .db "PRINT" + dwa(print) + .db "POKE" + dwa(poke) + .db "OUT" + dwa(outp) +#ifdef CPM + .db "LOAD" + dwa(load) + .db "SAVE" + dwa(save) +#endif +datastmt: + .db "DATA" + dwa(data) + .db "READ" + dwa(read) + .db "RESTORE" + dwa(restore) + .db "END" + dwa(endd) + dwa(deflt) +tab4: ; functions + .db "PEEK" + dwa(peek) + .db "IN" + dwa(inp) + .db "RND" + dwa(rnd) + .db "ABS" + dwa(abs) + .db "USR" + dwa(usrexec) + .db "SIZE" + dwa(size) + dwa(xp40) +tab5: ; 'TO' in 'FOR' + .db "TO" + dwa(fr1) +tab6: ; 'STEP' in 'FOR' + .db "STEP" + dwa(fr2) + dwa(fr3) +tab8: ; relational operators + .db ">=" + dwa(xp11) + .db "#" + dwa(xp12) + .db ">" + dwa(xp13) + .db "=" + dwa(xp15) + .db "<=" + dwa(xp14) + .db "<" + dwa(xp16) + dwa(xp17) + +direct: + ld hl,tab1-1 ; ** Direct ** +exec: + call skipspace ; ** Exec ** + push de +ex1: + ld a,(de) + inc de + cp '.' + jr z,ex3 + inc hl + cp (hl) + jr z,ex1 + ld a,7fh + dec de + cp (hl) + jr c,ex5 +ex2: + inc hl + cp (hl) + jr nc,ex2 + inc hl + pop de + jr exec +ex3: + ld a,7fh +ex4: + inc hl + cp (hl) + jr nc,ex4 +ex5: + ld a,(hl) + inc hl + ld l,(hl) + and 7fh + ld h,a + pop af + jp (hl) + +;------------------------------------------------------------------------------- + +LST_ROM: ; all the above _can_ be in rom + ; all following *must* be in ram +padding .equ (TBC_LOC + USRPTR_OFFSET - $) + .echo "TASTYBASIC ROM padding: " + .echo padding + .echo " bytes.\n" + .org TBC_LOC + USRPTR_OFFSET +usrptr .ds 2 ; -> user defined function area +usrfunc .equ $ ; start of user defined function area + .org TBC_LOC + INTERNAL_OFFSET ; start of state +ocsw .ds 1 ; output control switch +current .ds 2 ; points to current line +stkgos .ds 2 ; saves sp in 'GOSUB' +varnext .ds 2 ; temp storage +stkinp .ds 2 ; save sp in 'INPUT' +loopvar .ds 2 ; 'FOR' loop save area +loopinc .ds 2 ; loop increment +looplmt .ds 2 ; loop limit +loopln .ds 2 ; loop line number +loopptr .ds 2 ; loop text pointer +rndptr .ds 2 ; random number pointer +readptr .ds 2 ; read pointer +textunfilled .ds 2 ; -> unfilled text area +textbegin .ds 2 ; start of text save area + .org TBC_LOC + TEXTEND_OFFSET +textend .ds 0 ; end of text area +varbegin .ds 55 ; variable @(0) +varend .equ $ ; end of variable area +buffer .ds 72 ; input buffer +bufend .ds 1 +stacklimit .equ $ + .org TBC_LOC + STACK_OFFSET +stack .equ $ + +#ifdef ROMWBW +slack .equ (TBC_END - LST_ROM) + .fill slack,'t' + + .echo "TASTYBASIC space remaining: " + .echo slack + .echo " bytes.\n" +#endif + .end diff --git a/Source/TastyBasic/src/zemuio.asm b/Source/TastyBasic/src/zemuio.asm new file mode 100644 index 00000000..d4acb063 --- /dev/null +++ b/Source/TastyBasic/src/zemuio.asm @@ -0,0 +1,54 @@ + +; ----------------------------------------------------------------------------- +; Copyright 2021 Dimitri Theulings +; +; This file is part of Tasty Basic. +; +; Tasty Basic is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; Tasty Basic is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with Tasty Basic. If not, see . +; ----------------------------------------------------------------------------- +; Tasty Basic is derived from earlier works by Li-Chen Wang, Peter Rauskolb, +; and Doug Gabbard. Refer to the source code repository for details +; . +; ----------------------------------------------------------------------------- + +USRPTR_OFFSET .equ 09feh +INTERNAL_OFFSET .equ 0c00h +TEXTEND_OFFSET .equ 07dffh +STACK_OFFSET .equ 07fffh + +tty_data .equ 7ch +tty_status .equ 7dh +rx_full .equ 1 +tx_empty .equ 0 + +putchar: + call uart_tx_ready ; see if transmit is available + out (tty_data),a ; and send it + ret +uart_tx_ready: + push af +uart_tx_ready_loop: + in a,(tty_status) + bit tx_empty,a + jp z,uart_tx_ready_loop + pop af + ret +haschar: + in a,(tty_status) ; check if character available + bit rx_full,a + ret + +getchar: + in a,(tty_data) ; get the character + ret From 6d115df490fc8ff533279531a32ad8a16a3a3c1f Mon Sep 17 00:00:00 2001 From: Wayne Warthen Date: Sat, 30 Oct 2021 20:32:09 -0700 Subject: [PATCH 2/2] TastyBasic Build Adjustments - Minor changes to the build process in TastyBasic to ensure binary identical results across all platforms. --- Source/HBIOS/Build.cmd | 4 ++-- Source/TastyBasic/src/Build.cmd | 4 ++-- Source/TastyBasic/src/Clean.cmd | 3 +-- Source/TastyBasic/src/Makefile | 14 +++++++++----- Source/ver.inc | 2 +- Source/ver.lib | 2 +- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/Source/HBIOS/Build.cmd b/Source/HBIOS/Build.cmd index b5967ec5..c0a9fe0f 100644 --- a/Source/HBIOS/Build.cmd +++ b/Source/HBIOS/Build.cmd @@ -70,7 +70,7 @@ call :asm romldr || exit /b call :asm eastaegg || exit /b call :asm nascom || exit /b -call :asm tastybasic || exit /b +:: call :asm tastybasic || exit /b call :asm game || exit /b call :asm usrrom || exit /b call :asm updater || exit /b @@ -84,7 +84,7 @@ call :asm imgpad2 || exit /b :: copy /b romldr.bin + dbgmon.bin + ..\zsdos\zsys_wbw.bin + ..\cpm22\cpm_wbw.bin osimg.bin || exit /b -copy /b ..\Forth\camel80.bin + nascom.bin + tastybasic.bin + game.bin + eastaegg.bin + netboot.mod + updater.bin + usrrom.bin osimg1.bin || exit /b +copy /b ..\Forth\camel80.bin + nascom.bin + ..\tastybasic\src\tastybasic.bin + game.bin + eastaegg.bin + netboot.mod + updater.bin + usrrom.bin osimg1.bin || exit /b copy /b imgpad2.bin osimg2.bin || exit /b copy /b romldr.bin + dbgmon.bin + ..\zsdos\zsys_wbw.bin osimg_small.bin || exit /b diff --git a/Source/TastyBasic/src/Build.cmd b/Source/TastyBasic/src/Build.cmd index 5c0bc0ec..9ee76528 100644 --- a/Source/TastyBasic/src/Build.cmd +++ b/Source/TastyBasic/src/Build.cmd @@ -7,7 +7,7 @@ set PATH=%TOOLS%\tasm32;%PATH% set TASMTABS=%TOOLS%\tasm32 -tasm -80 -dROMWBW tastybasic.asm tastybasic.bin tastybasic.bin.lst -tasm -80 -dCPM tastybasic.asm tastybasic.com tastybasic.com.lst +tasm -80 -g3 -fFF -dROMWBW tastybasic.asm tastybasic.bin tastybasic.bin.lst +tasm -80 -g3 -fFF -dCPM tastybasic.asm tastybasic.com tastybasic.com.lst copy /b /v tastybasic.com ..\..\..\Binary\Apps\tbasic.com diff --git a/Source/TastyBasic/src/Clean.cmd b/Source/TastyBasic/src/Clean.cmd index 9d2d37a1..420369b5 100644 --- a/Source/TastyBasic/src/Clean.cmd +++ b/Source/TastyBasic/src/Clean.cmd @@ -7,5 +7,4 @@ if exist *.prn del *.prn if exist *.hex del *.hex if exist *.rel del *.rel if exist *.sym del *.sym - -if exist ..\..\..\Binary\Apps\tbasic.com del ..\..\..\Binary\Apps\tbasic.com +if exist *.com del *.com diff --git a/Source/TastyBasic/src/Makefile b/Source/TastyBasic/src/Makefile index 226bc02a..dbe3fae8 100644 --- a/Source/TastyBasic/src/Makefile +++ b/Source/TastyBasic/src/Makefile @@ -1,5 +1,5 @@ UNAME := $(shell uname) -VER := $(shell git describe --tags --abbrev=0) +# VER := $(shell git describe --tags --abbrev=0) APPDIR := $(wildcard ../../../Binary/Apps) BINDIR := ../../../Tools/$(UNAME) @@ -14,17 +14,21 @@ export PATH := $(BINDIR):${PATH} all: $(ROMIMAGE) $(CPMCMD) | $(APPDIR) -clean clobber: - @rm -f *.lst *.img *.com *.bin $(CPMAPP) +clean: + @rm -f *.lst *.img *.com *.bin + +clobber: clean + @rm -f $(CPMAPP) + $(ROMIMAGE): $(ROMDEPS) - @uz80as -dROMWBW -d"VERSION \"$(VER)\"" tastybasic.asm tastybasic.bin tastybasic.bin.lst + @uz80as -dROMWBW tastybasic.asm tastybasic.bin tastybasic.bin.lst $(CPMIMAGE): $(CPMCMD) @mkfs.cpm -f wbw_fd144 tastybasic.img @cpmcp -f wbw_fd144 tastybasic.img tastybasic.com 0:tbasic.com $(CPMCMD): $(CPMDEPS) - @uz80as -dCPM -d"VERSION \"$(VER)\"" tastybasic.asm tastybasic.com tastybasic.com.lst + @uz80as -dCPM tastybasic.asm tastybasic.com tastybasic.com.lst $(APPDIR): $(CPMCMD) @cat $(CPMCMD) > $(CPMAPP) diff --git a/Source/ver.inc b/Source/ver.inc index 5797acd2..09a06696 100644 --- a/Source/ver.inc +++ b/Source/ver.inc @@ -2,4 +2,4 @@ #DEFINE RMN 1 #DEFINE RUP 1 #DEFINE RTP 0 -#DEFINE BIOSVER "3.1.1-pre.133" +#DEFINE BIOSVER "3.1.1-pre.134" diff --git a/Source/ver.lib b/Source/ver.lib index c13e585e..b86cd056 100644 --- a/Source/ver.lib +++ b/Source/ver.lib @@ -3,5 +3,5 @@ rmn equ 1 rup equ 1 rtp equ 0 biosver macro - db "3.1.1-pre.133" + db "3.1.1-pre.134" endm