Last tweaks for 1.4.1.
[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      ' '
828         STRDECL(VENDOR_MAGIC)
829         db      0x0D, 0x0A
830         STRDECL(COPYRIGHT)
831         db      0x0D, 0x0A
832 crlf:   db      0x0D, 0x0A
833         db      0
834
835
836 ; Magic numbers for boot record and bootp entry
837
838 bmagic: dd      BOOT_MAGIC              ; boot image magic number
839 vmagic: STRDECL(VENDOR_MAGIC)           ; vendor magic ID
840         db      0                       ; end of vendor magic ID
841 pmagic: db      BOOTP_MAGIC_RFC         ; bootp magic ID for RFC 1048
842         db      BOOTP_MAGIC_CMU         ; bootp magic ID for CMU
843         db      BOOTP_MAGIC_STA         ; bootp magic ID for Stanford
844         db      0
845
846
847 ; Specifications for different types of disk drives. The order is:
848 ; # dir entries, # sects per FAT, # sects per cluster, media ID
849
850 drvtab: db      112, 2, 2, 0xfd         ; 360kB disk
851         db      224, 7, 1, 0xf9         ; 1.2MB disk
852         db      112, 3, 2, 0xf9         ; 720kB disk
853         db      224, 9, 1, 0xf0         ; 1.44 MB disk
854
855 typtab: db      DPB_T_360               ; type values for drive parameter block
856         db      DPB_T_1200
857         db      DPB_T_720
858         db      DPB_T_1440
859
860
861 ; Error messages
862
863 recerr: STRDECL('Error in load record data')
864         db      0x0D, 0x0A
865         db      0
866
867 bmerr:  STRDECL('Invalid boot header magic number')
868         db      0x0D, 0x0A
869         db      0
870
871 vmerr:  STRDECL('Invalid vendor magic ID')
872         db      0x0D, 0x0A
873         db      0
874
875 rderr:  STRDECL('Error while accessing ramdisk')
876         db      0x0D, 0x0A
877         db      0
878
879 dskerr: STRDECL('Wrong ramdisk image')
880         db      0x0D, 0x0A
881         db      0
882
883 btperr: STRDECL('BOOTP record invalid')
884         db      0x0D, 0x0A
885         db      0
886
887
888 ; Debug messages
889
890 #ifdef ASM_DEBUG
891 debug1: STRDECL('Making driver resident')
892         db      0x0D, 0x0A
893         db      0
894
895 debug2:
896 #ifdef ASM_FREEZE_AFTER_INIT
897         STRDECL('Freezing;')
898 #else
899         STRDECL('Calling boot block')
900 #endif
901         db      0x0D, 0x0A
902         db      0
903 #endif
904
905 #ifdef DRV_DEBUG
906 consz:  STRDECL('RAMDISK: reporting conventional memory size: ')
907         db      0
908 extsz:  STRDECL('RAMDISK: reporting extended memory size: ')
909         db      0
910 bytes:  STRDECL(' bytes')
911         db      0x0D,0x0A
912         db      0
913 #endif
914
915
916
917 ;====================================================================
918 ;
919 ; Variable definitions
920
921 header: dd      0                       ; pointer to boot header from boot rom
922 bootp:  dd      0                       ; pointer to bootp block from boot rom
923
924 resseg: dw      0                       ; segment of resident section
925
926 oldDS:  dw      0                       ; old DS from boot rom
927 oldES:  dw      0                       ; old ES from boot rom
928 oldBP:  dw      0                       ; old BP from boot rom
929 oldSI:  dw      0                       ; old SI from boot rom
930 oldDI:  dw      0                       ; old DI from boot rom
931
932
933
934 ;====================================================================
935 ;
936 ; Start of resident section. This will be placed at the end of the
937 ; low 640kB RAM area.
938 ;
939 ;====================================================================
940 ;
941
942         ALIGN(16)                       ; has to be paragraph aligned
943 start_resident:                         ; indicate start of resident section
944
945
946 ;====================================================================
947 ;
948 ; New interrupt 2Fh routine. This routine gets called by IO.SYS
949 ; in order to determine the maximum amount of memory usable to
950 ; DOS. This only works with DOS versions 5.0 and higher. The DOS
951 ; function which gets installed into the interrupt 2Fh vector
952 ; does not call the old vector (i.e. it does not daisy-chain),
953 ; and therefore we can redirect 2Fh without a problem even when
954 ; considering to remove the ram disk lateron.
955 ;
956 ; NOTE THAT THIS INTERRUPT HAS TO BE THE FIRST ROUTINE IN THE
957 ; RESIDENT SECTION!
958 ;
959 ; Input:  AX     -  Magic ID
960 ;         DX     -  segment following last usable byte
961 ; Output: DX     -  new segment  following last usable byte
962 ; Registers changed: DX
963
964 int2F:  JMP(int2F1)                     ; this has to be a relative jump
965         nop
966
967         STRDECL('RPL')                  ; magic ID string for DOS
968
969 int2F1: cmp     ax,CON(0x4A06)          ; check for magic ID
970         jne     int2F9
971         push    cx
972         mov     dx,CON(start_resident)  ; determine last usable segment
973         mov     cl,CON(4)               ; from segment and offset of
974         shr     dx,cl                   ; the resident section
975         pop     cx
976         push    ax
977         push    cs
978         pop     ax
979         add     dx,ax                   ; add offset to segment
980         dec     dx
981         pop     ax
982         iret
983
984 int2F9: SEGCS
985         jmp     far [old2Fh]            ; jump to old interrupt routine
986
987
988
989 ;====================================================================
990 ;
991 ; New interrupt F8h routine. It can be used to retrieve several
992 ; values from the resident driver.
993 ; Input:  AX  -  function code
994 ; Output: depends on function:
995 ;
996 ; Installation check (AX = 0x9C00)
997 ;         AX     -  contains 0x009C
998 ;
999 ; Return ramdisk size and address (AX = 0x9C01)
1000 ;         BX:DX  -  address of ramdisk
1001 ;         CX     -  size of ramdisk in kb
1002 ;
1003 ; Return size and address of BOOTP block (AX = 0x9C02)
1004 ;         BX:DX  -  address of BOOTP block
1005 ;         CX     -  size of BOOTP block
1006 ;
1007 ; Return miscellaneous values for handling the ramdisk (AX = 0x9C03)
1008 ;         AX     -  XMS handle for ram disk
1009 ;         BX:DX  -  address of old interrupt vector table
1010 ;         CL     -  ramdisk id
1011 ;
1012 ; Remove ramdisk (AX = 0x9C04)
1013 ;         AL     -  non-zero if error
1014 ;
1015 ; Registers changed: depends on function
1016
1017 intF8:  cmp     ah,CON(0x9C)            ; check for magic ID
1018         jne     intF89
1019         cmp     al,CON(01)              ; check for function number
1020         jne     intF81
1021         SEGCS
1022         mov     bx,LOC(rdaddr+2)        ; return ramdisk address
1023         SEGCS
1024         mov     dx,LOC(rdaddr+0)
1025         SEGCS
1026         mov     cx,LOC(rdsize)          ; return ramdisk size
1027         iret
1028
1029 intF81: cmp     al,CON(0x02)
1030         jne     intF82
1031         mov     bx,cs                   ; return address of BOOTP record
1032         mov     dx,CON(btpnew)
1033         SEGCS
1034         mov     cx,LOC(btplen)          ; return BOOTP length
1035         iret
1036
1037 intF82: cmp     al,CON(0x03)
1038         jne     intF83
1039         mov     bx,cs                   ; return address of old interrupt
1040         mov     dx,CON(oldints)         ; vector table
1041         SEGCS
1042         mov     cl,LOC(drvid)           ; return drive id
1043         SEGCS
1044         mov     ax,LOC(xmshdl)          ; return XMS handle
1045         iret
1046
1047 intF83: cmp     al,CON(0x04)
1048         jne     intF88
1049         call    rmrd                    ; remove ramdisk
1050         iret
1051
1052 intF88: or      al,al
1053         jnz     intF89
1054         xchg    al,ah                   ; return installation check code
1055 intF89: iret
1056
1057
1058 ;====================================================================
1059 ;
1060 ; New interrupt 13h routine to handle disk accesses. It is different
1061 ; for simulating either a floppy drive or a hard disk. DOS provides
1062 ; a way for restoring this interrupt to its original value when we
1063 ; want to remove the ramdisk lateron.
1064 ; Input:  AH  -  function code
1065 ;         DL  -  driver number
1066 ; Output: carry flag set if error
1067 ; Registers changed: depends on function
1068
1069 int13:  sti                             ; we dont need interrupts disabled
1070         push    ax
1071         SEGCS
1072         mov     ax,LOC(xmsadr+0)
1073         SEGCS
1074         or      ax,LOC(xmsadr+2)        ; check if XMS already initialized
1075         jnz     int13s
1076         mov     ax,CON(0x4300)          ; check if XMS available
1077         int     0x2f
1078         cmp     al,CON(0x80)            ; XMS not available
1079         jne     int13s
1080         push    bx
1081         push    es
1082         mov     ax,CON(0x4310)          ; get XMS driver address
1083         int     0x2f
1084         SEGCS
1085         mov     LOC(xmsadr+0),bx        ; save driver address
1086         SEGCS
1087         mov     LOC(xmsadr+2),es
1088         pop     es
1089         call    inixms                  ; initialize XMS
1090         pop     bx
1091 int13s: pop     ax
1092
1093         SEGCS
1094         cmp     dl,LOC(drvid)           ; check if its for us
1095         je      int132
1096         SEGCS
1097         cmp     BLOC(drvid),CON(0x80)   ; check if we are to simulate a hard
1098         jae     int13h                  ; disk drive
1099
1100
1101 ; First comes the floppy drive redirector
1102
1103         cmp     dl,CON(0x80)            ; check if its for a hard disk
1104         jb      int133
1105         SEGCS
1106         test    BLOC(nohd),CON(0xff)    ; check if hard disk accesses allowed
1107         jz      int131
1108         cmp     ah,CON(0x08)            ; function 0x08 should not return error
1109         mov     ah,CON(0x80)            ; return with error
1110         jne     int135
1111         xor     dl,dl                   ; indicate no hard disk present
1112         JMP(int13f)                     ; return without error
1113
1114 ; Handle function 0x08 for disk drives other than the ramdisk.
1115
1116 int133: cmp     ah,CON(0x08)
1117         jne     int131
1118         pushf
1119         SEGCS                           ; function 0x08 has to return the
1120         call    far [old13h]            ; correct number of disk drives
1121         SEGCS
1122         mov     dl,LOC(drvnum)
1123 int13f: xor     ah,ah                   ; never return an error
1124         JMP(int136)
1125
1126 ; Jump directly to the BIOS
1127
1128 int131: SEGCS
1129         jmp     far [old13h]            ; call the old interrupt routine
1130
1131 ; Now handle all ramdisk functions. First check if the function number
1132 ; is correct.
1133
1134 int132: cmp     ah,CON(0x18)
1135         jbe     int134
1136         mov     ah,CON(0x01)            ; unknown command
1137 int135: stc
1138 int136: push    ds
1139         JMP(int13e)
1140
1141 ; Determine the handlers address according to the function number in AH
1142 ; and jump to it.
1143
1144 int134: push    ds
1145         push    cs
1146         pop     ds                      ; set data segment
1147         push    bx
1148         mov     bl,ah
1149         xor     bh,bh
1150         shl     bx,CON(1)               ; compute pointer into routine table
1151         jmp     [bx+fntab]
1152
1153
1154 ; Now comes the hard disk drive redirector
1155
1156 int13h: cmp     dl,CON(0x80)            ; check if its for a floppy drive
1157         jb      int131
1158
1159 ; Handle function 0x08 for hard disk drives other than the ramdisk.
1160
1161         cmp     ah,CON(0x08)
1162         jne     int137
1163         dec     dl
1164         pushf
1165         SEGCS                           ; function 0x08 has to return the
1166         call    far [old13h]            ; correct number of disk drives
1167         SEGCS
1168         mov     dl,LOC(drvnum)
1169         JMP(int13f)                     ; always return without error
1170
1171 ; Handle function 0x15 for disk drives other than the ramdisk. This is
1172 ; the only function besides 0x08 which returns a value in DX and therefore
1173 ; has to have special handling.
1174
1175 int137: push    dx
1176         dec     dl
1177         cmp     ah,CON(0x15)
1178         jne     int138
1179         pushf
1180         SEGCS
1181         call    far [old13h]            ; call the BIOS for handling
1182         jc      int139
1183         cmp     ah,CON(0x03)            ; DX is only used if AH = 0x03
1184         jne     int139
1185         add     sp,BCON(0x0002)         ; remove DX from stack if the BIOS
1186         JMP(int136)                     ; returned a value in it
1187
1188 ; Handle all other functions for drives other than the ramdisk. This will
1189 ; just call the original BIOS handler.
1190
1191 int138: pushf
1192         SEGCS
1193         call    far [old13h]            ; simply call the old int 13h routine
1194 int139: pop     dx
1195         JMP(int136)
1196
1197
1198 ; Save the return status into the BIOS data area and return to the caller
1199 ; while preserving the carry flag.
1200
1201 int13e: push    es                      ; return from function handler
1202         push    bx                      ; this code is not allowed to change
1203         call    getsts                  ; any register or flag
1204         SEGES
1205         mov     [bx],ah                 ; set disk operation status
1206         pop     bx
1207         pop     es
1208 intend: pop     ds
1209         push    ax                      ; general exit point for interrupts
1210         pushf
1211         pop     ax
1212         push    bp
1213         mov     bp,sp
1214         mov     [bp+8],al               ; put the flags onto the stack
1215         pop     bp
1216         pop     ax
1217         iret
1218
1219
1220 ; Function table
1221
1222 fntab:  dw      f1300                   ; function 00: reset disk system
1223         dw      f1301                   ; function 01: return last error
1224         dw      f1302                   ; function 02: read disk
1225         dw      f1303                   ; function 03: write disk
1226         dw      f1304                   ; function 04: verify disk
1227         dw      f1305                   ; function 05: format disk
1228         dw      f1306                   ; function 06: format track
1229         dw      f1307                   ; function 07: format disk
1230         dw      f1308                   ; function 08: get drive parameters
1231         dw      f1309                   ; function 09: intialize controller
1232         dw      f130A                   ; function 0A: read long sectors
1233         dw      f130B                   ; function 0B: write long sectors
1234         dw      f130C                   ; function 0C: seek for cylinder
1235         dw      f130D                   ; function 0D: disk reset
1236         dw      f130E                   ; function 0E: read sector buffer
1237         dw      f130F                   ; function 0F: write sector buffer
1238         dw      f1310                   ; function 10: check if drive ready
1239         dw      f1311                   ; function 11: recalibrate drive
1240         dw      f1312                   ; function 12: controller ram diagnostic
1241         dw      f1313                   ; function 13: drive diagnostic
1242         dw      f1314                   ; function 14: controller int diagnostic
1243         dw      f1315                   ; function 15: get disk type
1244         dw      f1316                   ; function 16: detect disk change
1245         dw      f1317                   ; function 17: set media type for format
1246         dw      f1318                   ; function 18: set media type for format
1247
1248 f13end: JMP(int13e)
1249
1250
1251
1252 ;====================================================================
1253 ;
1254 ; Function 00 - reset disk system
1255 ;
1256
1257 f1300:  test    WLOC(syscnf),CON(0x0001)        ; check if we have physical floppy
1258         jz      f13001                  ; drives at all
1259         push    dx
1260         xor     dl,dl                   ; always reset the floppy system
1261         pushf
1262         call    far [old13h]            ; call old disk interrupt
1263         pop     dx
1264         jc      f13002
1265 f13001: xor     ah,ah                   ; no error
1266 f13002: pop     bx
1267         JMP(f13end)
1268
1269
1270
1271 ;====================================================================
1272 ;
1273 ; Function 01 - return last error status
1274 ;
1275
1276 f1301:  push    es
1277         call    getsts                  ; get offset to status byte
1278         SEGES
1279         mov     ah,[bx]                 ; get disk operation status
1280         pop     es
1281         pop     bx
1282         clc
1283         JMP(intend)
1284
1285
1286
1287 ;====================================================================
1288 ;
1289 ; Function 02/03 - read from/write to disk
1290
1291 f1302:
1292 f1303:
1293         pop     bx                      ; get old BX from stack
1294         push    dx
1295         push    cx
1296         push    bx
1297         push    ax
1298
1299         xchg    cx,ax
1300         sub     ch,2                    ; ch=0 for reads, ch=1 for writes
1301         call    cvtsec                  ; get linear sector number
1302         jnc     f13026
1303
1304         pop     ax
1305         mov     ah,CON(0x04)            ; error: sector not found
1306 f13021: 
1307         pop     bx
1308         pop     cx
1309         pop     dx
1310         stc
1311         JMP(f13end)                     ; terminate
1312 f13022:
1313         cmp     dx,LOC(secnumhi)        ; check if sector is still correct
1314         jb      f13024
1315         
1316         cmp     ax,LOC(secnumlo)
1317         jbe     f13024
1318
1319         mov     ah,CON(0x04)            ; error: sector not found
1320 f13023:
1321         pop     bx
1322         mov     al,bl
1323         sub     al,cl                   ; compute number of sectors processed
1324         JMP(f13021)
1325 f13024:
1326         call    rwsect                  ; actually handle request
1327         jnc     f13025
1328
1329         mov     ah,CON(0x20)            ; error: disk controller error
1330         JMP(f13023)
1331 f13025:
1332         inc     ax                      ; proceed with next linear sector
1333         adc     dx,CON(0)
1334         add     bx,CON(SECT_SIZE)       ; increment src/dst address
1335         dec     cl                      ; decrement sector count
1336 f13026:
1337         or      cl,cl
1338         jnz     f13022
1339 f13027:
1340         pop     ax
1341         pop     bx
1342         pop     cx
1343         pop     dx
1344         xor     ah,ah                   ; no error
1345 f13e1:
1346         JMP(f13end)
1347
1348
1349
1350 ;====================================================================
1351 ;
1352 ; Function 08  -  get disk drive parameters
1353
1354 f1308:  pop     bx                      ; get old BX from stack
1355         mov     dl,LOC(drvnum)          ; get number of disk drives
1356         mov     cl,LOC(secptk)          ; get sectors per track
1357         mov     ch,LOC(cylnum)          ; number of cylinders
1358         mov     dh,LOC(heads)           ; number of heads - 1
1359         dec     dh
1360         xor     bx,bx                   ; ramdisk drive type
1361         xor     ax,ax
1362         dec     ch
1363         JMP(f13e1)
1364
1365
1366
1367 ;====================================================================
1368 ;
1369 ; Function 04, 05, 06, 07, 09, 0D, 10, 11, 12, 13, 14, 16  -  no operation
1370
1371 f1304:
1372 f1305:
1373 f1306:
1374 f1307:
1375 f1309:
1376 f130D:
1377 f1310:
1378 f1311:
1379 f1312:
1380 f1313:
1381 f1314:
1382 f1316:  pop     bx                      ; get old BX from stack
1383         xor     ah,ah                   ; no error
1384         JMP(f13e1)
1385
1386
1387
1388 ;====================================================================
1389 ;
1390 ; Function 0A, 0B, 0E, 0F, 17, 18  -  not implemented
1391
1392 f130A:
1393 f130B:
1394 f130E:
1395 f130F:
1396 f1317:
1397 f1318:  pop     bx                      ; get old BX from stack
1398         mov     ah,CON(0x01)            ; invalid opcode
1399         stc
1400         JMP(f13e1)
1401
1402
1403
1404 ;====================================================================
1405 ;
1406 ; Function 0C  -  seek for cylinder
1407
1408 f130C:  pop     bx                      ; get old BX from stack
1409         push    dx
1410         push    cx
1411         push    ax
1412         xchg    ax,cx
1413         call    cvtsec                  ; get linear sector number
1414         pop     ax
1415         mov     ah,CON(0x00)            ; no error
1416         jnc     f130C1
1417         mov     ah,CON(0x04)            ; error: sector not found
1418 f130C1: pop     cx
1419         pop     dx
1420         JMP(f13e1)                      ; terminate
1421
1422
1423
1424 ;====================================================================
1425 ;
1426 ; Function 15  -  get disk type
1427
1428 f1315:  pop     bx                      ; get old BX from stack
1429         mov     ah,CON(0x02)            ; indicate floppy disk
1430         cmp     dl,CON(0x80)            ; check if floppy disk type requested
1431         jb      f13159
1432         inc     ah                      ; indicate hard disk
1433         mov     dx,LOC(secnumlo)        ; get number of sectors on disk
1434         mov     cx,LOC(secnumhi)
1435 f13159: clc
1436         JMP(f13e1)
1437
1438
1439 ;====================================================================
1440 ;
1441 ; Convert Cyl/Sec/Head notation into a linear sector number
1442 ; Input:  AH  -  cylinder number
1443 ;         AL  -  sector number
1444 ;         DH  -  head number
1445 ; Output: DX:AX  -  32-bit linear sector number
1446 ;         carry flag set if invalid sector number
1447 ; Registers changed: AX,DX
1448
1449 cvtsec: push    bx
1450         push    cx
1451
1452         mov     bl,al                   ; move sector number into BL
1453         mov     bh,dh                   ; move head number into BH
1454         
1455         xchg    ah,al                   ; compute track number into AX, the
1456         mov     cl,CON(6)               ; upper two bits of CL are the high
1457         shr     ah,cl                   ; bits of the 10 bit cylinder number
1458         xor     dx,dx
1459         xor     cx,cx
1460         mov     cl,LOC(heads)
1461         mul     cx                      ; compute track number from cylinders
1462         mov     cl,bh
1463         add     ax,cx
1464         adc     dx,CON(0)
1465         mov     cl,LOC(secptk)
1466         push    bx
1467         xor     bx,bx
1468         or      dx,dx                   ; is 32-bit value > 16-bits yet?
1469         jz      cvts6
1470         
1471         push    ax
1472         push    dx
1473         mov     ax,dx                   ; multiply upper 16-bits first
1474         mul     cx
1475         mov     bx,ax                   ; save result in bx
1476         pop     dx
1477         pop     ax
1478 cvts6:
1479         mul     cx                      ; compute number of track starting
1480         add     dx,bx                   ; fixup upper 16-bits
1481         pop     bx
1482
1483         mov     cl,bl
1484         and     cl,CON(0x3F)            ; move sector number into AX
1485         cmp     cl,LOC(secptk)          ; check if sector number is correct
1486         ja      cvts8
1487
1488         dec     cx                      ; sector numbers start with 1
1489         js      cvts8                   ; therefore sector 0 does not exist
1490
1491         add     ax,cx                   ; compute final sector number
1492         adc     dx,CON(0)
1493
1494         cmp     dx,LOC(secnumhi)        ; check if the sector is valid
1495         jb      cvts8
1496         ja      cvts7
1497
1498         cmp     ax,LOC(secnumlo)
1499         jb      cvts8
1500 cvts7:
1501         stc
1502         JMP(cvts9)
1503 cvts8:
1504         clc                             ; return with error
1505 cvts9:
1506         pop     cx
1507         pop     bx
1508         ret
1509
1510
1511 ;====================================================================
1512 ;
1513 ; Read/write a sector from the ram disk. This routine requires a
1514 ; sector to be 512 bytes long.
1515 ; Input:  CH     -  non-zero if write to ram disk
1516 ;         DX:AX  -  logical sector number
1517 ;         ES:BX  -  pointer to destination buffer
1518 ; Output: carry flag set if error
1519 ; Registers changed: AX
1520
1521 rwsect:
1522         push    si
1523         push    di
1524         push    cx
1525         push    dx
1526         push    ax
1527
1528         mov     si,rd_srcb
1529         mov     di,rd_dstb
1530         or      ch,ch                   ; check direction of transfer
1531         jz      rwsec1
1532
1533         mov     si,rd_dstb
1534         mov     di,rd_srcb
1535 rwsec1:
1536         mov     cx,CON(SECT_SIZE)       ; compute linear ramdisk address
1537         mul     cx                      ; from sector number
1538         add     ax,LOC(rdaddr+0)
1539         adc     dx,LOC(rdaddr+2)
1540
1541         mov     [si],ax                 ; set src for read, dst for write
1542         mov     [si+2],dl
1543         mov     [si+5],dh
1544
1545         mov     ax,es
1546         xor     dx,dx
1547         mov     cl,CON(0x10)
1548         xor     ch,ch
1549         mul     cx                      ; compute linear buffer address
1550         add     ax,bx
1551         adc     dx,CON(0)
1552
1553         mov     [di],ax                 ; set dst for read, src for write
1554         mov     [di+2],dl
1555         mov     [di+5],dh
1556
1557         push    es
1558         mov     ax,cs
1559         mov     es,ax
1560         mov     si,CON(rd_gdt)
1561         mov     cx,CON(SECT_SIZE/2)     ; copy 512 bytes, e.g. 256 words
1562         mov     ax,CON(0x8700)          ; let the BIOS move the sector
1563         int     0x15
1564         pop     es
1565
1566         pop     ax
1567         pop     dx
1568         pop     cx
1569         pop     di
1570         pop     si
1571         ret
1572
1573
1574
1575 ;====================================================================
1576 ;
1577 ; Return a pointer to the disk drive status byte. This routine should
1578 ; not change any flags!
1579 ; Input:  none
1580 ; Output: ES:BX  -  pointer to disk drive status byte
1581 ; Registers changed: BX, ES
1582
1583 getsts: mov     bx,CON(BIOS_SEG)        ; status byte is in BIOS data
1584         mov     es,bx                   ; segment
1585         SEGCS
1586         mov     bx,LOC(statof)
1587         ret
1588
1589
1590
1591 ;====================================================================
1592 ;
1593 ; Initialize the XMS interface. This is necessary to prevent the
1594 ; ram disk from getting overwritten. The way this works is to
1595 ; first allocate all of the available XMS, then resize the memory
1596 ; block to end just above the ramdisk and lock it. Unfortunately
1597 ; we have to do it this complicated because there is no way of
1598 ; knowing how the XMS is going to allocate the available memory.
1599 ; Another problem is that at least HIMEM.SYS requires up to 256
1600 ; bytes of stack, and we cannot assume the caller of INT 13 to
1601 ; provide that much so we have to change stacks.
1602 ; Input:  none
1603 ; Output: none
1604 ; Registers changed: AX, BX
1605
1606 inixms: call    setstk                  ; set new stack
1607
1608 ; First check that the XMS version number is correct. To support all
1609 ; necessary functions it has to be version 2.0+.
1610
1611         xor     ah,ah
1612         call    callxm                  ; get version number
1613         cmp     ah,CON(0x02)
1614         jb      inixm8                  ; wrong XMS version
1615
1616 ; Determine how much memory we can allocate.
1617
1618         mov     ah,CON(0x08)
1619         xor     bl,bl                   ; get amount of extended memory
1620         call    callxm
1621         or      bl,bl                   ; check for error
1622         jnz     inixm8
1623         mov     bx,LOC(rdsize)          ; get size of ramdisk
1624         add     bx,BCON(65)             ; care for a missing HMA
1625         cmp     bx,ax                   ; check if enough memory for ram disk
1626         ja      inixm8
1627
1628 ; Grab all of the extended memory.
1629
1630         push    bx
1631         mov     dx,ax                   ; grab largest block - which is whole
1632         mov     ah,CON(0x09)            ; memory because there should be no
1633         call    callxm                  ; other process using XMS
1634         pop     bx
1635         or      ax,ax                   ; check for error
1636         jz      inixm8
1637         mov     LOC(xmshdl),dx          ; save handle
1638
1639 ; Now resize the memory block so that it will contain the ramdisk image.
1640
1641         mov     ah,CON(0x0f)            ; reallocate memory block
1642         call    callxm
1643         or      ax,ax                   ; check for error
1644         jnz     inixm1
1645
1646 inixm8: mov     WLOC(xmshdl),CON(0)     ; in case of error dont return handle
1647         JMP(inixm9)
1648
1649 ; Now lock the memory block and check that the physical address of the
1650 ; memory block is correct.
1651
1652 inixm1: mov     dx,LOC(xmshdl)
1653         mov     ah,CON(0x0c)            ; lock memory block
1654         call    callxm
1655         add     bx,CON(0x03ff)
1656         adc     dx,BCON(0x0001)         ; add 65kb - maximum difference
1657         sub     bx,LOC(rdaddr+0)        ; check that ramdisk address is below
1658         sbb     dx,LOC(rdaddr+2)
1659         jc      inixm8
1660
1661 ; Thats it. Restore all registers and swap the stack back to its
1662 ; original state.
1663
1664 inixm9: call    rststk                  ; restore old stack
1665         ret
1666
1667
1668
1669 ;====================================================================
1670 ;
1671 ; Call XMS driver.
1672 ; Input:  AH  -  function code
1673 ;         other registers depending on function code
1674 ; Output: depends on called function
1675 ; Registers changed: depends on called function
1676
1677 callxm: push    ax
1678         push    bp
1679         push    ax
1680         mov     bp,sp
1681         mov     ax,[bp+6]
1682         mov     [bp+4],ax               ; make far return address from
1683         mov     [bp+6],cs               ; near call
1684         pop     ax
1685         pop     bp
1686         SEGCS
1687         push    WLOC(xmsadr+2)          ; push address of XMS driver
1688         SEGCS
1689         push    WLOC(xmsadr+0)
1690         retf                            ; call XMS driver
1691
1692
1693
1694 ;====================================================================
1695 ;
1696 ; Set new stack
1697 ; Input:  none
1698 ; Output: none
1699 ; Registers changed: AX, BX, DS, SS, SP
1700
1701 setstk: cli
1702         pop     bx                      ; get return address
1703         mov     ax,sp
1704         SEGCS
1705         mov     LOC(oldstk+0),ax
1706         SEGCS
1707         mov     LOC(oldstk+2),ss        ; save old stack pointer
1708         mov     ax,cs
1709         mov     ss,ax
1710         mov     sp,CON(newtos - 2)      ; change to new stack
1711         sti
1712         push    cx
1713         push    dx
1714         push    si                      ; save all registers
1715         push    di
1716         push    es
1717         push    ds
1718         mov     ax,cs                   ; set DS to current segment
1719         mov     ds,ax
1720         jmp     bx                      ; return to caller
1721
1722
1723
1724 ;====================================================================
1725 ;
1726 ; Reset stack to old stack
1727 ; Input:  none
1728 ; Output: none
1729 ; Registers changed: all (reset to old state)
1730
1731
1732 rststk: pop     bx                      ; get return address
1733         pop     ds
1734         pop     es
1735         pop     di
1736         pop     si                      ; restore all registers
1737         pop     dx
1738         pop     cx
1739         cli
1740         SEGCS
1741         mov     ax,LOC(oldstk+0)        ; restore old stack
1742         SEGCS
1743         mov     ss,LOC(oldstk+2)
1744         mov     sp,ax
1745         sti
1746         jmp     bx                      ; return to caller
1747
1748
1749
1750 ;====================================================================
1751 ;
1752 ; Remove ramdisk from memory. This involves restoring all interrupt
1753 ; vectors, freeing all used memory and restoring the DOS drive para-
1754 ; meter table. Since we need to call the XMS drive, we have to switch
1755 ; stacks like with inixms.
1756 ; Input:  none
1757 ; Output: AL  -  non-zero if error
1758 ; Registers changed: AX
1759
1760 rmrd:   push    bx
1761         call    setstk                  ; set new stack
1762         mov     al,LOC(drvid)
1763         cmp     al,CON(0x80)            ; can only restore floppy drives
1764         jb      rmrd1
1765 rmrd8:  call    rststk                  ; return with error
1766         pop     bx
1767         mov     al,CON(0x01)
1768         ret
1769
1770 ; First determine the address of the DOS disk parameter block for the
1771 ; ramdisk and check that the open count is zero, i.e. no open file on
1772 ; the device.
1773
1774 rmrd1:  push    ds
1775         mov     ax,CON(0x0803)
1776         int     0x2f                    ; get address of drive parameter
1777         mov     ax,ds                   ; table from DOS
1778         mov     es,ax
1779         pop     ds
1780
1781 rmrd2:  SEGES
1782         mov     al,[di+4]               ; get physical unit number
1783         cmp     al,LOC(drvid)           ; is it our drive?
1784         je      rmrd3
1785         cmp     di,CON(0xffff)          ; error if we couldnt find the DPB
1786         je      rmrd8                   ; for the ramdisk
1787         SEGES
1788         les     di,[di]                 ; get pointer to next entry
1789         JMP(rmrd2)
1790
1791 rmrd3:  mov     LOC(dpb_addr+0),di
1792         mov     LOC(dpb_addr+2),es
1793         SEGES
1794         mov     ax,[di+0x20]            ; get device open count
1795         or      ax,ax
1796         jnz     rmrd8
1797
1798 ; Next restore the interrupt vectors. Int 13h is special as it is
1799 ; redirected by DOS. However, DOS provides a function to restore
1800 ; that interrupt. Interrupt 2Fh doesnt have to get restored because
1801 ; DOS does never call the old interrupt again.
1802
1803         xor     ax,ax
1804         mov     es,ax
1805         mov     ax,cs
1806         SEGES                           ; first check that nobody redirected
1807         cmp     ax,LOC(IF8_INT+2)       ; our own interrupts. In that case
1808         jne     rmrd8                   ; there is no chance of removing the
1809         SEGES                           ; ramdisk.
1810         mov     ax,LOC(IF8_INT+0)
1811         cmp     ax,CON(intF8)
1812         jne     rmrd8
1813         mov     si,CON(if1sig)
1814         mov     di,CON(IF1_INT)
1815         mov     cx,CON(4)               ; interrupt F1h contains a signature
1816         repz                            ; and no vector
1817         cmpsb
1818         jnz     rmrd8
1819
1820         push    ds
1821         les     bx,LOC(old13h)          ; get old interrupt vector 13h
1822         mov     dx,bx
1823         mov     ax,es                   ; save it into DS:DX and ES:BX
1824         mov     ds,ax
1825         mov     ah,CON(0x13)
1826         int     0x2f                    ; call DOS to restore vector
1827         mov     ax,ds
1828         mov     cx,cs
1829         cmp     ax,cx                   ; check that its indeed our interrupt
1830         jne     rmrd4                   ; which we are replacing
1831         mov     ax,es
1832         cmp     ax,cx
1833         jne     rmrd4
1834         cmp     bx,CON(int13)
1835         jne     rmrd4
1836         cmp     dx,CON(int13)
1837         je      rmrd5
1838
1839 rmrd4:  mov     ah,CON(0x13)
1840         int     0x2f                    ; restore old interrupt
1841         pop     ds                      ; someone redirected the interrupt
1842 #ifdef  USE_AS86
1843         jmp     near rmrd8              ; already, cant restore
1844 #endif
1845 #ifdef  USE_NASM
1846         jmp     rmrd8                   ; already, cant restore
1847 #endif
1848
1849 rmrd5:  pop     ds                      ; restore the other interrupts
1850         cli
1851         xor     ax,ax
1852         mov     es,ax
1853         mov     ax,LOC(oldF8h+0)
1854         SEGES
1855         mov     LOC(IF8_INT+0),ax
1856         mov     ax,LOC(oldF8h+2)
1857         SEGES
1858         mov     LOC(IF8_INT+2),ax
1859         sti
1860
1861 ; OK, we can now setup the DOS drive parameter table to contain the
1862 ; correct values for the physical floppy drive. If we couldnt create
1863 ; a valid parameter table entry for this drive, simply mark the DOS
1864 ; entry as invalid. This will cause "Not Ready" errors in DOS. This
1865 ; doesnt work with DR-DOS 5.0!
1866
1867         les     di,LOC(dpb_addr)        ; get address of DPB
1868         SEGES
1869 #ifdef  USE_AS86
1870         or      [di+0x1f],BCON(80)      ; mark drive as invalid
1871 #endif
1872 #ifdef  USE_NASM
1873         or      word [di+0x1f],BCON(80) ; mark drive as invalid
1874 #endif
1875         test    BLOC(dpb_valid),CON(0xff)       ; check if DPB valid
1876         jz      rmrd6
1877         cld                             ; got correct table entry
1878         mov     cx,CON(dpb_end - dpb)
1879         mov     si,CON(dpb)
1880         add     di,BCON(4)
1881         rep
1882         movsb                           ; simply copy the DPB
1883
1884 ; Next remove the ramdisk image from extended memory using the XMS driver.
1885
1886 rmrd6:  mov     dx,LOC(xmshdl)
1887         or      dx,dx                   ; only free memory if we really
1888         jz      rmrd7                   ; assigned it with XMS
1889         push    dx
1890         mov     ah,CON(0x0d)
1891         call    callxm                  ; unlock memory block
1892         pop     dx
1893         or      ax,ax                   ; dont free block if error
1894         jz      rmrd7
1895         mov     ah,CON(0x0a)
1896         call    callxm                  ; free memory block
1897
1898 ; Finally we can remove the memory for the ramdisk driver. We only
1899 ; reset the owner field of the memory control block to 0 to indicate
1900 ; it as free.
1901
1902 rmrd7:  mov     dx,CON(start_resident)  ; determine last usable segment
1903         mov     cl,CON(4)               ; from segment and offset of
1904         shr     dx,cl                   ; the resident section
1905         mov     ax,cs
1906         add     dx,ax                   ; add offset to segment
1907         sub     dx,BCON(2)
1908         mov     es,dx
1909         mov     di,CON(1)
1910         xor     ax,ax                   ; set owner field to 0
1911         stosw
1912         add     di,BCON(5)
1913         mov     cx,CON(4)               ; clear owner name
1914         rep
1915         stosw
1916
1917 ; Thats it. Return to caller.
1918
1919 rmrd9:  call    rststk                  ; restore old stack
1920         pop     bx
1921         xor     al,al                   ; return without error
1922         ret
1923
1924
1925
1926 ;====================================================================
1927 ;
1928 ; Variables for the resident section
1929
1930                 ALIGN(2)
1931
1932 oldints:
1933 old13h:         dd      0               ; old interrupt 13h vector
1934 old2Fh:         dd      0               ; old interrupt 2Fh vector
1935 oldF1h:         dd      0               ; old interrupt F1h vector
1936 oldF8h:         dd      0               ; old interrupt F8h vector
1937
1938
1939 ; Disk parameters for ram disk
1940
1941 statof:         dw      BIOS_FDSTAT     ; offset to BIOS disk status byte
1942 rdaddr:         dd      0               ; base address of ram disk
1943 rdsize:         dw      0               ; size of ram disk in kb
1944 cylnum:         dw      80              ; number of cylinders
1945 secnumlo:       dw      2400            ; number of sectors on disk
1946 secnumhi:       dw      0
1947 secptk:         dw      15              ; number of sectors per track
1948 heads:          db      1               ; number of heads
1949 drvnum:         db      1               ; number of disk drives
1950 drvid:          db      0               ; ram disk drive id
1951 nohd:           db      0               ; no-hard-disk flag
1952 syscnf:         dw      0               ; system configuration from BIOS
1953
1954                 ALIGN(2)
1955
1956
1957 ; Variables used to access the XMS interface
1958
1959 xmshdl:         dw      0               ; XMS handle for ram disk
1960 xmsadr:         dd      0               ; address of XMS driver interface
1961
1962
1963 ; Variables used to redirect the stack
1964
1965 oldstk:         dd      0               ; old stack pointer
1966 newstk:         SPACE(512)              ; new stack for calling XMS driver
1967 newtos:                                 ; new top of stack
1968
1969
1970 ; Signature to put into interrupt vector F1h
1971
1972 if1sig:         STRDECL('NetB')
1973
1974
1975         ALIGN(16)                       ; has to be paragraph aligned
1976
1977 ; Descriptor table to access ram disk using the BIOS
1978
1979 rd_gdt:         dw      0,0,0,0
1980                 dw      0,0,0,0
1981 rd_src:         dw      0xffff          ; length
1982 rd_srcb:        db      0,0,0           ; base
1983                 db      0x93            ; typebyte
1984                 db      0               ; limit16 =0
1985 rd_srcbh:       db      0               ; base24
1986 rd_dst:         dw      0xffff          ; length
1987 rd_dstb:        db      0,0,0           ; base
1988                 db      0x93            ; typebyte
1989                 db      0               ; limit16 =0
1990 rd_dstbh:       db      0               ; base24
1991                 dw      0,0,0,0         ; BIOS CS
1992                 dw      0,0,0,0         ; BIOS DS
1993
1994
1995 ; DOS disk parameter block. It contains the definitions for the
1996 ; floppy disk drive which is redirected by the ramdisk, and used
1997 ; for removing the ramdisk drive. Note that this DPB is only
1998 ; valid for DOS 4.0 and higher.
1999
2000 dpb_addr:       dd      0               ; address of DPB in DOS data area
2001 dpb_valid:      db      0               ; non-zero if DPB is valid
2002
2003 dpb:
2004 dpb_phys:       db      0               ; BIOS ID of physical drive
2005 dpb_log:        db      0               ; logical DOS drive ID
2006
2007 dpb_bpb_low:    dw      512             ; BIOS param block for lowest capacity
2008                 db      0xff
2009                 dw      1
2010                 db      2
2011                 dw      64
2012                 dw      360
2013                 db      0x00
2014                 dw      2
2015                 dw      9
2016                 dw      1
2017                 dd      0
2018                 dd      0
2019
2020 dpb_fat:        db      0               ; flag indicating 16-bit FAT
2021 dpb_open:       dw      0               ; device open count
2022 dpb_type:       db      0x01            ; device type
2023 dpb_flags:      dw      DPB_F_DEFAULT   ; flags describing drive
2024 dpb_cyls:       dw      80              ; number of cylinders
2025
2026 dpb_bpb_cur:    dw      512             ; BIOS parameter block for current
2027                 db      1
2028                 dw      1
2029                 db      2
2030                 dw      224
2031                 dw      2400
2032                 db      0xf9
2033                 dw      7
2034                 dw      15
2035                 dw      2
2036                 dd      0
2037                 dd      0
2038
2039 dpb_rsvd:       db      0, 0, 0, 0, 0, 0
2040 dpb_ltrack:     db      0xff            ; last accessed track
2041 dpb_lacc:       dd      0xffffffff      ; time of last disk access
2042 dpb_volname:    STRDECL('NO NAME    ')  ; volume name
2043                 db      0
2044 dpb_sernum:     dd      0               ; volume serial number
2045 dpb_fsname:     STRDECL('FAT12   ')     ; file system name
2046                 db      0
2047 dpb_end:
2048
2049
2050 ; Copy of bootp block from bootrom. This has to be last in the data area!
2051
2052 btplen:         dw      0               ; length of bootp block
2053 btpnew:                                 ; bootp block has to be at the very end
2054