1158559Snyan# 2158559Snyan# Copyright (c) 2006 TAKAHASHI Yoshihiro <nyan@FreeBSD.org> 3158559Snyan# Copyright (c) 2001 John Baldwin <jhb@FreeBSD.org> 4158559Snyan# All rights reserved. 5158559Snyan# 6158559Snyan# Redistribution and use in source and binary forms, with or without 7158559Snyan# modification, are permitted provided that the following conditions 8158559Snyan# are met: 9158559Snyan# 1. Redistributions of source code must retain the above copyright 10158559Snyan# notice, this list of conditions and the following disclaimer. 11158559Snyan# 2. Redistributions in binary form must reproduce the above copyright 12158559Snyan# notice, this list of conditions and the following disclaimer in the 13158559Snyan# documentation and/or other materials provided with the distribution. 14158559Snyan# 3. Neither the name of the author nor the names of any co-contributors 15158559Snyan# may be used to endorse or promote products derived from this software 16158559Snyan# without specific prior written permission. 17158559Snyan# 18158559Snyan# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19158559Snyan# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20158559Snyan# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21158559Snyan# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22158559Snyan# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23158559Snyan# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24158559Snyan# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25158559Snyan# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26158559Snyan# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27158559Snyan# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28158559Snyan# SUCH DAMAGE. 29158559Snyan# 30158559Snyan 31158559Snyan# $FreeBSD$ 32158559Snyan 33158559Snyan# 34158559Snyan# Basically, we first create a set of boot arguments to pass to the loaded 35158559Snyan# binary. Then we attempt to load /boot/loader from the CD we were booted 36158559Snyan# off of. 37158559Snyan# 38158559Snyan 39243448Snyan#include <bootargs.h> 40243448Snyan 41158559Snyan# 42158559Snyan# Memory locations. 43158559Snyan# 44158559Snyan .set STACK_OFF,0x6000 # Stack offset 45158559Snyan .set LOAD_SEG,0x0700 # Load segment 46158559Snyan .set LOAD_SIZE,2048 # Load size 47158559Snyan .set DAUA,0x0584 # DA/UA 48158559Snyan 49158559Snyan .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k 50158559Snyan .set MEM_ARG,0x900 # Arguments at start 51158559Snyan .set MEM_ARG_BTX,0xa100 # Where we move them to so the 52158559Snyan # BTX client can see them 53158559Snyan .set MEM_ARG_SIZE,0x18 # Size of the arguments 54158559Snyan .set MEM_BTX_ADDRESS,0x9000 # where BTX lives 55158559Snyan .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute 56158559Snyan .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader 57158559Snyan .set MEM_BTX_CLIENT,0xa000 # where BTX clients live 58158559Snyan# 59158559Snyan# PC98 machine type from sys/pc98/pc98/pc98_machdep.h 60158559Snyan# 61158559Snyan .set MEM_SYS, 0xa100 # System common area segment 62158559Snyan .set PC98_MACHINE_TYPE, 0x0620 # PC98 machine type 63158559Snyan .set EPSON_ID, 0x0624 # EPSON machine id 64158559Snyan 65158559Snyan .set M_NEC_PC98, 0x0001 66158559Snyan .set M_EPSON_PC98, 0x0002 67158559Snyan .set M_NOT_H98, 0x0010 68158559Snyan .set M_H98, 0x0020 69158559Snyan .set M_NOTE, 0x0040 70158559Snyan .set M_NORMAL, 0x1000 71158559Snyan .set M_8M, 0x8000 72158559Snyan# 73158559Snyan# Signature Constants 74158559Snyan# 75158559Snyan .set SIG1_OFF,0x1fe # Signature offset 76158559Snyan .set SIG2_OFF,0x7fe # Signature offset 77158559Snyan# 78158559Snyan# a.out header fields 79158559Snyan# 80158559Snyan .set AOUT_TEXT,0x04 # text segment size 81158559Snyan .set AOUT_DATA,0x08 # data segment size 82158559Snyan .set AOUT_BSS,0x0c # zero'd BSS size 83158559Snyan .set AOUT_SYMBOLS,0x10 # symbol table 84158559Snyan .set AOUT_ENTRY,0x14 # entry point 85158559Snyan .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header 86158559Snyan# 87158559Snyan# Segment selectors. 88158559Snyan# 89158559Snyan .set SEL_SDATA,0x8 # Supervisor data 90158559Snyan .set SEL_RDATA,0x10 # Real mode data 91158559Snyan .set SEL_SCODE,0x18 # PM-32 code 92158559Snyan .set SEL_SCODE16,0x20 # PM-16 code 93158559Snyan# 94158559Snyan# BTX constants 95158559Snyan# 96158559Snyan .set INT_SYS,0x30 # BTX syscall interrupt 97158559Snyan# 98158559Snyan# Constants for reading from the CD. 99158559Snyan# 100158559Snyan .set ERROR_TIMEOUT,0x90 # BIOS timeout on read 101158559Snyan .set NUM_RETRIES,3 # Num times to retry 102158559Snyan .set SECTOR_SIZE,0x800 # size of a sector 103158559Snyan .set SECTOR_SHIFT,11 # number of place to shift 104158559Snyan .set BUFFER_LEN,0x100 # number of sectors in buffer 105158559Snyan .set MAX_READ,0xf800 # max we can read at a time 106158559Snyan .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT 107158559Snyan .set MEM_READ_BUFFER,0x9000 # buffer to read from CD 108158559Snyan .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor 109158559Snyan .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer 110158559Snyan .set VOLDESC_LBA,0x10 # LBA of vol descriptor 111158559Snyan .set VD_PRIMARY,1 # Primary VD 112158559Snyan .set VD_END,255 # VD Terminator 113158559Snyan .set VD_ROOTDIR,156 # Offset of Root Dir Record 114158559Snyan .set DIR_LEN,0 # Offset of Dir Record length 115158559Snyan .set DIR_EA_LEN,1 # Offset of EA length 116158559Snyan .set DIR_EXTENT,2 # Offset of 64-bit LBA 117158559Snyan .set DIR_SIZE,10 # Offset of 64-bit length 118158559Snyan .set DIR_NAMELEN,32 # Offset of 8-bit name len 119158559Snyan .set DIR_NAME,33 # Offset of dir name 120158559Snyan 121158559Snyan# 122158559Snyan# Program start. 123158559Snyan# 124158559Snyan .code16 125158559Snyan .globl start 126158559Snyan 127158559Snyanstart: jmp main 128158559Snyan 129158559Snyan .org 4 130158559Snyan .ascii "IPL1 " 131158559Snyan 132158559Snyanmain: cld 133158559Snyan 134158559Snyan /* Setup the stack */ 135158559Snyan xor %ax,%ax 136158559Snyan mov %ax,%ss 137158559Snyan mov $STACK_OFF,%sp 138158559Snyan 139158559Snyan push %ecx 140158559Snyan 141158559Snyan /* Setup graphic screen */ 142158559Snyan mov $0x42,%ah # 640x400 143158559Snyan mov $0xc0,%ch 144158559Snyan int $0x18 145158559Snyan mov $0x40,%ah # graph on 146158559Snyan int $0x18 147158559Snyan 148158559Snyan /* Setup text screen */ 149158559Snyan mov $0x0a00,%ax # 80x25 150158559Snyan int $0x18 151158559Snyan mov $0x0c,%ah # text on 152158559Snyan int $0x18 153158559Snyan mov $0x13,%ah # cursor home 154158559Snyan xor %dx,%dx 155158559Snyan int $0x18 156158559Snyan mov $0x11,%ah # cursor on 157158559Snyan int $0x18 158158559Snyan 159158559Snyan /* Setup keyboard */ 160158559Snyan mov $0x03,%ah 161158559Snyan int $0x18 162158559Snyan 163158559Snyan /* Transfer PC-9801 system common area */ 164158559Snyan xor %ax,%ax 165158559Snyan mov %ax,%si 166158559Snyan mov %ax,%ds 167158559Snyan mov %ax,%di 168158559Snyan mov $MEM_SYS,%ax 169158559Snyan mov %ax,%es 170158559Snyan mov $0x0600,%cx 171158559Snyan rep 172158559Snyan movsb 173158559Snyan 174158559Snyan /* Transfer EPSON machine type */ 175158559Snyan mov $0xfd00,%ax 176158559Snyan mov %ax,%ds 177158559Snyan mov (0x804),%eax 178158559Snyan and $0x00ffffff,%eax 179158559Snyan mov %eax,%es:(EPSON_ID) 180158559Snyan 181158559Snyan /* Set machine type to PC98_SYSTEM_PARAMETER */ 182158559Snyan call machine_check 183158559Snyan 184158559Snyan /* Load cdboot */ 185158559Snyan xor %ax,%ax 186158559Snyan mov %ax,%ds 187158559Snyan mov $0x06,%ah /* Read data */ 188158559Snyan mov (DAUA),%al /* Read drive */ 189158559Snyan pop %ecx /* cylinder */ 190158559Snyan xor %dx,%dx /* head / sector */ 191158559Snyan mov $LOAD_SEG,%bx /* Load address */ 192158559Snyan mov %bx,%es 193158559Snyan xor %bp,%bp 194158559Snyan mov $LOAD_SIZE,%bx /* Load size */ 195158559Snyan int $0x1b 196158559Snyan mov $msg_readerr,%si 197158559Snyan jc error 198158559Snyan 199158559Snyan /* Jump to cdboot */ 200158559Snyan ljmp $LOAD_SEG,$cdboot 201158559Snyan 202158559Snyan# 203158559Snyan# Set machine type to PC98_SYSTEM_PARAMETER. 204158559Snyan# 205158559Snyanmachine_check: xor %edx,%edx 206158559Snyan mov %dx,%ds 207158559Snyan mov $MEM_SYS,%ax 208158559Snyan mov %ax,%es 209158559Snyan 210158559Snyan /* Wait V-SYNC */ 211158559Snyanvsync.1: inb $0x60,%al 212158559Snyan test $0x20,%al 213158559Snyan jnz vsync.1 214158559Snyanvsync.2: inb $0x60,%al 215158559Snyan test $0x20,%al 216158559Snyan jz vsync.2 217158559Snyan 218158559Snyan /* ANK 'A' font */ 219158559Snyan xor %al,%al 220158559Snyan outb %al,$0xa1 221158559Snyan mov $0x41,%al 222158559Snyan outb %al,$0xa3 223158559Snyan 224158559Snyan /* Get 'A' font from CG window */ 225158559Snyan push %ds 226158559Snyan mov $0xa400,%ax 227158559Snyan mov %ax,%ds 228158559Snyan xor %eax,%eax 229158559Snyan xor %bx,%bx 230158559Snyan mov $4,%cx 231158559Snyanfont.1: add (%bx),%eax 232158559Snyan add $4,%bx 233158559Snyan loop font.1 234158559Snyan pop %ds 235158559Snyan cmp $0x6efc58fc,%eax 236158559Snyan jnz m_epson 237158559Snyan 238158559Snyanm_pc98: or $M_NEC_PC98,%edx 239158559Snyan mov $0x0458,%bx 240158559Snyan mov (%bx),%al 241158559Snyan test $0x80,%al 242158559Snyan jz m_not_h98 243158559Snyan or $M_H98,%edx 244158559Snyan jmp 1f 245158559Snyanm_epson: or $M_EPSON_PC98,%edx 246158559Snyanm_not_h98: or $M_NOT_H98,%edx 247158559Snyan 248158559Snyan1: inb $0x42,%al 249158559Snyan test $0x20,%al 250158559Snyan jz 1f 251158559Snyan or $M_8M,%edx 252158559Snyan 253158559Snyan1: mov $0x0400,%bx 254158559Snyan mov (%bx),%al 255158559Snyan test $0x80,%al 256158559Snyan jz 1f 257158559Snyan or $M_NOTE,%edx 258158559Snyan 259158559Snyan1: mov $PC98_MACHINE_TYPE,%bx 260158559Snyan mov %edx,%es:(%bx) 261158559Snyan ret 262158559Snyan 263158559Snyan# 264158559Snyan# Print out the error message at [SI], wait for a keypress, and then 265158559Snyan# reboot the machine. 266158559Snyan# 267158559Snyanerror: call putstr 268158559Snyan mov $msg_keypress,%si 269158559Snyan call putstr 270158559Snyan xor %ax,%ax # Get keypress 271158559Snyan int $0x18 272158559Snyan xor %ax,%ax # CPU reset 273158559Snyan outb %al,$0xf0 274158559Snyanhalt: hlt 275158559Snyan jmp halt # Spin 276158559Snyan 277158559Snyan# 278158559Snyan# Display a null-terminated string at [SI]. 279158559Snyan# 280158559Snyan# Trashes: AX, BX, CX, DX, SI, DI 281158559Snyan# 282158559Snyanputstr: push %ds 283158559Snyan push %es 284158559Snyan mov %cs,%ax 285158559Snyan mov %ax,%ds 286158559Snyan mov $0xa000,%ax 287158559Snyan mov %ax,%es 288158559Snyan mov cursor,%di 289158559Snyan mov $0x00e1,%bx # Attribute 290158559Snyan mov $160,%cx 291158559Snyanputstr.0: lodsb 292158559Snyan testb %al,%al 293158559Snyan jz putstr.done 294158559Snyan cmp $0x0d,%al 295158559Snyan jz putstr.cr 296158559Snyan cmp $0x0a,%al 297158559Snyan jz putstr.lf 298158559Snyan mov %bl,%es:0x2000(%di) 299158559Snyan stosb 300158559Snyan inc %di 301158559Snyan jmp putstr.move 302158559Snyanputstr.cr: xor %dx,%dx 303158559Snyan mov %di,%ax 304158559Snyan div %cx 305158559Snyan sub %dx,%di 306158559Snyan jmp putstr.move 307158559Snyanputstr.lf: add %cx,%di 308158559Snyanputstr.move: mov %di,%dx 309158559Snyan mov $0x13,%ah # Move cursor 310158559Snyan int $0x18 311158559Snyan jmp putstr.0 312158559Snyanputstr.done: mov %di,cursor 313158559Snyan pop %es 314158559Snyan pop %ds 315158559Snyan ret 316158559Snyan 317158559Snyan# 318158559Snyan# Display a single char at [AL], but don't move a cursor. 319158559Snyan# 320158559Snyanputc: push %es 321158559Snyan push %di 322158559Snyan push %bx 323158559Snyan mov $0xa000,%bx 324158559Snyan mov %bx,%es 325158559Snyan mov cursor,%di 326158559Snyan mov $0xe1,%bl # Attribute 327158559Snyan mov %bl,%es:0x2000(%di) 328158559Snyan stosb 329158559Snyan pop %bx 330158559Snyan pop %di 331158559Snyan pop %es 332158559Snyan ret 333158559Snyan 334158559Snyanmsg_readerr: .asciz "Read Error\r\n" 335158559Snyanmsg_keypress: .asciz "\r\nPress any key to reboot\r\n" 336158559Snyan 337158559Snyan/* Boot signature */ 338158559Snyan 339158559Snyan .org SIG1_OFF,0x90 340158559Snyan 341158559Snyan .word 0xaa55 # Magic number 342158559Snyan 343158559Snyan# 344158559Snyan# cdboot 345158559Snyan# 346158559Snyancdboot: mov %cs,%ax 347158559Snyan mov %ax,%ds 348158559Snyan xor %ax,%ax 349158559Snyan mov %ax,%es 350158559Snyan mov %es:(DAUA),%al # Save BIOS boot device 351158559Snyan mov %al,drive 352158559Snyan mov %cx,cylinder # Save BIOS boot cylinder 353158559Snyan 354158559Snyan mov $msg_welcome,%si # %ds:(%si) -> welcome message 355158559Snyan call putstr # display the welcome message 356158559Snyan# 357158559Snyan# Setup the arguments that the loader is expecting from boot[12] 358158559Snyan# 359158559Snyan mov $msg_bootinfo,%si # %ds:(%si) -> boot args message 360158559Snyan call putstr # display the message 361158559Snyan mov $MEM_ARG,%bx # %ds:(%bx) -> boot args 362158559Snyan mov %bx,%di # %es:(%di) -> boot args 363158559Snyan xor %eax,%eax # zero %eax 364158559Snyan mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit 365158559Snyan # dwords 366158559Snyan rep # Clear the arguments 367158559Snyan stosl # to zero 368158559Snyan mov drive,%dl # Store BIOS boot device 369158559Snyan mov %dl,%es:0x4(%bx) # in kargs->bootdev 370158559Snyan or $KARGS_FLAGS_CD,%es:0x8(%bx) # kargs->bootflags |= 371158559Snyan # KARGS_FLAGS_CD 372158559Snyan# 373158559Snyan# Load Volume Descriptor 374158559Snyan# 375158559Snyan mov $VOLDESC_LBA,%eax # Set LBA of first VD 376158559Snyanload_vd: push %eax # Save %eax 377158559Snyan mov $1,%dh # One sector 378158559Snyan mov $MEM_VOLDESC,%ebx # Destination 379158559Snyan call read # Read it in 380158559Snyan cmpb $VD_PRIMARY,%es:(%bx) # Primary VD? 381158559Snyan je have_vd # Yes 382158559Snyan pop %eax # Prepare to 383158559Snyan inc %eax # try next 384158559Snyan cmpb $VD_END,%es:(%bx) # Last VD? 385158559Snyan jne load_vd # No, read next 386158559Snyan mov $msg_novd,%si # No VD 387158559Snyan jmp error # Halt 388158559Snyanhave_vd: # Have Primary VD 389158559Snyan# 390158559Snyan# Try to look up the loader binary using the paths in the loader_paths 391158559Snyan# array. 392158559Snyan# 393158559Snyan mov $loader_paths,%si # Point to start of array 394158559Snyanlookup_path: push %si # Save file name pointer 395158559Snyan call lookup # Try to find file 396158559Snyan pop %di # Restore file name pointer 397158559Snyan jnc lookup_found # Found this file 398158559Snyan push %es 399158559Snyan mov %cs,%ax 400158559Snyan mov %ax,%es 401158559Snyan xor %al,%al # Look for next 402158559Snyan mov $0xffff,%cx # path name by 403158559Snyan repnz # scanning for 404158559Snyan scasb # nul char 405158559Snyan pop %es 406158559Snyan mov %di,%si # Point %si at next path 407158559Snyan mov (%si),%al # Get first char of next path 408158559Snyan or %al,%al # Is it double nul? 409158559Snyan jnz lookup_path # No, try it. 410158559Snyan mov $msg_failed,%si # Failed message 411158559Snyan jmp error # Halt 412158559Snyanlookup_found: # Found a loader file 413158559Snyan# 414158559Snyan# Load the binary into the buffer. Due to real mode addressing limitations 415219126Sbrucec# we have to read it in 64k chunks. 416158559Snyan# 417158559Snyan mov %es:DIR_SIZE(%bx),%eax # Read file length 418158559Snyan add $SECTOR_SIZE-1,%eax # Convert length to sectors 419158559Snyan shr $SECTOR_SHIFT,%eax 420158559Snyan cmp $BUFFER_LEN,%eax 421158559Snyan jbe load_sizeok 422158559Snyan mov $msg_load2big,%si # Error message 423158559Snyan jmp error 424158559Snyanload_sizeok: movzbw %al,%cx # Num sectors to read 425158559Snyan mov %es:DIR_EXTENT(%bx),%eax # Load extent 426158559Snyan xor %edx,%edx 427158559Snyan mov %es:DIR_EA_LEN(%bx),%dl 428158559Snyan add %edx,%eax # Skip extended 429158559Snyan mov $MEM_READ_BUFFER,%ebx # Read into the buffer 430158559Snyanload_loop: mov %cl,%dh 431158559Snyan cmp $MAX_READ_SEC,%cl # Truncate to max read size 432158559Snyan jbe load_notrunc 433158559Snyan mov $MAX_READ_SEC,%dh 434158559Snyanload_notrunc: sub %dh,%cl # Update count 435158559Snyan push %eax # Save 436158559Snyan call read # Read it in 437158559Snyan pop %eax # Restore 438158559Snyan add $MAX_READ_SEC,%eax # Update LBA 439158559Snyan add $MAX_READ,%ebx # Update dest addr 440158559Snyan jcxz load_done # Done? 441158559Snyan jmp load_loop # Keep going 442158559Snyanload_done: 443158559Snyan# 444158559Snyan# Turn on the A20 address line 445158559Snyan# 446158559Snyan xor %ax,%ax # Turn A20 on 447158559Snyan outb %al,$0xf2 448158559Snyan mov $0x02,%al 449158559Snyan outb %al,$0xf6 450158559Snyan# 451158559Snyan# Relocate the loader and BTX using a very lazy protected mode 452158559Snyan# 453158559Snyan mov $msg_relocate,%si # Display the 454158559Snyan call putstr # relocation message 455158559Snyan mov %es:(MEM_READ_BUFFER+AOUT_ENTRY),%edi # %edi is the destination 456158559Snyan mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is 457158559Snyan # the start of the text 458158559Snyan # segment 459158559Snyan mov %es:(MEM_READ_BUFFER+AOUT_TEXT),%ecx # %ecx = length of the text 460158559Snyan # segment 461158559Snyan push %edi # Save entry point for later 462158559Snyan lgdt gdtdesc # setup our own gdt 463158559Snyan cli # turn off interrupts 464158559Snyan mov %cr0,%eax # Turn on 465158559Snyan or $0x1,%al # protected 466158559Snyan mov %eax,%cr0 # mode 467158559Snyan ljmp $SEL_SCODE,$pm_start # long jump to clear the 468158559Snyan # instruction pre-fetch queue 469158559Snyan .code32 470158559Snyanpm_start: mov $SEL_SDATA,%ax # Initialize 471158559Snyan mov %ax,%ds # %ds and 472158559Snyan mov %ax,%es # %es to a flat selector 473158559Snyan rep # Relocate the 474158559Snyan movsb # text segment 475158559Snyan add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page 476158559Snyan and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment 477158559Snyan mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment 478158559Snyan rep # Relocate the 479158559Snyan movsb # data segment 480158559Snyan mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss 481158559Snyan xor %eax,%eax # zero %eax 482158559Snyan add $3,%cl # round %ecx up to 483158559Snyan shr $2,%ecx # a multiple of 4 484158559Snyan rep # zero the 485158559Snyan stosl # bss 486158559Snyan mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader 487158559Snyan add $MEM_BTX_OFFSET,%esi # %esi -> BTX in the loader 488158559Snyan mov $MEM_BTX_ADDRESS,%edi # %edi -> where BTX needs to go 489158559Snyan movzwl 0xa(%esi),%ecx # %ecx -> length of BTX 490158559Snyan rep # Relocate 491158559Snyan movsb # BTX 492158559Snyan ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM 493158559Snyan .code16 494158559Snyanpm_16: mov $SEL_RDATA,%ax # Initialize 495158559Snyan mov %ax,%ds # %ds and 496158559Snyan mov %ax,%es # %es to a real mode selector 497158559Snyan mov %cr0,%eax # Turn off 498158559Snyan and $~0x1,%al # protected 499158559Snyan mov %eax,%cr0 # mode 500158559Snyan ljmp $LOAD_SEG,$pm_end # Long jump to clear the 501158559Snyan # instruction pre-fetch queue 502158559Snyanpm_end: sti # Turn interrupts back on now 503158559Snyan# 504158559Snyan# Copy the BTX client to MEM_BTX_CLIENT 505158559Snyan# 506158559Snyan mov %cs,%ax 507158559Snyan mov %ax,%ds 508158559Snyan xor %ax,%ax 509158559Snyan mov %ax,%es 510158559Snyan mov $MEM_BTX_CLIENT,%di # Prepare to relocate 511158559Snyan mov $btx_client,%si # the simple btx client 512158559Snyan mov $(btx_client_end-btx_client),%cx # length of btx client 513158559Snyan rep # Relocate the 514158559Snyan movsb # simple BTX client 515158559Snyan# 516158559Snyan# Copy the boot[12] args to where the BTX client can see them 517158559Snyan# 518158559Snyan xor %ax,%ax 519158559Snyan mov %ax,%ds 520158559Snyan mov $MEM_ARG,%si # where the args are at now 521158559Snyan mov $MEM_ARG_BTX,%di # where the args are moving to 522158559Snyan mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs 523158559Snyan rep # Relocate 524158559Snyan movsl # the words 525158559Snyan# 526158559Snyan# Save the entry point so the client can get to it later on 527158559Snyan# 528158559Snyan pop %eax # Restore saved entry point 529158559Snyan stosl # and add it to the end of 530158559Snyan # the arguments 531158559Snyan# 532158559Snyan# Now we just start up BTX and let it do the rest 533158559Snyan# 534158559Snyan mov $msg_jump,%si # Display the 535158559Snyan call putstr # jump message 536158559Snyan ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point 537158559Snyan 538158559Snyan# 539158559Snyan# Lookup the file in the path at [SI] from the root directory. 540158559Snyan# 541158559Snyan# Trashes: All but BX 542158559Snyan# Returns: CF = 0 (success), BX = pointer to record 543158559Snyan# CF = 1 (not found) 544158559Snyan# 545158559Snyanlookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record 546158559Snyan push %bx 547158559Snyan push %si 548158559Snyan mov $msg_lookup,%si # Display lookup message 549158559Snyan call putstr 550158559Snyan pop %si 551158559Snyan push %si 552158559Snyan call putstr 553158559Snyan mov $msg_lookup2,%si 554158559Snyan call putstr 555158559Snyan pop %si 556158559Snyan pop %bx 557158559Snyanlookup_dir: lodsb # Get first char of path 558158559Snyan cmp $0,%al # Are we done? 559158559Snyan je lookup_done # Yes 560158559Snyan cmp $'/',%al # Skip path separator. 561158559Snyan je lookup_dir 562158559Snyan dec %si # Undo lodsb side effect 563158559Snyan call find_file # Lookup first path item 564158559Snyan jnc lookup_dir # Try next component 565158559Snyan mov $msg_lookupfail,%si # Not found message 566158559Snyan push %bx 567158559Snyan call putstr 568158559Snyan pop %bx 569158559Snyan stc # Set carry 570158559Snyan ret 571158559Snyanlookup_done: mov $msg_lookupok,%si # Success message 572158559Snyan push %bx 573158559Snyan call putstr 574158559Snyan pop %bx 575158559Snyan clc # Clear carry 576158559Snyan ret 577158559Snyan 578158559Snyan# 579158559Snyan# Lookup file at [SI] in directory whose record is at [BX]. 580158559Snyan# 581158559Snyan# Trashes: All but returns 582158559Snyan# Returns: CF = 0 (success), BX = pointer to record, SI = next path item 583158559Snyan# CF = 1 (not found), SI = preserved 584158559Snyan# 585158559Snyanfind_file: mov %es:DIR_EXTENT(%bx),%eax # Load extent 586158559Snyan xor %edx,%edx 587158559Snyan mov %es:DIR_EA_LEN(%bx),%dl 588158559Snyan add %edx,%eax # Skip extended attributes 589158559Snyan mov %eax,rec_lba # Save LBA 590158559Snyan mov %es:DIR_SIZE(%bx),%eax # Save size 591158559Snyan mov %eax,rec_size 592158559Snyan xor %cl,%cl # Zero length 593158559Snyan push %si # Save 594158559Snyanff.namelen: inc %cl # Update length 595158559Snyan lodsb # Read char 596158559Snyan cmp $0,%al # Nul? 597158559Snyan je ff.namedone # Yes 598158559Snyan cmp $'/',%al # Path separator? 599158559Snyan jnz ff.namelen # No, keep going 600158559Snyanff.namedone: dec %cl # Adjust length and save 601158559Snyan mov %cl,name_len 602158559Snyan pop %si # Restore 603158559Snyanff.load: mov rec_lba,%eax # Load LBA 604158559Snyan mov $MEM_DIR,%ebx # Address buffer 605158559Snyan mov $1,%dh # One sector 606158559Snyan call read # Read directory block 607158559Snyan incl rec_lba # Update LBA to next block 608158559Snyanff.scan: mov %ebx,%edx # Check for EOF 609158559Snyan sub $MEM_DIR,%edx 610158559Snyan cmp %edx,rec_size 611158559Snyan ja ff.scan.1 612158559Snyan stc # EOF reached 613158559Snyan ret 614158559Snyanff.scan.1: cmpb $0,%es:DIR_LEN(%bx) # Last record in block? 615158559Snyan je ff.nextblock 616158559Snyan push %si # Save 617158559Snyan movzbw %es:DIR_NAMELEN(%bx),%si # Find end of string 618158559Snyanff.checkver: cmpb $'0',%es:DIR_NAME-1(%bx,%si) # Less than '0'? 619158559Snyan jb ff.checkver.1 620158559Snyan cmpb $'9',%es:DIR_NAME-1(%bx,%si) # Greater than '9'? 621158559Snyan ja ff.checkver.1 622158559Snyan dec %si # Next char 623158559Snyan jnz ff.checkver 624158559Snyan jmp ff.checklen # All numbers in name, so 625158559Snyan # no version 626158559Snyanff.checkver.1: movzbw %es:DIR_NAMELEN(%bx),%cx 627158559Snyan cmp %cx,%si # Did we find any digits? 628158559Snyan je ff.checkdot # No 629158559Snyan cmpb $';',%es:DIR_NAME-1(%bx,%si) # Check for semicolon 630158559Snyan jne ff.checkver.2 631158559Snyan dec %si # Skip semicolon 632158559Snyan mov %si,%cx 633158559Snyan mov %cl,%es:DIR_NAMELEN(%bx) # Adjust length 634158559Snyan jmp ff.checkdot 635158559Snyanff.checkver.2: mov %cx,%si # Restore %si to end of string 636158559Snyanff.checkdot: cmpb $'.',%es:DIR_NAME-1(%bx,%si) # Trailing dot? 637158559Snyan jne ff.checklen # No 638158559Snyan decb %es:DIR_NAMELEN(%bx) # Adjust length 639158559Snyanff.checklen: pop %si # Restore 640158559Snyan movzbw name_len,%cx # Load length of name 641158559Snyan cmp %cl,%es:DIR_NAMELEN(%bx) # Does length match? 642158559Snyan je ff.checkname # Yes, check name 643158559Snyanff.nextrec: add %es:DIR_LEN(%bx),%bl # Next record 644158559Snyan adc $0,%bh 645158559Snyan jmp ff.scan 646158559Snyanff.nextblock: subl $SECTOR_SIZE,rec_size # Adjust size 647158559Snyan jnc ff.load # If subtract ok, keep going 648158559Snyan ret # End of file, so not found 649158559Snyanff.checkname: lea DIR_NAME(%bx),%di # Address name in record 650158559Snyan push %si # Save 651158559Snyan repe cmpsb # Compare name 652158559Snyan je ff.match # We have a winner! 653158559Snyan pop %si # Restore 654158559Snyan jmp ff.nextrec # Keep looking. 655158559Snyanff.match: add $2,%sp # Discard saved %si 656158559Snyan clc # Clear carry 657158559Snyan ret 658158559Snyan 659158559Snyan# 660158559Snyan# Load DH sectors starting at LBA EAX into [EBX]. 661158559Snyan# 662158559Snyan# Trashes: EAX 663158559Snyan# 664158559Snyanread: push %es # Save 665158559Snyan push %bp 666158559Snyan push %dx 667158559Snyan push %cx 668158559Snyan push %ebx 669158559Snyan mov %bx,%bp # Set destination address 670158559Snyan and $0x000f,%bp 671158559Snyan shr $4,%ebx 672158559Snyan mov %bx,%es 673158559Snyan xor %bx,%bx # Set read bytes 674158559Snyan mov %dh,%bl 675158559Snyan shl $SECTOR_SHIFT,%bx # 2048 bytes/sec 676158559Snyan mov %ax,%cx # Set LBA 677158559Snyan shr $16,%eax 678158559Snyan mov %ax,%dx 679158559Snyanread.retry: mov $0x06,%ah # BIOS device read 680158559Snyan mov drive,%al 681158559Snyan and $0x7f,%al 682158559Snyan call twiddle # Entertain the user 683158559Snyan int $0x1b # Call BIOS 684158559Snyan jc read.fail # Worked? 685158559Snyan pop %ebx # Restore 686158559Snyan pop %cx 687158559Snyan pop %dx 688158559Snyan pop %bp 689158559Snyan pop %es 690158559Snyan ret # Return 691158559Snyanread.fail: cmp $ERROR_TIMEOUT,%ah # Timeout? 692158559Snyan je read.retry # Yes, Retry. 693158559Snyanread.error: mov %ah,%al # Save error 694158559Snyan mov $hex_error,%di # Format it 695158559Snyan call hex8 # as hex 696158559Snyan mov $msg_badread,%si # Display Read error message 697158559Snyan jmp error 698158559Snyan 699158559Snyan# 700158559Snyan# Output the "twiddle" 701158559Snyan# 702158559Snyantwiddle: push %ax # Save 703158559Snyan push %bx # Save 704158559Snyan mov twiddle_index,%al # Load index 705167191Snyan mov $twiddle_chars,%bx # Address table 706158559Snyan inc %al # Next 707158559Snyan and $3,%al # char 708158559Snyan mov %al,twiddle_index # Save index for next call 709158559Snyan xlat # Get char 710158559Snyan call putc # Output it 711158559Snyan pop %bx # Restore 712158559Snyan pop %ax # Restore 713158559Snyan ret 714158559Snyan 715158559Snyan# 716158559Snyan# Convert AL to hex, saving the result to [EDI]. 717158559Snyan# 718158559Snyanhex8: pushl %eax # Save 719158559Snyan shrb $0x4,%al # Do upper 720158559Snyan call hex8.1 # 4 721158559Snyan popl %eax # Restore 722158559Snyanhex8.1: andb $0xf,%al # Get lower 4 723158559Snyan cmpb $0xa,%al # Convert 724158559Snyan sbbb $0x69,%al # to hex 725158559Snyan das # digit 726158559Snyan orb $0x20,%al # To lower case 727158559Snyan mov %al,(%di) # Save char 728158559Snyan inc %di 729158559Snyan ret # (Recursive) 730158559Snyan 731158559Snyan# 732158559Snyan# BTX client to start btxldr 733158559Snyan# 734158559Snyan .code32 735158559Snyanbtx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi 736158559Snyan # %ds:(%esi) -> end 737158559Snyan # of boot[12] args 738158559Snyan mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push 739158559Snyan std # Go backwards 740158559Snyanpush_arg: lodsl # Read argument 741158559Snyan push %eax # Push it onto the stack 742158559Snyan loop push_arg # Push all of the arguments 743158559Snyan cld # In case anyone depends on this 744158559Snyan pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of 745158559Snyan # the loader 746158559Snyan push %eax # Emulate a near call 747158559Snyan mov $0x1,%eax # 'exec' system call 748158559Snyan int $INT_SYS # BTX system call 749158559Snyanbtx_client_end: 750158559Snyan .code16 751158559Snyan 752158559Snyan .p2align 4 753158559Snyan# 754158559Snyan# Global descriptor table. 755158559Snyan# 756158559Snyangdt: .word 0x0,0x0,0x0,0x0 # Null entry 757158559Snyan .word 0xffff,0x0000,0x9200,0x00cf # SEL_SDATA 758158559Snyan .word 0xffff,0x0000,0x9200,0x0000 # SEL_RDATA 759158559Snyan .word 0xffff,LOAD_SEG<<4,0x9a00,0x00cf # SEL_SCODE (32-bit) 760158559Snyan .word 0xffff,LOAD_SEG<<4,0x9a00,0x008f # SEL_SCODE16 (16-bit) 761158559Snyangdt.1: 762158559Snyan# 763158559Snyan# Pseudo-descriptors. 764158559Snyan# 765158559Snyangdtdesc: .word gdt.1-gdt-1 # Limit 766158559Snyan .long LOAD_SEG<<4 + gdt # Base 767158559Snyan 768158559Snyan# 769158559Snyan# BOOT device 770158559Snyan# 771158559Snyandrive: .byte 0 772158559Snyancylinder: .word 0 773158559Snyan 774158559Snyan# 775158559Snyan# State for searching dir 776158559Snyan# 777158559Snyanrec_lba: .long 0x0 # LBA (adjusted for EA) 778158559Snyanrec_size: .long 0x0 # File size 779158559Snyanname_len: .byte 0x0 # Length of current name 780158559Snyan 781158559Snyancursor: .word 0 782158559Snyantwiddle_index: .byte 0x0 783158559Snyan 784158559Snyanmsg_welcome: .asciz "CD Loader 1.2\r\n\n" 785158559Snyanmsg_bootinfo: .asciz "Building the boot loader arguments\r\n" 786158559Snyanmsg_relocate: .asciz "Relocating the loader and the BTX\r\n" 787158559Snyanmsg_jump: .asciz "Starting the BTX loader\r\n" 788158559Snyanmsg_badread: .ascii "Read Error: 0x" 789173720Snyanhex_error: .asciz "00\r\n" 790158559Snyanmsg_novd: .asciz "Could not find Primary Volume Descriptor\r\n" 791158559Snyanmsg_lookup: .asciz "Looking up " 792158559Snyanmsg_lookup2: .asciz "... " 793158559Snyanmsg_lookupok: .asciz "Found\r\n" 794158559Snyanmsg_lookupfail: .asciz "File not found\r\n" 795158559Snyanmsg_load2big: .asciz "File too big\r\n" 796158559Snyanmsg_failed: .asciz "Boot failed\r\n" 797158559Snyantwiddle_chars: .ascii "|/-\\" 798158559Snyanloader_paths: .asciz "/BOOT.PC98/LOADER" 799158559Snyan .asciz "/boot.pc98/loader" 800158559Snyan .asciz "/BOOT/LOADER" 801158559Snyan .asciz "/boot/loader" 802158559Snyan .byte 0 803158559Snyan 804158559Snyan/* Boot signature */ 805158559Snyan 806158559Snyan .org SIG2_OFF,0x90 807158559Snyan 808158559Snyan .word 0xaa55 # Magic number 809