The boot loader now works (probably).
authorDavid Given <dg@cowlark.com>
Sat, 9 Dec 2017 22:50:17 +0000 (23:50 +0100)
committerDavid Given <dg@cowlark.com>
Sat, 9 Dec 2017 22:50:17 +0000 (23:50 +0100)
Kernel/platform-nc200/Makefile
Kernel/platform-nc200/autoprg.s
Kernel/platform-nc200/bootblock.s

index b178d2b..f0b91c4 100644 (file)
@@ -3,8 +3,9 @@ bootfloppy.img: bootblock.img autoprg.bin
        cp bootblock.img bootfloppy.img
        truncate bootfloppy.img --size 7680
        mcopy -i bootfloppy.img autoprg.bin ::auto.prg
-       mcopy -i bootfloppy.img big ::bank80.img
-       mcopy -i bootfloppy.img big ::bank81.img
+       #mcopy -i bootfloppy.img kernel1 ::load4000.80
+       #mcopy -i bootfloppy.img kernel2 ::load4000.81
+       #echo -n | mcopy -i bootfloppy.img - ::call4000.80
 
 bootblock.img: bootblock.s
        sdasz80 -fflopzws bootblock.rel bootblock.s
index 4637848..5e9d0ff 100644 (file)
@@ -1,5 +1,18 @@
 .globl entry
 
+; This is a simple bootstrap loader which uses the Amstrad's built-in software to
+; load files off disk. It first searches for LOAD????.?? files, where the first
+; field is a hex address and the second is a bank ID, and loads them all. Then it
+; searches for a CALL????.?? file, pages in the bank, and calls that address.
+; The paged in bank is always at 0x4000. Typically you'll want something like:
+;
+;   LOAD4000.80
+;   LOAD4000.81
+;   CALL4000.80
+;
+; If there are multiple CALL????.?? files, it will pick one at random and ignore
+; the rest.
+
 ; Amstrad kernel entrypoints
 
 kmreadchar = 0xb9b3
@@ -7,7 +20,10 @@ textout = 0xb81e
 txtclearwindow = 0xb824
 txtoutput = 0xb833
 
-; Ranger FDD entrypoints
+; Ranger FDD entrypoints. I have no idea whether these are consistent between ROM
+; versions, if there are different ROM versions, but I've found at least two
+; machines with these values. Unfortunately the Ranger software doesn't expose
+; things like read_sector via a system call.
 
 r_finish = 0xf34b
 r_set_dta = 0xf391
@@ -30,7 +46,7 @@ update_watchdog_timer = 0xcc78
     ; 0x8000-0xbfff: Kernel workspace
     ; 0xc000-0xffff: Ranger floppy disk routines
     ;
-    ; The only bank we can change while still keeping the Amstral kernel running
+    ; The only bank we can change while still keeping the Amstrad kernel running
     ; from is the one at 0x4000. So we need to relocate ourself into the transient
     ; data area at 0xa000 to allow this. This is 4kB wide.
 
@@ -41,11 +57,12 @@ entry:
     ldir
     jp start
 start:
+    ld (.data.entry_stack), sp
     call txtclearwindow
     ld hl, #.str.hello
     call textout
 
-    ld hl, #.str.pattern
+    ld hl, #.str.load_pattern
     ld a, #0xff
     call r_find_first
 filename_loop:
@@ -55,6 +72,7 @@ filename_loop:
     call textout
     ld a, #' '
     call txtoutput
+    call parse_filename
     call load_file
 
     call r_find_next
@@ -70,30 +88,53 @@ really_failed:
     call textout
     pop af
     call print_hex_i8
-    jp press_any_key
+press_any_key_and_exit:
+    call press_any_key
+exit:
+    ld sp, (.data.entry_stack)
+    ret
 
 finished:
     ld a, (.data.found_a_file)
     or a
-    jr z, no_files
+    jr z, no_bank_files
+
+    ld hl, #.str.call_pattern
+    ld a, #0xff
+    call r_find_first
+    jr c, no_boot_file
+    call parse_filename
+
+    push af
+    ld hl, #.str.booting
+    call textout
+    pop af
+    call print_hex_i8
+
     call r_finish
-    jp press_any_key
-no_files:
-    ld hl, #.str.nofiles
+    ld sp, (.data.entry_stack)
+    ld hl, (.data.destination)
+    jp (hl)
+
+no_bank_files:
+    ld hl, #.str.no_bank_files
+textout_and_exit:
     call textout
-    jp press_any_key
+    jr press_any_key_and_exit
+no_boot_file:
+    ld hl, #.str.no_boot_file
+    jr textout_and_exit
 
 nl:
     ld hl, #.str.nl
     jp textout
 
+; Assumes that the dta contains a dirent for a file and that .data.destination
+; is set correctly; reads it all in.
 load_file:
     ld a, #1
     ld (.data.found_a_file), a
 
-    ld hl, #0x4000
-    ld (.data.destination), hl
-    
     ld iy, (.data.dta)
     ld l, 0x1c(iy) ; start cluster in dirent
     ld h, 0x1d(iy)
@@ -117,7 +158,7 @@ cluster_loop:
     add hl, de
     ld (.data.destination), hl
     call read_sector
-    jr c, really_failed
+    jp c, really_failed
     call update_watchdog_timer
 
     ld hl, (.data.destination)
@@ -133,25 +174,58 @@ cluster_loop:
     jr nc, cluster_loop
     jp nl
 
-.data.found_a_file:
-    .db 0
-.data.destination:
-    .dw 0
-
-.str.hello:
-    .ascii "Loading..."
-.str.nl:
-    .ascii "\r\n"
-    .db 0
-
-.str.nofiles:
-    .ascii "No files to load!"
-    .db 0
-
-.str.pattern:
-    .ascii "BANK??.IMG"
-    .db 0
+; Assumes that the dta contains the dirent for the current file.
+parse_filename:
+    ld iy, (.data.dta)
 
-.str.error:
-    .ascii "\r\nError: "
-    .db 0
\ No newline at end of file
+    ld d, 6(iy)
+    ld e, 7(iy)
+    call parse_hex
+    ld (.data.destination+0), a
+
+    ld d, 4(iy)
+    ld e, 5(iy)
+    call parse_hex
+    ld (.data.destination+1), a
+
+    ld d, 9(iy)
+    ld e, 10(iy)
+    call parse_hex
+    ld (0xb001), a ; tell the OS that we're changing banks
+    out (0x11), a  ; actually change banks
+    ret
+
+; Takes a hex number in D, E and reads it into A.
+parse_hex:
+    ld a,d
+    call parse_hex_digit
+    add a,a
+    add a,a
+    add a,a
+    add a,a
+    ld d,a
+    ld a,e
+    call parse_hex_digit
+    or d
+    ret
+
+parse_hex_digit:
+    sub a, #'0'
+    cp #10
+    ret c
+    sub a, #'A'-'0'-10
+    ret
+
+.data.entry_stack:  .dw 0
+.data.found_a_file: .db 0
+.data.destination:  .dw 0
+
+.str.hello:         .ascii "Loading..." ; falls through
+.str.nl:            .asciz "\r\n"
+
+.str.no_bank_files: .asciz "No files to load!"
+.str.no_boot_file:  .asciz "No boot configuration!"
+.str.load_pattern:  .asciz "LOAD????.??"
+.str.call_pattern:  .asciz "CALL????.??"
+.str.error:         .asciz "\r\nError: "
+.str.booting:       .asciz "Starting bank "
index a4ff529..3afe3fb 100644 (file)
     .org 0x01fe
     .db 0x55, 0xaa
     
+; We mark bad blocks in the cluster map because the Amstrad floppy disk routines
+; have a bug in it which means that in bootable disks it's unable to read clusters
+; which span tracks --- it applies the reserved sector offset *after* it calculates
+; track/head/sector, which means that it thinks that some clusters occupy sectors
+; 9 and 10 of a track, which doesn't work. We mark these as being inaccessible.
+;
 ; The FAT entries for two cylinders (four physical tracks).
 ; That's 18*4 = 72 sectors = 0x12 clusters. But we are offset left
 ; by one sector, so clusters 0 and 5 span two tracks (and are inaccessible).