Tag Archives: watdos

Disk Address Packet (DAP)

;***********************************************************************
; *
; *  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


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 pinInterrupt NumberDescription
IRQ00x08Timer
IRQ10x09Keyboard
IRQ20x0ACascade for 8259A Slave controller
IRQ30x0BSerial port 2
IRQ40x0CSerial port 1
IRQ50x0DAT systems: Parallel Port 2. PS/2 systems: reserved
IRQ60x0EDiskette drive
IRQ70x0FParallel Port 1
IRQ8/IRQ00x70CMOS Real time clock
IRQ9/IRQ10x71CGA vertical retrace
IRQ10/IRQ20x72Reserved
IRQ11/IRQ30x73Reserved
IRQ12/IRQ40x74AT systems: reserved. PS/2: auxiliary device
IRQ13/IRQ50x75FPU
IRQ14/IRQ60x76Hard disk controller
IRQ15/IRQ70x77Reserved

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 -Command0x0020
Master PIC – Data0x0021
Slave PIC -Command0x00A0
Slave PIC – Data0x00A1

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:

  1. Loads watload.bin to 0xC000
  2. Overlay BIOS parameter block (BPB) data to watload.bin
  3. 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