1
votes

While creating instruction functions for my 6502/NES emulator, I got stuck at understanding the concept of signed bytes and two's complement in the 6502. So apparently, branch instructions such as BMI use a signed byte in memory for forwards/backwards branching, and some instructions allow arithmetic operations with negative numbers. Also the negative flag detects the 7th bit of the accumulator. (Two's complement)

Does this mean that all bytes in memory are signed and I could initialize the memory as int8_t CPUMEMORY[0x10000]; and not uint8_t CPUMEMORY[0x10000]?

The thing that is bothering me though, is that the carry flag is set when an operation result exceeds the unsigned 8 bit limit, which is 255. But shouldn't this be 127 if all bytes are signed? (The overflow flag does this, but then what is the point of having a carry flag?)

2

2 Answers

3
votes

Bytes in the memory are just bytes: they are neither signed nor unsigned. The value of a byte can however be interpreted as signed number (int8_t) or unsigned number (uint8_t). The same for the value of two consecutive bytes (int16_t or uint16_t) and the value of four consecutive bytes (int32_t or uint32_t).

To handle these bytes, you have several 6502 instructions that allow you to perform several operations for both signed and unsigned interpretations. You just have to use the right instructions and use the right flags (carry, overflow, negative...) for what you need to do.

1
votes

The carry is used for multi-byte arithmetic (amongst other things).

These topics have been written about hundreds of times, so I will just quote a good source.

1.2 REVIEW OF TWOS COMPLEMENT (SIGNED) NUMBERS

There are 256 possible values for a byte; in hex, they are: $00 to $FF. The range of an 8-bit unsigned number is 0 ($00) to 255 ($FF). The range of a 16-bit unsigned number is 0 ($0000) to 65535 ($FFFF), and so on. They are called unsigned numbers because they are zero or greater, i.e. there is no (minus) sign. A signed number, on the other hand, can be negative or positive (or zero). The term "signed number" is used below to mean a twos complement number (although there are other ways of representing signed numbers). The range of an 8-bit signed number is -128 to 127. The values -128 through -1 are, in hex, $80 through $FF, respectively. The values 0 through 127 are, in hex, $00 through $7F, respectively. So the minimum value of a signed number is $80 and the maximum value of a signed number is $7F. The range of a 16-bit signed number is -32768 ($8000) to 32767 ($7FFF) ($8000 through $FFFF are the negative numbers), and so on. This may seem like a strange way of handling negative numbers, but this method has several useful properties.

First, 0 to 127 (the overlap of the ranges of 8-bit signed and unsigned numbers) is, in hex, $00 to $7F, regardless of whether the number is signed or unsigned.

Second, the most significant bit (bit 7 for an 8-bit number) is zero when the number is non-negative (0 to 127), and one when the number is negative. In fact, this is how the N (negative) flag of 6502 got its name. (Notice that the N flag, when affected by an instruction, reflects bit 7 of the result of that instruction.) One other note: in mathematics, zero is not a postive or a negative number, but in the computer world, things are less formal; the term "positive number" typically includes zero because (a) all of the other possible values of a signed number whose most significant bit is zero are positive numbers, and (b) all of the other possible values for an unsigned number are positive numbers.

Third, consider the following addition:

CLC
LDA #$FF
ADC #$01

The result (in the accumulator) is $00, and the carry is set. The addition, in unsigned numbers, is: 255 + 1 = 256 (remember, the carry is set). The addition, in signed numbers, is -1 + 1 = 0. In other words, adding (and subtracting) signed numbers is exactly the same as adding (and subtracting) unsigned numbers.

6502.org also have a detailed discussion about sign and the overflow flag.