Search the Community
Showing results for tags '6502'.
-
assembly Chapter 4: The 6502 Microprocessor Assembly Language Tutorial
Linux Hint posted a topic in Linux
Chapter 4: The 6502 Microprocessor Assembly Language Tutorial 4.1 Introduction The 6502 microprocessor was released in 1975. It was used as the microprocessor for some personal computers then such as Apple II, Commodore 64, and BBC Micro. The 6502 microprocessor is still being produced in large numbers today. It is no longer a central processing unit that is used in personal computers (laptops) today, but it is still produced in large numbers and used in electronic and electrical appliances today. In order to understand the more modern computer architectures, it is very helpful to examine an older but quite successful microprocessor such as the 6502. Since it is simple to understand and program, it is one of the best (if not the best) microprocessor to use for teaching assembly language. Assembly language is a low-level language that can be used to program a computer. Note that the assembly language for one microprocessor is different from the assembly language of another microprocessor. The 6502 microprocessor assembly language is taught in this chapter. More precisely, it is the 65C02 that is taught, but is simply referred to, as the 6502. A famous computer in the past is called the commodore_64. The 6502 is a microprocessor of the 6500 family. The commodore_64 computer uses the 6510 microprocessor. The 6510 microprocessor is of the 6500 µP. The instruction set of the 6502 µP is almost all of the instructions of the 6510 µP. The knowledge of this chapter and the next is based on the commodore_64 computer. This knowledge is used as the bases to explain the modern computer architectures and modern operating systems in this part of the online career course. Computer architecture refers to the components of the motherboard of the computer and an explanation of how the data flows within each component, especially the microprocessor, how the data flows between the components, and also how the data interacts. The singular for data is datum. An effective way to study the computer architecture of a computer is to study the assembly language of the motherboard. The commodore_64 computer is said to be a computer of 8-bit computer word. This means that the information is stored, transferred, and manipulated in the form of eight-bit binary codes. Block Diagram of the Commodore 64 Motherboard The block diagram of the commodore 64 motherboard is: Fig 4.1 Block Diagram of the Commodore_64 System Unit Imagine the 6510 microprocessor as the 6502 microprocessor. The total memory is a series of bytes (8-bits per byte). There is the random-access (read/write) memory to which the bytes can be written or erased. When the power of the computer is switched off, all information in the random-access memory (RAM) is erased. There is also the read-only memory (ROM). When power of the computer is switched off, the information in the ROM remains (is not erased). There is the input/output port (circuit) which is referred to as the input/output devices in the diagram. This port should not be confused with the ports that are visible at the left-and-right or front-and-back vertical surfaces of the computer system unit. Those are two different things. The connections from this inner port to the peripherals such as the hard-disk (or floppy-disk), keyboard, and monitor are not shown in the diagram. There are three busses (groups of electrical very small wire conductors) in the diagram. Each wire can transfer a bit 1 or bit 0. The data bus, for the transfer of eight-bit byte at a time (one clock pulse) to the RAM and input/output port (input/output devices) is bidirectional. The data bus is eight bits wide. All the components are connected to the address bus. The address bus is unidirectional from the microprocessor. There are sixteen conductors for the address bus, and each carries one bit (1 or 0). Sixteen bits are sent in one clock pulse. There is the control bus. Some of the conductors of the control bus would transfer one bit each from the microprocessor to the other components. A few control lines carry the bits from the input/output (IO) port to the microprocessor. Computer Memory The RAM and ROM are considered as one memory assembly. This assembly is represented diagrammatically as follows where hexadecimal numbers have the “$” prefix: Fig 4.11 Memory Layout for the Commodore 64 Computer The RAM is from 000016 to DFFF16 which is written as from $0000 to $DFFF. With the 6502 µP assembly language, a hexadecimal number is prefixed with “$” and not suffixed (subscripted) with 16 or H or hex. Any information in RAM goes off when the computer is switched off. The ROM begins from $E000 to $FFFF. It has subroutines which do not go off when the computer is switched off. These subroutines are the commonly used routines which assists in programming. The user program calls them (refer to next chapter). The space (bytes) from $0200 to $D000 is for the user programs. The space from $D000 to $DFFF is for information that is directly related to the peripherals (input/output devices). This is part of the operating system. So, the operating system of the commodore-64 computer is in two main parts: the part in ROM that never goes off and the part from $D000 to $DFFF that goes off when the power is switched off. This IO (input/output) data has to be loaded from a disk each time the computer is switched on. Today, such data are called peripheral drivers. The peripherals begin from the Input/Output Device port through the connections on the motherboard to the identifiable ports on the vertical surfaces of the computer to which the monitor, keyboard, etc. are connected to and to the peripherals themselves (monitor, keyboard, etc.). The memory consists of 216 = 65,536 byte locations. In hexadecimal form, these are 1000016 = 10000H = 10000hex= $10000 locations. In computing, counting in base two, base ten, base sixteen, etc. begins from 0 and not from 1. So, the first location is actually the location number of 00000000000000002 = 010 = 000016 = $0000. In the 6502 µP assembly language, the identification of an address location is prefixed with $ and there is no suffix or subscript. The last location is the location number of 11111111111111112 = 65,53510 = FFFF16 = $FFFF and not 100000000000000002, or 65,53610, or 1000016, or $10000. The 100000000000000002, 65,53610, 1000016, or $10000 gives the total number of byte locations. Here, 216 = 65,536 = 64 x 1024 = 64 x 210 = 64 Kbytes (Kilobytes). The suffix of 64 in the name Commodore-64 means 64KB of total memory (RAM and ROM). A byte is 8 bits, and the 8 bits will go into one byte location in the memory. The 64 Kbytes of memory is divided into pages. Each page has 010016 = 25610 byte locations. The first 25610 = first 010016 locations is page 0. The second is page 1, the third is page 2, and so on. In order to address the 65,536 locations, 16 bits are necessary for each location (address). So, the address bus from the microprocessor to the memory consists of 16 lines; one line for one bit. A bit is either 1 or 0. The 6502 µP Registers A register is like the byte cells for a byte memory location. The 6502 µP has six registers: five 8-bit registers and one 16-bit register. The 16-bit register is called the Program Counter which is abbreviated as PC. It holds the memory address for the next instruction. An assembly language program consists of instructions that are placed in memory. Sixteen (16) different bits are needed to address a particular byte location in the memory. At a particular clock pulse, these bits are sent to the 16-bit address lines of the address bus for the reading of an instruction. All the registers for the 6502 µP are depicted as follows: Fig. 4.12 6502 µP Registers The Program Counter or PC can be seen as a 16-bit register in the diagram. The lower significant eight bits is labelled as PCL for Program Counter Low. The higher significant eight bits is labelled as PCH for Program Counter High. An instruction in memory for Commodore-64 can consist of one, two, or three bytes. The 16 bits in the PC point to the next instruction to be executed, in memory. Among the circuits in the microprocessor, two of them are called Arithmetic Logic Unit and Instruction Decoder. If the current instruction that is being processed in the µP (microprocessor) is one byte long, these two circuits increase the PC for the next instruction by 1 unit. If the current instruction that is being processed in the µP is two bytes long, meaning it occupies two consecutive bytes in memory, these two circuits increase the PC for the next instruction by 2 units. If the current instruction that is being processed in the µP is three bytes long, meaning it occupies three consecutive bytes in memory, these two circuits increase the PC for the next instruction by 3 units. The accumulator “A” is an eight-bit general purpose register that stores the result of most arithmetic and logic operations. The “X” and “Y” registers are each used to count the program steps. The counting in programming begins from 0. So, they are called as index registers. They have a few other purposes. Though the Stack Pointer register, “S” has 9 bits which is considered as an eight-bit register. Its content points to a byte location in page 1 of the Random Access Memory (RAM). Page 1 begins from byte $0100 (25610) to byte $01FF (51110). When a program is running, it moves from one instruction to the next consecutive instruction in the memory. However, this is not always the case. There are times when it jumps from one memory area to another memory area to continue running the instructions there, consecutively. Page 1 in RAM is used as the stack. The stack is a large RAM memory area that has the next addresses for the continuation of the code from where there is a jump. The codes with jumping instructions are not in the stack; they are elsewhere in the memory. However, after the jump-to instructions are executed, the continuation addresses (not code segments) are in the stack. They were pushed there as a result of the jump or branch instructions. The eight-bit Processor Status register of P is a special kind of register. The individual bits are not related or connected to one another. Each bit there is called a flag and is appreciated independently of the others. The meanings of the flags are given in the following as the need arises. The first and last bit index for each register are indicated above each register in the previous diagram. The bit index (position) counting in a register begins from 0 on the right. Memory Pages in Binary, Hexadecimal, and Decimal The following table shows the beginning of the memory pages in binary, hexadecimal, and decimal: Each page has 1,0000,00002 number of bytes which is the same as 100H number of bytes which is the same as 25610 number of bytes. In the previous memory diagram, the pages are indicated going up from page 0 and not going down as indicated in the table. The binary, hexadecimal, and decimal columns of this table give the memory byte location addresses in their different bases. Notice that for page zero, only the bits for the lower byte are necessary to type when coding. The bits for the higher byte can be omitted since they are always zeros (for page zero). For the rest of the pages, the bits for the higher byte should be used. The rest of this chapter explains the 6502 µP Assembly Language using all the previous information. In order to quickly understand the language, the reader has to add and subtract in base sixteen instead of base ten. It is actually supposed to be base two, but calculating in base two is cumbersome. Remember that when adding two numbers in base two, a carry is still 1 as in base ten. But when subtracting two numbers in base two, a borrow is two and not ten as in base ten. When adding two numbers in base sixteen, a carry is still 1 as in base ten. But when subtracting two numbers in base sixteen, a borrow is sixteen and not ten as in base ten. 4.2 Data Transfer Instructions Consider the following table of the assembly language data transfer instructions for the 6502 µP: When a byte (8-bits) is copied from a memory byte location to the Accumulator Register, X Register, or Y Register, that is loading. When a byte is copied from any of these registers to a memory byte location, that is transferring. When a byte is copied from one register to another, that is still transferring. In the second column of the table, the arrow shows the direction of the copy for a byte. The rest of the four columns show different addressing modes. An entry in the addressing mode column is the actual byte code for the corresponding mnemonic part of the instruction in hexadecimal. AE, for example, is the actual byte code for LDX which is to load a byte from the memory to the X register in absolute addressing mode like AE16 = 101011102. So, the bits for LDX in a memory byte location is 10101110. Notice that for the LDX mnemonic part of the instruction, there are three possible bytes which are A2, AE, and A6, and each is for a particular addressing mode. When the byte that loads into the X register is not to be copied from a memory byte location, the value has to be typed with (just after) the LDX mnemonic in the instruction in hexadecimal or decimal. In this chapter, such values are typed in hexadecimal. This is immediate addressing, so the actual byte in memory to represent LDX is A216 = 101000102 and not AE16 which is equal to 101011102. In the table, all the bytes under the addressing mode headings are called Operation Codes which is abbreviated as opcodes. There can be more than one opcode for one mnemonic, depending on the addressing mode. Note: The word “load” in the computer system unit can have two meanings: it can refer to the loading of a file from a disk to the computer’s memory or it can refer to the transferring of a byte from a memory byte location to a microprocessor register. There are more addressing modes than the four in the table for the 6502 µP. Unless otherwise stated, all user programming code in this chapter begins from address 020016 which is the beginning of the user area in the memory. Memory M and Accumulator A Memory to Accumulator Immediate Addressing The following instruction stores the number FF16 = 25510 into the accumulator: LDA #$FF The “$” is not only used to identify a memory address. In general, it is used to indicate that the next number that follows is hexadecimal. In this case, $FF is not the address of any memory byte location. It is the number 25510 in hexadecimal. The base 16 or any of its other equivalent subscripts must not be written in the assembly language instruction. The “#” indicates that whatever follows next is the value to be put into the accumulator register. The value can also be written in base ten, but that is not done in this chapter. The “#” means immediate addressing. A mnemonic has some resemblance to its corresponding English phrase. “LDA #$FF” means load the number 25510 into the accumulator A. Since this is immediate addressing from the previous table, LDA is A9 and not AD or A5. A9 in binary is 101010001. So, if A9 for LDA is in $0200 address in the memory, $FF is in $0301 = 0300 + 1 address. The #$FF is precisely the operand for the LDA mnemonic. Absolute Addressing If the value of $FF is in $0333 location in the memory, the previous instruction is: LDA $0333 Note the absence of #. In this case, the absence of # means that what follows is a memory address and not the value of interest (not the value to put into the accumulator). So, the opcode for LDA, this time, is AD and not A9 or A5. The operand for LDA here is the $0333 address and not the $FF value. $FF is in $0333 location which is rather far away. The “LDA $0333” instruction occupies three consecutive locations in the memory, and not two, as the previous illustration did. “AD” for LDA is in the $0200 location. The lower byte of 0333 which is 33 is in the $0301 location. The higher byte of $0333 which is 03 is in the $0302 location. This is little endianness which is used by the 6502 assembly language. The assembly languages of different microprocessors are different. This is an example of absolute addressing. The $0333 is the address of the location that has $FF. The instruction consists of three consecutive bytes and does not include the $FF or its actual byte location. Zero-Page Addressing Assume that the $FF value is in the $0050 memory location in page zero. The byte locations for the zero-page begin from $0000 and end at $00FF. These are 25610 locations in total. Each page of the Commodore-64 memory is 25610 long. Notice that the higher byte is zero for all possible locations in the zero-page space in memory. The zero-page addressing mode is the same as the absolute addressing mode, but the higher byte of 00 is not typed into the instruction. So, to load the $FF from the $0050 location into the accumulator, the zero-page addressing mode instruction is: LDA $50 With LDA being A5 and not A9 or AD, A516 = 101001012. Remember that each byte in the memory is of 8 cells, and each cell houses a bit. The instruction here consists of two consecutive bytes. A5 for LDA is in the $0200 memory location and the $50 address, without the higher byte of 00, is in the $0301 location. The absence of 00, which would have consumed a byte in the total 64K memory, economizes the memory space. Accumulator to Memory Absolute Addressing The following instruction copies a byte value, whatever it is, from the accumulator to the memory location of $1444: STA $1444 This is said to be transferring from the accumulator to the memory. It is not loading. Loading is the opposite. The opcode byte for STA is 8D16 = 100011012. This instruction consists of three consecutive bytes in the memory. The 8D16 is in the $0200 location. The 4416 of the $1444 address is in the $0201 location. And 1416 is in the $0202 location – little endianness. The actual byte that is copied is not part of the instruction. 8D and not 85 for zero-page addressing (in the table) are used here for STA. Zero Page Addressing The following instruction copies a byte value, whatever it is, from the accumulator to memory location of $0050 in page zero: STA $0050 The opcode byte for STA here is 8516 = 100001012. This instruction consists of two consecutive bytes in memory. The 8516 is in location $0200. The 5016 of the $0050 address is in location $0201. The issue of endianness does not arise here because the address has only one byte which is the lower byte. The actual byte that is copied is not part of the instruction. 85 and not 8D for zero-page addressing are used here for STA. It does not make sense to use the immediate addressing to transfer a byte from the accumulator to a location in the memory. This is because the actual value like $FF has to be quoted in the instruction in immediate addressing. So, immediate addressing is not possible for the transfer of a byte value from a register in the µP to any memory location. LDX, STX, LDY, and STY Mnemonics LDX and STX are similar to LDA and STA, respectively. But here, the X register is used and not the A (accumulator) register. LDY and STY are similar to LDA and STA, respectively. But here, the Y register is used and not the A register. Refer to Table 4.21 for each opcode in hexadecimal that corresponds to a particular mnemonic and a particular addressing mode. Register-to-Register Transfers The previous two sets of instructions in Table 4.21 deal with memory/microprocessor-register copying (transfer) and register/register copying (transfer). The TAX, TXA, TAY, TYA, TSX and TXS instructions do the copying (transfer) from the register in the microprocessor to another register of the same microprocessor. To copy the byte from A to X, the instruction is: TAX To copy the byte from X to A, the instruction is: TXA To copy the byte from A to Y, the instruction is: TAY To copy the byte from Y to A, the instruction is: TYA For the commodore 64 computer, the stack is page 1 just after page 0 in the memory. Like every other page, it consists of 2561010 byte locations, from $0100 to $01FF. Normally, a program executes from one instruction to the next consecutive instruction in the memory. From time to time, there is a jump to another memory code (set of instructions) segment. The stack area the in memory (RAM) has the next instruction addresses from where the jumps (or branches) left off for program continuation. The stack pointer “S” is a 9-bit register in the 6502 µP. The first bit (leftmost) is always 1. All the byte location addresses in page one begin with 1 followed by 8 different bits for the 25610 locations. The stack pointer has the address of the location in page 1 which has the address of the next instruction that the program has to return and continue with after executing the current (jumped-to) code segment. Since the first bit of all the addresses of the stack (page one) begin with 1, the stack pointer register only needs to hold the remaining eight bits. After all, its first bit, which is the leftmost-bit (the ninth bit counting from its right), is always 1. To copy the byte from S to X, the instruction is: TSX To copy the byte from X to S, the instruction is: TXS The register-to-register instructions do not take any operand. They consist of just the mnemonic. Each mnemonic has its opcode in hexadecimal. This is in implied addressing mode because there is no operand (no memory address, no value). Note: There is no X to Y or Y to X transfer (copying). 4.3 Arithmetic Operations The circuit, Arithmetic Logic Unit in the 6502 µP, can only add two eight-bit numbers at a time. It does not subtract, it does not multiply, and it does not divide. The following table shows the opcodes and addressing modes for arithmetic operations: Note: All mnemonics for arithmetic operations and other types of operations (i.e. all 6502 mnemonics) take one byte of operation (op) code. If there is more than one addressing mode for the mnemonic, there would be different opcodes for the same mnemonic: one per addressing mode. The C, D, and V in the table are the flags of the status register. Their meanings will be given later as the need arises. Addition of Unsigned Numbers With the 6502 µP, the signed numbers are two’s complement numbers. The unsigned numbers are ordinary positive numbers that begin from zero. So, for a byte of eight-bits, the smallest unsigned number is 000000002 = 010 = 0016 and the biggest unsigned number is 111111112 = 25510 = FF16. For two unsigned numbers, the addition is: A+M+C→A That means that the 8-bit content of the accumulator is added by the arithmetic logic unit to a byte (8-bits) from the memory. After the addition of A and M, the carry to the nineth bit goes to the carry flag cell in the status register. Any previous carry bit from a previous addition which is still in the carry flag cell in the status register is also added to the sum of A and M, making A+M+C→A. The result is put back into the accumulator. If the addition of interest is: A + M And there is no need to add any previous carry, the carry flag has to be cleared which is made 0, so that the addition is: A+M+0→A same as A+M→A Note: If M is added to A, and a carry of 1 occurs because the result is greater than 25510 = 111111112 = FF16, this is a new carry. This new carry of 1 is automatically sent to the carry flag cell in case it is needed by the next pair of eight-bits to be summed (another A + M). Code to Add Two Unsigned Eight-Bits 001111112 + 000101012 is the same as 3F16 + 1516 which is the same as 6310 + 2110. The result is 0101010022 which is the same as 5416 and 8410. The result is not beyond the maximum number for eight bits which is 25510 = 111111112 = FF16. So, there is no resulting carry of 1. To put it another way, the resulting carry is 0. Before the addition, there is no previous carry of 1. In other words, the previous carry is 0. The code to do this addition can be: CLC LDA #$3F ADC #$15 Note: While typing the assembly language, the “Enter” key of the keyboard is pressed at the end of each instruction. There are three instructions in this code. The first instruction (CLC) clears the carry flag in case a previous addition has 1. CLC can only be done in implied addressing mode. The mnemonic for implied addressing mode takes no operand. This clears the carry cell of the status register of P. Clearing means giving the bit of 0 to the carry flag cell. The next two instructions in the code use the immediate addressing mode. With immediate addressing, there is only one operand for the mnemonic which is a number (and neither a memory nor register address). And so, the number must be preceded by “#”. The “$” means that the number that follows is hexadecimal. The second instruction loads the number 3F16 into the accumulator. For the third instruction, the arithmetic logic unit circuit of the µP takes the previous (cleared) carry of 0 (forced to 0) of the carry flag cell, of the status register and adds it to 1516 as well as to the value that is already in the 3F16 accumulator and puts the complete result back into the accumulator. In this case, there is a resulting carry of 0. The ALU (Arithmetic Logic Unit) sends (puts) 0 into the carry flag cell of the status register. The processor status register and the status register mean the same thing. If a carry of 1 resulted, the ALU sends 1 to the carry flag of the status register. The three lines of previous code have to be in the memory before they are executed. The opcode 1816 for CLC (implied addressing) is in $0200 byte location. The opcode A916 for LDA (immediate addressing) is in $0201 byte location. The number 3F10 is in $0202 byte location. The opcode 6916 for LDA (immediate addressing) is in $0203 byte location. The number 1510 is in $0204 byte location. Note: LDA is a transfer (load) instruction and not an arithmetic instruction (mnemonic). Code to Add Two Unsigned Sixteen-Bits All the registers in the 6502 µP are essentially eight-bit registers, except the PC (Program Counter) that is 16-bits. Even the status register is 8-bits wide, though its eight bits do not operate together. In this section, the addition of two 16 unsigned bits, with a carry from the first pair of eight bits to the second pair of eight bits, is considered. The carry of interest here is the carry from the eighth bit position to the nineth bit position. Let the numbers be 00101010101111112 = 2ABF1616 = 10,94310 and 00101010100101012 = 2A9516 = 10,90110. The sum is 01010101010101002 = 555416 = 21,84410. Adding these two unsigned numbers in base two is as follows: The following table shows the same addition with the carry of 1 from the eighth bit to the ninth bit position, beginning from the right: In coding this, the two lower bytes are added first. Then, the ALU (Arithmetic Logic Unit) sends the carry of 1 from the eighth bit position to the nineth bit position, to the carry flag cell in the status register. The result of 0 1 0 1 0 1 0 0 without the carry goes to the accumulator. Then, the second pair of bytes is added with the carry. The ADC mnemonic means adding with the previous carry automatically. In this case, the previous carry, which is 1, must not be changed before the second addition. For the first addition, since any previous carry is not part of this complete addition, it must be cleared (made 0). For the complete addition of the two pairs of bytes, the first addition is: A + M + 0 -> A The second addition is: A + M + 1 -> A So, the carry flag has to be cleared (given value of 0) just before the first addition. The following program of which the reader must read the explanation that follows uses the absolute addressing mode for this summing: CLC LDA $0213 ADC $0215 ; no clearing because the carry flag value is needed STA $0217 LDA $0214 ADC $0216 STA $0218 Note that with the 6502 assembly language, a semicolon begins a comment. This means that in the execution of the program, the semicolon and everything on its right is ignored. The program that is previously written is in a text file is saved with the name of the programmer’s choice and with the“.asm” extension. The previous program is not the exact program that goes to the memory for execution. The corresponding program in the memory is called the translated program where the mnemonics are replaced with the opcodes (bytes). Any comment remains in the assembly language text file, and is stripped off before the translated program reaches the memory. In fact, there are two files that are saved in the disk today: the “.asm” file and the “.exe” file. The “.asm” file is the one in the previous illustration. The “.exe” file is the “.asm” file with all comments stripped off, and all mnemonics replaced by their opcodes. When opened in a text editor, the “.exe” file is unrecognizable. Unless otherwise stated, for the purpose of this chapter, the “.exe” file is copied to the memory beginning from the $0200 location. This is the other meaning of loading. The two 16-bit numbers to be added occupy four bytes in the memory for absolute addressing: two bytes per number (memory is a sequence of bytes). With absolute addressing, the operand to the opcode is in the memory. The summing result is two bytes wide and is also has to be placed in the memory. This gives a total of 610 = 616 bytes for inputs and output. The inputs are not from the keyboard and the output is not from the monitor or printer. The inputs are in the memory (RAM) and the output (summing result) goes back to the memory (RAM) in this situation. Before a program is executed, the translated version has to be in the memory first. Looking at the previous program code, it can be seen that the instructions without the comment make up 1910 = 1316 bytes. So, the program takes from $0200 byte location in the memory to $0200 + $13 – $1 = $0212 byte locations (beginning from $0200 and not $0201 which implies – $1). Adding the 6 bytes for the input and output numbers makes all the program end at $0212 + $6 = $0218. The total length of the program is 1916 = 2510. The lower byte of the augend should be in the $0213 address, and the higher byte of the same augend should be in the $0214 address – little endianness. Similarly, the lower byte of the addend should be in the $0215 address, and the higher byte of the same addend should be in the $0216 address – little endianness. The lower byte of the result (sum) should be in the $0217 address, and the higher byte of the same result should be in the $0218 address – little endianness. The opcode 1816 for CLC (implied addressing) is in the byte location of $0200. The opcode for “LDA $0213”, i.e. AD16 for LDA (absolute addressing), is in the byte location of $0201. The lower byte of the augend which is 10111111 is in the memory byte location of $0213. Remember that each opcode occupies one byte. The “$0213” address of “LDA $0213” is in the byte locations of $0202 and $0203. The “LDA $0213” instruction loads the lower byte of the augend to the accumulator. The opcode for “ADC $0215”, i.e. 6D16 for ADC (absolute addressing), is in the byte location of $0204. The lower byte of the addend which is 10010101 is in the byte location of $0215. The “$0215” address of “ADC $0215” is in the byte locations of $0205 and $0206. The “ADC $0215” instruction adds the lower byte of the addend to the lower byte of the augend which is already in the accumulator. The result is placed back in the accumulator. Any carry after the eighth bit is sent to the carry flag of the status register. The carry flag cell must not be cleared before the second addition of the higher bytes. This carry is added to the sum of the higher bytes automatically. In fact, a carry of 0 is added to the sum of the lower bytes automatically at the beginning (equivalent to no carry being added) because of CLC. The comment takes the next 4810 = 3016 bytes. However, this remains only in the “.asm” text file. It does not reach the memory. It is removed by the translation which is done by the assembler (a program). For the next instruction which is “STA $0217”, the opcode of STA which is 8D16 (absolute addressing) is in the byte location of $0207. The “$0217” address of “STA $0217” is in the memory locations of $0208 and $0209. The “STA $0217” instruction copies the eight-bit content of the accumulator to the memory location of $0217. The higher byte of the augend which is 00101010 is in the memory location of $0214, and the higher byte of the addend which is 00101010 is in the byte location of $0216. The opcode for “LDA $0214” which is AD16 for LDA (absolute addressing) is in the byte location of $020A. The “$0214” address of “LDA $0214” is in the locations of $020B and $020C. The “LDA $0214” instruction loads the higher byte of the augend to the accumulator, erasing whatever is in the accumulator. The opcode for “ADC $0216” which is 6D16 for ADC (absolute addressing) is in the byte location of $020D. The “$0216” address of “ADC 0216” is in the byte locations of $020E and $020F. The “ADC $0216” instruction adds the higher byte of the addend to the higher byte of the augend which is already in the accumulator. The result is placed back into the accumulator. If there is a carry of 1, for this second addition, it is automatically placed in the carry cell of the status register. Though the carry beyond the sixteenth bit (left) is not required for this problem, it is nice to check if a carry of 1 did occur by checking if the carry flag became 1. For the next and last instruction which is “STA $0218”, the opcode of STA which is 8D16 (absolute addressing) is in the byte location of $0210. The “$0218” address of “STA $0218” is in the memory locations of $0211 and $0212. The “STA $0218” instruction copies the eight-bit content of the accumulator to the memory location of $0218. The result of the addition of the two sixteen-bit numbers is 0101010101010100, with the lower byte of 01010100 in the memory location of $0217 and the higher byte of 01010101 in the memory location of $0218 – little endianness. Subtraction With the 6502 µP, the signed numbers are two’s complement numbers. A two’s complement number can be eight-bits, sixteen bits, or any multiple of eight bits. With two’s complement, the first bit from the left is the sign bit. For a positive number, this first bit is 0 to indicate the sign. The rest of the bits form the number in the normal way. To obtain the two’s complement of a negative number, invert all the bits for the corresponding positive number, and then add 1 to the result from the right end. To subtract one positive number from another positive number, the subtrahend is converted into a two’s complement negative number. Then, the minuend and the new negative number are added in the normal way. So, the eight-bits subtraction becomes: Where the carry is assumed as 1. The result in the accumulator is the difference in two’s complement. So, to subtract two numbers, the carry flag must be set (made to 1). When subtracting two sixteen-bit numbers, the subtraction is done twice like with the addition of two sixteen-bit numbers. Since subtraction is a form of addition with the 6502 µP, when subtracting two sixteen-bit numbers, the carry flag is set only once for the first subtraction. For the second subtraction, any setting of the carry flag is done automatically. Programming the subtraction for eight-bit numbers or sixteen bit numbers is done similarly to programming the addition. However, the carry flag must be set at the very beginning. The mnemonic to do this is: Subtraction with Sixteen-bit Positive Numbers Consider the subtraction with the following numbers: This subtraction does not involve two’s complement. Since the subtraction in 6502 µP is done in two’s complement, the subtraction in base two is done as follows: The two’s complement result is the same as the result that is obtained from the ordinary subtraction. However, note that the 1 that goes to the seventeenth bit position from the right is ignored. The minuend and the subtrahend are split into two eights bits each. The two’s complement of 10010110 of the subtrahend’s lower byte is determined independently of its higher byte and of any carry. The two’s complement of 11101011 of the subtrahend’s higher byte is determined independently of its lower byte and of any carry. The 16-bits of the minuend is already in two’s complement, beginning with 0 from the left. So, it does not need any adjustment in bits. With the 6502 µP, the lower byte of the minuend without any modification is added to the lower byte of the two’s complement of the subtrahend. The lower byte of the minuend is not converted in two’s complement because the sixteen bits of the whole minuend have to be already in two’s complement (with a 0 as the first bit on the left). In this first addition, a compulsory carry of 1 is added due to the 1=0 SEC instruction. In the current effective subtraction, there is a carry of 1 (of addition) from the eighth bit to the ninth bit (from the right). Since this is effectively subtraction, whatever bit that is supposed to be in the carry flag in the status register is complemented (inverted). So, the carry of 1 becomes 0 in the C flag. In the second operation, the higher byte of the minuend is added to the higher two’s complement byte of the subtrahend. The automatically complemented carry flag bit of the status register (in this case is 0) is also added (to the higher bytes). Any 1 that goes beyond the sixteenth bit from the right is ignored. The next thing is just to code all that scheme as follows: SEC LDA $0213 SBC $0215 ; no clearing because the inverted carry flag value is needed STA $0217 LDA $0214 SBC $0216 STA $0218 Remember that with the 6502 assembly language, a semicolon begins a comment which is not included into the translated program version in the memory. The two 16-bit numbers for subtraction occupy four bytes of memory with absolute addressing; two per number (memory is a series of bytes). These inputs are not from the keyboard. The summing result is two bytes and also has to be placed in memory in a different place. This output does not go to the monitor or printer; it goes to the memory. This gives a total of 610 = 616 bytes for inputs and output to be placed in the memory (RAM). Before a program is executed, it has to be in the memory first. Looking at the program code, it can be seen that the instructions without the comment make up 1910 = 1316 bytes. Since all programs in this chapter begin from the memory location of $0200, the program takes from the $0200 byte location in the memory to the $0200 + $13 – $1 = $0212 byte location (beginning from $0200 and not $0201). This range does not include the region for the input and output bytes. The two input numbers take 4 bytes and the one output number takes 2 bytes. Adding the 6 bytes for the input and output numbers makes the range for the program which ends at $0212 + $6 = $0218. The total length of the program is 1916 = 2510. The lower byte of the minuend should be in the $0213 address, and the higher byte of the same minuend should be in the $0214 address – little endianness. Similarly, the lower byte of the subtrahend should be in the $0215 address, and the higher byte of the same subtrahend should be in the $0216 address – little endianness. The lower byte of the result (difference) should be in the $0217 address, and the higher byte of the same result should be in the $0218 address – little endianness. The opcode of 3816 for SEC (implied addressing) is in the $0200 address. All programs in this chapter are assumed to start at the memory location of $0200, annulling any program that would have been there; except otherwise stated. The opcode for “LDA $0213”, i.e. AD16, for LDA (absolute addressing) is in the $0201 byte location. The lower byte of the minuend which is 10111111 is in the memory byte location of $0213. Remember that each opcode occupies one byte. The “$0213” address of “LDA $0213” is in the byte locations of $0202 and $0203. The “LDA $0213” instruction loads the lower byte of the minuend to the accumulator. The opcode for “SBC $0215”, i.e. ED16, for SBC (absolute addressing) is in the $0204 byte location. The lower byte of the subtrahend which is 01101010 is in the $0215 byte location. The “$0215” address of “ADC $0215” is in the byte locations of $0205 and $0206. The “SBC $0215” instruction subtracts the lower byte of the subtrahend from the lower byte of the minuend which is already in the accumulator. This is two’s complement subtraction. The result is placed back in the accumulator. The complement (inversion) of any carry after the eighth bit is sent to the carry flag of the status register. This carry flag must not be cleared before the second subtraction with the higher bytes. This carry is added to the subtraction of the higher bytes automatically. The comment takes the next 5710 = 391616 bytes. However, this remains only in the “.asm” text file. It does not reach the memory. It is removed by the translation which is done by the assembler (a program). For the next instruction which is “STA $0217”, the opcode of STA, i.e. 8D16 (absolute addressing), is in the $0207 byte location. The “$0217” address of “STA $0217” is in the memory locations of $0208 and $0209. The “STA $0217” instruction copies the eight-bit content of the accumulator to the memory location of $0217. The higher byte of the minuend which is 00101010 is in the memory location of $0214, and the higher byte of the subtrahend which is 00010101 is in the byte location of $0216. The opcode for “LDA $0214”, i.e. AD16 for LDA (absolute addressing), is in the $020A byte location. The “$0214” address of “LDA $0214” is in the locations of $020B and $020C. The “LDA $0214” instruction loads the higher byte of the minuend to the accumulator, erasing whatever is in the accumulator. The opcode for “SBC $0216”, i.e. ED16 for SBC (absolute addressing), is in the $020D byte location. The “$0216” address of “SBC $0216” is in the byte locations of $020E and $020F. The “SBC $0216” instruction subtracts the higher byte of the subtrahend from the higher byte of the minuend (two’s complement) which is already in the accumulator. The result is placed back into the accumulator. If there is a carry of 1 for this second subtraction, its complement is placed in the carry cell of the status register automatically. Though the carry beyond the sixteenth bit (left) is not required for this problem, it is nice to check if the complement carry occurs by checking the carry flag. For the next and last instruction which is the “STA $0218”, the opcode of STA, i.e. 8D16 (absolute addressing), is in the $0210 byte location. The “$0218” address of “STA $0218” is in the memory locations of $0211 and $0212. The “STA $0218” instruction copies the eight-bit content of the accumulator to the memory location of $0218. The result of the subtraction with the two sixteen-bit numbers is 0001010101010101 with the lower byte of 01010101 in the memory location of $0217 and the higher byte of 00010101 in the memory location of $0218 – little endianness. The 6502 µP has circuitry only for addition, and indirectly for the two’s complement subtraction. It does not have circuitry for multiplication and division. To do the multiplication and division, an assembly language program with details, including the shifting of partial products and partial dividends, should be written. 4.4 Logical Operations In the 6502 µP, the mnemonic for OR is ORA and the mnemonic for exclusive OR is EOR. Notice that the logical operations do not have the implied addressing. The implied addressing takes no operand. Each of the logical operators must take two operands. The first is in the accumulator, and the second is in the memory or in the instruction. The result (8-bits) is back into the accumulator. The first in the accumulator is either put there by an immediate instruction or is copied from the memory with absolute addressing. In this section, only the zero-page addressing is used for illustration. These logical operators are all Bitwise operators. AND The following table illustrates the Bitwise AND in binary, hexadecimal, and decimal: All the programs in this chapter should start at the memory byte location of $0200. However, the programs in this section are in page zero, with the aim of illustrating the use of page zero without the higher byte of 000000002. The previous ANDing may be coded as follows: LDA #$9A ; not from memory – immediate addressing AND #$CD ; not from memory – immediate addressing STA $30 ; stores $88 at zero-based $0030 OR The following table illustrates the Bitwise OR in binary, hexadecimal, and decimal: LDA #$9A ; not from memory – immediate addressing ORA #$CD ; not from memory – immediate addressing STA $30 ; stores $CF at zero-based $0030 XOR The following table illustrates the Bitwise XOR in binary, hexadecimal, and decimal: LDA #$9A ; not from memory – immediate addressing EOR #$CD ; not from memory – immediate addressing STA $30 ; stores $57 at zero-based $0030 4.5 Shift and Rotate Operations The mnemonics and opcodes for the shift and rotate operators are: ASL: Shift left one bit of accumulator or memory location, inserting 0 in the vacated rightmost cell. LSR: Shift right one bit of accumulator or memory location, inserting 0 in the vacated leftmost cell. ROL: Rotate one bit left of accumulator or memory location, inserting the bit that is dropped out on the left into the vacated rightmost cell. ROR: Rotate one bit right of accumulator or memory location, inserting the bit that is dropped out on the right into the vacated leftmost cell. To do a shift or rotation with the accumulator, the instruction is something like this: LSR A This uses another addressing mode called the accumulator addressing mode. To do a shift or rotation with a byte memory location, the instruction is something like this: ROR $2BCD Where 2BCD is the memory location. Note that there is no immediate or implied addressing mode for shifting or rotating. There is no immediate addressing mode because there is no point in shifting or rotating a number that remains only in the instruction. There is no implied addressing mode because the designers of the 6502 µP want only the content of the accumulator (A register) or a memory byte location to be shifted or rotated. 4.6 Relative Addressing Mode The microprocessor always increments (by 1, 2, or 3 units) the Program Counter (PC) to point to the next instruction that is to be executed. The 6502 µP has an instruction whose mnemonic is BVS which means Branch on Overflow Set. The PC consists of two bytes. This instruction causes the PC to have a different memory address for the next instruction to be executed not resulting from a normal increment. It does so by adding or subtracting a value, called an offset, to the content of the PC. And so, the PC then points to a different (branched) memory location for the computer to carry on executing from there. The offset is an integer from -12810 to +12710 (two’s complement). So, the offset can make the jump go ahead in the memory. If it is positive or behind in the memory, or if it is negative. The BVS instruction takes only one operand which is the offset. BVS uses the relative addressing. Consider the following instruction: BVS $7F In base two, 7FH is 011111112 = 12710. Assume that the content in the PC for the next instruction is $0300. The BVS instruction causes $7F (a positive number already in two’s complement) to be added to $0300 to give $037F. So, instead of the next instruction to be executed at the memory location of $0300, it is at the memory location of $037F (approximately half a page difference). There are other branch instructions, but BVS is a very good one to use to illustrate the relative addressing. Relative addressing deals with branch instructions. 4.7 Indexed Addressing and Indirect Addressing Separately These addressing modes enable the 6502 µP to handle the enormous amounts of data in short periods of time with reduced number of instructions. There are 64KB locations for the whole Comodore-64 memory. So, to accesses any byte location, of 16 bits, making two bytes are needed. The only exception to the needing of two bytes is for page zero where the higher byte of $00 is omitted to economize the space that is taken up by the instruction in the memory. With a non-page-zero addressing mode, both the higher and lower bytes of the 16-bit memory address are mostly indicated somehow. Basic Indexed Addressing Absolute Index Addressing Remember that the X or Y register is called the index register. Consider the following instruction: LDA $C453,X Assume that the value of 6H is in the X register. Note that 6 is not typed anywhere in the instruction. This instruction adds the value of 6H to C453H which is part of the typed instruction in the text file that is still to be assembled – C453H + 6H = C459H. LDA means load a byte to the accumulator. The byte to be loaded into the accumulator comes from the $C459 address. The $C459 which is the sum of $C453 that is typed with the instruction and 6H that is found in the X register becomes the effective address from which the byte to be loaded into the accumulator comes from. If 6H was in the Y register, Y is typed in place of X in the instruction. In the typed instruction statement, $C453 is known as the base address and the 6H in the X or Y register is known as the counting or index part for the effective address. The base address can refer to any byte address in the memory, and the next 25610 addresses can be accessed, assuming that the started index (or count) in the X or Y register is 0. Remember that one byte can give a continuous range of up to 25610 numbers (i.e. 000000002 to 111111112). So, the absolute addressing adds whatever is already put (has been put by another instruction) in the X or Y register to the 16 addresses that are typed with the instruction to obtain the effective address. In the typed instruction, the two index registers are distinguished by X or Y which are typed after a comma. Either X or Y is typed; not both. After all the program is typed in a text editor and saved with the “.asm” extension filename, the assembler, which is another program, has to translate the typed program to what is (loaded) in the memory. The previous instruction, which is “LDA $C453,X”, occupies three byte locations in the memory, and not five. Remember that a mnemonic such as LDA can have more than one opcode (different bytes). The opcode for the instruction that uses the X register is different from the opcode that uses the Y register. The assembler knows what opcode to use based on the typed instruction. The one byte opcode for “LDA $C453,X” is different from the one byte opcode for “LDA $C453,Y”. In fact, the opcode for LDA in “LDA $C453,X” is BD, and the opcode for LDA in “LDA $C453,9” is BD. If the opcode for LDA is in the $0200 byte location. Then, the 16-bit address of $C453 takes the next to the byte locations in the memory which are $0201 and $0202. The particular opcode byte indicates whether it is the X register or the Y register that is involved. And so, the assembled language instruction which is “LDA $C453,X” or “LDA $C453,Y” occupies three consecutive bytes in the memory, and not four or five. Zero-Page Indexed Addressing The zero-page index addressing is like the absolute index addressing which is previously described, but the target byte must be only on page zero (from $0000 to $00FF). Now, when dealing with the zero page, the higher byte which is always 00H for the memory locations is usually avoided. So, it is normally mentioned that page zero begins from $00 to FF. And so, the previous instruction of “LDA $C453,X” is: LDA $53,X The $C4, a higher byte that refers to a page above the page zero, cannot be employed in this instruction as it puts the expected target byte to be loaded into the accumulated byte outside and above the page zero. When the value that is typed in the instruction is added to the value in the index register, the sum should not give a result above the page zero (FFH). So, it is out of the question to have an instruction like “LDA $FF, X” and a value like FFH in the index register because FFH + FFH = 200H which is the first byte ($0200) location of page 2 (third page) in the memory, is a big distance away from page 0. So, with zero-page indexed addressing, the effective address must lie in page zero. Indirect Addressing Jump Absolute Addressing Before discussing the Absolute Indirect Addressing, it is good to first look at the JMP absolute addressing. Assume that the address that has the value of interest (target byte) is $8765. This is 16-bits consisting of two bytes: the higher byte which is 87H and the lower byte which is 65H. So, the two bytes for $8765 are put in the PC (program counter) for the next instruction. What is typed in the assembly language program (file) is: JMP $8765 The executing program in the memory jumps from whatever address it accessed to $8765. The JMP mnemonic has three opcodes which are 4C, 6C, and 7C. The opcode for this absolute addressing is 4C. The opcode for the JMP absolute indirect addressing is 6C (refer to the following illustrations). Absolute Indirect Addressing This is used only with the jump (JMP) instruction. Assume that the address that has the byte of interest (target byte) is $8765. This is 16-bits consisting of two bytes: the higher byte which is 87H and the lower byte which is 65H. With absolute indirect addressing, these two bytes are actually located in two consecutive byte locations elsewhere in the memory. Assume that they are located in the memory locations of $0210 and $0211. Then, the lower byte of the address of interest which is 65H is in the $0210 address, and the higher byte which is 87H is in the $0211 address. That means that the lower memory byte of interest goes to lower consecutive address, and the higher memory byte of interest goes to the higher consecutive address – little endianness. The 16-bit address can refer to two consecutive addresses in the memory. In that light, the $0210 address refers to the addresses of $0210 and $0211. The address pair of $0210 and $0211 hold the ultimate address (16-bits of two bytes) of the target byte, with the lower byte of 65H in $0210 and the higher byte of 87H in $0211. So, the jump instruction that is typed is: JMP ($0210) The JMP mnemonic has three opcodes which are 4C, 6C, and 7C. The opcode for absolute indirect addressing is 6C. What is typed in the text file is “JMP ($0210)”. Because of the parentheses, the assembler (translator) uses the opcode 6C for JMP, and not 4C or 7C. With absolute indirect addressing, there are actually three memory regions. The first region may consist of the byte locations of $0200, $0201, and $0202. This has the three bytes for the “JMP ($0210)” instruction. The second region, which is not necessarily next to the first, consists of the two consecutive byte locations of $0210 and $0211. It is the lower byte here ($0210) that is typed in the assembly language program instruction. If the address of interest is $8765, the lower byte of 65H is in the $0210 byte location, and the higher byte of 87H is in the $0211 byte location. The third region consists of just one byte location. It is of the $8765 address for the targeted byte (ultimate byte of interest). The pair of consecutive addresses, $0210 and $0211, holds the $8765 pointer which is the address of interest. After the computing interpretation, it is $8765 that goes into the PC (Program Counter) to access the target byte. Zero Page Indirect Addressing This addressing is the same as the absolute indirect addressing, but the pointer must be in page zero. The lower byte address of the pointer region is what is in the typed instruction as follows: JMP ($50) The higher byte of the pointer is in the $51 byte location. The effective address (pointed) does not have to be in page zero. So, with index addressing, the value in an index register is added to the base address that is given in the instruction to have the effective address. Indirect addressing uses a pointer. 4.8 Indexed Indirect Addressing Absolute Indexed Indirect Addressing This addressing mode is used with the JMP instruction only. With absolute indirect addressing, there is the pointed value (byte) with its own two consecutive byte addresses. These two consecutive addresses form the pointer to be in the pointer region of two consecutive bytes in the memory. The lower byte of the pointer region is what is typed in the instruction in parentheses. The pointer is the address of the pointed value. In the previous situation, $8765 is the address of the pointed value. The $0210 (followed by $0211) is the address whose content is $8765 which is the pointer. With the absolute indirect addressing mode, it is ($0210) that is typed in the program (text file), including the parentheses. On the other hand, with the Absolute Indexed Indirect Addressing Mode, the lower address byte for the pointer region is formed by adding the value in the X register to the typed address. For example, if the pointer is in the address location of $0210, the typed instruction may be something like this: JMP ($020A,X) Where the X register has the value of 6H. 020AH + 6H = 0210H. The Y register is not used with this addressing mode. Zero Page Indexed Indirect Addressing This addressing mode uses the X register and not the Y register. With this addressing mode, there is still the pointed value and the pointer in its two-byte address pointer region. There must be two consecutive bytes in page zero for the pointer. The address that is typed in the instruction is a one byte address. This value is added to the value in the X register and any carry is discarded. The result points to the pointer region in page 0. For example, if the address of interest (pointed) is $8765 and it is in the byte locations of $50 and $51 of page 0, and the value in the X register is $30, the typed instruction is something like this: LDA ($20,X) Because $20 + $30 = $50. Indirect Indexed Addressing This addressing mode uses the Y register and not the X register. With this addressing mode, there is still the pointed value and the pointer region, but the content of the pointer region operates differently. There must be two consecutive bytes in page zero for the pointer region. The lower address of the pointer region is typed in the instruction. This number (pair of bytes) that is contained in the pointer region is added to the value in the Y register to have the real pointer. For example, let the address of interest (pointed) be $8765, the value of 6H be in the Y register, and the number (two bytes) be at the address of 50H and 51H. The two bytes together are $875F since $875F + $6 = $8765. The typed instruction is something like this: LDA ($50),Y 4.9 Increment, Decrement, and Test-BITs Instructions The following table shows the operations of the increment and decrement instructions: The INA and DEA increment and decrement the accumulator, respectively. That is called accumulator addressing. INX, DEX, INY, and DEY are for the X and Y registers, respectively. They do not take any operand. So, they use the implied addressing mode. Increment means adding 1 to the register or memory byte. Decrement means subtracting 1 from the register or memory byte. The INC and DEC increment and decrement a memory byte, respectively (and not a register). Using of the zero page addressing instead of the absolute addressing is to economize the memory for the instruction. Zero page addressing is one byte less than the absolute addressing for the instruction in the memory. However, the zero page addressing mode affects only the page zero. The BIT instruction tests the bits of a byte in the memory with the 8 bits in the accumulator, but changes neither. Only some flags of the Processor Status Register “P” are set. The bits of the specified memory location are logically ANDed with those of the accumulator. Then, the following status bits are set: N which is bit 7 and the last bit (left) of the status register, receives bit 7 of the memory location before the ANDing. V which is bit 6 of the status register receives bit 6 of the memory location before the ANDing. The Z flag of the status register is set (made 1) if the result of the AND is zero (000000002). Otherwise, it is cleared (made 0). 4.10 Compare Instructions The compare instruction mnemonics for the 6502 µP are CMP, CPX, and CPY. After each comparison, the N, Z, and C flags of the processor status register “P” are affected. The N flag is set (made 1) when the result is a negative number. The Z flag is set (made 1) when the result is a zero (000000002). The C flag is set (made 1) when there is a carry from the eight to the nineth bit. The following table gives a detailed illustration The means “greater than”. With that, the compare table should be self-explanatory. 4.11 Jump and Branch Instructions The following table summarises the jump and branch instructions: The JMP instruction uses the absolute and indirect addressing. The rest of the instructions in the table are branch instructions. They use only the relative addressing with the 6502 µP. With that, the table becomes self-explanatory if it is read from left to right and top to bottom. Note that the branches can only be applied to addresses within -128 to +127 bytes from the given address. This is relative addressing. For both the JMP and branch instructions, the Program Counter (PC) is directly affected. The 6502 µP does not allow the branches to an absolute address, though the jump can do the absolute addressing. The JMP instruction is not a branch instruction. Note: Relative addressing is used only with branch instructions. 4.12 The Stack Area A subroutine is like one of the previous short programs to add two numbers or subtract two numbers. The stack area in the memory starts from $0100 to $01FF inclusively. This area is simply called the stack. When the microprocessor executes a jump to the subroutine instruction (JSR – refer to the following discussion), it needs to know where to return when finished. The 6502 µP keeps this information (return address) in low memory from $0100 to $01FF (the stack area) and uses the stack pointer register content which is “S” in the microprocessor as a pointer (9 bits) to the last returned address that is stored in the page 1 ($0100 to $01FF) of the memory. The stack grows down from $01FF and makes it possible to nest the subroutines up to 128 levels deep. Another use of the stack pointer is to handle the interrupts. The 6502 µP has the pins labelled as IRQ and NMI. It is possible for some small electrical signals to be applied to these pins and cause the 6502 µP to quit executing one program and make it start executing another. In this case, the first program is interrupted. Like subroutines, the interrupt code segments can be nested. Interrupt processing is discussed in the next chapter. Note: The stack pointer has 8 bits for the lower byte address in addressing the locations from $0100 to $01FF. The higher byte of 000000012 is assumed. The following table gives the instructions that relate the stack pointer “S” with the A, X, Y, and P registers to the stack area in the memory: 4.13 Subroutine Call and Return A subroutine is a set of instructions that achieve a particular objective. The previous addition or subtraction program is a very short subroutine. Subroutines are sometimes just called routines. The instruction to call a subroutine is: JSR : Jump to SubRoutine The instruction to return from a subroutine is: RTS : ReTurn from Subroutine The microprocessor has the tendency to continuously execute the instructions in the memory, one after the next. Assume that the microprocessor is currently executing a code segment and it encounters a jump (JMP) instruction to go and execute a code segment which is coded behind that it might already executed. It executes that code segment behind and continues to execute all the code segments (instructions) following the code segment behind, until it re-executes the current code segment again and continue below. JMP does not push the next instruction to the stack. Unlike JMP, JSR pushes the address of the next instruction after itself from the PC (program counter) onto the stack. The stack position of this address is placed in the stack pointer “S”. When an RTS instruction is encountered (executed) in the subroutine, the address that is pushed on the stack pulls off the stack and the program resumes at that pulled-off address which is the next instruction address just before the subroutine call. The last address that is removed from the stack is sent to the program counter. The following table gives the technical details of the JSR and RTS instructions: See the following illustration for the uses of JSR and RTS: 4.14 A Count Down Loop Example The following subroutine counts down from $FF to $00 (total of 25610 counts): start LDX #$FF ; load X with $FF = 255 loop DEX ; X = X – 1 BNE loop ; if X not zero then goto loop RTS ; return Each line has a comment. Comments never go into the memory for execution. The assembler (translator) that converts a program to what it is in the memory for execution (running) always strip off the comments. A comment begins with “;” . The “start” and “loop” in this program are called labels. A label identifies (the name) for the address of the instruction. If the instruction is a single byte instruction (implied addressing), the label is the address of that instruction. If the instruction is a multibyte instruction, the label identifies the first byte for the multibyte instruction. The first instruction for this program consists of two bytes. Assuming that it starts at the $0300 address, the $0300 address can be substituted with “start” down in the program. The second instruction (DEX) is a single byte instruct6ion, and should be at the $0302 address. This means that the $0302 address can be substituted with “loop”, down in the program, which is actually so in “BNE loop”. “BNE loop” means the branch to the given address when the Z flag of the status register is 0. When the value in the A or X or Y register is 000000002, due to the last operation, the Z flag is 1 (set). So, while it is 0 (not 1), the second and third instructions in the program are repeated in that order. In each repeated sequence, the value (whole number) in the X register is decreased by 1. DEX means X = X – 1. When the value in the X register is $00 = 000000002, Z becomes 1. At that point, there is no more repetition of the two instructions. The last RTS instruction in the program, which is a single byte instruction (implied addressing), returns from the subroutine. The effect of this instruction is to make the program counter address in the stack for the code that is to executed before the subroutine call and go back to the program counter (PC). This address is the address of the instruction that is to be executed before the subroutine is called. Note: When writing an assembly language program for the 6502 µP, only a label must start at the beginning of a line; any other line code must be shifted at least one space to the right. Calling a Subroutine Ignoring the memory space that is taken up by the previous labels, the program takes 6 bytes of consecutive locations in the memory (RAM) from $0300 to $0305. In this case, the program is: LDX #$FF ; load X with $FF = 255 DEX ; X = X – 1 BNE $0302 ; if X not zero then goto loop RTS ; return Beginning from the $0200 address in the memory can be the call for the subroutine. The call instruction is: JSR start ; start is address $0300, i.e., JSR $0300 The subroutine and its call that are properly written in the text editor file is: start LDX #$FF; load X with $FF = 255 loop DEX ; X = X – 1 BNE loop ; if X not zero then goto loop RTS ; return JSR start : jump to routine beginning at $0300 Now, there can be many subroutines in one long program. All of them cannot have the name “start”. They should have different names. In fact, none of them might have the name “start”. “Start” is used here for teaching reasons. 4.15 Translating a Program Translating a program or assembling it means the same thing. Consider the following program: start LDX #$FF : load X with $FF = 255 loop DEX : X = X – 1 BNE loop : if X not zero then goto loop RTS : return JSR start : jump to routine beginning at $0300 This is the program that is previously written. It consists of the subroutine, start, and the call to the subroutine. The program counts down from 25510 to 010. The program begins at the user beginning address of $0200 (RAM). The program is typed in a text editor and is saved in the disk. It has a name like “sample.asm” where “sample” is the name of the programmer’s choice but the “.asm” extension for the assembly language must be associated with the filename. The assembled program is produced by another program which is called an assembler. The assembler is supplied by the manufacturer of the 6502 µP or by a third party. The assembler reproduces the program in such a way that it is in the memory (RAM) as it executes (run). Assume that the JSR instruction begins at the $0200 address and the subroutine begins at the $0300 address. The assembler strips off all the comments and white spaces. The comments and white spaces waste the memory which is always scarce. A possible blank line between the previous subroutine code segment and the subroutine call is an example of whitespace. The assembled file is still saved in the disk, and it is named something like “sample.exe”. The “sample” is the name of the programmer’s choice, but the “.exe” extension should be there to indicate that it is an executable file. The assembled program can be documented as follows: Producing a document like this is said to be assembling by hand. Note that the comments in this document do not appear in the memory (for execution). The address column in the table indicates the starting addresses of the instructions in the memory. Note that “JSR start” that is “JSR $0300”, which is expected to be coded as “20 03 00”, is actually coded as “20 00 03” with the lower memory byte address taking the lower byte in the memory and the higher memory byte address taking the higher byte in memory – little endianness. The opcode for JSR is 2016. Note that the offset to a branch instruction such as BNE is a two’s complement number in the range of 12810 to + 12710. So, “BNE loop” means “BNE -110” which is actually “D0 FF” in the code form of FF16 is -1 in two’s complement which is written as = 11111111 in base two. The assembler program replaces the labels and fields to actual hexadecimal numbers (hexadecimal numbers are binary numbers that are grouped in four bits). The actual addresses where each instruction starts are actually included. Note: The “JSR start” instruction is replaced by shorter instructions that send the current content (high and low bytes) of the program counter to the stack with the stack pointer that is decremented two times (once for high byte and once for low byte) and then reloads the PC with the $0300 address. The stack pointer now points to $00FD, assuming that it is initialized to $01FF. Also, the RTS instruction is replaced by a number of shorter instructions which increments the stack pointer “S” two times (once for low byte and once for high byte) and pulls the corresponding two bytes of address from the stack pointer to the PC for the next instruction. Note: A label text should not have more than 8 characters. “BNE loop” uses the relative addressing. It means to add -310 to the next program counter content of $0305. The bytes for “BNE loop” are “D0 FD” where FD is the two’s complement of -310. Note: This chapter does not present all the instructions for the 6502 µP. All the instructions and their details can be found in the document entitled “SY6500 8-Bit Microprocessor Family”. There is a PDF file with the name “6502.pdf” for this document which is freely available in the Internet. The 6502 µP that is described in this document is 65C02. 4.16 Interrupts The signals of any device that is connected to the external (vertical surface) ports of the Commodore 64 have to pass through either the CIA 1 or CIA 2 circuits (ICs) before reaching the 6502 microprocessor. The signals from the data-bus of the 6502 µP have to pass either through the CIA 1 or CIA 2 chip before reaching any external device. CIA stands for Complex Interface Adapter. In Fig 4.1 “Block Diagram of the Commodore_64 Motherboard”, the block input/output devices represent the CIA 1 and the CIA 2. When a program is running, it can be interrupted to run some other piece of code before continuing. There is hardware interruption and software interruption. For hardware interruption, there are two input signal pins to the 6502 µP. The names of these pins are IRQ and NMI. These are not µP data lines. The data lines for the µP are D7, D6, D5, D4, D3, D2, D1 and D0; with D0 for the least significant bit and D7 for the most significant bit. IRQ stands for Interrupt ReQuest “active” low. This input line to the µP is normally high, at approximately 5 volts. When it goes down to approximately 0 volt, that is an interrupt request that signals the µP. As soon as the request is granted, the line goes back high. Granting the interrupt request means that the µP branches to the code (subroutine) that handles the interrupt. NMI stands for Non-Maskable Interrupt “active” low. While the code for IRQ is being executed NMI can go low. In this case, NMI is handled (its own code is executed). After that, the code for IRQ continues. After the code for IRQ ends, the main program code continues. That is, NMI interrupts the IRQ handler. The signal for NMI can still be given to the µP even when the µP is idle and not handling anything or not running a main program. Note: It is actually the transition from high to low, of NMI, that is the NMI signal – more on that later. IRQ normally comes from CIA 1 and NMI normally comes from CIA 2. NMI, which stands for Non-Maskable Interrupt, can be considered as non-stoppable interrupt. Handling Interrupts Whether the request is from IRQ or NMI, the current instruction must complete. The 6502 has only the A, X, and Y registers. While a subroutine is operating, it might be using these three registers together. An interrupt handler is still a subroutine, though not seen as such. After the current instruction is completed, the contents of the A, X, and Y registers for the 65C02 µP are saved in the stack. The address of the next instruction of the Program Counter is also sent to the stack. The µP then branches to the code for the interrupt. After that, the contents of the A, X, and Y registers are then restored from the stack in the reverse order to which they are sent. Example Coding for an Interrupt For simplicity, assume that the routine for the µP IRQ interrupt is just to add the numbers $01 and $02 and save the result of $03 at the memory address of $0400. The code is: ISR PHA PHX PHY ; LDA #$01 ADC #$02 STA $0400 ; PLY PLX PLA RTI ISR is a label and identifies the memory address where the PHA instruction is. ISR means Interrupt Service Routine. PHA, PHX, and PHY send the contents of the A, X, and Y registers to the stack with the hope that they will be needed by whatever code (program) that is running just before the interruption. The next three instructions form the core of the interrupt handler. The PLY, PLX, and PLA instructions have to be in that order, and they bring back the contents of the Y, X, and A registers. The last instruction, which is RTI, (without an operand) returns the continuation of execution to whatever code (program) that is executing before interruption. RTI pulls the address of the next instruction of the code that is executing from the stack back to the program counter. RTI means ReTurn from Interrupt. With that, the interrupt handling (subroutine) is over. Software Interrupt The main way to have a software interrupt for the 6502 µP is with the use of the BRK implied address instruction. Assume that the main program is running and it encounters the BRK instruction. From that point, the address of the next instruction in the PC should be sent to the stack as the current instruction is completed. A subroutine to handle the software instruction should be called “next”. This interrupt subroutine should push the A, X, and Y register contents to the stack. After the core of the subroutine is executed, the contents of the A, X, and Y registers should be pulled back from the stack to their registers by the completing subroutine. The last statement in the routine is RTI. The PC content is also pulled back from the stack to the PC automatically because of RTI. Comparing and Contrasting of Subroutine and Interrupt Service Routine The following table compares and contrasts the Subroutine and Interrupt Service Routine: 4.17 Summary of the 6502 Main Addressing Modes Each instruction for the 6502 is one byte, followed by zero or more operands. Immediate Addressing Mode With immediate addressing mode, after the operand, is the value and not a memory address. The value has to be preceded by #. If the value is in hexadecimal the “#” has to be followed by “$”. The immediate addressing instructions for the 65C02 are: ADC, AND, BIT, CMP, CPX, CPY, EOR, LDA, LDX, LDY, ORA, SBC. The reader should consult the documentation for 65C02 µP in order to know how to use the instructions that are listed here that are not explained in this chapter. An example instruction is: LDA #$77 Absolute Addressing Mode In absolute addressing mode, there is one operand. This operand is the address of the value in the memory (usually in hexadecimal or a label). There are 64K10 = 65,53610 memory addresses for the 6502 µP. Typically, the one-byte value is at one of these addresses. The absolute addressing instructions for the 65C02 are: ADC, AND, ASL, BIT, CMP, CPX, CPY, DEC, EOR, INC, JMP, JSR, LDA, LDX, LDY, LSR, ORA, ROL, ROR, SBC, STA, STX, STY, STZ, TRB, TSB. The reader should consult the documentation for 65C02 µP in order to know how to use the instructions that are listed here, as well as for the rest of the addressing modes that are not explained in this chapter. An example instruction is: STA $1234 Implied Addressing Mode In implied addressing mode, there is no operand. Any µP register involved is implied by the instruction. The implied addressing instructions for the 65C02 are: BRK, CLC, CLD, CLI, CLV, DEX, DEY, INX, INY, NOP, PHA, PHP, PHX, PHY, PLA, PLP, PLX, PLY, RTI, RTS, SEC, SED, SEI, TAX, TAY, TSX, TXA, TXS, TYA. An example instruction is: DEX : Decrement the X register by one unit. Relative Addressing Mode Relative addressing mode deals only with branch instructions. In relative addressing mode, there is only one operand. It is a value from -12810 to +12710. This value is called an offset. Based on the sign, this value is added or subtracted from the next instruction of the Program Counter to result in the address of the intended next instruction. The relative address mode instructions are: BCC, BCS, BEQ, BMI, BNE, BPL, BRA, BVC, BVS. The instruction examples are: BNE $7F : (branch if Z = 0 in status register, P) Which adds 127 to the current program counter (address to execute) and starts executing the instruction at that address. Similarly: BEQ $F9 : (branch if Z = : in status register, P) Which adds a -7 to the current program counter and starts execution at the new program counter address. The operand is a two’s complement number. Absolute Indexed Addressing In absolute index addressing, the content of the X or Y register is added to the given absolute address (anywhere from $0000 to $FFFF, i.e. from 010 to 6553610) to have the real address. This given absolute address is called the base address. If the X register is used, the assembly instruction is something like this: LDA $C453,X If the Y register is used, it is something like: LDA $C453,Y The value for the X or Y register is called the count or index value, and it can be anywhere from $00 (010) to $FF (25010). It is not called the offset. The absolute index addressing instructions are: ADC, AND, ASL (X only), BIT (with accumulator and memory, with X only), CMP, DEC (memory and X only), EOR, INC (memory and X only), LDA, LDX, LDY, LSR (X only), ORA, ROL (X only), ROR (X only), SBC, STA, STZ (X only). Absolute Indirect Addressing This is used only with the jump instruction. With this, the given absolute address has a pointer address. The pointer address consists of two bytes. The two-bytes pointer points to (is the address of) the destination byte value in the memory. So, the assembly language instruction is: JMP ($3456) With the parentheses, and $13 is in the address location of $3456 while $EB is in the address location of $3457 (= $3456 + 1). Then, the destination address is $13EB and $13EB is the pointer. The absolute $3456 is in parentheses in the instruction where 34 is the lower byte and 56 is the higher byte. 4.18 Creating a String with the 6502 µP Assembly Language As demonstrated in the next chapter, after creating a file in the memory, the file can be saved into the disk. The file needs to be given a name. The name is an example of a string. There are many other examples of strings in programming. There are two main ways of creating a string of ASCII codes. In both ways, all the ASCII codes (characters) take consecutive byte locations in memory. In one of the ways, this sequence of bytes is preceded by an integer byte which is the length (number of characters) in the sequence (string). In the other way, the sequence of characters is succeeded (immediately followed) by the Null byte which is 0016, i.e. $00. The length of the string (number of characters) is not indicated in this other way. The Null character is not used in the first way. For example, consider the “I love you!” string without the quotes. The length here is 11; a space counts as one ASCII byte (character). Assume that the string has to be placed in the memory with the first character being at address $0300. The following table shows the string memory setting when the first byte is 1110 = 0B16: The following table shows the string memory setting when the first byte is “I” and the last byte is Null ($00): The following instruction can be used to begin creating the string: STA $0300 Assume that the first byte is in the accumulator that is to be sent to the address location of $0300. This instruction is true for both cases (both types of strings). After fitting all characters in the memory cells, one-by-one, the string can be read using a loop. For the first case, the number of characters after the length is read out. For the second case, the characters are read from “I” until the Null character which is “Null” is met. 4.19 Creating an Array with the 6502 µP Assembly Language An array of single byte integers consists of consecutive memory byte locations with the integers. Then, there is a pointer that points to the location of the first integer. So, an array of integers consists of two parts: the pointer and the series of locations. For an array of strings, each string can be in a different place in the memory. Then, there are consecutive memory locations with pointers where each pointer points to the first location of each string. A pointer in this case consists of two bytes. If a string begins with its length, the corresponding pointer points to the location of that length. If a string does not begin with its length but ends with a null character, the corresponding pointer points to the location of the first character of the string. And there is a pointer that points to the lower byte address of the first pointer of consecutive pointers. So, an array of strings consists of three parts: the strings at different places in the memory, the corresponding consecutive pointers, and the pointer to the first pointer of the consecutive pointers. 4.20 Problems The reader is advised to solve all the problems in a chapter before moving to the next chapter. Write an assembly language program which starts at $0200 for the 6502 µP and adds the unsigned numbers of 2A94H (addend) to 2ABFH (augend). Let the inputs and output be in the memory. Also, produce the assembled program document by hand. Write an assembly language program which starts at $0200 for the 6502 µP and subtracts the unsigned numbers of 1569H (subtrahend) from 2ABFH (minuend). Let the inputs and outputs be in the memory. Also, produce the assembled program document by hand. Write an assembly language program for the 6502 µP that counts up from $00 to $09 using a loop. The program should start at $0200. Also, produce the assembled program document by hand. Write an assembly language program which starts at $0200 for the 6502 µP. The program has two subroutines. The first subroutine adds the unsigned numbers of 0203H (augend) and 0102H (addend). The second subroutine adds the sum from the first subroutine which is 0305H to 0006H (augend). The final result is stored in the memory. Call the first subroutine which is FSTSUB and the second subroutine which is SECSUB. Let the inputs and outputs be in the memory. Also, produce the assembled program document for the whole program by hand. Given that an IRQ handler adds $02 to $01 at the accumulator as core handling while NMI is issued and the core handling for NMI adds $05 to $04 at the accumulator, write an assembly language for both handlers including their calls. The call to the IRQ handler should be at the address of $0200. The IRQ handler should start at the address of $0300. The NMI handler should start at the address of $0400. The result of the IRQ handler should be put at the address of $0500, and the result of the NMI handler should be put at the address of $0501. Briefly explain how the BRK instruction is used to produce the software interrupt in a 65C02 computer. Produce a table that compares and contrasts a normal subroutine with an interrupt service routine. Briefly explain the main addressing modes of the 65C02 µP given the assembly language instruction examples. a) Write a 6502 machine language program to put the “I love you!” string of ASCII codes in the memory, beginning from the $0300 address with the length of the string. The program should start at the $0200 address. Obtain each character from the accumulator one-by-one, assuming that they are sent there, by some subroutine. Also, assemble the program by hand. (If you need to know the ASCII codes for “I love you!”. Here they are: ‘I’:4916, space : 2016, ‘l’: 6C16, ‘o’:6F16, ‘v’:7616, ‘e’:65, ‘y’:7916, ‘u’:7516, and ‘!’:2116 (Note: each code occupies 1 byte). b) Write a 6502 machine language program to put the “I love you!” string of ASCII codes in the memory, beginning from the $0300 address without the length of the string but ending in 0016. The program should start at the $0200 address. Obtain each character from the accumulator, assuming that they are sent there, one-by-one, by some subroutine. Also, assemble the program by hand. View the full article
-
Forum Statistics
67.4k
Total Topics65.3k
Total Posts