;***********************************************************************
; *
; * daptable.inc -- Disk Address Packet
; *
; * ===================================================================
; *
; * Version 1.0 Michael K Greene
; * March 2019
; *
; * ===================================================================
; *
; * Description: DAP : LBA Disk Address Packet
; *
; * ===================================================================
; *
; * This program is free software; you can redistribute it and/or modify
; * it under the terms of the GNU General Public License as published by
; * the Free Software Foundation; either version 2 of the License, or
; * (at your option) any later version.
; *
; * This program is distributed in the hope that it will be useful,
; * but WITHOUT ANY WARRANTY; without even the implied warranty of
; * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; * GNU General Public License for more details.
; *
; * You should have received a copy of the GNU General Public License
; * along with this program; if not, write to the Free Software
; * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
; *
; **********************************************************************
DAP_TABLE struc
DAP_size db 0x10 ; packet size
DAP_unused db 0x00 ; unused
DAP_num_sectors dw 0x0000 ; number of sectors, I usually set to cluster size
DAP_buffer_off dw 0x7E00 ; read to buffer offset
DAP_buffer_seg dw 0x0000 ; read to buffer segment
DAP_sector_low dd 0x00000000 ; lower LBA
DAP_sector_high dd 0x00000000 ; upper LBA
DAP_TABLE ends
Tag Archives: watdos
WatDOS Build Environment
My development environment is mixed. I have a VM of Windows XP that runs on a remote server and connects to one of my NFS shares. This VM downloads and compiles the current Gitlab version of Open Watcom. I also use this VM to compile WatDOS files. I will try and switch Open Watcom and WatDOS to ArcaOS if I can get it on the NFS share.
The VM has a spare drive attached which is a FAT formatted image. Once the WatDOS source is compiled, the files (except the boot sector) are copied to the drive image. Next, switch to the Linux system which is also attached to the NFS share.
From the Linux system, the boot sector is transferred to the hard drive image. The image can be booted using Bochs or Qemu.
BDA – BIOS Data Area – PC Memory Map
Address Size Description 00:00 256dwords Interrupt vector table 30:00 256bytes Stack area used during post and bootstrap 40:00 word COM1 port address 40:02 word COM2 port address 40:04 word COM3 port address 40:06 word COM4 port address 40:08 word LPT1 port address 40:0A word LPT2 port address 40:0C word LPT3 port address 40:0E word LPT4 port address (except PS/2) Extended BIOS Data Area segment (PS/2, see EBDA) 40:10 2 bytes Equipment list flags (see INT 11) |7|6|5|4|3|2|1|0| 40:10 (value in INT 11 register AL) | | | | | | | `- IPL diskette installed | | | | | | `-- math coprocessor | | | | |-+--- old PC system board RAM < 256K | | | | | `-- pointing device installed (PS/2) | | | | `--- not used on PS/2 | | `------ initial video mode `--------- # of diskette drives, less 1 |7|6|5|4|3|2|1|0| 40:11 (value in INT 11 register AH) | | | | | | | `- 0 if DMA installed | | | | `------ number of serial ports | | | `------- game adapter | | `-------- not used, internal modem (PS/2) `----------- number of printer ports 40:12 byte PCjr: infrared keyboard link error count 40:13 word Memory size in Kbytes (see INT 12) 40:15 byte Reserved 40:16 byte PS/2 BIOS control flags 40:17 byte Keyboard flag byte 0 (see KB FLAGS) |7|6|5|4|3|2|1|0| keyboard flag byte 0 | | | | | | | `--- right shift key depressed | | | | | | `---- left shift key depressed | | | | | `----- CTRL key depressed | | | | `------ ALT key depressed | | | `------- scroll-lock is active | | `-------- num-lock is active | `--------- caps-lock is active `---------- insert is active 40:18 byte Keyboard flag byte 1 (see KB FLAGS) |7|6|5|4|3|2|1|0| keyboard flag byte | | | | | | | `--- left CTRL key depressed | | | | | | `---- left ALT key depressed | | | | | `----- system key depressed and held | | | | `------ suspend key has been toggled | | | `------- scroll lock key is depressed | | `-------- num-lock key is depressed | `--------- caps-lock key is depressed `---------- insert key is depressed 40:19 byte Storage for alternate keypad entry 40:1A word Offset from 40:00 to keyboard buffer head 40:1C word Offset from 40:00 to keyboard buffer tail 40:1E 32bytes Keyboard buffer (circular queue buffer) 40:3E byte Drive recalibration status |7|6|5|4|3|2|1|0| drive recalibration status | | | | | | | `-- 1=recalibrate drive 0 | | | | | | `--- 1=recalibrate drive 1 | | | | | `---- 1=recalibrate drive 2 | | | | `----- 1=recalibrate drive 3 | `---------- unused `----------- 1=working interrupt flag 40:3F byte Diskette motor status |7|6|5|4|3|2|1|0| diskette motor status | | | | | | | `-- 1=drive 0 motor on | | | | | | `--- 1=drive 1 motor on | | | | | `---- 1=drive 2 motor on | | | | `----- 1=drive 3 motor on | `---------- unused `----------- 1=write operation 40:40 byte Motor shutoff counter (decremented by INT 8) 40:41 byte Status of last diskette operation (see INT 13,1) |7|6|5|4|3|2|1|0| status of last diskette operation | | | | | | | `--- invalid diskette command | | | | | | `---- diskette address mark not found | | | | | `----- sector not found | | | | `------ diskette DMA error | | | `------- CRC check / data error | | `-------- diskette controller failure | `--------- seek to track failed `---------- diskette time-out 40:42 7 bytes NEC diskette controller status (see FDC) 40:49 byte Current video mode (see VIDEO MODE) 40:4A word Number of screen columns 40:4C word Size of current video regen buffer in bytes 40:4E word Offset of current video page in video regen buffer 40:50 8 words Cursor position of pages 1-8, high order byte=row low order byte=column; changing this data isn't reflected immediately on the display 40:60 byte Cursor ending (bottom) scan line (don't modify) 40:61 byte Cursor starting (top) scan line (don't modify) 40:62 byte Active display page number 40:63 word Base port address for active 6845 CRT controller 3B4h = mono, 3D4h = color 40:65 byte 6845 CRT mode control register value (port 3x8h) EGA/VGA values emulate those of the MDA/CGA 40:66 byte CGA current color palette mask setting (port 3d9h) EGA and VGA values emulate the CGA 40:67 dword CS:IP for 286 return from protected mode dword Temp storage for SS:SP during shutdown dword Day counter on all products after AT dword PS/2 Pointer to reset code with memory preserved 5 bytes Cassette tape control (before AT) 40:6C dword Daily timer counter, equal to zero at midnight; incremented by INT 8; read/set by INT 1A 40:70 byte Clock rollover flag, set when 40:6C exceeds 24hrs 40:71 byte BIOS break flag, bit 7 is set if Ctrl-Break was *ever* hit; set by INT 9 40:72 word Soft reset flag via Ctl-Alt-Del or JMP FFFF:0 1234h Bypass memory tests & CRT initialization 4321h Preserve memory 5678h System suspend 9ABCh Manufacturer test ABCDh Convertible POST loop ????h many other values are used during POST 40:74 byte Status of last hard disk operation (see INT 13,1) 40:75 byte Number of hard disks attached 40:76 byte XT fixed disk drive control byte 40:77 byte Port offset to current fixed disk adapter 40:78 4 bytes Time-Out value for LPT1,LPT2,LPT3(,LPT4 except PS/2) 40:7C 4 bytes Time-Out value for COM1,COM2,COM3,COM4 40:80 word Keyboard buffer start offset (seg=40h,BIOS 10-27-82) 40:82 word Keyboard buffer end offset (seg=40h,BIOS 10-27-82) 40:84 byte Rows on the screen (less 1, EGA+) 40:85 word Point height of character matrix (EGA+) byte PCjr: character to be repeated if the typematic repeat key takes effect 40:86 byte PCjr: initial delay before repeat key action begins 40:87 byte PCjr: current Fn function key number byte Video mode options (EGA+) |7|6|5|4|3|2|1|0| Video mode options (EGA+) | | | | | | | `-- 1=alphanumeric cursor emulation enabled | | | | | | `--- 1=video subsystem attached to monochrome | | | | | `---- reserved | | | | `----- 1=video subsystem is inactive | | | `------ reserved | `--------- video RAM 00-64K 10-192K 01-128K 11-256K `---------- video mode number passed to INT 10, function 0 40:88 byte PCjr: third keyboard status byte EGA feature bit switches, emulated on VGA |7|6|5|4|3|2|1|0| EGA feature bit switches (EGA+) | | | | | | | `-- EGA SW1 config (1=off) | | | | | | `--- EGA SW2 config (1=off) | | | | | `---- EGA SW3 config (1=off) | | | | `----- EGA SW4 config (1=off) | | | `------ Input FEAT0 (ISR0 bit 5) after output on FCR0 | | `------- Input FEAT0 (ISR0 bit 6) after output on FCR0 | `-------- Input FEAT1 (ISR0 bit 5) after output on FCR1 `--------- Input FEAT1 (ISR0 bit 6) after output on FCR1 40:89 byte Video display data area (MCGA and VGA) |7|6|5|4|3|2|1|0| Video display data area (MCGA and VGA) | | | | | | | `-- 1=VGA is active | | | | | | `--- 1=gray scale is enabled | | | | | `---- 1=using monochrome monitor | | | | `----- 1=default palette loading is disabled | | | `------ see table below | | `------- reserved | `-------- 1=display switching enabled `--------- alphanumeric scan lines (see table below) Bit7 Bit4 Scan Lines 0 0 350 line mode 0 1 400 line mode 1 0 200 line mode 1 1 reserved 40:8A byte Display Combination Code (DCC) table index (EGA+) 40:8B byte Last diskette data rate selected |7|6|5|4|3|2|1|0| last diskette data rate selected | | | | `--------- reserved | | `------------ last floppy drive step rate selected `-------------- last floppy data rate selected Data Rate Step Rate 00 500K bps 00 step rate time of 0C 01 300K bps 01 step rate time of 0D 10 250K bps 10 step rate time of 0A 11 reserved 11 reserved 40:8C byte Hard disk status returned by controller 40:8D byte Hard disk error returned by controller 40:8E byte Hard disk interrupt control flag(bit 7=working int) 40:8F byte Combination hard/floppy disk card when bit 0 set 40:90 4 bytes Drive 0,1,2,3 media state |7|6|5|4|3|2|1|0| drive media state (4 copies) | | | | | `------- drive/media state (see below) | | | | `------- reserved | | | `------- 1=media/drive established | | `------- double stepping required `--------- data rate: 00=500K bps 01=300K bps 10=250K bps 11=reserved Bits 210 Drive Media State 000 360Kb diskette/360Kb drive not established 001 360Kb diskette/1.2Mb drive not established 010 1.2Mb diskette/1.2Mb drive not established 011 360Kb diskette/360Kb drive established 100 360Kb diskette/1.2Mb drive established 101 1.2Mb diskette/1.2Mb drive established 110 Reserved 111 None of the above 40:94 byte Track currently seeked to on drive 0 40:95 byte Track currently seeked to on drive 1 40:96 byte Keyboard mode/type |7|6|5|4|3|2|1|0| Keyboard mode/type | | | | | | | `--- last code was the E1 hidden code | | | | | | `---- last code was the E0 hidden code | | | | | `----- right CTRL key depressed | | | | `------ right ALT key depressed | | | `------- 101/102 enhanced keyboard installed | | `-------- force num-lock if Rd ID & KBX | `--------- last char was first ID char `---------- read ID in process 40:97 byte Keyboard LED flags |7|6|5|4|3|2|1|0| Keyboard LED flags | | | | | | | `--- scroll lock indicator | | | | | | `---- num-lock indicator | | | | | `----- caps-lock indicator | | | | `------ circus system indicator | | | `------- ACK received | | `-------- re-send received flag | `--------- mode indicator update `---------- keyboard transmit error flag 40:98 dword Pointer to user wait complete flag 40:9C dword User wait Time-Out value in microseconds 40:A0 byte RTC wait function flag |7|6|5|4|3|2|1|0| INT 15,86 RTC wait function flag | | | | | | | `--- 1= wait pending | `-------------- not used `--------------- 1=INT 15,86 wait time elapsed 40:A1 byte LANA DMA channel flags 40:A2 2 bytes Status of LANA 0,1 40:A4 dword Saved hard disk interrupt vector 40:A8 dword BIOS Video Save/Override Pointer Table address (see VIDEO TABLES) 40:AC 8 bytes Reserved 40:B4 byte Keyboard NMI control flags (convertible) 40:B5 dword Keyboard break pending flags (convertible) 40:B9 byte Port 60 single byte queue (convertible) 40:BA byte Scan code of last key (convertible) 40:BB byte NMI buffer head pointer (convertible) 40:BC byte NMI buffer tail pointer (convertible) 40:BD 16bytes NMI scan code buffer (convertible) 40:CE word Day counter (convertible and after) 40:F0 16bytes Intra-Applications Communications Area (IBM Technical Reference incorrectly locates this at 50:F0-50:FF) Address Size Description (BIOS/DOS Data Area) 50:00 byte Print screen status byte 00 = PrtSc not active, 01 = PrtSc in progress FF = error 50:01 3 bytes Used by BASIC 50:04 byte DOS single diskette mode flag, 0=A:, 1=B: 50:05 10bytes POST work area 50:0F byte BASIC shell flag; set to 2 if current shell 50:10 word BASICs default DS value (DEF SEG) 50:12 dword Pointer to BASIC INT 1C interrupt handler 50:16 dword Pointer to BASIC INT 23 interrupt handler 50:1A dword Pointer to BASIC INT 24 disk error handler 50:20 word DOS dynamic storage 50:22 14bytes DOS diskette initialization table (INT 1E) 50:30 4bytes MODE command 70:00 I/O drivers from IO.SYS/IBMBIO.COM
The following map varies in size and locus
07C0:0 Boot code is loaded here at startup (31k mark) A000:0 EGA/VGA RAM for graphics display mode 0Dh & above B000:0 MDA RAM, Hercules graphics display RAM B800:0 CGA display RAM C000:0 EGA/VGA BIOS ROM (thru C7FF) C400:0 Video adapter ROM space C600:0 256bytes PGA communication area C800:0 16K Hard disk adapter BIOS ROM C800:5 XT Hard disk ROM format, AH=Drive, AL=Interleave D000:0 32K Cluster adapter BIOS ROM D800:0 PCjr conventionalsoftware cartridge address E000:0 64K Expansion ROM space (hardwired on AT+) 128K PS/2 System ROM (thru F000) F000:0 System monitor ROM PCjr: software cartridge override address F400:0 System expansion ROMs F600:0 IBM ROM BASIC (AT) F800:0 PCjr software cartridge override address FC00:0 BIOS ROM FF00:0 System ROM FFA6:E ROM graphics character table FFFF:0 ROM bootstrap code FFFF:5 8 bytes ROM date (not applicable for all clones) FFFF:E byte ROM machine id (see MACHINE ID) Ref: http://staff.ustc.edu.cn/~xyfeng/research/cos/resources/BIOS/Resources/assembly/biosdataarea.html
PIC–ing
Ref: www.jamesmolloy.co.uk https://wiki.osdev.org/8259_PIC / http://www.brokenthorn.com/Resources/OSDevPic.html

The IBM PC 8259 PIC Architecture
In the beginning (IBM PC and XT), only a single 8259 PIC chip was used, which provided 8 IRQs to the system. These were traditionally mapped by the BIOS to interrupts 8 to 15 (0x08 to 0x0F). It is unlikely that any of these single-PIC machines will be encountered these days.
The IBM PC/AT 8259 PIC Architecture
The IBM PC/AT extended the PC architecture by adding a second 8259 PIC chip. This was possible due to the 8259A’s ability to cascade interrupts, that is, have them flow through one chip and into another. This gives a total of 15 interrupts. Why 15 and not 16? That’s because when you cascade chips, the PIC needs to use one of the interrupt lines to signal the other chip.
The low-level concepts behind external interrupts are not very complex. All devices that are interrupt-capable have a line connecting them to the PIC (programmable interrupt controller). The PIC is the only device that is directly connected to the CPU’s interrupt pin. It is used as a multiplexer, and has the ability to prioritize between interrupting devices. It is, essentially, a glorified 8-1 multiplexer. At some point, someone somewhere realized that 8 IRQ lines just wasn’t enough, and they daisy-chained another 8-1 PIC beside the original. So in all modern PCs, you have 2 PICs, the master and the slave, serving a total of 15 interruptable devices (one line is used to signal the slave PIC).
Only hardware interrupts are handled through the Programmable Interrupt Controller. The special, CPU-dedicated interrupts are shown below.
0 – Division by zero exception
1 – Debug exception
2 – Non maskable interrupt
3 – Break-point exception
4 – ‘Into detected overflow’
5 – Out of bounds exception
6 – Invalid opcode exception
7 – No co-processor exception
8 – Double fault (pushes an error code)
9 – Co-processor segment overrun
10 – Bad TSS (pushes an error code)
11 – Segment not present (pushes an error code)
12 – Stack fault (pushes an error code)
13 – General protection fault (pushes an error code)
14 – Page fault (pushes an error code)
15 – Unknown interrupt exception
16 – Co-processor fault
17 – Alignment check exception
18 – Machine check exception
19-31 – Reserved
See: Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3 (3A, 3B, 3C & 3D): System Programming Guide – 6.3.1 External Interrupts
WR Pin: This pin connects to a write strobe signal (One of 8 on a Pentium)
RD Pin: This connects to the IOCR (Input Output Control Routine) signal.
INT Pin: Connects to the INTR pin on the microprocessor.
INTA Pin: Connects to the INTA pin on the microprocessor.
A0 Pin: Selects different Command WORDS
CS Pin: Enables the chip for programming and control.
SP/EN Pin: Slave program (SP) / Enable Buffer (EN).
Slave Program (1=Master, 0=Slave)
Enable Buffer (Controls data bus transievers when in buffered mode)
CAS0, CAS1, CAS2 Pins: Used to output from master to slave PIC controllers in cascaded systems.
D0 – D7 Pins: 8 bit Data connector pins.
x86 Hardware Interrupts | ||
---|---|---|
8259A Input pin | Interrupt Number | Description |
IRQ0 | 0x08 | Timer |
IRQ1 | 0x09 | Keyboard |
IRQ2 | 0x0A | Cascade for 8259A Slave controller |
IRQ3 | 0x0B | Serial port 2 |
IRQ4 | 0x0C | Serial port 1 |
IRQ5 | 0x0D | AT systems: Parallel Port 2. PS/2 systems: reserved |
IRQ6 | 0x0E | Diskette drive |
IRQ7 | 0x0F | Parallel Port 1 |
IRQ8/IRQ0 | 0x70 | CMOS Real time clock |
IRQ9/IRQ1 | 0x71 | CGA vertical retrace |
IRQ10/IRQ2 | 0x72 | Reserved |
IRQ11/IRQ3 | 0x73 | Reserved |
IRQ12/IRQ4 | 0x74 | AT systems: reserved. PS/2: auxiliary device |
IRQ13/IRQ5 | 0x75 | FPU |
IRQ14/IRQ6 | 0x76 | Hard disk controller |
IRQ15/IRQ7 | 0x77 | Reserved |
8259A Registers
- Command Register – This is a write only register that is used to send commands to the microcontroller.
- Status register – This is a read only register that can be accessed to determin the status of the PIC.
- Interrupt Request Register (IRR) – This register specifies which interrupts are pending acknowledgment. Note: This register is internal, and cannot be accessed directly.
- In-Sevice Register (ISR) – This register specifies which interrupts have already been acknowledged, but are awaiting for the End of Interrupt (EOI) signal.
- Interrupt Mask Register (IMR) – This specifies what interrupts are to be ignored, and not acknowledged.
Programming with the 8259 PIC
When the computer boots, the default interrupt mappings are:
IRQ 0..7 – INT 0x8..0xF
IRQ 8..15 – INT 0x70..0x77
Each chip (master and slave) has a command port and a data port (given in the table below). When no command is issued, the data port allows us to access the interrupt mask of the 8259 PIC.
Chip – Purpose | I/O port |
Master PIC -Command | 0x0020 |
Master PIC – Data | 0x0021 |
Slave PIC -Command | 0x00A0 |
Slave PIC – Data | 0x00A1 |
WatDOS Pages
Every few months over a long time I will sit down and work on what I call WatDOS. In general, it is a set of files I compile to boot a system, now just a VM, to explore various results. I call it WatDOS because I use the Open Watcom compiler and it is meant to be a DOS, pretty straight forward. My build and test environment is explained here.
Boot Sector
The boot sector is compiled and inserted on to the test hard drive image. My manual BPB compiled with UASM and use dd to manually install on my 50 Meg DOS vdi. I cannot remember why I used UASM instead of wasm or jwasm, but maybe it will come to me.
Overall Purpose
Only does LBA reads – int 13 0x41 check error if no extensions. Assumes a modern system has more than 1 meg RAM so no TOM check. The basic function this BPB:
- Loads watload.bin to 0xC000
- Overlay BIOS parameter block (BPB) data to watload.bin
- Far jump to watload.bin
This is the big start and the following is a memory map I threw together to help track of what memory areas I was using. The example is a simple old 32bit system boot which starts in real mode (16bit). The following represents the boot block found by the system as the machine initializes and loaded to 0x0000:0x7C00. Once the BIOS initializes, it jumps to 0x0000:0x7C00 and it is all you at that point, no safety nets.
; --------
; | |
; |watload | at 0xC000 (0x800 bytes)
; +--------+
; | |
; | Buffer | Use start 0x7E00 for Root Dir and FAT load buffer
; |--------| 0000:7E00 (0:BP+200)
; |BOOT SEC| contains BPB
; |ORIGIN |
; |--------| 0000:7C00 (0:BP)
; |VARS | only known is 1st data sector (start of cluster 2)
; |--------| 0000:7BFC (DS:[BP-4])
; |STACK | minimal 256 bytes (1/2 sector)
; |- - - - |
; |KERNEL | kernel loaded here (max 58 sectors, 29KB)
; |LOADED | also used as FAT buffer
; |--------| 0070:0000 (0:0700)
; |KERNEL |
; |LOADED |
; |--------| 0000:0600
; |DOS DA | DOS Data Area,
; | | Cluster list temp from FAT read
; |********| 0000:0500
; |BDA | BIOS Data Area
; +--------+ 0000:0400
; |IVT | Interrupt Vector Table
; +--------+ 0000:0000
It’s All You
This is the most rewarding thing about learning the low level stuff, it is all you from this point. When it works, there is nothing like. When it doesn’t work, there is nothing like it. In the next snippet, I set up some address for use and add daptable.inc. Note the nop to keep the short jmp.
;
.8086 ; enable assembler warnings to limit instruction set
include daptable.inc
BASE equ 0x7C00 ; boot sector originally at 0x0:BASE
BUFFER_SEG equ 0x07E0
BUFFER_OFF equ 0x0000 ; buffer at 0x7E00 for Root and FAT load
BIOS_SEG equ 0x0C00 ; watload.bin load at 0xC000
CLUSTERLIST equ 0x0500 ; location to store bios cluster list
;-----------------------------------------------------------------------
; Entry point after MBR hand off. Stock MBR is located at 0000:0x0600
_TEXT segment use16 'code'
org BASE
BS_jmpBoot:
jmp short start
nop
BIOS parameter block (BPB)
So, this is the BPB and some form of BPB, for whatever filesystem in use, will be written when the media is formatted. In my case, I made my test drive image, FAT, and pulled the BPB for use in my boot sector.
BPB_Start:
BS_OEMName db 'IBM 7.0' ; OEM label
BPB_BytsPerSec dw 0x200 ; Number of bytes per sector (512) Must be one of 512, 1024, 2048, 4096.
BPB_SecPerClus db 0x4 ; Number of sectors per cluster Must be one of 1, 2, 4, 8, 16, 32, 64, 128.
BPB_RsvdSecCnt dw 0x1 ; reserved sectors, in 12/16 usually 1 for BPB, FAT32 uses 32
BPB_NumFATs db 0x2 ; number of FATs,
BPB_RootEntCnt dw 0x200 ; root directory entries, 0 for FAT32. 512 is recommended for FAT16.
BPB_TotSec16 dw 0x0 ; 16-bit total count of sectors on the volume, if 0 see BPB_TotSec32
BPB_Media db 0xF8 ; is no longer usually used, F8 HD FA Ram Disk
BPB_FATSz16 dw 0x64 ; sectors per 1 FAT copy
BPB_SecPerTrk dw 0x3F ; sectors per track
BPB_NumHeads dw 0x10 ; number of heads
BPB_HiddSec dd 0x3F ; hidden sectors
BPB_TotSec32 dd 0x18D71 ; big total sectors BPB_TotSec32 * BPB_BytsPerSec = HD size
BS_DrvNum db 0x80 ; boot unit
BS_Reserved1 db 0 ; Reserved (used by Windows NT). FAT always 0
BS_BootSig db 0x29 ; 0x29 indicates next 3 fields in the boot sector present
BS_VolID dd 0x30E1671C ; volume serial number
BS_VolLab db 'PCDOS_DEV '; volume label
BS_FilSysType db 'FAT16 ' ; filesystem id
fat_start dd ? ; first FAT sector
data_start dd ? ; first Data sector
bios_cluster dw ? ; bios cluster from root dir
Disk Address Packet (DAP)
The DAP is used for LBA reads and was included early. This link: daptable.inc shows the DAP with comments.
; DAP : Disk Address Packet
DAP DAP_TABLE <0x10,0x0,0x0,0x7E00,0x0,0x0,0x0>
BPB Code
This is where the previous short jump leads us to, the start of the code.
********************************************************************
; * Start of BPB code
; ********************************************************************
start:
cli
cld
mov BS_DrvNum, dl ; save BIOS drive number
xor ax, ax ; segment registers 0x0000
mov ds, ax
mov es, ax
mov ss, ax
mov bp, BASE ; setup stack
mov sp, BASE
sti
; Volume Structure:
; | | | |
; BPB | FAT | FAT | Root Dir | Data
; | | Copy | |
; | | | |
; fat_start dir_start data_start
; fat_start = BPB_HiddSec + BPB_RsvdSecCnt
mov si, word ptr BPB_HiddSec
mov di, word ptr BPB_HiddSec+2
add si, word ptr BPB_RsvdSecCnt
adc word ptr fat_start+2, di ; DI:SI = first FAT sector
mov word ptr fat_start, si
; dir_start = (BPB_NumFATs * BPB_FATSz16) + fat_start
mov al, BPB_NumFATs
cbw
mul BPB_FATSz16 ; DX:AX = total number of FAT sectors
add si, ax
adc di, dx ; DI:SI = first root directory sector
mov word ptr DAP.DAP_sector_low, si
mov word ptr DAP.DAP_sector_low+2, di ; root dir start in DAP will be
; first read in
; RootDirSectors = (BPB_RootEntCnt * 32) / BPB_BytsPerSec;
mov ax, 32
xor dx, dx
mul word ptr BPB_RootEntCnt
div word ptr BPB_BytsPerSec ; Divide (dx:ax,sectsize) to (ax,dx)
mov DAP.DAP_num_sectors, ax ; number of root dir sectors to DAP
; where the data starts
add si, ax
mov word ptr data_start, si
adc word ptr data_start+2, di ; DI:SI = first data sector
; First, read the root directory into buffer.
; IBM/MS INT 13 Extensions - INSTALLATION CHECK
; will not work without LBA extensions
mov ah,041h
mov bx,055aah
mov dl, [BS_DrvNum] ; BIOS drive, 0=A:, 80=C:
int 0x13
jnc root_read
mov ax, 0x0E31
jmp print_error ; Error 1 - No bios extensions
; yep, damn - int 13 0x42 is supported, read in full root dir at
; 0x7E00 - big read
root_read:
call readdrive ; read in root directory
jnc get_biosname
mov ax, 0x0E32
jmp print_error ; Error 2 - Root directory read
; read through root dir directory for watload.bin
get_biosname:
lea si, filename ;Starting address of first buffer
lea di, buffer ;Starting address of first buffer
push si
push di
mov ax, BPB_RootEntCnt ;Count FAT 16 directory entries max
next_dir:
mov cx, 0x0B ;Scanning 11 bytes (CX is used by REPE)
repe cmpsb ; ...and compare it.
je bios_found
pop di
add di, 0x20
pop si
push si
push di
cmp byte ptr [es:di], 0x00
jz no_bios ; ax counts max dir entries, but if first
dec ax ; char di is zero, no more dir entries
jne next_dir ; so bail early
; get here if entry not found 0x00, first free entry or for some
; reason you read through all entries not found or 0xE5 and
; ax counts down to zero
no_bios:
mov ax, 0x0E33
jmp print_error ; Error 3 - watload.bin
bios_found:
pop di ; offset of found dir entry
pop si ; empty stack
; watload directory entry found, pull cluster, store in data
mov ax, [di+0x1A] ; first cluster
mov [bios_cluster], ax
; reuse DAP structure - setup DAP for FAT read. Read in full FAT
; at 0x7E00
lea bx, buffer
mov ax, [BPB_FATSz16]
mov DAP.DAP_num_sectors, ax
mov ax, word ptr [fat_start]
mov dx, word ptr [fat_start+2]
mov word ptr DAP.DAP_sector_low, ax
mov word ptr DAP.DAP_sector_low+2, dx
call readdrive ; read FAT into memory at 0x7E00
jnc read_fat
mov ax, 0x0E35
jmp print_error ; Error 5 - FAT read error
read_fat:
; set up ds:di to the FAT buffer
mov si, BUFFER_OFF
mov ax, BUFFER_SEG
mov ds, ax
mov ax, [ds:si]
sub ax, 0xFFF8
je good_fat_table ; first word should be 0xFFF8 or
mov ax, 0x0E36
jmp print_error ; Error 6 - FAT table bad
; At this point, the entire FAT is loaded at 0x7E00 and ds:si are set
; to that seg:off. Assume es - 0, set es:di 0000:0500
good_fat_table:
lea di, [CLUSTERLIST]
mov ax, bios_cluster ; cluster number from root dir
next_clust:
stosw ; store cluster number, inc next after store
mov si, ax
add si, si ; cluster * 2
mov ax, [ds:si]
; In some docs the end-of-clusterchain marker is listed as FFF8, but
; in others FFFF (FAT16). From what I have seen FFFF is the correct
; marker and FFF8 is only at cluster 1 position.
cmp ax, 0xFFFF
jne next_clust
xor ax, ax ; mark end of temp cluster list 0x0000
stosw
; set ds back to 0x0000 so my next int 13 0x42 reads the DAP at the
; correct address
mov ds, ax
; **************************************************************
; **** Note: buffer at 0x7E00 no longer needed ****
; **************************************************************
; The issue is that the stock IBMBIOS loads at 0x0700 and a full
; load will over write
mov DAP.DAP_buffer_off, ax ; load to DAP off -- 0x0000
mov ax, BIOS_SEG ; load segment
mov DAP.DAP_buffer_seg, ax ; load to DAP seg -- 0x0D00
mov al, [BPB_SecPerClus] ; each read will be size of cluster
cbw
mov DAP.DAP_num_sectors, ax ; load in DAP
; LBA_sector = ((cluster_number - 2) * BPB_SecPerClus) + data_start
; Start read at LBA_sector for BPB_SecPerClus
; then read next cluster number and repeat until loaded
lea di, [CLUSTERLIST]
mov ax, word ptr [data_start] ; low word data_start
mov word ptr DAP.DAP_sector_low, ax
mov ax, word ptr [data_start+2] ; high word data_start
mov word ptr DAP.DAP_sector_low+2, ax
xor dx, dx
mov ax, [es:di]
sub ax, 0x0002 ; subtract 2, comp for FAT position
mul [BPB_SecPerClus] ; dx:ax
add word ptr [DAP.DAP_sector_low], ax
add word ptr [DAP.DAP_sector_low+2], dx
call readdrive
mov cx, word ptr [fat_start]
mov dx, word ptr [fat_start+2]
mov si, word ptr data_start
mov di, word ptr data_start+2
; far jump to the start of watload.bin
jmp far ptr loader_start
; Print string pointed to by DS:SI using
; BIOS TTY output via int 10h/AH=0eh
print_error:
int 0x10
jmp $
readdrive:
mov ah, 0x42
mov dl, BS_DrvNum
lea si, DAP
int 0x13
ret
filename db "WATLOAD BIN"
; Fill free space between code/data and signature with zero
db ((0x200 - 2) - ($ - BS_jmpBoot)) dup(0)
; True, the MBR checks at 0x7DFE after BPB loaded for signature before
; jumping to start. -MKG
signature dw 0xAA55
; This will be 0x7E00 - where FAT16 RootDir will load and reuse for
; FAT read - for FAT I set ds:di == 0x7E00
buffer:
_TEXT ends
loader segment use16 'code' at 0x0000
org 0xC000
loader_start label near
loader ends
WatBIOS
WatBIOS is loaded by watload and the entry point is at 0x10000.
The watinit.asm file contains the entry point and segments are defined in segments.inc and segment order is defined in the link file. The BIOSinitEntry segment must be the first segment due to containing the entry point code _cstart_. Entry is via a far jump from watinit.asm switching from real mode to protected mode. On entry, the interrupts must be disabled and stay disabled.
The entry code first sets up the protected mode descriptors and the stack. The watload the location of real mode data in di/si:
typedef struct realmode_data { uint16_t IDTAddr; // IDT location uint16_t GDTAddr; // GDT location uint16_t MEMMapAddr; // BIOS memmap location uint16_t INT11RET; // Return from INT 0x11 uint16_t INT15C0Size; // INT15 C0 number of bytes following uint8_t INT15C0Data[0x0F]; // Actual returned data from INT15 C0 uint8_t VESAMajor; // VESA version major uint8_t VESAMinor; // VESA version minor uint16_t VESATotalMem; // VESA memory chunks } realmode_data;
Note: Because of the above rmdata.inc and rndata.h must be kept synchronized.
This data is stored and registers are cleared as is normal for a known starting value. Next, a jump into _TEXT is executed. As a default, and probably will not change, the IDT will start at 0x00. Now move the IVT to an out of the way location at 0x90000 and clear the old IVT area with 0x00. This module also sets BSS and stack parameters.
Finally, an IODelay is calculated calling FindIODelay. I am not sure if I need the IODelay, but at this point I have it. The code for IODelay is located in iodelay.asm and timer.inc which was pulled in from an old loader project. The defines used do not match those in other C-headers. This probably needs to be fixed in the future. A jump to the start of C-code in ldrinit.c is executed.
Execution picks up in ldrinit.c as C-code at main(). At this point ldrinit.c is used for calling functions and printing debug information. The module defines the global bootparams structure to store all system information as it is processed. There are two calls:
init_pics( ) detect_memory( )
Initial Memory Map
The initial probe for memory is performed in watload and stored at a default address of 0000:0900. This address is passed to watbios as a hand-off check and in case the location changes, symbol _MMPADDR. Three probes are performed, following an older Linux method, INT 0x15 88, e801, and e802.
BIOS Function: INT 0x15, AH = 0x88
This function may limit itself to reporting 15M (for legacy reasons) even if BIOS detects more memory than that. It may also report up to 64M. It only reports contiguous (usable) RAM. Stored in a 16bit (2 byte) value at _MMPADDR + 0. In watbios, value is retrieved to meminfo.ext_mem_k representing number of contiguous KB of usable RAM starting at 0x00100000. If contains signature then there was an error.
BIOS Function: INT 0x15, AX = 0xE801
BIOS Function: INT 0x15, EAX = 0xE820
Each e820 entry is 24 bytes stored sequentially, starting at _MMPADDR + 0x0F with the format:
- 8 bytes – memory chunk start;
- 8 bytes – memory chunk length;
- 4 bytes – memory type
- 4 bytes – four NOPs 0x90 as separator