├── LICENSE ├── Makefile ├── README.md ├── disgen ├── cdisgen.py ├── disgen.py └── mips.json ├── example.gif ├── images └── vmlinux.srec ├── include └── mips.h └── src ├── emu.c ├── main.c ├── srec.c └── uart.c /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Andrew Chambers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean 2 | 3 | all: emu 4 | 5 | emu: ./src/*.c ./src/gen/doop.gen.c ./include/*.h 6 | gcc -O2 -I./include/ ./src/*.c -lpthread -o emu 7 | 8 | 9 | ./src/gen/doop.gen.c: ./disgen/*.py ./disgen/mips.json 10 | mkdir -p ./src/gen/ 11 | python ./disgen/disgen.py ./disgen/cdisgen.py ./disgen/mips.json > ./src/gen/doop.gen.c 12 | 13 | clean: 14 | rm -vrf ./src/gen/ 15 | rm -fv ./emu 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | cmips 2 | ===== 3 | 4 | Tiny mips 4kc emulator (smallest and easiest emulator to hack on that I know of - It boots linux in a few thousands lines of C code.) 5 | I wrote this emulator in the month or so after I graduated university and before I started work. 6 | 7 | Let me know if it has helped you in any way, I appreciate any feedback. 8 | 9 | Building and Running 10 | ==================== 11 | 12 | How to compile and run the emulator. 13 | 14 | ![example gif](example.gif) 15 | 16 | I have not put my custom linux kernel board support code online yet, so I included a precompiled kernel in the images folder. I have tested on linux (main platform) and cygwin in windows. 17 | 18 | 19 | Motivations 20 | =========== 21 | 22 | 1. To test my skills and learn about processors and operating systems. 23 | 2. To compile to javascript (asm.js) with emscripten to test it vs Fabrice Bellard's http://bellard.org/jslinux/ (I started this, but didnt finish writing a terminal emulator.) 24 | 3. To embed in other applications like games, simulators and sandboxes so people can have a realistic processor to play with. It doesn't really have any dependencies, 25 | so if you rewrite the serial port code, and embed the step function in your program loop somehow, it will work very easily. 26 | 27 | Status 28 | ====== 29 | 30 | Currently boots a custom built linux kernel. timers don't work quite right, 31 | so calculating bogomips takes a while during boot, and time readings are off. 32 | It currently supports a uart chip, and uses the cpu timer. 33 | It may eventually support pluggable framebuffer, mtd flash, 8250 uart, real time clock if there are reasons to extend it. 34 | It uses the linux emulation of floating point instructions. 35 | 36 | There is no way to quit the emulator currently with the keyboard because I set the terminal to raw mode and was lazy. 37 | 38 | (Pull requests and issues welcome.) 39 | 40 | Tests 41 | ===== 42 | test suite I used for bootstrapping located at https://github.com/andrewchambers/met. 43 | The linux kernel itself is more comprehensive. 44 | 45 | I used creduce and csmith as a way of fuzzing my emulator initially too. 46 | 47 | Misc 48 | ==== 49 | 50 | The disgen.py program is just a python script which converts a json representation of the opcodes 51 | into a giant switch case for disassembling and executing. 52 | 53 | Info 54 | ==== 55 | 56 | I used buildroot to build the image, and wrote a custom linux kernel board support package for the mips kernel following: 57 | http://linux.junsun.net/porting-howto/ 58 | Unfortunately it was a bit out of date, so I had to improvise. 59 | 60 | Those who wish to try similar projects, I learnt alot by working with my friend on a predecessor javascript emulator and a gameboy emulator. 61 | I mainly used the mips 4kc manual I got from wikipedia, and I read parts of the qemu code if things were not totally clear. 62 | 63 | The uart code is inspired from http://s-macke.github.io/jor1k/. 64 | 65 | The people on #linux-mips also helped me when I hit a bug in the mips linux kernel which prevented booting. 66 | -------------------------------------------------------------------------------- /disgen/cdisgen.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class CGen(CodeGenerator): 4 | 5 | def startFunc(self): 6 | print self.ws + "void doop(Mips * emu, uint32_t op) {" 7 | 8 | def endFunc(self): 9 | print self.ws + " //printf(\"unhandled opcode at %x -> %x\\n\",emu->pc,op);" 10 | print self.ws + " setExceptionCode(emu,EXC_RI);" 11 | print self.ws + " emu->exceptionOccured = 1;" 12 | print self.ws + " return;" 13 | print self.ws + "}" 14 | 15 | def startSwitch(self,switch): 16 | print self.ws + "switch(op & %s) {" % hex(int(switch,2)) 17 | 18 | def genCase(self,name,value): 19 | self.depth -= 1 20 | print self.ws + " case %s:"%hex(value) 21 | print self.ws + " op_%s(emu,op);"%name 22 | print self.ws + " return;" 23 | self.depth += 1 24 | 25 | def endSwitch(self): 26 | print self.ws + "}" 27 | -------------------------------------------------------------------------------- /disgen/disgen.py: -------------------------------------------------------------------------------- 1 | #Script to convert the json instruction description into a switch case. 2 | #Uses a simple plugin system (exec based) because I reused this across a few projects. 3 | 4 | 5 | import sys 6 | import json 7 | import itertools 8 | 9 | #replace anything that is not binary with x 10 | #remove spaces 11 | def fixOpstring(mstring): 12 | ret = "" 13 | for c in mstring: 14 | if c in "01": 15 | ret += c 16 | elif c == " ": 17 | continue 18 | else: 19 | ret += "x" 20 | return ret 21 | 22 | #test for opstring equivalence 23 | #used to make sure all different 24 | #values can really be distinguished 25 | def canNotDistinguishOpstring(m1,m2): 26 | assert len(m1) == len(m2) 27 | 28 | for idx in range(len(m1)): 29 | c1,c2 = m1[idx],m2[idx] 30 | if c1 in "01" and c2 not in "01": 31 | continue 32 | if c2 in "01" and c1 not in "01": 33 | continue 34 | 35 | if c1 != c2: 36 | return False 37 | 38 | return True 39 | 40 | def ensureValid(input): 41 | names = map(lambda x : x[0],input) 42 | if len(set(names)) != len(names): 43 | raise Exception("non unique names in input") 44 | 45 | opstrings = map(lambda x : x[1],input) 46 | 47 | for s in opstrings: 48 | if len(s) != 32: 49 | raise Exception("all opstrings must be 32 bits - " + s) 50 | 51 | 52 | #input is same as informat 53 | def ensureCanDistinguish(input): 54 | opstrings = map(lambda x : x[1],input) 55 | for a,b in itertools.combinations(opstrings,2): 56 | if canNotDistinguishOpstring(a,b): 57 | raise Exception("opstrings not distinguishable! %s %s" %(a,b)) 58 | 59 | def opstringToMask(opstring): 60 | andMask = "" 61 | for c in opstring: 62 | if c in "01": 63 | andMask += "1" 64 | else: 65 | andMask += "0" 66 | return andMask 67 | 68 | class Counter(object): 69 | def __init__(self,col): 70 | self.counts = {} 71 | for x in col: 72 | if x in self.counts: 73 | self.counts[x] += 1 74 | else: 75 | self.counts[x] = 0 76 | def __iter__(self): 77 | return iter(self.counts) 78 | def __getitem__(self,idx): 79 | return self.counts[idx] 80 | 81 | def findBestAndMask(input): 82 | opstrings = map(lambda x : x[1],input) 83 | andMasks = map(opstringToMask,opstrings) 84 | counts = Counter(andMasks) 85 | andMask = max(counts,key=lambda x : counts[x]) 86 | return andMask 87 | 88 | 89 | def opstringToVal(opstring): 90 | val = 0 91 | for idx,c in enumerate(opstring): 92 | if c == "1": 93 | val += 2**(len(opstring) - 1 -idx) 94 | return val 95 | 96 | 97 | class CodeGenerator(object): 98 | 99 | def __init__(self): 100 | self.depth = 0 101 | 102 | @property 103 | def ws(self): 104 | return " " * self.depth 105 | 106 | def generate(self,masks): 107 | self.depth = 0 108 | 109 | self.startFunc() 110 | self.depth += 1 111 | for m in masks: 112 | self.startSwitch(m[0]) 113 | self.depth += 1 114 | for value,name in m[1]: 115 | self.genCase(name,value) 116 | self.depth -= 1 117 | self.endSwitch() 118 | self.depth -= 1 119 | self.endFunc() 120 | 121 | def getCodeGenerator(fname): 122 | locals = {} 123 | execfile(fname,{"CodeGenerator":CodeGenerator},locals) 124 | for v in locals: 125 | if issubclass(locals[v],CodeGenerator): 126 | return locals[v]() 127 | raise Exception("No CodeGenerator subclass found") 128 | 129 | 130 | 131 | def main(): 132 | data = open(sys.argv[2]).read() 133 | input = json.loads(data) 134 | input.sort(key=lambda x:x[1]) 135 | for k in range(len(input)): 136 | input[k][1] = fixOpstring(input[k][1]) 137 | ensureValid(input) 138 | ensureCanDistinguish(input) 139 | 140 | #list of [andmask , [[val,name] ...] ...] 141 | #mask is what you and the instruction with 142 | #val is the result 143 | #eventually to decode you will move down the list 144 | #in order testing masks and switching on val 145 | masks = [] 146 | 147 | #we iterate until we shift all from input into the masks array 148 | while len(input): 149 | newinput = [] 150 | bestAndmask = findBestAndMask(input) 151 | masks.append([bestAndmask , []]) 152 | 153 | for name,mask in input: 154 | if opstringToMask(mask) == bestAndmask: 155 | masks[-1][1].append([opstringToVal(mask),name]) 156 | else: 157 | newinput.append([name,mask]) 158 | 159 | input = newinput 160 | 161 | 162 | g = getCodeGenerator(sys.argv[1]) 163 | g.generate(masks) 164 | 165 | if __name__ == "__main__": 166 | main() 167 | -------------------------------------------------------------------------------- /disgen/mips.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "add", 4 | "000000xxxxxxxxxxxxxxxxxxxx100000" 5 | ], 6 | [ 7 | "srav", 8 | "000000xxxxxxxxxxxxxxxxxxxx000111" 9 | ], 10 | [ 11 | "addi", 12 | "001000xxxxxxxxxxxxxxxxxxxxxxxxxx" 13 | ], 14 | [ 15 | "addiu", 16 | "001001xxxxxxxxxxxxxxxxxxxxxxxxxx" 17 | ], 18 | [ 19 | "addu", 20 | "000000xxxxxxxxxxxxxxxxxxxx100001" 21 | ], 22 | [ 23 | "and", 24 | "000000xxxxxxxxxxxxxxxxxxxx100100" 25 | ], 26 | [ 27 | "andi", 28 | "001100xxxxxxxxxxxxxxxxxxxxxxxxxx" 29 | ], 30 | [ 31 | "beq", 32 | "000100xxxxxxxxxxxxxxxxxxxxxxxxxx" 33 | ], 34 | [ 35 | "beql", 36 | "010100xxxxxxxxxxxxxxxxxxxxxxxxxx" 37 | ], 38 | [ 39 | "eret", 40 | "01000010000000000000000000011000" 41 | ], 42 | [ 43 | "tne", 44 | "000000xxxxxxxxxxxxxxxxxxxx110110" 45 | ], 46 | [ 47 | "bne", 48 | "000101xxxxxxxxxxxxxxxxxxxxxxxxxx" 49 | ], 50 | [ 51 | "blez", 52 | "000110xxxxxxxxxxxxxxxxxxxxxxxxxx" 53 | ], 54 | [ 55 | "blezl", 56 | "010110xxxxxxxxxxxxxxxxxxxxxxxxxx" 57 | ], 58 | [ 59 | "bnel", 60 | "010101xxxxxxxxxxxxxxxxxxxxxxxxxx" 61 | ], 62 | [ 63 | "bgez", 64 | "000001xxxxx00001xxxxxxxxxxxxxxxx" 65 | ], 66 | [ 67 | "bgezal", 68 | "000001xxxxx10001xxxxxxxxxxxxxxxx" 69 | ], 70 | [ 71 | "bgezl", 72 | "000001xxxxx00011xxxxxxxxxxxxxxxx" 73 | ], 74 | [ 75 | "bltz", 76 | "000001xxxxx00000xxxxxxxxxxxxxxxx" 77 | ], 78 | [ 79 | "bltzal", 80 | "000001xxxxx10000xxxxxxxxxxxxxxxx" 81 | ], 82 | [ 83 | "bltzl", 84 | "000001xxxxx00010xxxxxxxxxxxxxxxx" 85 | ], 86 | [ 87 | "bgtz", 88 | "000111xxxxxxxxxxxxxxxxxxxxxxxxxx" 89 | ], 90 | [ 91 | "bgtzl", 92 | "010111xxxxx00000xxxxxxxxxxxxxxxx" 93 | ], 94 | [ 95 | "div", 96 | "000000xxxxxxxxxxxxxxxxxxxx011010" 97 | ], 98 | [ 99 | "divu", 100 | "000000xxxxxxxxxxxxxxxxxxxx011011" 101 | ], 102 | [ 103 | "j", 104 | "000010xxxxxxxxxxxxxxxxxxxxxxxxxx" 105 | ], 106 | [ 107 | "jal", 108 | "000011xxxxxxxxxxxxxxxxxxxxxxxxxx" 109 | ], 110 | [ 111 | "jalr", 112 | "000000xxxxxxxxxxxxxxxxxxxx001001" 113 | ], 114 | [ 115 | "jr", 116 | "000000xxxxxxxxxxxxxxxxxxxx001000" 117 | ], 118 | [ 119 | "lbu", 120 | "100100xxxxxxxxxxxxxxxxxxxxxxxxxx" 121 | ], 122 | [ 123 | "lb", 124 | "100000xxxxxxxxxxxxxxxxxxxxxxxxxx" 125 | ], 126 | [ 127 | "lhu", 128 | "100101xxxxxxxxxxxxxxxxxxxxxxxxxx" 129 | ], 130 | [ 131 | "ll", 132 | "110000xxxxxxxxxxxxxxxxxxxxxxxxxx" 133 | ], 134 | [ 135 | "cache", 136 | "101111xxxxxxxxxxxxxxxxxxxxxxxxxx" 137 | ], 138 | [ 139 | "pref", 140 | "110011xxxxxxxxxxxxxxxxxxxxxxxxxx" 141 | ], 142 | [ 143 | "sc", 144 | "111000xxxxxxxxxxxxxxxxxxxxxxxxxx" 145 | ], 146 | [ 147 | "lwl", 148 | "100010xxxxxxxxxxxxxxxxxxxxxxxxxx" 149 | ], 150 | [ 151 | "lwr", 152 | "100110xxxxxxxxxxxxxxxxxxxxxxxxxx" 153 | ], 154 | [ 155 | "swl", 156 | "101010xxxxxxxxxxxxxxxxxxxxxxxxxx" 157 | ], 158 | [ 159 | "swr", 160 | "101110xxxxxxxxxxxxxxxxxxxxxxxxxx" 161 | ], 162 | [ 163 | "lh", 164 | "100001xxxxxxxxxxxxxxxxxxxxxxxxxx" 165 | ], 166 | [ 167 | "lui", 168 | "001111xxxxxxxxxxxxxxxxxxxxxxxxxx" 169 | ], 170 | [ 171 | "lw", 172 | "100011xxxxxxxxxxxxxxxxxxxxxxxxxx" 173 | ], 174 | [ 175 | "mfhi", 176 | "000000xxxxxxxxxxxxxxxxxxxx010000" 177 | ], 178 | [ 179 | "mflo", 180 | "000000xxxxxxxxxxxxxxxxxxxx010010" 181 | ], 182 | [ 183 | "mthi", 184 | "000000xxxxxxxxxxxxxxxxxxxx010001" 185 | ], 186 | [ 187 | "mtlo", 188 | "000000xxxxxxxxxxxxxxxxxxxx010011" 189 | ], 190 | [ 191 | "mfc0", 192 | "01000000000xxxxxxxxxxxxxxxxxxxxx" 193 | ], 194 | [ 195 | "mtc0", 196 | "01000000100xxxxxxxxxxxxxxxxxxxxx" 197 | ], 198 | [ 199 | "mul", 200 | "011100xxxxxxxxxxxxxxx00000000010" 201 | ], 202 | [ 203 | "movz", 204 | "000000xxxxxxxxxxxxxxx00000001010" 205 | ], 206 | [ 207 | "movn", 208 | "000000xxxxxxxxxxxxxxx00000001011" 209 | ], 210 | [ 211 | "mult", 212 | "000000xxxxxxxxxxxxxxxxxxxx011000" 213 | ], 214 | [ 215 | "multu", 216 | "000000xxxxxxxxxxxxxxxxxxxx011001" 217 | ], 218 | [ 219 | "tlbwi", 220 | "01000010000000000000000000000010" 221 | ], 222 | [ 223 | "tlbwr", 224 | "01000010000000000000000000000110" 225 | ], 226 | [ 227 | "tlbp", 228 | "01000010000000000000000000001000" 229 | ], 230 | [ 231 | "nor", 232 | "000000xxxxxxxxxxxxxxxxxxxx100111" 233 | ], 234 | [ 235 | "xor", 236 | "000000xxxxxxxxxxxxxxxxxxxx100110" 237 | ], 238 | [ 239 | "xori", 240 | "001110xxxxxxxxxxxxxxxxxxxxxxxxxx" 241 | ], 242 | [ 243 | "or", 244 | "000000xxxxxxxxxxxxxxxxxxxx100101" 245 | ], 246 | [ 247 | "ori", 248 | "001101xxxxxxxxxxxxxxxxxxxxxxxxxx" 249 | ], 250 | [ 251 | "sb", 252 | "101000xxxxxxxxxxxxxxxxxxxxxxxxxx" 253 | ], 254 | [ 255 | "sh", 256 | "101001xxxxxxxxxxxxxxxxxxxxxxxxxx" 257 | ], 258 | [ 259 | "slt", 260 | "000000xxxxxxxxxxxxxxxxxxxx101010" 261 | ], 262 | [ 263 | "slti", 264 | "001010xxxxxxxxxxxxxxxxxxxxxxxxxx" 265 | ], 266 | [ 267 | "sltiu", 268 | "001011xxxxxxxxxxxxxxxxxxxxxxxxxx" 269 | ], 270 | [ 271 | "sltu", 272 | "000000xxxxxxxxxxxxxxxxxxxx101011" 273 | ], 274 | [ 275 | "sll", 276 | "000000xxxxxxxxxxxxxxxxxxxx000000" 277 | ], 278 | [ 279 | "srl", 280 | "000000xxxxxxxxxxxxxxxxxxxx000010" 281 | ], 282 | [ 283 | "srlv", 284 | "000000xxxxxxxxxxxxxxxxxxxx000110" 285 | ], 286 | [ 287 | "sllv", 288 | "000000xxxxxxxxxxxxxxxxxxxx000100" 289 | ], 290 | [ 291 | "sra", 292 | "000000xxxxxxxxxxxxxxxxxxxx000011" 293 | ], 294 | [ 295 | "sub", 296 | "000000xxxxxxxxxxxxxxxxxxxx100010" 297 | ], 298 | [ 299 | "subu", 300 | "000000xxxxxxxxxxxxxxxxxxxx100011" 301 | ], 302 | [ 303 | "sw", 304 | "101011xxxxxxxxxxxxxxxxxxxxxxxxxx" 305 | ], 306 | [ 307 | "syscall", 308 | "000000xxxxxxxxxxxxxxxxxxxx001100" 309 | ], 310 | [ 311 | "sync", 312 | "000000xxxxxxxxxxxxxxxxxxxx001111" 313 | ], 314 | [ 315 | "wait", 316 | "0100001xxxxxxxxxxxxxxxxxxx100000" 317 | ] 318 | ] 319 | -------------------------------------------------------------------------------- /example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewchambers/cmips/72de5368647a3513c11993cb33c5fcb78605e7fc/example.gif -------------------------------------------------------------------------------- /include/mips.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define UARTBASE 0x140003f8 5 | #define UARTSIZE 20 6 | #define POWERBASE 0x1fbf0004 7 | #define POWERSIZE 4 8 | 9 | 10 | typedef struct { 11 | uint32_t VPN2; 12 | uint8_t ASID; 13 | uint16_t G:1; 14 | uint32_t C0:3; 15 | uint32_t C1:3; 16 | uint32_t V0:1; 17 | uint32_t V1:1; 18 | uint32_t D0:1; 19 | uint32_t D1:1; 20 | uint32_t PFN[2]; 21 | } TLB_entry; 22 | 23 | typedef struct { 24 | TLB_entry entries[16]; 25 | int exceptionWasNoMatch; 26 | } TLB; 27 | 28 | 29 | typedef struct { 30 | uint8_t LCR; // Line Control, reset, character has 8 bits 31 | uint8_t LSR; // Line Status register, Transmitter serial register empty and Transmitter buffer register empty 32 | uint8_t MSR; // modem status register 33 | uint8_t IIR; // Interrupt Identification, no interrupt 34 | uint8_t IER; // Interrupt Enable 35 | uint8_t DLL; 36 | uint8_t DLH; 37 | uint8_t FCR; // FIFO Control; 38 | uint8_t MCR; // Modem Control 39 | uint8_t SCR; // Modem Control 40 | 41 | //NOTE dont change this size without changing the corresponding fifo function 42 | uint8_t fifo[32]; //ring buffer 43 | uint32_t fifoFirst; 44 | uint32_t fifoLast; 45 | uint32_t fifoCount; 46 | 47 | } Uart; 48 | 49 | 50 | 51 | typedef struct { 52 | uint32_t * mem; 53 | uint32_t pmemsz; 54 | uint32_t shutdown; 55 | uint32_t pc; 56 | uint32_t regs[32]; 57 | uint32_t hi; 58 | uint32_t lo; 59 | uint32_t delaypc; 60 | uint8_t inDelaySlot; 61 | uint8_t exceptionOccured; 62 | 63 | 64 | int llbit; 65 | uint32_t CP0_Index; 66 | uint32_t CP0_EntryHi; 67 | uint32_t CP0_EntryLo0; 68 | uint32_t CP0_EntryLo1; 69 | uint32_t CP0_Context; 70 | uint32_t CP0_Wired; 71 | uint32_t CP0_Status; 72 | uint32_t CP0_Epc; 73 | uint32_t CP0_BadVAddr; 74 | uint32_t CP0_ErrorEpc; 75 | uint32_t CP0_Cause; 76 | uint32_t CP0_PageMask; 77 | 78 | uint32_t CP0_Count; 79 | uint32_t CP0_Compare; 80 | 81 | int waiting; 82 | 83 | Uart serial; 84 | 85 | TLB tlb; 86 | } Mips; 87 | 88 | 89 | Mips * new_mips(uint32_t physMemSize); 90 | void free_mips(Mips * mips); 91 | void step_mips(Mips * emu); 92 | 93 | typedef struct { 94 | void * userdata; 95 | int (*nextChar)(void *); 96 | int (*isEof)(void *); 97 | } SrecLoader; 98 | 99 | int loadSrec_mips(Mips * emu,SrecLoader * s); 100 | int loadSrecFromFile_mips(Mips * emu,char * fname); 101 | int loadSrecFromString_mips(Mips * emu,char * srec); 102 | 103 | typedef struct { 104 | FILE * file; 105 | uint32_t regs[32]; 106 | uint32_t hi; 107 | uint32_t lo; 108 | uint32_t pc; 109 | uint32_t canary; // memory canary used to trace writes 110 | } EmuTrace; 111 | 112 | EmuTrace * startTrace(char * tracefile); 113 | void updateTrace(EmuTrace * t,Mips * emu); 114 | void endTrace(EmuTrace * t); 115 | 116 | static void triggerExternalInterrupt(Mips * emu,unsigned int intNum) { 117 | emu->CP0_Cause |= ((1 << intNum) & 0x3f ) << 10; 118 | } 119 | 120 | static void clearExternalInterrupt(Mips * emu,unsigned int intNum) { 121 | emu->CP0_Cause &= ~(((1 << intNum) & 0x3f ) << 10); 122 | } 123 | 124 | 125 | void uart_Reset(Mips * emu); 126 | 127 | uint32_t uart_read(Mips * emu,uint32_t offset); 128 | void uart_write(Mips * emu,uint32_t offset,uint32_t v); 129 | uint8_t uart_readb(Mips * emu,uint32_t offset); 130 | void uart_writeb(Mips * emu,uint32_t offset,uint8_t v); 131 | void uart_RecieveChar(Mips * emu, uint8_t c); 132 | 133 | extern char * regn2o32[]; 134 | 135 | -------------------------------------------------------------------------------- /src/emu.c: -------------------------------------------------------------------------------- 1 | 2 | #include "mips.h" 3 | #include 4 | #include 5 | 6 | 7 | #define CP0St_CU3 31 8 | #define CP0St_CU2 30 9 | #define CP0St_CU1 29 10 | #define CP0St_CU0 28 11 | #define CP0St_RP 27 12 | #define CP0St_FR 26 13 | #define CP0St_RE 25 14 | #define CP0St_MX 24 15 | #define CP0St_PX 23 16 | #define CP0St_BEV 22 17 | #define CP0St_TS 21 18 | #define CP0St_SR 20 19 | #define CP0St_NMI 19 20 | #define CP0St_IM 8 21 | #define CP0St_KX 7 22 | #define CP0St_SX 6 23 | #define CP0St_UX 5 24 | #define CP0St_UM 4 25 | #define CP0St_KSU 3 26 | #define CP0St_ERL 2 27 | #define CP0St_EXL 1 28 | #define CP0St_IE 0 29 | 30 | 31 | char * regn2o32[] = { 32 | "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", 33 | "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", 34 | "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", 35 | "t8", "t9", "k0", "k1", "gp", "sp", "s8","ra" 36 | }; 37 | 38 | 39 | void doop(Mips * emu, uint32_t op); // predeclare doop its only local to this file 40 | 41 | 42 | Mips * new_mips(uint32_t physMemSize) { 43 | 44 | if (physMemSize % 4 != 0) { 45 | return 0; 46 | } 47 | 48 | void * mem = calloc(physMemSize,1); 49 | 50 | if(!mem) { 51 | return 0; 52 | } 53 | 54 | Mips * ret = calloc(1,sizeof(Mips)); 55 | 56 | if (!ret) { 57 | free(mem); 58 | return 0; 59 | } 60 | 61 | ret->mem = mem; 62 | ret->pmemsz = physMemSize; 63 | 64 | // Init status registers 65 | 66 | ret->CP0_Status |= (1 << CP0St_ERL); //start in kernel mode with unmapped useg 67 | 68 | uart_Reset(ret); 69 | 70 | return ret; 71 | } 72 | 73 | void free_mips(Mips * mips) { 74 | free(mips->mem); 75 | free(mips); 76 | } 77 | 78 | /* bitwise helpers */ 79 | 80 | int32_t sext18(uint32_t val) { 81 | if ( (val&0x20000) != 0 ) { 82 | return 0xfffc0000 | val; 83 | } 84 | return 0x0003ffff&val; 85 | } 86 | 87 | //Possible Values for the EXC field in the status reg 88 | #define EXC_Int 0 89 | #define EXC_Mod 1 90 | #define EXC_TLBL 2 91 | #define EXC_TLBS 3 92 | #define EXC_AdEL 4 93 | #define EXC_AdES 5 94 | #define EXC_IBE 6 95 | #define EXC_DBE 7 96 | #define EXC_SYS 8 97 | #define EXC_BP 9 98 | #define EXC_RI 10 99 | #define EXC_CpU 11 100 | #define EXC_Ov 12 101 | #define EXC_Tr 13 102 | #define EXC_Watch 23 103 | #define EXC_MCheck 24 104 | 105 | static inline uint32_t getExceptionCode(Mips * emu) { 106 | return (emu->CP0_Cause >> 2) & 0x1f; 107 | } 108 | 109 | static inline void setExceptionCode(Mips * emu,int code) { 110 | emu->CP0_Cause &= ~(0x1f << 2); // clear exccode 111 | emu->CP0_Cause |= (code & 0x1f) << 2; //set with new code 112 | } 113 | 114 | static inline uint32_t isKernelMode(Mips * emu) { 115 | 116 | if (! (emu->CP0_Status & (1 << CP0St_UM))) { 117 | return 1; 118 | } 119 | return emu->CP0_Status & ((1 << CP0St_EXL) | (1 << CP0St_ERL)); 120 | } 121 | 122 | //tlb lookup return codes 123 | 124 | #define TLBRET_MATCH 0 125 | #define TLBRET_NOMATCH 1 126 | #define TLBRET_DIRTY 2 127 | #define TLBRET_INVALID 3 128 | 129 | //horrible but deterministic fake rand for testing 130 | uint32_t counter = 0; 131 | 132 | static uint32_t randomInRange(uint32_t a,uint32_t b) { 133 | counter++; 134 | return a + (counter % (1 + b - a)); 135 | } 136 | 137 | static void writeTlbExceptionExtraData(Mips * emu,uint32_t vaddr) { 138 | emu->CP0_BadVAddr = vaddr; 139 | emu->CP0_Context = (emu->CP0_Context & ~0x007fffff) |((vaddr >> 9) & 0x007ffff0); 140 | emu->CP0_EntryHi = (emu->CP0_EntryHi & 0xff) | (vaddr & (0xffffe000)); 141 | } 142 | 143 | 144 | static void debug_dumpTlb(Mips * emu) { 145 | int i; 146 | for (i = 0; i < 16; i++) { 147 | TLB_entry *tlb_e = &emu->tlb.entries[i]; 148 | printf("TLBENT %d:\n" 149 | " VPN2: %08x\n" 150 | " ASID: %02x\n" 151 | " G: %d\n" 152 | " V0: %d\n" 153 | " V1: %d\n" 154 | //XXX more fields 155 | ,i,tlb_e->VPN2,tlb_e->ASID,tlb_e->G,tlb_e->V0,tlb_e->V1); 156 | } 157 | 158 | } 159 | 160 | //XXX currently hardcoded for 4k pages 161 | static int tlb_lookup (Mips *emu,uint32_t vaddress, uint32_t *physical, int write) { 162 | uint8_t ASID = emu->CP0_EntryHi & 0xFF; 163 | int i; 164 | emu->tlb.exceptionWasNoMatch = 0; 165 | 166 | for (i = 0; i < 16; i++) { 167 | TLB_entry *tlb_e = &emu->tlb.entries[i]; 168 | uint32_t tag = (vaddress & 0xfffff000) >> 13; 169 | uint32_t VPN2 = tlb_e->VPN2; 170 | /* Check ASID, virtual page number & size */ 171 | if ((tlb_e->G == 1 || tlb_e->ASID == ASID) && VPN2 == tag) { 172 | /* TLB match */ 173 | int n = (vaddress >> 12) & 1; 174 | /* Check access rights */ 175 | if (!(n ? tlb_e->V1 : tlb_e->V0)) { 176 | emu->exceptionOccured = 1; 177 | setExceptionCode(emu,write ? EXC_TLBS : EXC_TLBL); 178 | writeTlbExceptionExtraData(emu,vaddress); 179 | return TLBRET_INVALID; 180 | } 181 | if (write == 0 || (n ? tlb_e->D1 : tlb_e->D0)) { 182 | *physical = tlb_e->PFN[n] | (vaddress & 0xfff); 183 | return TLBRET_MATCH; 184 | } 185 | emu->exceptionOccured = 1; 186 | setExceptionCode(emu,EXC_Mod); 187 | writeTlbExceptionExtraData(emu,vaddress); 188 | return TLBRET_DIRTY; 189 | } 190 | } 191 | emu->tlb.exceptionWasNoMatch = 1; 192 | emu->exceptionOccured = 1; 193 | setExceptionCode(emu,write ? EXC_TLBS : EXC_TLBL); 194 | writeTlbExceptionExtraData(emu,vaddress); 195 | return TLBRET_NOMATCH; 196 | } 197 | 198 | //internally triggers exceptions on error 199 | //returns an error code 200 | static inline int translateAddress(Mips * emu,uint32_t vaddr,uint32_t * paddr_out,int write) { 201 | 202 | if (vaddr <= 0x7FFFFFFF) { 203 | /* useg */ 204 | if (emu->CP0_Status & (1 << CP0St_ERL)) { 205 | *paddr_out = vaddr; 206 | return 0; 207 | } else { 208 | return tlb_lookup(emu,vaddr,paddr_out, write); 209 | } 210 | } else if ( vaddr >= 0x80000000 && vaddr <= 0x9fffffff ) { 211 | *paddr_out = vaddr - 0x80000000; 212 | return 0; 213 | } else if ( vaddr >= 0xa0000000 && vaddr <= 0xbfffffff ) { 214 | *paddr_out = vaddr - 0xa0000000; 215 | return 0; 216 | } else { //kseg2 and 3 217 | if(isKernelMode(emu)) { 218 | return tlb_lookup(emu,vaddr,paddr_out, write); 219 | } else { 220 | *paddr_out = vaddr; 221 | puts("translateAddress: unhandled exception"); 222 | exit(1); 223 | return 1; 224 | } 225 | 226 | } 227 | 228 | puts("unreachable."); 229 | return 1; 230 | 231 | } 232 | 233 | static uint32_t readVirtWord(Mips * emu, uint32_t addr) { 234 | uint32_t paddr; 235 | int err = translateAddress(emu,addr,&paddr,0); 236 | if(err) { 237 | return 0; 238 | } 239 | 240 | if(paddr % 4 != 0) { 241 | printf("Unhandled alignment error reading addr %08x\n",addr); 242 | exit(1); 243 | } 244 | 245 | if(paddr >= UARTBASE && paddr <= UARTBASE + UARTSIZE) { 246 | return uart_read(emu,paddr - UARTBASE); 247 | } 248 | 249 | if (paddr >= emu->pmemsz) { 250 | printf("unhandled bus error at pc: %08x reading paddr: %08x\n",emu->pc,paddr); 251 | exit(1); 252 | } 253 | 254 | return emu->mem[paddr/4]; 255 | } 256 | 257 | static void writeVirtWord(Mips * emu, uint32_t addr,uint32_t val) { 258 | uint32_t paddr; 259 | int err = translateAddress(emu,addr,&paddr,1); 260 | if(err) { 261 | return; 262 | } 263 | 264 | if(paddr % 4 != 0) { 265 | printf("Unhandled alignment error reading addr %08x\n",addr); 266 | exit(1); 267 | } 268 | 269 | if(paddr >= UARTBASE && paddr <= UARTBASE + UARTSIZE) { 270 | uart_write(emu,paddr - UARTBASE,val); 271 | return; 272 | } 273 | 274 | if (paddr >= emu->pmemsz) { 275 | printf("bus error at pc: %08x writing paddr: %08x\n",emu->pc,paddr); 276 | setExceptionCode(emu,EXC_DBE); 277 | emu->exceptionOccured = 1; 278 | return; 279 | } 280 | 281 | emu->mem[paddr/4] = val; 282 | } 283 | 284 | static uint8_t readVirtByte(Mips * emu, uint32_t addr) { 285 | uint32_t paddr; 286 | int err = translateAddress(emu,addr,&paddr,0); 287 | if(err) { 288 | return 0; 289 | } 290 | 291 | if(paddr >= UARTBASE && paddr <= UARTBASE + UARTSIZE) { 292 | return uart_readb(emu,paddr - UARTBASE); 293 | } 294 | 295 | unsigned int offset = paddr&3; 296 | 297 | if (paddr >= emu->pmemsz) { 298 | printf("unhandled bus error paddr: %08x\n",paddr); 299 | exit(1); 300 | } 301 | 302 | uint32_t word = emu->mem[(paddr&(~0x3)) /4]; 303 | uint32_t shamt = 8*(3 - offset); 304 | uint32_t mask = 0xff << shamt; 305 | uint8_t b = (word&mask) >> shamt; 306 | return b; 307 | } 308 | 309 | static void writeVirtByte(Mips * emu, uint32_t addr,uint8_t val) { 310 | uint32_t paddr; 311 | 312 | int err = translateAddress(emu,addr,&paddr,1); 313 | if(err) { 314 | return; 315 | } 316 | 317 | if(paddr >= UARTBASE && paddr <= UARTBASE + UARTSIZE) { 318 | uart_writeb(emu,paddr - UARTBASE,val); 319 | return; 320 | } 321 | 322 | if(paddr >= POWERBASE && paddr <= POWERBASE + POWERSIZE) { 323 | emu->shutdown = 1; 324 | return; 325 | } 326 | 327 | if (paddr >= emu->pmemsz) { 328 | printf("unhandled bus error paddr: %08x\n",paddr); 329 | exit(1); 330 | } 331 | 332 | unsigned int offset = paddr&3; 333 | uint32_t baseaddr = paddr&(~0x3); 334 | uint32_t word = emu->mem[baseaddr/4]; 335 | unsigned int shamt = 8*(3 - offset); 336 | uint32_t clearmask = ~(0xff<mem[baseaddr/4] = word; 341 | } 342 | 343 | static void handleException(Mips * emu,int inDelaySlot) { 344 | 345 | uint32_t offset; 346 | int exccode = getExceptionCode(emu); 347 | 348 | emu->inDelaySlot = 0; 349 | 350 | if ( (emu->CP0_Status & (1 << CP0St_EXL)) == 0) { 351 | if (inDelaySlot) { 352 | emu->CP0_Epc = emu->pc - 4; 353 | emu->CP0_Cause |= (1 << 31); // set BD 354 | } else { 355 | emu->CP0_Epc = emu->pc; 356 | emu->CP0_Cause &= ~(1 << 31); // clear BD 357 | } 358 | 359 | if (exccode == EXC_TLBL || exccode == EXC_TLBS ) { 360 | //printf("tlb exception %x\n",emu->CP0_Epc); 361 | //XXX this seems inverted? bug in also qemu? test with these cases reversed after booting. 362 | if(!emu->tlb.exceptionWasNoMatch) { 363 | offset = 0x180; 364 | } else { 365 | offset = 0; 366 | } 367 | } else if ( (exccode == EXC_Int) && ((emu->CP0_Cause & (1 << 23)) != 0)) { 368 | offset = 0x200; 369 | } else { 370 | offset = 0x180; 371 | } 372 | } else { 373 | offset = 0x180; 374 | } 375 | 376 | // Faulting coprocessor number set at fault location 377 | // exccode set at fault location 378 | emu->CP0_Status |= (1 << CP0St_EXL); 379 | 380 | if (emu->CP0_Status & (1 << CP0St_BEV)) { 381 | emu->pc = 0xbfc00200 + offset; 382 | } else { 383 | emu->pc = 0x80000000 + offset; 384 | } 385 | 386 | //if(exccode != EXC_Int) { 387 | // #define TARGET_FMT_lx "%08x" 388 | // printf("\nException: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n" 389 | // " S %08x C %08x A " TARGET_FMT_lx "\n", 390 | // emu->pc, emu->CP0_Epc, exccode, 391 | // emu->CP0_Status, emu->CP0_Cause, emu->CP0_BadVAddr); 392 | //} 393 | 394 | emu->exceptionOccured = 0; 395 | 396 | } 397 | 398 | /* return 1 if interrupt occured else 0*/ 399 | static int handleInterrupts(Mips * emu) { 400 | 401 | //if interrupts disabled or ERL or EXL set 402 | if((emu->CP0_Status & 1) == 0 || (emu->CP0_Status & ((1 << 1) | (1 << 2))) ) { 403 | return 0; // interrupts disabled 404 | } 405 | 406 | if((emu->CP0_Cause & emu->CP0_Status & 0xfc00) == 0) { 407 | return 0; // no pending interrupts 408 | } 409 | 410 | emu->waiting = 0; 411 | setExceptionCode(emu,EXC_Int); 412 | handleException(emu,emu->inDelaySlot); 413 | 414 | return 1; 415 | 416 | } 417 | 418 | void step_mips(Mips * emu) { 419 | 420 | if (emu->shutdown){ 421 | return; 422 | } 423 | 424 | emu->CP0_Count++; 425 | /* timer code */ 426 | if (emu->CP0_Count == emu->CP0_Compare){ 427 | // but only do this if interrupts are enabled to save time. 428 | triggerExternalInterrupt(emu,5); // 5 is the timer int :) 429 | } 430 | 431 | if(handleInterrupts(emu)) { 432 | return; 433 | } 434 | 435 | if(emu->waiting) { 436 | return; 437 | } 438 | 439 | /* end timer code */ 440 | 441 | int startInDelaySlot = emu->inDelaySlot; 442 | 443 | uint32_t opcode = readVirtWord(emu,emu->pc); 444 | 445 | 446 | if(emu->exceptionOccured) { //instruction fetch failed 447 | handleException(emu,startInDelaySlot); 448 | return; 449 | } 450 | 451 | 452 | doop(emu,opcode); 453 | emu->regs[0] = 0; 454 | 455 | if(emu->exceptionOccured) { //instruction failed 456 | handleException(emu,startInDelaySlot); 457 | return; 458 | } 459 | 460 | if (startInDelaySlot) { 461 | emu->pc = emu->delaypc; 462 | emu->inDelaySlot = 0; 463 | return; 464 | } 465 | 466 | emu->pc += 4; 467 | } 468 | 469 | 470 | static inline void setRt(Mips * emu,uint32_t op,uint32_t val) { 471 | uint32_t idx = (op&0x1f0000) >> 16; 472 | emu->regs[idx] = val; 473 | } 474 | 475 | 476 | static inline uint32_t getRt(Mips * emu,uint32_t op) { 477 | uint32_t idx = (op&0x1f0000) >> 16; 478 | return emu->regs[idx]; 479 | } 480 | 481 | static inline void setRd(Mips * emu,uint32_t op,uint32_t val) { 482 | uint32_t idx = (op&0xf800) >> 11; 483 | emu->regs[idx] = val; 484 | } 485 | 486 | static inline uint32_t getRs(Mips * emu,uint32_t op) { 487 | uint32_t idx = (op&0x3e00000) >> 21; 488 | return emu->regs[idx]; 489 | } 490 | 491 | static inline uint16_t getImm(uint32_t op) { 492 | return op&0xffff; 493 | } 494 | 495 | static inline uint32_t getShamt(uint32_t op) { 496 | return (op&0x7c0) >> 6; 497 | } 498 | 499 | /* start opcode implementations */ 500 | 501 | static void op_wait(Mips * emu,uint32_t op) { 502 | emu->waiting = 1; 503 | } 504 | 505 | static void op_syscall(Mips * emu,uint32_t op) { 506 | emu->exceptionOccured = 1; 507 | setExceptionCode(emu,EXC_SYS); 508 | } 509 | 510 | static void op_sync(Mips * emu,uint32_t op) { 511 | /* hopefully nothing needed */ 512 | } 513 | 514 | static void op_swl(Mips * emu,uint32_t op) { 515 | int32_t c = (int16_t)(op&0x0000ffff); 516 | uint32_t addr = ((int32_t)getRs(emu,op)+c); 517 | uint32_t rtVal = getRt(emu,op); 518 | uint32_t wordVal = readVirtWord(emu,addr&(~3)); 519 | if(emu->exceptionOccured) { 520 | return; 521 | } 522 | uint32_t offset = addr&3; 523 | uint32_t result; 524 | 525 | switch(offset) { 526 | case 0: 527 | result = rtVal; 528 | break; 529 | 530 | case 1: 531 | result = (wordVal&0xff000000) | ((rtVal>>8)&0xffffff); 532 | break; 533 | 534 | case 2: 535 | result = (wordVal&0xffff0000) | ((rtVal>>16)&0xffff); 536 | break; 537 | 538 | case 3: 539 | result = (wordVal&0xffffff00) | ((rtVal>>24)&0xff); 540 | break; 541 | } 542 | 543 | writeVirtWord(emu,addr&(~3),result); 544 | } 545 | 546 | static void op_lwl(Mips * emu,uint32_t op) { 547 | int32_t c = (int16_t)(op&0x0000ffff); 548 | uint32_t addr = (int32_t)getRs(emu,op)+c; 549 | uint32_t rtVal = getRt(emu,op); 550 | uint32_t wordVal = readVirtWord(emu,addr&(~3)); 551 | if(emu->exceptionOccured) { 552 | return; 553 | } 554 | uint32_t offset = addr % 4; 555 | uint32_t result; 556 | 557 | switch(offset) { 558 | case 0: 559 | result = wordVal; 560 | break; 561 | case 1: 562 | result = ((wordVal << 8) | (rtVal & 0xff)); 563 | break; 564 | case 2: 565 | result = ((wordVal << 16) | (rtVal & 0xffff)); 566 | break; 567 | case 3: 568 | result = ((wordVal << 24) | (rtVal & 0xffffff)); 569 | break; 570 | } 571 | 572 | setRt(emu,op,result); 573 | } 574 | 575 | static void op_bne(Mips * emu,uint32_t op) { 576 | int32_t offset = sext18(getImm(op) * 4); 577 | if (getRs(emu,op) != getRt(emu,op) ) { 578 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 579 | } else { 580 | emu->delaypc = emu->pc + 8; 581 | } 582 | emu->inDelaySlot = 1; 583 | } 584 | 585 | 586 | static void op_bnel(Mips * emu,uint32_t op) { 587 | int32_t offset = sext18(getImm(op) * 4); 588 | if (getRs(emu,op) != getRt(emu,op) ) { 589 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 590 | emu->inDelaySlot = 1; 591 | } else { 592 | emu->pc += 4; 593 | } 594 | } 595 | 596 | static void op_tne(Mips * emu,uint32_t op) { 597 | if (getRs(emu,op) != getRt(emu,op) ) { 598 | puts("unhandled trap!"); 599 | exit(1); 600 | } 601 | } 602 | 603 | static void op_andi(Mips * emu,uint32_t op) { 604 | uint32_t v = getRs(emu,op) & getImm(op); 605 | setRt(emu,op,v); 606 | } 607 | 608 | static void op_jal(Mips * emu,uint32_t op) { 609 | uint32_t pc = emu->pc; 610 | uint32_t top = pc&0xf0000000; 611 | uint32_t addr = (top|((op&0x3ffffff) << 2)); 612 | emu->delaypc = addr; 613 | emu->regs[31] = pc + 8; 614 | emu->inDelaySlot = 1; 615 | } 616 | 617 | static void op_sb(Mips * emu,uint32_t op) { 618 | uint32_t addr = (int32_t)getRs(emu,op) + (int16_t)getImm(op); 619 | writeVirtByte(emu,addr,getRt(emu,op)&0xff); 620 | } 621 | 622 | static void op_lb(Mips * emu,uint32_t op) { 623 | uint32_t addr = ((int32_t)getRs(emu,op) + (int16_t)getImm(op)); 624 | int8_t v = (int8_t)readVirtByte(emu,addr); 625 | if(emu->exceptionOccured) { 626 | return; 627 | } 628 | setRt(emu,op,(int32_t)v); 629 | } 630 | 631 | static void op_beq(Mips * emu,uint32_t op) { 632 | int32_t offset = sext18(getImm(op) * 4); 633 | if (getRs(emu,op) == getRt(emu,op) ) { 634 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 635 | } else { 636 | emu->delaypc = emu->pc + 8; 637 | } 638 | emu->inDelaySlot = 1; 639 | } 640 | 641 | static void op_beql(Mips * emu,uint32_t op) { 642 | int32_t offset = sext18(getImm(op) * 4); 643 | if (getRs(emu,op) == getRt(emu,op) ) { 644 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 645 | emu->inDelaySlot = 1; 646 | } else { 647 | emu->pc = emu->pc += 4; 648 | } 649 | } 650 | 651 | static void op_lbu(Mips * emu,uint32_t op) { 652 | uint32_t addr = ((int32_t)getRs(emu,op) + (int16_t)getImm(op)); 653 | uint32_t v = readVirtByte(emu,addr); 654 | if(emu->exceptionOccured) { 655 | return; 656 | } 657 | setRt(emu,op,v); 658 | } 659 | 660 | static void op_lw(Mips * emu,uint32_t op) { 661 | int16_t offset = getImm(op); 662 | uint32_t addr = ((int32_t)getRs(emu,op) + offset); 663 | uint32_t v = readVirtWord(emu,addr); 664 | if(emu->exceptionOccured) { 665 | return; 666 | } 667 | setRt(emu,op,v); 668 | } 669 | 670 | static void op_j(Mips * emu,uint32_t op) { 671 | uint32_t top = emu->pc&0xf0000000; 672 | uint32_t addr = top|((op&0x3ffffff)*4); 673 | emu->delaypc = addr; 674 | emu->inDelaySlot = 1; 675 | } 676 | 677 | static void op_sh(Mips * emu,uint32_t op) { 678 | int16_t offset = getImm(op); 679 | uint32_t addr = (int32_t)getRs(emu,op) + offset; 680 | uint8_t vlo = getRt(emu,op)&0xff; 681 | uint8_t vhi = (getRt(emu,op)&0xff00)>>8; 682 | writeVirtByte(emu,addr,vhi); 683 | if(emu->exceptionOccured) { 684 | return; 685 | } 686 | writeVirtByte(emu,addr+1,vlo); 687 | } 688 | 689 | static void op_slti(Mips * emu,uint32_t op) { 690 | int32_t rs = getRs(emu,op); 691 | int32_t c = (int16_t)getImm(op); 692 | if ( rs < c ) { 693 | setRt(emu,op,1); 694 | } else { 695 | setRt(emu,op,0); 696 | } 697 | } 698 | 699 | static void op_sltiu(Mips * emu,uint32_t op) { 700 | uint32_t rs = getRs(emu,op); 701 | uint32_t c = (uint32_t)(int32_t)(int16_t)getImm(op); 702 | if ( rs < c ) { 703 | setRt(emu,op,1); 704 | } else { 705 | setRt(emu,op,0); 706 | } 707 | } 708 | 709 | static void op_addiu(Mips * emu,uint32_t op) { 710 | int32_t v = (int32_t)getRs(emu,op) + (int16_t)(getImm(op)); 711 | setRt(emu,op,(uint32_t)v); 712 | } 713 | 714 | static void op_xori(Mips * emu,uint32_t op) { 715 | uint32_t v = getRs(emu,op) ^ getImm(op); 716 | setRt(emu,op,v); 717 | } 718 | 719 | static void op_ori(Mips * emu,uint32_t op) { 720 | uint32_t v = getRs(emu,op) | getImm(op); 721 | setRt(emu,op,v); 722 | } 723 | 724 | static void op_swr(Mips * emu,uint32_t op) { 725 | int32_t c = (int16_t)(op&0x0000ffff); 726 | uint32_t addr = (int32_t)getRs(emu,op)+c; 727 | uint32_t rtVal = getRt(emu,op); 728 | uint32_t wordVal = readVirtWord(emu,addr&(~3)); 729 | if(emu->exceptionOccured) { 730 | return; 731 | } 732 | uint32_t offset = addr&3; 733 | uint32_t result; 734 | 735 | switch(offset) { 736 | case 3: 737 | result = rtVal; 738 | break; 739 | 740 | case 2: 741 | result = ((rtVal<<8) & 0xffffff00) | (wordVal&0xff); 742 | break; 743 | 744 | case 1: 745 | result = ((rtVal<<16) & 0xffff0000) | (wordVal&0xffff); 746 | break; 747 | 748 | case 0: 749 | result = ((rtVal<<24) & 0xff000000) | (wordVal&0xffffff); 750 | break; 751 | } 752 | writeVirtWord(emu,addr&(~3),result); 753 | } 754 | 755 | static void op_sw(Mips * emu,uint32_t op) { 756 | int16_t offset = getImm(op); 757 | uint32_t addr = ((int32_t)getRs(emu,op) + offset); 758 | writeVirtWord(emu,addr,getRt(emu,op)); 759 | } 760 | 761 | static void op_lh(Mips * emu,uint32_t op) { 762 | uint32_t addr = (int32_t)getRs(emu,op) + (int16_t)getImm(op); 763 | uint8_t vlo = readVirtByte(emu,addr+1); 764 | if(emu->exceptionOccured) { 765 | return; 766 | } 767 | uint8_t vhi = readVirtByte(emu,addr); 768 | if(emu->exceptionOccured) { 769 | return; 770 | } 771 | uint32_t v = (int32_t)(int16_t)((vhi<<8)| vlo); 772 | setRt(emu,op,v); 773 | } 774 | 775 | static void op_lui(Mips * emu,uint32_t op) { 776 | uint32_t v = getImm(op) << 16; 777 | setRt(emu,op,v); 778 | } 779 | 780 | static void op_addi(Mips * emu,uint32_t op) { 781 | uint32_t v = (int32_t)getRs(emu,op) + (int16_t)getImm(op); 782 | setRt(emu,op,v); 783 | } 784 | 785 | static void op_lhu(Mips * emu,uint32_t op) { 786 | uint32_t addr = (int32_t)getRs(emu,op) + (int16_t)getImm(op); 787 | uint8_t vlo = readVirtByte(emu,addr+1); 788 | if(emu->exceptionOccured) { 789 | return; 790 | } 791 | uint8_t vhi = readVirtByte(emu,addr); 792 | if(emu->exceptionOccured) { 793 | return; 794 | } 795 | uint32_t v = (vhi<<8)| vlo; 796 | setRt(emu,op,v); 797 | } 798 | 799 | static void op_bgtz(Mips * emu,uint32_t op) { 800 | int32_t offset = sext18(getImm(op) * 4); 801 | if (((int32_t)getRs(emu,op)) > 0) { 802 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 803 | } else { 804 | emu->delaypc = emu->pc + 8; 805 | } 806 | emu->inDelaySlot = 1; 807 | } 808 | 809 | static void op_bgtzl(Mips * emu,uint32_t op) { 810 | int32_t offset = sext18(getImm(op) * 4); 811 | if (((int32_t)getRs(emu,op)) > 0) { 812 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 813 | emu->inDelaySlot = 1; 814 | } else { 815 | emu->pc += 4; 816 | } 817 | 818 | } 819 | 820 | static void op_blez(Mips * emu,uint32_t op) { 821 | int32_t offset = sext18(getImm(op) * 4); 822 | if (((int32_t)getRs(emu,op)) <= 0) { 823 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 824 | } else { 825 | emu->delaypc = emu->pc + 8; 826 | } 827 | emu->inDelaySlot = 1; 828 | } 829 | 830 | static void op_blezl(Mips * emu,uint32_t op) { 831 | int32_t offset = sext18(getImm(op) * 4); 832 | if (((int32_t)getRs(emu,op)) <= 0) { 833 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 834 | emu->inDelaySlot = 1; 835 | } else { 836 | emu->pc += 4; 837 | } 838 | } 839 | 840 | static void op_lwr(Mips * emu,uint32_t op) { 841 | int32_t c = (int16_t)(op&0x0000ffff); 842 | uint32_t addr = ((int32_t)getRs(emu,op))+c; 843 | uint32_t rtVal = getRt(emu,op); 844 | uint32_t wordVal = readVirtWord(emu,addr & (~3)); 845 | if(emu->exceptionOccured) { 846 | return; 847 | } 848 | uint32_t offset = addr & 3; 849 | uint32_t result; 850 | 851 | switch (offset) { 852 | case 3: 853 | result = wordVal; 854 | break; 855 | 856 | case 2: 857 | result = ((rtVal & 0xff000000) | (wordVal >> 8)); 858 | break; 859 | 860 | case 1: 861 | result = ((rtVal & 0xffff0000) | (wordVal >> 16)); 862 | break; 863 | 864 | case 0: 865 | result = ((rtVal & 0xffffff00) | (wordVal >> 24)); 866 | break; 867 | } 868 | setRt(emu,op,result); 869 | } 870 | 871 | static void op_mfc0(Mips * emu,uint32_t op) { 872 | uint32_t regNum = (op&0xf800) >> 11; 873 | uint32_t sel = op & 7; 874 | uint32_t retval; 875 | switch(regNum) { 876 | 877 | case 0: // Index 878 | if(sel != 0) { 879 | goto unhandled; 880 | } 881 | retval = emu->CP0_Index; 882 | break; 883 | case 2: //EntryLo0 884 | retval = emu->CP0_EntryLo0; 885 | break; 886 | 887 | case 3://EntryLo1 888 | retval = emu->CP0_EntryLo1; 889 | break; 890 | 891 | case 4: // Context 892 | if(sel != 0) { 893 | goto unhandled; 894 | } 895 | retval = emu->CP0_Context; 896 | break; 897 | 898 | case 5: // Page Mask 899 | if(sel != 0) { 900 | goto unhandled; 901 | } 902 | retval = emu->CP0_PageMask; 903 | break; 904 | 905 | case 6: // Wired 906 | if(sel != 0) { 907 | goto unhandled; 908 | } 909 | retval = emu->CP0_Wired; 910 | break; 911 | 912 | 913 | case 8: // BadVAddr 914 | if(sel != 0) { 915 | goto unhandled; 916 | } 917 | retval = emu->CP0_BadVAddr; 918 | break; 919 | 920 | case 9: // Count 921 | if(sel != 0) { 922 | goto unhandled; 923 | } 924 | retval = emu->CP0_Count; 925 | break; 926 | 927 | case 10: // EntryHi 928 | if (sel != 0 ) { 929 | goto unhandled; 930 | } 931 | retval = emu->CP0_EntryHi; 932 | break; 933 | 934 | case 11: // Compare 935 | if(sel != 0) { 936 | goto unhandled; 937 | } 938 | retval = emu->CP0_Compare; 939 | break; 940 | 941 | case 12: // Status 942 | if (sel != 0 ) { 943 | goto unhandled; 944 | } 945 | retval = emu->CP0_Status; 946 | break; 947 | 948 | case 13: 949 | if (sel != 0 ) { 950 | goto unhandled; 951 | } 952 | retval = emu->CP0_Cause; 953 | break; 954 | 955 | 956 | case 14: // EPC 957 | if (sel != 0 ) { 958 | goto unhandled; 959 | } 960 | retval = emu->CP0_Epc; 961 | break; 962 | 963 | case 15: 964 | retval = 0x00018000; //processor id, just copied qemu 4kc 965 | break; 966 | case 16: 967 | if (sel == 0) { 968 | retval = 0x80008082; // XXX cacheability fields shouldnt be hardcoded as writeable 969 | break; 970 | } 971 | if (sel == 1) { 972 | retval = 0x1e190c8a; 973 | break; 974 | } 975 | goto unhandled; 976 | 977 | case 18: 978 | case 19: 979 | retval = 0; 980 | break; 981 | 982 | default: 983 | unhandled: 984 | printf("unhandled cp0 reg selector in mfc0 %d %d\n",regNum,sel); 985 | exit(1); 986 | } 987 | 988 | setRt(emu,op,retval); 989 | } 990 | 991 | static void op_mtc0(Mips * emu,uint32_t op) { 992 | uint32_t rt = getRt(emu,op); 993 | uint32_t regNum = (op&0xf800) >> 11; 994 | uint32_t sel = op & 7; 995 | 996 | switch(regNum) { 997 | 998 | case 0: // Index 999 | if(sel != 0) { 1000 | goto unhandled; 1001 | } 1002 | 1003 | emu->CP0_Index = (emu->CP0_Index & 0x80000000 ) | (rt & 0xf); 1004 | break; 1005 | case 2: // EntryLo0 1006 | emu->CP0_EntryLo0 = (rt & 0x3ffffff); 1007 | break; 1008 | 1009 | case 3: // EntryLo1 1010 | emu->CP0_EntryLo1 = (rt & 0x3ffffff); 1011 | break; 1012 | 1013 | case 4: // Context 1014 | emu->CP0_Context &= (emu->CP0_Context & ~0xff800000 ) | (rt & 0xff800000 ); 1015 | break; 1016 | 1017 | case 5: // Page Mask 1018 | if(sel != 0) { 1019 | goto unhandled; 1020 | } 1021 | if (rt) { 1022 | puts("untested page mask!"); 1023 | exit(1); 1024 | } 1025 | if (rt != 0) { 1026 | puts("XXX unhandled page mask"); 1027 | exit(1); 1028 | } 1029 | emu->CP0_PageMask = rt & 0x1ffe000; 1030 | break; 1031 | 1032 | case 6: // Wired 1033 | if(sel != 0) { 1034 | goto unhandled; 1035 | } 1036 | emu->CP0_Wired = rt & 0xf; 1037 | break; 1038 | 1039 | case 9: // Count 1040 | if(sel != 0) { 1041 | goto unhandled; 1042 | } 1043 | emu->CP0_Count = rt; 1044 | break; 1045 | 1046 | case 10: // EntryHi 1047 | if (sel != 0 ) { 1048 | goto unhandled; 1049 | } 1050 | emu->CP0_EntryHi = rt & (~0x1f00); 1051 | break; 1052 | 1053 | case 11: // Compare 1054 | if(sel != 0) { 1055 | goto unhandled; 1056 | } 1057 | clearExternalInterrupt(emu,5); 1058 | emu->CP0_Compare = rt; 1059 | break; 1060 | 1061 | case 12: // Status 1062 | if (sel != 0 ) { 1063 | goto unhandled; 1064 | } 1065 | uint32_t status_mask = 0x7d7cff17; 1066 | emu->CP0_Status = (emu->CP0_Status & ~status_mask ) | (rt & status_mask); 1067 | //XXX NMI is one way write 1068 | break; 1069 | 1070 | case 13: //cause 1071 | if(sel != 0) { 1072 | goto unhandled; 1073 | } 1074 | uint32_t cause_mask = ((1 << 23) | (1 << 22) | (3 << 8)); 1075 | emu->CP0_Cause = (emu->CP0_Cause & ~cause_mask ) | (rt & cause_mask); 1076 | break; 1077 | 1078 | case 14: //epc 1079 | if(sel != 0) { 1080 | goto unhandled; 1081 | } 1082 | emu->CP0_Epc = rt; 1083 | break; 1084 | 1085 | case 16: 1086 | break; 1087 | 1088 | case 18: 1089 | case 19: 1090 | break; 1091 | 1092 | default: 1093 | unhandled: 1094 | printf("unhandled cp0 reg selector in mtc0 %d %d\n",regNum,sel); 1095 | exit(1); 1096 | } 1097 | 1098 | } 1099 | 1100 | static void op_cache(Mips * emu, uint32_t op) { 1101 | /* noop? */ 1102 | } 1103 | 1104 | static void op_pref(Mips * emu, uint32_t op) { 1105 | /* noop? */ 1106 | } 1107 | 1108 | static void op_eret(Mips * emu, uint32_t op) { 1109 | 1110 | 1111 | if(emu->inDelaySlot){ 1112 | return; 1113 | } 1114 | 1115 | emu->llbit = 0; 1116 | 1117 | if(emu->CP0_Status & (1 << 2)) { //if ERL is set 1118 | emu->CP0_Status &= ~(1 << 2); //clear ERL; 1119 | emu->pc = emu->CP0_ErrorEpc; 1120 | } else { 1121 | emu->pc = emu->CP0_Epc; 1122 | emu->CP0_Status &= ~(1 << 1); //clear EXL; 1123 | } 1124 | emu->pc -= 4; //counteract typical pc += 4 1125 | } 1126 | 1127 | static void helper_writeTlbEntry(Mips * emu,uint32_t idx) { 1128 | //printf("tlb write idx %d\n",idx); 1129 | idx &= 0xf; //only 16 entries must mask it off 1130 | TLB_entry * tlbent = &emu->tlb.entries[idx]; 1131 | tlbent->VPN2 = emu->CP0_EntryHi >> 13; 1132 | tlbent->ASID = emu->CP0_EntryHi & 0xff; 1133 | tlbent->G = (emu->CP0_EntryLo0 & emu->CP0_EntryLo1) & 1; 1134 | tlbent->V0 = (emu->CP0_EntryLo0 & 2) > 0; 1135 | tlbent->V1 = (emu->CP0_EntryLo1 & 2) > 0; 1136 | tlbent->D0 = (emu->CP0_EntryLo0 & 4) > 0; 1137 | tlbent->D1 = (emu->CP0_EntryLo1 & 4) > 0; 1138 | tlbent->C0 = (emu->CP0_EntryLo0 >> 3) & 7; 1139 | tlbent->C1 = (emu->CP0_EntryLo1 >> 3) & 7; 1140 | tlbent->PFN[0] = ((emu->CP0_EntryLo0 >> 6) & 0xfffff) << 12; 1141 | tlbent->PFN[1] = ((emu->CP0_EntryLo1 >> 6) & 0xfffff) << 12; 1142 | } 1143 | 1144 | static void op_tlbwi(Mips * emu, uint32_t op) { 1145 | 1146 | uint32_t idx = emu->CP0_Index; 1147 | helper_writeTlbEntry(emu,idx); 1148 | 1149 | } 1150 | 1151 | static void op_tlbwr(Mips * emu, uint32_t op) { 1152 | uint32_t idx = randomInRange(emu->CP0_Wired,15); 1153 | helper_writeTlbEntry(emu,idx); 1154 | } 1155 | 1156 | //XXX hardcoded for 4k pages 1157 | static void op_tlbp(Mips * emu, uint32_t op) { 1158 | 1159 | uint8_t ASID = emu->CP0_EntryHi & 0xFF; 1160 | int i; 1161 | 1162 | emu->CP0_Index = 0x80000000; 1163 | 1164 | for (i = 0; i < 16; i++) { 1165 | TLB_entry *tlb_e = &emu->tlb.entries[i]; 1166 | uint32_t tag = (emu->CP0_EntryHi & 0xfffff000) >> 13; 1167 | uint32_t VPN2 = tlb_e->VPN2; 1168 | /* Check ASID, virtual page number & size */ 1169 | if ((tlb_e->G == 1 || tlb_e->ASID == ASID) && VPN2 == tag) { 1170 | /* TLB match */ 1171 | emu->CP0_Index = i; 1172 | return; 1173 | } 1174 | } 1175 | } 1176 | 1177 | static void op_sltu(Mips * emu,uint32_t op) { 1178 | uint32_t rs = getRs(emu,op); 1179 | uint32_t rt = getRt(emu,op); 1180 | if (rs < rt) { 1181 | setRd(emu,op,1); 1182 | } else { 1183 | setRd(emu,op,0); 1184 | } 1185 | } 1186 | 1187 | static void op_slt(Mips * emu,uint32_t op) { 1188 | int32_t rs = getRs(emu,op); 1189 | int32_t rt = getRt(emu,op); 1190 | if (rs < rt) { 1191 | setRd(emu,op,1); 1192 | } else { 1193 | setRd(emu,op,0); 1194 | } 1195 | } 1196 | 1197 | static void op_nor(Mips * emu,uint32_t op) { 1198 | setRd(emu,op,~(getRs(emu,op) | getRt(emu,op))); 1199 | } 1200 | 1201 | static void op_xor(Mips * emu,uint32_t op) { 1202 | setRd(emu,op,getRs(emu,op) ^ getRt(emu,op)); 1203 | } 1204 | 1205 | static void op_or(Mips * emu,uint32_t op) { 1206 | setRd(emu,op,getRs(emu,op) | getRt(emu,op)); 1207 | } 1208 | 1209 | static void op_and(Mips * emu,uint32_t op) { 1210 | setRd(emu,op,getRs(emu,op) & getRt(emu,op)); 1211 | } 1212 | 1213 | static void op_subu(Mips * emu,uint32_t op) { 1214 | setRd(emu,op,getRs(emu,op) - getRt(emu,op)); 1215 | } 1216 | 1217 | static void op_sub(Mips * emu,uint32_t op) { 1218 | setRd(emu,op,getRs(emu,op) - getRt(emu,op)); 1219 | } 1220 | 1221 | static void op_addu(Mips * emu,uint32_t op) { 1222 | setRd(emu,op,getRs(emu,op) + getRt(emu,op)); 1223 | } 1224 | 1225 | static void op_add(Mips * emu,uint32_t op) { 1226 | setRd(emu,op,getRs(emu,op) + getRt(emu,op)); 1227 | } 1228 | 1229 | 1230 | static void op_ll(Mips * emu,uint32_t op) { 1231 | int16_t offset = getImm(op); 1232 | uint32_t addr = ((int32_t)getRs(emu,op) + offset); 1233 | uint32_t v = readVirtWord(emu,addr); 1234 | if(emu->exceptionOccured) { 1235 | return; 1236 | } 1237 | emu->llbit = 1; 1238 | setRt(emu,op,v); 1239 | } 1240 | 1241 | static void op_sc(Mips * emu,uint32_t op) { 1242 | int16_t offset = getImm(op); 1243 | uint32_t addr = ((int32_t)getRs(emu,op) + offset); 1244 | if(emu->llbit) { 1245 | writeVirtWord(emu,addr,getRt(emu,op)); 1246 | } 1247 | setRt(emu,op,emu->llbit); 1248 | 1249 | } 1250 | 1251 | 1252 | static void op_divu(Mips * emu,uint32_t op) { 1253 | uint32_t rs = getRs(emu,op); 1254 | uint32_t rt = getRt(emu,op); 1255 | 1256 | if(rt == 0) { 1257 | return; 1258 | } 1259 | 1260 | emu->lo = rs / rt; 1261 | emu->hi = rs % rt; 1262 | 1263 | } 1264 | 1265 | static void op_div(Mips * emu,uint32_t op) { 1266 | int32_t rs = getRs(emu,op); 1267 | int32_t rt = getRt(emu,op); 1268 | 1269 | if(rt == 0) { 1270 | return; 1271 | } 1272 | 1273 | emu->lo = rs / rt; 1274 | emu->hi = rs % rt; 1275 | } 1276 | 1277 | static void op_multu(Mips * emu,uint32_t op) { 1278 | uint64_t result = (uint64_t)getRs(emu,op) * (uint64_t)getRt(emu,op); 1279 | emu->hi = result >> 32; 1280 | emu->lo = result & 0xffffffff; 1281 | } 1282 | 1283 | static void op_mult(Mips * emu,uint32_t op) { 1284 | int64_t result = (int64_t)(int32_t)getRs(emu,op) * (int64_t)(int32_t)getRt(emu,op); 1285 | emu->hi = result >> 32; 1286 | emu->lo = result & 0xffffffff; 1287 | } 1288 | 1289 | static void op_movz(Mips * emu,uint32_t op) { 1290 | if(getRt(emu,op) == 0) { 1291 | setRd(emu,op,getRs(emu,op)); 1292 | } 1293 | } 1294 | 1295 | static void op_movn(Mips * emu,uint32_t op) { 1296 | if(getRt(emu,op) != 0) { 1297 | setRd(emu,op,getRs(emu,op)); 1298 | } 1299 | } 1300 | 1301 | static void op_mul(Mips * emu,uint32_t op) { 1302 | int32_t result = (int32_t)getRs(emu,op) * (int32_t)getRt(emu,op); 1303 | setRd(emu,op,result); 1304 | } 1305 | 1306 | 1307 | static void op_mflo(Mips * emu,uint32_t op) { 1308 | setRd(emu,op,emu->lo); 1309 | } 1310 | 1311 | static void op_mfhi(Mips * emu,uint32_t op) { 1312 | setRd(emu,op,emu->hi); 1313 | } 1314 | 1315 | static void op_mtlo(Mips * emu,uint32_t op) { 1316 | emu->lo = getRs(emu,op); 1317 | } 1318 | 1319 | static void op_mthi(Mips * emu,uint32_t op) { 1320 | emu->hi = getRs(emu,op); 1321 | } 1322 | 1323 | static void op_jalr(Mips * emu,uint32_t op) { 1324 | emu->delaypc = getRs(emu,op); 1325 | emu->regs[31] = emu->pc + 8; 1326 | emu->inDelaySlot = 1; 1327 | } 1328 | 1329 | static void op_jr(Mips * emu,uint32_t op) { 1330 | emu->delaypc = getRs(emu,op); 1331 | emu->inDelaySlot = 1; 1332 | } 1333 | 1334 | static void op_srav(Mips * emu,uint32_t op) { 1335 | setRd(emu,op,((int32_t)getRt(emu,op) >> (getRs(emu,op)&0x1f))); 1336 | } 1337 | 1338 | static void op_srlv(Mips * emu,uint32_t op) { 1339 | setRd(emu,op,(getRt(emu,op) >> (getRs(emu,op)&0x1f))); 1340 | } 1341 | 1342 | static void op_sllv(Mips * emu,uint32_t op) { 1343 | setRd(emu,op,(getRt(emu,op) << (getRs(emu,op)&0x1f))); 1344 | } 1345 | 1346 | static void op_sra(Mips * emu,uint32_t op) { 1347 | int32_t rt = getRt(emu,op); 1348 | int32_t sa = getShamt(op); 1349 | int32_t v = rt >> sa; 1350 | setRd(emu,op,v); 1351 | } 1352 | 1353 | static void op_srl(Mips * emu,uint32_t op) { 1354 | uint32_t v = getRt(emu,op) >> getShamt(op); 1355 | setRd(emu,op,v); 1356 | } 1357 | 1358 | static void op_sll(Mips * emu,uint32_t op) { 1359 | uint32_t v = getRt(emu,op) << getShamt(op); 1360 | setRd(emu,op,v); 1361 | } 1362 | 1363 | static void op_bgezal(Mips * emu,uint32_t op) { 1364 | int32_t offset = sext18(getImm(op) * 4); 1365 | if (((int32_t)getRs(emu,op)) >= 0) { 1366 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 1367 | } else { 1368 | emu->delaypc = emu->pc + 8; 1369 | } 1370 | emu->regs[31] = emu->pc + 8; 1371 | emu->inDelaySlot = 1; 1372 | } 1373 | 1374 | static void op_bgez(Mips * emu,uint32_t op) { 1375 | int32_t offset = sext18(getImm(op) * 4); 1376 | if (((int32_t)getRs(emu,op)) >= 0) { 1377 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 1378 | } else { 1379 | emu->delaypc = emu->pc + 8; 1380 | } 1381 | emu->inDelaySlot = 1; 1382 | } 1383 | 1384 | static void op_bgezl(Mips * emu,uint32_t op) { 1385 | int32_t offset = sext18(getImm(op) * 4); 1386 | if (((int32_t)getRs(emu,op)) >= 0) { 1387 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 1388 | emu->inDelaySlot = 1; 1389 | } else { 1390 | emu->pc += 4; 1391 | } 1392 | } 1393 | 1394 | static void op_bltzal(Mips * emu,uint32_t op) { 1395 | int32_t offset = sext18(getImm(op) * 4); 1396 | if (((int32_t)getRs(emu,op)) < 0) { 1397 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 1398 | } else { 1399 | emu->delaypc = emu->pc + 8; 1400 | } 1401 | emu->regs[31] = emu->pc + 8; 1402 | emu->inDelaySlot = 1; 1403 | } 1404 | 1405 | static void op_bltz(Mips * emu,uint32_t op) { 1406 | int32_t offset = sext18(getImm(op) * 4); 1407 | if (((int32_t)getRs(emu,op)) < 0) { 1408 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 1409 | } else { 1410 | emu->delaypc = emu->pc + 8; 1411 | } 1412 | emu->inDelaySlot = 1; 1413 | } 1414 | 1415 | static void op_bltzl(Mips * emu,uint32_t op) { 1416 | int32_t offset = sext18(getImm(op) * 4); 1417 | if (((int32_t)getRs(emu,op)) < 0) { 1418 | emu->delaypc = (int32_t)(emu->pc + 4) + offset; 1419 | emu->inDelaySlot = 1; 1420 | } else { 1421 | emu->pc += 4; 1422 | } 1423 | } 1424 | 1425 | #include "./gen/doop.gen.c" 1426 | 1427 | 1428 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "mips.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | pthread_mutex_t emu_mutex; 9 | 10 | int ttyraw() 11 | { 12 | int fd = STDIN_FILENO; 13 | struct termios oldtermios; 14 | /* Set terminal mode as follows: 15 | Noncanonical mode - turn off ICANON. 16 | Turn off signal-generation (ISIG) 17 | including BREAK character (BRKINT). 18 | Turn off any possible preprocessing of input (IEXTEN). 19 | Turn ECHO mode off. 20 | Disable CR-to-NL mapping on input. 21 | Disable input parity detection (INPCK). 22 | Disable stripping of eighth bit on input (ISTRIP). 23 | Disable flow control (IXON). 24 | Use eight bit characters (CS8). 25 | Disable parity checking (PARENB). 26 | Disable any implementation-dependent output processing (OPOST). 27 | One byte at a time input (MIN=1, TIME=0). 28 | */ 29 | struct termios newtermios; 30 | if(tcgetattr(fd, &oldtermios) < 0) 31 | return 1; 32 | newtermios = oldtermios; 33 | 34 | newtermios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); 35 | /* OK, why IEXTEN? If IEXTEN is on, the DISCARD character 36 | is recognized and is not passed to the process. This 37 | character causes output to be suspended until another 38 | DISCARD is received. The DSUSP character for job control, 39 | the LNEXT character that removes any special meaning of 40 | the following character, the REPRINT character, and some 41 | others are also in this category. 42 | */ 43 | 44 | newtermios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); 45 | /* If an input character arrives with the wrong parity, then INPCK 46 | is checked. If this flag is set, then IGNPAR is checked 47 | to see if input bytes with parity errors should be ignored. 48 | If it shouldn't be ignored, then PARMRK determines what 49 | character sequence the process will actually see. 50 | 51 | When we turn off IXON, the start and stop characters can be read. 52 | */ 53 | 54 | newtermios.c_cflag &= ~(CSIZE | PARENB); 55 | /* CSIZE is a mask that determines the number of bits per byte. 56 | PARENB enables parity checking on input and parity generation 57 | on output. 58 | */ 59 | 60 | newtermios.c_cflag |= CS8; 61 | /* Set 8 bits per character. */ 62 | 63 | newtermios.c_oflag &= ~(OPOST); 64 | /* This includes things like expanding tabs to spaces. */ 65 | 66 | newtermios.c_cc[VMIN] = 1; 67 | newtermios.c_cc[VTIME] = 0; 68 | 69 | /* You tell me why TCSAFLUSH. */ 70 | if(tcsetattr(fd, TCSAFLUSH, &newtermios) < 0) 71 | return -1; 72 | return 0; 73 | } 74 | 75 | static void printstate(Mips * emu,uint64_t n) { 76 | int i; 77 | fprintf(stderr,"State: %lu\n",n); 78 | for(i = 0; i < 32; i++){ 79 | fprintf(stderr,"%s%d: %08x\n","gr",i,emu->regs[i]); 80 | } 81 | 82 | #define PRFIELD(X) fprintf(stderr, #X ": %08x\n",emu->X); 83 | PRFIELD(hi); 84 | PRFIELD(lo); 85 | PRFIELD(pc); 86 | PRFIELD(delaypc); 87 | PRFIELD(CP0_Index); 88 | PRFIELD(CP0_EntryHi); 89 | PRFIELD(CP0_EntryLo0); 90 | PRFIELD(CP0_EntryLo1); 91 | PRFIELD(CP0_Context); 92 | PRFIELD(CP0_Wired); 93 | PRFIELD(CP0_Status); 94 | PRFIELD(CP0_Epc); 95 | PRFIELD(CP0_BadVAddr); 96 | PRFIELD(CP0_ErrorEpc); 97 | PRFIELD(CP0_Cause); 98 | PRFIELD(CP0_PageMask); 99 | PRFIELD(CP0_Count); 100 | PRFIELD(CP0_Compare); 101 | #undef PRFIELD 102 | 103 | } 104 | 105 | void * runEmulator(void * p) { 106 | Mips * emu = (Mips *)p; 107 | 108 | while(emu->shutdown != 1) { 109 | int i; 110 | 111 | if(pthread_mutex_lock(&emu_mutex)) { 112 | puts("mutex failed lock, exiting"); 113 | exit(1); 114 | } 115 | 116 | for(i = 0; i < 1000 ; i++) { 117 | step_mips(emu); 118 | } 119 | 120 | if(pthread_mutex_unlock(&emu_mutex)) { 121 | puts("mutex failed unlock, exiting"); 122 | exit(1); 123 | } 124 | 125 | } 126 | free_mips(emu); 127 | exit(0); 128 | 129 | } 130 | 131 | 132 | 133 | 134 | int main(int argc,char * argv[]) { 135 | 136 | pthread_t emu_thread; 137 | 138 | if(pthread_mutex_init(&emu_mutex,NULL)) { 139 | puts("failed to create mutex"); 140 | return 1; 141 | } 142 | 143 | if (argc < 2) { 144 | printf("Usage: %s image.srec\n",argv[0]); 145 | return 1; 146 | } 147 | 148 | Mips * emu = new_mips(64 * 1024 * 1024); 149 | 150 | if (!emu) { 151 | puts("allocating emu failed."); 152 | return 1; 153 | } 154 | 155 | if (loadSrecFromFile_mips(emu,argv[1]) != 0) { 156 | puts("failed loading srec"); 157 | return 1; 158 | } 159 | 160 | if(ttyraw()) { 161 | puts("failed to configure raw mode"); 162 | return 1; 163 | } 164 | 165 | if(pthread_create(&emu_thread,NULL,runEmulator,emu)) { 166 | puts("creating emulator thread failed!"); 167 | return 1; 168 | } 169 | 170 | while(1) { 171 | int c = getchar(); 172 | if(c == EOF) { 173 | exit(1); 174 | } 175 | 176 | if(pthread_mutex_lock(&emu_mutex)) { 177 | puts("mutex failed lock, exiting"); 178 | exit(1); 179 | } 180 | uart_RecieveChar(emu,c); 181 | if(pthread_mutex_unlock(&emu_mutex)) { 182 | puts("mutex failed unlock, exiting"); 183 | exit(1); 184 | } 185 | } 186 | 187 | 188 | return 0; 189 | } 190 | -------------------------------------------------------------------------------- /src/srec.c: -------------------------------------------------------------------------------- 1 | #include "mips.h" 2 | 3 | #include 4 | #include 5 | 6 | 7 | static int fnextChar(void * ud) { 8 | int c = fgetc((FILE*)ud); 9 | if(c == EOF) { 10 | return -1; 11 | } 12 | return c; 13 | } 14 | 15 | static int fisEof(void * ud) { 16 | return feof((FILE*)ud); 17 | } 18 | 19 | 20 | int loadSrecFromFile_mips(Mips * emu,char * fname) { 21 | 22 | void * ud = fopen(fname,"r"); 23 | 24 | if(!ud) { 25 | fprintf(stdout,"srec %s failed to open",fname); 26 | return 1; 27 | } 28 | 29 | SrecLoader loader; 30 | loader.nextChar = &fnextChar; 31 | loader.isEof = &fisEof; 32 | loader.userdata = ud; 33 | int ret = loadSrec_mips(emu,&loader); 34 | fclose((FILE*)ud); 35 | return ret; 36 | } 37 | 38 | 39 | static int snextChar(void * ud) { 40 | int c = **(char**)ud; 41 | (*(char**)ud)++; 42 | if(c == 0) { 43 | return -1; 44 | } 45 | return c; 46 | } 47 | 48 | static int sisEof(void * ud) { 49 | return (**(char**)ud) == 0; 50 | } 51 | 52 | int loadSrecFromString_mips(Mips * emu,char * srec) { 53 | 54 | void * ud = (void*)&srec; 55 | 56 | SrecLoader loader; 57 | loader.nextChar = &snextChar; 58 | loader.isEof = &sisEof; 59 | loader.userdata = ud; 60 | int ret = loadSrec_mips(emu,&loader); 61 | return ret; 62 | } 63 | 64 | 65 | //a minimized version of address translation which checks fixed map ranges 66 | static void writeb(Mips * emu,uint32_t addr,uint8_t v) { 67 | //printf("loading %02x to %08x\n",v,addr); 68 | 69 | if ( addr >= 0x80000000 && addr <= 0x9fffffff ) { 70 | addr -= 0x80000000; 71 | } else if ( addr >= 0xa0000000 && addr <= 0xbfffffff ) { 72 | addr -= 0xa0000000; 73 | } else { 74 | return; 75 | } 76 | 77 | if (addr > emu->pmemsz) { 78 | return; 79 | } 80 | 81 | uint32_t word = emu->mem[addr / 4]; 82 | unsigned int offset = addr % 4; 83 | int shamt = 8*(3 - offset); 84 | word = (word & ~(0xff << shamt)) | (v << shamt); 85 | emu->mem[addr / 4] = word; 86 | 87 | } 88 | 89 | static int isHexChar(char c){ 90 | if(c >= '0' && c <= '9') 91 | return 1; 92 | if(c >= 'a' && c <= 'f') 93 | return 1; 94 | if(c >= 'A' && c <= 'F') 95 | return 1; 96 | return 0; 97 | } 98 | 99 | static int srecReadType(SrecLoader * loader){ 100 | int c = loader->nextChar(loader->userdata); 101 | if(c == -1){ 102 | return -1; // 0 type srecord will trigger a skip, which will terminate 103 | } 104 | if(c != 'S'){ 105 | return -2; 106 | } 107 | c = loader->nextChar(loader->userdata) - '0'; 108 | if(c >= 0 && c <= 9) 109 | return c; 110 | return -2; 111 | } 112 | 113 | static int srecReadByte(SrecLoader * loader, uint8_t * count){ 114 | char chars[3]; 115 | int i; 116 | 117 | for(i = 0 ; i < 2; i++){ 118 | chars[i] = loader->nextChar(loader->userdata); 119 | if(!isHexChar(chars[i])) 120 | return 1; 121 | } 122 | chars[2] = 0; 123 | *count = (uint8_t) strtoul(&chars[0],0,16); 124 | return 0; 125 | } 126 | 127 | static int srecReadAddress(SrecLoader * loader, uint32_t * addr){ 128 | char chars[9]; 129 | int i; 130 | 131 | for(i = 0 ; i < 8; i++){ 132 | chars[i] = loader->nextChar(loader->userdata); 133 | if(!isHexChar(chars[i])) 134 | return 1; 135 | } 136 | chars[8] = 0; 137 | 138 | *addr = strtoul(&chars[0],0,16); 139 | return 0; 140 | } 141 | 142 | static void srecSkipToNextLine(SrecLoader * loader){ 143 | while(1){ 144 | int c = loader->nextChar(loader->userdata); 145 | if(c == -1 || c == '\n'){ 146 | break; 147 | } 148 | } 149 | } 150 | 151 | static int srecLoadData(SrecLoader * loader,Mips * emu,uint32_t addr,uint32_t count){ 152 | 153 | uint8_t b; 154 | while(count--){ 155 | if(srecReadByte(loader,&b)){ 156 | return 1; 157 | } 158 | 159 | writeb(emu,addr++,b); 160 | } 161 | return 0; 162 | } 163 | 164 | int loadSrec_mips(Mips * emu, SrecLoader * loader) 165 | { 166 | 167 | uint32_t addr; 168 | uint8_t count; 169 | 170 | while(!loader->isEof(loader->userdata)){ 171 | switch(srecReadType(loader)){ 172 | 173 | case -1: 174 | //EOF 175 | break; 176 | case 0: 177 | srecSkipToNextLine(loader); 178 | break; 179 | case 3: 180 | if(srecReadByte(loader,&count)){ 181 | fputs("srecLoader: failed to parse bytecount.\n",stdout); 182 | return 1; 183 | } 184 | if(srecReadAddress(loader,&addr)){ 185 | fputs("srecLoader: failed to parse address.\n",stdout); 186 | return 1; 187 | } 188 | if(srecLoadData(loader,emu,addr,count-5)){ 189 | fputs("srecLoader: failed to load data.\n",stdout); 190 | return 1; 191 | } 192 | srecSkipToNextLine(loader); 193 | break; 194 | case 7: 195 | if(srecReadByte(loader,&count)){ 196 | fputs("srecLoader: failed to parse bytecount.\n",stdout); 197 | return 1; 198 | } 199 | if(srecReadAddress(loader,&addr)){ 200 | fputs("srecLoader: failed to parse address.\n",stdout); 201 | return 1; 202 | } 203 | emu->pc = addr; 204 | srecSkipToNextLine(loader); 205 | break; 206 | 207 | default: 208 | fputs("Bad/Unsupported srec type\n",stdout); 209 | return 1; 210 | } 211 | } 212 | 213 | return 0; 214 | } 215 | -------------------------------------------------------------------------------- /src/uart.c: -------------------------------------------------------------------------------- 1 | #include "mips.h" 2 | 3 | #include 4 | 5 | //XXX remove this when exit calls are removed... 6 | #include 7 | 8 | //XXX ThrowCTI NextInterrupt and ClearInterrupt recurse sometimes 9 | //seemingly pointlessly. 10 | 11 | /* some forward declarations */ 12 | 13 | static void uart_NextInterrupt(Mips * emu); 14 | static uint8_t uart_ReadReg8(Mips * emu ,uint32_t offset); 15 | static void uart_WriteReg8(Mips * emu,uint32_t offset, uint8_t x); 16 | 17 | uint32_t uart_read(Mips * emu,uint32_t offset){ 18 | return 0; 19 | } 20 | 21 | 22 | void uart_write(Mips * emu,uint32_t offset,uint32_t v){ 23 | 24 | } 25 | 26 | uint8_t uart_readb(Mips * emu,uint32_t offset){ 27 | uint8_t ret = uart_ReadReg8(emu,offset); 28 | //printf("serial readb %u %x\n",offset, ret); 29 | return ret; 30 | } 31 | 32 | 33 | void uart_writeb(Mips * emu,uint32_t offset,uint8_t v){ 34 | //printf("serial writeb %u %c\n",offset,v); 35 | uart_WriteReg8(emu,offset,v); 36 | } 37 | 38 | 39 | // code inspired by/ported from 40 | // https://raw.github.com/s-macke/jor1k/master/js/worker/uart.js 41 | // ------------------------------------------------- 42 | // -------------------- UART ----------------------- 43 | // ------------------------------------------------- 44 | 45 | #define UART_LSR_DATA_READY 0x1 46 | #define UART_LSR_FIFO_EMPTY 0x20 47 | #define UART_LSR_TRANSMITTER_EMPTY 0x40 48 | 49 | #define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ 50 | #define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ 51 | 52 | #define UART_IIR_MSI 0x00 /* Modem status interrupt (Low priority) */ 53 | #define UART_IIR_NO_INT 0x01 54 | #define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ 55 | #define UART_IIR_RDI 0x04 /* Receiver data interrupt */ 56 | #define UART_IIR_RLSI 0x06 /* Receiver line status interrupt (High p.) */ 57 | #define UART_IIR_CTI 0x0c /* Character timeout */ 58 | 59 | #define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ 60 | 61 | #define UART_DLL 0 /* R/W: Divisor Latch Low, DLAB=1 */ 62 | #define UART_DLH 1 /* R/W: Divisor Latch High, DLAB=1 */ 63 | 64 | #define UART_IER 1 /* R/W: Interrupt Enable Register */ 65 | #define UART_IIR 2 /* R: Interrupt ID Register */ 66 | #define UART_FCR 2 /* W: FIFO Control Register */ 67 | #define UART_LCR 3 /* R/W: Line Control Register */ 68 | #define UART_MCR 4 /* W: Modem Control Register */ 69 | #define UART_LSR 5 /* R: Line Status Register */ 70 | #define UART_MSR 6 /* R: Modem Status Register */ 71 | #define UART_SCR 7 /* R/W: */ 72 | 73 | /* FIFO code - used so we dont drop characters of input */ 74 | // dont call these directly, 75 | 76 | static int uart_fifoHasData(Mips * emu) { 77 | return emu->serial.fifoCount > 0; 78 | } 79 | 80 | static void uart_fifoPush(Mips * emu, uint8_t c) { 81 | emu->serial.fifo[emu->serial.fifoLast] = c; 82 | emu->serial.fifoLast = (emu->serial.fifoLast + 1) % 32; 83 | emu->serial.fifoCount++; 84 | if(emu->serial.fifoCount > 32) { 85 | emu->serial.fifoCount = 32; 86 | } 87 | } 88 | 89 | static uint8_t uart_fifoGet(Mips * emu) { 90 | 91 | uint8_t c; 92 | 93 | if(!uart_fifoHasData(emu)) { 94 | return 0; 95 | } 96 | 97 | c = emu->serial.fifo[emu->serial.fifoFirst]; 98 | emu->serial.fifoFirst = (emu->serial.fifoFirst + 1) % 32; 99 | 100 | if(emu->serial.fifoCount) { 101 | emu->serial.fifoCount--; 102 | } 103 | 104 | return c; 105 | } 106 | 107 | static void uart_fifoClear(Mips * emu) { 108 | emu->serial.fifoLast = 0; 109 | emu->serial.fifoFirst = 0; 110 | emu->serial.fifoCount = 0; 111 | } 112 | 113 | 114 | /* end Fifo code */ 115 | 116 | 117 | void uart_Reset(Mips * emu) { 118 | emu->serial.LCR = 3; 119 | emu->serial.LSR = UART_LSR_TRANSMITTER_EMPTY | UART_LSR_FIFO_EMPTY; 120 | emu->serial.MSR = 0; 121 | emu->serial.IIR = UART_IIR_NO_INT; 122 | emu->serial.IER = 0; 123 | emu->serial.DLL = 0; 124 | emu->serial.DLH = 0; 125 | emu->serial.FCR = 0; 126 | emu->serial.MCR = 0; 127 | emu->serial.SCR = 0; 128 | uart_fifoClear(emu); 129 | } 130 | 131 | static void uart_UpdateIrq (Mips * emu){ 132 | if ((emu->serial.LSR & UART_LSR_DATA_READY) && (emu->serial.IER & UART_IER_RDI)) { 133 | emu->serial.IIR = UART_IIR_RDI; 134 | } else if ((emu->serial.LSR & UART_LSR_FIFO_EMPTY) && (emu->serial.IER & UART_IER_THRI)) { 135 | emu->serial.IIR = UART_IIR_THRI; 136 | } else { 137 | emu->serial.IIR = UART_IIR_NO_INT; 138 | } 139 | 140 | //if there is an interrupt pending 141 | if (emu->serial.IIR != UART_IIR_NO_INT) { 142 | triggerExternalInterrupt(emu,0); 143 | } else { 144 | clearExternalInterrupt(emu,0); 145 | } 146 | }; 147 | 148 | void uart_RecieveChar(Mips * emu, uint8_t c) { 149 | uart_fifoPush(emu,c); 150 | emu->serial.LSR |= UART_LSR_DATA_READY; 151 | uart_UpdateIrq(emu); 152 | }; 153 | 154 | static uint8_t uart_ReadReg8(Mips * emu ,uint32_t offset) { 155 | 156 | uint8_t ret; 157 | 158 | offset &= 7; 159 | 160 | if (emu->serial.LCR & UART_LCR_DLAB) { 161 | switch (offset) { 162 | case UART_DLL: 163 | return emu->serial.DLL; 164 | case UART_DLH: 165 | return emu->serial.DLH; 166 | } 167 | } 168 | switch (offset) { 169 | case 0: 170 | ret = 0; 171 | if (uart_fifoHasData(emu)) { 172 | ret = uart_fifoGet(emu); 173 | emu->serial.LSR &= ~UART_LSR_DATA_READY; 174 | if (uart_fifoHasData(emu)) { 175 | emu->serial.LSR |= UART_LSR_DATA_READY; 176 | } 177 | } 178 | uart_UpdateIrq(emu); 179 | return ret; 180 | case UART_IER: 181 | return emu->serial.IER & 0x0F; 182 | case UART_MSR: 183 | return emu->serial.MSR; 184 | case UART_MCR: 185 | return emu->serial.MCR; 186 | case UART_IIR: 187 | ret = emu->serial.IIR; // the two top bits are always set 188 | return ret; 189 | case UART_LCR: 190 | return emu->serial.LCR; 191 | case UART_LSR: 192 | if (uart_fifoHasData(emu)) { 193 | emu->serial.LSR |= UART_LSR_DATA_READY; 194 | } else { 195 | emu->serial.LSR &= ~UART_LSR_DATA_READY; 196 | } 197 | return emu->serial.LSR; 198 | case UART_SCR: 199 | return emu->serial.SCR; 200 | default: 201 | printf("Error in uart ReadRegister: not supported %u\n",offset); 202 | exit(1); 203 | } 204 | }; 205 | 206 | static void uart_WriteReg8(Mips * emu,uint32_t offset, uint8_t x) { 207 | 208 | offset &= 7; 209 | 210 | if (emu->serial.LCR & UART_LCR_DLAB) { 211 | switch (offset) { 212 | case UART_DLL: 213 | emu->serial.DLL = x; 214 | return; 215 | case UART_DLH: 216 | emu->serial.DLH = x; 217 | return; 218 | } 219 | } 220 | 221 | switch (offset) { 222 | case 0: 223 | emu->serial.LSR &= ~UART_LSR_FIFO_EMPTY; 224 | if (emu->serial.MCR & (1 << 4)) { //LOOPBACK 225 | uart_RecieveChar(emu,x); 226 | } else { 227 | putchar(x); 228 | fflush(stdout); 229 | } 230 | // Data is sent with a latency of zero! 231 | emu->serial.LSR |= UART_LSR_FIFO_EMPTY; // send buffer is empty 232 | uart_UpdateIrq(emu); 233 | return; 234 | case UART_IER: 235 | // 2 = 10b ,5=101b, 7=111b 236 | emu->serial.IER = x & 0x0F; // only the first four bits are valid 237 | // Ok, check immediately if there is a interrupt pending 238 | uart_UpdateIrq(emu); 239 | break; 240 | case UART_FCR: 241 | emu->serial.FCR = x; 242 | if (emu->serial.FCR & 2) { 243 | uart_fifoClear(emu); 244 | } 245 | break; 246 | case UART_LCR: 247 | emu->serial.LCR = x; 248 | break; 249 | case UART_MCR: 250 | emu->serial.MCR = x; 251 | break; 252 | case UART_SCR: 253 | emu->serial.SCR = x; 254 | break; 255 | default: 256 | printf("Error in uart WriteRegister: not supported %u\n",offset); 257 | } 258 | }; 259 | 260 | --------------------------------------------------------------------------------