As a way of re-learning BASIC I chose the Beedat.mwb program to modify and extend functionality.
Uploading my version for GROK to review, GROK says "The BEEDAT2.8 program is a functional database management system written in BASIC, designed for a microcomputer with direct memory and screen control. It uses a simple text file (BEEDAT.FIL) for storage and supports basic CRUD (Create, Read, Update, Delete) operations, sorting, and searching. The variable list includes scalar numeric and string variables for control flow and user input, and string arrays for data storage and manipulation. The program’s design reflects the constraints and conventions of early 1980s microcomputer programming."
Program Overview.
The BEEDAT2.8 program is adapted and modified from the tape program in WILDCARDS vol 3, using the disk access code that was included in WILDCARDS 3. The original author provided a UI/screen design that reflected both the Microbee UI -think original WordBee- and text input terminals of the '70's. I have resisted changing the UI/screen design both to pay homage to Beedat 20 and to reflect the look and feel of text based interfaces of yesteryear.
I wanted to attempt to increase user interaction and provide a wider scope of use. Listing now allows exiting anytime during the record display. I recoded sorting/searching automatically across all fields, with a visual indication that sorting is underway. The program sorts A-Z followed by a-z, on user specified field. I have used a 40-50 record database file to test the program. To QUIT the program use CTRL C.
Issues: The KEY command appears erratic at times, and I hard coded a "N" on line 2030 to solve this. In the sort routine the variable M caused crashes, and I had to use FLT to get the division right. I am not sure if these two issues are due to using BASIC version 6.23e or using ubee512 emulator, or an interaction of both.
The program BEEDAT2.8 is a simple DBMS with the following key functionalities:
SORT (Lines 140–295): Implements a Comb Sort algorithm to sort records based on a user-specified field (1–5).
LOAD (Lines 300–380): Loads records from BEEDAT.FIL into a two-dimensional string array D0$. It checks for end-of-file (EOF) and validates the load process.
SAVE (Lines 390–520): Saves the current records from D0$ to BEEDAT.FIL.
INSERT (Lines 530–630): Adds a new record by prompting for input for each of the five fields, validates field length, and sorts the records after insertion.
DELETE (Lines 780–870): Removes a record by shifting subsequent records up and clearing the last record.
EDIT (Lines 880–1020): Allows editing of a specific field in a record, with length validation, followed by re-sorting.
FIND (Lines 1030–1150): Searches for a string across all fields in all records and displays matches, allowing the user to confirm or continue searching.
LIST (Lines 1160–1255): Displays records on the screen, pausing every three records for user input. The sort is ascending by default but can be modified for descending order, I leave this as an exercise to the user. REM gives a clue. pressing X will exit.
PRINT (Lines 1260–1330): Sends records to a printer using LPRINT. I have been UNABLE to verify this code section.
HELP (Lines 2040–2260): Displays a menu of available commands.
Control Loop (Lines 2060–2120): Provides a command-line interface where users input single-letter commands (C, D, E, F, G, H, I, L, P, R, S) to invoke corresponding functions.
Key variables:
Numeric Variables:
A: Loop counter or index, used in various subroutines (e.g., listing records, copying records).
C: Cursor position, initialized to 960 (line 130) and used in subroutine at line 2010 to clear a portion of the screen.
D: Destination record index, used in delete, edit, and copy operations.
F: Field number (1–5) for editing a specific field in a record.
I: General-purpose index, used as a loop counter or record index in multiple subroutines (e.g., load, save, list).
J: Field index (1–5) or secondary loop counter in sorting and searching.
K: Loop counter, used in sorting and searching subroutines.
L: Field number (1–5) for sorting, input by the user.
M: Gap size in the Comb Sort algorithm. The original program had a much simpler use of this but I had to use m1 for gap size.
M1:Temporary floating-point variable for calculating the gap size (M1 = M / 1.3). This consumed several hours of debugging.
N: Loop counter for swapping fields in the sort subroutine.
Q: Running total of field lengths during insertion to ensure the total does not exceed 170 characters.
R: Number of records in the database, updated during load, insert, and delete operations.
S: Flag indicating whether a swap occurred in the sort subroutine (1 if swapped, 0 otherwise).
T: Tab position for menu display (read from DATA in line 2230).
V: Length of a field’s content during insertion or editing, used for validation.
Z: Command code derived from the ASCII value of the input command (Z = ASC(Z7$) - 65) or counter for listing records.
String Variables:
N1$: Used in the print subroutine (line 1270) but not assigned a value in the provided code, likely intended for a header or printer control string.
S1$: Input string for sorting field number or search query.
Z7$: General-purpose string for user input (e.g., commands, confirmation in search, field values).
Array Variables
String Arrays:
D0$(100,5): Main database array, storing up to 100 records with 5 fields each. Each element D0$(I,J) holds the value of field J for record I.
D1$(5): Temporary array for storing a new or edited record’s fields during insertion or editing.
T1$(5): Temporary array used during sorting to swap records.I added this as I found D1$(5) was overworked leading to spaghetti code.
Notes on Variables
Array Dimensions: D0$ is dimensioned to hold 100 records with 5 fields each, while D1$ and T1$ are single-dimensional arrays for 5 fields String Array STRS (5000): Declared in line 110, to allocate string memory.
Uninitialized Variables: N1$ is referenced in the print subroutine but not assigned, being deprecated from original.
Memory Locations: The use of POKE 61440+R,13 and POKE 61438+I,9 are direct screen memory manipulation, for screen control whilst screen is off during disk read write operations.
Additional Notes
Potential Issues:The program assumes BEEDAT.FIL exists and is correctly formatted during loading, with minimal error handling for file issues. I used Grok to generate a 40 record x 5 list of words that I saved as "beedat.fil". From command menu G loads the beedat.fil file, whilst R saves any modifications,insertions of new records or record deletion.
The END statement (line 2264) follows debug print statements (lines 2261–2263).
Data Structure: The DATA statements (lines 2230–2260) define the help menu, with 12 commands
and a tab position of 43 for centered display.
Possible future additions:
As noted above listing from Z-A by adding two lines of code.
Allowing other files to be saved and loaded by adding a file name input section.
Changing the UI/Screen display ( Menu along bottom, data display formatted differently)
Increasing Record nos to 200 records of 4 fields etc.
Consolidating duplicated code in sorting/
If anyone is interested I can supply both beedat28.mwb and the beedat.fil file.
PS: An interesting aside -the BEEDAT program had 153 lines of code, Beedat2.8 has 158 lines. I used AI to discuss and modify optimisation routines. I mainly used GROK which would not accept Microbee basic DIM D0(100), every new question it would insist on a new line 120 DIM D0$(100) etc. It would accept my explanation and kept referring to correct syntax in subsequent questions. However, when I commenced a new session it would revert back to DIM d0$(100). As the old adage goes "dumb machines." However,I can forsee a time in the next three-five years when GROK and other AI's will natively run Microbee BASIC. At present it emulates BASIC code in python to run the code. It took brute force for it to accept variable naming constructs, often confusing MBASIC with Microbee BASIC. I assume the sheer number of people querying the LLM means that realtime reasoning and logic flowpaths in GROK breakdown to some extent. Scary for actual real world issues. I would not use AI to run mission critical business computing.
Apart from the Microbee manuals in the repository I also found
https://mbee.compoutpost.com/basic-reference/ useful for testing code snippets.
GROK emulation of Microbee Basic in action:
import re
def is_valid_string_var(var):
# Matches one letter (A-Z), one digit (0-9), then $
return bool(re.match(r'^[A-Z][0-9]\$', var))
def parse_basic_line(line):
# Match line number and command
match = re.match(r'^\s*(\d+)\s+(\w+)\s*(.*)', line)
if not match:
print(f"Invalid line: {line}")
return
line_num, command, args = match.groups()
# Handle string assignments (e.g., A1$ = "Text")
if command == "LET" or (command not in ["DIM", "INPUT", "PRINT", "END"] and '=' in args):
var, value = args.split('=', 1)
var = var.strip()
value = value.strip().strip('"')
if is_valid_string_var(var):
print(f"Assigned {var} = {value}")
else:
print(f"Invalid string variable: {var} (must be one letter, one digit, then $)")
# Handle INPUT (e.g., INPUT "prompt"; N8$)
elif command == "INPUT":
parts = args.split(';', 1)
if len(parts) == 2:
prompt = parts[0].strip().strip('"')
var = parts[1].strip()
if is_valid_string_var(var):
user_input = input(prompt + " ")
print(f"Stored in {var}: {user_input}")
else:
print(f"Invalid string variable: {var} (must be one letter, one digit, then $)")
else:
print(f"Invalid INPUT syntax: {args}")
# Handle PRINT
elif command == "PRINT":
# Check for string variables in PRINT
tokens = args.split(';')
for token in tokens:
token = token.strip().strip('"')
if token.endswith('$') and not is_valid_string_var(token):
print(f"Invalid string variable in PRINT: {token} (must be one letter, one digit, then $)")
print(f"Output: {args.strip()}")
# Create and test a .bas file
with open("program.bas", "w") as f:
f.write('10 A1$ = "Welcome to MicroBee BASIC!"\n')
f.write('20 INPUT "Enter your name"; N8$\n')
f.write('30 PRINT A1$\n')
f.write('40 PRINT "Hello, "; N8$; "!"\n')
f.write('50 END\n')
with open("program.bas", "r") as f:
for line in f:
parse_basic_line(line.strip())
Uploading my version for GROK to review, GROK says "The BEEDAT2.8 program is a functional database management system written in BASIC, designed for a microcomputer with direct memory and screen control. It uses a simple text file (BEEDAT.FIL) for storage and supports basic CRUD (Create, Read, Update, Delete) operations, sorting, and searching. The variable list includes scalar numeric and string variables for control flow and user input, and string arrays for data storage and manipulation. The program’s design reflects the constraints and conventions of early 1980s microcomputer programming."
Program Overview.
The BEEDAT2.8 program is adapted and modified from the tape program in WILDCARDS vol 3, using the disk access code that was included in WILDCARDS 3. The original author provided a UI/screen design that reflected both the Microbee UI -think original WordBee- and text input terminals of the '70's. I have resisted changing the UI/screen design both to pay homage to Beedat 20 and to reflect the look and feel of text based interfaces of yesteryear.
I wanted to attempt to increase user interaction and provide a wider scope of use. Listing now allows exiting anytime during the record display. I recoded sorting/searching automatically across all fields, with a visual indication that sorting is underway. The program sorts A-Z followed by a-z, on user specified field. I have used a 40-50 record database file to test the program. To QUIT the program use CTRL C.
Issues: The KEY command appears erratic at times, and I hard coded a "N" on line 2030 to solve this. In the sort routine the variable M caused crashes, and I had to use FLT to get the division right. I am not sure if these two issues are due to using BASIC version 6.23e or using ubee512 emulator, or an interaction of both.
The program BEEDAT2.8 is a simple DBMS with the following key functionalities:
SORT (Lines 140–295): Implements a Comb Sort algorithm to sort records based on a user-specified field (1–5).
LOAD (Lines 300–380): Loads records from BEEDAT.FIL into a two-dimensional string array D0$. It checks for end-of-file (EOF) and validates the load process.
SAVE (Lines 390–520): Saves the current records from D0$ to BEEDAT.FIL.
INSERT (Lines 530–630): Adds a new record by prompting for input for each of the five fields, validates field length, and sorts the records after insertion.
DELETE (Lines 780–870): Removes a record by shifting subsequent records up and clearing the last record.
EDIT (Lines 880–1020): Allows editing of a specific field in a record, with length validation, followed by re-sorting.
FIND (Lines 1030–1150): Searches for a string across all fields in all records and displays matches, allowing the user to confirm or continue searching.
LIST (Lines 1160–1255): Displays records on the screen, pausing every three records for user input. The sort is ascending by default but can be modified for descending order, I leave this as an exercise to the user. REM gives a clue. pressing X will exit.
PRINT (Lines 1260–1330): Sends records to a printer using LPRINT. I have been UNABLE to verify this code section.
HELP (Lines 2040–2260): Displays a menu of available commands.
Control Loop (Lines 2060–2120): Provides a command-line interface where users input single-letter commands (C, D, E, F, G, H, I, L, P, R, S) to invoke corresponding functions.
Key variables:
Numeric Variables:
A: Loop counter or index, used in various subroutines (e.g., listing records, copying records).
C: Cursor position, initialized to 960 (line 130) and used in subroutine at line 2010 to clear a portion of the screen.
D: Destination record index, used in delete, edit, and copy operations.
F: Field number (1–5) for editing a specific field in a record.
I: General-purpose index, used as a loop counter or record index in multiple subroutines (e.g., load, save, list).
J: Field index (1–5) or secondary loop counter in sorting and searching.
K: Loop counter, used in sorting and searching subroutines.
L: Field number (1–5) for sorting, input by the user.
M: Gap size in the Comb Sort algorithm. The original program had a much simpler use of this but I had to use m1 for gap size.
M1:Temporary floating-point variable for calculating the gap size (M1 = M / 1.3). This consumed several hours of debugging.
N: Loop counter for swapping fields in the sort subroutine.
Q: Running total of field lengths during insertion to ensure the total does not exceed 170 characters.
R: Number of records in the database, updated during load, insert, and delete operations.
S: Flag indicating whether a swap occurred in the sort subroutine (1 if swapped, 0 otherwise).
T: Tab position for menu display (read from DATA in line 2230).
V: Length of a field’s content during insertion or editing, used for validation.
Z: Command code derived from the ASCII value of the input command (Z = ASC(Z7$) - 65) or counter for listing records.
String Variables:
N1$: Used in the print subroutine (line 1270) but not assigned a value in the provided code, likely intended for a header or printer control string.
S1$: Input string for sorting field number or search query.
Z7$: General-purpose string for user input (e.g., commands, confirmation in search, field values).
Array Variables
String Arrays:
D0$(100,5): Main database array, storing up to 100 records with 5 fields each. Each element D0$(I,J) holds the value of field J for record I.
D1$(5): Temporary array for storing a new or edited record’s fields during insertion or editing.
T1$(5): Temporary array used during sorting to swap records.I added this as I found D1$(5) was overworked leading to spaghetti code.
Notes on Variables
Array Dimensions: D0$ is dimensioned to hold 100 records with 5 fields each, while D1$ and T1$ are single-dimensional arrays for 5 fields String Array STRS (5000): Declared in line 110, to allocate string memory.
Uninitialized Variables: N1$ is referenced in the print subroutine but not assigned, being deprecated from original.
Memory Locations: The use of POKE 61440+R,13 and POKE 61438+I,9 are direct screen memory manipulation, for screen control whilst screen is off during disk read write operations.
Additional Notes
Potential Issues:The program assumes BEEDAT.FIL exists and is correctly formatted during loading, with minimal error handling for file issues. I used Grok to generate a 40 record x 5 list of words that I saved as "beedat.fil". From command menu G loads the beedat.fil file, whilst R saves any modifications,insertions of new records or record deletion.
The END statement (line 2264) follows debug print statements (lines 2261–2263).
Data Structure: The DATA statements (lines 2230–2260) define the help menu, with 12 commands
and a tab position of 43 for centered display.
Possible future additions:
As noted above listing from Z-A by adding two lines of code.
Allowing other files to be saved and loaded by adding a file name input section.
Changing the UI/Screen display ( Menu along bottom, data display formatted differently)
Increasing Record nos to 200 records of 4 fields etc.
Consolidating duplicated code in sorting/
If anyone is interested I can supply both beedat28.mwb and the beedat.fil file.
PS: An interesting aside -the BEEDAT program had 153 lines of code, Beedat2.8 has 158 lines. I used AI to discuss and modify optimisation routines. I mainly used GROK which would not accept Microbee basic DIM D0(100), every new question it would insist on a new line 120 DIM D0$(100) etc. It would accept my explanation and kept referring to correct syntax in subsequent questions. However, when I commenced a new session it would revert back to DIM d0$(100). As the old adage goes "dumb machines." However,I can forsee a time in the next three-five years when GROK and other AI's will natively run Microbee BASIC. At present it emulates BASIC code in python to run the code. It took brute force for it to accept variable naming constructs, often confusing MBASIC with Microbee BASIC. I assume the sheer number of people querying the LLM means that realtime reasoning and logic flowpaths in GROK breakdown to some extent. Scary for actual real world issues. I would not use AI to run mission critical business computing.
Apart from the Microbee manuals in the repository I also found
https://mbee.compoutpost.com/basic-reference/ useful for testing code snippets.
GROK emulation of Microbee Basic in action:
import re
def is_valid_string_var(var):
# Matches one letter (A-Z), one digit (0-9), then $
return bool(re.match(r'^[A-Z][0-9]\$', var))
def parse_basic_line(line):
# Match line number and command
match = re.match(r'^\s*(\d+)\s+(\w+)\s*(.*)', line)
if not match:
print(f"Invalid line: {line}")
return
line_num, command, args = match.groups()
# Handle string assignments (e.g., A1$ = "Text")
if command == "LET" or (command not in ["DIM", "INPUT", "PRINT", "END"] and '=' in args):
var, value = args.split('=', 1)
var = var.strip()
value = value.strip().strip('"')
if is_valid_string_var(var):
print(f"Assigned {var} = {value}")
else:
print(f"Invalid string variable: {var} (must be one letter, one digit, then $)")
# Handle INPUT (e.g., INPUT "prompt"; N8$)
elif command == "INPUT":
parts = args.split(';', 1)
if len(parts) == 2:
prompt = parts[0].strip().strip('"')
var = parts[1].strip()
if is_valid_string_var(var):
user_input = input(prompt + " ")
print(f"Stored in {var}: {user_input}")
else:
print(f"Invalid string variable: {var} (must be one letter, one digit, then $)")
else:
print(f"Invalid INPUT syntax: {args}")
# Handle PRINT
elif command == "PRINT":
# Check for string variables in PRINT
tokens = args.split(';')
for token in tokens:
token = token.strip().strip('"')
if token.endswith('$') and not is_valid_string_var(token):
print(f"Invalid string variable in PRINT: {token} (must be one letter, one digit, then $)")
print(f"Output: {args.strip()}")
# Create and test a .bas file
with open("program.bas", "w") as f:
f.write('10 A1$ = "Welcome to MicroBee BASIC!"\n')
f.write('20 INPUT "Enter your name"; N8$\n')
f.write('30 PRINT A1$\n')
f.write('40 PRINT "Hello, "; N8$; "!"\n')
f.write('50 END\n')
with open("program.bas", "r") as f:
for line in f:
parse_basic_line(line.strip())
