Skip to content

Latest commit

 

History

History
116 lines (82 loc) · 10.1 KB

how.md

File metadata and controls

116 lines (82 loc) · 10.1 KB
  • explain how 6502 does pipeliing. ref
  1. DIRECT ADDRESSING

  2. INDEXED ADDRESSIN Absolute indexed address is absolute addressing with an index register added to the absolute address. Zero-Paged Indexed address is zero-paged addressing with an index register added to the absolute address.

  3. INDIRECT ADDRESSING In solving a certain class of problems, it is sometimes necessary to have an address which is a truly computed value, not just a base address with some type of offset, but a value which is calculated or sometimes obtained as a group of addresses. In order to implement this type of indexing or addressing, the use of indirect addressing has been introduced.

Why do we have Indexed Addressing?

As has been developed in many of the previous examples, an index register has primary values as a modifier and as a counter. As a modifier to a base address operation, it allows the accessing of contiguous groups of data by simple modification of the index.

On arithmetic

Addition

There are 2 ways to do binary arithmetic addition. The first is unsigned. The second is signed. Doing unsigned addition is simple, you simply update the carry flag as needed. The role of the carry flag is clear here. Your computation exceded the 8-bit boundary into the 9th bit (which will certainly be 1 in such a case). Overflow tagis useless here.

In the case of signed addition, the carry flag is useless. You are working wih two 7-bit numbers, not 8, as the 8th bits are for the signs. The overflow flag comes in handy here. Any carry generated by the operation is found in the 8th bit. It is now upon the developer to interpret it that way. The overflow flag is also needed to interpret the results os signed arithmetic correctly. You use the same adc,sbc and co. in both cases. The difference is how you interpret he bits and flags.

The point of the overflow flag is this: Case 1: When the 8th bit of the two operands are set, and the 8th bit of the result is unset, there is an overflow Case 2: Also, when the 8th bit of the two operands are unset, but the 8th bit of the result is set, there is an overflow.

Case 1 tells us that two negative numbers operated on gave a +ve number as result. Surely there was an overflow. Case 2 tells us that two positive numbers operated upon gave a -ve number result. Surely there must have been an overflow.

Again, it is important to know adc (and sbc) will always set both these flags (i.e. carry and overflow) in every case. The difference is in how the programmer interprets the flags, either by reading them or clearing them. That is the difference between signed and unsigned arithmetic. Its a matter of interpretation. The processor knows no difference between the two. It is also important to note that the processor sets (or unsets) the zero and negative flags in every arithmetic operation, including inx, iny, dex, dey, inc, dec.

Subtraction

Subtaction is done by deducting the value of the memory-fetched value and the carry from the accumulator. It uses twos compliment arithmetic. Rsult is stored in the accumulator. If a borrow occurs as a result of the subtraction, i.e. the result is >= 0, the carry flag is set, else it is reset. The carry flag being set means no borrow occured. sbc therefore means A = A - M - !C Since C being unset signifies a borrow, and a carry is set when the 9th bit is set aftter the computation, it becomes easy to do multi-byte addition. Overflow occurs in twos commpliment subtraction when the result positive. If the result is negative then there will be no overflow into the 9th bit.

To see how this would work, I wrote two small routines:

     fn sub(a: u8, b: u8, carry: bool) -> (u8, bool) {
          let mut b = b;
          if !carry {
               b += 1;    
          }
          let twos_comp = !u16::from(b) + 1;
          let mut a = u16::from(a);
          let overflow;
          (a, overflow) = a.overflowing_add(twos_comp);
          (a as u8, overflow)
     }

     fn u16_sub(a: u16, b: u16) -> (u16, bool) {
          let (a0, b0) = (a as u8, b as u8);
          let (a1, b1) = ((a >> 8) as u8, (b >>8) as u8);
          let (res0, over) = sub(a0, b0, true);
          let (res1, over) = sub(a1, b1, over);
          let res: u16 = u16::from_be_bytes([res1, res0]);
          (res, over)
     }

To do twos compliment subtraction, you find the twos compliment of the subtrahend (that is flip the bits and add 1), then you add this to the other number. Whether it overflows or not matters. If it overflows, then the result is positive (it also means that the MSB will be 0). If it doesn't overflow, then the result is negative (Thats why we said earlier that the compliment of the carry flag signifies if there is a borrow or not). This as implications for multi-byte subtraction, as seen in the code snippet above. The programmer has to set the carry bit to 1 before the subtraction of the lowest byte(or the only byte in the case of ingle byte subtraction). Setting the carry flag indicates that there is no borrow going on here, or also that this is the first (or only) byte we're subtracting. As is apparent, the carry flag is set as it normally is, i.e. because of a set 9th bit. What matters here, again, is the interpretation (by the programmer and by the operation). After doit all these, sbc still has to set other flags like the overflow, zero and negative flags. Their values are as expected.z if the accumulator now contains a zero value, n if the 8th bit is set.

The basic rule for 6502 prorammers is to set the carry flag at the beginning of a subtraction, and clear the carry flag at the beginning of an addition

The Concept of Oveflow in 6502

AN oveflow occurs (or is only checked) in signed operations. It signifies that although bit 7, ie. 8th bit usually contains the sign in signed operands, this articular operation is different in that the sign bit is no longer representing the sign but an overflow for the operation from the lower 7 bits. How does the system know this?

  1. The system knows that all signed numbers are between the range of -128 and +127
  2. It knows that -ve numbers begin with 1 while +ve numbers begin with 0
  3. In the case of adc, it knows that in adc, addition of a +ve number to a -ve number can never give a number exceding this range stated above
  4. So for adc, it only considers two cases:
    • case where both are +ve. It knows that adding two +ve numbers (numbers with bit 7 unset) would never give a result with the bit 7 set (i.e. negative). It sets the overflow flag
    • case where both operands arre -ve. It knows that adding two -ve numbers (with bit 7 set) would never give a result with the bit 7 unset (i.e. positive). It sets the oveflow flag
  5. sbc is basically the same add operation with the second operand as twos compliment Used to indicate that a value greater han 7 bits is the actual result of the computatio what this means is that the sign bit is not actually a sign bit but an overflow from the lower seven bits its major purpose is to monitor this used in signed aritmetic. user who is not using signed arithmetic can totally ignore this flag

Addressing

Addressing is about selection of memory addresses by the processor. The processor has an address bus attached to it. Selected address is sent to the memory through the 16-bit address bus, which can be divided into two parts of 8 bits each, namely ADH and ADL. Since the 6502 is an 8-bit processor, it can only address 8 bytes at once. 6502 uses byte-aaddressing, meaning it addresses memory as chunks of 8-bit fields. Also, since its address space is based on 16 bits, the totall number of addressible bytes is 65536 bytes.

Control

The PC (program counter) is the major point of control. It stores the address of the next instruction the processor will execute. AFter every instruction is fetched decoded, and executed, it is incremented by one, meaning that the processor moves on to execute the next instruction, except you tell it to do otherwise through a jump or a branch, or an interrupt. In the 6502, this next thing the PC addresses might also be some data neded after an opcode has been fetched and decoded, but expects a value as an operand. That operand, which could be an immediate value or a memory location is the next thing the PC will refer to. Essentially, it hoes from x to x+1; it is needed when the processor wishes to fetch an instruction.

The program counter also has 16 bits, allowig it to potentially contain any value between 0 and 65k. Were it limited to 8 bits, we wouldnt be able to write programs with more than 256 instructions.

The address selected by the PC is put on the address bus, and sent to the main memory. The instruction (or operand) is delivered to the processor for decoding. The PC increments by one. It goes on to fetch the next. And on and on it goes except for two things:

  1. The presence of interrupts which well talk about later
  2. Tests based on flags that allow us to branch or jump

Flags such as negative, zero, carry, etc are useful when we wish to change the flow of computation based on the state of the computation. Theyre used to implement, among other things, programming language constructs like if-else, switch, and loops. Since the processor relies on the PC to know what to fetch-decode-execute next, he way to influence it to take a detour is to manipulate the value of the PC.

JMP: JMP is unconditional jump. It doesn't care for whatever is in the flags register. It takes two immediate values that follow it in sequence, sends the first one to be stored temporarily, and in the next cycle, takes the other byte. When we say take the other byte, it means that the byte is appears on the address bus.

JMPI:

Other forms of branching require tests bfore the PC value is changed. SO, the processor fetches and decodes the op, checks the value of a particular flag, and decides whether to make the jump based on the test. If the test is not passed, the PC is not set by the instruction, and so the next sequential instruction is fetched and executed.