// Commander X16 Emulator // Copyright (c) 2019 Michael Steil // All rights reserved. License: 2-clause BSD #include #include #include "sdcard.h" #include "vera_spi.h" #define SPI_CLOCK_RATE_MHZ 12.5f bool ss; bool busy; bool autotx; uint8_t sending_byte, received_byte; float outcounter; void vera_spi_init() { ss = false; busy = false; autotx = false; received_byte = 0xff; } void vera_spi_step(int mhz, int clocks) { if (busy) { outcounter += (float)clocks * SPI_CLOCK_RATE_MHZ / (float)mhz; if (outcounter >= 10) { // A value of 9 here is closer to reality, but hardware // can take slightly longer depending on clock-alignment. // 10 cycles here should be safe and won't succeed in emulation // while failing on hardware. busy = false; if (sdcard_attached) { received_byte = sdcard_handle(sending_byte); } else { received_byte = 0xff; } } } } uint8_t vera_spi_read(uint8_t reg) { switch (reg) { case 0: if (autotx && ss && !busy) { // autotx mode will automatically send $FF after each read sending_byte = 0xff; busy = true; outcounter = 0; } return received_byte; case 1: return busy << 7 | autotx << 2 | ss; } return 0; } // The same registers, without the side effect. Reading $9F3E normally starts // the next autotx transfer; a debugger showing memory must be able to look at // it without taking a byte out of the transfer the machine is in the middle of. uint8_t vera_spi_peek(uint8_t reg) { switch (reg) { case 0: return received_byte; case 1: return busy << 7 | autotx << 2 | ss; } return 0; } void vera_spi_write(uint8_t reg, uint8_t value) { switch (reg) { case 0: if (ss && !busy) { sending_byte = value; busy = true; outcounter = 0; } break; case 1: if (ss != (value & 1)) { ss = value & 1; if (ss) { sdcard_select(ss); } } autotx = !!(value & 4); break; } } // Side-effect-free snapshot of SPI state for the ImGui debugger. void vera_spi_debug_get_state(vera_spi_debug_state_t *out) { out->ss = ss; out->busy = busy; out->autotx = autotx; out->sending_byte = sending_byte; out->received_byte = received_byte; out->outcounter = outcounter; }