2021-09-06 00:09:15 +00:00
|
|
|
/* This file is included into vb.c and cannot be compiled on its own. */
|
|
|
|
#ifdef VBAPI
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/********************************* Constants *********************************/
|
|
|
|
|
2021-09-19 01:31:40 +00:00
|
|
|
/* Operations states */
|
|
|
|
#define CPU_FETCH 0
|
|
|
|
#define CPU_EXECUTE 1
|
|
|
|
#define CPU_EXCEPTION 2
|
|
|
|
#define CPU_HALTED 3
|
|
|
|
#define CPU_FATAL 4
|
|
|
|
|
2021-09-06 00:09:15 +00:00
|
|
|
/* Instruction IDs */
|
|
|
|
#define CPU_ILLEGAL -1
|
|
|
|
#define CPU_ADD_IMM 0
|
|
|
|
#define CPU_ADD_REG 1
|
|
|
|
#define CPU_ADDF_S 2
|
|
|
|
#define CPU_ADDI 3
|
|
|
|
#define CPU_AND 4
|
|
|
|
#define CPU_ANDBSU 5
|
|
|
|
#define CPU_ANDI 6
|
|
|
|
#define CPU_ANDNBSU 7
|
|
|
|
#define CPU_BCOND 8
|
|
|
|
#define CPU_CAXI 9
|
|
|
|
#define CPU_CLI 10
|
|
|
|
#define CPU_CMP_IMM 11
|
|
|
|
#define CPU_CMP_REG 12
|
|
|
|
#define CPU_CMPF_S 13
|
|
|
|
#define CPU_CVT_SW 14
|
|
|
|
#define CPU_CVT_WS 15
|
|
|
|
#define CPU_DIV 16
|
|
|
|
#define CPU_DIVF_S 17
|
|
|
|
#define CPU_DIVU 18
|
|
|
|
#define CPU_HALT 19
|
|
|
|
#define CPU_IN_B 20
|
|
|
|
#define CPU_IN_H 21
|
|
|
|
#define CPU_IN_W 22
|
|
|
|
#define CPU_JAL 23
|
|
|
|
#define CPU_JMP 24
|
|
|
|
#define CPU_JR 25
|
|
|
|
#define CPU_LD_B 26
|
|
|
|
#define CPU_LD_H 27
|
|
|
|
#define CPU_LD_W 28
|
|
|
|
#define CPU_LDSR 29
|
|
|
|
#define CPU_MOV_IMM 30
|
|
|
|
#define CPU_MOV_REG 31
|
|
|
|
#define CPU_MOVBSU 32
|
|
|
|
#define CPU_MOVEA 33
|
|
|
|
#define CPU_MOVHI 34
|
|
|
|
#define CPU_MPYHW 35
|
|
|
|
#define CPU_MUL 36
|
|
|
|
#define CPU_MULF_S 37
|
|
|
|
#define CPU_MULU 38
|
|
|
|
#define CPU_NOT 39
|
|
|
|
#define CPU_NOTBSU 40
|
|
|
|
#define CPU_OR 41
|
|
|
|
#define CPU_ORBSU 42
|
|
|
|
#define CPU_ORI 43
|
|
|
|
#define CPU_ORNBSU 44
|
|
|
|
#define CPU_OUT_B 45
|
|
|
|
#define CPU_OUT_H 46
|
|
|
|
#define CPU_OUT_W 47
|
|
|
|
#define CPU_RETI 48
|
|
|
|
#define CPU_REV 49
|
|
|
|
#define CPU_SAR_IMM 50
|
|
|
|
#define CPU_SAR_REG 51
|
|
|
|
#define CPU_SCH0BSD 52
|
|
|
|
#define CPU_SCH0BSU 53
|
|
|
|
#define CPU_SCH1BSD 54
|
|
|
|
#define CPU_SCH1BSU 55
|
|
|
|
#define CPU_SEI 56
|
|
|
|
#define CPU_SETF 57
|
|
|
|
#define CPU_SHL_IMM 58
|
|
|
|
#define CPU_SHL_REG 59
|
|
|
|
#define CPU_SHR_IMM 60
|
|
|
|
#define CPU_SHR_REG 61
|
|
|
|
#define CPU_ST_B 62
|
|
|
|
#define CPU_ST_H 63
|
|
|
|
#define CPU_ST_W 64
|
|
|
|
#define CPU_STSR 65
|
|
|
|
#define CPU_SUB 66
|
|
|
|
#define CPU_SUBF_S 67
|
|
|
|
#define CPU_TRAP 68
|
|
|
|
#define CPU_TRNC_SW 69
|
|
|
|
#define CPU_XB 70
|
|
|
|
#define CPU_XH 71
|
|
|
|
#define CPU_XOR 72
|
|
|
|
#define CPU_XORBSU 73
|
|
|
|
#define CPU_XORI 74
|
|
|
|
#define CPU_XORNBSU 75
|
|
|
|
#define CPU_BITSTRING 76
|
|
|
|
#define CPU_FLOATENDO 77
|
|
|
|
|
2021-09-19 01:31:40 +00:00
|
|
|
/* Mapping for opcodes to operation IDs */
|
|
|
|
static const int8_t CPU_OPCODES[] = {
|
|
|
|
CPU_MOV_REG, CPU_ADD_REG, CPU_SUB , CPU_CMP_REG ,
|
|
|
|
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 ,
|
|
|
|
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 ,
|
|
|
|
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 ,
|
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2021-09-06 00:09:15 +00:00
|
|
|
/* Mapping for bit string sub-opcodes */
|
|
|
|
static const int8_t CPU_BITSTRINGS[] = {
|
|
|
|
CPU_SCH0BSU, CPU_SCH0BSD, CPU_SCH1BSU, CPU_SCH1BSD,
|
|
|
|
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
|
|
|
|
CPU_ORBSU , CPU_ANDBSU , CPU_XORBSU , CPU_MOVBSU ,
|
|
|
|
CPU_ORNBSU , CPU_ANDNBSU, CPU_XORNBSU, CPU_NOTBSU ,
|
|
|
|
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
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Mapping for floating-point/Nintendo sub-opcodes */
|
2021-09-19 01:31:40 +00:00
|
|
|
static const int8_t CPU_FLOATENDOS[] = {
|
2021-09-06 00:09:15 +00:00
|
|
|
CPU_CMPF_S , CPU_ILLEGAL, CPU_CVT_WS , CPU_CVT_SW ,
|
|
|
|
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,
|
|
|
|
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,
|
|
|
|
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,
|
|
|
|
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
|
|
|
|
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL,
|
|
|
|
CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL
|
|
|
|
};
|
|
|
|
|
2021-09-19 01:31:40 +00:00
|
|
|
/* Instruction sizes by opcode */
|
|
|
|
static const uint8_t CPU_SIZES[] = {
|
|
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
|
|
2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
|
|
4, 4, 2, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************** Utility Functions *****************************/
|
|
|
|
|
|
|
|
/* Read a data unit from the bus */
|
|
|
|
static int cpuRead(VB *emu, uint32_t address, int8_t type) {
|
|
|
|
|
|
|
|
/* TODO: Determine clock count here */
|
|
|
|
emu->cpu.access.clocks = 0;
|
|
|
|
|
|
|
|
/* Not using a breakpoint callback */
|
|
|
|
if (emu->onRead == NULL) {
|
|
|
|
emu->cpu.access.value = busRead(emu, address, type, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Invoke the breakpoint callback */
|
|
|
|
emu->cpu.access.address = address;
|
|
|
|
emu->cpu.access.type = type;
|
|
|
|
return emu->onRead(emu, &emu->cpu.access);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read a data unit from the bus for the purpose of an instruction fetch */
|
|
|
|
static int cpuReadFetch(VB *emu, uint32_t address) {
|
|
|
|
|
|
|
|
/* TODO: Determine clock count here */
|
|
|
|
emu->cpu.access.clocks = 0;
|
|
|
|
|
|
|
|
/* Not using a breakpoint callback */
|
|
|
|
if (emu->onFetch == NULL) {
|
|
|
|
emu->cpu.access.value = busRead(emu, address, VB_U16, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Invoke the breakpoint callback */
|
|
|
|
emu->cpu.access.address = address;
|
|
|
|
emu->cpu.access.type = VB_U16;
|
|
|
|
return emu->onFetch(emu, emu->cpu.fetch, &emu->cpu.access);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write a data unit to the bus */
|
|
|
|
static int cpuWrite(VB *emu, uint32_t address, int8_t type, int32_t value) {
|
|
|
|
|
|
|
|
/* TODO: Determine clock count here */
|
|
|
|
emu->cpu.access.clocks = 0;
|
|
|
|
emu->cpu.access.type = type;
|
|
|
|
|
|
|
|
/* Using a breakpoint callback */
|
|
|
|
if (emu->onWrite != NULL) {
|
|
|
|
emu->cpu.access.value = value;
|
|
|
|
if (emu->onWrite(emu, &emu->cpu.access))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write the value if the operation wasn't cancelled */
|
|
|
|
if (emu->cpu.access.type != VB_CANCEL)
|
|
|
|
busWrite(emu, address, type, value, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**************************** Instruction Helpers ****************************/
|
|
|
|
|
|
|
|
/* Add two numbers and update the flags */
|
|
|
|
static int32_t cpuAdd(VB *emu, int32_t left, int32_t right) {
|
|
|
|
int32_t result = left + right;
|
|
|
|
emu->cpu.clocks = 1;
|
|
|
|
emu->cpu.psw.cy = (uint32_t) result < (uint32_t) left;
|
|
|
|
emu->cpu.psw.ov = (~(left ^ right) & (left ^ result)) >> 31 & 1;
|
|
|
|
emu->cpu.psw.s = result < 0;
|
|
|
|
emu->cpu.psw.z = result == 0;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Bit string search */
|
|
|
|
static int cpuBitSearch(VB *emu, int bit, int dir) {
|
|
|
|
int32_t offset; /* Bit offset in source word */
|
|
|
|
int32_t value; /* Alias of source word */
|
|
|
|
|
|
|
|
/* The bit string is of zero length */
|
|
|
|
if (emu->cpu.program[28] == 0) {
|
|
|
|
emu->cpu.clocks = dir == 1 ? 13 : 15;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the source word */
|
|
|
|
if (!emu->cpu.busWait && !emu->cpu.substring) {
|
|
|
|
|
|
|
|
/* Initialize state */
|
|
|
|
emu->cpu.program[30] &= 0xFFFFFFFC;
|
|
|
|
emu->cpu.program[27] &= 0x0000001F;
|
|
|
|
emu->cpu.psw.z = 1;
|
|
|
|
|
|
|
|
/* Read the data unit from the bus */
|
|
|
|
if (cpuRead(emu, emu->cpu.program[30], VB_S32))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 1;
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search the bit string */
|
|
|
|
for (
|
|
|
|
offset = emu->cpu.program[27], value = emu->cpu.access.value;
|
|
|
|
emu->cpu.program[28] != 0 && (offset & 31) == offset;
|
|
|
|
offset += dir, emu->cpu.program[28]--, emu->cpu.program[29]++
|
|
|
|
) {
|
|
|
|
|
|
|
|
/* The current bit does not match */
|
|
|
|
if ((value >> bit & 1) != bit)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* The current bit matches */
|
|
|
|
emu->cpu.substring = 0;
|
|
|
|
emu->cpu.clocks = dir == 1 ? 46 : 51;
|
|
|
|
emu->cpu.psw.z = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No bit in the current word matches */
|
|
|
|
emu->cpu.substring = 1;
|
|
|
|
emu->cpu.clocks = 5;
|
|
|
|
emu->cpu.program[30] += dir << 2;
|
|
|
|
emu->cpu.program[27] &= 0x0000001F;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Bit string bitwise operation */
|
|
|
|
static int cpuBitString(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t dest; /* Destination word value */
|
|
|
|
int32_t src; /* Source word value */
|
|
|
|
|
|
|
|
/* Initial invocation */
|
|
|
|
if (emu->cpu.busWait == 0 && !emu->cpu.substring) {
|
|
|
|
|
|
|
|
/* Initialize state */
|
|
|
|
emu->cpu.program[30] &= 0xFFFFFFFC;
|
|
|
|
emu->cpu.program[29] &= 0xFFFFFFFC;
|
|
|
|
emu->cpu.program[27] &= 0x0000001F;
|
|
|
|
emu->cpu.program[26] &= 0x0000001F;
|
|
|
|
|
|
|
|
/* The bit string is of zero length */
|
|
|
|
if (emu->cpu.program[28] == 0) {
|
|
|
|
emu->cpu.clocks = 20;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the data unit from the bus */
|
|
|
|
if (cpuRead(emu, emu->cpu.program[30], VB_S32))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
inst->aux[0] = emu->cpu.access.value;
|
|
|
|
emu->cpu.busWait = 1;
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the next source word */
|
|
|
|
if (emu->cpu.busWait == 1) {
|
|
|
|
|
|
|
|
/* Read the data unit from the bus */
|
|
|
|
if (cpuRead(emu, emu->cpu.program[30] + 4, VB_S32))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
inst->aux[1] = emu->cpu.access.value;
|
|
|
|
emu->cpu.busWait = 2;
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the destination word */
|
|
|
|
if (emu->cpu.busWait == 2) {
|
|
|
|
|
|
|
|
/* Read the data unit from the bus */
|
|
|
|
if (cpuRead(emu, emu->cpu.program[29], VB_S32))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 3;
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compute and store the destination word */
|
|
|
|
if (emu->cpu.busWait == 3) {
|
|
|
|
dest = emu->cpu.access.value;
|
|
|
|
src = ((int64_t) inst->aux[1] << 32 | (uint32_t) inst->aux[0]) >>
|
|
|
|
(32 + emu->cpu.program[27] - emu->cpu.program[26]);
|
|
|
|
|
|
|
|
/* Perform the operation */
|
|
|
|
switch (inst->bits[0] & 7) {
|
|
|
|
case 0 : dest |= src; break; /* ORBSU */
|
|
|
|
case 1 : dest &= src; break; /* ANDBSU */
|
|
|
|
case 2 : dest ^= src; break; /* XORBSU */
|
|
|
|
case 3 : dest = src; break; /* MOVBSU */
|
|
|
|
case 4 : dest |= ~src; break; /* ORNBSU */
|
|
|
|
case 5 : dest &= ~src; break; /* ANDNBSU */
|
|
|
|
case 6 : dest ^= ~src; break; /* XORNBSU */
|
|
|
|
default: dest = ~src; break; /* NOTBSU */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Incorporate only the bits occupied by the bit string */
|
|
|
|
src = (int32_t) 0xFFFFFFFF << emu->cpu.program[26];
|
|
|
|
if ((uint32_t)emu->cpu.program[28] < (uint32_t)32-emu->cpu.program[26])
|
|
|
|
src &= (1 << (emu->cpu.program[28] + emu->cpu.program[26])) - 1;
|
|
|
|
dest = (dest & src) | (emu->cpu.access.value & ~src);
|
|
|
|
|
|
|
|
/* Write the data unit to the bus */
|
|
|
|
if (cpuWrite(emu, emu->cpu.program[30], VB_S32, dest))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 4;
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Working variables */
|
|
|
|
dest = 32 - emu->cpu.program[26]; /* Bits processed this invocation */
|
|
|
|
if ((uint32_t) emu->cpu.program[28] < (uint32_t) dest)
|
|
|
|
dest = emu->cpu.program[28];
|
|
|
|
src = emu->cpu.program[30] + dest; /* New source bit offset */
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 0;
|
|
|
|
emu->cpu.substring = dest != emu->cpu.program[28];
|
|
|
|
if (src >= 32) {
|
|
|
|
inst->aux[0] = inst->aux[1];
|
|
|
|
emu->cpu.program[30] += 4;
|
|
|
|
}
|
|
|
|
emu->cpu.program[29] += 4;
|
|
|
|
emu->cpu.program[28] -= dest;
|
|
|
|
emu->cpu.program[27] = src & 0x1F;
|
|
|
|
emu->cpu.program[26] = 0;
|
|
|
|
emu->cpu.clocks = emu->cpu.substring ? 6 : 36;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Common processing for most bitwise operations */
|
|
|
|
static int32_t cpuBitwise(VB *emu, int32_t result) {
|
|
|
|
emu->cpu.clocks = 1;
|
|
|
|
emu->cpu.psw.ov = 0;
|
|
|
|
emu->cpu.psw.s = result < 0;
|
|
|
|
emu->cpu.psw.z = result == 0;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Test a condition code */
|
|
|
|
static int cpuCondition(VB *emu, int cond) {
|
|
|
|
|
|
|
|
/* Falsey condition */
|
|
|
|
if (cond > 7)
|
|
|
|
return !cpuCondition(emu, cond & 7);
|
|
|
|
|
|
|
|
/* Truthy condition */
|
|
|
|
switch (cond) {
|
|
|
|
case 0: return emu->cpu.psw.ov; /* V */
|
|
|
|
case 1: return emu->cpu.psw.cy; /* L */
|
|
|
|
case 2: return emu->cpu.psw.z; /* Z */
|
|
|
|
case 3: return emu->cpu.psw.cy | emu->cpu.psw.z; /* NH */
|
|
|
|
case 4: return emu->cpu.psw.s; /* N */
|
|
|
|
case 5: return 1; /* T */
|
|
|
|
case 6: return emu->cpu.psw.ov ^ emu->cpu.psw.s; /* LT */
|
|
|
|
default:return(emu->cpu.psw.ov^emu->cpu.psw.s)|emu->cpu.psw.z; /* LE */
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for a floating-point reserved operand */
|
|
|
|
static int cpuFloatReserved(VB *emu, int32_t bits) {
|
|
|
|
uint8_t e = bits >> 23; /* Exponent field */
|
|
|
|
|
|
|
|
/* Not reserved */
|
|
|
|
if (e != 0xFF && (e != 0x00 || (bits & 0x007FFFFF) == 0))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Reserved */
|
|
|
|
emu->cpu.causeCode = 0xFF60;
|
|
|
|
emu->cpu.psw.fro = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert a 32-bit integer to the represented floating-point value */
|
|
|
|
static double cpuFloatOperand(VB *emu, int32_t bits) {
|
|
|
|
return cpuFloatReserved(emu, bits) ? 0 : (double) *(float *) &bits;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert a floating-point result to bits as a 32-bit integer */
|
|
|
|
static int32_t cpuFloatResult(VB *emu, double resultd) {
|
|
|
|
float resultf; /* 32-bit conversion of result */
|
|
|
|
int32_t ret; /* Output value */
|
|
|
|
|
|
|
|
/* Overflow */
|
|
|
|
if (resultd < -FLT_MAX || resultd > FLT_MAX) {
|
|
|
|
emu->cpu.causeCode = 0xFF64;
|
|
|
|
emu->cpu.psw.fov = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Underflow */
|
|
|
|
if (resultd > -FLT_MIN && resultd < FLT_MIN) {
|
|
|
|
emu->cpu.psw.fud = 1;
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Normalized */
|
|
|
|
else {
|
|
|
|
resultf = (float) resultd;
|
|
|
|
ret = *(int32_t *) &resultf;
|
|
|
|
if (ret == INT32_MIN)
|
|
|
|
ret = 0; /* Prevent negative zero */
|
|
|
|
|
|
|
|
/* Precision degradation */
|
|
|
|
if (resultf != resultd)
|
|
|
|
emu->cpu.psw.fpr = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.psw.cy = ret < 0;
|
|
|
|
emu->cpu.psw.ov = 0;
|
|
|
|
emu->cpu.psw.s = ret < 0;
|
|
|
|
emu->cpu.psw.z = ret == 0;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert a floating-point value to a word value */
|
|
|
|
static void cpuFloatToWord(VB *emu, VB_INSTRUCTION *inst, int truncate) {
|
|
|
|
int32_t bits = emu->cpu.program[inst->bits[0] & 0x1F];
|
|
|
|
int32_t result; /* Output value */
|
|
|
|
int32_t x; /* Working variable field */
|
|
|
|
|
|
|
|
/* Zero */
|
|
|
|
if ((bits & 0x7FFFFFFF) == 0)
|
|
|
|
result = 0;
|
|
|
|
|
|
|
|
/* Minimum value */
|
|
|
|
else if (bits == (int32_t) 0xCF000000)
|
|
|
|
result = INT32_MIN;
|
|
|
|
|
|
|
|
/* Conversion */
|
|
|
|
else {
|
|
|
|
x = bits >> 23 & 0xFF; /* Exponent field */
|
|
|
|
|
|
|
|
/* Reserved operand */
|
|
|
|
if (x == 0xFF || x == 0x00) {
|
|
|
|
emu->cpu.causeCode = 0xFF60;
|
|
|
|
emu->cpu.psw.fro = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Invalid operation */
|
|
|
|
if (x >= 135) {
|
|
|
|
emu->cpu.causeCode = 0xFF70;
|
|
|
|
emu->cpu.psw.fiv = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse significand bits */
|
|
|
|
result = (bits & 0x007FFFFF) | 0x00800000;
|
|
|
|
|
|
|
|
/* Left shift */
|
|
|
|
if (x >= 127)
|
|
|
|
result <<= x - 127;
|
|
|
|
|
|
|
|
/* Right shift */
|
|
|
|
else {
|
|
|
|
x = 127 - x; /* Number of bits to shift */
|
|
|
|
result >>= x; /* Update state */
|
|
|
|
x = 1 << x; /* Position of the "one halfths" bit */
|
|
|
|
|
|
|
|
/* Precision degradation */
|
|
|
|
if ((bits & (x - 1)) != 0) {
|
|
|
|
emu->cpu.psw.fpr = 1;
|
|
|
|
|
|
|
|
/* Apply rounding */
|
|
|
|
if (!truncate && (bits & x) != 0)
|
|
|
|
result += x;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Incorporate sign */
|
|
|
|
if (bits < 0)
|
|
|
|
result = -result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = result;
|
|
|
|
emu->cpu.psw.ov = 0;
|
|
|
|
emu->cpu.psw.s = result < 0;
|
|
|
|
emu->cpu.psw.z = result == 0;
|
|
|
|
emu->cpu.clocks = 14;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform a jump */
|
|
|
|
static void cpuJump(VB *emu, VB_INSTRUCTION *inst, uint32_t address) {
|
|
|
|
emu->cpu.pcFrom = emu->cpu.pc;
|
|
|
|
emu->cpu.pc = address & 0xFFFFFFFE;
|
|
|
|
emu->cpu.pcTo = emu->cpu.pc;
|
|
|
|
emu->cpu.clocks = 3;
|
|
|
|
inst->size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform an input or load */
|
|
|
|
static int cpuLoad(VB *emu,VB_INSTRUCTION *inst,uint8_t type,uint32_t clocks) {
|
|
|
|
|
|
|
|
/* Initiate the read */
|
|
|
|
if (!emu->cpu.busWait) {
|
|
|
|
|
|
|
|
/* Read the data unit from the bus */
|
|
|
|
if (cpuRead(
|
|
|
|
emu,
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F] +
|
|
|
|
SignExtend((int32_t) inst->bits[1], 16),
|
|
|
|
type
|
|
|
|
)) return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 1;
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Complete the read */
|
|
|
|
emu->cpu.busWait = 0;
|
|
|
|
emu->cpu.clocks = clocks;
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = emu->cpu.access.value;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Specify a new value for a system register */
|
|
|
|
static uint32_t cpuSetSystemRegister(VB *emu,int id,uint32_t value,int debug) {
|
|
|
|
switch (id) {
|
|
|
|
case VB_ADTRE: return emu->cpu.adtre = value & 0xFFFFFFFE;
|
|
|
|
case VB_EIPC : return emu->cpu.eipc = value & 0xFFFFFFFE;
|
|
|
|
case VB_EIPSW: return emu->cpu.eipsw = value & 0x000FF3FF;
|
|
|
|
case VB_FEPC : return emu->cpu.fepc = value & 0xFFFFFFFE;
|
|
|
|
case VB_FEPSW: return emu->cpu.fepsw = value & 0x000FF3FF;
|
|
|
|
case VB_PIR : return 0x00005346;
|
|
|
|
case VB_TKCW : return 0x000000E0;
|
|
|
|
case 29 : return emu->cpu.sr29 = value & 0x00000001;
|
|
|
|
case 31 :
|
|
|
|
if (!debug && (int32_t) value < 0)
|
|
|
|
value = ~value + 1;
|
|
|
|
return emu->cpu.sr31 = value;
|
|
|
|
case VB_CHCW :
|
|
|
|
emu->cpu.chcw.ice = value >> 1 & 1;
|
|
|
|
/* TODO: Perform dump/restore operations */
|
|
|
|
return value & 0x00000002;
|
|
|
|
case VB_ECR :
|
|
|
|
if (debug) {
|
|
|
|
emu->cpu.ecr.fecc = value >> 16;
|
|
|
|
emu->cpu.ecr.eicc = value;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
return vbGetSystemRegister(emu, id);
|
|
|
|
case VB_PSW :
|
|
|
|
emu->cpu.psw.i = value >> 16 & 15;
|
|
|
|
emu->cpu.psw.np = value >> 15 & 1;
|
|
|
|
emu->cpu.psw.ep = value >> 14 & 1;
|
|
|
|
emu->cpu.psw.ae = value >> 13 & 1;
|
|
|
|
emu->cpu.psw.id = value >> 12 & 1;
|
|
|
|
emu->cpu.psw.fro = value >> 9 & 1;
|
|
|
|
emu->cpu.psw.fiv = value >> 8 & 1;
|
|
|
|
emu->cpu.psw.fzd = value >> 7 & 1;
|
|
|
|
emu->cpu.psw.fov = value >> 6 & 1;
|
|
|
|
emu->cpu.psw.fud = value >> 5 & 1;
|
|
|
|
emu->cpu.psw.fpr = value >> 4 & 1;
|
|
|
|
emu->cpu.psw.cy = value >> 3 & 1;
|
|
|
|
emu->cpu.psw.ov = value >> 2 & 1;
|
|
|
|
emu->cpu.psw.s = value >> 1 & 1;
|
|
|
|
emu->cpu.psw.z = value & 1;
|
|
|
|
return value & 0x000FF3FF;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform a right shift */
|
|
|
|
static int cpuShiftRight(VB *emu, int32_t value, int bits, int arithmetic) {
|
|
|
|
if (bits != 0) {
|
|
|
|
emu->cpu.psw.cy = (value >> (bits - 1)) & 1;
|
2021-09-20 12:51:12 +00:00
|
|
|
value = value >> bits & (((uint32_t) 1 << (32 - bits)) - 1);
|
2021-09-19 01:31:40 +00:00
|
|
|
if (arithmetic)
|
|
|
|
value = SignExtend(value, 32 - bits);
|
|
|
|
} else emu->cpu.psw.cy = 0;
|
|
|
|
return cpuBitwise(emu, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform an output or store */
|
|
|
|
static int cpuStore(VB *emu,VB_INSTRUCTION *inst,uint8_t type,uint32_t clocks){
|
|
|
|
|
|
|
|
/* Initiate the write */
|
|
|
|
if (!emu->cpu.busWait) {
|
|
|
|
|
|
|
|
/* Read the data unit from the bus */
|
|
|
|
if (cpuWrite(
|
|
|
|
emu,
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F] +
|
|
|
|
SignExtend((int32_t) inst->bits[1], 16),
|
|
|
|
type,
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F]
|
|
|
|
)) return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 1;
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Complete the write */
|
|
|
|
emu->cpu.busWait = 0;
|
|
|
|
emu->cpu.clocks = clocks;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Subtract two numbers and update the flags */
|
|
|
|
static int32_t cpuSubtract(VB *emu, int32_t left, int32_t right) {
|
|
|
|
int32_t result = left - right;
|
|
|
|
emu->cpu.clocks = 1;
|
|
|
|
emu->cpu.psw.cy = (uint32_t) result > (uint32_t) left;
|
|
|
|
emu->cpu.psw.ov = ((left ^ right) & (left ^ result)) >> 31 & 1;
|
|
|
|
emu->cpu.psw.s = result < 0;
|
|
|
|
emu->cpu.psw.z = result == 0;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/************************ Instruction Implementations ************************/
|
|
|
|
|
|
|
|
/* Add Immediate */
|
|
|
|
static void cpuADD_IMM(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
int32_t right = inst->bits[0] & 0x1F;
|
|
|
|
*reg2 = cpuAdd(emu, *reg2, SignExtend(right, 5));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add Register */
|
|
|
|
static void cpuADD_REG(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
*reg2 = cpuAdd(emu, *reg2, emu->cpu.program[inst->bits[0] & 0x1F]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add Floating Short */
|
|
|
|
static void cpuADDF_S(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
double left = cpuFloatOperand(emu, *reg2);
|
|
|
|
double right = cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]&0x1F]);
|
|
|
|
int32_t result; /* Output bits */
|
|
|
|
|
|
|
|
/* Perform the operation */
|
|
|
|
if (emu->cpu.causeCode == 0)
|
|
|
|
result = cpuFloatResult(emu, left + right);
|
|
|
|
if (emu->cpu.causeCode != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
*reg2 = result;
|
|
|
|
emu->cpu.clocks = 28;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add Immediate */
|
|
|
|
static void cpuADDI(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t right = inst->bits[1];
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuAdd(emu,
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F], SignExtend(right, 16));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* And */
|
|
|
|
static void cpuAND(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
*reg2 = cpuBitwise(emu, *reg2 & emu->cpu.program[inst->bits[0] & 0x1F]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* And Bit String Upward */
|
|
|
|
#define cpuANDBSU(emu, inst) cpuBitString(emu, inst)
|
|
|
|
|
|
|
|
/* And Immediate */
|
|
|
|
static void cpuANDI(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu,
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F] & inst->bits[1]);
|
|
|
|
}
|
|
|
|
|
2021-09-19 19:36:30 +00:00
|
|
|
/* And Not Bit String Upward */
|
|
|
|
#define cpuANDNBSU(emu, inst) cpuBitString(emu, inst)
|
|
|
|
|
2021-09-19 01:31:40 +00:00
|
|
|
/* Conditional Branch */
|
|
|
|
static void cpuBCOND(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t disp; /* Target address displacement */
|
|
|
|
|
|
|
|
/* Branch to the target address */
|
|
|
|
if (cpuCondition(emu, inst->bits[0] >> 9 & 15)) {
|
|
|
|
disp = inst->bits[0] & 0x1FF;
|
|
|
|
cpuJump(emu, inst, emu->cpu.pc + SignExtend(disp, 9));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do not branch */
|
|
|
|
else emu->cpu.clocks = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare And Exchahge Interlocked */
|
|
|
|
static int cpuCAXI(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2; /* Program register reg2 */
|
|
|
|
|
|
|
|
/* Read the lock word */
|
|
|
|
if (emu->cpu.busWait == 0) {
|
|
|
|
|
|
|
|
/* Compute the address of the lock word */
|
|
|
|
inst->aux[0] = emu->cpu.program[inst->bits[0] & 0x1F] +
|
|
|
|
SignExtend((int32_t) inst->bits[1], 16);
|
|
|
|
|
|
|
|
/* Read the data unit from the bus */
|
|
|
|
if (cpuRead(emu, inst->aux[0], VB_S32))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 1;
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare and exchange */
|
|
|
|
if (emu->cpu.busWait == 1) {
|
|
|
|
|
|
|
|
/* Process the lock word */
|
|
|
|
reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
cpuSubtract(emu, *reg2, emu->cpu.access.value);
|
|
|
|
*reg2 = emu->cpu.access.value;
|
|
|
|
|
|
|
|
/* Store the exchange value to the bus */
|
|
|
|
if (cpuWrite(emu, inst->aux[0], VB_S32,
|
|
|
|
emu->cpu.psw.z ? emu->cpu.program[30] : emu->cpu.access.value))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
emu->cpu.busWait = 2;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 0;
|
|
|
|
emu->cpu.clocks = 26;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear Interrupt Disable Flag */
|
|
|
|
static void cpuCLI(VB *emu) {
|
|
|
|
emu->cpu.psw.id = 0;
|
|
|
|
emu->cpu.clocks = 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare Immediate */
|
|
|
|
static void cpuCMP_IMM(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t right = inst->bits[0] & 0x1F;
|
|
|
|
cpuSubtract(emu, emu->cpu.program[inst->bits[0] >> 5 & 0x1F],
|
|
|
|
SignExtend(right, 5));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare Register */
|
|
|
|
static void cpuCMP_REG(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
cpuSubtract(emu, emu->cpu.program[inst->bits[0] >> 5 & 0x1F],
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare Floating Short */
|
|
|
|
static void cpuCMPF_S(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
double left =cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]>>5&0x1F]);
|
|
|
|
double right=cpuFloatOperand(emu,emu->cpu.program[inst->bits[0] &0x1F]);
|
|
|
|
|
|
|
|
/* Perform the operation */
|
|
|
|
if (emu->cpu.causeCode == 0)
|
|
|
|
cpuFloatResult(emu, left - right);
|
|
|
|
if (emu->cpu.causeCode != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.clocks = 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert Short Floating to Word Integer */
|
|
|
|
#define cpuCVT_SW(emu, inst) cpuFloatToWord(emu, inst, 0)
|
|
|
|
|
|
|
|
/* Convert Word Integer to Short Floating */
|
|
|
|
static void cpuCVT_WS(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t value = emu->cpu.program[inst->bits[0] & 0x1F];
|
|
|
|
float result = (float) value;
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = *(int32_t *) &result;
|
|
|
|
if (result != value)
|
|
|
|
emu->cpu.psw.fpr = 1;
|
|
|
|
emu->cpu.psw.cy = result < 0;
|
|
|
|
emu->cpu.psw.ov = 0;
|
|
|
|
emu->cpu.psw.s = result < 0;
|
|
|
|
emu->cpu.psw.z = result == 0;
|
|
|
|
emu->cpu.clocks = 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Divide */
|
|
|
|
static void cpuDIV(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t right = emu->cpu.program[inst->bits[0] & 0x1F];
|
|
|
|
int32_t *reg2; /* Program register reg2 */
|
|
|
|
|
|
|
|
/* Zero division */
|
|
|
|
if (right == 0) {
|
|
|
|
emu->cpu.causeCode = 0xFF80;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Special case */
|
|
|
|
reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
if (*reg2 == INT32_MIN && right == -1) {
|
|
|
|
emu->cpu.program[30] = 0;
|
|
|
|
emu->cpu.psw.ov = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform the operation */
|
|
|
|
else {
|
|
|
|
emu->cpu.program[30] = *reg2 % right;
|
|
|
|
*reg2 /= right;
|
|
|
|
emu->cpu.psw.ov = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.psw.s = *reg2 < 0;
|
|
|
|
emu->cpu.psw.z = *reg2 == 0;
|
|
|
|
emu->cpu.clocks = 38;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Divide Floating Short */
|
|
|
|
static void cpuDIVF_S(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
double left = cpuFloatOperand(emu, *reg2);
|
|
|
|
double right = cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]&0x1F]);
|
|
|
|
int32_t result; /* Output bits */
|
|
|
|
|
|
|
|
/* Reserved operand */
|
|
|
|
if (emu->cpu.causeCode != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Zero division */
|
|
|
|
if (right == 0) {
|
|
|
|
|
|
|
|
/* Invalid operation */
|
|
|
|
if (left == 0) {
|
|
|
|
emu->cpu.causeCode = 0xFF70;
|
|
|
|
emu->cpu.psw.fiv = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Zero division */
|
|
|
|
else {
|
|
|
|
emu->cpu.causeCode = 0xFF68;
|
|
|
|
emu->cpu.psw.fzd = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform the operation */
|
|
|
|
result = cpuFloatResult(emu, left / right);
|
|
|
|
if (emu->cpu.causeCode != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
*reg2 = result;
|
|
|
|
emu->cpu.clocks = 44;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Divide Unsigned */
|
|
|
|
static void cpuDIVU(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
uint32_t right = emu->cpu.program[inst->bits[0] & 0x1F];
|
|
|
|
uint32_t *reg2; /* Program register reg2 */
|
|
|
|
|
|
|
|
/* Zero division */
|
|
|
|
if (right == 0) {
|
|
|
|
emu->cpu.causeCode = 0xFF80;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform the operation */
|
|
|
|
reg2 = (uint32_t *) &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
emu->cpu.program[30] = *reg2 % right;
|
|
|
|
*reg2 /= right;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.psw.ov = 0;
|
|
|
|
emu->cpu.psw.s = *reg2 >> 31 & 1;
|
|
|
|
emu->cpu.psw.z = *reg2 == 0;
|
|
|
|
emu->cpu.clocks = 36;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Halt */
|
|
|
|
static void cpuHALT(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
emu->cpu.state = CPU_HALTED;
|
|
|
|
inst->size = 0;
|
|
|
|
/* TODO: Find out how many clocks this takes */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Input Byte */
|
|
|
|
#define cpuIN_B(emu, inst) cpuLoad(emu, inst, VB_U8, 5)
|
|
|
|
|
|
|
|
/* Input Halfword */
|
|
|
|
#define cpuIN_H(emu, inst) cpuLoad(emu, inst, VB_U16, 5)
|
|
|
|
|
|
|
|
/* Input Word */
|
|
|
|
#define cpuIN_W(emu, inst) cpuLoad(emu, inst, VB_S32, 5)
|
|
|
|
|
|
|
|
/* Jump and Link */
|
|
|
|
static void cpuJAL(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t disp = ((int32_t) inst->bits[0]<<16 | inst->bits[1]) & 0x03FFFFFF;
|
|
|
|
emu->cpu.program[31] = emu->cpu.pc + 4;
|
|
|
|
cpuJump(emu, inst, emu->cpu.pc + SignExtend(disp, 26));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Jump Register */
|
|
|
|
#define cpuJMP(emu,inst) cpuJump(emu,inst,emu->cpu.program[inst->bits[0]&0x1F])
|
|
|
|
|
|
|
|
/* Jump Relative */
|
|
|
|
static void cpuJR(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t disp = ((int32_t) inst->bits[0]<<16 | inst->bits[1]) & 0x03FFFFFF;
|
|
|
|
cpuJump(emu, inst, emu->cpu.pc + SignExtend(disp, 26));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Load Byte */
|
|
|
|
#define cpuLD_B(emu, inst) cpuLoad(emu, inst, VB_S8, 5)
|
|
|
|
|
|
|
|
/* Load Halfword */
|
|
|
|
#define cpuLD_H(emu, inst) cpuLoad(emu, inst, VB_S16, 5)
|
|
|
|
|
|
|
|
/* Load Word */
|
|
|
|
#define cpuLD_W(emu, inst) cpuLoad(emu, inst, VB_S32, 5)
|
|
|
|
|
|
|
|
/* Load to System Register */
|
|
|
|
static void cpuLDSR(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
cpuSetSystemRegister(emu, inst->bits[0] & 0x1F,
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F], 0);
|
|
|
|
emu->cpu.clocks = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Move Immediate */
|
|
|
|
static void cpuMOV_IMM(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t value = inst->bits[0] & 0x1F;
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = SignExtend(value, 5);
|
|
|
|
emu->cpu.clocks = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Move Register */
|
|
|
|
static void cpuMOV_REG(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] =
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F];
|
|
|
|
emu->cpu.clocks = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Move Bit String Upward */
|
|
|
|
#define cpuMOVBSU(emu, inst) cpuBitString(emu, inst)
|
|
|
|
|
|
|
|
/* Add */
|
|
|
|
static void cpuMOVEA(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t right = inst->bits[1];
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] =
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F] + SignExtend(right, 16);
|
|
|
|
emu->cpu.clocks = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add */
|
|
|
|
static void cpuMOVHI(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] =
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F] + ((int32_t)inst->bits[1]<<16);
|
|
|
|
emu->cpu.clocks = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Multiply Halfword */
|
|
|
|
static void cpuMPYHW(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t right = emu->cpu.program[inst->bits[0] & 0x1F] & 0x0001FFFF;
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] *= SignExtend(right, 17);
|
|
|
|
emu->cpu.clocks = 9;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Multiply */
|
|
|
|
static void cpuMUL(VB *emu, VB_INSTRUCTION *inst) {
|
2021-09-20 12:51:12 +00:00
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
2021-09-19 01:31:40 +00:00
|
|
|
int64_t result = (int64_t) *reg2 *
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F];
|
|
|
|
emu->cpu.program[30] = result >> 32;
|
|
|
|
*reg2 = result;
|
|
|
|
emu->cpu.psw.ov = *reg2 != result;
|
|
|
|
emu->cpu.psw.s = *reg2 < 0;
|
|
|
|
emu->cpu.psw.z = *reg2 == 0;
|
|
|
|
emu->cpu.clocks = 13;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Multiply Floating Short */
|
|
|
|
static void cpuMULF_S(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
double left = cpuFloatOperand(emu, *reg2);
|
|
|
|
double right = cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]&0x1F]);
|
|
|
|
int32_t result; /* Output bits */
|
|
|
|
|
|
|
|
/* Perform the operation */
|
|
|
|
if (emu->cpu.causeCode == 0)
|
|
|
|
result = cpuFloatResult(emu, left * right);
|
|
|
|
if (emu->cpu.causeCode != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
*reg2 = result;
|
|
|
|
emu->cpu.clocks = 30;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Multiply Unsigned */
|
|
|
|
static void cpuMULU(VB *emu, VB_INSTRUCTION *inst) {
|
2021-09-20 12:51:12 +00:00
|
|
|
uint32_t *reg2 = (uint32_t *) &emu->cpu.program[inst->bits[0]>>5&0x1F];
|
2021-09-19 01:31:40 +00:00
|
|
|
uint64_t result = (uint64_t) *reg2 *
|
|
|
|
(uint32_t) emu->cpu.program[inst->bits[0] & 0x1F];
|
|
|
|
emu->cpu.program[30] = result >> 32;
|
|
|
|
*reg2 = result;
|
|
|
|
emu->cpu.psw.ov = *reg2 != result;
|
|
|
|
emu->cpu.psw.s = *reg2 >> 31 & 1;
|
|
|
|
emu->cpu.psw.z = *reg2 == 0;
|
|
|
|
emu->cpu.clocks = 13;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Not */
|
|
|
|
static void cpuNOT(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] =
|
|
|
|
cpuBitwise(emu, ~emu->cpu.program[inst->bits[0] & 0x1F]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Not Bit String Upward */
|
|
|
|
#define cpuNOTBSU(emu, inst) cpuBitString(emu, inst)
|
|
|
|
|
|
|
|
/* Or */
|
|
|
|
static void cpuOR(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
*reg2 = cpuBitwise(emu, *reg2 | emu->cpu.program[inst->bits[0] & 0x1F]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Or Bit String Upward */
|
|
|
|
#define cpuORBSU(emu, inst) cpuBitString(emu, inst)
|
|
|
|
|
|
|
|
/* Or Immediate */
|
|
|
|
static void cpuORI(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu,
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F] | inst->bits[1]);
|
|
|
|
}
|
|
|
|
|
2021-09-19 19:36:30 +00:00
|
|
|
/* Or Not Bit String Upward */
|
|
|
|
#define cpuORNBSU(emu, inst) cpuBitString(emu, inst)
|
|
|
|
|
2021-09-19 01:31:40 +00:00
|
|
|
/* Output Byte */
|
|
|
|
#define cpuOUT_B(emu, inst) cpuStore(emu, inst, VB_U8, 4)
|
|
|
|
|
|
|
|
/* Output Halfword */
|
|
|
|
#define cpuOUT_H(emu, inst) cpuStore(emu, inst, VB_U16, 4)
|
|
|
|
|
|
|
|
/* Output Word */
|
|
|
|
#define cpuOUT_W(emu, inst) cpuStore(emu, inst, VB_S32, 4)
|
|
|
|
|
|
|
|
/* Return from Trap or Interrupt */
|
|
|
|
static void cpuRETI(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
|
|
|
|
/* Duplexed exception */
|
|
|
|
if (emu->cpu.psw.np) {
|
|
|
|
emu->cpu.pc = emu->cpu.fepc;
|
|
|
|
emu->cpu.pcFrom = emu->cpu.fepcFrom;
|
|
|
|
emu->cpu.pcTo = emu->cpu.fepcTo;
|
|
|
|
cpuSetSystemRegister(emu, VB_PSW, emu->cpu.fepsw, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Non-duplexed exception */
|
|
|
|
else {
|
|
|
|
|
|
|
|
/* Exception */
|
|
|
|
if (emu->cpu.psw.ep) {
|
|
|
|
emu->cpu.pc = emu->cpu.eipc;
|
|
|
|
emu->cpu.pcFrom = emu->cpu.eipcFrom;
|
|
|
|
emu->cpu.pcTo = emu->cpu.eipcTo;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No exception */
|
|
|
|
else {
|
|
|
|
emu->cpu.pcFrom = emu->cpu.pc;
|
|
|
|
emu->cpu.pc = emu->cpu.eipc;
|
|
|
|
emu->cpu.pcTo = emu->cpu.pc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
cpuSetSystemRegister(emu, VB_PSW, emu->cpu.eipsw, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.clocks = 10;
|
|
|
|
inst->size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reverse Bits in Word */
|
|
|
|
static void cpuREV(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t value = emu->cpu.program[inst->bits[0] & 0x1F];
|
2021-09-20 12:51:12 +00:00
|
|
|
value = (value >> 16 & 0x0000FFFF) | (value << 16 & (int32_t) 0xFFFF0000);
|
|
|
|
value = (value >> 8 & 0x00FF00FF) | (value << 8 & (int32_t) 0xFF00FF00);
|
|
|
|
value = (value >> 4 & 0x0F0F0F0F) | (value << 4 & (int32_t) 0xF0F0F0F0);
|
|
|
|
value = (value >> 2 & 0x33333333) | (value << 2 & (int32_t) 0xCCCCCCCC);
|
2021-09-19 01:31:40 +00:00
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] =
|
2021-09-20 12:51:12 +00:00
|
|
|
(value >> 1 & 0x55555555) | (value << 1 & (int32_t) 0xAAAAAAAA);
|
2021-09-19 01:31:40 +00:00
|
|
|
emu->cpu.clocks = 22;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Shift Arithmetic Right by Immediate */
|
|
|
|
static void cpuSAR_IMM(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
*reg2 = cpuShiftRight(emu, *reg2, inst->bits[0] & 0x1F, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Shift Arithmetic Right by Register */
|
|
|
|
static void cpuSAR_REG(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
*reg2 = cpuShiftRight(emu, *reg2,
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F] & 0x1F, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search Bit 0 Downward */
|
|
|
|
#define cpuSCH0BSD(emu) cpuBitSearch(emu, 0, -1)
|
|
|
|
|
|
|
|
/* Search Bit 0 Upward */
|
|
|
|
#define cpuSCH0BSU(emu) cpuBitSearch(emu, 0, 1)
|
|
|
|
|
|
|
|
/* Search Bit 1 Downward */
|
|
|
|
#define cpuSCH1BSD(emu) cpuBitSearch(emu, 1, -1)
|
|
|
|
|
|
|
|
/* Search Bit 1 Upward */
|
|
|
|
#define cpuSCH1BSU(emu) cpuBitSearch(emu, 1, 1)
|
|
|
|
|
|
|
|
/* Set Interrupt Disable Flag */
|
|
|
|
static void cpuSEI(VB *emu) {
|
|
|
|
emu->cpu.psw.id = 1;
|
|
|
|
emu->cpu.clocks = 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set Flag Condition */
|
|
|
|
static void cpuSETF(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] =
|
|
|
|
cpuCondition(emu, inst->bits[0] & 15);
|
|
|
|
emu->cpu.clocks = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Shift Logical Left by Immediate */
|
|
|
|
static void cpuSHL_IMM(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
int32_t bits = inst->bits[0] & 0x1F;
|
|
|
|
emu->cpu.psw.cy = bits == 0 ? 0 : *reg2 >> (32 - bits) & 1;
|
|
|
|
*reg2 = cpuBitwise(emu, *reg2 << bits);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Shift Logical Left by Register */
|
|
|
|
static void cpuSHL_REG(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
int32_t bits = emu->cpu.program[inst->bits[0] & 0x1F] & 0x1F;
|
|
|
|
emu->cpu.psw.cy = bits == 0 ? 0 : *reg2 >> (32 - bits) & 1;
|
|
|
|
*reg2 = cpuBitwise(emu, *reg2 << bits);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Shift Logical Right by Immediate */
|
|
|
|
static void cpuSHR_IMM(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
*reg2 = cpuShiftRight(emu, *reg2, inst->bits[0] & 0x1F, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Shift Logical Right by Register */
|
|
|
|
static void cpuSHR_REG(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
*reg2 = cpuShiftRight(emu, *reg2,
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F] & 0x1F, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store Byte */
|
|
|
|
#define cpuST_B(emu, inst) cpuStore(emu, inst, VB_S8, 4)
|
|
|
|
|
|
|
|
/* Store Halfword */
|
|
|
|
#define cpuST_H(emu, inst) cpuStore(emu, inst, VB_S16, 4)
|
|
|
|
|
|
|
|
/* Store Word */
|
|
|
|
#define cpuST_W(emu, inst) cpuStore(emu, inst, VB_S32, 4)
|
|
|
|
|
|
|
|
/* Store Contents of System Register */
|
|
|
|
static void cpuSTSR(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] =
|
|
|
|
vbGetSystemRegister(emu, inst->bits[0] & 0x1F);
|
|
|
|
emu->cpu.clocks = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Subtract */
|
|
|
|
static void cpuSUB(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
*reg2 = cpuSubtract(emu, *reg2, emu->cpu.program[inst->bits[0] & 0x1F]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Subtract Floating Short */
|
|
|
|
static void cpuSUBF_S(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
double left = cpuFloatOperand(emu, *reg2);
|
|
|
|
double right = cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]&0x1F]);
|
|
|
|
int32_t result; /* Output bits */
|
|
|
|
|
|
|
|
/* Perform the operation */
|
|
|
|
if (emu->cpu.causeCode == 0)
|
|
|
|
result = cpuFloatResult(emu, left - right);
|
|
|
|
if (emu->cpu.causeCode != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
*reg2 = result;
|
|
|
|
emu->cpu.clocks = 28;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Trap */
|
|
|
|
static void cpuTRAP(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
emu->cpu.causeCode = 0xFFA0 + (inst->bits[0] & 0x1F);
|
|
|
|
emu->cpu.clocks = 15;
|
|
|
|
emu->cpu.pc += 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Truncate Short Floating to Word Integer */
|
|
|
|
#define cpuTRNC_SW(emu, inst) cpuFloatToWord(emu, inst, 1)
|
|
|
|
|
|
|
|
/* Exchange Byte */
|
|
|
|
static void cpuXB(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
*reg2 =
|
2021-09-20 12:51:12 +00:00
|
|
|
(*reg2 >> 8 & (int32_t) 0x000000FF) |
|
|
|
|
(*reg2 << 8 & (int32_t) 0x0000FF00) |
|
|
|
|
(*reg2 & (int32_t) 0xFFFF0000)
|
2021-09-19 01:31:40 +00:00
|
|
|
;
|
|
|
|
emu->cpu.clocks = 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Exchange Halfword */
|
|
|
|
static void cpuXH(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
*reg2 = (*reg2 >> 16 & 0x0000FFFF) | *reg2 << 16;
|
|
|
|
emu->cpu.clocks = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Exclusive Or */
|
|
|
|
static void cpuXOR(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F];
|
|
|
|
*reg2 = cpuBitwise(emu, *reg2 ^ emu->cpu.program[inst->bits[0] & 0x1F]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Exclusive Or Bit String Upward */
|
|
|
|
#define cpuXORBSU(emu, inst) cpuBitString(emu, inst)
|
|
|
|
|
|
|
|
/* Exclusive Or Immediate */
|
|
|
|
static void cpuXORI(VB *emu, VB_INSTRUCTION *inst) {
|
|
|
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu,
|
|
|
|
emu->cpu.program[inst->bits[0] & 0x1F] ^ inst->bits[1]);
|
|
|
|
}
|
|
|
|
|
2021-09-19 19:36:30 +00:00
|
|
|
/* Exclusive Or Not Bit String Upward */
|
|
|
|
#define cpuXORNBSU(emu, inst) cpuBitString(emu, inst)
|
|
|
|
|
2021-09-19 01:31:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
/***************************** Module Functions ******************************/
|
|
|
|
|
|
|
|
/* Check for interrupts */
|
|
|
|
static int cpuCheckIRQs(VB *emu) {
|
|
|
|
int x; /* Iterator */
|
|
|
|
|
|
|
|
/* Interrupts are masked */
|
|
|
|
if (emu->cpu.psw.np || emu->cpu.psw.ep || emu->cpu.psw.id)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Check for interrupts */
|
|
|
|
for (x = 4; x >= emu->cpu.psw.i; x--) {
|
|
|
|
|
|
|
|
/* The interrupt request line is low */
|
|
|
|
if (!emu->cpu.irq[x])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Cause a pending HALT instruction to complete */
|
|
|
|
if (emu->cpu.state == CPU_HALTED)
|
|
|
|
emu->cpu.pc += 2;
|
|
|
|
|
|
|
|
/* Trigger an interrupt */
|
|
|
|
emu->cpu.causeCode = 0xFE00 | x << 4;
|
|
|
|
emu->cpu.state = CPU_EXCEPTION;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No interrupt */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform instruction execute operations */
|
|
|
|
static int cpuExecute(VB *emu) {
|
2021-09-20 12:51:12 +00:00
|
|
|
int broke; /* Application break occurred */
|
|
|
|
VB_INSTRUCTION *inst; /* Shorthand reference */
|
|
|
|
|
|
|
|
/* Check for address trap */
|
|
|
|
if (emu->cpu.psw.ae && emu->cpu.adtre == emu->cpu.pc) {
|
|
|
|
emu->cpu.causeCode = 0xFFC0;
|
|
|
|
emu->cpu.state = CPU_EXCEPTION;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Working variables */
|
|
|
|
inst = &emu->cpu.inst;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
|
|
|
/* Check for application break */
|
|
|
|
if (emu->onExecute != NULL && emu->onExecute(emu, inst))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.causeCode = 0;
|
|
|
|
|
|
|
|
/* Processing by ID */
|
2021-09-20 12:51:12 +00:00
|
|
|
broke = 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
switch (inst->id) {
|
|
|
|
case CPU_ADD_IMM: cpuADD_IMM(emu, inst); break;
|
|
|
|
case CPU_ADD_REG: cpuADD_REG(emu, inst); break;
|
|
|
|
case CPU_ADDF_S : cpuADDF_S (emu, inst); break;
|
|
|
|
case CPU_ADDI : cpuADDI (emu, inst); break;
|
|
|
|
case CPU_AND : cpuAND (emu, inst); break;
|
|
|
|
case CPU_ANDBSU : broke = cpuANDBSU (emu, inst); break;
|
|
|
|
case CPU_ANDI : cpuANDI (emu, inst); break;
|
|
|
|
case CPU_ANDNBSU: broke = cpuANDNBSU(emu, inst); break;
|
|
|
|
case CPU_BCOND : cpuBCOND (emu, inst); break;
|
|
|
|
case CPU_CAXI : broke = cpuCAXI (emu, inst); break;
|
|
|
|
case CPU_CLI : cpuCLI (emu ); break;
|
|
|
|
case CPU_CMP_IMM: cpuCMP_IMM(emu, inst); break;
|
|
|
|
case CPU_CMP_REG: cpuCMP_REG(emu, inst); break;
|
|
|
|
case CPU_CMPF_S : cpuCMPF_S (emu, inst); break;
|
|
|
|
case CPU_CVT_SW : cpuCVT_SW (emu, inst); break;
|
|
|
|
case CPU_CVT_WS : cpuCVT_WS (emu, inst); break;
|
|
|
|
case CPU_DIV : cpuDIV (emu, inst); break;
|
|
|
|
case CPU_DIVF_S : cpuDIVF_S (emu, inst); break;
|
|
|
|
case CPU_DIVU : cpuDIVU (emu, inst); break;
|
|
|
|
case CPU_HALT : cpuHALT (emu, inst); break;
|
|
|
|
case CPU_IN_B : broke = cpuIN_B (emu, inst); break;
|
|
|
|
case CPU_IN_H : broke = cpuIN_H (emu, inst); break;
|
|
|
|
case CPU_IN_W : broke = cpuIN_W (emu, inst); break;
|
|
|
|
case CPU_JAL : cpuJAL (emu, inst); break;
|
|
|
|
case CPU_JMP : cpuJMP (emu, inst); break;
|
|
|
|
case CPU_JR : cpuJR (emu, inst); break;
|
|
|
|
case CPU_LD_B : broke = cpuLD_B (emu, inst); break;
|
|
|
|
case CPU_LD_H : broke = cpuLD_H (emu, inst); break;
|
|
|
|
case CPU_LD_W : broke = cpuLD_W (emu, inst); break;
|
|
|
|
case CPU_LDSR : cpuLDSR (emu, inst); break;
|
|
|
|
case CPU_MOV_IMM: cpuMOV_IMM(emu, inst); break;
|
|
|
|
case CPU_MOV_REG: cpuMOV_REG(emu, inst); break;
|
|
|
|
case CPU_MOVBSU : broke = cpuMOVBSU (emu, inst); break;
|
|
|
|
case CPU_MOVEA : cpuMOVEA (emu, inst); break;
|
|
|
|
case CPU_MOVHI : cpuMOVHI (emu, inst); break;
|
|
|
|
case CPU_MPYHW : cpuMPYHW (emu, inst); break;
|
|
|
|
case CPU_MUL : cpuMUL (emu, inst); break;
|
|
|
|
case CPU_MULF_S : cpuMULF_S (emu, inst); break;
|
|
|
|
case CPU_MULU : cpuMULU (emu, inst); break;
|
|
|
|
case CPU_NOT : cpuNOT (emu, inst); break;
|
|
|
|
case CPU_NOTBSU : broke = cpuNOTBSU (emu, inst); break;
|
|
|
|
case CPU_OR : cpuOR (emu, inst); break;
|
|
|
|
case CPU_ORBSU : broke = cpuORBSU (emu, inst); break;
|
|
|
|
case CPU_ORI : cpuORI (emu, inst); break;
|
|
|
|
case CPU_ORNBSU : broke = cpuORNBSU (emu, inst); break;
|
|
|
|
case CPU_OUT_B : broke = cpuOUT_B (emu, inst); break;
|
|
|
|
case CPU_OUT_H : broke = cpuOUT_H (emu, inst); break;
|
|
|
|
case CPU_OUT_W : broke = cpuOUT_W (emu, inst); break;
|
|
|
|
case CPU_RETI : cpuRETI (emu, inst); break;
|
|
|
|
case CPU_REV : cpuREV (emu, inst); break;
|
|
|
|
case CPU_SAR_IMM: cpuSAR_IMM(emu, inst); break;
|
|
|
|
case CPU_SAR_REG: cpuSAR_REG(emu, inst); break;
|
|
|
|
case CPU_SCH0BSD: cpuSCH0BSD(emu ); break;
|
|
|
|
case CPU_SCH0BSU: cpuSCH0BSU(emu ); break;
|
|
|
|
case CPU_SCH1BSD: cpuSCH1BSD(emu ); break;
|
|
|
|
case CPU_SCH1BSU: cpuSCH1BSU(emu ); break;
|
|
|
|
case CPU_SEI : cpuSEI (emu ); break;
|
|
|
|
case CPU_SETF : cpuSETF (emu, inst); break;
|
|
|
|
case CPU_SHL_IMM: cpuSHL_IMM(emu, inst); break;
|
|
|
|
case CPU_SHL_REG: cpuSHL_REG(emu, inst); break;
|
|
|
|
case CPU_SHR_IMM: cpuSHR_IMM(emu, inst); break;
|
|
|
|
case CPU_SHR_REG: cpuSHR_REG(emu, inst); break;
|
|
|
|
case CPU_ST_B : broke = cpuST_B (emu, inst); break;
|
|
|
|
case CPU_ST_H : broke = cpuST_H (emu, inst); break;
|
|
|
|
case CPU_ST_W : broke = cpuST_W (emu, inst); break;
|
|
|
|
case CPU_STSR : cpuSTSR (emu, inst); break;
|
|
|
|
case CPU_SUB : cpuSUB (emu, inst); break;
|
|
|
|
case CPU_SUBF_S : cpuSUBF_S (emu, inst); break;
|
|
|
|
case CPU_TRAP : cpuTRAP (emu, inst); break;
|
|
|
|
case CPU_TRNC_SW: cpuTRNC_SW(emu, inst); break;
|
|
|
|
case CPU_XB : cpuXB (emu, inst); break;
|
|
|
|
case CPU_XH : cpuXH (emu, inst); break;
|
|
|
|
case CPU_XOR : cpuXOR (emu, inst); break;
|
|
|
|
case CPU_XORBSU : broke = cpuXORBSU (emu, inst); break;
|
|
|
|
case CPU_XORI : cpuXORI (emu, inst); break;
|
|
|
|
case CPU_XORNBSU: broke = cpuXORNBSU(emu, inst); break;
|
|
|
|
default: /* CPU_ILLEGAL */ emu->cpu.causeCode = 0xFF90;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Instructions cannot modify r0 */
|
|
|
|
emu->cpu.program[0] = 0x00000000;
|
|
|
|
|
|
|
|
/* An application break was requested */
|
|
|
|
if (broke)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Post-instruction tasks */
|
|
|
|
if (emu->cpu.causeCode == 0 && !emu->cpu.busWait) {
|
|
|
|
|
|
|
|
/* Advance to next instruction if not processing a bit string */
|
|
|
|
if (!emu->cpu.substring)
|
|
|
|
emu->cpu.pc += inst->size;
|
|
|
|
|
|
|
|
/* Check for interrupts */
|
|
|
|
cpuCheckIRQs(emu);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* An exception or interrupt occurred */
|
|
|
|
if (emu->cpu.causeCode != 0) {
|
|
|
|
emu->cpu.state = CPU_EXCEPTION;
|
|
|
|
emu->cpu.substring = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Switch to fetch mode if not halting and execution has completed */
|
|
|
|
else if (emu->cpu.state != CPU_HALTED &&
|
|
|
|
!emu->cpu.busWait && !emu->cpu.substring) {
|
|
|
|
emu->cpu.state = CPU_FETCH;
|
|
|
|
emu->cpu.fetch = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enter an exception state */
|
|
|
|
static int cpuException(VB *emu) {
|
2021-09-19 19:36:30 +00:00
|
|
|
uint16_t causeCode = emu->cpu.causeCode;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
|
|
|
/* Fatal exception */
|
|
|
|
if (emu->cpu.psw.np) {
|
|
|
|
|
|
|
|
/* Write the cause code for debugging */
|
|
|
|
if (emu->cpu.busWait == 0) {
|
2021-09-19 19:36:30 +00:00
|
|
|
if (cpuWrite(emu, 0x00000000, VB_S32, 0xFFFF0000 | causeCode))
|
2021-09-19 01:31:40 +00:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 1;
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write PSW for debugging */
|
|
|
|
if (emu->cpu.busWait == 1) {
|
|
|
|
if (cpuWrite(emu, 0x00000000, VB_S32,
|
|
|
|
vbGetSystemRegister(emu, VB_PSW)))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 2;
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write PC for debugging */
|
|
|
|
if (emu->cpu.busWait == 2) {
|
|
|
|
if (cpuWrite(emu, 0x00000000, VB_S32, emu->cpu.pc))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 3;
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 0;
|
|
|
|
emu->cpu.causeCode = 0;
|
|
|
|
emu->cpu.state = CPU_FATAL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Duplexed exception */
|
|
|
|
if (emu->cpu.psw.ep) {
|
2021-09-19 19:36:30 +00:00
|
|
|
emu->cpu.ecr.fecc = causeCode;
|
2021-09-19 01:31:40 +00:00
|
|
|
emu->cpu.fepsw = vbGetSystemRegister(emu, VB_PSW);
|
|
|
|
emu->cpu.fepc = emu->cpu.pc;
|
|
|
|
emu->cpu.fepcFrom = emu->cpu.pcFrom;
|
|
|
|
emu->cpu.fepcTo = emu->cpu.pcTo;
|
|
|
|
emu->cpu.psw.np = 1;
|
|
|
|
emu->cpu.pc = 0xFFFFFFD0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Exception or interrupt */
|
|
|
|
else {
|
2021-09-19 19:36:30 +00:00
|
|
|
emu->cpu.ecr.eicc = causeCode;
|
2021-09-19 01:31:40 +00:00
|
|
|
emu->cpu.eipsw = vbGetSystemRegister(emu, VB_PSW);
|
|
|
|
emu->cpu.eipc = emu->cpu.pc;
|
2021-09-19 19:36:30 +00:00
|
|
|
emu->cpu.eipcFrom = emu->cpu.pcFrom;
|
|
|
|
emu->cpu.eipcTo = emu->cpu.pcTo;
|
2021-09-19 01:31:40 +00:00
|
|
|
emu->cpu.psw.ep = 1;
|
2021-09-19 19:36:30 +00:00
|
|
|
emu->cpu.pc = (causeCode & 0x0040) != 0 ?
|
|
|
|
0xFFFFFF60 : ((uint32_t) 0xFFFF0000 | causeCode);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Interrupt */
|
2021-09-19 19:36:30 +00:00
|
|
|
if (causeCode < 0xFF00)
|
|
|
|
emu->cpu.psw.i += emu->cpu.psw.i == 15 ? 0 : 1;
|
2021-09-19 01:31:40 +00:00
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.causeCode = 0;
|
|
|
|
emu->cpu.state = CPU_FETCH;
|
|
|
|
emu->cpu.psw.id = 1;
|
|
|
|
emu->cpu.psw.ae = 0;
|
|
|
|
emu->cpu.pcFrom = emu->cpu.pc;
|
|
|
|
emu->cpu.pcTo = emu->cpu.pc;
|
|
|
|
/* emu->cpu.clocks = ? */
|
2021-09-19 19:36:30 +00:00
|
|
|
|
|
|
|
/* Call the breakpoint handler if available */
|
|
|
|
return emu->onException != NULL && emu->onException(emu, causeCode);
|
2021-09-19 01:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform instruction fetch operations */
|
|
|
|
static int cpuFetch(VB *emu) {
|
|
|
|
VB_INSTRUCTION *inst; /* Reference to emu->cpu.inst */
|
|
|
|
uint8_t opcode; /* 6-bit instruction opcode */
|
|
|
|
|
|
|
|
/* Need to read a data unit */
|
|
|
|
if (!emu->cpu.busWait) {
|
|
|
|
|
|
|
|
/* Read the data unit from the bus */
|
|
|
|
if (cpuReadFetch(emu, emu->cpu.pc + (emu->cpu.fetch << 1)))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
emu->cpu.busWait = 1;
|
|
|
|
emu->cpu.clocks = emu->cpu.access.clocks;
|
|
|
|
|
|
|
|
/* Wait for the bus access to complete */
|
|
|
|
if (emu->cpu.clocks > 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
inst = &emu->cpu.inst;
|
|
|
|
inst->bits[emu->cpu.fetch] = emu->cpu.access.value;
|
|
|
|
emu->cpu.busWait = 0;
|
|
|
|
|
|
|
|
/* Working variables */
|
|
|
|
opcode = inst->bits[0] >> 10 & 0x3F;
|
|
|
|
|
|
|
|
/* First fetch */
|
|
|
|
if (emu->cpu.fetch == 0) {
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
inst->size = CPU_SIZES[opcode];
|
|
|
|
|
|
|
|
/* A second fetch is needed */
|
|
|
|
if (inst->size == 4) {
|
|
|
|
emu->cpu.fetch = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine the internal ID of the instruction */
|
|
|
|
inst->id = CPU_OPCODES[opcode];
|
|
|
|
switch (inst->id) {
|
|
|
|
case CPU_BITSTRING: inst->id = CPU_BITSTRINGS[inst->id & 0x1F]; break;
|
|
|
|
case CPU_FLOATENDO: inst->id = CPU_FLOATENDOS[inst->bits[1]>>10&0x3F];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update state */
|
2021-09-20 12:51:12 +00:00
|
|
|
emu->cpu.fetch = 0;
|
2021-09-19 01:31:40 +00:00
|
|
|
emu->cpu.state = CPU_EXECUTE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process the simulation for some number of clocks */
|
|
|
|
static int cpuEmulate(VB *emu, uint32_t clocks) {
|
|
|
|
|
|
|
|
/* Fatal halt: cannot break */
|
|
|
|
if (emu->cpu.state == CPU_FATAL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Process all clocks */
|
|
|
|
do {
|
|
|
|
|
|
|
|
/* The next operation is after the remaining clocks */
|
|
|
|
if (clocks < emu->cpu.clocks) {
|
|
|
|
emu->cpu.clocks -= clocks;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update remaining clocks */
|
|
|
|
clocks -= emu->cpu.clocks;
|
|
|
|
emu->cpu.clocks = 0;
|
|
|
|
|
|
|
|
/* Processing by operations state */
|
|
|
|
switch (emu->cpu.state) {
|
|
|
|
case CPU_FETCH : if (cpuFetch (emu)) return 1; break;
|
|
|
|
case CPU_EXECUTE : if (cpuExecute (emu)) return 1; break;
|
|
|
|
case CPU_EXCEPTION: if (cpuException(emu)) return 1; break;
|
|
|
|
case CPU_HALTED : if (cpuCheckIRQs(emu)) return 0; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (clocks > 0);
|
|
|
|
|
|
|
|
/* No break occurred */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine the number of clocks before a break condititon could occur */
|
|
|
|
static uint32_t cpuUntil(VB *emu, uint32_t clocks) {
|
|
|
|
|
|
|
|
/* Cannot break */
|
|
|
|
if (
|
|
|
|
emu->cpu.state == CPU_HALTED ||
|
|
|
|
emu->cpu.state == CPU_FATAL || (
|
|
|
|
emu->onException == NULL &&
|
|
|
|
emu->onExecute == NULL &&
|
|
|
|
emu->onRead == NULL &&
|
|
|
|
emu->onWrite == NULL
|
|
|
|
)) return clocks;
|
|
|
|
|
|
|
|
/* Will not break before next operation */
|
|
|
|
return emu->cpu.clocks < clocks ? emu->cpu.clocks : clocks;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-09-06 00:09:15 +00:00
|
|
|
#endif /* VBAPI */
|