; WINDOWS 98 (WINDOWS 4.1)
; MS-DOS 7.1
; FAT16 File Sytem BOOT SECTOR/RECORD Assembly File

; Disassembled & Edited by Erdogan Tan [ ISTANBUL/TURKEY, May 2003 ]
; via following programs:
; DEBUG.EXE 1999
; DASM.EXE 1993
; PV.EXE 1993 
; and
; Microsoft FAT FS Specification v1.02 1999

; Assembly/Source Code is compatible with
; Microsoft Macro Assembler v5.10 [ MASM.EXE v6.11 1993 ]
; (Use: 'masm.exe <thisfile.asm>' then 'link /t <thisfile.obj>')
; (Note: Except BPB/BS parameters, all boot sector code will be original!)

; http://www.singlix.org/trdos/index.html

; This file is a part of 'Turkish Rational DOS' development project
; Specially for "How DOS Starts Up" Article

           
;ÄÄÄÄÄÄÄÄÄÄ CODE SEGMENT ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

SEGMENT_1       segment para public
                assume  CS:SEGMENT_1, DS:SEGMENT_1, SS:SEGMENT_1, ES:SEGMENT_1


                org     7C00h


;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±
;±		ENTRY POINT
;±
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±


;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
;±
;±              PROCEDURE WIN98_FAT16_BOOTSECTOR
;±
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±

Buffer equ 526h 

WIN98_FAT16_BOOTSECTOR    proc    far

BS_Start:        jmp     short BootProcess      

                 nop    ; db 90h
                                      ; Here is 7C03h
BS_OEMName:      db      'MSWIN4.1'   ; 8 bytes, Operation System Indicator
BPB_BytsPerSec:  dw      200h         ; 1 word, Bytes Per Sector = 512
BPB_SecPerClus:  db      40h ;max     ; 1 byte, Sector Per Cluster = (64)
BPB_Rsvd_SecCnt: dw      1h           ; 1 byte, Reserved Sector = 1
BPB_Num_FATs:    db      2h           ; 1 byte, Number of FATs = 2
BPB_RootEntCnt:  dw      200h         ; 1 word, Root Directory Entries = 512
BPB_TotalSec16:  dw      0            ; 1 word, 16 bit Volume Size = 0 
BPB_Media:       db      0F8h         ; 1 byte, Media Code, Fixed Disk = F8h
BPB_FATSz16:     dw      100h ;max    ; 1 word, FAT Size = (100h) sectors
BPB_SecPerTrk:   dw      3Fh ;INT13h  ; 1 word, Sectors Per Track Value= (63)
BPB_NumHeads:    dw      0FFh ;INT13h ; 1 word, Number of Heads Value= (255)
BPB_HiddSecs:    dd      0 ;partition ; 1 dword, Sectors before this partition
BPB_TotalSec32:  dd      0 ;partition ; 1 dword, 32 bit Volume Size = max 2 GB
BS_DrvNum:       db      80h ;INT13h  ; 1 byte, 80h for Hard Disk 0 (Drive C:) 
BS_Reserved1:    db      0            ; 1 byte, always 0, used by Windows NT
BS_BootSig:      db      29h          ; 1 byte, Extended Boot Signature = 29h
BS_VolID:        dd      0 ;SerialNo  ; 1 dword, Volume Serial Number
BS_VolLab:       db      'NO NAME    '; 11 bytes, Volume Label/Name
BS_FileSysType:  db      'FAT16   '   ; 8 bytes, File System Indicator

BootProcess:
		xor	CX,CX			
		mov	SS,CX			

;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
		assume  SS:nothing
;ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ

                mov     SP,7BFCh   ; 7C00h - 4      
		push	SS			
		pop	ES			

;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
		assume  ES:nothing
;ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ

		mov	BP,78h			
		lds	SI,Dword Ptr [BP+0]	; Load FAR ptr
                                                ; 0000:0078h = INT 1Eh vector

;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
		assume  DS:nothing
;ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ

                                                ; Floppy Disk Parameters Table
                push    DS                      ; ROMBIOS INT 1Eh FDPT address
                push    SI                      ; ROMBIOS INT 1Eh FDPT address
                push    SS                      ; 0
                push    BP                      ; 78h
                mov     DI,522h                 ; 0:522h or 50h:22h
                mov     Word Ptr [BP+0],DI      ; New offset for INT 1Eh, 522h
                mov     Word Ptr [BP+2],CX      ; New segment for INT 1Eh, 0
                mov     CL,0Bh                  ; 11 bytes transfer count
		cld				; Clear direction flag
		repz	movsb			; Repeat if ZF = 1, CX > 0
						; Move byte from DS:SI to ES:DI
		push	ES			
                pop     DS                      ; Now, source data segment is 0
                mov     BP,7C00h                ; Beginning of this Boot Sector
                mov     Byte Ptr [DI-2],0Fh     ; Modify head settle time
                cmp     Byte Ptr [BP+24h],CL    ; Drive Number Comparison 
                jnl     set_startup_jo_sys      ; Jump if not less/negative
                                                ; Jump if not Hard Disk (>=80)
                                                ; Signed Comparison
                mov	AX,CX			
		cwd				; Convert word to dword
                                                ; DX:AX= 0
                call    near ptr read_mb_sector ; MasterBoot will be read
                sub     BX,3Ah                  ; Result= 900h - 3Ah =  8C6h
                                                ; or 1C6h of MasterBoot Record
                                                ; is Partition Starting Sector
                db      66h
                ; 80386 : 'mov eax, [7C1Ch]'  or 'db 66h & mov ax, [7C1Ch]' 
                mov     AX, [7C1Ch]             ; Hidden Sectors
                                                ; Sectors before this partition
check_partition_entry:
                db      66h
                cmp     AX, [BX]                ; Is this our partition ?
                ; 80386: cmp EAX, dword ptr [BX] 
                mov     DL, [BX-4]              ; Put FS ID into DL
                jnz     next_p_entry            ; If it isn't ours then jump to
                or      DL, 2                   ; 0Ch or 0Eh will be 0Eh
                                                ; <This is nonsence for FAT16> 
                mov     [BP+2], DL              ; Save into BS "nop" area !
next_p_entry:
                add     BL, 10h                 ; Look at Next Partition
                jnc     check_partition_entry   ; If BL <= FFh jump to

set_startup_jo_sys:
                xor     CX,CX
                inc     Byte Ptr DS:[Startup_File]
                                                ; "IO      SYS" location
                                                ; Now, it is "JO.SYS"
load_root_dir:
                mov     AL,Byte Ptr [BP+10h]    ; Number of FATs

		cbw				; Convert byte to word
                mul     Word Ptr [BP+16h]       ; Sectors Per FAT
                                                ; DX_AX = AX * Secs Per FAT
                add     AX,Word Ptr [BP+1Ch]    ; Add Hidden Sectors LSW
                adc     DX,Word Ptr [BP+1Eh]    ; Add HiddSecs MSW with carry
                add     AX,Word Ptr [BP+0Eh]    ; Add Reserved Sectors
                adc     DX,CX                   ; Add with carry  (CX = 0)
                mov     SI,Word Ptr [BP+11h]    ; Root Directory Entries
                                                ; DX:AX = Root Dir Location

                db      60h  ; 80386: 'pusha'

                mov     Word Ptr [BP-4], AX     ; 7BFCh = DATA AREA LSW
                mov     Word Ptr [BP-2], DX     ; 7BFEh = DATA AREA MSW
                                                
                mov     AX, 20h                 ; Root Dir Entry Size in bytes
                mul     SI                      ; DX:AX = AX * SI
                mov     BX, Word Ptr [BP+0Bh]   ; Bytes Per Sector
                add     AX, BX
                dec     AX                      ; Round down
                div     BX                      ; AX= DX:AX / BX
                add     Word Ptr [BP-4], AX              
                adc     Word Ptr [BP-2], CX     ; 7BFCh= DATA AREA ADDRESS

                db      61h ; 80386: 'popa'
                                                   
search_startup_file:
                mov     DI, 700h                ; IO.SYS loading address
                                                ; 0:700h  or 70h:0

                call    near ptr read_rd_sector
                jc      loc_disk_err_msg
search_next_dir_entry:
                cmp     Byte Ptr [DI], CH       ; Is Dir Entry Empty ?
                jz      search_io_sys
                
                db      60h ; 80386: 'pusha'

                mov     CL, 0Bh                 ; 11 bytes transfer
                mov     SI, Offset Startup_File ; 'JO      SYS' location
                repz    cmpsb                   ; Compare startup file name

                db      61h ; 80386: 'popa'

                jz     load_startup_file

                dec     SI                      ; Count down from 512 to 0
                jz      search_io_sys           ; JO.SYS not found
                add     DI, 32                  ; Dir Entry Size in bytes
                cmp     DI, BX
                jc      search_next_dir_entry   ; If DI < BX jump to
                jmp     short  search_startup_file
search_io_sys:
                dec     Byte Ptr DS:[Startup_File]
                                                ; Now, it is "IO.SYS"
                jpo     load_root_dir           ; Jump if Parity is Odd
                                                ; 'I' = 49h (Parity= Odd)
                                                ; 'H' = 48h (Parity= Even)

                ; Display "Invalid System Disk" Message

                mov    SI, offset_ptr_invsd_msg ; (3h at 7D7Fh, displacement)    
loc_msg_address:
		lodsb				; Load byte at DS:SI to AL
		cbw				; Convert byte to word
                add     SI,AX                   ; (AX = 3, then SI = 7D82h)
display_err_msg:
		lodsb				; Load byte at DS:SI to AL

                cbw				; Convert byte to word
                inc     AX                      ; If AL = FFh
                                                ; cbw
                                                ; AX=FFFFh
                                                ; inc AX, then AX= 0

                jz      loc_replace_disk_msg    ; Jump if equal/zero

                dec     AX                      ; If AL was 0 before cbw
                                                ; now, AX is 0

                jz      wait_for_any_key        ; Jump if equal/zero

                mov     AH,0Eh
                mov	BX,7
		int	10h			; BIOS Service func ( ah ) = 0Eh
						; Write char as TTY
						;AL-char BH-page BL-color
                jmp     short display_err_msg      

loc_replace_disk_msg:
                mov     SI, offset_ptr_repl_msg ; (27h at 7D82h, displacement)
                jmp     short loc_msg_address   ; "Replace the disk, and then"
                                                ; "press any key to reboot"
loc_disk_err_msg:
                mov     SI, offset_ptr_derr_msg ; (18h at 7D80h, displacement)
                jmp     short loc_msg_address   ; "Disk I/O Error"          

wait_for_any_key:                               ; Restore & Reboot Process
		int	16h			; BIOS Service func ( ah ) = 0Eh
						; Keyboard service

                pop     SI                      ; Pushed BP = 78h
                pop     DS                      ; Pushed SS = 0
                                                ; DS:SI = INT 1Eh vector
                db      66h
                pop     Word Ptr [SI]           ; pop old INT 1Eh FDPT address

             ;  pop     Dword Ptr DS:[SI]   = db 66h, pop [SI]

                                                ; INT 1Eh has been restored

                int     19h                     ; Reboot

load_startup_file:
                mov     SI, offset addr_7D81h   ; SI= 7D81h
                
                ; IO.SYS will use this SI value/address later !?
                
                mov     DI, Word Ptr [DI+1Ah]   ; First Cluster Area
                lea     AX, Word Ptr [DI-2]     ; Cluster = ClusterNum-2
                mov     CL, Byte Ptr [BP+0Dh]   ; Sectors Per Cluster
                mul     CX
                add     AX, Word Ptr [BP-4]     ; File Data Location LSW
                adc     DX, Word Ptr [BP-2]     ; File Data Location MSW
                mov     CL, 4                   ; 4 Sectors
                call    near ptr read_data_sector
                jc      loc_disk_err_msg

             ; MASM.EXE don't accept
             ; jmp     70h:200h
             ; for OP Code: EA00027000
                db      0EAh
                dw      0200h
                dw      0070h 

read_disk_sector:
                push    DX
                push    AX
                push    ES
                push    BX

                db      6Ah, 1    ; 80386: 'push 1' 
                db      6Ah, 10h  ; 80386: 'push 10h'
                
               ; INT 13h - Function 42h - Extended Read
               ; Pushed word value 10h = 10h, 0
               ; 10h is Disk address packet Size in bytes
               ; 0 is default/reserved 
               ; Pushed word value 1h= 1, 0 
               ; 1 is Number of blocks to transfer
               ; 0 is default/reserved
               ; Pushed ES:BX is Transfer Buffer Address
               ; Pushed DX:AX is Low double word of QuadWord LBA
               ; Pushed double word ZERO is High double word of LBA

               ; DISK ADRESS PACKET - INT 13h - Extended Read/Write
               ; Offset 0 = Packet Size in bytes, byte
               ; Offset 1 = Reserved, must be 0, byte
               ; Offset 2 = Block (Sector) Transfer Count, byte
               ; Offset 3 = Reserved, must be 0, byte
               ; Offset 4 = Double Word, Transfer Buffer, Seg:Off
               ; Offset 8 = Quad Word, Starting Logical Block Address

                xchg    CX, AX
                mov     AX, Word Ptr [BP+18h] ; Sectors Per Track

               ; MASM.EXE don't allow immediate operand
               ; as destination with MOV instruction

                db     0A2h       ; Instruction:
                                  ; MOV BYTE PTR [526H], AL 
                db     26h        ; Save Sec Per Track
                db     05h        ; into FDPT offset 4
                                  ; or 526h   
                xchg    SI, AX
                xchg    DX, AX
                xor     DX, DX
                div     SI
                xchg    CX, AX
                div     SI
                inc     DX
                xchg    CX, DX
                div     Word Ptr [BP+1Ah]     ; Number of Heads
                mov     DH, DL
                mov     CH, AL

              ; Instruction: (80386)
              ; ROR AH,2
                db      0C0h
                db      0CCh
                db      02h
                
                or      CL, AH
                mov     AX, 201h
                cmp     Byte Ptr [BP+2], 0Eh  ; Is FS ID 0Eh?
                                              ; 0Eh is FAT16 LBA
                jnz     pass_extended_read

                mov     AH, 42h               ; Extended Read
                mov     SI, SP                ; DS:SI = DAP

pass_extended_read:
                mov     DL, Byte Ptr [BP+24h]  ; Drive Number
                int     13h

                db      61h ; 80386: 'popa'
                db      61h ; 80386: 'popa'

                jc      return_from_read

                inc     AX
                jnz     pass_inc_dx

                inc     DX                    ; AX=0, DX=DX+1
pass_inc_dx:
                add     BX, Word Ptr [BP+0Bh] ; Bytes Per Sector
                dec     CX                    ; Count down
                jnz     read_next_sector            

return_from_read:
                retn    ; OP Code: C3h

offset_ptr_invsd_msg:   ; 7D7Fh
                db      offset msg_invalid_sys_disk - $ ;3h

offset_ptr_derr_msg:    ; 7D80h
                db      offset msg_disk_io_err - $  ;18h

addr_7D81h:             ; 7D81h
                db      1h

offset_ptr_repl_msg:    ; 7D82h
                db      offset msg_replace_disk - $  ;27h

msg_invalid_sys_disk:
                db      0Dh, 0Ah
		db	'Invalid system disk'
		db	0FFh
msg_disk_io_err:
                db      0Dh, 0Ah
                db	'Disk I/O error'
		db	0FFh
msg_replace_disk:
                db      0Dh, 0Ah
		db	'Replace the disk, and then press any key'
		db	0Dh, 0Ah, 0, 0

Startup_File:   ; Location= 7DD8h
                db      'IO      SYS'
Kernel_File:
                db      'MSDOS   SYS'

		db	7Fh, 1, 0 ; MS-DOS v7.1 on FAT16 FS 
read_mb_sector:
read_rd_sector:
                inc	CX
read_data_sector:
                mov     BX, 700h  ; ES:BX = directory buffer 
read_next_sector:
                db      60h       ; 80386: 'pusha'
                db      66h       ; Dword Instruction Prefix
                db      6Ah, 00h  ; 80386: 'push 0' (Dword)
                jmp     read_disk_sector

                dw      0         ; 2 bytes ZERO
                dw      0AA55h    ; Boot Signature

WIN98_FAT16_BOOTSECTOR  endp

SEGMENT_1       ends

                end     BS_Start 

