mirror of
https://github.com/emu-russia/pureikyubu.git
synced 2025-04-02 10:42:15 -04:00
529 lines
12 KiB
C++
529 lines
12 KiB
C++
// text output and refresh
|
|
#include "pch.h"
|
|
|
|
WINDControl wind;
|
|
ROLLControl roll;
|
|
|
|
static const char *logcol[] = {
|
|
"<font color=#000000>",
|
|
"<font color=#000080>",
|
|
"<font color=#008000>",
|
|
"<font color=#008080>",
|
|
"<font color=#800000>",
|
|
"<font color=#800080>",
|
|
"<font color=#808000>",
|
|
"<font color=#C0C0C0>",
|
|
"<font color=#808080>",
|
|
"<font color=#0000FF>",
|
|
"<font color=#00FF00>",
|
|
"<font color=#00FFFF>",
|
|
"<font color=#FF0000>",
|
|
"<font color=#FF00FF>",
|
|
"<font color=#FFFF00>",
|
|
"<font color=#FFFFFF>"
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
int con_wraproll(int roll, int value)
|
|
{
|
|
roll += value;
|
|
if(roll >= CON_LINES) return roll - CON_LINES;
|
|
else if(roll < 0) return roll + CON_LINES;
|
|
else return roll;
|
|
}
|
|
|
|
void con_set_disa_cur(uint32_t addr)
|
|
{
|
|
con.disa_cursor = addr & ~3;
|
|
con.text = con.disa_cursor - (wind.disa_h - 1) / 2 * 4;
|
|
con.update |= CON_UPDATE_DISA;
|
|
}
|
|
|
|
void con_recalc_wnds()
|
|
{
|
|
wind.regs_y = 0;
|
|
|
|
if(wind.visible && CON_UPDATE_REGS)
|
|
wind.data_y = wind.regs_y + wind.regs_h;
|
|
else
|
|
wind.data_y = wind.regs_y;
|
|
|
|
if(wind.visible && CON_UPDATE_DATA)
|
|
wind.disa_y = wind.data_y + wind.data_h;
|
|
else
|
|
wind.disa_y = wind.data_y;
|
|
|
|
if(wind.visible && CON_UPDATE_DISA)
|
|
wind.roll_y = wind.disa_y + wind.disa_h;
|
|
else
|
|
wind.roll_y = wind.disa_y;
|
|
|
|
wind.roll_h = CON_HEIGHT - wind.roll_y - 2; // - statusline - editline
|
|
wind.stat_h = wind.edit_h = 1;
|
|
wind.edit_y = wind.roll_y + wind.roll_h;
|
|
wind.stat_y = wind.edit_y + 1;
|
|
}
|
|
|
|
void con_blt_region(int regY, int regH)
|
|
{
|
|
COORD pos = { 0, (SHORT)regY };
|
|
COORD sz = { CON_WIDTH, CON_HEIGHT };
|
|
SMALL_RECT rgn = { 0, (SHORT)regY, CON_WIDTH-1, (SHORT)(regY + regH - 1) };
|
|
BOOL success = WriteConsoleOutputA(con.output, *con.buf, sz, pos, &rgn);
|
|
}
|
|
|
|
void con_nextline()
|
|
{
|
|
con.X = 0;
|
|
con.Y++;
|
|
if(con.Y >= CON_HEIGHT) con.Y = CON_HEIGHT - 1;
|
|
}
|
|
|
|
void con_printchar(char ch)
|
|
{
|
|
con.buf[con.Y][con.X].Attributes = con.attr;
|
|
con.buf[con.Y][con.X].Char.AsciiChar = ch;
|
|
con.X++;
|
|
if(con.X >= CON_WIDTH) con_nextline();
|
|
}
|
|
|
|
void con_printline(const char *text)
|
|
{
|
|
if(!text) return;
|
|
|
|
while(*text)
|
|
{
|
|
if(*text == 1)
|
|
{
|
|
con.attr = (con.attr & 0xfff0) | text[1];
|
|
text += 2;
|
|
}
|
|
else if(*text == 2)
|
|
{
|
|
con.attr &= 0xff0f;
|
|
con.attr |= (text[1] & 0xf) << 4;
|
|
text += 2;
|
|
}
|
|
else if(*text == '\n')
|
|
{
|
|
con_nextline();
|
|
text++;
|
|
}
|
|
else if(*text == '\t')
|
|
{
|
|
int numToRound = con.X + 1;
|
|
int multiple = 4;
|
|
int untilX = ((((numToRound)+(multiple)-1) / (multiple)) * (multiple));
|
|
|
|
int numspaces = untilX - con.X;
|
|
while (numspaces--)
|
|
{
|
|
con_printchar(' ');
|
|
}
|
|
text++;
|
|
}
|
|
else con_printchar(*text++);
|
|
}
|
|
}
|
|
|
|
void con_gotoxy(int X, int Y)
|
|
{
|
|
con.X = X;
|
|
con.Y = Y;
|
|
}
|
|
|
|
void con_print_at(int X, int Y, const char *text)
|
|
{
|
|
con_gotoxy(X, Y);
|
|
con_printline(text);
|
|
}
|
|
|
|
void con_status(const char *txt)
|
|
{
|
|
sprintf_s (roll.statusline, sizeof(roll.statusline), " %s\n", txt);
|
|
con_update(CON_UPDATE_STAT);
|
|
}
|
|
|
|
void con_cursorxy(int x, int y)
|
|
{
|
|
COORD cr = { (SHORT)x, (SHORT)y };
|
|
SetConsoleCursorPosition(con.output, cr);
|
|
}
|
|
|
|
void con_fill_line(int y, int charCode)
|
|
{
|
|
for(int i = 0; i < CON_WIDTH; i++)
|
|
{
|
|
con.buf[y][i].Attributes = con.attr;
|
|
con.buf[y][i].Char.AsciiChar = (char)charCode;
|
|
}
|
|
}
|
|
|
|
void con_clear_line(int y, uint16_t attr)
|
|
{
|
|
for(int i = 0; i < CON_WIDTH; i++)
|
|
{
|
|
con.buf[y][i].Attributes = attr;
|
|
con.buf[y][i].Char.AsciiChar = ' ';
|
|
}
|
|
}
|
|
|
|
void con_printf_at(int x, int y, const char *txt, ...)
|
|
{
|
|
va_list varg;
|
|
char buf[0x200] = { 0, };
|
|
|
|
va_start(varg, txt);
|
|
vsprintf_s (buf, sizeof(buf), txt, varg);
|
|
con_print_at(x, y, buf);
|
|
va_end(varg);
|
|
}
|
|
|
|
void con_set_autoscroll(bool value)
|
|
{
|
|
roll.autoscroll = value;
|
|
if(value) con_status("Ready. Press PgUp to look behind.");
|
|
else con_status("Scroll Mode - Press PgUp, PgDown, Up, Down to scroll.");
|
|
}
|
|
|
|
// generate HTML text
|
|
static char * string_to_HTML_string(char *txt, char *html, size_t htmlSize)
|
|
{
|
|
char *ptr = html;
|
|
size_t len = strlen(txt);
|
|
|
|
const char * logcurcol = logcol[(int)ConColor::NORM];
|
|
ptr += sprintf_s (ptr, htmlSize - (ptr - html), "%s", logcurcol);
|
|
|
|
for(size_t n=0; n<len;)
|
|
{
|
|
char c = txt[n];
|
|
if(c == 1)
|
|
{
|
|
ptr += sprintf_s(ptr, htmlSize - (ptr - html), "</font>");
|
|
logcurcol = logcol[txt[n+1]];
|
|
ptr += sprintf_s(ptr, htmlSize - (ptr - html), "%s", logcurcol);
|
|
n+=2;
|
|
}
|
|
else if(c == 2)
|
|
{
|
|
n+=2;
|
|
}
|
|
else if(c == '<')
|
|
{
|
|
ptr += sprintf_s(ptr, htmlSize - (ptr - html), "<");
|
|
n++;
|
|
}
|
|
else if(c == '>')
|
|
{
|
|
ptr += sprintf_s (ptr, htmlSize - (ptr - html), ">");
|
|
n++;
|
|
}
|
|
else
|
|
{
|
|
*ptr++ = c;
|
|
n++;
|
|
}
|
|
}
|
|
|
|
ptr += sprintf_s (ptr, htmlSize - (ptr - html), "</font>");
|
|
ptr += sprintf_s (ptr, htmlSize - (ptr - html), "\n");
|
|
*ptr++ = 0;
|
|
return html;
|
|
}
|
|
|
|
static void log_console_output(char *txt)
|
|
{
|
|
static int nwrites = 10;
|
|
|
|
if(con.log)
|
|
{
|
|
if(!con.logf)
|
|
{
|
|
con.logf = nullptr;
|
|
fopen_s (&con.logf, con.logfile, "w");
|
|
if (con.logf)
|
|
{
|
|
fprintf(con.logf, "<html>\n");
|
|
fprintf(con.logf, "<style>pre { font-family: Courier New; font-size: 8pt; }</style>\n");
|
|
fprintf(con.logf, "<body bgcolor=#000000>\n");
|
|
fprintf(con.logf, "<pre>\n");
|
|
}
|
|
}
|
|
|
|
if(con.logf)
|
|
{
|
|
fprintf(con.logf, "%s", txt);
|
|
}
|
|
|
|
if(!nwrites--)
|
|
{
|
|
nwrites = 10;
|
|
if (con.logf)
|
|
fflush(con.logf);
|
|
}
|
|
}
|
|
}
|
|
|
|
void con_add_roller_line(const char *txt, ConColor col)
|
|
{
|
|
char line[0x1000], *ptr = (char *)txt;
|
|
char html[0x1000] = { 0, };
|
|
|
|
sprintf_s(line, sizeof(line), "\x1%c%s", col, txt);
|
|
ptr = line;
|
|
|
|
// roll console "roller" 1 line up
|
|
con.reportLock.Lock();
|
|
roll.rollpos = con_wraproll(roll.rollpos, 1);
|
|
strncpy_s(roll.data[roll.rollpos], sizeof(roll.data[roll.rollpos]), ptr, CON_LINELEN-1);
|
|
log_console_output(string_to_HTML_string(ptr, html, sizeof(html)));
|
|
con.reportLock.Unlock();
|
|
con_update(CON_UPDATE_MSGS);
|
|
}
|
|
|
|
void con_change_focus(FOCUSWND newfocus)
|
|
{
|
|
FOCUSWND oldfocus = wind.focus;
|
|
|
|
if(oldfocus == newfocus)
|
|
return; // we dont need any repaint
|
|
|
|
wind.focus = newfocus;
|
|
|
|
switch(oldfocus)
|
|
{ // switch focus from?
|
|
case WREGS: con.update |= (CON_UPDATE_REGS); break;
|
|
case WDATA: con.update |= (CON_UPDATE_DATA); break;
|
|
case WDISA: con.update |= (CON_UPDATE_DISA); break;
|
|
case WCONSOLE: con.update |= (CON_UPDATE_MSGS); break;
|
|
}
|
|
|
|
switch(newfocus)
|
|
{ // switch focus to?
|
|
case WREGS: con.update |= (CON_UPDATE_REGS); break;
|
|
case WDATA: con.update |= (CON_UPDATE_DATA); break;
|
|
case WDISA: con.update |= (CON_UPDATE_DISA); break;
|
|
case WCONSOLE: con.update |= (CON_UPDATE_MSGS); break;
|
|
}
|
|
}
|
|
|
|
static void con_update_scroll_window()
|
|
{
|
|
int i, y, back, line;
|
|
|
|
// cleanup window and skip header line
|
|
memset(con.buf[wind.roll_y + 1], 0, (sizeof(CHAR_INFO) * (wind.roll_h - 1) * CON_WIDTH));
|
|
con_attr(0, 3);
|
|
con_fill_line(wind.roll_y, 0xc4);
|
|
if(wind.focus == WCONSOLE) con_printf_at(0, wind.roll_y, "\x1%c\x1f", ConColor::WHITE);
|
|
con_attr(0, 3);
|
|
con_print_at(2, wind.roll_y, "F4");
|
|
con_print_at(6, wind.roll_y, " console output");
|
|
con_attr(7, 0);
|
|
|
|
// printing coord in buffer (skip header)
|
|
y = wind.roll_y + 1;
|
|
|
|
// shift backward
|
|
back = wind.roll_h - 1;
|
|
|
|
// where to get buffer line
|
|
line = (roll.autoscroll) ? (roll.rollpos - back) : (roll.viewpos - back);
|
|
line += 1;
|
|
con.reportLock.Lock();
|
|
for(i=1; i<wind.roll_h; i++)
|
|
{
|
|
if(line >= 0) con_print_at(0, y, roll.data[line]);
|
|
line++;
|
|
y++;
|
|
}
|
|
con.reportLock.Unlock();
|
|
}
|
|
|
|
void con_fullscreen(bool full)
|
|
{
|
|
for(int i=0; i<CON_HEIGHT; i++)
|
|
con_clear_line(i, 7);
|
|
|
|
wind.full = full;
|
|
|
|
if(wind.full)
|
|
{
|
|
wind.regs_h = 0;
|
|
wind.data_h = 0;
|
|
wind.disa_h = CON_HEIGHT - 3;
|
|
}
|
|
else
|
|
{
|
|
wind.regs_h = 17;
|
|
wind.data_h = 8;
|
|
wind.disa_h = 18; // 16
|
|
}
|
|
con_recalc_wnds();
|
|
}
|
|
|
|
// IMPORTANT : read carefully.
|
|
// this function actually will update windows not instantly,
|
|
// but sometimes, so if you really want to be sure, that
|
|
// window is updated, force "con.update |= mask" instead!
|
|
void con_update(uint32_t mask)
|
|
{
|
|
if(!con.active) return;
|
|
|
|
if(!emu.loaded)
|
|
{
|
|
con.update |= mask;
|
|
return;
|
|
}
|
|
|
|
if(mask & CON_UPDATE_REGS)
|
|
{
|
|
con.update |= mask;
|
|
}
|
|
else if(mask & CON_UPDATE_DATA)
|
|
{
|
|
con.update |= mask;
|
|
}
|
|
else if(mask & CON_UPDATE_DISA)
|
|
{
|
|
con.update |= mask;
|
|
}
|
|
else con.update |= mask;
|
|
}
|
|
|
|
void con_refresh(bool showpc)
|
|
{
|
|
if(con.active == FALSE) return;
|
|
if(showpc)
|
|
{
|
|
con_set_disa_cur(Gekko::Gekko->regs.pc);
|
|
}
|
|
if(con.update == 0) return;
|
|
|
|
// registres
|
|
if(con.update & CON_UPDATE_REGS)
|
|
{
|
|
con_update_registers();
|
|
con_blt_region(wind.regs_y, wind.regs_h);
|
|
}
|
|
|
|
// data dump window
|
|
if(con.update & CON_UPDATE_DATA)
|
|
{
|
|
con_update_dump_window();
|
|
con_blt_region(wind.data_y, wind.data_h);
|
|
}
|
|
|
|
// disassembler window
|
|
if(con.update & CON_UPDATE_DISA)
|
|
{
|
|
con_update_disa_window();
|
|
con_blt_region(wind.disa_y, wind.disa_h);
|
|
}
|
|
|
|
// message history
|
|
if(con.update & CON_UPDATE_MSGS)
|
|
{
|
|
con_update_scroll_window();
|
|
con_blt_region(wind.roll_y, wind.roll_h);
|
|
}
|
|
|
|
// editline window
|
|
if(con.update & CON_UPDATE_EDIT)
|
|
{
|
|
memset(&con.buf[wind.edit_y][0], 0, sizeof(CHAR_INFO) * 80 );
|
|
con_attr(7, 0);
|
|
con_print_at(0, wind.edit_y, "> ");
|
|
con_printline(roll.editline);
|
|
con_blt_region(wind.edit_y, 1);
|
|
con_cursorxy(roll.editpos + 2, wind.edit_y);
|
|
}
|
|
|
|
// statusline window
|
|
if(con.update & CON_UPDATE_STAT)
|
|
{
|
|
int i;
|
|
|
|
con_attr(0, 3);
|
|
for(i=0; i<CON_WIDTH; i++)
|
|
{
|
|
con.buf[CON_HEIGHT - 1][i].Char.AsciiChar = ' ';
|
|
con.buf[CON_HEIGHT - 1][i].Attributes = con.attr;
|
|
}
|
|
|
|
con_print_at(1, wind.stat_y, roll.statusline);
|
|
con_blt_region(wind.stat_y, 1);
|
|
}
|
|
|
|
con.update = 0;
|
|
}
|
|
|
|
void con_error(const char *txt, ...)
|
|
{
|
|
char buf[0x1000];
|
|
va_list arg;
|
|
|
|
// emulator can do output, even if console closed
|
|
if(!con.active) return;
|
|
|
|
va_start(arg, txt);
|
|
vsprintf_s(buf, sizeof(buf), txt, arg);
|
|
va_end(arg);
|
|
buf[strlen(buf) + 1] = 0;
|
|
|
|
char *s, *p = buf;
|
|
while(*p)
|
|
{
|
|
while(*p && *p == '\n')
|
|
{
|
|
con_add_roller_line("", ConColor::BRED);
|
|
p++;
|
|
}
|
|
|
|
s = p;
|
|
while(*p && *p != '\n') p++;
|
|
*p = 0;
|
|
|
|
if(*s) con_add_roller_line(s, ConColor::BRED);
|
|
p++;
|
|
}
|
|
|
|
// break
|
|
con.update = CON_UPDATE_ALL;
|
|
con_refresh();
|
|
Sleep(10);
|
|
con_break();
|
|
}
|
|
|
|
void con_print(ConColor col, const char *txt, ...)
|
|
{
|
|
char buf[0x1000];
|
|
va_list arg;
|
|
|
|
// emulator can do output, even if console closed
|
|
if(!con.active) return;
|
|
|
|
va_start(arg, txt);
|
|
vsprintf_s (buf, sizeof(buf), txt, arg);
|
|
va_end(arg);
|
|
buf[strlen(buf) + 1] = 0;
|
|
|
|
char *s, *p = buf;
|
|
while(*p)
|
|
{
|
|
while(*p && *p == '\n')
|
|
{
|
|
con_add_roller_line("", col);
|
|
p++;
|
|
}
|
|
|
|
s = p;
|
|
while(*p && *p != '\n') p++;
|
|
*p = 0;
|
|
|
|
if(*s) con_add_roller_line(s, col);
|
|
p++;
|
|
}
|
|
}
|