Mercurial > repos > blastem
diff i8255.c @ 2521:8cf7cadc17ee
Initial SC-3000 support
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 11 Oct 2024 00:46:53 -0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/i8255.c Fri Oct 11 00:46:53 2024 -0700 @@ -0,0 +1,290 @@ +#include "i8255.h" + +#include <string.h> + +#define BIT_OBFA 0x80 +#define BIT_ACKA 0x40 +#define BIT_IBFA 0x20 +#define BIT_STBA 0x10 +#define BIT_INTRA 0x08 +#define BIT_STB_ACKB 0x04 +#define BIT_IBF_OBFB 0x02 +#define BIT_INTRB 0x01 + +#define BIT_INTE1 BIT_ACKA +#define BIT_INTE2 BIT_STBA +#define BIT_INTEB BIT_STB_ACKB + +void i8255_init(i8255 *ppi, i8255_out_update out, i8255_in_sample in) +{ + memset(ppi->latches, 0, sizeof(ppi->latches)); + ppi->control = 0x1B; //all ports start as input + ppi->portc_write_mask = 0xFF; + ppi->portc_out_mask = 0; + ppi->out_handler = out; + ppi->in_handler = in; +} + +static uint8_t porta_out_enabled(i8255 *ppi) +{ + return (ppi->control & 0x40) || !(ppi->control & 0x10); +} + +void i8255_write(uint32_t address, i8255 *ppi, uint8_t value, uint32_t cycle) +{ + switch(address) + { + case 0: + ppi->latches[0] = value; + if (porta_out_enabled(ppi)) { + if (ppi->control & 0x60) { + //Mode 1 or 2 + ppi->latches[2] &= ~BIT_OBFA; + if ((ppi->control & 0x60) == 0x20 || !(ppi->latches[2] & BIT_IBFA)) { + ppi->latches[2] &= ~BIT_INTRA; + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } + if (ppi->out_handler && !(ppi->control & 0x40)) { + ppi->out_handler(ppi, cycle, address, value); + } + } + break; + case 1: + if (!(ppi->control & 0x02)) { + ppi->latches[1] = value; + if (ppi->control & 0x04) { + //Mode 1 + ppi->latches[2] &= ~(BIT_IBF_OBFB|BIT_INTRB); + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, address, value); + } + } + break; + case 2: + ppi->latches[2] &= ~ppi->portc_write_mask; + ppi->latches[2] |= value & ppi->portc_write_mask; + if (ppi->out_handler && ppi->portc_out_mask) { + ppi->out_handler(ppi, cycle, address, ppi->latches[2] & ppi->portc_out_mask); + } + break; + case 3: + if (value & 0x80) { + uint8_t changed = ppi->control ^ value; + //datasheet says "output" state is cleared on mode changes + if (changed & 0x60) { + //group A mode changed + ppi->latches[0] = 0; + ppi->latches[2] &= 0x0F; + } + if (changed & 4) { + //group B mode changed + ppi->latches[1] = 0; + if (value & 0x60) { + //PC4 is INTRa + ppi->latches[2] &= 0xF8; + } else { + ppi->latches[2] &= 0xF0; + } + } + ppi->control = value; + ppi->portc_write_mask = ppi->portc_out_mask = 0; + if (value & 0x40) { + //Port A Mode 2 + ppi->portc_out_mask |= BIT_OBFA | BIT_IBFA | BIT_INTRA; + ppi->portc_write_mask |= BIT_INTE1 | BIT_INTE2; + } else if (value & 0x20) { + //Port A Mode 1 + ppi->portc_out_mask |= BIT_INTRA; + if (value & 0x10) { + //Input + ppi->portc_out_mask |= BIT_IBFA; + ppi->portc_write_mask |= BIT_INTE2 | 0xC0; + if (!(value & 0x08)) { + //Port C upper Output + ppi->portc_out_mask |= 0xC0; + } + } else { + //Output + ppi->portc_out_mask |= BIT_OBFA; + ppi->portc_out_mask |= BIT_INTE1 | 0x30; + if (!(value & 0x08)) { + //Port C upper Output + ppi->portc_out_mask |= 0x30; + } + } + } else { + ppi->portc_write_mask |= 0xF0; + if (!(value & 0x08)) { + //Port C upper Output + ppi->portc_out_mask |= 0xF0; + } + } + if (value & 0x04) { + //Port B Mode 1 + ppi->portc_out_mask |= BIT_IBF_OBFB | BIT_INTRB; + ppi->portc_write_mask |= BIT_INTEB; + if (!(ppi->portc_out_mask & BIT_INTRA) && !(value & 1)) { + //Port C lower Output + ppi->portc_out_mask |= 0x08; + ppi->portc_write_mask |= 0x08; + } + } else { + if (!(value & 1)) { + //Port C lower Output + ppi->portc_out_mask |= 0x07; + ppi->portc_write_mask |= 0x07; + if (!(ppi->portc_out_mask & BIT_INTRA)) { + ppi->portc_out_mask |= 0x08; + ppi->portc_write_mask |= 0x08; + } + } + } + } else { + uint8_t bit = 1 << ((value >> 1) & 7); + if (ppi->portc_write_mask & bit) { + if (value & 1) { + ppi->latches[2] |= bit; + } else { + ppi->latches[2] &= bit; + } + if (ppi->out_handler && ppi->portc_out_mask) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } + } + + } +} + +uint8_t i8255_read(uint32_t address, i8255 *ppi, uint32_t cycle) +{ + switch(address) + { + case 0: + if (ppi->control & 0x60) { + //Mode 1 or 2 + if (ppi->control & 0x50) { + //Mode 2 or Mode 1 input + ppi->latches[2] &= ~BIT_IBFA; + if (!(ppi->control & 0x40) || (ppi->latches[2] & BIT_OBFA)) { + ppi->latches[2] &= ~BIT_INTRA; + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } + return ppi->latches[3]; + } + if (ppi->control & 0x10) { + if (ppi->in_handler) { + return ppi->in_handler(ppi, cycle, address); + } + return 0xFF; + } + return ppi->latches[0]; + case 1: + if (ppi->control & 0x40) { + //Mode 1 + if (ppi->control & 0x2) { + //input + ppi->latches[2] &= ~(BIT_IBF_OBFB|BIT_INTRB); + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } + return ppi->latches[1]; + } + if (ppi->control & 0x2) { + //input + if (ppi->in_handler) { + return ppi->in_handler(ppi, cycle, address); + } + return 0xFF; + } + return ppi->latches[1]; + case 2: + return ppi->latches[2]; + case 3: + default: + return 0xFF;//described as illegal in datasheet + } +} + +void i8255_input_strobe_a(i8255 *ppi, uint8_t value, uint32_t cycle) +{ + if ((ppi->control & 0x70) == 0x30 || (ppi->control & 0x40)) { + //Mode 2 or Mode 1 input + ppi->latches[3] = value; + ppi->latches[2] |= BIT_IBFA; + if (ppi->latches[2] & BIT_INTE2) { + ppi->latches[2] |= BIT_INTRA; + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } +} + +void i8255_input_strobe_b(i8255 *ppi, uint8_t value, uint32_t cycle) +{ + if ((ppi->control & 6) == 6) { + //Mode 1 input + ppi->latches[1] = value; + ppi->latches[2] |= BIT_IBF_OBFB; + if (ppi->latches[2] & BIT_INTEB) { + ppi->latches[2] |= BIT_INTRB; + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } +} + +uint8_t i8255_output_ack_a(i8255 *ppi, uint32_t cycle) +{ + if ((ppi->control & 0x70) == 0x20 || (ppi->control & 0x40)) { + //Mode 2 or Mode 1 output + ppi->latches[2] |= BIT_OBFA; + if (ppi->latches[2] & BIT_INTE1) { + ppi->latches[2] |= BIT_INTRA; + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + return ppi->latches[0]; + } + if (ppi->control & 0x10) { + //input mode + return 0xFF; + } + //Mode 0 output + return ppi->latches[0]; +} + +uint8_t i8255_output_ack_b(i8255 *ppi, uint32_t cycle) +{ + if ((ppi->control & 0x06) == 0x04) { + //Mode 1 output + ppi->latches[2] |= BIT_IBF_OBFB; + if (ppi->latches[2] & BIT_INTEB) { + ppi->latches[2] |= BIT_INTRB; + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + return ppi->latches[1]; + } + if (ppi->control & 2) { + //input mode + return 0xFF; + } + //Mode 0 output + return ppi->latches[1]; +}