How am I able to fetch multiple bytes from a byte-addressable RAM
simultaneously to use them in my instruction register? Would I have to
read two bytes in sequence and re-assemble them into a single 16-bit
instruction before storing it in the register? What about other kind
of data, does reading a 2-byte value require me to load two bytes from
RAM manually into registers and re-assemble them? Is there a more
useful (or simpler) way I just can't think of?
Yes, there is. Most modern general-purpose computers are both (a) byte-addressable and at the same time (b) load every byte of an multi-byte instruction simultaneously into the instruction register (IR) in a single memory cycle.
As you know, the system bus between the CPU and the RAM is divided into the address bus, the data bus, and the control bus.
(Sometimes I/O devices are also attached to this bus).
The difference between byte addressing and word addressing is in how address registers are connected to the address bus; it has nothing to do with the width of the data bus.
Many processors (such as modern ARM Thumb and ARM Thumb 2) have mostly 16-bit-wide instructions. (Some people say 16-bits is the "sweet spot" compared to processors with 8-bit instructions or processors where every instruction is at least 32 bits wide).
The simplest approach for 16-bit instructions is to make the data bus 16 bits wide, and load the entire instruction from RAM into the instruction register (IR) in a single memory cycle.
Many such CPUs have the least-significant bit of the program counter (PC) hard-wired to zero, forcing every instruction to be "aligned" to a 16-bit boundary.
There are at least 3 popular ways of handling 8-bit data (such as ASCII and UTF-8) with such a 16-bit data bus (i.e., different ways of dealing with A0 of the address register), without wasting storage:
- (a) Simplest RAM hardware, simplest CPU hardware, complex software: Each address pin and each control lines /WE, /OE, and /CE on either of the 2 RAM chips is directly wired to the corresponding pin of the other RAM chip. This forces the RAM to always read and write a full 16 bits at a time, like word-addressed systems; there is no STORE BYTE instruction. To overwrite a single ASCII letter pointed to by a pointer P in the middle of a string, software generally requires at least 3 instructions: LOAD to read the entire 16 bit word containing that byte into some register, update the appropriate byte of that register (depending on the least-significant bit A0 of P), and then STORE the entire 16-bit word back out with the updated byte value.
- (b) Simplest RAM hardware, complex CPU hardware, simplest software: We use the exactly the same RAM setup as (a), and the software appears to use the same "STORE byte to P" instruction as (c), but when the CPU executes that instruction, it takes (at least) 2 memory cycles to read the entire 16 bit word containing that byte (possibly into some microarchitectural register not really visible in the programmer model), update the appropriate byte of that register, and then write the entire 16-bit register back out.
- (c) less-simple RAM hardware, less-simple CPU hardware, simplest software: the CPU has a "STORE byte to P" instruction.
During such a "store byte" instruction, one RAM chip sees its /WE line yanked Lo, i.e., active, the other RAM chip is left unchanged, with its /WE and /OE left Hi (inactive). The CPU generates a separate /WE_0 and /WE_1 byte-lane write signals dependent on the pointer P's least-significant bit (A0): /WE_0 is activated during a STORE BYTE when A0 is 0; /WE_1 is activated during a STORE BYTE when A1 is 1; and both /WE_0 and /WE_1 are activated during a STORE 16-bits instruction.
Approach (c) is common enough that many "x16 bit" chips (memory chips with 16 data I/O pins per chip), such as the ISSI IS61WV3216BLL, have 2 separate Byte Lane Select pins.
Approach (c) is common enough that 8-byte-wide (64 bit wide) DIMM memory modules have a control bus with 8 separate byte lane strobe signals (to support STORE Byte, STORE 16-bits, STORE 32-bits, etc.).
Approach (b) was once common in microcoded minicomputers, but it has fallen out of favor because it makes pipelining much more difficult for no real benefit.
Some teams develop a "high-end" computer with hardware that directly supports (c) or (b) with fast "STORE byte" instructions, and also a compatible "low-end" computer with simpler hardware more like (a). Most instructions operate exactly the same on these two machines, but when the low-end machine tries to execute the "STORE byte" instruction, it's trapped as an unimplemented instruction, and the unimplemented instruction exception handler simulates that instruction using a series of instructions that are implemented, much like (a).
Occasionally programmers need to compactly store data that is not byte-aligned (such as packed BCD digits, bit-oriented variable-length codes such as Huffman codes, etc.), and they are forced to write code that operates much like (a).
Early RAM chips had only 1 data I/O pin per chip. Back then, 16-bit-wide data buses required a minimum of 16 separate RAM chips. The cost of the chips and the PCB space to hold all those chips made that approach much more expensive than the same "amount" of memory using 8-bit-wide buses.
This is why some popular computers used the 68008 or the 8088, which had an 8-bit bus that did exactly as you suggested -- 16-bit instructions and 16-bit data values were loaded from RAM 8 bits at a time and re-assembled before storing into the instruction register (IR) or some data register.
Fortunately, today it's fairly easy to build a 16-bit-wide buses with "x8 bit" chips (memory chips with 8 data I/O pins per chip), such as 2 of the Hitachi HM62256A or 2 of the Alliance AS6C1008.
A byte-addressable CPU connected to such a memory array would not connect the least-significant bit of the CPU address registers (A0) to the least-significant address pin of the memory chips (A0) -- that would make things word-addressable. Instead, to support byte-addressability, A1 of each address registers and address bus would be connected to A0 of both memory chips; A2 of the data registers and address bus would be connected to A1 of both memory chips, etc.