Skip to content

Source, names & types

This page explains how a d/BASIC program is written down: how lines and statements are laid out, what a name may look like, the six kinds of value the language works with, how you write numbers and text, and what happens when one kind of number is stored into another. After reading it you will be able to read any listing on this site and know why each part is spelled the way it is.

d/BASIC is a BASIC for d/OS, the operating system that brings modern software to 8-bit home computers such as the Atari 800, the Commodore 64 and the Apple II. Its first promise starts right here in the source code: old listings should look familiar, but old accidents should not become new rules.

A source file at a glance

A console program (one that reads and writes text, the way a classic BASIC program does) may contain module directives (settings for the whole file, such as OPTION BASE), declarations, procedures, and ordinary statements:

OPTION BASE 1
DEFINT A-Z

CONST MaxGuests = 8
DECLARE FUNCTION Greeting$ (Name AS STRING)

DIM Guests(MaxGuests) AS STRING
Guests(1) = "Ada"
PRINT Greeting$(Guests(1))

FUNCTION Greeting$ (Name AS STRING)
  Greeting$ = "WELCOME, " + UCASE$(Name)
END FUNCTION

A window program (one that draws in a window and responds to clicks and keys) adds module-level WINDOW SUB and ON ... SUB handlers. d/OS calls these the program's two faces: the console face and the window face. A file with handlers is a window program, and dbasic run opens a window for it; a file with only top-level statements is a console program. One compiled program file carries whichever faces the source declares, and the place it is launched from (the desktop, the browser, a d/OS machine) supplies the window. Classes, records, and procedure bodies stay at module level in either kind of program.

Lines and separators

The rules below talk about a logical line, meaning one complete statement line as the compiler reads it.

  • A newline ends a statement.
  • : separates several statements on one logical line.
  • A decimal whole number at the start of a logical line is a line number (it is read as a 32-bit unsigned value).
  • Name: declares a named label, a place that GOTO and GOSUB can jump to.
  • An apostrophe or REM starts a comment that runs to the end of the logical line.
10 X% = 2: Y% = 3     ' Two statements, one numbered line
20 PRINT X% + Y%

Again:
  REM A named label, not a procedure
  PRINT "HELLO"

Inside a REM comment, a colon is just comment text. For new code, prefer one statement per line; the compact colon-separated style is most useful when you are keeping a classic listing as it was.

Names

d/BASIC names are case-insensitive (for ASCII letters). score, Score, and SCORE are the same name; error messages and the compiled code both spell it in uppercase.

A name:

  • starts with an ASCII letter;
  • continues with ASCII letters, digits, _, or dotted parts such as Customer.Name;
  • may end with a type suffix such as % or $;
  • may have at most 40 characters in each dotted part; and
  • cannot be a reserved word.

Whitespace separates words. FOR I is the keyword FOR followed by the name I; FORI is one perfectly ordinary name. Some early BASICs pulled keywords out of the middle of names, or quietly cut long names short. d/BASIC does neither.

DIM Customer.Name AS STRING
DIM Customer_Order_7 AS LONG

Dots also mark record fields, object members, and the qualified names of procedure bodies, so use them with intent.

The six everyday value types

Type Suffix Storage Best used for
INTEGER % 16-bit signed counters, coordinates, compact whole numbers
LONG & 32-bit signed larger counts, identifiers, milliseconds
DECIMAL @ 64-bit fixed point, 4 decimal places money and exact base-10 quantities
SINGLE ! 32-bit floating point (IEEE binary32) general real-number work; the default numeric type
DOUBLE # 64-bit floating point (IEEE binary64) wider-range or higher-precision real work
STRING $ counted reference variable-length text

DECIMAL is d/BASIC's exact base-10 number type. A value such as 12.3400@ is stored as a signed whole number scaled by 10,000, so it is never converted to binary floating point and back.

You can define your own types too: TYPE records (a bundle of named fields) and CLASS objects. A few other kinds of value exist, such as HANDLE, a whole array, and references the system uses internally, but they only appear where a particular built-in call expects them; they do not behave like ordinary numbers.

Three ways to say a type

Use a suffix for compact classic code:

Lives% = 3
Population& = 120000
Price@ = 19.9500@
Ratio! = .625
Distance# = 1.2D30
Name$ = "MIRA"

Use AS for declarations that should read like prose:

DIM Lives AS INTEGER
DIM Price AS DECIMAL
DIM Name AS STRING

Use a DEF range to choose a default type for names that have neither a suffix nor an AS clause, based on their first letter:

DEFINT A-C, I-N
DEFDBL D-H
DEFSTR S-Z

The available directives are DEFINT, DEFLNG, DEFSNG, DEFDBL, and DEFSTR. Without a suffix, an AS clause, or a matching DEF range, a numeric name is SINGLE.

The default surprises modern readers

DIM Count declares a SINGLE, not an INTEGER. Write Count%, DIM Count AS INTEGER, or a deliberate DEFINT range when you mean a whole number.

Literals

A literal is a value written directly in the source, such as 42 or "MIRA".

Numbers

d/BASIC accepts decimal whole numbers and real numbers, E and D exponents, hexadecimal with &H, and octal with &O (or the classic bare &). A suffix can force the type you intend.

42        ' narrowest fitting INTEGER/LONG, then SINGLE
42000&    ' LONG
3.5!      ' SINGLE
3.5#      ' DOUBLE
3.5@      ' DECIMAL
1.25E-3   ' SINGLE exponent
1.25D-30  ' DOUBLE exponent
&HFF      ' hexadecimal
&O377     ' octal

A hexadecimal literal keeps its bit pattern, sign bit included. For example, &HFFFF stored as an INTEGER is -1.

Strings

A string literal is text between double quotes and may hold up to 32,767 bytes.

Message$ = "A SMALL LANGUAGE CAN HOLD A LARGE IDEA."

There are no backslash escapes such as \n inside a string literal. Build special characters with CHR$ when you need them.

Variables and declarations

DIM declares one or more names:

DIM X AS INTEGER, Y AS INTEGER
DIM Caption$
DIM Balance AS DECIMAL

Use SHARED for a module-level variable that procedures can also see, and STATIC for a variable inside a procedure that keeps its value from one call to the next:

DIM SHARED TotalRuns AS LONG

SUB CountVisit ()
  STATIC Visits AS LONG
  Visits = Visits + 1
  TotalRuns = TotalRuns + 1
END SUB

An array adds its bounds after the name; Arrays covers how arrays are allocated and the statements that work on a whole array at once.

Constants

CONST gives a name to a value that is fixed when the program is compiled:

CONST ScreenMargin = 12
CONST ProductName$ = "d/BASIC"
CONST Debt@ = -4.2500@

The compiler replaces each constant with its value in the compiled program file, .dbc. Use constants to name a meaning, not merely to avoid typing a number twice.

Array base

At module scope, one OPTION BASE chooses the lower bound an array gets when you leave it out:

OPTION BASE 1
DIM Deck%(52)       ' 1 TO 52
DIM Pixels%(0 TO 7) ' Explicit bounds always win

Only OPTION BASE 0 and OPTION BASE 1 are valid. The compiler writes the actual bounds into the compiled program file, .dbc, so the d/OS runtime, the program that runs your compiled code, never has to guess which option the source used.

Truth

False is 0 and true is -1 (every bit set). Comparisons produce those values, and the logical operators work bit by bit on whole-number values.

PRINT 7 > 3       ' -1
PRINT 7 = 3       '  0
PRINT NOT 0       ' -1

This makes classic bit masks natural, but it also means d/BASIC's idea of true is not the modern 1.

Conversion and promotion

When an expression mixes number types, the narrower value is widened to the wider type, in this order:

INTEGER → LONG → DECIMAL → SINGLE → DOUBLE

Storing a value into a variable converts it to the variable's type. CINT and CLNG round to the nearest whole number, and to the even one when exactly halfway; INT rounds down toward negative infinity; FIX drops the fraction, toward zero.

PRINT CINT(2.5)    ' 2
PRINT CINT(3.5)    ' 4
PRINT INT(-2.2)    ' -3
PRINT FIX(-2.2)    ' -2

The same conversion rules apply to whole-array statements: ARRAYFILL A%(), 2.6 stores 3 in every element.


Next: Variables, DIM & Memory →