/** ** Supermodel ** A Sega Model 3 Arcade Emulator. ** Copyright 2011-2017 Bart Trzynadlowski, Nik Henson, Ian Curtis ** ** This file is part of Supermodel. ** ** Supermodel is free software: you can redistribute it and/or modify it under ** the terms of the GNU General Public License as published by the Free ** Software Foundation, either version 3 of the License, or (at your option) ** any later version. ** ** Supermodel is distributed in the hope that it will be useful, but WITHOUT ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ** more details. ** ** You should have received a copy of the GNU General Public License along ** with Supermodel. If not, see . **/ /* * JTAG.cpp * * Model 3's JTAG test access port (TAP). This is accessed through the system * register space and is connected to the Real3D chipset and possibly other * devices. Hence, it is emulated as an independent module. * * It is unclear which exact JTAG standard the device conforms to (and it * probably doesn't matter), so we assume IEEE 1149.1-1990 here. */ #include "Supermodel.h" #include "Model3/JTAG.h" #include // Finite state machine. Each state has two possible next states. const CJTAG::State CJTAG::s_fsm[][2] = { // tms = 0 tms = 1 { RunTestIdle, TestLogicReset }, // 0 Test-Logic/Reset { RunTestIdle, SelectDRScan }, // 1 Run-Test/Idle { CaptureDR, SelectIRScan }, // 2 Select-DR-Scan { ShiftDR, Exit1DR }, // 3 Capture-DR { ShiftDR, Exit1DR }, // 4 Shift-DR { PauseDR, UpdateDR }, // 5 Exit1-DR { PauseDR, Exit2DR }, // 6 Pause-DR { ShiftDR, UpdateDR }, // 7 Exit2-DR { RunTestIdle, SelectDRScan }, // 8 Update-DR { CaptureIR, TestLogicReset }, // 9 Select-IR-Scan { ShiftIR, Exit1IR }, // 10 Capture-IR { ShiftIR, Exit1IR }, // 11 Shift-IR { PauseIR, UpdateIR }, // 12 Exit1-IR { PauseIR, Exit2IR }, // 13 Pause-IR { ShiftIR, UpdateIR }, // 14 Exit2-IR { RunTestIdle, SelectDRScan } // 15 Update-IR }; static const char *s_state[] = { "Test-Logic/Reset", "Run-Test/Idle", "Select-DR-Scan", "Capture-DR", "Shift-DR", "Exit1-DR", "Pause-DR", "Exit2-DR", "Update-DR", "Select-IR-Scan", "Capture-IR", "Shift-IR", "Exit1-IR", "Pause-IR", "Exit2-IR", "Update-IR" }; static void SaveBitRegister(CBlockFile *SaveState, const Util::BitRegister ®) { uint16_t size = reg.Size() + 1; // include null terminator SaveState->Write(&size, sizeof(size)); SaveState->Write(reg.ToBinaryString()); } static void LoadBitRegister(CBlockFile *SaveState, Util::BitRegister *reg) { uint16_t size; SaveState->Read(&size, sizeof(size)); char *str = new char[size]; SaveState->Read(str, size); reg->Set(str); } void CJTAG::SaveState(CBlockFile *SaveState) { SaveState->NewBlock("JTAG", __FILE__); SaveBitRegister(SaveState, m_instructionShiftReg); SaveBitRegister(SaveState, m_dataShiftReg); SaveState->Write(&m_instructionReg, sizeof(m_instructionReg)); SaveState->Write(&m_state, sizeof(m_state)); SaveState->Write(&m_lastTck, sizeof(m_lastTck)); SaveState->Write(&m_tdo, sizeof(m_tdo)); } void CJTAG::LoadState(CBlockFile *SaveState) { if (OKAY != SaveState->FindBlock("JTAG")) { ErrorLog("Unable to load JTAG state. Save state file is corrupt."); return; } LoadBitRegister(SaveState, &m_instructionShiftReg); LoadBitRegister(SaveState, &m_dataShiftReg); SaveState->Read(&m_instructionReg, sizeof(m_instructionReg)); SaveState->Read(&m_state, sizeof(m_state)); SaveState->Read(&m_lastTck, sizeof(m_lastTck)); SaveState->Read(&m_tdo, sizeof(m_tdo)); } uint8_t CJTAG::Read() { return m_tdo; } void CJTAG::LoadASICIDCodes() { /* * ID code retrieval has not been carefully studied but based on observation, * it appears that the ID codes are loaded on logic reset (Step 2.x games and * some 1.x games rely on this) as well as instruction 0x06318fc63fff. Some * games rely on both (e.g., von2). */ m_dataShiftReg.SetZeros(); m_dataShiftReg.Insert(2 + 0*32 + 0, Util::Hex(m_real3D.GetASICIDCode(CReal3D::ASIC::Jupiter))); m_dataShiftReg.Insert(2 + 1*32 + 0, Util::Hex(m_real3D.GetASICIDCode(CReal3D::ASIC::Mercury))); m_dataShiftReg.Insert(2 + 2*32 + 0, Util::Hex(m_real3D.GetASICIDCode(CReal3D::ASIC::Venus))); m_dataShiftReg.Insert(2 + 3*32 + 0, Util::Hex(m_real3D.GetASICIDCode(CReal3D::ASIC::Earth))); m_dataShiftReg.Insert(2 + 4*32 + 1, Util::Hex(m_real3D.GetASICIDCode(CReal3D::ASIC::Mars))); m_dataShiftReg.Insert(2 + 5*32 + 1, Util::Hex(m_real3D.GetASICIDCode(CReal3D::ASIC::Mars))); } void CJTAG::Write(uint8_t tck, uint8_t tms, uint8_t tdi, uint8_t trst) { tck = !!tck; tms = !!tms; tdi = !!tdi; trst = !!trst; //TODO: is trst used anywhere? If so, need to emulate. //if (!trst) // printf("TRST=0\n"); //printf("%d trst=%d tms=%d tdi=%d\n", tck, trst, tms, tdi); // Transitions occur on rising edge uint8_t lastTck = m_lastTck; m_lastTck = tck; if (!tck || lastTck != 0) return; // Current state logic switch (m_state) { default: break; case State::TestLogicReset: LoadASICIDCodes(); break; case State::CaptureDR: if (m_instructionReg == Instruction::ReadASICIDCodes) LoadASICIDCodes(); break; case State::ShiftDR: m_tdo = m_dataShiftReg.ShiftOutRight(tdi); break; case State::UpdateDR: if (m_instructionReg == Instruction::SetReal3DRenderConfig0 || m_instructionReg == Instruction::SetReal3DRenderConfig1) { uint64_t data = m_dataShiftReg.GetBits(0, 42); m_real3D.WriteJTAGRegister(m_instructionReg, data); } //std::cout << "DR = " << m_dataShiftReg << std::endl; break; case State::CaptureIR: // Load lower 2 bits with 01 as per IEEE 1149.1-1990 m_instructionShiftReg.Insert(44, "01"); break; case State::ShiftIR: m_tdo = m_instructionShiftReg.ShiftOutRight(tdi); break; case State::UpdateIR: // Latch the instruction register (technically, this should occur on // falling edge of clock as per the spec) m_instructionReg = m_instructionShiftReg.GetBits(); //std::cout << "IR = " << Util::Hex(m_instructionReg, 12) << std::endl; break; } // Go to next state m_state = s_fsm[m_state][tms]; //printf(" -> %s\n", s_state[m_state]); } void CJTAG::Reset() { m_state = State::TestLogicReset; DebugLog("JTAG reset\n"); } CJTAG::CJTAG(CReal3D &real3D) : m_real3D(real3D), m_instructionShiftReg(46, 0), m_dataShiftReg(197, 0) { DebugLog("Built JTAG logic\n"); }