The NC100 built-in Assembler


One of the benefits of using the built-in assembler of the NC100 is that you can actually make use of variables that have been defined in BASIC. The built-in assembler or interpreter has a few commands that are handy to know when starting out with assembler programming on the NC100.

OPT n

This statement, placed at the beginning of the assembler code, determines what output is generated when the interpreter assembles the code. The OPT statement must be followed by a number between 0 and 7 as follows.

CmdBinExplanation
OPT 0000Assembler errors suppressed, no listing
OPT 1001Assembler errors suppressed, listing displayed
OPT 2010Assembler errors reported, no listing
OPT 3011Assembler errors reported, listing displayed
OPT 4100Assembler errors suppressed, no listing, assemble to O%
OPT 5101Assembler errors suppressed, listing displayed, assemble to O%
OPT 6110Assembler errors reported, no listing, assemble to O%
OPT 7111Assembler errors reported, listing displayed, assemble to O%

Looking at the binary digits, we can see that the right most bit determines if the listing is shown, the middle bit determines if errors are reported and the left most bit lets you create Offset Assembly.

This is the explanation from the Advanced User Guide:

If you become serious about writing assembler programs there will come a time when you'll need to assemble code to a memory address which is reserved or even occupied by the Basic program itself. Obviously this is a big problem, but with a simple solution. Luckily the Notepad allows you to perform Offset Assembly.
This is where a complete assembly goes ahead, as if it was assembled at the address pointed to by P% but, in fact, the assembled machine code is stored starting at the location pointed to by O%. In other words, you can, for example, quite happily assemble code to run from &C000 onwards (as you might if you were writing a 16K system application to put in the first 16K of a RAM card), but actually only store the code in a safe are of RAM at &6000-&9FFF.

the OPT statement can only occur inside the square brackets which signify the os of assembler directives. If labels are used, it is necessary to do the assembly twice in order for the assembler to figure out where the different labes are. This is known as a two-pass assembly. The first pass figures out the addresses of all labels. The second pass replaces the labels with the correct address in the resulting machine code.

Normally an assembly sequence would look something like the following, where the variable PASS is used in a FOR...NEXT loop to set the OPTion:

10 DIM A% 100             :REM Allocate space for the assembled program
20 FOR PASS=0 TO 3 STEP 3 :REM 1st pass=no output, 2nd pass=listing & errors displayed
30 P%=A%                  :REM Tell interpreter where assembled code should be stored
40 [
50 OPT PASS
60 :
70 \ Your code here...
80 :
90 ]
100 NEXT

Under normal circumstances, you would not have the interpreter display the assembly listing as it does the assembling, you would use OPTions 0 and 2 so that only errors are displayed:

20 FOR PASS=0 TO 2 STEP 2

For completeness sake, here is an example of assembling code that is meant to be run from a different address than it is actually being assembled to:

10 FOR PASS=4 TO 7 STEP 3: REM Offset assembly, show both listing and errors
20 P%=&C000:               REM Assembler assumes &C000 as offset address when assembling
30 O%=&6000:               REM Code is actually stored at &6000
40 [
50 OPT PASS
60 :
70 \ Your code here...
80 :
90 ]
100 NEXT

DEF

While in the assembler there are 3 commands you can use for allocating space:

DEFBAllocate a single byte
DEFWAllocate a 2-byte word
DEFMAllocate a string

Below are some examples of acceptable syntax:

DEFB &FF
DEFB byte
DEFB ASC("A")
DEFW Z%
DEFW 0
DEFW &1234
DEFM "This is a string"

Register Variables

It is possible to pass values directly to the Z80's registers by setting the corresponding variables.
The variables are A%, B%, C%, D%, E%, F%, H%, L%
Note that F% is the flags registers.
For example you could print a character to the screen with a call to TXTOUTPUT (&B833), using these commands.

A%=ASC("J")
CALL &B833

The Amstrad NC100 Advanced User Guide states that it is possible to examine the same variables after a CALL or USR(n) to see what the Z80 registers contain, but I can not confirm this.
As far as I can tell, the only way to get information back from assembly code is to use USR(n) and examine the return result which should be registers: H, L, H' and L' in a 32 bit value in that order. In order to print the individual registers you could do something like this:

L'
N=USR(&6000)
PRINT N AND &FF
H'
N=USR(&6000)
PRINT (N AND &0000FF00) / 256
L
N=USR(&6000)
PRINT (N AND &00FF0000) / 256 / 256
H
N=USR(&6000)
PRINT (N AND &FF000000) / 256 / 256 / 256