Basic Reference
This is a reference for Neo6502's BASIC interpreter.
There are many example programs available which are designed to show and explain the features of the Neo6502 and its hardware and firmware in the examples directory of the release.
For example if.bsc/if.bas so the options available for the 'if' statement, graphics.bsc/graphics.bas show the drawing commands, and joypad.bsc/joypad.bas show how to access the joypad (or the keyboard backup if none is plugged in)
Many of these are helpful for understanding specific API functions, as many BASIC commands are just wrappers for those functions.
Binary Operators
Precedence | Operator | Notes |
---|---|---|
4 | * | |
4 | / | Forward slash is floating point divide. 22/7 is 3.142857 |
4 | \ | Backward slash is integer divide, 22\7 is 3 |
4 | % | Modulus of integer division ignoring signs |
4 | >> | Logical shift right, highest bit zero |
4 | << | Logical shift left |
3 | + | |
3 | - | |
2 | < | Return -1 for true,0 for false |
2 | <= | Return -1 for true,0 for false |
2 | > | Return -1 for true,0 for false |
2 | >= | Return -1 for true,0 for false |
2 | <> | Return -1 for true,0 for false |
2 | = | Return -1 for true,0 for false |
1 | & | Binary AND operator on integers |
1 | | | Binary OR operator on integers |
1 | ^ | Binary XOR operator on integers |
Arithmetic functions
Operator | Notes |
---|---|
alloc(n) | Allocate n bytes of memory, return address |
analog(n) | Read voltage level on pin n -- returns a value from 0 to 4095 |
asc(s$) | Return ASCII value of first character or zero for empty string |
atan(n) | Arctangent of n in degrees |
atan2(y,x) | Arctangent 2 calculation, dy / dx => angle |
chr$(n) | Convert ASCII to string |
cos(n) | Cosine of n, n Is in degrees. |
deek(a) | Read word value at a |
eof(f) | Returns non-zero value if at end of file f. |
err | Current error number |
erl | Current error line number |
event(v,r) | event takes an integer variable and a fire rate (r) in 1/100 s, and uses the integer variable to return -1 at that rate. If the value in 'v' is zero, it resets (if you pause say), if the value in v is -1 the timer will not fire -- to unfreeze, set it to zero and it will resynchronise. |
exists(file$) | Returns true (-1) if the file exists, false (0) otherwise |
exp(n) | e to the power n |
false | Return constant 0, improves boolean readability |
havemouse() | Return non zero if a mouse is connected. |
himem | First byte after end of memory -- the stack is allocated below here, and string memory below that. |
inkey$() | Return the key stroke if one is in the keyboard buffer, otherwise returns a n empty string. |
idevice(device) | Returns true if i2c device present. |
iread(device,register) | Read byte from I2C Device Register |
instr(str$,search$) | Returns the first position of search$ in str$, indexed from 1. Returns zero if not found. |
int(n) | Whole part of the float value n. Integers are unchanged. |
isval(s$) | Converts string to number, returns -1 if okay, 0 if fails. |
joycount() | Read the number of attached joypads, not including keyboard emulation of one. |
joypad([index],dx,dy) | Reads the current joypad. The return value has bit 0 set if A is pressed, bit 1 set if B is pressed. Values -1,0 or 1 are placed into dx,dy representing movement on the D-Pad. If there is no gamepad plugged in (at the time of writing it doesn't work) the key equivalents are WASDOP and the cursor keys. If [index] is provided it is a specific joypad (from 1,0 is the keyboard), otherwise it is a composite of all of them. |
key(n) | Return the state of the given key. The key is the USB HID key scan code. |
left$(a$,n) | Left most n characters of a$ |
len(a$) | Return length of string in characters. |
locale a$ | Sets the locale to the 2 character country code a$ e.g. locale "de" |
log(n) | Natural Logarithm (e.g. ln2) of n. |
lower$(a$) | Convert a string to lower case |
max(a,b) | Return the largest of a and b (numbers or strings) |
mid$(a$,f[,s]) | Characters from a$ starting at f (1 indexed), s characters, s is optional and defaults to the rest of the line. |
min(a,b) | Return the smaller of a and b (numbers or strings) |
mos(command) | Like the mos command, but returns an non zero error code if the command caused an error. |
mouse(x,y[,scroll]) | Reads the mouse. The return value indicates button state (bit 0 left, bit 1 right), and the mouse position and also the scrolling wheel position are updated into the given variables. |
not [term] | Returns the logical not of a term. |
notes(c) | Return the number of notes outstanding on channel c including the one currently playing -- so will be zero when the channel goes silent. |
page | Return the address of the program base (e.g. the variable table) |
peek(a) | Read byte value at a |
pin(n) | Return value on UEXT pin n if input, output latch value if output. |
point(x,y) | Read the screen pixel at coordinates x,y. This is graphics data only. |
pow(a,b) | Returns a raised to the power b ; the result is always floating point. |
rand(n) | Random integer 0 { x { n (e.g. 0 to n-1) |
right$(a$,n) | Rightmost n characters of a$ |
rnd(n) | Random number 0 { x { 1, ignores n. |
sin(n) | Sine of n, n Is in degrees. |
spc(n) | Returns a string of n spaces. |
spoint(x,y) | Reads the colour index on the sprite layer. 0 is transparency |
sqr(n) | Square root of n |
str$(n) | Convert n to a string |
tab(n) | Advance to screen column n if not past it already. |
tan(n) | Tangent of n, n Is in degrees. |
true | Return constant -1, improves boolean readability |
time() | Return time since power on in 100^th^ of a seconds. |
uhasdata() | Return true if there is data in the UART Receive buffer. |
upper$(a$) | Convert a string to upper case |
val(s$) | Convert string to number. Error if bad number. |
vblanks() | Return the number of vblanks since power on. This is updated at the start of the vblank period. |
BASIC Commands (General)
Command | Notes |
---|---|
' {string} | Comment. This is a string for syntactic consistency. The tokeniser will process a line that doesn't have speech marks as this is not common, so you can type in ' hello world and it will be represented as ' "hello world" in code. |
assert {expr}[,{msg}] | Error generated if {expr} is zero, with optional message. |
call {name}(p1,p2,p3) | Call named procedure with optional parameters. |
cat [{pattern}] | Show contents of current directory, can take an optional string which only displays filenames containing those characters, so cat "ac" only displays files with the sequence ac in them. |
clear [{address}] | Clear out stack, strings, reset all variables. If an address is provided then memory above that will not be touched by BASIC. Note because this resets the stack, it cannot be done in a loop, subroutine or procedure -- they will be forgotten. Also clears the sprites and the sprite layer. |
close [handle] | Close a file by handle. If the handle is not provided, close all files. |
cls | Clear the graphics screen to current background colour. This does not clear sprites. |
cursor {x},{y} | Set the text cursor position |
data {const},.... | DATA statement. For syntactic consistency, strings must be enclosed in quote marks e.g. data |
defchr ch,.... | Define UDG ch (192-255) as a 6x7 font -- should be followed by 7 values from 0-63 representing the |
delete | Delete a line or range of lines |
dim {array}(n,[m]), $... | Dimension a one or two dimension string or number array, up to 255 |
do ... exit ... loop | General loop you can break out of at any point. |
doke {addr},{data} | Write word to address |
edit | Basic Screen Editor |
end | End Program |
fkey | Lists the defined function keys |
fkey {key},{string} | Define the behaviour of F1..F10 -- the characters in the string |
for {var} = {start} to/downto | For loop. Note this is non standard,Limitations are : the index must be an integer. Step can only be 1 (to) or -1 (downto). Next does not specify an index and cannot be used to terminate loops using the 'wrong' index. |
gload {filename} | Load filename into graphics memory. |
gosub {expr} | Call subroutine at line number. For porting only. See goto. |
goto {expr} | Transfer execution to line number. For porting only. Use in general coding is a capital offence. If I write RENUMBER it |
if {expr} then .... | Standard BASIC if, executes command or line number. (IF .. GOTO doesn't work, use IF .. THEN nn) |
if {expr}: .. else .. endif | Extended multiline if, without THEN. The else clause is optional. |
ink fgr[,bgr] | Set the ink foreground and optionally background for the console. |
input {stuff} | Input has an identical syntax and behaviour to Print except that variables are entered via the keyboard rather than printed. |
input #{channel},{var},{var} | Reads a sequence of variables from the open file. |
input line #{channel}.{var}.{var} | Reads text from an ASCII file. This is processed from the source, primarily due to Windows' usage of CR/LF. So this at current, by default, ignores all characters before space, except for LF (10) which marks the end of line, and TAB (9) which is converted into a space. All variables are strings |
ireceive {d},{a},{s} | Send or receive bytes starting at a, count s to or from device d. |
itransmit {d},{a},{s} | |
isend {device},{data} | Send data to i2c {device} ; this is comma seperated data, numbers or strings. If a semicolon is used as a seperator e.g. 4137; then the constant is sent as a 16 bit value. |
iwrite {dev},{reg},{b} | Write byte to I2C Device Register |
let {var} = {expr} | Assignment statement. The LET is optional. |
library | Librarise / Unlibrarise code. |
list [{from}][,][{to}] | List program to display by line number or procedure name. |
list {procedure}() | |
load "file"[,{address}] | Load file to BASIC space or given address. |
local {var},{var} | Local variables, use after PROC, restored at ENDPROC variables can |
mon | Enter the machine code monitor |
mos {command} | Execute MOS command. |
mouse cursor {n} | Select mouse cursor {n} [0 is the default hand pointer] |
mouse show | hide |
mouse TO {x},{y} | Position mouse cursor |
new | Erase Program |
next {variable} | Ends for loop. The variable parameter is optional. You cannot unwind nested FOR/NEXTs , next must operate in order. |
old | Undoes a new. This can fail depending on what has been done since the 'new'. |
on error {code} | Install an error handler that is called when an error occurs. Effectively this is doing a GOTO that code, so recovery is dependent on what you actually |
open input|output [channel],[file] | Open a file for input or output on the given channel, using the given file name. Output erases the current file. This gives an error if the file does not exist ; rather than trap this error it is recommended to use the exists() function if you think the file may not be present. |
palette c,r,g,b | Set colour c to r,g,b values -- these are all 0-255 however it is actually 3:2:3 colour, so they will be approximations. |
palette clear | Reset palette to default |
pin {pin},{value} | Set UEXT {pin} to given value. |
pin {pin} INPUT | output |
poke {addr},{data} | Write byte to address |
print {stuff} | Print strings and numbers, standard format - , is used for |
print #{channel},{expr},{expr} | Writes a sequence of expressions to the open file. |
print line #{channel}.{var}.{var} | Prints a line to an output channel as an ASCII file, in LF format (e.g. lines are seperated by character code 10). This can be mixed with the above format but the sequence has to be the same ; you ann't write a string using print line and read it back with input and vice versa. All variables must be strings. |
proc {name>([ref] p1,p2,...) .. endproc | Delimits procedures, optional parameters, must match call. Parameters can be defined as reference parameters and will return values. Parameters cannot be arrays. |
read {var},... | Read variables from data statements. Types must match those in data statements. |
renumber [{start}] | Renumber the program from start, or from 1000 by default. This does not handle GOTO and GOSUB. Use those, you are on your own. |
repeat .. until {expr} | Execute code until {expr} is true |
restore | Restore data pointer to program start |
restore {line} | Restore data pointer to line number |
return | Return from subroutine called with gosub. |
run | Run Program |
run "{program}" | Load & Run program. |
save "file"[,{adr},{sz}] | Save BASIC program or memory from {adr} length {sz} |
sreceive {a},{s} | Send or receive bytes starting at a, count s to SPI device |
stransmit {a},{s} | |
ssend {data} | Send data to SPI device ; this is comma seperated data, numbers or strings. If a semicolon is used as a seperator e.g. 4137; then the constant is sent as a 16 bit value. |
stop | Halt program with error |
sys {address} | Call 65C02 machine code at given address. Passes contents of variables A,X,Y in those registers. |
tilemap addr,x,y | Define a tilemap. The tilemap data format is in the API. The tilemap is stored in memory at addr, and the offset into the |
uconfig {baud}[,{prt}] | Set the baud rate and protocol for the UART. Currently only 8N1 is supported. |
ureceive {d},{a},{s} | Send or receive bytes to/from the UART starting at a, count s |
utransmit {d},{a},{s} | |
usend {device},{data} | Send data to UART ; this is comma seperated data, numbers or strings. If a semicolon is used |
wait {cs} | Waits for {cs} hundredths of a second |
while {expr} .. wend | Repeat code while expression is true |
who | Display contributors list. |
The Inline Assembler
The inline assembler works in a very similar way to that of the BBC Micro, except that it does not use the square brackets [ and ] to delimit assembler code. Assembler code is in normal BASIC programs.
A simple example shown below (in the samples directory). It prints a row of 10 asterisks.
Most standard 65C02 syntax is supported, except currently you cannot use lsr a ; it has to be just lsr (and similarly for rol, asl, ror,inc and dec).
You can also pass A X Y as variables. So you could delete line 150 and run it with X = 12: sys start which would print 12 asterisks.
Line | Code | Notes |
---|---|---|
100 | mem = alloc(32) | Allocate 32 bytes of memory to store the program code. |
110 | for i = 0 to 1 | We pass through the code twice because of forward referenced labels. This actually doesn't apply here. |
120 | p = mem | P is the code pointer -- it is like $* = {xx} - it means put the code here |
130 | o = i * 3 | Bit 0 is the pass (0 or 1) Bit 1 should display the code generated on pass 2 only, this is stored in 'O' for options. |
140 | .start | Superfluous -- creates a label 'start' -- which contains the address here |
150 | ldx #10 | Use X to count the starts |
160 | .loop1 | Loop position. We can't use loop because it's a keyword |
170 | lda #42 | ASCII code for asterisk |
180 | jsr $fff1 | Monitor instruction to print a character |
190 | dex | Classic 6502 loop |
200 | bne loop1 | |
210 | rts | Return to caller |
220 | next | Do it twice and complete both passes |
230 | sys mem | BASIC instruction to 'call 6502 code'. Could do sys start here. |
[ ] Operator
The [] operator is used like an array, but it is actually a syntactic equivalent of deek and doke, e.g. reading and writing 16 bytes. mem[x] means the 16 bit value in mem + x $* 2, so if mem = 813 then mem[2] = -1 writes a 16 bit word to 817 and 818, and print mem[2] reads it. The index can only be from 0..127
The purpose of this is to provide a clean readable interface to data in 65C02 and other programs running under assembly language ; often accessing elements in the 'array' as a structure.
Zero Page Usage
Neo6502 is a clean machine, rather like the Sharp machines in the 1980s. When BASIC is not running it has no effect on anything, nor does the firmware. It is not like a Commodore 64 (for example) where changing some zero page locations can cause crashes.
However, BASIC does make use of zero page. At the time of writing this is memory locations $10-$41.
These can however be used in machine code programs called via SYS. Only 4 bytes of that usage is system critical (the line pointer and the stack pointer), those are saved on the stack by SYS, so even if you overwrite them it does not matter.
However, you can't use this range to store intermediate values between sys calls. It is advised that you work usage backwards from $FF (as BASIC is developed forwards from $10). It is very unlikely that these will meet in the middle.
$00 and $01 are used on BASIC boot (and maybe other languages later) but this should not affect anything.
Basic Commands (Graphics)
The graphics commands are MOVE, PLOT (draws a pixel), LINE (draws a line) RECT (draws a rectangle) ELLIPSE (draws a circle or ellipse) IMAGE (draws a sprite or tile), TILEDRAW (draws a tilemap) and TEXT (draws text)
The keywords are followed by a sequence of modifiers and commands which do various things as listed below
Keyword | Notes |
---|---|
from x,y | Sets the origin position, can be repeated and optional. |
to x,y | Draw the element at x,y or between the current position and x,y depending on the command. So you could have text "Hello" to 10,10 or rect 0,0 to 100,50 |
by x,y | Same as to but x and y are an offset from the current position |
x,y | Set the current position without doing the action |
ink c | Draw in solid colour c |
ink a,x | Draw by anding the colour with a, and xoring it with x. |
solid | Fill in rectangles and ellipses. For images and text, forces black background. |
frame | Just draw the outline of rectangles and ellipses |
dim n | Set the scaling to n (for TEXT, IMAGE, TILEMAP only), so text "Hello" dim 2 to 10,10 to 10,100 will draw it twice double size. Tiles can only be 1 or 2 (when 2, tiles are drawn double size giving a 32x32 tile map) |
These can be arbitrarily chained together so you can do (say) LINE 0,0 TO 100,10 TO 120,120 TO 0,0 to draw an outline triangle. You can also switch drawing type in mid command, though I probably wouldn't recommend it for clarity.
State is remember until you clear the screen so if you do INK 2 in a graphics command things will be done in colour 2 (green) until finished.
TEXT is followed by one parameter, which is the text to be printed, these too can be repeated
TEXT "Hello" TO 10,10 TEXT "Goodbye" DIM 2 TO 100,10
IMAGE is followed by two parameters, one specifies the image, the second the 'flip'. These can be repeated as for TEXT.
The image parameter is 0-127 for the first 128 tiles, 128-191 for the first 64 16x16 sprites and 192-255 for the first 64 32x32 sprites. The flip parameter, which is optional, is 0 (no flip) 1 (horizontal flip) 2 (vertical flip) 3 (both).
An example would be
image 4 dim 2 to 10,10 image 192,3 dim 1 to 200,10
Note that images are $not$ sprites or tiles, they use the image to draw on the screen in the same way that LINE etc. do.
Sprite Commands
Sprite commands closely resemble the graphics commands.
They begin with SPRITE {n} which sets the working sprite. Options include IMAGE {n} which sets the image, TO {x},{y} which sets the position, FLIP {n} which sets the flip to a number (bit 0 is horizontal flip, bit 1 is vertical flip), ANCHOR {n} which sets the anchor point and BY {x},{y} which sets the position by offset.
With respect to the latter, this is the position from the TO and is used to do attached sprites e.g. you might write.
SPRITE 1 IMAGE 2 TO 200,200 SPRITE 2 IMAGE 3 BY 10,10
Which will draw Sprite 1 and 200,200 and sprite 2 offset at 210,210. It does not offset a sprite from its current position.
As with Graphics these are not all required. It only changes what you specify not all elements are required each time SPRITE 1 IMAGE 3 is fine.
SPRITE can also take the single command CLEAR ; this resets all sprites and removes them from the display
Sprite 127 is used for the turtle sprite, so if the turtle is turned on, then it will adjust its graphic, size to reflect the turtle position. If turtle graphics are not used, it can be used like any other.
Implementation notes
Up to 128 sprites are supported. However, sprite drawing is done by the Pico and is not hardware, so more sprites means the system will run slower.
Additionally, the sprites are currently done with XOR drawing, which causes effects when they overlap. This should not be relied on (it may be replaced by a clear/invalidate system at some point), but the actual implementation should not change.
This is an initial sprite implementation and is quite limited.
(The plan is to add a feature like the animation languages on STOS and AMOS which effectively run a background script on a sprite)
Sprite Support
=spritex(n) =spritey(n)
These return the x and y coordinates of the sprites draw position (currently the centre) respectively.
= hit(sprite#1,sprite#2,distance)
The hit function is designed to do sprite collision. It returns true if the pixel distance between the centre of sprite 1 and the centre of sprite 2 is less than or equal to the distance.
So if you wanted to move a sprite until it collided with another sprite, assuming both are 32x32, the collision distance would be 32 (the distance from the centre to the edge of both sprites added together), so you could write something like :
x = 0
repeat
x = x + 1: sprite 1 to x,40
until hit(1,2,32)
In my experience of this the distance needs to be checked experimentally, as it affects the 'feel' of the game ; sometimes you want near exact collision, sometimes it's about getting the correct feel. It also depends on the shape and size of the sprites, and how they move.
I think it's better than a simple box collision test, and more practical than a pixel based collision test which is very processor heavy.
Sound Commands
The Neo6502 has four sound channels, 0-3 which can generate a square wave or white noise sounds.
The main sound command is called "sound" and has the following forms.
Sound clear
Resets the entire sound system, silences all channels, empties all queues
Sound {channel} clear
Resets a single channel ; silences it, and empties its queue
Sound {channel},{frequency},{time}[,{slide}]
Queues a note on the given channel of the given frequency (in Hz) and time (in centiseconds). These will be played in the background as other notes finish so you can 'queue up' an entire phrase and let it play by itself. The slide value adds that much to the frequency every centisecond allowing some additional effects (note, done in 50Hz ticks)
A mixture of the two syntaxes SOUND 0 CLEAR 440,200 is now supported.
To use the white noise feature use the keyword "noise" instead of sound.
Sfx
Sfx plays sound effects. Sound effects are played immediately as they are usually in response to an event.
It's format is sfx* {channel},{effect***} .
Screen Editor
The edit command starts the screen editor. This currently supports left, right, home, end, backspace and delete on the current line, enter, up, down, page up and page down to change lines.
Esc exits the editor, and Ctrl+P and Ctrl+Q insert and delete a whole line.
Note the editor is slightly eccentric ; it is not a text editor, what it is doing is editing the underlying program on the fly -- much the same as if you were typing lines in.
The editor uses line numbers, so is not compatible with their use in programs. Any program will be renumbered from 1 upwards in steps of 1 (except library routines).
You shouldn't be using line numbers anyway !
Libraries
Libraries are part of the BASIC program, placed at the start. However, their line numbers are all set to zero. (So you cannot use GOTO or GOSUB in libraries, but you should only use them for porting old code).
NEW will not remove them, LIST does not show them (except LIST 0), You cannot edit them using the line editors.
However RUN does not skip them. This is so you can have initialisation code e.g.
{do initialisation code}
if false
proc myhw.dosomething(p1) ....
proc myhw.panic()
endif
To support this, there is a LIBRARY command. LIBRARY on its own undoes the library functionality. It renumbers the whole program from the start, starting from line number 1000.
Otherwise LIBRARY works like LIST. You can do LIBRARY {line} or LIBRARY {from},{to} and similar. Instead of listing this code, it causes them to "vanish" by setting their line numbers to zero.
They are also supported in the makebasic script. Adding the command library makes all code so far library code.
e.g.
python makebasic.zip mylib.bsc library mainprogram.bsc
Turtle Graphics
The Neo6502 has a built in turtle graphics system. This uses sprite $7F as the turtle, which it will take over, so it cannot be used for other purposes.
The following commands are supported.
Command | Purpose |
---|---|
forward {n} | Move turtle forward n (pixel distance) |
left {n} | Rotate turtle at current position |
right {n} | |
penup | Does not draw as the turtle moves |
pendown | Draw as the turtle moves in the current colour |
pendown {n} | Draw as the turtle moves in colour {n} |
turtle home | Reset turtle to home position, facing up the screen. |
turtle hide | Hide the turtle |
turtle show | Show the turtle |
turtle fast | The turtle is deliberately slowed to give it an animated feel so you can see the drawing, this is because it's primary purpose is educational. This makes it go full speed. |
There is an example in the crossdev folder which gives some idea on how to get started.