Skip to content

Commit

Permalink
Add proper parity error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Spritetm committed Jun 20, 2024
1 parent e2ea479 commit 87ee2c4
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 40 deletions.
48 changes: 36 additions & 12 deletions csr.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@
#define MISC_DISMAP 0x100 /* disables map (active hi ) */
#define MISC_DIAGMB 0x200 /* put multibus into diagnostic mode */
#define MISC_DIAGPESC 0x400 /* force parity scsi parity error */
#define MISC_DIAGPH 0x800 /* force parity error low byte */
#define MISC_DIAGPL 0x1000 /* force parity error hi byte */
#define MISC_DIAGPL 0x800 /* force parity error low byte */
#define MISC_DIAGPH 0x1000 /* force parity error hi byte */
#define MISC_SCSIDL 0x2000 /* enable diag latch (ACTIVE LOW) */
#define MISC_BOOTJOB 0x4000 /* force job's A23 high (ACTIVE LOW ) */
#define MISC_BOOTDMA 0x8000 /* force dma's A23 high (ACTIVE LOW ) */
Expand Down Expand Up @@ -129,7 +129,7 @@ int csr_try_mbus_held(csr_t *csr) {
return 1;
}

void cst_set_access_error(csr_t *csr, int cpu, int type) {
void csr_set_access_error(csr_t *csr, int cpu, int type) {
int v=0;
if (cpu==0) {
if (type&ACCESS_ERROR_U) v|=ERR_UBE_DMA;
Expand All @@ -141,8 +141,15 @@ void cst_set_access_error(csr_t *csr, int cpu, int type) {
csr->reg[CSR_I_ERR/2]|=v;
}

void csr_set_parity_error(csr_t *c, int hl) {
c->reg[CSR_I_PERR1/2]&=~((1<<12)|(1<<13));
if (hl&2) c->reg[CSR_I_PERR1/2]|=(1<<12);
if (hl&1) c->reg[CSR_I_PERR1/2]|=(1<<13);
}

void csr_write16(void *obj, unsigned int a, unsigned int val) {
csr_t *c=(csr_t*)obj;
if (a==CSR_I_PERR1) return; //ro
if (a==CSR_O_RSEL) {
CSR_LOG_DEBUG("csr write16 0x%X (reset sel) val 0x%X\n", a, val);
} else if (a==CSR_O_SC_C || a==CSR_O_SC_C+2) {
Expand All @@ -167,8 +174,8 @@ void csr_write16(void *obj, unsigned int a, unsigned int val) {
if (!(val&MISC_BOOTJOB)) v|=2;
emu_set_force_a23(v);
v=0;
if (val&MISC_DIAGPH) v|=1;
if (val&MISC_DIAGPL) v|=2;
if (val&MISC_DIAGPL) v|=1;
if (val&MISC_DIAGPH) v|=2;
emu_set_force_parity_error(v);
} else if (a==CSR_O_KILL) { //kill
CSR_LOG_DEBUG("csr write16 0x%X (kill) val 0x%X\n", a, val);
Expand Down Expand Up @@ -203,7 +210,7 @@ void csr_write8(void *obj, unsigned int a, unsigned int val) {


unsigned int csr_read16(void *obj, unsigned int a) {
if (a<4) CSR_LOG_WARN("Read from unknown reg %x\n", a);
if (a>=2 && a<4) CSR_LOG_WARN("Read from unknown reg %x\n", a);
csr_t *c=(csr_t*)obj;
int b=scsi_get_bytecount(c->scsi);
c->reg[CSR_O_SC_C/2]=b>>16;
Expand Down Expand Up @@ -242,7 +249,14 @@ void csr_write16_mmio(void *obj, unsigned int a, unsigned int val) {
//note: a has the start of MMIO as base, but RESET_* has the base of CSR,
//so we adjust the address here.
a=a+0x20;
if (a==RESET_CLR_JOBINT) {
if (a==RESET_MULTERR) {
CSR_LOG_DEBUG("CSR: Reset mbus error\n");
c->reg[CSR_O_MISC]&=~MISC_TBUSY;
emu_raise_int(INT_VECT_MB_IF_ERR, 0, 0);
emu_raise_int(INT_VECT_MB_IF_ERR, 0, 1);
} else if (a==RESET_SCSI_PFLG) {
emu_raise_int(INT_VECT_SCSI_PARITY, 0, 0);
} else if (a==RESET_CLR_JOBINT) {
CSR_LOG_DEBUG("CSR: Clear job int\n");
c->reg[CSR_O_KILL/2] &= ~KILL_INT_JOB;
emu_raise_int(INT_VECT_JOB, 0, 1);
Expand All @@ -258,17 +272,27 @@ void csr_write16_mmio(void *obj, unsigned int a, unsigned int val) {
CSR_LOG_DEBUG("CSR: Set dma int\n");
c->reg[CSR_O_KILL/2] |= KILL_INT_DMA;
emu_raise_int(INT_VECT_DMA, INT_LEVEL_DMA, 0);
} else if (a==RESET_MULTERR) {
CSR_LOG_DEBUG("CSR: Reset mbus error\n");
c->reg[CSR_O_MISC]&=~MISC_TBUSY;
} else if (a==RESET_CINTJ) {
emu_raise_int(INT_VECT_CLOCK, 0, 1);
} else if (a==RESET_CINTD) {
emu_raise_int(INT_VECT_CLOCK, 0, 0);
} else if (a==RESET_JBERR) {
CSR_LOG_DEBUG("CSR: Reset job bus error\n");
c->reg[CSR_I_ERR/2]&=~(ERR_UBE_JOB|ERR_ABE_JOB);
} else if (a==RESET_DBERR) {
CSR_LOG_DEBUG("CSR: Reset dma bus error\n");
c->reg[CSR_I_ERR/2]&=~(ERR_UBE_DMA|ERR_ABE_DMA);
} else if (a==RESET_CINTJ || a==RESET_CINTJ) {
//do nothing, our implementation doesn't need this reset.
} else if (a==RESET_MPERR) {
CSR_LOG_DEBUG("CSR: Reset parity error\n");
emu_raise_int(INT_VECT_PARITY_ERR, 0, 0);
emu_raise_int(INT_VECT_PARITY_ERR, 0, 1);
// c->reg[CSR_I_PERR1/2]&=~((1<<13)|(1<<12));
} else if (a==RESET_SWINT) {
//not implemented yet
} else if (a==RESET_SCSIBERR) {
for (int i=0; i<16; i++) {
if (i!=4) emu_raise_int(INT_VECT_SCSI_SPURIOUS+i, 0, 0);
}
} else {
CSR_LOG_DEBUG("Unhandled MMIO write 0x%x\n", a);
}
Expand Down
5 changes: 4 additions & 1 deletion csr.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ void csr_raise_error(csr_t *c, int error, unsigned int addr);

#define ACCESS_ERROR_U 1
#define ACCESS_ERROR_A 2
void cst_set_access_error(csr_t *csr, int cpu, int type);
void csr_set_access_error(csr_t *csr, int cpu, int type);


void csr_set_parity_error(csr_t *c, int hl);
85 changes: 64 additions & 21 deletions emu.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ static int check_mem_access(unsigned int address, int flags) {
// if (address==0x1000) do_tracefile=1;
// dump_cpu_state();
// dump_callstack();
cst_set_access_error(csr, cur_cpu, ACCESS_ERROR_A);
csr_set_access_error(csr, cur_cpu, ACCESS_ERROR_A);
m68k_pulse_bus_error();
return 0;
}
Expand All @@ -294,24 +294,61 @@ void emu_set_force_a23(int val) {
int parity_force_error=0;

void emu_set_force_parity_error(int val) {
if (parity_force_error!=val) EMU_LOG_INFO("Parity error force %x\n", val);
if (parity_force_error!=val) EMU_LOG_DEBUG("Parity error force %x enabled\n", val);
parity_force_error=val;
}



#define PARITY_ERR_BUF_SZ 8
#define PARITY_ERR_ACTIVE 0x80000000
static unsigned int parity_errors[PARITY_ERR_BUF_SZ]={0};
static unsigned int parity_errors_count=0;

void check_parity_error(unsigned int address, int len) {
if (parity_force_error==0) return;
if (address>=0x80000) return;
EMU_LOG_INFO("Access %x len %x, parity_force_error %x cpu %x\n", address, len, parity_force_error, cur_cpu);
int err=0;
if (len==1) {
if (((address&1)==0) && (parity_force_error&1)) err=1;
if (((address&1)==1) && (parity_force_error&2)) err=1;
} else {
if (parity_force_error) err=1;
if (parity_errors_count==0) return;
int v=0;
for (int a=address; a<address+len; a++) {
for (int i=0; i<PARITY_ERR_BUF_SZ; i++) {
if (parity_errors[i]==(a|PARITY_ERR_ACTIVE)) {
if (a&1) v|=2; else v|=1;
}
}
}
if (err) {
EMU_LOG_INFO("Raising parity error on addr %x\n", address);
if (v) {
EMU_LOG_DEBUG("Raising parity error on addr %x\n", address);
emu_raise_int(INT_VECT_PARITY_ERR, INT_LEVEL_PARITY_ERR, cur_cpu);
csr_set_parity_error(csr, v);
}
}


static void handle_write_parity_error(unsigned int address, int len) {
if (address>=0x80000) return;
if (parity_errors_count==0 && parity_force_error==0) return;
for (int a=address; a<address+len; a++) {
if (((!(a&1)) && (parity_force_error&1)) ||
((a&1) && (parity_force_error&2))) {
//Mark as error
EMU_LOG_DEBUG("Marking parity error on addr %x\n", a);
for (int i=0; i<PARITY_ERR_BUF_SZ; i++) {
if (parity_errors[i]==(a|PARITY_ERR_ACTIVE)) break;
if (!(parity_errors[i]&PARITY_ERR_ACTIVE)) {
parity_errors[i]=a|PARITY_ERR_ACTIVE;
parity_errors_count++;
break;
}
}
} else {
//Clear error
for (int i=0; i<PARITY_ERR_BUF_SZ; i++) {
if (parity_errors[i]==(a|PARITY_ERR_ACTIVE)) {
EMU_LOG_DEBUG("Clearing parity error on addr %x\n", a);
parity_errors[i]=0;
parity_errors_count--;
}
}
}
}
}

Expand Down Expand Up @@ -340,21 +377,21 @@ unsigned int m68k_read_memory_8(unsigned int address) {
void m68k_write_memory_8(unsigned int address, unsigned int value) {
if (force_a23 & (1<<cur_cpu)) address|=0x800000;
if (!check_mem_access(address, ACCESS_W)) return;
check_parity_error(address, 1);
handle_write_parity_error(address, 1);
write_memory_8(address, value);
}

void m68k_write_memory_16(unsigned int address, unsigned int value) {
if (force_a23 & (1<<cur_cpu)) address|=0x800000;
if (!check_mem_access(address, ACCESS_W)) return;
check_parity_error(address, 2);
handle_write_parity_error(address, 2);
write_memory_16(address, value);
}

void m68k_write_memory_32(unsigned int address, unsigned int value) {
if (force_a23 & (1<<cur_cpu)) address|=0x800000;
if (!check_mem_access(address, ACCESS_W)) return;
check_parity_error(address, 4);
handle_write_parity_error(address, 4);
write_memory_32(address, value);
}

Expand Down Expand Up @@ -535,18 +572,24 @@ int m68k_int_cb(int level) {
r=i;
}
}
vectors[cur_cpu][r]=0;
if (level==INT_LEVEL_UART) {
//perhaps this self-clears?
vectors[cur_cpu][r]=0;
}
raise_highest_int();
EMU_LOG_DEBUG("Int ack %x\n", r);
EMU_LOG_DEBUG("Int ack level %x cpu %x vect %x\n", level, cur_cpu, r);
return r;
}

int need_raise_highest_int[2]={0};

void emu_raise_int(uint8_t vector, uint8_t level, int cpu) {
if (level) EMU_LOG_DEBUG("Interrupt raised: %x\n", vector);
vectors[cpu][vector]=level;
need_raise_highest_int[cpu]=1;
if (vectors[cpu][vector]!=level) {
EMU_LOG_DEBUG("Interrupt %s: %x\n", level?"raised":"cleared", vector);
vectors[cpu][vector]=level;
need_raise_highest_int[cpu]=1;
m68k_end_timeslice();
}
}

void emu_raise_rtc_int() {
Expand Down
4 changes: 2 additions & 2 deletions int.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#Interrupt vector / level definitions
//Interrupt vector / level definitions

#define INT_LEVEL_UART 5
#define INT_LEVEL_JOB 4
Expand All @@ -15,7 +15,7 @@
#define INT_VECT_SCSI_PARITY 0x64
#define INT_VECT_SCSI_POINTER 0x68 //plus message (4), command (2) input (1) flag
#define INT_VECT_PARITY_ERR 0x41
#efine INT_VECT_CLOCK 0x83
#define INT_VECT_CLOCK 0x83
#define INT_VECT_MB_IF_ERR 0x7F
#define INT_VECT_DMA 0xc2
#define INT_VECT_JOB 0xc1
Expand Down
13 changes: 9 additions & 4 deletions uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ void uart_write8(void *obj, unsigned int addr, unsigned int val) {
UART_LOG_DEBUG("uart %s chan %s: baud rate gen 0x%X\n", u->name, chan?"B":"A", val);
} else if (a==REG_VECT) {
UART_LOG_DEBUG("uart %s chan %s: vector ctl 0x%X\n", u->name, chan?"B":"A", val);
//there's only one vect register, it's mirrored in both channels
u->chan[0].regs[a]=val;
u->chan[1].regs[a]=val;
}
u->chan[chan].regs[a]=val;
check_ints(u);
Expand All @@ -183,13 +186,14 @@ unsigned int uart_read8(void *obj, unsigned int addr) {
}
}

int ret=u->chan[chan].regs[a];
if (a==REG_STAT0) {
//D7-0: break, underrun, cts, hunt, dcd, tx buf empty, int pending, rx char avail
int r=0;
if (u->chan[chan].ticks_to_loopback==0) r|=0x4;
if (u->chan[chan].has_char_rcv && u->chan[chan].ticks_to_loopback==0) r|=0x3;
UART_LOG_DEBUG("uart %s chan %s: read8 status0 -> %x\n", u->name, chan?"B":"A", addr, r);
return r;
ret=r;
} else if (a==REG_STAT1) {
//D7-0: eof, crc err, rx overrun, parity err, res c2, res c1, res c0, all sent

Expand All @@ -200,19 +204,20 @@ unsigned int uart_read8(void *obj, unsigned int addr) {
if (u->chan[chan].char_rcv==0x3E) r=0x11;

UART_LOG_DEBUG("uart %s chan %s: read8 status1 -> %x\n", u->name, chan?"B":"A", addr, r);
return r;
ret=r;
} else if (a==REG_DATA) {
if (u->is_console && !is_in_loopback) {
UART_LOG_DEBUG("read char %x\n", u->chan[chan].char_rcv);
} else {
UART_LOG_DEBUG("read char %x\n", u->chan[chan].char_rcv);
}
u->chan[chan].has_char_rcv=0;
return u->chan[chan].char_rcv;
ret=u->chan[chan].char_rcv;
}

UART_LOG_DEBUG("uart %s chan %s: read8 %x -> %x\n", u->name, chan?"B":"A", addr, u->chan[chan].regs[addr]);
return u->chan[chan].regs[a];
check_ints(u);
return ret;
}


Expand Down

0 comments on commit 87ee2c4

Please sign in to comment.