Skip to content

Commit

Permalink
basic: use FreeBASIC/fbc for qbasic mode
Browse files Browse the repository at this point in the history
- 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).
  • Loading branch information
kanaka committed Nov 19, 2024
1 parent 05461b7 commit f945cc0
Show file tree
Hide file tree
Showing 11 changed files with 67 additions and 88 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 15 additions & 18 deletions impls/basic/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ubuntu:wily
FROM ubuntu:24.04
MAINTAINER Joel Martin <[email protected]>

##########################################################
Expand All @@ -9,10 +9,8 @@ MAINTAINER Joel Martin <[email protected]>
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
Expand All @@ -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

10 changes: 5 additions & 5 deletions impls/basic/Makefile
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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
18 changes: 3 additions & 15 deletions impls/basic/basicpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -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()]
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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])
Expand Down Expand Up @@ -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__':
Expand Down
4 changes: 2 additions & 2 deletions impls/basic/core.in.bas
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion impls/basic/mem.in.bas
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ INIT_MEMORY:

REM start of time clock
#cbm BT=TI
#qbasic BT#=TIMER(0.001)
#qbasic BT#=TIMER()

RETURN

Expand Down
2 changes: 1 addition & 1 deletion impls/basic/reader.in.bas
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 31 additions & 3 deletions impls/basic/readline.in.bas
Original file line number Diff line number Diff line change
@@ -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'
31 changes: 0 additions & 31 deletions impls/basic/readline_char.in.bas

This file was deleted.

6 changes: 0 additions & 6 deletions impls/basic/readline_line.in.bas

This file was deleted.

2 changes: 2 additions & 0 deletions impls/basic/step0_repl.in.bas
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit f945cc0

Please sign in to comment.