Lang Directive
Standard FreeBASIC is often less concise than other dialects like QBasic and fblite. A large part of this is that FreeBASIC requires explicit variable declaration, whereas the other dialects don't. Print j+1
is a perfectly valid QBasic program; since j
hasn't been declared it will default to a value of 0. Both alternate dialects allow the variable suffix system to specify the type instead of using dim
, but in turn, disallow the use of var
.
%
for Integer (default for fblite)
&
for Long
!
for Single (default for qb)
#
for Double
$
for String
E.g. a$ = "abc"
is a string variable, which has a default of an empty string.
This can be taken advantage of using BASIC's #lang
directive, which can be placed in a program to switch which dialect it uses. #lang "qb"
sets the language to QBasic; #lang "fblite"
sets it to fblite.
QBasic is often preferable to fblite because it requires less bytes in the lang directive, but QBasic lacks some functions that fblite and FreeBASIC such as iif()
(replaced by __iif()
) and some string functions need the suffix $
(e.g. chr$()
and mid$()
)
See https://freebasic.net/wiki/CompilerDialects for more info about the differences between dialects.
QBasic also has a CGSE tips page.
Open strings
If a string literal appears at the end of a line, it doesn't need a closing quotation mark. ?"Hello
is perfectly valid. This also helps with the #lang
directive since it uses a string, ie. #lang"qb
.
Printing
?
is a shorter alias for Print
, ie. ?"Hello"
vs Print"Hello"
. You can just have ?
on a line to print a newline on its own. Elements can be separated by semicolons to print without newlines, or with a trailing semicolon to have no newline.
?"abc";i;"def" ' Print number in string
? ' Print just a newline
?"no newline"; ' Print string with no newline
When printing a positive signed integer, BASIC will append a space beforehand, so variables will need to be converted to unsigned or stringified.
var i=5u:?i ' Declare it to be unsigned to begin with
var i=5:?i+0u ' Convert to unsigned
var i=5:?""&i ' Stringify it (can't do ?i&", since that looks like a suffix)
i=5:?""&i ' Use alternate dialects to avoid var (note that qb prints integers with a trailing space)
Remove spaces after numbers
Spaces can be left out after number literals. This can help in for loops (see below), and other infix operators. As a specific example, moduloing by a power of 2 can be replaced with an and
call, e.g. x mod 2
=> 1and x
.
Looping
There are a few ways to loop in BASIC:
var i=0:for i=0to 99:code:next
var i=0:while i<99:code:i+=1:wend
var i=0:a:code:i+=1:if i<99 goto a
var i=0:do:code:*ERROR*i+=1:loop ' Termination by error (details are left as an exercise to the reader)
These are useful in various contexts. The for loop is the shortest and most straightforward. The while loop is good if you're looping until a loop variable is zero or if you don't want to increment the variable. The goto is good for infinite loops, terminating on errors (e.g. going to the wrong place in memory). The goto can also be used in conjunction with other labels, using on x goto l1,l2,l3
, which doesn't jump if x is not in 1,2,3.
Don't use step
, it is shorter to just add whatever to the variable in the loop:
for i=0to 99step 9:code:next
for i=0to 99:code:i+=8:next
Negative steps can start with a negated start where you negate it in the loop.
for i=99to 0step-1:?i:next
for i=-99to 0:?-i:next
Consecutive next
s can be combined for a single byte save (or more if you manage to line up three or more)
for i=1To 10
for j=1To 2
' ...
next j,i
Misc tips
- Print a string conditionally with
mid
or iif
, e.g. mid("str",-(i=1))
or iif(i=1,"str","")
- The data section in the qb dialect can have strings separated by commas without quotes
- Escape a string with
!
, e.g. ?!"\0abc\n"
. Using a string function with a null byte in it will terminate the string at the first null.
- Manipulating strings as pointers is often useful, e.g.
*(@"string"+2)
- You can index into raw strings by encasing them in parentheses, e.g.
("abcd")[0]
- Argv is stored pretty much sequentially, both as a list of pointers to strings, and the actual args with nulls between
a mod b
is the same size as a/b=a\b
- Single line if statements with
if b then code:etc:etc