#include #include #include #include #include #include #include #include "memory.h" #include "assembler.h" #include "cpu.h" #include "spi.h" #ifndef STAND_ALONE #include #include "audio.h" #include "loader.h" #include "editor.h" #include "timing.h" #include "graphics.h" #include "gigatron_0x1c.h" #include "gigatron_0x20.h" #include "gigatron_0x28.h" #include "gigatron_0x38.h" #include "gigatron_0x40.h" #endif #ifdef _WIN32 #include #ifdef max #undef max #endif #ifdef min #undef min #endif #endif namespace Cpu { class RAM_s { size_t size; uint16_t ctrl; uint8_t xin; uint8_t bank; uint8_t ram[4][0x8000]; public: RAM_s(size_t size) { resize(size);} const uint8_t& operator[](uint16_t address) const { if (! address && (ctrl & 1)) return xin; return const_cast(this)->operator[](address); } uint8_t& operator[](uint16_t address) { if ((!(ctrl & 0x20)) && (address & 0x7f80) == 0x80) address ^= 0x8000; return ram[(address & 0x8000) ? bank : 0][address &0x7fff]; } bool has_extension() { return size==0x20000;} void setctrl(uint16_t c) { if (has_extension()) { c &= 0x80fd; Spi::clock(ctrl, c); ctrl = c; bank = (c & 0xC0) >> 6; } } uint16_t getctrl() { return ctrl; } void setxin(uint8_t x) {xin=x;} uint8_t getxin() { return xin; } void resize(size_t s) { bank=(s>=0x10000)?1:0; ctrl=uint16_t((bank<<6)|0x3c); size=s; if (size!=0x8000 && size!=0x10000 && size!=0x20000) abort(); } uint8_t get(uint32_t addr) {return ram[(addr>>15)&3][addr&0x7fff];} void set(uint32_t addr,uint8_t data) {ram[(addr>>15)&3][addr&0x7fff]=data;} } _RAM(0x8000); const RAM_s &_cRAM = _RAM; const uint8_t _endianBytes[] = {0x00, 0x01, 0x02, 0x03}; int _numRoms = 0; int _romIndex = 0; int _vCpuInstPerFrame = 0; int _vCpuInstPerFrameMax = 0; float _vCpuUtilisation = 0.0; bool _coldBoot = true; bool _isInReset = false; bool _checkRomType = true; bool _debugging = false; bool _initAudio = true; bool _consoleSaveFile = true; uint8_t _ROM[ROM_SIZE][2]; std::vector _romFiles; RomType _romType = ROMERR; std::map _romTypeMap = {{"ROMV1", ROMv1}, {"ROMV2", ROMv2}, {"ROMV3", ROMv3}, {"ROMV4", ROMv4}, {"ROMV5A", ROMv5a}, {"SDCARD", SDCARD}, {"DEVROM", DEVROM}}; std::map _romTypeStr = {{ROMv1, "ROMv1"}, {ROMv2, "ROMv2"}, {ROMv3, "ROMv3"}, {ROMv4, "ROMv4"}, {ROMv5a, "ROMv5A"}, {SDCARD, "SDCARD"}, {DEVROM, "DEVROM"}}; std::vector _scanlinesRom0; std::vector _scanlinesRom1; int _scanlineMode = ScanlineMode::Normal; std::vector _internalGt1s; int getNumRoms(void) {return _numRoms;} int getRomIndex(void) {return _romIndex;} uint8_t* getPtrToROM(int& romSize) {romSize = sizeof(_ROM); return (uint8_t*)_ROM;} RomType getRomType(void) {return _romType;} std::map& getRomTypeMap(void) {return _romTypeMap;} bool getRomTypeStr(RomType romType, std::string& romTypeStr) { if(_romTypeStr.find(romType) == _romTypeStr.end()) { romTypeStr = ""; return false; } romTypeStr = _romTypeStr[romType]; return true; } //#define COLLECT_INST_STATS #if defined(COLLECT_INST_STATS) struct InstCount { uint8_t _inst = 0; uint64_t _count = 0; }; uint64_t _totalCount = 0; float _totalPercent = 0.0f; std::vector _instCounts(256); void displayInstCounts(void) { std::sort(_instCounts.begin(), _instCounts.end(), [](const InstCount& a, const InstCount& b) { return (a._count > b._count); }); for(int i=0; i< 1.0f) { _totalPercent += percent; fprintf(stderr, "inst:%02x count:%012lld %.1f%%\n", _instCounts[i]._inst, _instCounts[i]._count, percent); } } fprintf(stderr, "Total instructions:%lld\n", _totalCount); fprintf(stderr, "Total percentage:%f\n", _totalPercent); } #endif #ifdef _WIN32 void enableWin32ConsoleSaveFile(bool consoleSaveFile) { _consoleSaveFile = consoleSaveFile; } #endif Endianness getHostEndianness(void) { return *((Endianness*)_endianBytes); } void swapEndianness(uint16_t& value) { value = (value >>8) | (value <<8); } void swapEndianness(uint32_t& value) { value = (value >>24) | ((value >>8) & 0x0000FF00) | ((value <<8) & 0x00FF0000) | (value <<24); } void swapEndianness(uint64_t& value) { value = (value >>56) | ((value >>40) & 0x000000000000FF00LL) | ((value >>24) & 0x0000000000FF0000LL) | ((value >>8) & 0x00000000FF000000LL) | ((value <<8) & 0x000000FF00000000LL) | ((value <<24) & 0x0000FF0000000000LL) | ((value <<40) & 0x00FF000000000000LL) | (value <<56); } void initialiseInternalGt1s(void) { InternalGt1 internalGt1Snake = {0xE39C, 0xFDB1, 0xFC89, 5}; _internalGt1s.push_back(internalGt1Snake); InternalGt1 internalGt1Racer = {0xEA2C, 0xFDBB, 0xFC90, 5}; _internalGt1s.push_back(internalGt1Racer); InternalGt1 internalGt1Mandelbrot = {0xF16E, 0xFDC5, 0xFC97, 10}; _internalGt1s.push_back(internalGt1Mandelbrot); InternalGt1 internalGt1Pictures = {0xF655, 0xFDCF, 0xFCA3, 8}; _internalGt1s.push_back(internalGt1Pictures); InternalGt1 internalGt1Credits = {0xF731, 0xFDD9, 0xFCAD, 7}; _internalGt1s.push_back(internalGt1Credits); InternalGt1 internalGt1Loader = {0xF997, 0xFDE3, 0xFCB6, 6}; _internalGt1s.push_back(internalGt1Loader); } void patchSYS_Exec_88(void) { _ROM[0x00AD][ROM_INST] = 0x00; _ROM[0x00AD][ROM_DATA] = 0x00; _ROM[0x00AF][ROM_INST] = 0x00; _ROM[0x00AF][ROM_DATA] = 0x67; _ROM[0x00B5][ROM_INST] = 0xDC; _ROM[0x00B5][ROM_DATA] = 0xCF; _ROM[0x00B6][ROM_INST] = 0x80; _ROM[0x00B6][ROM_DATA] = 0x23; _ROM[0x00BB][ROM_INST] = 0x80; _ROM[0x00BB][ROM_DATA] = 0x00; } void patchScanlineModeVideoB(void) { _ROM[0x01C2][ROM_INST] = 0x14; _ROM[0x01C2][ROM_DATA] = 0x01; _ROM[0x01C9][ROM_INST] = 0x01; _ROM[0x01C9][ROM_DATA] = 0x09; _ROM[0x01CA][ROM_INST] = 0x90; _ROM[0x01CA][ROM_DATA] = 0x01; _ROM[0x01CB][ROM_INST] = 0x01; _ROM[0x01CB][ROM_DATA] = 0x0A; _ROM[0x01CC][ROM_INST] = 0x8D; _ROM[0x01CC][ROM_DATA] = 0x00; _ROM[0x01CD][ROM_INST] = 0xC2; _ROM[0x01CD][ROM_DATA] = 0x0A; _ROM[0x01CE][ROM_INST] = 0x00; _ROM[0x01CE][ROM_DATA] = 0xD4; _ROM[0x01CF][ROM_INST] = 0xFC; _ROM[0x01CF][ROM_DATA] = 0xFD; _ROM[0x01D0][ROM_INST] = 0xC2; _ROM[0x01D0][ROM_DATA] = 0x0C; _ROM[0x01D1][ROM_INST] = 0x02; _ROM[0x01D1][ROM_DATA] = 0x00; _ROM[0x01D2][ROM_INST] = 0x02; _ROM[0x01D2][ROM_DATA] = 0x00; _ROM[0x01D3][ROM_INST] = 0x02; _ROM[0x01D3][ROM_DATA] = 0x00; } void patchScanlineModeVideoC(void) { _ROM[0x01DA][ROM_INST] = 0xFC; _ROM[0x01DA][ROM_DATA] = 0xFD; _ROM[0x01DB][ROM_INST] = 0xC2; _ROM[0x01DB][ROM_DATA] = 0x0C; _ROM[0x01DC][ROM_INST] = 0x02; _ROM[0x01DC][ROM_DATA] = 0x00; _ROM[0x01DD][ROM_INST] = 0x02; _ROM[0x01DD][ROM_DATA] = 0x00; _ROM[0x01DE][ROM_INST] = 0x02; _ROM[0x01DE][ROM_DATA] = 0x00; } void patchTitleIntoRom(const std::string& title) { int minLength = std::min(int(title.size()), MAX_TITLE_CHARS); for(int i=0; i RAM_SIZE_HI) { fprintf(stderr, "Cpu::patchSplitGt1IntoRom() : ROM file %s must be less than %d in size.\n", std::string(splitGt1path + "_ti").c_str(), RAM_SIZE_HI); return false; } romfile_ti.read(filebuffer, filelength); if(romfile_ti.eof() || romfile_ti.bad() || romfile_ti.fail()) { fprintf(stderr, "Cpu::patchSplitGt1IntoRom() : failed to read %s ROM file.\n", std::string(splitGt1path + "_ti").c_str()); return false; } for(int i=0; i RAM_SIZE_HI) { fprintf(stderr, "Cpu::patchSplitGt1IntoRom() : ROM file %s must be less than %d in size.\n", std::string(splitGt1path + "_td").c_str(), RAM_SIZE_HI); return false; } romfile_td.read(filebuffer, filelength); if(romfile_td.eof() || romfile_td.bad() || romfile_td.fail()) { fprintf(stderr, "Cpu::patchSplitGt1IntoRom() : failed to read %s ROM file.\n", std::string(splitGt1path + "_td").c_str()); return false; } for(int i=0; i romv5aAddrs = {0x0056, 0x005E, 0x012C, 0x015C, 0x01A6, 0x01A7, 0x02D6, 0x02D7}; const std::vector romv5aOpcodes = {0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x40, 0x20 }; const std::vector romv5aOperands = {0x03, 0x03, 0x03, 0xFC, 0x03, 0xFC, 0x03, 0xFC }; static bool firstTime = true; static std::map romv5aBackup; switch(romType) { case ROMv5a: case SDCARD: case DEVROM: { if(getRomType() < Cpu::ROMv5a) { if(enable) { std::string romTypeStr; getRomTypeStr(getRomType(), romTypeStr); fprintf(stderr, "\nCpu::enable6BitSound() : Error, ROM version must be >= ROMv5a, current ROM is %s\n", romTypeStr.c_str()); } return; } if(firstTime) romv5aBackup.clear(); // LED pattern reduced to lower 2 LED's for(uint16_t a=0x0130; a<=0x0147; a++) { if(firstTime) { romv5aBackup[a][0] = _ROM[a][ROM_INST]; romv5aBackup[a][1] = _ROM[a][ROM_DATA]; } if(enable) { _ROM[a][ROM_INST] = 0x00; _ROM[a][ROM_DATA] = a & 0x03; } else { _ROM[a][ROM_INST] = romv5aBackup[a][0]; _ROM[a][ROM_DATA] = romv5aBackup[a][1]; } } // Codes for(int i=0; i_type; std::string name = Loader::getConfigRom(i)->_name; std::ifstream file(name, std::ios::binary | std::ios::in); if(!file.is_open()) { fprintf(stderr, "Cpu::initialise() : failed to open ROM file : %s\n", name.c_str()); } else { // Load ROM file uint8_t* rom = new (std::nothrow) uint8_t[sizeof(_ROM)]; if(!rom) { // This is fairly pointless as the code does not have any exception handling for the many std:: memory allocations that occur // If you're running out of memory running this application, (which requires around 20 Mbytes), then you need to leave the 80's shutdown(); fprintf(stderr, "Cpu::initialise() : out of memory!\n"); _EXIT_(EXIT_FAILURE); } file.read((char *)rom, sizeof(_ROM)); if(file.bad() || file.fail()) { fprintf(stderr, "Cpu::initialise() : failed to read ROM file : %s\n", name.c_str()); } else { _romFiles.push_back(rom); Editor::addRomEntry(type, name); } } } // Switchable ROMS _numRoms = int(_romFiles.size()); memcpy(_ROM, _romFiles[_romIndex], sizeof(_ROM)); //#define CREATE_ROM_HEADER #ifdef CREATE_ROM_HEADER // Create a header file representation of a ROM, (match the ROM type number with the ROM file before enabling and running this code) createRomHeader((uint8_t *)_ROM, "gigatron_0x40.h", "_gigatron_0x40_rom", sizeof(_ROM)); #endif //#define CUSTOM_ROM #ifdef CUSTOM_ROM initialiseInternalGt1s(); patchSYS_Exec_88(); #define CUSTOM_ROMV0 #ifdef CUSTOM_ROMV0 patchTitleIntoRom(" TTL microcomputer ROM v0"); patchSplitGt1IntoRom("./roms/starfield.rom", "Starfield", 0x0b00, MandelbrotGt1); patchSplitGt1IntoRom("./roms/life.rom", "Life", 0x0f00, LoaderGt1); patchSplitGt1IntoRom("./roms/lines.rom", "Lines", 0x1100, SnakeGt1); patchSplitGt1IntoRom("./roms/gigatris.rom", "Gigatris", 0x1300, PicturesGt1); patchSplitGt1IntoRom("./roms/tetris.rom", "Tetris", 0x3000, CreditsGt1); patchSplitGt1IntoRom("./roms/miditest.rom", "Midi", 0x5800, RacerGt1); #else patchTitleIntoRom(" TTL microcomputer ROM v0"); patchSplitGt1IntoRom("./roms/midi64.rom", "Midi64", 0x0b00, PicturesGt1); #endif #endif // SDL initialisation if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS) < 0) { fprintf(stderr, "Cpu::initialise() : failed to initialise SDL.\n"); _EXIT_(EXIT_FAILURE); } // Initialise COM port here so that we can see error messages Loader::openComPort(); } void cycle(const State& S, State& T) { // New state is old state unless something changes T = S; // Instruction Fetch T._IR = _ROM[S._PC][ROM_INST]; T._D = _ROM[S._PC][ROM_DATA]; // Adapted from https://github.com/kervinck/gigatron-rom/blob/master/Contrib/dhkolf/libgtemu/gtemu.c // Optimise for the statistically most common instructions switch(S._IR) { case 0x5D: // ora [Y,X++],OUT { uint16_t addr = MAKE_ADDR(S._Y, S._X); T._OUT = _cRAM[addr] | S._AC; T._X++; T._PC = S._PC + 1; return; } break; case 0xC2: // st [D] { _RAM[S._D] = S._AC; T._PC = S._PC + 1; return; } break; case 0x01: // ld [D] { T._AC = _cRAM[S._D]; T._PC = S._PC + 1; return; } break; #if 1 case 0x00: // ld D { T._AC = S._D; T._PC = S._PC + 1; return; } break; case 0x80: // adda D { T._AC += S._D; T._PC = S._PC + 1; return; } break; case 0xFC: // bra D { T._PC = (S._PC & 0xFF00) | S._D; return; } break; case 0x0D: // ld [Y,X] { uint16_t addr = MAKE_ADDR(S._Y, S._X); T._AC = _cRAM[addr]; T._PC = S._PC + 1; return; } break; case 0xA0: // suba D { T._AC -= S._D; T._PC = S._PC + 1; return; } break; case 0xE8: // blt PC,D { T._PC = (S._AC & 0x80) ? (S._PC & 0xFF00) | S._D : S._PC + 1; return; } break; case 0x81: // adda [D] { T._AC += _cRAM[S._D]; T._PC = S._PC + 1; return; } break; case 0x89: // adda [Y,D] { uint16_t addr = MAKE_ADDR(S._Y, S._D); T._AC += _cRAM[addr]; T._PC = S._PC + 1; return; } break; case 0x12: // ld AC,X { T._X = S._AC; T._PC = S._PC + 1; return; } break; case 0x18: // ld D,OUT { T._OUT = S._D; T._PC = S._PC + 1; return; } break; #endif default: break; } int ins = S._IR >> 5; // Instruction int mod = (S._IR >> 2) & 7; // Addressing mode (or condition) int bus = S._IR & 3; // Busmode int W = (ins == 6); // Write instruction? int J = (ins == 7); // Jump instruction? uint8_t lo=S._D, hi=0, *to=NULL; // Mode Decoder int incX=0; if(!J) { switch(mod) { #define E(p) (W ? 0 : p) // Disable _AC and _OUT loading during _RAM write case 0: to = E(&T._AC); break; case 1: to = E(&T._AC); lo=S._X; break; case 2: to = E(&T._AC); hi=S._Y; break; case 3: to = E(&T._AC); lo=S._X; hi=S._Y; break; case 4: to = &T._X; break; case 5: to = &T._Y; break; case 6: to = E(&T._OUT); break; case 7: to = E(&T._OUT); lo=S._X; hi=S._Y; incX=1; break; default: break; } } uint16_t addr = (hi << 8) | lo; uint8_t B = S._undef; // Data Bus switch(bus) { case 0: B=S._D; break; case 1: if (!W) B = _cRAM[addr]; else _RAM.setctrl(addr); break; case 2: B=S._AC; break; case 3: B=_IN; break; default: break; } if(W && (bus != 1 || !_RAM.has_extension())) _RAM[addr] = B; // Random Access Memory uint8_t ALU = 0; // Arithmetic and Logic Unit switch(ins) { case 0: ALU = B; break; // LD case 1: ALU = S._AC & B; break; // ANDA case 2: ALU = S._AC | B; break; // ORA case 3: ALU = S._AC ^ B; break; // XORA case 4: ALU = S._AC + B; break; // ADDA case 5: ALU = S._AC - B; break; // SUBA case 6: ALU = S._AC; break; // ST case 7: ALU = -S._AC; break; // Bcc/JMP default: break; } if(to) *to = ALU; // Load value into register if(incX) T._X = S._X + 1; // Increment _X T._PC = S._PC + 1; // Next instruction if(J) { if(mod != 0) // Conditional branch within page { int cond = (S._AC>>7) + 2*(S._AC==0); if(mod & (1 << cond)) // 74153 { T._PC = (S._PC & 0xff00) | B; } } else { T._PC = (S._Y << 8) | B; // Unconditional far jump } } } void reset(bool coldBoot) { _coldBoot = coldBoot; _checkRomType = true; clearUserRAM(); setRAM(ZERO_CONST_ADDRESS, 0x00); setRAM(ONE_CONST_ADDRESS, 0x01); setClock(CLOCK_RESET); Memory::initialise(); Assembler::setvSpMin(0x00); Graphics::resetVTable(); Editor::setSingleStepAddress(FRAME_COUNT_ADDRESS); Audio::restoreWaveTables(); } void softReset(void) { Loader::setCurrentGame(std::string("")); } void swapMemoryModel(void) { if (Memory::getSizeRAM() == RAM_SIZE_LO) { Memory::setSizeRAM(RAM_SIZE_HI); _RAM.resize(RAM_SIZE_HI); } else if (! _RAM.has_extension()) { Memory::setSizeRAM(RAM_SIZE_HI); _RAM.resize(RAM_SIZE_HI * 2); } else { Memory::setSizeRAM(RAM_SIZE_LO); _RAM.resize(RAM_SIZE_LO); } Memory::initialise(); reset(false); } void vCpuUsage(const State& S, const State& T) { UNREFERENCED_PARAM(T); // All ROM's so far v1 through v5a/DEVROM use the same vCPU dispatch address! if(S._PC == ROM_VCPU_DISPATCH) { _vPC.first = (getRAM(0x0017) <<8) | getRAM(0x0016); if(_vPC.first < Editor::getCpuUsageAddressA() || _vPC.first > Editor::getCpuUsageAddressB()) _vCpuInstPerFrame++; _vCpuInstPerFrameMax++; // Soft reset if(_vPC.first == VCPU_SOFT_RESET) softReset(); static uint64_t prevFrameCounter = 0; double frameTime = double(SDL_GetPerformanceCounter() - prevFrameCounter) / double(SDL_GetPerformanceFrequency()); if(frameTime > VSYNC_TIMING_60) { // TODO: this is a bit of a hack, but it's emulation only so... // Check for magic cookie that defines a CpuUsageAddressA and CpuUsageAddressB sequence uint16_t magicWord0 = (getRAM(0x7F99) <<8) | getRAM(0x7F98); uint16_t magicWord1 = (getRAM(0x7F9B) <<8) | getRAM(0x7F9A); uint16_t cpuUsageAddressA = (getRAM(0x7F9D) <<8) | getRAM(0x7F9C); uint16_t cpuUsageAddressB = (getRAM(0x7F9F) <<8) | getRAM(0x7F9E); if(magicWord0 == 0xDEAD && magicWord1 == 0xBEEF) { Editor::setCpuUsageAddressA(cpuUsageAddressA); Editor::setCpuUsageAddressB(cpuUsageAddressB); } prevFrameCounter = SDL_GetPerformanceCounter(); _vCpuUtilisation = (_vCpuInstPerFrameMax) ? float(_vCpuInstPerFrame) / float(_vCpuInstPerFrameMax) : 0.0f; _vCpuInstPerFrame = 0; _vCpuInstPerFrameMax = 0; } } } bool process(bool disableOutput) { bool vBlank = false; // MCP100 Power-On Reset if(_clock < 0) { _stateS._PC = 0; _initAudio = true; _isInReset = true; Loader::setCurrentGame(std::string("")); } // Update CPU cycle(_stateS, _stateT); // vCPU instruction slot utilisation, also updates _vPC vCpuUsage(_stateS, _stateT); _hSync = (_stateT._OUT & 0x40) - (_stateS._OUT & 0x40); _vSync = (_stateT._OUT & 0x80) - (_stateS._OUT & 0x80); // Falling vSync edge if(_vSync < 0) { vBlank = true; _clockStall = _clock; _vgaY = VSYNC_START; if(!_debugging) { if(disableOutput) { Timing::synchronise(); } // Input and graphics 60 times per second else { Editor::handleInput(); Graphics::render(true); } } } // Pixel if(_vgaX++ < HLINE_END && !disableOutput) { if(_vgaY >= 0 && _vgaY < SCREEN_HEIGHT) { if(_vgaX >=HPIXELS_START && _vgaX < HPIXELS_END) Graphics::refreshPixel(_stateS, _vgaX-HPIXELS_START, _vgaY); // Show pixel reticle when debugging Native code //if(_debugging && _vgaX >=HPIXELS_START-1 && _vgaX <= HPIXELS_END-1) Graphics::pixelReticle(_stateS, _vgaX-(HPIXELS_START-1), _vgaY); } } #if defined(COLLECT_INST_STATS) _totalCount++; _instCounts[_stateT._IR]._count++; _instCounts[_stateT._IR]._inst = _stateT._IR; if(_clock > STARTUP_DELAY_CLOCKS * 500.0) { displayInstCounts(); _EXIT_(0); } #endif // RomType, init audio and watchdog if(_clock > STARTUP_DELAY_CLOCKS) { if(_isInReset) { setRomType(); _isInReset = false; } if(_initAudio && _clock > STARTUP_DELAY_CLOCKS*10.0) { // ROM's V1 to V3 do not re-initialise RAM Audio Wave Tables on soft reboot, (we re-initialise them for ALL ROM versions) Audio::initialiseChannels(); Audio::saveWaveTables(); _coldBoot = false; _initAudio = false; } if(!_debugging && _clock - _clockStall > CPU_STALL_CLOCKS) { fprintf(stderr, "Cpu::process(): CPU stall for %" PRId64 " clocks : rebooting.\n", _clock - _clockStall); _clockStall = CLOCK_RESET; reset(true); _vgaX = 0, _vgaY = 0; _hSync = 0, _vSync = 0; } } // Rising hSync edge if(_hSync > 0) { _XOUT = _stateT._AC; // Audio //Audio::playSample(); //Audio::fillBuffer(); Audio::fillCallbackBuffer(); #if defined(HARDWARE_LOADER) // TODO: don't enable until Loader::upload is fixed! // Emulation of hardware Loader if(_clock > STARTUP_DELAY_CLOCKS*10.0) Loader::upload(_vgaY); #endif // Horizontal timing errors if(_vgaY >= 0 && _vgaY < SCREEN_HEIGHT) { static uint32_t colour = 0xFF220000; if((_vgaY % 4) == 0) colour = 0xFF220000; if(_vgaX != 200 && _vgaX != 400) // Support for 6.25Mhz and 12.5MHz { colour = 0xFFFF0000; //fprintf(stderr, "Cpu::process(): Horizontal timing error : vgaX %03d : vgaY %03d : xout %02x : time %0.3f\n", _vgaX, _vgaY, _stateT._AC, float(_clock)/float(CLOCK_FREQ)); } if((_vgaY % 4) == 3) Graphics::refreshTimingPixel(_stateS, GIGA_WIDTH, _vgaY / 4, colour, _debugging); } _vgaX = 0; _vgaY++; // Change this once in a while _stateT._undef = rand() & 0xff; } // Debugger _debugging = Editor::handleDebugger(); _stateS = _stateT; _clock++; return vBlank; } #ifdef _WIN32 void restoreWin32Console(void) { if(!_consoleSaveFile) return; std::string line; std::ifstream infile(Loader::getCwdPath() + "/" + "console.txt"); if(infile.is_open()) { getline(infile, line); int xpos, ypos, width, height; sscanf_s(line.c_str(), "%d %d %d %d", &xpos, &ypos, &width, &height); if(xpos < -2000 || xpos > 4000 || ypos < 120 || ypos > 1000 || width < 100 || width > 2000 || height < 100 || height > 1000) { xpos = -1000; ypos = 120; width = 1000; height = 1000; } MoveWindow(_consoleWindowHWND, xpos, ypos, width, height, true); BringWindowToTop(_consoleWindowHWND); } } void saveWin32Console(void) { if(!_consoleSaveFile) return; RECT rect; if(GetWindowRect(_consoleWindowHWND, &rect)) { std::ofstream outfile(Loader::getCwdPath() + "/" + "console.txt"); if(outfile.is_open()) { int xpos = rect.left; int ypos = rect.top; int width = rect.right - rect.left; int height = rect.bottom - rect.top; outfile << xpos << " " << ypos << " " << width << " " << height << std::endl; } } } #endif #endif }