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:
So is this:
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:
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¶
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 →