From dfd5ca4a0f34f9346ff2decbd3cfc8e3f6fccc04 Mon Sep 17 00:00:00 2001
From: Jeremy Soller <jackpot51@gmail.com>
Date: Tue, 6 Sep 2022 15:51:30 -0600
Subject: [PATCH] Use CHS to support CD boot

---
 asm/x86-unknown-none/stage1.asm | 90 ++++++++++++++++++++++++++++++---
 src/os/bios/disk.rs             | 85 +++++++++++++++++++++++++------
 2 files changed, 153 insertions(+), 22 deletions(-)

diff --git a/asm/x86-unknown-none/stage1.asm b/asm/x86-unknown-none/stage1.asm
index 26740c9..d2175e1 100644
--- a/asm/x86-unknown-none/stage1.asm
+++ b/asm/x86-unknown-none/stage1.asm
@@ -22,18 +22,42 @@ stage1: ; dl comes with disk
     ; save disk number
     mov [disk], dl
 
-    mov si, name
+    mov si, stage_msg
     call print
+    mov al, '1'
+    call print_char
     call print_line
 
+    ; read CHS gemotry
+    ;  CL (bits 0-5) = maximum sector number
+    ;  CL (bits 6-7) = high bits of max cylinder number
+    ;  CH = low bits of maximum cylinder number
+    ;  DH = maximum head number
+    mov ah, 0x08
+    mov dl, [disk]
+    xor di, di
+    int 0x13
+    jc error ; carry flag set on error
+    mov bl, ch
+    mov bh, cl
+    shr bh, 6
+    mov [chs.c], bx
+    shr dx, 8
+    inc dx ; returns heads - 1
+    mov [chs.h], dx
+    and cl, 0x3f
+    mov [chs.s], cl
+
     mov eax, (stage2 - stage1) / 512
     mov bx, stage2
     mov cx, (stage3.end - stage2) / 512
     mov dx, 0
     call load
 
-    mov si, finished
+    mov si, stage_msg
     call print
+    mov al, '2'
+    call print_char
     call print_line
 
     jmp stage2.entry
@@ -70,11 +94,56 @@ load:
 
     call print_dapack
 
+    cmp byte [chs.s], 0
+    jne .chs
+    ;INT 0x13 extended read does not work on CDROM!
     mov dl, [disk]
     mov si, DAPACK
     mov ah, 0x42
     int 0x13
-    jc error
+    jc error ; carry flag set on error
+    ret
+
+.chs:
+    ; calculate CHS
+    xor edx, edx
+    mov eax, [DAPACK.addr]
+    div dword [chs.s] ; divide by sectors
+    mov ecx, edx ; move sector remainder to ecx
+    xor edx, edx
+    div dword [chs.h] ; divide by heads
+    ; eax has cylinders, edx has heads, ecx has sectors
+
+    ; Sector cannot be greater than 63
+    inc ecx ; Sector is base 1
+    cmp ecx, 63
+    ja error_chs
+
+    ; Head cannot be greater than 255
+    cmp edx, 255
+    ja error_chs
+
+    ; Cylinder cannot be greater than 1023
+    cmp eax, 1023
+    ja error_chs
+
+    ; Move CHS values to parameters
+    mov ch, al
+    shl ah, 6
+    and cl, 0x3f
+    or cl, ah
+    shl dx, 8
+
+    ; read from disk using CHS
+    mov al, [DAPACK.count]
+    mov ah, 0x02 ; disk read (CHS)
+    mov bx, [DAPACK.buf]
+    mov dl, [disk]
+    push es ; save ES
+    mov es, [DAPACK.seg]
+    int 0x13
+    pop es ; restore EC
+    jc error ; carry flag set on error
     ret
 
 print_dapack:
@@ -106,6 +175,9 @@ print_dapack:
 
     ret
 
+error_chs:
+    mov ah, 0
+
 error:
     call print_line
 
@@ -116,7 +188,7 @@ error:
     mov al, ' '
     call print_char
 
-    mov si, errored
+    mov si, error_msg
     call print
     call print_line
 .halt:
@@ -126,12 +198,16 @@ error:
 
 %include "print.asm"
 
-name: db "Redox Loader - Stage One",0
-errored: db "Could not read disk",0
-finished: db "Redox Loader - Stage Two",0
+stage_msg: db "Stage ",0
+error_msg: db "ERROR",0
 
 disk: db 0
 
+chs:
+.c: dd 0
+.h: dd 0
+.s: dd 0
+
 DAPACK:
         db 0x10
         db 0
diff --git a/src/os/bios/disk.rs b/src/os/bios/disk.rs
index 4f864f8..c24f48a 100644
--- a/src/os/bios/disk.rs
+++ b/src/os/bios/disk.rs
@@ -42,11 +42,33 @@ impl DiskAddressPacket {
 pub struct DiskBios {
     boot_disk: u8,
     thunk13: extern "C" fn(),
+    chs_opt: Option<(u32, u32, u32)>,
 }
 
 impl DiskBios {
     pub fn new(boot_disk: u8, thunk13: extern "C" fn()) -> Self {
-        Self { boot_disk, thunk13 }
+        let chs_opt = unsafe {
+            let mut data = ThunkData::new();
+            data.eax = 0x0800;
+            data.edx = boot_disk as u32;
+            data.edi = 0;
+
+            data.with(thunk13);
+
+            let c =
+                (data.ecx >> 8) & 0xFF |
+                ((data.ecx >> 6) & 0x3) << 8;
+            let h = ((data.edx >> 8) & 0xFF) + 1;
+            let s = data.ecx & 0x3F;
+            
+            Some((c, h, s))
+        };
+
+        Self {
+            boot_disk,
+            thunk13,
+            chs_opt,
+        }
     }
 }
 
@@ -69,21 +91,54 @@ impl Disk for DiskBios {
                 block + i as u64 * MAX_BLOCKS,
                 chunk.len() as u64 / BLOCK_SIZE
             );
-            ptr::write(DISK_ADDRESS_PACKET_ADDR as *mut DiskAddressPacket, dap);
-
-            let mut data = ThunkData::new();
-            data.eax = 0x4200;
-            data.edx = self.boot_disk as u32;
-            data.esi = DISK_ADDRESS_PACKET_ADDR as u32;
 
-            data.with(self.thunk13);
-
-            //TODO: return result on error
-            let ah = ({ data.eax } >> 8) & 0xFF;
-            assert_eq!(ah, 0);
-
-            //TODO: check blocks transferred
-            dap = ptr::read(DISK_ADDRESS_PACKET_ADDR as *mut DiskAddressPacket);
+            if let Some((c_max, h_max, s_max)) = self.chs_opt {
+                let s = (dap.address % s_max as u64) + 1;
+                assert!(s <= 63);
+
+                let tmp = dap.address / s_max as u64;
+                let h = tmp % h_max as u64;
+                assert!(h <= 255);
+
+                let c = tmp / h_max as u64;
+                assert!(c <= 1023);
+
+                let mut data = ThunkData::new();
+                data.eax =
+                    0x0200 |
+                    (dap.sectors as u32);
+                data.ebx = dap.buffer as u32;
+                data.ecx =
+                    (s as u32) |
+                    (((c as u32) & 0xFF) << 8) |
+                    ((((c as u32) >> 8) & 0x3) << 6);
+                data.edx =
+                    (self.boot_disk as u32) |
+                    ((h as u32) << 8);
+                data.es = dap.segment;
+
+                data.with(self.thunk13);
+                
+                //TODO: return result on error
+                let ah = ({ data.eax } >> 8) & 0xFF;
+                assert_eq!(ah, 0);
+            } else {
+                ptr::write(DISK_ADDRESS_PACKET_ADDR as *mut DiskAddressPacket, dap);
+
+                let mut data = ThunkData::new();
+                data.eax = 0x4200;
+                data.edx = self.boot_disk as u32;
+                data.esi = DISK_ADDRESS_PACKET_ADDR as u32;
+
+                data.with(self.thunk13);
+
+                //TODO: return result on error
+                let ah = ({ data.eax } >> 8) & 0xFF;
+                assert_eq!(ah, 0);
+
+                //TODO: check blocks transferred
+                dap = ptr::read(DISK_ADDRESS_PACKET_ADDR as *mut DiskAddressPacket);
+            }
 
             ptr::copy(DISK_BIOS_ADDR as *const u8, chunk.as_mut_ptr(), chunk.len());
         }
-- 
GitLab