#include #include #include #include #include #include #include #include #include #include #include #include "zap.h" struct zap_private { WINDOW *scr; WINDOW *regwin; const struct zap *zap; void *private; unsigned char width; unsigned char height; unsigned char x_addr; unsigned char w_addr; unsigned char x_val; unsigned char w_val; unsigned char u_val; unsigned char x_ascii; unsigned char w_ascii; unsigned char u_ascii; unsigned char w_mask; unsigned char l2_w; #define F_REDRAW_ADDR 1 #define F_REDRAW_VALS 2 unsigned char flags; char mode; int x_curs; int y_curs; u32 v_mask; unsigned long addr; u32 *vals; unsigned int ipidx; char ipbuf[16]; }; static void zap_quit(int signr) { if (!isendwin()) endwin(); signal(signr, SIG_DFL); raise(signr); } static int fls(u32 val) { int res = 32; if (!val) return 0; #define T(v,r,bits) \ if (!(v & (~0 << (32 - bits)))) { \ v <<= bits; \ r -= bits; \ } T(val, res, 16) T(val, res, 8) T(val, res, 4) T(val, res, 2) T(val, res, 1) #undef T return res; } static void dashes(int y, int x, int n) { const char *dashes = "========================================="; attron(COLOR_PAIR(3)); mvaddnstr(y, x, dashes, n); attroff(COLOR_PAIR(3)); } static void dashes_centered(int y, int x, int w, const char *text) { int text_w = strlen(text); int dashes_w = w - text_w; int dashes_l = dashes_w / 2; int dashes_r = w - dashes_l - text_w; dashes(y, x, dashes_l); attron(COLOR_PAIR(4)); mvaddstr(0, x + dashes_l, text); attroff(COLOR_PAIR(4)); dashes(y, x + dashes_l + text_w, dashes_r); } static void zap_header(const struct zap_private *priv) { attron(COLOR_PAIR(4)); mvaddstr(0, priv->x_addr, "Addr"); attroff(COLOR_PAIR(4)); dashes_centered(0, priv->x_val, priv->w_val, " Hex Value "); if (priv->x_ascii) dashes_centered(0, priv->x_ascii, priv->w_ascii, " ASCII "); } static void zap_write(const struct zap_private *priv, unsigned long val) { const struct zap *zap = priv->zap; if (zap->write) zap->write(priv->private, priv->addr << priv->zap->addr_shift, val); } static void zap_sub_header(const struct zap_private *priv) { const struct zap *zap = priv->zap; if (zap->print_header) { attron(COLOR_PAIR(2)); move(1, priv->x_val); zap->print_header(priv->private); attroff(COLOR_PAIR(2)); } } static void zap_input(struct zap_private *priv) { const struct zap *zap = priv->zap; if (zap->input) zap->input(priv->private, priv->mode, priv->ipbuf); } static void zap_key(struct zap_private *priv, int ch) { const struct zap *zap = priv->zap; if (zap->key) zap->key(priv, priv->private, ch); } static void zap_init(struct zap_private *priv, const struct zap *zap, void *private) { unsigned int width, n; memset(priv, 0, sizeof(*priv)); priv->zap = zap; priv->private = private; priv->mode = '\0'; priv->flags = F_REDRAW_ADDR | F_REDRAW_VALS; priv->ipidx = 0; priv->v_mask = (1 << (4 * zap->val_width)) - 1; priv->w_addr = (fls(zap->max_addr) + 3) / 4; priv->u_val = zap->val_width + 1; priv->u_ascii = zap->val_width / 2; priv->scr = initscr(); getmaxyx(priv->scr, priv->height, priv->width); priv->x_curs = 0; priv->y_curs = priv->height - 1; start_color(); init_pair(1, COLOR_GREEN, COLOR_BLACK); init_pair(2, COLOR_YELLOW, COLOR_BLACK); init_pair(3, COLOR_MAGENTA, COLOR_BLACK); init_pair(4, COLOR_CYAN, COLOR_BLACK); cbreak(); noecho(); nodelay(priv->scr, TRUE); keypad(priv->scr, TRUE); priv->x_addr = 1; priv->x_val = priv->x_addr + priv->w_addr + 2; width = priv->width - priv->x_val; if (zap->ascii) { width -= 2; width /= priv->u_val + priv->u_ascii; } else { width /= priv->u_val; } priv->l2_w = fls(width) - 1; n = 1 << priv->l2_w; priv->w_mask = n - 1; priv->w_val = priv->u_val * n - 1; priv->w_ascii = priv->u_ascii * n; // priv->vals = calloc(sizeof(*priv->vals), n); if (zap->ascii) { priv->x_ascii = priv->x_val + priv->w_val + 2; width = priv->x_ascii + priv->w_ascii; } else { width = priv->x_val + priv->w_val; } priv->regwin = subwin(priv->scr, priv->height - 3, width, 2, 0); syncok(priv->regwin, FALSE); zap_header(priv); zap_sub_header(priv); } static void move_ifnot(int y, int x) { int curx, cury; getyx(stdscr, curx, cury); if (curx != x || cury != y) move(y, x); } void zap_prompt_input(struct zap_private *priv, char mode, const char *prompt) { if (priv->mode == '\0') { priv->mode = mode; mvprintw(priv->height - 1, 0, "%s: ", prompt); clrtoeol(); curs_set(1); } } static void zap_prompt_clean(struct zap_private *priv) { priv->ipidx = 0; priv->mode = '\0'; move(priv->height - 1, 0); clrtoeol(); curs_set(0); } static char ascii(unsigned int val) { return val >= 127 || !isprint(val) ? '.' : val; } static void zap_format_val(const struct zap_private *priv, int line, int col, u32 val, bool ok) { unsigned int val_width = priv->u_val - 1; int i, x = priv->x_val + priv->u_val * col; char buf[8]; if (ok) mvwprintw(priv->regwin, line, x, "%.*x ", val_width, val); else mvwprintw(priv->regwin, line, x, "%.*s ", val_width, "xxxxxxxx"); if (priv->x_ascii) { for (i = 0; i < priv->u_ascii && i < sizeof(buf) - 1; i++, val >>= 8) buf[i] = ascii(val & 255); x = priv->x_ascii + priv->u_ascii * col; mvwaddnstr(priv->regwin, line, x, buf, i); } } static void zap_set_addr(struct zap_private *priv, unsigned long addr) { if (priv->addr != addr) { priv->addr = addr; priv->flags |= F_REDRAW_ADDR; } } static void zap_handle_key(struct zap_private *priv, int ch) { unsigned long val; switch (ch) { case 12: wrefresh(curscr); break; case '0'...'9': case 'a'...'f': if (priv->ipidx == 0 && priv->mode == '\0') zap_prompt_input(priv, 'c', "New value"); if (priv->ipidx < sizeof(priv->ipbuf) - 1) { priv->ipbuf[priv->ipidx++] = ch; addch(ch); } break; case 'g': zap_prompt_input(priv, 'g', "New addr"); break; case KEY_BACKSPACE: case 8: if (priv->ipidx > 0) { priv->ipidx -= 1; addch(8); clrtoeol(); } break; case KEY_ENTER: case '\n': if (priv->ipidx == 0) { zap_prompt_clean(priv); break; } priv->ipbuf[priv->ipidx] = '\0'; switch (priv->mode) { case 'c': errno = 0; val = strtoul(priv->ipbuf, NULL, 16); if (val != ULONG_MAX || errno != ERANGE) zap_write(priv, val); break; case 'g': errno = 0; val = strtoul(priv->ipbuf, NULL, 16); if (val != ULONG_MAX || errno != ERANGE) zap_set_addr(priv, val >> priv->zap->addr_shift); break; default: zap_input(priv); zap_sub_header(priv); break; } zap_prompt_clean(priv); break; default: zap_key(priv, ch); break; } } void zap(const struct zap *zap, unsigned long start, void *private) { struct zap_private priv; unsigned int page_offset, offset, page, hly; u32 regs[192 * 2]; bool oks[192 * 2]; bool quit = false; int ch; signal(SIGHUP, zap_quit); signal(SIGINT, zap_quit); signal(SIGQUIT, zap_quit); signal(SIGTSTP, zap_quit); signal(SIGTTIN, zap_quit); signal(SIGTTOU, zap_quit); zap_init(&priv, zap, private); /* page = how many "addresses" we can fit on a page */ page = (priv.height - 3) << priv.l2_w; /* offset = offset of the left-hand side of the highlighted line */ offset = ((page - 1) / 2) & ~priv.w_mask; /* hly = highlighted line y offset */ hly = offset >> priv.l2_w; /* how much to page by */ page_offset = 0x100 >> zap->addr_shift; priv.addr = start >> zap->addr_shift; do { unsigned int cur_left = priv.addr & ~priv.w_mask; unsigned int top_left = cur_left - offset; unsigned int i, flags; int attr = 0; flags = priv.flags; priv.flags = 0; if (flags & F_REDRAW_ADDR) { curs_set(0); for (i = 0; i < page; i += priv.w_mask + 1) { unsigned int line = i >> priv.l2_w; unsigned int addr = top_left + i; if (addr < zap->min_addr || addr > zap->max_addr) { wmove(priv.regwin, line, 0); wclrtoeol(priv.regwin); continue; } if (line == hly) wattron(priv.regwin, COLOR_PAIR(1)); mvwprintw(priv.regwin, line, priv.x_addr, "%*.*x", priv.w_addr, priv.w_addr, addr << zap->addr_shift); if (line == hly) wattroff(priv.regwin, COLOR_PAIR(1)); } } for (i = 0; i < page; i++) { unsigned int line = i >> priv.l2_w; unsigned int addr = top_left + i; unsigned int col = i & priv.w_mask; unsigned int mask; int new_attr; bool ok; u32 val; if (addr < zap->min_addr || addr > zap->max_addr) { oks[i] = false; continue; } if (line == hly) { /* The highlight line must be redrawn on * address changes */ mask = F_REDRAW_VALS | F_REDRAW_ADDR; if (addr == priv.addr) new_attr = COLOR_PAIR(2); else new_attr = COLOR_PAIR(1); } else { mask = F_REDRAW_VALS; new_attr = 0; } if (zap->read_block) { if (col == 0) { ok = zap->read_block(private, addr << zap->addr_shift, priv.vals, priv.w_mask + 1); } else { ok = 1; } val = priv.vals[col]; } else { ok = 0; } if (!ok) ok = zap->read(private, addr << zap->addr_shift, &val); if (!ok) val = 0; val &= priv.v_mask; if (flags & mask || ok != oks[i] || val != regs[i]) { regs[i] = val; oks[i] = ok; if (attr != new_attr) { if (new_attr) wattron(priv.regwin, new_attr); else wattroff(priv.regwin, attr); attr = new_attr; } zap_format_val(&priv, line, col, val, ok); } } if (attr) { wattroff(priv.regwin, attr); attr = 0; } wsyncup(priv.regwin); if (zap->update_aux) { attron(COLOR_PAIR(2)); zap->update_aux(private); attroff(COLOR_PAIR(2)); } /* Restore the cursor position */ move_ifnot(priv.y_curs, priv.x_curs); if (priv.mode && flags & F_REDRAW_ADDR) curs_set(1); ch = getch(); switch (ch) { case KEY_LEFT: case 'h': if (priv.addr > zap->min_addr) zap_set_addr(&priv, priv.addr - 1); break; case KEY_RIGHT: case 'l': if (priv.addr < zap->max_addr) zap_set_addr(&priv, priv.addr + 1); break; case KEY_UP: case 'k': if (priv.addr >= priv.w_mask + 1) zap_set_addr(&priv, priv.addr - (priv.w_mask + 1)); break; case KEY_DOWN: case 'j': if (priv.addr < zap->max_addr - priv.w_mask) zap_set_addr(&priv, priv.addr + priv.w_mask + 1); break; case KEY_PPAGE: if (priv.addr >= zap->min_addr + page_offset) { zap_set_addr(&priv, priv.addr - page_offset); break; } case KEY_HOME: if (priv.addr & ~priv.w_mask) zap_set_addr(&priv, (priv.addr & priv.w_mask) + (zap->min_addr & ~priv.w_mask)); break; case KEY_NPAGE: if (priv.addr < zap->max_addr - page_offset) { zap_set_addr(&priv, priv.addr + page_offset); break; } case KEY_END: if (priv.addr < zap->max_addr - priv.w_mask) zap_set_addr(&priv, (priv.addr & priv.w_mask) + zap->max_addr - priv.w_mask); break; case ERR: break; case 'q': quit = true; break; default: zap_handle_key(&priv, ch); break; } /* Save the current cursor position */ getyx(priv.scr, priv.x_curs, priv.y_curs); usleep(40000); } while (!quit); endwin(); }