From 9ebe9df2b00b8a8158e4aa93982a3391d12b563a Mon Sep 17 00:00:00 2001 From: Stephen Horn Date: Sun, 4 Apr 2021 06:28:05 -0500 Subject: [PATCH] Cumulative optimizations (#336) * Removing branches and needless loops from ps2, joystick, video_step, vera_spi_step. * byte -> value: Variable name change: Windows SDK conflict. * Documentation updates --- .clang-format | 0 README.md | 6 +- i2c.c | 20 +-- joystick.c | 355 +++++++++++++++++++++++++-------------- joystick.h | 44 ++--- main.c | 63 ++----- ps2.c | 255 +++++++++++++--------------- ps2.h | 29 ++-- rom_symbols.sh | 0 symbolize.sh | 0 vera_spi.c | 6 +- vera_spi.h | 2 +- via.c | 92 +++++----- via.h | 14 +- video.c | 19 ++- video.h | 2 +- webassembly/jszip.min.js | 0 17 files changed, 478 insertions(+), 429 deletions(-) mode change 100755 => 100644 .clang-format mode change 100755 => 100644 rom_symbols.sh mode change 100755 => 100644 symbolize.sh mode change 100755 => 100644 webassembly/jszip.min.js diff --git a/.clang-format b/.clang-format old mode 100755 new mode 100644 diff --git a/README.md b/README.md index c3b248bc..1d5b9317 100644 --- a/README.md +++ b/README.md @@ -142,10 +142,10 @@ Since the emulator tells the computer the *position* of keys that are pressed, y The following keys can be used for controlling games: -|Keyboard Key | NES Equivalent | +|Keyboard Key | SNES Equivalent | |--------------|----------------| -|Ctrl | A | -|Alt | B | +|Ctrl | B | +|Alt | Y | |Space | SELECT | |Enter | START | |Cursor Up | UP | diff --git a/i2c.c b/i2c.c index 68bcc662..1193e8fc 100644 --- a/i2c.c +++ b/i2c.c @@ -20,7 +20,7 @@ i2c_port_t i2c_port; static int state = STATE_STOP; static bool read_mode = false; -static uint8_t byte = 0; +static uint8_t value = 0; static int count = 0; static uint8_t device; static uint8_t offset; @@ -86,10 +86,10 @@ i2c_step() if (state < 8) { if (read_mode) { if (state == 0) { - byte = i2c_read(device, offset); + value = i2c_read(device, offset); } - i2c_port.data_out = !!(byte & 0x80); - byte <<= 1; + i2c_port.data_out = !!(value & 0x80); + value <<= 1; #if LOG_LEVEL >= 4 printf("I2C OUT#%d: %d\n", state, i2c_port.data_out); #endif @@ -98,8 +98,8 @@ i2c_step() #if LOG_LEVEL >= 4 printf("I2C BIT#%d: %d\n", state, i2c_port.data_in); #endif - byte <<= 1; - byte |= i2c_port.data_in; + value <<= 1; + value |= i2c_port.data_in; state++; } } else { // state == 8 @@ -121,17 +121,17 @@ i2c_step() bool ack = true; switch (count) { case 0: - device = byte >> 1; - read_mode = byte & 1; + device = value >> 1; + read_mode = value & 1; if (device != DEVICE_SMC && device != DEVICE_RTC) { ack = false; } break; case 1: - offset = byte; + offset = value; break; default: - i2c_write(device, offset, byte); + i2c_write(device, offset, value); offset++; break; } diff --git a/joystick.c b/joystick.c index 69d4acaf..4b43addf 100644 --- a/joystick.c +++ b/joystick.c @@ -1,159 +1,264 @@ -/**********************************************/ -// File : joystick.c -// Author : John Bliss -// Date : September 27th 2019 -/**********************************************/ - #include "joystick.h" -enum joy_status joy_mode[NUM_JOYSTICKS]; -static SDL_GameController *joystick[NUM_JOYSTICKS]; -static uint16_t joystick_state[NUM_JOYSTICKS]; -bool joystick_data[NUM_JOYSTICKS]; +#include + +struct joystick_info { + int instance_id; + SDL_GameController *controller; + uint16_t button_mask; + uint16_t shift_mask; +}; -static bool old_clock = false; -static bool writing = false; -static uint8_t clock_count = 0; +static const uint16_t button_map[SDL_CONTROLLER_BUTTON_MAX] = { + 1 << 0, //SDL_CONTROLLER_BUTTON_A, + 1 << 8, //SDL_CONTROLLER_BUTTON_B, + 1 << 1, //SDL_CONTROLLER_BUTTON_X, + 1 << 9, //SDL_CONTROLLER_BUTTON_Y, + 1 << 2, //SDL_CONTROLLER_BUTTON_BACK, + 0, //SDL_CONTROLLER_BUTTON_GUIDE, + 1 << 3, //SDL_CONTROLLER_BUTTON_START, + 0, //SDL_CONTROLLER_BUTTON_LEFTSTICK, + 0, //SDL_CONTROLLER_BUTTON_RIGHTSTICK, + 1 << 10, //SDL_CONTROLLER_BUTTON_LEFTSHOULDER, + 1 << 11, //SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, + 1 << 4, //SDL_CONTROLLER_BUTTON_DPAD_UP, + 1 << 5, //SDL_CONTROLLER_BUTTON_DPAD_DOWN, + 1 << 6, //SDL_CONTROLLER_BUTTON_DPAD_LEFT, + 1 << 7, //SDL_CONTROLLER_BUTTON_DPAD_RIGHT, +}; -bool joystick_latch, joystick_clock; +static struct joystick_info *Joystick_controllers = NULL; +static int Num_joystick_controllers = 0; -bool joystick_init() +static void +resize_joystick_controllers(int new_size) { - for (int i = 0; i < SDL_NumJoysticks(); i++) { - if (!SDL_IsGameController(i)) { - continue; + if (new_size == 0) { + free(Joystick_controllers); + Joystick_controllers = NULL; + Num_joystick_controllers = 0; + return; + } + + struct joystick_info *old_controllers = Joystick_controllers; + Joystick_controllers = (struct joystick_info *)malloc(sizeof(struct joystick_info) * new_size); + + int min_size = new_size < Num_joystick_controllers ? new_size : Num_joystick_controllers; + if (min_size > 0) { + memcpy(Joystick_controllers, old_controllers, sizeof(struct joystick_info) * min_size); + free(old_controllers); + } + + for (int i = min_size; i < new_size; ++i) { + Joystick_controllers[i].instance_id = -1; + Joystick_controllers[i].controller = NULL; + Joystick_controllers[i].button_mask = 0xffff; + Joystick_controllers[i].shift_mask = 0; + } +} + +static void +add_joystick_controller(struct joystick_info *info) +{ + int i; + for (i = 0; i < Num_joystick_controllers; ++i) { + if (Joystick_controllers[i].instance_id == -1) { + memcpy(&Joystick_controllers[i], info, sizeof(struct joystick_info)); + return; } + } - for (int j = 0; j < NUM_JOYSTICKS; j++) { - if (joy_mode[j] != NONE && !joystick[j]) { - joystick[j] = SDL_GameControllerOpen(i); - if (joystick[j]) { - break; - } else { - fprintf(stderr, "Could not open gamecontroller %i: %s\n", i, SDL_GetError()); - } - } + i = Num_joystick_controllers; + resize_joystick_controllers(Num_joystick_controllers << 1); + + memcpy(&Joystick_controllers[i], info, sizeof(struct joystick_info)); +} + +static void +remove_joystick_controller(int instance_id) +{ + for (int i = 0; i < Num_joystick_controllers; ++i) { + if (Joystick_controllers[i].instance_id == instance_id) { + Joystick_controllers[i].instance_id = -1; + Joystick_controllers[i].controller = NULL; + return; } } +} + +static struct joystick_info * +find_joystick_controller(int instance_id) +{ + for (int i = 0; i < Num_joystick_controllers; ++i) { + if (Joystick_controllers[i].instance_id == instance_id) { + return &Joystick_controllers[i]; + } + } + + return NULL; +} + +bool Joystick_slots_enabled[NUM_JOYSTICKS] = {false, false, false, false}; +static int Joystick_slots[NUM_JOYSTICKS]; + +static bool Joystick_latch = false; +uint8_t Joystick_data = 0; + +bool +joystick_init(void) +{ + for (int i = 0; i < NUM_JOYSTICKS; ++i) { + Joystick_slots[i] = -1; + } + + const int num_joysticks = SDL_NumJoysticks(); + + Num_joystick_controllers = num_joysticks > 16 ? num_joysticks : 16; + Joystick_controllers = malloc(sizeof(struct joystick_info) * Num_joystick_controllers); + + for (int i = 0; i < Num_joystick_controllers; ++i) { + Joystick_controllers[i].instance_id = -1; + Joystick_controllers[i].controller = NULL; + Joystick_controllers[i].button_mask = 0xffff; + Joystick_controllers[i].shift_mask = 0; + } + + for (int i = 0; i < num_joysticks; ++i) { + joystick_add(i); + } - writing = false; return true; } -void joystick_step() +void +joystick_add(int index) { - if (!writing) { //if we are not already writing, check latch to - //see if we need to start - handle_latch(joystick_latch, joystick_clock); + printf("joystick_add(%d)\n", index); + + if (!SDL_IsGameController(index)) { + return; + } + + SDL_GameController *controller = SDL_GameControllerOpen(index); + if (controller == NULL) { + fprintf(stderr, "Could not open controller %d: %s\n", index, SDL_GetError()); return; } - //if we have started writing controller data and the latch has dropped, - // we need to start the next bit - if (!joystick_latch) { - //check if clock has changed - if (joystick_clock != old_clock) { - if (old_clock) { - old_clock = joystick_clock; - } else { //only write next bit when the new clock is high - clock_count +=1; - old_clock = joystick_clock; - if (clock_count < 16) { // write out the next 15 bits - for (int i = 0; i < NUM_JOYSTICKS; i++) { - joystick_data[i] = (joy_mode[i] != NONE) ? (joystick_state[i] & 1) : 1; - joystick_state[i] = joystick_state[i] >> 1; - } - } else { - //Done writing controller data - //reset flag and set count to 0 - writing = false; - clock_count = 0; - for (int i = 0; i < NUM_JOYSTICKS; i++) { - joystick_data[i] = (joy_mode[i] != NONE) ? 0 : 1; - } - } + SDL_JoystickID instance_id = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)); + bool exists = false; + for (int i = 0; i < NUM_JOYSTICKS; ++i) { + if (!Joystick_slots_enabled[i]) { + continue; + } + + if (Joystick_slots[i] == instance_id) { + exists = true; + break; + } + } + + if (!exists) { + for (int i = 0; i < NUM_JOYSTICKS; ++i) { + if (!Joystick_slots_enabled[i]) { + continue; } + + if (Joystick_slots[i] == -1) { + Joystick_slots[i] = instance_id; + break; + } + } + + struct joystick_info new_info; + new_info.instance_id = instance_id; + new_info.controller = controller; + new_info.button_mask = 0xffff; + new_info.shift_mask = 0; + add_joystick_controller(&new_info); + } +} + +void +joystick_remove(int instance_id) +{ + printf("joystick_remove(%d)\n", instance_id); + + for (int i = 0; i < NUM_JOYSTICKS; ++i) { + if (Joystick_slots[i] == instance_id) { + Joystick_slots[i] = -1; + break; } } + SDL_GameController *controller = SDL_GameControllerFromInstanceID(instance_id); + if (controller == NULL) { + fprintf(stderr, "Could not find controller from instance_id %d: %s\n", instance_id, SDL_GetError()); + } else { + SDL_GameControllerClose(controller); + remove_joystick_controller(instance_id); + } +} + +void +joystick_button_down(int instance_id, uint8_t button) +{ + printf("joystick_button_down(%d, %d)\n", instance_id, button); + + struct joystick_info *joy = find_joystick_controller(instance_id); + if (joy != NULL) { + joy->button_mask &= ~(button_map[button]); + } +} +void +joystick_button_up(int instance_id, uint8_t button) +{ + printf("joystick_button_up(%d, %d)\n", instance_id, button); + struct joystick_info *joy = find_joystick_controller(instance_id); + if (joy != NULL) { + joy->button_mask |= button_map[button]; + } } -bool handle_latch(bool latch, bool clock) +static void +do_shift() { - if (latch){ - clock_count = 0; - //set writing flag to true to signal we will start writing controller data - writing = true; - old_clock = clock; - for (int i = 0; i < NUM_JOYSTICKS; i++) { - //get the 16-representation to put to the VIA - joystick_state[i] = get_joystick_state(joystick[i], joy_mode[i]); - //preload the first bit onto VIA - joystick_data[i] = (joy_mode[i] != NONE) ? (joystick_state[i] & 1) : 1; - joystick_state[i] = joystick_state[i] >> 1; + for (int i = 0; i < NUM_JOYSTICKS; ++i) { + if (Joystick_slots[i] >= 0) { + struct joystick_info *joy = find_joystick_controller(Joystick_slots[i]); + if (joy != NULL) { + Joystick_data |= ((joy->shift_mask & 1) ? (0x80 >> i) : 0); + joy->shift_mask >>= 1; + } else { + Joystick_data |= 0x80 >> i; + } + } else { + Joystick_data |= 0x80 >> i; } } +} - return latch; +void +joystick_set_latch(bool value) +{ + Joystick_latch = value; + if (value) { + for (int i = 0; i < Num_joystick_controllers; ++i) { + if (Joystick_controllers[i].instance_id != -1) { + Joystick_controllers[i].shift_mask = Joystick_controllers[i].button_mask | 0xF000; + } + } + do_shift(); + } } -//get current state from SDL controller -//Should replace this with SDL events, so we do not miss inputs when polling -uint16_t get_joystick_state(SDL_GameController *control, enum joy_status mode) +void +joystick_set_clock(bool value) { - if (mode == NES) { - bool a_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_A); - bool b_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_X); - bool select_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_BACK); - bool start_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_START); - bool up_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_DPAD_UP); - bool down_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_DPAD_DOWN); - bool left_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_DPAD_LEFT); - bool right_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); - - return - (!a_pressed) | - (!b_pressed) << 1 | - (!select_pressed) << 2 | - (!start_pressed) << 3 | - (!up_pressed) << 4 | - (!down_pressed) << 5 | - (!left_pressed) << 6 | - (!right_pressed) << 7 | - 0x0000; - } - if (mode == SNES) { - bool b_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_A); - bool y_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_X); - bool select_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_BACK); - bool start_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_START); - bool up_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_DPAD_UP); - bool down_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_DPAD_DOWN); - bool left_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_DPAD_LEFT); - bool right_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); - bool a_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_B); - bool x_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_Y); - bool l_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); - bool r_pressed = SDL_GameControllerGetButton(control, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); - - - return - (!b_pressed) | - (!y_pressed) << 1 | - (!select_pressed) << 2 | - (!start_pressed) << 3 | - (!up_pressed) << 4 | - (!down_pressed) << 5 | - (!left_pressed) << 6 | - (!right_pressed) << 7 | - (!a_pressed) << 8 | - (!x_pressed) << 9 | - (!l_pressed) << 10 | - (!r_pressed) << 11 | - 0xF000; - } - - return 0xFFFF; + if (!Joystick_latch && value) { + Joystick_data = 0; + do_shift(); + } } diff --git a/joystick.h b/joystick.h index 9739bf2a..5c1d8592 100644 --- a/joystick.h +++ b/joystick.h @@ -1,42 +1,26 @@ -/**********************************************/ -// File : joystick.h -// Author : John Bliss -// Date : September 27th 2019 -/**********************************************/ +#pragma once +#ifndef JOYSTICK_H +#define JOYSTICK_H -#ifndef _JOYSTICK_H_ -#define _JOYSTICK_H_ - -#include #include -#include -#include -#include "glue.h" -#include "via.h" +#include #define JOY_LATCH_MASK 0x04 -#define JOY_CLK_MASK 0x08 -#define JOY_DATA3_MASK 0x10 -#define JOY_DATA2_MASK 0x20 -#define JOY_DATA1_MASK 0x40 -#define JOY_DATA0_MASK 0x80 +#define JOY_CLK_MASK 0x08 #define NUM_JOYSTICKS 4 -enum joy_status { NONE, NES, SNES }; -extern enum joy_status joy_mode[NUM_JOYSTICKS]; -extern bool joystick_data[NUM_JOYSTICKS]; -extern bool joystick_latch, joystick_clock; - - -bool joystick_init(); //initialize SDL controllers +extern uint8_t Joystick_data; +extern bool Joystick_slots_enabled[NUM_JOYSTICKS]; -void joystick_step(); //do next step for handling joysticks +bool joystick_init(void); //initialize SDL controllers +void joystick_add(int index); +void joystick_remove(int index); -bool handle_latch(bool latch, bool clock); //used internally to check when to - // write to VIA +void joystick_button_down(int instance_id, uint8_t button); +void joystick_button_up(int instance_id, uint8_t button); - //Used to get the 16-bit data needed to send -uint16_t get_joystick_state(SDL_GameController *control, enum joy_status mode); +void joystick_set_latch(bool value); +void joystick_set_clock(bool value); #endif diff --git a/main.c b/main.c index ae122364..51255e42 100644 --- a/main.c +++ b/main.c @@ -387,10 +387,14 @@ usage() printf("-dump {C|R|B|V}...\n"); printf("\tConfigure system dump: (C)PU, (R)AM, (B)anked-RAM, (V)RAM\n"); printf("\tMultiple characters are possible, e.g. -dump CV ; Default: RB\n"); - printf("-joy1 {NES | SNES}\n"); - printf("\tChoose what type of joystick to use, e.g. -joy1 SNES\n"); - printf("-joy2 {NES | SNES}\n"); - printf("\tChoose what type of joystick to use, e.g. -joy2 SNES\n"); + printf("-joy1\n"); + printf("\tEnable binding a gamepad to SNES controller port 1\n"); + printf("-joy2\n"); + printf("\tEnable binding a gamepad to SNES controller port 2\n"); + printf("-joy3\n"); + printf("\tEnable binding a gamepad to SNES controller port 3\n"); + printf("-joy4\n"); + printf("\tEnable binding a gamepad to SNES controller port 4\n"); printf("-sound \n"); printf("\tSet the output device used for audio emulation\n"); printf("\tIf output device is 'none', no audio is generated\n"); @@ -647,51 +651,19 @@ main(int argc, char **argv) } else if (!strcmp(argv[0], "-joy1")) { argc--; argv++; - if (!strcmp(argv[0], "NES")) { - joy_mode[0] = NES; - argc--; - argv++; - } else if (!strcmp(argv[0], "SNES")) { - joy_mode[0] = SNES; - argc--; - argv++; - } + Joystick_slots_enabled[0] = true; } else if (!strcmp(argv[0], "-joy2")){ argc--; argv++; - if (!strcmp(argv[0], "NES")){ - joy_mode[1] = NES; - argc--; - argv++; - } else if (!strcmp(argv[0], "SNES")){ - joy_mode[1] = SNES; - argc--; - argv++; - } - } else if (!strcmp(argv[0], "-joy3")){ + Joystick_slots_enabled[1] = true; + } else if (!strcmp(argv[0], "-joy3")) { argc--; argv++; - if (!strcmp(argv[0], "NES")){ - joy_mode[2] = NES; - argc--; - argv++; - } else if (!strcmp(argv[0], "SNES")){ - joy_mode[2] = SNES; - argc--; - argv++; - } - } else if (!strcmp(argv[0], "-joy4")){ + Joystick_slots_enabled[2] = true; + } else if (!strcmp(argv[0], "-joy4")) { argc--; argv++; - if (!strcmp(argv[0], "NES")){ - joy_mode[3] = NES; - argc--; - argv++; - } else if (!strcmp(argv[0], "SNES")){ - joy_mode[3] = SNES; - argc--; - argv++; - } + Joystick_slots_enabled[3] = true; #ifdef TRACE } else if (!strcmp(argv[0], "-trace")) { argc--; @@ -1015,13 +987,10 @@ emulator_loop(void *param) step6502(); uint8_t clocks = clockticks6502 - old_clockticks6502; bool new_frame = false; + vera_spi_step(clocks); + new_frame |= video_step(MHZ, clocks); for (uint8_t i = 0; i < clocks; i++) { - ps2_step(0); - ps2_step(1); i2c_step(); - joystick_step(); - vera_spi_step(); - new_frame |= video_step(MHZ); } rtc_step(clocks); audio_render(clocks); diff --git a/ps2.c b/ps2.c index 14461440..4cb576e3 100644 --- a/ps2.c +++ b/ps2.c @@ -2,135 +2,130 @@ // Copyright (c) 2019 Michael Steil // All rights reserved. License: 2-clause BSD -#include -#include #include "ps2.h" +#include +#include #define HOLD 25 * 8 /* 25 x ~3 cycles at 8 MHz = 75µs */ #define PS2_BUFFER_SIZE 32 +enum ps2_state { + PS2_READY, + PS2_SEND_LO, + PS2_SEND_HI, +}; + +struct ring_buffer { + int m_oldest; + int m_count; + uint8_t m_elems[PS2_BUFFER_SIZE]; +}; + static struct { - bool sending; - bool has_byte; - uint8_t current_byte; - int bit_index; - int data_bits; - int send_state; - struct - { - uint8_t data[PS2_BUFFER_SIZE]; - uint8_t read; - uint8_t write; - } buffer; + enum ps2_state state; + uint8_t current_byte; + int data_bits; + int send_time; + struct ring_buffer buffer; } state[2]; ps2_port_t ps2_port[2]; -bool -ps2_buffer_can_fit(int i, int n) -{ - // Math is hard. There's certainly a way to do this without a loop. - for (int n2 = 1; n2 < n; n2++) { - if ((state[i].buffer.write + n2) % PS2_BUFFER_SIZE == state[i].buffer.read) { - return false; - } - } - return true; -} - void ps2_buffer_add(int i, uint8_t byte) { - if (!ps2_buffer_can_fit(i, 1)) { + const int index = (state[i].buffer.m_oldest + state[i].buffer.m_count) % PS2_BUFFER_SIZE; + if (state[i].buffer.m_count >= PS2_BUFFER_SIZE) { return; } + ++state[i].buffer.m_count; + state[i].buffer.m_elems[index] = byte; +} - state[i].buffer.data[state[i].buffer.write] = byte; - state[i].buffer.write = (state[i].buffer.write + 1) % PS2_BUFFER_SIZE; +uint8_t +ps2_buffer_pop_oldest(int i) +{ + const uint8_t value = state[i].buffer.m_elems[state[i].buffer.m_oldest]; + state[i].buffer.m_oldest = (state[i].buffer.m_oldest + 1) % PS2_BUFFER_SIZE; + --state[i].buffer.m_count; + return value; } -int -ps2_buffer_remove(int i) +void +ps2_step(int i, int clocks) { - if (state[i].buffer.read == state[i].buffer.write) { - return -1; // empty - } else { - uint8_t byte = state[i].buffer.data[state[i].buffer.read]; - state[i].buffer.read = (state[i].buffer.read + 1) % PS2_BUFFER_SIZE; - return byte; + switch (ps2_port[i].in) { + case PS2_DATA_MASK: + // Communication inhibited + ps2_port[i].out = 0; + state[i].state = PS2_READY; + return; + + case PS2_VIA_MASK: + // Idle + switch (state[i].state) { + case PS2_READY: + CASE_PS2_READY: + // get next byte + if (state[i].data_bits <= 0) { + if (state[i].buffer.m_count <= 0) { + // we have nothing to send + ps2_port[i].out = PS2_CLK_MASK; + return; + } + state[i].current_byte = ps2_buffer_pop_oldest(i); + } + + state[i].data_bits = state[i].current_byte << 1 | (1 - __builtin_parity(state[i].current_byte)) << 9 | (1 << 10); + state[i].send_time = 0; + state[i].state = PS2_SEND_LO; + // Fall-thru + case PS2_SEND_LO: + CASE_PS2_SEND_LO: + ps2_port[i].out = state[i].data_bits & 1; + state[i].send_time += clocks; + if (state[i].send_time >= HOLD) { + state[i].data_bits >>= 1; + state[i].state = PS2_SEND_HI; + state[i].send_time = 0; + clocks -= HOLD; + goto CASE_PS2_SEND_HI; + } + break; + case PS2_SEND_HI: + CASE_PS2_SEND_HI: + ps2_port[i].out = PS2_CLK_MASK; // not ready + state[i].send_time += clocks; + if (state[i].send_time >= HOLD) { + clocks -= HOLD; + if (state[i].data_bits > 0) { + state[i].send_time = 0; + state[i].state = PS2_SEND_LO; + goto CASE_PS2_SEND_LO; + } else { + state[i].state = PS2_READY; + goto CASE_PS2_READY; + } + } + break; + } + break; + default: + ps2_port[i].out = 0; } } void -ps2_step(int i) +ps2_autostep(int i) { - if (!ps2_port[i].clk_in && ps2_port[i].data_in) { // communication inhibited - ps2_port[i].clk_out = 0; - ps2_port[i].data_out = 0; - state[i].sending = false; -// printf("PS2[%d]: STATE: communication inhibited.\n", i); - return; - } else if (ps2_port[i].clk_in && ps2_port[i].data_in) { // idle state -// printf("PS2[%d]: STATE: idle\n", i); - if (!state[i].sending) { - // get next byte - if (!state[i].has_byte) { - int current_byte = ps2_buffer_remove(i); - if (current_byte < 0) { - // we have nothing to send - ps2_port[i].clk_out = 1; - ps2_port[i].data_out = 0; -// printf("PS2[%d]: nothing to send.\n", i); - return; - } - state[i].current_byte = current_byte; -// printf("PS2[%d]: current_byte: %x\n", i, state[i].current_byte); - state[i].has_byte = true; - } + static uint32_t port_clocks[2] = {0, 0}; + extern uint32_t clockticks6502; - state[i].data_bits = state[i].current_byte << 1 | (1 - __builtin_parity(state[i].current_byte)) << 9 | (1 << 10); -// printf("PS2[%d]: data_bits: %x\n", i, state[i].data_bits); - state[i].bit_index = 0; - state[i].send_state = 0; - state[i].sending = true; - } - - if (state[i].send_state <= HOLD) { - ps2_port[i].clk_out = 0; // data ready - ps2_port[i].data_out = state[i].data_bits & 1; -// printf("PS2[%d]: [%d]sending #%d: %x\n", i, state[i].send_state, state[i].bit_index, state[i].data_bits & 1); - if (state[i].send_state == 0 && state[i].bit_index == 10) { - // we have sent the last bit, if the host - // inhibits now, we'll send the next byte - state[i].has_byte = false; - } - if (state[i].send_state == HOLD) { - state[i].data_bits >>= 1; - state[i].bit_index++; - } - state[i].send_state++; - } else if (state[i].send_state <= 2 * HOLD) { -// printf("PS2[%d]: [%d]not ready\n", i, state[i].send_state); - ps2_port[i].clk_out = 1; // not ready - ps2_port[i].data_out = 0; - if (state[i].send_state == 2 * HOLD) { -// printf("XXX bit_index: %d\n", state[i].bit_index); - if (state[i].bit_index < 11) { - state[i].send_state = 0; - } else { - state[i].sending = false; - } - } - if (state[i].send_state) { - state[i].send_state++; - } - } - } else { -// printf("PS2[%d]: Warning: unknown PS/2 bus state: CLK_IN=%d, DATA_IN=%d\n", i, ps2_port[i].clk_in, ps2_port[i].data_in); - ps2_port[i].clk_out = 0; - ps2_port[i].data_out = 0; - } + int clocks = clockticks6502 - port_clocks[i]; + port_clocks[i] = clockticks6502; + + ps2_step(i, clocks); } // fake mouse @@ -150,56 +145,39 @@ static int16_t mouse_diff_y = 0; // byte 2: X Movement // byte 3: Y Movement - static bool mouse_send(int x, int y, int b) { - if (ps2_buffer_can_fit(1, 3)) { + if (PS2_BUFFER_SIZE - state[1].buffer.m_count >= 3) { uint8_t byte0 = - ((y >> 9) & 1) << 5 | - ((x >> 9) & 1) << 4 | - 1 << 3 | - b; - uint8_t byte1 = x; - uint8_t byte2 = y; -// printf("%02X %02X %02X\n", byte0, byte1, byte2); - + ((y >> 9) & 1) << 5 | + ((x >> 9) & 1) << 4 | + 1 << 3 | + b; ps2_buffer_add(1, byte0); - ps2_buffer_add(1, byte1); - ps2_buffer_add(1, byte2); + ps2_buffer_add(1, x); + ps2_buffer_add(1, y); return true; } else { -// printf("buffer full, skipping...\n"); + // printf("buffer full, skipping...\n"); return false; } } void -mouse_send_state() +mouse_send_state(void) { - if (mouse_diff_x > 255) { - mouse_send(255, 0, buttons); - mouse_diff_x -= 255; - } - if (mouse_diff_x < -256) { - mouse_send(-256, 0, buttons); - mouse_diff_x -= -256; - } - if (mouse_diff_y > 255) { - mouse_send(0, 255, buttons); - mouse_diff_y -= 255; - } - if (mouse_diff_y < -256) { - mouse_send(0, -256, buttons); - mouse_diff_y -= -256; - } - if (mouse_send(mouse_diff_x, mouse_diff_y, buttons)) { - mouse_diff_x = 0; - mouse_diff_y = 0; - } -} + do { + int send_diff_x = mouse_diff_x > 255 ? 255 : (mouse_diff_x < -256 ? -256 : mouse_diff_x); + int send_diff_y = mouse_diff_y > 255 ? 255 : (mouse_diff_y < -256 ? -256 : mouse_diff_y); + + mouse_send(send_diff_x, send_diff_y, buttons); + mouse_diff_x -= send_diff_x; + mouse_diff_y -= send_diff_y; + } while (mouse_diff_x != 0 && mouse_diff_y != 0); +} void mouse_button_down(int num) @@ -225,4 +203,3 @@ mouse_read(uint8_t reg) { return 0xff; } - diff --git a/ps2.h b/ps2.h index aa5acfe9..23300453 100644 --- a/ps2.h +++ b/ps2.h @@ -2,32 +2,31 @@ // Copyright (c) 2019 Michael Steil // All rights reserved. License: 2-clause BSD -#ifndef _PS2_H_ -#define _PS2_H_ +#ifndef PS2_H +#define PS2_H #include -#define PS2_DATA_MASK 1 -#define PS2_CLK_MASK 2 +#define PS2_DATA_MASK (0x01) +#define PS2_CLK_MASK (0x02) +#define PS2_VIA_MASK (0x03) typedef struct { - int clk_out; - int data_out; - int clk_in; - int data_in; + uint8_t out; + uint8_t in; } ps2_port_t; extern ps2_port_t ps2_port[2]; -bool ps2_buffer_can_fit(int i, int n); void ps2_buffer_add(int i, uint8_t byte); -void ps2_step(int i); +void ps2_step(int i, int clocks); +void ps2_autostep(int i); -// fake mouse -void mouse_button_down(int num); -void mouse_button_up(int num); -void mouse_move(int x, int y); -void mouse_send_state(); + // fake mouse +void mouse_button_down(int num); +void mouse_button_up(int num); +void mouse_move(int x, int y); uint8_t mouse_read(uint8_t reg); +void mouse_send_state(void); #endif diff --git a/rom_symbols.sh b/rom_symbols.sh old mode 100755 new mode 100644 diff --git a/symbolize.sh b/symbolize.sh old mode 100755 new mode 100644 diff --git a/vera_spi.c b/vera_spi.c index b76b2311..22ce076c 100644 --- a/vera_spi.c +++ b/vera_spi.c @@ -22,11 +22,11 @@ vera_spi_init() } void -vera_spi_step() +vera_spi_step(int clocks) { if (busy) { - outcounter++; - if (outcounter == 8) { + outcounter += clocks; + if (outcounter >= 8) { busy = false; if (sdcard_attached) { received_byte = sdcard_handle(sending_byte); diff --git a/vera_spi.h b/vera_spi.h index 8c675459..661cc58b 100644 --- a/vera_spi.h +++ b/vera_spi.h @@ -5,6 +5,6 @@ #include void vera_spi_init(); -void vera_spi_step(); +void vera_spi_step(int clocks); uint8_t vera_spi_read(uint8_t address); void vera_spi_write(uint8_t address, uint8_t value); diff --git a/via.c b/via.c index 9e824ebb..2b338927 100644 --- a/via.c +++ b/via.c @@ -2,14 +2,15 @@ // Copyright (c) 2019 Michael Steil // All rights reserved. License: 2-clause BSD -#include -#include -#include -#include #include "via.h" #include "ps2.h" #include "i2c.h" #include "memory.h" +#include "ps2.h" +#include +#include +#include +#include //XXX #include "glue.h" #include "joystick.h" @@ -18,12 +19,19 @@ // VIA#1 // // PA0: PS2KDAT PS/2 DATA keyboard +// PB0-2 ROM bank // PA1: PS2KCLK PS/2 CLK keyboard +// PB3 IECATT0 // PA2: NESLATCH NES LATCH (for all controllers) +// PB4 IECCLK0 // PA3: NESCLK NES CLK (for all controllers) +// PB5 IECDAT0 // PA4: NESDAT3 NES DATA (controller 3) +// PB6 IECCLK // PA5: NESDAT2 NES DATA (controller 2) +// PB7 IECDAT // PA6: NESDAT1 NES DATA (controller 1) +// CB1 IECSRQ // PA7: NESDAT0 NES DATA (controller 0) // PB0: PS2MDAT PS/2 DATA mouse // PB1: PS2MCLK PS/2 CLK mouse @@ -37,59 +45,55 @@ static uint8_t via1registers[16]; -void -via1_init() +void via1_init() { srand(time(NULL)); i2c_port.clk_in = 1; } -uint8_t -via1_read(uint8_t reg) +uint8_t via1_read(uint8_t reg) { // DDR=0 (input) -> take input bit // DDR=1 (output) -> take output bit - if (reg == 0) { // PB - uint8_t value = - (via1registers[2] & I2C_DATA_MASK ? 0 : i2c_port.data_out << 2) | - (via1registers[2] & PS2_CLK_MASK ? 0 : ps2_port[1].clk_out << 1) | - (via1registers[2] & PS2_DATA_MASK ? 0 : ps2_port[1].data_out); - return value; - } else if (reg == 1) { // PA - uint8_t value = - (via1registers[3] & PS2_CLK_MASK ? 0 : ps2_port[0].clk_out << 1) | - (via1registers[3] & PS2_DATA_MASK ? 0 : ps2_port[0].data_out); - value = value | - (joystick_data[0] ? JOY_DATA0_MASK : 0) | - (joystick_data[1] ? JOY_DATA1_MASK : 0) | - (joystick_data[2] ? JOY_DATA2_MASK : 0) | - (joystick_data[3] ? JOY_DATA3_MASK : 0); - return value; - } else if (reg == 4 || reg == 5 || reg == 8 || reg == 9) { // timer - // timer A and B: return random numbers for RND(0) - // XXX TODO: these should be real timers :) - return rand() & 0xff; - } else { - return via1registers[reg]; + switch (reg) { + case 0: // PB + ps2_autostep(1); + return (~via1registers[2] & ps2_port[1].out) | + (via1registers[2] & I2C_DATA_MASK ? 0 : i2c_port.data_out << 2); + + case 1: // PA + ps2_autostep(0); + return (~via1registers[3] & ps2_port[0].out) | Joystick_data; + + case 4: // timer + case 5: // timer + case 8: // timer + case 9: // timer + // timer A and B: return random numbers for RND(0) + // XXX TODO: these should be real timers :) + return rand() & 0xff; + + default: + return via1registers[reg]; } } -void -via1_write(uint8_t reg, uint8_t value) +void via1_write(uint8_t reg, uint8_t value) { via1registers[reg] = value; - if (reg == 0 || reg == 2) { + ps2_autostep(1); // PB + const uint8_t pb = via1registers[0] | ~via1registers[2]; + ps2_port[1].in = pb & PS2_VIA_MASK; i2c_port.data_in = via1registers[2] & I2C_DATA_MASK ? via1registers[0] & I2C_DATA_MASK : 1; - ps2_port[1].clk_in = via1registers[2] & PS2_CLK_MASK ? via1registers[0] & PS2_CLK_MASK : 1; - ps2_port[1].data_in = via1registers[2] & PS2_DATA_MASK ? via1registers[0] & PS2_DATA_MASK : 1; } else if (reg == 1 || reg == 3) { + ps2_autostep(0); // PA - ps2_port[0].clk_in = via1registers[3] & PS2_CLK_MASK ? via1registers[1] & PS2_CLK_MASK : 1; - ps2_port[0].data_in = via1registers[3] & PS2_DATA_MASK ? via1registers[1] & PS2_DATA_MASK : 1; - joystick_latch = via1registers[1] & JOY_LATCH_MASK; - joystick_clock = via1registers[1] & JOY_CLK_MASK; + const uint8_t pa = via1registers[1] | ~via1registers[3]; + ps2_port[0].in = pa & PS2_VIA_MASK; + joystick_set_latch(via1registers[1] & JOY_LATCH_MASK); + joystick_set_clock(via1registers[1] & JOY_CLK_MASK); } else if (reg == 12) { switch (value >> 5) { case 6: // %110xxxxx @@ -109,20 +113,16 @@ via1_write(uint8_t reg, uint8_t value) static uint8_t via2registers[16]; -void -via2_init() +void via2_init() { } -uint8_t -via2_read(uint8_t reg) +uint8_t via2_read(uint8_t reg) { return via2registers[reg]; } -void -via2_write(uint8_t reg, uint8_t value) +void via2_write(uint8_t reg, uint8_t value) { via2registers[reg] = value; } - diff --git a/via.h b/via.h index 5ee4a0b1..e4570d77 100644 --- a/via.h +++ b/via.h @@ -2,17 +2,17 @@ // Copyright (c) 2019 Michael Steil // All rights reserved. License: 2-clause BSD -#ifndef _VIA_H_ -#define _VIA_H_ +#ifndef VIA_H +#define VIA_H #include -void via1_init(); +void via1_init(); uint8_t via1_read(uint8_t reg); -void via1_write(uint8_t reg, uint8_t value); -uint8_t via2_read(uint8_t reg); -void via2_write(uint8_t reg, uint8_t value); +void via1_write(uint8_t reg, uint8_t value); -void via2_init(); +void via2_init(); +uint8_t via2_read(uint8_t reg); +void via2_write(uint8_t reg, uint8_t value); #endif diff --git a/video.c b/video.c index 94dde05d..0af5ba9c 100644 --- a/video.c +++ b/video.c @@ -10,6 +10,7 @@ #include "debugger.h" #include "keyboard.h" #include "gif.h" +#include "joystick.h" #include "vera_spi.h" #include "vera_psg.h" #include "vera_pcm.h" @@ -990,12 +991,12 @@ render_line(uint16_t y) } bool -video_step(float mhz) +video_step(float mhz, float steps) { uint8_t out_mode = reg_composer[0] & 3; bool new_frame = false; - float advance = ((out_mode & 2) ? NTSC_PIXEL_FREQ : VGA_PIXEL_FREQ) / mhz; + float advance = ((out_mode & 2) ? NTSC_PIXEL_FREQ : VGA_PIXEL_FREQ) * steps / mhz; scan_pos_x += advance; if (scan_pos_x > SCAN_WIDTH) { scan_pos_x -= SCAN_WIDTH; @@ -1183,6 +1184,20 @@ video_update() mouse_y = event.motion.y; mouse_changed = true; } + + if (event.type == SDL_JOYDEVICEADDED) { + joystick_add(event.jdevice.which); + } + if (event.type == SDL_JOYDEVICEREMOVED) { + joystick_remove(event.jdevice.which); + } + if (event.type == SDL_CONTROLLERBUTTONDOWN) { + joystick_button_down(event.cbutton.which, event.cbutton.button); + } + if (event.type == SDL_CONTROLLERBUTTONUP) { + joystick_button_up(event.cbutton.which, event.cbutton.button); + } + } if (mouse_changed) { mouse_send_state(); diff --git a/video.h b/video.h index 232f1ef6..2d152dde 100644 --- a/video.h +++ b/video.h @@ -13,7 +13,7 @@ bool video_init(int window_scale, char *quality); void video_reset(void); -bool video_step(float mhz); +bool video_step(float mhz, float steps); bool video_update(void); void video_end(void); bool video_get_irq_out(void); diff --git a/webassembly/jszip.min.js b/webassembly/jszip.min.js old mode 100755 new mode 100644