Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/parkertomatoes/basbolt

A QuickBASIC Compiler Explorer
https://github.com/parkertomatoes/basbolt

assembly basic compiler qbasic quickbasic

Last synced: about 8 hours ago
JSON representation

A QuickBASIC Compiler Explorer

Awesome Lists containing this project

README

        

# BasBolt

## Overview
BasBolt is an in-browser compiler explorer for QuickBASIC. It automatically
compiles code into 16-bit assembly using Microsoft BASIC Compiler as you type,
and integrates the results into the editor.

It shows generated assembly, colored to identify source code regions
![inline errors screenshot](doc/nibbles.png)

It also marks errors in the source code:
![inline errors screenshot](doc/error.png)

[Live Demo](https://parkertomatoes.github.io/basbolt/) - for a bonus, click a keyword and press F1!

## How It Works
The editor keeps a [V86 emulator](https://copy.sh/v86) instance in the background, running a small server on FreeDOS to facilitate communication. The server written in QuickBASIC (of course).

Whenever the code in the editor changes:
1. The editor transfers the source code to the server
2. The server saves the source code to a file
3. The server invokes BC.EXE (Microsoft BASIC Compiler) to compile
4. The server transfers the listing file output to the editor
5. The editor parses the listing file and annotates the source code

### Transferring Files

V86 doesn't have an API to access FAT disks, but file transfer is made possible by reading and writing to the emulated system's RAM:
![compilation flow](doc/compilation.png)

Since 16-bit real mode DOS doesn't concern itself about any silly nonsense like virtual memory protection, The server can just allocate a buffer, do a little arithmetic with the segment and offset, and write the physical address to the console:
```basic
CONST buffersize& = 16384
DIM SHARED buffer AS STRING * 16384

segment& = VARSEG(buffer)
IF segment& < 0 THEN segment& = 1 - segment&
pointer& = VARPTR(buffer)
IF pointer& < 0 THEN pointer& = 1 - pointer&
bufferaddr& = segment& * 16 + pointer&

PRINT USING "(buffer&, size&) "; STR$(bufferaddr&); STR$(buffersize&);
```
Which prints something like this, redirected to COM1:
```
(buffer 123456, size: 16384)
```
The compilation server code can be found in [another repostiory](https://github.com/parkertomatoes/basbolt-image)

The editor reads the pointer using the V86's serial API. V86 also has a convenient API to read and write blocks of memory in the emulated system:

```javascript
const bytes = emulator.read_memory(address, size);
emulator.write_memory(bytes, address);
```
Since the RAM is implemented as a `Uint8Array`, and `read_memory` is implemented with `UInt8Array.prototype.subarray`, we can effectively access shared memory. 16KB blocks are written at one time. A byte is sent over COM1 to signal that the data is ready to copied, and a message is sent back over COM1 to signal that the server is ready for more data. In formal CS literature, this transaction is called a podunk direct memory access (PDMA) transfer.

### Compiling

To compile, the server invokes the `SHELL` command to run BC.EXE. Passing with the /A option generates a .lst file that includes each line of source, followed by the assembly for that line and any errors.
```
BC.EXE /A /O JOB.BAS JOB.OBJ JOB.LST
```

job.bas:
```basic
PRIN "HELLO WORLD"
```

job.lst:
```txt
PAGE 1
12 Dec 20
16:34:29
Offset Data Source Line Microsoft (R) BASIC Compiler Version 7.10

0030 0006 PRIN "HELLO WORLD"
^ Equal sign missing
0030 ** I00002: call B$CENP
0035 0006

46074 Bytes Available
45976 Bytes Free

0 Warning Error(s)
1 Severe Error(s)
```
_yes, it's paginated and formatted to 80 characters for your dot matrix printer_

Errors and assembly don't include any explicit line numbers and columns. But by counting lines of code, and spaces between the start of the line and the "^" for errors, the line and column of errors can be determined. The listing file is parsed using a combination of regular expressions and a simple state machine.

The assembly mappings in the listing files are not as fine-grained as the ones generated by modern compilers. It appears to only associate entire blocks of source code between branches and labels, with blocks of assembly. But it's still enough to give you a near rough idea of what source code becomes what assembly.

## Building

### 1. Add BIOS Images
For X86 emulation with V86, a BIOS image is required to function:
* [SeaBIOS](https://www.seabios.org/downloads/) is used as the bios, and should be placed in `images/seabios.bin`
* [Bochs VGABios](http://www.nongnu.org/vgabios/#DOWNLOAD) is used as the VGA bios, and should be placed in `images/vgabios.bin`

### 2. Build the compilation server
A hard disk image containing the compilation server must be built. It can be found here:
https://github.com/parkertomatoes/basbolt-image

Run the makefile in that repository to build `basbolt.img`, then copy it to `images/basbolt.img`.

### 3. Building

The project is packaged with npm and webpack, and can be built using the following commands:
```
npm install
npm run buildRelease
```
If the bundling is successful, the contents of `/dist` can be deployed to any webserver.

## Roadmap
This project is currently a fun tech demo that started as a joke, and was written as a way for me to practice writing a semi-complex project using React Hooks.

But it would be neat to make something like a tweakable sandbox for showcasing old Q(uick)?BASIC games and demos. Eventual features could include

* Linking
* Running
* Multiple sources
* Non-source files
* Multiple compilers (QB 1-4, PDS 7.1, VBDOS 1.0, etc)
* In-app help, etc

## Contributing
Are you of sound mind? And want to contribute? Welcome to that very narrow middle of that Venn diagram, friend. I'll happily review any issues or pull requests.