#include "z80debug.h" #include #include "z80.h" #include "z80dis.h" #include "zx_ula.h" namespace z80debug { #define CHR_W 6 #define CHR_H 13 #define COLOR_BLACK 0 #define COLOR_DARK_BLUE 1 #define COLOR_GREEN 2 #define COLOR_TEAL 3 #define COLOR_BROWN 4 #define COLOR_PURPLE 5 #define COLOR_ORANGE 6 #define COLOR_GRAY 7 #define COLOR_DARK 8 #define COLOR_BLUE 9 #define COLOR_LIME 10 #define COLOR_CYAN 11 #define COLOR_RED 12 #define COLOR_MAGENTA 13 #define COLOR_YELLOW 14 #define COLOR_WHITE 15 uint8_t colors[16][3] = { {0,0,0}, {0,0,128}, {0,128,0}, {0,128,128}, {128,0,0}, {128,0,128}, {255,128,0}, {128,128,128}, {30,30,30}, {0,0,255}, {0,255,0}, {0,255,255}, {255,0,0}, {255,0,255}, {255,255,0}, {255,255,255}, }; uint16_t oAF, oBC, oDE, oHL, oAF2, oBC2, oDE2, oHL2, oIX, oIY, oSP, oPC; bool mem_modified[65536]; uint8_t offset_x = 0; uint8_t offset_y = 0; SDL_Window *win = nullptr; SDL_Renderer *ren = nullptr; SDL_Texture *tex = nullptr; bool is_debugging=false; uint16_t mem_viewer_pos = 0; char console[256]; char console_error[256]; uint8_t breakpoints[65536]; 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; } void processCommand(); void show() { is_debugging = false; if (win) return; win = SDL_CreateWindow("Z80 Debugger", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 71*CHR_W, 34*CHR_H, SDL_WINDOW_RESIZABLE); ren = SDL_CreateRenderer(win, -1, 0); tex = SDL_CreateTextureFromSurface(ren, SDL_LoadBMP("font.bmp")); SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); for (int i=0; i<65536; ++i) breakpoints[i]=0; z80debug::refresh(); } void stop() { zx_ula::sound_disable(); is_debugging = true; breakpoints[z80::getPC()] &= ~8; refresh(); } void cont() { is_debugging = false; refresh(); zx_ula::sound_enable(); } const bool debugging() { return is_debugging; } void box(int x, int y, int w, int h, uint8_t color) { SDL_Rect rect {((offset_x+x)*CHR_W)+3, ((offset_y+y)*CHR_H)+6, w*CHR_W-6, h*CHR_H-13}; SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255); SDL_RenderDrawRect(ren, &rect); } void printrect(int x, int y, int w, int h, uint8_t color) { SDL_Rect rect {(offset_x+x)*CHR_W, (offset_y+y)*CHR_H, w*CHR_W, h*CHR_H}; SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255); SDL_RenderFillRect(ren, &rect); } void printchar(int x, int y, char chr, uint8_t color=255) { if (color != 255) SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255); if (chr==32) return; if (chr<32 || chr>127) chr = '.'; SDL_Rect src {((chr-32)&0xf)*CHR_W, ((chr-32)>>4)*CHR_H, CHR_W, CHR_H}; SDL_Rect dst {(offset_x+x)*CHR_W, (offset_y+y)*CHR_H, CHR_W, CHR_H}; SDL_RenderCopy(ren, tex, &src, &dst); } void printtxt(int x, int y, const char *text, uint8_t color) { SDL_SetTextureColorMod(tex, colors[color][0], colors[color][1], colors[color][2]); for (int i=0; i=0;--i) { pos = find_previous_opcode(pos); if (breakpoints[pos]&9) printtxt(0,i,"*", COLOR_RED); printtxt(1,i,tohex(pos,4), COLOR_CYAN); printtxt(7,i, z80dis::getOpcode(pos), COLOR_GRAY); printtxt(19,i, z80dis::getAsm(pos), COLOR_WHITE); } //for (int i=0;i<20;++i) printtxt(1,i,tohex(pc,4), COLOR_CYAN); offset_x = offset_y = 0; box(46,0,25,8,COLOR_WHITE); printrect(48,0, 12,1, COLOR_DARK); printtxt(49,0, "REGISTERS:", COLOR_WHITE); offset_x=47; offset_y=1; printtxt(0,0, "AF AF' IX ", COLOR_WHITE); printtxt(0,1, "BC BC' IY ", COLOR_WHITE); printtxt(0,2, "DE DE' SP ", COLOR_WHITE); printtxt(0,3, "HL HL' PC ", COLOR_WHITE); printtxt(0,4, "(BC) (DE) (HL) ", COLOR_WHITE); printtxt(3,0, tohex(z80::getAF(), 4), oAF != z80::getAF() ? COLOR_RED : COLOR_GRAY); printtxt(11,0, tohex(z80::getAF(true), 4), oAF2 != z80::getAF(true) ? COLOR_RED : COLOR_GRAY); printtxt(19,0, tohex(z80::getIX(), 4), oIX != z80::getIX() ? COLOR_RED : COLOR_GRAY); printtxt(3,1, tohex(z80::getBC(), 4), oBC != z80::getBC() ? COLOR_RED : COLOR_GRAY); printtxt(11,1, tohex(z80::getBC(true), 4), oBC2 != z80::getBC(true) ? COLOR_RED : COLOR_GRAY); printtxt(19,1, tohex(z80::getIY(), 4), oIY != z80::getIY() ? COLOR_RED : COLOR_GRAY); printtxt(3,2, tohex(z80::getDE(), 4), oDE != z80::getDE() ? COLOR_RED : COLOR_GRAY); printtxt(11,2, tohex(z80::getDE(true), 4), oDE2 != z80::getDE(true) ? COLOR_RED : COLOR_GRAY); printtxt(19,2, tohex(z80::getSP(), 4), oSP != z80::getSP() ? COLOR_RED : COLOR_GRAY); printtxt(3,3, tohex(z80::getHL(), 4), oHL != z80::getHL() ? COLOR_RED : COLOR_GRAY); printtxt(11,3, tohex(z80::getHL(true), 4), oHL2 != z80::getHL(true) ? COLOR_RED : COLOR_GRAY); printtxt(19,3, tohex(z80::getPC(), 4), oPC != z80::getPC() ? COLOR_RED : COLOR_GRAY); printtxt(5,4, tohex(memory[z80::getBC()], 2), COLOR_GRAY); printtxt(13,4, tohex(memory[z80::getDE()], 2), COLOR_GRAY); 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); printtxt(0,5,"S", flags&0x80 ? (mod_flags&0x80 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x80 ? COLOR_BROWN : COLOR_GRAY) ); printtxt(1,5,"Z", flags&0x40 ? (mod_flags&0x40 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x40 ? COLOR_BROWN : COLOR_GRAY) ); printtxt(2,5,"X", flags&0x20 ? (mod_flags&0x20 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x20 ? COLOR_BROWN : COLOR_GRAY) ); printtxt(3,5,"H", flags&0x10 ? (mod_flags&0x10 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x10 ? COLOR_BROWN : COLOR_GRAY) ); printtxt(4,5,"Y", flags&0x08 ? (mod_flags&0x08 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x08 ? COLOR_BROWN : COLOR_GRAY) ); printtxt(5,5,"P", flags&0x04 ? (mod_flags&0x04 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x04 ? COLOR_BROWN : COLOR_GRAY) ); printtxt(6,5,"N", flags&0x02 ? (mod_flags&0x02 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x02 ? COLOR_BROWN : COLOR_GRAY) ); printtxt(7,5,"C", flags&0x01 ? (mod_flags&0x01 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x01 ? COLOR_BROWN : COLOR_GRAY) ); offset_x = offset_y = 0; box(46,8,11,12,COLOR_WHITE); printrect(48,8, 8,1, COLOR_DARK); printtxt(49,8, "STACK:", COLOR_WHITE); offset_x=47;offset_y=9; uint16_t sp = z80::getSP()-8; for (int i=0; i<10; ++i) { uint8_t c1=COLOR_CYAN, c2=COLOR_GRAY; if (sp == z80::getSP()) { printrect(0,i,9,1,COLOR_BLUE); c1 = c2 = COLOR_YELLOW; } printtxt(0,i, tohex(sp, 4), c1); printtxt(5,i, tohex(*((uint16_t*)&memory[sp]),4), c2); sp+=2; } offset_x = offset_y = 0; box(57,8,14,12,COLOR_WHITE); printrect(59,8, 9,1, COLOR_DARK); printtxt(60,8, "BREAKS:", COLOR_WHITE); offset_x=58;offset_y=9; int line=0; for (int i=0; i<65536; ++i) { if (breakpoints[i]&7) { printtxt(2,line, tohex(i,4), COLOR_WHITE); printtxt(7,line, breakpoints[i]&1?"x":"-", COLOR_GRAY); printtxt(8,line, breakpoints[i]&2?"r":"-", COLOR_GRAY); printtxt(9,line, breakpoints[i]&4?"w":"-", COLOR_GRAY); line++; if (line>9) break; } } offset_x=offset_y=0; box(0,20,71,8,COLOR_WHITE); printrect(2,20, 9,1, COLOR_DARK); printtxt(3,20, "MEMORY:", COLOR_WHITE); offset_x=1; offset_y=21; uint16_t mem_viewer_cursor = mem_viewer_pos; for (int i=0; i<6; ++i) { printtxt(0,i, tohex(mem_viewer_cursor, 4), COLOR_CYAN); for (int j=0; j<16; ++j) { printtxt(5+j*3, i, tohex(memory[mem_viewer_cursor],2), mem_modified[mem_viewer_cursor] ? COLOR_RED : COLOR_WHITE); printchar(53+j, i, memory[mem_viewer_cursor], mem_modified[mem_viewer_cursor] ? COLOR_BROWN : COLOR_GRAY); mem_viewer_cursor++; } //printtxt(5,0, "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", COLOR_WHITE); //printtxt(53,0, "0123456789AB\tDEF", COLOR_GRAY); } offset_x=offset_y=0; box(0,28,71,6,COLOR_WHITE); printrect(2,28, 10,1, COLOR_DARK); printtxt(3,28, "CONSOLE:", COLOR_WHITE); offset_x=1; offset_y=29; printtxt(0,0, ">", COLOR_WHITE); printtxt(1,0, console, COLOR_WHITE); printtxt(strlen(console)+1,0, "\x7F", COLOR_WHITE); printtxt(1,1, console_error, COLOR_RED); SDL_RenderPresent(ren); for (int i=0; i<65536; ++i) mem_modified[i] = false; oAF = z80::getAF(); oBC = z80::getBC(); oDE = z80::getDE(); oHL = z80::getHL(); oAF2 = z80::getAF(true); oBC2 = z80::getBC(true); oDE2 = z80::getDE(true); oHL2 = z80::getHL(true); oIX = z80::getIX(); oIY = z80::getIY(); oSP = z80::getSP(); oPC = z80::getPC(); } void sendToConsole(const char* text) { if (strlen(console)+strlen(text)<256) strcat(console, text); refresh(); } void DeleteCharConsole() { const int len = strlen(console); console[len-1] = 0; refresh(); } void executeConsole() { processCommand(); console[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() { console_error[0]=0; char cmd[256]; int i=0; char *console_ptr = console; 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) { z80::reset(z80::getMem()); 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; } int address = getnum(cmd); if (address<0 || address>65536) { strcpy(console_error, "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 { strcpy(console_error, "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) { strcpy(console_error, "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 { strcpy(console_error, "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) { strcpy(console_error, "Illegal memory address"); return; } mem_viewer_pos = address; } 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); } } 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) { strcpy(console_error, "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 = false; } }