compiler

Unnamed Compiled Systems Language Project
git clone http://frotz.net/git/compiler.git
Log | Files | Refs

risc5emu.c (9648B)


      1 // Copyright © 2014 Peter De Wachter
      2 //
      3 // Permission to use, copy, modify, and/or distribute this software for
      4 // any purpose with or without fee is hereby granted, provided that the
      5 // above copyright notice and this permission notice appear in all
      6 // copies.
      7 //
      8 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
      9 // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
     10 // WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
     11 // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12 // DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
     13 // PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
     14 // TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
     15 // PERFORMANCE OF THIS SOFTWARE.
     16 
     17 // based on src/risc5.c from git@github.com:pdewacht/oberon-risc-emu.git
     18 
     19 #include <stdint.h>
     20 #include <stdbool.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <stdio.h>
     24 #include <unistd.h>
     25 
     26 #include "risc5emu.h"
     27 #include "risc5emu-fp.h"
     28 #include "risc5.h"
     29 
     30 static void disasm(uint32_t pc, uint32_t ins) {
     31   char buf[256];
     32   risc5dis(pc, ins, buf);
     33   printf("%08x: %08x  %s\n", pc, ins, buf);
     34 }
     35 
     36 // Our memory layout is slightly different from the FPGA implementation:
     37 // The FPGA uses a 20-bit address bus and thus ignores the top 12 bits,
     38 // while we use all 32 bits. This allows us to have more than 1 megabyte
     39 // of RAM.
     40 
     41 #define DefaultMemSize 0x00100000
     42 #define IOStart        0xFFFF0000
     43 
     44 struct RISC {
     45   uint32_t PC;
     46   uint32_t R[16];
     47   uint32_t H;
     48   bool     Z, N, C, V;
     49 
     50   uint32_t *RAM;
     51   uint32_t mem_size;
     52   bool TRACE;
     53 };
     54 
     55 enum {
     56   MOV, LSL, ASR, ROR,
     57   AND, ANN, IOR, XOR,
     58   ADD, SUB, MUL, DIV,
     59   FAD, FSB, FML, FDV,
     60 };
     61 
     62 static uint32_t risc_load_io(struct RISC *risc, uint32_t address);
     63 static void risc_store_io(struct RISC *risc, uint32_t address, uint32_t value);
     64 
     65 struct RISC *risc_new(bool trace) {
     66   struct RISC *risc = calloc(1, sizeof(*risc));
     67   risc->mem_size = DefaultMemSize;
     68   risc->RAM = calloc(1, risc->mem_size);
     69   risc->TRACE = trace;
     70   risc_reset(risc);
     71   return risc;
     72 }
     73 
     74 void risc_trace(struct RISC *risc, bool trace) {
     75   risc->TRACE = trace;
     76 }
     77 
     78 void risc_reset(struct RISC *risc) {
     79   risc->PC = 0;
     80 }
     81 
     82 void risc_run(struct RISC *risc, int cycles) {
     83   // The progress value is used to detect that the RISC cpu is busy
     84   // waiting on the millisecond counter or on the keyboard ready
     85   // bit. In that case it's better to just pause emulation until the
     86   // next frame.
     87   for (int i = 0; i < cycles; i++) {
     88     risc_single_step(risc);
     89   }
     90 }
     91 
     92 void risc_single_step(struct RISC *risc) {
     93   uint32_t ir;
     94   if (risc->PC < risc->mem_size / 4) {
     95     ir = risc->RAM[risc->PC];
     96   } else {
     97     fprintf(stderr, "Branched into the void (PC=0x%08X), resetting...\n", risc->PC);
     98     exit(-1);
     99     //risc_reset(risc);
    100     return;
    101   }
    102 
    103   if (risc->TRACE) disasm(risc->PC << 2, ir); 
    104   risc->PC++;
    105 
    106   const uint32_t pbit = 0x80000000;
    107   const uint32_t qbit = 0x40000000;
    108   const uint32_t ubit = 0x20000000;
    109   const uint32_t vbit = 0x10000000;
    110 
    111   if ((ir & pbit) == 0) {
    112     // Register instructions
    113     uint32_t a  = (ir & 0x0F000000) >> 24;
    114     uint32_t b  = (ir & 0x00F00000) >> 20;
    115     uint32_t op = (ir & 0x000F0000) >> 16;
    116     uint32_t im =  ir & 0x0000FFFF;
    117     uint32_t c  =  ir & 0x0000000F;
    118 
    119     uint32_t a_val, b_val, c_val;
    120     b_val = risc->R[b];
    121     if ((ir & qbit) == 0) {
    122       c_val = risc->R[c];
    123     } else if ((ir & vbit) == 0) {
    124       c_val = im;
    125     } else {
    126       c_val = 0xFFFF0000 | im;
    127     }
    128 
    129     switch (op) {
    130       case MOV: {
    131         if ((ir & ubit) == 0) {
    132           a_val = c_val;
    133         } else if ((ir & qbit) != 0) {
    134           a_val = c_val << 16;
    135         } else if ((ir & vbit) != 0) {
    136           a_val = 0xD0 |   // ???
    137             (risc->N * 0x80000000U) |
    138             (risc->Z * 0x40000000U) |
    139             (risc->C * 0x20000000U) |
    140             (risc->V * 0x10000000U);
    141         } else {
    142           a_val = risc->H;
    143         }
    144         break;
    145       }
    146       case LSL: {
    147         a_val = b_val << (c_val & 31);
    148         break;
    149       }
    150       case ASR: {
    151         a_val = ((int32_t)b_val) >> (c_val & 31);
    152         break;
    153       }
    154       case ROR: {
    155         a_val = (b_val >> (c_val & 31)) | (b_val << (-c_val & 31));
    156         break;
    157       }
    158       case AND: {
    159         a_val = b_val & c_val;
    160         break;
    161       }
    162       case ANN: {
    163         a_val = b_val & ~c_val;
    164         break;
    165       }
    166       case IOR: {
    167         a_val = b_val | c_val;
    168         break;
    169       }
    170       case XOR: {
    171         a_val = b_val ^ c_val;
    172         break;
    173       }
    174       case ADD: {
    175         a_val = b_val + c_val;
    176         if ((ir & ubit) != 0) {
    177           a_val += risc->C;
    178         }
    179         risc->C = a_val < b_val;
    180         risc->V = ((a_val ^ c_val) & (a_val ^ b_val)) >> 31;
    181         break;
    182       }
    183       case SUB: {
    184         a_val = b_val - c_val;
    185         if ((ir & ubit) != 0) {
    186           a_val -= risc->C;
    187         }
    188         risc->C = a_val > b_val;
    189         risc->V = ((b_val ^ c_val) & (a_val ^ b_val)) >> 31;
    190         break;
    191       }
    192       case MUL: {
    193         uint64_t tmp;
    194         if ((ir & ubit) == 0) {
    195           tmp = (int64_t)(int32_t)b_val * (int64_t)(int32_t)c_val;
    196         } else {
    197           tmp = (uint64_t)b_val * (uint64_t)c_val;
    198         }
    199         a_val = (uint32_t)tmp;
    200         risc->H = (uint32_t)(tmp >> 32);
    201         break;
    202       }
    203       case DIV: {
    204         if ((int32_t)c_val > 0) {
    205           if ((ir & ubit) == 0) {
    206             a_val = (int32_t)b_val / (int32_t)c_val;
    207             risc->H = (int32_t)b_val % (int32_t)c_val;
    208             if ((int32_t)risc->H < 0) {
    209               a_val--;
    210               risc->H += c_val;
    211             }
    212           } else {
    213             a_val = b_val / c_val;
    214             risc->H = b_val % c_val;
    215           }
    216         } else {
    217           struct idiv q = idiv(b_val, c_val, ir & ubit);
    218           a_val = q.quot;
    219           risc->H = q.rem;
    220         }
    221         break;
    222       }
    223       case FAD: {
    224         a_val = fp_add(b_val, c_val, ir & ubit, ir & vbit);
    225         break;
    226       }
    227       case FSB: {
    228         a_val = fp_add(b_val, c_val ^ 0x80000000, ir & ubit, ir & vbit);
    229         break;
    230       }
    231       case FML: {
    232         a_val = fp_mul(b_val, c_val);
    233         break;
    234       }
    235       case FDV: {
    236         a_val = fp_div(b_val, c_val);
    237         break;
    238       }
    239       default: {
    240         abort();  // unreachable
    241       }
    242     }
    243     risc_set_register(risc, a, a_val);
    244   }
    245   else if ((ir & qbit) == 0) {
    246     // Memory instructions
    247     uint32_t a = (ir & 0x0F000000) >> 24;
    248     uint32_t b = (ir & 0x00F00000) >> 20;
    249     int32_t off = ir & 0x000FFFFF;
    250     off = (off ^ 0x00080000) - 0x00080000;  // sign-extend
    251 
    252     uint32_t address = risc->R[b] + off;
    253     if ((ir & ubit) == 0) {
    254       uint32_t a_val;
    255       if ((ir & vbit) == 0) {
    256         a_val = risc_load_word(risc, address);
    257       } else {
    258         a_val = risc_load_byte(risc, address);
    259       }
    260       risc_set_register(risc, a, a_val);
    261     } else {
    262       if ((ir & vbit) == 0) {
    263         risc_store_word(risc, address, risc->R[a]);
    264       } else {
    265         risc_store_byte(risc, address, (uint8_t)risc->R[a]);
    266       }
    267     }
    268   }
    269   else {
    270     // Branch instructions
    271     bool t = (ir >> 27) & 1;
    272     switch ((ir >> 24) & 7) {
    273       case 0: t ^= risc->N; break;
    274       case 1: t ^= risc->Z; break;
    275       case 2: t ^= risc->C; break;
    276       case 3: t ^= risc->V; break;
    277       case 4: t ^= risc->C | risc->Z; break;
    278       case 5: t ^= risc->N ^ risc->V; break;
    279       case 6: t ^= (risc->N ^ risc->V) | risc->Z; break;
    280       case 7: t ^= true; break;
    281       default: abort();  // unreachable
    282     }
    283     if (t) {
    284       if ((ir & vbit) != 0) {
    285         risc_set_register(risc, 15, risc->PC * 4);
    286       }
    287       if ((ir & ubit) == 0) {
    288         uint32_t c = ir & 0x0000000F;
    289         risc->PC = risc->R[c] / 4;
    290       } else {
    291         int32_t off = ir & 0x00FFFFFF;
    292         off = (off ^ 0x00800000) - 0x00800000;  // sign-extend
    293         risc->PC = risc->PC + off;
    294       }
    295     }
    296   }
    297 }
    298 
    299 void risc_set_register(struct RISC *risc, int reg, uint32_t value) {
    300   if (risc->TRACE) printf("                    R%d = 0x%08x\n", reg, value);
    301   risc->R[reg] = value;
    302   risc->Z = value == 0;
    303   risc->N = (int32_t)value < 0;
    304 }
    305 
    306 uint32_t risc_load_word(struct RISC *risc, uint32_t address) {
    307   if (address < risc->mem_size) {
    308     return risc->RAM[address/4];
    309   } else {
    310     return risc_load_io(risc, address);
    311   }
    312 }
    313 
    314 uint8_t risc_load_byte(struct RISC *risc, uint32_t address) {
    315   uint32_t w = risc_load_word(risc, address);
    316   return (uint8_t)(w >> (address % 4 * 8));
    317 }
    318 
    319 void risc_store_word(struct RISC *risc, uint32_t address, uint32_t value) {
    320   if (address < risc->mem_size) {
    321     if (risc->TRACE) printf("                    mem@ 0x%08x = 0x%08x\n", address, value);
    322     risc->RAM[address/4] = value;
    323   } else {
    324     risc_store_io(risc, address, value);
    325   }
    326 }
    327 
    328 void risc_store_byte(struct RISC *risc, uint32_t address, uint8_t value) {
    329   if (address < risc->mem_size) {
    330     uint32_t w = risc_load_word(risc, address);
    331     uint32_t shift = (address & 3) * 8;
    332     w &= ~(0xFFu << shift);
    333     w |= (uint32_t)value << shift;
    334     risc_store_word(risc, address, w);
    335   } else {
    336     risc_store_io(risc, address, (uint32_t)value);
    337   }
    338 }
    339 
    340 static uint32_t risc_load_io(struct RISC *risc, uint32_t address) {
    341   return 0;
    342 }
    343 
    344 static void risc_store_io(struct RISC *risc, uint32_t address, uint32_t value) {
    345   if (risc->TRACE) printf("                    io@ 0x%08x = 0x%08x\n", address, value);
    346   switch (address - IOStart) {
    347   case 0x100: {
    348     printf("X %08x\n", value);
    349     exit(0);
    350     break;
    351   case 0x104:
    352     printf("D %08x\n", value);
    353     break;
    354   case 0x108: {
    355     uint8_t x = value;
    356     write(0, &x, 1);
    357     break;
    358   }
    359   }
    360   }
    361 }
    362