Hardware Design: SIE
Sign in or create your account | Project List | Help
Hardware Design: SIE Git Source Tree
Root/
| 1 | /*------------------------------------------------------------------- |
| 2 | -- TITLE: Plasma CPU in software. Executes MIPS(tm) opcodes. |
| 3 | -- AUTHOR: Steve Rhoads (rhoadss@yahoo.com) |
| 4 | -- DATE CREATED: 1/31/01 |
| 5 | -- FILENAME: mlite.c |
| 6 | -- PROJECT: Plasma CPU core |
| 7 | -- COPYRIGHT: Software placed into the public domain by the author. |
| 8 | -- Software 'as is' without warranty. Author liable for nothing. |
| 9 | -- DESCRIPTION: |
| 10 | -- Plasma CPU simulator in C code. |
| 11 | -- This file served as the starting point for the VHDL code. |
| 12 | -- Assumes running on a little endian PC. |
| 13 | --------------------------------------------------------------------*/ |
| 14 | #include <stdio.h> |
| 15 | #include <stdlib.h> |
| 16 | #include <string.h> |
| 17 | #include <ctype.h> |
| 18 | #include <assert.h> |
| 19 | |
| 20 | //#define ENABLE_CACHE |
| 21 | #define SIMPLE_CACHE |
| 22 | |
| 23 | #define MEM_SIZE (1024*1024*2) |
| 24 | #define ntohs(A) ( ((A)>>8) | (((A)&0xff)<<8) ) |
| 25 | #define htons(A) ntohs(A) |
| 26 | #define ntohl(A) ( ((A)>>24) | (((A)&0xff0000)>>8) | (((A)&0xff00)<<8) | ((A)<<24) ) |
| 27 | #define htonl(A) ntohl(A) |
| 28 | |
| 29 | #ifndef WIN32 |
| 30 | //Support for Linux |
| 31 | #define putch putchar |
| 32 | #include <termios.h> |
| 33 | #include <unistd.h> |
| 34 | |
| 35 | void Sleep(unsigned int value) |
| 36 | { |
| 37 | usleep(value * 1000); |
| 38 | } |
| 39 | |
| 40 | int kbhit(void) |
| 41 | { |
| 42 | struct termios oldt, newt; |
| 43 | struct timeval tv; |
| 44 | fd_set read_fd; |
| 45 | |
| 46 | tcgetattr(STDIN_FILENO, &oldt); |
| 47 | newt = oldt; |
| 48 | newt.c_lflag &= ~(ICANON | ECHO); |
| 49 | tcsetattr(STDIN_FILENO, TCSANOW, &newt); |
| 50 | tv.tv_sec=0; |
| 51 | tv.tv_usec=0; |
| 52 | FD_ZERO(&read_fd); |
| 53 | FD_SET(0,&read_fd); |
| 54 | if(select(1, &read_fd, NULL, NULL, &tv) == -1) |
| 55 | return 0; |
| 56 | //tcsetattr(STDIN_FILENO, TCSANOW, &oldt); |
| 57 | if(FD_ISSET(0,&read_fd)) |
| 58 | return 1; |
| 59 | return 0; |
| 60 | } |
| 61 | |
| 62 | int getch(void) |
| 63 | { |
| 64 | struct termios oldt, newt; |
| 65 | int ch; |
| 66 | |
| 67 | tcgetattr(STDIN_FILENO, &oldt); |
| 68 | newt = oldt; |
| 69 | newt.c_lflag &= ~(ICANON | ECHO); |
| 70 | tcsetattr(STDIN_FILENO, TCSANOW, &newt); |
| 71 | ch = getchar(); |
| 72 | //tcsetattr(STDIN_FILENO, TCSANOW, &oldt); |
| 73 | return ch; |
| 74 | } |
| 75 | #else |
| 76 | //Support for Windows |
| 77 | #include <conio.h> |
| 78 | extern void __stdcall Sleep(unsigned long value); |
| 79 | #endif |
| 80 | |
| 81 | #define UART_WRITE 0x20000000 |
| 82 | #define UART_READ 0x20000000 |
| 83 | #define IRQ_MASK 0x20000010 |
| 84 | #define IRQ_STATUS 0x20000020 |
| 85 | #define CONFIG_REG 0x20000070 |
| 86 | #define MMU_PROCESS_ID 0x20000080 |
| 87 | #define MMU_FAULT_ADDR 0x20000090 |
| 88 | #define MMU_TLB 0x200000a0 |
| 89 | |
| 90 | #define IRQ_UART_READ_AVAILABLE 0x001 |
| 91 | #define IRQ_UART_WRITE_AVAILABLE 0x002 |
| 92 | #define IRQ_COUNTER18_NOT 0x004 |
| 93 | #define IRQ_COUNTER18 0x008 |
| 94 | #define IRQ_MMU 0x200 |
| 95 | |
| 96 | #define MMU_ENTRIES 4 |
| 97 | #define MMU_MASK (1024*4-1) |
| 98 | |
| 99 | typedef struct |
| 100 | { |
| 101 | unsigned int virtualAddress; |
| 102 | unsigned int physicalAddress; |
| 103 | } MmuEntry; |
| 104 | |
| 105 | typedef struct { |
| 106 | int r[32]; |
| 107 | int pc, pc_next, epc; |
| 108 | unsigned int hi; |
| 109 | unsigned int lo; |
| 110 | int status; |
| 111 | int userMode; |
| 112 | int processId; |
| 113 | int exceptionId; |
| 114 | int faultAddr; |
| 115 | int irqStatus; |
| 116 | int skip; |
| 117 | unsigned char *mem; |
| 118 | int wakeup; |
| 119 | int big_endian; |
| 120 | MmuEntry mmuEntry[MMU_ENTRIES]; |
| 121 | } State; |
| 122 | |
| 123 | static char *opcode_string[]={ |
| 124 | "SPECIAL","REGIMM","J","JAL","BEQ","BNE","BLEZ","BGTZ", |
| 125 | "ADDI","ADDIU","SLTI","SLTIU","ANDI","ORI","XORI","LUI", |
| 126 | "COP0","COP1","COP2","COP3","BEQL","BNEL","BLEZL","BGTZL", |
| 127 | "?","?","?","?","?","?","?","?", |
| 128 | "LB","LH","LWL","LW","LBU","LHU","LWR","?", |
| 129 | "SB","SH","SWL","SW","?","?","SWR","CACHE", |
| 130 | "LL","LWC1","LWC2","LWC3","?","LDC1","LDC2","LDC3" |
| 131 | "SC","SWC1","SWC2","SWC3","?","SDC1","SDC2","SDC3" |
| 132 | }; |
| 133 | |
| 134 | static char *special_string[]={ |
| 135 | "SLL","?","SRL","SRA","SLLV","?","SRLV","SRAV", |
| 136 | "JR","JALR","MOVZ","MOVN","SYSCALL","BREAK","?","SYNC", |
| 137 | "MFHI","MTHI","MFLO","MTLO","?","?","?","?", |
| 138 | "MULT","MULTU","DIV","DIVU","?","?","?","?", |
| 139 | "ADD","ADDU","SUB","SUBU","AND","OR","XOR","NOR", |
| 140 | "?","?","SLT","SLTU","?","DADDU","?","?", |
| 141 | "TGE","TGEU","TLT","TLTU","TEQ","?","TNE","?", |
| 142 | "?","?","?","?","?","?","?","?" |
| 143 | }; |
| 144 | |
| 145 | static char *regimm_string[]={ |
| 146 | "BLTZ","BGEZ","BLTZL","BGEZL","?","?","?","?", |
| 147 | "TGEI","TGEIU","TLTI","TLTIU","TEQI","?","TNEI","?", |
| 148 | "BLTZAL","BEQZAL","BLTZALL","BGEZALL","?","?","?","?", |
| 149 | "?","?","?","?","?","?","?","?" |
| 150 | }; |
| 151 | |
| 152 | static unsigned int HWMemory[8]; |
| 153 | |
| 154 | |
| 155 | static int mem_read(State *s, int size, unsigned int address) |
| 156 | { |
| 157 | unsigned int value=0, ptr; |
| 158 | |
| 159 | s->irqStatus |= IRQ_UART_WRITE_AVAILABLE; |
| 160 | switch(address) |
| 161 | { |
| 162 | case UART_READ: |
| 163 | if(kbhit()) |
| 164 | HWMemory[0] = getch(); |
| 165 | s->irqStatus &= ~IRQ_UART_READ_AVAILABLE; //clear bit |
| 166 | return HWMemory[0]; |
| 167 | case IRQ_MASK: |
| 168 | return HWMemory[1]; |
| 169 | case IRQ_MASK + 4: |
| 170 | Sleep(10); |
| 171 | return 0; |
| 172 | case IRQ_STATUS: |
| 173 | if(kbhit()) |
| 174 | s->irqStatus |= IRQ_UART_READ_AVAILABLE; |
| 175 | return s->irqStatus; |
| 176 | case MMU_PROCESS_ID: |
| 177 | return s->processId; |
| 178 | case MMU_FAULT_ADDR: |
| 179 | return s->faultAddr; |
| 180 | } |
| 181 | |
| 182 | ptr = (unsigned int)s->mem + (address % MEM_SIZE); |
| 183 | |
| 184 | if(0x10000000 <= address && address < 0x10000000 + 1024*1024) |
| 185 | ptr += 1024*1024; |
| 186 | |
| 187 | switch(size) |
| 188 | { |
| 189 | case 4: |
| 190 | if(address & 3) |
| 191 | printf("Unaligned access PC=0x%x address=0x%x\n", (int)s->pc, (int)address); |
| 192 | assert((address & 3) == 0); |
| 193 | value = *(int*)ptr; |
| 194 | if(s->big_endian) |
| 195 | value = ntohl(value); |
| 196 | break; |
| 197 | case 2: |
| 198 | assert((address & 1) == 0); |
| 199 | value = *(unsigned short*)ptr; |
| 200 | if(s->big_endian) |
| 201 | value = ntohs((unsigned short)value); |
| 202 | break; |
| 203 | case 1: |
| 204 | value = *(unsigned char*)ptr; |
| 205 | break; |
| 206 | default: |
| 207 | printf("ERROR"); |
| 208 | } |
| 209 | return(value); |
| 210 | } |
| 211 | |
| 212 | static void mem_write(State *s, int size, int unsigned address, unsigned int value) |
| 213 | { |
| 214 | unsigned int ptr; |
| 215 | |
| 216 | switch(address) |
| 217 | { |
| 218 | case UART_WRITE: |
| 219 | putch(value); |
| 220 | fflush(stdout); |
| 221 | return; |
| 222 | case IRQ_MASK: |
| 223 | HWMemory[1] = value; |
| 224 | return; |
| 225 | case IRQ_STATUS: |
| 226 | s->irqStatus = value; |
| 227 | return; |
| 228 | case CONFIG_REG: |
| 229 | return; |
| 230 | case MMU_PROCESS_ID: |
| 231 | //printf("processId=%d\n", value); |
| 232 | s->processId = value; |
| 233 | return; |
| 234 | } |
| 235 | |
| 236 | if(MMU_TLB <= address && address <= MMU_TLB+MMU_ENTRIES * 8) |
| 237 | { |
| 238 | //printf("TLB 0x%x 0x%x\n", address - MMU_TLB, value); |
| 239 | ptr = (unsigned int)s->mmuEntry + address - MMU_TLB; |
| 240 | *(int*)ptr = value; |
| 241 | s->irqStatus &= ~IRQ_MMU; |
| 242 | return; |
| 243 | } |
| 244 | |
| 245 | ptr = (unsigned int)s->mem + (address % MEM_SIZE); |
| 246 | |
| 247 | if(0x10000000 <= address && address < 0x10000000 + 1024*1024) |
| 248 | ptr += 1024*1024; |
| 249 | |
| 250 | switch(size) |
| 251 | { |
| 252 | case 4: |
| 253 | assert((address & 3) == 0); |
| 254 | if(s->big_endian) |
| 255 | value = htonl(value); |
| 256 | *(int*)ptr = value; |
| 257 | break; |
| 258 | case 2: |
| 259 | assert((address & 1) == 0); |
| 260 | if(s->big_endian) |
| 261 | value = htons((unsigned short)value); |
| 262 | *(short*)ptr = (unsigned short)value; |
| 263 | break; |
| 264 | case 1: |
| 265 | *(char*)ptr = (unsigned char)value; |
| 266 | break; |
| 267 | default: |
| 268 | printf("ERROR"); |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | #ifdef ENABLE_CACHE |
| 273 | /************* Optional MMU and cache implementation *************/ |
| 274 | /* TAG = VirtualAddress | ProcessId | WriteableBit */ |
| 275 | unsigned int mmu_lookup(State *s, unsigned int processId, |
| 276 | unsigned int address, int write) |
| 277 | { |
| 278 | int i; |
| 279 | unsigned int compare, tag; |
| 280 | |
| 281 | if(processId == 0 || s->userMode == 0) |
| 282 | return address; |
| 283 | //if(address < 0x30000000) |
| 284 | // return address; |
| 285 | compare = (address & ~MMU_MASK) | (processId << 1); |
| 286 | for(i = 0; i < MMU_ENTRIES; ++i) |
| 287 | { |
| 288 | tag = s->mmuEntry[i].virtualAddress; |
| 289 | if((tag & ~1) == compare && (write == 0 || (tag & 1))) |
| 290 | return s->mmuEntry[i].physicalAddress | (address & MMU_MASK); |
| 291 | } |
| 292 | //printf("\nMMUTlbMiss 0x%x PC=0x%x w=%d pid=%d user=%d\n", |
| 293 | // address, s->pc, write, processId, s->userMode); |
| 294 | //printf("m"); |
| 295 | s->exceptionId = 1; |
| 296 | s->faultAddr = address & ~MMU_MASK; |
| 297 | s->irqStatus |= IRQ_MMU; |
| 298 | return address; |
| 299 | } |
| 300 | |
| 301 | |
| 302 | #define CACHE_SET_ASSOC_LN2 0 |
| 303 | #define CACHE_SET_ASSOC (1 << CACHE_SET_ASSOC_LN2) |
| 304 | #define CACHE_SIZE_LN2 (13 - CACHE_SET_ASSOC_LN2) //8 KB |
| 305 | #define CACHE_SIZE (1 << CACHE_SIZE_LN2) |
| 306 | #define CACHE_LINE_SIZE_LN2 2 //4 bytes |
| 307 | #define CACHE_LINE_SIZE (1 << CACHE_LINE_SIZE_LN2) |
| 308 | |
| 309 | static int cacheData[CACHE_SET_ASSOC][CACHE_SIZE/sizeof(int)]; |
| 310 | static int cacheAddr[CACHE_SET_ASSOC][CACHE_SIZE/CACHE_LINE_SIZE]; |
| 311 | static int cacheSetNext; |
| 312 | static int cacheMiss, cacheWriteBack, cacheCount; |
| 313 | |
| 314 | static void cache_init(void) |
| 315 | { |
| 316 | int set, i; |
| 317 | for(set = 0; set < CACHE_SET_ASSOC; ++set) |
| 318 | { |
| 319 | for(i = 0; i < CACHE_SIZE/CACHE_LINE_SIZE; ++i) |
| 320 | cacheAddr[set][i] = 0xffff0000; |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | /* Write-back cache memory tagged by virtual address and processId */ |
| 325 | /* TAG = virtualAddress | processId | dirtyBit */ |
| 326 | static int cache_load(State *s, unsigned int address, int write) |
| 327 | { |
| 328 | int set, i, pid, miss, offsetAddr, offsetData, offsetMem; |
| 329 | unsigned int addrTagMatch, addrPrevMatch=0; |
| 330 | unsigned int addrPrev; |
| 331 | unsigned int addressPhysical, tag; |
| 332 | |
| 333 | ++cacheCount; |
| 334 | addrTagMatch = address & ~(CACHE_SIZE-1); |
| 335 | offsetAddr = (address & (CACHE_SIZE-1)) >> CACHE_LINE_SIZE_LN2; |
| 336 | |
| 337 | /* Find match */ |
| 338 | miss = 1; |
| 339 | for(set = 0; set < CACHE_SET_ASSOC; ++set) |
| 340 | { |
| 341 | addrPrevMatch = cacheAddr[set][offsetAddr] & ~(CACHE_SIZE-1); |
| 342 | if(addrPrevMatch == addrTagMatch) |
| 343 | { |
| 344 | miss = 0; |
| 345 | break; |
| 346 | } |
| 347 | } |
| 348 | |
| 349 | /* Cache miss? */ |
| 350 | if(miss) |
| 351 | { |
| 352 | ++cacheMiss; |
| 353 | set = cacheSetNext; |
| 354 | cacheSetNext = (cacheSetNext + 1) & (CACHE_SET_ASSOC-1); |
| 355 | } |
| 356 | //else if(write || (address >> 28) != 0x1) |
| 357 | //{ |
| 358 | // tag = cacheAddr[set][offsetAddr]; |
| 359 | // pid = (tag & (CACHE_SIZE-1)) >> 1; |
| 360 | // if(pid != s->processId) |
| 361 | // miss = 1; |
| 362 | //} |
| 363 | |
| 364 | if(miss) |
| 365 | { |
| 366 | offsetData = address & (CACHE_SIZE-1) & ~(CACHE_LINE_SIZE-1); |
| 367 | |
| 368 | /* Cache line dirty? */ |
| 369 | if(cacheAddr[set][offsetAddr] & 1) |
| 370 | { |
| 371 | /* Write back cache line */ |
| 372 | tag = cacheAddr[set][offsetAddr]; |
| 373 | addrPrev = tag & ~(CACHE_SIZE-1); |
| 374 | addrPrev |= address & (CACHE_SIZE-1); |
| 375 | pid = (tag & (CACHE_SIZE-1)) >> 1; |
| 376 | addressPhysical = mmu_lookup(s, pid, addrPrev, 1); //virtual->physical |
| 377 | if(s->exceptionId) |
| 378 | return 0; |
| 379 | offsetMem = addressPhysical & ~(CACHE_LINE_SIZE-1); |
| 380 | for(i = 0; i < CACHE_LINE_SIZE; i += 4) |
| 381 | mem_write(s, 4, offsetMem + i, cacheData[set][(offsetData + i) >> 2]); |
| 382 | ++cacheWriteBack; |
| 383 | } |
| 384 | |
| 385 | /* Read cache line */ |
| 386 | addressPhysical = mmu_lookup(s, s->processId, address, write); //virtual->physical |
| 387 | if(s->exceptionId) |
| 388 | return 0; |
| 389 | offsetMem = addressPhysical & ~(CACHE_LINE_SIZE-1); |
| 390 | cacheAddr[set][offsetAddr] = addrTagMatch; |
| 391 | for(i = 0; i < CACHE_LINE_SIZE; i += 4) |
| 392 | cacheData[set][(offsetData + i) >> 2] = mem_read(s, 4, offsetMem + i); |
| 393 | } |
| 394 | cacheAddr[set][offsetAddr] |= write; |
| 395 | return set; |
| 396 | } |
| 397 | |
| 398 | static int cache_read(State *s, int size, unsigned int address) |
| 399 | { |
| 400 | int set, offset; |
| 401 | int value; |
| 402 | |
| 403 | if((address & 0xfe000000) != 0x10000000) |
| 404 | return mem_read(s, size, address); |
| 405 | |
| 406 | set = cache_load(s, address, 0); |
| 407 | if(s->exceptionId) |
| 408 | return 0; |
| 409 | offset = (address & (CACHE_SIZE-1)) >> 2; |
| 410 | value = cacheData[set][offset]; |
| 411 | if(s->big_endian) |
| 412 | address ^= 3; |
| 413 | switch(size) |
| 414 | { |
| 415 | case 2: |
| 416 | value = (value >> ((address & 2) << 3)) & 0xffff; |
| 417 | break; |
| 418 | case 1: |
| 419 | value = (value >> ((address & 3) << 3)) & 0xff; |
| 420 | break; |
| 421 | } |
| 422 | return value; |
| 423 | } |
| 424 | |
| 425 | static void cache_write(State *s, int size, int unsigned address, unsigned int value) |
| 426 | { |
| 427 | int set, offset; |
| 428 | unsigned int mask; |
| 429 | |
| 430 | if((address >> 28) != 0x1) // && (s->processId == 0 || s->userMode == 0)) |
| 431 | { |
| 432 | mem_write(s, size, address, value); |
| 433 | return; |
| 434 | } |
| 435 | |
| 436 | set = cache_load(s, address, 1); |
| 437 | if(s->exceptionId) |
| 438 | return; |
| 439 | offset = (address & (CACHE_SIZE-1)) >> 2; |
| 440 | if(s->big_endian) |
| 441 | address ^= 3; |
| 442 | switch(size) |
| 443 | { |
| 444 | case 2: |
| 445 | value &= 0xffff; |
| 446 | value |= value << 16; |
| 447 | mask = 0xffff << ((address & 2) << 3); |
| 448 | break; |
| 449 | case 1: |
| 450 | value &= 0xff; |
| 451 | value |= (value << 8) | (value << 16) | (value << 24); |
| 452 | mask = 0xff << ((address & 3) << 3); |
| 453 | break; |
| 454 | case 4: |
| 455 | default: |
| 456 | mask = 0xffffffff; |
| 457 | break; |
| 458 | } |
| 459 | cacheData[set][offset] = (value & mask) | (cacheData[set][offset] & ~mask); |
| 460 | } |
| 461 | |
| 462 | #define mem_read cache_read |
| 463 | #define mem_write cache_write |
| 464 | |
| 465 | #else |
| 466 | static void cache_init(void) {} |
| 467 | #endif |
| 468 | |
| 469 | |
| 470 | #ifdef SIMPLE_CACHE |
| 471 | |
| 472 | //Write through direct mapped 4KB cache |
| 473 | #define CACHE_MISS 0x1ff |
| 474 | static unsigned int cacheData[1024]; |
| 475 | static unsigned int cacheAddr[1024]; //9-bit addresses |
| 476 | static int cacheTry, cacheMiss, cacheInit; |
| 477 | |
| 478 | static int cache_read(State *s, int size, unsigned int address) |
| 479 | { |
| 480 | int offset; |
| 481 | unsigned int value, value2, address2=address; |
| 482 | |
| 483 | if(cacheInit == 0) |
| 484 | { |
| 485 | cacheInit = 1; |
| 486 | for(offset = 0; offset < 1024; ++offset) |
| 487 | cacheAddr[offset] = CACHE_MISS; |
| 488 | } |
| 489 | |
| 490 | offset = address >> 20; |
| 491 | if(offset != 0x100 && offset != 0x101) |
| 492 | return mem_read(s, size, address); |
| 493 | |
| 494 | ++cacheTry; |
| 495 | offset = (address >> 2) & 0x3ff; |
| 496 | if(cacheAddr[offset] != (address >> 12) || cacheAddr[offset] == CACHE_MISS) |
| 497 | { |
| 498 | ++cacheMiss; |
| 499 | cacheAddr[offset] = address >> 12; |
| 500 | cacheData[offset] = mem_read(s, 4, address & ~3); |
| 501 | } |
| 502 | value = cacheData[offset]; |
| 503 | if(s->big_endian) |
| 504 | address ^= 3; |
| 505 | switch(size) |
| 506 | { |
| 507 | case 2: |
| 508 | value = (value >> ((address & 2) << 3)) & 0xffff; |
| 509 | break; |
| 510 | case 1: |
| 511 | value = (value >> ((address & 3) << 3)) & 0xff; |
| 512 | break; |
| 513 | } |
| 514 | |
| 515 | //Debug testing |
| 516 | value2 = mem_read(s, size, address2); |
| 517 | if(value != value2) |
| 518 | printf("miss match\n"); |
| 519 | //if((cacheTry & 0xffff) == 0) printf("\n***cache(%d,%d)\n ", cacheMiss, cacheTry); |
| 520 | return value; |
| 521 | } |
| 522 | |
| 523 | static void cache_write(State *s, int size, int unsigned address, unsigned int value) |
| 524 | { |
| 525 | int offset; |
| 526 | |
| 527 | mem_write(s, size, address, value); |
| 528 | |
| 529 | offset = address >> 20; |
| 530 | if(offset != 0x100 && offset != 0x101) |
| 531 | return; |
| 532 | |
| 533 | offset = (address >> 2) & 0x3ff; |
| 534 | if(size != 4) |
| 535 | { |
| 536 | cacheAddr[offset] = CACHE_MISS; |
| 537 | return; |
| 538 | } |
| 539 | cacheAddr[offset] = address >> 12; |
| 540 | cacheData[offset] = value; |
| 541 | } |
| 542 | |
| 543 | #define mem_read cache_read |
| 544 | #define mem_write cache_write |
| 545 | #endif //SIMPLE_CACHE |
| 546 | /************* End optional cache implementation *************/ |
| 547 | |
| 548 | |
| 549 | void mult_big(unsigned int a, |
| 550 | unsigned int b, |
| 551 | unsigned int *hi, |
| 552 | unsigned int *lo) |
| 553 | { |
| 554 | unsigned int ahi, alo, bhi, blo; |
| 555 | unsigned int c0, c1, c2; |
| 556 | unsigned int c1_a, c1_b; |
| 557 | |
| 558 | ahi = a >> 16; |
| 559 | alo = a & 0xffff; |
| 560 | bhi = b >> 16; |
| 561 | blo = b & 0xffff; |
| 562 | |
| 563 | c0 = alo * blo; |
| 564 | c1_a = ahi * blo; |
| 565 | c1_b = alo * bhi; |
| 566 | c2 = ahi * bhi; |
| 567 | |
| 568 | c2 += (c1_a >> 16) + (c1_b >> 16); |
| 569 | c1 = (c1_a & 0xffff) + (c1_b & 0xffff) + (c0 >> 16); |
| 570 | c2 += (c1 >> 16); |
| 571 | c0 = (c1 << 16) + (c0 & 0xffff); |
| 572 | *hi = c2; |
| 573 | *lo = c0; |
| 574 | } |
| 575 | |
| 576 | void mult_big_signed(int a, |
| 577 | int b, |
| 578 | unsigned int *hi, |
| 579 | unsigned int *lo) |
| 580 | { |
| 581 | unsigned int ahi, alo, bhi, blo; |
| 582 | unsigned int c0, c1, c2; |
| 583 | unsigned int c1_a, c1_b; |
| 584 | |
| 585 | ahi = a >> 16; |
| 586 | alo = a & 0xffff; |
| 587 | bhi = b >> 16; |
| 588 | blo = b & 0xffff; |
| 589 | |
| 590 | c0 = alo * blo; |
| 591 | c1_a = ahi * blo; |
| 592 | c1_b = alo * bhi; |
| 593 | c2 = ahi * bhi; |
| 594 | |
| 595 | c2 += (c1_a >> 16) + (c1_b >> 16); |
| 596 | c1 = (c1_a & 0xffff) + (c1_b & 0xffff) + (c0 >> 16); |
| 597 | c2 += (c1 >> 16); |
| 598 | c0 = (c1 << 16) + (c0 & 0xffff); |
| 599 | *hi = c2; |
| 600 | *lo = c0; |
| 601 | } |
| 602 | |
| 603 | //execute one cycle of a Plasma CPU |
| 604 | void cycle(State *s, int show_mode) |
| 605 | { |
| 606 | unsigned int opcode; |
| 607 | unsigned int op, rs, rt, rd, re, func, imm, target; |
| 608 | int imm_shift, branch=0, lbranch=2, skip2=0; |
| 609 | int *r=s->r; |
| 610 | unsigned int *u=(unsigned int*)s->r; |
| 611 | unsigned int ptr, epc, rSave; |
| 612 | |
| 613 | opcode = mem_read(s, 4, s->pc); |
| 614 | op = (opcode >> 26) & 0x3f; |
| 615 | rs = (opcode >> 21) & 0x1f; |
| 616 | rt = (opcode >> 16) & 0x1f; |
| 617 | rd = (opcode >> 11) & 0x1f; |
| 618 | re = (opcode >> 6) & 0x1f; |
| 619 | func = opcode & 0x3f; |
| 620 | imm = opcode & 0xffff; |
| 621 | imm_shift = (((int)(short)imm) << 2) - 4; |
| 622 | target = (opcode << 6) >> 4; |
| 623 | ptr = (short)imm + r[rs]; |
| 624 | r[0] = 0; |
| 625 | if(show_mode) |
| 626 | { |
| 627 | printf("%8.8x %8.8x ", s->pc, opcode); |
| 628 | if(op == 0) |
| 629 | printf("%8s ", special_string[func]); |
| 630 | else if(op == 1) |
| 631 | printf("%8s ", regimm_string[rt]); |
| 632 | else |
| 633 | printf("%8s ", opcode_string[op]); |
| 634 | printf("$%2.2d $%2.2d $%2.2d $%2.2d ", rs, rt, rd, re); |
| 635 | printf("%4.4x", imm); |
| 636 | if(show_mode == 1) |
| 637 | printf(" r[%2.2d]=%8.8x r[%2.2d]=%8.8x", rs, r[rs], rt, r[rt]); |
| 638 | printf("\n"); |
| 639 | } |
| 640 | if(show_mode > 5) |
| 641 | return; |
| 642 | epc = s->pc + 4; |
| 643 | if(s->pc_next != s->pc + 4) |
| 644 | epc |= 2; //branch delay slot |
| 645 | s->pc = s->pc_next; |
| 646 | s->pc_next = s->pc_next + 4; |
| 647 | if(s->skip) |
| 648 | { |
| 649 | s->skip = 0; |
| 650 | return; |
| 651 | } |
| 652 | rSave = r[rt]; |
| 653 | switch(op) |
| 654 | { |
| 655 | case 0x00:/*SPECIAL*/ |
| 656 | switch(func) |
| 657 | { |
| 658 | case 0x00:/*SLL*/ r[rd]=r[rt]<<re; break; |
| 659 | case 0x02:/*SRL*/ r[rd]=u[rt]>>re; break; |
| 660 | case 0x03:/*SRA*/ r[rd]=r[rt]>>re; break; |
| 661 | case 0x04:/*SLLV*/ r[rd]=r[rt]<<r[rs]; break; |
| 662 | case 0x06:/*SRLV*/ r[rd]=u[rt]>>r[rs]; break; |
| 663 | case 0x07:/*SRAV*/ r[rd]=r[rt]>>r[rs]; break; |
| 664 | case 0x08:/*JR*/ s->pc_next=r[rs]; break; |
| 665 | case 0x09:/*JALR*/ r[rd]=s->pc_next; s->pc_next=r[rs]; break; |
| 666 | case 0x0a:/*MOVZ*/ if(!r[rt]) r[rd]=r[rs]; break; /*IV*/ |
| 667 | case 0x0b:/*MOVN*/ if(r[rt]) r[rd]=r[rs]; break; /*IV*/ |
| 668 | case 0x0c:/*SYSCALL*/ epc|=1; s->exceptionId=1; break; |
| 669 | case 0x0d:/*BREAK*/ epc|=1; s->exceptionId=1; break; |
| 670 | case 0x0f:/*SYNC*/ s->wakeup=1; break; |
| 671 | case 0x10:/*MFHI*/ r[rd]=s->hi; break; |
| 672 | case 0x11:/*FTHI*/ s->hi=r[rs]; break; |
| 673 | case 0x12:/*MFLO*/ r[rd]=s->lo; break; |
| 674 | case 0x13:/*MTLO*/ s->lo=r[rs]; break; |
| 675 | case 0x18:/*MULT*/ mult_big_signed(r[rs],r[rt],&s->hi,&s->lo); break; |
| 676 | case 0x19:/*MULTU*/ mult_big(r[rs],r[rt],&s->hi,&s->lo); break; |
| 677 | case 0x1a:/*DIV*/ s->lo=r[rs]/r[rt]; s->hi=r[rs]%r[rt]; break; |
| 678 | case 0x1b:/*DIVU*/ s->lo=u[rs]/u[rt]; s->hi=u[rs]%u[rt]; break; |
| 679 | case 0x20:/*ADD*/ r[rd]=r[rs]+r[rt]; break; |
| 680 | case 0x21:/*ADDU*/ r[rd]=r[rs]+r[rt]; break; |
| 681 | case 0x22:/*SUB*/ r[rd]=r[rs]-r[rt]; break; |
| 682 | case 0x23:/*SUBU*/ r[rd]=r[rs]-r[rt]; break; |
| 683 | case 0x24:/*AND*/ r[rd]=r[rs]&r[rt]; break; |
| 684 | case 0x25:/*OR*/ r[rd]=r[rs]|r[rt]; break; |
| 685 | case 0x26:/*XOR*/ r[rd]=r[rs]^r[rt]; break; |
| 686 | case 0x27:/*NOR*/ r[rd]=~(r[rs]|r[rt]); break; |
| 687 | case 0x2a:/*SLT*/ r[rd]=r[rs]<r[rt]; break; |
| 688 | case 0x2b:/*SLTU*/ r[rd]=u[rs]<u[rt]; break; |
| 689 | case 0x2d:/*DADDU*/r[rd]=r[rs]+u[rt]; break; |
| 690 | case 0x31:/*TGEU*/ break; |
| 691 | case 0x32:/*TLT*/ break; |
| 692 | case 0x33:/*TLTU*/ break; |
| 693 | case 0x34:/*TEQ*/ break; |
| 694 | case 0x36:/*TNE*/ break; |
| 695 | default: printf("ERROR0(*0x%x~0x%x)\n", s->pc, opcode); |
| 696 | s->wakeup=1; |
| 697 | } |
| 698 | break; |
| 699 | case 0x01:/*REGIMM*/ |
| 700 | switch(rt) { |
| 701 | case 0x10:/*BLTZAL*/ r[31]=s->pc_next; |
| 702 | case 0x00:/*BLTZ*/ branch=r[rs]<0; break; |
| 703 | case 0x11:/*BGEZAL*/ r[31]=s->pc_next; |
| 704 | case 0x01:/*BGEZ*/ branch=r[rs]>=0; break; |
| 705 | case 0x12:/*BLTZALL*/r[31]=s->pc_next; |
| 706 | case 0x02:/*BLTZL*/ lbranch=r[rs]<0; break; |
| 707 | case 0x13:/*BGEZALL*/r[31]=s->pc_next; |
| 708 | case 0x03:/*BGEZL*/ lbranch=r[rs]>=0; break; |
| 709 | default: printf("ERROR1\n"); s->wakeup=1; |
| 710 | } |
| 711 | break; |
| 712 | case 0x03:/*JAL*/ r[31]=s->pc_next; |
| 713 | case 0x02:/*J*/ s->pc_next=(s->pc&0xf0000000)|target; break; |
| 714 | case 0x04:/*BEQ*/ branch=r[rs]==r[rt]; break; |
| 715 | case 0x05:/*BNE*/ branch=r[rs]!=r[rt]; break; |
| 716 | case 0x06:/*BLEZ*/ branch=r[rs]<=0; break; |
| 717 | case 0x07:/*BGTZ*/ branch=r[rs]>0; break; |
| 718 | case 0x08:/*ADDI*/ r[rt]=r[rs]+(short)imm; break; |
| 719 | case 0x09:/*ADDIU*/ u[rt]=u[rs]+(short)imm; break; |
| 720 | case 0x0a:/*SLTI*/ r[rt]=r[rs]<(short)imm; break; |
| 721 | case 0x0b:/*SLTIU*/ u[rt]=u[rs]<(unsigned int)(short)imm; break; |
| 722 | case 0x0c:/*ANDI*/ r[rt]=r[rs]&imm; break; |
| 723 | case 0x0d:/*ORI*/ r[rt]=r[rs]|imm; break; |
| 724 | case 0x0e:/*XORI*/ r[rt]=r[rs]^imm; break; |
| 725 | case 0x0f:/*LUI*/ r[rt]=(imm<<16); break; |
| 726 | case 0x10:/*COP0*/ |
| 727 | if((opcode & (1<<23)) == 0) //move from CP0 |
| 728 | { |
| 729 | if(rd == 12) |
| 730 | r[rt]=s->status; |
| 731 | else |
| 732 | r[rt]=s->epc; |
| 733 | } |
| 734 | else //move to CP0 |
| 735 | { |
| 736 | s->status=r[rt]&1; |
| 737 | if(s->processId && (r[rt]&2)) |
| 738 | { |
| 739 | s->userMode|=r[rt]&2; |
| 740 | //printf("CpuStatus=%d %d %d\n", r[rt], s->status, s->userMode); |
| 741 | //s->wakeup = 1; |
| 742 | //printf("pc=0x%x\n", epc); |
| 743 | } |
| 744 | } |
| 745 | break; |
| 746 | // case 0x11:/*COP1*/ break; |
| 747 | // case 0x12:/*COP2*/ break; |
| 748 | // case 0x13:/*COP3*/ break; |
| 749 | case 0x14:/*BEQL*/ lbranch=r[rs]==r[rt]; break; |
| 750 | case 0x15:/*BNEL*/ lbranch=r[rs]!=r[rt]; break; |
| 751 | case 0x16:/*BLEZL*/ lbranch=r[rs]<=0; break; |
| 752 | case 0x17:/*BGTZL*/ lbranch=r[rs]>0; break; |
| 753 | // case 0x1c:/*MAD*/ break; /*IV*/ |
| 754 | case 0x20:/*LB*/ r[rt]=(signed char)mem_read(s,1,ptr); break; |
| 755 | case 0x21:/*LH*/ r[rt]=(signed short)mem_read(s,2,ptr); break; |
| 756 | case 0x22:/*LWL*/ |
| 757 | //target=8*(ptr&3); |
| 758 | //r[rt]=(r[rt]&~(0xffffffff<<target))| |
| 759 | // (mem_read(s,4,ptr&~3)<<target); break; |
| 760 | case 0x23:/*LW*/ r[rt]=mem_read(s,4,ptr); break; |
| 761 | case 0x24:/*LBU*/ r[rt]=(unsigned char)mem_read(s,1,ptr); break; |
| 762 | case 0x25:/*LHU*/ r[rt]=(unsigned short)mem_read(s,2,ptr); break; |
| 763 | case 0x26:/*LWR*/ |
| 764 | //target=32-8*(ptr&3); |
| 765 | //r[rt]=(r[rt]&~((unsigned int)0xffffffff>>target))| |
| 766 | //((unsigned int)mem_read(s,4,ptr&~3)>>target); |
| 767 | break; |
| 768 | case 0x28:/*SB*/ mem_write(s,1,ptr,r[rt]); break; |
| 769 | case 0x29:/*SH*/ mem_write(s,2,ptr,r[rt]); break; |
| 770 | case 0x2a:/*SWL*/ |
| 771 | //mem_write(s,1,ptr,r[rt]>>24); |
| 772 | //mem_write(s,1,ptr+1,r[rt]>>16); |
| 773 | //mem_write(s,1,ptr+2,r[rt]>>8); |
| 774 | //mem_write(s,1,ptr+3,r[rt]); break; |
| 775 | case 0x2b:/*SW*/ mem_write(s,4,ptr,r[rt]); break; |
| 776 | case 0x2e:/*SWR*/ break; //fixme |
| 777 | case 0x2f:/*CACHE*/break; |
| 778 | case 0x30:/*LL*/ r[rt]=mem_read(s,4,ptr); break; |
| 779 | // case 0x31:/*LWC1*/ break; |
| 780 | // case 0x32:/*LWC2*/ break; |
| 781 | // case 0x33:/*LWC3*/ break; |
| 782 | // case 0x35:/*LDC1*/ break; |
| 783 | // case 0x36:/*LDC2*/ break; |
| 784 | // case 0x37:/*LDC3*/ break; |
| 785 | // case 0x38:/*SC*/ *(int*)ptr=r[rt]; r[rt]=1; break; |
| 786 | case 0x38:/*SC*/ mem_write(s,4,ptr,r[rt]); r[rt]=1; break; |
| 787 | // case 0x39:/*SWC1*/ break; |
| 788 | // case 0x3a:/*SWC2*/ break; |
| 789 | // case 0x3b:/*SWC3*/ break; |
| 790 | // case 0x3d:/*SDC1*/ break; |
| 791 | // case 0x3e:/*SDC2*/ break; |
| 792 | // case 0x3f:/*SDC3*/ break; |
| 793 | default: printf("ERROR2 address=0x%x opcode=0x%x\n", s->pc, opcode); |
| 794 | s->wakeup=1; |
| 795 | } |
| 796 | s->pc_next += (branch || lbranch == 1) ? imm_shift : 0; |
| 797 | s->pc_next &= ~3; |
| 798 | s->skip = (lbranch == 0) | skip2; |
| 799 | |
| 800 | if(s->exceptionId) |
| 801 | { |
| 802 | r[rt] = rSave; |
| 803 | s->epc = epc; |
| 804 | s->pc_next = 0x3c; |
| 805 | s->skip = 1; |
| 806 | s->exceptionId = 0; |
| 807 | s->userMode = 0; |
| 808 | //s->wakeup = 1; |
| 809 | return; |
| 810 | } |
| 811 | } |
| 812 | |
| 813 | void show_state(State *s) |
| 814 | { |
| 815 | int i,j; |
| 816 | printf("pid=%d userMode=%d, epc=0x%x\n", s->processId, s->userMode, s->epc); |
| 817 | for(i = 0; i < 4; ++i) |
| 818 | { |
| 819 | printf("%2.2d ", i * 8); |
| 820 | for(j = 0; j < 8; ++j) |
| 821 | { |
| 822 | printf("%8.8x ", s->r[i*8+j]); |
| 823 | } |
| 824 | printf("\n"); |
| 825 | } |
| 826 | //printf("%8.8lx %8.8lx %8.8lx %8.8lx\n", s->pc, s->pc_next, s->hi, s->lo); |
| 827 | j = s->pc; |
| 828 | for(i = -4; i <= 8; ++i) |
| 829 | { |
| 830 | printf("%c", i==0 ? '*' : ' '); |
| 831 | s->pc = j + i * 4; |
| 832 | cycle(s, 10); |
| 833 | } |
| 834 | s->pc = j; |
| 835 | } |
| 836 | |
| 837 | void do_debug(State *s) |
| 838 | { |
| 839 | int ch; |
| 840 | int i, j=0, watch=0, addr; |
| 841 | s->pc_next = s->pc + 4; |
| 842 | s->skip = 0; |
| 843 | s->wakeup = 0; |
| 844 | show_state(s); |
| 845 | ch = ' '; |
| 846 | for(;;) |
| 847 | { |
| 848 | if(ch != 'n') |
| 849 | { |
| 850 | if(watch) |
| 851 | printf("0x%8.8x=0x%8.8x\n", watch, mem_read(s, 4, watch)); |
| 852 | printf("1=Debug 2=Trace 3=Step 4=BreakPt 5=Go 6=Memory "); |
| 853 | printf("7=Watch 8=Jump 9=Quit> "); |
| 854 | } |
| 855 | ch = getch(); |
| 856 | if(ch != 'n') |
| 857 | printf("\n"); |
| 858 | switch(ch) |
| 859 | { |
| 860 | case '1': case 'd': case ' ': |
| 861 | cycle(s, 0); show_state(s); break; |
| 862 | case 'n': |
| 863 | cycle(s, 1); break; |
| 864 | case '2': case 't': |
| 865 | cycle(s, 0); printf("*"); cycle(s, 10); break; |
| 866 | case '3': case 's': |
| 867 | printf("Count> "); |
| 868 | scanf("%d", &j); |
| 869 | for(i = 0; i < j; ++i) |
| 870 | cycle(s, 1); |
| 871 | show_state(s); |
| 872 | break; |
| 873 | case '4': case 'b': |
| 874 | printf("Line> "); |
| 875 | scanf("%x", &j); |
| 876 | printf("break point=0x%x\n", j); |
| 877 | break; |
| 878 | case '5': case 'g': |
| 879 | s->wakeup = 0; |
| 880 | cycle(s, 0); |
| 881 | while(s->wakeup == 0) |
| 882 | { |
| 883 | if(s->pc == j) |
| 884 | break; |
| 885 | cycle(s, 0); |
| 886 | } |
| 887 | show_state(s); |
| 888 | break; |
| 889 | case 'G': |
| 890 | s->wakeup = 0; |
| 891 | cycle(s, 1); |
| 892 | while(s->wakeup == 0) |
| 893 | { |
| 894 | if(s->pc == j) |
| 895 | break; |
| 896 | cycle(s, 1); |
| 897 | } |
| 898 | show_state(s); |
| 899 | break; |
| 900 | case '6': case 'm': |
| 901 | printf("Memory> "); |
| 902 | scanf("%x", &j); |
| 903 | for(i = 0; i < 8; ++i) |
| 904 | { |
| 905 | printf("%8.8x ", mem_read(s, 4, j+i*4)); |
| 906 | } |
| 907 | printf("\n"); |
| 908 | break; |
| 909 | case '7': case 'w': |
| 910 | printf("Watch> "); |
| 911 | scanf("%x", &watch); |
| 912 | break; |
| 913 | case '8': case 'j': |
| 914 | printf("Jump> "); |
| 915 | scanf("%x", &addr); |
| 916 | s->pc = addr; |
| 917 | s->pc_next = addr + 4; |
| 918 | show_state(s); |
| 919 | break; |
| 920 | case '9': case 'q': |
| 921 | return; |
| 922 | } |
| 923 | } |
| 924 | } |
| 925 | /************************************************************/ |
| 926 | |
| 927 | int main(int argc,char *argv[]) |
| 928 | { |
| 929 | State state, *s=&state; |
| 930 | FILE *in; |
| 931 | int bytes, index; |
| 932 | printf("Plasma emulator\n"); |
| 933 | memset(s, 0, sizeof(State)); |
| 934 | s->big_endian = 1; |
| 935 | s->mem = (unsigned char*)malloc(MEM_SIZE); |
| 936 | memset(s->mem, 0, MEM_SIZE); |
| 937 | if(argc <= 1) |
| 938 | { |
| 939 | printf(" Usage: mlite file.exe\n"); |
| 940 | printf(" mlite file.exe B {for big_endian}\n"); |
| 941 | printf(" mlite file.exe L {for little_endian}\n"); |
| 942 | printf(" mlite file.exe BD {disassemble big_endian}\n"); |
| 943 | printf(" mlite file.exe LD {disassemble little_endian}\n"); |
| 944 | |
| 945 | return 0; |
| 946 | } |
| 947 | in = fopen(argv[1], "rb"); |
| 948 | if(in == NULL) |
| 949 | { |
| 950 | printf("Can't open file %s!\n",argv[1]); |
| 951 | getch(); |
| 952 | return(0); |
| 953 | } |
| 954 | bytes = fread(s->mem, 1, MEM_SIZE, in); |
| 955 | fclose(in); |
| 956 | memcpy(s->mem + 1024*1024, s->mem, 1024*1024); //internal 8KB SRAM |
| 957 | printf("Read %d bytes.\n", bytes); |
| 958 | cache_init(); |
| 959 | if(argc == 3 && argv[2][0] == 'B') |
| 960 | { |
| 961 | printf("Big Endian\n"); |
| 962 | s->big_endian = 1; |
| 963 | } |
| 964 | if(argc == 3 && argv[2][0] == 'L') |
| 965 | { |
| 966 | printf("Big Endian\n"); |
| 967 | s->big_endian = 0; |
| 968 | } |
| 969 | s->processId = 0; |
| 970 | if(argc == 3 && argv[2][0] == 'S') |
| 971 | { /*make big endian*/ |
| 972 | printf("Big Endian\n"); |
| 973 | for(index = 0; index < bytes+3; index += 4) |
| 974 | { |
| 975 | *(unsigned int*)&s->mem[index] = htonl(*(unsigned int*)&s->mem[index]); |
| 976 | } |
| 977 | in = fopen("big.exe", "wb"); |
| 978 | fwrite(s->mem, bytes, 1, in); |
| 979 | fclose(in); |
| 980 | return(0); |
| 981 | } |
| 982 | if(argc == 3 && argv[2][1] == 'D') |
| 983 | { /*dump image*/ |
| 984 | for(index = 0; index < bytes; index += 4) { |
| 985 | s->pc = index; |
| 986 | cycle(s, 10); |
| 987 | } |
| 988 | free(s->mem); |
| 989 | return(0); |
| 990 | } |
| 991 | s->pc = 0x0; |
| 992 | index = mem_read(s, 4, 0); |
| 993 | if((index & 0xffffff00) == 0x3c1c1000) |
| 994 | s->pc = 0x10000000; |
| 995 | do_debug(s); |
| 996 | free(s->mem); |
| 997 | return(0); |
| 998 | } |
| 999 | |
| 1000 |
Branches:
master
