Files
atari2600/source/6502debugger.cpp
T
raimon@prometeo e245410c0a - [NEW] 6502dis i 6502debugger implementats
- [FIX] font.bmp afegit
- [NEW] Afegides regles de comprobació de memòria en la versió debug de compilació en el lagueirtofile
- [FIX] la rom ha de carregar abans de atari2600::init()
- [NEW] Implementació bàsica del PIA completada.
- [FIX] El framebuffer intern en el mòdul TIA era incorrecte.
- [NEW] Afegits accesors als registres del 6502
- [NEW] m6502::reset()
- [FIX] [6502m] Corregits opcodes amb direccionament implicit: CLC, SEC, PHA, CLI, PLA, SEI, DEY, TXA, TYA, TXS, TAY, TAX, CLV, TSX, INY, DEX, CLD, INX, NOP i SED). Augmentaven el PC una posició de més.
2026-09-17 17:43:33 +02:00

1161 lines
41 KiB
C++

#include "6502debugger.h"
#include <SDL3/SDL.h>
#include <stdio.h>
#include "atari2600.h"
#include "mem.h"
#include "6502m.h"
#include "6502dis.h"
#include "display.h"
#include "ui.h"
#include "ui_window.h"
#include "rom.h"
#define RESIZING_NONE 0
#define RESIZING_MEMORY 1
#define RESIZING_CONSOLE 2
#define RESIZING_SYMBOLS 3
#define RESIZING_X 4
namespace m6502debugger
{
int midx = 58;
int mem_y = 20;
int con_y = 28;
int sym_y = 24;
int win_h = 44;
int mem_h = 8;
int con_h = 6;
int sym_h = 14;
uint8_t tags[65536];
int resizing_type = RESIZING_MEMORY;
bool resizing = false;
namespace history
{
uint16_t buffer[65536];
uint8_t top=0;
uint8_t pos=0;
}
uint16_t oPC;
uint8_t oA, oX, oY, oS, oP;
bool mem_modified[65536]; //[CHECK]
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;
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;
uint8_t use[256];
uint16_t line_address[256];
uint16_t line_breakpoint_address[256];
int num_breakpoint_lines=0;
bool sync_mem_with_cursor = false;
uint8_t search_sequence[32];
int search_sequence_len=0;
int search_pos=0;
uint16_t inspected_value = 255;
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;
}
const char *todec(int value, bool sign)
{
if (sign)
sprintf(temp, "%i", int8_t(value));
else
sprintf(temp, "%u", value);
return temp;
}
bool eventHandler(SDL_Event *e)
{
if (e->type == SDL_EVENT_MOUSE_MOTION) {
SDL_ShowCursor();
}
if (atari2600::is_paused()) {
if ((e->type==SDL_EVENT_WINDOW_SHOWN) || (e->type==SDL_EVENT_WINDOW_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_h = win_h - sym_y;
refresh();
} else if (e->type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
hide();
//zxscreen::focus();
}
if (e->type == SDL_EVENT_MOUSE_WHEEL) {
if (e->wheel.mouse_x<midx*CHR_W && e->wheel.mouse_y<mem_y*CHR_H) {
if (e->wheel.y>0) {
cursorback();
refresh();
} else if (e->wheel.y<0) {
cursorfwd();
refresh();
}
} else if (e->wheel.mouse_x<midx*CHR_W && e->wheel.mouse_y>=mem_y*CHR_H && e->wheel.mouse_y<con_y*CHR_H) {
if (e->wheel.y>0) {
mem_viewer_pos-=0x10;
refresh();
} else if (e->wheel.y<0) {
mem_viewer_pos+=0x10;
refresh();
}
}
}
if (e->type == SDL_EVENT_KEY_DOWN) {
if (e->key.scancode==SDL_SCANCODE_ESCAPE) {
return false;
} else if (e->key.scancode==SDL_SCANCODE_F1) {
history::gototop();
refresh();
} else if (e->key.scancode==SDL_SCANCODE_F2) {
history::goback();
refresh();
} else if (e->key.scancode==SDL_SCANCODE_F3) {
history::goforward();
refresh();
} else if (e->key.scancode==SDL_SCANCODE_F9) {
const uint16_t address = cursor;
if (breakpoints[line_address[address]]==0)
breakpoints[line_address[address]]=1;
else
breakpoints[line_address[address]]=0;
refresh();
} else if (e->key.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();
refresh();
} else if (e->key.scancode==SDL_SCANCODE_DOWN) {
if (console_history_nav != console_history_pos) console_history_nav++;
//z80debug::cursorfwd();
refresh();
} else if ( (e->key.scancode==SDL_SCANCODE_RETURN) || (e->key.scancode==SDL_SCANCODE_KP_ENTER)) {
executeConsole();
} else if (e->key.scancode==SDL_SCANCODE_BACKSPACE) {
DeleteCharConsole();
}
}
if (e->type == SDL_EVENT_MOUSE_MOTION) {
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;
}
const int chrx = e->motion.x/CHR_W;
const int chry = e->motion.y/CHR_H;
//printf("%ix%i\n", chrx, chry);
// Si pasa per damunt de l'adreça en el desensamblador
if ( (chrx>1) && (chrx<6) && (chry<mem_y-1) && (chry>0) ) {
inspected_value = line_address[chry-1];
refresh();
// Si pasa per damunt dels bytes que formen el opcode en el desensamblador
} else if ((chrx>=19) && (chrx<30) && (chry<mem_y-1) && (chry>0)) {
const uint16_t address = line_address[chry-1];
const int opcodesize = m6502dis::getOpcodeSize(address);
const int byte = (chrx-19)/3;
if (byte < opcodesize) {
inspected_value = mem::read(address+byte);
refresh();
}
// Si pasa per damunt de l'adreça en el visor de memòria
} else if ((chrx>1) && (chrx<6) && (chry>mem_y) && (chry<mem_y+mem_h-1)) {
inspected_value = mem_viewer_pos + (chry-mem_y-1)*16;
refresh();
// Si pasa per damunt d'un byte en el visor de memòria
} else if ((chrx>6) && (chrx<55) && (chry>mem_y) && (chry<mem_y+mem_h-1)) {
const uint16_t address = mem_viewer_pos + (chry-mem_y-1)*16 + (chrx-7)/3;
inspected_value = mem::read(address);
refresh();
// Si pasa per damunt d'un registre
} else if ((chrx>=midx+3) && (chrx<midx+5) && (chry==1)) { inspected_value = m6502::getA(); refresh();
} else if ((chrx>=midx+3) && (chrx<midx+5) && (chry==2)) { inspected_value = m6502::getX(); refresh();
} else if ((chrx>=midx+3) && (chrx<midx+5) && (chry==3)) { inspected_value = m6502::getY(); refresh();
} else if ((chrx>=midx+3) && (chrx<midx+5) && (chry==4)) { inspected_value = m6502::getS(); refresh();
} else if ((chrx>=midx+3) && (chrx<midx+5) && (chry==5)) { inspected_value = m6502::getP(); refresh();
} else if ((chrx>=midx+20) && (chrx<midx+24) && (chry==1)) { inspected_value = m6502::getPC(); refresh();
}
} 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_EVENT_MOUSE_BUTTON_DOWN) {
const int chrx = e->button.x/CHR_W;
const int chry = e->button.y/CHR_H;
//printf("%ix%i\n", chrx, chry);
if ( (e->button.x<midx*CHR_W) && (e->button.y<(mem_y-1)*CHR_H) && (e->button.y>CHR_H) ) {
const uint16_t address = ((e->button.y)/CHR_H)-1;
if (breakpoints[line_address[address]]==0)
breakpoints[line_address[address]]=1;
else
breakpoints[line_address[address]]=0;
refresh();
}
if (chrx>=midx+12 && chry>=9 && chry<sym_y-1) {
const int breakpoint_selected = chry-9;
if (num_breakpoint_lines>breakpoint_selected) {
cursor = line_breakpoint_address[breakpoint_selected];
refresh();
}
}
if (chrx>=10 && chrx<=12 && chry==mem_y) {
sync_mem_with_cursor = ! sync_mem_with_cursor;
refresh();
}
if (chrx>=midx+2 && chry>=sym_y+1) {
const int line = chry-(sym_y+1);
if (line<m6502dis::getNumSymbols()) {
cursor = m6502dis::getSymbolAddress(line);
refresh();
}
}
if (!resizing && resizing_type != RESIZING_NONE) {
resizing = true;
}
}
if (e->type == SDL_EVENT_MOUSE_BUTTON_UP) {
if (resizing) {
resizing = false;
}
}
if (e->type == SDL_EVENT_TEXT_INPUT) {
sendToConsole(e->text.text);
}
}
return true;
}
void processCommand();
uint8_t getTag(uint16_t address)
{
return tags[address];
}
void setTag(uint16_t address, uint8_t value)
{
tags[address] = value;
}
void onInstructionExecute(uint16_t address)
{
uint8_t &tag = tags[address];
if ( !(tag & MEMTAG_IGNORE) ) tag |= MEMTAG_INST;
tag |= (!(tag&MEMTAG_TOUCHED) ? MEMTAG_TREPEAT : MEMTAG_TINST);
setcursor(m6502::getPC());
history::store();
}
void onMemRead(uint16_t address, bool code)
{
if (isbreak(address, 2)) stop();
uint8_t &tag = tags[address];
if ( tag & MEMTAG_IGNORE) return;
if ( code ) {
tag |= MEMTAG_CODE;
} else if ( !(tag & MEMTAG_INST) ) {
tag |= MEMTAG_DATA;
}
}
void onMemWrite(uint16_t address)
{
if (isbreak(address, 4)) stop();
mem_modified[address] = true;
uint8_t &tag = tags[address];
if ( tag & MEMTAG_IGNORE ) return;
if ( !(tag & MEMTAG_INST) ) {
tag |= ( MEMTAG_DATA | MEMTAG_TDATA );
}
}
void init()
{
for (int i=0; i<65536; ++i) {
breakpoints[i]=0;
tags[i] = MEMTAG_NONE;
}
if (!cur_df) cur_df = SDL_GetCursor();
if (!cur_ns) cur_ns = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE);
if (!cur_we) cur_we = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE);
memchr(console_log, 0, 65536);
memchr(console_history, 0, 65536);
//show();
}
void show()
{
if (!win) {
win = SDL_CreateWindow("6502 Debugger", 98*CHR_W, 44*CHR_H, 0/*SDL_WINDOW_RESIZABLE*/);
ren = SDL_CreateRenderer(win, 0);
ui::window::registerWindow(SDL_GetWindowID(win), eventHandler);
tex = ui::createtexture(ren);
}
SDL_StartTextInput(win);
focus();
}
void focus()
{
if (win) {
SDL_RaiseWindow(win);
refresh();
}
}
void hide()
{
SDL_StopTextInput(win);
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()
{
//speaker::disable();
//is_paused = true;
breakpoints[m6502::getPC()] &= ~8;
}
void stop()
{
//history::gototop();
//zxscreen::setTitle(" (stopped)");
pause();
//z80::resetStackedCalls();
//is_debugging = true;
show();
/*refresh();*/
//if ( zxscreen::getFullRefresh()) zxscreen::fullrefresh();
}
void cont() {
display::setTitle("");
//is_debugging = is_paused = false;
hide();
refresh();
display::focus();
//speaker::enable();
}
uint16_t find_previous_opcode(uint16_t pc)
{
pc--;
if ( !(tags[pc] & (MEMTAG_CODE | MEMTAG_INST) ) ) return pc;
while ( !(tags[pc] & MEMTAG_INST) ) pc--;
return pc;
}
void printDissasemblerLine(const uint16_t address, const int line, const bool heuristics=false)
{
uint8_t colors[4] = { COLOR_RED, COLOR_CYAN, COLOR_WHITE, COLOR_WHITE};
const uint8_t tag = tags[address];
if ( !(tag & (MEMTAG_TINST | MEMTAG_TREPEAT)) ) colors[3]=COLOR_GRAY;
if ( !(tag & MEMTAG_TOUCHED) ) colors[1]=COLOR_GRAY;
if (atari2600::is_paused() && (address == m6502::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 ( (tag & MEMTAG_INST) || heuristics ) {
const char *sym = m6502dis::getSymbol(address);
if (sym[0]!=0) ui::printtxt(7,line, sym, COLOR_YELLOW);
const int opcodesize = m6502dis::getOpcodeSize(address);
for (int i=0; i<opcodesize; ++i) {
const uint8_t tag = tags[address+i];
const uint32_t color = !(tag & MEMTAG_KNOWN) ? COLOR_GRAY : (tag & MEMTAG_DATA) ? ( (tag & (MEMTAG_CODE|MEMTAG_INST)) ? COLOR_MAGENTA : COLOR_BLUE ) : COLOR_GREEN;
ui::printrect(19+i*3,line,2,1,color);
}
ui::printtxt(19,line, m6502dis::getOpcode(address), colors[2]);
ui::printtxt(31,line, m6502dis::getAsm(address), colors[3]);
} else {
ui::printrect(19,line,2,1,COLOR_GRAY);
ui::printtxt(19,line, tohex(mem::read(address),2), COLOR_WHITE);
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);
uint16_t pc = cursor; //z80::getPC();
if (sync_mem_with_cursor) mem_viewer_pos = cursor;
uint16_t pos = pc;
int num_lines = mem_y-2;
line_address[(num_lines/2)-1] = pos;
ui::printvoidrect(0,(num_lines/2)-1, midx-2,1, COLOR_BLUE);
printDissasemblerLine(pc, (num_lines/2)-1, true);
for (int i=num_lines/2;i<num_lines;++i) {
pos += m6502dis::getOpcodeSize(pos);
line_address[i] = pos;
printDissasemblerLine(pos, i, true);
}
pos = pc;
for (int i=(num_lines/2)-2;i>=0;--i) {
pos = find_previous_opcode(pos);
line_address[i] = 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, "A ", COLOR_WHITE);
ui::printtxt(0,1, "X ", COLOR_WHITE);
ui::printtxt(0,2, "Y ", COLOR_WHITE);
ui::printtxt(0,3, "S ", COLOR_WHITE);
ui::printtxt(0,4, "P ", COLOR_WHITE);
ui::printtxt(0,5, " ", COLOR_WHITE);
ui::printtxt(3,0, tohex(m6502::getA(), 2), oA != m6502::getA() ? COLOR_RED : COLOR_GRAY);
ui::printtxt(19,0, tohex(m6502::getPC(), 4), oPC != m6502::getPC() ? COLOR_RED : COLOR_GRAY);
ui::printtxt(3,1, tohex(m6502::getX(), 4), oX != m6502::getX() ? COLOR_RED : COLOR_GRAY);
ui::printtxt(3,2, tohex(m6502::getY(), 4), oY != m6502::getY() ? COLOR_RED : COLOR_GRAY);
ui::printtxt(3,3, tohex(m6502::getS(), 4), oS != m6502::getS() ? COLOR_RED : COLOR_GRAY);
ui::printtxt(3,4, tohex(m6502::getP(), 4), oP != m6502::getP() ? COLOR_RED : COLOR_GRAY);
const uint8_t flags = m6502::getP();
const uint8_t mod_flags = flags ^ oP;
ui::printtxt(0,5,"N", flags&0x80 ? (mod_flags&0x80 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x80 ? COLOR_BROWN : COLOR_GRAY) );
ui::printtxt(1,5,"V", flags&0x40 ? (mod_flags&0x40 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x40 ? COLOR_BROWN : COLOR_GRAY) );
ui::printtxt(2,5,"-", flags&0x20 ? (mod_flags&0x20 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x20 ? COLOR_BROWN : COLOR_GRAY) );
ui::printtxt(3,5,"B", flags&0x10 ? (mod_flags&0x10 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x10 ? COLOR_BROWN : COLOR_GRAY) );
ui::printtxt(4,5,"D", flags&0x08 ? (mod_flags&0x08 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x08 ? COLOR_BROWN : COLOR_GRAY) );
ui::printtxt(5,5,"I", flags&0x04 ? (mod_flags&0x04 ? COLOR_RED : COLOR_WHITE) : (mod_flags&0x04 ? COLOR_BROWN : COLOR_GRAY) );
ui::printtxt(6,5,"Z", 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,12,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 = 0x0100 | m6502::getS();//-(((sym_y-12)>>1)<<1);
for (int i=0; i<10; ++i) {
uint8_t c1=COLOR_CYAN, c2=COLOR_GRAY;
if ((sp & 0xff) == m6502::getS()) {
ui::printrect(0,i,9,1,COLOR_BLUE);
c1 = c2 = COLOR_YELLOW;
}
ui::printtxt(0,i, tohex(sp, 4), c1);
uint16_t value = mem::read(sp) + (mem::read(sp+1)<<8);
ui::printtxt(5,i, tohex(value, 4), c2);
sp+=2;
}
// BREAKPOINTS
// ******************************************
ui::setoffset(0, 0);
ui::box(midx+11,8,14,12,COLOR_WHITE);
ui::printrect(midx+13,8, 9,1, COLOR_DARK);
ui::printtxt(midx+14,8, "BREAKS:", COLOR_WHITE);
ui::setoffset(midx+12, 9);
int line=0;
num_breakpoint_lines=0;
for (int i=0; i<65536; ++i) {
if (breakpoints[i]&7) {
line_breakpoint_address[line] = i;
num_breakpoint_lines++;
uint8_t colors[2] = {COLOR_WHITE, COLOR_GRAY};
if (i==cursor) {
ui::printrect(1,line,10,1,COLOR_BLUE);
colors[0]=colors[1]=COLOR_YELLOW;
}
ui::printtxt(2,line, tohex(i,4), colors[0]);
ui::printtxt(7,line, breakpoints[i]&1?"x":"-", colors[1]);
ui::printtxt(8,line, breakpoints[i]&2?"r":"-", colors[1]);
ui::printtxt(9,line, breakpoints[i]&4?"w":"-", colors[1]);
line++;
if (line>9) break;
}
}
// INSPECTOR
// ******************************************
ui::setoffset(0, 0);
ui::box(midx,20,25,4,COLOR_WHITE);
ui::printrect(midx+2,20,12,1, COLOR_DARK);
ui::printtxt(midx+3,20, "INSPECTOR:", COLOR_WHITE);
ui::setoffset(midx+1, 21);
ui::printtxt(1,0,"VALUE:", COLOR_GRAY); ui::printtxt(8,0,"$", COLOR_CYAN);
ui::printtxt(9,0,tohex(inspected_value,4), COLOR_CYAN);
ui::printtxt(14,0,todec(inspected_value,false), COLOR_WHITE);
ui::printtxt(20,0,todec(inspected_value,true), COLOR_WHITE);
ui::printtxt(1,1,"H:", COLOR_GRAY); ui::printtxt(4,1,"$", COLOR_CYAN);
ui::printtxt(5,1,tohex(inspected_value>>8,2), COLOR_CYAN);
ui::printtxt(8,1,todec(inspected_value>>8,false), COLOR_WHITE);
ui::printtxt(12,1,"L:", COLOR_GRAY); ui::printtxt(15,1,"$", COLOR_CYAN);
ui::printtxt(16,1,tohex(inspected_value&0xff,2), COLOR_CYAN);
ui::printtxt(19,1,todec(inspected_value&0xff,false), COLOR_WHITE);
// MEMORY
// ******************************************
ui::setoffset(0, 0);
ui::box(0,mem_y,midx,mem_h,COLOR_WHITE);
ui::printrect(2,mem_y, 11,1, COLOR_DARK);
ui::printtxt(3,mem_y, "MEMORY:", COLOR_WHITE);
ui::printvoidrect(10,mem_y,3,1,COLOR_WHITE);
if (sync_mem_with_cursor) ui::printchar(11,mem_y,'X',COLOR_WHITE);
ui::setoffset(1, mem_y+1);
uint16_t mem_viewer_cursor = mem_viewer_pos;
for (int i=0; i<mem_h-2; ++i) {
ui::printtxt(1,i, tohex(mem_viewer_cursor, 4), COLOR_CYAN);
for (int j=0; j<16; ++j) {
const uint8_t tag = tags[mem_viewer_cursor];
const uint32_t color = !(tag & MEMTAG_KNOWN) ? COLOR_GRAY : (tag & MEMTAG_DATA) ? ( (tag & (MEMTAG_CODE|MEMTAG_INST)) ? COLOR_MAGENTA : COLOR_BLUE ) : COLOR_GREEN;
ui::printrect(6+j*3,i,2,1,color);
ui::printrect(54+j,i,1,1,color);
ui::printtxt(6+j*3, i, tohex(mem::read(mem_viewer_cursor), 2), ( tag & MEMTAG_MODIFIED) ? COLOR_RED : COLOR_WHITE);
ui::printchar(54+j, i, mem::read(mem_viewer_cursor), ( tag & MEMTAG_MODIFIED) ? 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);
}
// CONSOLE
// ******************************************
ui::setoffset(0, 0);
ui::box(0,con_y,midx,con_h,COLOR_WHITE);
ui::printrect(2,con_y, 10,1, COLOR_DARK);
ui::printtxt(3,con_y, "CONSOLE:", COLOR_WHITE);
ui::setoffset(1, con_y+1);
line = con_h-3;
ui::printtxt(0,line, ">", 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 = m6502dis::getNumSymbols();
for (int i=0;i<num_symbols;++i) {
uint8_t colors[2] = {COLOR_GRAY, COLOR_WHITE};
const uint16_t address = m6502dis::getSymbolAddress(i);
if (address==cursor) {
ui::printrect(0,line,23,1,COLOR_BLUE);
colors[0]=colors[1]=COLOR_YELLOW;
}
ui::printtxt(1, i, tohex(address,4), colors[0]);
ui::printtxt(6, i, m6502dis::getSymbol(address), colors[1]);
}
if (!atari2600::is_paused()) {
SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(ren, 0, 0, 0, 128);
int w, h;
SDL_GetWindowSize(win, &w, &h);
SDL_FRect rect {0,0,float(w), float(h)};
SDL_RenderFillRect(ren, &rect);
}
SDL_RenderPresent(ren);
for (int i=0; i<65536; ++i) mem_modified[i] = false;
oA = m6502::getA();
oX = m6502::getX();
oY = m6502::getY();
oS = m6502::getS();
oP = m6502::getP();
oPC = m6502::getPC();
}
void sendToConsole(const char* text)
{
if (console_history_nav != console_history_pos) {
strcpy(console_history[console_history_pos], console_history[console_history_nav]);
console_history_nav=console_history_pos;
}
if (strlen(console_history[console_history_pos])+strlen(text)<256) strcat(console_history[console_history_pos], text);
refresh();
}
void sendToConsoleLog(const char *text)
{
strcpy(console_log[++console_log_pos], text);
}
void sendMoreToConsoleLog(const char *text)
{
strcat(console_log[console_log_pos], text);
}
void DeleteCharConsole()
{
if (console_history_nav != console_history_pos) {
strcpy(console_history[console_history_pos], console_history[console_history_nav]);
console_history_nav=console_history_pos;
}
const int len = strlen(console_history[console_history_pos]);
console_history[console_history_pos][len-1] = 0;
refresh();
}
void executeConsole()
{
if (console_history_nav != console_history_pos) {
strcpy(console_history[console_history_pos], console_history[console_history_nav]);
console_history_nav=console_history_pos;
}
strcpy(console_log[++console_log_pos], ">");
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){
atari2600::step();
display::redraw();
} else if (strcmp(cmd, "c")==0 || strcmp(cmd, "cont")==0) {
atari2600::resume();
} else if (strcmp(cmd, "r")==0 || strcmp(cmd, "reset")==0) {
atari2600::init();
history::reset();
} else if (strcmp(cmd, "b")==0 || strcmp(cmd, "break")==0) {
getcmd();
if (cmd[0] == 0) { breakpoints[m6502::getPC()]=1; return; }
//if (cmd[0] == 'i') { z80::setOption(Z80_OPTION_BREAK_ON_INTERRUPT, !z80::getOption(Z80_OPTION_BREAK_ON_INTERRUPT)); sendToConsoleLog("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[m6502::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);
loadstate(filename);
} else if (strcmp(cmd, "rom")==0) {
getcmd();
if (strcmp(cmd, "load")==0) {
getcmd();
char filename[256];
strcpy(filename, cmd);
rom::load(filename);
atari2600::init();
history::reset();
}
} else if (strcmp(cmd, "poke")==0) {
getcmd();
int address = getnum(cmd);
getcmd();
int value = getnum(cmd);
mem::write(address, value);
} else if (strcmp(cmd, "peek")==0) {
getcmd();
int address = getnum(cmd);
int value = mem::read(address);
char tmp[10];
sendToConsoleLog(SDL_itoa(value, tmp, 10));
// } else if (strcmp(cmd, "reg")==0) {
// getcmd();
// if (strcmp(cmd, "a")==0) { getcmd(); int value = getnum(cmd); m6502::getRegs()[0] = value; }
// else if (strcmp(cmd, "x")==0) { getcmd(); int value = getnum(cmd); m6502::getRegs()[1] = value; }
// else if (strcmp(cmd, "y")==0) { getcmd(); int value = getnum(cmd); m6502::getRegs()[2] = value; }
// else if (strcmp(cmd, "s")==0) { getcmd(); int value = getnum(cmd); m6502::getRegs()[3] = value; }
// else if (strcmp(cmd, "p")==0) { getcmd(); int value = getnum(cmd); m6502::getRegs()[4] = value; }
// else if (strcmp(cmd, "pc")==0) { getcmd(); int value = getnum(cmd); m6502::getRegs()[24] = value&0xff; z80::getRegs()[25] = (value>>8)&0xff; }
// 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 ( !(tags[address] & MEMTAG_INST) ) address = find_previous_opcode(address);
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) {
m6502dis::setSymbol(m6502::getPC(), tmp);
} else {
int value = getnum(cmd);
m6502dis::setSymbol(value, tmp);
}
sendToConsoleLog("Symbol added");
} else if (strcmp(cmd, "del")==0) {
getcmd();
if (cmd[0]==0) {
m6502dis::setSymbol(m6502::getPC(), NULL);
} else {
int value = getnum(cmd);
m6502dis::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);
}
} else if (strcmp(cmd, "opcodes")==0) {
getcmd();
if (strcmp(cmd, "clear")==0 || strcmp(cmd, "reset")==0) {
clearUsedOpcodes();
} else if (strcmp(cmd, "mark")==0) {
markUsedOpcodes();
} else if (strcmp(cmd, "count")==0) {
printf("Num opcodes used: %i\n", getNumOpcodesUsed());
} else if (strcmp(cmd, "print")==0) {
printOpcodesUsed();
}
} else if (strcmp(cmd, "ignore")==0) {
getcmd();
const int address = getnum(cmd);
tags[address] |= MEMTAG_IGNORE;
} else if (strcmp(cmd, "search")==0) {
getcmd();
if (strcmp(cmd, "next")==0) {
search();
} else {
search(cmd);
}
} else if (strcmp(cmd, "show")==0) {
getcmd();
if (strcmp(cmd, "analyzer")==0) {
//z80analyze::show();
} else {
sendToConsoleLog("Unrecognized window. Usage: Show [analyzer]");
}
} else {
sendToConsoleLog("Unrecognized command.");
}
}
const bool isbreak(const uint16_t address, const uint8_t type)
{
return (breakpoints[address]&type);
}
uint32_t next()
{
breakpoints[m6502::getPC()+m6502dis::getOpcodeSize(m6502::getPC())] |= 8;
//const uint32_t t = m6502::step();
cont();
return 0; //t;
}
uint32_t stepout()
{
//z80::setOption(Z80_OPTION_BREAK_ON_RET, true);
//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);
uint32_t memsize;
uint8_t *memory = mem::getRawPointer(&memsize);
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();
//FILE *f = fopen(filename, "wb");
//fwrite(regs, 31, 1, f);
//mem::saveState(f);
//fclose(f);
}
void loadstate(const char *filename)
{
//uint8_t *regs = z80::getRegs();
//FILE *f = fopen(filename, "rb");
//fread(regs, 31, 1, f);
//mem::loadState(f);
//fclose(f);
//history::store();
//history::gototop();
}
void setcursor(const uint16_t address)
{
//if (!debugging()) return;
if ( !(tags[address] & MEMTAG_INST) )
cursor = find_previous_opcode(address);
else
cursor = address;
}
void cursorfwd()
{
cursor += m6502dis::getOpcodeSize(cursor);
}
void cursorback()
{
cursor = find_previous_opcode(cursor);
}
void useOpcode(const uint8_t opcode, const uint8_t base)
{
if (use[opcode]<255) use[opcode]++;
}
void clearUsedOpcodes()
{
for (int i=0; i<256; ++i) {
use[i]=0;
}
}
void markUsedOpcodes()
{
for (int i=0; i<256; ++i) {
if (use[i]==1) use[i]++;
}
}
const int getNumOpcodesUsed()
{
int count = 0;
for (int i=0; i<256; ++i) {
if (use[i]==1) count++;
}
return count;
}
void printOpcodesUsed()
{
for (int i=0; i<256; ++i) {
if (use[i]==1) {
printf("%2X\n",i);
}
}
}
uint8_t hexchartonum(char chr)
{
int num = chr-48;
if (num<0) return 0;
if (num>9) {
num-=7;
if (num>15) {
num-=32;
if (num<10 || num>15) return 0;
}
}
return uint8_t(num);
}
void search(const char *seq)
{
if (seq) {
search_pos = 0;
search_sequence_len=0;
while (*seq!=0) search_sequence[search_sequence_len++] = (hexchartonum(*(seq++))<<4) + hexchartonum(*(seq++));
}
int num_found=0;
while (search_pos<65536) {
if (search_sequence[num_found] == mem::read(search_pos)) {
num_found++; search_pos++;
if (num_found==search_sequence_len) {
mem_viewer_pos=search_pos-search_sequence_len;
return;
}
} else {
num_found=0; search_pos++;
}
}
sendToConsoleLog("Sequence not found.");
}
namespace history
{
void reset()
{
buffer[0]=buffer[1]=0;
cursor=pos=top=0;
}
void store()
{
buffer[++top] = m6502::getPC();
pos=top;
}
void gototop()
{
pos = top;
//m6502::setPC(buffer[pos]);
cursor = m6502::getPC();
}
void goforward()
{
if (pos == top) return;
//m6502::setPC(buffer[++pos]);
cursor = m6502::getPC();
}
void goback()
{
if (uint8_t(pos-1) == top) return;
//m6502::setPC(buffer[--pos]);
cursor = m6502::getPC();
}
}
}