2023/05/23 - 12:53
Help session:
ASM file, assembler produces an XME file and a LIS file.
XME:
- executable file
- Contains S records
   → S0 : contains filename
   → s1: instructions and data
   → s9: starting address
      ⇒ typically put into the program counter
- Typical format: s0s then S1s then S9s
     
LIS
We are writing an emulator
- what does it do?
   → Emulating the CPU
   → Emulating Memory
   → NOt emulating devices (Yet?)
- In order to make it useful we have to write support software for it.
   → Loader
   → Loader takes the xme files and puts them into memory.
   → It can also set the program counter to the value in the S9 record.
   → If wedont put an end directive in, the default starting address is 0.
   → Couple things to note about the loader:
      ⇒ Always need an S record
         • s[type][length][address] are present in every record
         • what comes after depends on the typee
         • 0: chars
         • 1: data, instructions
         • 9: whatever the PC gets
         • at the end of all of them, theres a checksum
         • don't read the data twice. only read it once, and send a diagnostic if the checksum fails.
      ⇒ Main()
      ⇒ initializations
      ⇒ call debugger
         • debugger has options: load... go
         • go calls CPU
            ◇ WHILE COND
               ▪ fetch
               ▪ decode
               ▪ execute
            ◇ END WHILE
      ⇒ Memory:
         • 64 KiB
         • 0000-FFFF
         • what's the last value the PC can have in memory? 
            ◇ FFFE
            ◇ because instructions have to start on an even byte location
            ◇ if it exceeds FFFF, you should wrap back around to address 0
            ◇ FFFE +2 = 0000 anyway
         • Any register or memory lcoaiton change is a state change of the machine.
         • An arithmetic operation can change the PSW, also a state change.
      ⇒ While emulating the machine, we also want to emulate memory. How?
         • An array
         • How will we define an array of 64 kibibytes?
         • #define MEM_SIZE 1 << 16
         • unsigned char memory[MEM_SIZE]
         • we would like to access memory at the word level
         • word memory only goes from 0 - 4FFF but maps into the same locations
         • each word occupies 2 bytes
         • #define WDMEMSZ 1<<15
         • #define BMEMSZ 1 << 16
         • short wmem[WDMEMSZ]
         • We can make a union
            ◇ unions allow us to share memory between two structs/arrays/etc
            ◇ union mem{
               ▪ unsigned short wm[WDMEMSZ]
               ▪ unsigned char bm[BMEMSZ]
            ◇ }
            ◇ union mem memory
            ◇ wm and bm are fields inside the structure
            ◇ access like this: memory.wm[index], memory.bm[index]
         • Accessing memory:
            ◇ we have to distingish between reading and writing
            ◇ also have to indicate whether its a word or byte
            ◇ also be given the equivalent of a memory address register or memory data register, and memory buffer register
         • Larry suggests a function called Bus that would take (MAR, MDR, RW, W/B)
         • ifwe are doing a write to memory, this would look like:
            ◇ WORD: mem.wm[MAR>>1] = MDR
            ◇ BYTE: mem.bm[MAR] = MDR ...&0xFF(if you want.. stops overflow)
         • assume that MDR is a short.
         • even if they're writing a byte it gets mapped to a short
         • READ:
            ◇ WORD: MDR = mem.wm[MAR>>1]
            ◇ BYTE: MDR = mem.bm[MAR]
            ◇ this doesnt work because it only changes the local MDR
                 • WRITE:
                    ◇ call Bus(Address, data, WRITE, w/b)
                 • READ:
                    ◇ call Bus(Address, data, READ, w/b)
         • These parameters are passed by value
         • have to pass by reference: pass &data.
         • bus( unsigned short MAR, unsigned short *MBR, ACCESS r/w, WORDBYTE w/b)
            ◇ pointer needs to be dereferenced.
            ◇ WRITE:
               ▪ WORD: mem.wm[MAR>>1] = *MDR
               ▪ BYTE: mem.bm[MAR] = *MDR ...&0xFF(if you want.. stops overflow)
            ◇ READ:
               ▪  WORD: &MDR = mem.wm[MAR>>1]
               ▪ BYTE: &MDR = mem.bm[MAR]
Regsiter file
- holds all the registers
- values of all registers
- registers go from R0-R7
- how do we set that up?
   → an array.
   → #define RFSZ 8
   → unsigned short regfile[RFSZ]
- Where do we get the register values?
   → each instruction 
   → they all have fields
   → Least sig 3 bits are Destination
   → next 3 are SRC registers (or constant)
      ⇒ if we're talking about registers, the value will go from 0-7 which will let us access our array
      ⇒ bits 3-5 are listed as s/c s/c s/c
      ⇒ indicating they can be a source register or a constant indicator
      ⇒ not the value of the constant itself
      ⇒ bit 6 indicates whether the operation is WORD oR BYTE
      ⇒ bit 7 indicates whether the operation is REGISTER or CONSTANT
         • 0 - REG
         • 1 - CONS
   → Set up a 2d array that can index the registers and constants
   → how do we know which one to choose?
      ⇒ bit 7
      ⇒ if r/c is 0
         • src gets regfile[s/c]
      ⇒ else
         • src gets confile[s/c]
      ⇒ this is clunky and we should use a table
      ⇒ how will we define the data structure?
         • it's a 2D array
         • unsigned short Regfile[2][RFSZ] = {{0,0,0,0,0,0,0,0}{0,1,2,4,8,16,32,-1}}
         • nothing stops us from overwriting the constants besides our own code.
      ⇒ how are we gonna use this
         • ADD #2 R3
         • R/C =1
         • WB = 0
         • SRC = 010
         • DST = 011
         • DST = DST + s/c
         • DST = Regfile[0][DST] + Regfile[R/C][SRC]
- Want to minimize the number of times you touch data.
- Aim for table driven software. Index
  Index