diff --git a/Src/Model3/DriveBoard.cpp b/Src/Model3/DriveBoard.cpp new file mode 100644 index 0000000..6281a6f --- /dev/null +++ b/Src/Model3/DriveBoard.cpp @@ -0,0 +1,684 @@ +#include "Supermodel.h" + +#include +#include + +bool CDriveBoard::IsAttached(void) +{ + return m_attached; +} + +bool CDriveBoard::IsSimulated(void) +{ + return m_simulated; +} + +void CDriveBoard::GetDIPSwitches(UINT8 &dip1, UINT8 &dip2) +{ + dip1 = m_dip1; + dip2 = m_dip2; +} + +void CDriveBoard::SetDIPSwitches(UINT8 dip1, UINT8 dip2) +{ + m_dip1 = dip1; + m_dip2 = dip2; +} + +unsigned CDriveBoard::GetSteeringStrength() +{ + return ((~(m_dip1>>2))&7) + 1; +} + +void CDriveBoard::SetSteeringStrength(unsigned steeringStrength) +{ + m_dip1 = (m_dip1&0xE3) | (((~(steeringStrength - 1))&7)<<2); +} + +void CDriveBoard::Get7SegDisplays(UINT8 &seg1Digit1, UINT8 &seg1Digit2, UINT8 &seg2Digit1, UINT8 &seg2Digit2) +{ + seg1Digit1 = m_seg1Digit1; + seg1Digit2 = m_seg1Digit2; + seg2Digit1 = m_seg2Digit1; + seg2Digit2 = m_seg2Digit2; +} + +CZ80 *CDriveBoard::GetZ80(void) +{ + return &m_z80; +} + +void CDriveBoard::SaveState(CBlockFile *SaveState) +{ + SaveState->NewBlock("DriveBoard", __FILE__); + + // Overall config + SaveState->Write(&m_simulated, sizeof(m_simulated)); + + // DIP switches and digit displays + SaveState->Write(&m_dip1, sizeof(m_dip1)); + SaveState->Write(&m_dip2, sizeof(m_dip2)); + //SaveState->Write(&m_seg1Digit1, sizeof(m_seg1Digit1)); // No point in saving these + //SaveState->Write(&m_seg1Digit2, sizeof(m_seg1Digit2)); + //SaveState->Write(&m_seg2Digit1, sizeof(m_seg2Digit1)); + //SaveState->Write(&m_seg2Digit2, sizeof(m_seg2Digit2)); + + // RAM + SaveState->Write(&m_ram, sizeof(m_ram)); + + // Board emulation state + SaveState->Write(&m_initialized, sizeof(m_initialized)); + SaveState->Write(&m_allowInterrupts, sizeof(m_allowInterrupts)); + SaveState->Write(&m_dataSent, sizeof(m_dataSent)); + SaveState->Write(&m_dataReceived, sizeof(m_dataReceived)); + SaveState->Write(&m_adcPortRead, sizeof(m_adcPortRead)); + SaveState->Write(&m_adcPortBit, sizeof(m_adcPortBit)); + SaveState->Write(&m_uncenterVal1, sizeof(m_uncenterVal1)); + SaveState->Write(&m_uncenterVal2, sizeof(m_uncenterVal2)); + + // TODO - board simulation state + + // CPU + m_z80.SaveState(SaveState, "DriveBoard Z80"); +} + +void CDriveBoard::LoadState(CBlockFile *SaveState) +{ + if (SaveState->FindBlock("DriveBoard") != OKAY) + { + ErrorLog("Unable to load DriveBoard state. Save state file is corrupted."); + return; + } + + SaveState->Read(&m_simulated, sizeof(m_simulated)); + + SaveState->Read(&m_dip1, sizeof(m_dip1)); + SaveState->Read(&m_dip2, sizeof(m_dip2)); + //SaveState->Read(&m_seg1Digit1, sizeof(m_seg1Digit1)); + //SaveState->Read(&m_seg1Digit2, sizeof(m_seg1Digit2)); + //SaveState->Read(&m_seg2Digit1, sizeof(m_seg2Digit1)); + //SaveState->Read(&m_seg2Digit2, sizeof(m_seg2Digit2)); + + SaveState->Read(&m_ram, sizeof(m_ram)); + + SaveState->Read(&m_initialized, sizeof(m_initialized)); + SaveState->Read(&m_allowInterrupts, sizeof(m_allowInterrupts)); + SaveState->Read(&m_dataSent, sizeof(m_dataSent)); + SaveState->Read(&m_dataReceived, sizeof(m_dataReceived)); + SaveState->Read(&m_adcPortRead, sizeof(m_adcPortRead)); + SaveState->Read(&m_adcPortBit, sizeof(m_adcPortBit)); + SaveState->Read(&m_uncenterVal1, sizeof(m_uncenterVal1)); + SaveState->Read(&m_uncenterVal2, sizeof(m_uncenterVal2)); + + m_z80.LoadState(SaveState, "DriveBoard Z80"); + + SendStopAll(); +} + +BOOL CDriveBoard::Init(const UINT8 *romPtr) +{ + // Assign ROM + m_rom = romPtr; + + // Check have a valid ROM and force feedback is enabled + m_attached = m_rom && g_Config.enableFFeedback; + if (!m_attached) + return OKAY; + + // Allocate memory for RAM + m_ram = new (std::nothrow) UINT8[RAM_SIZE]; + if (NULL == m_ram) + { + float ramSizeMB = (float)RAM_SIZE/(float)0x100000; + return ErrorLog("Insufficient memoy for drive board (needs %1.1f MB).", ramSizeMB); + } + memset(m_ram, 0, RAM_SIZE); + + // Configure options + m_simulated = g_Config.simulateDrvBoard; + SetSteeringStrength(g_Config.steeringStrength); + + // Initialize Z80 + m_z80.Init(this, NULL); + + return OKAY; +} + +void CDriveBoard::AttachInputs(CInputs *InputsPtr, unsigned gameInputFlags) +{ + m_inputs = InputsPtr; + m_inputFlags = gameInputFlags; + + DebugLog("DriveBoard attached inputs\n"); +} + +void CDriveBoard::Reset(void) +{ + m_initialized = false; + m_allowInterrupts = false; + + m_seg1Digit1 = 0xFF; + m_seg1Digit2 = 0xFF; + m_seg2Digit1 = 0xFF; + m_seg2Digit2 = 0xFF; + + m_dataSent = 0; + m_dataReceived = 0; + m_adcPortRead = 0; + m_adcPortBit = 0; + m_port42Out = 0; + m_port46Out = 0; + m_prev42Out = 0; + m_prev46Out = 0; + + m_initState = 0; + m_boardMode = 0; + m_readMode = 0; + m_wheelCenter = 0x80; + m_uncenterVal1 = 0; + m_uncenterVal2 = 0; + + m_lastConstForce = 0; + m_lastSelfCenter = 0; + m_lastFriction = 0; + m_lastVibrate = 0; + + if (!m_simulated) + m_z80.Reset(); +} + +UINT8 CDriveBoard::Read(void) +{ + // TODO - simulate initialization sequence even when emulating to get rid of long pause at boot up (drive board can + // carry on booting whilst game starts) + if (m_simulated) + return SimulateRead(); + else + return m_dataReceived; +} + +void CDriveBoard::Write(UINT8 data) +{ + if (data >= 0x01 && data <= 0x0F || + data >= 0x20 && data <= 0x2F || + data >= 0x30 && data <= 0x3F || + data >= 0x40 && data <= 0x4F || + data >= 0x70 && data <= 0x7F) + printf("DriveBoard.Write(%02X)\n", data); + if (m_simulated) + SimulateWrite(data); + else + { + m_dataSent = data; + if (data == 0xCB) + m_initialized = false; + } +} + +UINT8 CDriveBoard::SimulateRead(void) +{ + if (m_initialized) + { + switch (m_readMode) + { + case 0x0: return m_statusFlags; // Status flags + case 0x1: return m_dip1; // DIP switch 1 value + case 0x2: return m_dip2; // DIP switch 2 value + case 0x3: return m_wheelCenter; // Wheel center + case 0x4: return 0x80; // Cockpit banking center + case 0x5: return m_inputs->steering->value; // Wheel position + case 0x6: return 0x80; // Cockpit banking position + case 0x7: return m_echoVal; // Init status/echo test + default: return 0xFF; + } + } + else + { + switch (m_initState / 5) + { + case 0: return 0xCF; // Initiate start + case 1: return 0xCE; + case 2: return 0xCD; + case 3: return 0xCC; // Centering wheel + default: + m_initialized = true; + return 0x80; + } + } +} + +void CDriveBoard::SimulateWrite(UINT8 cmd) +{ + // Following are commands for Scud Race. Daytona 2 has a compatible command set while Sega Rally 2 is completely different + // TODO - finish for Scud Race and Daytona 2 + // TODO - implement for Sega Rally 2 + UINT8 type = cmd>>4; + UINT8 val = cmd&0xF; + switch (type) + { + case 0: // 0x00-0F Play sequence + /* TODO */ + break; + case 1: // 0x10-1F Set centering strength + if (val == 0) + // Disable auto-centering + // TODO - is 0x10 for disable? + SendSelfCenter(0); + else + // Enable auto-centering (0x1 = weakest, 0xF = strongest) + SendSelfCenter(val * 0x11); + break; + case 2: // 0x20-2F Friction strength + if (val == 0) + // Disable friction + // TODO - is 0x20 for disable? + SendFriction(0); + else + // Enable friction (0x1 = weakest, 0xF = strongest) + SendFriction(val * 0x11); + break; + case 3: // 0x30-3F Uncentering (vibrate) + if (val == 0) + // Disable uncentering + SendVibrate(0); + else + // Enable uncentering (0x1 = weakest, 0xF = strongest) + SendVibrate(val * 0x11); + break; + case 4: // 0x40-4F Play power-slide sequence + /* TODO */ + break; + case 5: // 0x50-5F Rotate wheel right + SendConstantForce((val + 1) * 0x5); + break; + case 6: // 0x60-6F Rotate wheel left + SendConstantForce(-(val + 1) * 0x5); + break; + case 7: // 0x70-7F Set steering parameters + /* TODO */ + break; + case 8: // 0x80-8F Test Mode + switch (val&0x7) + { + case 0: SendStopAll(); break; // 0x80 Stop motor + case 1: SendConstantForce(20); break; // 0x81 Roll wheel right + case 2: SendConstantForce(-20); break; // 0x82 Roll wheel left + case 3: /* Ignore - no clutch */ break; // 0x83 Clutch on + case 4: /* Ignore - no clutch */ break; // 0x84 Clutch off + case 5: m_wheelCenter = m_inputs->steering->value; break; // 0x85 Set wheel center position + case 6: /* Ignore */ break; // 0x86 Set cockpit banking position + case 7: /* Ignore */ break; // 0x87 Lamp on/off + } + case 0x9: // 0x90-9F ??? Don't appear to have any effect with Scud Race ROM + /* TODO */ + break; + case 0xA: // 0xA0-AF ??? Don't appear to have any effect with Scud Race ROM + /* TODO */ + break; + case 0xB: // 0xB0-BF Invalid command (reserved for use by PPC to send cabinet type 0xB0 or 0xB1 during initialization) + /* Ignore */ + break; + case 0xC: // 0xC0-CF Set board mode (0xCB = reset board) + SendStopAll(); + if (val >= 0xB) + { + // Reset board + m_initialized = false; + m_initState = 0; + } + else + m_boardMode = val; + break; + case 0xD: // 0xD0-DF Set read mode + m_readMode = val&0x7; + break; + case 0xE: // 0xE0-EF Invalid command + /* Ignore */ + break; + case 0xF: // 0xF0-FF Echo test + m_echoVal = val; + break; + } +} + +void CDriveBoard::RunFrame(void) +{ + if (m_simulated) + SimulateFrame(); + else + EmulateFrame(); +} + +void CDriveBoard::SimulateFrame(void) +{ + if (!m_initialized) + m_initState++; + // TODO - update m_statusFlags and play preset scripts according to board mode +} + +void CDriveBoard::EmulateFrame(void) +{ + // Assuming Z80 runs @ 4.0MHz and NMI triggers @ 60.0KHz + // TODO - find out if Z80 frequency is correct and exact frequency of NMI interrupts (just guesswork at the moment!) + int cycles = 4.0 * 1000000 / 60; + int loopCycles = 10000; + while (cycles > 0) + { + if (m_allowInterrupts) + m_z80.TriggerNMI(); + cycles -= m_z80.Run(min(loopCycles, cycles)); + } +} + +UINT8 CDriveBoard::Read8(UINT32 addr) +{ + if (addr < 0x9000) // ROM is 0x0000-0x8FFF + return m_rom[addr]; + else if (addr >= 0xE000) // RAM is 0xE000-0xFFFF + return m_ram[addr-0xE000]; + else + { + printf("Unhandled Z80 read of %08X (at PC = %04X)\n", addr, m_z80.GetPC()); + return 0xFF; + } +} + +void CDriveBoard::Write8(UINT32 addr, UINT8 data) +{ + if (addr >= 0xE000) // ROM is 0x0000-0x8FFF + m_ram[addr-0xE000] = data; + else + printf("Unhandled Z80 write to %08X (at PC = %04X)\n", addr, m_z80.GetPC()); +} + +UINT8 CDriveBoard::IORead8(UINT32 portNum) +{ + UINT8 adcVal; + switch (portNum) + { + case 32: // DIP 1 value + return m_dip1; + case 33: // DIP 2 value + return m_dip2; + case 36: // ADC channel 1 - not connected + case 37: // ADC channel 2 - steering wheel position (0x00 = full left, 0x80 = center, 0xFF = full right) + case 38: // ADC channel 3 - cockpit bank position (deluxe cabinets) (0x00 = full left, 0x80 = center, 0xFF = full right) + case 39: // ADC channel 4 - not connected + if (portNum == m_adcPortRead && m_adcPortBit-- > 0) + { + switch (portNum) + { + case 36: // Not connected + adcVal = 0x00; + break; + case 37: // Steering wheel for twin racing cabinets - TODO - check actual range of steering, suspect it is not really 0x00-0xFF + if (m_initialized) + adcVal = (UINT8)m_inputs->steering->value; + else + adcVal = 0x80; // If not initialized, return 0x80 so that wheel centering test does not fail + break; + case 38: // Cockpit bank position for deluxe racing cabinets + adcVal = 0x80; + break; + case 39: // Not connected + adcVal = 0x00; + break; + default: + printf("Unhandled Z80 input on ADC port %u (at PC = %04X)\n", portNum, m_z80.GetPC()); + return 0xFF; + } + return (adcVal>>m_adcPortBit)&0x01; + } + else + { + printf("Unhandled Z80 input on ADC port %u (at PC = %04X)\n", portNum, m_z80.GetPC()); + return 0xFF; + } + case 40: // PPC command + return m_dataSent; + case 44: // Encoder error reporting (kept at 0x00 for no error) + // Bits 0 & 1 clear = no error + // Bit 1 set = + // Bit 2 set = + // Bit 3 set = + return 0x00; + default: + printf("Unhandled Z80 input on port %u (at PC = %04X)\n", portNum, m_z80.GetPC()); + return 0xFF; + } +} + +void CDriveBoard::IOWrite8(UINT32 portNum, UINT8 data) +{ + switch (portNum) + { + case 16: // Unsure? - single byte 0x03 sent at initialization, then occasionally writes 0x07 & 0xFA to port + return; + case 17: // Interrupt control + if (data == 0x57) + m_allowInterrupts = true; + else if (data == 0x53) // Strictly speaking 0x53 then 0x04 + m_allowInterrupts = false; + return; + case 28: // Unsure? - two bytes 0xFF, 0xFF sent at initialization only + case 29: // Unsure? - two bytes 0x0F, 0x17 sent at initialization only + case 30: // Unsure? - same as port 28 + case 31: // Unsure? - same as port 31 + return; + case 32: // Left digit of 7-segment display 1 + m_seg1Digit1 = data; + return; + case 33: // Right digit of 7-segment display 1 + m_seg1Digit2 = data; + return; + case 34: // Left digit of 7-segment display 2 + m_seg2Digit1 = data; + return; + case 35: // Right digit of 7-segment display 2 + m_seg2Digit2 = data; + return; + case 36: // ADC channel 1 control + case 37: // ADC channel 2 control + case 38: // ADC channel 3 control + case 39: // ADC channel 4 control + m_adcPortRead = portNum; + m_adcPortBit = 8; + return; + case 41: // Reply for PPC + m_dataReceived = data; + if (data == 0xCC) + m_initialized = true; + return; + case 42: // Encoder motor data + m_port42Out = data; + ProcessEncoderCmd(); + return; + case 45: // Clutch/lamp control (deluxe cabinets) + return; + case 46: // Encoder motor control + m_port46Out = data; + return; + case 240: // Unsure? - single byte 0xBB sent at initialization only + return; + case 241: // Unsure? - single byte 0x4E sent regularly - some sort of watchdog? + return; + default: + printf("Unhandled Z80 output on port %u (at PC = %04X)\n", portNum, m_z80.GetPC()); + return; + } +} + +void CDriveBoard::ProcessEncoderCmd(void) +{ + if (m_prev42Out != m_port42Out || m_prev46Out != m_port46Out) + { + printf("46 [%02X] / 42 [%02X]\n", m_port46Out, m_port42Out); + switch (m_port46Out) + { + case 0xFB: + // Friction? Sent during power slide + SendFriction(m_port42Out); + break; + + case 0xFC: + // Centering / uncentering (vibrate) + // Bit 2 = on for centering, off for uncentering + if (m_port42Out&0x04) + { + // Centering + // Bit 7 = on for disable, off for enable + if (m_port42Out&0x80) + { + // Disable centering + SendSelfCenter(0); + } + else + { + // Bits 3-6 = centering strength 0x0-0xF. This is scaled to range 0x0F-0xFF + UINT8 strength = ((m_port42Out&0x78)>>3) * 0x10 + 0xF; + SendSelfCenter(strength); + } + } + else + { + // Uncentering + // Bits 0-1 = data sequence number 0-3 + UINT8 seqNum = m_port42Out&0x03; + // Bits 4-7 = data values + UINT16 data = (m_port42Out&0xF0)>>4; + switch (seqNum) + { + case 0: m_uncenterVal1 = data<<4; break; + case 1: m_uncenterVal1 |= data; break; + case 2: m_uncenterVal2 = data<<4; break; + case 3: m_uncenterVal2 |= data; break; + } + if (seqNum == 3) + { + if (m_uncenterVal1 == 0) + { + // Disable uncentering + SendVibrate(0); + } + else + { + // Uncentering - unsure exactly how values sent map to strength or whether they specify some other attributes of effect + // For now just attempting to map them to a sensible value in range 0x00-0xFF + UINT8 strength = ((m_uncenterVal1>>1) - 7) * 0x50 + ((m_uncenterVal2>>1) - 5) * 0x10 + 0xF; + SendVibrate(strength); + } + } + } + break; + + case 0xFD: + // Unsure? Sent as velocity changes, similar to self-centering + break; + + case 0xFE: + // Apply constant force to wheel + // Value is: 0x80 = stop motor, 0x80-0xC0 = roll wheel left, 0x40-0x80 = roll wheel right, scale to range -0xFF-0xFF + SendConstantForce((0x80 - m_port42Out) * 4); + break; + + case 0xFF: + // Stop all effects + if (m_port42Out == 0) + SendStopAll(); + break; + + default: + printf("Unknown = 46 [%02X] / 42 [%02X]\n", m_port46Out, m_port42Out); + break; + } + + m_prev42Out = m_port42Out; + m_prev46Out = m_port46Out; + } +} + +void CDriveBoard::SendStopAll(void) +{ + printf(">> Stop All Effects\n"); + + ForceFeedbackCmd ffCmd; + ffCmd.id = FFStop; + m_inputs->steering->SendForceFeedbackCmd(ffCmd); +} + +void CDriveBoard::SendConstantForce(INT8 val) +{ + if (val == m_lastConstForce) + return; + if (val >= 0) + printf(">> Constant Force Right %02X\n", val); + else + printf(">> Constant Force Left %02X\n", -val); + + ForceFeedbackCmd ffCmd; + ffCmd.id = FFConstantForce; + ffCmd.force = (float)val / (val >= 0 ? 127.0f : 128.0f); + m_inputs->steering->SendForceFeedbackCmd(ffCmd); + + m_lastConstForce = val; +} + +void CDriveBoard::SendSelfCenter(UINT8 val) +{ + if (val == m_lastSelfCenter) + return; + printf(">> Self center %02X\n", val); + + ForceFeedbackCmd ffCmd; + ffCmd.id = FFSelfCenter; + ffCmd.force = (float)val / 255.0f; + m_inputs->steering->SendForceFeedbackCmd(ffCmd); + + m_lastSelfCenter = val; +} + +void CDriveBoard::SendFriction(UINT8 val) +{ + if (val == m_lastFriction) + return; + printf(">> Friction %02X\n", val); + + ForceFeedbackCmd ffCmd; + ffCmd.id = FFFriction; + ffCmd.force = (float)val / 255.0f; + m_inputs->steering->SendForceFeedbackCmd(ffCmd); + + m_lastFriction = val; +} + +void CDriveBoard::SendVibrate(UINT8 val) +{ + if (val == m_lastVibrate) + return; + printf(">> Vibrate %02X\n", val); + + ForceFeedbackCmd ffCmd; + ffCmd.id = FFVibrate; + ffCmd.force = (float)val / 255.0f; + m_inputs->steering->SendForceFeedbackCmd(ffCmd); + + m_lastVibrate = val; +} + +CDriveBoard::CDriveBoard() : m_attached(false), m_simulated(false), m_rom(NULL), m_ram(NULL), m_inputs(NULL), m_dip1(0xCF), m_dip2(0xFF) +{ + DebugLog("Built Drive Board\n"); +} + +CDriveBoard::~CDriveBoard(void) +{ + if (m_ram != NULL) + { + delete[] m_ram; + m_ram = NULL; + } + m_rom = NULL; + m_inputs = NULL; + + DebugLog("Destroyed Drive Board\n"); +} diff --git a/Src/Model3/DriveBoard.h b/Src/Model3/DriveBoard.h new file mode 100644 index 0000000..82fdba6 --- /dev/null +++ b/Src/Model3/DriveBoard.h @@ -0,0 +1,153 @@ +#ifndef INCLUDED_DRIVEBOARD_H +#define INCLUDED_DRIVEBOARD_H + +#define RAM_SIZE 0x2000 + +/* + * CDriveBoardConfig: + * + * Settings used by CDriveBoard. + */ +class CDriveBoardConfig +{ +public: + bool enableFFeedback; // Enable drive board emulation/simulation + bool simulateDrvBoard; // Simulate drive board rather than emulating it + unsigned steeringStrength; // Setting for steering strength on DIP switches of drive board + + // Defaults + CDriveBoardConfig(void) + { + enableFFeedback = false; + simulateDrvBoard = false; + steeringStrength = 5; + } +}; + +/* + * CDriveBoard + */ +class CDriveBoard : public CBus +{ +public: + bool IsAttached(void); + + bool IsSimulated(void); + + void GetDIPSwitches(UINT8 &dip1, UINT8 &dip2); + + void SetDIPSwitches(UINT8 dip1, UINT8 dip2); + + unsigned GetSteeringStrength(); + + void SetSteeringStrength(unsigned steeringStrength); + + void Get7SegDisplays(UINT8 &seg1Digit, UINT8 &seg1Digit2, UINT8 &seg2Digit1, UINT8 &seg2Digit2); + + CZ80 *GetZ80(void); + + void SaveState(CBlockFile *SaveState); + + void LoadState(CBlockFile *SaveState); + + BOOL Init(const UINT8 *romPtr); + + void AttachInputs(CInputs *InputsPtr, unsigned gameInputFlags); + + void Reset(void); + + UINT8 Read(void); + + void Write(UINT8 data); + + void RunFrame(void); + + CDriveBoard(); + + ~CDriveBoard(void); + + // CBus methods + UINT8 Read8(UINT32 addr); + + void Write8(UINT32 addr, UINT8 data); + + UINT8 IORead8(UINT32 portNum); + + void IOWrite8(UINT32 portNum, UINT8 data); + +private: + bool m_attached; // True if drive board is attached + bool m_simulated; // True if drive board should be simulated rather than emulated + + UINT8 m_dip1; // Value of DIP switch 1 + UINT8 m_dip2; // Value of DIP switch 2 + + const UINT8* m_rom; // 32k ROM + UINT8* m_ram; // 8k RAM + + CZ80 m_z80; // Z80 CPU @ 4MHz + + CInputs *m_inputs; + unsigned m_inputFlags; + + // Emulation state + bool m_initialized; // True if drive board has finished initialization + bool m_allowInterrupts; // True if drive board has enabled NMI interrupts + + UINT8 m_seg1Digit1; // Current value of left digit on 7-segment display 1 + UINT8 m_seg1Digit2; // Current value of right digit on 7-segment display 1 + UINT8 m_seg2Digit1; // Current value of left digit on 7-segment display 2 + UINT8 m_seg2Digit2; // Current value of right digit on 7-segment display 2 + + UINT8 m_dataSent; // Last command sent by main board + UINT8 m_dataReceived; // Data to send back to main board + + UINT16 m_adcPortRead; // ADC port currently reading from + UINT8 m_adcPortBit; // Bit number currently reading on ADC port + + UINT8 m_port42Out; // Last value sent to Z80 I/O port 42 (encoder motor data) + UINT8 m_port46Out; // Last value sent to Z80 I/O port 46 (encoder motor control) + + UINT8 m_prev42Out; // Previous value sent to Z80 I/O port 42 + UINT8 m_prev46Out; // Previous value sent to Z80 I/O port 46 + + UINT8 m_uncenterVal1; // First part of pending uncenter command + UINT8 m_uncenterVal2; // Second part of pending uncenter command + + // Simulation state + UINT8 m_initState; + UINT8 m_statusFlags; + UINT8 m_boardMode; + UINT8 m_readMode; + UINT8 m_wheelCenter; + UINT8 m_cockpitCenter; + UINT8 m_echoVal; + + // Feedback state + INT8 m_lastConstForce; // Last constant force command sent + UINT8 m_lastSelfCenter; // Last self center command sent + UINT8 m_lastFriction; // Last friction command sent + UINT8 m_lastVibrate; // Last vibrate command sent + + UINT8 SimulateRead(void); + + void SimulateWrite(UINT8 data); + + void SimulateFrame(void); + + void EmulateFrame(void); + + void ProcessEncoderCmd(void); + + void SendStopAll(void); + + void SendConstantForce(INT8 val); + + void SendSelfCenter(UINT8 val); + + void SendFriction(UINT8 val); + + void SendVibrate(UINT8 val); +}; + +#endif // INCLUDED_DRIVEBOARD_H