diff options
Diffstat (limited to 'src/emu/machine/8255ppi.c')
-rw-r--r-- | src/emu/machine/8255ppi.c | 589 |
1 files changed, 589 insertions, 0 deletions
diff --git a/src/emu/machine/8255ppi.c b/src/emu/machine/8255ppi.c new file mode 100644 index 00000000000..592c9153239 --- /dev/null +++ b/src/emu/machine/8255ppi.c @@ -0,0 +1,589 @@ +/********************************************************************* + + 8255ppi.c + + Intel 8255 PPI I/O chip + + + NOTE: When port is input, then data present on the ports + outputs is 0xff + + The 8255 PPI has three basic modes: + + Mode 0: Basic Input/Output + Mode 1: Strobed Input/Output + Mode 2: Strobed Bi-directional Bus + + Control Word: + + bit 7 - Mode set flag (1=active) + bit 6-5 - Group A Mode selection + 00 - Mode 0 + 01 - Mode 1 + 1x - Mode 2 + bit 4 - Port A direction (1=input 0=output) + bit 3 - Port C upper direction (1=input 0=output) + bit 2 - Group B Mode selection + 0 - Mode 0 + 1 - Mode 1 + bit 1 - Port B direction (1=input 0=output) + bit 0 - Port C lower direction (1=input 0=output) + + Port A and Port C upper are in group A, and Port B and Port C lower + are in group B + + + Mode 0: Basic Input/Output + In Mode 0, each of the ports (A, B and C) operate as independent + ports for whom direction can be set independently. + + Port C Usage In Mode 0: + + bits 7-4 Input/Output A (direction specified by ctrl bit 3) + bits 3-0 Input/Output B (direction specified by ctrl bit 0) + + Mode 1: Strobed Input/Output + In Mode 1, Port A and Port B use their resepective parts of Port C to + either generate or accept handshaking signals. The STB (strobe) input + "loads" data into the port, and the IBF (input buffer full) output is + then asserted, and the INTR (interrupt request) output is triggered if + interrupts are enabled. Bits 7-6 of Port C remain usable as + conventional IO. + + Group A Port C Usage In Mode 1: + + bits 7-6 Input/Output (direction specified by ctrl bit 3) + bit 5 IBFa (input buffer full A) output + bit 4 !STBa (strobe A) input + bit 3 INTRa (interrupt request A) output + + Group B Port C Usage In Mode 1: + + bit 2 !STBb (strobe B) input + bit 1 IBFb (input buffer full B) output + bit 0 INTRb (interrupt request B) output + + + Mode 2: Strobed Bi-directional Bus + Mode 2 is used to implement a two way handshaking bus. + + When data is written to port A, the OBF (output buffer full) output + will be asserted by the PPI. However, port A will not be asserted + unless the ACK input is asserted, otherwise port A will be high + impedence. + + The STB input and IBF output behaves similar to how it does under mode + 1. Bits 2-0 of Port C remain usable as conventional IO. + + Port C Usage In Mode 2: + + bit 7 !OBFa (output buffer full A) output + bit 6 !ACKa (acknowledge A) input + bit 5 IBFa (interrupt buffer full A) output + bit 4 !STBa (strobe A) input + bit 3 INTRa (interrupt A) output + bit 2-0 Reserved by Group B + + KT 10/01/2000 - Added bit set/reset feature for control port + - Added more accurate port i/o data handling + - Added output reset when control mode is programmed + +*********************************************************************/ + +#include "driver.h" +#include "8255ppi.h" +#include "memconv.h" + + +static int num; + +typedef struct +{ + read8_handler port_read[3]; + write8_handler port_write[3]; + + /* mode flags */ + UINT8 groupA_mode; + UINT8 groupB_mode; + UINT8 portA_dir; + UINT8 portB_dir; + UINT8 portCH_dir; + UINT8 portCL_dir; + + /* handshake signals (1=asserted; 0=non-asserted) */ + UINT8 obf_a; + UINT8 obf_b; + UINT8 ibf_a; + UINT8 ibf_b; + UINT8 inte_a; + UINT8 inte_b; + + UINT8 in_mask[3]; /* input mask */ + UINT8 out_mask[3]; /* output mask */ + UINT8 read[3]; /* data read from ports */ + UINT8 latch[3]; /* data written to ports */ + UINT8 output[3]; /* actual output data */ +} ppi8255; + +static ppi8255 chips[MAX_8255]; + +static void set_mode(int which, int data, int call_handlers); +static void ppi8255_write_port(ppi8255 *chip, int port); + + +void ppi8255_init( const ppi8255_interface *intfce ) +{ + int i; + + num = intfce->num; + + for (i = 0; i < num; i++) + { + ppi8255 *chip = &chips[i]; + + memset(chip, 0, sizeof(*chip)); + + chip->port_read[0] = intfce->portAread[i]; + chip->port_read[1] = intfce->portBread[i]; + chip->port_read[2] = intfce->portCread[i]; + chip->port_write[0] = intfce->portAwrite[i]; + chip->port_write[1] = intfce->portBwrite[i]; + chip->port_write[2] = intfce->portCwrite[i]; + + set_mode(i, 0x1b, 0); /* Mode 0, all ports set to input */ + + state_save_register_item("ppi8255", i, chip->groupA_mode); + state_save_register_item("ppi8255", i, chip->groupB_mode); + state_save_register_item("ppi8255", i, chip->portA_dir); + state_save_register_item("ppi8255", i, chip->portB_dir); + state_save_register_item("ppi8255", i, chip->portCH_dir); + state_save_register_item("ppi8255", i, chip->portCL_dir); + state_save_register_item("ppi8255", i, chip->obf_a); + state_save_register_item("ppi8255", i, chip->obf_b); + state_save_register_item("ppi8255", i, chip->ibf_a); + state_save_register_item("ppi8255", i, chip->ibf_b); + state_save_register_item("ppi8255", i, chip->inte_a); + state_save_register_item("ppi8255", i, chip->inte_b); + state_save_register_item_array("ppi8255", i, chip->in_mask); + state_save_register_item_array("ppi8255", i, chip->out_mask); + state_save_register_item_array("ppi8255", i, chip->read); + state_save_register_item_array("ppi8255", i, chip->latch); + } +} + + + +static void ppi8255_get_handshake_signals(ppi8255 *chip, int is_read, UINT8 *result) +{ + UINT8 handshake = 0x00; + UINT8 mask = 0x00; + + /* group A */ + if (chip->groupA_mode == 1) + { + if (chip->portA_dir) + { + handshake |= chip->ibf_a ? 0x20 : 0x00; + handshake |= (chip->ibf_a && chip->inte_a) ? 0x08 : 0x00; + mask |= 0x28; + } + else + { + handshake |= chip->obf_a ? 0x00 : 0x80; + handshake |= (chip->obf_a && chip->inte_a) ? 0x08 : 0x00; + mask |= 0x88; + } + } + else if (chip->groupA_mode == 2) + { + handshake |= chip->inte_a ? 0x08 : 0x00; + handshake |= chip->obf_a ? 0x00 : 0x80; + handshake |= chip->ibf_a ? 0x20 : 0x00; + mask |= 0xA8; + } + + /* group B */ + if (chip->groupB_mode == 1) + { + if (chip->portA_dir) + { + handshake |= chip->ibf_b ? 0x02 : 0x00; + handshake |= (chip->ibf_b && chip->inte_b) ? 0x01 : 0x00; + mask |= 0x03; + } + else + { + handshake |= chip->obf_b ? 0x00 : 0x02; + handshake |= (chip->obf_b && chip->inte_b) ? 0x01 : 0x00; + mask |= 0x03; + } + } + + *result &= ~mask; + *result |= handshake & mask; +} + + + +static void ppi8255_input(ppi8255 *chip, int port, UINT8 data) +{ + int changed = 0; + + chip->read[port] = data; + + /* port C is special */ + if (port == 2) + { + if (((chip->groupA_mode == 1) && (chip->portA_dir == 0)) || (chip->groupA_mode == 2)) + { + /* is !ACKA asserted? */ + if (chip->obf_a && !(data & 0x40)) + { + chip->obf_a = 0; + changed = 1; + } + } + + if ((chip->groupB_mode == 1) && (chip->portB_dir == 0)) + { + /* is !ACKB asserted? */ + if (chip->obf_b && !(data & 0x04)) + { + chip->obf_b = 0; + changed = 1; + } + } + + if (changed) + ppi8255_write_port(chip, 2); + } +} + + + +static UINT8 ppi8255_read_port(ppi8255 *chip, int port) +{ + UINT8 result = 0x00; + + if (chip->in_mask[port]) + { + if (chip->port_read[port]) + ppi8255_input(chip, port, chip->port_read[port](0)); + + result |= chip->read[port] & chip->in_mask[port]; + } + result |= chip->latch[port] & chip->out_mask[port]; + + /* read special port 2 signals */ + if (port == 2) + ppi8255_get_handshake_signals(chip, 1, &result); + + return result; +} + + + +UINT8 ppi8255_r(int which, offs_t offset) +{ + ppi8255 *chip = &chips[which]; + UINT8 result = 0; + + /* some bounds checking */ + if (which > num) + { + logerror("Attempting to access an unmapped 8255 chip. PC: %04X\n", activecpu_get_pc()); + return 0xff; + } + + offset %= 4; + + switch(offset) + { + case 0: /* Port A read */ + case 1: /* Port B read */ + case 2: /* Port C read */ + result = ppi8255_read_port(chip, offset); + break; + + case 3: /* Control word */ + result = 0xFF; + break; + } + + return result; +} + + + +static void ppi8255_write_port(ppi8255 *chip, int port) +{ + UINT8 write_data; + + write_data = chip->latch[port] & chip->out_mask[port]; + write_data |= 0xFF & ~chip->out_mask[port]; + + /* write out special port 2 signals */ + if (port == 2) + ppi8255_get_handshake_signals(chip, 0, &write_data); + + chip->output[port] = write_data; + if (chip->port_write[port]) + chip->port_write[port](0, write_data); +} + + + +void ppi8255_w(int which, offs_t offset, UINT8 data) +{ + ppi8255 *chip = &chips[which]; + + /* Some bounds checking */ + if (which > num) + { + logerror("Attempting to access an unmapped 8255 chip. PC: %04X\n", activecpu_get_pc()); + return; + } + + offset %= 4; + + switch( offset ) + { + case 0: /* Port A write */ + case 1: /* Port B write */ + case 2: /* Port C write */ + chip->latch[offset] = data; + ppi8255_write_port(chip, offset); + + switch(offset) + { + case 0: + if (!chip->portA_dir && (chip->groupA_mode != 0)) + { + chip->obf_a = 1; + ppi8255_write_port(chip, 2); + } + break; + + case 1: + if (!chip->portB_dir && (chip->groupB_mode != 0)) + { + chip->obf_b = 1; + ppi8255_write_port(chip, 2); + } + break; + } + break; + + case 3: /* Control word */ + if (data & 0x80) + { + set_mode(which, data & 0x7f, 1); + } + else + { + /* bit set/reset */ + int bit; + + bit = (data >> 1) & 0x07; + + if (data & 1) + chip->latch[2] |= (1<<bit); /* set bit */ + else + chip->latch[2] &= ~(1<<bit); /* reset bit */ + + ppi8255_write_port(chip, 2); + } + break; + } +} + +#ifdef MESS +UINT8 ppi8255_peek( int which, offs_t offset ) +{ + ppi8255 *chip; + + + /* Some bounds checking */ + if (which > num) + { + logerror("Attempting to access an unmapped 8255 chip. PC: %04X\n", activecpu_get_pc()); + return 0xff; + } + + chip = &chips[which]; + + + if (offset > 2) + { + logerror("Attempting to access an invalid 8255 port. PC: %04X\n", activecpu_get_pc()); + return 0xff; + } + + + chip = &chips[which]; + + return chip->latch[offset]; +} +#endif + + +void ppi8255_set_portAread(int which, read8_handler portAread) +{ + chips[which].port_read[0] = portAread; +} + +void ppi8255_set_portBread(int which, read8_handler portBread) +{ + chips[which].port_read[1] = portBread; +} + +void ppi8255_set_portCread(int which, read8_handler portCread) +{ + chips[which].port_read[2] = portCread; +} + + +void ppi8255_set_portAwrite(int which, write8_handler portAwrite) +{ + chips[which].port_write[0] = portAwrite; +} + +void ppi8255_set_portBwrite(int which, write8_handler portBwrite) +{ + chips[which].port_write[1] = portBwrite; +} + +void ppi8255_set_portCwrite(int which, write8_handler portCwrite) +{ + chips[which].port_write[2] = portCwrite; +} + + +static void set_mode(int which, int data, int call_handlers) +{ + ppi8255 *chip = &chips[which]; + int i; + + /* parse out mode */ + chip->groupA_mode = (data >> 5) & 3; + chip->groupB_mode = (data >> 2) & 1; + chip->portA_dir = (data >> 4) & 1; + chip->portB_dir = (data >> 1) & 1; + chip->portCH_dir = (data >> 3) & 1; + chip->portCL_dir = (data >> 0) & 1; + + /* normalize groupA_mode */ + if (chip->groupA_mode == 3) + chip->groupA_mode = 2; + + /* Port A direction */ + if (chip->portA_dir) + chip->in_mask[0] = 0xFF, chip->out_mask[0] = 0x00; /* input */ + else + chip->in_mask[0] = 0x00, chip->out_mask[0] = 0xFF; /* output */ + + /* Port B direction */ + if (chip->portB_dir) + chip->in_mask[1] = 0xFF, chip->out_mask[1] = 0x00; /* input */ + else + chip->in_mask[1] = 0x00, chip->out_mask[1] = 0xFF; /* output */ + + /* Port C upper direction */ + if (chip->portCH_dir) + chip->in_mask[2] = 0xF0, chip->out_mask[2] = 0x00; /* input */ + else + chip->in_mask[2] = 0x00, chip->out_mask[2] = 0xF0; /* output */ + + /* Port C lower direction */ + if (chip->portCL_dir) + chip->in_mask[2] |= 0x0F; /* input */ + else + chip->out_mask[2] |= 0x0F; /* output */ + + /* now depending on the group modes, certain Port C lines may be replaced + * with varying control signals */ + switch(chip->groupA_mode) + { + case 0: /* Group A mode 0 */ + /* no changes */ + break; + + case 1: /* Group A mode 1 */ + /* bits 5-3 are reserved by Group A mode 1 */ + chip->in_mask[2] &= ~0x38; + chip->out_mask[2] &= ~0x38; + break; + + case 2: /* Group A mode 2 */ + /* bits 7-3 are reserved by Group A mode 2 */ + chip->in_mask[2] &= ~0xF8; + chip->out_mask[2] &= ~0xF8; + break; + } + + switch(chip->groupB_mode) + { + case 0: /* Group B mode 0 */ + /* no changes */ + break; + + case 1: /* Group B mode 1 */ + /* bits 2-0 are reserved by Group B mode 1 */ + chip->in_mask[2] &= ~0x07; + chip->out_mask[2] &= ~0x07; + break; + } + + /* KT: 25-Dec-99 - 8255 resets latches when mode set */ + chip->latch[0] = chip->latch[1] = chip->latch[2] = 0; + + if (call_handlers) + { + for (i = 0; i < 3; i++) + ppi8255_write_port(chip, i); + } +} + + +void ppi8255_set_portA( int which, UINT8 data ) { ppi8255_input(&chips[which], 0, data); } +void ppi8255_set_portB( int which, UINT8 data ) { ppi8255_input(&chips[which], 1, data); } +void ppi8255_set_portC( int which, UINT8 data ) { ppi8255_input(&chips[which], 2, data); } + +UINT8 ppi8255_get_portA( int which ) { return chips[which].output[0]; } +UINT8 ppi8255_get_portB( int which ) { return chips[which].output[1]; } +UINT8 ppi8255_get_portC( int which ) { return chips[which].output[2]; } + +/* Helpers */ +READ8_HANDLER( ppi8255_0_r ) { return ppi8255_r( 0, offset ); } +READ8_HANDLER( ppi8255_1_r ) { return ppi8255_r( 1, offset ); } +READ8_HANDLER( ppi8255_2_r ) { return ppi8255_r( 2, offset ); } +READ8_HANDLER( ppi8255_3_r ) { return ppi8255_r( 3, offset ); } +READ8_HANDLER( ppi8255_4_r ) { return ppi8255_r( 4, offset ); } +READ8_HANDLER( ppi8255_5_r ) { return ppi8255_r( 5, offset ); } +READ8_HANDLER( ppi8255_6_r ) { return ppi8255_r( 6, offset ); } +READ8_HANDLER( ppi8255_7_r ) { return ppi8255_r( 7, offset ); } +WRITE8_HANDLER( ppi8255_0_w ) { ppi8255_w( 0, offset, data ); } +WRITE8_HANDLER( ppi8255_1_w ) { ppi8255_w( 1, offset, data ); } +WRITE8_HANDLER( ppi8255_2_w ) { ppi8255_w( 2, offset, data ); } +WRITE8_HANDLER( ppi8255_3_w ) { ppi8255_w( 3, offset, data ); } +WRITE8_HANDLER( ppi8255_4_w ) { ppi8255_w( 4, offset, data ); } +WRITE8_HANDLER( ppi8255_5_w ) { ppi8255_w( 5, offset, data ); } +WRITE8_HANDLER( ppi8255_6_w ) { ppi8255_w( 6, offset, data ); } +WRITE8_HANDLER( ppi8255_7_w ) { ppi8255_w( 7, offset, data ); } + +READ16_HANDLER( ppi8255_16le_0_r ) { return read16le_with_read8_handler(ppi8255_0_r, offset, mem_mask); } +READ16_HANDLER( ppi8255_16le_1_r ) { return read16le_with_read8_handler(ppi8255_1_r, offset, mem_mask); } +READ16_HANDLER( ppi8255_16le_2_r ) { return read16le_with_read8_handler(ppi8255_2_r, offset, mem_mask); } +READ16_HANDLER( ppi8255_16le_3_r ) { return read16le_with_read8_handler(ppi8255_3_r, offset, mem_mask); } +READ16_HANDLER( ppi8255_16le_4_r ) { return read16le_with_read8_handler(ppi8255_4_r, offset, mem_mask); } +READ16_HANDLER( ppi8255_16le_5_r ) { return read16le_with_read8_handler(ppi8255_5_r, offset, mem_mask); } +READ16_HANDLER( ppi8255_16le_6_r ) { return read16le_with_read8_handler(ppi8255_6_r, offset, mem_mask); } +READ16_HANDLER( ppi8255_16le_7_r ) { return read16le_with_read8_handler(ppi8255_7_r, offset, mem_mask); } +WRITE16_HANDLER( ppi8255_16le_0_w ) { write16le_with_write8_handler(ppi8255_0_w, offset, data, mem_mask); } +WRITE16_HANDLER( ppi8255_16le_1_w ) { write16le_with_write8_handler(ppi8255_1_w, offset, data, mem_mask); } +WRITE16_HANDLER( ppi8255_16le_2_w ) { write16le_with_write8_handler(ppi8255_2_w, offset, data, mem_mask); } +WRITE16_HANDLER( ppi8255_16le_3_w ) { write16le_with_write8_handler(ppi8255_3_w, offset, data, mem_mask); } +WRITE16_HANDLER( ppi8255_16le_4_w ) { write16le_with_write8_handler(ppi8255_4_w, offset, data, mem_mask); } +WRITE16_HANDLER( ppi8255_16le_5_w ) { write16le_with_write8_handler(ppi8255_5_w, offset, data, mem_mask); } +WRITE16_HANDLER( ppi8255_16le_6_w ) { write16le_with_write8_handler(ppi8255_6_w, offset, data, mem_mask); } +WRITE16_HANDLER( ppi8255_16le_7_w ) { write16le_with_write8_handler(ppi8255_7_w, offset, data, mem_mask); } |