Mercurial > repos > blastem
changeset 1348:040c5600e2d9
Implemented slow rise time of IO pins set as inputs, but not driven by device. Fixes input in Decap Attack and possibly other games with buggy controller code
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Mon, 08 May 2017 22:31:28 -0700 |
parents | 4c4beb22b042 |
children | 6c7bcb49e840 |
files | genesis.c io.c io.h |
diffstat | 3 files changed, 73 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/genesis.c Sat May 06 00:09:14 2017 -0700 +++ b/genesis.c Mon May 08 22:31:28 2017 -0700 @@ -513,13 +513,13 @@ io_data_write(gen->io.ports+2, value, context->current_cycle); break; case 0x4: - gen->io.ports[0].control = value; + io_control_write(gen->io.ports, value, context->current_cycle); break; case 0x5: - gen->io.ports[1].control = value; + io_control_write(gen->io.ports+1, value, context->current_cycle); break; case 0x6: - gen->io.ports[2].control = value; + io_control_write(gen->io.ports+2, value, context->current_cycle); break; case 0x7: gen->io.ports[0].serial_out = value;
--- a/io.c Sat May 06 00:09:14 2017 -0700 +++ b/io.c Mon May 08 22:31:28 2017 -0700 @@ -1506,6 +1506,16 @@ port->device.mouse.ready_cycle -= deduction; } } + for (int i = 0; i < 8; i++) + { + if (port->slow_rise_start[i] != CYCLE_NEVER) { + if (port->slow_rise_start[i] >= deduction) { + port->slow_rise_start[i] -= deduction; + } else { + port->slow_rise_start[i] = CYCLE_NEVER; + } + } + } if (last_poll_cycle >= deduction) { last_poll_cycle -= deduction; } else { @@ -1623,6 +1633,25 @@ KB_WRITE }; +void io_control_write(io_port *port, uint8_t value, uint32_t current_cycle) +{ + uint8_t changes = value ^ port->control; + if (changes) { + for (int i = 0; i < 8; i++) + { + if (!(value & 1 << i) && !(port->output & 1 << i)) { + //port switched from output to input and the output value was 0 + //since there is a weak pull-up on input pins, this will lead + //to a slow rise from 0 to 1 if the pin isn't being externally driven + port->slow_rise_start[i] = current_cycle; + } else { + port->slow_rise_start[i] = CYCLE_NEVER; + } + } + port->control = value; + } +} + void io_data_write(io_port * port, uint8_t value, uint32_t current_cycle) { uint8_t old_output = (port->control & port->output) | (~port->control & 0xFF); @@ -1766,12 +1795,34 @@ return bytes; } +#define SLOW_RISE_DEVICE (30*7) +#define SLOW_RISE_INPUT (12*7) + +static uint8_t get_output_value(io_port *port, uint32_t current_cycle, uint32_t slow_rise_delay) +{ + uint8_t output = (port->control | 0x80) & port->output; + for (int i = 0; i < 8; i++) + { + if (!(port->control & 1 << i)) { + if (port->slow_rise_start[i] != CYCLE_NEVER) { + if (current_cycle - port->slow_rise_start[i] >= slow_rise_delay) { + output |= 1 << i; + } + } else { + output |= 1 << i; + } + } + } + return output; +} + uint8_t io_data_read(io_port * port, uint32_t current_cycle) { + uint8_t output = get_output_value(port, current_cycle, SLOW_RISE_DEVICE); uint8_t control = port->control | 0x80; - uint8_t output = (control & port->output) | (~control & 0xFF); uint8_t th = output & 0x40; uint8_t input; + uint8_t device_driven; if (current_cycle - last_poll_cycle > MIN_POLL_INTERVAL) { process_events(); last_poll_cycle = current_cycle; @@ -1789,6 +1840,7 @@ } //controller output is logically inverted input = ~input; + device_driven = 0x3F; break; } case IO_GAMEPAD6: @@ -1816,6 +1868,7 @@ } //controller output is logically inverted input = ~input; + device_driven = 0x3F; break; } case IO_MOUSE: @@ -1875,6 +1928,7 @@ } input |= ((port->device.mouse.tr_counter & 1) == 0) << 4; } + device_driven = 0x1F; break; } case IO_SATURN_KEYBOARD: @@ -1947,6 +2001,7 @@ } input |= ((port->device.keyboard.tr_counter & 1) == 0) << 4; } + device_driven = 0x1F; break; } case IO_XBAND_KEYBOARD: @@ -2008,6 +2063,8 @@ input = 0xF; } input |= ((port->device.keyboard.tr_counter & 1) == 0) << 4; + //this is not strictly correct at all times, but good enough for now + device_driven = 0x1F; } break; } @@ -2018,6 +2075,7 @@ service_pipe(port); } input = port->input[th ? IO_TH1 : IO_TH0]; + device_driven = 0x3F; break; case IO_GENERIC: if (port->input[IO_TH0] & 0x80 && port->input[IO_STATE] == IO_WRITTEN) @@ -2027,13 +2085,20 @@ } service_socket(port); input = port->input[IO_TH0]; + device_driven = 0x7F; break; #endif default: - input = 0xFF; + input = 0; + device_driven = 0; break; } - uint8_t value = (input & (~control)) | (port->output & control); + uint8_t value = (input & (~control) & device_driven) | (port->output & control); + //deal with pins that are configured as inputs, but not being actively driven by the device + uint8_t floating = (~device_driven) & (~control); + if (floating) { + value |= get_output_value(port, current_cycle, SLOW_RISE_INPUT) & floating; + } /*if (port->input[GAMEPAD_TH0] || port->input[GAMEPAD_TH1]) { printf ("value: %X\n", value); }*/
--- a/io.h Sat May 06 00:09:14 2017 -0700 +++ b/io.h Mon May 08 22:31:28 2017 -0700 @@ -60,6 +60,7 @@ uint8_t output; uint8_t control; uint8_t input[3]; + uint32_t slow_rise_start[8]; uint8_t serial_out; uint8_t serial_in; uint8_t serial_ctrl; @@ -95,6 +96,7 @@ void map_all_bindings(sega_io *io); void setup_io_devices(tern_node * config, rom_info *rom, sega_io *io); void io_adjust_cycles(io_port * pad, uint32_t current_cycle, uint32_t deduction); +void io_control_write(io_port *port, uint8_t value, uint32_t current_cycle); void io_data_write(io_port * pad, uint8_t value, uint32_t current_cycle); uint8_t io_data_read(io_port * pad, uint32_t current_cycle); void handle_keydown(int keycode, uint8_t scancode);