Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae22c95dbe | ||
|
|
b134903878 | ||
|
|
52838e4a6d | ||
|
|
6cba6e5d4f | ||
|
|
650d9dfd05 | ||
|
|
f5a84fd4d0 | ||
|
|
9cc01bb6f7 | ||
|
|
e828d99da1 | ||
|
|
da001e02cd | ||
|
|
67bcb6d508 | ||
|
|
bcd80d291b | ||
|
|
0c914fce1a | ||
|
|
226da7a5b3 | ||
|
|
8f9950f39e |
+17
-16
@@ -95,17 +95,17 @@ static int32_t busReadMisc(VB *sim, uint8_t address, int type) {
|
|||||||
|
|
||||||
/* Access by register */
|
/* Access by register */
|
||||||
switch (address >> 2 & 15) {
|
switch (address >> 2 & 15) {
|
||||||
case 0x00>>2: break; /* CCR */
|
case 0x00>>2: return extReadCCR(sim); /* CCR */
|
||||||
case 0x04>>2: break; /* CCSR */
|
case 0x04>>2: return extReadCCSR(sim); /* CCSR */
|
||||||
case 0x08>>2: break; /* CDTR */
|
case 0x08>>2: return sim->ext.cdtr; /* CDTR */
|
||||||
case 0x0C>>2: break; /* CDRR */
|
case 0x0C>>2: return sim->ext.cdrr; /* CDRR */
|
||||||
case 0x10>>2: return sim->pad.sdlr; /* SDLR */
|
case 0x10>>2: return sim->pad.sdlr; /* SDLR */
|
||||||
case 0x14>>2: return sim->pad.sdhr; /* SDHR */
|
case 0x14>>2: return sim->pad.sdhr; /* SDHR */
|
||||||
case 0x18>>2: return sim->tmr.counter & 0xFF; /* TLR */
|
case 0x18>>2: return sim->tmr.counter & 0xFF; /* TLR */
|
||||||
case 0x1C>>2: return sim->tmr.counter >> 8 & 0xFF; /* THR */
|
case 0x1C>>2: return sim->tmr.counter >> 8 & 0xFF; /* THR */
|
||||||
case 0x20>>2: return tmrReadControl(sim); /* TCR */
|
case 0x20>>2: return tmrReadControl(sim); /* TCR */
|
||||||
case 0x24>>2: return sim->wcr; /* WCR */
|
case 0x24>>2: return sim->wcr.exp1w << 1 | sim->wcr.rom1w; /* WCR */
|
||||||
case 0x28>>2: return padReadControl(sim); /* SCR */
|
case 0x28>>2: return padReadControl(sim); /* SCR */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unmapped */
|
/* Unmapped */
|
||||||
@@ -123,15 +123,16 @@ static void busWriteMisc(VB *sim, uint8_t address, int type, int32_t value) {
|
|||||||
|
|
||||||
/* Access by register */
|
/* Access by register */
|
||||||
switch (address >> 2 & 15) {
|
switch (address >> 2 & 15) {
|
||||||
case 0x00>>2: break; /* CCR */
|
case 0x00>>2: extWriteCCR (sim, value); break; /* CCR */
|
||||||
case 0x04>>2: break; /* CCSR */
|
case 0x04>>2: extWriteCCSR(sim, value); break; /* CCSR */
|
||||||
case 0x08>>2: break; /* CDTR */
|
case 0x08>>2: sim->ext.cdtr = value; break; /* CDTR */
|
||||||
case 0x0C>>2: break; /* CDRR */
|
|
||||||
case 0x18>>2: tmrWriteLow (sim, value); break; /* TLR */
|
case 0x18>>2: tmrWriteLow (sim, value); break; /* TLR */
|
||||||
case 0x1C>>2: tmrWriteHigh (sim, value); break; /* THR */
|
case 0x1C>>2: tmrWriteHigh (sim, value); break; /* THR */
|
||||||
case 0x20>>2: tmrWriteControl(sim, value); break; /* TCR */
|
case 0x20>>2: tmrWriteControl(sim, value); break; /* TCR */
|
||||||
case 0x24>>2: sim->wcr = value & 3; break; /* WCR */
|
|
||||||
case 0x28>>2: padWriteControl(sim, value); break; /* SCR */
|
case 0x28>>2: padWriteControl(sim, value); break; /* SCR */
|
||||||
|
case 0x24>>2: /* WCR */
|
||||||
|
sim->wcr.exp1w = value >> 1 & 1;
|
||||||
|
sim->wcr.rom1w = value & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+133
-95
@@ -10,97 +10,86 @@
|
|||||||
#define CPU_HALTING 1
|
#define CPU_HALTING 1
|
||||||
#define CPU_FATAL 2
|
#define CPU_FATAL 2
|
||||||
#define CPU_FETCH 3
|
#define CPU_FETCH 3
|
||||||
#define CPU_ILLEGAL 4
|
#define CPU_PHALT 4
|
||||||
#define CPU_BITSTRING 5
|
#define CPU_ILLEGAL 5
|
||||||
#define CPU_FLOATENDO 6
|
#define CPU_BITSTRING 6
|
||||||
#define CPU_ADD_IMM 7
|
#define CPU_FLOATENDO 7
|
||||||
#define CPU_ADD_REG 8
|
#define CPU_ADD_IMM 8
|
||||||
#define CPU_ADDF_S 9
|
#define CPU_ADD_REG 9
|
||||||
#define CPU_ADDI 10
|
#define CPU_ADDF_S 10
|
||||||
#define CPU_AND 11
|
#define CPU_ADDI 11
|
||||||
#define CPU_ANDI 12
|
#define CPU_AND 12
|
||||||
#define CPU_BCOND 13
|
#define CPU_ANDI 13
|
||||||
#define CPU_CAXI 14
|
#define CPU_BCOND 14
|
||||||
#define CPU_CLI 15
|
#define CPU_CAXI 15
|
||||||
#define CPU_CMP_IMM 16
|
#define CPU_CLI 16
|
||||||
#define CPU_CMP_REG 17
|
#define CPU_CMP_IMM 17
|
||||||
#define CPU_CMPF_S 18
|
#define CPU_CMP_REG 18
|
||||||
#define CPU_CVT_SW 19
|
#define CPU_CMPF_S 19
|
||||||
#define CPU_CVT_WS 20
|
#define CPU_CVT_SW 20
|
||||||
#define CPU_DIV 21
|
#define CPU_CVT_WS 21
|
||||||
#define CPU_DIVF_S 22
|
#define CPU_DIV 22
|
||||||
#define CPU_DIVU 23
|
#define CPU_DIVF_S 23
|
||||||
#define CPU_HALT 24
|
#define CPU_DIVU 24
|
||||||
#define CPU_IN_B 25
|
#define CPU_HALT 25
|
||||||
#define CPU_IN_H 26
|
#define CPU_IN_B 26
|
||||||
#define CPU_IN_W 27
|
#define CPU_IN_H 27
|
||||||
#define CPU_JAL 28
|
#define CPU_IN_W 28
|
||||||
#define CPU_JMP 29
|
#define CPU_JAL 29
|
||||||
#define CPU_JR 30
|
#define CPU_JMP 30
|
||||||
#define CPU_LD_B 31
|
#define CPU_JR 31
|
||||||
#define CPU_LD_H 32
|
#define CPU_LD_B 32
|
||||||
#define CPU_LD_W 33
|
#define CPU_LD_H 33
|
||||||
#define CPU_LDSR 34
|
#define CPU_LD_W 34
|
||||||
#define CPU_MOV_IMM 35
|
#define CPU_LDSR 35
|
||||||
#define CPU_MOV_REG 36
|
#define CPU_MOV_IMM 36
|
||||||
#define CPU_MOVEA 37
|
#define CPU_MOV_REG 37
|
||||||
#define CPU_MOVHI 38
|
#define CPU_MOVEA 38
|
||||||
#define CPU_MPYHW 39
|
#define CPU_MOVHI 39
|
||||||
#define CPU_MUL 40
|
#define CPU_MPYHW 40
|
||||||
#define CPU_MULF_S 41
|
#define CPU_MUL 41
|
||||||
#define CPU_MULU 42
|
#define CPU_MULF_S 42
|
||||||
#define CPU_NOT 43
|
#define CPU_MULU 43
|
||||||
#define CPU_OR 44
|
#define CPU_NOT 44
|
||||||
#define CPU_ORI 45
|
#define CPU_OR 45
|
||||||
#define CPU_OUT_B 46
|
#define CPU_ORI 46
|
||||||
#define CPU_OUT_H 47
|
#define CPU_OUT_B 47
|
||||||
#define CPU_OUT_W 48
|
#define CPU_OUT_H 48
|
||||||
#define CPU_RETI 49
|
#define CPU_OUT_W 49
|
||||||
#define CPU_REV 50
|
#define CPU_RETI 50
|
||||||
#define CPU_SAR_IMM 51
|
#define CPU_REV 51
|
||||||
#define CPU_SAR_REG 52
|
#define CPU_SAR_IMM 52
|
||||||
#define CPU_SCH0BSD 53
|
#define CPU_SAR_REG 53
|
||||||
#define CPU_SCH0BSU 54
|
#define CPU_SCH0BSD 54
|
||||||
#define CPU_SCH1BSD 55
|
#define CPU_SCH0BSU 55
|
||||||
#define CPU_SCH1BSU 56
|
#define CPU_SCH1BSD 56
|
||||||
#define CPU_SEI 57
|
#define CPU_SCH1BSU 57
|
||||||
#define CPU_SETF 58
|
#define CPU_SEI 58
|
||||||
#define CPU_SHL_IMM 59
|
#define CPU_SETF 59
|
||||||
#define CPU_SHL_REG 60
|
#define CPU_SHL_IMM 60
|
||||||
#define CPU_SHR_IMM 61
|
#define CPU_SHL_REG 61
|
||||||
#define CPU_SHR_REG 62
|
#define CPU_SHR_IMM 62
|
||||||
#define CPU_ST_B 63
|
#define CPU_SHR_REG 63
|
||||||
#define CPU_ST_H 64
|
#define CPU_ST_B 64
|
||||||
#define CPU_ST_W 65
|
#define CPU_ST_H 65
|
||||||
#define CPU_STSR 66
|
#define CPU_ST_W 66
|
||||||
#define CPU_SUB 67
|
#define CPU_STSR 67
|
||||||
#define CPU_SUBF_S 68
|
#define CPU_SUB 68
|
||||||
#define CPU_TRAP 69
|
#define CPU_SUBF_S 69
|
||||||
#define CPU_TRNC_SW 70
|
#define CPU_TRAP 70
|
||||||
#define CPU_XB 71
|
#define CPU_TRNC_SW 71
|
||||||
#define CPU_XH 72
|
#define CPU_XB 72
|
||||||
#define CPU_XOR 73
|
#define CPU_XH 73
|
||||||
#define CPU_XORI 74
|
#define CPU_XOR 74
|
||||||
#define CPU_ANDBSU 75 /* Keep bit string ALU commands sequential */
|
#define CPU_XORI 75
|
||||||
#define CPU_ANDNBSU 76
|
#define CPU_ANDBSU 76 /* Keep bit string ALU IDs consecutive */
|
||||||
#define CPU_MOVBSU 77
|
#define CPU_ANDNBSU 77
|
||||||
#define CPU_NOTBSU 78
|
#define CPU_MOVBSU 78
|
||||||
#define CPU_ORBSU 79
|
#define CPU_NOTBSU 79
|
||||||
#define CPU_ORNBSU 80
|
#define CPU_ORBSU 80
|
||||||
#define CPU_XORBSU 81
|
#define CPU_ORNBSU 81
|
||||||
#define CPU_XORNBSU 82
|
#define CPU_XORBSU 82
|
||||||
|
#define CPU_XORNBSU 83
|
||||||
/* 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 */
|
/* Functional operand types */
|
||||||
#define CPU_LITERAL 0
|
#define CPU_LITERAL 0
|
||||||
@@ -206,6 +195,13 @@ static const uint8_t OPDEFS_FLOATENDO[] = {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**************************** Forward references *****************************/
|
||||||
|
|
||||||
|
static int phAssess (VB *, uint32_t, int, int32_t);
|
||||||
|
static uint32_t phUntil (VB *);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************** Callback Handlers *****************************/
|
/***************************** Callback Handlers *****************************/
|
||||||
|
|
||||||
/* Prepare to handle an exception */
|
/* Prepare to handle an exception */
|
||||||
@@ -248,6 +244,10 @@ static int cpuRead(VB *sim, uint32_t address, int type, int32_t *value) {
|
|||||||
/* Retrieve the value from the simulation state directly */
|
/* Retrieve the value from the simulation state directly */
|
||||||
busRead(sim, address, type, value);
|
busRead(sim, address, type, value);
|
||||||
|
|
||||||
|
/* Check for pseudo-halt */
|
||||||
|
if (sim->ph.enabled && phAssess(sim, address, type, *value))
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Invoke the callback if available */
|
/* Invoke the callback if available */
|
||||||
if (
|
if (
|
||||||
sim->onRead != NULL &&
|
sim->onRead != NULL &&
|
||||||
@@ -296,6 +296,10 @@ static int cpuWrite(VB *sim, uint32_t address, int type, int32_t value) {
|
|||||||
int cancel = 0;
|
int cancel = 0;
|
||||||
uint32_t cycles = 3; /* TODO: Research this */
|
uint32_t cycles = 3; /* TODO: Research this */
|
||||||
|
|
||||||
|
/* Reset pseudo-halt */
|
||||||
|
if (sim->ph.enabled)
|
||||||
|
sim->ph.step = 0;
|
||||||
|
|
||||||
/* Invoke the callback if available */
|
/* Invoke the callback if available */
|
||||||
if (
|
if (
|
||||||
sim->onWrite != NULL &&
|
sim->onWrite != NULL &&
|
||||||
@@ -482,10 +486,13 @@ static int cpuException(VB *sim) {
|
|||||||
/* Interrupts only */
|
/* Interrupts only */
|
||||||
if ((cause & 0xFF00) == 0xFE00) {
|
if ((cause & 0xFF00) == 0xFE00) {
|
||||||
sim->cpu.psw.i = (cause >> 4 & 7) + 1;
|
sim->cpu.psw.i = (cause >> 4 & 7) + 1;
|
||||||
|
|
||||||
|
/* HALT instruction */
|
||||||
if (sim->cpu.halt) {
|
if (sim->cpu.halt) {
|
||||||
sim->cpu.halt = 0;
|
sim->cpu.halt = 0;
|
||||||
sim->cpu.pc += 2;
|
sim->cpu.pc += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* All exceptions */
|
/* All exceptions */
|
||||||
@@ -583,6 +590,30 @@ static int cpuHalt(VB *sim) {
|
|||||||
return !cpuIRQ(sim);
|
return !cpuIRQ(sim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Pseudo-halt is pending */
|
||||||
|
static int cpuPHalt(VB *sim) {
|
||||||
|
int32_t value; /* Value read from memory */
|
||||||
|
|
||||||
|
/* An interrupt will be accepted */
|
||||||
|
if (cpuIRQ(sim)) {
|
||||||
|
sim->ph.step = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Monitor value has not changed */
|
||||||
|
busRead(sim, sim->ph.address, sim->ph.type, &value);
|
||||||
|
if (value == sim->ph.value) {
|
||||||
|
sim->cpu.clocks = phUntil(sim);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release pseudo-halt */
|
||||||
|
sim->cpu.operation = CPU_FETCH;
|
||||||
|
sim->cpu.step = 0;
|
||||||
|
sim->ph.step = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**************************** Instruction Helpers ****************************/
|
/**************************** Instruction Helpers ****************************/
|
||||||
@@ -1699,10 +1730,11 @@ static int cpuEmulate(VB *sim, uint32_t clocks) {
|
|||||||
|
|
||||||
/* Processing by operation ID */
|
/* Processing by operation ID */
|
||||||
switch (sim->cpu.operation) {
|
switch (sim->cpu.operation) {
|
||||||
case CPU_EXCEPTION: brk = cpuException(sim); break;
|
case CPU_EXCEPTION: brk = cpuException(sim); break;
|
||||||
case CPU_FATAL : return 0;
|
case CPU_FATAL : return 0;
|
||||||
case CPU_FETCH : brk = cpuFetch (sim); break;
|
case CPU_FETCH : brk = cpuFetch (sim); break;
|
||||||
case CPU_HALTING : if (cpuHalt(sim)) return 0; break;
|
case CPU_HALTING : if (cpuHalt (sim)) return 0; break;
|
||||||
|
case CPU_PHALT : if (cpuPHalt(sim)) return 0; break;
|
||||||
|
|
||||||
case CPU_ADD_IMM: cpuADDImm (sim); break;
|
case CPU_ADD_IMM: cpuADDImm (sim); break;
|
||||||
case CPU_ADD_REG: cpuADDReg (sim); break;
|
case CPU_ADD_REG: cpuADDReg (sim); break;
|
||||||
@@ -1826,8 +1858,14 @@ static void cpuReset(VB *sim) {
|
|||||||
/* Determine how many clocks are guaranteed to process */
|
/* Determine how many clocks are guaranteed to process */
|
||||||
static uint32_t cpuUntil(VB *sim, uint32_t clocks) {
|
static uint32_t cpuUntil(VB *sim, uint32_t clocks) {
|
||||||
|
|
||||||
|
/* Pseudo-halting */
|
||||||
|
if (sim->cpu.operation == CPU_PHALT) {
|
||||||
|
if (sim->cpu.clocks == 0)
|
||||||
|
return clocks;
|
||||||
|
}
|
||||||
|
|
||||||
/* Halting */
|
/* Halting */
|
||||||
if (sim->cpu.halt) {
|
else if (sim->cpu.halt) {
|
||||||
return
|
return
|
||||||
sim->cpu.operation == CPU_HALTING &&
|
sim->cpu.operation == CPU_HALTING &&
|
||||||
!(sim->cpu.psw.id | sim->cpu.psw.ep | sim->cpu.psw.np) &&
|
!(sim->cpu.psw.id | sim->cpu.psw.ep | sim->cpu.psw.np) &&
|
||||||
|
|||||||
+164
@@ -0,0 +1,164 @@
|
|||||||
|
/* This file is included into vb.c and cannot be compiled on its own. */
|
||||||
|
#ifdef VBAPI
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************** Callback Handlers *****************************/
|
||||||
|
|
||||||
|
/* Prepare to handle an exception */
|
||||||
|
#ifndef VB_DIRECT_LINK
|
||||||
|
#define VB_ON_LINK sim1->onLink
|
||||||
|
#else
|
||||||
|
extern int VB_DIRECT_LINK(VB *, VB *, uint8_t *, uint8_t *);
|
||||||
|
#define VB_ON_LINK VB_DIRECT_LINK
|
||||||
|
#endif
|
||||||
|
static int extOnLink(VB *sim1, VB *sim2, uint8_t *value1, uint8_t *value2) {
|
||||||
|
return sim1->onLink != NULL && VB_ON_LINK(sim1, sim2, value1, value2);
|
||||||
|
}
|
||||||
|
#undef VB_ON_LINK
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************** Library Functions *****************************/
|
||||||
|
|
||||||
|
/* Process component */
|
||||||
|
static int extEmulate(VB *sim, uint32_t clocks) {
|
||||||
|
VB *peer; /* Communication peer */
|
||||||
|
VB *sims[2]; /* Communication peers */
|
||||||
|
uint8_t values[2]; /* Transmission values */
|
||||||
|
int x; /* Iterator */
|
||||||
|
|
||||||
|
/* Communication will not occur */
|
||||||
|
if (!sim->ext.c_stat || sim->ext.c_clk_sel)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Communication completes after time to process */
|
||||||
|
if (sim->ext.clocks > clocks) {
|
||||||
|
sim->ext.clocks -= clocks;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exchange transmission data */
|
||||||
|
sims [0] = sim;
|
||||||
|
sims [1] = sim->peer;
|
||||||
|
values[0] = sim->ext.cdtr;
|
||||||
|
values[1] = sim->peer == NULL ? 0 : sim->peer->ext.cdtr;
|
||||||
|
if (extOnLink(sim, sim->peer, &values[0], &values[1]))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Update state */
|
||||||
|
for (x = 0; x < 2; x++) {
|
||||||
|
sim = sims[x];
|
||||||
|
peer = sims[x ^ 1];
|
||||||
|
if (sim == NULL)
|
||||||
|
break;
|
||||||
|
if (peer == NULL)
|
||||||
|
peer = sim;
|
||||||
|
|
||||||
|
/* Registers */
|
||||||
|
sim->ext.cdrr = values[x ^ 1];
|
||||||
|
sim->ext.c_irq |= !sim->ext.c_int_inh;
|
||||||
|
sim->ext.c_stat = 0;
|
||||||
|
sim->ext.cc_rd = sim->ext.cc_wr & peer->ext.cc_wr;
|
||||||
|
sim->ext.cc_smp = sim->ext.cc_sig & peer->ext.cc_sig & sim->ext.cc_rd;
|
||||||
|
sim->ext.cc_irq |= !sim->ext.cc_int_inh &&
|
||||||
|
sim->ext.cc_smp == sim->ext.cc_int_lev;
|
||||||
|
|
||||||
|
/* Interrupt */
|
||||||
|
if (sim->ext.c_irq | sim->ext.cc_irq)
|
||||||
|
sim->cpu.irq |= 0x0008;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read a value from CCR */
|
||||||
|
static int32_t extReadCCR(VB *sim) {
|
||||||
|
return 0x6D |
|
||||||
|
sim->ext.c_int_inh << 7 |
|
||||||
|
sim->ext.c_clk_sel << 4 |
|
||||||
|
sim->ext.c_stat << 1
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read a value from CCSR */
|
||||||
|
static int32_t extReadCCSR(VB *sim) {
|
||||||
|
sim->ext.cc_rd = sim->ext.cc_wr &
|
||||||
|
(sim->peer == NULL ? 1 : sim->peer->ext.cc_wr);
|
||||||
|
return 0x60 |
|
||||||
|
sim->ext.cc_int_inh << 7 |
|
||||||
|
sim->ext.cc_int_lev << 4 |
|
||||||
|
sim->ext.cc_sig << 3 |
|
||||||
|
sim->ext.cc_smp << 2 |
|
||||||
|
sim->ext.cc_wr << 1 |
|
||||||
|
sim->ext.cc_rd
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simulate a hardware reset */
|
||||||
|
static void extReset(VB *sim) {
|
||||||
|
|
||||||
|
/* Normal */
|
||||||
|
sim->ext.c_clk_sel = 0;
|
||||||
|
sim->ext.c_int_inh = 0;
|
||||||
|
sim->ext.c_stat = 0;
|
||||||
|
sim->ext.cc_int_inh = 1;
|
||||||
|
sim->ext.cc_int_lev = 1;
|
||||||
|
sim->ext.cc_sig = 1;
|
||||||
|
sim->ext.cc_wr = 1;
|
||||||
|
sim->ext.cdrr = 0x00;
|
||||||
|
sim->ext.cdtr = 0x00;
|
||||||
|
|
||||||
|
/* Other */
|
||||||
|
sim->ext.c_irq = 0;
|
||||||
|
sim->ext.cc_irq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine how many clocks are guaranteed to process */
|
||||||
|
static uint32_t extUntil(VB *sim, uint32_t clocks) {
|
||||||
|
return !sim->ext.c_stat || sim->ext.c_clk_sel || clocks < sim->ext.clocks ?
|
||||||
|
clocks : sim->ext.clocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write a value to CCR */
|
||||||
|
static void extWriteCCR(VB *sim, uint8_t value) {
|
||||||
|
|
||||||
|
/* Configure state */
|
||||||
|
sim->ext.c_int_inh = value >> 7 & 1;
|
||||||
|
sim->ext.c_clk_sel = value >> 4 & 1;
|
||||||
|
|
||||||
|
/* Acknowledge interrupt */
|
||||||
|
if (sim->ext.c_int_inh) {
|
||||||
|
sim->ext.c_irq = 0;
|
||||||
|
if (!sim->ext.cc_irq)
|
||||||
|
sim->cpu.irq &= ~0x0008;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do not initiate a communication */
|
||||||
|
if (sim->ext.c_stat || (value & 0x04) == 0) /* C-Start */
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Initiate a communication */
|
||||||
|
sim->ext.c_stat = 1;
|
||||||
|
sim->ext.clocks = 3200; /* 160us */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write a value to CCSR */
|
||||||
|
static void extWriteCCSR(VB *sim, uint8_t value) {
|
||||||
|
|
||||||
|
/* Configure state */
|
||||||
|
sim->ext.cc_int_inh = value >> 7 & 1;
|
||||||
|
sim->ext.cc_int_lev = value >> 4 & 1;
|
||||||
|
sim->ext.cc_sig = value >> 3 & 1;
|
||||||
|
sim->ext.cc_wr = value >> 1 & 1;
|
||||||
|
|
||||||
|
/* Acknowledge interrupt */
|
||||||
|
if (!sim->ext.cc_int_inh)
|
||||||
|
return;
|
||||||
|
sim->ext.cc_irq = 0;
|
||||||
|
if (!sim->ext.c_irq)
|
||||||
|
sim->cpu.irq &= ~0x0008;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* VBAPI */
|
||||||
+9
-8
@@ -9,12 +9,12 @@
|
|||||||
static void padEmulate(VB *sim, uint32_t clocks) {
|
static void padEmulate(VB *sim, uint32_t clocks) {
|
||||||
|
|
||||||
/* No hardware read in progress */
|
/* No hardware read in progress */
|
||||||
if (sim->pad.si_stat == 0)
|
if (!sim->pad.si_stat)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Hardware read completes after time to process */
|
/* Hardware read completes after time to process */
|
||||||
if (sim->pad.si_stat > clocks) {
|
if (sim->pad.clocks > clocks) {
|
||||||
sim->pad.si_stat -= clocks;
|
sim->pad.clocks -= clocks;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ static int32_t padReadControl(VB *sim) {
|
|||||||
sim->pad.k_int_inh << 7 |
|
sim->pad.k_int_inh << 7 |
|
||||||
sim->pad.para_si << 5 |
|
sim->pad.para_si << 5 |
|
||||||
sim->pad.soft_ck << 4 |
|
sim->pad.soft_ck << 4 |
|
||||||
!!sim->pad.si_stat << 1 |
|
sim->pad.si_stat << 1 |
|
||||||
sim->pad.s_abt_dis
|
sim->pad.s_abt_dis
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,8 @@ static void padWriteControl(VB *sim, uint8_t value) {
|
|||||||
sim->pad.si_stat == 0 && /* No hardware read underway */
|
sim->pad.si_stat == 0 && /* No hardware read underway */
|
||||||
!sim->pad.s_abt_dis /* Hardware reads enabled */
|
!sim->pad.s_abt_dis /* Hardware reads enabled */
|
||||||
) {
|
) {
|
||||||
sim->pad.si_stat = 10240; /* 512us */
|
sim->pad.clocks = 10240; /* 512us */
|
||||||
|
sim->pad.si_stat = 1;
|
||||||
sim->pad.step = 0; /* Abort software read */
|
sim->pad.step = 0; /* Abort software read */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +87,7 @@ static void padWriteControl(VB *sim, uint8_t value) {
|
|||||||
sim->pad.para_si = value >> 5 & 1;
|
sim->pad.para_si = value >> 5 & 1;
|
||||||
if (
|
if (
|
||||||
sim->pad.para_si &&
|
sim->pad.para_si &&
|
||||||
sim->pad.si_stat == 0 /* No hardware read underway */
|
!sim->pad.si_stat /* No hardware read underway */
|
||||||
) sim->pad.step = 32;
|
) sim->pad.step = 32;
|
||||||
|
|
||||||
/* Signal a bit for a software read */
|
/* Signal a bit for a software read */
|
||||||
@@ -107,8 +108,8 @@ static void padWriteControl(VB *sim, uint8_t value) {
|
|||||||
|
|
||||||
/* Determine how many clocks are guaranteed to process */
|
/* Determine how many clocks are guaranteed to process */
|
||||||
static uint32_t padUntil(VB *sim, uint32_t clocks) {
|
static uint32_t padUntil(VB *sim, uint32_t clocks) {
|
||||||
return sim->pad.si_stat != 0 && sim->pad.si_stat < clocks ?
|
return sim->pad.si_stat && sim->pad.clocks < clocks ?
|
||||||
sim->pad.si_stat : clocks;
|
sim->pad.clocks : clocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,264 @@
|
|||||||
|
/* This file is included into vb.c and cannot be compiled on its own. */
|
||||||
|
#ifdef VBAPI
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************* Constants *********************************/
|
||||||
|
|
||||||
|
/* Pseudo-halt operations */
|
||||||
|
#define PH_NEVER 0
|
||||||
|
#define PH_DPSTTS 1
|
||||||
|
#define PH_GAME_PAD 2
|
||||||
|
#define PH_INTPND 3
|
||||||
|
#define PH_TCR 4
|
||||||
|
#define PH_TLHR 5
|
||||||
|
#define PH_XPSTTS 6
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************** Module Functions ******************************/
|
||||||
|
|
||||||
|
/* Activate pseudo-halt */
|
||||||
|
static void phActivate(VB *sim, uint32_t address, int type) {
|
||||||
|
int range; /* Memory address range by component */
|
||||||
|
|
||||||
|
/* Working variables */
|
||||||
|
address &= 0x07FFFFFF;
|
||||||
|
range = address >> 24;
|
||||||
|
|
||||||
|
/* Configure pseudo-halt */
|
||||||
|
sim->ph.operation = PH_NEVER;
|
||||||
|
|
||||||
|
/* VIP */
|
||||||
|
if (range == 0) {
|
||||||
|
address &= 0x0007FFFF;
|
||||||
|
|
||||||
|
/* TODO: Frame buffer */
|
||||||
|
|
||||||
|
/* I/O register */
|
||||||
|
switch (address) {
|
||||||
|
case 0x5F800: /* INTPND */
|
||||||
|
if (
|
||||||
|
sim->vip.dp.step != 0 ||
|
||||||
|
sim->vip.dp.enabled ||
|
||||||
|
(sim->vip.dp.disp && sim->vip.dp.synce) ||
|
||||||
|
sim->vip.xp.enabled ||
|
||||||
|
sim->vip.xp.xpen
|
||||||
|
) sim->ph.operation = PH_INTPND;
|
||||||
|
break;
|
||||||
|
case 0x5F820: /* DPSTTS */
|
||||||
|
if (
|
||||||
|
sim->vip.dp.step != 0 ||
|
||||||
|
sim->vip.dp.enabled ||
|
||||||
|
(sim->vip.dp.disp && sim->vip.dp.synce)
|
||||||
|
) sim->ph.operation = PH_DPSTTS;
|
||||||
|
break;
|
||||||
|
case 0x5F840: /* XPSTTS */
|
||||||
|
if (sim->vip.xp.enabled || sim->vip.xp.xpen)
|
||||||
|
sim->ph.operation = PH_XPSTTS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc. I/O */
|
||||||
|
else if (range == 2 && (type == VB_S8 || type == VB_U8)) {
|
||||||
|
address &= 0x0000003F;
|
||||||
|
switch (address) {
|
||||||
|
|
||||||
|
/* TODO: Communication port */
|
||||||
|
|
||||||
|
/* Game pad */
|
||||||
|
case 0x10: /* SDLR */
|
||||||
|
case 0x14: /* SDHR */
|
||||||
|
case 0x28: /* SCR */
|
||||||
|
if (sim->pad.si_stat != 0)
|
||||||
|
sim->ph.operation = PH_GAME_PAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Timer */
|
||||||
|
case 0x18: /* TLR */
|
||||||
|
case 0x1C: /* THR */
|
||||||
|
if (sim->tmr.t_enb)
|
||||||
|
sim->ph.operation = PH_TLHR;
|
||||||
|
break;
|
||||||
|
case 0x20: /* TCR */
|
||||||
|
if (sim->tmr.t_enb)
|
||||||
|
sim->ph.operation = PH_TCR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure CPU */
|
||||||
|
sim->cpu.clocks = phUntil(sim);
|
||||||
|
sim->cpu.operation = CPU_PHALT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test whether the current memory access matches the monitored access */
|
||||||
|
static int phMatches(VB *sim, uint32_t address, int type, int32_t value) {
|
||||||
|
int match; /* Parameter match */
|
||||||
|
int x; /* Iterator */
|
||||||
|
|
||||||
|
/* New memory access */
|
||||||
|
if (sim->ph.step == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Check memory access parameters */
|
||||||
|
match =
|
||||||
|
address == sim->ph.address &&
|
||||||
|
sim->cpu.pc == sim->ph.pc &&
|
||||||
|
type == sim->ph.type &&
|
||||||
|
value == sim->ph.value
|
||||||
|
;
|
||||||
|
if (!match || sim->ph.step != 3)
|
||||||
|
return match;
|
||||||
|
|
||||||
|
/* Check full CPU state */
|
||||||
|
for (x = 1; x < 32; x++) {
|
||||||
|
if (sim->ph.program[x - 1] != sim->cpu.program[x])
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return
|
||||||
|
sim->ph.adtre == sim->cpu.adtre &&
|
||||||
|
sim->ph.chcw == cpuGetSystemRegister(sim, VB_CHCW) &&
|
||||||
|
sim->ph.ecr == cpuGetSystemRegister(sim, VB_ECR) &&
|
||||||
|
sim->ph.eipc == sim->cpu.eipc &&
|
||||||
|
sim->ph.eipsw == sim->cpu.eipsw &&
|
||||||
|
sim->ph.fepc == sim->cpu.fepc &&
|
||||||
|
sim->ph.fepsw == sim->cpu.fepsw &&
|
||||||
|
sim->ph.psw == cpuGetSystemRegister(sim, VB_PSW )
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/****************************** Clock Measurers ******************************/
|
||||||
|
|
||||||
|
/* DPSTTS */
|
||||||
|
static uint32_t phDPSTTS(VB *sim) {
|
||||||
|
switch (sim->vip.dp.step) {
|
||||||
|
case 0: /* 0ms - FCLK rising edge */
|
||||||
|
case 2: /* 3ms-8ms - Display left frame buffer */
|
||||||
|
case 3: /* 8ms - L*BSY falling edge */
|
||||||
|
case 6: /* 13ms-18ms - Display right frame buffer */
|
||||||
|
case 7: /* 18ms - R*BSY falling edge */
|
||||||
|
case 8: /* 20ms - Display interval complete */
|
||||||
|
return sim->vip.dp.until;
|
||||||
|
}
|
||||||
|
/* case 1: 3ms - L*BSY rising edge */
|
||||||
|
/* case 4: 10ms - FCLK falling edge */
|
||||||
|
/* case 5: 13ms - R*BSY rising edge */
|
||||||
|
return sim->vip.dp.clocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* INTPND */
|
||||||
|
static uint32_t phINTPND(VB *sim) {
|
||||||
|
if (!(
|
||||||
|
sim->vip.dp.step != 0 ||
|
||||||
|
sim->vip.dp.enabled ||
|
||||||
|
(sim->vip.dp.disp && sim->vip.dp.synce)
|
||||||
|
)) return sim->vip.xp.until;
|
||||||
|
if (!(sim->vip.xp.enabled || sim->vip.xp.xpen))
|
||||||
|
return sim->vip.dp.until;
|
||||||
|
return sim->vip.dp.until < sim->vip.xp.until ?
|
||||||
|
sim->vip.dp.until : sim->vip.xp.until;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XPSTTS */
|
||||||
|
static uint32_t phXPSTTS(VB *sim) {
|
||||||
|
uint32_t clocks; /* Return value */
|
||||||
|
uint16_t *halfwords; /* Drawing clocks this SBCOUNT */
|
||||||
|
uint32_t x; /* Iterator */
|
||||||
|
|
||||||
|
/* Drawing is underway */
|
||||||
|
if (sim->vip.xp.f0bsy || sim->vip.xp.f1bsy) {
|
||||||
|
clocks = 0;
|
||||||
|
halfwords = &sim->vip.halfwords[
|
||||||
|
(uint32_t) sim->vip.xp.sbcount * 384 + sim->vip.xp.column];
|
||||||
|
for (x = sim->vip.xp.column; x < 384; x++, halfwords++)
|
||||||
|
clocks += *halfwords;
|
||||||
|
return clocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drawing is idle */
|
||||||
|
switch (sim->vip.dp.step) {
|
||||||
|
case 0: /* 0ms - FCLK rising edge */
|
||||||
|
return sim->vip.dp.until;
|
||||||
|
case 1: /* 3ms - L*BSY rising edge */
|
||||||
|
case 2: /* 3ms-8ms - Display left frame buffer */
|
||||||
|
case 3: /* 8ms - L*BSY falling edge */
|
||||||
|
return sim->vip.dp.until + vipClocksMs(12);
|
||||||
|
}
|
||||||
|
/* case 4: 10ms - FCLK falling edge */
|
||||||
|
/* case 5: 13ms - R*BSY rising edge */
|
||||||
|
/* case 6: 13ms-18ms - Display right frame buffer */
|
||||||
|
/* case 7: 18ms - R*BSY falling edge */
|
||||||
|
return sim->vip.dp.until + vipClocksMs(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************** Library Functions *****************************/
|
||||||
|
|
||||||
|
/* Test whether to activate pseudo-halt */
|
||||||
|
static int phAssess(VB *sim, uint32_t address, int type, int32_t value) {
|
||||||
|
int x; /* Iterator */
|
||||||
|
|
||||||
|
/* Memory access does not match last time */
|
||||||
|
address &= TYPE_MASKS[type];
|
||||||
|
if (!phMatches(sim, address, type, value))
|
||||||
|
sim->ph.step = 0;
|
||||||
|
|
||||||
|
/* New memory access */
|
||||||
|
if (sim->ph.step == 0) {
|
||||||
|
sim->ph.address = address;
|
||||||
|
sim->ph.pc = sim->cpu.pc;
|
||||||
|
sim->ph.step = 1;
|
||||||
|
sim->ph.type = type;
|
||||||
|
sim->ph.value = value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Repeated memory access, not checking full CPU state */
|
||||||
|
if (sim->ph.step < 2) {
|
||||||
|
sim->ph.step++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take a snapshot of the full CPU state */
|
||||||
|
if (sim->ph.step == 2) {
|
||||||
|
for (x = 1; x < 32; x++)
|
||||||
|
sim->ph.program[x - 1] = sim->cpu.program[x];
|
||||||
|
sim->ph.adtre = sim->cpu.adtre;
|
||||||
|
sim->ph.chcw = cpuGetSystemRegister(sim, VB_CHCW);
|
||||||
|
sim->ph.ecr = cpuGetSystemRegister(sim, VB_ECR);
|
||||||
|
sim->ph.eipc = sim->cpu.eipc;
|
||||||
|
sim->ph.eipsw = sim->cpu.eipsw;
|
||||||
|
sim->ph.fepc = sim->cpu.fepc;
|
||||||
|
sim->ph.fepsw = sim->cpu.fepsw;
|
||||||
|
sim->ph.psw = cpuGetSystemRegister(sim, VB_PSW);
|
||||||
|
sim->ph.step = 3;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Activate pseudo-halt */
|
||||||
|
phActivate(sim, address, type);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine how long the CPU should wait before checking the monitor value */
|
||||||
|
static uint32_t phUntil(VB *sim) {
|
||||||
|
switch (sim->ph.operation) {
|
||||||
|
case PH_DPSTTS : return phDPSTTS(sim);
|
||||||
|
case PH_GAME_PAD: return sim->pad.si_stat;
|
||||||
|
case PH_INTPND : return phINTPND(sim);
|
||||||
|
case PH_TCR : return sim->tmr.until;
|
||||||
|
case PH_TLHR : return sim->tmr.clocks;
|
||||||
|
case PH_XPSTTS : return phXPSTTS(sim);
|
||||||
|
}
|
||||||
|
return 0; /* PH_NEVER */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* VBAPI */
|
||||||
@@ -146,6 +146,28 @@ struct VB {
|
|||||||
int step; /* Operation sub-task ID */
|
int step; /* Operation sub-task ID */
|
||||||
} cpu;
|
} cpu;
|
||||||
|
|
||||||
|
/* Communication port */
|
||||||
|
struct {
|
||||||
|
|
||||||
|
/* Register state */
|
||||||
|
uint8_t c_clk_sel; /* Transmission clock source */
|
||||||
|
uint8_t c_int_inh; /* Interrupt acknowledge/disable */
|
||||||
|
uint8_t c_stat; /* Communication is underway */
|
||||||
|
uint8_t cc_int_inh; /* Interrupt acknowledge/disable */
|
||||||
|
uint8_t cc_int_lev; /* Interrupt condition */
|
||||||
|
uint8_t cc_rd; /* Manual read */
|
||||||
|
uint8_t cc_sig; /* Automatic write */
|
||||||
|
uint8_t cc_smp; /* Automatic read */
|
||||||
|
uint8_t cc_wr; /* Manual write */
|
||||||
|
uint8_t cdrr; /* Data received */
|
||||||
|
uint8_t cdtr; /* Data to transmit */
|
||||||
|
|
||||||
|
/* Other state */
|
||||||
|
int8_t c_irq; /* COM interrupt request */
|
||||||
|
int8_t cc_irq; /* COMCNT interrupt request */
|
||||||
|
uint32_t clocks; /* Master clocks to wait */
|
||||||
|
} ext;
|
||||||
|
|
||||||
/* Game pad */
|
/* Game pad */
|
||||||
struct {
|
struct {
|
||||||
|
|
||||||
@@ -159,8 +181,9 @@ struct VB {
|
|||||||
uint8_t soft_ck; /* Controller communication signal */
|
uint8_t soft_ck; /* Controller communication signal */
|
||||||
|
|
||||||
/* Other state */
|
/* Other state */
|
||||||
uint16_t keys; /* Next input bits */
|
uint32_t clocks; /* Master clocks to wait */
|
||||||
int step; /* Software read processing phase */
|
uint16_t keys; /* Next input bits */
|
||||||
|
int step; /* Software read processing phase */
|
||||||
} pad;
|
} pad;
|
||||||
|
|
||||||
/* Timer */
|
/* Timer */
|
||||||
@@ -209,6 +232,7 @@ struct VB {
|
|||||||
uint32_t clocks; /* Master clocks to wait */
|
uint32_t clocks; /* Master clocks to wait */
|
||||||
int column; /* Index of column to display */
|
int column; /* Index of column to display */
|
||||||
uint32_t cta; /* Column table pointer in memory */
|
uint32_t cta; /* Column table pointer in memory */
|
||||||
|
uint8_t enabled; /* Was enabled at FCLK */
|
||||||
uint32_t fbDest; /* Output frame pixel address */
|
uint32_t fbDest; /* Output frame pixel address */
|
||||||
uint32_t fbSrc; /* Source frame buffer address */
|
uint32_t fbSrc; /* Source frame buffer address */
|
||||||
int32_t repeat; /* Current column table repeat value */
|
int32_t repeat; /* Current column table repeat value */
|
||||||
@@ -231,6 +255,7 @@ struct VB {
|
|||||||
/* Other state */
|
/* Other state */
|
||||||
uint32_t clocks; /* Master clocks to wait */
|
uint32_t clocks; /* Master clocks to wait */
|
||||||
int column; /* Current horizontal output position */
|
int column; /* Current horizontal output position */
|
||||||
|
uint8_t enabled; /* Was enabled at FCLK */
|
||||||
int frame; /* FRMCYC counter */
|
int frame; /* FRMCYC counter */
|
||||||
int32_t halfword; /* Current output halfword offset */
|
int32_t halfword; /* Current output halfword offset */
|
||||||
int step; /* Processing phase */
|
int step; /* Processing phase */
|
||||||
@@ -294,9 +319,10 @@ struct VB {
|
|||||||
struct {
|
struct {
|
||||||
float i1[2]; /* Previous analog input sample */
|
float i1[2]; /* Previous analog input sample */
|
||||||
float o1[2]; /* Previous analog output sample */
|
float o1[2]; /* Previous analog output sample */
|
||||||
uint32_t capacity; /* Number of audio frames in samples */
|
unsigned capacity; /* Number of audio frames in output buffer */
|
||||||
uint32_t offset; /* Position in output buffer */
|
unsigned offset; /* Position in output buffer */
|
||||||
int16_t *samples; /* Output memory */
|
void *samples; /* Output buffer */
|
||||||
|
int type; /* Output data type */
|
||||||
} out;
|
} out;
|
||||||
|
|
||||||
/* Memory */
|
/* Memory */
|
||||||
@@ -308,8 +334,37 @@ struct VB {
|
|||||||
int sample; /* Output sample index, period 417 */
|
int sample; /* Output sample index, period 417 */
|
||||||
} vsu;
|
} vsu;
|
||||||
|
|
||||||
|
/* Wait controller */
|
||||||
|
struct {
|
||||||
|
uint8_t exp1w; /* Cartridge expansion 1-wait */
|
||||||
|
uint8_t rom1w; /* Cartridge ROM 1-wait */
|
||||||
|
} wcr;
|
||||||
|
|
||||||
|
/* Pseudo-halt */
|
||||||
|
struct {
|
||||||
|
uint32_t address; /* Monitor address */
|
||||||
|
uint8_t enabled; /* Pseudo-halt function is enabled */
|
||||||
|
uint8_t operation; /* Monitoring operation */
|
||||||
|
uint8_t step; /* Number of consecutive matching reads */
|
||||||
|
int type; /* Memory access type */
|
||||||
|
int32_t value; /* Value read from monitor address */
|
||||||
|
|
||||||
|
/* CPU snapshot */
|
||||||
|
uint32_t adtre;
|
||||||
|
uint32_t chcw;
|
||||||
|
uint32_t ecr;
|
||||||
|
uint32_t eipc;
|
||||||
|
uint32_t eipsw;
|
||||||
|
uint32_t fepc;
|
||||||
|
uint32_t fepsw;
|
||||||
|
uint32_t pc;
|
||||||
|
int32_t program[31];
|
||||||
|
uint32_t psw;
|
||||||
|
uint32_t sr29;
|
||||||
|
uint32_t sr31;
|
||||||
|
} ph;
|
||||||
|
|
||||||
/* Other state */
|
/* Other state */
|
||||||
uint8_t wcr; /* Wait controller state */
|
|
||||||
uint8_t wram[0x10000]; /* System RAM */
|
uint8_t wram[0x10000]; /* System RAM */
|
||||||
|
|
||||||
/* Application data */
|
/* Application data */
|
||||||
@@ -317,9 +372,11 @@ struct VB {
|
|||||||
vbOnExecute onExecute; /* CPU instruction execute */
|
vbOnExecute onExecute; /* CPU instruction execute */
|
||||||
vbOnFetch onFetch; /* CPU instruction fetch */
|
vbOnFetch onFetch; /* CPU instruction fetch */
|
||||||
vbOnFrame onFrame; /* VIP frame ready */
|
vbOnFrame onFrame; /* VIP frame ready */
|
||||||
|
vbOnLink onLink; /* Communication transfer */
|
||||||
vbOnRead onRead; /* CPU instruction read */
|
vbOnRead onRead; /* CPU instruction read */
|
||||||
vbOnSamples onSamples; /* VSU samples full */
|
vbOnSamples onSamples; /* VSU samples full */
|
||||||
vbOnWrite onWrite; /* CPU instruction write */
|
vbOnWrite onWrite; /* CPU instruction write */
|
||||||
|
VB *peer; /* Communication peer */
|
||||||
void *tag; /* User data */
|
void *tag; /* User data */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -342,12 +399,14 @@ static int32_t SignExtend(int32_t value, int32_t bits) {
|
|||||||
|
|
||||||
/******************************** Sub-Modules ********************************/
|
/******************************** Sub-Modules ********************************/
|
||||||
|
|
||||||
|
#include "ext.c"
|
||||||
#include "game-pad.c"
|
#include "game-pad.c"
|
||||||
#include "timer.c"
|
#include "timer.c"
|
||||||
#include "bus.c"
|
#include "bus.c"
|
||||||
#include "cpu.c"
|
#include "cpu.c"
|
||||||
#include "vip.c"
|
#include "vip.c"
|
||||||
#include "vsu.c"
|
#include "vsu.c"
|
||||||
|
#include "pseudo-halt.c"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -355,18 +414,29 @@ static int32_t SignExtend(int32_t value, int32_t bits) {
|
|||||||
|
|
||||||
/* Process a simulation for a given number of clocks */
|
/* Process a simulation for a given number of clocks */
|
||||||
static int sysEmulate(VB *sim, uint32_t clocks) {
|
static int sysEmulate(VB *sim, uint32_t clocks) {
|
||||||
int ret;
|
int ret = 0;
|
||||||
ret = cpuEmulate(sim, clocks);
|
|
||||||
padEmulate(sim, clocks);
|
/* CPU is in a pseudo-halt state that requires an interrupt to exit */
|
||||||
tmrEmulate(sim, clocks);
|
int never =
|
||||||
vsuEmulate(sim, clocks);
|
sim->cpu.operation == CPU_PHALT &&
|
||||||
ret |= vipEmulate(sim, clocks);
|
sim->ph.operation == PH_NEVER
|
||||||
|
;
|
||||||
|
|
||||||
|
/* Process all components */
|
||||||
|
if (!never){ret = cpuEmulate(sim, clocks);}
|
||||||
|
ret |= extEmulate(sim, clocks);
|
||||||
|
padEmulate(sim, clocks);
|
||||||
|
tmrEmulate(sim, clocks);
|
||||||
|
vsuEmulate(sim, clocks);
|
||||||
|
ret |= vipEmulate(sim, clocks);
|
||||||
|
if ( never) ret |= cpuEmulate(sim, clocks);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine how many clocks are guaranteed to process */
|
/* Determine how many clocks are guaranteed to process */
|
||||||
static uint32_t sysUntil(VB *sim, uint32_t clocks) {
|
static uint32_t sysUntil(VB *sim, uint32_t clocks) {
|
||||||
clocks = cpuUntil(sim, clocks);
|
clocks = cpuUntil(sim, clocks);
|
||||||
|
clocks = extUntil(sim, clocks);
|
||||||
clocks = padUntil(sim, clocks);
|
clocks = padUntil(sim, clocks);
|
||||||
clocks = tmrUntil(sim, clocks);
|
clocks = tmrUntil(sim, clocks);
|
||||||
clocks = vipUntil(sim, clocks);
|
clocks = vipUntil(sim, clocks);
|
||||||
@@ -392,19 +462,23 @@ VBAPI int vbEmulate(VB *sim, uint32_t *clocks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Process multiple simulations */
|
/* Process multiple simulations */
|
||||||
VBAPI int vbEmulateEx(VB **sims, int count, uint32_t *clocks) {
|
VBAPI int vbEmulateEx(VB **sims, unsigned count, uint32_t *clocks) {
|
||||||
int brk; /* A callback requested a break */
|
int brk; /* A callback requested a break */
|
||||||
uint32_t until; /* Clocks guaranteed to process */
|
uint32_t until; /* Clocks guaranteed to process */
|
||||||
int x; /* Iterator */
|
unsigned x; /* Iterator */
|
||||||
|
|
||||||
|
/* Process one simulation */
|
||||||
|
if (count == 1)
|
||||||
|
return vbEmulate(sims[0], clocks);
|
||||||
|
|
||||||
|
/* Process multiple simulations */
|
||||||
while (*clocks != 0) {
|
while (*clocks != 0) {
|
||||||
until = *clocks;
|
until = *clocks;
|
||||||
for (x = count - 1; x >= 0; x--)
|
for (x = count; x > 0; x--)
|
||||||
until = sysUntil(sims[x], until);
|
until = sysUntil(sims[x - 1], until);
|
||||||
|
|
||||||
brk = 0;
|
brk = 0;
|
||||||
for (x = count - 1; x >= 0; x--)
|
for (x = count; x > 0; x--)
|
||||||
brk |= sysEmulate(sims[x], until);
|
brk |= sysEmulate(sims[x - 1], until);
|
||||||
|
|
||||||
*clocks -= until;
|
*clocks -= until;
|
||||||
if (brk)
|
if (brk)
|
||||||
return brk; /* TODO: return 1 */
|
return brk; /* TODO: return 1 */
|
||||||
@@ -451,6 +525,24 @@ VBAPI uint16_t vbGetKeys(VB *sim) {
|
|||||||
return sim->pad.keys;
|
return sim->pad.keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Retrieve the current link callback handler */
|
||||||
|
VBAPI vbOnLink vbGetLinkCallback(VB *sim) {
|
||||||
|
return sim->onLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve a core option value */
|
||||||
|
VBAPI int vbGetOption(VB *sim, int key) {
|
||||||
|
switch (key) {
|
||||||
|
case VB_PSEUDO_HALT: return sim->ph.enabled;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve the communication peer */
|
||||||
|
VBAPI VB* vbGetPeer(VB *sim) {
|
||||||
|
return sim->peer;
|
||||||
|
}
|
||||||
|
|
||||||
/* Retrieve the most recent frame image pixels */
|
/* Retrieve the most recent frame image pixels */
|
||||||
VBAPI void vbGetPixels(VB *sim, void *left, int leftStrideX, int leftStrideY,
|
VBAPI void vbGetPixels(VB *sim, void *left, int leftStrideX, int leftStrideY,
|
||||||
void *right, int rightStrideX, int rightStrideY) {
|
void *right, int rightStrideX, int rightStrideY) {
|
||||||
@@ -497,7 +589,7 @@ VBAPI uint32_t vbGetProgramCounter(VB *sim) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Retrieve the value in a program register */
|
/* Retrieve the value in a program register */
|
||||||
VBAPI int32_t vbGetProgramRegister(VB *sim, int index) {
|
VBAPI int32_t vbGetProgramRegister(VB *sim, unsigned index) {
|
||||||
return index < 1 || index > 31 ? 0 : sim->cpu.program[index];
|
return index < 1 || index > 31 ? 0 : sim->cpu.program[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,13 +599,18 @@ VBAPI vbOnRead vbGetReadCallback(VB *sim) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Retrieve the audio samples buffer */
|
/* Retrieve the audio samples buffer */
|
||||||
VBAPI void* vbGetSamples(VB *sim, uint32_t *capacity, uint32_t *position) {
|
VBAPI void* vbGetSamples(VB *sim, int *type,
|
||||||
|
unsigned *capacity, unsigned *position) {
|
||||||
if (sim->vsu.out.samples == NULL) {
|
if (sim->vsu.out.samples == NULL) {
|
||||||
|
if (type != NULL)
|
||||||
|
*type = 0;
|
||||||
if (capacity != NULL)
|
if (capacity != NULL)
|
||||||
*capacity = 0;
|
*capacity = 0;
|
||||||
if (position != NULL)
|
if (position != NULL)
|
||||||
*position = 0;
|
*position = 0;
|
||||||
} else {
|
} else {
|
||||||
|
if (type != NULL)
|
||||||
|
*type = sim->vsu.out.type;
|
||||||
if (capacity != NULL)
|
if (capacity != NULL)
|
||||||
*capacity = sim->vsu.out.capacity;
|
*capacity = sim->vsu.out.capacity;
|
||||||
if (position != NULL)
|
if (position != NULL)
|
||||||
@@ -528,8 +625,8 @@ VBAPI vbOnSamples vbGetSamplesCallback(VB *sim) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Retrieve the value in a system register */
|
/* Retrieve the value in a system register */
|
||||||
VBAPI uint32_t vbGetSystemRegister(VB *sim, int index) {
|
VBAPI uint32_t vbGetSystemRegister(VB *sim, unsigned index) {
|
||||||
return index < 0 || index > 31 ? 0 : cpuGetSystemRegister(sim, index);
|
return index > 31 ? 0 : cpuGetSystemRegister(sim, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Retrieve a simulation's userdata pointer */
|
/* Retrieve a simulation's userdata pointer */
|
||||||
@@ -550,9 +647,12 @@ VBAPI VB* vbInit(VB *sim) {
|
|||||||
sim->onExecute = NULL;
|
sim->onExecute = NULL;
|
||||||
sim->onFetch = NULL;
|
sim->onFetch = NULL;
|
||||||
sim->onFrame = NULL;
|
sim->onFrame = NULL;
|
||||||
|
sim->onLink = NULL;
|
||||||
sim->onRead = NULL;
|
sim->onRead = NULL;
|
||||||
sim->onSamples = NULL;
|
sim->onSamples = NULL;
|
||||||
sim->onWrite = NULL;
|
sim->onWrite = NULL;
|
||||||
|
sim->peer = NULL;
|
||||||
|
sim->ph.enabled = 0;
|
||||||
vbReset(sim);
|
vbReset(sim);
|
||||||
return sim;
|
return sim;
|
||||||
}
|
}
|
||||||
@@ -571,7 +671,8 @@ VBAPI VB* vbReset(VB *sim) {
|
|||||||
int x; /* Iterator */
|
int x; /* Iterator */
|
||||||
|
|
||||||
/* Wait controller */
|
/* Wait controller */
|
||||||
sim->wcr = 0x00;
|
sim->wcr.exp1w = 0;
|
||||||
|
sim->wcr.rom1w = 0;
|
||||||
|
|
||||||
/* WRAM (the hardware does not do this) */
|
/* WRAM (the hardware does not do this) */
|
||||||
for (x = 0; x < 0x10000; x++)
|
for (x = 0; x < 0x10000; x++)
|
||||||
@@ -579,10 +680,14 @@ VBAPI VB* vbReset(VB *sim) {
|
|||||||
|
|
||||||
/* Components */
|
/* Components */
|
||||||
cpuReset(sim);
|
cpuReset(sim);
|
||||||
|
extReset(sim);
|
||||||
padReset(sim);
|
padReset(sim);
|
||||||
tmrReset(sim);
|
tmrReset(sim);
|
||||||
vipReset(sim);
|
vipReset(sim);
|
||||||
vsuReset(sim);
|
vsuReset(sim);
|
||||||
|
|
||||||
|
/* Pseudo-halt */
|
||||||
|
sim->ph.step = 0;
|
||||||
return sim;
|
return sim;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,6 +746,42 @@ VBAPI uint16_t vbSetKeys(VB *sim, uint16_t keys) {
|
|||||||
return sim->pad.keys = keys;
|
return sim->pad.keys = keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Specify a new link callback handler */
|
||||||
|
VBAPI vbOnLink vbSetLinkCallback(VB *sim, vbOnLink callback) {
|
||||||
|
vbOnLink prev = sim->onLink;
|
||||||
|
sim->onLink = callback;
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify a new core option value */
|
||||||
|
VBAPI int vbSetOption(VB *sim, int key, int value) {
|
||||||
|
switch (key) {
|
||||||
|
case VB_PSEUDO_HALT:
|
||||||
|
sim->ph.enabled = value = !!value;
|
||||||
|
if (!value) {
|
||||||
|
sim->ph.step = 0;
|
||||||
|
if (sim->cpu.operation == CPU_PHALT) {
|
||||||
|
sim->cpu.operation = CPU_FETCH;
|
||||||
|
sim->cpu.step = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify a new communication peer */
|
||||||
|
VBAPI void vbSetPeer(VB *sim, VB *peer) {
|
||||||
|
if (sim->peer == peer)
|
||||||
|
return;
|
||||||
|
sim->peer = peer;
|
||||||
|
if (peer == NULL)
|
||||||
|
return;
|
||||||
|
if (peer->peer != NULL)
|
||||||
|
peer->peer->peer = NULL;
|
||||||
|
peer->peer = sim;
|
||||||
|
}
|
||||||
|
|
||||||
/* Specify a new value for the program counter */
|
/* Specify a new value for the program counter */
|
||||||
VBAPI uint32_t vbSetProgramCounter(VB *sim, uint32_t value) {
|
VBAPI uint32_t vbSetProgramCounter(VB *sim, uint32_t value) {
|
||||||
sim->cpu.operation = CPU_FETCH;
|
sim->cpu.operation = CPU_FETCH;
|
||||||
@@ -650,7 +791,7 @@ VBAPI uint32_t vbSetProgramCounter(VB *sim, uint32_t value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Specify a new value for a program register */
|
/* Specify a new value for a program register */
|
||||||
VBAPI int32_t vbSetProgramRegister(VB *sim, int index, int32_t value) {
|
VBAPI int32_t vbSetProgramRegister(VB *sim, unsigned index, int32_t value) {
|
||||||
return index < 1 || index > 31 ? 0 : (sim->cpu.program[index] = value);
|
return index < 1 || index > 31 ? 0 : (sim->cpu.program[index] = value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,12 +803,14 @@ VBAPI vbOnRead vbSetReadCallback(VB *sim, vbOnRead callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Specify a new audio samples buffer */
|
/* Specify a new audio samples buffer */
|
||||||
VBAPI int vbSetSamples(VB *sim, void *samples, uint32_t capacity) {
|
VBAPI int vbSetSamples(VB *sim, void *samples, int type, unsigned capacity) {
|
||||||
if (samples != NULL && (capacity == 0 || capacity > 0x40000000))
|
if (samples != NULL &&
|
||||||
|
(capacity == 0 || (type != VB_S16 && type != VB_F32)))
|
||||||
return 1;
|
return 1;
|
||||||
sim->vsu.out.capacity = samples == NULL ? 0 : capacity;
|
sim->vsu.out.capacity = capacity;
|
||||||
sim->vsu.out.offset = 0;
|
sim->vsu.out.offset = 0;
|
||||||
sim->vsu.out.samples = samples;
|
sim->vsu.out.samples = samples;
|
||||||
|
sim->vsu.out.type = type;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -679,9 +822,8 @@ VBAPI vbOnSamples vbSetSamplesCallback(VB *sim, vbOnSamples callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Specify a new value for a system register */
|
/* Specify a new value for a system register */
|
||||||
VBAPI uint32_t vbSetSystemRegister(VB *sim, int index, uint32_t value) {
|
VBAPI uint32_t vbSetSystemRegister(VB *sim, unsigned index, uint32_t value) {
|
||||||
return index < 0 || index > 31 ? 0 :
|
return index > 31 ? 0 : cpuSetSystemRegister(sim, index, value, 1);
|
||||||
cpuSetSystemRegister(sim, index, value, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Specify a new write callback handler */
|
/* Specify a new write callback handler */
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ extern "C" {
|
|||||||
VB_DIRECT_EXECUTE
|
VB_DIRECT_EXECUTE
|
||||||
VB_DIRECT_FETCH
|
VB_DIRECT_FETCH
|
||||||
VB_DIRECT_FRAME
|
VB_DIRECT_FRAME
|
||||||
|
VB_DIRECT_LINK
|
||||||
VB_DIRECT_READ
|
VB_DIRECT_READ
|
||||||
VB_DIRECT_SAMPLES
|
VB_DIRECT_SAMPLES
|
||||||
VB_DIRECT_WRITE
|
VB_DIRECT_WRITE
|
||||||
@@ -78,6 +79,10 @@ extern "C" {
|
|||||||
#define VB_S16 2
|
#define VB_S16 2
|
||||||
#define VB_U16 3
|
#define VB_U16 3
|
||||||
#define VB_S32 4
|
#define VB_S32 4
|
||||||
|
#define VB_F32 5
|
||||||
|
|
||||||
|
/* Option keys */
|
||||||
|
#define VB_PSEUDO_HALT 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -91,6 +96,7 @@ typedef int (*vbOnException)(VB *sim, uint16_t *cause);
|
|||||||
typedef int (*vbOnExecute )(VB *sim, uint32_t address, const uint16_t *code, int length);
|
typedef int (*vbOnExecute )(VB *sim, uint32_t address, const uint16_t *code, int length);
|
||||||
typedef int (*vbOnFetch )(VB *sim, int fetch, uint32_t address, int32_t *value, uint32_t *cycles);
|
typedef int (*vbOnFetch )(VB *sim, int fetch, uint32_t address, int32_t *value, uint32_t *cycles);
|
||||||
typedef int (*vbOnFrame )(VB *sim);
|
typedef int (*vbOnFrame )(VB *sim);
|
||||||
|
typedef int (*vbOnLink )(VB *sim1, VB *sim2, uint8_t *value1, uint8_t *value2);
|
||||||
typedef int (*vbOnRead )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles);
|
typedef int (*vbOnRead )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles);
|
||||||
typedef void (*vbOnSamples )(VB *sim, int16_t *buffer, uint32_t capacity);
|
typedef void (*vbOnSamples )(VB *sim, int16_t *buffer, uint32_t capacity);
|
||||||
typedef int (*vbOnWrite )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles, int *cancel);
|
typedef int (*vbOnWrite )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles, int *cancel);
|
||||||
@@ -101,7 +107,7 @@ typedef int (*vbOnWrite )(VB *sim, uint32_t address, int type, int32_t *valu
|
|||||||
/******************************* API Commands ********************************/
|
/******************************* API Commands ********************************/
|
||||||
|
|
||||||
VBAPI int vbEmulate (VB *sim, uint32_t *clocks);
|
VBAPI int vbEmulate (VB *sim, uint32_t *clocks);
|
||||||
VBAPI int vbEmulateEx (VB **sims, int count, uint32_t *clocks);
|
VBAPI int vbEmulateEx (VB **sims, unsigned count, uint32_t *clocks);
|
||||||
VBAPI void* vbGetCallback (VB *sim, int id);
|
VBAPI void* vbGetCallback (VB *sim, int id);
|
||||||
VBAPI void* vbGetCartRAM (VB *sim, uint32_t *size);
|
VBAPI void* vbGetCartRAM (VB *sim, uint32_t *size);
|
||||||
VBAPI void* vbGetCartROM (VB *sim, uint32_t *size);
|
VBAPI void* vbGetCartROM (VB *sim, uint32_t *size);
|
||||||
@@ -110,13 +116,16 @@ VBAPI vbOnExecute vbGetExecuteCallback (VB *sim);
|
|||||||
VBAPI vbOnFetch vbGetFetchCallback (VB *sim);
|
VBAPI vbOnFetch vbGetFetchCallback (VB *sim);
|
||||||
VBAPI vbOnFrame vbGetFrameCallback (VB *sim);
|
VBAPI vbOnFrame vbGetFrameCallback (VB *sim);
|
||||||
VBAPI uint16_t vbGetKeys (VB *sim);
|
VBAPI uint16_t vbGetKeys (VB *sim);
|
||||||
|
VBAPI vbOnLink vbGetLinkCallback (VB *sim);
|
||||||
|
VBAPI int vbGetOption (VB *sim, int key);
|
||||||
|
VBAPI VB* vbGetPeer (VB *sim);
|
||||||
VBAPI void vbGetPixels (VB *sim, void *left, int leftStrideX, int leftStrideY, void *right, int rightStrideX, int rightStrideY);
|
VBAPI void vbGetPixels (VB *sim, void *left, int leftStrideX, int leftStrideY, void *right, int rightStrideX, int rightStrideY);
|
||||||
VBAPI uint32_t vbGetProgramCounter (VB *sim);
|
VBAPI uint32_t vbGetProgramCounter (VB *sim);
|
||||||
VBAPI int32_t vbGetProgramRegister (VB *sim, int index);
|
VBAPI int32_t vbGetProgramRegister (VB *sim, unsigned index);
|
||||||
VBAPI vbOnRead vbGetReadCallback (VB *sim);
|
VBAPI vbOnRead vbGetReadCallback (VB *sim);
|
||||||
VBAPI void* vbGetSamples (VB *sim, uint32_t *capacity, uint32_t *position);
|
VBAPI void* vbGetSamples (VB *sim, int *type, unsigned *capacity, unsigned *position);
|
||||||
VBAPI vbOnSamples vbGetSamplesCallback (VB *sim);
|
VBAPI vbOnSamples vbGetSamplesCallback (VB *sim);
|
||||||
VBAPI uint32_t vbGetSystemRegister (VB *sim, int index);
|
VBAPI uint32_t vbGetSystemRegister (VB *sim, unsigned index);
|
||||||
VBAPI void* vbGetUserData (VB *sim);
|
VBAPI void* vbGetUserData (VB *sim);
|
||||||
VBAPI vbOnWrite vbGetWriteCallback (VB *sim);
|
VBAPI vbOnWrite vbGetWriteCallback (VB *sim);
|
||||||
VBAPI VB* vbInit (VB *sim);
|
VBAPI VB* vbInit (VB *sim);
|
||||||
@@ -128,13 +137,16 @@ VBAPI vbOnException vbSetExceptionCallback(VB *sim, vbOnException callback);
|
|||||||
VBAPI vbOnExecute vbSetExecuteCallback (VB *sim, vbOnExecute callback);
|
VBAPI vbOnExecute vbSetExecuteCallback (VB *sim, vbOnExecute callback);
|
||||||
VBAPI vbOnFetch vbSetFetchCallback (VB *sim, vbOnFetch callback);
|
VBAPI vbOnFetch vbSetFetchCallback (VB *sim, vbOnFetch callback);
|
||||||
VBAPI vbOnFrame vbSetFrameCallback (VB *sim, vbOnFrame callback);
|
VBAPI vbOnFrame vbSetFrameCallback (VB *sim, vbOnFrame callback);
|
||||||
|
VBAPI vbOnLink vbSetLinkCallback (VB *sim, vbOnLink callback);
|
||||||
VBAPI uint16_t vbSetKeys (VB *sim, uint16_t keys);
|
VBAPI uint16_t vbSetKeys (VB *sim, uint16_t keys);
|
||||||
|
VBAPI int vbSetOption (VB *sim, int key, int value);
|
||||||
|
VBAPI void vbSetPeer (VB *sim, VB *peer);
|
||||||
VBAPI uint32_t vbSetProgramCounter (VB *sim, uint32_t value);
|
VBAPI uint32_t vbSetProgramCounter (VB *sim, uint32_t value);
|
||||||
VBAPI int32_t vbSetProgramRegister (VB *sim, int index, int32_t value);
|
VBAPI int32_t vbSetProgramRegister (VB *sim, unsigned index, int32_t value);
|
||||||
VBAPI vbOnRead vbSetReadCallback (VB *sim, vbOnRead callback);
|
VBAPI vbOnRead vbSetReadCallback (VB *sim, vbOnRead callback);
|
||||||
VBAPI int vbSetSamples (VB *sim, void *samples, uint32_t capacity);
|
VBAPI int vbSetSamples (VB *sim, void *samples, int type, unsigned capacity);
|
||||||
VBAPI vbOnSamples vbSetSamplesCallback (VB *sim, vbOnSamples callback);
|
VBAPI vbOnSamples vbSetSamplesCallback (VB *sim, vbOnSamples callback);
|
||||||
VBAPI uint32_t vbSetSystemRegister (VB *sim, int index, uint32_t value);
|
VBAPI uint32_t vbSetSystemRegister (VB *sim, unsigned index, uint32_t value);
|
||||||
VBAPI void* vbSetUserData (VB *sim, void *tag);
|
VBAPI void* vbSetUserData (VB *sim, void *tag);
|
||||||
VBAPI vbOnWrite vbSetWriteCallback (VB *sim, vbOnWrite callback);
|
VBAPI vbOnWrite vbSetWriteCallback (VB *sim, vbOnWrite callback);
|
||||||
VBAPI size_t vbSizeOf ();
|
VBAPI size_t vbSizeOf ();
|
||||||
|
|||||||
+217
-158
@@ -42,18 +42,44 @@ static const uint8_t BG_TEMPLATES[][64] = {
|
|||||||
|
|
||||||
/* 8-bit color magnitude by brightness level */
|
/* 8-bit color magnitude by brightness level */
|
||||||
static const uint8_t BRIGHT8[] = {
|
static const uint8_t BRIGHT8[] = {
|
||||||
0, 10, 16, 21, 25, 30, 33, 37, 40, 44, 47, 50, 53, 56, 59, 61,
|
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
|
||||||
64, 67, 69, 72, 74, 77, 79, 82, 84, 86, 89, 91, 93, 95, 97,100,
|
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
|
||||||
102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,131,
|
64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94,
|
||||||
133,135,137,139,141,142,144,146,148,149,151,153,155,156,158,160,
|
96, 98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,
|
||||||
161,163,165,166,168,170,171,173,175,176,178,179,181,183,184,186,
|
129,131,133,135,137,139,141,143,145,147,149,151,153,155,157,159,
|
||||||
187,189,190,192,194,195,197,198,200,201,203,204,206,207,209,210,
|
161,163,165,167,169,171,173,175,177,179,181,183,185,187,189,191,
|
||||||
212,213,215,216,217,219,220,222,223,225,226,227,229,230,232,233,
|
193,195,197,199,201,203,205,207,209,211,213,215,217,219,221,223,
|
||||||
235,236,237,239,240,241,243,244,246,247,248,250,251,252,254,255
|
225,227,229,231,233,235,237,239,241,243,245,247,249,251,253,255
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** Types ***********************************/
|
||||||
|
|
||||||
|
/* World attribtues per eye */
|
||||||
|
typedef struct {
|
||||||
|
int32_t bp; /* BG source parallax */
|
||||||
|
int32_t left, right; /* Window bounds */
|
||||||
|
int32_t wx; /* World horizontal position */
|
||||||
|
} EyeAttribs;
|
||||||
|
|
||||||
|
/* Parsed world attributes */
|
||||||
|
typedef struct {
|
||||||
|
int32_t base; /* Base BG map index */
|
||||||
|
uint8_t *bg; /* Background template */
|
||||||
|
int32_t bgm; /* World type */
|
||||||
|
int32_t bgw, bgh; /* Background dimension masks */
|
||||||
|
int32_t bottom, top; /* Window bounds */
|
||||||
|
int32_t gx, gp, gy; /* World screen position */
|
||||||
|
int32_t mx, mp, my; /* Background scroll */
|
||||||
|
uint8_t *over; /* Overplane character */
|
||||||
|
uint32_t params; /* Line parameters address */
|
||||||
|
int32_t scx, scy; /* Background dimensions */
|
||||||
|
int32_t w, h; /* Window size */
|
||||||
|
} WorldAttribs;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************** Module Functions ******************************/
|
/***************************** Module Functions ******************************/
|
||||||
|
|
||||||
/* Retrieve a pointer to character data in host memory */
|
/* Retrieve a pointer to character data in host memory */
|
||||||
@@ -68,10 +94,9 @@ static int32_t vipReadPalette(uint8_t *entries) {
|
|||||||
|
|
||||||
/* Raise an interrupt request */
|
/* Raise an interrupt request */
|
||||||
static void vipThrow(VB *sim, uint16_t cause) {
|
static void vipThrow(VB *sim, uint16_t cause) {
|
||||||
if (!(sim->vip.intenb & cause))
|
|
||||||
return;
|
|
||||||
sim->vip.intpnd |= cause;
|
sim->vip.intpnd |= cause;
|
||||||
sim->cpu.irq |= 0x0010;
|
if (sim->vip.intenb & cause)
|
||||||
|
sim->cpu.irq |= 0x0010;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a palette */
|
/* Write a palette */
|
||||||
@@ -421,11 +446,10 @@ static void vipTransferColumn(VB *sim, int32_t eye) {
|
|||||||
[sim->vip.dp.buffer][eye][sim->vip.dp.column];
|
[sim->vip.dp.buffer][eye][sim->vip.dp.column];
|
||||||
|
|
||||||
/* Output is disabled */
|
/* Output is disabled */
|
||||||
if ((sim->vip.dp.disp & sim->vip.dp.synce) == 0) {
|
if (!sim->vip.dp.enabled) {
|
||||||
for (z = 0; z < 0x15000; z += 4) {
|
for (y = 0; y < 224; y += 16)
|
||||||
busWriteBuffer(dest, VB_S32, 0);
|
for (z = 0; z < 16; z++, dest += 384)
|
||||||
dest += 4;
|
*dest = 0;
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,10 +491,11 @@ static int vipEmulateDisplay(VB *sim, uint32_t clocks) {
|
|||||||
switch (sim->vip.dp.step) {
|
switch (sim->vip.dp.step) {
|
||||||
|
|
||||||
case 0: /* 0ms - FCLK rising edge */
|
case 0: /* 0ms - FCLK rising edge */
|
||||||
sim->vip.dp.clocks = vipClocksMs(3);
|
sim->vip.dp.clocks = vipClocksMs(3);
|
||||||
sim->vip.dp.fclk = 1;
|
sim->vip.dp.enabled = sim->vip.dp.disp & sim->vip.dp.synce;
|
||||||
sim->vip.dp.step = 1;
|
sim->vip.dp.fclk = 1;
|
||||||
sim->vip.dp.until = vipClocksMs(8);
|
sim->vip.dp.step = 1;
|
||||||
|
sim->vip.dp.until = vipClocksMs(8);
|
||||||
vipThrow(sim, 0x0010); /* FRAMESTART */
|
vipThrow(sim, 0x0010); /* FRAMESTART */
|
||||||
|
|
||||||
/* Game frame */
|
/* Game frame */
|
||||||
@@ -481,6 +506,7 @@ static int vipEmulateDisplay(VB *sim, uint32_t clocks) {
|
|||||||
/* Initiate drawing procedure */
|
/* Initiate drawing procedure */
|
||||||
if (sim->vip.xp.step == 0) {
|
if (sim->vip.xp.step == 0) {
|
||||||
sim->vip.dp.buffer ^= 1;
|
sim->vip.dp.buffer ^= 1;
|
||||||
|
sim->vip.xp.enabled = 1;
|
||||||
sim->vip.xp.overtime = 0;
|
sim->vip.xp.overtime = 0;
|
||||||
sim->vip.xp.step = 1;
|
sim->vip.xp.step = 1;
|
||||||
vipThrow(sim, 0x0008); /* GAMESTART */
|
vipThrow(sim, 0x0008); /* GAMESTART */
|
||||||
@@ -500,9 +526,11 @@ static int vipEmulateDisplay(VB *sim, uint32_t clocks) {
|
|||||||
/* 0ms-3ms - Idle */
|
/* 0ms-3ms - Idle */
|
||||||
|
|
||||||
case 1: /* 3ms - L*BSY rising edge */
|
case 1: /* 3ms - L*BSY rising edge */
|
||||||
if (sim->vip.dp.buffer == 0)
|
if (sim->vip.dp.enabled) {
|
||||||
sim->vip.dp.l0bsy = 1;
|
if (sim->vip.dp.buffer == 0)
|
||||||
else sim->vip.dp.l1bsy = 1;
|
sim->vip.dp.l0bsy = 1;
|
||||||
|
else sim->vip.dp.l1bsy = 1;
|
||||||
|
}
|
||||||
sim->vip.dp.column = 0;
|
sim->vip.dp.column = 0;
|
||||||
sim->vip.dp.cta = 0x3DC00 | (uint32_t)sim->vip.cta.cta_l<<1;
|
sim->vip.dp.cta = 0x3DC00 | (uint32_t)sim->vip.cta.cta_l<<1;
|
||||||
sim->vip.dp.step = 2;
|
sim->vip.dp.step = 2;
|
||||||
@@ -523,13 +551,15 @@ static int vipEmulateDisplay(VB *sim, uint32_t clocks) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 3: /* 8ms - L*BSY falling edge */
|
case 3: /* 8ms - L*BSY falling edge */
|
||||||
if (sim->vip.dp.buffer == 0)
|
if (sim->vip.dp.enabled) {
|
||||||
sim->vip.dp.l0bsy = 0;
|
if (sim->vip.dp.buffer == 0)
|
||||||
else sim->vip.dp.l1bsy = 0;
|
sim->vip.dp.l0bsy = 0;
|
||||||
|
else sim->vip.dp.l1bsy = 0;
|
||||||
|
vipThrow(sim, 0x0002); /* LFBEND */
|
||||||
|
}
|
||||||
sim->vip.dp.clocks = vipClocksMs(2);
|
sim->vip.dp.clocks = vipClocksMs(2);
|
||||||
sim->vip.dp.step = 4;
|
sim->vip.dp.step = 4;
|
||||||
sim->vip.dp.until = vipClocksMs(10);
|
sim->vip.dp.until = vipClocksMs(10);
|
||||||
vipThrow(sim, 0x0002); /* LFBEND */
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* 8ms-10ms - Idle */
|
/* 8ms-10ms - Idle */
|
||||||
@@ -543,9 +573,11 @@ static int vipEmulateDisplay(VB *sim, uint32_t clocks) {
|
|||||||
/* 10ms-13ms - Idle */
|
/* 10ms-13ms - Idle */
|
||||||
|
|
||||||
case 5: /* 13ms - R*BSY rising edge */
|
case 5: /* 13ms - R*BSY rising edge */
|
||||||
if (sim->vip.dp.buffer == 0)
|
if (sim->vip.dp.enabled) {
|
||||||
sim->vip.dp.r0bsy = 1;
|
if (sim->vip.dp.buffer == 0)
|
||||||
else sim->vip.dp.r1bsy = 1;
|
sim->vip.dp.r0bsy = 1;
|
||||||
|
else sim->vip.dp.r1bsy = 1;
|
||||||
|
}
|
||||||
sim->vip.dp.column = 0;
|
sim->vip.dp.column = 0;
|
||||||
sim->vip.dp.cta = 0x3DE00 | (uint32_t)sim->vip.cta.cta_r<<1;
|
sim->vip.dp.cta = 0x3DE00 | (uint32_t)sim->vip.cta.cta_r<<1;
|
||||||
sim->vip.dp.step = 6;
|
sim->vip.dp.step = 6;
|
||||||
@@ -565,19 +597,26 @@ static int vipEmulateDisplay(VB *sim, uint32_t clocks) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 7: /* 18ms - R*BSY falling edge */
|
case 7: /* 18ms - R*BSY falling edge */
|
||||||
if (sim->vip.dp.buffer == 0)
|
if (sim->vip.dp.enabled) {
|
||||||
sim->vip.dp.r0bsy = 0;
|
if (sim->vip.dp.buffer == 0)
|
||||||
else sim->vip.dp.r1bsy = 0;
|
sim->vip.dp.r0bsy = 0;
|
||||||
sim->vip.dp.clocks = vipClocksMs(2);
|
else sim->vip.dp.r1bsy = 0;
|
||||||
sim->vip.dp.step = 0;
|
vipThrow(sim, 0x0004); /* RFBEND */
|
||||||
sim->vip.dp.until = vipClocksMs(2);
|
}
|
||||||
vipThrow(sim, 0x0004); /* RFBEND */
|
sim->vip.dp.clocks = vipClocksMs(2);
|
||||||
if (vipOnFrame(sim))
|
sim->vip.dp.enabled = 0;
|
||||||
return 1;
|
sim->vip.dp.step = 8;
|
||||||
|
sim->vip.dp.until = vipClocksMs(2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* 18ms-20ms - Idle */
|
/* 18ms-20ms - Idle */
|
||||||
|
|
||||||
|
case 8: /* 20ms - Display interval complete */
|
||||||
|
sim->vip.dp.step = 0;
|
||||||
|
if (vipOnFrame(sim))
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,6 +627,89 @@ static int vipEmulateDisplay(VB *sim, uint32_t clocks) {
|
|||||||
|
|
||||||
/****************************** Pixel Processor ******************************/
|
/****************************** Pixel Processor ******************************/
|
||||||
|
|
||||||
|
/* Parse eye attributes and test window bounds */
|
||||||
|
static int vipParseEye(WorldAttribs *wttr, EyeAttribs *ettr, int eye) {
|
||||||
|
|
||||||
|
/* Validate the world isn't right of frame */
|
||||||
|
ettr->wx = wttr->gx - (eye == 0 ? wttr->gp : -wttr->gp);
|
||||||
|
if (ettr->wx > 383)
|
||||||
|
return 1;
|
||||||
|
ettr->left = ettr->wx < 0 ? 0 : ettr->wx;
|
||||||
|
|
||||||
|
/* Validate the world isn't left of frame */
|
||||||
|
ettr->right = ettr->wx + wttr->w - 1;
|
||||||
|
if (ettr->right < 0)
|
||||||
|
return 1;
|
||||||
|
if (ettr->right > 383)
|
||||||
|
ettr->right = 383;
|
||||||
|
|
||||||
|
/* BG source parallax */
|
||||||
|
if (wttr->bgm != 2)
|
||||||
|
ettr->bp = eye == 0 ? -wttr->mp : wttr->mp;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse world attributes and test window bounds */
|
||||||
|
static int vipParseWorld(VB*sim,uint8_t*world,uint16_t bits,WorldAttribs*attr){
|
||||||
|
int32_t z; /* Scratch */
|
||||||
|
|
||||||
|
/* Validate the world isn't below the frame */
|
||||||
|
attr->gy = busReadBuffer(world + 6, VB_S16);
|
||||||
|
if (attr->gy > 223)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Validate the world isn't above the frame, and height is positive */
|
||||||
|
attr->bgm = bits >> 12 & 3;
|
||||||
|
attr->h = busReadBuffer(world + 16, VB_S16) + 1;
|
||||||
|
z = attr->bgm == 2 ? 0 : 8;
|
||||||
|
if (attr->h < z)
|
||||||
|
attr->h = z;
|
||||||
|
if (attr->h == 0)
|
||||||
|
return 1;
|
||||||
|
attr->bottom = attr->gy + attr->h - 1;
|
||||||
|
attr->top = (int32_t) sim->vip.xp.sbcount << 3;
|
||||||
|
if (attr->bottom < attr->top)
|
||||||
|
return 1;
|
||||||
|
if (attr->bottom > 223)
|
||||||
|
attr->bottom = 223;
|
||||||
|
if (attr->top < attr->gy)
|
||||||
|
attr->top = attr->gy;
|
||||||
|
|
||||||
|
/* Validate width is positive */
|
||||||
|
attr->w = SignExtend(busReadBuffer(world + 14, VB_U16),
|
||||||
|
attr->bgm == 2 ? 10 : 13) + 1;
|
||||||
|
if (attr->w < 1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Parse attributes */
|
||||||
|
attr->bg = (uint8_t *) BG_TEMPLATES[bits >> 8 & 15];
|
||||||
|
attr->gx = SignExtend(busReadBuffer(world + 2, VB_U16), 10);
|
||||||
|
attr->gp = SignExtend(busReadBuffer(world + 4, VB_U16), 10);
|
||||||
|
attr->over = (bits & 0x0080) == 0 ? NULL :
|
||||||
|
&sim->vip.ram[0x00020000 | busReadBuffer(world + 10, VB_U16) << 1];
|
||||||
|
attr->scx = bits >> 10 & 3;
|
||||||
|
attr->scy = bits >> 8 & 3;
|
||||||
|
attr->bgw = (1 << attr->scx) - 1;
|
||||||
|
attr->bgh = (1 << attr->scy) - 1;
|
||||||
|
z = attr->scx + attr->scy;
|
||||||
|
attr->base = bits & 15 & ~((1 << (z < 3 ? z : 3)) - 1);
|
||||||
|
if (attr->bgm != 2) {
|
||||||
|
attr->mx = SignExtend(busReadBuffer(world + 8, VB_U16), 13);
|
||||||
|
attr->mp = SignExtend(busReadBuffer(world + 10, VB_U16), 15);
|
||||||
|
attr->my = SignExtend(busReadBuffer(world + 12, VB_U16), 13);
|
||||||
|
}
|
||||||
|
if (attr->bgm != 0) {
|
||||||
|
attr->params =
|
||||||
|
0x20000 +
|
||||||
|
(attr->top - attr->gy) * (attr->bgm == 1 ? 4 : 16) +
|
||||||
|
((uint32_t) busReadBuffer(world + 18, VB_U16) << 1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Draw an object group into shadow memory */
|
/* Draw an object group into shadow memory */
|
||||||
static void vipDrawObjects(VB *sim, int group) {
|
static void vipDrawObjects(VB *sim, int group) {
|
||||||
uint8_t *dest; /* Pointer to object position in shadow memory */
|
uint8_t *dest; /* Pointer to object position in shadow memory */
|
||||||
@@ -678,124 +800,56 @@ static void vipDrawObjects(VB *sim, int group) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Draw a background world into shadow memory */
|
/* Draw a background world into shadow memory */
|
||||||
static void vipDrawWorld(VB *sim, uint8_t *world, uint16_t attr) {
|
static void vipDrawWorld(VB *sim, uint8_t *world, uint16_t bits) {
|
||||||
uint8_t *bg; /* Background arrangement */
|
uint16_t bgAttr; /* BG map cell attributes */
|
||||||
uint16_t bgAttr; /* BG map cell attributes */
|
int32_t bx; /* BG source X */
|
||||||
int32_t bgh; /* Height of background in BG maps, minus 1 */
|
int32_t by; /* BG source Y */
|
||||||
int32_t bgw; /* Width of background in BG maps, minus 1 */
|
uint8_t *cell; /* Pointer to BG map cell in host memory */
|
||||||
int32_t bottom; /* Window bottom edge position */
|
uint8_t *chr; /* Pointer to character data in host memory */
|
||||||
int32_t bp; /* Eye BG map source parallax */
|
int32_t cx; /* BG cell/character source X */
|
||||||
int32_t bx; /* BG horizontal map source position */
|
int32_t cy; /* BG cell/character source Y */
|
||||||
int32_t by; /* BG vertical map source position */
|
uint8_t *dest; /* Pointer to output in shadow memory */
|
||||||
uint8_t *cell; /* Pointer to BG map cell in host memory */
|
EyeAttribs ettr; /* Eye attributes */
|
||||||
uint8_t *chr; /* Pointer to character data in host memory */
|
uint32_t param; /* Current line parameter address */
|
||||||
int32_t cx; /* BG cell/character horizontal pixel source position */
|
int8_t pixel; /* Character pixel value */
|
||||||
int32_t cy; /* BG cell/character vertical pixel source position */
|
int32_t i, x, y; /* Iterators */
|
||||||
uint8_t *dest; /* Pointer to output in shadow memory */
|
|
||||||
int32_t left; /* Window left edge position */
|
|
||||||
uint32_t param; /* Current line parameter address */
|
|
||||||
int8_t pixel; /* Character pixel value */
|
|
||||||
int32_t right; /* Window right edge position */
|
|
||||||
int32_t top; /* Window top edge position */
|
|
||||||
int32_t wx; /* Eye horizontal position */
|
|
||||||
int32_t i, x, y; /* Iterators */
|
|
||||||
|
|
||||||
/* World attributes */
|
/* World attributes */
|
||||||
int32_t base; /* BG map base index */
|
WorldAttribs wttr; /* Common attributes */
|
||||||
int bgm; /* BG mode */
|
int32_t dx; /* Affine per-pixel X delta */
|
||||||
int32_t dx; /* Affine per-pixel X delta */
|
int32_t dy; /* Affine per-pixel Y delta */
|
||||||
int32_t dy; /* Affine per-pixel Y delta */
|
int32_t mp; /* BG source parallax */
|
||||||
int16_t gp; /* Window stereo parallax */
|
int32_t mx; /* BG source X */
|
||||||
int16_t gx; /* Window base horizontal position */
|
int32_t my; /* BG source Y */
|
||||||
int16_t gy; /* Window vertical position */
|
int16_t hofst; /* H-bias shift */
|
||||||
int16_t h; /* Window height */
|
|
||||||
int16_t hofst; /* H-bias shift */
|
|
||||||
int32_t mp; /* BG map source stereo parallax */
|
|
||||||
int32_t mx = 0; /* BG map horizontal source */
|
|
||||||
int32_t my = 0; /* BG map vertical source */
|
|
||||||
int32_t scx; /* Background width shift amount */
|
|
||||||
int32_t scy; /* Background height shift amount */
|
|
||||||
uint8_t *over; /* Pointer to overplane "cell" in host memory */
|
|
||||||
uint32_t params = 0; /* Base address of line parameters */
|
|
||||||
int16_t w; /* Window width */
|
|
||||||
|
|
||||||
/* Validate the world isn't below the frame */
|
/* Working variables */
|
||||||
gy = busReadBuffer(world + 6, VB_S16);
|
ettr.bp = wttr.mp = wttr.mx = wttr.my = wttr.params = 0;
|
||||||
if (gy > 223)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Validate the world isn't above the frame, and height is positive */
|
|
||||||
bgm = attr >> 12 & 3;
|
|
||||||
y = bgm == 2 ? 0 : 8;
|
|
||||||
h = busReadBuffer(world + 16, VB_S16) + 1;
|
|
||||||
if (h < y)
|
|
||||||
h = y;
|
|
||||||
if (h == 0)
|
|
||||||
return;
|
|
||||||
bottom = gy + h - 1;
|
|
||||||
top = (int32_t) sim->vip.xp.sbcount << 3;
|
|
||||||
if (bottom < top)
|
|
||||||
return;
|
|
||||||
if (bottom > 223)
|
|
||||||
bottom = 223;
|
|
||||||
if (top < gy)
|
|
||||||
top = gy;
|
|
||||||
|
|
||||||
/* Validate width is positive */
|
|
||||||
w = SignExtend(busReadBuffer(world + 14, VB_U16), bgm == 2 ? 10 : 13) + 1;
|
|
||||||
if (w < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Parse attributes */
|
/* Parse attributes */
|
||||||
bg = (uint8_t *) BG_TEMPLATES[attr >> 8 & 15];
|
if (vipParseWorld(sim, world, bits, &wttr))
|
||||||
gx = SignExtend(busReadBuffer(world + 2, VB_U16), 10);
|
return; /* Window not in frame */
|
||||||
gp = SignExtend(busReadBuffer(world + 4, VB_U16), 10);
|
|
||||||
over = (attr & 0x0080) ? world + 20 : NULL;
|
|
||||||
scx = attr >> 10 & 3;
|
|
||||||
scy = attr >> 8 & 3;
|
|
||||||
bgw = (1 << scx) - 1;
|
|
||||||
bgh = (1 << scy) - 1;
|
|
||||||
base = attr & 15 & ~((1 << (scx + scy < 3 ? scx + scy : 3)) - 1);
|
|
||||||
if (bgm != 2) {
|
|
||||||
mx = SignExtend(busReadBuffer(world + 8, VB_U16), 13);
|
|
||||||
mp = SignExtend(busReadBuffer(world + 10, VB_U16), 15);
|
|
||||||
my = SignExtend(busReadBuffer(world + 12, VB_U16), 13);
|
|
||||||
}
|
|
||||||
if (bgm != 0) {
|
|
||||||
params = 0x20000 + (top - gy) * (bgm == 1 ? 4 : 16) +
|
|
||||||
((uint32_t) busReadBuffer(world + 18, VB_U16) << 1);
|
|
||||||
}
|
|
||||||
else hofst = 0;
|
|
||||||
|
|
||||||
/* Draw the world */
|
/* Draw the world */
|
||||||
for (i = 0; i < 2; i++) {
|
for (i = 0; i < 2; i++) {
|
||||||
|
|
||||||
/* Validate world is enabled */
|
/* World is not visible */
|
||||||
if ((attr & (int32_t) 0x8000 >> i) == 0) /* LON, RON */
|
if ((bits & (int32_t) 0x8000 >> i) == 0) /* LON, RON */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Validate the world isn't right of frame */
|
/* Process attributes */
|
||||||
wx = gx - (i == 0 ? gp : -gp);
|
if (vipParseEye(&wttr, &ettr, i))
|
||||||
if (wx > 383)
|
continue;
|
||||||
return;
|
|
||||||
left = wx < 0 ? 0 : wx;
|
|
||||||
|
|
||||||
/* Validate the world isn't left of frame */
|
|
||||||
right = wx + w - 1;
|
|
||||||
if (right < 0)
|
|
||||||
return;
|
|
||||||
if (right > 383)
|
|
||||||
right = 383;
|
|
||||||
|
|
||||||
/* BG source parallax */
|
|
||||||
if (bgm != 2)
|
|
||||||
bp = i == 0 ? -mp : mp;
|
|
||||||
|
|
||||||
/* Draw all rows */
|
/* Draw all rows */
|
||||||
for (y = top, param = params; y <= bottom; y++) {
|
for (y = wttr.top, param = wttr.params; y <= wttr.bottom; y++) {
|
||||||
|
|
||||||
/* Parse line parameters */
|
/* Parse line parameters */
|
||||||
switch (bgm) {
|
hofst = 0;
|
||||||
|
mp = wttr.mp;
|
||||||
|
mx = wttr.mx;
|
||||||
|
my = wttr.my;
|
||||||
|
switch (wttr.bgm) {
|
||||||
case 1: /* H-bias */
|
case 1: /* H-bias */
|
||||||
hofst = SignExtend(vipParam(param | i << 1), 13);
|
hofst = SignExtend(vipParam(param | i << 1), 13);
|
||||||
param += 4;
|
param += 4;
|
||||||
@@ -804,22 +858,22 @@ static void vipDrawWorld(VB *sim, uint8_t *world, uint16_t attr) {
|
|||||||
dx = (int32_t) (int16_t) vipParam(param|6) << 7;
|
dx = (int32_t) (int16_t) vipParam(param|6) << 7;
|
||||||
dy = (int32_t) (int16_t) vipParam(param|8) << 7;
|
dy = (int32_t) (int16_t) vipParam(param|8) << 7;
|
||||||
mp = (int32_t) (int16_t) vipParam(param|2);
|
mp = (int32_t) (int16_t) vipParam(param|2);
|
||||||
mp = left - wx - ((mp < 0) ^ i ? mp : 0);
|
mp = ettr.left - ettr.wx - ((mp < 0) ^ i ? mp : 0);
|
||||||
mx = ((int32_t) (int16_t) vipParam(param ) << 13) + dx*mp;
|
mx = ((int32_t) (int16_t) vipParam(param ) << 13) + dx*mp;
|
||||||
my = ((int32_t) (int16_t) vipParam(param|4) << 13) + dy*mp;
|
my = ((int32_t) (int16_t) vipParam(param|4) << 13) + dy*mp;
|
||||||
param += 16;
|
param += 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Select output in shadow memory */
|
/* Select output in shadow memory */
|
||||||
dest = &sim->vip.shadow[i][left * 224 + y];
|
dest = &sim->vip.shadow[i][ettr.left * 224 + y];
|
||||||
|
|
||||||
/* Draw all columns */
|
/* Draw all columns */
|
||||||
for (x = left; x <= right; x++, dest += 224) {
|
for (x = ettr.left; x <= ettr.right; x++, dest += 224) {
|
||||||
|
|
||||||
/* Locate the pixel in the background */
|
/* Locate the pixel in the background */
|
||||||
if (bgm != 2) { /* Normal, H-bias */
|
if (wttr.bgm != 2) { /* Normal, H-bias */
|
||||||
cx = x - wx + mx + bp + hofst;
|
cx = x - ettr.wx + mx + ettr.bp + hofst;
|
||||||
cy = y - gy + my;
|
cy = y - wttr.gy + my;
|
||||||
} else { /* Affine */
|
} else { /* Affine */
|
||||||
cx = (int16_t) (mx >> 16);
|
cx = (int16_t) (mx >> 16);
|
||||||
cy = (int16_t) (my >> 16);
|
cy = (int16_t) (my >> 16);
|
||||||
@@ -833,20 +887,24 @@ static void vipDrawWorld(VB *sim, uint8_t *world, uint16_t attr) {
|
|||||||
cell = NULL;
|
cell = NULL;
|
||||||
|
|
||||||
/* BG map is out of bounds */
|
/* BG map is out of bounds */
|
||||||
if (bx < 0 || bx > bgw || by < 0 || by > bgh) {
|
if (
|
||||||
if (over == NULL) {
|
bx < 0 || bx > wttr.bgw ||
|
||||||
bx &= bgw;
|
by < 0 || by > wttr.bgh
|
||||||
by &= bgh;
|
) {
|
||||||
}
|
if (wttr.over == NULL) {
|
||||||
else cell = over;
|
bx &= wttr.bgw;
|
||||||
|
by &= wttr.bgh;
|
||||||
|
} else cell = wttr.over;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Locate the cell in the BG map */
|
/* Locate the cell in the BG map */
|
||||||
if (cell == NULL) {
|
if (cell == NULL) {
|
||||||
cell = &sim->vip.ram[
|
cell = &sim->vip.ram[
|
||||||
0x20000 |
|
0x20000 | ((
|
||||||
(base + bg[by << scx | bx]) << 13 |
|
wttr.base +
|
||||||
(cy << 4 & 0x1F80) |
|
wttr.bg[by << wttr.scx | bx]
|
||||||
|
) << 13) |
|
||||||
|
(cy << 4 & 0x1F80) |
|
||||||
(cx >> 2 & 0x007E)
|
(cx >> 2 & 0x007E)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -1008,7 +1066,8 @@ static void vipEmulateDrawing(VB *sim, uint32_t clocks) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 4: /* Drawing complete */
|
case 4: /* Drawing complete */
|
||||||
sim->vip.xp.step = 0;
|
sim->vip.xp.enabled = 0;
|
||||||
|
sim->vip.xp.step = 0;
|
||||||
if (sim->vip.dp.buffer == 0)
|
if (sim->vip.dp.buffer == 0)
|
||||||
sim->vip.xp.f1bsy = 0;
|
sim->vip.xp.f1bsy = 0;
|
||||||
else sim->vip.xp.f0bsy = 0;
|
else sim->vip.xp.f0bsy = 0;
|
||||||
|
|||||||
+28
-9
@@ -164,7 +164,7 @@ static void vsuEmulateChannel(VB *sim, int index, uint32_t clocks) {
|
|||||||
if (!chan->int_.enb)
|
if (!chan->int_.enb)
|
||||||
return;
|
return;
|
||||||
sim->vsu.freqmod.clocks = (uint32_t) sim->vsu.freqmod.interval *
|
sim->vsu.freqmod.clocks = (uint32_t) sim->vsu.freqmod.interval *
|
||||||
(sim->vsu.freqmod.clk == 0 ? 19201 : 153610);
|
(sim->vsu.freqmod.clk == 0 ? 19200 : 153600);
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (clocks != 0);
|
} while (clocks != 0);
|
||||||
@@ -207,6 +207,8 @@ static void vsuWriteEV0(VB *sim, int index, uint8_t value) {
|
|||||||
chan->env.interval = value & 7;
|
chan->env.interval = value & 7;
|
||||||
chan->env.value = value >> 4 & 15;
|
chan->env.value = value >> 4 & 15;
|
||||||
chan->env.reload = chan->env.value;
|
chan->env.reload = chan->env.value;
|
||||||
|
if (index == 4)
|
||||||
|
vsuNextFreqMod(sim, chan);
|
||||||
|
|
||||||
/* Configure state */
|
/* Configure state */
|
||||||
chan->env.clocks = 307220 * ((uint32_t) chan->env.interval + 1);
|
chan->env.clocks = 307220 * ((uint32_t) chan->env.interval + 1);
|
||||||
@@ -243,6 +245,8 @@ static void vsuWriteFQH(VB *sim, int index, uint16_t value) {
|
|||||||
value = value << 8 & 0x0700;
|
value = value << 8 & 0x0700;
|
||||||
chan->freq.current = (chan->freq.current & 0x00FF) | value;
|
chan->freq.current = (chan->freq.current & 0x00FF) | value;
|
||||||
chan->freq.written = (chan->freq.written & 0x00FF) | value;
|
chan->freq.written = (chan->freq.written & 0x00FF) | value;
|
||||||
|
if (index == 4)
|
||||||
|
vsuNextFreqMod(sim, chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a value to S*FQL */
|
/* Write a value to S*FQL */
|
||||||
@@ -250,6 +254,8 @@ static void vsuWriteFQL(VB *sim, int index, uint8_t value) {
|
|||||||
Channel *chan = &sim->vsu.channels[index];
|
Channel *chan = &sim->vsu.channels[index];
|
||||||
chan->freq.current = (chan->freq.current & 0x0700) | value;
|
chan->freq.current = (chan->freq.current & 0x0700) | value;
|
||||||
chan->freq.written = (chan->freq.written & 0x0700) | value;
|
chan->freq.written = (chan->freq.written & 0x0700) | value;
|
||||||
|
if (index == 4)
|
||||||
|
vsuNextFreqMod(sim, chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a value to S*INT */
|
/* Write a value to S*INT */
|
||||||
@@ -274,7 +280,7 @@ static void vsuWriteINT(VB *sim, int index, uint8_t value) {
|
|||||||
}
|
}
|
||||||
if (index == 4) {
|
if (index == 4) {
|
||||||
sim->vsu.freqmod.clocks = (uint32_t) sim->vsu.freqmod.interval *
|
sim->vsu.freqmod.clocks = (uint32_t) sim->vsu.freqmod.interval *
|
||||||
(sim->vsu.freqmod.clk ? 153610 : 19201);
|
(sim->vsu.freqmod.clk == 0 ? 19200 : 153600);
|
||||||
sim->vsu.freqmod.sample = 0;
|
sim->vsu.freqmod.sample = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,14 +303,14 @@ static void vsuWriteSWP(VB *sim, uint8_t value) {
|
|||||||
uint32_t clocks;
|
uint32_t clocks;
|
||||||
|
|
||||||
/* Parse fields */
|
/* Parse fields */
|
||||||
sim->vsu.freqmod.clk = value >> 7 & 1;
|
sim->vsu.freqmod.clk = value >> 7 & 1;
|
||||||
sim->vsu.freqmod.dir = value >> 3 & 1;
|
sim->vsu.freqmod.dir = value >> 3 & 1;
|
||||||
sim->vsu.freqmod.interval = value >> 4 & 15;
|
sim->vsu.freqmod.interval = value >> 4 & 7;
|
||||||
sim->vsu.freqmod.shift = value & 7;
|
sim->vsu.freqmod.shift = value & 7;
|
||||||
|
|
||||||
/* Configure state */
|
/* Configure state */
|
||||||
clocks = (uint32_t) sim->vsu.freqmod.interval *
|
clocks = (uint32_t) sim->vsu.freqmod.interval *
|
||||||
(sim->vsu.freqmod.clk ? 153610 : 19201);
|
(sim->vsu.freqmod.clk == 0 ? 19200 : 153600);
|
||||||
if (clocks < sim->vsu.freqmod.clocks)
|
if (clocks < sim->vsu.freqmod.clocks)
|
||||||
sim->vsu.freqmod.clocks = clocks;
|
sim->vsu.freqmod.clocks = clocks;
|
||||||
}
|
}
|
||||||
@@ -362,8 +368,21 @@ static void vsuEmulate(VB *sim, uint32_t clocks) {
|
|||||||
sim->vsu.out.samples != NULL &&
|
sim->vsu.out.samples != NULL &&
|
||||||
sim->vsu.out.offset >> 1 < sim->vsu.out.capacity
|
sim->vsu.out.offset >> 1 < sim->vsu.out.capacity
|
||||||
) {
|
) {
|
||||||
sim->vsu.out.samples[sim->vsu.out.offset++] =
|
|
||||||
(int16_t) (o0 * 32767.0f + (o0 < 0.0f ? -0.5f : +0.5f));
|
/* Processing by data type*/
|
||||||
|
switch (sim->vsu.out.type) {
|
||||||
|
case VB_S16:
|
||||||
|
((int16_t *) sim->vsu.out.samples)
|
||||||
|
[sim->vsu.out.offset++] = (int16_t)
|
||||||
|
(o0 * 32767.0f + (o0 < 0.0f ? -0.5f : +0.5f));
|
||||||
|
break;
|
||||||
|
case VB_F32:
|
||||||
|
((float *) sim->vsu.out.samples)
|
||||||
|
[sim->vsu.out.offset++] = o0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invoke the samples callback */
|
||||||
if (
|
if (
|
||||||
e == 1 &&
|
e == 1 &&
|
||||||
sim->vsu.out.offset >> 1 == sim->vsu.out.capacity
|
sim->vsu.out.offset >> 1 == sim->vsu.out.capacity
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ help:
|
|||||||
@echo "Available make targets:"
|
@echo "Available make targets:"
|
||||||
@echo " clean Remove output files"
|
@echo " clean Remove output files"
|
||||||
@echo " core Check the C core source for compiler warnings"
|
@echo " core Check the C core source for compiler warnings"
|
||||||
|
@echo " util Check the C utility source for compiler warnings"
|
||||||
@echo " wasm Build the WebAssembly module"
|
@echo " wasm Build the WebAssembly module"
|
||||||
@echo
|
@echo
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ core:
|
|||||||
-D VB_DIRECT_EXCEPTION=textException -D VB_DIRECT_EXECUTE=textExecute \
|
-D VB_DIRECT_EXCEPTION=textException -D VB_DIRECT_EXECUTE=textExecute \
|
||||||
-D VB_DIRECT_FETCH=testFetch -D VB_DIRECT_FRAME=testFrame \
|
-D VB_DIRECT_FETCH=testFetch -D VB_DIRECT_FRAME=testFrame \
|
||||||
-D VB_DIRECT_WRITE=testWrite -D VB_DIRECT_SAMPLES=testSamples \
|
-D VB_DIRECT_WRITE=testWrite -D VB_DIRECT_SAMPLES=testSamples \
|
||||||
-D VB_DIRECT_READ=testRead
|
-D VB_DIRECT_READ=testRead -D VB_DIRECT_LINK=testLink
|
||||||
# Clang generic
|
# Clang generic
|
||||||
@emcc core/vb.c -I core -c -o /dev/null -O3 \
|
@emcc core/vb.c -I core -c -o /dev/null -O3 \
|
||||||
-Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing
|
-Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing
|
||||||
@@ -40,14 +41,32 @@ core:
|
|||||||
-D VB_DIRECT_EXCEPTION=textException -D VB_DIRECT_EXECUTE=textExecute \
|
-D VB_DIRECT_EXCEPTION=textException -D VB_DIRECT_EXECUTE=textExecute \
|
||||||
-D VB_DIRECT_FETCH=testFetch -D VB_DIRECT_FRAME=testFrame \
|
-D VB_DIRECT_FETCH=testFetch -D VB_DIRECT_FRAME=testFrame \
|
||||||
-D VB_DIRECT_WRITE=testWrite -D VB_DIRECT_SAMPLES=testSamples \
|
-D VB_DIRECT_WRITE=testWrite -D VB_DIRECT_SAMPLES=testSamples \
|
||||||
-D VB_DIRECT_READ=testRead
|
-D VB_DIRECT_READ=testRead -D VB_DIRECT_LINK=testLink
|
||||||
|
|
||||||
|
.PHONY: util
|
||||||
|
util:
|
||||||
|
# GCC generic
|
||||||
|
@gcc util/vbu.c -I core -I util -c -o /dev/null -O3 \
|
||||||
|
-Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing
|
||||||
|
# GCC compilation control
|
||||||
|
@gcc util/vbu.c -I core -I util -c -o /dev/null -O3 \
|
||||||
|
-Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing \
|
||||||
|
-D VB_SIGNED_PROPAGATE -D VBU_REALLOC=testRealloc
|
||||||
|
# Clang generic
|
||||||
|
@emcc util/vbu.c -I core -I util -c -o /dev/null -O3 \
|
||||||
|
-Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing
|
||||||
|
# Clang compilation control
|
||||||
|
@emcc util/vbu.c -I core -I util -c -o /dev/null -O3 \
|
||||||
|
-Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing \
|
||||||
|
-D VB_SIGNED_PROPAGATE -D VBU_REALLOC=testRealloc
|
||||||
|
|
||||||
.PHONY: wasm
|
.PHONY: wasm
|
||||||
wasm:
|
wasm:
|
||||||
@emcc -o web/core.wasm web/wasm.c core/vb.c -I core \
|
@emcc -o web/core.wasm web/wasm.c core/vb.c util/vbu.c -I core -I util \
|
||||||
-D VB_LITTLE_ENDIAN -D VB_SIGNED_PROPAGATE \
|
-D VB_LITTLE_ENDIAN -D VB_SIGNED_PROPAGATE \
|
||||||
-D "VBAPI=__attribute__((used))" \
|
-D "VBAPI=__attribute__((used))" -D "VBUAPI=__attribute__((used))" \
|
||||||
--no-entry -O2 -flto -s WASM=1 -s WARN_ON_UNDEFINED_SYMBOLS=0 \
|
-D VB_DIRECT_FRAME=wasmOnFrame \
|
||||||
|
--no-entry -O2 -flto -s WASM=1 \
|
||||||
-s EXPORTED_RUNTIME_METHODS=[] -s ALLOW_MEMORY_GROWTH \
|
-s EXPORTED_RUNTIME_METHODS=[] -s ALLOW_MEMORY_GROWTH \
|
||||||
-s MAXIMUM_MEMORY=4GB -fno-strict-aliasing
|
-s MAXIMUM_MEMORY=4GB -fno-strict-aliasing
|
||||||
@rm -f web/*.wasm.tmp*
|
@rm -f web/*.wasm.tmp*
|
||||||
|
|||||||
@@ -0,0 +1,694 @@
|
|||||||
|
/* This file is included into vb.c and cannot be compiled on its own. */
|
||||||
|
#ifdef VBUAPI
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************* Constants *********************************/
|
||||||
|
|
||||||
|
/* Operand types */
|
||||||
|
#define DASM_BCOND 0
|
||||||
|
#define DASM_DISP9 1
|
||||||
|
#define DASM_DISP26 2
|
||||||
|
#define DASM_IMM5S 3
|
||||||
|
#define DASM_IMM5U 4
|
||||||
|
#define DASM_IMM16S 5
|
||||||
|
#define DASM_IMM16U 6
|
||||||
|
#define DASM_JMP 7
|
||||||
|
#define DASM_MEMORY 8
|
||||||
|
#define DASM_REG1 9
|
||||||
|
#define DASM_REG2 10
|
||||||
|
#define DASM_SETF 11
|
||||||
|
#define DASM_SYSTEM 12
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************** Lookup Data ********************************/
|
||||||
|
|
||||||
|
/* Instruction descriptor */
|
||||||
|
typedef struct {
|
||||||
|
char mnemonic[8]; /* Mnemonic, uppercase */
|
||||||
|
uint8_t operandsLength; /* Number of operands */
|
||||||
|
uint8_t operands[3]; /* Operand types */
|
||||||
|
} OpDef;
|
||||||
|
|
||||||
|
/* Instruction descriptors */
|
||||||
|
static const OpDef OPDEFS[] = {
|
||||||
|
{ "MOV" , 2, { DASM_REG1, DASM_REG2 } }, /* 000000 */
|
||||||
|
{ "ADD" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "SUB" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "CMP" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "SHL" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "SHR" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "JMP" , 1, { DASM_JMP } },
|
||||||
|
{ "SAR" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "MUL" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "DIV" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "MULU" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "DIVU" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "OR" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "AND" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "XOR" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "NOT" , 1, { DASM_REG1 } },
|
||||||
|
|
||||||
|
{ "MOV" , 2, { DASM_IMM5S, DASM_REG2 } }, /* 010000 */
|
||||||
|
{ "ADD" , 2, { DASM_IMM5S, DASM_REG2 } },
|
||||||
|
{ "" , 0, { 0 } }, /* SETF */
|
||||||
|
{ "CMP" , 2, { DASM_IMM5S, DASM_REG2 } },
|
||||||
|
{ "SHL" , 2, { DASM_IMM5U, DASM_REG2 } },
|
||||||
|
{ "SHR" , 2, { DASM_IMM5U, DASM_REG2 } },
|
||||||
|
{ "CLI" , 0, { 0 } },
|
||||||
|
{ "SAR" , 2, { DASM_IMM5U, DASM_REG2 } },
|
||||||
|
{ "TRAP" , 1, { DASM_IMM5U } },
|
||||||
|
{ "RETI" , 0, { 0 } },
|
||||||
|
{ "HALT" , 0, { 0 } },
|
||||||
|
{ "---" , 0, { 0 } },
|
||||||
|
{ "LDSR" , 2, { DASM_REG2, DASM_SYSTEM } },
|
||||||
|
{ "STSR" , 2, { DASM_SYSTEM, DASM_REG2 } },
|
||||||
|
{ "SEI" , 0, { 0 } },
|
||||||
|
{ "" , 0, { 0 } }, /* Bit string */
|
||||||
|
|
||||||
|
{ "" , 0, { 0 } }, /* BCOND */ /* 100000 */
|
||||||
|
{ "" , 0, { 0 } }, /* BCOND */
|
||||||
|
{ "" , 0, { 0 } }, /* BCOND */
|
||||||
|
{ "" , 0, { 0 } }, /* BCOND */
|
||||||
|
{ "" , 0, { 0 } }, /* BCOND */
|
||||||
|
{ "" , 0, { 0 } }, /* BCOND */
|
||||||
|
{ "" , 0, { 0 } }, /* BCOND */
|
||||||
|
{ "" , 0, { 0 } }, /* BCOND */
|
||||||
|
{ "MOVEA", 3, { DASM_IMM16U, DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "ADDI" , 3, { DASM_IMM16S, DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "JR" , 1, { DASM_DISP26 } },
|
||||||
|
{ "JAL" , 1, { DASM_DISP26 } },
|
||||||
|
{ "ORI" , 3, { DASM_IMM16U, DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "ANDI" , 3, { DASM_IMM16U, DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "XORI" , 3, { DASM_IMM16U, DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "MOVHI", 3, { DASM_IMM16U, DASM_REG1, DASM_REG2 } },
|
||||||
|
|
||||||
|
{ "LD.B" , 2, { DASM_MEMORY, DASM_REG2 } }, /* 110000 */
|
||||||
|
{ "LD.H" , 2, { DASM_MEMORY, DASM_REG2 } },
|
||||||
|
{ "---" , 0, { 0 } },
|
||||||
|
{ "LD.W" , 2, { DASM_MEMORY, DASM_REG2 } },
|
||||||
|
{ "ST.B" , 2, { DASM_REG2, DASM_MEMORY } },
|
||||||
|
{ "ST.H" , 2, { DASM_REG2, DASM_MEMORY } },
|
||||||
|
{ "---" , 0, { 0 } },
|
||||||
|
{ "ST.W" , 2, { DASM_REG2, DASM_MEMORY } },
|
||||||
|
{ "IN.B" , 2, { DASM_MEMORY, DASM_REG2 } },
|
||||||
|
{ "IN.H" , 2, { DASM_MEMORY, DASM_REG2 } },
|
||||||
|
{ "CAXI" , 0, { DASM_MEMORY, DASM_REG2 } },
|
||||||
|
{ "IN.W" , 2, { DASM_MEMORY, DASM_REG2 } },
|
||||||
|
{ "OUT.B", 2, { DASM_REG2, DASM_MEMORY } },
|
||||||
|
{ "OUT.H", 2, { DASM_REG2, DASM_MEMORY } },
|
||||||
|
{ "" , 0, { 0 } }, /* Floating-point/Nintendo */
|
||||||
|
{ "OUT.W", 2, { DASM_REG2, DASM_MEMORY } }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Bit string instruction descriptors */
|
||||||
|
static const OpDef OPDEFS_BITSTRING[] = {
|
||||||
|
{ "SCH0BSU", 0, { 0 } }, /* 00000 */
|
||||||
|
{ "SCH0BSD", 0, { 0 } },
|
||||||
|
{ "SCH1BSU", 0, { 0 } },
|
||||||
|
{ "SCH1BSD", 0, { 0 } },
|
||||||
|
{ "---" , 0, { 0 } },
|
||||||
|
{ "---" , 0, { 0 } },
|
||||||
|
{ "---" , 0, { 0 } },
|
||||||
|
{ "---" , 0, { 0 } },
|
||||||
|
{ "ORBSU" , 0, { 0 } },
|
||||||
|
{ "ANDBSU" , 0, { 0 } },
|
||||||
|
{ "XORBSU" , 0, { 0 } },
|
||||||
|
{ "MOVBSU" , 0, { 0 } },
|
||||||
|
{ "ORNBSU" , 0, { 0 } },
|
||||||
|
{ "ANDNBSU", 0, { 0 } },
|
||||||
|
{ "XORNBSU", 0, { 0 } },
|
||||||
|
{ "NOTBSU" , 0, { 0 } }
|
||||||
|
/* +16 invalid opcodes */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Floating-point/Nintendo instruction descriptors */
|
||||||
|
static const OpDef OPDEFS_FLOATENDO[] = {
|
||||||
|
{ "CMPF.S" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "---" , 0, { 0 } },
|
||||||
|
{ "CVT.WS" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "CVT.SW" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "ADDF.S" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "SUBF.S" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "MULF.S" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "DIVF.S" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "XB" , 1, { DASM_REG2 } },
|
||||||
|
{ "XH" , 1, { DASM_REG2 } },
|
||||||
|
{ "REV" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "TRNC.SW", 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
{ "MPYHW" , 2, { DASM_REG1, DASM_REG2 } },
|
||||||
|
/* +51 invalid opcodes */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Invalid instruction descriptors */
|
||||||
|
static const OpDef OPDEF_ILLEGAL = { "---", 0, { 0 } };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************** Module Functions ******************************/
|
||||||
|
|
||||||
|
/* Select the display text for a condition */
|
||||||
|
static char* dasmCondition(VBU_DasmConfig *config, int cond) {
|
||||||
|
switch (cond) {
|
||||||
|
case 0: return "V" ;
|
||||||
|
case 1: return config->conditionCL == VBU_C ? "C" : "L";
|
||||||
|
case 2: return config->conditionEZ == VBU_E ? "E" : "Z";
|
||||||
|
case 3: return "NH";
|
||||||
|
case 4: return "N" ;
|
||||||
|
case 5: return "T" ;
|
||||||
|
case 6: return "LT";
|
||||||
|
case 7: return "LE";
|
||||||
|
case 8: return "NV";
|
||||||
|
case 9: return config->conditionCL == VBU_C ? "NC" : "NL";
|
||||||
|
case 10: return config->conditionEZ == VBU_E ? "NE" : "NZ";
|
||||||
|
case 11: return "H" ;
|
||||||
|
case 12: return "P" ;
|
||||||
|
case 13: return "F" ;
|
||||||
|
case 14: return "GE";
|
||||||
|
}
|
||||||
|
return "GT";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a number as hexadecimal */
|
||||||
|
static void dasmToHex(char *dest, VBU_DasmConfig *config,
|
||||||
|
int minDigits, int32_t value) {
|
||||||
|
char format[9];
|
||||||
|
char *prefix = "";
|
||||||
|
char *suffix = "";
|
||||||
|
switch (config->hexNotation) {
|
||||||
|
case VBU_0X : prefix = "0x"; break;
|
||||||
|
case VBU_DOLLAR: prefix = "$" ; break;
|
||||||
|
case VBU_H : suffix = "h" ; break;
|
||||||
|
}
|
||||||
|
sprintf(format, "%%s%%0%d%s%%s", minDigits,
|
||||||
|
config->hexCase == VBU_LOWER ? "x" : "X");
|
||||||
|
sprintf(dest, format, prefix, value, suffix);
|
||||||
|
if (config->hexNotation != VBU_H || *dest <= '9')
|
||||||
|
return;
|
||||||
|
*dest++ = '0';
|
||||||
|
sprintf(dest, format, prefix, value, suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert a string to lowercase */
|
||||||
|
static void dasmToLower(char *str) {
|
||||||
|
for (; *str; str++) {
|
||||||
|
if (*str >= 'A' && *str <= 'Z')
|
||||||
|
*str += 'a' - 'A';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a program register */
|
||||||
|
static void dasmProgram(char *dest, VBU_DasmConfig *config, int id) {
|
||||||
|
char *text = NULL;
|
||||||
|
if (config->programNotation != VBU_NUMBERS) {
|
||||||
|
switch (id) {
|
||||||
|
case 2: text = "HP"; break;
|
||||||
|
case 3: text = "SP"; break;
|
||||||
|
case 4: text = "GP"; break;
|
||||||
|
case 5: text = "TP"; break;
|
||||||
|
case 31: text = "LP"; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (text == NULL)
|
||||||
|
sprintf(dest, "R%d", id);
|
||||||
|
else strcpy(dest, text);
|
||||||
|
if (config->programCase == VBU_LOWER)
|
||||||
|
dasmToLower(dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**************************** Operand Formatters *****************************/
|
||||||
|
|
||||||
|
/* Format the condition operand of a split BCOND instruction */
|
||||||
|
static void dasmOpBCOND(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
|
||||||
|
int cond = line->code[1] >> 2 & 15;
|
||||||
|
if (config->conditionNotation == VBU_NUMBERS)
|
||||||
|
sprintf(dest, "%d", cond);
|
||||||
|
else {
|
||||||
|
strcpy(dest, dasmCondition(config, cond));
|
||||||
|
if (config->conditionCase == VBU_LOWER)
|
||||||
|
dasmToLower(dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a 9-bit displacement operand */
|
||||||
|
static void dasmOpDisp9(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
|
||||||
|
int32_t disp = SignExtend((int32_t) line->code[1] << 8 | line->code[0], 9);
|
||||||
|
sprintf(dest, config->hexCase == VBU_LOWER ?
|
||||||
|
"%08x" : "%08X", line->address + disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a 26-bit displacement operand */
|
||||||
|
static void dasmOpDisp26(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
|
||||||
|
int32_t disp = SignExtend(
|
||||||
|
(int32_t) line->code[1] << 24 |
|
||||||
|
(int32_t) line->code[0] << 16 |
|
||||||
|
(int32_t) line->code[3] << 8 |
|
||||||
|
line->code[2]
|
||||||
|
, 26);
|
||||||
|
sprintf(dest, config->hexCase == VBU_LOWER ?
|
||||||
|
"%08x" : "%08X", line->address + disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a 5-bit sign-extended immediate operand */
|
||||||
|
static void dasmOpImm5S(char *dest, VBU_DasmLine *line) {
|
||||||
|
sprintf(dest, "%d", SignExtend(line->code[0], 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a 5-bit zero-filled immediate operand */
|
||||||
|
static void dasmOpImm5U(char *dest, VBU_DasmLine *line) {
|
||||||
|
sprintf(dest, "%d", line->code[0] & 31);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a 16-bit sign-extended immediate operand */
|
||||||
|
static void dasmOpImm16S(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
|
||||||
|
int32_t imm = (int16_t) ((int16_t) line->code[3] << 8 | line->code[2]);
|
||||||
|
if (imm >= -256 && imm <= 256) {
|
||||||
|
sprintf(dest, "%d", imm);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (imm < 0) {
|
||||||
|
*dest++ = '-';
|
||||||
|
imm = -imm;
|
||||||
|
}
|
||||||
|
dasmToHex(dest, config, 0, imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a 16-bit zero-filled immediate operand */
|
||||||
|
static void dasmOpImm16U(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
|
||||||
|
uint16_t imm = (uint16_t) line->code[3] << 8 | line->code[2];
|
||||||
|
dasmToHex(dest, config, 4, imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format the operand of a JMP instruction */
|
||||||
|
static void dasmOpJMP(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
|
||||||
|
*dest++ = '[';
|
||||||
|
dasmProgram(dest, config, line->code[0] & 31);
|
||||||
|
strcat(dest, "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a memory operand */
|
||||||
|
static void dasmOpMemory(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
|
||||||
|
int reg1 = line->code[0] & 31;
|
||||||
|
int32_t disp = (int16_t) ((int16_t) line->code[3] << 8 | line->code[2]);
|
||||||
|
|
||||||
|
/* Displacement of zero */
|
||||||
|
if (disp == 0) {
|
||||||
|
*dest++ = '[';
|
||||||
|
dasmProgram(dest, config, reg1);
|
||||||
|
strcat(dest, "]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inside */
|
||||||
|
if (config->memoryNotation == VBU_INSIDE) {
|
||||||
|
*dest++ = '[';
|
||||||
|
dasmProgram(dest, config, reg1);
|
||||||
|
dest += strlen(dest);
|
||||||
|
if (disp < 0) {
|
||||||
|
strcpy(dest, " - ");
|
||||||
|
disp = -disp;
|
||||||
|
} else strcpy(dest, " + ");
|
||||||
|
dest += 3;
|
||||||
|
if (disp <= 256)
|
||||||
|
sprintf(dest, "%d", disp);
|
||||||
|
else dasmToHex(dest, config, 0, disp);
|
||||||
|
strcat(dest, "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Outside */
|
||||||
|
else {
|
||||||
|
if (disp < 0) {
|
||||||
|
*dest++ = '-';
|
||||||
|
disp = -disp;
|
||||||
|
}
|
||||||
|
if (disp <= 256)
|
||||||
|
sprintf(dest, "%d", disp);
|
||||||
|
else dasmToHex(dest, config, 0, disp);
|
||||||
|
dest += strlen(dest);
|
||||||
|
*dest++ = '[';
|
||||||
|
dasmProgram(dest, config, reg1);
|
||||||
|
strcat(dest, "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a source register operand */
|
||||||
|
static void dasmOpReg1(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
|
||||||
|
dasmProgram(dest, config, line->code[0] & 31);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a destination register operand */
|
||||||
|
static void dasmOpReg2(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
|
||||||
|
dasmProgram(dest, config,
|
||||||
|
((uint16_t) line->code[1] << 8 | line->code[0]) >> 5 & 31);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format the condition operand of a split SETF instruction */
|
||||||
|
static void dasmOpSETF(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
|
||||||
|
int cond = line->code[0] & 15;
|
||||||
|
if (config->conditionNotation == VBU_NUMBERS)
|
||||||
|
sprintf(dest, "%d", cond);
|
||||||
|
else {
|
||||||
|
strcpy(dest, dasmCondition(config, cond));
|
||||||
|
if (config->conditionCase == VBU_LOWER)
|
||||||
|
dasmToLower(dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a system register operand */
|
||||||
|
static void dasmOpSystem(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
|
||||||
|
int id = line->code[0] & 31;
|
||||||
|
char *text = NULL;
|
||||||
|
if (config->systemNotation != VBU_NUMBERS) {
|
||||||
|
switch (id) {
|
||||||
|
case 0: text = "EIPC" ; break;
|
||||||
|
case 1: text = "EIPSW"; break;
|
||||||
|
case 2: text = "FEPC" ; break;
|
||||||
|
case 3: text = "FEPSW"; break;
|
||||||
|
case 4: text = "ECR" ; break;
|
||||||
|
case 5: text = "PSW" ; break;
|
||||||
|
case 6: text = "PIR" ; break;
|
||||||
|
case 7: text = "TKCW" ; break;
|
||||||
|
case 24: text = "CHCW" ; break;
|
||||||
|
case 25: text = "ADTRE"; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (text == NULL) {
|
||||||
|
sprintf(dest, "%d", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
strcpy(dest, text);
|
||||||
|
if (config->systemCase == VBU_LOWER)
|
||||||
|
dasmToLower(dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************** Module Functions ******************************/
|
||||||
|
|
||||||
|
/* Prepare an OpDef for a BCOND instruction */
|
||||||
|
static OpDef* dasmBCOND(VBU_DasmConfig *config,
|
||||||
|
VBU_DasmLine *line, OpDef *opdef) {
|
||||||
|
int cond; /* Condition code */
|
||||||
|
char *text; /* Mnemonic text */
|
||||||
|
|
||||||
|
/* Split notation */
|
||||||
|
if (config->bcondNotation == VBU_SPLIT) {
|
||||||
|
strcpy(opdef->mnemonic, "BCOND");
|
||||||
|
opdef->operandsLength = 2;
|
||||||
|
opdef->operands[0] = DASM_BCOND;
|
||||||
|
opdef->operands[1] = DASM_DISP9;
|
||||||
|
return opdef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Joined notation */
|
||||||
|
cond = line->code[1] >> 1 & 15;
|
||||||
|
switch (cond) {
|
||||||
|
case 0: text = "BV" ; break;
|
||||||
|
case 1: text = config->conditionCL == VBU_C ? "BC" : "BL"; break;
|
||||||
|
case 2: text = config->conditionEZ == VBU_E ? "BE" : "BZ"; break;
|
||||||
|
case 3: text = "BNH"; break;
|
||||||
|
case 4: text = "BN" ; break;
|
||||||
|
case 5: text = "BR" ; break;
|
||||||
|
case 6: text = "BLT"; break;
|
||||||
|
case 7: text = "BLE"; break;
|
||||||
|
case 8: text = "BNV"; break;
|
||||||
|
case 9: text = config->conditionCL == VBU_C ? "BNC" : "BNL"; break;
|
||||||
|
case 10: text = config->conditionEZ == VBU_E ? "BNE" : "BNZ"; break;
|
||||||
|
case 11: text = "BH" ; break;
|
||||||
|
case 12: text = "BP" ; break;
|
||||||
|
case 13: text = "NOP"; break;
|
||||||
|
case 14: text = "BGE"; break;
|
||||||
|
default: text = "BGT";
|
||||||
|
}
|
||||||
|
strcpy(opdef->mnemonic, text);
|
||||||
|
opdef->operandsLength = cond != 13;
|
||||||
|
opdef->operands[0] = DASM_DISP9;
|
||||||
|
return opdef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure an output buffer matches the required size */
|
||||||
|
static VBU_DasmLine* dasmGrow(
|
||||||
|
VBU_DasmLine **lines, size_t *size, size_t target, unsigned index) {
|
||||||
|
if (*size < target) {
|
||||||
|
while (*size < target)
|
||||||
|
*size *= 2;
|
||||||
|
*lines = VBU_REALLOC(*lines, *size);
|
||||||
|
}
|
||||||
|
return *lines == NULL ? NULL : &(*lines)[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine the size of an instruction */
|
||||||
|
static uint32_t dasmInstSize(VB *sim, uint32_t address) {
|
||||||
|
unsigned opcode = vbRead(sim, address, VB_U16) >> 10 & 63;
|
||||||
|
return opcode < 0x20 || opcode == 0x32 || opcode == 0x36 ? 2 : 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format an operand */
|
||||||
|
static void dasmOperand(char *dest, VBU_DasmConfig *config,
|
||||||
|
VBU_DasmLine *line, uint8_t type) {
|
||||||
|
switch (type) {
|
||||||
|
case DASM_BCOND : dasmOpBCOND (dest, config, line); break;
|
||||||
|
case DASM_DISP9 : dasmOpDisp9 (dest, config, line); break;
|
||||||
|
case DASM_DISP26: dasmOpDisp26(dest, config, line); break;
|
||||||
|
case DASM_IMM5S : dasmOpImm5S (dest, line); break;
|
||||||
|
case DASM_IMM5U : dasmOpImm5U (dest, line); break;
|
||||||
|
case DASM_IMM16S: dasmOpImm16S(dest, config, line); break;
|
||||||
|
case DASM_IMM16U: dasmOpImm16U(dest, config, line); break;
|
||||||
|
case DASM_JMP : dasmOpJMP (dest, config, line); break;
|
||||||
|
case DASM_MEMORY: dasmOpMemory(dest, config, line); break;
|
||||||
|
case DASM_REG1 : dasmOpReg1 (dest, config, line); break;
|
||||||
|
case DASM_REG2 : dasmOpReg2 (dest, config, line); break;
|
||||||
|
case DASM_SETF : dasmOpSETF (dest, config, line); break;
|
||||||
|
case DASM_SYSTEM: dasmOpSystem(dest, config, line); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare an OpDef for a SETF instruction */
|
||||||
|
static OpDef* dasmSETF(VBU_DasmConfig *config,
|
||||||
|
VBU_DasmLine *line, OpDef *opdef) {
|
||||||
|
|
||||||
|
/* Split notation */
|
||||||
|
if (config->bcondNotation == VBU_SPLIT) {
|
||||||
|
strcpy(opdef->mnemonic, "SETF");
|
||||||
|
opdef->operandsLength = 2;
|
||||||
|
opdef->operands[0] = DASM_SETF;
|
||||||
|
opdef->operands[1] = DASM_REG2;
|
||||||
|
return opdef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Joined notation */
|
||||||
|
sprintf(opdef->mnemonic, "SETF%s",
|
||||||
|
dasmCondition(config, line->code[1] >> 1 & 15));
|
||||||
|
opdef->operandsLength = 1;
|
||||||
|
opdef->operands[0] = DASM_REG2;
|
||||||
|
return opdef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disassemble one instruction */
|
||||||
|
static int dasmLine(VB *sim, uint32_t *address, uint32_t pc,
|
||||||
|
VBU_DasmConfig *config, VBU_DasmLine **lines, size_t *size, size_t *offset,
|
||||||
|
unsigned index) {
|
||||||
|
char *dest; /* Text output pointer */
|
||||||
|
char *format; /* Hexadecimal format string */
|
||||||
|
VBU_DasmLine *line; /* Output line */
|
||||||
|
size_t more; /* Number of bytes to output */
|
||||||
|
OpDef *opdef; /* Instruction descriptor */
|
||||||
|
OpDef special; /* Special-case descriptor */
|
||||||
|
char tAddress[9]; /* Address display text */
|
||||||
|
char tCode[4][3]; /* Code display text */
|
||||||
|
char tMnemonic[8]; /* Mnemonic display text */
|
||||||
|
char tOperands[3][15]; /* Operands display text */
|
||||||
|
uint32_t x; /* Scratch and iterator */
|
||||||
|
|
||||||
|
/* Process non-text members */
|
||||||
|
line = &(*lines)[index];
|
||||||
|
line->address = *address;
|
||||||
|
line->codeLength = dasmInstSize(sim, *address);
|
||||||
|
line->isPC = *address == pc;
|
||||||
|
for (x = 0; x < line->codeLength; x++)
|
||||||
|
line->code[x] = vbRead(sim, *address + x, VB_U8);
|
||||||
|
*address += pc - *address < line->codeLength ?
|
||||||
|
pc - *address : line->codeLength;
|
||||||
|
|
||||||
|
/* Do not process text members */
|
||||||
|
if (config == NULL) {
|
||||||
|
line->text.address = NULL;
|
||||||
|
line->text.mnemonic = NULL;
|
||||||
|
line->text.operandsLength = 0;
|
||||||
|
for (x = 0; x < line->codeLength; x++)
|
||||||
|
line->text.code[x] = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select instruction descriptor */
|
||||||
|
x = line->code[1] >> 2 & 63;
|
||||||
|
switch (x) {
|
||||||
|
case 0x12: /* SETF */
|
||||||
|
opdef = dasmSETF(config, line, &special);
|
||||||
|
break;
|
||||||
|
case 0x1F: /* Bit string */
|
||||||
|
x = line->code[0] & 31;
|
||||||
|
opdef = (OpDef *) (x < 16 ? &OPDEFS_BITSTRING[x] : &OPDEF_ILLEGAL);
|
||||||
|
break;
|
||||||
|
case 0x20: case 0x21: case 0x22: case 0x23: /* BCOND */
|
||||||
|
case 0x24: case 0x25: case 0x26: case 0x27:
|
||||||
|
opdef = dasmBCOND(config, line, &special);
|
||||||
|
break;
|
||||||
|
case 0x3E: /* Floating-point/Nintendo */
|
||||||
|
x = line->code[3] >> 2 & 63;
|
||||||
|
opdef = (OpDef *) (x < 13 ? &OPDEFS_FLOATENDO[x] : &OPDEF_ILLEGAL);
|
||||||
|
break;
|
||||||
|
default: opdef = (OpDef *) &OPDEFS[x]; /* Other */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Address */
|
||||||
|
format = config->hexCase == VBU_LOWER ? "%08x" : "%08X";
|
||||||
|
sprintf(tAddress, format, line->address);
|
||||||
|
more = 9;
|
||||||
|
|
||||||
|
/* Code */
|
||||||
|
format = config->hexCase == VBU_LOWER ? "%02x" : "%02X";
|
||||||
|
for (x = 0; x < line->codeLength; x++) {
|
||||||
|
sprintf(tCode[x], format, (int) line->code[x]);
|
||||||
|
more += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mnemonic */
|
||||||
|
strcpy(tMnemonic, opdef->mnemonic);
|
||||||
|
if (config->mnemonicCase == VBU_LOWER)
|
||||||
|
dasmToLower(tMnemonic);
|
||||||
|
more += strlen(tMnemonic) + 1;
|
||||||
|
|
||||||
|
/* Operands */
|
||||||
|
line->text.operandsLength = opdef->operandsLength;
|
||||||
|
for (x = 0; x < opdef->operandsLength; x++) {
|
||||||
|
dasmOperand(tOperands[x], config, line, opdef->operands[x]);
|
||||||
|
more += strlen(tOperands[x]) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grow the text buffer */
|
||||||
|
line = dasmGrow(lines, size, *offset + more, index);
|
||||||
|
if (line == NULL)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Working variables */
|
||||||
|
dest = &((char *) *lines)[*offset];
|
||||||
|
*offset += more;
|
||||||
|
|
||||||
|
/* Address */
|
||||||
|
strcpy(dest, tAddress);
|
||||||
|
line->text.address = dest;
|
||||||
|
dest += 9;
|
||||||
|
|
||||||
|
/* Code */
|
||||||
|
for (x = 0; x < line->codeLength; x++) {
|
||||||
|
strcpy(dest, tCode[x]);
|
||||||
|
line->text.code[x] = dest;
|
||||||
|
dest += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mnemonic */
|
||||||
|
strcpy(dest, tMnemonic);
|
||||||
|
line->text.mnemonic = dest;
|
||||||
|
dest += strlen(dest) + 1;
|
||||||
|
|
||||||
|
/* Operands */
|
||||||
|
for (x = 0; x < opdef->operandsLength; x++) {
|
||||||
|
strcpy(dest, tOperands[
|
||||||
|
config->operandOrder == VBU_DEST_FIRST ?
|
||||||
|
opdef->operandsLength - 1 - x :
|
||||||
|
x
|
||||||
|
]);
|
||||||
|
line->text.operands[x] = dest;
|
||||||
|
dest += strlen(dest) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************** Library Functions *****************************/
|
||||||
|
|
||||||
|
/* Disassemble from a simulation */
|
||||||
|
static VBU_DasmLine* dasmDisassemble(VB *sim, uint32_t address,
|
||||||
|
VBU_DasmConfig *config, unsigned length, int line) {
|
||||||
|
uint32_t addr; /* Working address */
|
||||||
|
uint32_t *circle = NULL; /* Circular address buffer */
|
||||||
|
VBU_DasmLine *lines = NULL; /* Output line buffer */
|
||||||
|
size_t offset; /* Offset into lines buffer */
|
||||||
|
uint32_t pc; /* Program counter */
|
||||||
|
size_t size; /* Number of bytes in lines */
|
||||||
|
unsigned x; /* Iterator */
|
||||||
|
|
||||||
|
/* Nothing to disassemble */
|
||||||
|
if (length == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Establish a circular buffer */
|
||||||
|
if (line > 0) {
|
||||||
|
circle = VBU_REALLOC(NULL, (size_t) (line + 1) * sizeof (uint32_t));
|
||||||
|
if (circle == NULL)
|
||||||
|
goto catch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Begin decoding from at least 10 lines before first/reference */
|
||||||
|
addr = (address & 0xFFFFFFFE) -
|
||||||
|
(int32_t) (line <= 0 ? 10 : line + 10) * 4;
|
||||||
|
|
||||||
|
/* Locate the address of the line containing the reference address */
|
||||||
|
pc = vbGetProgramCounter(sim);
|
||||||
|
for (x = 0;;) {
|
||||||
|
|
||||||
|
/* Track the address in the circular buffer */
|
||||||
|
if (line > 0) {
|
||||||
|
circle[x] = addr;
|
||||||
|
if (++x > (size_t) line)
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the instruction contains the reference address */
|
||||||
|
size = dasmInstSize(sim, addr);
|
||||||
|
if (address - addr < size)
|
||||||
|
break;
|
||||||
|
addr += pc - addr < size ? pc - addr : size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Address of first line is in the circular buffer */
|
||||||
|
if (line > 0) {
|
||||||
|
addr = circle[x];
|
||||||
|
circle = VBU_REALLOC(circle, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep decoding until the first line of output */
|
||||||
|
else for (; line < 0; line++)
|
||||||
|
addr += dasmInstSize(sim, addr);
|
||||||
|
|
||||||
|
/* Working variables */
|
||||||
|
size = length * sizeof (VBU_DasmLine);
|
||||||
|
lines = VBU_REALLOC(NULL, size);
|
||||||
|
offset = size;
|
||||||
|
if (lines == NULL)
|
||||||
|
goto catch;
|
||||||
|
|
||||||
|
/* Process output lines */
|
||||||
|
for (x = 0; x < length; x++) {
|
||||||
|
if (dasmLine(sim, &addr, pc, config, &lines, &size, &offset, x))
|
||||||
|
goto catch;
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
|
||||||
|
/* Exception handler */
|
||||||
|
catch:
|
||||||
|
circle = VBU_REALLOC(circle, 0);
|
||||||
|
lines = VBU_REALLOC(lines , 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* VBUAPI */
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
#ifndef VBUAPI
|
||||||
|
#define VBUAPI
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <vbu.h>
|
||||||
|
|
||||||
|
/* Memory management */
|
||||||
|
#ifndef VBU_REALLOC
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define VBU_REALLOC realloc
|
||||||
|
#else
|
||||||
|
extern void* VBU_REALLOC(void *, size_t);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************** Library Functions *****************************/
|
||||||
|
|
||||||
|
/* Sign-extend an integer of variable width */
|
||||||
|
static int32_t SignExtend(int32_t value, int32_t bits) {
|
||||||
|
#ifndef VB_SIGNED_PROPAGATE
|
||||||
|
value &= ~((uint32_t) 0xFFFFFFFF << bits);
|
||||||
|
bits = (int32_t) 1 << (bits - (int32_t) 1);
|
||||||
|
return (value ^ bits) - bits;
|
||||||
|
#else
|
||||||
|
return value << (32 - bits) >> (32 - bits);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************** Sub-Modules ********************************/
|
||||||
|
|
||||||
|
#include "disassembler.c"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************* API Commands ********************************/
|
||||||
|
|
||||||
|
/* Initialize disassembler options with default settings */
|
||||||
|
VBUAPI VBU_DasmConfig* vbuDasmInit(VBU_DasmConfig *config) {
|
||||||
|
config->bcondNotation = VBU_JOINED;
|
||||||
|
config->conditionCase = VBU_LOWER;
|
||||||
|
config->conditionCL = VBU_L;
|
||||||
|
config->conditionEZ = VBU_Z;
|
||||||
|
config->conditionNotation = VBU_NAMES;
|
||||||
|
config->hexCase = VBU_UPPER;
|
||||||
|
config->hexNotation = VBU_0X;
|
||||||
|
config->memoryNotation = VBU_OUTSIDE;
|
||||||
|
config->mnemonicCase = VBU_UPPER;
|
||||||
|
config->operandOrder = VBU_DEST_LAST;
|
||||||
|
config->programCase = VBU_LOWER;
|
||||||
|
config->programNotation = VBU_NAMES;
|
||||||
|
config->setfNotation = VBU_SPLIT;
|
||||||
|
config->systemCase = VBU_LOWER;
|
||||||
|
config->systemNotation = VBU_NAMES;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disassemble from a simulation */
|
||||||
|
VBUAPI VBU_DasmLine* vbuDisassemble(VB *sim, uint32_t address,
|
||||||
|
VBU_DasmConfig *config, unsigned length, int line) {
|
||||||
|
return dasmDisassemble(sim, address, config, length, line);
|
||||||
|
}
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
#ifndef VBU_H_
|
||||||
|
#define VBU_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef VBUAPI
|
||||||
|
#define VBUAPI extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Header includes */
|
||||||
|
#include <vb.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************* Constants *********************************/
|
||||||
|
|
||||||
|
/* Disassembler options */
|
||||||
|
#define VBU_0X 0
|
||||||
|
#define VBU_C 1
|
||||||
|
#define VBU_DEST_FIRST 1
|
||||||
|
#define VBU_DEST_LAST 0
|
||||||
|
#define VBU_DOLLAR 1
|
||||||
|
#define VBU_E 0
|
||||||
|
#define VBU_H 2
|
||||||
|
#define VBU_INSIDE 1
|
||||||
|
#define VBU_JOINED 0
|
||||||
|
#define VBU_L 0
|
||||||
|
#define VBU_LOWER 1
|
||||||
|
#define VBU_NAMES 1
|
||||||
|
#define VBU_NUMBERS 0
|
||||||
|
#define VBU_OUTSIDE 0
|
||||||
|
#define VBU_SPLIT 1
|
||||||
|
#define VBU_UPPER 0
|
||||||
|
#define VBU_Z 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** Types ***********************************/
|
||||||
|
|
||||||
|
/* Disassembler text options */
|
||||||
|
typedef struct { /* Defaults listed first */
|
||||||
|
uint8_t bcondNotation; /* JOINED, SPLIT */
|
||||||
|
uint8_t conditionCase; /* LOWER, UPPER */
|
||||||
|
uint8_t conditionCL; /* L, C */
|
||||||
|
uint8_t conditionEZ; /* Z, E */
|
||||||
|
uint8_t conditionNotation; /* NAMES, NUMBERS */
|
||||||
|
uint8_t hexCase; /* UPPER, LOWER */
|
||||||
|
uint8_t hexNotation; /* 0X, H, DOLLAR */
|
||||||
|
uint8_t memoryNotation; /* OUTSIDE, INSIDE */
|
||||||
|
uint8_t mnemonicCase; /* UPPER, LOWER */
|
||||||
|
uint8_t operandOrder; /* DEST_LAST, DEST_FIRST */
|
||||||
|
uint8_t programCase; /* LOWER, UPPER */
|
||||||
|
uint8_t programNotation; /* NAMES, NUMBERS */
|
||||||
|
uint8_t setfNotation; /* SPLIT, JOINED */
|
||||||
|
uint8_t systemCase; /* LOWER, UPPER */
|
||||||
|
uint8_t systemNotation; /* NAMES, NUMBERS */
|
||||||
|
} VBU_DasmConfig;
|
||||||
|
|
||||||
|
/* Disassembler output line */
|
||||||
|
typedef struct {
|
||||||
|
|
||||||
|
/* Parsed */
|
||||||
|
uint32_t address; /* Memory address */
|
||||||
|
uint16_t code[4]; /* Instruction bytes */
|
||||||
|
uint8_t codeLength; /* Number of items in code */
|
||||||
|
uint8_t isPC; /* Set if PC is on this line */
|
||||||
|
|
||||||
|
/* Display text */
|
||||||
|
struct {
|
||||||
|
char *address; /* Memory address */
|
||||||
|
char *code[4]; /* Instruction bytes */
|
||||||
|
char *mnemonic; /* Instruction mnemonic */
|
||||||
|
char *operands[3]; /* Instruction operands */
|
||||||
|
uint8_t operandsLength; /* Number of items in operands */
|
||||||
|
} text;
|
||||||
|
|
||||||
|
} VBU_DasmLine;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************* API Commands ********************************/
|
||||||
|
|
||||||
|
VBUAPI VBU_DasmConfig* vbuDasmInit (VBU_DasmConfig *config);
|
||||||
|
VBUAPI VBU_DasmLine* vbuDisassemble(VB *sim, uint32_t address, VBU_DasmConfig *config, unsigned length, int line);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* VBU_H_ */
|
||||||
Reference in New Issue
Block a user