1281 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1281 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
	
| /* This file is included into vb.c and cannot be compiled on its own. */
 | |
| #ifdef VBAPI
 | |
| 
 | |
| 
 | |
| 
 | |
| /********************************* Constants *********************************/
 | |
| 
 | |
| /* Operation IDs */
 | |
| #define CPU_EXCEPTION 0
 | |
| #define CPU_HALTING   1
 | |
| #define CPU_FATAL     2
 | |
| #define CPU_FETCH     3
 | |
| #define CPU_ILLEGAL   4
 | |
| #define CPU_BITSTRING 5
 | |
| #define CPU_FLOATENDO 6
 | |
| #define CPU_ADD_IMM   7
 | |
| #define CPU_ADD_REG   8
 | |
| #define CPU_ADDF_S    9
 | |
| #define CPU_ADDI     10
 | |
| #define CPU_AND      11
 | |
| #define CPU_ANDBSU   12
 | |
| #define CPU_ANDI     13
 | |
| #define CPU_ANDNBSU  14
 | |
| #define CPU_BCOND    15
 | |
| #define CPU_CAXI     16
 | |
| #define CPU_CLI      17
 | |
| #define CPU_CMP_IMM  18
 | |
| #define CPU_CMP_REG  19
 | |
| #define CPU_CMPF_S   20
 | |
| #define CPU_CVT_SW   21
 | |
| #define CPU_CVT_WS   22
 | |
| #define CPU_DIV      23
 | |
| #define CPU_DIVF_S   24
 | |
| #define CPU_DIVU     25
 | |
| #define CPU_HALT     26
 | |
| #define CPU_IN_B     27
 | |
| #define CPU_IN_H     28
 | |
| #define CPU_IN_W     29
 | |
| #define CPU_JAL      30
 | |
| #define CPU_JMP      31
 | |
| #define CPU_JR       32
 | |
| #define CPU_LD_B     33
 | |
| #define CPU_LD_H     34
 | |
| #define CPU_LD_W     35
 | |
| #define CPU_LDSR     36
 | |
| #define CPU_MOV_IMM  37
 | |
| #define CPU_MOV_REG  38
 | |
| #define CPU_MOVBSU   39
 | |
| #define CPU_MOVEA    40
 | |
| #define CPU_MOVHI    41
 | |
| #define CPU_MPYHW    42
 | |
| #define CPU_MUL      43
 | |
| #define CPU_MULF_S   44
 | |
| #define CPU_MULU     45
 | |
| #define CPU_NOT      46
 | |
| #define CPU_NOTBSU   47
 | |
| #define CPU_OR       48
 | |
| #define CPU_ORBSU    49
 | |
| #define CPU_ORI      50
 | |
| #define CPU_ORNBSU   51
 | |
| #define CPU_OUT_B    52
 | |
| #define CPU_OUT_H    53
 | |
| #define CPU_OUT_W    54
 | |
| #define CPU_RETI     55
 | |
| #define CPU_REV      56
 | |
| #define CPU_SAR_IMM  57
 | |
| #define CPU_SAR_REG  58
 | |
| #define CPU_SCH0BSD  59
 | |
| #define CPU_SCH0BSU  60
 | |
| #define CPU_SCH1BSD  61
 | |
| #define CPU_SCH1BSU  62
 | |
| #define CPU_SEI      63
 | |
| #define CPU_SETF     64
 | |
| #define CPU_SHL_IMM  65
 | |
| #define CPU_SHL_REG  66
 | |
| #define CPU_SHR_IMM  67
 | |
| #define CPU_SHR_REG  68
 | |
| #define CPU_ST_B     69
 | |
| #define CPU_ST_H     70
 | |
| #define CPU_ST_W     71
 | |
| #define CPU_STSR     72
 | |
| #define CPU_SUB      73
 | |
| #define CPU_SUBF_S   74
 | |
| #define CPU_TRAP     75
 | |
| #define CPU_TRNC_SW  76
 | |
| #define CPU_XB       77
 | |
| #define CPU_XH       78
 | |
| #define CPU_XOR      79
 | |
| #define CPU_XORBSU   80
 | |
| #define CPU_XORI     81
 | |
| #define CPU_XORNBSU  82
 | |
| 
 | |
| /* Abstract operand types */
 | |
| #define CPU_IMP(x) -x-1
 | |
| #define CPU_DISP9  1
 | |
| #define CPU_DISP26 2
 | |
| #define CPU_IMM16S 3
 | |
| #define CPU_IMM16U 4
 | |
| #define CPU_IMM5S  5
 | |
| #define CPU_IMM5U  6
 | |
| #define CPU_MEM    7
 | |
| #define CPU_REG1   8
 | |
| #define CPU_REG2   9
 | |
| 
 | |
| /* Functional operand types */
 | |
| #define CPU_LITERAL  0
 | |
| #define CPU_MEMORY   1
 | |
| #define CPU_REGISTER 2
 | |
| 
 | |
| /* Bit string operations */
 | |
| #define CPU_AND_BS  0
 | |
| #define CPU_ANDN_BS 1
 | |
| #define CPU_MOV_BS  2
 | |
| #define CPU_NOT_BS  3
 | |
| #define CPU_OR_BS   4
 | |
| #define CPU_ORN_BS  5
 | |
| #define CPU_XOR_BS  6
 | |
| #define CPU_XORN_BS 7
 | |
| 
 | |
| /* Instruction code lengths by opcode */
 | |
| static const uint8_t INST_LENGTHS[] = {
 | |
|     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 | |
|     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 | |
|     1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
 | |
|     2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| /********************************** Macros ***********************************/
 | |
| 
 | |
| /* Master clocks per CPU cycles */
 | |
| #define cpuClocks(x) x
 | |
| 
 | |
| /* Shorthand */
 | |
| #define auxData sim->cpu.aux.data
 | |
| 
 | |
| 
 | |
| 
 | |
| /******************************** Lookup Data ********************************/
 | |
| 
 | |
| /* Opdefs by opcode */
 | |
| static const uint8_t OPDEFS[] = {
 | |
|     CPU_MOV_REG, CPU_ADD_REG, CPU_SUB      , CPU_CMP_REG  , /* 000000 */
 | |
|     CPU_SHL_REG, CPU_SHR_REG, CPU_JMP      , CPU_SAR_REG  ,
 | |
|     CPU_MUL    , CPU_DIV    , CPU_MULU     , CPU_DIVU     ,
 | |
|     CPU_OR     , CPU_AND    , CPU_XOR      , CPU_NOT      ,
 | |
|     CPU_MOV_IMM, CPU_ADD_IMM, CPU_SETF     , CPU_CMP_IMM  , /* 010000 */
 | |
|     CPU_SHL_IMM, CPU_SHR_IMM, CPU_CLI      , CPU_SAR_IMM  ,
 | |
|     CPU_TRAP   , CPU_RETI   , CPU_HALT     , CPU_ILLEGAL  ,
 | |
|     CPU_LDSR   , CPU_STSR   , CPU_SEI      , CPU_BITSTRING,
 | |
|     CPU_BCOND  , CPU_BCOND  , CPU_BCOND    , CPU_BCOND    , /* 100000 */
 | |
|     CPU_BCOND  , CPU_BCOND  , CPU_BCOND    , CPU_BCOND    ,
 | |
|     CPU_MOVEA  , CPU_ADDI   , CPU_JR       , CPU_JAL      ,
 | |
|     CPU_ORI    , CPU_ANDI   , CPU_XORI     , CPU_MOVHI    ,
 | |
|     CPU_LD_B   , CPU_LD_H   , CPU_ILLEGAL  , CPU_LD_W     , /* 110000 */
 | |
|     CPU_ST_B   , CPU_ST_H   , CPU_ILLEGAL  , CPU_ST_W     ,
 | |
|     CPU_IN_B   , CPU_IN_H   , CPU_CAXI     , CPU_IN_W     ,
 | |
|     CPU_OUT_B  , CPU_OUT_H  , CPU_FLOATENDO, CPU_OUT_W
 | |
| };
 | |
| 
 | |
| /* Opdefs by bit string sub-opcode */
 | |
| static const uint8_t OPDEFS_BITSTRING[] = {
 | |
|     CPU_SCH0BSU, CPU_SCH0BSD, CPU_SCH1BSU, CPU_SCH1BSD, /* 00000 */
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
 | |
|     CPU_ORBSU  , CPU_ANDBSU , CPU_XORBSU , CPU_MOVBSU , /* 01000 */
 | |
|     CPU_ORNBSU , CPU_ANDNBSU, CPU_XORNBSU, CPU_NOTBSU ,
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, /* 10000 */
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, /* 11000 */
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL
 | |
| };
 | |
| 
 | |
| /* Opdefs by floating-point/Nintendo sub-opcodes */
 | |
| static const uint8_t OPDEFS_FLOATENDO[] = {
 | |
|     CPU_CMPF_S , CPU_ILLEGAL, CPU_CVT_WS , CPU_CVT_SW , /* 000000 */
 | |
|     CPU_ADDF_S , CPU_SUBF_S , CPU_MULF_S , CPU_DIVF_S ,
 | |
|     CPU_XB     , CPU_XH     , CPU_REV    , CPU_TRNC_SW,
 | |
|     CPU_MPYHW  , CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, /* 010000 */
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, /* 100000 */
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, /* 110000 */
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
 | |
|     CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| /***************************** Callback Handlers *****************************/
 | |
| 
 | |
| /* Prepare to execute an instruction */
 | |
| #ifndef VB_DIRECT_EXECUTE
 | |
|     #define VB_ON_EXECUTE sim->onExecute
 | |
| #else
 | |
|     extern int vbxOnExecute(VB *, uint32_t, const uint16_t *, int);
 | |
|     #define VB_ON_EXECUTE vbxOnExecute
 | |
| #endif
 | |
| static int cpuExecute(VB *sim) {
 | |
|     return
 | |
|         sim->onExecute != NULL &&
 | |
|         VB_ON_EXECUTE(sim, sim->cpu.pc, sim->cpu.code, sim->cpu.length)
 | |
|     ;
 | |
| }
 | |
| #undef VB_ON_EXECUTE
 | |
| 
 | |
| /* Read a value from the memory bus */
 | |
| #ifndef VB_DIRECT_READ
 | |
|     #define VB_ON_READ sim->onRead
 | |
| #else
 | |
|     extern int vbxOnRead(VB *, uint32_t, int, int32_t *, uint32_t *);
 | |
|     #define VB_ON_READ vbxOnRead
 | |
| #endif
 | |
| static int cpuRead(VB *sim, uint32_t address, int type, int32_t *value) {
 | |
|     uint32_t cycles = 4; /* TODO: Research this */
 | |
| 
 | |
|     /* Retrieve the value from the simulation state directly */
 | |
|     busRead(sim, address, type, value);
 | |
| 
 | |
|     /* Invoke the callback if available */
 | |
|     if (
 | |
|         sim->onRead != NULL &&
 | |
|         VB_ON_READ(sim, address, type, value, &cycles)
 | |
|     ) return 1;
 | |
| 
 | |
|     /* Update state */
 | |
|     sim->cpu.clocks += cpuClocks(cycles);
 | |
|     return 0;
 | |
| }
 | |
| #undef VB_ON_READ
 | |
| 
 | |
| /* Fetch a code unit from the memory bus */
 | |
| #ifndef VB_DIRECT_FETCH
 | |
|     #define VB_ON_FETCH sim->onFetch
 | |
| #else
 | |
|     extern int vbxOnFetch(VB *, int, uint32_t, int32_t *, uint32_t *);
 | |
|     #define VB_ON_FETCH vbxOnFetch
 | |
| #endif
 | |
| static int cpuReadFetch(VB *sim, int fetch, uint32_t address, int32_t *value) {
 | |
|     uint32_t cycles = 0;
 | |
| 
 | |
|     /* Retrieve the value from the simulation state directly */
 | |
|     busRead(sim, address, VB_U16, value);
 | |
| 
 | |
|     /* Invoke the callback if available */
 | |
|     if (
 | |
|         sim->onFetch != NULL &&
 | |
|         VB_ON_FETCH(sim, fetch, address, value, &cycles)
 | |
|     ) return 1;
 | |
| 
 | |
|     /* Update state */
 | |
|     sim->cpu.clocks += cpuClocks(cycles);
 | |
|     return 0;
 | |
| }
 | |
| #undef VB_ON_FETCH
 | |
| 
 | |
| /* Write a value to the memory bus */
 | |
| #ifndef VB_DIRECT_WRITE
 | |
|     #define VB_ON_WRITE sim->onWrite
 | |
| #else
 | |
|     extern int vbxOnWrite(VB *, uint32_t, int, int32_t *, uint32_t *, int *);
 | |
|     #define VB_ON_WRITE vbxOnWrite
 | |
| #endif
 | |
| static int cpuWrite(VB *sim, uint32_t address, int type, int32_t value) {
 | |
|     int      cancel = 0;
 | |
|     uint32_t cycles = 3; /* TODO: Research this */
 | |
| 
 | |
|     /* Invoke the callback if available */
 | |
|     if (
 | |
|         sim->onWrite != NULL &&
 | |
|         VB_ON_WRITE(sim, address, type, &value, &cycles, &cancel)
 | |
|     ) return 1;
 | |
| 
 | |
|     /* Write the value to the simulation state directly */
 | |
|     if (!cancel)
 | |
|         busWrite(sim, address, type, value, 0);
 | |
| 
 | |
|     /* Update state */
 | |
|     sim->cpu.clocks += cpuClocks(cycles);
 | |
|     return 0;
 | |
| }
 | |
| #undef VB_ON_WRITE
 | |
| 
 | |
| 
 | |
| 
 | |
| /****************************** Pipeline Stages ******************************/
 | |
| 
 | |
| /* Fetch the code bits for the next instruction */
 | |
| static int cpuFetch(VB *sim) {
 | |
|     int32_t value;
 | |
|     switch (sim->cpu.step) {
 | |
| 
 | |
|         case 0:
 | |
|             sim->cpu.pc = sim->cpu.nextPC;
 | |
|             /* Fallthrough */
 | |
| 
 | |
|         case 1:
 | |
| 
 | |
|             /* Retrieve the first code unit */
 | |
|             if (cpuReadFetch(sim, 0, sim->cpu.pc, &value)) {
 | |
|                 sim->cpu.step = 1;
 | |
|                 return 1;
 | |
|             }
 | |
| 
 | |
|             /* Update state */
 | |
|             sim->cpu.code[0] = value;
 | |
|             sim->cpu.length  = INST_LENGTHS[value >> 10 & 63];
 | |
|             sim->cpu.step    = 3 - sim->cpu.length;
 | |
| 
 | |
|             /* Wait any clocks taken */
 | |
|             if (sim->cpu.clocks != 0)
 | |
|                 return 0;
 | |
| 
 | |
|             /* Skip fetching a second code unit */
 | |
|             if (sim->cpu.length == 1)
 | |
|                 goto Step3;
 | |
| 
 | |
|             /* Fallthrough */
 | |
|         case 2:
 | |
| 
 | |
|             /* Retrieve the second code unit */
 | |
|             if (cpuReadFetch(sim, 1, sim->cpu.pc + 2, &value))
 | |
|                 return 1;
 | |
| 
 | |
|             /* Update state */
 | |
|             sim->cpu.code[1] = value;
 | |
|             sim->cpu.step    = 3;
 | |
| 
 | |
|             /* Wait any clocks taken */
 | |
|             if (sim->cpu.clocks != 0)
 | |
|                 return 0;
 | |
| 
 | |
|             /* Fallthrough */
 | |
|         case 3: Step3:
 | |
| 
 | |
|             /* Prepare to execute the instruction */
 | |
|             if (cpuExecute(sim))
 | |
|                 return 1;
 | |
| 
 | |
|             /* Select operation definition */
 | |
|             sim->cpu.operation = OPDEFS[sim->cpu.code[0] >> 10];
 | |
|             switch (sim->cpu.operation) {
 | |
|                 case CPU_BITSTRING:
 | |
|                     sim->cpu.operation =
 | |
|                         OPDEFS_BITSTRING[sim->cpu.code[0] & 31];
 | |
|                     break;
 | |
|                 case CPU_FLOATENDO:
 | |
|                     sim->cpu.operation =
 | |
|                         OPDEFS_FLOATENDO[sim->cpu.code[1] >> 10];
 | |
|             }
 | |
| 
 | |
|             /* Update state */
 | |
|             sim->cpu.step = 0;
 | |
| 
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**************************** Instruction Helpers ****************************/
 | |
| 
 | |
| /* Parse the immediate 4-bit condition value */
 | |
| static int32_t cpuGetCond(VB *sim) {
 | |
|     return sim->cpu.code[0] >> 9 & 15;
 | |
| }
 | |
| 
 | |
| /* Parse the 9-bit displacement value */
 | |
| static int32_t cpuGetDisp9(VB *sim) {
 | |
|     return SignExtend(sim->cpu.code[0], 9);
 | |
| }
 | |
| 
 | |
| /* Parse the 26-bit displacement value */
 | |
| static int32_t cpuGetDisp26(VB *sim) {
 | |
|     return SignExtend((int32_t) sim->cpu.code[0] << 16 | sim->cpu.code[1], 26);
 | |
| }
 | |
| 
 | |
| /* Parse the immediate 5-bit sign-extended value */
 | |
| static int32_t cpuGetImm5S(VB *sim) {
 | |
|     return SignExtend(sim->cpu.code[0], 5);
 | |
| }
 | |
| 
 | |
| /* Parse the immediate 5-bit zero-filled value */
 | |
| static int32_t cpuGetImm5U(VB *sim) {
 | |
|     return sim->cpu.code[0] & 31;
 | |
| }
 | |
| 
 | |
| /* Parse the immediate 16-bit sign-extended value */
 | |
| static int32_t cpuGetImm16S(VB *sim) {
 | |
|     return SignExtend(sim->cpu.code[1], 16);
 | |
| }
 | |
| 
 | |
| /* Parse the immediate 16-bit zero-filled value */
 | |
| static int32_t cpuGetImm16U(VB *sim) {
 | |
|     return sim->cpu.code[1];
 | |
| }
 | |
| 
 | |
| /* Resolve the operand value for reg1 */
 | |
| static int32_t cpuGetReg1(VB *sim) {
 | |
|     return sim->cpu.program[sim->cpu.code[0] & 31];
 | |
| }
 | |
| 
 | |
| /* Resolve the operand value for reg2 */
 | |
| static int32_t cpuGetReg2(VB *sim) {
 | |
|     return sim->cpu.program[sim->cpu.code[0] >> 5 & 31];
 | |
| }
 | |
| 
 | |
| /* Supply an operand value for reg2 */
 | |
| static void cpuSetReg2(VB *sim, int32_t value) {
 | |
|     int reg2 = sim->cpu.code[0] >> 5 & 31;
 | |
|     if (reg2 != 0)
 | |
|         sim->cpu.program[reg2] = value;
 | |
| }
 | |
| 
 | |
| /* Advance to the next instruction */
 | |
| static void cpuAdvance(VB *sim, uint32_t clocks) {
 | |
|     sim->cpu.clocks   += clocks;
 | |
|     sim->cpu.operation = CPU_FETCH;
 | |
|     sim->cpu.nextPC    = sim->cpu.pc + (sim->cpu.length << 1);
 | |
|     sim->cpu.step      = 0;
 | |
| }
 | |
| 
 | |
| /* Retrieve the value of a system register */
 | |
| static uint32_t cpuGetSystemRegister(VB *sim, int index) {
 | |
|     switch (index) {
 | |
|         case VB_ADTRE: return sim->cpu.adtre;
 | |
|         case VB_CHCW : return sim->cpu.chcw.ice << 1;
 | |
|         case VB_ECR  : return
 | |
|             (uint32_t) sim->cpu.ecr.fecc << 16 |
 | |
|             (uint32_t) sim->cpu.ecr.eicc;
 | |
|         case VB_EIPC : return sim->cpu.eipc;
 | |
|         case VB_EIPSW: return sim->cpu.eipsw;
 | |
|         case VB_FEPC : return sim->cpu.fepc;
 | |
|         case VB_FEPSW: return sim->cpu.fepsw;
 | |
|         case VB_PIR  : return 0x00005346;
 | |
|         case VB_PSW  : return
 | |
|             (uint32_t) sim->cpu.psw.i   << 16 |
 | |
|             (uint32_t) sim->cpu.psw.np  << 15 |
 | |
|             (uint32_t) sim->cpu.psw.ep  << 14 |
 | |
|             (uint32_t) sim->cpu.psw.ae  << 13 |
 | |
|             (uint32_t) sim->cpu.psw.id  << 12 |
 | |
|             (uint32_t) sim->cpu.psw.fro <<  9 |
 | |
|             (uint32_t) sim->cpu.psw.fiv <<  8 |
 | |
|             (uint32_t) sim->cpu.psw.fzd <<  7 |
 | |
|             (uint32_t) sim->cpu.psw.fov <<  6 |
 | |
|             (uint32_t) sim->cpu.psw.fud <<  5 |
 | |
|             (uint32_t) sim->cpu.psw.fpr <<  4 |
 | |
|             (uint32_t) sim->cpu.psw.cy  <<  3 |
 | |
|             (uint32_t) sim->cpu.psw.ov  <<  2 |
 | |
|             (uint32_t) sim->cpu.psw.s   <<  1 |
 | |
|             (uint32_t) sim->cpu.psw.z;
 | |
|         case VB_TKCW : return 0x000000E0;
 | |
|         case 29      : return sim->cpu.sr29;
 | |
|         case 30      : return 0x00000004;
 | |
|         case 31      : return sim->cpu.sr31;
 | |
|     }
 | |
|     return 0x00000000; /* All others */
 | |
| }
 | |
| 
 | |
| /* Specify a new value for a system register */
 | |
| static uint32_t cpuSetSystemRegister(VB*sim,
 | |
|     int index, uint32_t value, int debug) {
 | |
|     switch (index) {
 | |
|         case VB_ADTRE: return sim->cpu.adtre = value & 0xFFFFFFFE;
 | |
|         case VB_CHCW :
 | |
|             /* TODO: Configure instruction cache */
 | |
|             sim->cpu.chcw.ice = value >> 1 & 1;
 | |
|             return value & 0x00000002;
 | |
|         case VB_ECR  :
 | |
|             if (debug) {
 | |
|                 sim->cpu.ecr.fecc = value >> 16;
 | |
|                 sim->cpu.ecr.eicc = value;
 | |
|             }
 | |
|             return
 | |
|                 (uint32_t) sim->cpu.ecr.fecc << 16 |
 | |
|                 (uint32_t) sim->cpu.ecr.eicc;
 | |
|         case VB_EIPC : return sim->cpu.eipc  = value & 0xFFFFFFFE;
 | |
|         case VB_EIPSW: return sim->cpu.eipsw = value & 0x000FF3FF;
 | |
|         case VB_FEPC : return sim->cpu.fepc  = value & 0xFFFFFFFE;
 | |
|         case VB_FEPSW: return sim->cpu.fepsw = value & 0x000FF3FF;
 | |
|         case VB_PIR  : return 0x00005346;
 | |
|         case VB_PSW  :
 | |
|             sim->cpu.psw.i   = value >> 16 & 0xF;
 | |
|             sim->cpu.psw.np  = value >> 15 & 1;
 | |
|             sim->cpu.psw.ep  = value >> 14 & 1;
 | |
|             sim->cpu.psw.ae  = value >> 13 & 1;
 | |
|             sim->cpu.psw.id  = value >> 12 & 1;
 | |
|             sim->cpu.psw.fro = value >>  9 & 1;
 | |
|             sim->cpu.psw.fiv = value >>  8 & 1;
 | |
|             sim->cpu.psw.fzd = value >>  7 & 1;
 | |
|             sim->cpu.psw.fov = value >>  6 & 1;
 | |
|             sim->cpu.psw.fud = value >>  5 & 1;
 | |
|             sim->cpu.psw.fpr = value >>  4 & 1;
 | |
|             sim->cpu.psw.cy  = value >>  3 & 1;
 | |
|             sim->cpu.psw.ov  = value >>  2 & 1;
 | |
|             sim->cpu.psw.s   = value >>  1 & 1;
 | |
|             sim->cpu.psw.z   = value       & 1;
 | |
|             return value & 0x000FF3FF;
 | |
|         case VB_TKCW : return 0x000000E0;
 | |
|         case 29      : return sim->cpu.sr29 = value;
 | |
|         case 30      : return 0x00000004;
 | |
|         case 31      : return sim->cpu.sr31 = debug ? value :
 | |
|             *(int32_t *) &value < 0 ? (uint32_t) -*(int32_t *) &value : value;
 | |
|     }
 | |
|     return 0x00000000; /* All others */
 | |
| }
 | |
| 
 | |
| /* Addition common processing */
 | |
| static int32_t cpuAdd(VB *sim, int32_t b) {
 | |
|     int32_t a = cpuGetReg2(sim);
 | |
|     int32_t c = a + b;
 | |
|     sim->cpu.psw.cy = (uint32_t) c < (uint32_t) a;
 | |
|     sim->cpu.psw.ov = (~(a ^ b) & (a ^ c)) < 0;
 | |
|     sim->cpu.psw.s  = c <  0;
 | |
|     sim->cpu.psw.z  = c == 0;
 | |
|     return c;
 | |
| }
 | |
| 
 | |
| /* Bitwise common processing */
 | |
| static int32_t cpuBitwise(VB *sim, int32_t c) {
 | |
|     sim->cpu.psw.ov = 0;
 | |
|     sim->cpu.psw.s  = c <  0;
 | |
|     sim->cpu.psw.z  = c == 0;
 | |
|     return c;
 | |
| }
 | |
| 
 | |
| /* Test a condition */
 | |
| static int cpuCondition(VB *sim, int id) {
 | |
|     switch (id) {
 | |
|         case 0: return sim->cpu.psw.ov;
 | |
|         case 1: return sim->cpu.psw.cy;
 | |
|         case 2: return sim->cpu.psw.z;
 | |
|         case 3: return sim->cpu.psw.cy | sim->cpu.psw.z;
 | |
|         case 4: return sim->cpu.psw.s;
 | |
|         case 5: return 1;
 | |
|         case 6: return sim->cpu.psw.ov ^ sim->cpu.psw.s;
 | |
|         case 7: return (sim->cpu.psw.ov ^ sim->cpu.psw.s) | sim->cpu.psw.z;
 | |
|     }
 | |
|     return !cpuCondition(sim, id & 7);
 | |
| }
 | |
| 
 | |
| /* Jump and branch common processing */
 | |
| static void cpuJump(VB *sim, uint32_t address) {
 | |
|     sim->cpu.clocks   += cpuClocks(3);
 | |
|     sim->cpu.operation = CPU_FETCH;
 | |
|     sim->cpu.nextPC    = address & 0xFFFFFFFE;
 | |
|     sim->cpu.step      = 0;
 | |
|     /* TODO: Clear prefetch buffer */
 | |
| }
 | |
| 
 | |
| /* Arithmetic right shift common processing */
 | |
| static int32_t cpuShiftArithmetic(VB *sim, int32_t b) {
 | |
|     int32_t a = cpuGetReg2(sim);
 | |
|     #ifndef VB_SIGNED_PROPAGATE
 | |
|         int32_t c = b == 0 ? a : SignExtend(a >> b, 32 - b);
 | |
|     #else
 | |
|         int32_t c = a >> b;
 | |
|     #endif
 | |
|     sim->cpu.psw.cy = b == 0 ? 0 : a >> (b - 1) & 1;
 | |
|     sim->cpu.psw.ov = 0;
 | |
|     sim->cpu.psw.s  = c <  0;
 | |
|     sim->cpu.psw.z  = c == 0;
 | |
|     return c;
 | |
| }
 | |
| 
 | |
| /* Left shift common processing */
 | |
| static int32_t cpuShiftLeft(VB *sim, int32_t b) {
 | |
|     int32_t a = cpuGetReg2(sim);
 | |
|     int32_t c = (uint32_t) a << b;
 | |
|     sim->cpu.psw.cy = b == 0 ? 0 : a >> (32 - b) & 1;
 | |
|     sim->cpu.psw.ov = 0;
 | |
|     sim->cpu.psw.s  = c <  0;
 | |
|     sim->cpu.psw.z  = c == 0;
 | |
|     return c;
 | |
| }
 | |
| 
 | |
| /* Logical right shift common processing */
 | |
| static int32_t cpuShiftRight(VB *sim, int32_t b) {
 | |
|     int32_t a = cpuGetReg2(sim);
 | |
|     int32_t c = a >> b & (uint32_t) 0xFFFFFFFF << (32 - b);
 | |
|     sim->cpu.psw.cy = b == 0 ? 0 : a >> (b - 1) & 1;
 | |
|     sim->cpu.psw.ov = 0;
 | |
|     sim->cpu.psw.s  = c <  0;
 | |
|     sim->cpu.psw.z  = c == 0;
 | |
|     return c;
 | |
| }
 | |
| 
 | |
| /* Subtraction common processing */
 | |
| static int32_t cpuSubtract(VB *sim, int32_t b) {
 | |
|     int32_t a = cpuGetReg2(sim);
 | |
|     int32_t c = a - b;
 | |
|     sim->cpu.psw.cy = (uint32_t) a < (uint32_t) b;
 | |
|     sim->cpu.psw.ov = ((a ^ b) & (a ^ c)) < 0;
 | |
|     sim->cpu.psw.s  = c <  0;
 | |
|     sim->cpu.psw.z  = c == 0;
 | |
|     return c;
 | |
| }
 | |
| 
 | |
| /* Memory load/input instruction */
 | |
| static int cpuLD_IN(VB *sim, int type) {
 | |
|     switch (sim->cpu.step) {
 | |
| 
 | |
|         case 0:
 | |
|             auxData.address = cpuGetReg1(sim) + cpuGetImm16S(sim);
 | |
|             /* Fallthrough */
 | |
| 
 | |
|         case 1:
 | |
| 
 | |
|             /* Read the value from memory */
 | |
|             if (cpuRead(sim, auxData.address, type, &auxData.value)) {
 | |
|                 sim->cpu.step = 1;
 | |
|                 return 1;
 | |
|             }
 | |
| 
 | |
|             /* Update state */
 | |
|             sim->cpu.clocks += cpuClocks(1);
 | |
| 
 | |
|             /* Wait for clocks taken */
 | |
|             sim->cpu.step = 2;
 | |
|             return 0;
 | |
| 
 | |
|         case 2:
 | |
|             cpuSetReg2(sim, auxData.value);
 | |
|             cpuAdvance(sim, cpuClocks(4)); /* TODO: Research */
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* Memory store/output instruction */
 | |
| static int cpuST_OUT(VB *sim, int type) {
 | |
|     switch (sim->cpu.step) {
 | |
| 
 | |
|         case 0:
 | |
|             auxData.address = cpuGetReg1(sim) + cpuGetImm16S(sim);
 | |
|             /* Fallthrough */
 | |
| 
 | |
|         case 1:
 | |
| 
 | |
|             /* Write the value to memory */
 | |
|             if (cpuWrite(sim, auxData.address, type, cpuGetReg2(sim))) {
 | |
|                 sim->cpu.step = 1;
 | |
|                 return 1;
 | |
|             }
 | |
| 
 | |
|             /* Update state */
 | |
|             sim->cpu.clocks += cpuClocks(1);
 | |
| 
 | |
|             /* Wait for clocks taken */
 | |
|             sim->cpu.step = 2;
 | |
|             return 0;
 | |
| 
 | |
|         case 2:
 | |
|             cpuSetReg2(sim, auxData.value);
 | |
|             cpuAdvance(sim, cpuClocks(3)); /* TODO: Research */
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /************************** Instruction Operations ***************************/
 | |
| 
 | |
| /* ADD immediate */
 | |
| static int cpuADDImm(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuAdd(sim, cpuGetImm5S(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* ADD register */
 | |
| static int cpuADDReg(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuAdd(sim, cpuGetReg1(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* ADDI */
 | |
| static int cpuADDI(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuAdd(sim, cpuGetImm16S(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* AND */
 | |
| static int cpuAND(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) & cpuGetReg1(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* ANDI */
 | |
| static int cpuANDI(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) & cpuGetImm16U(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* BCOND */
 | |
| static int cpuBCOND(VB *sim) {
 | |
|     if (cpuCondition(sim, cpuGetCond(sim)))
 | |
|         cpuJump(sim, sim->cpu.pc + cpuGetDisp9(sim));
 | |
|     else cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* CAXI */
 | |
| static int cpuCAXI(VB *sim) {
 | |
|     switch (sim->cpu.step) {
 | |
| 
 | |
|         case 0:
 | |
|             auxData.address = cpuGetReg1(sim) + cpuGetImm16S(sim);
 | |
|             /* Fallthrough */
 | |
| 
 | |
|         case 1:
 | |
| 
 | |
|             /* Read the compare value */
 | |
|             if (cpuRead(sim, auxData.address, VB_S32, &auxData.value)) {
 | |
|                 sim->cpu.step = 1;
 | |
|                 return 1;
 | |
|             }
 | |
| 
 | |
|             /* Update state */
 | |
|             sim->cpu.clocks += cpuClocks(1); /* TODO: Research */
 | |
|             cpuSubtract(sim, auxData.value);
 | |
|             if (sim->cpu.psw.z)
 | |
|                 auxData.value = sim->cpu.program[30];
 | |
| 
 | |
|             /* Wait for clocks taken */
 | |
|             sim->cpu.step = 2;
 | |
|             return 0;
 | |
| 
 | |
|         case 2:
 | |
| 
 | |
|             /* Write the exchange value */
 | |
|             if (cpuWrite(sim, auxData.address, VB_S32, auxData.value))
 | |
|                 return 1;
 | |
| 
 | |
|             /* Update state */
 | |
|             cpuSetReg2(sim, auxData.value);
 | |
|             cpuAdvance(sim, cpuClocks(25)); /* TODO: Research */
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* CLI */
 | |
| static int cpuCLI(VB *sim) {
 | |
|     sim->cpu.psw.id = 0;
 | |
|     cpuAdvance(sim, cpuClocks(12));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* CMP immediate */
 | |
| static int cpuCMPImm(VB *sim) {
 | |
|     cpuSubtract(sim, cpuGetImm5S(sim));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* CMP register */
 | |
| static int cpuCMPReg(VB *sim) {
 | |
|     cpuSubtract(sim, cpuGetReg1(sim));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* DIV */
 | |
| static int cpuDIV(VB *sim) {
 | |
|     int32_t a;  /* Dividend */
 | |
|     int32_t b;  /* Divisor */
 | |
|     int32_t c;  /* Remainder */
 | |
|     int32_t d;  /* Quotient */
 | |
|     #ifdef VB_DIV_GENERIC
 | |
|         int sa; /* Sign of a */
 | |
|         int sb; /* Sign of b */
 | |
|     #endif
 | |
| 
 | |
|     /* Zero division */
 | |
|     b = cpuGetReg1(sim);
 | |
|     if (b == 0) {
 | |
|         sim->cpu.exception = 0xFF80;
 | |
|         sim->cpu.operation = CPU_EXCEPTION;
 | |
|         /* TODO: Research clocks */
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     /* Compute results */
 | |
|     a = cpuGetReg2(sim);
 | |
|     sim->cpu.psw.ov = (uint32_t) a == (uint32_t) 0x80000000 && b == -1;
 | |
|     #ifndef VB_DIV_GENERIC
 | |
|         c = a % b;
 | |
|         d = a / b;
 | |
|     #else
 | |
| 
 | |
|         /* Do not divide */
 | |
|         if (sim->cpu.psw.ov) {
 | |
|             c = 0;
 | |
|             d = a;
 | |
|         }
 | |
| 
 | |
|         /* Perform division */
 | |
|         else {
 | |
| 
 | |
|             /* Take signs and absolute values of operands */
 | |
|             sa = a < 0;
 | |
|             if (sa)
 | |
|                 a = -a;
 | |
|             sb = b < 0;
 | |
|             if (sb)
 | |
|                 b = -b;
 | |
| 
 | |
|             /* Compute results */
 | |
|             c = (uint32_t) a % (uint32_t) b;
 | |
|             if (sa)
 | |
|                 c = -c;
 | |
|             d = (uint32_t) a / (uint32_t) b;
 | |
|             if (sa ^ sb)
 | |
|                 d = -d;
 | |
|         }
 | |
| 
 | |
|     #endif
 | |
|     sim->cpu.program[30] = c;
 | |
|     cpuSetReg2(sim, d);
 | |
| 
 | |
|     /* Flags */
 | |
|     sim->cpu.psw.s  = d <  0;
 | |
|     sim->cpu.psw.z  = d == 0;
 | |
|     cpuAdvance(sim, cpuClocks(38));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* DIVU */
 | |
| static int cpuDIVU(VB *sim) {
 | |
|     uint32_t a;  /* Dividend */
 | |
|     uint32_t b;  /* Divisor */
 | |
|     int32_t  c;  /* Remainder */
 | |
|     int32_t  d;  /* Quotient */
 | |
| 
 | |
|     /* Zero division */
 | |
|     b = cpuGetReg1(sim);
 | |
|     if (b == 0) {
 | |
|         sim->cpu.exception = 0xFF80;
 | |
|         sim->cpu.operation = CPU_EXCEPTION;
 | |
|         /* TODO: Research clocks */
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     /* Compute results */
 | |
|     a = cpuGetReg2(sim);
 | |
|     c = a % b;
 | |
|     d = a / b;
 | |
|     sim->cpu.program[30] = c;
 | |
|     cpuSetReg2(sim, d);
 | |
| 
 | |
|     /* Flags */
 | |
|     sim->cpu.psw.ov = 0;
 | |
|     sim->cpu.psw.s  = d <  0;
 | |
|     sim->cpu.psw.z  = d == 0;
 | |
|     cpuAdvance(sim, cpuClocks(36));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* HALT */
 | |
| static int cpuHALT(VB *sim) {
 | |
|     sim->cpu.operation = CPU_HALTING;
 | |
|     /* TODO: Research clocks */
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* IN.B */
 | |
| static int cpuIN_B(VB *sim) {
 | |
|     return cpuLD_IN(sim, VB_U8);
 | |
| }
 | |
| 
 | |
| /* IN.H */
 | |
| static int cpuIN_H(VB *sim) {
 | |
|     return cpuLD_IN(sim, VB_U16);
 | |
| }
 | |
| 
 | |
| /* IN.W */
 | |
| static int cpuIN_W(VB *sim) {
 | |
|     return cpuLD_IN(sim, VB_S32);
 | |
| }
 | |
| 
 | |
| /* JAL */
 | |
| static int cpuJAL(VB *sim) {
 | |
|     sim->cpu.program[31] = sim->cpu.pc + 4;
 | |
|     cpuJump(sim, sim->cpu.pc + cpuGetDisp26(sim));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* JMP */
 | |
| static int cpuJMP(VB *sim) {
 | |
|     cpuJump(sim, cpuGetReg1(sim));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* JR */
 | |
| static int cpuJR(VB *sim) {
 | |
|     cpuJump(sim, sim->cpu.pc + cpuGetDisp26(sim));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* LD.B */
 | |
| static int cpuLD_B(VB *sim) {
 | |
|     return cpuLD_IN(sim, VB_S8);
 | |
| }
 | |
| 
 | |
| /* LD.H */
 | |
| static int cpuLD_H(VB *sim) {
 | |
|     return cpuLD_IN(sim, VB_S16);
 | |
| }
 | |
| 
 | |
| /* LD.W */
 | |
| static int cpuLD_W(VB *sim) {
 | |
|     return cpuLD_IN(sim, VB_S32);
 | |
| }
 | |
| 
 | |
| /* LDSR */
 | |
| static int cpuLDSR(VB *sim) {
 | |
|     cpuSetSystemRegister(sim, cpuGetImm5U(sim), cpuGetReg2(sim), 0);
 | |
|     cpuAdvance(sim, cpuClocks(8));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* MOV immediate */
 | |
| static int cpuMOVImm(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuGetImm5S(sim));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* MOV register */
 | |
| static int cpuMOVReg(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuGetReg1(sim));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* MOVEA */
 | |
| static int cpuMOVEA(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuGetReg1(sim) + cpuGetImm16S(sim));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* MOVHI */
 | |
| static int cpuMOVHI(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuGetReg1(sim) + (cpuGetImm16U(sim) << 16));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* MPYHW */
 | |
| static int cpuMPYHW(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuGetReg2(sim) * SignExtend(cpuGetReg1(sim), 17));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* MUL */
 | |
| static int cpuMUL(VB *sim) {
 | |
|     int64_t a = cpuGetReg2(sim);
 | |
|     int64_t b = cpuGetReg1(sim);
 | |
|     int64_t c = a * b;
 | |
|     int32_t d = c;
 | |
|     sim->cpu.program[30] = c >> 32;
 | |
|     cpuSetReg2(sim, d);
 | |
|     sim->cpu.psw.ov = d != c;
 | |
|     sim->cpu.psw.s  = d <  0;
 | |
|     sim->cpu.psw.z  = d == 0;
 | |
|     cpuAdvance(sim, 13);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* MULU */
 | |
| static int cpuMULU(VB *sim) {
 | |
|     uint64_t a = cpuGetReg2(sim);
 | |
|     uint64_t b = cpuGetReg1(sim);
 | |
|     int64_t  c = a * b;
 | |
|     int32_t  d = c;
 | |
|     sim->cpu.program[30] = c >> 32;
 | |
|     cpuSetReg2(sim, d);
 | |
|     sim->cpu.psw.ov = d != c;
 | |
|     sim->cpu.psw.s  = d <  0;
 | |
|     sim->cpu.psw.z  = d == 0;
 | |
|     cpuAdvance(sim, 13);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* NOT */
 | |
| static int cpuNOT(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuBitwise(sim, ~cpuGetReg1(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* OR */
 | |
| static int cpuOR(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) | cpuGetReg1(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* ORI */
 | |
| static int cpuORI(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) | cpuGetImm16U(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* OUT.B */
 | |
| static int cpuOUT_B(VB *sim) {
 | |
|     return cpuST_OUT(sim, VB_U8);
 | |
| }
 | |
| 
 | |
| /* OUT.H */
 | |
| static int cpuOUT_H(VB *sim) {
 | |
|     return cpuST_OUT(sim, VB_U16);
 | |
| }
 | |
| 
 | |
| /* OUT.W */
 | |
| static int cpuOUT_W(VB *sim) {
 | |
|     return cpuST_OUT(sim, VB_S32);
 | |
| }
 | |
| 
 | |
| /* RETI */
 | |
| static int cpuRETI(VB *sim) {
 | |
|     if (sim->cpu.psw.np) {
 | |
|         sim->cpu.nextPC = sim->cpu.fepc;
 | |
|         cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fepsw, 0);
 | |
|     } else {
 | |
|         sim->cpu.nextPC = sim->cpu.eipc;
 | |
|         cpuSetSystemRegister(sim, VB_PSW, sim->cpu.eipsw, 0);
 | |
|     }
 | |
|     sim->cpu.clocks   += cpuClocks(10);
 | |
|     sim->cpu.operation = CPU_FETCH;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* REV */
 | |
| static int cpuREV(VB *sim) {
 | |
|     uint32_t x = cpuGetReg1(sim);
 | |
|     x = (x << 16 & 0xFFFF0000) | (x >> 16 & 0x0000FFFF);
 | |
|     x = (x <<  8 & 0xFF00FF00) | (x >>  8 & 0x00FF00FF);
 | |
|     x = (x <<  4 & 0xF0F0F0F0) | (x >>  4 & 0x0F0F0F0F);
 | |
|     x = (x <<  2 & 0xCCCCCCCC) | (x >>  2 & 0x33333333);
 | |
|     x = (x <<  1 & 0xAAAAAAAA) | (x >>  1 & 0x55555555);
 | |
|     cpuSetReg2(sim, x);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* SAR immediate */
 | |
| static int cpuSARImm(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuShiftArithmetic(sim, cpuGetImm5U(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* SEI */
 | |
| static int cpuSEI(VB *sim) {
 | |
|     sim->cpu.psw.id = 1;
 | |
|     cpuAdvance(sim, cpuClocks(12));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* SETF */
 | |
| static int cpuSETF(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuCondition(sim, cpuGetImm5U(sim) & 15));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* SHR register */
 | |
| static int cpuSARReg(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuShiftArithmetic(sim, cpuGetReg1(sim) & 31));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* SHL immediate */
 | |
| static int cpuSHLImm(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuShiftLeft(sim, cpuGetImm5U(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* SHR register */
 | |
| static int cpuSHLReg(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuShiftLeft(sim, cpuGetReg1(sim) & 31));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* SHR immediate */
 | |
| static int cpuSHRImm(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuShiftRight(sim, cpuGetImm5U(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* SHR register */
 | |
| static int cpuSHRReg(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuShiftRight(sim, cpuGetReg1(sim) & 31));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* ST.B */
 | |
| static int cpuST_B(VB *sim) {
 | |
|     return cpuST_OUT(sim, VB_S8);
 | |
| }
 | |
| 
 | |
| /* ST.H */
 | |
| static int cpuST_H(VB *sim) {
 | |
|     return cpuST_OUT(sim, VB_S16);
 | |
| }
 | |
| 
 | |
| /* ST.W */
 | |
| static int cpuST_W(VB *sim) {
 | |
|     return cpuST_OUT(sim, VB_S32);
 | |
| }
 | |
| 
 | |
| /* STSR */
 | |
| static int cpuSTSR(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuGetSystemRegister(sim, cpuGetImm5U(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(8));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* SUB */
 | |
| static int cpuSUB(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuSubtract(sim, cpuGetReg1(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* TRAP */
 | |
| static int cpuTRAP(VB *sim) {
 | |
|     sim->cpu.clocks   += cpuClocks(15);
 | |
|     sim->cpu.exception = 0xFFA0 + cpuGetImm5U(sim);
 | |
|     sim->cpu.operation = CPU_EXCEPTION;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* XB */
 | |
| static int cpuXB(VB *sim) {
 | |
|     uint32_t x = cpuGetReg2(sim);
 | |
|     x = (x & 0xFFFF0000) | (x << 8 & 0x0000FF00) | (x >> 8 & 0x000000FF);
 | |
|     cpuSetReg2(sim, x);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* XH */
 | |
| static int cpuXH(VB *sim) {
 | |
|     uint32_t x = cpuGetReg2(sim);
 | |
|     x = (x << 16 & 0xFFFF0000) | (x >> 16 & 0x0000FFFF);
 | |
|     cpuSetReg2(sim, x);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* XOR */
 | |
| static int cpuXOR(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) ^ cpuGetReg1(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* XORI */
 | |
| static int cpuXORI(VB *sim) {
 | |
|     cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) ^ cpuGetImm16U(sim)));
 | |
|     cpuAdvance(sim, cpuClocks(1));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /***************************** Library Functions *****************************/
 | |
| 
 | |
| /* Process component */
 | |
| static int cpuEmulate(VB *sim, uint32_t clocks) {
 | |
|     int brk;
 | |
| 
 | |
|     /* Process until there are clocks to wait */
 | |
|     for (;;) {
 | |
| 
 | |
|         /* The next event is after the time remaining */
 | |
|         if (sim->cpu.clocks > clocks) {
 | |
|             sim->cpu.clocks -= clocks;
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         /* Advance forward the CPU's number of clocks */
 | |
|         if (sim->cpu.clocks != 0) {
 | |
|             clocks         -= sim->cpu.clocks;
 | |
|             sim->cpu.clocks = 0;
 | |
|         }
 | |
| 
 | |
|         /* Processing by operation ID */
 | |
|         switch (sim->cpu.operation) {
 | |
|             case CPU_FETCH: brk = cpuFetch(sim); break;
 | |
| 
 | |
|             case CPU_ADD_IMM: brk = cpuADDImm(sim); break;
 | |
|             case CPU_ADD_REG: brk = cpuADDReg(sim); break;
 | |
|             case CPU_ADDI   : brk = cpuADDI  (sim); break;
 | |
|             case CPU_AND    : brk = cpuAND   (sim); break;
 | |
|             case CPU_ANDI   : brk = cpuANDI  (sim); break;
 | |
|             case CPU_BCOND  : brk = cpuBCOND (sim); break;
 | |
|             case CPU_CAXI   : brk = cpuCAXI  (sim); break;
 | |
|             case CPU_CLI    : brk = cpuCLI   (sim); break;
 | |
|             case CPU_CMP_IMM: brk = cpuCMPImm(sim); break;
 | |
|             case CPU_CMP_REG: brk = cpuCMPReg(sim); break;
 | |
|             case CPU_DIV    : brk = cpuDIV   (sim); break;
 | |
|             case CPU_DIVU   : brk = cpuDIVU  (sim); break;
 | |
|             case CPU_HALT   : brk = cpuHALT  (sim); break;
 | |
|             case CPU_IN_B   : brk = cpuIN_B  (sim); break;
 | |
|             case CPU_IN_H   : brk = cpuIN_H  (sim); break;
 | |
|             case CPU_IN_W   : brk = cpuIN_W  (sim); break;
 | |
|             case CPU_JAL    : brk = cpuJAL   (sim); break;
 | |
|             case CPU_JMP    : brk = cpuJMP   (sim); break;
 | |
|             case CPU_JR     : brk = cpuJR    (sim); break;
 | |
|             case CPU_LD_B   : brk = cpuLD_B  (sim); break;
 | |
|             case CPU_LD_H   : brk = cpuLD_H  (sim); break;
 | |
|             case CPU_LD_W   : brk = cpuLD_W  (sim); break;
 | |
|             case CPU_LDSR   : brk = cpuLDSR  (sim); break;
 | |
|             case CPU_MOV_IMM: brk = cpuMOVImm(sim); break;
 | |
|             case CPU_MOV_REG: brk = cpuMOVReg(sim); break;
 | |
|             case CPU_MOVEA  : brk = cpuMOVEA (sim); break;
 | |
|             case CPU_MOVHI  : brk = cpuMOVHI (sim); break;
 | |
|             case CPU_MPYHW  : brk = cpuMPYHW (sim); break;
 | |
|             case CPU_MUL    : brk = cpuMUL   (sim); break;
 | |
|             case CPU_MULU   : brk = cpuMULU  (sim); break;
 | |
|             case CPU_NOT    : brk = cpuNOT   (sim); break;
 | |
|             case CPU_OR     : brk = cpuOR    (sim); break;
 | |
|             case CPU_ORI    : brk = cpuORI   (sim); break;
 | |
|             case CPU_OUT_B  : brk = cpuOUT_B (sim); break;
 | |
|             case CPU_OUT_H  : brk = cpuOUT_H (sim); break;
 | |
|             case CPU_OUT_W  : brk = cpuOUT_W (sim); break;
 | |
|             case CPU_RETI   : brk = cpuRETI  (sim); break;
 | |
|             case CPU_REV    : brk = cpuREV   (sim); break;
 | |
|             case CPU_SAR_IMM: brk = cpuSARImm(sim); break;
 | |
|             case CPU_SAR_REG: brk = cpuSARReg(sim); break;
 | |
|             case CPU_SEI    : brk = cpuSEI   (sim); break;
 | |
|             case CPU_SETF   : brk = cpuSETF  (sim); break;
 | |
|             case CPU_SHL_IMM: brk = cpuSHLImm(sim); break;
 | |
|             case CPU_SHL_REG: brk = cpuSHLReg(sim); break;
 | |
|             case CPU_SHR_IMM: brk = cpuSHRImm(sim); break;
 | |
|             case CPU_SHR_REG: brk = cpuSHRReg(sim); break;
 | |
|             case CPU_ST_B   : brk = cpuST_B  (sim); break;
 | |
|             case CPU_ST_H   : brk = cpuST_H  (sim); break;
 | |
|             case CPU_ST_W   : brk = cpuST_W  (sim); break;
 | |
|             case CPU_STSR   : brk = cpuSTSR  (sim); break;
 | |
|             case CPU_SUB    : brk = cpuSUB   (sim); break;
 | |
|             case CPU_TRAP   : brk = cpuTRAP  (sim); break;
 | |
|             case CPU_XB     : brk = cpuXB    (sim); break;
 | |
|             case CPU_XH     : brk = cpuXH    (sim); break;
 | |
|             case CPU_XOR    : brk = cpuXOR   (sim); break;
 | |
|             case CPU_XORI   : brk = cpuXORI  (sim); break;
 | |
| 
 | |
|             default: return -1; /* TODO: Temporary for debugging */
 | |
|         }
 | |
| 
 | |
|         /* A callback requested a break */
 | |
|         if (brk)
 | |
|             return 1;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* Determine how many clocks are guaranteed to process */
 | |
| static uint32_t cpuUntil(VB *sim, uint32_t clocks) {
 | |
|     return sim->cpu.clocks < clocks ? sim->cpu.clocks : clocks;
 | |
| }
 | |
| 
 | |
| #endif /* VBAPI */
 |