//////// / / I/O library for use with boot programs. Uses the BIOS. / / La Monte H. Yarroll <piggy@mwc.com>, September 1991 / //////// //////// / / Magic constants. / //////// RETF = 0xCB / Far return VIDEO = 0x10 / video swi DISK = 0x13 / disk io swi KEYBD = 0x16 / keyboard swi MON = 0x00 / Monitor swi READ1 = 0x0201 / read 1 sector BUFSIZE = 0x200 / Size of a physical disk block. NTRK = 40 / Number of tracks on a floppy. NSPT = 9 / Number of sectors per track on a floppy. NHD = 1 / Number of heads per drive on a floppy. FIRST = 8 / Relative start of partition. .shri / Shared code segment, initialized. //////// / / Read a block from disk, relative to the start of the boot partition, / using the code in the IBM firmware. / / It takes two parameters: / daddr_t blockno; /* 32 bit block number. */ / char *buff; /* Must point to a 512 byte buffer. */ / / The buffer must not cross a 4K boundry. Disk input should generally / be done through the C routine bread(), which calls _bread() with an / aligned buffer. / //////// .globl _bread_ _bread_: push es / Save registers push si push di push bp push dx push ds pop es / Set es:bp to address of the buffer. mov bp, sp mov ax, 12(bp) / Get low word of block number. mov dx, 14(bp) / Get high word of block number. mov bx, 16(bp) / Get a buffer to put it in. mov bp, bx mov di, bp / Blast the buffer contents. mov cx, $BUFSIZE / For block 0, this fills the buffer rep / with zeros. stosb / Block #0 is the sparse block--it means a block of all zeros. test ax, ax / if block 0, return zeroed buffer jnz 3f test dx, dx jnz 3f movb al, $1 / Say that we read 1 block. jmp 2f / Translate block number into cylinder, head, and sector. 3: add ax, first / add first block adc dx, first+2 / add rest mov bx, ax / save block number movb al, heads / get number of heads movb cl, sects / get number of sectors mulb cl / calculate sectors per cylinder xchg bx,ax / swap block/sectors div bx / calculate track xchg dx, ax / put track in DX divb cl / calculate head/sector movb cl, ah / set sector inc cx / sectors start at 1 [incb cl] cmp dx, traks / check for second side jb 0f sub dx, traks / fold track inc ax / next head [incb al] 0: rorb dh, $1 / rotate track(low) into rorb dh, $1 / msbits of DX orb cl, dh / set track(high) movb ch, dl / set track(low) movb dh, al / set head movb dl, drive / set drive mov bx, bp / set offset [bbuf] mov ax, $READ1 / Read, 1 sector. int DISK / Disk I/O. jnc 2f / Jump if no error. mov ax, $READ1 / try again int DISK jc berror 2: / al contains the number of blocks read (should be 1). pop dx / restore registers. pop bp pop di pop si pop es ret / return. berror: / error handling for _bread. xorb al, al / ah contains an error code. jmp 2b //////// / / Write the character in "al" out to / the display, using routines in the ROM. / Like most calls to the ROM, this routine spends / most of its time saving and restoring the / registers. / //////// .globl putchar_ putchar_: push si / Save registers. push di push bp mov bp, sp mov ax, 8(bp) / Fetch the single argument. mov bx, $0x0007 / Page 0, white on black movb ah, $0x0E / Write TTY. int VIDEO / Call video I/O in ROM. pop bp / Restore registers. pop di pop si ret //////// / / Fetch character from keyboard, using / routines in the ROM. / //////// .globl getchar_ getchar_: push si / Save registers. push di push bp movb ah, $0x00 / Read keystroke. int KEYBD movb ah, $0x00 pop bp / Restore registers. pop di pop si ret //////// / / Check for a pending keystroke using / routines in the ROM. / //////// .globl iskey_ iskey_: push si / Save registers. push di push bp movb ah, $0x01 / Check for keystroke. int KEYBD jne 0f xor ax, ax / Set false. jmp 1f 0: xor ax, ax inc ax / Set true. 1: pop bp / Restore registers. pop di pop si ret //////// / / Goto a far address / Takes two integer arguments: an offset, and a segment, in that order. / //////// .globl gotofar_ gotofar_: add sp, $2 .byte RETF //////// / / Goto a kernel. / Takes three integer arguments: an offset, a segment, and a new data segment / in that order. / //////// .globl gotoker_ gotoker_: mov bp, sp mov es, 6(bp) / Point es at the new data segment. mov si, $seconddat / Point ds:si at useful data. / add sp, $2 / .byte RETF mov bx, bp add bx, $2 xcall (bx) ret //////// / / Initilize hard disk parameters / //////// .globl hdinit_ hdinit_: push si / Save registers. push di push bp mov si, bp / set si to partition table movb dl, (si) / get drive number movb ah, $8 / get drive parameters int DISK jc 1f / abort on error (just return) movb al, ch / fetch cyl(lo) movb ah, cl / move cyl(hi), sects rolb ah, $1 / shift cylinder high to rolb ah, $1 / the least sig bits andb ah, $3 / mask out cylinder bits mov di, $traks / point to drive stosw / set number of tracks movb al, $0x3F / sector mask andb al, cl / mask sector stosb / set sector movb al, dh / get max head inc ax / change to # of heads (incb al) stosb / set number of heads movsb / set drive add si, $FIRST-1 / point to first block movsw / fetch first block movsw 1: pop bp / Restore registers. pop di pop si ret //////// / / Invoke the native monitor. / Useful for debugging. / //////// .globl intmon_ intmon_: int MON ret //////// / / void _ffcopy(from_fp, to_fp, count) / faddr_t from_fp, to_fp; / int count; / / Copy count bytes from from_fp to to_fp. / / Here is the stack after initial "push bp": / / 12(bp) count / 10(bp) FP_SEL(to_fp) / 8(bp) FP_OFF(to_fp) / 6(bp) FP_SEL(from_fp) / 4(bp) FP_OFF(from_fp) / 2(bp) return IP / 0(bp) old bp / //////// .globl _ffcopy_ _ffcopy_: push bp mov bp, sp push es push di push ds push si lds si, 4(bp) / from_fp to DS:SI les di, 8(bp) / to_fp to ES:DI mov cx, 12(bp) / rep count to CX rep movsb pop si pop ds pop di pop es pop bp ret / return from _ffcopy() //////// / / Read a block from disk, relative to start of disk, / using the code in the IBM firmware. / / It takes two parameters: / daddr_t blockno; /* 32 bit block number. */ / char *buff; /* Must point to a 512 byte buffer. */ / / The buffer must not cross a 4K boundry. Disk input should generally / be done through the C routine xbread(), which calls _xbread() with an / aligned buffer. / //////// .globl _xbread_ _xbread_: push es / Save registers push si push di push bp push dx push ds pop es / Set es:bp to address of the buffer. mov bp, sp mov ax, 12(bp) / Get low word of block number. mov dx, 14(bp) / Get high word of block number. mov bx, 16(bp) / Get a buffer to put it in. mov bp, bx / Translate block number into cylinder, head, and sector. 3: mov bx, ax / save block number movb al, heads / get number of heads movb cl, sects / get number of sectors mulb cl / calculate sectors per cylinder xchg bx,ax / swap block/sectors div bx / calculate track xchg dx, ax / put track in DX divb cl / calculate head/sector movb cl, ah / set sector inc cx / sectors start at 1 [incb cl] cmp dx, traks / check for second side jb 0f sub dx, traks / fold track inc ax / next head [incb al] 0: rorb dh, $1 / rotate track(low) into rorb dh, $1 / msbits of DX orb cl, dh / set track(high) movb ch, dl / set track(low) movb dh, al / set head movb dl, drive / set drive mov bx, bp / set offset [bbuf] mov ax, $READ1 / Read, 1 sector. int DISK / Disk I/O. jnc 2f / Jump if no error. mov ax, $READ1 / try again int DISK jc berror 2: / al contains the number of blocks read (should be 1). pop dx / restore registers. pop bp pop di pop si pop es ret / return.