How does the 6502's machine code process $ vs #$, as in it's assembly language? Does every piece of machine code have an extra byte or something telling to use addresses or values or was it something more clever? (finding info on 6502 assembly is easy, on machine code processing in general not so much, so I don't know if there's a standard way or if this is a stupid question or not!)
While @dirkt's is a good answer, I think it might be missing the high-level overview you're after.
On the most basic level, instructions using immediate vs absolute addressing are encoded into different opcodes. For example, LDA # (immediate mode) is encoded as A9 (after which the CPU knows, by mechanisms detailed in @dirkt's answer, to read one byte and interpret it as a value), whereas LDA (absolute mode) is encoded as AD (and the CPU knows to read two bytes and interpret it as an address).
The 6502 has been decapped and traced, and a simulator is available, so you can find out the workings of anything in as much detail as you want.
The block diagram (available even before the decap) helps a lot in understanding what is going on.
Generally, instructions were executed in several phases (T0-T5), and the instruction register and the current phases were decoded first with a ROM, and then with a PLA into signals which drive the various parts of the CPU, including the next phase. The state machine wiki page has some of the more intricate details.
So for immediate vs. absolute addressing, the state machine sequences would slightly differ: Immediate would fetch the byte after the opcode (actually this was pipelined, and happened always at the same time as opcode decoding, which is why each opcode takes at least 2 cycles), and then this value would be routed to the ALU. Absolute addressing would fetch two bytes after the opcode, route them to the address bus latches, use them to fetch the byte at this address, and route that to the ALU.
The simulator allows you to enter your own programs, so just enter both variantes and see what happens.
How does the 6502's machine code process $ vs #$, as in it's assembly language? Does every piece of machine code have an extra byte or something telling to use addresses or values or was it something more clever?
Just to expand a bit on Muzer's answer...
All 6502 opcodes use exactly one byte, no exceptions. In this one byte, is encoded both what the operation is (load, store, add, rotate etc) and what the address mode is. The address mode is what tells the processor where to get the operand. For example in
LDA #10is immediate - the operand follows the opcode. With
LDA $10 the address mode can be either absolute (the operand is in the location specified by the two bytes following the opcode) or zero page which is the same as absolute except that, because you know the highest byte of the address is 0, it can be specified by the one byte following the opcode.
The address mode is specified by some of the bits in the opcode. For LDA we have the following bit patterns:
LDA zp, x indirect 0xA1 10100001 LDA zero page 0xA5 10100101 LDA immediate 0xA9 10101001 LDA absolute 0xAD 10101101 LDA zp ind, y 0xB1 10110001 LDA zp, x 0xB5 10110101 LDA abs, y 0xB9 10111001 LDA abs, x 0xBD 10111101
If you focus on bits 2, 3 and 4, you can see that these three bits are the only difference for each of the above instructions and they specify the address mode for
LDA. The other bits specify "LDA" i.e.
Eight of the instructions follow that pattern, for example
000xxx01 is for
001xxx01 is for
100xxx01 is for
STA except that
10001001 does not mean STA immediate for obvious reasons.
The principle sort of holds for some of the other instructions too. e.g.
LDY but usually not all the address modes make sense for these or there is a deliberate choice not to have them. e.g.
CPX does not have a full set of address modes. The gaps are used for other things e.g.
INX is in the place where you would expect