#include "z80debug.h" #include #include "z80.h" #include "z80dis.h" #include "zx_ula.h" #include "zx_tape.h" #include "ui.h" #include "ui_window.h" #include "zx_screen.h" #include "z80analyze.h" #define RESIZING_NONE 0 #define RESIZING_MEMORY 1 #define RESIZING_CONSOLE 2 #define RESIZING_SYMBOLS 3 #define RESIZING_X 4 namespace z80debug { int midx = 58; int mem_y = 20; int con_y = 28; int sym_y = 20; int win_h = 44; int mem_h = 8; int con_h = 6; int sym_h = 14; int resizing_type = RESIZING_MEMORY; bool resizing = false; namespace history { uint16_t buffer[65536]; uint8_t top=0; uint8_t pos=0; } uint16_t oAF, oBC, oDE, oHL, oAF2, oBC2, oDE2, oHL2, oIX, oIY, oSP, oPC; uint8_t oI, oR; bool mem_modified[65536]; SDL_Window *win = nullptr; SDL_Renderer *ren = nullptr; SDL_Texture *tex = nullptr; SDL_Cursor *cur_df = nullptr; SDL_Cursor *cur_ns = nullptr; SDL_Cursor *cur_we = nullptr; bool is_debugging=false; bool is_paused=false; uint16_t mem_viewer_pos = 0; char console_log[256][256]; char console_history[256][256]; uint8_t console_log_pos = 255; uint8_t console_history_pos = 0; uint8_t console_history_nav = 0; uint8_t breakpoints[65536]; uint16_t cursor = 0; char temp[256]; const char *tohex(int value, int numdigits) { if (numdigits==2) sprintf(temp, "%02X", value); else if (numdigits==4) sprintf(temp, "%04X", value); return temp; } bool eventHandler(SDL_Event *e) { if (z80debug::debugging()) { if (e->type==SDL_WINDOWEVENT) { if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) { int w; int h; SDL_GetWindowSize(win, &w, &h); midx = (w/CHR_W) - 25; win_h = (h/CHR_H); mem_y = win_h - mem_h - con_h; con_y = win_h - con_h; sym_y = win_h - sym_h; z80debug::refresh(); zxscreen::redraw(); } else if (e->window.event == SDL_WINDOWEVENT_CLOSE) { hide(); zxscreen::focus(); } } if (e->type == SDL_MOUSEWHEEL) { if (e->wheel.mouseXwheel.mouseYwheel.y>0) { z80debug::cursorback(); z80debug::refresh(); } else if (e->wheel.y<0) { z80debug::cursorfwd(); z80debug::refresh(); } } } if (e->type == SDL_KEYDOWN) { if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) { return false; } else if (e->key.keysym.scancode==SDL_SCANCODE_F1) { z80debug::history::gototop(); z80debug::refresh(); } else if (e->key.keysym.scancode==SDL_SCANCODE_F2) { z80debug::history::goback(); z80debug::refresh(); } else if (e->key.keysym.scancode==SDL_SCANCODE_F3) { z80debug::history::goforward(); z80debug::refresh(); /*} else if (e->key.keysym.scancode==SDL_SCANCODE_F6) { z80debug::history::gototop(); const uint8_t dt = z80::step(); z80debug::refresh(); zxscreen::refresh(dt);*/ } else if (e->key.keysym.scancode==SDL_SCANCODE_UP) { if (console_history_nav != console_history_pos+1 && console_history[console_history_nav-1][0]!=0) console_history_nav--; //z80debug::cursorback(); z80debug::refresh(); } else if (e->key.keysym.scancode==SDL_SCANCODE_DOWN) { if (console_history_nav != console_history_pos) console_history_nav++; //z80debug::cursorfwd(); z80debug::refresh(); } else if ( (e->key.keysym.scancode==SDL_SCANCODE_RETURN) || (e->key.keysym.scancode==SDL_SCANCODE_KP_ENTER)) { z80debug::executeConsole(); } else if (e->key.keysym.scancode==SDL_SCANCODE_BACKSPACE) { z80debug::DeleteCharConsole(); } } if (e->type == SDL_MOUSEMOTION) { if (!resizing) { if ( (e->motion.y > (mem_y*CHR_H)-8) && (e->motion.y < (mem_y*CHR_H)+8) && ( e->motion.x < midx*CHR_W ) ) { if (resizing_type != RESIZING_MEMORY) SDL_SetCursor(cur_ns); resizing_type = RESIZING_MEMORY; } else if ( (e->motion.y > (sym_y*CHR_H)-8) && (e->motion.y < (sym_y*CHR_H)+8) && ( e->motion.x >= midx*CHR_W ) ) { if (resizing_type != RESIZING_SYMBOLS) SDL_SetCursor(cur_ns); resizing_type = RESIZING_SYMBOLS; } else if ( (e->motion.y > (con_y*CHR_H)-8) && (e->motion.y < (con_y*CHR_H)+8) && (e->motion.x < midx*CHR_W) ) { if (resizing_type != RESIZING_CONSOLE) SDL_SetCursor(cur_ns); resizing_type = RESIZING_CONSOLE; } else { if (resizing_type != RESIZING_NONE) SDL_SetCursor(cur_df); resizing_type = RESIZING_NONE; } } else { if (resizing_type==RESIZING_MEMORY) { const int new_mem_y = e->motion.y/CHR_H; const int new_mem_h = win_h - new_mem_y - con_h; if (new_mem_y>=5 && new_mem_h>=3) { mem_y = new_mem_y; mem_h = new_mem_h; refresh(); } } else if (resizing_type==RESIZING_CONSOLE) { const int new_con_y = e->motion.y/CHR_H; const int new_con_h = win_h - new_con_y; const int new_mem_y = win_h - mem_h - new_con_h; if (new_mem_y>=5 && new_con_h>=3) { mem_y = new_mem_y; con_y = new_con_y; con_h = new_con_h; refresh(); } } else if (resizing_type==RESIZING_SYMBOLS) { const int new_sym_y = e->motion.y/CHR_H; const int new_sym_h = win_h - new_sym_y; if (new_sym_y>=12 && new_sym_h >=3) { sym_y = new_sym_y; sym_h = new_sym_h; refresh(); } } } } if (e->type == SDL_MOUSEBUTTONDOWN) { if (!resizing && resizing_type != RESIZING_NONE) { resizing = true; } } if (e->type == SDL_MOUSEBUTTONUP) { if (resizing) { resizing = false; } } if (e->type == SDL_TEXTINPUT) { z80debug::sendToConsole(e->text.text); } } return true; } void processCommand(); void init() { is_debugging = is_paused = false; for (int i=0; i<65536; ++i) breakpoints[i]=0; if (!cur_df) cur_df = SDL_GetCursor(); if (!cur_ns) cur_ns = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); if (!cur_we) cur_we = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); memchr(console_log, 0, 65536); memchr(console_history, 0, 65536); //show(); } void show() { if (!win) { win = SDL_CreateWindow("Z80 Debugger", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 98*CHR_W, 44*CHR_H, SDL_WINDOW_RESIZABLE); ren = SDL_CreateRenderer(win, -1, 0); ui::window::registerWindow(SDL_GetWindowID(win), eventHandler); tex = ui::createtexture(ren); } focus(); } void focus() { if (win) { SDL_RaiseWindow(win); z80debug::refresh(); } } void hide() { ui::window::unregisterWindow(SDL_GetWindowID(win)); if (tex) SDL_DestroyTexture(tex); if (ren) SDL_DestroyRenderer(ren); if (win) SDL_DestroyWindow(win); tex = NULL; ren = NULL; win = NULL; } void pause() { zx_ula::sound_disable(); is_paused = true; breakpoints[z80::getPC()] &= ~8; } void stop() { zxscreen::setTitle(" (stopped)"); pause(); is_debugging = true; show(); /*refresh();*/ } void cont() { zxscreen::setTitle(""); is_debugging = is_paused = false; //hide(); refresh(); zxscreen::focus(); zx_ula::sound_enable(); } const bool debugging() { return is_debugging; } const bool paused() { return is_paused; } uint16_t find_previous_opcode(uint16_t pc) { pc--; if (z80::getMemTag(pc)!=MEMTAG_CODE && z80::getMemTag(pc)!=MEMTAG_INST) return pc; while (z80::getMemTag(pc)!=MEMTAG_INST) pc--; return pc; /* pc-=4; if (z80dis::getOpcodeSize(pc) == 4) return pc; if (z80dis::getOpcodeSize(pc) == 3) return pc+3; pc++; if (z80dis::getOpcodeSize(pc) == 3) return pc; if (z80dis::getOpcodeSize(pc) == 2) return pc+2; pc++; if (z80dis::getOpcodeSize(pc) == 2) return pc; return pc+1; */ } void printDissasemblerLine(const uint16_t address, const int line, const bool heuristics=false) { uint8_t colors[4] = { COLOR_RED, COLOR_CYAN, COLOR_GRAY, COLOR_WHITE}; if (z80::getMemTouched(address)!=MEMTAG_INST) colors[3]=COLOR_GRAY; if (z80::getMemTouched(address)==MEMTAG_NONE) colors[1]=COLOR_GRAY; if (is_debugging && (address == z80::getPC())) { for (int i=0; i<4;++i) colors[i]=COLOR_YELLOW; ui::printrect(0,line, midx-2,1, COLOR_BLUE); } if (breakpoints[address]&9) ui::printtxt(0,line,"*", colors[0]); ui::printtxt(1,line,tohex(address,4), colors[1]); if ( (z80::getMemTag(address)==MEMTAG_INST) || heuristics ) { const char *sym = z80dis::getSymbol(address); if (sym[0]!=0) ui::printtxt(7,line, sym, COLOR_YELLOW); ui::printtxt(19,line, z80dis::getOpcode(address), colors[2]); ui::printtxt(31,line, z80dis::getAsm(address), colors[3]); } else { ui::printtxt(19,line, tohex(z80::getMem()[address],2), COLOR_GRAY); ui::printtxt(31,line, "?????????", COLOR_GRAY); } } void refresh() { ui::setrenderer(ren, tex); SDL_SetRenderDrawColor(ren, 30, 30, 30, 255); SDL_RenderClear(ren); // DISSASSEBLY // ****************************************** ui::setoffset(0, 0); ui::box(0,0,midx,mem_y,COLOR_WHITE); ui::printrect(2,0, 12,1, COLOR_DARK); ui::printtxt(3,0, "ASSEMBLER:", COLOR_WHITE); ui::setoffset(1, 1); uint8_t *memory = z80::getMem(); uint16_t pc = cursor; //z80::getPC(); uint16_t pos = pc; int num_lines = mem_y-2; printDissasemblerLine(pc, (num_lines/2)-1, true); for (int i=num_lines/2;i=0;--i) { pos = find_previous_opcode(pos); printDissasemblerLine(pos, i); } // REGISTERS // ****************************************** ui::setoffset(0, 0); ui::box(midx,0,25,8,COLOR_WHITE); ui::printrect(midx+2,0, 12,1, COLOR_DARK); ui::printtxt(midx+3,0, "REGISTERS:", COLOR_WHITE); ui::setoffset(midx+1, 1); ui::printtxt(0,0, "AF AF' IX ", COLOR_WHITE); ui::printtxt(0,1, "BC BC' IY ", COLOR_WHITE); ui::printtxt(0,2, "DE DE' SP ", COLOR_WHITE); ui::printtxt(0,3, "HL HL' PC ", COLOR_WHITE); ui::printtxt(0,4, "(BC) (DE) (HL) ", COLOR_WHITE); ui::printtxt(0,5, " I R ", COLOR_WHITE); ui::printtxt(3,0, tohex(z80::getAF(), 4), oAF != z80::getAF() ? COLOR_RED : COLOR_GRAY); ui::printtxt(11,0, tohex(z80::getAF(true), 4), oAF2 != z80::getAF(true) ? COLOR_RED : COLOR_GRAY); ui::printtxt(19,0, tohex(z80::getIX(), 4), oIX != z80::getIX() ? COLOR_RED : COLOR_GRAY); ui::printtxt(3,1, tohex(z80::getBC(), 4), oBC != z80::getBC() ? COLOR_RED : COLOR_GRAY); ui::printtxt(11,1, tohex(z80::getBC(true), 4), oBC2 != z80::getBC(true) ? COLOR_RED : COLOR_GRAY); ui::printtxt(19,1, tohex(z80::getIY(), 4), oIY != z80::getIY() ? COLOR_RED : COLOR_GRAY); ui::printtxt(3,2, tohex(z80::getDE(), 4), oDE != z80::getDE() ? COLOR_RED : COLOR_GRAY); ui::printtxt(11,2, tohex(z80::getDE(true), 4), oDE2 != z80::getDE(true) ? COLOR_RED : COLOR_GRAY); ui::printtxt(19,2, tohex(z80::getSP(), 4), oSP != z80::getSP() ? COLOR_RED : COLOR_GRAY); ui::printtxt(3,3, tohex(z80::getHL(), 4), oHL != z80::getHL() ? COLOR_RED : COLOR_GRAY); ui::printtxt(11,3, tohex(z80::getHL(true), 4), oHL2 != z80::getHL(true) ? COLOR_RED : COLOR_GRAY); ui::printtxt(19,3, tohex(z80::getPC(), 4), oPC != z80::getPC() ? COLOR_RED : COLOR_GRAY); ui::printtxt(16,5, tohex(z80::getI(), 2), oI != z80::getI() ? COLOR_RED : COLOR_GRAY); ui::printtxt(21,5, tohex(z80::getR(), 2), oR != z80::getR() ? COLOR_RED : COLOR_GRAY); ui::printtxt(5,4, tohex(memory[z80::getBC()], 2), COLOR_GRAY); ui::printtxt(13,4, tohex(memory[z80::getDE()], 2), COLOR_GRAY); ui::printtxt(21,4, tohex(memory[z80::getHL()], 2), COLOR_GRAY); const uint8_t flags = (z80::getAF() & 0xFF); const uint8_t mod_flags = flags ^ (oAF & 0xFF); ui::printtxt(0,5,"S", flags&0x80 ? (mod_flags&0x80 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x80 ? COLOR_BROWN : COLOR_GRAY) ); ui::printtxt(1,5,"Z", flags&0x40 ? (mod_flags&0x40 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x40 ? COLOR_BROWN : COLOR_GRAY) ); ui::printtxt(2,5,"X", flags&0x20 ? (mod_flags&0x20 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x20 ? COLOR_BROWN : COLOR_GRAY) ); ui::printtxt(3,5,"H", flags&0x10 ? (mod_flags&0x10 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x10 ? COLOR_BROWN : COLOR_GRAY) ); ui::printtxt(4,5,"Y", flags&0x08 ? (mod_flags&0x08 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x08 ? COLOR_BROWN : COLOR_GRAY) ); ui::printtxt(5,5,"P", flags&0x04 ? (mod_flags&0x04 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x04 ? COLOR_BROWN : COLOR_GRAY) ); ui::printtxt(6,5,"N", flags&0x02 ? (mod_flags&0x02 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x02 ? COLOR_BROWN : COLOR_GRAY) ); ui::printtxt(7,5,"C", flags&0x01 ? (mod_flags&0x01 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x01 ? COLOR_BROWN : COLOR_GRAY) ); // STACK // ****************************************** ui::setoffset(0, 0); ui::box(midx,8,11,sym_y-8,COLOR_WHITE); ui::printrect(midx+2,8, 8,1, COLOR_DARK); ui::printtxt(midx+3,8, "STACK:", COLOR_WHITE); ui::setoffset(midx+1, 9); uint16_t sp = z80::getSP()-(((sym_y-12)>>1)<<1); for (int i=0; i9) break; } } // MEMORY // ****************************************** ui::setoffset(0, 0); ui::box(0,mem_y,midx,mem_h,COLOR_WHITE); ui::printrect(2,mem_y, 9,1, COLOR_DARK); ui::printtxt(3,mem_y, "MEMORY:", COLOR_WHITE); ui::setoffset(1, mem_y+1); uint16_t mem_viewer_cursor = mem_viewer_pos; for (int i=0; i", COLOR_WHITE); ui::printtxt(1,line, console_history[console_history_nav], COLOR_WHITE); ui::printtxt(strlen(console_history[console_history_nav])+1,line, "\x7F", COLOR_WHITE); uint8_t i = console_log_pos; while (line>0) { line--; ui::printtxt(1,line, console_log[i--], COLOR_GRAY); } // SYMBOLS // ***************************************** ui::setoffset(0, 0); ui::box(midx,sym_y,25,sym_h,COLOR_WHITE); ui::printrect(midx+2,sym_y, 9,1, COLOR_DARK); ui::printtxt(midx+3,sym_y, "SYMBOLS:", COLOR_WHITE); ui::setoffset(midx+1, sym_y+1); const int num_symbols = z80dis::getNumSymbols(); for (int i=0;i"); strcat(console_log[console_log_pos], console_history[console_history_pos]); processCommand(); console_history_pos++; console_history_nav=console_history_pos; console_history[console_history_pos][0] = 0; refresh(); } const char integer_digits[] = "0123456789"; const char hex_digits[] = "0123456789ABCDEF"; const int getnum(const char *str) { int i=0; int return_value = 0; if (str[0]=='$') { i++; while(str[i]!=0) { int num = str[i]-48; if (num<0) return -1; if (num>9) { num-=7; if (num>15) { num-=32; if (num<10 || num>15) return -1; } } return_value = (return_value<<4)+num; i++; } } else { while(str[i]!=0) { int num = str[i]-48; if (num<0 || num>9) return -1; return_value = (return_value*10)+num; i++; } } return return_value; } #define getcmd() {while (*console_ptr==32) console_ptr++;while (console_ptr[i]!=0 && console_ptr[i]!=32) { cmd[i]=console_ptr[i]; i++; } cmd[i]=0; console_ptr = &console_ptr[i]; i=0;} void processCommand() { char cmd[256]; int i=0; char *console_ptr = console_history[console_history_pos]; getcmd(); if (strcmp(cmd, "s")==0 || strcmp(cmd, "step")==0) { z80::step(); } else if (strcmp(cmd, "c")==0 || strcmp(cmd, "cont")==0) { z80::step(); z80debug::cont(); } else if (strcmp(cmd, "r")==0 || strcmp(cmd, "reset")==0) { uint8_t *mem = z80::getMem(); for (int i=0x4000; i<=0xffff; ++i) mem[i]=0; z80::reset(mem); z80::connect_port(0xfe, zx_ula::port_in, zx_ula::port_out); //for (int i=0; i<65536; ++i) breakpoints[i]=0; z80debug::refresh(); } else if (strcmp(cmd, "b")==0 || strcmp(cmd, "break")==0) { getcmd(); if (cmd[0] == 0) { breakpoints[z80::getPC()]=1; return; } if (cmd[0] == 'i') { z80::setOption(Z80_OPTION_BREAK_ON_INTERRUPT, !z80::getOption(Z80_OPTION_BREAK_ON_INTERRUPT)); return; } int address = getnum(cmd); if (address<0 || address>65536) { sendToConsoleLog("Illegal break address"); return; } getcmd(); uint8_t break_type = 1; if (cmd[0]!=0) { if (strcmp(cmd, "x")==0 || strcmp(cmd, "exec")==0) { break_type = 1; } else if (strcmp(cmd, "r")==0 || strcmp(cmd, "read")==0) { break_type = 2; } else if (strcmp(cmd, "w")==0 || strcmp(cmd, "write")==0) { break_type = 4; } else { sendToConsoleLog("Illegal break type"); return; } } breakpoints[address] |= break_type; } else if (strcmp(cmd, "d")==0 || strcmp(cmd, "delete")==0) { getcmd(); if (strcmp(cmd, "all")==0) { for (int i=0;i<65536;++i) breakpoints[i]=0; return; } if (cmd[0] == 0) { breakpoints[z80::getPC()]=0; return; } int address = getnum(cmd); if (address<0 || address>65536) { sendToConsoleLog("Illegal address"); return; } getcmd(); uint8_t break_type = 7; if (cmd[0]!=0) { if (strcmp(cmd, "x")==0 || strcmp(cmd, "exec")==0) { break_type = 1; } else if (strcmp(cmd, "r")==0 || strcmp(cmd, "read")==0) { break_type = 2; } else if (strcmp(cmd, "w")==0 || strcmp(cmd, "write")==0) { break_type = 4; } else { sendToConsoleLog("Illegal break type"); return; } } breakpoints[address] &= ~break_type; } else if (strcmp(cmd, "m")==0 || strcmp(cmd, "mem")==0) { getcmd(); int address = getnum(cmd); if (address<0 || address>65536) { sendToConsoleLog("Illegal memory address"); return; } mem_viewer_pos = address; } else if (strcmp(cmd, "st")==0 || strcmp(cmd, "save")==0) { getcmd(); char filename[256]; strcpy(filename, cmd); savestate(filename); } else if (strcmp(cmd, "l")==0 || strcmp(cmd, "load")==0) { getcmd(); char filename[256]; strcpy(filename, cmd); //getcmd(); //char address[256]; //strcpy(address, cmd); //loadngo(filename, address); loadstate(filename); } else if (strcmp(cmd, "t")==0 || strcmp(cmd, "tape")==0) { getcmd(); if (strcmp(cmd, "load")==0) { getcmd(); char filename[256]; strcpy(filename, cmd); zx_tape::load(filename); } else if (strcmp(cmd, "play")==0) { zx_tape::play(); } } else if (strcmp(cmd, "poke")==0) { getcmd(); int address = getnum(cmd); getcmd(); int value = getnum(cmd); uint8_t *mem = z80::getMem(); mem[address] = value; } else if (strcmp(cmd, "peek")==0) { getcmd(); int address = getnum(cmd); uint8_t *mem = z80::getMem(); int value = mem[address]; char tmp[10]; sendToConsoleLog(SDL_itoa(value, tmp, 10)); } else if (strcmp(cmd, "reg")==0) { getcmd(); if (strcmp(cmd, "f")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[0] = value; } else if (strcmp(cmd, "a")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[1] = value; } else if (strcmp(cmd, "c")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[2] = value; } else if (strcmp(cmd, "b")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[3] = value; } else if (strcmp(cmd, "e")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[4] = value; } else if (strcmp(cmd, "d")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[5] = value; } else if (strcmp(cmd, "l")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[6] = value; } else if (strcmp(cmd, "h")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[7] = value; } else { sendToConsoleLog("Syntax error: invalid register"); return; } } else if (strcmp(cmd, "g")==0 || strcmp(cmd, "goto")==0) { getcmd(); int address = getnum(cmd); if (address<0 || address>=65536) { sendToConsoleLog("Illegal address"); return; } if (z80::getMemTag(address)!=MEMTAG_INST) address = find_previous_opcode(address); z80debug::setcursor(address); } else if (strcmp(cmd, "sym")==0 || strcmp(cmd, "symbol")==0) { getcmd(); if (strcmp(cmd, "add")==0) { getcmd(); char tmp[12]; strcpy(tmp, cmd); getcmd(); if (cmd[0]==0) { z80dis::setSymbol(z80::getPC(), tmp); } else { int value = getnum(cmd); z80dis::setSymbol(value, tmp); } sendToConsoleLog("Symbol added"); } else if (strcmp(cmd, "del")==0) { getcmd(); if (cmd[0]==0) { z80dis::setSymbol(z80::getPC(), NULL); } else { int value = getnum(cmd); z80dis::setSymbol(value, NULL); } sendToConsoleLog("Symbol removed"); } } else if (strcmp(cmd, "ula")==0) { getcmd(); if (strcmp(cmd, "keydown")==0) { getcmd(); zx_ula::keydown(cmd[0]); sendToConsoleLog("Keydown sent for key: "); sendMoreToConsoleLog(cmd); } else if (strcmp(cmd, "keyup")==0) { getcmd(); zx_ula::keyup(cmd[0]); sendToConsoleLog("Keyup sent for key: "); sendMoreToConsoleLog(cmd); } } } const bool isbreak(const uint16_t address, const uint8_t type) { return (breakpoints[address]&type); } uint32_t next() { breakpoints[z80::getPC()+z80dis::getOpcodeSize(z80::getPC())] |= 8; const uint32_t t = z80::step(); cont(); return t; } void setmemmodified(const uint16_t addr) { mem_modified[addr] = true; } void loadngo(const char* filename, const char* addr) { int address = getnum(addr); if (address<0 || address>65536) { sendToConsoleLog("Illegal offset"); return; } FILE *f = fopen(filename, "rb"); fseek(f, 0, SEEK_END); int size = ftell(f); fseek(f, 0, SEEK_SET); uint8_t *memory = z80::getMem(); fread(memory+address, size, 1, f); fclose(f); z80::setPC(address); is_debugging = is_paused = false; } void savestate(const char *filename) { uint8_t *regs = z80::getRegs(); uint8_t *memory = z80::getMem(); FILE *f = fopen(filename, "wb"); fwrite(regs, 31, 1, f); fwrite(&memory[0x4000], 0xc000, 1, f); fclose(f); } void loadstate(const char *filename) { uint8_t *regs = z80::getRegs(); uint8_t *memory = z80::getMem(); FILE *f = fopen(filename, "rb"); fread(regs, 31, 1, f); fread(&memory[0x4000], 0xc000, 1, f); fclose(f); } void setcursor(const uint16_t address) { cursor = address; } void cursorfwd() { cursor += z80dis::getOpcodeSize(cursor); } void cursorback() { cursor = find_previous_opcode(cursor); } namespace history { void store() { buffer[++top] = z80::getPC(); pos=top; } void gototop() { pos = top; z80::setPC(buffer[pos]); cursor = z80::getPC(); } void goforward() { if (pos == top) return; z80::setPC(buffer[++pos]); cursor = z80::getPC(); } void goback() { if (uint8_t(pos-1) == top) return; z80::setPC(buffer[--pos]); cursor = z80::getPC(); } } }