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