; Fenders "3-sector" loader disassembly, 20070526 bkw
; Double-density version

; Note: the double-density loader doesn't actually fit in 3 sectors.
; It uses sectors 1-3 and 720.
; First 3 sectors of a DD disk are still only 128 bytes/sector.

; At boot, the OS boot code loads the first 3 sectors and jumps to
; the loaded code... which then loads sector 720 (a proper 256-byte DD
; sector), which contains the rest of the code.

; I haven't done a very thorough job of reverse-engineering the DD
; version of the loader. Its structure is similar to that of the SD
; loader (fenders.dasm).

 processor 6502

;;; Equates:

;; OS ROM entry points
SIOV        .equ  $e459
COLDSV      .equ  $e477
KEYBDV      .equ  $e420 ; K: handler device table
keyb_get_lo .equ  KEYBDV+4 ; pointer to "get byte" routine, minus 1
keyb_get_hi .equ  KEYBDV+5 ; (used by get_key)

;; OS zero page
BOOTQ       .equ   $09
SAVMSC      .equ   $58
ZROFRE      .equ   $80

;; OS and FMS page 2 RAM variables
COLDST      .equ   $0244
SDMCTL      .equ   $022f
SDLSTL      .equ   $0230
SDLSTH      .equ   $0231
RUNAD       .equ   $02e0
INITAD      .equ   $02e2

;; DCB, used for sector I/O parameters by SIOV (called by read_sector)
DDEVIC      .equ   $0300
DUNIT       .equ   $0301
DCOMND      .equ   $0302
DSTATS      .equ   $0303
DBUFLO      .equ   $0304
DBUFHI      .equ   $0305
DTIMLO      .equ   $0306
DBYTLO      .equ   $0308
DBYTHI      .equ   $0309
DAUX1       .equ   $030a
DAUX2       .equ   $030b

;; Hardware registers
COLPF1      .equ   $d017
VCOUNT      .equ   $d40b

;; Local variables (zero page)
end_address         .equ   $45
save_pos            .equ   $49
menu_counter        .equ   $b0
dir_sector_lo       .equ   $b1
tmp_dlistl          .equ   $b2
tmp_dlisth          .equ   $b3
menu_ptr_lo         .equ   $b4
menu_ptr_hi         .equ   $b5
dest_ptr            .equ   $43
start_sector_lo_tbl .equ   $c0
start_sector_hi_tbl .equ   $e0

;; Local variables (non zero page)
buffer              .equ   $0b10
sector_link_hi      .equ   $0c0d ; buffer + $fd
sector_link_lo      .equ   $0c0e ; buffer + $fe
sector_byte_count   .equ   $0c0f ; buffer + $ff

;;; Bootable disk image starts here:
   .org   $0700

;;; Standard Atari boot disk header (6 bytes)
boot_record:
   .byte $00         ; ignored
   .byte $03         ; number of sectors to read
   .word boot_record ; load address
   .word COLDSV      ; init address, don't think this gets used

;;; Actual code starts here:
boot_continuation:
OFFSET_COLDST_1_DD .equ *-boot_record+1
OFFSET_COLDST_2_DD .equ *-boot_record+5
   LDY   #$00   ; 0 .
   STY   COLDST
   INY
   STY   BOOTQ
   STY   DUNIT
   DEC   DTIMLO
set_dbl_density:
   LDA   #$4e   ; 78 N
   STA   DCOMND
   LDA   #$40   ; 64 @
   STA   DSTATS
   LDA   #$0c   ; 12 .
   STA   DBYTLO
   LDA   #$00   ; 0 .
   STA   DBYTHI
   LDA   #<buffer
   STA   DBUFLO
   LDA   #>buffer
   STA   DBUFHI
   JSR   SIOV
   BMI   set_dbl_density
   LDA   #$04   ; 4 .
   STA   buffer+5
   LDA   #$01   ; 1 .
   STA   buffer+6
   LDA   #$00   ; 0 .
   STA   buffer+7
   LDA   #$4f   ; 79 O
   STA   DCOMND
   LDA   #$80   ; 128 .
   STA   DSTATS
   JSR   SIOV
   BMI   set_dbl_density

; The bootloader code is 640 bytes long. First 384 bytes were loaded
; from the 3 boot sectors already; the rest lives in sector 720, which
; we have to load before running it:
read_sec_720:
   LDA   #$52   ; 82 R
   STA   DCOMND
   LDA   #$40   ; 64 @
   STA   DSTATS
   LDA   #$80   ; 128 .
   STA   DBUFLO
   LDA   #$08   ; 8 .
   STA   DBUFHI
   LDA   #$00   ; 0 .
   STA   DBYTLO
   LDA   #$01   ; 1 .
   STA   DBYTHI
   LDA   #$d0   ; 208 .
   STA   DAUX1
   LDA   #$02   ; 2 .
   STA   DAUX2
   JSR   SIOV
   BMI   read_sec_720

   ; setup display list
   LDA   SDLSTL
   STA   tmp_dlistl
   LDA   SDLSTH
   STA   tmp_dlisth
   LDA   #$00   ; 0 .
   STA   SDMCTL
   LDA   #<display_list   ; 58 :
   STA   SDLSTL
   LDA   #>display_list   ; 9 .
   STA   SDLSTH

   ; init menu
   LDA   #$6c   ; 108 l
   STA   menu_ptr_lo
   LDA   #$09   ; 9 .
   STA   menu_ptr_hi
   LDA   #$69   ; 105 i
   STA   dir_sector_lo

read_dir_sector:
   LDA   dir_sector_lo
   STA   DAUX1
   LDA   #$01   ; 1 .
   STA   DAUX2
   JSR   read_sector
   INC   dir_sector_lo
   DEX

do_dirent:
   LDA   buffer,X
   BEQ   dir_done
   BMI   next_dirent
   AND   #$01   ; 1 .
   BNE   next_dirent
   INC   menu_counter
   LDY   menu_counter
   LDA   buffer+3,X
   STA   start_sector_lo_tbl,Y
   LDA   buffer+4,X
   STA   start_sector_hi_tbl,Y
   TYA
   CLC
   ADC   #$a0   ; 160 .
   LDY   #$03   ; 3 .
   STA   (menu_ptr_lo),Y
   INY
   LDA   #$8e   ; 142 .
   STA   (menu_ptr_lo),Y
   INY
next_char:
   INY
   LDA   buffer+5,X
   INX
   SEC
   SBC   #$20   ; 32  
   STA   (menu_ptr_lo),Y
   CPY   #$10   ; 16 .
   BNE   next_char
   CLC
   LDA   menu_ptr_lo
   ADC   #$14   ; 20 .
   STA   menu_ptr_lo
   BCC   skip_ptr_hi
   INC   menu_ptr_hi
skip_ptr_hi:
   LDA   menu_counter
   CMP   #$14   ; 20 .
   BEQ   dir_done
next_dirent:
   TXA
   AND   #$f0   ; 240 .
   CLC
   ADC   #$10   ; 16 .
   TAX
   ASL
   BCC   do_dirent
   BCS   read_dir_sector
dir_done:
   LDA   #$22   ; 34 "
   STA   SDMCTL
wait_vcount_0:
   LDA   VCOUNT
   BNE   wait_vcount_0
wait_for_input:
   JSR   get_key
   SEC
   SBC   #$40   ; 64 @
   CMP   menu_counter
   BEQ   load_file
   BCS   wait_for_input
load_file:
   TAX
   LDA   start_sector_lo_tbl,X
   STA   DAUX1
   LDA   start_sector_hi_tbl,X
   STA   DAUX2
   LDA   #$68   ; 104 h
   STA   menu_ptr_lo
   LDA   #$09   ; 9 .
   STA   menu_ptr_hi
L0834:
   DEX
   BEQ   print_loading_msg
   CLC
   LDA   menu_ptr_lo
   ADC   #$14   ; 20 .
   STA   menu_ptr_lo
   BCC   L0834
   INC   menu_ptr_hi
   BNE   L0834
print_loading_msg:
   LDY   #$00   ; 0 .
next_msg_byte:
   LDA   loading_msg,Y
   STA   (SAVMSC),Y
   INY
   CPY   #$09   ; 9 .
   BNE   next_msg_byte
print_filename:
   LDA   (menu_ptr_lo),Y
   STA   (SAVMSC),Y
   INY
   CPY   #$15   ; 21 .
   BNE   print_filename
   LDA   #$00   ; 0 .
   STA   SDMCTL
   LDA   tmp_dlistl
   STA   SDLSTL
   LDA   tmp_dlisth
   STA   SDLSTH
   LDA   #$22   ; 34 "
   STA   SDMCTL
wait_vcount_again:
   LDA   VCOUNT
   BNE   wait_vcount_again
   LDY   #$00   ; 0 .
   TYA
clear_zp:
   STA   ZROFRE,Y
   INY
   BPL   clear_zp

; the "JSR try_read" below MUST be located at $087f.
; The JSR opcode is the last byte loaded in the 3-sector boot loader,
; and its operand is the first 2 bytes loaded from sector 720!
 .if *<>$087f
   .echo "Code offsets have changed, fix me (", *, "should be $087f)"
   .err
 .endif
   JSR   try_read ; cut here!

   DEX

read_segment:
   JSR   get_next_byte
   STA   dest_ptr
   JSR   get_next_byte
   STA   dest_ptr+1
   AND   dest_ptr
   CMP   #$ff   ; 255 .
   BEQ   read_segment
   JSR   get_next_byte
   STA   end_address
   JSR   get_next_byte
   STA   end_address+1
load_byte:
   JSR   get_next_byte
   STA   (dest_ptr),Y
   INC   dest_ptr
   BNE   check_seg_done
   INC   dest_ptr+1
   BEQ   check_for_init
check_seg_done:
   LDA   end_address
   CMP   dest_ptr
   LDA   end_address+1
   SBC   dest_ptr+1
   BCS   load_byte
check_for_init:
   LDA   INITAD
   ORA   INITAD+1
   BEQ   read_segment
   STX   save_pos
   JSR   do_init
   LDX   save_pos
   LDY   #$00   ; 0 .
   STY   INITAD
   STY   INITAD+1
   BEQ   read_segment

do_init:
   JMP   (INITAD)

get_next_byte:
   ; self-modifying code changes immediate CPX operand
   CPX   #$fd   ; 253 .
   BNE   return_next_byte
   LDA   DAUX1
   ORA   DAUX2
   BNE   try_read
OFFSET_SCREENOFF_DD .equ *-boot_record
   LDA   SDMCTL
   JMP   (RUNAD)

read_sector:
   LDA   #$31   ; 49 1
   STA   DDEVIC
   LDA   #$52   ; 82 R
   STA   DCOMND
   LDA   #<buffer   ; 16 .
   STA   DBUFLO
   LDA   #>buffer   ; 11 .
   STA   DBUFHI
   LDA   #$00   ; 0 .
   STA   DBYTLO
   LDA   #$01   ; 1 .
   STA   DBYTHI

try_read:
   LDA   #$40   ; 64 @
   STA   DSTATS
   JSR   SIOV
   BMI   try_read
   LDA   sector_link_hi
   AND   #$03   ; 3 .
   STA   DAUX2
   LDA   sector_link_lo
   STA   DAUX1
OFFSET_ROTCOLOR_DD .equ *-boot_record
   LDA   COLPF1
   LDA   sector_byte_count
   STA   get_next_byte+1
   LDY   #$00   ; 0 .
   LDX   #$00   ; 0 .
return_next_byte:
   LDA   buffer,X
   INX
   RTS

get_key:
   LDA   keyb_get_hi
   PHA
   LDA   keyb_get_lo
   PHA
   RTS

loading_msg:
   .byte   $00,$00,$2c,$6f,$61,$64,$69,$6e   ; "..,oadin"
   .byte   $67,$00,$00                       ; "g.."
display_list:
   .byte   $70,$70,$70,$47                   ; "pppG"
   .byte   <screen,>screen
   .byte   $70,$06,$06,$06,$06,$06,$06       ; "p......"
   .byte   $06,$06,$06,$06,$06,$06,$06,$06   ; "........"
   .byte   $06,$06,$06,$06,$06,$06,$41       ; "......A"
   .byte   <display_list,>display_list
OFFSET_TITLE_DD .equ *-boot_record
screen:
   .byte   $00,$00,$00,$00,$61,$74,$61       ; ".....ata"
   .byte   $72,$69,$00,$61,$72,$63,$61,$64   ; "ri.arcad"
   .byte   $65,$00,$00,$00,$00,$00,$00,$00   ; "e......."
   .byte   $00,$00,$00,$00,$00,$00,$00,$00   ; "........"
   .byte   $00,$00,$00,$00,$00,$00,$00,$00   ; "........"
   .byte   $00                               ; "."