mirror of
https://github.com/kanaka/mal.git
synced 2024-11-23 12:14:45 +03:00
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).
This commit is contained in:
parent
05461b75a7
commit
f945cc0e56
13
README.md
13
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
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM ubuntu:wily
|
||||
FROM ubuntu:24.04
|
||||
MAINTAINER Joel Martin <github@martintribe.org>
|
||||
|
||||
##########################################################
|
||||
@ -9,10 +9,8 @@ MAINTAINER Joel Martin <github@martintribe.org>
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 finalize(lines, args):
|
||||
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__':
|
||||
|
@ -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:
|
||||
|
@ -384,7 +384,7 @@ INIT_MEMORY:
|
||||
|
||||
REM start of time clock
|
||||
#cbm BT=TI
|
||||
#qbasic BT#=TIMER(0.001)
|
||||
#qbasic BT#=TIMER()
|
||||
|
||||
RETURN
|
||||
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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
|
@ -1,6 +0,0 @@
|
||||
REM READLINE(A$) -> R$
|
||||
READLINE:
|
||||
EZ=0
|
||||
PRINT A$ ;
|
||||
LINE INPUT ; R$
|
||||
RETURN
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user