Skip to content

Compatibility, migration & refusals

D/BASIC does not treat compatibility as a costume. It preserves the rules that determine what a classic compiled BASIC program means, then adds modern structure without requiring a second language mode.

The compatibility center

Area D/BASIC rule
names ASCII case-insensitive; suffix is part of the name's type
default numeric type SINGLE unless suffix, AS, or DEF* says otherwise
truth false 0, true -1
line numbers optional, valid branch/data targets
labels named labels coexist with line numbers
statements newline or : separated
comments apostrophe or REM through end of line
parameters BYREF by default; explicit BYVAL; extra parentheses force a value
operators D/BASIC precedence, integer \, MOD, ^, logical ladder
strings $ names and classic string intrinsic family
arrays inclusive bounds, OPTION BASE, LBOUND, UBOUND, REDIM, ERASE
console PRINT, separators, TAB, SPC, INPUT, LINE INPUT
program data DATA, READ, RESTORE
flow IF, FOR, WHILE, DO, SELECT CASE, GOTO, GOSUB, ON ... GOTO/GOSUB
procedures DECLARE, SUB, FUNCTION, DEF FN, function-name result assignment
records TYPE ... END TYPE value records

The acid tests are small details: -2 ^ 2 is -4; &HFFFF can be -1; CALL F((X)) passes a value; an untyped X is SINGLE. These rules survive because transferred programs depend on them.

Line numbers are not deprecated

This is valid D/BASIC:

10 FOR I% = 1 TO 3
20 PRINT I%
30 NEXT I%
40 END

So is this:

FOR I% = 1 TO 3
  PRINT I%
NEXT I%

And so is a careful mixture. Modernization should make a program clearer, not erase its history for sport.

What D/BASIC adds

Addition Why it exists
DECIMAL and @ exact four-place base-10 values
ARRAYCOPY, ARRAYFILL, ARRAYSWAP explicit, optimizable whole-array meaning
CLASS, NEW, NOTHING, THIS object identity and behavior
access, inheritance, virtual/abstract/override scalable application structure
properties and weak fields controlled state and acyclic ownership
WINDOW SUB and ON KEY/ON POINTER portable event-driven application faces
INTENT and SERVICE semantic host capabilities without machine addresses
IMPORT ... BAKED/SHARED governed source reuse and exact compiled dependencies
D/Works widgets reusable portable controls written in the language
DBC bytecode build once, validate and run across systems

All of this lives in one grammar. You do not choose “classic mode” to keep line numbers or “modern mode” to use a class.

Why machine access refuses

The following names are permanently outside portable D/BASIC:

Refused surface Historical purpose D/BASIC replacement
PEEK, POKE read/write an address typed state, handles, registered Intents
VARPTR obtain a native address whole-array/record carriers and governed references
DEF SEG select a memory segment no source-visible memory map
USR, SYS, address-form CALL jump into machine code declared procedures, shared libraries, native module/package route
WAIT poll a hardware port event faces, Services, pending Intents
raw PTR, address-of, pointer arithmetic native memory identity object references and opaque handles

This is not a missing retro feature. It is the line that makes one bytecode image meaningful on machines that share no address map.

The compiler recognizes these spellings so it can explain the boundary by name. It does not misparse the rest of the listing or silently ignore the operation.

Graphics migrate to meaning

Vendor commands such as direct screen memory, display lists, raster hooks, or chip registers cannot cross platforms intact. Translate their visual intention:

' Instead of writing a video-memory address:
INTENT GFX.FILL_RECT(X%, Y%, Width%, Height%, Color%)

A runner can upgrade that to a GPU/high-DPI path or lower it onto a constrained composed head. Read Intents & Services.

One refusal vocabulary, three different stages

D/BASIC uses one governed refusal vocabulary across compile, load, and runtime, but only a program that has started can catch a runtime refusal. A compile diagnostic produces no DBC. A load or admission refusal runs no program code. ON REFUSAL handles call-level failures after admission: an optional provider that disappears, a stale session, invalid live bounds, a capacity limit, or an operation the admitted provider declines.

ON REFUSAL GOTO OptionalRouteMissing
CALL UseAdmittedOptionalRoute
PRINT "OPTIONAL RESULT READY"
GOTO Done

OptionalRouteMissing:
  PRINT "UNAVAILABLE:"; ERR; " AT "; ERL
  RESUME Done

Done:
END

A truly optional feature is discovered through an admitted capability-query row, then used only when the host says a route exists. Missing required capability is a pre-start refusal, not a half-launched application. See Errors, Refusals & Diagnostics for handler and cleanup patterns.

A migration recipe

1. Run the listing before beautifying it

Preserve line numbers, suffixes, GOSUB, and odd-looking parentheses until tests capture behavior.

2. Make types visible

Keep original suffixes or introduce explicit AS clauses. Remember that an untyped numeric name remains SINGLE.

3. Isolate machine operations

Mark each PEEK, POKE, SYS, hardware graphics/sound command, and host path. Translate the purpose into an Intent, widget, data wrapper, or declared platform capability.

4. Name repeated work

Move substantial GOSUB routines into SUB/FUNCTION contracts. Keep GOSUB where it remains the clearest expression of a local classic algorithm.

5. Replace parallel arrays with records

TYPE Player
  Name AS STRING * 24
  Score AS LONG
END TYPE

Reach for a class only when identity, ownership, behavior, or dispatch actually matters.

6. State whole-array meaning

Replace obvious clearing/copy-buffer plumbing with ARRAYFILL, ARRAYCOPY, or ARRAYSWAP. The code becomes clearer and the optimizer gains a stronger contract.

7. Move display code into a face

Console output can remain PRINT. A graphical application should place state creation in OPEN, layout response in RESIZE, interaction in ON KEY/ON POINTER, and painting in DRAW.

8. Build and test the DBC

Compare the same compiled artifact and canonical trace across intended runners. Portability is a property of the delivered image, not just the source's appearance.

The dialect optimizer's role

The dialect optimizer can recognize historical forms and produce canonical D/BASIC, but its output remains reviewable source. It is allowed to remove syntactic distance; it is not allowed to guess machine-specific intent.

That is why D/BASIC can welcome many BASIC traditions without becoming a bag of runtime switches.

Common transfer surprises

Untyped means SINGLE

DIM Count is not an integer declaration. Use %, AS INTEGER, or DEFINT.

Calls default to BYREF

A bare variable can be changed through a default parameter. Use BYVAL in the declaration or extra parentheses at a call that deliberately passes a value.

True is -1

Code that compares a true result to 1 misunderstands D/BASIC truth. Test nonzero or compare to -1 when the exact mask matters.

A class variable is initially NOTHING

DIM Button AS WorksButton declares a reference. Construct it with NEW; declaration does not invoke a constructor.

Compatibility without stagnation

D/BASIC is not trying to recreate one old executable environment. It preserves the language contracts humans and programs relied on, then gives operating-system and hardware work a portable semantic home.

That is a more ambitious form of compatibility: yesterday's first line can still run, and tomorrow's full application does not need to leave BASIC behind.


Next: Quick Reference →