Mnemonic | Description |
JMP | jump to instruction whose address is |
given |
JMP &E48 or JMP addr
Instead of describing the address by a number, we can use a 'label' to indicate to the assembler where we want to go. In the assembler, labels are variables prefixed with a full stop (.).
10 oswrch =&FFEE 20 DIM P% 100 30[ 40.enter 50 LDA #ASC"*" 60.Loop 70 JSR oswrch 80 JMP Loop 90] 100 END
When the program is assembled the address corresponding to the label '.loop' will be inserted in the machine code. When the code is executed the value of the program counter will be set to this address and the CPU will collect its next instruction from the location with that address and will continue executing from there.
The label '.enter' at the start of the program has been included so that this label can be called in order to execute the program. This is a better way of executing a program than calling TOP since TOP doesn't always point to the first machine code instruction. This is true for the above example since TOP will point to the assignment statement 'oswrch=&FFEE'.
The program will output an asterisk (*), and then jump back to the previous instruction. The program has become stuck in an endless loop! Compare this program with the following BASIC program:
10 A = ASC"*" 20 VDU A 30 GOTO 20
A flowchart for this program is as follows:
To get out of the BASIC loop you press ESCAPE. This will not automatically halt machine code programs, however. To exit from a machine code loop without losing the program you must press BREAK, and then type 'OLD' to retrieve the original program.
Mnemonic | Description |
JSR | jump to subroutine |
JSR oswrch
In this case the address of the instruction directly following the JSR instruction in the code is noted, and then the value of the program counter is set to the address of 'oswrch'. The CPU will go to this address for its next instruction and start executing the code from there until it meets an RTS. This will set the program counter to the address which was noted earlier so that the CPU can then continue executing the code following the JSR instruction. Subroutines jumped to can either be part of the assembler program or, as in this example, sub-routines which exist in the operating system memory.
LDA &80
would set the zero flag if the contents of &80 were zero.
Similarly the negative flag, N, is set if the result of the previous operation was negative in two's complement notation, i.e. if the top bit was set, e.g.
LDA &80
would set the negative flag if the number stored in location &80 was greater than 127 (01111111).
The conditions of all the flags are stored in a byte called the status register (P), and each flag is represented by one bit: e.g. the top bit of the status register is set if N=1 and the bottom bit is set if C=I.
Mnemonic | Description | Status |
BEQ | branch if equal to zero | (ie Z=1) |
BNE | branch if not equal to zero | (ie Z=0) |
BCC | branch if carry clear | (ie C=0) |
BCS | branch if carry set | (ie C=1) |
BPL | branch if plus | (ie N=0) |
BMI | branch if minus | (ie N=1) |
BCS notzero
the assembler works out the difference (in bytes) between the current instruction and the place where the label '.notzero' is, and puts this value after the op-code. This means that the value of this byte is used, in conjunction with the address of the current instruction, to tell the CPU where to go next.
Because only a single byte is allowed in this relative addressing mode, the branch instructions can only point to one of 255 nearby bytes. The two's complement representation of numbers is used to give the offset relative to the current address. Branches which point forwards are restricted to 0-127 bytes beyond the current location. The value of the byte following the op-code for these is then 0-127. Branches which point backwards to places at lower addresses in memory require a negative value to be added to the current location. These use the numbers 128-255 to represent the values -128 to -1.
The JMP instruction does not use relative addressing; it is followed by two bytes which specify the absolute address which will be the destination. Hence the branch instruction is shorter than the jump instruction, the jump being three bytes long (op-code and two-byte address) and the branch being two bytes long (op-code and one-byte offset). This difference is automatically looked after by the assembler.
The following simple program will print an exclamation mark if 'character' contains zero, and a star if it does not. The comments to the right of the assembler statements may be omitted when you enter the program.
10 DIM P% 100 20 character=&80 30 oswrch = &FFEE 40[ 50.enter 60 LDA character 70 BEQ exclamation If zero print '!' 80 LDA #ASC"*" Star 90 JSR oswrch Print it 100 RTS Return 110.exclamation 120 LDA #ASC"!" ExcLamation mark 130 JSR oswrch Print it 140 RTS Return 150] 160 END
A flowchart for this program is as follows:
Note that the above program can be made shorter, by replacing the instructions
JSR oswrch RTS
with the single instruction
JMP oswrch
Replacing JSR and RTS instructions by a JMP to a subroutine reduces the size of both a source program and the object code it produces, and hence increases execution speed.
Now assemble the program by typing RUN. You should get the message:
No such variable at Line 70
This is because the assembler processes the mnemonic instructions in the order in which they are listed in the program. Therefore when it encounters 'BEQ exclamation' it has not yet found the label exclamation' so it cannot work out the offset which is required in the following byte. This is known as the forward-reference problem, and is easily overcome using the method of two-pass assembly which is explained below.
This can be done using the OPT statement, an assembler directive which has a single parameter for which the following values are possible:
OPT 0 No error messages, and no listing
OPT 1 No error messages, and listing
OPT 2 Error messages reported, and no listing
OPT 3 Error messages reported, and listing (Default)
Thus to suppress messages and a listing on the first pass, and to restore them on the second pass, we need to use OPT 0 and OPT 3 respectively. This can be effected by placing the directive inside a FOR ... NEXT loop, which goes from 0 to 3 in steps of 3. Then the value of the control variable is used as the parameter of the OPT statement. So, to alter the program which was given above, simply enter these lines:
10 DIM code 100 23 FOR pass = 0 TO 3 STEP 3 26 P% = code 30[ OPT pass 145 NEXT pass
This time the error message will not be produced and the correct offset will be calculated for the branch instruction.
Note lines 10 and 26, which replace the old 'DIM P% 100' statement. P% must be reset to the starting value each time that the code is assembled.
Now execute the program by typing
CALL enter
and verify that the program behaves as it should for different values in &80.
Mnemonic | Description | Symbol |
LDX | load X register from memory | X=M |
LDY | load Y register from memory | Y=M |
STX | store X register to memory | M=X |
STY | store Y register to memory | M=Y |
The X and Y registers are particularly useful as the control variables in iterative loops, because four special instructions exist which will either increment (add 1 to) or decrement (subtract 1 from) their values.
Mnemonic | Description | Symbol |
INX | increment X register | X=X+1 |
INY | increment Y register | Y=Y+1 |
DEX | decrement X register | X=X-1 |
DEY | decrement Y register | Y=Y-1 |
10 DIM P% 100 20 oswrch = &FFEE 30[ 40.enter 50 LDX #8 Initialise X 60 LDA #ASC"*" Code for star 70. Loop 80 JSR oswrch Output star 90 DEX Count it 100 BNE loop All done? 110 RTS 120 ] : END
A flowchart for the program is as follows:
Assemble the program by typing RUN. This program prints out a star, decrements the X register, and then branches back if the result after decrementing the X register is not zero. Consider what value X will have on successive trips around the loop and predict how many stars will be printed out; then execute the program with 'CALL enter' and see if your prediction was correct. (If you were wrong, try thinking about the case where X was initially set to 1 instead of 8 in line 50.)
How many stars are printed if you change the instruction on line 50 to 'LDX #0'?
Mnemonic | Description | Symbol |
CMP | compare accumulator with | A-M |
memory | ||
CPX | compare X register with | X-M |
memory | ||
CPY | compare Y register with | Y-M |
memory |
The next example again prints eight stars, but this time it uses X as a counter to count upwards from 0 to 8.
10 DIM P% 100 20 oswrch=&FFEE 30[ 40.enter 50 LDX #0 Start at zero 60.loop 70 LDA #ASC"*" Code for star 80 JSR oswrch Output star 90 INX Next X 100 CPX #8 All done? 110 BNE loop If not then repeat 120 RTS Else return 130] 140 END
In this program X takes the values 0, 1, 2, 3, 4, 5, 6, and 7. The last time around the loop X is incremented to 8, and the loop terminates. Try drawing a flowchart for this program.
STX tempaddr LDA tempaddr
where 'tempaddr' is not being used for any other purpose. However, there is a more convenient way, using one of four new instructions:
Mnemonic | Description | Symbol |
TAX | transfer accumulator to X | X=A |
register | ||
TAY | transfer accumulator to Y | Y=A |
register | ||
TXA | transfer X register to | A=X |
accumulator | ||
TYA | transfer Y register to | A=Y |
accumulator |
The following example prints out the alphabet by making X cover the range A to Z.
10 DIM P% 100 20 oswrch = &FFEE 30[ 40.enter 50 LDX #ASC"A" Start with the Letter A 60.loop 70 TXA Put it in the accumuLator 80 JSR oswrch Print it 90 INX Next one 100 CPX # (ASC"Z"+1) Finished ? 110 BNE loop If so - continue 120 RTS Else return 130] 140 END
All these examples could have used Y as the control variable instead of X in exactly the same way.
10 DIM CODE%100 20 char=&70 30 oswrch=&F FEE 40 osrdch=&FFEO 50 beLL=7 Bleep = VDU 9 60 prompt=ASC":" 70 INPUT"bell",beLL$ Input 'bell$' 80 bellflag=INSTR("Yy",bell$) bellflag' is true if 90 FOR pass=0 TO 3 STEP 3 bell$ = 'y' or 'Y' 100 P%=CODE% 110[ OPTpass 120.enter 130] 140 IF bellflag THEN [OPT pass:LDA #bell :JSR oswrch:] 150[OPT pass 160 LDA #prompt Load A with ':' prompt 170 JSR oswrch Print from A 180 JSR osrdch Read in a character 190 STA char store it at char and 200 JSR oswrch print it out 210 RTS 220] 230 NEXTpass
When this program is run it asks if you want the computer to bleep or not and sets 'bellflag' accordingly. Then when the machine code is executed it inputs a character from the keyboard, bleeping if 'bellflag' is set to remind you that an input is required, and prints out a character corresponding to the first key pressed. This character is also saved at the address 'char'.