# HG changeset patch # User Michael Pavone # Date 1613016807 28800 # Node ID aa338eb0ededfcf10210be7b746a7c2498027f41 # Parent e7a516f08cec4e872f99cc41ba40f6e3ba7d0a6a# Parent ba5d206dcfea5d0900f1944b95de2a6ef433fadd Merge diff -r ba5d206dcfea -r aa338eb0eded genesis.c --- a/genesis.c Thu Dec 03 00:30:16 2020 -0800 +++ b/genesis.c Wed Feb 10 20:13:27 2021 -0800 @@ -234,13 +234,14 @@ context->sync_cycle = context->current_cycle + gen->max_cycles; } context->int_cycle = CYCLE_NEVER; - if ((context->status & 0x7) < 6) { + uint8_t mask = context->status & 0x7; + if (mask < 6) { uint32_t next_vint = vdp_next_vint(v_context); if (next_vint != CYCLE_NEVER) { context->int_cycle = next_vint; context->int_num = 6; } - if ((context->status & 0x7) < 4) { + if (mask < 4) { uint32_t next_hint = vdp_next_hint(v_context); if (next_hint != CYCLE_NEVER) { next_hint = next_hint < context->current_cycle ? context->current_cycle : next_hint; @@ -250,6 +251,21 @@ } } + if (mask < 2 && (v_context->regs[REG_MODE_3] & BIT_EINT_EN)) { + uint32_t next_eint_port0 = io_next_interrupt(gen->io.ports, context->current_cycle); + uint32_t next_eint_port1 = io_next_interrupt(gen->io.ports + 1, context->current_cycle); + uint32_t next_eint_port2 = io_next_interrupt(gen->io.ports + 2, context->current_cycle); + uint32_t next_eint = next_eint_port0 < next_eint_port1 + ? (next_eint_port0 < next_eint_port2 ? next_eint_port0 : next_eint_port2) + : (next_eint_port1 < next_eint_port2 ? next_eint_port1 : next_eint_port2); + if (next_eint != CYCLE_NEVER) { + next_eint = next_eint < context->current_cycle ? context->current_cycle : next_eint; + if (next_eint < context->int_cycle) { + context->int_cycle = next_eint; + context->int_num = 2; + } + } + } } } if (context->int_cycle > context->current_cycle && context->int_pending == INT_PENDING_SR_CHANGE) { @@ -379,6 +395,9 @@ sync_z80(z_context, mclks); sync_sound(gen, mclks); vdp_run_context(v_context, mclks); + io_run(gen->io.ports, mclks); + io_run(gen->io.ports + 1, mclks); + io_run(gen->io.ports + 2, mclks); if (mclks >= gen->reset_cycle) { gen->reset_requested = 1; context->should_return = 1; @@ -793,7 +812,7 @@ io_control_write(gen->io.ports+2, value, context->current_cycle); break; case 0x7: - gen->io.ports[0].serial_out = value; + io_tx_write(gen->io.ports, value, context->current_cycle); break; case 0x8: case 0xB: @@ -801,19 +820,20 @@ //serial input port is not writeable break; case 0x9: + io_sctrl_write(gen->io.ports, value, context->current_cycle); gen->io.ports[0].serial_ctrl = value; break; case 0xA: - gen->io.ports[1].serial_out = value; + io_tx_write(gen->io.ports + 1, value, context->current_cycle); break; case 0xC: - gen->io.ports[1].serial_ctrl = value; + io_sctrl_write(gen->io.ports + 1, value, context->current_cycle); break; case 0xD: - gen->io.ports[2].serial_out = value; + io_tx_write(gen->io.ports + 2, value, context->current_cycle); break; case 0xF: - gen->io.ports[2].serial_ctrl = value; + io_sctrl_write(gen->io.ports + 2, value, context->current_cycle); break; } } else { @@ -949,28 +969,28 @@ value = gen->io.ports[0].serial_out; break; case 0x8: - value = gen->io.ports[0].serial_in; + value = io_rx_read(gen->io.ports, context->current_cycle); break; case 0x9: - value = gen->io.ports[0].serial_ctrl; + value = io_sctrl_read(gen->io.ports, context->current_cycle); break; case 0xA: value = gen->io.ports[1].serial_out; break; case 0xB: - value = gen->io.ports[1].serial_in; + value = io_rx_read(gen->io.ports + 1, context->current_cycle); break; case 0xC: - value = gen->io.ports[1].serial_ctrl; + value = io_sctrl_read(gen->io.ports, context->current_cycle); break; case 0xD: value = gen->io.ports[2].serial_out; break; case 0xE: - value = gen->io.ports[2].serial_in; + value = io_rx_read(gen->io.ports + 1, context->current_cycle); break; case 0xF: - value = gen->io.ports[2].serial_ctrl; + value = io_sctrl_read(gen->io.ports, context->current_cycle); break; default: value = get_open_bus_value(&gen->header) >> 8; diff -r ba5d206dcfea -r aa338eb0eded io.c --- a/io.c Thu Dec 03 00:30:16 2020 -0800 +++ b/io.c Wed Feb 10 20:13:27 2021 -0800 @@ -39,7 +39,8 @@ "EA 4-way Play cable A", "EA 4-way Play cable B", "Sega Parallel Transfer Board", - "Generic Device" + "Generic Device", + "Generic Serial" }; #define GAMEPAD_TH0 0 @@ -210,8 +211,20 @@ return find_keyboard(io) != NULL; } +static void set_serial_clock(io_port *port) +{ + switch(port->serial_ctrl >> 6) + { + case 0: port->serial_divider = 11186; break; //4800 bps + case 1: port->serial_divider = 22372; break; //2400 bps + case 2: port->serial_divider = 44744; break; //1200 bps + case 3: port->serial_divider = 178976; break; //300 bps + } +} + void process_device(char * device_type, io_port * port) { + set_serial_clock(port); //assuming that the io_port struct has been zeroed if this is the first time this has been called if (!device_type) { @@ -272,6 +285,12 @@ port->device.stream.data_fd = -1; port->device.stream.listen_fd = -1; } + } else if(!strcmp(device_type, "serial")) { + if (port->device_type != IO_GENERIC_SERIAL) { + port->device_type = IO_GENERIC_SERIAL; + port->device.stream.data_fd = -1; + port->device.stream.listen_fd = -1; + } } } @@ -354,7 +373,7 @@ } } } - } else if (ports[i].device_type == IO_GENERIC && ports[i].device.stream.data_fd == -1) { + } else if (ports[i].device_type == IO_GENERIC || ports[i].device_type == IO_GENERIC_SERIAL && ports[i].device.stream.data_fd == -1) { char *sock_name = tern_find_path(config, "io\0socket\0", TVAL_PTR).ptrval; if (!sock_name) { @@ -427,7 +446,6 @@ } } -uint32_t last_poll_cycle; void io_adjust_cycles(io_port * port, uint32_t current_cycle, uint32_t deduction) { /*uint8_t control = pad->control | 0x80; @@ -459,25 +477,80 @@ } } } - if (last_poll_cycle >= deduction) { - last_poll_cycle -= deduction; + if (port->transmit_end >= deduction) { + port->transmit_end -= deduction; } else { - last_poll_cycle = 0; + port->transmit_end = 0; + } + if (port->receive_end >= deduction) { + port->receive_end -= deduction; + } else { + port->receive_end = 0; + } + if (port->last_poll_cycle >= deduction) { + port->last_poll_cycle -= deduction; + } else { + port->last_poll_cycle = 0; } } #ifndef _WIN32 -static void wait_for_connection(io_port * port) +static void wait_for_connection(io_port *port) { if (port->device.stream.data_fd == -1) { - debug_message("Waiting for socket connection..."); + debug_message("Waiting for socket connection...\n"); port->device.stream.data_fd = accept(port->device.stream.listen_fd, NULL, NULL); fcntl(port->device.stream.data_fd, F_SETFL, O_NONBLOCK | O_RDWR); } } -static void service_pipe(io_port * port) +static void poll_for_connection(io_port *port) +{ + if (port->device.stream.data_fd == -1) + { + fcntl(port->device.stream.listen_fd, F_SETFL, O_NONBLOCK | O_RDWR); + port->device.stream.data_fd = accept(port->device.stream.listen_fd, NULL, NULL); + fcntl(port->device.stream.listen_fd, F_SETFL, O_RDWR); + if (port->device.stream.data_fd != -1) { + fcntl(port->device.stream.data_fd, F_SETFL, O_NONBLOCK | O_RDWR); + } + } +} + +static void write_serial_byte(io_port *port) +{ + fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR); + for (int sent = 0; sent != sizeof(port->serial_transmitting);) + { + sent = send(port->device.stream.data_fd, &port->serial_transmitting, sizeof(port->serial_transmitting), 0); + if (sent < 0) { + close(port->device.stream.data_fd); + port->device.stream.data_fd = -1; + wait_for_connection(port); + fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR); + } + } + fcntl(port->device.stream.data_fd, F_SETFL, O_NONBLOCK | O_RDWR); +} + +static void read_serial_byte(io_port *port) +{ + poll_for_connection(port); + if (port->device.stream.data_fd == -1) { + return; + } + int read = recv(port->device.stream.data_fd, &port->serial_receiving, sizeof(port->serial_receiving), 0); + if (read < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { + close(port->device.stream.data_fd); + port->device.stream.data_fd = -1; + } + if (read > 0) { + port->receive_end = port->serial_cycle + 10 * port->serial_divider; + } +} + +static void service_pipe(io_port *port) { uint8_t value; int numRead = read(port->device.stream.data_fd, &value, sizeof(value)); @@ -576,6 +649,61 @@ KB_WRITE }; +enum { + SCTRL_BIT_TX_FULL = 1, + SCTRL_BIT_RX_READY = 2, + SCTRL_BIT_RX_ERROR = 4, + SCTRL_BIT_RX_INTEN = 8, + SCTRL_BIT_TX_ENABLE = 0x10, + SCTRL_BIT_RX_ENABLE = 0x20 +}; + +void io_run(io_port *port, uint32_t current_cycle) +{ + uint32_t new_serial_cycle = ((current_cycle - port->serial_cycle) / port->serial_divider) * port->serial_divider + port->serial_cycle; + if (port->transmit_end && port->transmit_end <= new_serial_cycle) { + port->transmit_end = 0; + + if (port->serial_ctrl & SCTRL_BIT_TX_ENABLE) { + switch (port->device_type) + { +#ifndef _WIN32 + case IO_GENERIC_SERIAL: + write_serial_byte(port); + break; +#endif + //TODO: think about how serial mode might interact with non-serial peripherals + } + } + } + if (!port->transmit_end && new_serial_cycle != port->serial_cycle && (port->serial_ctrl & SCTRL_BIT_TX_FULL)) { + //there's a transmit byte pending and no byte is currently being sent + port->serial_transmitting = port->serial_out; + port->serial_ctrl &= ~SCTRL_BIT_TX_FULL; + //1 start bit, 8 data bits and 1 stop bit + port->transmit_end = new_serial_cycle + 10 * port->serial_divider; + } + port->serial_cycle = new_serial_cycle; + if (port->serial_ctrl && SCTRL_BIT_RX_ENABLE) { + if (port->receive_end && new_serial_cycle >= port->receive_end) { + port->serial_in = port->serial_receiving; + port->serial_ctrl |= SCTRL_BIT_RX_READY; + port->receive_end = 0; + } + if (!port->receive_end) { + switch(port->device_type) + { +#ifndef _WIN32 + case IO_GENERIC_SERIAL: + read_serial_byte(port); + break; +#endif + //TODO: think about how serial mode might interact with non-serial peripherals + } + } + } +} + void io_control_write(io_port *port, uint8_t value, uint32_t current_cycle) { uint8_t changes = value ^ port->control; @@ -723,6 +851,20 @@ } +void io_tx_write(io_port *port, uint8_t value, uint32_t current_cycle) +{ + io_run(port, current_cycle); + port->serial_out = value; + port->serial_ctrl |= SCTRL_BIT_TX_FULL; +} + +void io_sctrl_write(io_port *port, uint8_t value, uint32_t current_cycle) +{ + io_run(port, current_cycle); + port->serial_ctrl = (port->serial_ctrl & 0x7) | (value & 0xF8); + set_serial_clock(port); +} + uint8_t get_scancode_bytes(io_port *port) { if (port->device.keyboard.read_pos == 0xFF) { @@ -766,9 +908,9 @@ uint8_t th = output & 0x40; uint8_t input; uint8_t device_driven; - if (current_cycle - last_poll_cycle > MIN_POLL_INTERVAL) { + if (current_cycle - port->last_poll_cycle > MIN_POLL_INTERVAL) { process_events(); - last_poll_cycle = current_cycle; + port->last_poll_cycle = current_cycle; } switch (port->device_type) { @@ -1049,6 +1191,36 @@ return value; } +uint8_t io_rx_read(io_port * port, uint32_t current_cycle) +{ + io_run(port, current_cycle); + port->serial_ctrl &= ~SCTRL_BIT_RX_READY; + return port->serial_in; +} + +uint8_t io_sctrl_read(io_port *port, uint32_t current_cycle) +{ + io_run(port, current_cycle); + return port->serial_ctrl; +} + +uint32_t io_next_interrupt(io_port *port, uint32_t current_cycle) +{ + if (!(port->control & 0x80)) { + return CYCLE_NEVER; + } + if (port->serial_ctrl & SCTRL_BIT_RX_INTEN) { + if (port->serial_ctrl & SCTRL_BIT_RX_READY) { + return current_cycle; + } + if ((port->serial_ctrl & SCTRL_BIT_RX_ENABLE) && port->receive_end) { + return port->receive_end; + } + } + //TODO: handle external interrupts from TH transitions + return CYCLE_NEVER; +} + void io_serialize(io_port *port, serialize_buffer *buf) { save_int8(buf, port->output); diff -r ba5d206dcfea -r aa338eb0eded io.h --- a/io.h Thu Dec 03 00:30:16 2020 -0800 +++ b/io.h Wed Feb 10 20:13:27 2021 -0800 @@ -24,7 +24,8 @@ IO_EA_MULTI_A, IO_EA_MULTI_B, IO_SEGA_PARALLEL, - IO_GENERIC + IO_GENERIC, + IO_GENERIC_SERIAL }; typedef struct { @@ -62,8 +63,15 @@ uint8_t control; uint8_t input[3]; uint32_t slow_rise_start[8]; + uint32_t serial_cycle; + uint32_t serial_divider; + uint32_t last_poll_cycle; + uint32_t transmit_end; + uint32_t receive_end; uint8_t serial_out; + uint8_t serial_transmitting; uint8_t serial_in; + uint8_t serial_receiving; uint8_t serial_ctrl; uint8_t device_type; } io_port; @@ -106,9 +114,15 @@ 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_run(io_port *port, uint32_t current_cycle); 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); +void io_tx_write(io_port *port, uint8_t value, uint32_t current_cycle); +void io_sctrl_write(io_port *port, uint8_t value, uint32_t current_cycle); uint8_t io_data_read(io_port * pad, uint32_t current_cycle); +uint8_t io_rx_read(io_port * port, uint32_t current_cycle); +uint8_t io_sctrl_read(io_port *port, uint32_t current_cycle); +uint32_t io_next_interrupt(io_port *port, uint32_t current_cycle); void io_serialize(io_port *port, serialize_buffer *buf); void io_deserialize(deserialize_buffer *buf, void *vport); diff -r ba5d206dcfea -r aa338eb0eded vdp.h --- a/vdp.h Thu Dec 03 00:30:16 2020 -0800 +++ b/vdp.h Wed Feb 10 20:13:27 2021 -0800 @@ -118,7 +118,7 @@ #define BIT_SPRITE_SZ 0x02 //Mode reg 3 -#define BIT_EINT_EN 0x10 +#define BIT_EINT_EN 0x08 #define BIT_VSCROLL 0x04 //Mode reg 4