9b99bc8bb8316752ee0ec41e7a3220a5b3dc4566
[mknbi.git] / first-dos.S
1 ; first.S  -  primary boot loader for DOS
2 ;
3 ; Copyright (C) 1996-1998 Gero Kuhlmann   <gero@gkminix.han.de>
4 ; Modifications for booting FreeDOS by Ken Yap <ken_yap@users.sourceforge.net>
5 ;
6 ; 2003.04.28 Robb Main
7 ;  Tweaks & bugfixes to allow use with much larger disk images, remove
8 ;  '2 head assumption', use 32-bit GDT, and other 'stuff'.
9 ;
10 ;  This program is free software; you can redistribute it and/or modify
11 ;  it under the terms of the GNU General Public License as published by
12 ;  the Free Software Foundation; either version 2 of the License, or
13 ;  any later version.
14 ;
15 ;  This program is distributed in the hope that it will be useful,
16 ;  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;  GNU General Public License for more details.
19 ;
20 ;  You should have received a copy of the GNU General Public License
21 ;  along with this program; if not, write to the Free Software
22 ;  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24 #if     !defined(USE_NASM) && !defined(USE_AS86)
25 #define USE_AS86
26 #endif
27
28 #ifdef  USE_AS86
29 #define CON(x)          *x
30 #define BCON(x)         *x
31 #define LOC(x)          x
32 #define BLOC(x)         byte ptr x
33 #define WLOC(x)         word ptr x
34 #define JMP(x)          jmp x
35 #define STRDECL(s)      .ascii  s
36 #define SEGCS           seg     cs
37 #define SEGES           seg     es
38 #define ALIGN(x)        .align  x
39 #define SPACE(x)        .space  x
40 #endif
41
42 #ifdef  USE_NASM
43 #define CON(x)          x
44 #define BCON(x)         byte x
45 #define LOC(x)          [x]
46 #define BLOC(x)         byte [x]
47 #define WLOC(x)         word [x]
48 #define JMP(x)          jmp short x
49 #define STRDECL(s)      db      s
50 #define SEGCS           cs
51 #define SEGES           es
52 #define ALIGN(x)        align x, db 0
53 #define SPACE(x)        times x db 0
54 #endif
55
56 #ifndef ASM_DEBUG
57 #undef ASM_DEBUG
58 #endif
59 #include "first-dos.h"
60 #include "version-dos.h"
61
62 #ifdef  USE_AS86
63         .text
64         .org    0
65 #endif
66 #ifdef  USE_NASM
67         text
68 #endif
69
70         mov     dx,ds
71         mov     ax,cs                   ; set DS
72         mov     ds,ax
73         mov     LOC(oldES),es
74         mov     LOC(oldDS),dx           ; save old register values in case
75         mov     LOC(oldBP),bp           ; we have to return to the boot rom
76         mov     LOC(oldSI),si
77         mov     LOC(oldDI),di
78         mov     bp,sp
79         mov     ax,[bp+4]
80         mov     LOC(header+0),ax                ; load the address of the boot header
81         mov     ax,[bp+6]
82         mov     LOC(header+2),ax
83         mov     ax,[bp+8]
84         mov     LOC(bootp+0),ax         ; load the address of the bootp block
85         mov     ax,[bp+10]
86         mov     LOC(bootp+2),ax
87
88 ; Tell the user who we are and that we started running
89
90         mov     si,CON(sigmsg)
91         call    prnstr
92
93 ; Check if the boot image header is correct.
94
95         les     bx,LOC(header)
96         mov     si,CON(bmerr)           ; prepare for correct error message
97         SEGES
98         mov     ax,[bx+BOOT_HD_MAGIC+0]
99         cmp     ax,LOC(bmagic+0)        ; compare boot rom magic numbers
100         jne     doerr1
101         SEGES
102         mov     ax,[bx+BOOT_HD_MAGIC+2]
103         cmp     ax,LOC(bmagic+2)
104         jne     doerr1
105
106         mov     si,CON(vmerr)           ; prepare for correct error message
107         SEGES
108         mov     al,[bx+BOOT_HD_LENGTH]
109         mov     cl,CON(4)
110         shr     al,cl
111         and     al,CON(0x0F)
112         cmp     al,CON(VENDOR_SIZE)     ; check vendor ID size
113         jne     doerr1
114         xor     di,di
115 dovmag: mov     al,[di+vmagic]          ; check vendor ID
116         or      al,al
117         jz      getrd                   ; vendor ID ok, continue
118         SEGES
119         cmp     al,[bx+di+BOOT_HD_VENDOR]
120         jne     doerr1
121         inc     di
122         JMP(dovmag)
123
124 doerr1: call    prnstr                  ; in case of error return to the
125         mov     si,LOC(oldSI)           ; boot rom with registers set
126         mov     di,LOC(oldDI)           ; correctly
127         mov     bp,LOC(oldBP)
128         mov     es,LOC(oldES)
129         mov     ds,LOC(oldDS)
130         retf
131
132 ; Next get the address of the ramdisk and its size.
133
134 getrd:  mov     si,CON(recerr)
135         mov     al,CON(VENDOR_RAMDISK)
136         call    fndldr                          ; find load record for ramdisk
137         mov     ax,es
138         or      ax,di
139         jz      doerr1
140         SEGES
141         mov     al,[di+BOOT_LD_FLAGS]           ; get load record flags
142         test    al,CON(BOOT_FLAG_B0 + BOOT_FLAG_B1)     ; check that it has a
143         jnz     doerr1                          ; correct flag
144
145         SEGES
146         mov     ax,[di+BOOT_LD_ADDR+0]          ; get base adress of ramdisk
147         SEGES
148         mov     bx,[di+BOOT_LD_ADDR+2]
149         mov     LOC(rdaddr+0),ax
150         mov     LOC(rdaddr+2),bx
151
152         SEGES
153         mov     ax,[di+BOOT_LD_MLENGTH+0]       ; get ramdisk size
154         SEGES
155         mov     bx,[di+BOOT_LD_MLENGTH+2]
156         add     ax,CON(0x03ff)                  ; round to nearest kb
157         adc     bx,BCON(0)
158         mov     cl,CON(10)
159         shr     ax,cl                           ; convert into kb
160         mov     cl,CON(6)
161         shl     bx,cl
162         or      ax,bx
163         mov     LOC(rdsize),ax
164
165 ; Get the disk geometry out of the vendor information block in the
166 ; load record
167
168         SEGES
169         mov     bl,[di+BOOT_LD_LENGTH]
170         and     bl,CON(0x0f)
171         xor     bh,bh                           ; compute pointer to
172         shl     bx,CON(1)                       ; vendor block
173         shl     bx,CON(1)
174         SEGES
175         mov     ax,[di+bx+BOOT_LD_SECNUM]       ; get number of sectors
176         mov     LOC(secnumlo),ax
177         SEGES
178         mov     ax,[di+bx+BOOT_LD_SECNUM+2]     ; get number of sectors
179         mov     LOC(secnumhi),ax
180         SEGES
181         mov     ax,[di+bx+BOOT_LD_HEADS]        ; get head count
182         mov     LOC(heads),al
183         SEGES
184         mov     ax,[di+bx+BOOT_LD_SPT]          ; get sectors per track
185         mov     LOC(secptk),al
186         SEGES
187         mov     ax,[di+bx+BOOT_LD_CYL]          ; get number of cylinders
188         mov     LOC(cylnum),ax
189         SEGES
190         mov     al,[di+bx+BOOT_LD_NOHD]         ; get no-hard-disk flag
191         mov     LOC(nohd),al
192         SEGES
193         mov     al,[di+bx+BOOT_LD_DRIVE]                ; get ram disk drive id
194         mov     LOC(drvid),al
195
196 ; Set the address of the BIOS disk status byte
197
198         mov     bx,CON(BIOS_FDSTAT)
199         cmp     al,CON(0x80)
200         jb      setsts
201         mov     bx,CON(BIOS_HDSTAT)
202 setsts: mov     LOC(statof),bx
203
204 ; Get system configuration from BIOS
205
206         push    ax
207         int     0x11
208         mov     LOC(syscnf),ax
209         pop     ax
210
211 ; Get the number of floppy or hard disk drives in the system and set
212 ; the DOS disk parameter block
213
214         cmp     al,BCON(0x80)
215         jae     getnm2
216
217         mov     ah,CON(0x08)
218         xor     dl,dl
219         int     0x13                    ; get the number of floppy disk
220         jc      getnm1                  ; drives from the BIOS
221         or      dl,dl
222         jnz     gotnum
223         inc     dl                      ; indicate at least one drive
224         JMP(gotnum)
225 getnm1: mov     dx,LOC(syscnf)          ; if int 13h didnt work try it with
226         test    dl,CON(0x01)            ; the mainboard dip switch config
227         jz      getnm3
228         mov     cl,CON(6)
229         shr     dl,cl
230         and     dl,CON(0x03)            ; determine number of floppy disk
231         inc     dl                      ; drives
232         JMP(gotnum)
233
234 getnm2: mov     ah,CON(0x08)
235         mov     dl,CON(0x80)
236         int     0x13                    ; get the number of hard disk
237         jc      getnm3                  ; drives from the BIOS
238         inc     dl
239         JMP(gotnum)
240 ; The next line was mov dl,1 in netboot-0.8.1. This was probably an error.
241 getnm3: mov     dl,CON(1)               ; we have at least one drive
242
243 gotnum: mov     LOC(drvnum),dl          ; save number of disk drives
244         call    setdpb                  ; set disk parameter block
245
246 ; Now get the boot sector of the ramdisk and check that its correct. If
247 ; we are simulating a hard disk the boot sector contains the partition
248 ; table, which we have to analyze. Then load the partitions boot sector.
249
250         mov     ax,CON(TEMP_SEGMENT)
251         mov     es,ax                   ; pointer to temporary buffer
252         xor     bx,bx
253         xor     dx,dx                   ; first sector
254         xor     ax,ax
255         xor     ch,ch                   ; indicate read
256         mov     cl,1                    ; one sector
257         call    rwsect                  ; read boot sector
258         
259         mov     si,CON(rderr)
260         jc      doerr2
261
262         cmp     BLOC(drvid),CON(0x80)   ; if the ram disk is simulates a
263         jb      chkbot                  ; floppy, there is no partition table
264
265         mov     si,CON(dskerr)          ; prepare for correct error message
266         SEGES
267         cmp     BLOC(PART_STATUS),CON(PART_ACTIVE)
268         jne     doerr2
269         
270         SEGES
271         cmp     BLOC(PART_TYPE),CON(PART_FAT16)
272         je      partok
273         
274         SEGES
275         cmp     BLOC(PART_TYPE),CON(PART_FAT12)
276         jne     doerr2
277 partok:
278         SEGES
279         mov     dx,[PART_ABS_SECT+2]    ; get number of first sector
280         SEGES
281         mov     ax,[PART_ABS_SECT+0]
282         xor     ch,ch                   ; indicate read
283         mov     cl,1                    ; one sector
284         call    rwsect                  ; read boot sector
285         mov     si,CON(rderr)
286         jc      doerr2
287
288 #ifndef HD_PARM_CHECK
289         JMP(dobotp)
290 #endif
291
292 chkbot:
293         mov     si,CON(dskerr)          ; prepare for correct error message
294         mov     al,LOC(drvid)
295         SEGES
296         cmp     BLOC(DISK_BOOT),al      ; check boot disk number
297         jne     doerr2
298
299         SEGES
300         cmp     WLOC(DISK_BPS),CON(SECT_SIZE)   ; check sector size
301         jne     doerr2
302
303         SEGES
304         cmp     WLOC(DISK_HEADS),BCON(16)       ; check number of heads
305         jg      doerr2
306
307         SEGES
308         mov     ax,LOC(DISK_SPT)        ; check number of sectors per track
309         cmp     al,LOC(secptk)
310         je      dobotp
311 doerr2: 
312         call    prnstr                  ; in case of error return to the
313 #if 1
314         push    ax
315         mov     ah,0                    ; wait for a keypress, so user
316         int     0x16                    ; can read the error message
317         pop     ax
318 #endif
319         mov     si,LOC(oldSI)           ; boot rom with registers set
320         mov     di,LOC(oldDI)           ; correctly
321         mov     bp,LOC(oldBP)
322         mov     es,LOC(oldES)
323         mov     ds,LOC(oldDS)
324         retf
325
326 ; Save the BOOTP record for later retrieval by a DOS program.
327
328 dobotp: cld
329         xor     dx,dx
330         les     di,LOC(bootp)
331         mov     ax,es
332         or      ax,di
333         jz      dobot9
334         SEGES
335         mov     al,[di+BOOTP_OP]                ; op code must indicate reply
336         cmp     al,CON(BOOTP_REPLY)
337         jne     dobot9                  ; it isnt
338         add     di,CON(BOOTP_VEND)
339         mov     bx,di
340         mov     si,CON(pmagic)          ; compare vendor ID
341 dobot1: mov     di,bx
342         mov     cx,CON(BOOTP_MAGIC_LEN)
343         repe
344         cmpsb
345         jz      dobot2                  ; vendor ID is valid
346         add     si,cx
347 #ifdef  USE_AS86
348         cmp     byte ptr[si],CON(0)     ; check next vendor ID
349 #endif
350 #ifdef  USE_NASM
351         cmp     byte [si],CON(0)        ; check next vendor ID
352 #endif
353         jne     dobot1
354 dobot9: JMP(nobot2)                     ; vendor ID not found
355
356 doerr6: JMP(doerr2)
357
358 dobot2: sub     si,BCON(BOOTP_MAGIC_LEN)
359         sub     si,CON(pmagic)
360         mov     ax,si
361         push    ds
362         mov     bx,ds
363         mov     es,bx
364         mov     di,CON(btpnew)
365         lds     si,LOC(bootp)
366         mov     bx,si
367         mov     dx,CON(BOOTP_SIZE)
368         or      ax,ax                   ; if not RFC vendor ID the bootp
369         jnz     dobot7                  ; record has fixed length
370
371         xor     cx,cx
372         add     si,CON(BOOTP_VEND + BOOTP_MAGIC_LEN)
373 dobot3: lodsb
374         cmp     al,CON(BOOTP_RFC_NOP)   ; handle NOP tag
375         jnz     dobot4
376         inc     cx
377         cmp     cx,BCON(16)             ; more than 16 NOP tags is VERY unusual
378         jae     dobot7                  ; so the bootp record maybe broken
379         JMP(dobot3)                     ; loop to next tag
380
381 nobot2: JMP(nobotp)
382
383 dobot4: cmp     al,CON(BOOTP_RFC_END)   ; handle END tag
384         jnz     dobot6
385         mov     dx,si
386         sub     dx,bx                   ; compute length of bootp record
387         cmp     dx,CON(BOOTP_SIZE)
388         jae     dobot7
389         mov     dx,CON(BOOTP_SIZE)      ; use minimum size
390         JMP(dobot7)
391 dobot6: lodsb                           ; handle all other tags
392         mov     cl,al
393         xor     ch,ch
394         add     si,cx                   ; jump to next tag
395         xor     cx,cx                   ; reset NOP counter
396         JMP(dobot3)                     ; proceed with next tag
397
398 dobot7: mov     si,CON(btperr)
399         mov     ax,CON(btpnew)          ; bootp record cannot be larger
400         add     ax,dx                   ; than the current segment
401         jc      doerr6
402         mov     cx,dx
403         mov     si,bx                   ; restore source pointer
404         rep
405         movsb                           ; save the bootp record
406         pop     ds
407 nobotp: mov     LOC(btplen),dx          ; set length of bootp record
408
409 ; Everything is right, so we can now move the resident section to the
410 ; end of the conventional memory, thus overwriting the bootrom data
411 ; area. Therefore there is no chance of returning to the bootrom from
412 ; now on.
413 ; Note that the resident section doesnt start at offset 0, so we have
414 ; to set the segment address to somewhere lower.
415
416 #ifdef ASM_DEBUG
417         mov     si,CON(debug1)
418         call    prnstr
419 #endif
420         cli
421         mov     ax,CON(TEMP_SEGMENT)    ; set new stack
422         mov     ss,ax
423         mov     sp,CON(0xFFF0)
424
425         cld     
426         int     0x12                    ; get memory size in kB
427 #ifdef  FREEDOS
428         push    ax                      ; save mem size in kB
429 #endif  /* FREEDOS */
430         mov     cl,CON(6)
431         shl     ax,cl                   ; compute last usable segment
432         mov     bx,CON(btpnew)
433         add     bx,LOC(btplen)
434         mov     dx,bx
435         mov     cl,CON(4)
436         shr     bx,cl                   ; compute size of code segment in
437         inc     bx                      ; paragraphs
438 #ifdef  FREEDOS
439         push    bx                      ; save size in paragraphs
440 #endif  /* FREEDOS */
441         sub     ax,bx                   ; compute new code segment
442         mov     LOC(resseg),ax
443         mov     es,ax                   ; set source and destination ptrs
444         mov     si,CON(start_resident)
445         mov     di,si
446         mov     cx,dx
447         sub     cx,si                   ; compute size of resident area
448         rep
449         movsb                           ; move it
450
451 #ifdef  FREEDOS
452 ; New code for FreeDOS, adjust the value of the top of memory returned by
453 ; int 0x12. Currently there is no code to restore the original size
454         pop     bx                      ; restore size in paragraphs
455         add     bx,BCON(63)             ; round up to next kB
456         mov     cl,CON(6)               ; divide by 64
457         shr     bx,cl
458         pop     ax                      ; restore size in kB
459         sub     ax,bx
460         mov     bx,CON(0x40)
461         mov     es,bx
462         SEGES
463         mov     LOC(0x13),ax            ; store at 0x40:0x13 for int 12h
464 ; End of new code
465 #endif  /* FREEDOS */
466
467 ; Setup all interrupt vectors
468
469         mov     bx,LOC(resseg)
470         push    ds
471         mov     ds,bx
472         xor     ax,ax
473         mov     es,ax
474         SEGES
475         mov     ax,LOC(I13_INT+0)
476         mov     LOC(old13h+0),ax
477         SEGES
478         mov     ax,LOC(I13_INT+2)
479         mov     LOC(old13h+2),ax
480         SEGES
481         mov     ax,LOC(I2F_INT+0)
482         mov     LOC(old2Fh+0),ax
483         SEGES
484         mov     ax,LOC(I2F_INT+2)
485         mov     LOC(old2Fh+2),ax
486         SEGES
487         mov     ax,LOC(IF1_INT+0)
488         mov     LOC(oldF1h+0),ax
489         SEGES
490         mov     ax,LOC(IF1_INT+2)
491         mov     LOC(oldF1h+2),ax
492         SEGES
493         mov     ax,LOC(IF8_INT+0)
494         mov     LOC(oldF8h+0),ax
495         SEGES
496         mov     ax,LOC(IF8_INT+2)
497         mov     LOC(oldF8h+2),ax
498         pop     ds
499
500         SEGES
501         mov     LOC(I13_INT+2),bx       ; interrupt vector 13h
502         SEGES
503         mov     WLOC(I13_INT+0),CON(int13)
504         SEGES
505         mov     LOC(I2F_INT+2),bx       ; interrupt vector 2Fh
506         SEGES
507         mov     WLOC(I2F_INT+0),CON(int2F)
508         SEGES
509         mov     LOC(IF8_INT+2),bx       ; interrupt vector F8h
510         SEGES
511         mov     WLOC(IF8_INT+0),CON(intF8)
512         mov     di,CON(IF1_INT)
513         mov     si,CON(if1sig)          ; interrupt vector F1h
514         mov     cx,CON(4)               ; contains the string "NetB"
515         rep                             ; to provide as an installation
516         movsb                           ; check
517         sti
518
519 ; Output some debugging messages by simply calling the interrupt vectors
520 ; which we just created.
521
522 #ifdef DRV_DEBUG
523         mov     ax,CON(0x4A06)
524         int     0x2F
525         mov     si,CON(consz)
526         call    prnstr                  ; print out amount of conventional
527         mov     ax,dx                   ; memory
528         mov     cl,CON(12)
529         shr     ax,cl
530         call    prnwrd
531         mov     ax,dx
532         mov     cl,CON(4)
533         shl     ax,cl
534         call    prnwrd
535         mov     si,CON(bytes)
536         call    prnstr
537
538         mov     ax,CON(0x8800)
539         int     0x15
540         push    ax
541         mov     si,CON(extsz)
542         call    prnstr                  ; print out amount of extended memory
543         mov     cl,CON(6)
544         shr     ax,cl
545         call    prnwrd
546         pop     ax
547         mov     cl,CON(10)
548         shl     ax,cl
549         call    prnwrd
550         mov     si,CON(bytes)
551         call    prnstr
552 #endif
553
554 #ifndef FREEDOS
555 ; The boot sector had to be placed into a temporary memory area to
556 ; avoid overwriting any bootrom structures. However, a call back
557 ; to the bootrom is no longer possible, so we can move the bootblock
558 ; where it belongs.
559
560         push    ds
561         mov     ax,CON(TEMP_SEGMENT)
562         mov     ds,ax
563         xor     ax,ax
564         mov     es,ax
565         xor     si,si
566         mov     di,CON(BOOT_OFFSET)
567         mov     cx,CON(SECT_SIZE)
568         rep
569         movsb                           ; move it
570         pop     ds
571
572 ; Finally call the boot sector
573 #else
574 ; Finally call kernel.sys
575 #endif  /* FREEDOS */
576
577 #ifdef ASM_DEBUG
578         mov     si,CON(debug2)
579         call    prnstr
580 #endif
581 #ifdef  FREEDOS
582         mov     bl,LOC(drvid)           ; FreeDOS gets boot drive from bl, 0=A
583 #else
584         mov     dl,LOC(drvid)           ; boot block may expect this
585 #endif
586 #ifdef ASM_FREEZE_AFTER_INIT
587 lop:    JMP(lop)
588 #else
589 #ifdef  FREEDOS
590         jmp     FDKSEG:0                ; FreeDOS kernel.sys entry point
591 #else
592         jmp     0:BOOT_OFFSET
593 #endif  /* FREEDOS */
594 #endif
595
596
597 ;====================================================================
598 ;
599 ; Setup DOS drive parameter block (DPB) for floppy disk drive. The
600 ; DPB is required to activate the floppy disk drive after the ramdisk
601 ; has been turned off.
602 ; Input:  none
603 ; Output: none
604 ; Registers changed: AX, BX, CX, DX
605
606 setdpb: push    es
607         push    si
608         push    di
609         mov     dl,LOC(drvid)
610         cmp     dl,CON(0x80)            ; can only restore floppy drives
611         jae     setdp8
612
613 ; First get the drive parameters from the BIOS
614
615         mov     LOC(dpb_phys),dl        ; set physical drive ID
616         mov     ah,CON(0x08)            ; get drive parameters from BIOS
617         int     0x13
618         jc      setdp8
619         xor     ah,ah
620         mov     al,ch                   ; set max number of cylinders
621         inc     ax
622         mov     LOC(dpb_cyls),ax
623         mov     si,CON(dpb_bpb_cur)
624         mov     al,cl                   ; set max number of sectors per track
625         mov     [si+BPB_SPT],ax
626         mov     al,dh
627         inc     ax                      ; set max number of heads
628         mov     [si+BPB_HEADS],ax
629
630 ; Determine DOS disk parameters by drive type
631
632         cmp     bl,CON(5)
633         jb      setdp1                  ; check for invalid drive type
634         mov     bl,CON(4)
635 setdp1: xor     bh,bh
636         dec     bx
637         push    bx
638         shl     bx,CON(1)
639         shl     bx,CON(1)               ; compute address into drive para-
640         add     bx,CON(drvtab)          ; meter table
641         xor     ah,ah
642         mov     al,[bx+0]               ; get # of entries in root dir
643         mov     [si+BPB_DIR],al
644         mov     al,[bx+1]               ; get # of sectors per FAT
645         mov     [si+BPB_SPF],ax
646         mov     al,[bx+2]               ; get # of sectors per cluster
647         mov     [si+BPB_SPC],al
648         mov     al,[bx+3]               ; get media ID
649         mov     [si+BPB_MEDIA_ID],al
650         pop     bx
651         mov     al,[bx+typtab]          ; get drive type
652         mov     LOC(dpb_type),al
653
654 ; Determine number of bytes per sector
655
656         SEGES
657         mov     cl,[di+3]               ; get shift value from BIOS media
658         mov     ax,CON(128)             ; parameter table
659         shl     ax,cl                   ; shift base value
660         mov     [si+BPB_BPS],ax
661         JMP(setdp4)
662 setdp8: JMP(setdp9)                     ; needed for short jumps
663
664 ; Determine total number of sectors
665
666 setdp4: mov     ax,[si+BPB_SPT]
667         mul     WLOC(dpb_cyls)
668         or      dx,dx                   ; should not overflow
669         jnz     setdp8
670 #ifdef  USE_AS86
671         cmp     [si+BPB_HEADS],BCON(2)
672 #endif
673 #ifdef  USE_NASM
674         cmp     word [si+BPB_HEADS],BCON(2)
675 #endif
676         jb      setdp3
677         shl     ax,CON(1)
678         jc      setdp8
679 setdp3: mov     [si+BPB_TOT_SECTS],ax
680
681 ; Determine if the drive can detect disk changes
682
683         mov     ah,CON(0x15)
684         mov     dl,LOC(drvid)
685         int     0x13                    ; get DASD type from BIOS
686         mov     bl,ah
687         mov     ax,CON(DPB_F_DEFAULT)
688         jc      setdp2
689         cmp     bl,CON(0x02)            ; check if drive detects disk changes
690         jne     setdp2
691         or      ax,CON(DPB_F_DOOR)
692 setdp2: mov     LOC(dpb_flags),ax       ; set flags
693
694 ; Thats it
695
696         inc     BLOC(dpb_valid)         ; increment valid flag
697 setdp9: pop     di
698         pop     si
699         pop     es
700         ret
701
702
703
704 ;====================================================================
705 ;
706 ; Find a load record in the boot header. The ID number of the load
707 ; record is in AL, and ES:DI points to requested load record, or is
708 ; the NULL pointer if load record not found.
709 ;
710 ; Changed registers: AX, DI, ES
711
712 fndldr: push    cx
713         mov     ch,al
714         les     di,LOC(header)          ; examine boot image header
715         SEGES
716         mov     al,[di+BOOT_HD_LENGTH]  ; get length of image header
717         call    getlen
718         add     di,ax                   ; get the pointer to first load record
719 fndl1:  SEGES
720         cmp     ch,[di+BOOT_LD_TAG1]    ; is it the desired one ?
721         je      fndl3
722         SEGES
723         mov     al,[di+BOOT_LD_FLAGS]   ; no, so check if its the last record
724         test    al,CON(BOOT_FLAG_EOF)
725         jnz     fndl2
726         SEGES
727         mov     al,[di+BOOT_LD_LENGTH]  ; no, get the address of the next one
728         call    getlen
729         add     di,ax
730         JMP(fndl1)
731
732 fndl2:  xor     ax,ax                   ; couldnt find the desired record
733         mov     es,ax
734         mov     di,ax
735 fndl3:  pop     cx
736         ret
737
738
739
740 ;====================================================================
741 ;
742 ; Compute the length of a load record address from a length byte
743 ; in AL. Return the offset in AX.
744 ;
745 ; Changed registers: AX
746
747 getlen: push    cx
748         mov     ah,al
749         mov     cl,CON(4)
750         shr     ah,cl
751         and     ax,CON(0x0f0f)          ; compute the total length in
752         add     al,ah                   ; bytes from the length of the
753         xor     ah,ah                   ; record and that of the vendor
754         shl     ax,1                    ; information.
755         shl     ax,1
756         pop     cx
757         ret
758
759
760
761 ;====================================================================
762 ; Print a string in DS:SI onto the console
763 ;
764 ; Changed registers: AL
765
766 prnstr: push    si
767         cld
768 prns1:  lodsb                           ; loop over all characters of
769         or      al,al                   ; string
770         jz      prns2
771         push    bx
772         mov     ah,CON(0x0E)            ; print it
773         mov     bl,CON(0x07)
774         xor     bh,bh
775         int     0x10
776         pop     bx
777         JMP(prns1)
778 prns2:  pop     si
779         ret
780
781
782
783 #ifdef DRV_DEBUG
784 ;====================================================================
785 ;
786 ; Print hexadecimal values (in AX or AL) or characters onto the console
787 ;
788 ; Changed registers: AX
789
790 prnwrd: push    ax
791         mov     al,ah
792         call    prnbyt                  ; print the upper byte
793         pop     ax
794 prnbyt: push    ax
795         shr     al,1                    ; prepare upper nibble
796         shr     al,1
797         shr     al,1
798         shr     al,1
799         call    prnnib                  ; print it
800         pop     ax
801 prnnib: and     al,CON(0x0F)            ; prepare lower nibble
802         add     al,CON(0x30)
803         cmp     al,CON(0x39)            ; convert it into hex
804         jle     prnchr
805         add     al,CON(7)
806 prnchr: push    bx
807         mov     ah,CON(0x0E)            ; print it
808         mov     bl,CON(0x07)
809         xor     bh,bh
810         int     0x10
811         pop     bx
812         ret
813 #endif
814
815
816
817 ;====================================================================
818 ;
819 ; String and constants definitions
820
821
822 ; Startup signature
823
824 sigmsg: db      0x0D, 0x0A
825         STRDECL('DOS Net Boot Image Loader ')
826         STRDECL(VERSION)
827         db      0x0D, 0x0A
828         STRDECL(COPYRIGHT)
829         db      0x0D, 0x0A
830 crlf:   db      0x0D, 0x0A
831         db      0
832
833
834 ; Magic numbers for boot record and bootp entry
835
836 bmagic: dd      BOOT_MAGIC              ; boot image magic number
837 vmagic: STRDECL(VENDOR_MAGIC)           ; vendor magic ID
838         db      0                       ; end of vendor magic ID
839 pmagic: db      BOOTP_MAGIC_RFC         ; bootp magic ID for RFC 1048
840         db      BOOTP_MAGIC_CMU         ; bootp magic ID for CMU
841         db      BOOTP_MAGIC_STA         ; bootp magic ID for Stanford
842         db      0
843
844
845 ; Specifications for different types of disk drives. The order is:
846 ; # dir entries, # sects per FAT, # sects per cluster, media ID
847
848 drvtab: db      112, 2, 2, 0xfd         ; 360kB disk
849         db      224, 7, 1, 0xf9         ; 1.2MB disk
850         db      112, 3, 2, 0xf9         ; 720kB disk
851         db      224, 9, 1, 0xf0         ; 1.44 MB disk
852
853 typtab: db      DPB_T_360               ; type values for drive parameter block
854         db      DPB_T_1200
855         db      DPB_T_720
856         db      DPB_T_1440
857
858
859 ; Error messages
860
861 recerr: STRDECL('Error in load record data')
862         db      0x0D, 0x0A
863         db      0
864
865 bmerr:  STRDECL('Invalid boot header magic number')
866         db      0x0D, 0x0A
867         db      0
868
869 vmerr:  STRDECL('Invalid vendor magic ID')
870         db      0x0D, 0x0A
871         db      0
872
873 rderr:  STRDECL('Error while accessing ramdisk')
874         db      0x0D, 0x0A
875         db      0
876
877 dskerr: STRDECL('Wrong ramdisk image')
878         db      0x0D, 0x0A
879         db      0
880
881 btperr: STRDECL('BOOTP record invalid')
882         db      0x0D, 0x0A
883         db      0
884
885
886 ; Debug messages
887
888 #ifdef ASM_DEBUG
889 debug1: STRDECL('Making driver resident')
890         db      0x0D, 0x0A
891         db      0
892
893 debug2:
894 #ifdef ASM_FREEZE_AFTER_INIT
895         STRDECL('Freezing;')
896 #else
897         STRDECL('Calling boot block')
898 #endif
899         db      0x0D, 0x0A
900         db      0
901 #endif
902
903 #ifdef DRV_DEBUG
904 consz:  STRDECL('RAMDISK: reporting conventional memory size: ')
905         db      0
906 extsz:  STRDECL('RAMDISK: reporting extended memory size: ')
907         db      0
908 bytes:  STRDECL(' bytes')
909         db      0x0D,0x0A
910         db      0
911 #endif
912
913
914
915 ;====================================================================
916 ;
917 ; Variable definitions
918
919 header: dd      0                       ; pointer to boot header from boot rom
920 bootp:  dd      0                       ; pointer to bootp block from boot rom
921
922 resseg: dw      0                       ; segment of resident section
923
924 oldDS:  dw      0                       ; old DS from boot rom
925 oldES:  dw      0                       ; old ES from boot rom
926 oldBP:  dw      0                       ; old BP from boot rom
927 oldSI:  dw      0                       ; old SI from boot rom
928 oldDI:  dw      0                       ; old DI from boot rom
929
930
931
932 ;====================================================================
933 ;
934 ; Start of resident section. This will be placed at the end of the
935 ; low 640kB RAM area.
936 ;
937 ;====================================================================
938 ;
939
940         ALIGN(16)                       ; has to be paragraph aligned
941 start_resident:                         ; indicate start of resident section
942
943
944 ;====================================================================
945 ;
946 ; New interrupt 2Fh routine. This routine gets called by IO.SYS
947 ; in order to determine the maximum amount of memory usable to
948 ; DOS. This only works with DOS versions 5.0 and higher. The DOS
949 ; function which gets installed into the interrupt 2Fh vector
950 ; does not call the old vector (i.e. it does not daisy-chain),
951 ; and therefore we can redirect 2Fh without a problem even when
952 ; considering to remove the ram disk lateron.
953 ;
954 ; NOTE THAT THIS INTERRUPT HAS TO BE THE FIRST ROUTINE IN THE
955 ; RESIDENT SECTION!
956 ;
957 ; Input:  AX     -  Magic ID
958 ;         DX     -  segment following last usable byte
959 ; Output: DX     -  new segment  following last usable byte
960 ; Registers changed: DX
961
962 int2F:  JMP(int2F1)                     ; this has to be a relative jump
963         nop
964
965         STRDECL('RPL')                  ; magic ID string for DOS
966
967 int2F1: cmp     ax,CON(0x4A06)          ; check for magic ID
968         jne     int2F9
969         push    cx
970         mov     dx,CON(start_resident)  ; determine last usable segment
971         mov     cl,CON(4)               ; from segment and offset of
972         shr     dx,cl                   ; the resident section
973         pop     cx
974         push    ax
975         push    cs
976         pop     ax
977         add     dx,ax                   ; add offset to segment
978         dec     dx
979         pop     ax
980         iret
981
982 int2F9: SEGCS
983         jmp     far [old2Fh]            ; jump to old interrupt routine
984
985
986
987 ;====================================================================
988 ;
989 ; New interrupt F8h routine. It can be used to retrieve several
990 ; values from the resident driver.
991 ; Input:  AX  -  function code
992 ; Output: depends on function:
993 ;
994 ; Installation check (AX = 0x9C00)
995 ;         AX     -  contains 0x009C
996 ;
997 ; Return ramdisk size and address (AX = 0x9C01)
998 ;         BX:DX  -  address of ramdisk
999 ;         CX     -  size of ramdisk in kb
1000 ;
1001 ; Return size and address of BOOTP block (AX = 0x9C02)
1002 ;         BX:DX  -  address of BOOTP block
1003 ;         CX     -  size of BOOTP block
1004 ;
1005 ; Return miscellaneous values for handling the ramdisk (AX = 0x9C03)
1006 ;         AX     -  XMS handle for ram disk
1007 ;         BX:DX  -  address of old interrupt vector table
1008 ;         CL     -  ramdisk id
1009 ;
1010 ; Remove ramdisk (AX = 0x9C04)
1011 ;         AL     -  non-zero if error
1012 ;
1013 ; Registers changed: depends on function
1014
1015 intF8:  cmp     ah,CON(0x9C)            ; check for magic ID
1016         jne     intF89
1017         cmp     al,CON(01)              ; check for function number
1018         jne     intF81
1019         SEGCS
1020         mov     bx,LOC(rdaddr+2)        ; return ramdisk address
1021         SEGCS
1022         mov     dx,LOC(rdaddr+0)
1023         SEGCS
1024         mov     cx,LOC(rdsize)          ; return ramdisk size
1025         iret
1026
1027 intF81: cmp     al,CON(0x02)
1028         jne     intF82
1029         mov     bx,cs                   ; return address of BOOTP record
1030         mov     dx,CON(btpnew)
1031         SEGCS
1032         mov     cx,LOC(btplen)          ; return BOOTP length
1033         iret
1034
1035 intF82: cmp     al,CON(0x03)
1036         jne     intF83
1037         mov     bx,cs                   ; return address of old interrupt
1038         mov     dx,CON(oldints)         ; vector table
1039         SEGCS
1040         mov     cl,LOC(drvid)           ; return drive id
1041         SEGCS
1042         mov     ax,LOC(xmshdl)          ; return XMS handle
1043         iret
1044
1045 intF83: cmp     al,CON(0x04)
1046         jne     intF88
1047         call    rmrd                    ; remove ramdisk
1048         iret
1049
1050 intF88: or      al,al
1051         jnz     intF89
1052         xchg    al,ah                   ; return installation check code
1053 intF89: iret
1054
1055
1056 ;====================================================================
1057 ;
1058 ; New interrupt 13h routine to handle disk accesses. It is different
1059 ; for simulating either a floppy drive or a hard disk. DOS provides
1060 ; a way for restoring this interrupt to its original value when we
1061 ; want to remove the ramdisk lateron.
1062 ; Input:  AH  -  function code
1063 ;         DL  -  driver number
1064 ; Output: carry flag set if error
1065 ; Registers changed: depends on function
1066
1067 int13:  sti                             ; we dont need interrupts disabled
1068         push    ax
1069         SEGCS
1070         mov     ax,LOC(xmsadr+0)
1071         SEGCS
1072         or      ax,LOC(xmsadr+2)        ; check if XMS already initialized
1073         jnz     int13s
1074         mov     ax,CON(0x4300)          ; check if XMS available
1075         int     0x2f
1076         cmp     al,CON(0x80)            ; XMS not available
1077         jne     int13s
1078         push    bx
1079         push    es
1080         mov     ax,CON(0x4310)          ; get XMS driver address
1081         int     0x2f
1082         SEGCS
1083         mov     LOC(xmsadr+0),bx        ; save driver address
1084         SEGCS
1085         mov     LOC(xmsadr+2),es
1086         pop     es
1087         call    inixms                  ; initialize XMS
1088         pop     bx
1089 int13s: pop     ax
1090
1091         SEGCS
1092         cmp     dl,LOC(drvid)           ; check if its for us
1093         je      int132
1094         SEGCS
1095         cmp     BLOC(drvid),CON(0x80)   ; check if we are to simulate a hard
1096         jae     int13h                  ; disk drive
1097
1098
1099 ; First comes the floppy drive redirector
1100
1101         cmp     dl,CON(0x80)            ; check if its for a hard disk
1102         jb      int133
1103         SEGCS
1104         test    BLOC(nohd),CON(0xff)    ; check if hard disk accesses allowed
1105         jz      int131
1106         cmp     ah,CON(0x08)            ; function 0x08 should not return error
1107         mov     ah,CON(0x80)            ; return with error
1108         jne     int135
1109         xor     dl,dl                   ; indicate no hard disk present
1110         JMP(int13f)                     ; return without error
1111
1112 ; Handle function 0x08 for disk drives other than the ramdisk.
1113
1114 int133: cmp     ah,CON(0x08)
1115         jne     int131
1116         pushf
1117         SEGCS                           ; function 0x08 has to return the
1118         call    far [old13h]            ; correct number of disk drives
1119         SEGCS
1120         mov     dl,LOC(drvnum)
1121 int13f: xor     ah,ah                   ; never return an error
1122         JMP(int136)
1123
1124 ; Jump directly to the BIOS
1125
1126 int131: SEGCS
1127         jmp     far [old13h]            ; call the old interrupt routine
1128
1129 ; Now handle all ramdisk functions. First check if the function number
1130 ; is correct.
1131
1132 int132: cmp     ah,CON(0x18)
1133         jbe     int134
1134         mov     ah,CON(0x01)            ; unknown command
1135 int135: stc
1136 int136: push    ds
1137         JMP(int13e)
1138
1139 ; Determine the handlers address according to the function number in AH
1140 ; and jump to it.
1141
1142 int134: push    ds
1143         push    cs
1144         pop     ds                      ; set data segment
1145         push    bx
1146         mov     bl,ah
1147         xor     bh,bh
1148         shl     bx,CON(1)               ; compute pointer into routine table
1149         jmp     [bx+fntab]
1150
1151
1152 ; Now comes the hard disk drive redirector
1153
1154 int13h: cmp     dl,CON(0x80)            ; check if its for a floppy drive
1155         jb      int131
1156
1157 ; Handle function 0x08 for hard disk drives other than the ramdisk.
1158
1159         cmp     ah,CON(0x08)
1160         jne     int137
1161         dec     dl
1162         pushf
1163         SEGCS                           ; function 0x08 has to return the
1164         call    far [old13h]            ; correct number of disk drives
1165         SEGCS
1166         mov     dl,LOC(drvnum)
1167         JMP(int13f)                     ; always return without error
1168
1169 ; Handle function 0x15 for disk drives other than the ramdisk. This is
1170 ; the only function besides 0x08 which returns a value in DX and therefore
1171 ; has to have special handling.
1172
1173 int137: push    dx
1174         dec     dl
1175         cmp     ah,CON(0x15)
1176         jne     int138
1177         pushf
1178         SEGCS
1179         call    far [old13h]            ; call the BIOS for handling
1180         jc      int139
1181         cmp     ah,CON(0x03)            ; DX is only used if AH = 0x03
1182         jne     int139
1183         add     sp,BCON(0x0002)         ; remove DX from stack if the BIOS
1184         JMP(int136)                     ; returned a value in it
1185
1186 ; Handle all other functions for drives other than the ramdisk. This will
1187 ; just call the original BIOS handler.
1188
1189 int138: pushf
1190         SEGCS
1191         call    far [old13h]            ; simply call the old int 13h routine
1192 int139: pop     dx
1193         JMP(int136)
1194
1195
1196 ; Save the return status into the BIOS data area and return to the caller
1197 ; while preserving the carry flag.
1198
1199 int13e: push    es                      ; return from function handler
1200         push    bx                      ; this code is not allowed to change
1201         call    getsts                  ; any register or flag
1202         SEGES
1203         mov     [bx],ah                 ; set disk operation status
1204         pop     bx
1205         pop     es
1206 intend: pop     ds
1207         push    ax                      ; general exit point for interrupts
1208         pushf
1209         pop     ax
1210         push    bp
1211         mov     bp,sp
1212         mov     [bp+8],al               ; put the flags onto the stack
1213         pop     bp
1214         pop     ax
1215         iret
1216
1217
1218 ; Function table
1219
1220 fntab:  dw      f1300                   ; function 00: reset disk system
1221         dw      f1301                   ; function 01: return last error
1222         dw      f1302                   ; function 02: read disk
1223         dw      f1303                   ; function 03: write disk
1224         dw      f1304                   ; function 04: verify disk
1225         dw      f1305                   ; function 05: format disk
1226         dw      f1306                   ; function 06: format track
1227         dw      f1307                   ; function 07: format disk
1228         dw      f1308                   ; function 08: get drive parameters
1229         dw      f1309                   ; function 09: intialize controller
1230         dw      f130A                   ; function 0A: read long sectors
1231         dw      f130B                   ; function 0B: write long sectors
1232         dw      f130C                   ; function 0C: seek for cylinder
1233         dw      f130D                   ; function 0D: disk reset
1234         dw      f130E                   ; function 0E: read sector buffer
1235         dw      f130F                   ; function 0F: write sector buffer
1236         dw      f1310                   ; function 10: check if drive ready
1237         dw      f1311                   ; function 11: recalibrate drive
1238         dw      f1312                   ; function 12: controller ram diagnostic
1239         dw      f1313                   ; function 13: drive diagnostic
1240         dw      f1314                   ; function 14: controller int diagnostic
1241         dw      f1315                   ; function 15: get disk type
1242         dw      f1316                   ; function 16: detect disk change
1243         dw      f1317                   ; function 17: set media type for format
1244         dw      f1318                   ; function 18: set media type for format
1245
1246 f13end: JMP(int13e)
1247
1248
1249
1250 ;====================================================================
1251 ;
1252 ; Function 00 - reset disk system
1253 ;
1254
1255 f1300:  test    WLOC(syscnf),CON(0x0001)        ; check if we have physical floppy
1256         jz      f13001                  ; drives at all
1257         push    dx
1258         xor     dl,dl                   ; always reset the floppy system
1259         pushf
1260         call    far [old13h]            ; call old disk interrupt
1261         pop     dx
1262         jc      f13002
1263 f13001: xor     ah,ah                   ; no error
1264 f13002: pop     bx
1265         JMP(f13end)
1266
1267
1268
1269 ;====================================================================
1270 ;
1271 ; Function 01 - return last error status
1272 ;
1273
1274 f1301:  push    es
1275         call    getsts                  ; get offset to status byte
1276         SEGES
1277         mov     ah,[bx]                 ; get disk operation status
1278         pop     es
1279         pop     bx
1280         clc
1281         JMP(intend)
1282
1283
1284
1285 ;====================================================================
1286 ;
1287 ; Function 02/03 - read from/write to disk
1288
1289 f1302:
1290 f1303:
1291         pop     bx                      ; get old BX from stack
1292         push    dx
1293         push    cx
1294         push    bx
1295         push    ax
1296
1297         xchg    cx,ax
1298         sub     ch,2                    ; ch=0 for reads, ch=1 for writes
1299         call    cvtsec                  ; get linear sector number
1300         jnc     f13026
1301
1302         pop     ax
1303         mov     ah,CON(0x04)            ; error: sector not found
1304 f13021: 
1305         pop     bx
1306         pop     cx
1307         pop     dx
1308         stc
1309         JMP(f13end)                     ; terminate
1310 f13022:
1311         cmp     dx,LOC(secnumhi)        ; check if sector is still correct
1312         jb      f13024
1313         
1314         cmp     ax,LOC(secnumlo)
1315         jbe     f13024
1316
1317         mov     ah,CON(0x04)            ; error: sector not found
1318 f13023:
1319         pop     bx
1320         mov     al,bl
1321         sub     al,cl                   ; compute number of sectors processed
1322         JMP(f13021)
1323 f13024:
1324         call    rwsect                  ; actually handle request
1325         jnc     f13025
1326
1327         mov     ah,CON(0x20)            ; error: disk controller error
1328         JMP(f13023)
1329 f13025:
1330         inc     ax                      ; proceed with next linear sector
1331         adc     dx,CON(0)
1332         add     bx,CON(SECT_SIZE)       ; increment src/dst address
1333         dec     cl                      ; decrement sector count
1334 f13026:
1335         or      cl,cl
1336         jnz     f13022
1337 f13027:
1338         pop     ax
1339         pop     bx
1340         pop     cx
1341         pop     dx
1342         xor     ah,ah                   ; no error
1343 f13e1:
1344         JMP(f13end)
1345
1346
1347
1348 ;====================================================================
1349 ;
1350 ; Function 08  -  get disk drive parameters
1351
1352 f1308:  pop     bx                      ; get old BX from stack
1353         mov     dl,LOC(drvnum)          ; get number of disk drives
1354         mov     cl,LOC(secptk)          ; get sectors per track
1355         mov     ch,LOC(cylnum)          ; number of cylinders
1356         mov     dh,LOC(heads)           ; number of heads - 1
1357         dec     dh
1358         xor     bx,bx                   ; ramdisk drive type
1359         xor     ax,ax
1360         dec     ch
1361         JMP(f13e1)
1362
1363
1364
1365 ;====================================================================
1366 ;
1367 ; Function 04, 05, 06, 07, 09, 0D, 10, 11, 12, 13, 14, 16  -  no operation
1368
1369 f1304:
1370 f1305:
1371 f1306:
1372 f1307:
1373 f1309:
1374 f130D:
1375 f1310:
1376 f1311:
1377 f1312:
1378 f1313:
1379 f1314:
1380 f1316:  pop     bx                      ; get old BX from stack
1381         xor     ah,ah                   ; no error
1382         JMP(f13e1)
1383
1384
1385
1386 ;====================================================================
1387 ;
1388 ; Function 0A, 0B, 0E, 0F, 17, 18  -  not implemented
1389
1390 f130A:
1391 f130B:
1392 f130E:
1393 f130F:
1394 f1317:
1395 f1318:  pop     bx                      ; get old BX from stack
1396         mov     ah,CON(0x01)            ; invalid opcode
1397         stc
1398         JMP(f13e1)
1399
1400
1401
1402 ;====================================================================
1403 ;
1404 ; Function 0C  -  seek for cylinder
1405
1406 f130C:  pop     bx                      ; get old BX from stack
1407         push    dx
1408         push    cx
1409         push    ax
1410         xchg    ax,cx
1411         call    cvtsec                  ; get linear sector number
1412         pop     ax
1413         mov     ah,CON(0x00)            ; no error
1414         jnc     f130C1
1415         mov     ah,CON(0x04)            ; error: sector not found
1416 f130C1: pop     cx
1417         pop     dx
1418         JMP(f13e1)                      ; terminate
1419
1420
1421
1422 ;====================================================================
1423 ;
1424 ; Function 15  -  get disk type
1425
1426 f1315:  pop     bx                      ; get old BX from stack
1427         mov     ah,CON(0x02)            ; indicate floppy disk
1428         cmp     dl,CON(0x80)            ; check if floppy disk type requested
1429         jb      f13159
1430         inc     ah                      ; indicate hard disk
1431         mov     dx,LOC(secnumlo)        ; get number of sectors on disk
1432         mov     cx,LOC(secnumhi)
1433 f13159: clc
1434         JMP(f13e1)
1435
1436
1437 ;====================================================================
1438 ;
1439 ; Convert Cyl/Sec/Head notation into a linear sector number
1440 ; Input:  AH  -  cylinder number
1441 ;         AL  -  sector number
1442 ;         DH  -  head number
1443 ; Output: DX:AX  -  32-bit linear sector number
1444 ;         carry flag set if invalid sector number
1445 ; Registers changed: AX,DX
1446
1447 cvtsec: push    bx
1448         push    cx
1449
1450         mov     bl,al                   ; move sector number into BL
1451         mov     bh,dh                   ; move head number into BH
1452         
1453         xchg    ah,al                   ; compute track number into AX, the
1454         mov     cl,CON(6)               ; upper two bits of CL are the high
1455         shr     ah,cl                   ; bits of the 10 bit cylinder number
1456         xor     dx,dx
1457         xor     cx,cx
1458         mov     cl,LOC(heads)
1459         mul     cx                      ; compute track number from cylinders
1460         mov     cl,bh
1461         add     ax,cx
1462         adc     dx,CON(0)
1463         mov     cl,LOC(secptk)
1464         push    bx
1465         xor     bx,bx
1466         or      dx,dx                   ; is 32-bit value > 16-bits yet?
1467         jz      cvts6
1468         
1469         push    ax
1470         push    dx
1471         mov     ax,dx                   ; multiply upper 16-bits first
1472         mul     cx
1473         mov     bx,ax                   ; save result in bx
1474         pop     dx
1475         pop     ax
1476 cvts6:
1477         mul     cx                      ; compute number of track starting
1478         add     dx,bx                   ; fixup upper 16-bits
1479         pop     bx
1480
1481         mov     cl,bl
1482         and     cl,CON(0x3F)            ; move sector number into AX
1483         cmp     cl,LOC(secptk)          ; check if sector number is correct
1484         ja      cvts8
1485
1486         dec     cx                      ; sector numbers start with 1
1487         js      cvts8                   ; therefore sector 0 does not exist
1488
1489         add     ax,cx                   ; compute final sector number
1490         adc     dx,CON(0)
1491
1492         cmp     dx,LOC(secnumhi)        ; check if the sector is valid
1493         jb      cvts8
1494         ja      cvts7
1495
1496         cmp     ax,LOC(secnumlo)
1497         jb      cvts8
1498 cvts7:
1499         stc
1500         JMP(cvts9)
1501 cvts8:
1502         clc                             ; return with error
1503 cvts9:
1504         pop     cx
1505         pop     bx
1506         ret
1507
1508
1509 ;====================================================================
1510 ;
1511 ; Read/write a sector from the ram disk. This routine requires a
1512 ; sector to be 512 bytes long.
1513 ; Input:  CH     -  non-zero if write to ram disk
1514 ;         DX:AX  -  logical sector number
1515 ;         ES:BX  -  pointer to destination buffer
1516 ; Output: carry flag set if error
1517 ; Registers changed: AX
1518
1519 rwsect:
1520         push    si
1521         push    di
1522         push    cx
1523         push    dx
1524         push    ax
1525
1526         mov     si,rd_srcb
1527         mov     di,rd_dstb
1528         or      ch,ch                   ; check direction of transfer
1529         jz      rwsec1
1530
1531         mov     si,rd_dstb
1532         mov     di,rd_srcb
1533 rwsec1:
1534         mov     cx,CON(SECT_SIZE)       ; compute linear ramdisk address
1535         mul     cx                      ; from sector number
1536         add     ax,LOC(rdaddr+0)
1537         adc     dx,LOC(rdaddr+2)
1538
1539         mov     [si],ax                 ; set src for read, dst for write
1540         mov     [si+2],dl
1541         mov     [si+5],dh
1542
1543         mov     ax,es
1544         xor     dx,dx
1545         mov     cl,CON(0x10)
1546         xor     ch,ch
1547         mul     cx                      ; compute linear buffer address
1548         add     ax,bx
1549         adc     dx,CON(0)
1550
1551         mov     [di],ax                 ; set dst for read, src for write
1552         mov     [di+2],dl
1553         mov     [di+5],dh
1554
1555         push    es
1556         mov     ax,cs
1557         mov     es,ax
1558         mov     si,CON(rd_gdt)
1559         mov     cx,CON(SECT_SIZE/2)     ; copy 512 bytes, e.g. 256 words
1560         mov     ax,CON(0x8700)          ; let the BIOS move the sector
1561         int     0x15
1562         pop     es
1563
1564         pop     ax
1565         pop     dx
1566         pop     cx
1567         pop     di
1568         pop     si
1569         ret
1570
1571
1572
1573 ;====================================================================
1574 ;
1575 ; Return a pointer to the disk drive status byte. This routine should
1576 ; not change any flags!
1577 ; Input:  none
1578 ; Output: ES:BX  -  pointer to disk drive status byte
1579 ; Registers changed: BX, ES
1580
1581 getsts: mov     bx,CON(BIOS_SEG)        ; status byte is in BIOS data
1582         mov     es,bx                   ; segment
1583         SEGCS
1584         mov     bx,LOC(statof)
1585         ret
1586
1587
1588
1589 ;====================================================================
1590 ;
1591 ; Initialize the XMS interface. This is necessary to prevent the
1592 ; ram disk from getting overwritten. The way this works is to
1593 ; first allocate all of the available XMS, then resize the memory
1594 ; block to end just above the ramdisk and lock it. Unfortunately
1595 ; we have to do it this complicated because there is no way of
1596 ; knowing how the XMS is going to allocate the available memory.
1597 ; Another problem is that at least HIMEM.SYS requires up to 256
1598 ; bytes of stack, and we cannot assume the caller of INT 13 to
1599 ; provide that much so we have to change stacks.
1600 ; Input:  none
1601 ; Output: none
1602 ; Registers changed: AX, BX
1603
1604 inixms: call    setstk                  ; set new stack
1605
1606 ; First check that the XMS version number is correct. To support all
1607 ; necessary functions it has to be version 2.0+.
1608
1609         xor     ah,ah
1610         call    callxm                  ; get version number
1611         cmp     ah,CON(0x02)
1612         jb      inixm8                  ; wrong XMS version
1613
1614 ; Determine how much memory we can allocate.
1615
1616         mov     ah,CON(0x08)
1617         xor     bl,bl                   ; get amount of extended memory
1618         call    callxm
1619         or      bl,bl                   ; check for error
1620         jnz     inixm8
1621         mov     bx,LOC(rdsize)          ; get size of ramdisk
1622         add     bx,BCON(65)             ; care for a missing HMA
1623         cmp     bx,ax                   ; check if enough memory for ram disk
1624         ja      inixm8
1625
1626 ; Grab all of the extended memory.
1627
1628         push    bx
1629         mov     dx,ax                   ; grab largest block - which is whole
1630         mov     ah,CON(0x09)            ; memory because there should be no
1631         call    callxm                  ; other process using XMS
1632         pop     bx
1633         or      ax,ax                   ; check for error
1634         jz      inixm8
1635         mov     LOC(xmshdl),dx          ; save handle
1636
1637 ; Now resize the memory block so that it will contain the ramdisk image.
1638
1639         mov     ah,CON(0x0f)            ; reallocate memory block
1640         call    callxm
1641         or      ax,ax                   ; check for error
1642         jnz     inixm1
1643
1644 inixm8: mov     WLOC(xmshdl),CON(0)     ; in case of error dont return handle
1645         JMP(inixm9)
1646
1647 ; Now lock the memory block and check that the physical address of the
1648 ; memory block is correct.
1649
1650 inixm1: mov     dx,LOC(xmshdl)
1651         mov     ah,CON(0x0c)            ; lock memory block
1652         call    callxm
1653         add     bx,CON(0x03ff)
1654         adc     dx,BCON(0x0001)         ; add 65kb - maximum difference
1655         sub     bx,LOC(rdaddr+0)        ; check that ramdisk address is below
1656         sbb     dx,LOC(rdaddr+2)
1657         jc      inixm8
1658
1659 ; Thats it. Restore all registers and swap the stack back to its
1660 ; original state.
1661
1662 inixm9: call    rststk                  ; restore old stack
1663         ret
1664
1665
1666
1667 ;====================================================================
1668 ;
1669 ; Call XMS driver.
1670 ; Input:  AH  -  function code
1671 ;         other registers depending on function code
1672 ; Output: depends on called function
1673 ; Registers changed: depends on called function
1674
1675 callxm: push    ax
1676         push    bp
1677         push    ax
1678         mov     bp,sp
1679         mov     ax,[bp+6]
1680         mov     [bp+4],ax               ; make far return address from
1681         mov     [bp+6],cs               ; near call
1682         pop     ax
1683         pop     bp
1684         SEGCS
1685         push    WLOC(xmsadr+2)          ; push address of XMS driver
1686         SEGCS
1687         push    WLOC(xmsadr+0)
1688         retf                            ; call XMS driver
1689
1690
1691
1692 ;====================================================================
1693 ;
1694 ; Set new stack
1695 ; Input:  none
1696 ; Output: none
1697 ; Registers changed: AX, BX, DS, SS, SP
1698
1699 setstk: cli
1700         pop     bx                      ; get return address
1701         mov     ax,sp
1702         SEGCS
1703         mov     LOC(oldstk+0),ax
1704         SEGCS
1705         mov     LOC(oldstk+2),ss        ; save old stack pointer
1706         mov     ax,cs
1707         mov     ss,ax
1708         mov     sp,CON(newtos - 2)      ; change to new stack
1709         sti
1710         push    cx
1711         push    dx
1712         push    si                      ; save all registers
1713         push    di
1714         push    es
1715         push    ds
1716         mov     ax,cs                   ; set DS to current segment
1717         mov     ds,ax
1718         jmp     bx                      ; return to caller
1719
1720
1721
1722 ;====================================================================
1723 ;
1724 ; Reset stack to old stack
1725 ; Input:  none
1726 ; Output: none
1727 ; Registers changed: all (reset to old state)
1728
1729
1730 rststk: pop     bx                      ; get return address
1731         pop     ds
1732         pop     es
1733         pop     di
1734         pop     si                      ; restore all registers
1735         pop     dx
1736         pop     cx
1737         cli
1738         SEGCS
1739         mov     ax,LOC(oldstk+0)        ; restore old stack
1740         SEGCS
1741         mov     ss,LOC(oldstk+2)
1742         mov     sp,ax
1743         sti
1744         jmp     bx                      ; return to caller
1745
1746
1747
1748 ;====================================================================
1749 ;
1750 ; Remove ramdisk from memory. This involves restoring all interrupt
1751 ; vectors, freeing all used memory and restoring the DOS drive para-
1752 ; meter table. Since we need to call the XMS drive, we have to switch
1753 ; stacks like with inixms.
1754 ; Input:  none
1755 ; Output: AL  -  non-zero if error
1756 ; Registers changed: AX
1757
1758 rmrd:   push    bx
1759         call    setstk                  ; set new stack
1760         mov     al,LOC(drvid)
1761         cmp     al,CON(0x80)            ; can only restore floppy drives
1762         jb      rmrd1
1763 rmrd8:  call    rststk                  ; return with error
1764         pop     bx
1765         mov     al,CON(0x01)
1766         ret
1767
1768 ; First determine the address of the DOS disk parameter block for the
1769 ; ramdisk and check that the open count is zero, i.e. no open file on
1770 ; the device.
1771
1772 rmrd1:  push    ds
1773         mov     ax,CON(0x0803)
1774         int     0x2f                    ; get address of drive parameter
1775         mov     ax,ds                   ; table from DOS
1776         mov     es,ax
1777         pop     ds
1778
1779 rmrd2:  SEGES
1780         mov     al,[di+4]               ; get physical unit number
1781         cmp     al,LOC(drvid)           ; is it our drive?
1782         je      rmrd3
1783         cmp     di,CON(0xffff)          ; error if we couldnt find the DPB
1784         je      rmrd8                   ; for the ramdisk
1785         SEGES
1786         les     di,[di]                 ; get pointer to next entry
1787         JMP(rmrd2)
1788
1789 rmrd3:  mov     LOC(dpb_addr+0),di
1790         mov     LOC(dpb_addr+2),es
1791         SEGES
1792         mov     ax,[di+0x20]            ; get device open count
1793         or      ax,ax
1794         jnz     rmrd8
1795
1796 ; Next restore the interrupt vectors. Int 13h is special as it is
1797 ; redirected by DOS. However, DOS provides a function to restore
1798 ; that interrupt. Interrupt 2Fh doesnt have to get restored because
1799 ; DOS does never call the old interrupt again.
1800
1801         xor     ax,ax
1802         mov     es,ax
1803         mov     ax,cs
1804         SEGES                           ; first check that nobody redirected
1805         cmp     ax,LOC(IF8_INT+2)       ; our own interrupts. In that case
1806         jne     rmrd8                   ; there is no chance of removing the
1807         SEGES                           ; ramdisk.
1808         mov     ax,LOC(IF8_INT+0)
1809         cmp     ax,CON(intF8)
1810         jne     rmrd8
1811         mov     si,CON(if1sig)
1812         mov     di,CON(IF1_INT)
1813         mov     cx,CON(4)               ; interrupt F1h contains a signature
1814         repz                            ; and no vector
1815         cmpsb
1816         jnz     rmrd8
1817
1818         push    ds
1819         les     bx,LOC(old13h)          ; get old interrupt vector 13h
1820         mov     dx,bx
1821         mov     ax,es                   ; save it into DS:DX and ES:BX
1822         mov     ds,ax
1823         mov     ah,CON(0x13)
1824         int     0x2f                    ; call DOS to restore vector
1825         mov     ax,ds
1826         mov     cx,cs
1827         cmp     ax,cx                   ; check that its indeed our interrupt
1828         jne     rmrd4                   ; which we are replacing
1829         mov     ax,es
1830         cmp     ax,cx
1831         jne     rmrd4
1832         cmp     bx,CON(int13)
1833         jne     rmrd4
1834         cmp     dx,CON(int13)
1835         je      rmrd5
1836
1837 rmrd4:  mov     ah,CON(0x13)
1838         int     0x2f                    ; restore old interrupt
1839         pop     ds                      ; someone redirected the interrupt
1840 #ifdef  USE_AS86
1841         jmp     near rmrd8              ; already, cant restore
1842 #endif
1843 #ifdef  USE_NASM
1844         jmp     rmrd8                   ; already, cant restore
1845 #endif
1846
1847 rmrd5:  pop     ds                      ; restore the other interrupts
1848         cli
1849         xor     ax,ax
1850         mov     es,ax
1851         mov     ax,LOC(oldF8h+0)
1852         SEGES
1853         mov     LOC(IF8_INT+0),ax
1854         mov     ax,LOC(oldF8h+2)
1855         SEGES
1856         mov     LOC(IF8_INT+2),ax
1857         sti
1858
1859 ; OK, we can now setup the DOS drive parameter table to contain the
1860 ; correct values for the physical floppy drive. If we couldnt create
1861 ; a valid parameter table entry for this drive, simply mark the DOS
1862 ; entry as invalid. This will cause "Not Ready" errors in DOS. This
1863 ; doesnt work with DR-DOS 5.0!
1864
1865         les     di,LOC(dpb_addr)        ; get address of DPB
1866         SEGES
1867 #ifdef  USE_AS86
1868         or      [di+0x1f],BCON(80)      ; mark drive as invalid
1869 #endif
1870 #ifdef  USE_NASM
1871         or      word [di+0x1f],BCON(80) ; mark drive as invalid
1872 #endif
1873         test    BLOC(dpb_valid),CON(0xff)       ; check if DPB valid
1874         jz      rmrd6
1875         cld                             ; got correct table entry
1876         mov     cx,CON(dpb_end - dpb)
1877         mov     si,CON(dpb)
1878         add     di,BCON(4)
1879         rep
1880         movsb                           ; simply copy the DPB
1881
1882 ; Next remove the ramdisk image from extended memory using the XMS driver.
1883
1884 rmrd6:  mov     dx,LOC(xmshdl)
1885         or      dx,dx                   ; only free memory if we really
1886         jz      rmrd7                   ; assigned it with XMS
1887         push    dx
1888         mov     ah,CON(0x0d)
1889         call    callxm                  ; unlock memory block
1890         pop     dx
1891         or      ax,ax                   ; dont free block if error
1892         jz      rmrd7
1893         mov     ah,CON(0x0a)
1894         call    callxm                  ; free memory block
1895
1896 ; Finally we can remove the memory for the ramdisk driver. We only
1897 ; reset the owner field of the memory control block to 0 to indicate
1898 ; it as free.
1899
1900 rmrd7:  mov     dx,CON(start_resident)  ; determine last usable segment
1901         mov     cl,CON(4)               ; from segment and offset of
1902         shr     dx,cl                   ; the resident section
1903         mov     ax,cs
1904         add     dx,ax                   ; add offset to segment
1905         sub     dx,BCON(2)
1906         mov     es,dx
1907         mov     di,CON(1)
1908         xor     ax,ax                   ; set owner field to 0
1909         stosw
1910         add     di,BCON(5)
1911         mov     cx,CON(4)               ; clear owner name
1912         rep
1913         stosw
1914
1915 ; Thats it. Return to caller.
1916
1917 rmrd9:  call    rststk                  ; restore old stack
1918         pop     bx
1919         xor     al,al                   ; return without error
1920         ret
1921
1922
1923
1924 ;====================================================================
1925 ;
1926 ; Variables for the resident section
1927
1928                 ALIGN(2)
1929
1930 oldints:
1931 old13h:         dd      0               ; old interrupt 13h vector
1932 old2Fh:         dd      0               ; old interrupt 2Fh vector
1933 oldF1h:         dd      0               ; old interrupt F1h vector
1934 oldF8h:         dd      0               ; old interrupt F8h vector
1935
1936
1937 ; Disk parameters for ram disk
1938
1939 statof:         dw      BIOS_FDSTAT     ; offset to BIOS disk status byte
1940 rdaddr:         dd      0               ; base address of ram disk
1941 rdsize:         dw      0               ; size of ram disk in kb
1942 cylnum:         dw      80              ; number of cylinders
1943 secnumlo:       dw      2400            ; number of sectors on disk
1944 secnumhi:       dw      0
1945 secptk:         dw      15              ; number of sectors per track
1946 heads:          db      1               ; number of heads
1947 drvnum:         db      1               ; number of disk drives
1948 drvid:          db      0               ; ram disk drive id
1949 nohd:           db      0               ; no-hard-disk flag
1950 syscnf:         dw      0               ; system configuration from BIOS
1951
1952                 ALIGN(2)
1953
1954
1955 ; Variables used to access the XMS interface
1956
1957 xmshdl:         dw      0               ; XMS handle for ram disk
1958 xmsadr:         dd      0               ; address of XMS driver interface
1959
1960
1961 ; Variables used to redirect the stack
1962
1963 oldstk:         dd      0               ; old stack pointer
1964 newstk:         SPACE(512)              ; new stack for calling XMS driver
1965 newtos:                                 ; new top of stack
1966
1967
1968 ; Signature to put into interrupt vector F1h
1969
1970 if1sig:         STRDECL('NetB')
1971
1972
1973         ALIGN(16)                       ; has to be paragraph aligned
1974
1975 ; Descriptor table to access ram disk using the BIOS
1976
1977 rd_gdt:         dw      0,0,0,0
1978                 dw      0,0,0,0
1979 rd_src:         dw      0xffff          ; length
1980 rd_srcb:        db      0,0,0           ; base
1981                 db      0x93            ; typebyte
1982                 db      0               ; limit16 =0
1983 rd_srcbh:       db      0               ; base24
1984 rd_dst:         dw      0xffff          ; length
1985 rd_dstb:        db      0,0,0           ; base
1986                 db      0x93            ; typebyte
1987                 db      0               ; limit16 =0
1988 rd_dstbh:       db      0               ; base24
1989                 dw      0,0,0,0         ; BIOS CS
1990                 dw      0,0,0,0         ; BIOS DS
1991
1992
1993 ; DOS disk parameter block. It contains the definitions for the
1994 ; floppy disk drive which is redirected by the ramdisk, and used
1995 ; for removing the ramdisk drive. Note that this DPB is only
1996 ; valid for DOS 4.0 and higher.
1997
1998 dpb_addr:       dd      0               ; address of DPB in DOS data area
1999 dpb_valid:      db      0               ; non-zero if DPB is valid
2000
2001 dpb:
2002 dpb_phys:       db      0               ; BIOS ID of physical drive
2003 dpb_log:        db      0               ; logical DOS drive ID
2004
2005 dpb_bpb_low:    dw      512             ; BIOS param block for lowest capacity
2006                 db      0xff
2007                 dw      1
2008                 db      2
2009                 dw      64
2010                 dw      360
2011                 db      0x00
2012                 dw      2
2013                 dw      9
2014                 dw      1
2015                 dd      0
2016                 dd      0
2017
2018 dpb_fat:        db      0               ; flag indicating 16-bit FAT
2019 dpb_open:       dw      0               ; device open count
2020 dpb_type:       db      0x01            ; device type
2021 dpb_flags:      dw      DPB_F_DEFAULT   ; flags describing drive
2022 dpb_cyls:       dw      80              ; number of cylinders
2023
2024 dpb_bpb_cur:    dw      512             ; BIOS parameter block for current
2025                 db      1
2026                 dw      1
2027                 db      2
2028                 dw      224
2029                 dw      2400
2030                 db      0xf9
2031                 dw      7
2032                 dw      15
2033                 dw      2
2034                 dd      0
2035                 dd      0
2036
2037 dpb_rsvd:       db      0, 0, 0, 0, 0, 0
2038 dpb_ltrack:     db      0xff            ; last accessed track
2039 dpb_lacc:       dd      0xffffffff      ; time of last disk access
2040 dpb_volname:    STRDECL('NO NAME    ')  ; volume name
2041                 db      0
2042 dpb_sernum:     dd      0               ; volume serial number
2043 dpb_fsname:     STRDECL('FAT12   ')     ; file system name
2044                 db      0
2045 dpb_end:
2046
2047
2048 ; Copy of bootp block from bootrom. This has to be last in the data area!
2049
2050 btplen:         dw      0               ; length of bootp block
2051 btpnew:                                 ; bootp block has to be at the very end
2052