RISC-Vに移植したxv6のデバッグを引き続き行っているのだが、どうやら表示系の調子がおかしい。
cpu-4096,33554432: init start
コア数が多すぎる(笑)。
デバッグしているのだが、ついでなのでxv6の表示系はどのように構成されているのか見ていく。
xv6のUARTの仕組み
xv6では、printfなどを実行するとUART経由でコンソールに表示されるようになっている。これにはx86の機能を使っているようだ。
#define COM1 0x3f8 static int uart; // is there a uart? void uartinit(void) { char *p; // Turn off the FIFO outb(COM1+2, 0); // 9600 baud, 8 data bits, 1 stop bit, parity off. outb(COM1+3, 0x80); // Unlock divisor outb(COM1+0, 115200/9600); outb(COM1+1, 0); outb(COM1+3, 0x03); // Lock divisor, 8 data bits. outb(COM1+4, 0); outb(COM1+1, 0x01); // Enable receive interrupts.
別のアーキテクチャに移植するときは、メモリマップを改造して、UARTの置かれているメモリ領域にデータを出力すればよい。
void uartputc(int c) { int i; if(!uart) return; for(i = 0; i < 128 && !(inb(COM1+5) & 0x20); i++) microdelay(10); outb(COM1+0, c); }
xv6のcprintfの仕組み
cprintfでは、可変引数の機能を使っている。
// Print to the console. only understands %d, %x, %p, %s. void cprintf(char *fmt, ...) { int i, c, locking; uint *argp; char *s; locking = cons.locking; if(locking) acquire(&cons.lock); if (fmt == 0) panic("null fmt"); argp = (uint*)(void*)(&fmt + 1); for(i = 0; (c = fmt[i] & 0xff) != 0; i++){ if(c != '%'){
ソースコードを見る限り、%d, %x, %p, %s, %%
が利用可能なようだ。
%d, %x, %pのときは、値を文字列に変換する処理に入る。
switch(c){ case 'd': printint(*argp++, 10, 1); break; ... static void printint(int xx, int base, int sign)
RISC-VのGCCでコンパイルしたcprintf
ところが、どうやら可変長引数の処理のところにバグがありそうで、うまく表示ができていない。
80100600 <cprintf>: 80100600: fa010113 addi sp,sp,-96 80100604: 02112e23 sw ra,60(sp) 80100608: 02812c23 sw s0,56(sp) 8010060c: 04010413 addi s0,sp,64 80100610: fca42623 sw a0,-52(s0) 80100614: 00b42223 sw a1,4(s0) 80100618: 00c42423 sw a2,8(s0) 8010061c: 00d42623 sw a3,12(s0) 80100620: 00e42823 sw a4,16(s0) 80100624: 00f42a23 sw a5,20(s0) 80100628: 01042c23 sw a6,24(s0) 8010062c: 01142e23 sw a7,28(s0)
最初に引数を格納するのだが、なぜa0だけオフセットが違う?a1以降とメモリ場所がずれており、引数が上手く取り扱えていない気がする。
とりあえず、このあたりも継続してデバッグしていく。