By itself, the 6502 is a very simple machine; but it can be made to perform relatively complex tasks (like interpreting programs written in BASIC) by stringing together many of its simple instructions into a machine code program. This section is not really a tutorial on machine code programming, but more an introduction to the 6502 to give an idea of how the rest of the BASIC system operates around it.
Figure 1.1 - The 6502 programming model.
The accumulator A is used for all of the arithmetic and logical operations done by the 6502, as well as just loading it from memory and storing it back into memory again. It is the only 6502 register which can be used for adding, subtracting, ANDing, etc. of numbers, and so tends to be used rather a lot. It is 8 bits (1 byte) wide, so it can only hold 256 (&100) different numbers altogether.
As an example, the instruction:
AND &80
ANDs the 8-bit number in the accumulator with the 8-bit number in location &80 (i.e. ?&80), leaving the result in the accumulator.
As an example, the instruction:
LDA &2000,Y
loads the accumulator from the location at &2000+ Y. Thus if the Y register contained &2A, the accumulator would be loaded with the contents of location &202A.
As an example, the instruction:
JMP &8000
jumps to location &8000 (in a similar way to the GOTO statement) by loading the number &8000 into the program counter.
The stack pointer S This register points into a stack in page 1, from &100 to &1FF. Numbers can be pushed on the top of the stack, to save them until later, and then pulled (or popped) again to get back the last number that was pushed. This is called a last in first out (LIFO) structure, because the first thing that you get out was the last thing that you put in.
When a single byte number is pushed on the stack, it is placed in memory at the location pointed to by the stack pointer (&1F0, say, if the S register contains &F0), and the stack pointer is decremented to point to the location below it in memory. When a byte is pulled, the opposite takes place: the stack pointer is incremented, and the number loaded from the location in page 1 which it points to.
As an example, the instruction:
PHA
pushes the contents of the accumulator on the 6502 stack.
N This is the negative flag. It is set whenever the top bit is set in the 8-bit number just calculated or loaded from memory (see section 1.2 for negative number representation).
V This is set if an overflow occurred the last time an 8-bit signed add or subtract operation was performed (see section 1.2).
B
D This is the decimal flag. It can be set if any binary coded decimal arithmetic is to be performed (see section 1.2).
I This is the interrupt flag. It can be set to prevent the 6502 from being interrupted by a hardware IRQ.
Z This is the zero flag. It is set whenever the 8-bit number just calculated or loaded from memory is zero.
C This is the carry flag. The ADC and SBC instructions use this to indicate whether there was a 'carry over' from the calculation just performed (see section 1.2). It is also used by the shift instructions (section 1.3).
Some of these flags can be tested so that parts of the machine code program are executed conditionally. For example the instruction:
BCS carry
will branch to the location 'carry' if the carry flag is set: otherwise the program will continue with the instruction after the 'BCS'. The use of these flags is explained more with the instructions in section 1.3.
1.2.1 Negative numbers
A single byte can be used to represent the positive integers from 0 to 255. This is convenient for counting; but for arithmetic, some way of representing negative numbers is really needed.
If you add the single byte number &04 to &FC, you get &00 (ignoring any carry out of the byte). So, in this case, &FC seems to be behaving as if it was -4 (as '-4' is 'the number which you add to 4 to get 0'). However, it can also represent the positive number 252. The answer is that with only 8 bits, you can't tell the difference between '252' or '252 - 256' or '252 + 256' or '252 + any number of 256s'.
So if you want half of the 256 numbers you can represent in a byte to be negative, all you have to do is leave &00 to &7F to be the positive numbers 0 to 127, and let &80 to &FF represent the negative ones. These negative ones will have the same representation as the positive numbers which you get by adding 256 to them, so '-4' will be the same as '-4+256' (252), i.e. &FC.
Choosing the numbers above &80 to be negative is very convenient, because it means that all the numbers with the top bit of the byte set will be negative, while all the numbers with the top bit zero will be positive. Thus the top bit of a signed number like this is the sign bit of the number. This is what the N flag in the 6502 is for: it indicates the sign bit of the number which has just been operated on.
This representation is often called 2's complement representation. This is because the negative of a number can be found by changing all the '1's in the binary representation to '0', and all the '0's to '1's (one's complement), and then adding 1 to it. For example, 4 is '00000100', so inverting all the bits we get '11111011', and adding 1 we get '11111100', or &FC. What you're really doing when you invert all the bits of a single byte number, is subtracting it from 255 (i.e. '11111111 '), so by adding the extra 1 again, you get the number subtracted from 256.
When addition is performed in decimal, the least significant digits are added first. Then the next digits are added, together with any carry from the first ones, if there was any. The same can be done to add a pair of large numbers in memory: for example, to add 1000 (&03E8) to 25 (&0019) the following operations will take place:
1 Add the LSB of the first number (&E8) to the LSB of the second number (&19). This gives the result &01 with a 1 to carry over to the next byte.
2 Add the MSB of the first number (&03) to the MSB of the second number (&00), with an extra 1 carried over from the last addition. This gives the result &04, with no carry.
The final result of the addition is then &0401, or 1025 in decimal.
The carry over from one byte to the next is done by the C (carry) flag in the 6502 status register. If this is set, the 6502 ADC (add with carry) instruction will automatically add an extra 1 to the addition it is about to do. To add the LSBs together, the carry flag must be cleared first (with the CLC instruction), or an extra 1 may be added where you didn't want one.
Subtraction of larger numbers is done in a very similar way, except the C flag is used as a 'borrow': if it is cleared, the last subtraction needed to borrow 1 from the next byte up, so 1 extra will be subtracted when the next subtraction is performed. To subtract the LSBs, the carry flag must be set first (with the SEC instruction), so the extra 1 is not subtracted.
The V (overflow) flag in the 6502 is set if the last add or subtract instruction caused an overflow, and the result which was obtained is not a correct 2's complement respresentation of the answer.
After an addition, the overflow flag will be set if:
(a) a carry occured from bit 6 to bit 7 of the byte, without a carry out of the byte; or
(b) a carry occurred out of the byte without a carry from bit 6 to bit 7.
In other words:
(a) the numbers being added were both positive, but the result is negative; or
(b) the numbers being added were both negative, but the result is positive.
For subtraction, the overflow flag will be set in the corresponding situations, as though you were adding the negative of the number being subtracted.
This mode is not used very often, although sometimes it is useful for representing decimal numbers exactly.
The decimal flag must never be set when using any operating system or BASIC routines, as they expect to operate in standard binary mode.
LDX The X register is loaded with the contents of the specified memory location. Flags affected: N,Z.
LDY The Y register is loaded with the contents of the specified memory location. Flags affected: N,Z.
STA The contents of the accumulator are stored in memory. The flag bits are unaffected.
STX The contents of the X register are stored in memory. The flag bits are unaffected.
STY The contents of the Y register are stored in memory. The flag bits are unaffected.
TAY Copy the contents of the accumulator to the Y register. The contents of A are unaffected. Flag bits affected: N ,Z.
TSX Copy the contents of the stack pointer to the X register. The contents of S are unaffected. Flags bits affected: N ,Z.
TXA Copy the contents of the X register to the accumulator. The contents of X are unaffected. Flags affected: N ,Z.
TXS Copy the contents of the X register to the stack pointer. The contents of X and the status register are unaffected.
TYA Copy the contents of the Y register to the accumulator. The contents of Y are unaffected. Flag bits affected: N,Z.
PHP The contents of the processor status register are pushed on the stack, and the stack pointer is updated. Flag bits are unaffected.
PLA The byte on top of the stack is transferred to the accumulator and the stack pointer is updated. Flag bits affected: N,Z.
PLP The byte on top of the stack is transferred to the P register and the stack pointer is updated. All flag bits are affected.
SBC The specified data is subtracted from the accumulator with a borrow if the carry flag is clear. The result is left in A. C is cleared if a borrow was required else it is set. Flags affected: N,V,Z,C
CMP The contents of the specified memory location are subtracted from the accumulator, setting the flags, but not storing the result. A is unaffected. Flags affected: N is set to bit7 of the result, Z is set if the result is zero. C is set if the unsigned number in the accumulator is greater than or equal to the data, otherwise cleared (as for the SBC instruction).
CPX The contents of the specified memory lcation are subtracted from the X register but the result is not stored. The flags are set in the same way as for CMP.
CPY The contents of the specified memory location are subtracted from the Y register but the result is not stored. The flags are set in the same way as for CMP.
AND Performs the bit by bit logical AND of the accumulator and the specified memory location. Result is left in the Accumulator. Flags affected: N,Z.
ORA The bit by bit logical ORing takes place between the accumulator and the memory location, the result is left in A. Flags affected: N,Z.
EOR The contents of the accumulator are exclusive-ored on a bit by bit basis with the specified data, the result is left in A. Flags affected: N,Z.
BIT The logical AND of the accumulator and memory is performed but is not stored. Flag bits affected: Z is set if the result was zero, V and N are set to bits 6 and 7 of the memory location respectively.
DEX The number in the X register is decremented by 1. Flags affected: N,Z.
DEY The number in the Y register is decremented by 1. Flags affected: N,Z.
INC The number in the specified memory location is incremented by 1. Flags affected: N,Z.
INX The number in the X register is incremented by 1. Flags affected: N,Z.
INY The number in the Y register is incrememted by 1. Flags affected: N,Z.
LSR The contents of the accumulator or the memory location are shifted to the right by 1 bit. 0 is placed in bit 7 , and bit 0 transfered to C. Flags affected: N is cleared, Z,C.
ROL The contents of the accumulator or the memory location are rotated by one bit to the left. The carry flag is shifted into bit 0, and bit7 is shifted in to the carry flag. Flags affected: N,Z,C.
ROR The contents of the accumulator or the memory location are rotated by one bit to the right. The carry flag is shifted into bit 7, and bit 0 is shifted in to the carry flag. Flags affected: N,Z,C.
RTS The program counter is pulled off the stack and incremented by one, to return from the subroutine. The stack pointer is updated. Flags bits are unaffected.
BCS If the C flag is 1 then branch to the new location, otherwise continue with the next instruction. Flag bits are unaffected.
BEQ If the Z flag is 1 then branch to the new location, otherwise continue with the next instruction. Flag bits are unaffected.
BNE If the Z flag is 0 then branch to the new location, otherwise continue with the next instruction. Flag bits are unaffected.
BMI If the N flag is 1 then branch to the new location, otherwise continue with the next instruction. Flag bits are unaffected.
BPL If the N flag is 0 then branch to the new location, otherwise continue with the next instruction. Flag bits are unaffected.
BVC If the V flag is 0 then branch to the new location, otherwise continue with the next instruction. Flag bits are unaffected.
BVS If the V flag is 1 then branch to the new location, otherwise continue with the next instruction. Flag bits are unaffected.
CLI The Interrupt flag is cleared, no other flags are affected. This enables interrupts from the IRQ input.
CLV The Overflow bit is cleared, no other flags are affected.
SEC C is set. Other flags remain unaffected.
SED D is set. The ADC and SBC instructions will now operate in the BCD mode. Other flags remain unaffected.
SEI I is set. No IRQs will be acknowledged until it is cleared. Other flag bits are unaffected.
System control operations
BRK This causes an interrupt to be generated and is not maskable. Flags affected: B is set.
NOP The processor does nothing for two cycles.
RTI This pulls the processor status and then the program counter off the stack. The stack pointer is updated. This is used to terminate an interrupt. All flags affected.
Altogether, the 6502 has 13 different addressing modes: these are listed in this section.
TAX
will transfer the contents of the accumulator to the X register, and doesn't need any other information.
ASL A
will shift the accumulator left 1 bit.
Immediate addressing
The single-byte number following the opcode is to be used directly by the instruction. This addressing mode is marked by a '#' in front of the data. For example:
ORA #&80
will logically OR the contents of the accumulator with the singlebyte number '&80' (128).
LDY &2000
will load the Y register with the contents of memory location &2000.
STA &70
will store the contents of the accumulator into the zero page memory location &70.
DEC &3000,X
will decrement the location at &3000+X by 1. If the X register contained &54, the contents of location &3054 will be decremented.
INC &80,Y
will increment the contents of the location whose LSB is given by &80+X, and whose MSB is &00. Thus if Y contains &04, the contents of zero page location &84 will be incremented; if Y contains &FE, the contents of zero page location &7E will be incremented.
.loop BEQ loop
will branch back to the same location if the Z flag is set. The byte following the opcode will be &FE (-2) for this instruction, because the branch instruction is 2 bytes back from the next instruction which would be executed if the branch did not take place.
Indirect addressing
The 2-byte absolute address following the opcode points to two consecutive bytes which contain the LSB and the MSB of the location to be used. The two bytes are stored LSB first, MSB second. This addressing mode is only used by the JMP instruction. For example:
JMP (&0200)
will jump to the location whose address is contained in &0200 (LSB) and &0201 (MSB).
LDA (&50,X)
will use the number in &50+X (LSB) and &51+X (MSB) as a pointer to the number to be loaded into the accumulator. Thus if X contained &20, location &70 contained the number &34, and location &71 contained the number &12, the number in location &1234 would be loaded into the accumulator.
CMP (&2A),Y
will compare the accumulator with the byte pointed to by the base pointer in &2A (LSB) and &2B (MSB), offset by Y. Thus if &2A contains &00, and &2B contains &40, and Y contains &45, the accumulator will be compared with the contents of location &4045.
These addressing mode groups are used extensively by the builtin assembler in BASIC. See chapter 6 for more on this.
BRK, CLC, CLD, CLI, CLV, DEX, DEY, INX, INY, NOP, PHA, PHP, PLA, PLP, RTI, RTS, SEC, SED, SEI, TAX, TAY, TSX, TXA, TXS, TYA.
BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS.
ADC, SBC, CMP, AND, EOR, ORA, LDA, STA
These instructions all operate on the accumulator, and allow the following addressing modes:
Immediate (not STA)
Zero page
Absolute
Zero page,X
Absolute,X
Absolute,Y
(Indirect,X)
(Indirect),Y
ASL, LSR, ROL, ROR
and they allow the following addressing modes:
Accumulator
Zero page
Absolute
Zero page,X
Absolute,X
DEC, INC and they allow the following addressing modes:
Zero page
Absolute
Zero page,X
Absolute,X
BIT, CPX, CPY
and they allow the following addressing modes:
Immediate (not BIT)
Zero page
Absolute
Index load group
The instructions in this group are:
LDX, LDY
and they allow the following addressing modes:
Immediate
Zero page
Absolute
Zero page,X (',Y' for LDX)
Absolute,X (',Y' for LDX)
STX, STY
and they allow the following addressing modes:
Zero page
Absolute
Zero page,X (',Y' for STX)
JMP, JSR
and they allow the following addressing modes:
Absolute
(Indirect) (not JSR)
1.6 The BASIC assembler
The BBC User Guide and the Electron User Guide give an adequate description of the use of the built-in assembler, so I won't cover it again here. However, BBC micro owners may not be aware of the extra facilities available on the assembler in BASIC 2, over that in BASIC 1 (which is the one described in the User Guide). These extra facilities are remote assembly, and the EQU directive.
Bit | Option | |
0 | assembly listing if set | |
1 | errors enabled if set | |
2 | remote assembly if set |
If this is being used, P% should be set up to point to the location where the routine will end up (&8000 in the above example), but O% should point to the location where the generated code is to be stored.
EQUB | equate byte | reserves 1 byte |
EQUW | equate word | reserves 2 bytes |
EQUD | equate double word | reserves 4 bytes |
EQUS | equate string | reserves a string |
Note that the EQUS directive only reserves the space for the characters of the string; if a carriage return or CRLF is needed on the end, this must be done separately with an EQUB directive.
For example:
EQUB &40 EQUS "HI" EQUW &1234
will reserve and initialise the following bytes in memory:
&40 &48 ("H") &49 ("I") &34 &12
Using the EQU directive is not only more convenient than using the BASIC equivalent, but it also makes the program much more readable. Many of the programs in this book use the EQU directive, although where it has been used, the alternative BASIC form is available for BASIC 1 users.