FAT16 bootloader question in bochs

If you are new to OS Development, plan on spending some time here first before going into the other forums.

Moderator: Moderators

Post Reply
Posts: 59
Joined: Tue Oct 13, 2009 8:49 pm

FAT16 bootloader question in bochs

Post by xixpsychoxix » Sat Jun 21, 2014 3:46 am

Ok, so I am trying to make a simple FAT16 bootloader using the code from the existing tutorial loader. The only modifications I made were to strip the code pertaining to adjusting the clusters to get the correct 12 bits and the Bios parameter block. I created a bochs hard disk (512 mb) and have it working correctly in ECHS mode to read the root directory and locate the stage2.sys file. I have run into a strange issue when trying to load the FAT tables. Here is the source file for the boot code:

Code: Select all

;	Boot1.asm
;		- A Simple Bootloader
;	Operating Systems Development Series

bits	16						; we are in 16 bit real mode
org	0						; we will set regisers later

start:	jmp	main					; jump to start of bootloader

;	BIOS Parameter Block

; BPB Begins 3 bytes from start. We do a far jump, which is 3 bytes in size.
; If you use a short jump, add a "nop" after it to offset the 3rd byte.

bpbOEM			db "My OS   "			; OEM identifier (Cannot exceed 8 bytes!)
bpbBytesPerSector:  	DW 512
bpbSectorsPerCluster: 	DB 8
bpbReservedSectors: 	DW 1
bpbNumberOfFATs: 	DB 2
bpbRootEntries: 	DW 512
bpbTotalSectors: 	DW 0
bpbMedia: 		DB 0xf8  ;; 0xF1
bpbSectorsPerFAT: 	DW 504
bpbSectorsPerTrack: 	DW 63
bpbHeadsPerCylinder: 	DW 32
bpbHiddenSectors: 	DD 0
bpbTotalSectorsBig:     DD 0FFF00h
bsDriveNumber: 	        DB 80h
bsUnused: 		DB 0
bsExtBootSignature: 	DB 0x29
bsSerialNumber:	        DD 0xa0a1a2a3
bsVolumeLabel: 	        DB "BENS FLOPPY"
bsFileSystem: 	        DB "FAT16   "

;	Prints a string
;	DS=>SI: 0 terminated string
			lodsb				; load next byte from string from SI to AL
			or	al, al			; Does AL=0?
			jz	PrintDone		; Yep, null terminator found-bail out
			mov	ah, 0eh			; Nope-Print the character
			int	10h
			jmp	Print			; Repeat until null terminator found
			ret				; we are done, so return

; Reads a series of sectors
; CX=>Number of sectors to read
; AX=>Starting sector
; ES:BX=>Buffer to read to

          mov     di, 0x0005                          ; five retries for error
          push    ax
          push    bx
          push    cx
          call    LBACHS                              ; convert starting sector to CHS
          mov     ah, 0x02                            ; BIOS read sector
          mov     al, 0x01                            ; read one sector
          mov     ch, BYTE [absoluteTrack]            ; track
          mov     cl, BYTE [absoluteSector]           ; sector
          mov     dh, BYTE [absoluteHead]             ; head
          mov     dl, BYTE [bsDriveNumber]            ; drive
          int     0x13                                ; invoke BIOS
          jnc     .SUCCESS                            ; test for read error
          xor     ax, ax                              ; BIOS reset disk
          int     0x13                                ; invoke BIOS
          dec     di                                  ; decrement error counter
          pop     cx
          pop     bx
          pop     ax
          jnz     .SECTORLOOP                         ; attempt to read again
          int     0x18
          mov     si, msgProgress
          call    Print
          pop     cx
          pop     bx
          pop     ax
          add     bx, WORD [bpbBytesPerSector]        ; queue next buffer
          inc     ax                                  ; queue next sector
          loop    .MAIN                               ; read next sector

; Convert Cluster to LBA
; LBA = (cluster - 2) * sectors per cluster


          sub     ax, 0x0002                          ; zero base cluster number
          xor     cx, cx
          mov     cl, BYTE [bpbSectorsPerCluster]     ; convert byte to word
          mul     cx
          add     ax, WORD [datasector]               ; base data sector
; Convert LBA to CHS
; AX=>LBA Address to convert
; absolute sector = (logical sector / sectors per track) + 1
; absolute head   = (logical sector / sectors per track) MOD number of heads
; absolute track  = logical sector / (sectors per track * number of heads)

          xor     dx, dx                              ; prepare dx:ax for operation
          div     WORD [bpbSectorsPerTrack]           ; calculate
          inc     dl                                  ; adjust for sector 0
          mov     BYTE [absoluteSector], dl
          xor     dx, dx                              ; prepare dx:ax for operation
          div     WORD [bpbHeadsPerCylinder]          ; calculate
          mov     BYTE [absoluteHead], dl
          mov     BYTE [absoluteTrack], al

;	Bootloader Entry Point


     ; code located at 0000:7C00, adjust segment registers
          cli						; disable interrupts
          mov     ax, 0x07C0				; setup registers to point to our segment
          mov     ds, ax
          mov     es, ax
          mov     fs, ax
          mov     gs, ax

     ; create stack
          mov     ax, 0x0000				; set the stack
          mov     ss, ax
          mov     sp, 0xFFFF
          sti						; restore interrupts

	  mov byte [bsDriveNumber],dl

     ; Display loading message
          mov     si, msgLoading
          call    Print
     ; Load root directory table

     ; compute size of root directory and store in "cx"
          xor     cx, cx
          mov     ax, 0x0020                           ; 32 byte directory entry
          mul     WORD [bpbRootEntries]                ; total size of directory
          div     WORD [bpbBytesPerSector]             ; sectors used by directory
          xchg    ax, cx
     ; compute location of root directory and store in "ax"
          mov     al, BYTE [bpbNumberOfFATs]            ; number of FATs
          mul     WORD [bpbSectorsPerFAT]               ; sectors used by FATs
          add     ax, WORD [bpbReservedSectors]         ; adjust for bootsector
          mov     WORD [datasector], ax                 ; base of root directory
          add     WORD [datasector], cx
     ; read root directory into memory (07C0:0200)
          mov     bx, 0x0200                            ; copy root dir above bootcode
          call    ReadSectors

     ; Find stage 2

     ; browse root directory for binary image
          mov     cx, WORD [bpbRootEntries]             ; load loop counter
          mov     di, 0x0200                            ; locate first root entry
          push    cx
          mov     cx, 0x000B                            ; eleven character name
          mov     si, ImageName                         ; image name to find
          push    di
     rep  cmpsb                                         ; test for entry match
          pop     di
          je      LOAD_FAT
          pop     cx
          add     di, 0x0020                            ; queue next directory entry
          loop    .LOOP
          jmp     FAILURE

     ; Load FAT

     ; save starting cluster of boot image
          mov     si, msgCRLF
          call    Print
          mov     dx, WORD [di + 0x001A]
          mov     WORD [cluster], dx                  ; file's first cluster
     ; compute size of FAT and store in "cx"
          mov cx,word [bpbSectorsPerFAT]

     ; compute location of FAT and store in "ax"

          mov     ax, WORD [bpbReservedSectors]       ; adjust for bootsector
     ; read FAT into memory (7C00:0200)

          mov     bx, 0x0200                          ; copy FAT above bootcode
          call    ReadSectors

     ; read image file into memory (0050:0000)
          mov     si, msgCRLF
          call    Print
          mov     ax, 0x0050
          mov     es, ax                              ; destination for image
          mov     bx, 0x0000                          ; destination for image
          push    bx

     ; Load Stage 2

          mov     ax, WORD [cluster]                  ; cluster to read
          pop     bx                                  ; buffer to read into
          call    ClusterLBA                          ; convert cluster to LBA
          xor     cx, cx
          mov     cl, BYTE [bpbSectorsPerCluster]     ; sectors to read
          call    ReadSectors
          push    bx
; compute next cluster
          mov     ax, WORD [cluster]                  ; identify current cluster
          mov     bx, 0x0200                          ; location of FAT in memory
          add     bx, ax                              ; index into FAT
          mov     dx, WORD [bx]                       ; read two bytes from FAT
          mov     WORD [cluster], dx                  ; store new cluster
          cmp     dx, 0xFFFF                         ; test for end of file
          jb      LOAD_IMAGE
          mov     si, msgCRLF
          call    Print
          push    WORD 0x0050
          push    WORD 0x0000
          mov     si, msgFailure
          call    Print
          mov     ah, 0x00
          int     0x16                                ; await keypress
          int     0x19                                ; warm boot computer
     absoluteSector db 0x00
     absoluteHead   db 0x00
     absoluteTrack  db 0x00
     driveNumber    db 0x00

     datasector  dw 0x0000
     cluster     dw 0x0000
     ImageName   db "STAGE2  SYS"
     msgLoading  db 0x0D, 0x0A, "Loading Boot Image from HDD...", 0x0D, 0x0A, 0x00
     msgCRLF     db 0x0D, 0x0A, 0x00
     msgProgress db ".", 0x00
     msgFailure  db 0x0D, 0x0A, "ERROR : Press Any Key to Reboot", 0x0A, 0x00
          TIMES 510-($-$$) DB 0
          DW 0xAA55

Bochs continually crashes with the error >>PANIC<< IO write(0x01f0): current command is 20h. Here is a copy of the information from the bottom of the log:

Code: Select all

00018959274i[CPU0 ] CPU is in real mode (active)
00018959274i[CPU0 ] CS.mode = 16 bit
00018959274i[CPU0 ] SS.mode = 16 bit
00018959274i[CPU0 ] EFER   = 0x00000000
00018959274i[CPU0 ] | EAX=0fff2c95  EBX=0000ff79  ECX=00090000  EDX=000001f0
00018959274i[CPU0 ] | ESP=00000004  EBP=0000ff53  ESI=000e0000  EDI=00008400
00018959274i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df IF tf SF zf af PF cf
00018959274i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00018959274i[CPU0 ] |  CS:f000( 0004| 0|  0) 000f0000 0000ffff 0 0
00018959274i[CPU0 ] |  DS:9fc0( 0005| 0|  0) 0009fc00 0000ffff 0 0
00018959274i[CPU0 ] |  SS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00018959274i[CPU0 ] |  ES:07c0( 0005| 0|  0) 00007c00 0000ffff 0 0
00018959274i[CPU0 ] |  FS:07c0( 0005| 0|  0) 00007c00 0000ffff 0 0
00018959274i[CPU0 ] |  GS:07c0( 0005| 0|  0) 00007c00 0000ffff 0 0
00018959274i[CPU0 ] | EIP=0000f04c (0000f04b)
00018959274i[CPU0 ] | CR0=0x60000010 CR2=0x00000000
00018959274i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00018959274i[CPU0 ] 0x000000000000f04b>> out dx, ax : EF
00018959274i[CMOS ] Last time is 1403321661 (Fri Jun 20 23:34:21 2014)
00018959274i[     ] restoring default signal behavior
00018959274i[CTRL ] quit_sim called with exit code 1
So I looked into this and it appears that the panic occurs when the CPU attempts to send a command to the ata controller. The command appears to be the readsectors command (0x20) from what I can tell from the panic message, but as you can see eax is definitely not set to the correct value if that is the case. Has anyone had a similar issue, and is there something important I am missing? I wasn't going to post, but this one has turned into a several day head-scratcher. Thanks guys!

User avatar
Posts: 2
Joined: Wed Jan 09, 2019 7:52 pm
Location: Burkina Faso

FAT16 bootloader question in bochs

Post by Blardy » Tue Feb 05, 2019 3:06 am

I double checked and the clip bootloader was 3 KB over limit. I committed two patches that reduce the size, so it should work now.

Post Reply