From f945cc0e56c553b1e6df11ea8e1339a166732337 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Tue, 19 Nov 2024 11:33:10 -0600 Subject: [PATCH] basic: use FreeBASIC/fbc for qbasic mode - Update Dockerfile to Ubuntu 24.04. - Install FreeBASIC 1.10.1 instead of qb64. - Convert basicpp.py to remove qb64 specific console vs UI support. - Convert to use freebasic file open (and error handling specifically), and TIMER call. - Fix bug in step0 revealed by FreeBASIC: X% is not declared. Call DIM_MEMORY during startup. - Remove the whole line version of readline. The character at a time version works now for both implementations (and has the ability to backspace the line and exit with Ctrl-D). --- README.md | 13 ++++++------ impls/basic/Dockerfile | 33 ++++++++++++++----------------- impls/basic/Makefile | 10 +++++----- impls/basic/basicpp.py | 18 +++-------------- impls/basic/core.in.bas | 4 ++-- impls/basic/mem.in.bas | 2 +- impls/basic/reader.in.bas | 2 +- impls/basic/readline.in.bas | 34 +++++++++++++++++++++++++++++--- impls/basic/readline_char.in.bas | 31 ----------------------------- impls/basic/readline_line.in.bas | 6 ------ impls/basic/step0_repl.in.bas | 2 ++ 11 files changed, 67 insertions(+), 88 deletions(-) delete mode 100644 impls/basic/readline_char.in.bas delete mode 100644 impls/basic/readline_line.in.bas diff --git a/README.md b/README.md index 77e8332793..9bd066ab75 100644 --- a/README.md +++ b/README.md @@ -252,26 +252,27 @@ bash stepX_YYY.sh ### BASIC (C64 and QBasic) The BASIC implementation uses a preprocessor that can generate BASIC -code that is compatible with both C64 BASIC (CBM v2) and QBasic. The +code that is compatible with both C64 BASIC (CBM v2) or QBasic. The C64 mode has been tested with [cbmbasic](https://github.com/kanaka/cbmbasic) (the patched version is currently required to fix issues with line input) and the QBasic mode -has been tested with [qb64](http://www.qb64.net/). +has been tested with [FreeBASIC](freebasic.net). Generate C64 code and run it using cbmbasic: ``` cd impls/basic -make stepX_YYY.bas -STEP=stepX_YYY ./run +make MODE=cbm stepX_YYY.bas +STEP=stepX_YYY basic_MODE=cbm ./run ``` -Generate QBasic code and load it into qb64: +Generate QBasic code, compile using FreeBASIC, and execute it: ``` cd impls/basic make MODE=qbasic stepX_YYY.bas -./qb64 stepX_YYY.bas +make MODE=qbasic stepX_YYY +./stepX_YYY ``` Thanks to [Steven Syrek](https://github.com/sjsyrek) for the original diff --git a/impls/basic/Dockerfile b/impls/basic/Dockerfile index 928b1b1c62..ea6125710c 100644 --- a/impls/basic/Dockerfile +++ b/impls/basic/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:wily +FROM ubuntu:24.04 MAINTAINER Joel Martin ########################################################## @@ -9,10 +9,8 @@ MAINTAINER Joel Martin RUN apt-get -y update # Required for running tests -RUN apt-get -y install make python - -# Some typical implementation and test requirements -RUN apt-get -y install curl libreadline-dev libedit-dev +RUN apt-get -y install make python3 +RUN ln -fs /usr/bin/python3 /usr/local/bin/python RUN mkdir -p /mal WORKDIR /mal @@ -21,26 +19,25 @@ WORKDIR /mal # Specific implementation requirements ########################################################## +RUN DEBIAN_FRONTEND=noninteractive apt-get -y install \ + ca-certificates curl gcc g++ libasound2-dev \ + libglu1-mesa-dev mesa-common-dev patch unzip wget \ + xz-utils libncurses-dev + # cbmbasic -RUN apt-get install -y gcc unzip patch +# Remove duplicate RAM (https://github.com/mist64/cbmbasic/commit/352a313313dd0a15a47288c8f8031b54ac8c92a2). RUN cd /tmp && \ curl -L https://github.com/kanaka/cbmbasic/archive/master.zip -o cbmbasic.zip && \ unzip cbmbasic.zip && \ cd cbmbasic-master && \ + sed -i '/unsigned char RAM.65536.;/d' runtime.c && \ make && \ - cp cbmbasic /usr/bin/cbmbasic && \ + mv cbmbasic /usr/local/bin && \ cd .. && \ rm -r cbmbasic* -RUN apt-get install -y g++ mesa-common-dev libglu1-mesa-dev libasound2-dev wget -RUN cd /tmp && \ - curl -L http://www.qb64.net/release/official/2017_02_09__02_14_38-1.1-20170120.51/linux/qb64-1.1-20170120.51-lnx.tar.gz | tar xzf - && \ - cd qb64 && \ - find . -name '*.sh' -exec sed -i "s/\r//g" {} \; && \ - env EUID=1 ./setup_lnx.sh && \ - mkdir -p /usr/share/qb64 && \ - cp -a qb64 internal LICENSE programs source /usr/share/qb64/ && \ - echo '#!/bin/sh\ncd /usr/share/qb64\n./qb64 "${@}"' > /usr/bin/qb64 && \ - chmod +x /usr/bin/qb64 - +# qbasic (using freebasic: `fbc -lang qb`) +RUN cd /opt && \ + curl -L https://sourceforge.net/projects/fbc/files/FreeBASIC-1.10.1/Binaries-Linux/FreeBASIC-1.10.1-ubuntu-22.04-x86_64.tar.xz | tar xvJf - && \ + ln -sf /opt/FreeBASIC-1.10.1-ubuntu-22.04-x86_64/bin/fbc /usr/local/bin/fbc diff --git a/impls/basic/Makefile b/impls/basic/Makefile index df8eba0b28..2fbd365bf9 100644 --- a/impls/basic/Makefile +++ b/impls/basic/Makefile @@ -1,7 +1,7 @@ basic_MODE = cbm BASICPP_OPTS = --mode $(basic_MODE) -QB64 = qb64 +FBC = fbc -lang qb STEPS4_A = step4_if_fn_do.bas step5_tco.bas step6_file.bas \ step7_quote.bas step8_macros.bas step9_try.bas stepA_mal.bas @@ -11,8 +11,8 @@ STEPS0_A = step0_repl.bas $(STEPS1_A) all: $(if $(filter qbasic,$(basic_MODE)),$(subst .bas,,$(STEPS0_A)),$(STEPS0_A)) -$(STEPS0_A): readline.in.bas readline_line.in.bas readline_char.in.bas -$(STEPS1_A): debug.in.bas mem.in.bas types.in.bas reader.in.bas printer.in.bas +$(STEPS0_A): debug.in.bas mem.in.bas readline.in.bas +$(STEPS1_A): types.in.bas reader.in.bas printer.in.bas $(STEPS3_A): env.in.bas $(STEPS4_A): core.in.bas @@ -26,7 +26,7 @@ tests/%.bas: tests/%.in.bas # QBasic specific compilation rule step%: step%.bas - $(QB64) -x $(abspath $<) -o $(abspath $@) + $(FBC) $< -x $@ # CBM/C64 image rules @@ -58,5 +58,5 @@ mal.d64: mal.prg .args.mal.prg core.mal.prg .PHONY: clean clean: - rm -f $(STEPS0_A) $(subst .bas,,$(STEPS0_A)) *.d64 *.prg qb64 + rm -f $(STEPS0_A) $(subst .bas,,$(STEPS0_A)) *.d64 *.prg rm -rf ./internal diff --git a/impls/basic/basicpp.py b/impls/basic/basicpp.py index cb2f6223f8..86a2e6c419 100755 --- a/impls/basic/basicpp.py +++ b/impls/basic/basicpp.py @@ -13,7 +13,6 @@ def parse_args(): parser.add_argument('infiles', type=str, nargs='+', help='the Basic files to preprocess') parser.add_argument('--mode', choices=["cbm", "qbasic"], default="cbm") - parser.add_argument('--sub-mode', choices=["noui", "ui"], default="noui") parser.add_argument('--keep-rems', action='store_true', default=False, help='The type of REMs to keep (0 (none) -> 4 (all)') parser.add_argument('--keep-blank-lines', action='store_true', default=False, @@ -26,7 +25,6 @@ def parse_args(): help='Do not combine lines using the ":" separator') args = parser.parse_args() - args.full_mode = "%s-%s" % (args.mode, args.sub_mode) if args.keep_rems and not args.skip_combine_lines: debug("Option --keep-rems implies --skip-combine-lines ") args.skip_combine_lines = True @@ -48,7 +46,7 @@ def resolve_includes(orig_lines, args): if m: mode = m.group(1) f = m.group(2) - if mode and mode != args.mode and mode != args.full_mode: + if mode and mode != args.mode: position += 1 elif f not in included: ilines = [l.rstrip() for l in open(f).readlines()] @@ -68,8 +66,6 @@ def resolve_mode(orig_lines, args): if m: if m.group(1) == args.mode: lines.append(m.group(2)) - elif m.group(1) == args.full_mode: - lines.append(m.group(2)) continue lines.append(line) return lines @@ -111,7 +107,7 @@ def misc_fixups(orig_lines): text = re.sub(r"\bIF ", "IF", text) text = re.sub(r"\bPRINT *", "PRINT", text) text = re.sub(r"\bDIM ", "DIM", text) - text = re.sub(r"\OPEN ", "OPEN", text) + text = re.sub(r"\bOPEN ", "OPEN", text) text = re.sub(r"\bGET ", "GET", text) text = re.sub(r"\bPOKE ", "POKE", text) text = re.sub(r"\bCLOSE ", "CLOSE", text) @@ -161,7 +157,7 @@ def finalize(lines, args): if m: prefix = m.groups(1)[0] sub = m.groups(1)[1] - if not call_index.has_key(sub): + if not sub in call_index: call_index[sub] = 0 call_index[sub] += 1 label = sub+"_"+str(call_index[sub]) @@ -295,14 +291,6 @@ def renum(line): text = update_labels_lines(text, a, b) lines = text.split("\n") - # Force non-UI QBasic to use text console. LINE INPUT also needs - # to be used instead in character-by-character READLINE - if args.full_mode == "qbasic-noui": - # Add console program prefix for qb64/qbasic - lines = ["$CONSOLE", - "$SCREENHIDE", - "_DEST _CONSOLE"] + lines - return lines if __name__ == '__main__': diff --git a/impls/basic/core.in.bas b/impls/basic/core.in.bas index 64625723d4..e87a205e99 100644 --- a/impls/basic/core.in.bas +++ b/impls/basic/core.in.bas @@ -280,8 +280,8 @@ DO_FUNCTION: EZ=0 #cbm OPEN 2,8,0,S$(A1) #qbasic A$=S$(A1) - #qbasic IF NOT _FILEEXISTS(A$) THEN ER=-1:E$="File not found":RETURN #qbasic OPEN A$ FOR INPUT AS #2 + #qbasic IF ERR()<>0 THEN ER=-1:E$="File not found":RETURN DO_SLURP_LOOP: C$="" RJ=1:GOSUB READ_FILE_CHAR @@ -323,7 +323,7 @@ DO_FUNCTION: RETURN DO_TIME_MS: #cbm T=2:L=INT((TI-BT)*16.667):GOSUB ALLOC - #qbasic T=2:L=INT((TIMER(0.001)-BT#)*1000):GOSUB ALLOC + #qbasic T=2:L=INT((TIMER()-BT#)*1000):GOSUB ALLOC RETURN DO_LIST: diff --git a/impls/basic/mem.in.bas b/impls/basic/mem.in.bas index d4a6d54570..266a1aec59 100644 --- a/impls/basic/mem.in.bas +++ b/impls/basic/mem.in.bas @@ -384,7 +384,7 @@ INIT_MEMORY: REM start of time clock #cbm BT=TI - #qbasic BT#=TIMER(0.001) + #qbasic BT#=TIMER() RETURN diff --git a/impls/basic/reader.in.bas b/impls/basic/reader.in.bas index 67bc8db1da..d8dc46298a 100644 --- a/impls/basic/reader.in.bas +++ b/impls/basic/reader.in.bas @@ -267,8 +267,8 @@ READ_FILE: SD=0: REM sequence read depth D$="": REM pending read/peek character #cbm OPEN 2,8,0,A$ - #qbasic IF NOT _FILEEXISTS(A$) THEN ER=-1:E$="File not found":RETURN #qbasic OPEN A$ FOR INPUT AS #2 + #qbasic IF ERR()<>0 THEN ER=-1:E$="File not found":RETURN REM READ_TOKEN adds "(do ... )" CALL READ_FORM CLOSE 2 diff --git a/impls/basic/readline.in.bas b/impls/basic/readline.in.bas index 67cbaa76ba..0ec7f232f6 100644 --- a/impls/basic/readline.in.bas +++ b/impls/basic/readline.in.bas @@ -1,4 +1,32 @@ +REM READLINE(A$) -> R$ +READLINE: + EZ=0 + PRINT A$; + C$="":R$="":C=0 + READCH: + #cbm GET C$ + #qbasic C$=INKEY$ + IF C$="" THEN GOTO READCH + C=ASC(C$) + #qbasic IF ASC(C$)=8 THEN C=20:C$=CHR$(20) + IF C=4 OR C=0 THEN EZ=1:GOTO RL_DONE: REM EOF + IF C=127 OR C=20 THEN GOSUB RL_BACKSPACE + IF C=127 OR C=20 THEN GOTO READCH + IF (C<32 OR C>127) AND C<>13 THEN GOTO READCH + PRINT C$; + IF LEN(R$)<255 AND C$<>CHR$(13) THEN R$=R$+C$ + IF LEN(R$)<255 AND C$<>CHR$(13) THEN GOTO READCH + RL_DONE: + #qbasic PRINT + RETURN + + REM Assumes R$ has input buffer + RL_BACKSPACE: + IF LEN(R$)=0 THEN RETURN + R$=LEFT$(R$,LEN(R$)-1) + #cbm PRINT CHR$(157)+" "+CHR$(157); + #qbasic LOCATE ,POS(0)-1 + #qbasic PRINT " "; + #qbasic LOCATE ,POS(0)-1 + RETURN -#cbm REM $INCLUDE: 'readline_char.in.bas' -#qbasic-ui REM $INCLUDE: 'readline_char.in.bas' -#qbasic-noui REM $INCLUDE: 'readline_line.in.bas' diff --git a/impls/basic/readline_char.in.bas b/impls/basic/readline_char.in.bas deleted file mode 100644 index 75b7996343..0000000000 --- a/impls/basic/readline_char.in.bas +++ /dev/null @@ -1,31 +0,0 @@ -REM READLINE(A$) -> R$ -READLINE: - EZ=0 - PRINT A$; - C$="":R$="":C=0 - READCH: - #cbm GET C$ - #qbasic C$=INKEY$ - IF C$="" THEN GOTO READCH - C=ASC(C$) - REM PRINT C - #qbasic IF ASC(C$)=8 THEN C=20:C$=CHR$(20) - IF C=4 OR C=0 THEN EZ=1:GOTO RL_DONE: REM EOF - IF C=127 OR C=20 THEN GOSUB RL_BACKSPACE - IF C=127 OR C=20 THEN GOTO READCH - IF (C<32 OR C>127) AND C<>13 THEN GOTO READCH - PRINT C$; - IF LEN(R$)<255 AND C$<>CHR$(13) THEN R$=R$+C$ - IF LEN(R$)<255 AND C$<>CHR$(13) THEN GOTO READCH - RL_DONE: - RETURN - - REM Assumes R$ has input buffer - RL_BACKSPACE: - IF LEN(R$)=0 THEN RETURN - R$=LEFT$(R$,LEN(R$)-1) - #cbm PRINT CHR$(157)+" "+CHR$(157); - #qbasic LOCATE ,POS(0)-1 - #qbasic PRINT " "; - #qbasic LOCATE ,POS(0)-1 - RETURN diff --git a/impls/basic/readline_line.in.bas b/impls/basic/readline_line.in.bas deleted file mode 100644 index 3d65f4172f..0000000000 --- a/impls/basic/readline_line.in.bas +++ /dev/null @@ -1,6 +0,0 @@ -REM READLINE(A$) -> R$ -READLINE: - EZ=0 - PRINT A$ ; - LINE INPUT ; R$ - RETURN diff --git a/impls/basic/step0_repl.in.bas b/impls/basic/step0_repl.in.bas index aae8a06383..417e316695 100755 --- a/impls/basic/step0_repl.in.bas +++ b/impls/basic/step0_repl.in.bas @@ -23,6 +23,8 @@ END SUB REM MAIN program MAIN: + GOSUB DIM_MEMORY + REPL_LOOP: A$="user> ":GOSUB READLINE: REM call input parser IF EZ=1 THEN GOTO QUIT