Thursday, 22 May 2025

Improving MSBASIC

Intro

Our 6502 Basic programming language implementation is quite comprehensive, but it does have some big problems.  In particular, since we didn't implement the LOAD and SAVE commands when setting up basic we have to start inputting lines of code from scratch each time we start MSBASIC.  Since BEN2 possesses no disk storage we cannot easily implement those commands.

A good, effective, convenient and practical workaround is to cut and paste programs from a PC editor into our terminal window.  This technique is ok for a single line of code but the MSBASIC program cant process larger chunks of code arriving quickly.
The first step in solving this problem, which Ben describes, is to receive characters into a 256 byte buffer, from which BASIC can read them at a more leisurely pace.
However, when reading in larger programs the 256 byte buffer fills up and characters are dropped.  To resolve this Ben implements RTS/CTS hardware flow control.  When the buffer is nearly full the 6502 sets the serial pin RTS low which causes the the terminal emulator to stop sending characters.  When the buffer is mostly empty the 6502 sets RTS high at which point more characters can be sent.

Ben explains how to implement these features in two videos How input buffering works and RS232 Flow Control.

Input Buffering

The original MSBASIC program uses a subroutine CHRIN to read characters in from the ACIA UART serial interface.  If no character is ready CHRIN waits until a key is pressed.  This works fine when a human (me) is typing on the keyboard as the program can process characters faster than I can type them.


We are going to create a buffer which will hold upto 256 characters which have been "pasted" into the terminal screen so that MSBASIC doesn't need to read them immediately.  Characters are placed in the buffer as soon as they arrive and are stored in the buffer at a location pointed to by WRITE_PTR, which is then incremented.  When MSBASIC needs to read  a character it uses the location pointed to by READ_PTR which is then incremented.  If READ_PTR=WRITE_PTR no character is available so the program waits.

We need to use an interrupt mechanism to help us with this.  The ACIA UART has an interrupt (pin 26) which we connect to the Interrupt (pin 4) on the 6502.  Now the 6502 interrupt pin will go high when a the ACIA has received a character.
In the 6502 software we enable interrupts and setup an interrupt vector at address 0xFFFE and 0xFFFF.
When an interrupt is received MSBASIC will jump to the interrupt vector address and run the interrupt service route to read in the character to the buffer.

On completion it will return from the interrupt to continue whatever program is running.
When MSBASIC needs to read character in we use a modified version of CHRIN.  First it calls a subroutine BUFFER_SIZE to see if there are any characters in the buffer, if not it waits for characters.
If characters are available CHRIN calls READ_BUFFER to get the next character and increment READ_PTR.

This mechanism works very nicely for small chunks of data pasted in to the terminal but fails if there are more than 256 bytes waiting.

The mechanism is called a circular buffer. The WRITE_PTR variable can take values from 0 to 255.  When the pointer is incremented from 255 it resets to 0 so the buffer wraps around.  Similarly READ_PTR "follows" it so the buffer continues to work.

When WOZMON, the monitor, starts we initialise the buffer so READ_PTR=WRITE_PTR and the bufer is empty before we enable interrupts. We must also use the modified CHRIN subroutine to read characters in from WOZMON to the buffer.

Flow Control

The RS232 protocol specifies wires in the cable to be used for flow control, the ones we will use are CTS/RTS.  On the PC end of the cable the serial driver looks at the CTS "Clear To Send" wire and if the voltage is set low the serial driver will not send any more characters until  the voltage is set high.  On the 6502 end the same wire is connected to RTS "Request to Send" (more appropriately "Ready for Receiving") which the 6502 will set low if it cant cope with any more characters.

Previously our serial UART only used four wires 5V, GND, RX, TX.  We need to make sure our RS232 cable or FTDI connector has six wires including CTS/RTS.  On the PC side the cable still plugs into a USB port.  On the 6502 side we connect a BLUE wire to RTS.

Now all we have to do is update our interrupt handler so that if the buffer is nearly full we set RTS low to stop communication.  When the buffer has plenty of space, set it high again.  In fact the ACIA RTS pin doesn't work very well so we use PORTA PA0 instead.


Lastly we need to tell PC terminal emulation software picocom or TeraTerm that it should use hardware flow control, otherwise the terminal emulator will just keep sending data.

It works, I can cut and paste a large BASIC program (say 500 lines) into the terminal window and it will be read in correctly, although it takes a few seconds.

Outro

Input buffering and flow control are vital but not very interesting.  It is pretty awesome how straightforward it is to program it on BEN2 with Bens help.  By implementing both buffering and flow control we have now written a proper serial driver on our 6502.
The benefits of this work are huge.  There is no way anyone would type a BASIC program in each time BEN2 was started up, but it isn't much work to spend a few seconds cutting and pasting a file before you start.



3D Printing : STL


Intro

I have started printing a variety of models on my Elegoo Neptune 3d printer.  3D models which I make using Tinkercad or are available at 3D websites are predominantly defined in the STL file format.  Once a suitable STL file has been created and saved the Cura app is used to "slice" them into layers and printing instructions are saved in TFT / gcode files which are sent to the printer.

STL stands for stereolithography but is better translated as "Standard Triangle Language". It makes 3D shapes by combining 2D triangles.  Curved surfaces are approximately with lots of triangles.  As you make the triangle smaller, you can make smoother surfaces at the expense of using more and more triangles.  Most STL files are in an unreadable binary format but ASCII STL is a valid alternative where points, polygons and surfaces are defined in a readable format.

It occurred to me that it should be interesting and straightforward to create simple ASCII STL models, e.g. for polyhedra.  In particular, the simplest polyhedron is a tetrahedron which comprises four vertices and four triangular edges.

Rules

The STL file format is very simple, you define a number of facets (i.e. triangular faces) each with three vertices.  You should also define a unit vector which is normal to the surface in the triangle.
A "solid" is made up of a set of facets.
There are some simple rules which are explained below:
1  The normal should be pointing outwards
2 Vertices should be listed anti-clockwise.
3 Each triangle should share two vertices
4 All vertices should be positive values.
5 values should be exponentials e.g. +2.00e03


It seems that, in practice, it is not necessary to follow all the rules.
As I want to read my STL files into Tinkercad or Cura I ignore any rules that they dont appear to require.

I didn't specify normal values as they are difficult to work out.
It appears that values dont have to be positive.
I used floating point or integers instead of exponentials.



Googling

I started by googling for ASCII STL examples but there don't appear to be many out there.  Florida State University have a Computational Geometry course, which contains a number of examples I may look at.


A couple of printables users Ambrosia and GaelLaFond provide details of STL tetrahedrons, including ASCII STL, which I will investigate further.


There seem to be a number of python apps which help with STL generation, Hand-and-Machine looks the best, it appears to generate various polyhedra in ASCII STL form.

A simple calculator for working out tetrahedron co-ordinates, an STL file viewer and some notes written by someone writing his own STL files from scratch may be useful.


As I hoped I came across an app, called OpenSCAD, which will be extremely helpful if I want to write STL to generate geometric shapes. Gael LaFond's tetrahedron was written in openscad as shown below.
There is a little quirk with OpenSCAD, in that it will only export ASCII STL files if you run it from the command line.  However, that isn't too much of a problem.



Tetrahedron

I have two possible files for my simplest possible STL object, from printables users Ambrosia and GaelLaFond . 

Ambrosia
Gael LaFond

Ambrosia's tetrahedron definition is very simple.  Normals are all set to zero, vertices are at unit addresses and are set as integers.
Unfortunately there is an error somewhere and I cant import the file into Tinkercad.
In contrast Gael LaFond provides a regular tetrahedron with all faces the same.  For a regular tetrahedron some of the vertices will always be at non-integer values.

I decided to make my simplest tetrahedron like Ambrosia's.
I started from Gael LaFond tetrahedron then changed the vertices to have unit co-ordinates and set normals to zero.







The result loads perfectly. We now have our simplest tetrahedron.  It isn't at all regular but it is very simple.  It has vertices (0,0,0), (1,0,0), (1,1,0), (1,1,1).
It is also very small as each co-ordinate is only 1 unit.  However choosing the units as inches makes it 25.4 times larger, alternatively it is easy to enlarge within Tinkercad.

I wanted to improve my design a bit. Firstly I wanted to make the tetrahedron with a right angle corner at the origin.  This has co-ordinates origin O(0,0,0), x-axis X(1,0,0), y-axis Y(0,1,0), z-axis Z(0,0,1).
I also found that the vertex command can have a description after it, so I added the vertex name after each.  This makes it easier to identify the order for the four facets (O,X,Z), (O,Y,X), (OZ,Y), (X,Y,Z).




Outro


It is interesting to understand how STL files are used to make up 3D objects and to be able to define them myself.  The files quickly become very complicated as they grow so a tetrahedron, as the simplest example is very instructive.  To me the hardest part is putting the vertices in anti-clockwise order when speciying triangular facets.  I suspect it would be very difficult to construct more than a few shapes manually.  I aim to be able to create more STL files using OpenSCAD, particularly poly


Thursday, 8 May 2025

MSBASIC for 6502

BASIC


The BASIC programming language plays an important part in computer history.  On early computers it was often the only high level language implemented and the only way of programming other than wrestling with the complexities of assembly language.

The Altair 8800 is widely accepted as the first Personal Computer, it was released in December 1974 and featured in Popular Electronics magazine in January 1975.  I have some recollection of reading about it at the time and wanting to buy one - although it would have been much too expensive for me and impractical to obtain one from America.


A couple of Harvard students, Bill Gates and Paul Allen, wrote a version of the BASIC language for the Altair and it became the first product of the company they formed called Microsoft.

To celebrate 50 years of Microsoft Bill Gates released the original BASIC source code 


Sometime later, when the IBM PC was released  in 1981, Microsoft won the contract to supply the PC DOS Operating System, and BASIC.
When I first used IBM PCs they contained no hard drive, just floppy disks.  If you forgot to put a floppy disk in the computer it would start running cassette basic.


I have known for some time that MSBASIC has been implemented in 6502 assembly language and it has been on my bucket list to try and get it working.  Luckily for me Ben Eater also decided to implement BASIC and he has provided a tutorial to show you how to do it on his 6502 system.  He also provides his assembly code version of MSBASIC to make it easy for us acolytes to follow in his footsteps.

Building MSBASIC for BEN2

The 6502 BASIC code is available on Github, and most of the files are 17 years old, reflecting the time when code was released to the public domain.
Rather unusually for Ben, he uses a trial and error method to build BASIC.   There are three main changes, the variables defined, character input and output, and memory configuration.  The Commodore Basic variables were used initally and adjusted to fit Ben's 6502.  We just have to provide our own I/O routines which are readily available and specify the details of our memory in eater.cfg.



Ben programs a ROM chip each time he modifies a program so he creates a 32KB image. My 6502 system usually has software loaded via the serial port so I dont have to remove/insert chips each time I make a change, since I find it incredibly tedious.
Unfortunately the BASIC program is about 9kB in size and much bigger than my usual programs.  My serial download program fell over so I had to revert to Bens ROM burning approach.

This required me to learn a little more about the assembler and linker utilities CA65 and LD65 so that I could choose where to locate BASIC in ROM.  Ben stores MSBASIC at the start of ROM address $8000 and the WOZMON monitor at $FE00 at the end of ROM.  I modified my system so that WOZMON could be stored at the end of ROM and BASIC is stored at $C000 in the middle.  This means I can still use my monitor MON5 at $A001 or the backup MON4 at $8001.


First startup of BASIC running on BEN2 felt awesome, suddenly we have a computer that is easy to write programs for.  Ben mentioned that there is a little "Easter egg" in the program.  When prompted for memory size, if you type "A", the names of the programmers "Weiland and Gates" are displayed.

So my last two projects have used source code written by Steve Wozniak, founder of Apple, and Bill Gates, founder of Microsoft.

As always there are some enhancements we need to make but this is definitely a leap forward.