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