--- /dev/null
+bootfloppy.img: bootblock.img autoprg.bin
+ rm -f bootfloppy.img
+ 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
+
+bootblock.img: bootblock.s
+ sdasz80 -fflopzws bootblock.rel bootblock.s
+ sdldz80 -nwmx -i bootblock.ihx bootblock.rel
+ srec_cat -disable-sequence-warning \
+ bootblock.ihx -intel \
+ -output bootblock.img -binary
+
+autoprg.bin: autoprg.s
+ sdasz80 -fflopzws autoprg.rel autoprg.s
+ sdldz80 -nwmx -b _CODE=0xa000 -i autoprg.ihx autoprg.rel
+ srec_cat -disable-sequence-warning \
+ autoprg.ihx -intel -offset -0xa000 \
+ -output autoprg.bin -binary
+
+clean:
+ rm -f bootblock.img autoprg.bin bootfloppy.img
--- /dev/null
+.globl entry
+
+; Amstrad kernel entrypoints
+
+kmreadchar = 0xb9b3
+textout = 0xb81e
+txtclearwindow = 0xb824
+txtoutput = 0xb833
+
+; Ranger FDD entrypoints
+
+r_finish = 0xf34b
+r_set_dta = 0xf391
+r_find_first = 0xf396
+r_find_next = 0xf3bf
+.data.gp = 0xb7e0
+.data.dta = 0xb7f1
+load_sectors_to_4000 = 0xe884
+print_hex_i8 = 0xcb0e
+press_any_key = 0xcb50
+advance_to_next_cluster = 0xd8ca
+read_sector = 0xdffe
+calculate_location_of_cluster = 0xda57
+update_watchdog_timer = 0xcc78
+
+ ; On entry, the memory map is as follows:
+ ;
+ ; 0x0000-0x3fff: Kernel workspace
+ ; 0x4000-0x7ccc: Dedicated 16kB bank for us
+ ; 0x8000-0xbfff: Kernel workspace
+ ; 0xc000-0xffff: Ranger floppy disk routines
+ ;
+ ; The only bank we can change while still keeping the Amstral 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.
+
+entry:
+ ld de, #0xa000
+ ld hl, #0x4000
+ ld bc, #0x1000
+ ldir
+ jp start
+start:
+ call txtclearwindow
+ ld hl, #.str.hello
+ call textout
+
+ ld hl, #.str.pattern
+ ld a, #0xff
+ call r_find_first
+filename_loop:
+ jp c, failed
+
+ ld hl, (.data.dta)
+ call textout
+ ld a, #' '
+ call txtoutput
+ call load_file
+
+ call r_find_next
+ jr filename_loop
+
+failed:
+ cp #0x12 ; ran out of files?
+ jr z, finished
+ ; Otherwise it's a hard error.
+really_failed:
+ push af
+ ld hl, #.str.error
+ call textout
+ pop af
+ call print_hex_i8
+ jp press_any_key
+
+finished:
+ ld a, (.data.found_a_file)
+ or a
+ jr z, no_files
+ call r_finish
+ jp press_any_key
+no_files:
+ ld hl, #.str.nofiles
+ call textout
+ jp press_any_key
+
+nl:
+ ld hl, #.str.nl
+ jp textout
+
+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)
+cluster_loop:
+ push hl
+ call calculate_location_of_cluster
+ ld 0x18(ix), a ; track
+ ld 0x1a(ix), l ; sector
+
+ ld hl, (.data.destination)
+ call read_sector
+ jr c, really_failed
+ call update_watchdog_timer
+
+ ld a, 0x1a(ix)
+ inc a
+ ld 0x1a(ix), a
+
+ ld hl, (.data.destination)
+ ld de, #0x200
+ add hl, de
+ ld (.data.destination), hl
+ call read_sector
+ jr c, really_failed
+ call update_watchdog_timer
+
+ ld hl, (.data.destination)
+ ld de, #0x200
+ add hl, de
+ ld (.data.destination), hl
+
+ ld a, #'.'
+ call txtoutput
+
+ pop hl
+ call advance_to_next_cluster
+ 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
+
+.str.error:
+ .ascii "\r\nError: "
+ .db 0
\ No newline at end of file
--- /dev/null
+ .area _BOOT (ABS)
+
+.macro bios_parameter_block reserved_sectors
+ .dw 512 ; bytes per sector
+ .db 2 ; sectors per cluster
+ .dw reserved_sectors
+ .db 2 ; FAT count
+ .dw 0x70 ; number of root directory entries
+ .dw 108 ; filesystem size, in sectors
+ .db 0xf9 ; media byte
+ .dw 3 ; sectors per FAT
+ .dw 9 ; number of sectors per track
+ .dw 2 ; number of heads
+ .dw 0 ; number of hidden sectors
+.endm
+
+ .org 0x0003
+ .ascii "NCBOOT "
+
+ .org 0x000b
+ bios_parameter_block 2
+ .org 0x020b
+ bios_parameter_block 0
+
+ .org 0x1be
+
+ ; Partition 0
+ .db 0 ; partition status (not bootable)
+ .db 0, 2, 0 ; encoded CHS of start
+ .db 0xda ; partition type
+ .db 1, 8, 5 ; encoded CHS of end
+ .dw 1, 0 ; LBA of start
+ .dw 0x6a, 0 ; LBA of end
+
+ ; partition 1
+ .db 0 ; partition status (not bootable)
+ .db 1, 9, 5 ; encoded CHS of start
+ .db 0x83 ; partition type
+ .db 1, 9, 0x4f ; encoded CHS of end
+ .dw 0x6b, 0 ; LBA of start
+ .dw 0x534, 0 ; LBA of end
+
+ .org 0x01fe
+ .db 0x55, 0xaa
+
+; 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).
+; Then the pattern is repeated again for the other two tracks.
+.macro four_tracks
+ .db 0xf7, 0x0f, 0x00 ; clusters 0, 1
+ .db 0x00, 0x00, 0x00 ; clusters 2, 3
+ .db 0x00, 0x70, 0xff ; clusters 4, 5
+ .db 0x00, 0x00, 0x00 ; clusters 6, 7
+ .db 0x00, 0x70, 0xff ; clusters 8, 9
+ .db 0x00, 0x00, 0x00 ; clusters a, b
+ .db 0x00, 0x00, 0x00 ; clusters c, d
+ .db 0xf7, 0x0f, 0x00 ; clusters d, e
+ .db 0x00, 0x00, 0x00 ; clusters f, 10
+.endm
+
+.macro fat_definition
+ .db 0xf9, 0xff, 0xff
+ .db 0x00, 0x70, 0xff ; clusters 2, 3
+ .db 0x00, 0x00, 0x00 ; clusters 4, 5
+ .db 0x00, 0x00, 0x00 ; clusters 6, 7
+ .db 0xf7, 0x0f, 0x00 ; clusters 8, 9
+ .db 0x00, 0x00, 0x00 ; clusters a, b
+
+ four_tracks
+ four_tracks
+.endm
+
+ .org 0x400
+ fat_definition
+ .org 0xa00
+ fat_definition
+
+; Make an empty root directory.
+
+ .org 0x1000
+ .db 0