Using The Assembler Language 02

By Roy Warner

Originally published in EUG #04

Last time we began with the bare bones of a program. This time we will see how to expand upon it.

The program needs some commands: LDX, LDA, JSR, INX, CMP, BNE and RTS will be used.

There are a number of registers in the Electron; three are currently of importance to us, namely "A", "X" and "Y". "A" is the accumulator - where the 'work' is done. "X" and "Y" hold values that may be incremented or decremented so they are used for counting and are referred to as the indexing registers. LDX means load X, an addressing mode is

applicable, LDX#0 means load X with the value zero and is known as immediate addressing; LDX 0 also LDX &5800 means load X with the content of the location or address specified, the former is zero page addressing and the latter absolute addressing.

Zero page is worth a small diversion at this point. The Electron is a 64K machine. This is set by the fact that the address bus is 16bit capacity. One byte requires eight bits, 0 to 7. The highest number that can be defined with eight bits is 255 (hex &FF). If the address bus is 16 bits wide, then it can hold two bytes to give a maximum in hex of &FFFF - type PRINT &FFFF/1024 and there is the answer - 64K or near enough.

The memory may be and is divided into 256 times 256, the Electron treats 0 as a number. Zero page is the first page i.e. 0 to 255 and the addresses range between hex &0000 and &00FF. The first byte &00 is not required. This means that the Electron only has half the work to do when using Zero page thus everything is manipulated much faster.

Acorn have reserved most of zero page for the Electron's Operating System, but have left a small amount for us - &0070 to &008F or as it is used, &70 to &8F. This is not a lot, therefore we have to be economical when we use it.

There are two forms of indexed addressing that may only be used with zero page addresses. These are known as Indirect and Post-Indexed Indirect addressing. It is regrettable that most aspiring programmers give up when confronted with these two, which is a pity as the names are the most difficult part. Later, I will introduce 'Post-Indexed' and it will not hurt one little bit! Indirect is so rarely used that most programmers using it have to consult the handbook as a refresher. End of the diversion!

LDA (LoaD the Accumulator)

Various forms of addressing may be applied to the accumulator, but for our purposes it will be indexed to the value in X.

"Indexed Addressing" the accumulator will process the code.

JSR (Jump Sub Routine) is similar to BASIC's GOSUB. JSR commands should be balanced by RTS commands. INX means INcrement the X register, CMP means CoMPare with the accumulator - the program may branch in different

directions depending on the result of the comparison. BNE stands for Branch if Not Equal.

Going back to the program we began last time, add the following lines:

      240.go:LDX#0
      250.loop:LDAdata,X:JSR&FFEE
      260INX:CMP#0:BNEloop
      270RTS
      320.data
      350EQUB10
      360EQUB0
      650CALLgo

At line 240 a label has been introduced. Labels are global variables and assembler knows a label by the preceding full stop. The assembler will transfer the value of a label to any part of the code and compute the extent of the difference between the two locations (this will be discussed in more detail later). Using immediate addressing, X has been loaded with the value zero. At line 250, another label defines the start of a loop (like a FOR NEXT loop).

LDAdata,X means load the accumulator with the contents of data - the content remains unaltered and is copied to the accumulator. The content is the ASCII code of the first character of "my string". LDAdata,X indexes data to X which has the value zero - data plus zero equals data.

JSR&FFEE - Jump to Sub Routine at address &FFEE which is a vector for an Acorn sub-routine called OSWRCH that prints out to the screen. Any code stored in the accumulator may be processed by OSWRCH (Operating System WRite CHaracter). OSWRCH is not resident at &FFEE but code stored at &FFEE directs the program control to OSWRCH. OSWRCH will activate all VDU codes - for instance LDA#7:JSR&FFEE will sound the "bell" and LDA#12:JSR&FFEE will clear the screen. When making this CALL the accumulator may only be loaded with "legal" control or ASCII codes.

All the resident Acorn subroutines finish with an RTS, so having printed the character in the accumulator the program returns to INX. X is incremented to contain the value one. CMP#0 compares the value in the accumulator to zero, this is still the ASCII code for lower case "m" so it cannot be zero.

BNEloop? The assembler has given the loop part a value that returns the program to the label loop so the program branches back to .loop and goes through the loop again, this time, as the LDAdata,X had the value data+1 (which is the ASCII code for lower case "y") the Acorn subroutine then prints "y". Eventually, having printed all the characters plus the carriage return "13", the accumulator is loaded with the ASCII code for 0. The test CMP#0 succeeds, the BNE fails and the loop ends. RTS returns the program to BASIC.

SAVE it then RUN it and the assembler reports "No such variable"! Do not throw this article in the bin! The offending variable is data. The assembler cannot find it because the label data is after LDAdata,X: therefore at that stage in the assembly the assembler cannot possibly know the numerical value of data and accordingly is unable to assign that value to the data part of LDAdata,X.

Acorn have provided for the problem with the OPT statement. This has several forms (See page 173 of the USER GUIDE).

OPT0 and OPT3 are the ones needed. To use the two, the source code is placed in a stepped FOR NEXT loop. BASIC to the rescue! Type:

      190 FOR pass=0 TO 3 STEP 3

and change line 200 to:

      200 P%=base:[OPT pass

Line 640 should read:

      640 ]:NEXT
SAVE the listing again as it has been altered. When RUN first time though the loop errors are not reported. Second time though the assembler knows where data is and puts the address in the machine code. P% must always be defined within the FOR NEXT loop else the code will not assembler at the same address and data would be wrongly defined.

SAVE then RUN the code. Screen display should be:-

  1. The assembler listing
  2. My string

The first assembler program! SAVE the object code by typing:-

      *SAVE first 5001 <num>

(Remember after running the source code, type PRINT~(P%+2) to get num.)

Having saved the object and source code, type NEW <RETURN>. Type HIMEM=&5800 then *LOAD first 5001. When loaded back, type CALL &5001.

The string "my string" should be printed on screen.

Next time the source listing will be adapted to tidy it up and the object listing discussed.

Roy Warner, EUG #4