Platform Notes
How a DAAD game behaves on the ZX Spectrum Next, in the places where this target is not quite like the other machines DAAD runs on.
Nothing on this page is going to change. Some of it is forced by the hardware - text lives on the tilemap here and pictures live on Layer 2, which are two separate surfaces rather than one screen. The rest is settled by choice. Either way, this is how the target works, and it is what to read first when a game behaves differently here than it did somewhere else.
For behaviour that is not settled - differences we mean to remove - see Known differences.
This one is not the interpreter. The compiler rewrites the number before the interpreter ever sees it. DRC multiplies every PAUSE and BEEP duration by this target's own note length, 0.6, on the way into the database: PAUSE 100 in your source compiles to PAUSE 60, and BEEP 50 120 compiles with a duration of 30. The interpreter then waits exactly the number of frames the database asks for, at 50 Hz.
The consequence is that a pause you authored as one second lasts about 0.6 of a second.
Author against what you hear, not against the arithmetic. One second is PAUSE 83 on this target - 83 scaled by 0.6 is 50 frames at 50 Hz. PAUSE 167 gives about two seconds. Duration tables in older DAAD or MALUVA guides were written for other targets, so treat them as a starting point and check the result by ear.
XPLAY's generated notes are scaled the same way. See Audio for the rest of what the compiler does to a BEEP.
DISPLAY n with a non-zero value clears the Layer 2 picture surface and flips it into view. It does not clear text, because on this machine text is not on the picture layer at all. The old DAAD idiom DISPLAY @0
- "clear the screen when it is dark" - therefore does not clear your prose here. Use
CLSfor text andDISPLAYfor pictures; they are two different surfaces and each condact reaches one of them.
DISPLAY 0 re-draws the current picture. It always does the work, even if the same picture is already on screen, so it is not free - do not put one inside a tight loop and expect it to cost nothing.
On PC/DOS these set and read one palette entry with six bits per channel; the Next's palette holds three, so red, green and blue given as 0-255 keep their top three bits and read back as 0, 32, 64 ... 224. Index 255 is the reserved transparent entry: GFX n 9 ignores it, and a colour that would make an entry transparent is nudged one green step, exactly as the picture loader does. See Colour cycling and palette entries.
GFX n 11's third flag is documented everywhere as frames, and that is what it counts here: one frame interrupt, 1/50 s or 1/60 s by the machine's timing mode. PC/DOS's interpreter actually counts milliseconds, so a value ported from a PC game is twenty times too slow: divide it by 20. LOAD and RAMLOAD stop a running cycle here (PC leaves it running), and a last index of 255 is treated as 254.
Sub 15 is a no-op for a different reason. On CPC and C64 it is XSPLITSCR, a split-screen toggle; this target has no split-screen mode, so 15 is accepted and does nothing here too. Worth stating explicitly now that its neighbour, sub 16, installs a font - see Fonts.
Every other interpreter draws an underscore in the text colour after the typed line; here the default is an inverse block at the insertion point, and the line editor moves through the line (cursor left and right, insert and delete anywhere). GFX 95 22 gives the underscore look; GFX n 23 to 26 add a blink and colours - see Graphics. The typed line wraps inside the input window and is capped to the window's capacity, where PC and ADP refuse at the end of one row.
The GFX sub-commands that are implemented here - the buffer copies and swaps, the draw-target subs 3 and 4 (screen vs. back-buffer drawing) and their reveal semantics on 0 and 2, the surface clears, the palette subs 9 and 10 and colour cycling on 11 and 12, video playback on 13 and 14, and font installation on 16 - are listed in Graphics, Video and Fonts.
The tilemap computes each cell's palette index as (attribute AND $FE) OR pixel, so a cell's paper and its ink always sit in two adjacent palette entries, one even and one odd above it. That splits the 256-entry palette into 128 pairs, so at most 128 distinct ink/paper combinations can be live on screen at once. That is combinations, not colours - a game that uses only a handful of colours but pairs them in many different ways can still reach the ceiling, while a game that uses every one of the 256 colours but only ever as a single ink/paper pairing uses one.
Combinations are allocated as your game uses them and reclaimed automatically once the table fills, so nothing needs managing by hand. Exceeding 128 live combinations at once is the only way to see a colour change happen under you - some other combination is reclaimed to make room for the new one, and text already on screen in that combination changes colour without you having asked it to. No realistic adventure comes near that ceiling; this is here to document what happens on the way past it, not to warn you off anything achievable in practice.
Values 16 to 255 select the standard Next colour of that number - see Colours for what the numbers mean. This is a NextDAAD extension: jDAAD folds all three condacts modulo 16, so the INK 224 that gives full red here gives INK 0 there. The extension is deliberate and one-way - a game that stays inside 0-15 plays identically on both, but a game written against the extended palette will not look right under jDAAD, since jDAAD wraps those values into its own sixteen rather than rejecting them. Stay inside 0-15 if the same source needs to look right on jDAAD too.
0 to 7 are the eight ULA colours and 8 to 15 are the same eight again, bright - the ordinary Spectrum attribute convention, and the settled behaviour here. If you are bringing across a game that relied on 8-15 folding to the dim hue instead, expect the picture to look different: write the dim colour's own number, 0-7, if that is what you want.
There is one DOALL at a time. If a sub-process called from inside a DOALL starts its own, the game stops with runtime error 4 rather than quietly producing the wrong answer.
Do not nest DOALL loops. The loud failure is the point: a nested DOALL has no correct meaning here, and finding that out as an error on screen is better than finding it out as an inventory that silently lost half its objects.
Despite the symbol names DELTAXMS and DELTAYMS, these two do not report how far the mouse moved. They set the hotspot offset inside the pointer bitmap - the pixel of your artwork that lands on the reported coordinate. If your pointer is a cross, you probably want its hotspot at 5,5 rather than at the top-left corner.
All eight sub-commands, 0 to 7, are implemented. A pointer shape switch (MOUSE n 5) does not move or reset the hotspot these two set - it stays wherever you last put it. The full table, and how to supply your own pointer artwork, base and numbered alike, is in Mouse.
EXTERN n v picks one of sixteen dispatch vectors - the mechanism MALUVA used to bolt extra features onto older DAAD. Three of them do something here:
- Vector 3,
XMESSAGE- print external text held in0.XMB. It reads exactly like a message stored in the database: same token expansion, same word wrap, same More... paging. A missing or unreadable0.XMBprints nothing and the game carries on. In a version 3 database the nativeXMESopcode reaches the same text with noEXTERNinvolved; the vector stays for version 2 databases. See Limits. - Vector 4,
XPART- switch the running game to another part. See Multi-part games. - Vector 7,
XUNDONE- clear the current action's done stamp, for aSYNONYM-style entry that should not count as a completed turn.
Every other vector is a safe no-op unless a GAME.XBN is loaded, in which case it forwards to the extern (see Externs); with no extern the condact is consumed and play continues. The rest of classic MALUVA - XPICTURE, XSAVE, XLOAD, XBEEP, XSPEED, XNEXTCLS, XNEXTRST - is deprecated in the compiler in favour of engine features this target covers natively: PICTURE/DISPLAY for pictures, SAVE/LOAD for game state, EXIT for a reset, and SFX for sound.
Two things are deliberately unavailable. The compiler's -X (dumpToXMB) switch, which would route all of a game's text through 0.XMB rather than only the text you asked for by name, is not implemented. And do not hand-write EXTERN n 3 for anything else: vector 3 has a three-byte encoding of its own, and the engine always consumes the matching third byte whenever it sees that shape, so any other use of a literal 3 there misaligns every condact after it.
A NextDAAD extension, settled behaviour. When a loaded extern returns with the carry flag set, the EXTERN behaves as a failed condition: the entry stops and processing falls to the next matching entry. The done state is handled as for any failed condition: the extern does not count as an action performed, and an earlier action's done stamp in the same table survives. With no GAME.XBN present, and for the three reserved vectors, EXTERN remains a pure action that never fails; CALL never carries a verdict. Classic DAAD interpreters treat EXTERN as an action only; a database that relies on this extension is portable only to NextDAAD. The full contract is in Externs.
AUTOG - the condact behind a GET entry that takes its object from what the player typed rather than from a number in your source - looks for that noun at the player's location first, then among carried objects, then among worn ones, and takes the first it finds.
That order only shows when the same noun matches more than one object at once. If your game can have a duplicate both carried and worn, the carried one is the one GET resolves to here. jDAAD tries worn before carried, so a game written against it can pick up the other object. Where it matters, name the object explicitly rather than relying on the search order.
END prints SM13 and reads a line. A reply beginning with SM31's first character ends the session; anything else restarts the game. (QUIT is the mirror image: it prints SM12 and confirms on SM30's first character.)
Write SM30 as your "yes" word and SM31 as your "no" word, and both read correctly. The prompt itself is SM13 either way; what differs is which message the reply is tested against - jDAAD tests END against SM30 instead, so a game whose message table was written for it can act on the wrong reply here. Check SM30 and SM31 when you bring one across.
RAMLOAD with a flag argument restores object locations in full and flags 0 to n inclusive - that is, n + 1 flags. RAMLOAD 10 restores eleven flags, 0 through 10.
jDAAD stops one short, at 0 to n - 1. A game that leans on the partial restore to protect a particular flag will find that flag restored here and not there, or the other way round, with nothing on screen to say so. Choose an argument that leaves an unused flag on the boundary and the question stops mattering.
One exception. In a multi-part game, a RAMLOAD whose snapshot was taken in a different part is always a full restore - the argument is ignored entirely, and every flag comes back. The partial restore only applies within one part.
_ substitutes the referenced object's name into a message, in every language. @ is the capitalising form of the same escape and it exists for Spanish databases only.
So in an English database @ is an ordinary printable character: a message containing -@@- prints -@@-. In a Spanish database, @ substitutes the name with its first letter uppercased and _ substitutes it unchanged.
If you are bringing in a game whose English messages relied on @ substituting, change those to _.
When an object's text is substituted into a message, a leading "a ", "an ", "some " or "the " is removed - matched whatever the case - and nothing else is. Any other first word survives: an object text of "rusty sword" substitutes as "rusty sword", not "sword".
The stock system messages supply their own article ("You now have the _."), so write your /OTX entries as "a lamp", "an axe", "some rope" or "the key" and all four read correctly. An /OTX beginning with any other word keeps that word, which is the point - a descriptive name stays intact.
Leading spaces are not touched. The article has to be the literal start of the text.
Porting note. jDAAD removes the first word whatever it is. A game written against that will have object texts that only read correctly when the first word is thrown away - "my wallet" gives "You now have the my wallet." here. Rewrite those few texts without the possessive.
Listings are not affected at all. LISTOBJ, LISTAT and the inventory print the object text whole, article included.
A substituted object name is truncated at the first full stop. An object text of "a quaint lamp. It is unlit." reads as "quaint lamp" inside a message and prints whole in a listing, so one /OTX entry can serve as both a short name and a longer description.
If the player types a noun on its own and gives no verb, DAAD moves the noun into the verb slot when its vocabulary id is below 40. Ids 39 and below convert; 40 and above do not.
The 1991 DAAD manual says the limit is 20 ("a Noun with a word value <20"). That figure does not match the shipped interpreters: the original ZX Spectrum interpreter converts ids up to 39, and the DRC compiler accepts nouns up to 39 in the verb position of a process entry or a SYNONYM. NextDAAD follows the interpreter and compiler, not the manual.
What to do with that: any noun you number in the 20-39 band will act as a command when it is typed bare. If you have a noun that must never be a verb, number it 40 or above. Direction words and other words meant to work when typed on their own belong below 40.
PARSE 1 re-parses the quoted section of the last order. Two separate rules govern it, and it is worth knowing which is which:
- Whether a quoted section exists decides whether the sentence flags are refilled.
SAY HELLO, with no quotes, leaves the current sentence untouched.SAY "", with empty quotes, clears it. - What the quoted section contains decides the condition, by the same "a verb or a noun was found" test
PARSE 0uses.SAY "PLUGH"fails the condition, because it filled a verb.SAY "FAST"passes - an adverb on its own is not a sentence.SAY "ZZZZ"passes.
Words after the closing quote still parse into the normal sentence, and convertible nouns are converted inside a quoted section exactly as they are outside one.
- Flag 29, the graphics capability byte, reads 129: bit 7 because Layer 2 location graphics exist, bit 0 because the
MOUSEcondact is implemented.HASAT GMODEis therefore true here, so a period game that gates its picture drawing onHASAT GMODEdoes draw its pictures. Bit 0 is set whether or not a mouse is plugged in. - Flag 62, the screen mode byte, reads 144: bit 7 for "palette switching is available", bit 4 for "a native machine mode, not one of the original ST or PC values", and the low four bits naming the mode - 0 here, for Layer 2 at 256x192 in 256 colours. Test the bit you care about rather than comparing the whole byte against a number; every machine puts a different value in it.
Both are written once when the game starts and are not rewritten if the game switches Layer 2 mode later.
A save file made before these flags were published carries the old values, so an old save restores flag 29 as 0 and takes the no-graphics branch of HASAT GMODE until something rewrites it. Start a fresh game to pick up the current values.
Compiling with the debug flag writes a marker into the process tables wherever you put a DEBUG line. NextDAAD steps over those markers and carries on.
This is the only DAAD interpreter where that works. Everywhere else the marker is read as NEWTEXT, which throws away the rest of a multi-command order - so a debug build stops obeying GET SWORD AND KILL ORC half way through, with nothing on screen to say why. Here it does not, so you can leave DEBUG lines in your source while you work and build with or without the flag without the game changing under you.
What a DEBUG line does not give you is a breakpoint. There is no debugger attached to it on this target, so the line is simply inert. Do not write one expecting the run to stop.