Lab Details



Define MEMSIZE 0x10000
unsigned char memory[MEMSIZE]

how do we access memory? we index into the array
the adresses are 0x0000 to 0xFFFF

address can be an unsigned short.

Larry recommends to make memory a global.
The easiest way to access memory is to do x = memory[--]
instead, we should make a function that will perform a read or a write.

mem-write( address, data )
address: 16 bits
data: 8 bits

mem-write(Reg, 3) (address is the contents of the register)

What is mem-write going to consist of?
mem_write(unsigned short addr, unsigned char data){
memory[addr] = data;
}

mem_read( addr ), returns data

unsigned short mem_read(unsigned short addr){
return memory[addr];
}

he expects these functions for the lab.

If we just want to read and display a series of locations, we could ask the user for a start and stop address.
Then we could go into a loop
FOR i = START TO STOP STEP
x = mem_read(i)
PRINT x
END

How to access word-wise?
If we can only access bytes at a time, we can read the low byte first and then the high byte. We read this way because the storage is little endian.
temp = mem_read(addr)
value = temp
addr ++
temp = mem_read(addr) ← hi byte
value = (temp << 8 ) | value ← (MSB << 8 | LSB)

Why did we increment the address by 1?
Because the memory is stored contiguously, and hte LSB is stored first.


Branching instructions:


- these are not the same as jump instructions
- jump can take us anywhere in the machine's memory.
- branches go to a fixed range.
- The problem with a RISC machine is, we dont have the luxury of storing the address right after the instruction and use the entire 16 bits of the address.
- The entire instruction needs to be able to hold the address.
- The first part of the branch has the instruction, and the second part has the address. Buut not an absolute value
- The reason we don't store the addresses as absolute values is because we wouldn't be able to access the full range of addresses (because we couldnt use all the bits)
- Instead, we use a relative address for the branch instruction.
- The address is relative to the program counter.
- Why?
→ It represents the offset from the current instruction being executed.
- EX:
→ Branch + Offset
→ if we simply add the offset to the program counter, the implication is we can only go forward.
→ how can we go forward OR backward?
→ The offset has to be signed.
→ By making the offset signed, we can go in either direction (relative to the PC)
- Ther are three types of branches:
Subroutine branch (BL - branch with link)
⇒ Link register gets the PC, and the PC gets the PC + Offset
⇒ The assumption is when the subroutine returns, the link register gets put into the PC (to resume execution where it left off)
⇒ The link register contains the next instruction after the subroutine call.
The offset is relative to the NEXT instruction after the branch call
⇒ that means when we store PC in the LR, we need to increment it.
• BL Subr += 8192(8KiB) ← if we have this range, what is the lowest possible value of the PC?
• The lowst possible PC value is -8190
• The highest is +8194
⇒ In order to have a loop,
• LOOP BRA LOOP (infinite loop) (we are branching to the offset. In order to get back to the same line, the offset needs to be -2)
◇ PC → LOOP + 2
◇ BRA -2
• Offset has to be -2 since the offset is relative to the NEXT PC value
⇒ How does the assembler calculate this?
• LOOP BRA LOOP
• The assembler has the LC. When the instruction is produced the LC has the location. say, 1000.
• The target address is 1000, but the LC is now at 1002
• Everything is relative to the target, so the offset = target - LC which is 1000-1002 which gives us -2
⇒ If the assembler is generating instructions (opcodes and operands), what do we know about the location counter?
• Its increasing
• It's always even
• Therefore every offset we have is going to be even

• Why?
• The target is always even, and the location is always even.
Whatever the offset is, the LSB is always 0
This is a waste of space
• Why keep track of it if you know it will always be 0?
• If we wereto shift the offset, this would double the available address range for the offset.
• By doubling the address range we can go twice as far in both directions.
• If the LSB can be 0/1, we cant simply add it to the PC since that might end up with an odd address
• We have to adjust it for the shift we did.
• To ensure we have an even byte address, we have to shift the offset to the left by 1

◇ Offset << 1 | 0
⇒ Another problem:
• Loop
• ...
• BRA Loop ← offset is negative in this case (MSB is 1)
• BRA Loop2 ← offset is positive, (MSB is 0)
• ...
• Loop2
• This information has to be stored in the offset.
• The MSB is the sign bit
• For the BL (opcode 000) how would we mask it off?
◇ INST & 0x1FFF gives us the offset bits.
• What about the other branch instructions (conditionals)
◇ BRA means branch always, but all theo thers are based on the PSW in some way.
◇ Mask: we are getting rid of the first 6 bits (opcode)
◇ INST & 0x03FF
• Sign bit is bit 9 (12 for the BL)
• What's the easiest way to determine if the sign bit is set or not?
◇ Mask it
◇ for BL: 0x1000
◇ for other branches: 0x0200
• Now we know whether the sign bit is set or not.
• We want to sign extend the offset
◇ if the sign bit is 1: BL: 0xE000 | offset (most sig 4 bits are set)
◇ conditionals: 0xFC00 | offset
• Take the sign extended offset and shift it left by 1
• Offset = (SIGN | OFFSET) << 1
• Add this to the PC
• PC ← PC + OFFSET
• This can be combined into a few instructions
⇒ We should now be able to determine which direction we're going with our branch instruction, and add it to our PC

PSW


- We have 7 conditional branch instructions
- these are based on 1 or more of the PSW bits.
- Status bits:
→ V : oVerflow (signed arithmetic only)

⇒ Sgined operations can set or clear the V bit.
⇒ Overflow if SRC + DST (both negative) and the result is positive, or if SRC +DST are positive and result is negative.
⇒ Sign bit in signed arithmetic is the MSB
⇒ only set or cleared with addition or subtraction
⇒ How do we determine overflow?
• Src Dest Res (MSB)
• SDR = 000 then V = 0
• SDR = 001 then V = 1
• SDR = 010 then V = 0
• SDR = 011 then V = 0
• SDR = 100 then V = 0
• SDR = 101 then V = 0
• SDR = 110 then V = 1
• SDR = 111 then V = 0
• We can set this up as a table likew edid the cary bit
• OVERFLOW_BIT[2][2][2] = {0,1,0,0,0,0,1,0}
• PSW.v = OVERFLOW_BIT[MSB_SRC][MSB_DST][MSB_RES]
N : Negative
⇒ signalled when the MSB is 1
⇒ 0 if the MSB is positive (0)
⇒ How do we determine if something is negative?
• In 8bit case, check the 7th bit to see if its set. or bit 15 in a 16bit case.
• The N bit of the PSW is the MSB of the result
Z: SET if the result is 0
⇒ 0 otherwise
⇒ this is tricky because it's backwards
⇒ How is the Z bit determined?
• RESULT == 0?
• If true, Z is set. otherwise Z is clear lol
C: Carry bit (unsigned arithmetic only)
⇒ Occurs when the MSB of the two operands is 1 and the MSB would be a 1 outside of the number of available bits
⇒ Or when a carry from previous bits could not be resolved by the MSB.
⇒ MSB is not the sign bit
⇒ How is carry determined?
• We have SRC, DST, and RES to work with. And we have the MSB of each.
• MSB of SrcDstRes = 000 then C = 0
• SDR = 001 then C = 0
• SDR = 010 then C = 1
• SDR = 011 then C = 0
• SDR = 100 then C = 1
• SDR = 101 then C = 0
• SDR = 110 then C = 1
• SDR = 111 then C = 1
• How can we check for this?
• We could make a 3D array
• CARRY_BIT[2][2][2] = {0,0,1,0,1,0,1,1}
• What you're doing is PSW.c = CARRY_BIT[MSB_SRC][MSB_DST][MSB_RES]
- Why is he telling us this?
→ because of the conditional branches.
→ Once we have the PSW bits, we can check the status of its bits to determine whether we will branch or not.
→ PSW table:
⇒ BEQ → Z = 1 → signed/unsigned
⇒ BNE → Z = 0 → signed/unsigned
⇒ BC → C = 1 → unsigned
⇒ BNC → C = 0 → unsigned
⇒ BN → N = 1 → signed
⇒ BGE → N = V → signed
⇒ BLT → N != V → signed

Index