EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

boot16.htm
Parameters
Program

Boot sector is a 512 bytes long data structure recorded in the first sector of disk volume. When computer starts, its BIOS will read boot sector and store its contents at address 0x07C00 in memory. This sample source file compiles boot sector of floppy diskette 3.5" formated by MS-DOS ver.6.

Code in this sector is executed in real CPU mode, no DOS services are available yet. As the binary format does not contain any metainformation (entry point, relocations etc), all absolute addresses in its code must be assembled to fixed values.

BIOS loads the sector at fixed agreed address 0x07C00 and then it will set register DL to the drive number (DL=0 for floppy drive A:), CS=0 and IP=0x7C00. Other register are undefined. This transfers control to the first boot-sector instruction Entry: JMPS Start:.

As the code is expected to run on fixed address 0x0000:0x7C00, origin is elevated to this offset by the statement $ EQU %^ImageBase (%^ImageBase contains 0x7C00). €ASM fills the gap between the start of segment 0 and sector 0x7C00 with NUL bytes, normally emitted to the output file. They will be trimmed off at link time with the option PROGRAM OutFile="boot16.sec"[%Base+1 .. %Base+512] which will select only those 512 bytes assembled at 0x7C00.

Notice the attribute operator OFFSET# used with absolutely addressed symbols, for instance MOV DI,OFFSET# Dpt:. This converts the address of symbol Dpt to a plain number, which doesn't need relocation. Without OFFSET# this would work as well (MOV DI,Dpt:) but €ASM vainly marks the offset in listing dump column using brackets [ ] as relocatable (which it isn't). So the OFFSET# operator is used from cosmetic reason only.

Instruction modifier CODE=LONG is unnecessary, too. I wanted the sample code to be binary-identical with real boot sector of floppy disk formated by MS-DOS 6, and the compiler used by Microsoft prefers machine instructions with alternative opcode.

Format
Special binary file for 16bit real mode, executable by BIOS.
Platform
Personal Computer with x86 processor
Documented
[BootSec]
Build
euroasm boot16.htm
Run
Boot sector cannot be run directly. Use 3rd-party utility which is able to store boot16.sec to the first disk sector, for instance dd.exe if=boot16.sec of=\\.\A: and then boot from the disk A:.
Parameters
Set parameters which individualize boot-sector contents of this floppy diskette.
%Base %SETA 0x7C00 ; Absolute address where the boot sector will be loaded at. Aliased as %^ImageBase.
%OEM_ID       %SET "EUROASM",0   ; This 8 bytes identifies OS which created the boot sector.
%VolumeLabel  %SET "EUROASMBOOT" ; Diskette label: arbitrary 11 characters, space-padded.
%VolumeSerial %SET 0x11223344    ; Serial number: random DWORD generated at disk format-time.
Program
The actual boot-sector code and data.
       EUROASM AutoAlign=Off, DumpAll=Yes, DumpWidth=29
boot16 PROGRAM Format=BIN, Model=TINY, Width=16,           \
               OutFile="boot16.sec"[%Base+1 .. %Base+512], \
               ImageBase=%Base, Entry=Entry:,              \
               ListMap=Off, ListGlobals=Off
       INCLUDE bioss.htm ; bioss.htm defines layout of structures used by boot code.
$      EQU %^ImageBase ; Boot sector is assembled at this fixed origin %Base alias %^ImageBase.
Entry: JMPS Start:     ; Skip structures allocated in the beginning of boot-sector.
       NOP
$      EQU %^ImageBase + 0x0003 ; Fixed address of OEM_ID.
       DB %OEM_ID
$      EQU %^ImageBase + 0x000B ; Fixed address of statically-defined Bios Parameter Block.
Bpb:   DS BPB_FAT16, .VolumeLabel=%VolumeLabel, .VolumeSerialNumber=%VolumeSerial
Dpt:   DS 0*DPT ; Diskette Parameter Table (11 bytes) will be copied here, overwriting the Start: code.
; Immediately below the copied Dpt follow four memory variables LBA* for calculation of disk geometry,
; they overwrite the Start: code, too.
LBAdata     EQU %^ImageBase+0x0049 ; DWORD with the number of 1st sector with data, immediately following the root-directory.
LBAcylinder EQU %^ImageBase+0x004D ; WORD with LBA translated for INT 0x13.
LBAtrack    EQU %^ImageBase+0x004F ; BYTE with LBA translated for INT 0x13..
LBAroot     EQU %^ImageBase+0x0050 ; DWORD with the number of 1st sector in root-directory entry.
       ; Entry point jumps to this fixed label Start: at address %Boot+0x3E, where the boot code actually starts.
Start: CLI                ; Disable HW interrupts, as the stack is not settled yet.
       XOR AX,AX,CODE=LONG
       MOV SS,AX
       MOV SP,%^ImageBase ; Set machine stack just below the boot sector.
       PUSH SS
       POP ES
       MOV BX,0x0078      ; Address of default DPT prepared by BIOS.
       LDS SI,[SS:BX]
       PUSH DS,SI,SS,BX
         MOV DI,OFFSET# Dpt:
         MOV CX,SIZE# DPT:
         CLD
         REP MOVSB        ; Copy DPT from BIOS memory to the Dpt:.
         PUSH ES
         POP DS           ; DS=ES=SS=0.
         MOVB [DI-SIZE#DPT+DPT.bHdSettle],15
         MOV CX,[OFFSET# Bpb.SectorsPerTrack]
         MOV [DI-SIZE#DPT+DPT.bLastTrack],CL
         ; Modify the vector address of local DPT copy (Dpt) in BIOS memory.
         MOV [BX+2],AX ; PARA# Dpt.
         MOV [BX+0],OFFSET# Dpt, DATA=WORD
         STI
         ; Recalibrate floppy drive number DL=0.
         INT 13h ; AH=0 Reset disk system.
         JC Error:
         XOR AX,AX
         CMP [OFFSET# Bpb.SmallSectors],AX
         JZ Large:
         MOV CX,[OFFSET# Bpb.SmallSectors]    ; 2880 sectors = 1.44 MB.
         MOV [OFFSET# Bpb.LargeSectors],CX    ; Use .LargeSectors rather than .SmallSectors.
Large:   MOV AL,[OFFSET# Bpb.NumberOfFats]    ; 2.
         MULW [OFFSET# Bpb.SectorsPerFat]     ; 9.
         ADD AX,[OFFSET# Bpb.HiddenSectors+0] ; 0
         ADC DX,[OFFSET# Bpb.HiddenSectors+2] ; 0.
         ADD AX,[OFFSET# Bpb.ReservedSectors] ; 1.
         ADC DX,0                             ; DX:AX is now LBAroot of root-directory (19).
         MOV [LBAroot+0],AX
         MOV [LBAroot+2],DX
         MOV [LBAdata+0],AX
         MOV [LBAdata+2],DX
         MOV AX,SIZE# DIR_ENTRY               ; 32.
         MULW [OFFSET# Bpb.RootEntries]       ; 224.
         MOV BX,[OFFSET# Bpb.BytesPerSector]  ; 512.
         ADD AX,BX,CODE=LONG
         DEC AX
         DIV BX                               ; Divide root-dir size by sector size (512).
         ADD [LBAdata+0],AX ; +14.
         ADC [LBAdata+2],0,DATA=WORD
         MOV BX,0x0500                        ; Memory address where to read disk sectors.
         MOV DX,[LBAroot+2]
         MOV AX,[LBAroot+0]
         CALL LBAtranslate:
         JB  Error:
         MOV AL,1
         CALL ReadSec:                        ; Read the directory entry.
         JB Error:
         MOV DI,BX,CODE=LONG
         MOV CX,8+3                           ; Filename size.
         MOV SI,OFFSET# IO.SYS:
         REPE CMPSB                           ; The first file in dir must be IO.SYS.
         JNE Error:
         LEA DI,[BX+SIZE# DIR_ENTRY]          ; The next dir entry should be MSDOS.SYS.
         MOV CX,8+3                           ; Filename size. SI points to MSDOS.SYS.
         REPE CMPSB                           ; Check if MSDOS.SYS it at expected position on disk.
         JE Loader: ; Load the contents of IO.SYS at address 0x00700 and start its entry 0x0070:0.
Error:   MOV SI,OFFSET# Message:
         CALL Display:
         XOR AX,AX
         INT 16h                              ; Wait for any key pressed.
       POP SI,DS,[SI+0],[SI+2]
       INT 19h ; Invoke the bootstrap loader. Try to boot again with a better disk.
Error2:POP AX,AX,AX
       JMP Error:

Loader: ; IO.SYS file loader reads the first three sectors of IO.SYS to the address 0x00700.
        MOV AX,[BX+DIR_ENTRY.wClstrNo] ; BX=0x500 points to the directory entry of IO.SYS.
        DEC AX,AX
        MOV BL,[OFFSET# Bpb.SectorsPerCluster] ; 1.
        XOR BH,BH
        MUL BX
        ADD AX,[LBAdata+0]
        ADC DX,[LBAdata+2]
        MOV BX,0x0700        ; Memory address where to read.
        MOV CX,3             ; Only read 3 sectors. IO.SYS manages the rest.
NextSec:PUSH AX,DX,CX
          CALL LBAtranslate: ; Convert the cluster number in DX:AX to C/H/S geometry.
          JC Error2:
          MOV AL,1
          CALL ReadSec:      ; Read AL sectors to address BX.
        POP CX,DX,AX
        JC Error:
        ADD AX,1             ; Prepare to read the next cluster.
        ADC DX,0
        ADD BX,[OFFSET# Bpb.BytesPerSector]
        LOOP NextSec:
        MOV CH,[OFFSET# Bpb.MediaDescriptor]
        MOV DL,[OFFSET# Bpb.PhysicalDriveNumber]
        MOV BX,[LBAdata+0]
        MOV AX,[LBAdata+2]
        JMP 0x0070:0         ; Start the code in IO.SYS.

Display:PROC ; Subprocedure which displays a zero-terminated string DS:SI.
        LODSB
        OR AL,AL,CODE=LONG
        JZ Return:           ; Return when the string is completely displayed.
        MOV AH,0x0E
        MOV BX,0x0007
        INT 10h              ; Output character AL on screen, advance cursor.
        JMP Display:

LBAtranslate:PROC ; Subprocedure which translates LBA in DX:AX
                  ; (cluster number, 19 for root-dir, 33 for data) to the disk geometry.
        CMP DX,[OFFSET# Bpb.SectorsPerTrack] ; 18.
        JNB RetCF:
        DIVW [OFFSET# Bpb.SectorsPerTrack] ; AX=track number, DX=sector number in the track.
        INC DL
        MOV [LBAtrack],DL
        XOR DX,DX,CODE=LONG
        DIVW [OFFSET# Bpb.NumberOfHeads] ; 2.
        MOV [OFFSET# Bpb.Reserved],DL   ; This BPB member is misused for the  head number.
        MOV [LBAcylinder],AX
        CLC
        RET
RetCF:  STC                             ; Signalize return with error.
Return: RET
        ENDPROC LBAtranslate:
        ENDPROC Display:

ReadSec:PROC ; Subprocedure which reads AL sectors to memory at ES:BX from translated disk address.
        MOV AH,2
        MOV DX,[LBAcylinder]
        MOV CL,6
        SHL DH,CL
        OR DH,[LBAtrack]
        MOV CX,DX,CODE=LONG
        XCHG CH,CL
        MOV DL,[OFFSET# Bpb.PhysicalDriveNumber]
        MOV DH,[OFFSET# Bpb.Reserved] ; Head number.
        INT 0x13 ; Read AL sectors starting from CL to ES:BX by BIOS service.
        RET
        ENDPROC ReadSec:

Message:DB 13,10,"Non-System disk or disk error"
        DB 13,10,"Replace and press any key when ready"
        DB 13,10,0
IO.SYS: DB "IO      SYS"         ; File names of MS DOS boot files.
        DB "MSDOS   SYS"
        DB 0,0                   ; Unused sector space.
$       EQU %^ImageBase + 0x01FE ; Fixed origin of boot-sector signature 55AAh.
        DB 0x55,0xAA             ; Boot sector end signature.
      ENDPROGRAM boot16

▲Back to the top▲