improved debugger, refactored

This commit is contained in:
trimill 2024-02-03 00:37:02 -05:00
parent 72df91d99b
commit 6d7a563d36
No known key found for this signature in database
GPG key ID: 4F77A16E17E10BCB
9 changed files with 276 additions and 208 deletions

View file

@ -3,11 +3,11 @@
#define _PANIC_STR(x) _PANIC_STR2(x)
#define _PANIC_STR2(x) #x
#define panic(msg, ...) _panic_impl(_PANIC_STR(__LINE__), __FILE__, msg, ## __VA_ARGS__)
#define kassert(val, msg, ...) do { if (!(val)) { panic(msg, ## __VA_ARGS__); } } while(0)
#define panic(...) _panic_impl(_PANIC_STR(__LINE__), __FILE__, __VA_ARGS__)
#define kassert(val, ...) do { if (!(val)) { panic(__VA_ARGS__); } } while(0)
__attribute__((noreturn,format(printf, 3, 4)))
void _panic_impl(char *line, char *file, char *msg, ...);
__attribute__((format(printf, 3, 4)))
_Noreturn void _panic_impl(char *line, char *file, char *format, ...);
__attribute__((noreturn,format(printf, 3, 4)))
void _panic_interrupt(void *ip, void *bp, char *msg, ...);
__attribute__((format(printf, 3, 4)))
_Noreturn void panic_interrupt(void *ip, void *bp, char *format, ...);

View file

@ -6,7 +6,6 @@
#include "bindings.h"
#include "memory.h"
#include "serial.h"
/* global state, idk a better way rn */
struct acpi_state state;
@ -208,7 +207,7 @@ static void *acpi_find_table_rsdt(struct rsdt *rsdt, const char *identifier, int
memcpy(buf, h->signature, 4);
buf[4] = '\n';
buf[5] = '\0';
serial_out_str(buf);
kputs(buf);
if (!strncmp(h->signature, identifier, ident_len))
return (void *)h;
}

View file

@ -1,6 +1,5 @@
#include <backtrace.h>
#include <lib.h>
#include "serial.h"
struct stackframe {
struct stackframe *rbp;
@ -36,17 +35,10 @@ void log_backtrace() {
void log_backtrace_ex(void *ip, void *bp) {
struct stackframe *frame = bp;
char buf[20];
serial_out_str("Stack trace:\n");
ultoa((size_t)ip, buf, 16);
serial_out_str(" 0x");
serial_out_str(buf);
serial_out_str("\n");
kputs("Stack trace:\n");
kprintf(" %#lx\n", (size_t)ip);
while (frame) {
ultoa((size_t)frame->rip, buf, 16);
serial_out_str(" 0x");
serial_out_str(buf);
serial_out_str("\n");
kprintf(" %#lx\n", (size_t)frame->rip);
frame = frame->rbp;
}

View file

@ -1,7 +1,7 @@
#include <lib.h>
#include <stddef.h>
#include <serial.h>
#include <backtrace.h>
#include <serial.h>
#include "debugger.h"
struct dr6 {
@ -73,183 +73,217 @@ struct breakpoint {
uint8_t rw;
uint8_t used;
uint8_t enable;
uint8_t instr_len;
};
static size_t dbg_steps = 0;
static int dbg_steps = 0;
static int dbg_continue = 0;
static struct breakpoint bkps[4] = {{0}, {0}, {0}, {0}};
static void dbg_putc(char c) {
serial_out(c);
}
static void dbg_puts(char *msg) {
serial_out_str(msg);
}
static char dbg_getc() {
return serial_in();
static size_t debugger_read(char *buf, size_t len) {
char *p = buf;
size_t result;
while (p < buf + len - 1) {
char ch = serial_in();
switch (ch) {
case '\r':
goto end;
case '\x7f':
if (p > buf) {
p--;
kputs("\x08 \x08");
}
break;
default:
kputc(ch);
*p++ = ch;
}
}
end:
result = p - buf;
kputc('\n');
for(; p < buf + len; p++) {
*p = 0;
}
return result;
}
static void debugger_msg(int cause, struct dr6 dr6) {
char buf[24];
switch (cause) {
case DEBUG_INT3:
dbg_puts("dbg: reached int3");
kputs("dbg: reached int3");
break;
case DEBUG_FAULT:
dbg_puts("dbg: a fault occured");
kputs("dbg: a fault occured");
break;
case DEBUG_DBG:
if (dr6.bs)
dbg_puts("dbg: finished steps");
else if (dr6.b0)
dbg_puts("dbg: reached breakpoint 0");
kputs("dbg: finished steps");
if (dr6.b0)
kputs("dbg: reached breakpoint 0");
else if (dr6.b1)
dbg_puts("dbg: reached breakpoint 1");
kputs("dbg: reached breakpoint 1");
else if (dr6.b2)
dbg_puts("dbg: reached breakpoint 2");
kputs("dbg: reached breakpoint 2");
else if (dr6.b3)
dbg_puts("dbg: reached breakpoint 3");
kputs("dbg: reached breakpoint 3");
}
if (dbg_steps > 0) {
ultoa(dbg_steps, buf, 10);
dbg_puts(" (");
dbg_puts(buf);
dbg_puts(" steps left)");
kprintf(" (%d steps left)", dbg_steps);
}
if (dbg_continue > 0) {
dbg_puts(" (paused continue)");
kputs(" (paused continue)");
}
dbg_puts("\n");
}
static void debugger_print_reg(char *name, uint64_t val) {
char buf[24];
char *end;
dbg_puts(name);
dbg_puts(": 0x");
end = ultoa(val, buf, 16);
for (int i = 0; i < 16 - (end - buf); i++) {
dbg_putc('0');
}
dbg_puts(buf);
dbg_puts(" (");
end = ultoa(val, buf, 10);
dbg_puts(buf);
dbg_puts(")\n");
kputs("\n");
}
static void debugger_print_regs(struct isr_regs *state) {
debugger_print_reg("rax", state->rax);
debugger_print_reg("rbx", state->rbx);
debugger_print_reg("rcx", state->rcx);
debugger_print_reg("rdx", state->rdx);
debugger_print_reg("rsi", state->rsi);
debugger_print_reg("rdi", state->rdi);
debugger_print_reg("rsp", state->rsp);
debugger_print_reg("rbp", state->rbp);
debugger_print_reg("r8 ", state->r8 );
debugger_print_reg("r9 ", state->r9 );
debugger_print_reg("r10", state->r10);
debugger_print_reg("r11", state->r11);
debugger_print_reg("r12", state->r12);
debugger_print_reg("r13", state->r13);
debugger_print_reg("r14", state->r14);
debugger_print_reg("r15", state->r15);
dbg_puts("---\n");
debugger_print_reg("rip", state->rip);
dbg_puts("---\n");
debugger_print_reg("rflags", state->rflags);
kprintf("rax: %#016lx (%lu)\n", state->rax, state->rax);
kprintf("rbx: %#016lx (%lu)\n", state->rbx, state->rbx);
kprintf("rcx: %#016lx (%lu)\n", state->rcx, state->rcx);
kprintf("rdx: %#016lx (%lu)\n", state->rdx, state->rdx);
kprintf("rsi: %#016lx (%lu)\n", state->rsi, state->rsi);
kprintf("rdi: %#016lx (%lu)\n", state->rdi, state->rdi);
kprintf("rsp: %#016lx (%lu)\n", state->rsp, state->rsp);
kprintf("rbp: %#016lx (%lu)\n", state->rbp, state->rbp);
kprintf("r8 : %#016lx (%lu)\n", state->r8 , state->r8 );
kprintf("r9 : %#016lx (%lu)\n", state->r9 , state->r9 );
kprintf("r10: %#016lx (%lu)\n", state->r10, state->r10);
kprintf("r11: %#016lx (%lu)\n", state->r11, state->r11);
kprintf("r12: %#016lx (%lu)\n", state->r12, state->r12);
kprintf("r13: %#016lx (%lu)\n", state->r13, state->r13);
kprintf("r14: %#016lx (%lu)\n", state->r14, state->r14);
kprintf("r15: %#016lx (%lu)\n", state->r15, state->r15);
kputs("---\n");
kprintf("rip: %#016lx (%lu)\n", state->rip, state->rip);
kputs("---\n");
kprintf("rflags: %#016lx (%lu)\n", state->rflags, state->rflags);
struct rflags *rflags = (struct rflags *)state->rflags;
dbg_puts("rflags: ");
if(rflags->cf) dbg_puts("CF ");
if(rflags->pf) dbg_puts("PF ");
if(rflags->af) dbg_puts("AF ");
if(rflags->zf) dbg_puts("ZF ");
if(rflags->sf) dbg_puts("SF ");
if(rflags->tf) dbg_puts("TF ");
if(rflags->if_) dbg_puts("IF ");
if(rflags->df) dbg_puts("DF ");
if(rflags->of) dbg_puts("OF ");
if(rflags->iopl) dbg_puts("IOPL ");
if(rflags->nt) dbg_puts("NT ");
if(rflags->md) dbg_puts("MD ");
if(rflags->rf) dbg_puts("RF ");
if(rflags->vm) dbg_puts("VM ");
if(rflags->ac) dbg_puts("AC ");
if(rflags->vif) dbg_puts("VIF ");
if(rflags->vip) dbg_puts("VIP ");
if(rflags->id) dbg_puts("ID ");
dbg_putc('\n');
kputs("rflags: ");
if(rflags->cf) kputs("CF ");
if(rflags->pf) kputs("PF ");
if(rflags->af) kputs("AF ");
if(rflags->zf) kputs("ZF ");
if(rflags->sf) kputs("SF ");
if(rflags->tf) kputs("TF ");
if(rflags->if_) kputs("IF ");
if(rflags->df) kputs("DF ");
if(rflags->of) kputs("OF ");
if(rflags->iopl) kputs("IOPL ");
if(rflags->nt) kputs("NT ");
if(rflags->md) kputs("MD ");
if(rflags->rf) kputs("RF ");
if(rflags->vm) kputs("VM ");
if(rflags->ac) kputs("AC ");
if(rflags->vif) kputs("VIF ");
if(rflags->vip) kputs("VIP ");
if(rflags->id) kputs("ID ");
kputs("\n");
}
#define PROMPT_LEN 60
static int debugger_handle_bkp_cmd(char *buf) {
if (buf[0] == 'l') {
static int debugger_handle_bkp_cmd(char *msg) {
if (msg[0] == 'l') {
// list breakpoints
char buf[20];
for (int i = 0; i < 4; i++) {
dbg_puts("breakpoint ");
dbg_putc('0' + i);
dbg_puts(": ");
struct breakpoint bkp = bkps[i];
if(!bkp.used) {
dbg_puts("unset\n");
kprintf("breakpoint %d: unset\n", i);
continue;
}
dbg_puts(" 0x");
ultoa(bkp.addr, buf, 16);
dbg_puts(buf);
dbg_puts(" len=");
switch (bkp.len) {
case 0x0: dbg_putc('1'); break;
case 0x1: dbg_putc('2'); break;
case 0x2: dbg_putc('8'); break;
case 0x3: dbg_putc('4'); break;
}
dbg_puts(" trigger=");
switch (bkp.rw) {
case 0x0: dbg_puts("x "); break;
case 0x1: dbg_puts("w "); break;
case 0x2: dbg_puts("io"); break;
case 0x3: dbg_puts("rw"); break;
}
dbg_puts(bkp.enable ? " enabled" : " disabled");
const char *lenstrs[] = {"1", "2", "8", "4"};
const char *rwstrs[] = {"x ", "w ", "io", "rw"};
kprintf(
"breakpoint %d: %#016lx len=%s trigger=%s instrlen=%01d %s\n",
i,
bkp.addr,
lenstrs[bkp.len],
rwstrs[bkp.rw],
bkp.instr_len,
bkp.enable ? " enabled" : " disabled"
);
}
return 1;
} else if (buf[0] == 'd') {
} else if (msg[0] == 'd') {
// disable breakpoints
bkps[0].enable = 0;
bkps[1].enable = 0;
bkps[2].enable = 0;
bkps[3].enable = 0;
kputs("disabled breakpoints\n");
return 1;
} else if (buf[0] == 'c') {
} else if (msg[0] == 'c') {
// clear breakpoints
bkps[0].used = 0;
bkps[1].used = 0;
bkps[2].used = 0;
bkps[3].used = 0;
kputs("cleared breakpoints\n");
return 1;
} else if (buf[0] <= '0' && buf[0] >= '3') {
dbg_puts("invalid breakpoint command\n");
} else if (msg[0] <= '0' && msg[0] >= '3') {
kputs("invalid breakpoint command\n");
return 1;
}
char buf[64];
int id = msg[0] - '0';
switch (msg[1]) {
case 'e':
bkps[id].enable = bkps[id].used;
kprintf("enabled breakpoint %d\n", id);
return 1;
case 'd':
bkps[id].enable = 0;
kprintf("disabled breakpoint %d\n", id);
return 1;
case 'c':
bkps[id].used = 0;
kprintf("cleared breakpoint %d\n", id);
return 1;
case 'a':
kputs("addr> 0x");
debugger_read(buf, 64);
bkps[id].addr = strtoll(buf, NULL, 16);
kputs("len (1/2/4/8)> ");
debugger_read(buf, 64);
switch (buf[0]) {
case '1': bkps[id].len = 0x0; break;
case '2': bkps[id].len = 0x1; break;
case '8': bkps[id].len = 0x2; break;
case '4': bkps[id].len = 0x3; break;
default: bkps[id].len = 0x0; break;
}
kputs("cond (x/w/io/rw)> ");
debugger_read(buf, 64);
switch ((int)buf[0] * 256 + (int)buf[1]) {
case (int)'x'*256 + (int)0: bkps[id].rw = 0x0; break;
case (int)'w'*256 + (int)0: bkps[id].rw = 0x1; break;
case (int)'i'*256 + (int)'o': bkps[id].rw = 0x2; break;
case (int)'r'*256 + (int)'w': bkps[id].rw = 0x3; break;
default: bkps[id].len = 0x0; break;
}
kputs("instr len (1..15)> ");
debugger_read(buf, 64);
bkps[id].instr_len = strtoi(buf, NULL, 10);
kputs("enable (y/n)> ");
debugger_read(buf, 64);
bkps[id].used = 1;
bkps[id].enable = !(buf[0] == 'n' || buf[0] == 'N');
kprintf("added breakpoint %d\n", id);
return 1;
default:
kputs("invalid breakpoint command\n");
return 1;
}
// TODO set breakpoints
return 1;
}
static void debugger_load_bkps() {
struct dr7 dr7;
__asm__ volatile ("mov %%dr7, %0" : "=r"(dr7));
dr7.g0 = bkps[0].enable & bkps[0].used;
dr7.g1 = bkps[1].enable & bkps[0].used;
dr7.g2 = bkps[2].enable & bkps[0].used;
dr7.g3 = bkps[3].enable & bkps[0].used;
dr7.g1 = bkps[1].enable & bkps[1].used;
dr7.g2 = bkps[2].enable & bkps[2].used;
dr7.g3 = bkps[3].enable & bkps[3].used;
dr7.l0 = 0;
dr7.l1 = 0;
dr7.l2 = 0;
@ -263,19 +297,16 @@ static void debugger_load_bkps() {
dr7.len2 = bkps[2].len;
dr7.len3 = bkps[3].len;
__asm__ volatile ("mov %0, %%dr7" : : "r"(dr7));
__asm__ volatile ("mov %0, %%dr0" : : "r"(bkps[0].addr));
__asm__ volatile ("mov %0, %%dr1" : : "r"(bkps[1].addr));
__asm__ volatile ("mov %0, %%dr2" : : "r"(bkps[2].addr));
__asm__ volatile ("mov %0, %%dr3" : : "r"(bkps[3].addr));
}
static int debugger_prompt(struct isr_regs *state) {
char buf[PROMPT_LEN];
char *p = buf;
dbg_puts("dbg> ");
while ((*p = dbg_getc()) != '\r' && p < buf + PROMPT_LEN - 1) {
dbg_putc(*p++);
}
dbg_putc('\n');
for(; p < buf + PROMPT_LEN; p++) {
*p = 0;
}
kputs("dbg> ");
char buf[64];
debugger_read(buf, 64);
struct rflags *rflags = (struct rflags *)&state->rflags;
@ -283,7 +314,7 @@ static int debugger_prompt(struct isr_regs *state) {
case '\0': // nothing entered
return 1;
case 'h': // help
dbg_puts("see debugger.c for help information\n");
kputs("see debugger.c for help information\n");
return 1;
case 'q': // quit
return 0;
@ -302,6 +333,36 @@ static int debugger_prompt(struct isr_regs *state) {
dbg_steps = atol(buf + 1) - 1;
//if (dbg_steps < 0) dbg_steps = 0;
return 0;
case 'm': // memory
{
kputs("addr> 0x");
debugger_read(buf, 64);
uint64_t start = (~0xF) & strtoll(buf, NULL, 16);
kputs("len> 0x");
debugger_read(buf, 64);
uint64_t len = (~0xF) & strtoll(buf, NULL, 16);
for (uint64_t a = start; a <= start + len; a += 0x10) {
kprintf("%08lx: ", a);
for (uint64_t b = 0; b < 0x10; b++) {
kprintf("%02x ", *(uint8_t *)(a + b));
if (b % 0x08 == 7) {
kputc(' ');
}
}
kputs("|");
for (uint64_t b = 0; b < 0x10; b++) {
uint8_t ch = *(uint8_t *)(a + b);
if (ch < 0x20 || ch >= 0x7f) {
ch = '.';
}
kputc(ch);
}
kputs("|\n");
}
return 1;
}
case 'b': // breakpoints
{
int res = debugger_handle_bkp_cmd(buf+1);
@ -309,7 +370,7 @@ static int debugger_prompt(struct isr_regs *state) {
return res;
}
default:
dbg_puts("unknown command\n");
kputs("unknown command\n");
return 1;
}
}
@ -318,9 +379,7 @@ void debugger(struct isr_regs *state, int cause) {
struct dr6 dr6;
__asm__ volatile ("mov %%dr6, %0" : "=r"(dr6));
if (dr6.b0 || dr6.b1 || dr6.b2 || dr6.b3) {
} else {
if (!(dr6.b0 || dr6.b1 || dr6.b2 || dr6.b3)) {
if (dr6.bs && dbg_steps > 0) {
dbg_steps--;
return;
@ -333,11 +392,22 @@ void debugger(struct isr_regs *state, int cause) {
debugger_msg(cause, dr6);
kputs("hi\n");
dbg_steps = 0;
dbg_continue = 0;
struct rflags *rflags = (struct rflags *)&state->rflags;
rflags->tf = 0;
((struct rflags *)&state->rflags)->tf = 0;
if (dr6.b0) {
state->rip += bkps[0].instr_len;
} else if (dr6.b1) {
state->rip += bkps[1].instr_len;
} else if (dr6.b2) {
state->rip += bkps[2].instr_len;
} else if (dr6.b3) {
state->rip += bkps[3].instr_len;
}
while (debugger_prompt(state));
}

View file

@ -1,6 +1,5 @@
#include <lib.h>
#include <memory.h>
#include <serial.h>
#include <stdint.h>
#include <panic.h>
#include <pci.h>

View file

@ -1,7 +1,6 @@
#include <stdint.h>
#include <stddef.h>
#include <lib.h>
#include <serial.h>
#include <panic.h>
#include "debugger.h"
@ -74,48 +73,48 @@ void idt_init(void) {
// Intel manual vol 3 ch 6.3.1
char *EXCEPTIONS[] = {
"0x00 Division Error",
"0x01 Debug",
"0x02 NMI",
"0x03 Breakpoint",
"0x04 Overflow",
"0x05 BOUND Range Exceeded",
"0x06 Invalid Opcode",
"0x07 Device Not Available",
"0x08 Double Fault",
"0x09 Coprocessor Segment Overrun",
"0x0A Invalid TSS",
"0x0B Segment Not Present",
"0x0C Stack-Segment Fault",
"0x0D General Protection Fault",
"0x0E Page Fault",
"0x0F Reserved",
"0x10 x87 Floating-Point Error",
"0x11 Alignment Check",
"0x12 Machine Check",
"0x13 SIMD Floaing-Point Exception",
"0x14 Virtualization Exception",
"0x15 Control Protection Exception",
"0x16 Reserved",
"0x17 Reserved",
"0x18 Reserved",
"0x19 Reserved",
"0x1A Reserved",
"0x1B Reserved",
"0x1C Hypervisor Injection Exception",
"0x1D VMM Communication Exception",
"0x1E Security Exception",
"0x1F Reserved",
"Division Error",
"Debug",
"NMI",
"Breakpoint",
"Overflow",
"BOUND Range Exceeded",
"Invalid Opcode",
"Device Not Available",
"Double Fault",
"Coprocessor Segment Overrun",
"Invalid TSS",
"Segment Not Present",
"Stack-Segment Fault",
"General Protection Fault",
"Page Fault",
"Reserved",
"x87 Floating-Point Error",
"Alignment Check",
"Machine Check",
"SIMD Floaing-Point Exception",
"Virtualization Exception",
"Control Protection Exception",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Hypervisor Injection Exception",
"VMM Communication Exception",
"Security Exception",
"Reserved",
};
void idt_exception_handler(uint64_t exception, uint64_t code, struct isr_regs *state) {
// breakpoint interrupt
if (exception == 0x03) {
debugger(state, DEBUG_INT3);
return;
} else if (exception == 0x01) {
switch (exception) {
case 0x01: // debug
debugger(state, DEBUG_DBG);
return;
case 0x03: // breakpoint
debugger(state, DEBUG_INT3);
return;
}
char custom[64];
@ -124,12 +123,12 @@ void idt_exception_handler(uint64_t exception, uint64_t code, struct isr_regs *s
// page faults store the offending address in cr2
if (exception == 0x0E) {
strcat(custom, "\nPage fault address: 0x");
void *addr;
__asm__ volatile ("mov %%cr2, %0" : "=r"(addr));
ultoa((size_t)addr, custom + 23, 16);
uint64_t cr2;
__asm__ volatile ("mov %%cr2, %0" : "=r"(cr2));
ultoa((size_t)cr2, custom + 23, 16);
}
_panic_interrupt(
panic_interrupt(
(void *)state->rip,
(void *)state->rbp,
"Exception %s\nError code 0x%lu%s",
@ -149,7 +148,7 @@ void idt_pic_timer(void) {
// print a message once we know the timer works
// but avoid spamming the logs
if (counter == 3) {
serial_out_str("pic timer!\n");
kputs("pic timer!\n");
}
if (counter <= 3) {
counter++;

View file

@ -1,31 +1,34 @@
#include <panic.h>
#include <backtrace.h>
#include <stdarg.h>
#include <lib.h>
#include "bindings.h"
void _panic_impl(char *line, char *file, char *format, ...) {
_Noreturn void _panic_impl(char *line, char *file, char *format, ...) {
cli();
va_list list;
va_start(list, msg);
va_start(list, format);
kprintf("\n\n!!! PANIC !!!\n");
kprintf("In file %s at line %s:\n", file, line);
kvprintf(format, list);
kprintf("\n\n");
log_backtrace();
while (1) {
halt();
}
}
void _panic_interrupt(void *ip, void *bp, char *format, ...) {
cli();
_Noreturn void panic_interrupt(void *ip, void *bp, char *msg, ...) {
va_list list;
va_start(list, msg);
kprintf("\n\n!!! PANIC !!!\n");
kvprintf(format, list);
kprintf("\n\n");
cli();
kputs("\n\n!!! PANIC !!!\n");
kvprintf(msg, list);
kputs("\n\n");
log_backtrace_ex(ip, bp);
while (1) {
halt();

View file

@ -2,7 +2,6 @@
#include <acpi.h>
#include <memory.h>
#include <lib.h>
#include <serial.h>
#include <fb.h>
#include <shim.h>
#include <panic.h>
@ -20,6 +19,7 @@ void kmain(struct boot_info *info) {
*(char *)(0xB8000 + 0x144) = 'h';
*(char *)(0xB8000 + 0x146) = 'i';
while (1) {
__asm__("hlt;");
// loop so we dont halt

View file

@ -344,7 +344,7 @@ static void print_unum(
bool space_pre = (flag & FLG_LEFT_ALIGN) || !(flag & FLG_ZERO);
if (!space_pre && radix == 16 && flag & FLG_ALTERNATE) {
if (!space_pre && radix == 16 && (flag & FLG_ALTERNATE)) {
char x = base + ('x' - 'a');
serial_out('0');
serial_out(x);
@ -371,6 +371,12 @@ static void print_unum(
if (flag & FLG_ZERO)
zero_padded = true;
}
if (space_pre && radix == 16 && (flag & FLG_ALTERNATE)) {
char x = base + ('x' - 'a');
serial_out('0');
serial_out(x);
}
if (space_pre && radix == 16 && flag & FLG_ALTERNATE) {
char x = base + ('x' - 'a');