Skip to content

Commit

Permalink
added basic asic vector interrupts : PANG now is working (check issue #…
Browse files Browse the repository at this point in the history
…59)

* fixed relock asic (reimplemented completly)
* added new asic page_read to avoid colin dma_mem hack.
* adapted old asic page_write to the new code.
* added asic_ram - atm just for help in test but i think we need it...
* more asic DMA work.

Thanks to Kevin Thacker, Barry Rodewald, Fran Gallego, Juan Carlos Gonzalez. Enjoy :)
  • Loading branch information
David Skywalker committed Mar 26, 2019
1 parent 1e645e4 commit 7e1989c
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 60 deletions.
185 changes: 132 additions & 53 deletions cap32/asic.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,53 +73,46 @@ extern uint8_t *membank_write[4];

uint8_t *pbRegisterPage;

uint8_t asic_ram[16384]; // asic full ram
t_asic asic;
double asic_colours[32][3];
const uint8_t asic_lock_data[ASIC_LOCK_SIZE] = { 0xff, 0x77, 0xb3, 0x51, 0xa8, 0xd4, 0x62, 0x39, 0x9c, 0x46, 0x2b, 0x15, 0x8a, 0xcd, 0xee };

void asic_reset(){
memset(&asic, 0, sizeof(asic));

asic.locked = true;
asic.raster_interrupt = false;
asic.interrupt_vector = 1;
asic.interrupt_vector = 0x01; // // interrupt vector bit 0 is always 1 on startup.
asic.irq_cause = 0x06;
asic.dma.clear = 1;
memset(asic_ram, 0, 16384);
}

void asic_poke_lock_sequence(uint8_t val) {
static const uint8_t lockSeq[] = { 0x00, 0x00, 0xff, 0x77, 0xb3, 0x51, 0xa8, 0xd4, 0x62, 0x39, 0x9c, 0x46, 0x2b, 0x15, 0x8a, 0xcd };
static const int lockSeqLength = sizeof(lockSeq)/sizeof(lockSeq[0]);
// Lock sequence can only start after a non zero value
if (asic.lockSeqPos == 0) {
if (val > 0) {
asic.lockSeqPos = 1;
if(val == 0 && asic.lock_prev_data != 0) {
asic.lock_seq_pos = 0;
}

if(val == asic_lock_data[asic.lock_seq_pos]) {
asic.lock_seq_pos++;
if (asic.lock_seq_pos == (ASIC_LOCK_SIZE - 1) && !asic.locked ) {
LOG("ASIC locked at %u", asic.lock_seq_pos);
asic.locked = true;
}
if (asic.lock_seq_pos >= ASIC_LOCK_SIZE) {
LOG("ASIC unlocked!! (%u)", asic.lock_seq_pos);
asic.locked = false;
}
} else {
if(asic.lockSeqPos < lockSeqLength) {
if (val == lockSeq[asic.lockSeqPos]) {
asic.lockSeqPos++;
} else {
asic.lockSeqPos++;
// If the lock sequence is matched except for the last byte, it means lock
if (asic.lockSeqPos == lockSeqLength) {
LOG("ASIC locked: %u", asic.lockSeqPos);
asic.locked = true;
}
if (val == 0) {
asic.lockSeqPos = 2;
} else {
// We had a non 0, we're now waiting for 0
asic.lockSeqPos = 1;
}
}
} else {
// Full sequence matched and an additional value was written, it means unlock
if (asic.lockSeqPos == lockSeqLength) {
LOG("ASIC unlocked!! (%u)", asic.lockSeqPos);
asic.locked = false;
asic.lockSeqPos = (val == 0) ? -1 : 0;
}
// ASIC last byte can be any value
if (asic.lock_seq_pos == (ASIC_LOCK_SIZE - 1)) {
asic.lock_seq_pos++;
LOG("ASIC unlocked at %u", asic.lock_seq_pos);
asic.locked = false;
}
}
asic.lock_prev_data = val;
}

static INLINE uint16_t decode_magnification(uint8_t val) {
Expand All @@ -128,13 +121,6 @@ static INLINE uint16_t decode_magnification(uint8_t val) {
return mag;
}


// z80 interrupts (mode 0/2) - mode 1 ign
void asic_int(uint8_t mode)
{
printf("asic int %u\n", mode);
}

// Use the DMA info to feed PSG from RAM:
// Read one 16bits instruction for each enabled channel X at each scan line and execute it.
// More precisely: after leading edge of HSYNC, one dead cycle followed by a fetch cycle for each active channel (enabled and not executing a pause) followed by an execution cycle for each active channel.
Expand Down Expand Up @@ -203,6 +189,7 @@ void asic_dma_channel(int c)
{
asic.irq_cause = c * 2;
channel->interrupt = true;
asic.dma.dcsr |= (0x40 >> c); // Control and Status register
//LOG_DEBUG("DMA [" << c << "] interrupt");
}
if(instruction & 0x0020) // STOP
Expand All @@ -217,7 +204,7 @@ void asic_dma_channel(int c)
channel->source_address += 2;
}

// TODO: cleaner way to modify back the register value here ...
// TODO: remove
void asic_dma_mem(int c)
{
uint8_t dcsr = 0;
Expand Down Expand Up @@ -250,6 +237,26 @@ void asic_dma_mem(int c)
}
}

// z80 interrupts (mode 0/2) - mode 1 ign
// TODO: make a better implementation, just a first step
uint8_t asic_int()
{
LOG("asic int - mode 2 cause: %02x", asic.irq_cause);
if( asic.irq_cause != 0x06 && asic.dma.clear & 0x1 ) {
LOG("IRQ: Not cleared, IRQ was called by DMA [%i]", asic.irq_cause);
asic.dma.dcsr &= ~0x80; // not a raster interrupt, so this bit is reset
return (asic.irq_vector & 0xf8) | asic.irq_cause;
}
CRTC.hsw_count &= 0x1F;
if(asic.irq_cause == 0x06) // bit 7 is set "if last interrupt acknowledge cycle was caused by a raster interrupt"
asic.dma.dcsr |= 0x80;
else {
asic.dma.dcsr &= ~0x80;
asic.dma.dcsr &= (0x40 >> asic.irq_cause/2);
}
return (asic.irq_vector & 0xf8) | asic.irq_cause;
}

void asic_dma_cycle()
{
int c;
Expand All @@ -258,19 +265,78 @@ void asic_dma_cycle()
if (asic.dma.ch[c].enabled)
{
asic_dma_channel(c);
asic_dma_mem(c);
//asic_dma_mem(c);
}
}
}

// Return true if byte should be read in memory - Run RAM test of testplus.cpr when touching this
// ASIC register page, from 4000h to 7FFFh is used - http://www.cpctech.org.uk/docs.html
bool asic_register_page_read(uint16_t addr, uint8_t* val) {
if (addr < ASIC_RAM_INIT || addr > ASIC_RAM_END)
return true;

// sprite data at ASIC_RAM_INIT
if (addr >= 0x4000 && addr < 0x5000) {
*val = asic_ram[addr - ASIC_RAM_INIT] & 0x0F;
}
// sprite position and magnification information
else if (addr >= 0x6000 && addr < 0x6080) {
*val = asic_ram[addr - ASIC_RAM_INIT];
switch (addr & 0x03) {
case 0x03:
if ((*val & 0x01) == 0x01) {
*val = 0x0ff;
} else {
*val &= 0x01;
}
break;
case 0x01:
if ((*val & 0x03) == 0x03) {
*val = 0x0ff;
} else {
*val &= 0x03;
}
break;
}
}
// palette data
else if (addr >= 0x6400 && addr < 0x6440) {
*val = asic_ram[addr - ASIC_RAM_INIT];
if (addr & 0x01)
*val &= 0x0F;
}
// misc registers
else if (addr >= 0x06800 && addr < 0x06807) {
*val = 0x0B0 + (addr & 0x1); // random - invalid data area
}
// analog input stuff
else if (addr >= 0x6808 && addr <= 0x680C) {
*val = 0x3F;
} else if (addr == 0x680E) {
*val = 0x3F;
} else if (addr == 0x680D || addr == 0x680F) {
*val = 0x00;
}
// DMA channels and interrupt control
else if (addr >= 0x6C00 && addr < 0x06C0F) {
*val = asic.dma.dcsr;
} else {
*val = asic_ram[addr - ASIC_RAM_INIT];
}
//printf("Received read at %x (%x) - val: %02x\n", addr, addr - ASIC_RAM_INIT, *val);
return false;
}

// Return true if byte should be written in memory
// ASIC register page, from 4000h to 7FFFh is used
bool asic_register_page_write(uint16_t addr, uint8_t val) {
if (addr < 0x4000 || addr > 0x7FFF)
if (addr < ASIC_RAM_INIT || addr > ASIC_RAM_END)
return true;

//printf("Received write at %x - val: %u\n", addr, (int) val);
//printf("Received write at %x - val: %u\n", addr, val);
// TODO:double check the writes (more cases with mirroring / write only ?)
asic_ram[addr - ASIC_RAM_INIT] = val; // force write values in asic ram - help to test/debug
if (addr >= 0x4000 && addr < 0x5000) {
int id = ((addr & 0xF00) >> 8);
int y = ((addr & 0xF0) >> 4);
Expand All @@ -282,7 +348,6 @@ bool asic_register_page_write(uint16_t addr, uint8_t val) {
//LOG("Received sprite %u data", id);
} else if (addr >= 0x5000 && addr < 0x6000) {
// 0x5000 --- unused
return true;
}
else if (addr >= 0x6000 && addr < 0x6080) {
// 6000h 2 N R/W X0 Sprite 0 X position
Expand Down Expand Up @@ -325,7 +390,6 @@ bool asic_register_page_write(uint16_t addr, uint8_t val) {
asic.sprites_mag_x[id] = decode_magnification(val >> 2);
asic.sprites_mag_y[id] = decode_magnification(val);
// Write-only: does not affect pbRegisterPage
return false;
}
}
// 0x6080 --- unused
Expand All @@ -347,7 +411,6 @@ bool asic_register_page_write(uint16_t addr, uint8_t val) {
GateArray.palette[colour] = CPC.video_monitor( asic_colours[colour][0],
asic_colours[colour][1],
asic_colours[colour][2]);
return false;
}
// 0x6440 --- unused
// ASIC - Programmable raster, from 6800h to 6806h
Expand All @@ -368,13 +431,10 @@ bool asic_register_page_write(uint16_t addr, uint8_t val) {
case 0x6805:
// TODO: Write this part - Pang (IM 2)
// (Interrupt service part from http://www.cpcwiki.eu/index.php/Arnold_V_Specs_Revised)
asic.interrupt_vector = val;
asic.irq_vector = (val & 0xf8) + (asic.irq_cause);
if(val & 0x01) // asic dma_clear
{
for (int c = 0; c < NB_DMA_CHANNELS; c++)
asic.dma.ch[c].enabled = 0;
}
//printf("Received interrupt vector write %02x, data = &%02x\n", asic.irq_vector, (int) val);
asic.dma.clear = val & 0x01;
LOG("Received interrupt vector write - data = &%02x", asic.interrupt_vector);
}
}
// 0x6806 --- unused
Expand Down Expand Up @@ -414,17 +474,36 @@ bool asic_register_page_write(uint16_t addr, uint8_t val) {
}
}
// 0x6C0F --- DMA control/status register (DCSR)
// bit 7 - raster interrupt
// bit 6 - DMA channel 0 interrupt
// bit 5 - DMA channel 1 interrupt
// bit 4 - DMA channel 2 interrupt
// bit 3 - unused (write 0)
// bit 2 - DMA channel 2 enable
// bit 1 - DMA channel 1 enable
// bit 0 - DMA channel 0 enable
else if(addr == 0x6C0F) {
// DMA channel X enable
for (int c = 0; c < NB_DMA_CHANNELS; c++) {
if(val & (0x1 << c)) {
asic.irq_cause = 0x06;
asic.dma.ch[c].enabled = 1;
} else {
asic.dma.ch[c].enabled = 0;
}
}
// DMA channel X interrupt
for (int c = 0; c < NB_DMA_CHANNELS; c++) {
if(val & (0x1 << (c + 0x4))) {
asic.irq_cause = 0x06;
asic_ram[addr - ASIC_RAM_INIT] &= ~(0x1 << (c + 0x4));
asic.dma.ch[c].interrupt = false;
LOG(" DMA %u IRQ acknowledge", c);
}
}
asic.dma.dcsr = (asic.dma.dcsr & 0xf8) | (val & 0x07);
//LOG("Received 0x6C0F val: %x", (int) val);
} else {
//printf("Received unused write at %x - val: %u\n", addr, (int) val);
}
return true;
return false;
}
12 changes: 10 additions & 2 deletions cap32/asic.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@

#define ASIC_SPRITES 16
#define ASIC_SPRITE_SIZE 16
#define ASIC_LOCK_SIZE 15
#define NB_DMA_CHANNELS 3
#define ASIC_RAM_INIT 0x4000
#define ASIC_RAM_END 0x7FFF

typedef struct {
uint32_t source_address;
Expand All @@ -39,12 +42,16 @@ typedef struct {

typedef struct {
t_DMA_channel ch[NB_DMA_CHANNELS];
uint8_t dcsr;
uint8_t clear;
} t_dma;

typedef struct {
bool locked;
int lockSeqPos;
int lock_seq_pos;
int lock_prev_data;

uint8_t rmr2;
bool extend_border;
int hscroll;
int vscroll;
Expand All @@ -68,7 +75,8 @@ extern uint8_t *pbRegisterPage;
void asic_reset();
void asic_poke_lock_sequence(uint8_t val);
void asic_dma_cycle();
bool asic_register_page_read(uint16_t addr, uint8_t* val);
bool asic_register_page_write(uint16_t addr, uint8_t val);
void asic_int(uint8_t val);
uint8_t asic_int();

#endif
6 changes: 3 additions & 3 deletions cap32/cap32.c
Original file line number Diff line number Diff line change
Expand Up @@ -637,14 +637,14 @@ void z80_OUT_handler (reg_pair port, uint8_t val)
break;
case 2: // set mode
if (!asic.locked && (val & 0x20)) {
// 6128+ RMR2 register
asic.rmr2 = val; // 6128+ RMR2 register
int membank = (val >> 3) & 3;
if (membank == 3) { // Map register page at 0x4000
//printf("Register page on\n");
//printf("Register page on (%x)\n", val);
GateArray.registerPageOn = true;
membank = 0;
} else {
//printf("Register page off\n");
//printf("Register page off (%x)\n", val);
GateArray.registerPageOn = false;
}
GateArray.lower_ROM_bank = membank;
Expand Down
13 changes: 11 additions & 2 deletions cap32/z80.c
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,11 @@ extern uint8_t *membank_read[4], *membank_write[4];

static INLINE uint8_t read_mem(uint16_t addr)
{
if (GateArray.registerPageOn) {
uint8_t value;
if(!asic_register_page_read(addr, &value))
return value;
}
return (*(membank_read[addr >> 14] + (addr & 0x3fff))); // returns a byte from a 16KB memory bank
}

Expand All @@ -371,7 +376,11 @@ static INLINE void write_mem(uint16_t addr, uint8_t val)
*(membank_write[addr >> 14] + (addr & 0x3fff)) = val; // writes a byte to a 16KB memory bank
}


static INLINE uint8_t read_ptr(){
if(!asic.locked)
return asic_int();
return 0xFF;
}

#define z80_wait_states \
{ \
Expand Down Expand Up @@ -953,7 +962,7 @@ static INLINE uint8_t SRL(uint8_t val) {
} \
write_mem(--_SP, z80.PC.b.h); /* store high byte of current PC */ \
write_mem(--_SP, z80.PC.b.l); /* store low byte of current PC */ \
addr.b.l = 0xff; /* assemble pointer */ \
addr.b.l = read_ptr(); /* assemble pointer */ \
addr.b.h = _I; \
z80.PC.b.l = read_mem(addr.w.l); /* retrieve low byte of vector */ \
z80.PC.b.h = read_mem(addr.w.l+1); /* retrieve high byte of vector */ \
Expand Down

0 comments on commit 7e1989c

Please sign in to comment.