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.
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.
|OPT 0||000||Assembler errors suppressed, no listing|
|OPT 1||001||Assembler errors suppressed, listing displayed|
|OPT 2||010||Assembler errors reported, no listing|
|OPT 3||011||Assembler errors reported, listing displayed|
|OPT 4||100||Assembler errors suppressed, no listing, assemble to O%|
|OPT 5||101||Assembler errors suppressed, listing displayed, assemble to O%|
|OPT 6||110||Assembler errors reported, no listing, assemble to O%|
|OPT 7||111||Assembler 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
While in the assembler there are 3 commands you can use for allocating space:
|DEFB||Allocate a single byte|
|DEFW||Allocate a 2-byte word|
|DEFM||Allocate 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"
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:
N=USR(&6000) PRINT N AND &FF
N=USR(&6000) PRINT (N AND &0000FF00) / 256
N=USR(&6000) PRINT (N AND &00FF0000) / 256 / 256
N=USR(&6000) PRINT (N AND &FF000000) / 256 / 256 / 256