; Quay 900 disk formatter ; Copyright 2014 Eric Smith ; $Id: format.asm,v 1.15 2015/08/16 06:00:20 eric Exp eric $ ; Use of long symbols make this source code incompatible with some ; common Z80 assemblers. It may be cross-assembled using the ; AS Macro Assembler: ; http://john.ccac.rwth-aachen.de:8000/as/ ; This program is free software: you can redistribute it and/or modify ; it under the terms of version 3 of the GNU General Public License as ; published by the Free Software Foundation. ; 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, see ; . max_track equ 77 max_sector equ 26 max_retry equ 5 ; macros ; equality comparison of HL and DE eqhlde macro or a sbc hl,de ; sets flags add hl,de ; does not affect Z endm ; characters char_bs equ 008h char_lf equ 00ah char_cr equ 00dh char_del equ 07fh ; I/O ports ser_port_recv_data equ 000h ser_port_status equ 001h ser_port_status_tbre_bit equ 4 ser_port_status_dr_bit equ 3 ser_port_status_oe_bit equ 2 ser_port_status_fe_bit equ 1 ser_port_status_pe_bit equ 0 ser_port_xmit_data equ 002h mem_ctl_port equ 003h ; 004h to disable ROM? ctc_chan_0_port equ 008h ctc_chan_1_port equ 009h ctc_chan_2_port equ 00ah ctc_chan_3_port equ 00bh sio_port_a_data equ 00ch sio_port_b_data equ 00dh sio_port_a_ctrl equ 00eh sio_port_b_ctrl equ 00fh pio_port_a_data equ 010h pio_port_b_data equ 011h pio_port_a_ctrl equ 012h pio_port_b_ctrl equ 013h dmac_port equ 014h fdc_status_port equ 018h fdc_data_port equ 019h fdc_xfer_dir_none equ 000h fdc_xfer_dir_read equ 001h fdc_xfer_dir_write equ 002h fdc_head_select equ 004h fdc_cmd_flag_none equ 000h fdc_cmd_flag_multitrack equ 080h fdc_cmd_flag_density equ 040h fdc_cmd_flag_skip_del equ 020h fdc_cmd_read_track equ 002h fdc_cmd_specify equ 003h fdc_cmd_sense_drive_status equ 004h fdc_cmd_write_data equ 005h fdc_cmd_read_data equ 006h fdc_cmd_recalibrate equ 007h fdc_cmd_sense_interrupt_status equ 008h fdc_cmd_write_deleted equ 009h fdc_cmd_read_id equ 00ah fdc_cmd_read_deleted equ 00ch fdc_cmd_format_track equ 00dh fdc_cmd_seek equ 00fh fdc_cmd_scan_equal equ 011h fdc_cmd_scan_low_or_equal equ 019h fdc_cmd_scan_high_or_equal equ 01dh ; offsets into FDC data transfer commands fdc_rw_cmd_cmd equ 0 fdc_rw_cmd_unit equ 1 fdc_rw_cmd_c equ 2 fdc_rw_cmd_h equ 3 fdc_rw_cmd_r equ 4 fdc_rw_cmd_n equ 5 fdc_rw_cmd_eot equ 6 fdc_rw_cmd_gpl equ 7 fdc_rw_cmd_dtl equ 8 fdc_rw_cmd_len equ 9 ; offsets into FDC format command fdc_fmt_cmd_cmd equ 0 fdc_fmt_cmd_unit equ 1 fdc_fmt_cmd_n equ 2 fdc_fmt_cmd_sc equ 3 fdc_fmt_cmd_gpl equ 4 fdc_fmt_cmd_d equ 5 ; filler byte fdc_fmt_cmd_len equ 6 ; offsets into FDC results fdc_result_st0 equ 0 fdc_result_st1 equ 1 fdc_result_st2 equ 2 fdc_result_c equ 3 fdc_result_h equ 4 fdc_result_r equ 5 fdc_result_n equ 6 fdc_rw_resp_len equ 7 ; data structure offsets ; must match the parameter tables at fm_param and mfm_param fdp_density equ 0 fdp_sector_count equ 1 fdp_sector_size_n equ 2 fdp_sector_size_dtl equ 3 fdp_sector_size equ 4 fdp_fmt_gpl equ 6 fdp_rw_gpl equ 7 ; RAM ram_top equ 0c000h org 0100h origin: ld sp,stack call tx_msg_inline db "disk formatter",char_cr,0 ld hl,0 ld (fd_callback),hl call tx_msg_inline db "drive (A..D)? ",0 get_drive: call rx_char call upcase sub 'A' jr c,get_drive cp 4 jr nc,get_drive ld (drive),a add a,'A' call tx_char call tx_char_cr call tx_msg_inline db "density (S, D)? ",0 get_dens: call rx_char call upcase ld hl,fm_param cp 'S' jr z,gd0 ld hl,mfm_param cp 'D' jr nz,get_dens gd0: ld (fd_param),hl call tx_char call tx_char_cr if 0 ; check for two-sided medium call fdc_specify call recal call nz,recal_error call fdc_sense_drive_status call tx_msg_inline db "drive status: ",0 call tx_hex_byte call tx_char_cr and 008h jp z,gs9 endif xor a ld (double_sided),a call tx_msg_inline db "single or double sided (S, D)? ",0 get_sided: call rx_char call upcase ld hl,fm_param cp 'S' jr z,gs9 ld hl,mfm_param cp 'D' jr nz,get_sided push af ld a,001h ld (double_sided),a pop af gs9: call tx_char call tx_char_cr do_format: call format_disk call verify_disk call recal call nz,recal_error call wait_key jp origin format_disk: ld ix,(fd_param) call tx_msg_inline db "formatting track 0",0 ld a,(double_sided) or a jr z,fd1 call tx_msg_inline db " side 0",0 fd1: xor a ld (side),a ld (track),a call fdc_specify call recal jp nz,recal_error fmt_seek_track: call seek jp nz,seek_error fmt_track: ld hl,buffer ld (fdc_buffer),hl call fmt_compose_buffer ld de,-buffer add hl,de ld (fdc_buf_len),hl ld hl,format_track_callback ld (fd_callback),hl call fdc_format_track ld hl,0 ld (fd_callback),hl ld a,(fdc_resp_buf+fdc_result_st0) and 0c0h jp nz,fmt_error ld a,(double_sided) or a jr z,fmt_next_track ld a,(side) or a jr nz,fmt_next_track inc a ; advance to side 1 ld (side),a jr nz,fmt_track fmt_next_track: xor a ld (side),a ld hl,track inc (hl) ld a,max_track cp (hl) jp nz,fmt_seek_track call tx_msg_inline db char_cr,"no errors detected during formatting",char_cr,0 ret verify_disk: ld ix,(fd_param) xor a ld (side),a ld (track),a inc a ld (verify_msg_flag),a verify_seek_track: call seek jp nz,seek_error verify_track: ld a,(verify_msg_flag) or a jr z,vd1 xor a ld (verify_msg_flag),a call tx_msg_inline db "verifying track 0",0 ld a,(double_sided) or a jr z,vd1 call tx_msg_inline db " side 0",0 vd1: ld a,(double_sided) or a jr z,vd2 ; backspace over side msg call tx_msg_inline db char_bs,char_bs,char_bs,char_bs db char_bs,char_bs,char_bs,0 vd2: call tx_msg_inline ; update display of track number db char_bs,char_bs,0 ld a,(track) call tx_dec_two_dig ld a,(double_sided) or a jr z,vd3 ; update display of side number call tx_msg_inline db " side ",0 ld a,(side) call tx_hex_digit vd3: call read_track call verify_track_content jp nc,verify_track_ok call tx_msg_inline db char_cr,"verify failed",char_cr,0 jp abort verify_track_ok: ld a,(double_sided) or a jr z,verify_next_track ld a,(side) or a jr nz,verify_next_track inc a ; advance to side 1 ld (side),a jp nz,verify_track verify_next_track: xor a ld (side),a ld hl,track inc (hl) ld a,max_track cp (hl) jp nz,verify_seek_track call tx_msg_inline db char_cr,"no errors detected during verification",char_cr,0 ret format_track_callback: push af ld a,(double_sided) or a jr z,ftc0 ; backspace over side msg call tx_msg_inline db char_bs,char_bs,char_bs,char_bs db char_bs,char_bs,char_bs,0 ftc0: ; backspace over track number call tx_msg_inline db char_bs,char_bs,0 ; update display of track number ld a,(track) call tx_dec_two_dig ld a,(double_sided) or a jr z,ftc1 ; update display of side number call tx_msg_inline db " side ",0 ld a,(side) call tx_hex_digit ftc1: pop af ret ; On entry: ; IX: pointer to floppy parameters ; (track): track number ; Working: ; A: scratch ; C: sector number ; DE: scratch ; XXX use D as retry up-counter? ; HL: scratch, pointer to sector status buffer ; four bytes per sector ; retry count, bit 80h set if error ; ST0 ; ST1 ; ST2 read_track: push af push bc push de push hl ; clear sector status buffer ld a,(ix+fdp_sector_count) add a,a add a,a ld b,a ld hl,sector_status ld a,0 rt_clr_status: ld (hl),a inc hl djnz rt_clr_status ; start with sector 1 ld c,1 ; set up sector and sector_count for fdc_read rt0: ld a,(ix+fdp_sector_count) inc a sub c ld (sector_count),a ld a,c ld (sector),a ; try to read the sectors rt1: call compute_buffer_addr call fdc_read ld a,(fdc_resp_buf+fdc_result_st0) and 0c0h jp z,rt9 ; we get here when there's been a read error ; compute the status buffer location ld a,(fdc_resp_buf+fdc_result_r) ld c,a ; update starting sector for next read dec a add a,a add a,a ld e,a ld d,0 ld hl,sector_status add hl,de inc (hl) ; increment retry count ld a,(hl) cp max_retry jp nz,rt0 ; try again ; unrecoverable read error ld a,(hl) ; set MSB of retry count byte to or 080h ; indicate unrecovered error ld (hl),a inc hl ld a,(fdc_resp_buf+fdc_result_st0) ld (hl),a inc hl ld a,(fdc_resp_buf+fdc_result_st1) ld (hl),a inc hl ld a,(fdc_resp_buf+fdc_result_st2) ld (hl),a ; advance to next sector inc c ld a,(ix+fdp_sector_count) inc a cp c ; more sectors to read? jp nz,rt0 ; yes rt9: pop hl pop de pop bc pop af ret ; On entry: ; IX: floppy parameters ; (sector): sector number compute_buffer_addr: push af push bc push de push hl ld hl,buffer ld e,(ix+fdp_sector_size) ld d,(ix+fdp_sector_size+1) ld a,(sector) ld b,a jr cba2 cba1: add hl,de cba2: djnz cba1 ld (fdc_buffer),hl pop hl pop de pop bc pop af ret ; without interrupts, the only way to tell if a seek is done ; is apparently to try to issue the sense interrupt status, which will ; be reported as an illegal command if the interrupt hasn't occurred recal: push bc push de ld b,2 recal0: call fdc_recal call fdc_sense_interrupt_status ld a,(fdc_resp_buf) ; check ST0 equipment check and 010h jr z,recal9 djnz recal0 ; retry inc b ; set non-zero recal9: pop de pop bc ret seek: push bc push de ld b,5 seek0: ld a,(track) call fdc_seek call fdc_sense_interrupt_status ld a,(fdc_resp_buf) ; check ST0 equipment check and 010h jr z,seek9 ; success call recal jr nz,seek9 ; recal failed djnz seek0 ; retry inc b ; set non-zero seek9: pop de pop bc ret tx_fdc_recal_result: call tx_msg_inline db "recal",0 jr tfsr0 tx_fdc_seek_result: call tx_msg_inline db "seek",0 tfsr0: call tx_msg_inline db " st0: ",0 ld a,(fdc_resp_buf) call tx_hex_byte call tx_msg_inline db " pcn: ",0 ld a,(fdc_resp_buf+1) call tx_hex_byte jp tx_char_cr fmt_error: call tx_msg_inline db char_cr,"format",0 scf call tx_error_trk_sect call tx_fdc_result jp abort read_error: call tx_msg_inline db char_cr,"read",0 jr we1 write_error: call tx_msg_inline db char_cr,"write",0 we1: or a ; clear carry to include sector call tx_error_trk_sect call tx_fdc_result ld de,buffer ld ix,(fd_param) ; get sector size ld l,(ix+fdp_sector_size) ld h,(ix+fdp_sector_size+1) add hl,de ex de,hl call dump_hex jp abort recal_error: call tx_msg_inline db char_cr,"recal error ",0 jr err_bytes seek_error: call tx_msg_inline db char_cr,"seek error ",0 err_bytes: ld a,d call tx_hex_byte call tx_msg_inline db " ",0 ld a,e call tx_hex_byte call tx_char_cr jp wait_key ; omit sector if carry set on entry tx_error_trk_sect: call tx_msg_inline db " error track ",0 ld a,(track) call tx_dec_two_dig jr c,tets9 call tx_msg_inline db " sector ",0 ld a,(sector) call tx_dec_two_dig call tx_msg_inline tets9: jp tx_char_cr fdc_result_names: db "st0",0 db "st1",0 db "st2",0 db "c",0 db "h",0 db "r",0 db "n",0 tx_fdc_result: push af push bc push de push hl ld de,fdc_resp_buf ld hl,fdc_result_names ld b,7 tfr0: call tx_msg_hl call tx_msg_inline db " ",0 ld a,(de) inc de call tx_hex_byte call tx_msg_inline db " ",0 djnz tfr0 call tx_char_cr pop hl pop de pop bc pop af ret tx_msg_inline: ex (sp),hl call tx_msg_hl ex (sp),hl ret tx_msg_hl: push af tx_msg_hl_loop: ld a,(hl) inc hl or a jr z,tx_msg_hl_done call tx_char jr tx_msg_hl_loop tx_msg_hl_done: pop af ret ; Output a two digit decimal number, with ; leading zero blanked ; on entry: ; A = binary value, 00 to 99 decimal tx_dec_two_dig: push bc push af ld b,'0'-1 td2d0: inc b sub 10 jr nc,td2d0 add a,10 ld c,a ld a,b cp '0' jr nz,td2d1 ld a,' ' td2d1: call tx_char ld a,'0' add a,c call tx_char pop af pop bc ret tx_hex_word: push af ld a,h call tx_hex_byte ld a,l call tx_hex_byte pop af ret tx_hex_byte: push af rra rra rra rra call tx_hex_digit pop af push af call tx_hex_digit pop af ret tx_hex_digit: and 00fh cp 10 ccf adc a,'0' daa ; fall tx_char: push af tx_char_loop: in a,(ser_port_status) and a,010h jr z,tx_char_loop pop af out (ser_port_xmit_data),a cp char_cr ret nz push af ld a,char_lf call tx_char pop af ret tx_char_cr: push af ld a,char_cr call tx_char pop af ret wait_key: call tx_msg_inline db "press any key",char_cr,0 rx_char: in a,(ser_port_status) and a,008h jr z,rx_char in a,(ser_port_recv_data) and 07fh cp 003h ; control-c jr z,abort ret check_abort: push af in a,(ser_port_status) and a,008h jr z,ca9 in a,(ser_port_recv_data) and 07fh cp 003h ; control-c jr z,abort ca9: pop af ret abort: call tx_msg_inline db char_cr,"aborted",char_cr,0 jp origin upcase: push af sub 'a' jr c,uc8 cp 26 jr nc,uc8 add a,'A' ex (sp),hl ld h,a ex (sp),hl uc8: pop af ret ; On entry: ; HL = start address ; DE = end address + 1 dump_hex: push af push bc dc1: call tx_hex_word ld a,':' call tx_char ld b,16 eqhlde ; if empty range, bail out jr z,dc9 dc2: ld a,' ' call tx_char ld a,(hl) inc hl call tx_hex_byte ; test for end of range eqhlde jr z,dc9 ; not done, but check for end of line djnz dc2 call tx_char_cr jr dc1 dc9: call tx_char_cr pop bc pop af ret fdc_specify: call fdc_setup db 030h db fdc_cmd_flag_none+fdc_xfer_dir_none db fdc_cmd_specify dec hl ld a,03fh ; SRT = 003h (13 ms), HUT = 00fh (240 ms) ld (hl),a inc hl ld a,080h ; HLT = 040h (64 ms), ND = 0 (use DMA) ld (hl),a ret ; executes FDC command then returns fdc_sense_interrupt_status: call fdc_setup db 012h db fdc_cmd_flag_none+fdc_xfer_dir_none db fdc_cmd_sense_interrupt_status ret fdc_sense_drive_status: call fdc_setup db 021h db fdc_cmd_flag_none+fdc_xfer_dir_none db fdc_cmd_sense_drive_status ret fdc_recal: call fdc_setup db 020h db fdc_cmd_flag_none+fdc_xfer_dir_none db fdc_cmd_recalibrate ret ; executes FDC command then returns fdc_seek: call fdc_setup db 030h db fdc_cmd_flag_none+fdc_xfer_dir_none db fdc_cmd_seek ld a,(track) ld (hl),a inc hl ret ; executes FDC command then returns fdc_read_id: call fdc_setup db 027h db fdc_cmd_flag_density+fdc_head_select+fdc_xfer_dir_none db fdc_cmd_read_id ret ; executes FDC command then returns fdc_format_track: call fdc_setup db 067h db fdc_cmd_flag_density+fdc_head_select+fdc_xfer_dir_write db fdc_cmd_format_track ld a,(ix+fdp_sector_size_n) ; N ld (hl),a inc hl ld a,(ix+fdp_sector_count) ; SC ld (hl),a inc hl ld a,(ix+fdp_fmt_gpl) ; GPL ld (hl),a inc hl ld a,0e5h ; D ld (hl),a ret ; executes FDC command then returns fdc_read: call fdc_setup db 097h db fdc_cmd_flag_density+fdc_head_select+fdc_xfer_dir_read db fdc_cmd_read_data jr fdc_rw fdc_write: call fdc_setup db 097h db fdc_cmd_flag_density+fdc_head_select+fdc_xfer_dir_write db fdc_cmd_write_data ; fallto fdc_rw fdc_rw: push hl ; compute transfer length ld hl,0 ld a,(sector_count) ld b,a ld e,(ix+fdp_sector_size) ld d,(ix+fdp_sector_size+1) vns0: add hl,de djnz vns0 ld (fdc_buf_len),hl pop hl ld a,(track) ; C ld (hl),a inc hl ld a,(side) ; H ld (hl),a inc hl ld a,(sector) ; R ld (hl),a inc hl ld a,(ix+fdp_sector_size_n) ; N ld (hl),a inc hl ld a,(sector_count) ld b,a ld a,(sector) ; EOT add a,b dec a ld (hl),a inc hl ld a,(ix+fdp_rw_gpl) ; GPL ld (hl),a inc hl ld a,(ix+fdp_sector_size_dtl) ; DTL ld (hl),a ret ; executes FDC command then returns fdc_setup: ex (sp),hl ; get ret addr and save hl push af push bc push ix ld bc,fdc_do_cmd push bc ld ix,(fd_param) ld a,(hl) ; command and response byte counts rra rra rra rra and 00fh ld (fdc_cmd_len),a ld a,(hl) inc hl and 00fh ld (fdc_resp_len),a ld a,(hl) ; DMA transfer direction and 003h ld (fdc_xfer_dir),a ld a,(hl) ; head select mask and fdc_head_select ld b,a ld a,(hl) ; command flag mask and fdc_cmd_flag_multitrack+fdc_cmd_flag_density+fdc_cmd_flag_skip_del ld c,a inc hl ld a,(ix+fdp_density) ; command flags and c ; mask off appropriate command flags or (hl) ; or in command inc hl push hl ; push updated return address ld hl,fdc_cmd_buf ld (hl),a inc hl ld a,(side) rla rla and b ld b,a ld a,(drive) or b ld (hl),a inc hl if 0 ; print first two command bytes for debugging push af push hl dec hl dec hl ld a,(hl) call tx_msg_inline db char_cr,"cmd: ",0 call tx_hex_byte call tx_msg_inline db " ",0 inc hl ld a,(hl) call tx_hex_byte call tx_char_cr pop hl pop af endif ret fdc_do_cmd: ld a,(fdc_xfer_dir) dec a jp m,fdc1 ; no DMA ld a,005h ; read, WR0: A->B jr z,fdc0 ld a,001h ; write, WR0: B->A fdc0: ld (dmac_wr0_dir),a ld hl,(fdc_buffer) ld (dmac_buf),hl ld hl,(fdc_buf_len) dec hl ld (dmac_buf_len),hl ld hl,dmac_tbl ld c,dmac_port ld b,dmac_tbl_len otir fdc1: ld hl,fdc_cmd_buf ld a,(fdc_cmd_len) ld b,a fdc2: ld a,(hl) call fdc_write_byte inc hl djnz fdc2 call do_fd_callback ld hl,fdc_resp_buf ld a,(fdc_resp_len) or a ; if zero, no response phase jr z,fdc9 ld b,a call fdc_read_byte ; check ST0 for invalid cmd ld (hl),a inc hl and 0c0h cp 080h jr z,fdc_do_cmd ; if invalid cmd, retry djnz fdc3 jr fdc4 fdc3: call fdc_read_byte ld (hl),a inc hl djnz fdc3 fdc4: ld a,0a3h ; WR6: cmd: reset and disable interrupts out (dmac_port),a ; unnecesssary if no DMA transfer was ; done, but doesn't hurt anything fdc9: ; call tx_fdc_transaction ; XXX debug pop ix pop bc pop af pop hl ret do_fd_callback: ld hl,(fd_callback) ld a,h or a,l ret z jp (hl) if 0 tx_fdc_transaction: push af push de push hl call tx_msg_inline db "fdc_xfer_dir: ",0 ld a,(fdc_xfer_dir) call tx_hex_byte call tx_char_cr or a jr z,tft1 call tx_msg_inline db "dmac_wr0_dir: ",0 ld a,(dmac_wr0_dir) call tx_hex_byte call tx_char_cr call tx_msg_inline db "buffer: ",0 ld hl,(fdc_buffer) call tx_hex_word call tx_char_cr call tx_msg_inline db "buffer length: ",0 ld hl,(fdc_buf_len) call tx_hex_word call tx_char_cr tft1: call tx_msg_inline db "fdc_cmd_len: ",0 ld a,(fdc_cmd_len) call tx_hex_byte call tx_char_cr call tx_msg_inline db "fdc_resp_len: ",0 ld a,(fdc_resp_len) call tx_hex_byte call tx_char_cr call tx_msg_inline db "fdc command:",char_cr,0 ld hl,fdc_cmd_buf push hl ld a,(fdc_cmd_len) ld d,0 ld e,a add hl,de ex de,hl pop hl call dump_hex call tx_msg_inline db "fdc response:",char_cr,0 ld hl,fdc_resp_buf push hl ld a,(fdc_resp_len) ld d,0 ld e,a add hl,de ex de,hl pop hl call dump_hex pop hl pop de pop af ret endif dmac_tbl: ; port A is FDC ; port B is memory ; even if doing a write (B->A), we have to temporarily configure for A->B ; due to Z80 DMA problem with fixed-address destination ports db 0c3h ; WR6: reset db 06dh ; WR0: port A addr (8), block length (16), A->B, transfer db fdc_data_port ; port A addr dmac_buf_len: dw 104 ; block length db 02ch ; WR1: port A fixed IO db 010h ; WR2: port B incr memory db 09dh ; WR4: byte, port B addr (16), int ctl byte dmac_buf: dw buffer ; port B addr (RAM) db 002h ; int ctl byte - int at end of block db 08ah ; WR5: stop on end of block, CE/ only, ready act high db 0cfh ; WR6: cmd: load dmac_wr0_dir: db 001h ; WR0: B->A db 0cfh ; WR6: cmd: load db 0abh ; WR6: cmd: enable interrupts db 087h ; WR6: cmd: enable DMA dmac_tbl_len equ $-dmac_tbl fdc_read_byte: call check_abort in a,(fdc_status_port) ; wait for FDC ready rla jr nc,fdc_read_byte rla ; direction is FDC to CPU? jr nc,fdc_read_byte ; no, try again in a,(fdc_data_port) ; get data ret fdc_write_byte: call check_abort push af wfb0 in a,(fdc_status_port) ; wait for FDC ready rla jr nc,wfb0 rla ; direction is CPU to FDC? jr c,wfb0 ; no, try again pop af ; send data out (fdc_data_port),a ret ; On entry: ; IX points to floppy disk parameters ; On return: ; Carry flag set if error verify_track_content: push bc push de push hl or 0 ; clear carry flag push af ld hl,buffer ld de,sector_status ld a,(ix+fdp_sector_count) ld b,a ld c,1 vtc1: ld a,c ld (verify_sector),a call verify_sector_content jr nc,vtc2 pop af scf push af vtc2: inc c djnz vtc1 pop af pop hl pop de pop bc ret ; On entry: ; HL points to data buffer ; DE points to sector status buffer ; IX points to floppy disk parameters ; On return: ; HL advanced past end of sector data ; DE advanced past end of sector status ; Carry flag set if error verify_sector_content: or 0 ; clear carry flag push af push hl ld h,d ld l,e ld a,(hl) or 0 ; any retries or errors? jr z,vsc1 ; no ld (verify_msg_flag),a call vsc_print_ts ld a,(hl) and 07fh call tx_dec_two_dig call tx_msg_inline db " retries",0 ld a,(hl) or 0 ; unrecovered error? jp m,vsc0 ; yes call tx_msg_inline db char_cr,0 jr vsc1 vsc0: inc hl call tx_msg_inline db ", unrecovered, st0=",0 ld a,(hl) call tx_hex_byte inc hl call tx_msg_inline db ", st1=",0 ld a,(hl) call tx_hex_byte inc hl call tx_msg_inline db ", st2=",0 ld a,(hl) call tx_hex_byte inc hl call tx_msg_inline db char_cr,0 pop hl jr vsc_err ; sector read OK, compare data vsc1: inc de inc de inc de inc de pop hl push hl push bc ld c,(ix+fdp_sector_size) ld b,(ix+fdp_sector_size+1) ld a,0e5h ; fill byte vsc2: cpi jr nz,vsc7 jp pe,vsc2 ; data compare OK pop bc pop hl jr vsc9 ; data compare error vsc7: pop bc pop hl call vsc_print_ts call tx_msg_inline db "data compare error",char_cr,0 push hl ; print buffer push de ld e,(ix+fdp_sector_size) ld d,(ix+fdp_sector_size+1) add hl,de call dump_hex pop de pop hl vsc_err: pop af scf ; indicate error push af vsc9: push de ; advance data buffer pointer ld e,(ix+fdp_sector_size) ld d,(ix+fdp_sector_size+1) add hl,de pop de pop af ret vsc_print_ts: call tx_msg_inline db char_cr,"track ",0 ld a,(track) call tx_dec_two_dig call tx_msg_inline db " sector ",0 ld a,(verify_sector) call tx_dec_two_dig call tx_msg_inline db ": ",0 ld a,1 ld (verify_msg_flag),a ret fmt_compose_buffer: ld hl,buffer ld c,01 ; R ld b,(ix+fdp_sector_count) fcb0: ld a,(track) ; C ld (hl),a inc hl ld a,(side) ; H ld (hl),a inc hl ld (hl),c ; R inc hl ld a,(ix+fdp_sector_size_n) ; N ld (hl),a inc hl inc c djnz fcb0 ret ; Standard IBM 3740 single-density format with 128 bytes per sector. ; This is the standard CP/M interchange format. fm_param: db 000h ; density - FM db 26 ; sector_count db 000h ; sector_size_n db 080h ; sector_size_dtl dw 128 ; sector_size db 27 ; fmt_gpl db 7 ; rw_gpl ; IBM System 34 double-density format with 1024 bytes per sector. mfm_param: mfm_param_8x1024: db 040h ; density - MFM db 8 ; sector_count db 003h ; sector_size_n db 0ffh ; sector_size_dtl dw 1024 ; sector_size db 116 ; fmt_gpl db 53 ; rw_gpl ; It's possible to squeeze nine 1024-byte sectors onto a track by using ; the same gap length that would be used for 256-byte sectors. This probably ; isn't a good idea since writing a sector to such a disk on a drive with ; a spindle motor running fast (but within tolerance) may overwrite the ; following address field. mfm_param_9x1024: db 040h ; density - MFM db 9 ; sector_count db 003h ; sector_size_n db 0ffh ; sector_size_dtl dw 1024 ; sector_size db 54 ; fmt_gpl db 14 ; rw_gpl ; variables double_sided: ds 1 drive: ds 1 side: ds 1 track: ds 1 sector: ds 1 sector_count: ds 1 fd_param: ds 2 ; points to parameter table fd_callback ds 2 verify_sector: ds 1 ; used for printing error messages verify_msg_flag: ds 1 ; flag set true if verify routine ; printed a message ; uPD765 command fdc_cmd_len: ds 1 fdc_cmd_buf: ds 9 ; uPD765 transfer fdc_xfer_dir ds 1 fdc_buffer ds 2 fdc_buf_len ds 2 ; uPD765 response fdc_resp_len: ds 1 fdc_resp_buf: ds 7 ds 100 stack: equ $ align 256 sector_status: ds max_sector*4 ; retry count, ST0, ST1, ST2 for each sector align 256 buffer: equ $ end origin