initial commit
[freebsd-arm:freebsd-arm.git] / boot / pc98 / cdboot / cdboot.s
1 #
2 # Copyright (c) 2006 TAKAHASHI Yoshihiro <nyan@FreeBSD.org>
3 # Copyright (c) 2001 John Baldwin <jhb@FreeBSD.org>
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 # 1. Redistributions of source code must retain the above copyright
10 #    notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 #    notice, this list of conditions and the following disclaimer in the
13 #    documentation and/or other materials provided with the distribution.
14 # 3. Neither the name of the author nor the names of any co-contributors
15 #    may be used to endorse or promote products derived from this software
16 #    without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 # SUCH DAMAGE.
29 #
30
31 # $FreeBSD$
32
33 #
34 # Basically, we first create a set of boot arguments to pass to the loaded
35 # binary.  Then we attempt to load /boot/loader from the CD we were booted
36 # off of. 
37 #
38
39 #
40 # Memory locations.
41 #
42                 .set STACK_OFF,0x6000           # Stack offset
43                 .set LOAD_SEG,0x0700            # Load segment
44                 .set LOAD_SIZE,2048             # Load size
45                 .set DAUA,0x0584                # DA/UA
46
47                 .set MEM_PAGE_SIZE,0x1000       # memory page size, 4k
48                 .set MEM_ARG,0x900              # Arguments at start
49                 .set MEM_ARG_BTX,0xa100         # Where we move them to so the
50                                                 #  BTX client can see them
51                 .set MEM_ARG_SIZE,0x18          # Size of the arguments
52                 .set MEM_BTX_ADDRESS,0x9000     # where BTX lives
53                 .set MEM_BTX_ENTRY,0x9010       # where BTX starts to execute
54                 .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader
55                 .set MEM_BTX_CLIENT,0xa000      # where BTX clients live
56 #
57 # PC98 machine type from sys/pc98/pc98/pc98_machdep.h
58 #
59                 .set MEM_SYS,           0xa100  # System common area segment
60                 .set PC98_MACHINE_TYPE, 0x0620  # PC98 machine type
61                 .set EPSON_ID,          0x0624  # EPSON machine id
62
63                 .set M_NEC_PC98,        0x0001
64                 .set M_EPSON_PC98,      0x0002
65                 .set M_NOT_H98,         0x0010
66                 .set M_H98,             0x0020
67                 .set M_NOTE,            0x0040
68                 .set M_NORMAL,          0x1000
69                 .set M_8M,              0x8000
70 #
71 # Signature Constants
72 #
73                 .set SIG1_OFF,0x1fe             # Signature offset
74                 .set SIG2_OFF,0x7fe             # Signature offset
75 #
76 # a.out header fields
77 #
78                 .set AOUT_TEXT,0x04             # text segment size
79                 .set AOUT_DATA,0x08             # data segment size
80                 .set AOUT_BSS,0x0c              # zero'd BSS size
81                 .set AOUT_SYMBOLS,0x10          # symbol table
82                 .set AOUT_ENTRY,0x14            # entry point
83                 .set AOUT_HEADER,MEM_PAGE_SIZE  # size of the a.out header
84 #
85 # Flags for kargs->bootflags
86 #
87                 .set KARGS_FLAGS_CD,0x1         # flag to indicate booting from
88                                                 #  CD loader
89 #
90 # Segment selectors.
91 #
92                 .set SEL_SDATA,0x8              # Supervisor data
93                 .set SEL_RDATA,0x10             # Real mode data
94                 .set SEL_SCODE,0x18             # PM-32 code
95                 .set SEL_SCODE16,0x20           # PM-16 code
96 #
97 # BTX constants
98 #
99                 .set INT_SYS,0x30               # BTX syscall interrupt
100 #
101 # Constants for reading from the CD.
102 #
103                 .set ERROR_TIMEOUT,0x90         # BIOS timeout on read
104                 .set NUM_RETRIES,3              # Num times to retry
105                 .set SECTOR_SIZE,0x800          # size of a sector
106                 .set SECTOR_SHIFT,11            # number of place to shift
107                 .set BUFFER_LEN,0x100           # number of sectors in buffer
108                 .set MAX_READ,0xf800            # max we can read at a time
109                 .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT
110                 .set MEM_READ_BUFFER,0x9000     # buffer to read from CD
111                 .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor
112                 .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer
113                 .set VOLDESC_LBA,0x10           # LBA of vol descriptor
114                 .set VD_PRIMARY,1               # Primary VD
115                 .set VD_END,255                 # VD Terminator
116                 .set VD_ROOTDIR,156             # Offset of Root Dir Record
117                 .set DIR_LEN,0                  # Offset of Dir Record length
118                 .set DIR_EA_LEN,1               # Offset of EA length
119                 .set DIR_EXTENT,2               # Offset of 64-bit LBA
120                 .set DIR_SIZE,10                # Offset of 64-bit length
121                 .set DIR_NAMELEN,32             # Offset of 8-bit name len
122                 .set DIR_NAME,33                # Offset of dir name
123
124 #
125 # Program start.
126 #
127                 .code16
128                 .globl start
129
130 start:          jmp main
131
132                 .org 4
133                 .ascii "IPL1   "
134
135 main:           cld
136
137                 /* Setup the stack */
138                 xor %ax,%ax
139                 mov %ax,%ss
140                 mov $STACK_OFF,%sp
141
142                 push %ecx
143
144                 /* Setup graphic screen */
145                 mov $0x42,%ah                   # 640x400
146                 mov $0xc0,%ch
147                 int $0x18
148                 mov $0x40,%ah                   # graph on
149                 int $0x18
150
151                 /* Setup text screen */
152                 mov $0x0a00,%ax                 # 80x25
153                 int $0x18
154                 mov $0x0c,%ah                   # text on
155                 int $0x18
156                 mov $0x13,%ah                   # cursor home
157                 xor %dx,%dx
158                 int $0x18
159                 mov $0x11,%ah                   # cursor on
160                 int $0x18
161
162                 /* Setup keyboard */
163                 mov $0x03,%ah
164                 int $0x18
165
166                 /* Transfer PC-9801 system common area */
167                 xor %ax,%ax
168                 mov %ax,%si
169                 mov %ax,%ds
170                 mov %ax,%di
171                 mov $MEM_SYS,%ax
172                 mov %ax,%es
173                 mov $0x0600,%cx
174                 rep
175                 movsb
176
177                 /* Transfer EPSON machine type */
178                 mov $0xfd00,%ax
179                 mov %ax,%ds
180                 mov (0x804),%eax
181                 and $0x00ffffff,%eax
182                 mov %eax,%es:(EPSON_ID)
183
184                 /* Set machine type to PC98_SYSTEM_PARAMETER */
185                 call machine_check
186
187                 /* Load cdboot */
188                 xor %ax,%ax
189                 mov %ax,%ds
190                 mov $0x06,%ah           /* Read data */
191                 mov (DAUA),%al          /* Read drive */
192                 pop %ecx                /* cylinder */
193                 xor %dx,%dx             /* head / sector */
194                 mov $LOAD_SEG,%bx       /* Load address */
195                 mov %bx,%es
196                 xor %bp,%bp
197                 mov $LOAD_SIZE,%bx      /* Load size */
198                 int $0x1b
199                 mov $msg_readerr,%si
200                 jc error
201
202                 /* Jump to cdboot */
203                 ljmp $LOAD_SEG,$cdboot
204
205 #
206 # Set machine type to PC98_SYSTEM_PARAMETER.
207 #
208 machine_check:  xor %edx,%edx
209                 mov %dx,%ds
210                 mov $MEM_SYS,%ax
211                 mov %ax,%es
212
213                 /* Wait V-SYNC */
214 vsync.1:        inb $0x60,%al
215                 test $0x20,%al
216                 jnz vsync.1
217 vsync.2:        inb $0x60,%al
218                 test $0x20,%al
219                 jz vsync.2
220
221                 /* ANK 'A' font */
222                 xor %al,%al
223                 outb %al,$0xa1
224                 mov $0x41,%al
225                 outb %al,$0xa3
226
227                 /* Get 'A' font from CG window */
228                 push %ds
229                 mov $0xa400,%ax
230                 mov %ax,%ds
231                 xor %eax,%eax
232                 xor %bx,%bx
233                 mov $4,%cx
234 font.1:         add (%bx),%eax
235                 add $4,%bx
236                 loop font.1
237                 pop %ds
238                 cmp $0x6efc58fc,%eax
239                 jnz m_epson
240
241 m_pc98:         or $M_NEC_PC98,%edx
242                 mov $0x0458,%bx
243                 mov (%bx),%al
244                 test $0x80,%al
245                 jz m_not_h98
246                 or $M_H98,%edx
247                 jmp 1f
248 m_epson:        or $M_EPSON_PC98,%edx
249 m_not_h98:      or $M_NOT_H98,%edx
250
251 1:              inb $0x42,%al
252                 test $0x20,%al
253                 jz 1f
254                 or $M_8M,%edx
255
256 1:              mov $0x0400,%bx
257                 mov (%bx),%al
258                 test $0x80,%al
259                 jz 1f
260                 or $M_NOTE,%edx
261
262 1:              mov $PC98_MACHINE_TYPE,%bx
263                 mov %edx,%es:(%bx)
264                 ret
265
266 #
267 # Print out the error message at [SI], wait for a keypress, and then
268 # reboot the machine.
269 #
270 error:          call putstr
271                 mov $msg_keypress,%si
272                 call putstr
273                 xor %ax,%ax                     # Get keypress
274                 int $0x18
275                 xor %ax,%ax                     # CPU reset
276                 outb %al,$0xf0
277 halt:           hlt
278                 jmp halt                        # Spin
279
280 #
281 # Display a null-terminated string at [SI].
282 #
283 # Trashes: AX, BX, CX, DX, SI, DI
284 #
285 putstr:         push %ds
286                 push %es
287                 mov %cs,%ax
288                 mov %ax,%ds
289                 mov $0xa000,%ax
290                 mov %ax,%es
291                 mov cursor,%di
292                 mov $0x00e1,%bx                 # Attribute
293                 mov $160,%cx
294 putstr.0:       lodsb
295                 testb %al,%al
296                 jz putstr.done
297                 cmp $0x0d,%al
298                 jz putstr.cr
299                 cmp $0x0a,%al
300                 jz putstr.lf
301                 mov %bl,%es:0x2000(%di)
302                 stosb
303                 inc %di
304                 jmp putstr.move
305 putstr.cr:      xor %dx,%dx
306                 mov %di,%ax
307                 div %cx
308                 sub %dx,%di
309                 jmp putstr.move
310 putstr.lf:      add %cx,%di
311 putstr.move:    mov %di,%dx
312                 mov $0x13,%ah                   # Move cursor
313                 int $0x18
314                 jmp putstr.0
315 putstr.done:    mov %di,cursor
316                 pop %es
317                 pop %ds
318                 ret
319
320 #
321 # Display a single char at [AL], but don't move a cursor.
322 #
323 putc:           push %es
324                 push %di
325                 push %bx
326                 mov $0xa000,%bx
327                 mov %bx,%es
328                 mov cursor,%di
329                 mov $0xe1,%bl                   # Attribute
330                 mov %bl,%es:0x2000(%di)
331                 stosb
332                 pop %bx
333                 pop %di
334                 pop %es
335                 ret
336
337 msg_readerr:    .asciz "Read Error\r\n"
338 msg_keypress:   .asciz "\r\nPress any key to reboot\r\n"
339
340 /* Boot signature */
341
342                 .org SIG1_OFF,0x90
343
344                 .word 0xaa55                    # Magic number
345
346 #
347 # cdboot
348 #
349 cdboot:         mov %cs,%ax
350                 mov %ax,%ds
351                 xor %ax,%ax
352                 mov %ax,%es
353                 mov %es:(DAUA),%al              # Save BIOS boot device
354                 mov %al,drive
355                 mov %cx,cylinder                # Save BIOS boot cylinder
356
357                 mov $msg_welcome,%si            # %ds:(%si) -> welcome message
358                 call putstr                     # display the welcome message
359 #
360 # Setup the arguments that the loader is expecting from boot[12]
361 #
362                 mov $msg_bootinfo,%si           # %ds:(%si) -> boot args message
363                 call putstr                     # display the message
364                 mov $MEM_ARG,%bx                # %ds:(%bx) -> boot args
365                 mov %bx,%di                     # %es:(%di) -> boot args
366                 xor %eax,%eax                   # zero %eax
367                 mov $(MEM_ARG_SIZE/4),%cx       # Size of arguments in 32-bit
368                                                 #  dwords
369                 rep                             # Clear the arguments
370                 stosl                           #  to zero
371                 mov drive,%dl                   # Store BIOS boot device
372                 mov %dl,%es:0x4(%bx)            #  in kargs->bootdev
373                 or $KARGS_FLAGS_CD,%es:0x8(%bx) # kargs->bootflags |=
374                                                 #  KARGS_FLAGS_CD
375 #
376 # Load Volume Descriptor
377 #
378                 mov $VOLDESC_LBA,%eax           # Set LBA of first VD
379 load_vd:        push %eax                       # Save %eax
380                 mov $1,%dh                      # One sector
381                 mov $MEM_VOLDESC,%ebx           # Destination
382                 call read                       # Read it in
383                 cmpb $VD_PRIMARY,%es:(%bx)      # Primary VD?
384                 je have_vd                      # Yes
385                 pop %eax                        # Prepare to
386                 inc %eax                        #  try next
387                 cmpb $VD_END,%es:(%bx)          # Last VD?
388                 jne load_vd                     # No, read next
389                 mov $msg_novd,%si               # No VD
390                 jmp error                       # Halt
391 have_vd:                                        # Have Primary VD
392 #
393 # Try to look up the loader binary using the paths in the loader_paths
394 # array.
395 #
396                 mov $loader_paths,%si           # Point to start of array
397 lookup_path:    push %si                        # Save file name pointer
398                 call lookup                     # Try to find file
399                 pop %di                         # Restore file name pointer
400                 jnc lookup_found                # Found this file
401                 push %es
402                 mov %cs,%ax
403                 mov %ax,%es
404                 xor %al,%al                     # Look for next
405                 mov $0xffff,%cx                 #  path name by
406                 repnz                           #  scanning for
407                 scasb                           #  nul char
408                 pop %es
409                 mov %di,%si                     # Point %si at next path
410                 mov (%si),%al                   # Get first char of next path
411                 or %al,%al                      # Is it double nul?
412                 jnz lookup_path                 # No, try it.
413                 mov $msg_failed,%si             # Failed message
414                 jmp error                       # Halt
415 lookup_found:                                   # Found a loader file
416 #
417 # Load the binary into the buffer.  Due to real mode addressing limitations
418 # we have to read it in 64k chunks.
419 #
420                 mov %es:DIR_SIZE(%bx),%eax      # Read file length
421                 add $SECTOR_SIZE-1,%eax         # Convert length to sectors
422                 shr $SECTOR_SHIFT,%eax
423                 cmp $BUFFER_LEN,%eax
424                 jbe load_sizeok
425                 mov $msg_load2big,%si           # Error message
426                 jmp error
427 load_sizeok:    movzbw %al,%cx                  # Num sectors to read
428                 mov %es:DIR_EXTENT(%bx),%eax    # Load extent
429                 xor %edx,%edx
430                 mov %es:DIR_EA_LEN(%bx),%dl
431                 add %edx,%eax                   # Skip extended
432                 mov $MEM_READ_BUFFER,%ebx       # Read into the buffer
433 load_loop:      mov %cl,%dh
434                 cmp $MAX_READ_SEC,%cl           # Truncate to max read size
435                 jbe load_notrunc
436                 mov $MAX_READ_SEC,%dh
437 load_notrunc:   sub %dh,%cl                     # Update count
438                 push %eax                       # Save
439                 call read                       # Read it in
440                 pop %eax                        # Restore
441                 add $MAX_READ_SEC,%eax          # Update LBA
442                 add $MAX_READ,%ebx              # Update dest addr
443                 jcxz load_done                  # Done?
444                 jmp load_loop                   # Keep going
445 load_done:
446 #
447 # Turn on the A20 address line
448 #
449                 xor %ax,%ax                     # Turn A20 on
450                 outb %al,$0xf2
451                 mov $0x02,%al
452                 outb %al,$0xf6
453 #
454 # Relocate the loader and BTX using a very lazy protected mode
455 #
456                 mov $msg_relocate,%si           # Display the
457                 call putstr                     #  relocation message
458                 mov %es:(MEM_READ_BUFFER+AOUT_ENTRY),%edi # %edi is the destination
459                 mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is
460                                                 #  the start of the text
461                                                 #  segment
462                 mov %es:(MEM_READ_BUFFER+AOUT_TEXT),%ecx # %ecx = length of the text
463                                                 #  segment
464                 push %edi                       # Save entry point for later
465                 lgdt gdtdesc                    # setup our own gdt
466                 cli                             # turn off interrupts
467                 mov %cr0,%eax                   # Turn on
468                 or $0x1,%al                     #  protected
469                 mov %eax,%cr0                   #  mode
470                 ljmp $SEL_SCODE,$pm_start       # long jump to clear the
471                                                 #  instruction pre-fetch queue
472                 .code32
473 pm_start:       mov $SEL_SDATA,%ax              # Initialize
474                 mov %ax,%ds                     #  %ds and
475                 mov %ax,%es                     #  %es to a flat selector
476                 rep                             # Relocate the
477                 movsb                           #  text segment
478                 add $(MEM_PAGE_SIZE - 1),%edi   # pad %edi out to a new page
479                 and $~(MEM_PAGE_SIZE - 1),%edi #  for the data segment
480                 mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment
481                 rep                             # Relocate the
482                 movsb                           #  data segment
483                 mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss
484                 xor %eax,%eax                   # zero %eax
485                 add $3,%cl                      # round %ecx up to
486                 shr $2,%ecx                     #  a multiple of 4
487                 rep                             # zero the
488                 stosl                           #  bss
489                 mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader
490                 add $MEM_BTX_OFFSET,%esi        # %esi -> BTX in the loader
491                 mov $MEM_BTX_ADDRESS,%edi       # %edi -> where BTX needs to go
492                 movzwl 0xa(%esi),%ecx           # %ecx -> length of BTX
493                 rep                             # Relocate
494                 movsb                           #  BTX
495                 ljmp $SEL_SCODE16,$pm_16        # Jump to 16-bit PM
496                 .code16
497 pm_16:          mov $SEL_RDATA,%ax              # Initialize
498                 mov %ax,%ds                     #  %ds and
499                 mov %ax,%es                     #  %es to a real mode selector
500                 mov %cr0,%eax                   # Turn off
501                 and $~0x1,%al                   #  protected
502                 mov %eax,%cr0                   #  mode
503                 ljmp $LOAD_SEG,$pm_end          # Long jump to clear the
504                                                 #  instruction pre-fetch queue
505 pm_end:         sti                             # Turn interrupts back on now
506 #
507 # Copy the BTX client to MEM_BTX_CLIENT
508 #
509                 mov %cs,%ax
510                 mov %ax,%ds
511                 xor %ax,%ax
512                 mov %ax,%es
513                 mov $MEM_BTX_CLIENT,%di         # Prepare to relocate
514                 mov $btx_client,%si             #  the simple btx client
515                 mov $(btx_client_end-btx_client),%cx # length of btx client
516                 rep                             # Relocate the
517                 movsb                           #  simple BTX client
518 #
519 # Copy the boot[12] args to where the BTX client can see them
520 #
521                 xor %ax,%ax
522                 mov %ax,%ds
523                 mov $MEM_ARG,%si                # where the args are at now
524                 mov $MEM_ARG_BTX,%di            # where the args are moving to
525                 mov $(MEM_ARG_SIZE/4),%cx       # size of the arguments in longs
526                 rep                             # Relocate
527                 movsl                           #  the words
528 #
529 # Save the entry point so the client can get to it later on
530 #
531                 pop %eax                        # Restore saved entry point
532                 stosl                           #  and add it to the end of
533                                                 #  the arguments
534 #
535 # Now we just start up BTX and let it do the rest
536 #
537                 mov $msg_jump,%si               # Display the
538                 call putstr                     #  jump message
539                 ljmp $0,$MEM_BTX_ENTRY          # Jump to the BTX entry point
540
541 #
542 # Lookup the file in the path at [SI] from the root directory.
543 #
544 # Trashes: All but BX
545 # Returns: CF = 0 (success), BX = pointer to record
546 #          CF = 1 (not found)
547 #
548 lookup:         mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record
549                 push %bx
550                 push %si
551                 mov $msg_lookup,%si             # Display lookup message
552                 call putstr
553                 pop %si
554                 push %si
555                 call putstr
556                 mov $msg_lookup2,%si
557                 call putstr
558                 pop %si
559                 pop %bx
560 lookup_dir:     lodsb                           # Get first char of path
561                 cmp $0,%al                      # Are we done?
562                 je lookup_done                  # Yes
563                 cmp $'/',%al                    # Skip path separator.
564                 je lookup_dir
565                 dec %si                         # Undo lodsb side effect
566                 call find_file                  # Lookup first path item
567                 jnc lookup_dir                  # Try next component
568                 mov $msg_lookupfail,%si         # Not found message
569                 push %bx
570                 call putstr
571                 pop %bx
572                 stc                             # Set carry
573                 ret
574 lookup_done:    mov $msg_lookupok,%si           # Success message
575                 push %bx
576                 call putstr
577                 pop %bx
578                 clc                             # Clear carry
579                 ret
580
581 #
582 # Lookup file at [SI] in directory whose record is at [BX].
583 #
584 # Trashes: All but returns
585 # Returns: CF = 0 (success), BX = pointer to record, SI = next path item
586 #          CF = 1 (not found), SI = preserved
587 #
588 find_file:      mov %es:DIR_EXTENT(%bx),%eax    # Load extent
589                 xor %edx,%edx
590                 mov %es:DIR_EA_LEN(%bx),%dl
591                 add %edx,%eax                   # Skip extended attributes
592                 mov %eax,rec_lba                # Save LBA
593                 mov %es:DIR_SIZE(%bx),%eax      # Save size
594                 mov %eax,rec_size
595                 xor %cl,%cl                     # Zero length
596                 push %si                        # Save
597 ff.namelen:     inc %cl                         # Update length
598                 lodsb                           # Read char
599                 cmp $0,%al                      # Nul?
600                 je ff.namedone                  # Yes
601                 cmp $'/',%al                    # Path separator?
602                 jnz ff.namelen                  # No, keep going
603 ff.namedone:    dec %cl                         # Adjust length and save
604                 mov %cl,name_len
605                 pop %si                         # Restore
606 ff.load:        mov rec_lba,%eax                # Load LBA
607                 mov $MEM_DIR,%ebx               # Address buffer
608                 mov $1,%dh                      # One sector
609                 call read                       # Read directory block
610                 incl rec_lba                    # Update LBA to next block
611 ff.scan:        mov %ebx,%edx                   # Check for EOF
612                 sub $MEM_DIR,%edx
613                 cmp %edx,rec_size
614                 ja ff.scan.1
615                 stc                             # EOF reached
616                 ret
617 ff.scan.1:      cmpb $0,%es:DIR_LEN(%bx)        # Last record in block?
618                 je ff.nextblock
619                 push %si                        # Save
620                 movzbw %es:DIR_NAMELEN(%bx),%si # Find end of string
621 ff.checkver:    cmpb $'0',%es:DIR_NAME-1(%bx,%si)       # Less than '0'?
622                 jb ff.checkver.1
623                 cmpb $'9',%es:DIR_NAME-1(%bx,%si)       # Greater than '9'?
624                 ja ff.checkver.1
625                 dec %si                         # Next char
626                 jnz ff.checkver
627                 jmp ff.checklen                 # All numbers in name, so
628                                                 #  no version
629 ff.checkver.1:  movzbw %es:DIR_NAMELEN(%bx),%cx
630                 cmp %cx,%si                     # Did we find any digits?
631                 je ff.checkdot                  # No
632                 cmpb $';',%es:DIR_NAME-1(%bx,%si)       # Check for semicolon
633                 jne ff.checkver.2
634                 dec %si                         # Skip semicolon
635                 mov %si,%cx
636                 mov %cl,%es:DIR_NAMELEN(%bx)    # Adjust length
637                 jmp ff.checkdot
638 ff.checkver.2:  mov %cx,%si                     # Restore %si to end of string
639 ff.checkdot:    cmpb $'.',%es:DIR_NAME-1(%bx,%si)       # Trailing dot?
640                 jne ff.checklen                 # No
641                 decb %es:DIR_NAMELEN(%bx)       # Adjust length
642 ff.checklen:    pop %si                         # Restore
643                 movzbw name_len,%cx             # Load length of name
644                 cmp %cl,%es:DIR_NAMELEN(%bx)    # Does length match?
645                 je ff.checkname                 # Yes, check name
646 ff.nextrec:     add %es:DIR_LEN(%bx),%bl        # Next record
647                 adc $0,%bh
648                 jmp ff.scan
649 ff.nextblock:   subl $SECTOR_SIZE,rec_size      # Adjust size
650                 jnc ff.load                     # If subtract ok, keep going
651                 ret                             # End of file, so not found
652 ff.checkname:   lea DIR_NAME(%bx),%di           # Address name in record
653                 push %si                        # Save
654                 repe cmpsb                      # Compare name
655                 je ff.match                     # We have a winner!
656                 pop %si                         # Restore
657                 jmp ff.nextrec                  # Keep looking.
658 ff.match:       add $2,%sp                      # Discard saved %si
659                 clc                             # Clear carry
660                 ret
661
662 #
663 # Load DH sectors starting at LBA EAX into [EBX].
664 #
665 # Trashes: EAX
666 #
667 read:           push %es                        # Save
668                 push %bp
669                 push %dx
670                 push %cx
671                 push %ebx
672                 mov %bx,%bp                     # Set destination address
673                 and $0x000f,%bp
674                 shr $4,%ebx
675                 mov %bx,%es
676                 xor %bx,%bx                     # Set read bytes
677                 mov %dh,%bl
678                 shl $SECTOR_SHIFT,%bx           # 2048 bytes/sec
679                 mov %ax,%cx                     # Set LBA
680                 shr $16,%eax
681                 mov %ax,%dx
682 read.retry:     mov $0x06,%ah                   # BIOS device read
683                 mov drive,%al
684                 and $0x7f,%al
685                 call twiddle                    # Entertain the user
686                 int $0x1b                       # Call BIOS
687                 jc read.fail                    # Worked?
688                 pop %ebx                        # Restore
689                 pop %cx
690                 pop %dx
691                 pop %bp
692                 pop %es
693                 ret                             # Return
694 read.fail:      cmp $ERROR_TIMEOUT,%ah          # Timeout?
695                 je read.retry                   # Yes, Retry.
696 read.error:     mov %ah,%al                     # Save error
697                 mov $hex_error,%di              # Format it
698                 call hex8                       #  as hex
699                 mov $msg_badread,%si            # Display Read error message
700                 jmp error
701
702 #
703 # Output the "twiddle"
704 #
705 twiddle:        push %ax                        # Save
706                 push %bx                        # Save
707                 mov twiddle_index,%al           # Load index
708                 mov $twiddle_chars,%bx          # Address table
709                 inc %al                         # Next
710                 and $3,%al                      #  char
711                 mov %al,twiddle_index           # Save index for next call
712                 xlat                            # Get char
713                 call putc                       # Output it
714                 pop %bx                         # Restore
715                 pop %ax                         # Restore
716                 ret
717
718 #
719 # Convert AL to hex, saving the result to [EDI].
720 #
721 hex8:           pushl %eax                      # Save
722                 shrb $0x4,%al                   # Do upper
723                 call hex8.1                     #  4
724                 popl %eax                       # Restore
725 hex8.1:         andb $0xf,%al                   # Get lower 4
726                 cmpb $0xa,%al                   # Convert
727                 sbbb $0x69,%al                  #  to hex
728                 das                             #  digit
729                 orb $0x20,%al                   # To lower case
730                 mov %al,(%di)                   # Save char
731                 inc %di
732                 ret                             # (Recursive)
733
734 #
735 # BTX client to start btxldr
736 #
737                 .code32
738 btx_client:     mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi
739                                                 # %ds:(%esi) -> end
740                                                 #  of boot[12] args
741                 mov $(MEM_ARG_SIZE/4),%ecx      # Number of words to push
742                 std                             # Go backwards
743 push_arg:       lodsl                           # Read argument
744                 push %eax                       # Push it onto the stack
745                 loop push_arg                   # Push all of the arguments
746                 cld                             # In case anyone depends on this
747                 pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of
748                                                 #  the loader
749                 push %eax                       # Emulate a near call
750                 mov $0x1,%eax                   # 'exec' system call
751                 int $INT_SYS                    # BTX system call
752 btx_client_end:
753                 .code16
754
755                 .p2align 4
756 #
757 # Global descriptor table.
758 #
759 gdt:            .word 0x0,0x0,0x0,0x0                   # Null entry
760                 .word 0xffff,0x0000,0x9200,0x00cf       # SEL_SDATA
761                 .word 0xffff,0x0000,0x9200,0x0000       # SEL_RDATA
762                 .word 0xffff,LOAD_SEG<<4,0x9a00,0x00cf  # SEL_SCODE (32-bit)
763                 .word 0xffff,LOAD_SEG<<4,0x9a00,0x008f  # SEL_SCODE16 (16-bit)
764 gdt.1:
765 #
766 # Pseudo-descriptors.
767 #
768 gdtdesc:        .word gdt.1-gdt-1               # Limit
769                 .long LOAD_SEG<<4 + gdt         # Base
770
771 #
772 # BOOT device
773 #
774 drive:          .byte 0
775 cylinder:       .word 0
776
777 #
778 # State for searching dir
779 #
780 rec_lba:        .long 0x0                       # LBA (adjusted for EA)
781 rec_size:       .long 0x0                       # File size
782 name_len:       .byte 0x0                       # Length of current name
783
784 cursor:         .word 0
785 twiddle_index:  .byte 0x0
786
787 msg_welcome:    .asciz  "CD Loader 1.2\r\n\n"
788 msg_bootinfo:   .asciz  "Building the boot loader arguments\r\n"
789 msg_relocate:   .asciz  "Relocating the loader and the BTX\r\n"
790 msg_jump:       .asciz  "Starting the BTX loader\r\n"
791 msg_badread:    .ascii  "Read Error: 0x"
792 hex_error:      .asciz  "00\r\n"
793 msg_novd:       .asciz  "Could not find Primary Volume Descriptor\r\n"
794 msg_lookup:     .asciz  "Looking up "
795 msg_lookup2:    .asciz  "... "
796 msg_lookupok:   .asciz  "Found\r\n"
797 msg_lookupfail: .asciz  "File not found\r\n"
798 msg_load2big:   .asciz  "File too big\r\n"
799 msg_failed:     .asciz  "Boot failed\r\n"
800 twiddle_chars:  .ascii  "|/-\\"
801 loader_paths:   .asciz  "/BOOT.PC98/LOADER"
802                 .asciz  "/boot.pc98/loader"
803                 .asciz  "/BOOT/LOADER"
804                 .asciz  "/boot/loader"
805                 .byte 0
806
807 /* Boot signature */
808
809                 .org SIG2_OFF,0x90
810
811                 .word 0xaa55                    # Magic number