; commented disassembly of HP9121 firmware
; commented 2012 by Holger Veit
; provided as is under Creative Commons License CC-BY-SA
; http://creativecommons.org/licenses/by-sa/3.0/
;
; No warranty is given that documented information is 
; completely correct; the author (who commented it)
; is not responsible for any damage resulting from the
; use of this information.
;
; Note: this might look like valid assembler but might not work at all.
; This is only for documentation, and not intended to be usable 
; for producing binaries or ROMs.

;=============================================
; some structures
;=============================================

; CPU 6809 status register
cc_c:        equ 1
cc_v:        equ 10b
cc_z:        equ 100b
cc_n:        equ 1000b
cc_irq:      equ 10000b
cc_h:        equ 100000b
cc_firq:     equ 1000000b
cc_e:        equ 10000000b

;=============================================

; FDC controller, refer to datasheet
; note FDC has inverted data bus, i.e. everything read or written
; needs to be complemented (negative logic)
MB8876       struc
str_cr:      .db   ?             ; command register / status register
tr:          .db   ?             ; track register
scr:         .db   ?             ; sector register
dr:          .db   ?             ; data register
MB8876       ends

; FDC commands (negative logic)
fdcmd_restore   equ    ~0x0B
fdcmd_seek      equ    ~0x1B
fdcmd_writesec  equ    ~0x57
fdcmd_stepin    equ    ~0x5B
fdcmd_stepout   equ    ~0x7B
fdcmd_readsec   equ    ~0x88
fdcmd_writesec1 equ    ~0xA8
fdcmd_readaddr  equ    ~0xC0
fdcmd_forceint  equ    ~0xD0
fdcmd_writetrk  equ    ~0xF0


; fdc_status1 flags
fdc_status1_busy:      equ 1
fdc_status1_index:     equ 10b
fdc_status1_trk0:      equ 100b
fdc_status1_crcerror:  equ 1000b
fdc_status1_seekerror: equ 10000b
fdc_status1_headload:  equ 100000b
fdc_status1_wrtprot:   equ 1000000b
fdc_status1_notready:  equ 10000000b

; fdc_status23 flags
fdc_status23_busy:     equ 1
fdc_status23_drq:      equ 10b
fdc_status23_lostdata: equ 100b
fdc_status23_crcerror: equ 1000b
fdc_status23_recnf:    equ 10000b
fdc_status23_wrtflt:   equ 100000b
fdc_status23_wrtprot:  equ 1000000b
fdc_status23_notready: equ 10000000b

;=============================================

; GPIB controller; refer to data sheet
GPIB8291     struc
dataio:      .db ?               ; data in/out
intreg1:     .db ?               ; int status / enable
intreg2:     .db ?               ; int status /enable
serpoll:     .db ?               ; serial poll status / enable
addrreg:     .db ?               ; address status / mode
cmdauxreg:   .db ?               ; cmd pass thru        / aux mode
addr0_01:    .db ?               ; address 0 / 01
addr1_eos:   .db ?               ; address 1 / eos
GPIB8291     ends

; GPIB INT1 Status flags
ints1_bi:    equ 1
ints1_bo:    equ 10b
ints1_err:   equ 100b
ints1_dec:   equ 1000b
ints1_end:   equ 10000b
ints1_get:   equ 100000b
ints1_apt:   equ 1000000b
ints1_cpt:   equ 10000000b

; GPIB INT1 enable
inte1_bi:    equ 1
inte1_bo:    equ 10b
inte1_err:   equ 100b
inte1_dec:   equ 1000b
inte1_end:   equ 10000b
inte1_get:   equ 100000b
inte1_apt:   equ 1000000b
inte1_cpt:   equ 10000000b

; GPIB INT2 status flags
ints2_adsc:  equ 1
ints2_remc:  equ 10b
ints2_lloc:  equ 100b
ints2_spc:   equ 1000b
ints2_rem:   equ 10000b
ints2_llo:   equ 100000b
ints2_spas:  equ 1000000b
ints2_int:   equ 10000000b

; GPIB INT2 enable
inte2_adsc:  equ 1
inte2_remc:  equ 10b
inte2_lloc:  equ 100b
inte2_spc:   equ 1000b
inte2_dmai:  equ 10000b
inte2_dmao:  equ 100000b

; GPIB SPS flags (not used)
sps_s1:      equ 1
sps_s2:      equ 10b
sps_s3:      equ 100b
sps_s4:      equ 1000b
sps_s5:      equ 10000b
sps_s6:      equ 100000b
sps_rsv:     equ 1000000b
sps_s8:      equ 10000000b

; GPIB SP enable (not used)
spe_s1:      equ 1
spe_s2:      equ 10b
spe_s3:      equ 100b
spe_s4:      equ 1000b
spe_s5:      equ 10000b
spe_s6:      equ 100000b
spe_rsv:     equ 1000000b
spe_s8:      equ 10000000b

; GPIB Address status
as_mjmn:     equ 1
as_ta:       equ 10b
as_la:       equ 100b
as_tpas:     equ 1000b
as_lpas:     equ 10000b
as_eoi:      equ 100000b
as_lon:      equ 1000000b
as_ton:      equ 10000000b

; GPIB address mode
am_adm0:     equ 1
am_adm1:     equ 10b
am_lo:       equ 1000000b
am_to:       equ 10000000b

; GPIB AD 0 flags
ad0_ad10:    equ 1
ad0_ad20:    equ 10b
ad0_ad30:    equ 100b
ad0_ad40:    equ 1000b
ad0_ad50:    equ 10000b
ad0_dl0:     equ 100000b
ad0_dt0:     equ 1000000b
ad0_int:     equ 10000000b

; GPIB AD0/1 flags
ad01_ad1:    equ 1
ad01_ad2:    equ 10b
ad01_ad3:    equ 100b
ad01_ad4:    equ 1000b
ad01_ad5:    equ 10000b
ad01_dl:     equ 100000b
ad01_dt:     equ 1000000b
ad01_ars:    equ 10000000b

; GPIB AUXM commands
auxm_imm_pon: equ 0
auxm_clr_pp:  equ 1
auxm_reset:   equ 2
auxm_finhs:   equ 3
auxm_trigger: equ 4
auxm_clr_rtl: equ 5
auxm_sendeoi: equ 6
auxm_nv_sa:   equ 7
auxm_pon:     equ 8
auxm_set_pp:  equ 9
auxm_set_rtl: equ 0xD
auxm_v_sa:    equ 0xF

; GPIB AD1 flags
ad1_ad11:    equ 1
ad1_ad21:    equ 10b
ad1_ad31:    equ 100b
ad1_ad41:    equ 1000b
ad1_ad51:    equ 10000b
ad1_dl1:     equ 100000b
ad1_dt1:     equ 1000000b
ad1_x:       equ 10000000b

;=============================================

; Bits of CTRL Port
ctrl_sidesel: equ 1
ctrl_motor:   equ 10b
ctrl_hdld:    equ 100b
ctrl_clridx:  equ 1000b
ctrl_drsel1:  equ 10000b
ctrl_drsel0:  equ 100000b
ctrl_led:     equ 1000000b
ctrl_halten:  equ 10000000b

;=============================================

; Bits of STATUS port
status_addrsw0:  equ 1
status_addrsw1:  equ 10b
status_addrsw2:  equ 100b
status_drwpt:    equ 1000b
status_test:     equ 10000b
status_dualhead: equ 100000b
status_loop:     equ 1000000b
status_drrdy:    equ 10000000b

;=============================================

; Status S1 of Amigo command set, see documentation 
amigo_s1_normal:      equ 0
amigo_s1_illop:       equ 1
amigo_s1_cylcmp_err:  equ 7
amigo_s1_data_err:    equ 8
amigo_s1_seccmp_err:  equ 9
amigo_s1_ioprog_err:  equ 0xA
amigo_s1_dbitset:     equ 0x11
amigo_s1_rtry_hw_err: equ 0x12
amigo_s1_status2_err: equ 0x13
amigo_s1_unitunavail: equ 0x17
amigo_s1_drvatt:      equ 0x1F

; Status S2 of Amigo command set
amigo_s2_driveready:    equ 0
amigo_s2_drivenotready: equ 3
amigo_s2_seekcheck:     equ 4
amigo_s2_firststatus:   equ 8
amigo_s2_drvfault:      equ 0x10
amigo_s2_wrtprotect:    equ 0x40
amigo_s2_attention:     equ 0x80

;=============================================

; some direct page register definitions
; note DP register is set to 0x40, thus denoting
; GPIB address space 0x4000-0x4007

gpio_dio:     equ 0
gpib_int1:    equ 1
gpib_int2:    equ 2
gpib_spm:     equ 3
gpib_am:      equ 4
gpib_auxm:    equ 5
gpib_ad01:    equ 6
gpib_ad1:     equ 7

;=============================================
; RAM 1K at 0x0000

; variables tagged with UNIT exist for at most 4 drives
; in HP9121D, only two are in use

; The cylinder/track/head mess:
; Unfortunately, I have used track and cylinder interchangedly
; when trying to decode the ROM. The real situation is as follows:
; the drive is single sided with 70 tracks. For amigo compatibility,
; it is addressed as if it were a 35 track dual head drive. The
; FDC TR register counts actual drive tracks (70). The even tracks
; are assumed to be head 0, the odd ones are head 1, and they are 
; likewise formatted, i.e. the 2nd physical track has TRK# 0 and HEAD 1
; coded in. To calculate phys track from amigo convention, just double trk#
; and add head number. This happens several times in the code.

; sector format: a track contains 16 sectors of 256 bytes, numbered 0 to 15
; and a 17th sector with sector number 17 of 128 bytes.
; The only information in sector 17 are the first three bytes, which keep the
; "wear level information". This tries to count the number of revolutions and
; steppings of a disk, and is an indication of usage. The following three bytes
; in the sector differ for a newly formatted and already used disk. See the
; formatting rountine and the write_sector17 routine. The other 122 bytes
; of this sector are entirely unused.

                   org  0
diskbuf            .ds  256
end_buf:           .ds  1
status_s1:         .ds  1          ; status S1 from amigo_readstatus
status_s1_unit:    .ds  1          ; unit of status S1
status_s2_type:    .ds 1           ; type of status S2
status_s2_flags:   .ds 1           ; disk flags of status S2
sys_state:         .ds 1           ; internal system state (mainly set, not really used)
portctrl_state:    .ds 1           ; readable value of CTRL port (which HW is write only)
format_track:      .ds 1           ; flag for amigo_initalize (listen 00 will do formatting)
has_sec17:         .ds 1           ; flag that we have data from sector 17
drvN_endofdisk:    .ds 4           ;UNIT flag that unbuffered R/W reached end of disk
drvN_inactive:     .ds 4           ;UNIT flag that drive is in inactive (power save) mode
motoron_flg:       .ds 1           ; flag that drive motor is spinning
fdc_headisloaded:  .ds 1           ; flag that drive head is loaded
unbufread_flg:     .ds 1           ; flag to signal multi-sector read ("unbuffered read")
unbufwrite_flg:    .ds 1           ; flag to signal multi-sector write ("unbuffered write")
eoi_flag:          .ds 1           ; flag to send next byte with EOI
gpib_apt_flag:     .ds 1           ; flag to signal that a verify secondary interrupt was seen
drive_ptr:         .ds 2           ; holds 16bit 0/1 to address currently active UNIT flags (U register)
drvN_tgthead:      .ds 4           ;UNIT hold the target head to be positioned to
drvN_tgtcylinder:  .ds 4           ;UNIT hold target track to be positioned to
drvN_tgtsector:    .ds 4           ;UNIT hold target sector to be read/written next
drvN_activehead:   .ds 4           ;UNIT currently active head selected
drvN_activecyl:    .ds 4           ;UNIT currently active cylinder selected
drvN_curhead:      .ds 4           ;UNIT currently read head from disk
drvN_curtrk:       .ds 4           ;UNIT currently read trak from disk
drvN_storecyl:     .ds 4           ;UNIT holds addressed cylinder when drive is inactive
drvN_storehead:    .ds 4           ;UNIT holds addressed head when drive is inactive
drvN_savedtrk:     .ds 4           ;UNIT holds addressed cylinder when drive is inactive
drvN_savedhead:    .ds 4           ;UNIT holds addressed head when drive is inactive
                   .ds 4           ; unused
s1_errcode:        .ds 1           ; S1 completion code of operations
                   .ds 1           ; unused
selected_unit:     .ds 1           ; currently addressed UNIT (8 bit value of drive_ptr) 
drvN_disktype:     .ds 4           ;UNIT disk type
drvN_s2flags:      .ds 4           ;UNIT last S2 status of disk
dsj:               .ds 1           ; DSJ byte (see documentation)
drvN_disk_wornout: .ds 2           ;UNIT signal that disk might be no longer reliable and might need reforming
drv_softwrtprotect:.ds 2           ; preventive protection of disk due to errors
selftest_1         .ds 1           ; selftest status 1
selftest_2         .ds 1           ; selftest status 2
override_old_fmt:  .ds 1           ; override old format flag for formatting
format_interleave: .ds 1           ; interleave for format
format_head:       .ds 1           ; head to format
format_cylinder:   .ds 1           ; cylinder to format
format_filler:     .ds 1           ; data fill byte for format
curtrk:            .ds 1           ; current track as read from drive
curhead:           .ds 1           ; current head as read from drive
verify_seccnt:     .ds 2           ; 16bit counter for disk verify
seek_retrycnt:     .ds 1           ; counter for SEEK retry
drvstatus_retrycnt: .ds 1          ; counter for get disk status retry
fdc_status:        .ds 1           ; FDC status returned from controller
                                   ; (except bit 0,1,5, bit 7 is drive ready)
                                   ; also used as temporary
temp3              .ds 1           ; temporary save
trkpattern:        .ds 2           ; used in writedisk for formatting pattern index
tempx2             .ds 2           ; temporary X save
tempx:             .ds 2           ; temporary X save
dummy_buffer:      .ds 1           ; used in DRQ/HALTEN logic to reset DRQ cycle when
                                   ; nothing else addresses RAM
temp1:             .ds 1           ; temporary save
format_tgtcylinder: .ds 1          ; used in format, also temporary
temp4              .ds 2           ; temporary save
drvN_irqwearh:     .ds 4           ;UNIT counter for revolutions, set by INT on index pulse
drvN_irqwearm:     .ds 4
drvN_irqwearl:     .ds 4
drvN_wearh:        .ds 4           ;UNIT disk wear counters read from sector 17
drvN_wearm:        .ds 4
drvN_wearl:        .ds 4
drvN_updatewear:   .ds 4           ;UNIT flag to indicate that wear counters need to be written back to sector 17
drvN_selectmask:   .ds 4           ;UNIT drive selection mask
saveu:             .ds 2           ; used to store U register when another drive is to be selected than the one
                                   ; amigo expects, e.g. in checking disk wear level
format_specinterleave: .ds 1       ; temporary holds interleave in diskwrite
fmtcnt:            .ds 1           ; counter used in formatter
sector_numbers:    .ds 0x10        ; holds interleave table of sector numbers
cnt16:             .ds 2           ; 16bit counter used in interleave setting
drive_cnt:         .ds 1           ; used as disk marker in disk reforming
byte_1AA:          .ds 1           ; just written, never read
drv_stepdelta:     .ds 1           ; hold delta of steps between current and requested head position
readwear_retrycnt: .ds 1           ; retry counter for obtaining sector17 information
downloadbuf:       .ds 256         ; 256 byte code buffer for download command
                   .ds 325         ; unused
chkram_top:        .ds 1           ; top area to be checked by RAM selftest
                   .ds 12          ; stack area
stacktop:          .ds 1           ; initialization value of stack pointer

;============================================================
; FDC address space
                 .org      0x2000
FDC_port:        .ds       1           ; structure: str_cr 
                 .ds       1           ;            tr
                 .ds       1           ;            scr
                 .ds       1           ;            dr
				 .ds       8188        ; incomplete decoding, repeated

;============================================================
; GPIB address space

; Note: not addressed directly, rather by zero
; page DP=0x40
                 .org      0x4000
				 .ds       8           ; structure GPIB
                 .ds       8184        ; incomplete decoding, repeated

;============================================================
; control (writeonly) and status port (readonly
                 .org      0x6000
portcc           .ds       1
statuscc         equ       portcc
                 .ds       0x8191      ; incomplete decoding, repeated

;============================================================
; ROM starts here
                .org       0xE000
reset_vector:   lds        #stacktop        ; load stack top
                jsr        selftest_chk_ram ; test if RAM is okay
                bcs        fatal_error      ; not okay
                jsr        selftest_chk_rom ; test if ROM checksum is okay
                bcs        fatal_error      ; not okay
                jsr        selftest_chk_fdc ; test if FDC is present and TR and SCR regs are writeable
                bcs        selftest_fdc_chip_error ; not okay, set error code
                ldb        #5               ; constant 5 (confidence test passed)
                jsr        blink_led        ; blink 5 times
                bra        initialize_all

; cannot continue here on reset
fatal_error:    lda        #ctrl_led        ; enable LED
                sta        portcc           ; into port
                sta        portctrl_state   ; into state save
loc_E022:       bra        loc_E022         ; endless loop

;=============================================
rom_checksum:   .dw        0x5110
selftest_fdc_chip_error:
                lda        #0x88            ; error code 10001000 (Error bit set)
                sta        selftest_2
                lda        #9               ; FDC error 0100
                sta        selftest_1
                lda        portctrl_state   ; get ctrl state
                oraa       #ctrl_led        ; set LED on
                oraa       #ctrl_clridx     ; set CLRINDEX bit
                sta        portctrl_state
                sta        portcc

;=============================================
initialize_all: lda        #0x40            ; select HPIB through direct page
                tfr        a, dp
                jsr        init_gpib        ; initialize 8291
                lda        #2
                sta        dsj              ; init flag?
                lda        statuscc         ; check dual head operation
                bita       #status_dualhead
                bne        loc_E05C         ; no is  single head
                lda        #0x21            ; dualhead flag 1
                sta        drvN_selectmask+2
                lda        #0x11            ; dualhead flag 2
                sta        drvN_selectmask+3
                bra        device_clear
loc_E05C:       lda        #0               ; clear dual head flags
                sta        drvN_selectmask+2
                lda        #0
                sta        drvN_selectmask+3
				
;=============================================
device_clear:   ldx        #end_buf        ; start of ram to clear
                clra
loc_E06A:       sta        ,x+             ; clear it and advance
                cmpx        #dsj           ; until (excluding) DSJ
                bne        loc_E06A        ; loop
                jsr        fdc_restore_all_drives ; position all drives to track 0
                jsr        delay_5000      ; wait
                lda        portctrl_state  ; get port state
                oraa       #ctrl_clridx    ; set CLRINDEX bit
                sta        portctrl_state  ; store it
                sta        portcc          ; into port as well
                lda        dsj             ; get DSJ byte
                cmpa       #2
                bne        loc_E092        ; not just initialized
                jsr        fdc_spinup      ; start motor
                jsr        gpib_set_PP     ; set parallel poll
                jmp        mainloop        ; main loop
loc_E092:       jsr        gpib_set_PP     ; set parallel poll
                jsr        park_heads_at_17 ; position heads at trk 17
                jmp        mainloop        ; normal processing

;=============================================
; blink LED B times
blink_led:      aslb                       ; multiply B with 2 (on & off)
loc_E09C:       ldy        #62500          ; delay value
loc_E0A0:       leay       -1,y            ; long delay
                bne        loc_E0A0
                lda        #ctrl_led       ; ctrl LED bit
                eora       portctrl_state  ; toggle CTRL reg state
                sta        portcc          ; store ctrl reg
                sta        portctrl_state
                decb                       ; decrement blink loop
                bne        loc_E09C        ; loop
                rts

;=============================================
led_on:         lda        portctrl_state  ; get ctrl state
                oraa       #ctrl_led       ; set LED on
                sta        portctrl_state  ; store state
                sta        portcc          ; set CTRL port
                rts

;=============================================
led_off:        lda        portctrl_state  ; get ctrl state
                anda       #~ctrl_led      ; switch off LED
                sta        portctrl_state  ; store status
                sta        portcc          ; set ctrl port
                rts

;=============================================
; initialize GPIB controller 
; DP points to GPIB
init_gpib:      lda        #auxm_reset      ; issue Chip reset
                sta        gpib_auxm        ; initialize GPIB
                lda        #inte1_bi|inte1_bo|inte1_dec|inte1_apt
                sta        gpib_int1        ; enable BI, BO, DCAS, APT interrupts
                clrb                        ; clear int enable 2
                stb        gpib_int2        ; also disable DMA operation
                stb        gpib_spm         ; no serial poll bits
                lda        #am_adm0|am_adm1
                sta        gpib_am          ; set address mode 3 (extended talker/talker with APT check)
                lda        statuscc         ; get address switches
                anda        #status_addrsw0|status_addrsw1|status_addrsw2 
                sta        gpib_ad01        ; set as major primary address (0-7)
                lda        #ad01_ad1|ad01_ad2|ad01_ad3|ad01_ad4|ad01_ad5|ad01_dl|ad01_ars
                sta        gpib_ad01        ; set minor address 31, listener only
                lda        #0x22            ; preset internal counter to 2MHz
                sta        gpib_auxm
                lda        #0x80            ; no special AuxA bits
                sta        gpib_auxm
                lda        #0xA8            ; AuxB: enable active low interrupt
                sta        gpib_auxm
                stb        gpib_auxm        ; send immediate pon
                lda        gpib_ad01        ; read address 0
                coma                        ; complement address
                anda        #7              ; mask out bits 0-2
                oraa        #0x68           ; enable serial poll, bit 7-addr, sense 1
                sta        gpib_auxm
                lda        gpio_dio         ; actually read gpib_dio
                rts

;=============================================
; DRQ/Int logic:
; HaltEN=0:
;   never halt processor
;   ignore +DRQ
;   ignore +FDCInt
;   FIRQ=0 if HPIBInt=0
; HaltEn=1
;   halt processor if RAM or GPIB chip select (rising edge)
;   release processor if DRQ=1 or FIRQ=0
;   FIRQ=0 if DrRdy=0 or HPIBInt=0 or FDCInt=1

; handles GPIB Int / DRQ processing
firq_vector:    leas        3,s            ; drop CC, PC, won't ever return from FIRQ
                                           ; but return to caller saved on stack
                lda        portctrl_state  ; get ctrl port state
                anda        #~ctrl_halten  ; set HALTEN = 0 (will release FIRQ)
                sta        portctrl_state  ; set ctrl
                sta        portcc
                lda        sys_state        ; positive state?
                bpl        firq_process_fdc ; yes, this is processing of FDC / DRQ logic
                ldb        gpib_ad01        ; read GPIB interrupt status
                bpl        loc_E12E         ; INT flag not set? skip

; FIRQ was asserted by HPIBInt
; won't return to caller
                lds        #stacktop        ; reset stack pointer
                ldu        saveu            ; get saved U (drive selected by amigo)
                stu        drive_ptr        ; current drive
                ldb        #inte1_bi|inte1_bo|inte1_dec|inte1_apt
                stb        gpib_int1        ; reenable interrupts
                clr        sys_state        ; clear state
                jsr        fdc_select_curdrv ; select correct drive
                jmp        gpib_handle_int  ; handle GPIB interrupt

; FIRQ was asserted by FDCInt or DrRdy
loc_E12E:       ldb        statuscc         ; get drive ready flag
                bpl        firq_process_fdc ; DrRdy became 1?
                ldu        saveu            ; no, FIRQ because drv no longer ready
                stu        drive_ptr        ; reload drive in operation
                ldb        #inte1_bi|inte1_bo|inte1_dec|inte1_apt
                stb        gpib_int1        ; enable GPIB interrupts again
                clr        sys_state        ; clear state
                jsr        fdc_select_curdrv ; select current drive
                bra        loc_E14D         ; return status

; this is called when a  FDCINT is sent from FDC
; logic: disable GPIBINT, set HALTEN=1,
; and do a single read of RAM
; CPU halts until DRQ becomes 1; if so, it continues,
; e.g. by processing FDC DR
; at the end of operation FDCInt is generated and
; will trigger this code in FIRQ
firq_process_fdc:
                anda       #0x7F           ; mask low bits of state
                cmpa       #4              ; is fdc state > 4?
                lbge       gpib_errorstate ; yes, invalid state for FDC processing
loc_E14D:       lda        FDC_port.str_cr ; get FDC control reg
                coma                       ; complement (negative bus)
                                           ; mask error bits
                anda       #fdc_status23_lostdata|fdc_status23_crcerror|fdc_status23_recnf|fdc_status23_wrtprot
                sta        fdc_status      ; save status
                lda        statuscc        ; get drive status
                anda       #status_drrdy   ; mask highest bit
                oraa       fdc_status      ; put into error bits
                sta        fdc_status        ; save status
                jsr        fdc_terminate_no_int ; terminate current FDC command
                lda        #inte1_bi|inte1_bo|inte1_dec|inte1_apt
                sta        gpib_int1       ; reinitialize GPIB interrupt sources
                lda        fdc_status
                rts                        ; tricky code: fall back into routine that issued the FDC operation
                                           ; so DRQ transfers need to be called in a subroutine

;=============================================
; advance to the next sector on disk
; return CY=1 if at end of disk
fdc_nextsector: inc        drvN_tgtsector,u ; increment sector count
                lda        drvN_tgtsector,u ; is sector < 16?
                cmpa        #16
                bne        loc_E1A3        ; no, exit CY=0

fdc_nextsector1:
                clr        drvN_tgtsector,u ; set new sector=0
                tst        drvN_tgthead,u  ; check head
                bne        loc_E18A        ; is 1, toggle to 0
                lda        #1              ; was 0, set head=1
                sta        drvN_tgthead,u  
                bra        loc_E1A3        ; exit CY=0
loc_E18A:       clr        drvN_tgthead,u  ; set head=0
                inc        drvN_tgtcylinder,u ; increment cylinder
                lda        drvN_tgtcylinder,u ; get cylinder
                cmpa       #35             ; is it 35?
                bne        loc_E1A3        ; no, exit CY=0
                lda        #1              ; notify: at end of disk
                sta        drvN_endofdisk,u
                orcc       #cc_c           ; return CY=1
                rts
loc_E1A3:       andcc      #~cc_c          ; clear CY
                clr        drvN_endofdisk,u ; not at end of disk
                rts

;=============================================
fdc_movetrack_in:
                lda        drvN_curhead,u  ; is head=1
                bne        loc_E1BB        ; yes, skip
                lda        #1              ; select head 1
                sta        drvN_curhead,u  ; store it
                jsr        fdc_stepin      ; do a single step
                bra        loc_E1D9        ; exit with CY=0
loc_E1BB:       clr        drvN_curhead,u
                inc        drvN_curtrk,u   ; increment current track
                lda        drvN_curtrk,u   ; get it
                cmpa       #35             ; is last track reached?
                bne        loc_E1D6        ; no, do a step
                dec        drvN_curtrk,u   ; remain at last track
                inc        drvN_curhead,u
                orcc       #cc_c           ; exit with CY=1
                rts
loc_E1D6:       jsr        fdc_stepin      ; do a step
loc_E1D9:       andcc      #~cc_c          ; exit with CY=0
                rts
				
;=============================================
fdc_movetrack_out:
                lda        drvN_curhead,u  ; is head 0?
                beq        loc_E1EB        ; yes, toggle 0 1
                clr        drvN_curhead,u  ; no, set to 0
                jsr        fdc_stepout     ; step out and issue interrupt
                bra        loc_E20A        ; exit with CY=0
loc_E1EB:       inc        drvN_curhead,u  ; set head=1
                dec        drvN_curtrk,u   ; decrement current track
                lda        drvN_curtrk,u   ; get current track
                bpl        loc_E207        ; not yet at track 0, skip
                clr        drvN_curtrk,u   ; was at track 0, set currtrk=0
                clr        drvN_curhead,u  ; and curhead=0
                jsr        fdc_stepout     ; step out and issue interrupt
                orcc       #cc_c           ; exit with CY=1
                rts
loc_E207:       jsr        fdc_stepout     ; do step out
loc_E20A:       andcc      #~cc_c          ; clear CY
                rts
				
;=============================================
; start motor
fdc_spinup:     lda        portctrl_state  ; get ctrl port
                bita       #ctrl_motor     ; is motor already on?
                bne        loc_E22C        ; yes, skip
                oraa       #ctrl_motor     ; set motor on
                sta        portctrl_state  ; set state
                sta        portcc
                ldx        #55295          ; long delay (about 1 second)
loc_E21F:       jsr        nullsub_1       ; wait
                jsr        nullsub_1       ; wait
                leax       -1,x
                bne        loc_E21F        ; delay loop
                jsr        fdc_loadhead    ; load drive head
loc_E22C:       lda        #1              ; set flag motor on
                sta        motoron_flg
nullsub_1:      rts
				
;=============================================
; switch off drive motor
fdc_motoroff:   jsr        fdc_unloadhead
                lda        portctrl_state  ; get port state
                anda       #~ctrl_motor
                sta        portctrl_state  ; motor off
                sta        portcc
                clr        motoron_flg     ; signal motor off
                orcc       #cc_irq         ; disable IRQ
                rts

;=============================================
; wait until FDC ready and select current drive (specified by drive_ptr)
fdc_waitrdy_select_curdrv:
                jsr        fdc_waitrdy     ; wait until FDC is ready

;=============================================
; select current drive (specified by drive_ptr)
fdc_select_curdrv:
                pshs       a,b             ; save A,B
                ldb        drive_ptr+1     ; get drive # (0-3)
                andb       #3              ; mask out drive#
                cmpb       #0              ; is drive 0?
                bne        loc_E258        ; no, skip
                lda        #ctrl_drsel0    ; bitmask for drive 0
                bra        loc_E26C
loc_E258:       cmpb       #1              ; is drive 1?
                bne        loc_E260        ; no, skip
                lda        #ctrl_drsel1    ; bitmask for drive 1
                bra        loc_E26C
loc_E260:       cmpb       #2              ; is drive 2?
                bne        loc_E269        ; no, skip
                lda        drvN_selectmask+2 ; get mask for drive 2
                bra        loc_E26C
loc_E269:       lda        drvN_selectmask+3 ; get mask for drive 3
loc_E26C:       ldb        portctrl_state  ; get ctrl port
                andb       #ctrl_sidesel|ctrl_drsel1|ctrl_drsel0
                stb        temp1           ; save last selected drive/surface
                cmpa       temp1           ; same as requested?
                beq        loc_E2AC        ; yes, skip
                ldb        portctrl_state  ; get ctrl port
                bitb       #ctrl_hdld      ; is head loaded?
                beq        loc_E298        ; no, skip
                andb       #~ctrl_hdld     ; unload head
                stb        portctrl_state
                stb        portcc          ; set to port
                clr        fdc_headisloaded ; clear headload flag
                stx        tempx2          ; save X
                ldx        #0x100          ; delay for unload head
loc_E291:       leax       -1,x
                bne        loc_E291        ; loop
                ldx        tempx2          ; restore X
loc_E298:       ldb        portctrl_state  ; get state
                andb       #~ctrl_sidesel|ctrl_drsel1|ctrl_drsel0 ; mask out drive bits
                stb        portcc          ; store state
                stb        portctrl_state
                oraa       portctrl_state  ; put in requested drive bits
                sta        portctrl_state
                sta        portcc          ; set ctrl port
loc_E2AC:       lda        drvN_inactive,u ; is drive active?
                beq        loc_E2BD        ; yes, skip
                lda        drvN_storecyl,u ; get stored position
                asla                       ; *2
                adda       drvN_storehead,u ; add head
                bra        loc_E2C6        ; put into track register
loc_E2BD:       lda        drvN_activecyl,u ; get the current position
                asla                       ; *2
                adda       drvN_activehead,u ; + head
loc_E2C6:       coma                       ; negative logic
                sta        FDC_port.tr     ; set track
                puls       b,a
                rts
				
;=============================================
; select drive 0, side 0
fdc_select_drv0:
                ldy        #0x100          ; small delay
loc_E2D1:                                  ; ...
                leay        -1,y
                bne        loc_E2D1        ; loop
                lda        portctrl_state  ; get ctrl port
                anda        #~ctrl_sidesel|ctrl_drsel1|ctrl_drsel0
                sta        portctrl_state  ; drsel 0, drsel 1, drsidesel
                sta        portcc          ; set drive 0, side 0
                rts

;=============================================
; wait until FDC is no longer busy
fdc_waitrdy:    pshs       a               ; save A
                stx        tempx           ; save X
                ldx        #2              ; init long delay counter 0x00020000
                ldy        #0
loc_E2ED:       jsr        nullsub_2       ; wait
                lda        FDC_port.str_cr ; get FDC status
                bita       #fdc_status1_busy ; is busy?
                bne        loc_E302        ; not busy (negative logic!), exit
                leay        -1,y           ; wait
                bne        loc_E2ED
                leax        -1,x
                bne        loc_E2ED
; timing loop ended without command done
                jsr        fdc_terminate_no_int ; terminate with no interrupt
loc_E302:       puls        a              ; restore A, X, exit
                ldx        tempx
nullsub_2:      rts

;=============================================
; send force no-int command to terminate pending operation
fdc_terminate_no_int:
                lda        #fdcmd_forceint ; force interrupt (terminate with no interrupt)
                sta        FDC_port.str_cr ; set command
                jsr        fdc_waitrdy     ; wait until done
                rts

;=============================================
; return drive status in A, negative if        drive not ready
; set cur_trk (physical) and cur_side
fdc_getdrvstatus:
                lda        #4
                sta        drvstatus_retrycnt ; try 4 times
loc_E316:       jsr        fdc_readaddress_status ; get status of drive
                beq        loc_E322        ; is zero: drive ready and no error
                bmi        loc_E322        ; is negative: drive not ready
                dec        drvstatus_retrycnt ; drive is ready, and error bits were set
                bne        loc_E316        ; retry  4 times
loc_E322:       lda        curtrk          ; get current track
                bmi        loc_E334        ; negative, return status
                asr        curtrk          ; convert logical track to physical
                bcc        loc_E334        ; if LSBit was 1, set side bit
                lda        curhead
                oraa        #1             ; odd number of logical track is on side 1
                                           ; even is on side 0
                sta        curhead
loc_E334:       lda        fdc_status      ; get fdc status
                rts                        ; return to caller

;=============================================
; issue a READADDR to get track# and side# and the FDC status bits
; interacts with FIRQ code which returns to the caller. Thus, no endless loop
fdc_readaddress_status:
                lda        sys_state       ; get system state
                anda       #0x80           ; mask bit 7
                oraa       #3              ; set state 3
                sta        sys_state
                lda        FDC_port.str_cr ; read FDC status to acknowledge
                clra
                sta        gpib_int1       ; disable GPIB interrupts
                tst        fdc_headisloaded ; check whether head is loaded
                bne        loc_E350        ; no, load it
                jsr        fdc_loadhead    ; load head
loc_E350:       andcc      #~cc_firq       ; enable FIRQ
                lda        portctrl_state
                oraa       #ctrl_halten    ; set HALTEN
                sta        portctrl_state
                sta        portcc          ; thus, allow FDCInt to occur if drive is ready
                ldb        #fdcmd_readaddr ; FDC READ ADDRESS command
                                           ; will return 6 bytes in DR:
                                           ; 1 track address
                                           ; 2 side number
                                           ; 3 sector address (ignored)
                                           ; 4 sector length (ignored)
                                           ; 5 CRC1 (ignored)
                                           ; 6 CRC2 (ignored)
                stb        FDC_port.str_cr
                lda        dummy_buffer    ; dummy instruction: trigger HALT-FF
                nop                        ; DRQ releases CPU
                nop                        ; dummy
                lda        FDC_port.dr     ; read the 1st byte
                coma
                sta        curtrk          ; save it, trigger HALT-FF
                nop                        ; DRQ releases CPU
                nop                        ; dummy
                lda        FDC_port.dr     ; read 2nd byte
                coma
                sta        curhead         ; save it, trigger HALT-FF
loc_E377:       lda        FDC_port.dr     ; DRQ releases CPU, read 3rd-6th byte
                nop
                nop
                lda        dummy_buffer    ; trigger HALT-FF
                bra        loc_E377        ; DRQ releases CPU, loop until FIRQ stops this loop and
; returns to caller with FDC flags

;=============================================
; load drive head if drive ready
fdc_loadhead:   lda        statuscc        ; get status port
                bmi        locret_E39E     ; not drive ready, exit
; drive is ready, and contains a disk
                lda        portctrl_state
                oraa       #ctrl_hdld      ; load head
                sta        portctrl_state
                sta        portcc
                lda        #1              ; set flag head load
                sta        fdc_headisloaded
                ldy        #15000          ; delay
loc_E39A:       leay       -1,y
                bne        loc_E39A        ; wait for head load
locret_E39E:    rts

;=============================================
; unload drive head
fdc_unloadhead: lda        portctrl_state  ; get ctrl state
                anda       #~ctrl_hdld
                sta        portctrl_state  ; unload head
                sta        portcc
                clr        fdc_headisloaded ; clear flag head load
                stx        tempx2          ; save x
                ldx        #0x100          ; small delay
loc_E3B3:       leax       -1,x
                bne        loc_E3B3        ; loop
                ldx        tempx2          ; restore x
                rts

;=============================================
; do a step in (towards trk 69)
fdc_stepin:     pshs       a               ; save A
                lda        #fdcmd_stepin   ; issue STEPIN command, update track reg, load head
                sta        FDC_port.str_cr ; send command
                jsr        fdc_waitrdy     ; wait until ready
                puls       a               ; restore A
                rts

;=============================================
; do a step out (towards trk0)
fdc_stepout:    pshs       a               ; save A
                lda        #fdcmd_stepout  ; issue STEP OUT cmd, update track reg, load head
                sta        FDC_port.str_cr
                jsr        fdc_waitrdy     ; wait until command done
                puls       a               ; restore A
                rts

;=============================================
; position head to track 0
; return CY=1 if failure, 0 if success
fdc_restore_trk0:
                lda        #fdcmd_restore  ; Restore command
                sta        FDC_port.str_cr ; set command
                jsr        fdc_waitrdy     ; wait until done
                lda        FDC_port.str_cr
                bita       #fdc_status1_trk0 ; track0 reached?
                beq        loc_E3E7        ; yes, skip
                orcc       #cc_c           ; set CY=1 (failure)
                rts
loc_E3E7:       lda        #~0             ; set track register = 0
                sta        FDC_port.tr
                clra
                sta        drvN_activecyl,u ; clear active position
                sta        drvN_activehead,u
                sta        drvN_curtrk,u   ; clear current position
                sta        drvN_curhead,u
                sta        drvN_endofdisk,u ; clear end of disk flag
                andcc      #~cc_c          ; set CY=0 (success)
                rts

;=============================================
; position both drives to track 0
fdc_restore_all_drives:
                lda        #1              ; select drive 1
                sta        drive_ptr+1     ; note driveptr+0 was initialized to 0
loc_E409:       ldu        drive_ptr       ; get ptr to drive parameters
                jsr        fdc_waitrdy_select_curdrv ; select the correct drive
                jsr        fdc_restore_trk0 ; goto track 0
                dec        drive_ptr+1     ; point to drive 0
                bpl        loc_E409        ; loop for drives
                clr        drive_ptr+1     ; select drive 0
                ldu        drive_ptr       ; get current drive ptr
                rts
				
;=============================================
fdc_seek:       lda        #5              ; set seek retry count
                sta        seek_retrycnt
                lda        drvN_inactive,u ; is drive inactive?
                beq        loc_E43D        ; no, skip
                clr        drvN_inactive,u ; is now active
                lda        drvN_storecyl,u ; copy parameters
                sta        drvN_activecyl,u
                lda        drvN_storehead,u
                sta        drvN_activehead,u
loc_E43D:       lda        drvN_tgtcylinder,u ; target to reach
                cmpa       drvN_activecyl,u ; is position reached?
                bne        seek_reposition ; no, reposition
                lda        drvN_tgthead,u  ; is the right head selected?
                cmpa       drvN_activehead,u
                bne        seek_reposition ; no, reposition
                jmp        loc_E4D7        ; yes, seems we are done
seek_reposition:
                lda        drvN_curtrk,u   ; get current track
                asla                       ; shift left
                adda       drvN_curhead,u ; insert head
                coma                       ; negative logic
                sta        FDC_port.tr     ; store in fdc track register
                ldb        drvN_tgtcylinder,u ; get target to reach
                subb       drvN_activecyl,u ; subtract current position
                aslb                       ; shift left
                addb       drvN_tgthead,u ; add target head
                subb       drvN_activehead,u ; subtract current head
                stb        drv_stepdelta   ; store number of steps to do
                tstb                       ; is it zero?
                beq        loc_E4BB        ; yes, positioned right
                tfr        b, a            ; put delta into A
                bpl        loc_E47D        ; positive?
                coma                       ; no, complement
                                           ; set positive number of steps to do
loc_E47D:       lsra                       ; add steps/4 to current wear count
                lsra
                adda       drvN_irqwearl,u
                sta        drvN_irqwearl,u
                bcc        loc_E493
                inc        drvN_irqwearm,u
                bne        loc_E493
                inc        drvN_irqwearh,u
loc_E493:       lda        FDC_port.tr     ; get track register (current pos)
                coma                       ; negative logic
                pshs       b               ; save delta
                adda       ,s+             ; pop and add to track
                sta        temp1           ; save it in temporary (position to reach)
                anda       #1              ; mask out head
                sta        drvN_curhead,u  ; save into current head
                lda        temp1           ; get position
                lsra                       ; discard head
                sta        drvN_curtrk,u   ; save in current track
                lda        temp1           ; get position to reach
                coma                       ; negative logic
                sta        FDC_port.dr     ; position to reach in data register
                lda        #fdcmd_seek     ; issue SEEK command to FDC
                sta        FDC_port.str_cr
                jsr        fdc_waitrdy     ; wait until done
loc_E4BB:       lda        drvN_tgtcylinder,u ; copy target into active cylinder
                sta        drvN_activecyl,u
                asla                       ; shift left
                adda       drvN_tgthead,u  ; and add target head
                coma                       ; negative logic
                sta        FDC_port.tr     ; and store combined into track register
                lda        drvN_tgthead,u  ; copy target head into active head
                sta        drvN_activehead,u
seek_chkpos:    jsr        delay_5000      ; wait delay
loc_E4D7:       jsr        fdc_getdrvstatus ; get drive status
                bmi        locret_E53E     ; drive not ready? exit
                bne        loc_E508        ; error bit in status?
                lda        curtrk          ; get current track (set by fdc_getdrvstatus)
                bpl        loc_E50B        ; positive? we have a valid track
                bra        loc_E4F7        ; no, this track was marked defective
seek_miss:      ldb        drvN_tgtcylinder,u ; get target track
                subb       curtrk          ; subtract track where we are
                aslb                       ; shift left
                addb       drvN_tgthead,u  ; add target head
                subb       curhead         ; subtract surface where we are on
                stb        drv_stepdelta   ; set as new positioning delta
loc_E4F7:       tst        drv_stepdelta   ; if positive, skip
                bpl        loc_E503
                jsr        fdc_movetrack_out ; move a step towards trk 0
                bcs        loc_E508        ; error, reposition to track 0
                bra        seek_chkpos     ; recheck
loc_E503:       jsr        fdc_movetrack_in ; move a step towards track 69
                bcc        seek_chkpos     ; recheck
loc_E508:       jmp        seek_resettrk0  ; if error, reposition to track 0
loc_E50B:       cmpa       drvN_tgtcylinder,u ; is it the requested cylinder?
                bne        seek_miss       ; no, seek miss, reposition
                lda        curhead         ; get curside (from fdc_getdrvstatus)
                bita       #0x20           ; is bit 5 set?
                bne        seek_resettrk0  ; yes, reposition to trk 0 and try again
                cmpa       drvN_tgthead,u  ; is it the head to use?
                bne        seek_miss       ; no, seek miss, reposition
                lda        drvN_activecyl,u ; fine, we have found the position
                                           ; get active cylinder
                asla                       ; shiftleft
                adda       drvN_activehead,u ; put in active head
                coma                       ; negative logic
                sta        FDC_port.tr     ; store in track register
                clra                       ; return no error
                rts                        ; done
seek_resettrk0: jsr        fdc_restore_trk0 ; reposition to track 0
                bcs        loc_E53C        ; error? return error code 1
                dec        seek_retrycnt   ; decrement seek retry cnt
                lbne        seek_reposition ; tries left, redo
                lda        #0x20           ; return error 0x20
                rts
loc_E53C:       lda        #1              ; return error 1
locret_E53E:    rts

;=============================================
;read specified sector into buffer
fdc_readsector: lda        drvN_tgtsector,u ; get the sector# to read
                coma                       ; negative logic
                sta        FDC_port.scr    ; store in sector register
                clra                       ; disable GPIB interrupts
                sta        gpib_int1
                sta        sys_state       ; set state = 0
                tst        fdc_headisloaded ; if head is not loaded, load it
                bne        loc_E555
                jsr        fdc_loadhead
loc_E555:       lda        FDC_port.str_cr ; drain FDC status
                lda        portctrl_state
                oraa        #ctrl_halten   ; set HALTEN=1
                sta        portctrl_state
                sta        portcc
                andcc        #~cc_firq     ; enable FIRQ
                lda        #fdcmd_readsec  ; issue read single sector, check head
                sta        FDC_port.str_cr
                lda        dummy_buffer    ; trigger wait for FDC
                ldx        #diskbuf        ; DRQ occurred, load buffer index
                nop
loc_E571:       lda        FDC_port.dr     ; read data
                coma                       ; negative logic
                sta        ,x+             ; store in buffer, stop CPU to wait for DRQ
                bra        loc_E571        ; DRQ seen, loop until FIRQ

;=============================================
fdc_write_sector:
                lda        drvN_tgtsector,u ; get target sector
                coma
                sta        FDC_port.scr    ; store in sector register
                lda        #1
                sta        sys_state       ; set state=1
                tst        fdc_headisloaded ; if not head loaded, load it
                bne        loc_E58E
                jsr        fdc_loadhead
loc_E58E:       lda        FDC_port.str_cr ; drain FDC status register
                lda        statuscc        ; get status port
                bita       #status_drwpt   ; write protect?
                bne        loc_E5BF        ; yes, skip
                tst        drv_softwrtprotect,u ; soft write protect?
                bne        loc_E5BF        ; yes, skip
                clra                       ; disable GPIB interrupts
                sta        gpib_int1
                andcc      #~cc_firq       ; enable FIRQ
                lda        portctrl_state  ; set HALTEN=1
                oraa       #ctrl_halten
                sta        portcc
                ldb        #fdcmd_writesec ; issue writesector command
                stb        FDC_port.str_cr ; to fdc
                sta        portctrl_state  ; trigger HALT-FF
                ldx        #diskbuf        ; load start of disk buffer     
                nop
loc_E5B7:       lda        ,x+             ; get byte from buffer
                coma
                sta        FDC_port.dr     ; send to disk
                bra        loc_E5B7        ; loop until FIRQ terminate this
loc_E5BF:       lda        statuscc        ; get status port
                anda       #status_drrdy   ; mask drive ready (as amigo_s2_attention)
                oraa       #amigo_s2_wrtprotect ; set write protect
loc_E5C6:       sta        fdc_status      ; into FDC status
                rts

;=============================================
; build list of sectors in a track
; depending on track/head and interleave
setup_sector_table:
                ldx        #0x10           ; counter for 16 bytes
                lda        #0xFF           ; not-used value for table
loc_E5CF:       sta        sector_numbers-1,x ; mark sector number buffer slots as unset
                leax       -1,x
                bne        loc_E5CF        ; loop
                tst        format_cylinder ; cylinder <0? exit
                bmi        locret_E60B
                ldx        #interleave_factors ; get table index
                lda        format_interleave ; get interleave
                ldb        a,x             ; get increment for interleave
                lda        format_cylinder ; get cylinder
                asla                       ; *2
                adda       format_head     ; add head
                mul                        ; multiply with interleave increment
                comb                       ; complement result
                andb       #0xF            ; in range 0-15
                                           ; note: this effectively permutates the starting sector
                                           ; after the index hole for each track, hopefully
                                           ; shifting the track to the previous one on a way
                                           ; that a track step and read will not require another
                                           ; complete revolution.
                clra                       ; clear A
                ldx        #sector_numbers ; get interleave table
loc_E5F3:       tst        b,x             ; check entry in sector table
                bpl        loc_E604        ; if positive, skip
                sta        b,x             ; store the sector number
                subb       format_interleave ; subtract interleave
                andb       #0xF            ; restrict to range 0-15
                inca                       ; next sector number
                cmpa       #0x10           ; at last sector?
                bne        loc_E5F3        ; no, loop
                rts                        ; done
loc_E604:       decb                       ; we have a clash, decrement sector position
                bpl        loc_E5F3        ; still positive, try again finding a free slot
                ldb        #0xF            ; no, set to 15
                bra        loc_E5F3        ; loop
locret_E60B:    rts

;=============================================
interleave_factors:
                .db        10
                .db        10
                .db        10
                .db        8
                .db        10
                .db        6
                .db        6
                .db        4
                .db        10
                .db        2
                .db        2
                .db        0
                .db        3
                .db        0
                .db        1
                .db        0

;=============================================
; format the current track
fdc_format:     lda        #2
                sta        sys_state       ; set state=2
                jsr        setup_sector_table ; setup sector numbers
                lda        format_cylinder ; get cylinder to format
                bmi        loc_E63E        ; negative, skip
                asla                       ; shift left
                ldb        format_head     ; get head
                andb       #1              ; mask bit 0
                pshs       b               ; save
                adda       ,s+             ; add to cylinder
                sta        format_cylinder ; store physical cylinder
                lda        format_head     ; get head
                anda       #0xFE           ; discard head number (leave D bit)
                sta        format_head     ; save
loc_E63E:       ldx        #0x10           ; sector counter
                tst        fdc_headisloaded ; if head not loaded, load it
                bne        loc_E649
                jsr        fdc_loadhead
loc_E649:       lda        FDC_port.str_cr ; drain FDC status
                lda        statuscc
                bita       #status_drwpt   ; write protect?
                lbne       loc_E7F9        ; yes, exit set write protect
                tst        drv_softwrtprotect,u
                lbne       loc_E7F9        ; yes, exit set write protect
                clra
                sta        gpib_int1        ; disable GPIB interrupts
                lda        FDC_port.str_cr  ; drain fdc status
                andcc      #~cc_firq        ; enable FIRQ
                lda        portctrl_state
                oraa       #ctrl_halten     ; enable HALTEN
                sta        portcc
                ldb        #fdcmd_writetrk  ; issue WRITE TRACK cmd
                stb        FDC_port.str_cr
                sta        portctrl_state   ; start DRQ logic
                lda        #32              ; 32 bytes of GAP1
                ldb        #~0x4E
; time to explain...
; format of track: modified IBM System 34 format
; modified: some gap lengths different, missing GAP5, sector 17
; 32  bytes of 0x4E (GAP1)
; for each sector:
;   12  bytes of 0x00 (GAP1)
;   3   bytes of 0xF5 (ID AM Sync)
;   1   byte  of 0xFE (ID AM)
;   1   byte  of 0xXX (Cylinder)
;   1   byte  of 0xXX (Head: 0-1, 0x20-0x21)
;   1   byte  of 0xXX (Sector)
;   1   byte  of 0x01 (Sector length: 0=128, 1=256,...)
;   1   byte  of 0xF7 (2 byte CRC)
;   22  bytes of 0x4E (GAP2)
;   12  bytes of 0x00 (GAP2)
;   3   bytes of 0xF5 (ID AM sync)
;   1   byte  of 0xFB (Data AM)
;   256 bytes of 0xXX (data, fill byte)
;   1   byte  of 0xF7 (2 byte CRC)
;   46  bytes of 0x4E (GAP3)
;
; last sector as the others, but sector#=17
; and sector length=0 (128 bytes)
; containing:
;   3   bytes of disk wear count
;   1   byte  of 0xFF (initial value)
;   1   byte  of 0xA4 (initial value)
;   1   byte  of 0xEA (initial value)
;   122 bytea of 0xXX (filler)
loc_E679:       stb        FDC_port.dr     ; store data
                deca                       ; decrement counter
                sta        dummy_buffer    ; reenable DRQ logic
                bne        loc_E679        ; loop
                ldb        #~0             ; part 2 of GAP1
format_sector:  lda        #12             ; counter
loc_E686:       stb        FDC_port.dr     ; store data
                deca                       ; decrement counter
                sta        dummy_buffer    ; reenable DRQ logic
                bne        loc_E686        ; loop
                lda        #3
                ldb        #~0xF5          ; ID AM sync
loc_E693:       stb        FDC_port.dr     ; store data
                deca                       ; decrement counter
                sta        dummy_buffer    ; reenable DRQ logic
                bne        loc_E693        ; loop
                ldb        #~0xFE          ; ID AM
                stb        FDC_port.dr     ; store data
                ldb        format_cylinder ; reenable DRQ logic
                comb                       ; negative logic
                nop
                stb        FDC_port.dr     ; store track#
                ldb        format_head     ; reenable DRQ logic
                comb                       ; negative logic
                nop
                stb        FDC_port.dr     ; store head#
                ldb        sector_number-1,x ; get sector# from table
                comb                       ; negative logic
                nop
                stb        FDC_port.dr     ; store data
                sta        dummy_buffer    ; reenable DRQ logic
                ldb        #~1             ; sector length (256)
                nop
                stb        FDC_port.dr     ; store data
                sta        dummy_buffer    ; reenable DRQ logic
                ldb        #~0xF7          ; 2 byte CRC
                nop
                stb        FDC_port.dr     ; store data
                sta        dummy_buffer    ; reenable DRQ logic
                lda        #22
                ldb        #~0x4E          ; ID GAP2
loc_E6D3:       stb        FDC_port.dr     ; store data
                deca                       ; decrement count
                sta        dummy_buffer    ; reenable DRQ logic
                bne        loc_E6D3        ; loop
                lda        #12
                ldb        #~0             ; ID GAP2
loc_E6E0:       stb        FDC_port.dr     ; store data
                deca                       ; decrement counter
                sta        dummy_buffer    ; reenable DRQ logic
                bne        loc_E6E0        ; loop
                lda        #3
                ldb        #~0xF5          ; ID AM Sync
loc_E6ED:       stb        FDC_port.dr     ; store data
                deca                       ; decrement counter
                sta        dummy_buffer    ; reenable DRQ logic
                bne        loc_E6ED        ; loop
                ldb        #~0xFB          ; DATA AM
                stb        FDC_port.dr     ; store data
                clra                       ; counter=256
                ldb        format_filler   ; get filler byte
                comb                       ; negative logic
                nop
loc_E701:       stb        FDC_port.dr     ; store data
                deca                       ; decrement
                sta        dummy_buffer    ; reenable DRQ logic
                bne        loc_E701        ; loop
                ldb        #~0xF7          ; 2 byte CRC
                stb        FDC_port.dr     ; store data
                sta        dummy_buffer    ; reenable DRQ logic
                ldb        #45
                lda        #~0x4E
loc_E716:       sta        FDC_port.dr     ; store data
                decb                       ; decrement
                stb        dummy_buffer    ; reenable DRQ logic
                bpl        loc_E716        ; loop
                leax        -1,x           ; decrement sector count
                lbne        format_sector  ; not all sectors, loop
                                           ; note: B is now FF, as required for part2 of GAP1
                ldb        #~0
                lda        #12             ; GAP1 for sector 17, if required
loc_E729:       stb        FDC_port.dr     ; store data
                deca                       ; decrement
                sta        dummy_buffer    ; reenable DRQ logic
                bne        loc_E729        ; loop
                lda        #3
                ldb        #~0xF5          ; ID AM Sync
loc_E736:       stb        FDC_port.dr     ; store data
                deca                       ; decrement
                sta        dummy_buffer    ; reenable DRQ logic
                bne        loc_E736        ; loop
                ldb        #~0xFE          ; ID AM
                stb        FDC_port.dr     ; store data
                ldb        format_cylinder ; reenable DRQ logic
                comb                       ; negative logic
                nop
                stb        FDC_port.dr     ; store cylinder#
                ldb        format_head     ; reenable DRQ logic
                comb                       ; negative logic
                nop
                stb        FDC_port.dr     ; store head#
                sta        dummy_buffer    ; reenable DRQ logic
                ldb        #17             ; load sector# 17
                comb
                stb        FDC_port.dr     ; store sector#
                sta        dummy_buffer    ; reenable DRQ logic
                ldb        #~0             ; sector length 0 (128 bytes)
                nop
                stb        FDC_port.dr     ; store data
                sta        dummy_buffer    ; reenable DRQ logic
                ldb        #~0xF7          ; 2 byte CRC
                nop
                stb        FDC_port.dr     ; store data
                sta        dummy_buffer    ; reenable DRQ logic
                lda        #22
                ldb        #~0x4E          ; GAP2
loc_E776:       stb        FDC_port.dr     ; store data
                deca                       ; decrement
                sta        dummy_buffer    ; reenable DRQ logic
                bne        loc_E776        ; loop
                lda        #12
                ldb        #~0             ; GAP2
loc_E783:       stb        FDC_port.dr     ; store data
                deca
                sta        dummy_buffer
                bne        loc_E783
                lda        #3
                ldb        #~0xF5          ; ID AM Sync
loc_E790:       stb        FDC_port.dr     ; store data
                deca                       ; decrement
                sta        dummy_buffer    ; reenable DRQ logic
                bne        loc_E790        ; loop
                ldb        #~0xFB          ; DATA AM
                stb        FDC_port.dr     ; store data
                ldb        drvN_wearh,u    ; reenable DRQ logic
                comb                       ; negative logic
                nop
                stb        FDC_port.dr     ; write 3 bytes of disk wear count
                ldb        drvN_wearm,u
                comb
                nop
                stb        FDC_port.dr
                ldb        drvN_wearl,u
                comb
                nop
                stb        FDC_port.dr
                ldb        #~0xFF          ; store 0xFF
                stb        dummy_buffer    ; reenable DRQ logic
                comb                       ; negative logic
                nop
                stb        FDC_port.dr     ; store data
                ldb        #~0xA4          ; store 0xA4
                stb        dummy_buffer    ; reenable DRQ logic
                comb
                nop
                stb        FDC_port.dr     ; store data
                ldb        #~0xEA          ; store 0xEA
                stb        dummy_buffer    ; reenable DRQ logic
                comb                       ; negative logic
                nop
                stb        FDC_port.dr     ; store data
                lda        #122
                ldb        format_filler   ; get filler
                comb
                nop
loc_E7DE:       stb        FDC_port.dr     ; store data
                deca                       ; decrement
                sta        dummy_buffer    ; reenable DRQ logic
                bne        loc_E7DE        ; loop
                ldb        #~0xF7          ; 2 byte CRC
                stb        FDC_port.dr     ; store data
                sta        dummy_buffer    ; reenable DRQ logic
                ldb        #~0x4E          ; GAP4
loc_E7F1:       stb        FDC_port.dr     ; store data
                sta        dummy_buffer    ; reenable DRQ logic
                bra        loc_E7F1        ; loop until FDC interrupt (end of track)
loc_E7F9:       lda        statuscc        ; get drive ready
                anda        #status_drrdy  ; mask only bit 7
                oraa        #amigo_s2_wrtprotect ; set write protect
                sta        fdc_status      ; store status
                rts

;=============================================
; format the entire disk
fdc_format_disk:
                jsr        fdc_getdrvstatus ; check disk status
                lbmi        locret_E91D    ; drive not ready? exit
                bne        loc_E835        ; errors, skip
                lda        drvN_selectmask,u ; drive selected?
                bne        loc_E844        ; yes, skip
                clr        drvN_tgthead,u  ; clear target position
                clr        drvN_tgtcylinder,u
                clr        drvN_tgtsector,u
                jsr        fdc_seek        ; seek to track 0, head 0, sector 0
                bne        loc_E835        ; error, exit
                lda        #2
                sta        sys_state       ; set state=2
                jsr        find_sector17   ; check whether sector 17 exists
                bne        loc_E835        ; no, skip
                lda        #1
                sta        has_sec17       ; flag: have wear data from sector17
                bra        loc_E844        ; skip
loc_E835:       lda        #0xFE           ; errors reading, set wear count very high
                sta        drvN_wearh,u
                clra
                sta        drvN_wearm,u
                sta        drvN_wearl,u
loc_E844:       jsr        fdc_restore_trk0 ; position to track 0
                clr        drvN_tgthead,u  ; clear position
                clr        drvN_tgtcylinder,u
                clr        drvN_tgtsector,u
loc_E853:       jsr        fdc_movetrack_in ; position to head 1
                lda        drvN_curtrk,u   ; get current track
                cmpa        #17            ; is it 17? (parking position)
                bne        loc_E853        ; no, loop
                jsr        delay_5000      ; wait some time
                jsr        fdc_checkmotorspeed ; check motor speed
                bcc        loc_E86C        ; if okay, skip
                lda        #amigo_s2_wrtprotect ; return WRITE PROTECT status (!)
                sta        fdc_status
                rts
loc_E86C:       jsr        fdc_restore_trk0 ; go back to track 0
loc_E86F:       jsr        delay_5000      ; wait some time
                tst        override_old_fmt ; check format override
                bne        loc_E893        ; if set, skip
                jsr        fdc_getdrvstatus ; not override
                                           ; get drive status
                lbmi       loc_E914        ; if drive not ready, exit
                bne        loc_E893        ; if error, skip
                lda        curhead         ; get current head
                bmi        loc_E889        ; negative, skip
                bita       #0x20           ; is D bit set?
                beq        loc_E893        ; no, skip
loc_E889:       lda        #0xFF           ; set cylinder and head to invalid
                sta        format_cylinder
                sta        format_head
                bra        loc_E8A1        ; continue
loc_E893:       lda        drvN_tgtcylinder,u ; get target
                sta        format_cylinder ; store it
                lda        drvN_tgthead,u
                sta        format_head
loc_E8A1:       jsr        fdc_format      ; format that track
                bne        loc_E914        ; if error, exit
                ldy        #0x89           ; small delay
loc_E8AA:       leay       -1,y
                bne        loc_E8AA        ; loop
                jsr        fdc_movetrack_in ; next track
                bcs        loc_E8BD        ; if end of disk, skip
                tst        format_cylinder ; cylinder is negative?
                bmi        loc_E86F        ; loop
                jsr        fdc_nextsector1 ; advance to next secotr
                bra        loc_E86F        ; loop
loc_E8BD:       jsr        fdc_restore_trk0 ; go back to trk 0
                jsr        delay_5000      ; wait some time
                clra
                sta        drvN_tgtcylinder,u ; reset target
                sta        drvN_tgthead,u
                sta        drvN_tgtsector,u
                jsr        fdc_seek        ; position to there
                bne        loc_E914        ; if error, exit
                lda        drvN_selectmask,u ; get drive select
                bne        loc_E8F0        ; set, skip
                tst        has_sec17       ; had sector 17?
                bne        loc_E8F0        ; yes, skip
                lda        #0xE7           ; set a rather high value for wear level
                sta        drvN_wearh,u
                clr        drvN_wearm,u
                clr        drvN_wearl,u
                bra        loc_E906        ; skip
loc_E8F0:       lda        #90             ; add some wear to the current value
                adda       drvN_wearl,u
                sta        drvN_wearl,u
                bcc        loc_E906
                inc        drvN_wearm,u
                bne        loc_E906
                inc        drvN_wearh,u
loc_E906:       clra                       ; disable GPIB interrupt
                sta        gpib_int1
                sta        sys_state       ; set state=0
                jsr        write_sector17  ; store the current wear count
                clra
                sta        drvN_selectmask,u ; unselect disk, return error=0
loc_E914:       sta        dummy_buffer    ; save A
                clr        has_sec17       ; clear flag for sector17
                lda        dummy_buffer    ; restore A
locret_E91D:    rts                        ; done

;=============================================
verify_sectors: tst        drvN_tgtsector,u ; is target sector=0?
                bne        loc_E929        ; no, skip
                jsr        fdc_seek        ; reposition
                bne        locret_E93E     ; if error, exit
loc_E929:       jsr        fdc_readsector  ; read a sector
                bne        locret_E93E     ; if error, eixt
                jsr        fdc_nextsector  ; advance to next sector
                bcs        loc_E93D        ; if end of disk, exit
                ldx        verify_seccnt   ; get sector counter
                leax       -1,x            ; decrement
                stx        verify_seccnt   ; store sector counter
                bne        verify_sectors  ; not finished, loop
loc_E93D:       clra                       ; finished, no error
locret_E93E:    rts

;=============================================
; decode the error from drive in A
; bit 7: no disc/drive not ready
; bit 6: write protect
; bit 5: track not found
; bit 4: sector not found
; bit 3: CRC error on read or verify
; bit 2: retryable HW error
; bit 1: unused
; bit 0: drive fault
set_s2_error:   bita       #0x40           ; test bit 6
                beq        loc_E952        ; not set, skip
                ldb        #amigo_s1_status2_err ; status 2 error
                stb        s1_errcode
                ldb        #amigo_s2_wrtprotect ; write protect
                orab       drvN_s2flags,u  ; add to S2 bits
                stb        drvN_s2flags,u
loc_E952:       bita       #0x20           ; test bit 5
                beq        loc_E95B        ; not set, skip
                ldb        #amigo_s1_cylcmp_err ; cylinder not found
                stb        s1_errcode
loc_E95B:       bita       #0x10           ; test bit 4
                beq        loc_E964        ; not set, skip
                ldb        #amigo_s1_seccmp_err ; sector not found
                stb        s1_errcode
loc_E964:       bita       #8              ; test bit 3
                beq        loc_E96D        ; not set, skip
                ldb        #amigo_s1_data_err ; data error
                stb        s1_errcode
loc_E96D:       bita       #4              ; test bit 2
                beq        loc_E976        ; not set, skip
                ldb        #amigo_s1_rtry_hw_err ; retryable HW error
                stb        s1_errcode
loc_E976:       bita       #0x80           ; test bit 7
                beq        loc_E995        ; not set, skip
                ldb        #amigo_s1_status2_err ; status 2 error
                stb        s1_errcode
                ldb        #0x80           ; set status 2 error flags
                orab       drvN_disktype,u
                stb        drvN_disktype,u
                ldb        #amigo_s2_drivenotready ; drive not ready (no disc)
                orab       drvN_s2flags,u
                andb       #~amigo_s2_wrtprotect ; unset write protect
                stb        drvN_s2flags,u
loc_E995:       bita       #1              ; test bit 0
                beq        locret_E9B2     ; not set, exit
                ldb        #amigo_s1_drvatt ; drive attention
                stb        s1_errcode
                ldb        drvN_disktype,u
                orab       #0x80           ; set error bit for S2 error
                stb        drvN_disktype,u
                ldb        drvN_s2flags,u
                orab       #amigo_s2_drvfault ; drive defective
                stb        drvN_s2flags,u
locret_E9B2:    rts

;=============================================
; main processing loop
; unless GPIB becomes active,
; check disk quality if disk is spinning,
; update the wear counters on disk
; and otherwise just wait for a request from GPIB
mainloop:       ldx        #0xFFF0         ; large counter
                ldy        #0xFFF0         ; large counter
                tst        fdc_headisloaded ; is head loaded?
                bne        loc_E9C3        ; no, skip
                ldy        #0x5A           ; smaller delay
loc_E9C3:       tst        gpib_apt_flag   ; was last command interrupted by readdressing?
                bne        select_secondary ; yes, skip
                lda        #4
                sta        sys_state       ; set state=4
loc_E9CD:       tst        fdc_headisloaded ; is head loaded?
                beq        loc_E9E4        ; no, skip
                andcc      #~cc_irq        ; reenable IRQ
                leay       -1,y            ; decrement Y delay
                bne        loc_EA07        ; not yet zero, continue with GPIB
                                           ; yes, timeout occurred, handle it
                jsr        fdc_chk_diskquality ; check whether disks are still usable
                jsr        fdc_unloadhead  ; unload head
                ldy        #0x5A           ; small delay
                orcc       #cc_irq         ; disable IRQ
loc_E9E4:       lda        drvN_disk_wornout ; is one of the disks worn out?
                oraa       drvN_disk_wornout+1
                beq        loc_E9F1        ; no, skip
                jsr        rfdc_reformdisk ; there is a disk of suspicious quality
                                           ; try reseating it
                bra        mainloop        ; loop
loc_E9F1:       tst        motoron_flg     ; is motor on?
                beq        loc_EA07        ; no, skip
                leax       -1,x            ; delay loop
                bne        loc_EA07
                leay       -1,y            ; more delay
                bne        loc_EA07        ; timeout
                jsr        park_heads_at_17 ; park heads
                jsr        fdc_motoroff    ; set motor off
                jsr        fdc_select_drv0 ; select drive 0
loc_EA07:       lda        gpib_ad01       ; get GPIB INT flag
                bpl        loc_E9CD        ; no int from GPIB, skip
gpib_handle_int:
                lda        gpib_int1
                bita       #ints1_dec      ; Device clear active state?
                lbne       do_dcas         ; yes, skip
loc_EA13:       bita       #ints1_apt      ; must recognize secondary address
                bne        select_secondary
loc_EA17:       bita       #ints1_bi       ; byte input interrupt
                beq        loc_E9CD        ; no, skip
                lda        gpio_dio        ; actually gpib_dio: read byte
                bra        loc_E9CD
select_secondary:
                clr        gpib_apt_flag
                orcc       #cc_irq         ; disable interrupt
                lda        gpib_am         ; read address status
                bita       #as_lpas        ; has seen listener primary?
                beq        loc_EA30        ; no, skip
                jsr        amigo_listen    ; handle amigo listen command
                jmp        mainloop
loc_EA30:       bita       #as_tpas        ; has seen talker primary?
                bne        loc_EA38        ; yes, skip
                bita       #as_ta          ; is talker addressed?
                beq        loc_E9CD        ; no, skip
loc_EA38:       bita       #as_mjmn        ; is a talker: major or minor mode?
                bne        loc_EA42        ; minor mode: skip
                jsr        amigo_talk      ; handle amigo talk commands
                jmp        mainloop
loc_EA42:       lda        gpib_auxm       ; get passthru value (secondary address)
                anda       #0x1F           ; mask only 5 bits
                sta        fdc_status      ; save secondary address
                                           ; (fdc_status used as temp)
                lda        gpib_ad01       ; get address
                anda       #0x1F           ; mask 5 bits
                cmpa       fdc_status      ; compary with secondary presented
                bne        loc_EAA0        ; not the same, skip
                jsr        gpib_validsecondary ; secondary is valid
; special command prim=31, sec=addr, ID1, ID4+EOI
loc_EA55:       jsr        gpib_assert_minortalker ; verify we are in minor mode
                lbcc       loc_E9CD        ; no, ignore
                lda        gpib_ad01       ; loop until GPIB interrupt
                bpl        loc_EA55
                ldb        gpib_int1       ; get interrupt status
                bitb       #ints1_bo       ; request to send a byte?
                bne        loc_EA73        ; yes, continue
                bitb       #ints1_dec      ; device clear reaceived?
                lbne       do_dcas         ; yes, do device clear
                bitb       #ints1_apt      ; verify secondary?
                bne        select_secondary ; yes, do it
                jmp        loc_EA17        ; go drain input
loc_EA73:       lda        #1              ; ID1
                sta        gpio_dio        ; send ID1 byte
                lda        #4              ; ID4
loc_EA79:       jsr        gpib_assert_minortalker ; verify still in minor mode
                lbcc       loc_E9C3        ; no, ignore
                ldb        gpib_ad01       ; get interrupt flag
                bpl        loc_EA79        ; wait until interrupt
                ldb        gpib_int1       ; get interrupt status
                bitb       #ints1_bo       ; request to send a byte?
                bne        loc_EA97        ; yes, continue
                bitb       #ints1_dec      ; device clear?
                lbne       do_dcas         ; do device clear
                bitb        #ints1_apt     ; verify secondary?
                beq        loc_EA17        ; no drain input
                jmp        select_secondary ; handle secondary
; send byte with EOI
loc_EA97:       ldb        #6              ; set EOI
                stb        gpib_auxm
                sta        gpio_dio        ; send ID4 byte
                jmp        loc_E9CD        ; return to main loop
loc_EAA0:       jsr        gpib_invalidsecondary
                jmp        loc_E9C3

;=============================================
; verify we are in minor mode
gpib_assert_minortalker:
                ldb        gpib_am         ; get address mode
                bitb       #as_tpas        ; talker addressed?
                bne        loc_EAB0        ; yes, continue
                bitb       #as_ta          ; talker addressed?
                beq        loc_EAB7        ; no, exit with CY=0
loc_EAB0:       bitb       #as_mjmn        ; major mode?
                beq        loc_EAB7        ; yes, exit with CY=0
                orcc       #cc_c           ; return with CY=1
                rts
loc_EAB7:       andcc      #~cc_c          ; clear CY
                rts

;=============================================
; IRQ is generated on index pulse of FDC if enabled
; provided CLRIndex is not set (bit3 of portctrl)
irq_vector:     inc        drvN_irqwearl,u ; increment index pulse counter (24 bit)
                bne        loc_EACA
                inc        drvN_irqwearm,u
                bne        loc_EACA
                inc        drvN_irqwearh,u
loc_EACA:       lda        portctrl_state  ; get port ctrl
                anda        #~ctrl_clridx  ; reset index flipflop (IRQ generator)
                sta        portcc          ; by setting clear input to 0
                oraa        #ctrl_clridx   ; set clear input to 1 again
                sta        portcc
                rti                        ; return from interrupt

;=============================================
; check whether disks are still usable
fdc_chk_diskquality:
                orcc       #cc_irq         ; disable IRQ
                lda        #0x80 
                sta        sys_state       ; set state=0x80
                stu        saveu           ; save U
                lda        drvN_irqwearh,u ;  get high wearlevel
                bne        loc_EAF0        ; not zero
                lda        drvN_irqwearm,u ; get medium wear level
                cmpa       #4              ; about 1000
                bcs        loc_EB0A        ; less than 1000
loc_EAF0:       andcc      #~cc_firq       ; disable FIRQ
                jsr        fdc_parkdrive   ; park drive and update wear info
                orcc       #cc_firq        ; reenable FIRQ
                lda        drvN_wearh,u    ; is drive wear > f7?
                cmpa       #0xF7
                bcc        loc_EB04        ; yes, skip
                jsr        fdc_check_worndisk ; measure speed of disk
                bra        loc_EB0A
loc_EB04:       lda        #1              ; shouldn't use this disk any more
                sta        drvN_disk_wornout,u
loc_EB0A:       lda        drive_ptr+1     ; toggle drive selecton
                coma
                anda       #1
                sta        drive_ptr+1
                ldu        drive_ptr       ; select the other drive
                lda        drvN_irqwearh,u ; get high wear level
                bne        loc_EB24        ; not zero, skip
                lda        drvN_irqwearm,u ; get medium wear level
                cmpa       #4              ; about 1000?
                bcs        loc_EB43        ; less, skip
loc_EB24:       orcc       #cc_firq        ; disable FIRQ
                jsr        fdc_waitrdy_select_curdrv ; wait for drive
                andcc      #~cc_firq       ; enable FIRQ
                jsr        fdc_parkdrive   ; park it and update wear info
                orcc       #cc_firq        ; disable FIRQ
                lda        drvN_wearh,u    ; is drive wear > F7?
                cmpa       #0xF7
                bcc        loc_EB3D        ; yes, skip
                jsr        fdc_check_worndisk ; measure speed of disk
                bra        loc_EB43        ; continue
loc_EB3D:       lda        #1              ; mark disk as worn out
                sta        drvN_disk_wornout,u
loc_EB43:       ldu        saveu           ; restore old selected disk
                stu        drive_ptr
                orcc       #cc_firq        ; disable FIRQ
                jsr        fdc_waitrdy_select_curdrv ; wait for disk ready
                clr        sys_state       ; set state=0
                rts

;=============================================
; park drive at position 0 and update wear info
fdc_parkdrive:  orcc       #cc_firq        ; disable FIRQ
                jsr        fdc_inactivate_drive ; mark drive as inactive
                lda        drvN_storecyl,u ; cylinder or head not 0
                bne        loc_EB65
                lda        drvN_storehead,u
                bne        loc_EB65        ; yes, move to track 0
                bra        at_trk0         ; is at track 0
loc_EB65:       lda        drvN_curtrk,u   ; get current track
                bne        loc_EB71        ; not 0
                lda        drvN_curhead,u
                beq        move_to_trk0
loc_EB71:       orcc       #cc_firq        ; disable FIRQ
                jsr        fdc_movetrack_out ; step a track towards trk0
                jsr        decrement_storedpos ; adjust store pos
                andcc      #~cc_firq       ; reenable FIRQ
                bra        loc_EB65        ; loop
move_to_trk0:   lda        FDC_port.str_cr ; get FDC status
                bita       #fdc_status1_trk0 ; check trk0 bit
                beq        at_trk0         ; if at trk0, skip
                orcc       #cc_firq        ; disable FIRQ
                jsr        fdc_movetrack_out ; move a single step
                andcc      #~cc_firq       ; reenable FIRQ
                bra        move_to_trk0    ; loop
at_trk0:        andcc      #~cc_firq       ; re-enable FIRQ
                jsr        delay_5000      ; wait some time
                jsr        fdc_getdrvstatus ; read drive position
                lbne       loc_EC1B        ; error, exit
                lda        curtrk          ; is track positive?
                bpl        loc_EBA9        ; yes, skip
realign:        orcc       #cc_firq        ; disable FIRQ
                jsr        fdc_movetrack_in ; move track back in
                lbcs       locret_EC2B     ; if error, exit
                bra        at_trk0         ; loop until at track 0
loc_EBA9:       bne        realign         ; not yet at 0, realign
                lda        curhead         ; is also head=0?
                bne        realign         ; no realign
                sta        drvN_storecyl,u ; set position to true zero
                sta        drvN_storehead,u
                lda        #~0             ; set track register = 0
                sta        FDC_port.tr
                tst        drvN_updatewear,u ; has wear information?
                bne        has_wearinfo    ; yes, skip
                lda        #5              ; try 5 times to get the wear info
                sta        readwear_retrycnt
loc_EBC8:       jsr        read_sector17   ; get sector 17 of track 0
                beq        has_wearinfo    ; was okay, skip (now have wear info)
                bmi        locret_EC2B     ; drive not ready, exit
                dec        readwear_retrycnt ; retry getting sector
                bne        loc_EBC8        ; loop
                bra        loc_EC1B        ; restart with wear level 0
has_wearinfo:   lda        drvN_wearh,u    ; get wear info from drive
                cmpa       #0xFE           ; was just initialized?
                bcc        loc_EC2C        ; higher, skip
                lda        drvN_wearl,u    ; get wear info from drive
                adda       drvN_irqwearl,u ; add revolution count
                orcc       #cc_firq        ; disable FIRQ
                sta        drvN_wearl,u    ; store it
                lda        drvN_wearm,u    ; add middle and high bytes
                adca       drvN_irqwearm,u
                sta        drvN_wearm,u
                lda        drvN_wearh,u
                adca       drvN_irqwearh,u
                sta        drvN_wearh,u
                bcc        loc_EC0C        ; if carry? set to 0xfe
                lda        #0xFE
                sta        drvN_wearh,u
loc_EC0C:       andcc      #~cc_firq       ; re-enable FIRQ
                lda        #1              ; mark we have wear info to write
                sta        drvN_updatewear,u
                jsr        write_sector17  ; write it to disk
                clr        drvN_updatewear,u ; clear flag
loc_EC1B:       orcc       #cc_firq        ; disable FIRQ
                clr        drvN_irqwearh,u ; clear revolution count
                clr        drvN_irqwearm,u
                clr        drvN_irqwearl,u
                andcc      #~cc_firq       ; re-enable FIRQ
locret_EC2B:    rts
loc_EC2C:       lda        #1              ; soft write protect?
                sta        drv_softwrtprotect,u
                sta        drvN_disk_wornout,u
                clr        drvN_irqwearh,u ; clear index counter (24 bit)
                clr        drvN_irqwearm,u
                clr        drvN_irqwearl,u
                rts

;=============================================
; try 5 times reading sector 17
find_sector17:  lda        #5              ; 5 attempts
                sta        readwear_retrycnt
loc_EC48:       clra                       ; disable GPIB interupts
                sta        gpib_int1
                jsr        read_sector17   ; read disk wear parameters
                beq        locret_EC5A     ; no error, exit
                bmi        locret_EC5A     ; drive not ready, exit
                dec        readwear_retrycnt ; retry
                bne        loc_EC48        ; loop
                lda        fdc_status      ; get last status
locret_EC5A:    rts

;=============================================
; read the sector containing the disk wear count
; which is trk0, head0, sector17
; disk wear is number of revolutions + number of steps/4
read_sector17:  tst        fdc_headisloaded ; is head loaded?
                bne        loc_EC63        ; no, load it
                jsr        fdc_loadhead
loc_EC63:       lda        #~17            ; load sector 17
                sta        FDC_port.scr    ; into sector register
                lda        FDC_port.str_cr ; drain status register
                andcc      #~cc_firq       ; enable FIRQ
                lda        portctrl_state
                oraa       #ctrl_halten    ; set HALTEN bit
                sta        portcc
                ldb        #fdcmd_readsec  ; issue single sector read / Side compare flg 1
                stb        FDC_port.str_cr
                sta        portctrl_state  ; trigger DRQ/HALTEN/FIRQ logic
                nop                        ; dummy
                nop
                lda        FDC_port.dr     ; DRQ occured: read data
                coma                       ; negative logic
                sta        drvN_wearh,u    ; save drive wear count
                nop
                nop
                lda        FDC_port.dr     ; DRQ occurred: read data
                coma                       ; negative logic
                sta        drvN_wearm,u    ; save drive waer count
                nop
                nop
                lda        FDC_port.dr     ; DRQ occurred, read data
                coma
                sta        drvN_wearl,u    ; save drive wear count
                nop
                nop
drain_wearread: lda        FDC_port.dr     ; DRQ occurred
                coma                       ; negative logic
                sta        dummy_buffer    ; drain data
                bra        drain_wearread  ; loop until final FIRQ (end of record)

;=============================================
write_sector17: tst        fdc_headisloaded ; if not head loaded, load it
                bne        loc_ECAE
                jsr        fdc_loadhead
loc_ECAE:       lda        #~17            ; set sector 17
                sta        FDC_port.scr
                lda        FDC_port.str_cr ; drain FDC status
                andcc      #~cc_firq       ; enable FIRQ
                lda        portctrl_state
                oraa       #ctrl_halten    ; set HALTEN=1
                sta        portcc
                ldb        #fdcmd_writesec1 ; issue WRITESECTOR cmd
                stb        FDC_port.str_cr
                sta        portctrl_state   ; enable DRQ logic
                nop
                nop
                lda        drvN_wearh,u     ; store three wear bytes
                coma
                sta        FDC_port.dr
                lda        drvN_wearm,u
                nop
                nop
                coma
                sta        FDC_port.dr
                lda        drvN_wearl,u
                nop
                nop
                coma
                sta        FDC_port.dr
                lda        #0              ; store 0x00
                sta        dummy_buffer    ; reenable DRQ logic
                coma                       ; negative logic
                nop
                sta        FDC_port.dr     ; store data
                lda        #0x5B           ; store 0x5B
                sta        dummy_buffer
                coma
                nop
                sta        FDC_port.dr
                lda        #0x15           ; store 0x15
                sta        dummy_buffer
                coma
                nop
                sta        FDC_port.dr
                clra                       ; store 0x00
loc_ED05:       sta        dummy_buffer    ; reenable DRQ logic
                coma
                nop
                sta        FDC_port.dr     ; store data
                bra        loc_ED05        ; loop until FDC Int

;=============================================
; try to find out if disk is still usable
fdc_check_worndisk:
                ldy        #0              ; counter
                lda        FDC_port.tr     ; get track
                sta        FDC_port.dr     ; set to target to reach
                lda        #fdcmd_seek
                sta        FDC_port.str_cr
loc_ED1E:       lda        gpib_ad01       ; get GPIB interrupt
                bmi        fdc_diskchk_gpib_int ; yes, continue
                jsr        nullsub_1       ; wait
                jsr        nullsub_1
                lda        FDC_port.str_cr ; get drive status
                bita       #fdc_status1_busy ; is busy?
                beq        loc_ED1E        ; yes, loop
loc_ED2F:       leay       -1,y            ; decrement count
                bne        loc_ED34        ; exit if timeout
                rts
loc_ED34:       lda        gpib_ad01       ; get GPIB interrupt?
                bmi        fdc_diskchk_gpib_int ; yes, continue
                lda        FDC_port.str_cr ; get drive status
                anda       #fdc_status1_index ; index seen?
                beq        loc_ED2F        ; yes
                ldy        #0              ; reset counter
loc_ED43:       leay       -1,y            ; decrement counter
                bne        loc_ED48        ; skip
                rts                        ; exit if timeout
loc_ED48:       lda        gpib_ad01       ; get GPIB interrupt?
                bmi        fdc_diskchk_gpib_int ; yes, skip
                lda        FDC_port.str_cr ; get stauts
                anda       #fdc_status1_index ; index pulse?
                bne        loc_ED43        ; no, loop
                ldy        #12000          ; load counter
loc_ED57:       lda        gpib_ad01       ; if GPIB interrupt
                bmi        fdc_diskchk_gpib_int ; yes, exit
                leay       -1,y            ; decrement counter
                bne        loc_ED57        ; loop
                jsr        fdc_measure_speed ; measure drive speed
                cmpx       #764            ; is lower than 764
                bls        locret_ED6D     ; exit
                lda        #1
                sta        drvN_disk_wornout,u ; set flag disk no longer okay
locret_ED6D:    rts
fdc_diskchk_gpib_int:                      
                jmp        loc_EE03        ; handle gpib int

;=============================================
; reforming/reseating seems to try to move the heads between inner and outer most track
; several times, after that, the wear count is reset
rfdc_reformdisk: 
                lda        #1
                sta        drive_cnt       ; counter
                sta        byte_1AA        ; this is written but never used again
                stu        saveu           ; save current disk
                lda        drive_ptr+1     ; select disk 0 or 1
                anda       #1
                sta        drive_ptr+1
                ldu        drive_ptr       ; select this disk
                ldx        #1055           ; counter
loc_ED8A:       cmpu       #0              ; is drive 0?
                beq        loc_ED94        ; yes switch to disk 1
                leau       -1,u            ; decrement drive
                bra        loc_ED96        ; continue
loc_ED94:       leau       1,u             ; select drive 1
loc_ED96:       lda        drvN_disk_wornout,u ; get wornout flag
                beq        loc_ED8A        ; not set, try the other drive
loc_ED9C:       stu        drive_ptr       ; okay, this disk is suspicious
                jsr        fdc_waitrdy_select_curdrv ; select it
                jsr        fdc_inactivate_drive ; set inactive
                ldy        #0              ; counter
                jsr        fdc_unloadhead  ; unload head
loc_EDAC:       lda        gpib_ad01       ; if GPIB interrupt, discard operation
                bmi        loc_EE03
                lda        statuscc        ; get port status
                bmi        loc_EDFE        ; drive not ready, skip
                leay       -1,y            ; decrement counter
                bne        loc_EDAC        ; loop
                lda        portctrl_state  ; load head
                oraa       #ctrl_hdld
                sta        portctrl_state
                sta        portcc
                lda        #1              ; set head loaded
                sta        fdc_headisloaded
                ldy        #3072           ; some delay
loc_EDCD:       lda        gpib_ad01       ; if GPIB interrupt, discard operation
                bmi        loc_EE03
                leay       -1,y            ; wait
                bne        loc_EDCD
                lda        drive_cnt,u     ; flag is 1?
                bne        loc_EDE5        ; yes, skip
                jsr        fdc_movetrack_out ; step towards trk 0
                bcs        loc_EDEF        ; error, skip
                jsr        decrement_storedpos ; adjust stored position
                bra        loc_EDFA        ; skip
loc_EDE5:       jsr        fdc_movetrack_in ; move towards track 69
                bcs        loc_EDEF        ; error, exit
                jsr        increment_storedpos ; adjust stored position
                bra        loc_EDFA        ; skip
loc_EDEF:       lda        drive_cnt,u     ; select other drive
                coma
                anda       #1
                sta        drive_cnt,u
loc_EDFA:       leax       -1,x            ; decrement counter
                bne        loc_ED8A        ; not zero, skip
loc_EDFE:       jsr        fdc_unloadhead  ; unload head
                bra        reform_done     ; done
loc_EE03:       jsr        fdc_unloadhead  ; unload head
                ldu        saveu           ; get saved drive ptr
                stu        drive_ptr       ; set it
                jsr        fdc_select_curdrv ; select this drive
                clr        sys_state       ; set state=0
                lda        gpib_int1       ; check int status
                bita       #ints1_dec      ; device clear?
                lbne       do_dcas         ; yes, clear device
                lds        #stacktop       ; reset stack
                jmp        loc_EA13        ; handle interrupt further
reform_done:    clra                       ; clear flag
                sta        drvN_disk_wornout,u ; clear flag
                sta        drv_softwrtprotect,u ; clear soft write protect
                sta        drvN_wearh,u    ; clear usage counters
                sta        drvN_wearm,u
                sta        drvN_wearl,u
                sta        drvN_updatewear,u ; don't need to update
                sta        drvN_selectmask,u ; drive selection mask
                sta        sys_state       ; set state=0
                cmpu       #0              ; was drive 0
                beq        loc_EE4B        ; yes, try the other one
                leau       -1,u            ; was drive one, try the other
                bra        loc_EE4D        ; continue
loc_EE4B:       leau       1,u             ; select drive 1
loc_EE4D:       lda        drvN_disk_wornout,u ; is it marked as suspicious?
                beq        loc_EE56        ; no, finished
                jmp        loc_ED9C        ; try reforming this
loc_EE56:       ldu        saveu           ; restore old selection
                stu        drive_ptr
                jsr        fdc_waitrdy_select_curdrv ; select the current drive
                rts

;=============================================
; park all heads at track 17
park_heads_at_17:
                stu        saveu           ; save current drive ptr
                jsr        park_head_at_17 ; park drive head at track 17
                lda        drive_ptr+1     ; toggle to other drive
                coma
                anda       #1
                sta        drive_ptr+1
                ldu        drive_ptr
                jsr        fdc_waitrdy_select_curdrv ; select it
                jsr        park_head_at_17 ; park head
                ldu        saveu           ; restore drive ptr
                stu        drive_ptr
                jsr        fdc_waitrdy_select_curdrv ; select drive
                rts
				
;=============================================
; park head of selected drive to track 17
park_head_at_17:
                jsr        fdc_inactivate_drive ; set drive inactive
loc_EE85:       lda        gpib_ad01       ; GPIB interrupt?
                bmi        loc_EEA4        ; yes, skip
                lda        drvN_curtrk,u   ; get track position
                cmpa       #17             ; is it 17?
                beq        locret_EEA3     ; yes, exit
                bhi        loc_EE9B        ; is it higher?
                jsr        fdc_movetrack_in ; move head towards track 17 in
                jsr        increment_storedpos ; adjust stored position
                bra        loc_EE85        ; loop
loc_EE9B:       jsr        fdc_movetrack_out ; position head out
                jsr        decrement_storedpos ; decrement position
                bra        loc_EE85        ; loop
locret_EEA3:    rts
loc_EEA4:       lds        #stacktop       ; reset stack
                ldu        saveu           ; restore selected drive
                stu        drive_ptr
                jsr        fdc_waitrdy_select_curdrv ; wait until ready
                jmp        gpib_handle_int ; handle interrupt

;=============================================
; decrement stored position of drive after a step out
decrement_storedpos:
                lda        drvN_storehead,u ; get stored head
                beq        loc_EEBF        ; if zero, skip
                clr        drvN_storehead,u ; set it 0
                rts
loc_EEBF:       inc        drvN_storehead,u ; set head=1
                dec        drvN_storecyl,u ; decrement cylinder
                rts

;=============================================
; increment stored position of drive after a step in
increment_storedpos:
                lda        drvN_storehead,u ; get stored head
                bne        loc_EED3        ; is 1
                inc        drvN_storehead,u ; no, make one
                rts
loc_EED3:       clr        drvN_storehead,u ; set to 0
                inc        drvN_storecyl,u ; increment cylinder
                rts

;=============================================
; mark drive as no longer selected, store parameters
fdc_inactivate_drive:
                tst        drvN_inactive,u ; is drive inactive?
                bne        locret_EF08     ; yes, exit
                lda        #1
                sta        drvN_inactive,u ; set in inactive
                lda        drvN_activecyl,u ; save active clyinder
                sta        drvN_storecyl,u
                lda        drvN_activehead,u ; save active head
                sta        drvN_storehead,u
                lda        drvN_curtrk,u   ; save track as reported by drive
                sta        drvN_savedtrk,u
                lda        drvN_curhead,u  ; save head as reported by drive
                sta        drvN_savedhead,u
locret_EF08:    rts

;=============================================
amigo_listen:   lda        #8
                sta        sys_state       ; set state 8: secondary listen
                lda        #1
                sta        gpib_auxm       ; disable parallel poll mode
                lda        gpib_auxm       ; get pass thru value
                anda       #0x7F           ; discard parity bit
                cmpa       #0x6B           ; secondary=0B: Buffered read verify
                lbeq       amigo_listen0b
                cmpa       #0x60           ; secondary=00: Receive data
                lbeq       amigo_listen00
                cmpa       #0x68           ; secondary=08: seek/req status/unbuffered read/verify/
                                           ; unbuffered write/initialize/set addr record
                lbeq       amigo_listen08
                cmpa       #0x69           ; secondary=09: Buffered write
                lbeq       amigo_listen09
                cmpa       #0x6A           ; secondary=0a: request status/buffered read/request logaddr
                lbeq       amigo_listen0a
                cmpa       #0x6C           ; secondary=0c: unbuf read verify/request physaddr/format/writedisk
                lbeq       amigo_listen0c
                cmpa       #0x6F           ; secondary=0f: download
                lbeq       amigo_download
                cmpa       #0x71           ; secondary=11: HPIB CRC
                lbeq       amigo_sendcrc
                cmpa       #0x70           ; secondary=10: amigo HP300 clear
                lbeq       amigo_hp300_clear
                cmpa       #0x7E           ; secondary=1e: write loopback
                lbeq       amigo_writeloopback
                cmpa       #0x7F           ; secondary=1f: init selftest
                lbeq       amigo_initselftest
                cmpa       #0x7D           ; secondary=1d: diagnose stepper
                bne        amigo_listen_invalid
                tst        end_buf
                lbne       amigo_diagnostic ; do it
amigo_listen_invalid:
                jsr        gpib_invalidsecondary ; acknowledge invalid secondary
                jmp        gpib_errorstate ; go to error state

;=============================================
amigo_talk:     lda        #9
                sta        sys_state       ; set amigo talk state
                lda        #auxm_clr_pp
                sta        gpib_auxm       ; disable parallel poll mode
                lda        gpib_auxm       ; get passthru value
                anda       #0x7F           ; discard parity
                cmpa       #0x60           ; secondary=00: send data
                bne        loc_EF84        ; no, skip
                lda        #auxm_v_sa
                sta        gpib_auxm       ; acknowledge valid secondary
                jsr        assert_dsj_not_2 ; check fully initialized
                jmp        amigo_senddata  ; send up to 256+1 bytes
loc_EF84:       cmpa       #0x68           ; secondary=08: send address or status
                bne        loc_EF91        ; no, skip
                jsr        gpib_validsecondary ; acknowledge valid secondary
                jsr        assert_dsj_not_2 ; check fully initialized
                jmp        amigo_sendstatus ; send 4+1 status bytes
loc_EF91:       cmpa       #0x70           ; secondary=10: send DSJ
                lbeq       amigo_senddsj
                cmpa       #0x71           ; secondary=11: HPIB CRC
                lbeq       amigo_sendcrc
                cmpa       #0x7E           ; secondary=1e: Read Loopback
                lbeq       amigo_readloopback
                cmpa       #0x7F           ; secondary=1f: Read Selftest
                lbeq       amigo_readselftest
                jsr        gpib_invalidsecondary ; signal invalid secondary
                jmp        gpib_errorstate ; set error state dsj=1

;=============================================
; listen function 08
; 00: cold load read
; 02: Seek
; 03: request status
; 05: unbuffered read
; 07: Verify
; 0B: initialize (D=0)
; 14: req logical address
; 15: End
; 2B: initialize (D=1)
amigo_listen08: jsr        gpib_validsecondary ; acknowledge valid command
                jsr        gpib_readbyte   ; read opcode
                lbcs       gpib_errorstate ; if error, exit
                anda       #0x3F           ; mask out 6 bits
                cmpa       #0              ; subfunction 0: cold read
                lbeq       amigo_coldload  ; skip to it
                jsr        assert_dsj_not_2 ; require for the following functions
                                           ; that DSJ was correct read after powerup
                cmpa       #5              ; subfunction 5: unbuffered read
                bne        loc_EFD0        ; no, skip
                lda        #1              ; set unbuffered mode
                sta        unbufread_flg
                jmp        fdc_read        ; common read entry
loc_EFD0:       cmpa       #8              ; subfunction 8: unbuffered write
                bne        loc_EFDC        ; no, skip
                lda        #1              ; set unbuffered mode
                sta        unbufwrite_flg
                jmp        amigo_bufwrite  ; common write entry
loc_EFDC:       cmpa       #2              ; subfunction 2: seek
                lbeq       amigo_seek      ; do it
                cmpa       #3              ; subfunction 03: read status
                lbeq       amigo_reqstatus ; do it
                cmpa       #7              ; subfunction 7: Verify
                lbeq       amigo_verify    ; do it
                cmpa       #0xB            ; subfunction 0B: initialize (D=0)
                lbeq       amigo_initialize_d0 ; do it
                cmpa       #0x14           ; subfunction 14: request logical address
                lbeq       amigo_reqlogical ; do it
                cmpa       #0x15           ; subfunction 15: end
                lbeq       amigo_end       ; do it
                cmpa       #0x2B           ; subfunction 2B: initialize (D=1)
                lbeq       amigo_initialize_d1 ; do it
                jmp        amigo_illopcode ; return illegal opcode error

;=============================================
; perform 09 functions:
; 08: buffered write
amigo_listen09: lda        #auxm_v_sa      ; acknowledge valid secondary
                sta        gpib_auxm
                jsr        gpib_readbyte   ; get opcode
                lbcs       gpib_errorstate ; if error, exit
                anda       #0x1F           ; mask 5 bits
                cmpa       #8              ; subfunction 8: buffered write
                lbne       amigo_illopcode ; no error ill opcode
                jsr        assert_dsj_not_2 ; require full powerup
                jmp        amigo_bufwrite  ; common write entry

;=============================================
; perform 0a functions:
; 03: request status
; 05: buffered read
; 14: request logical
amigo_listen0a: lda        #auxm_v_sa      ; acknowledge valid secondary
                sta        gpib_auxm
                jsr        gpib_readbyte   ; get opcode
                lbcs       gpib_errorstate ; if error, exit
                jsr        assert_dsj_not_2 ; require full initialization
                anda       #0x1F           ; mask 5 bits
                cmpa       #5              ; subfunction 5: buffered read
                lbeq       fdc_read        ; yes, do it
                cmpa       #3              ; subfunction 3: request status
                lbeq       amigo_reqstatus ; do it
                cmpa       #0x14           ; subfunction 14: request logical
                lbne       amigo_illopcode ; no, error ill opcode
                jmp        amigo_reqlogical ; do it

;=============================================
; perform 0b functions:
; 5: unbuffered read verify
amigo_listen0b: jsr        gpib_validsecondary ; acknowledge valid secondary
                jsr        gpib_readbyte   ; get opcode
                lbcs       gpib_errorstate ; if error, exit
                anda       #0x1F           ; mask 5 bits of opcode
                cmpa       #5              ; is it subfunction 5: buffered read verify?
                lbne       amigo_illopcode ; no, exit illegal opcode
                jsr        assert_dsj_not_2 ; require that full power up
                jmp        fdc_read        ; perform a read, effectively same as buffered read

;=============================================
; perform 0c functions:
; 14 request physical address
; 16 read disk wear count
; 18 format
; 1a write entire disk
amigo_listen0c: jsr        gpib_validsecondary ; acknowledge valid secondary
                jsr        gpib_readbyte   ; get opcode
                lbcs       gpib_errorstate ; if error, exit
                jsr        assert_dsj_not_2 ; require device powered up
                anda       #0x1F           ; mask 5 bits of opcode
                cmpa       #0x14           ; subfunction 14: request physical addr
                lbeq       amigo_reqphysical ; do it
                cmpa       #0x16           ; subfunction 16: read wear
                lbeq       amigo_readwear  ; do it
                cmpa       #0x1A           ; subfunction 1a: format special
                lbeq       amigo_writedisk ; do it
                cmpa       #0x18           ; subfunction 18: format disk
                lbeq       amigo_format    ; do it
                cmpa       #5              ; subfunction 5: unbuffered read
                lbne       amigo_illopcode ; no, error illegal opcode
                lda        #1
                sta        unbufread_flg   ; set multisector read mode
                jmp        fdc_read        ; common read entry

;=============================================
; check if passed unit number in A is valid
; set U index to unit
chk_validunit:  anda       #0xF            ; mask lower nibble
                cmpa       #3              ; compare with maximum unit number
                bls        loc_F0A2        ; is 0...3? yes continue
                lda        #amigo_s1_unitunavail ; signal error code
                sta        s1_errcode
                jmp        amigo_errorexit ; end command
loc_F0A2:       sta        drive_ptr+1     ; store unit
                ldu        drive_ptr       ; load unit as index
                rts

;=============================================
; bootstrap
amigo_coldload: jsr        gpib_readbyte   ; get head/sector information
                lbcs        gpib_errorstate ; if error, exit
                clr        drive_ptr+1     ; select drive 0
                ldu        drive_ptr       ; get drive ptr
                sta        temp1           ; store HHSSSSSS info
                anda        #0x3F          ; mask out sector
                cmpa        #15            ; if >15, exit with seek error
                bgt        coldload_error
                sta        drvN_tgtsector,u ; save as target sector
                lda        temp1           ; get info
                lsra                       ; shift 6 bits
                lsra
                lsra
                lsra
                lsra
                lsra
                cmpa       #1              ; if >1, exit with error
                bgt        coldload_error
                sta        drvN_tgthead,u  ; save target head
                clr        drvN_tgtcylinder,u ; cylinder is fixed to 0
                jsr        fdc_waitrdy_select_curdrv ; wait for drive
                jsr        fdc_spinup      ; start motor
                jsr        fdc_seek        ; position to track
                beq        do_coldload     ; no error, contiune
                jsr        set_s2_error    ; decode drive errors
                lda        #1              ; set unsuccessful
                sta        dsj
                jsr        gpib_set_PP     ; set parallel poll
                jsr        led_on          ; leave LED on
                rts
do_coldload:    lda        #1              ; enable multisector read
                sta        unbufread_flg
                jmp        loc_F4B0        ; jump into readsector code
coldload_error: lda        #amigo_s1_status2_err ; status 2 error
                sta        s1_errcode
                lda        drvN_disktype,u ; set S2 error bit
                oraa       #amigo_s2_attention ; set attention
                sta        drvN_disktype,u
                lda        drvN_s2flags,u
                oraa       #amigo_s2_seekcheck ; invalid sector for bootstrap
                sta        drvN_s2flags,u
                jmp        amigo_errorexit ; goto error state

;=============================================
; HPIB CRC, return CRC byte
amigo_sendcrc:  jsr        gpib_validsecondary ; acknowledge valid secondary
                jsr        gpib_set_PP     ; enable parallel poll
                lda        #1              ; send a byte 1 with EOI
                jsr        gpib_sendbyte_with_eoi
                bcc        locret_F12A     ; no error, exit
drain_crc:      jsr        gpib_readbyte   ; read a byte
                bcs        locret_F12A     ; error, exit
                bvc        drain_crc       ; no EOI, loop
locret_F12A:    rts

;=============================================
; read loopback record
amigo_readloopback:
                jsr        gpib_validsecondary ; aknowledge valid address
                ldx        #0              ; load buffer start
loc_F131:       lda        ,x+             ; get byte from buffer
                jsr        gpib_sendbyte   ; send it on bus
                bcs        loc_F142        ; abort seen?, yes, exit
                cmpx       #0xFF           ; is it end of buffer?
                bne        loc_F131        ; no, continue sending
                lda        ,x              ; send last byte with EOI
                jsr        gpib_sendbyte_with_eoi
loc_F142:       clr        s1_errcode      ; S1 = success
                jsr        gpib_set_PP     ; enable parallel poll
                rts                        ; done

;=============================================
; write loopback
; read up to 256 bytes into buffer
amigo_writeloopback:
                jsr        gpib_validsecondary ; acknowledge valid secondary
                ldx        #0              ; load start of buffer
loc_F14F:       jsr        gpib_readbyte   ; accept a byte from GPIB
                lbcs       gpib_errorstate ; error? exit
                bvs        loc_F161        ; EOI? yes skip
                sta        ,x+             ; store byte and advance to next buffer pos
                cmpx       #end_buf        ; end of buffer reached?
                bne        loc_F14F        ; no loop
                bra        loc_F163        ; yes, exit done
loc_F161:       sta        ,x              ; store last byte
loc_F163:       jsr        gpib_set_PP     ; set parallel poll
                rts

;=============================================
; download up to 256 bytes of 6809 code into download
; buffer and jump to beginning of code.
;
; Note: unless the code does bad things, a RTS will return to the main loop.
amigo_download: jsr        gpib_validsecondary ; acknowledge valid secondary
                ldx        #downloadbuf    ; load start of download buffer
loc_F16D:       jsr        gpib_readbyte   ; get a byte
                lbcs       gpib_errorstate ; if error, exit
                bvs        loc_F17F        ; if EOF, skip
                sta        ,x+             ; store byte, advance ptr
                cmpx       #downloadbuf+256 ; a maximum of 256 bytes loaded?
                bne        loc_F16D        ; no, loop
                bra        loc_F181        ; yes, call the code
loc_F17F:       sta        ,x              ; store last EOI byte
loc_F181:       jmp        downloadbuf     ; call the downloaded code

;=============================================
; read wear count of drive
; subfunction 0: current drive
; subfunction 1: drive specified by UNIT
amigo_readwear: jsr        gpib_readbyte   ; read sub opcode
                lbcs       gpib_errorstate ; if error, exit
                beq        loc_F1C2        ; is it zero?, yes, skip
                jsr        gpib_readbyte   ; get unit byte
                lbcs       gpib_errorstate ; error, exit
                jsr        chk_validunit   ; select unit
                sta        selected_unit   ; set as selected unit
                clr        drvN_tgtcylinder,u
                clr        drvN_tgthead,u
                clr        drvN_endofdisk,u
                jsr        fdc_waitrdy_select_curdrv ; select current drive
                jsr        fdc_spinup      ; start motor
                jsr        fdc_seek        ; do a seek to track 0 head 0
                bne        readwearfailed  ; didn't succeed
                clra
                sta        gpib_int1       ; disable GPIB interrupt
                sta        sys_state       ; system state is 0
                jsr        read_sector17   ; read the sector containing disk wear
                bne        loc_F1EB        ; reenable GPIB and error
                lda        #inte1_bi|inte1_bo|inte1_dec|inte1_apt
                sta        gpib_int1       ; reset GPIB interrupts
                bra        loc_F1C9        ; continue
loc_F1C2:       jsr        gpib_readbyte   ; drain EOI
                lbcs       gpib_errorstate ; if error, exit
loc_F1C9:       lda        drvN_wearh,u
                sta        status_s1       ; store 1st byte
                lda        drvN_wearm,u
                sta        status_s1_unit  ; store 2nd byte
                lda        drvN_wearl,u
                sta        status_s2_type  ; store 3rd byte
                clr        dsj             ; clear error code
                clr        s1_errcode
                jsr        gpib_set_PP     ; enable parallel poll
                jsr        led_off         ; switch of LED
                rts
loc_F1EB:       ldb        #inte1_bi|inte1_bo|inte1_dec|inte1_apt
                stb        gpib_int1       ; reenable GPIB interrupts
readwearfailed: jsr        set_s2_error    ; decode drive error
                lda        #1              ; command unsuccessful
                sta        dsj
                jsr        gpib_set_PP     ; enable parallel poll
                jsr        led_on          ; leave LED on
                rts

;=============================================
; seek position on floppy
amigo_seek:     jsr        gpib_readbyte   ; get unit number
                lbcs       gpib_errorstate ; if error, exit
                jsr        chk_validunit   ; check valid unit and select it
                sta        selected_unit   ; save as currently selected unit
                jsr        gpib_readbyte   ; read high byte of cylinder
                lbcs       gpib_errorstate ; if error, exit
                bne        seek_error      ; not zero, exit setting error
                jsr        gpib_readbyte   ; get low byte of cylinder
                lbcs       gpib_errorstate ; if error, exit
                cmpa       #34             ; is it higher than 34?
                bhi        seek_error      ; yes, error
                sta        drvN_tgtcylinder,u ; store the target cylinder
                jsr        gpib_readbyte   ; get head
                lbcs       gpib_errorstate ; if error, exit
                cmpa       #1              ; is it > 1?
                bhi        seek_error      ; yes, error
                sta        drvN_tgthead,u  ; store the target head
                jsr        gpib_readbyte   ; get target sector
                lbcs       gpib_errorstate ; if error, exit
                cmpa       #16             ; is it >16?
                bhi        seek_error      ; yes error
                sta        drvN_tgtsector,u ; store target sector
                jsr        fdc_waitrdy_select_curdrv ; select drive
                jsr        fdc_spinup      ; start motor
                jsr        fdc_seek        ; seek the specified position
                beq        seek_noerror    ; no error, done
                jsr        set_s2_error    ; decode the error from drive
                lda        #1              ; unsuccessful
                sta        dsj
                jsr        gpib_set_PP     ; enable parallel poll
                jsr        led_on          ; leave LED on
                rts                        ; exit
seek_noerror:   clr        dsj             ; operation completed successfully
                jsr        led_off         ; switch of select LED
                lda        #amigo_s1_drvatt ; drive atention
                sta        s1_errcode
                lda        drvN_disktype,u ; set disk type
                anda       #0x7F           ; without error bit
                sta        drvN_disktype,u
                lda        drvN_s2flags,u  ; set drive attention in s2
                oraa       #amigo_s2_attention
                sta        drvN_s2flags,u
                jsr        gpib_set_PP     ; set parallel poll
                rts                        ; done
seek_error:     lda        #amigo_s1_drvatt ; signal drive attention
                sta        s1_errcode
                lda        drvN_disktype,u ; get disktype
                oraa       #amigo_s2_attention ; set attention bit
                sta        drvN_disktype,u
                lda        drvN_s2flags,u
                oraa       #amigo_s2_attention
                oraa       #amigo_s2_seekcheck ; set attention and drive fault bits
                sta        drvN_s2flags,u
                jmp        amigo_errorexit ; exit with error

;=============================================
; command: request status
; needs amigo_talk_08 to send these bytes to controller
amigo_reqstatus:
                jsr        gpib_readbyte   ; read unit byte
                lbcs       gpib_errorstate ; if error, exit
                jsr        chk_validunit   ; check valid unit and set U
                cmpa       selected_unit   ; same as selected unit?
                bne        loc_F2B2        ; no, do not check for last operation
                lda        dsj             ; was last operation in error?
                cmpa       #1
                beq        amigo_savestatus ; yes, fill s1/s2 status bytes
; prepare status for other drive than selected one
loc_F2B2:       clr        drvN_disktype,u ; clear disktype
                clr        drvN_s2flags,u  ; clear s2 flags
                jsr        fdc_waitrdy_select_curdrv ; select drive
                jsr        fdc_spinup      ; start motor
                lda        statuscc        ; get write protect flag
                bita       #status_drwpt
                beq        loc_F2D1        ; no set, skip
                lda        #amigo_s2_wrtprotect ; set write protect flag
                oraa       drvN_s2flags,u
                sta        drvN_s2flags,u  ; into s2flags
loc_F2D1:       jsr        fdc_getdrvstatus ; get drive status
                beq        loc_F2FE        ; everything okay, skip
                bita       #0x80           ; check drive ready status
                beq        loc_F2F2        ; is ready, but there were error bits
                lda        #amigo_s2_attention ; set attention bit in S2
                oraa       drvN_disktype,u ; inject into status
                sta        drvN_disktype,u
                lda        #amigo_s2_drivenotready ; set drive not ready bits in S2
                oraa       drvN_s2flags,u
                anda       #~amigo_s2_wrtprotect ; remove wrt protect bit - irrelevant
                sta        drvN_s2flags,u
                bra        amigo_savestatus
loc_F2F2:       lda        #0xA            ; disktype TTTT=0101: unformatted disk
                oraa       drvN_disktype,u
                sta        drvN_disktype,u
                bra        amigo_savestatus
loc_F2FE:       lda        #0xC            ; disktype TTTT=0110: HP Format
                oraa       drvN_disktype,u
                sta        drvN_disktype,u ; inject TTTT bits

;=============================================
; save status of last operation
amigo_savestatus:
                lda        s1_errcode      ; get current error status
                sta        status_s1       ; save it in status byte
                lda        selected_unit   ; get selected unit
                sta        status_s1_unit  ; save it in unit byte
                lda        drvN_disktype,u ; get disk type of last operation
                oraa       #1              ; add flag for 9121 (in contrast to 8290X)
                sta        status_s2_type  ; save as status 2
                lda        drvN_s2flags,u  ; get s2 flags for last operation
                sta        status_s2_flags ; save them
                clr        drvN_disktype,u ; clear disktype
                clr        drvN_s2flags,u  ; clear s2 status
                clr        s1_errcode      ; clear error code
                clr        dsj             ; clear DSJ
                jsr        gpib_set_PP     ; enable parallel poll
                jsr        led_off         ; turn off LED
                rts

;=============================================
; set D bit for sectors to write
amigo_initialize_d0:
                jsr        gpib_readbyte   ; get unit
                lbcs       gpib_errorstate ; if error, exit
                jsr        chk_validunit   ; check valid unit
                sta        selected_unit   ; and select it
                lda        drvN_activehead,u ; get the active head
                anda       #1
                sta        format_head
initialize:     lda        drvN_activecyl,u ; get cylinder
                sta        format_cylinder ; store it
                lda        #2
                sta        format_interleave ; set interleave to 2
                lda        #1
                sta        format_track    ; enable format before write
                jsr        gpib_set_PP     ; set parallel poll
                rts

;=============================================
amigo_initialize_d1:
                jsr        gpib_readbyte   ; get unit
                lbcs       gpib_errorstate ; if error, exit
                jsr        chk_validunit   ; check unit
                sta        selected_unit   ; select unit
                lda        drvN_activehead,u ; get head
                oraa       #0x20           ; set D bit in head info
                sta        format_head     ; store in header info for head
                bra        initialize      ; common init

;=============================================
amigo_verify:   jsr        gpib_readbyte   ; get unit
                lbcs       gpib_errorstate ; if error, exit
                jsr        chk_validunit   ; check unit
                sta        selected_unit   ; select it
                jsr        gpib_readbyte   ; get sector count high
                lbcs       gpib_errorstate ; if error, exit
                sta        verify_seccnt   ; store sector high
                jsr        gpib_readbyte   ; get sector low
                lbcs       gpib_errorstate ; if error, exit
                sta        verify_seccnt+1 ; store sector low
                ldd        verify_seccnt   ; get sector count in D
                beq        loc_F3D2        ; if zero, exit
                lsra                       ; divide by 4
                rorb
                lsra
                rorb
                addb       drvN_irqwearl,u ; add to current disk wear
                stb        drvN_irqwearl,u
                adca       drvN_irqwearm,u
                sta        drvN_irqwearm,u
                bcc        loc_F3BC
                inc        drvN_irqwearh,u
loc_F3BC:       jsr        fdc_waitrdy_select_curdrv ; select drive
                jsr        fdc_spinup      ; start motor
                tst        drvN_inactive,u ; is inactive
                beq        loc_F3CD        ; no, skip
                jsr        fdc_seek        ; reposition drive
                bne        loc_F3DF        ; if error, exit
loc_F3CD:       jsr        verify_sectors  ; verify sectors in loop
                bne        loc_F3DF        ; if error, exit
loc_F3D2:       clr        s1_errcode      ; clear error code
                clr        dsj             ; return success
                jsr        gpib_set_PP     ; enable parallel poll
                jsr        led_off         ; deselect LED
                rts
loc_F3DF:       jsr        set_s2_error    ; decode drive errors
                lda        #1              ; unsuccessful
                sta        dsj
                jsr        gpib_set_PP     ; set parallel poll
                jsr        led_on          ; leave LED on
                rts

;=============================================
; return cylinder (2bytes), head, sector of last
; (failing) operation. Use amigo talk 08 to retrieve result.
amigo_reqlogical:
                jsr        gpib_readbyte   ; read and discard EOI byte
                lbcs       gpib_errorstate ; if error, exit
                clr        status_s1       ; clear 1st byte (cylinder high)
                lda        drvN_tgtcylinder,u ; get cylinder
                sta        status_s1_unit  ; store as 2nd byte (cylinder low)
                lda        drvN_tgthead,u  ; get head
                sta        status_s2_type  ; store as 3rd byte
                lda        drvN_tgtsector,u ; get sector
                sta        status_s2_flags ; store as 4th byte
                clr        s1_errcode      ; clear error code
                clr        dsj             ; clear DSJ
                jsr        gpib_set_PP     ; enable parallel poll
                jsr        led_off         ; deselect LED
                rts                        ; done

;=============================================
; get cylinder (2 bytes) and head
; current drive is positioned
; need amigo talk 08 to retrieve result
amigo_reqphysical:
                jsr        gpib_readbyte   ; drain EOI byte
                lbcs       gpib_errorstate ; if error, exit
                clr        status_s1       ; clear 1st byte
                tst        drvN_inactive,u ; active?
                bne        loc_F43A        ; no, skip
                lda        drvN_curtrk,u   ; get current positioned track
                sta        status_s1_unit  ; store as 2nd byte
                lda        drvN_curhead,u  ; get current positioned head
                sta        status_s2_type  ; store as 3rd byte
                bra        loc_F448        ; continue
loc_F43A:       lda        drvN_savedtrk,u ; get saved trk
                sta        status_s1_unit  ; save as 2nd byte
                lda        drvN_savedhead,u ; get saved head
                sta        status_s2_type  ; save as 3rd byte
loc_F448:       clr        status_s2_flags ; clear 4th byte
                clr        s1_errcode      ; clear error code
                clr        dsj             ; clear DSJ
                jsr        gpib_set_PP     ; set parallel poll
                jsr        led_off         ; deselect LED
                rts

;=============================================
; clear error code and dsj bytes
amigo_end:      jsr        gpib_readbyte   ; drain EOI byte
                lbcs       gpib_errorstate ; if error, exit
                clr        dsj             ; clear dsj
                clr        s1_errcode      ; clear error code
                jsr        gpib_clr_PP     ; disable parallel poll
                jsr        led_off         ; deselect LED
                rts

;=============================================
; read single or multiple sectors
fdc_read:       jsr        gpib_readbyte   ; get unit
                lbcs       gpib_errorstate ; if error, exit
                jsr        chk_validunit   ; check if valid unit
                sta        selected_unit   ; and set selected
                lda        s1_errcode      ; get last error code
                beq        loc_F490        ; was none, continue
                cmpa       #1              ; error was illegal opcode: nonfatal, continue
                beq        loc_F490
                cmpa       #0xA            ; error was I/O program error: nonfatal, continue
                beq        loc_F490
                cmpa       #0x1F           ; was drive attention
                bne        read_failed     ; no, this is fatal
                tst        drvN_disktype,u ; was a S2 attention?
                bmi        read_failed     ; yes, fatal
loc_F490:       jsr        fdc_waitrdy_select_curdrv ; select drive
                jsr        fdc_spinup      ; start motor
                tst        drvN_inactive,u ; is it inactive
                bne        loc_F4B0        ; yes, skip
                lda        drvN_activecyl,u ; is at the correct cylinder
                cmpa       drvN_tgtcylinder,u
                bne        loc_F4B0        ; no, reposition
                lda        drvN_activehead,u ; is it at correct head
                cmpa       drvN_tgthead,u
                beq        loc_F4B5        ; yes, skip to reader
loc_F4B0:       jsr        fdc_seek        ; position at correct track/head
                bne        read_error
loc_F4B5:       jsr        fdc_readsector  ; read selected sector
                bne        read_error      ; status not 0, process error
                clr        dsj             ; was successful
                clr        s1_errcode      ; no error
                jsr        fdc_nextsector  ; advance to next sector
                lda        #9
                sta        gpib_auxm       ; enable parallel poll
                jsr        led_off         ; deselct LED
                rts                        ; done
read_error:     jsr        set_s2_error    ; decode drive errors
read_failed:    lda        #1              ; set unsuccessful
                sta        dsj
                jsr        gpib_set_PP     ; enable parallel poll
                jsr        led_on          ; leave LED on
                clr        unbufread_flg   ; clear multiread flag
                rts

;=============================================
; select drive for writing
; actual write is performed in amigo_receivedata
amigo_bufwrite: jsr        gpib_readbyte   ; get unit
                lbcs       gpib_errorstate ; if error, exit
                jsr        chk_validunit   ; check if valid
                sta        selected_unit   ; select it
                lda        #9              ; enable parallel poll
                sta        gpib_auxm
                rts

;=============================================
amigo_listen00: lda        #auxm_v_sa      ; acknowledge valid secondary
                sta        gpib_auxm
                jsr        assert_dsj_not_2 ; require full init
                tst        format_track    ; should format (set/reset D bits)
                lbne       format_disk     ; yes, skip
                lda        s1_errcode      ; get error code
                beq        loc_F518        ; last cmd was okay, skip
                cmpa       #amigo_s1_illop ; last error was ill op
                beq        loc_F518        ; ignore
                cmpa       #amigo_s1_ioprog_err ; last error was i/o program
                beq        loc_F518        ; ignore
                cmpa       #amigo_s1_drvatt ; last code was drive attention
                lbne        loc_F5DC       ; no, fatal
                tst        drvN_disktype,u ; get disktype
                lbmi       loc_F5DC        ; has S2 attention flag set
                                           ; fatal error
loc_F518:       ldx        #0              ; load buffer counter
                lda        portctrl_state
                anda       #~ctrl_clridx   ; hold index interrupt
                sta        portctrl_state
                orcc       #cc_irq         ; disable IRQ
                sta        portcc
                lda        #inte1_bi|inte1_bo|inte1_dec|inte1_end|inte1_apt
                sta        gpib_int1       ; enable GPIB interrupts
                lda        gpib_ad01       ; get GPIB interrupt status
                bpl        loc_F540        ; no interrupt, go wait
                lda        gpib_int1       ; get int flags
                bita       #ints1_bi       ; byte received?
                beq        loc_F551        ; no, skip
                bita       #ints1_end      ; EOI received?
                beq        loc_F570        ; no, skip
                lda        gpio_dio        ; get data byte
                sta        ,x              ; stor in buffer
                bra        loc_F57F        ; buffer complete
loc_F540:       sync                       ; wait for a GPIB interrupt
                lda        gpib_int1       ; get int status
                cmpa       #ints1_bi       ; byte in interrupt?
                beq        loc_F570        ; yes, skip
                cmpa       #ints1_bi|ints1_end ; with EOI?
                bne        loc_F551        ; no, skip
                ldb        gpio_dio        ; get data byte
                stb        ,x+             ; store in buffer
                bra        loc_F57F        ; buffer complete
loc_F551:       bita       #ints1_end      ; EOI seen?
                bne        loc_F57F        ; yes, buffer complete
                bita       #ints1_apt      ; other secondary
                lbne       loc_F5F3        ; yes, verify secondary and exit
                bita        #ints1_dec     ; device clear?
                beq        loc_F562        ; no, skip
                jmp        do_dcas         ; do device clear
loc_F562:       ldb        gpib_am         ; get address mode
                bitb       #as_la          ; addressed as listener?
                bne        loc_F540        ; yes, loop
                tst        unbufwrite_flg  ; is multisector?
                bne        write_exit      ; yes, skip
                jmp        gpib_errorstate ; no, goto error
loc_F570:       ldb        gpio_dio        ; get byte from        bus
                stb        ,x+             ; store in buffer
                cmpx       #end_buf        ; buffer full?
                bne        loc_F540        ; no, loop
                lda        gpib_am         ; get address status
                bita       #as_eoi         ; EOI seen?
                beq        loc_F582        ; no, skip
loc_F57F:       clr        unbufwrite_flg  ; clear unbuffered mode, if set
loc_F582:       lda        portctrl_state
                oraa       #ctrl_clridx    ; hold index interrupt
                sta        portctrl_state
                sta        portcc
                lda        #inte1_bi|inte1_bo|inte1_dec|inte1_apt
                sta        gpib_int1       ; set interrupts
                jsr        fdc_waitrdy_select_curdrv ; select drive
                jsr        fdc_spinup      ; start motor
                tst        drvN_inactive,u ; is drive active?
                bne        loc_F5B1        ; no, skip
                lda        drvN_activecyl,u ; at the right cylinder and head?
                cmpa       drvN_tgtcylinder,u
                bne        loc_F5B1        ; no, reposition
                lda        drvN_activehead,u
                cmpa       drvN_tgthead,u
                beq        loc_F5B6        ; yes, go ahead
loc_F5B1:       jsr        fdc_seek        ; seek to right position
                bne        loc_F5D9        ; error exit
loc_F5B6:       jsr        fdc_write_sector ; do write the sector
                bne        loc_F5D9        ; error, exit
                jsr        fdc_nextsector  ; advance to next sector
                clr        dsj             ; successful
                clr        s1_errcode      ; clear error code
                tst        unbufwrite_flg  ; multisector write?
                beq        write_exit      ; no, done
                andcc      #~cc_irq        ; enable IRQ again
                jmp        loc_F518        ; go accept another sector
write_exit:     lda        #9              ; send parallel poll
                sta        gpib_auxm
                jsr        led_off         ; deselect LED
                clr        unbufwrite_flg  ; terminate multisector mode
                rts                        ; exit
loc_F5D9:       jsr        set_s2_error
loc_F5DC:       lda        #1              ; set unsuccessful
                sta        dsj
                jsr        gpib_set_PP     ; set parallel poll
                jsr        led_on          ; leave LED on
                tst        unbufwrite_flg  ; was multiple write?
                beq        locret_F5F2     ; no, exit
                clr        unbufwrite_flg  ; stop multiple write
                jmp        drain_read      ; drain incoming stream
locret_F5F2:    rts
loc_F5F3:       ldb        #1              ; notify verify secondary flag
                stb        gpib_apt_flag
                jmp        gpib_errorstate ; goto error state

;=============================================
format_disk:    clr        format_track    ; clear flag again
loc_F5FE:       jsr        gpib_readbyte   ; read the filler byte
                bvs        loc_F609        ; EOI? skip
                lbcs       gpib_errorstate ; error, exit
                bra        loc_F5FE        ; drain until EOI
loc_F609:       sta        format_filler   ; save last byte as sector filler
                jsr        fdc_waitrdy_select_curdrv ; select dive
                jsr        fdc_spinup      ; start motor
                tst        drvN_inactive,u ; is inactive?
                beq        loc_F61D        ; no, skip
                jsr        fdc_seek        ; reposition drive
                bne        init_error
loc_F61D:       lda        format_head     ; get head
                anda       #1              ; mask head bit
                adda       format_cylinder ; add cylinder (shifted left)
                adda       format_cylinder
                sta        format_tgtcylinder ; save
                bne        loc_F63E        ; not on track 0, skip
                clr        sys_state       ; clear system state
                jsr        find_sector17   ; find sector17 on track0
                bne        loc_F63E        ; not found or error
                lda        #1
                sta        has_sec17       ; has sector17
                sta        drvN_selectmask,u ; set selectmask
loc_F63E:       jsr        fdc_format      ; format a track (including sector17)
                bne        init_error      ; error, exit
                lda        format_tgtcylinder ; get formatted cylinder
                bne        loc_F667        ; not cylinder 0, exit
                tst        has_sec17       ; had a sector17?
                bne        loc_F65B        ; yes, skip
                lda        #0xF7
                sta        drvN_wearh,u    ; set a high value 0xf70000 for wear count
                clr        drvN_wearm,u
                clr        drvN_wearl,u
loc_F65B:       clra                       ; disable GPIB interrupts
                sta        gpib_int1
                sta        sys_state       ; set state=0
                jsr        write_sector17  ; write current disk wear count
                clr        has_sec17       ; clear the flag
loc_F667:       clr        dsj             ; successful
                jsr        led_off         ; deselect LED
                clr        s1_errcode      ; clear error code
                jsr        gpib_set_PP     ; set parallel poll
                rts                        ; done
init_error:     jsr        set_s2_error    ; decode drive error
                lda        #1              ; unsuccessful
                sta        dsj
                clr        has_sec17
                clr        drvN_selectmask,u ; deselect
                jsr        gpib_set_PP     ; set parallel poll
                jsr        led_on          ; leave LED on
                rts

;=============================================
; prepare to wait for a DCAS message
amigo_hp300_clear:
                jsr        gpib_validsecondary ; acknowledge valid command received
                jsr        gpib_readbyte   ; drain EOI byte
                lbcs       gpib_errorstate ; error?
                lbvc       gpib_errorstate ; not EOI? error
loc_F698:       lda        gpib_ad01       ; get interrupt flag
                bpl        loc_F698        ; wait until interrupt
                lda        gpib_int1       ; get interrupt flags
                bita       #ints1_dec      ; is DCAS?
                beq        loc_F698        ; no, wait
                jmp        do_dcas         ; do device clear

;=============================================
; initiate AMIGO selftest
amigo_initselftest:
                jsr        gpib_validsecondary ; acknowledge valid secondary
                jsr        gpib_readbyte   ; read dummy byte
                bcs        selftest_fatal  ; if CY, fatal error
                jsr        gpib_readbyte   ; read format flag
                bcs        selftest_fatal  ; if CY set, error
                sta        stacktop        ; save selftest format flag
                jsr        selftest_chk_ram ; test RAM
                bcc        loc_F6C1        ; no, error, continue
                lda        #1              ; set RAM test error
                bra        selftest_seterror ; exit selftest with error
selftest_fatal: jmp        gpib_errorstate ; skip
loc_F6C1:       jsr        selftest_chk_rom ; test ROM
                bcc        loc_F6CA        ; no error, continue
                lda        #2              ; set ROM test error
                bra        selftest_seterror ; exit selftest with error
loc_F6CA:       jsr        selftest_chk_fdc ; test FDC chip
                bcc        loc_F6D3        ; no error, continue
                lda        #4              ; set FDC chip error
                bra        selftest_seterror ; exit with selftest error
loc_F6D3:       jsr        selftest_chk_seek ; do seek test
                bcc        loc_F6DC        ; no error, continue
                lda        #6              ; set SEEK test failure
                bra        selftest_seterror ; exit with selftest error
loc_F6DC:       jsr        selftest_chk_motor ; do motor test
                bcc        loc_F6E5        ; no error, continue
                lda        #5              ; set MOTOR error
                bra        selftest_seterror ; exit with selftest error
loc_F6E5:       lda        stacktop        ; get selftest format flag
                anda       #8              ; check "W" bit
                beq        loc_F6F5        ; not set, skip format test
                jsr        selftest_chk_format ; do format test
                bcc        loc_F6F5        ; no error, continue
                lda        #7              ; set format test error
                bra        selftest_seterror ; exit with selftest error
loc_F6F5:       jsr        selftest_chk_verify ; do verify test
                bcc        selftest_noerror ; no error, continue
                lda        #8              ; verify test error
selftest_seterror:
                orcc       #cc_c           ; set carry (shift in as bit 0)
                rola                       ; make error code
                sta        selftest_1      ; store into ST2
                lda        drive_ptr+1     ; get drive number
                asla                       ; put into position xxUUxxxx
                asla
                asla
                asla
                oraa       #0x88           ; set E bit and 1000 -> E0UU1000
                sta        selftest_2      ; store into ST1
                lds        #stacktop       ; reset stack
                jmp        initialize_all  ; initialize main loop
selftest_noerror:
                clr        selftest_2      ; clear selftest status1
                clr        selftest_1      ; clear selftest status2
                lds        #stacktop       ; reset stack
                jmp        initialize_all  ; initialize mainloop

; device clear
do_dcas:        jsr        gpib_clr_PP     ; clear parallel poll mode
                lds        #stacktop       ; reinitialize stack
                clr        dsj
                jsr        fdc_motoroff    ; deselect drive
                jmp        device_clear    ; continue device initialization

;=============================================
; DSJ command
; return DSJ byte
amigo_senddsj:  lda        #auxm_v_sa      ; acknowledge valid secondary
                sta        gpib_auxm
                lda        dsj             ; get DSJ byte
                jsr        gpib_sendbyte_with_eoi ; send byte
                lda        dsj             ; get status
                cmpa       #2              ; is powerup or selftest?
                bne        locret_F749     ; no, exit
                clr        dsj             ; operation completed
                jsr        led_off         ; set off LED
locret_F749:    rts                        ; done

;=============================================
; read selftest         result:
; ST1: EHUU1000         (E=error bit, H=head#, UU=Unit)
; ST2: 000TTTTA         (A=attention, test failed,
;      TTTT=0001        RAM test failed
;           0010        ROM test failed
;           0100        FDC chip failed
;           0101        Motor speed error
;           0111        Format Test error
;           1000        Verify error
;           0110        Seek test failure
amigo_readselftest:
                jsr        gpib_validsecondary ; acknowledge valid secondary
                lda        selftest_2      ; selftest result1
                jsr        gpib_sendbyte   ; send it
                bcs        locret_F761     ; did an error occur?
                                           ; e.g. defective hardware-> cannot send
                                           ; discard output
                lda        selftest_1      ; selftest error code
                jsr        gpib_sendbyte_with_eoi ; send second byte
                clr        s1_errcode      ; S1 = 0
                jsr        gpib_set_PP     ; enable PP
locret_F761:    rts                        ; done

;=============================================
amigo_format:   jsr        gpib_readbyte   ; get unit
                lbcs       gpib_errorstate ; if error, exit
                jsr        chk_validunit   ; check valid unit
                sta        selected_unit   ; and select it
                clr        override_old_fmt ; clear override old format bit
                jsr        gpib_readbyte   ; get type byte
                lbcs        gpib_errorstate ; if error, exit
                bpl        loc_F77E        ; if format bit clear, skip
                inc        override_old_fmt ; set override=1
loc_F77E:       jsr        gpib_readbyte   ; get interleave
                lbcs       gpib_errorstate ; if error, exit
                sta        format_interleave ; store interleave
                jsr        gpib_readbyte   ; get byte
                lbcs       gpib_errorstate ; if error, exit
                sta        format_filler   ; store filler byte
                jsr        fdc_waitrdy_select_curdrv ; select drive
                jsr        fdc_spinup      ; start motor
                jsr        fdc_format_disk ; format entire disk
                bne        loc_F7AA        ; error, exit
                clr        dsj             ; successful
                jsr        led_off         ; deselect LED
                clr        s1_errcode      ; clear error code
                jsr        gpib_set_PP     ; set parallel poll
                rts                        ; done
loc_F7AA:       jsr        set_s2_error    ; decode disk error
                lda        #1              ; unsuccessful
                sta        dsj
                jsr        gpib_set_PP     ; set parallel poll
                jsr        led_on          ; leave LED on
                rts                        ; exit

;=============================================
amigo_senddata: tst        dsj             ; check DSJ
                lbne       senddata_eoibyte ; last command not successful, skip
                lda        gpib_int2       ; get interrupt status2
loc_F7C2:       ldx        #0              ; load start of buffer
                lda        portctrl_state
                anda       #~ctrl_clridx   ; clear index bit
                sta        portctrl_state
                orcc       #cc_irq         ; disable IRQ
                sta        portcc          ; clear status port
                lda        #inte2_adsc     ; enable interrupt if write abort
                sta        gpib_int2
                ldb        ,x+             ; get byte to send
                lda        gpib_ad01       ; get interrupt status
                bpl        wait_gpib_int   ; no interrupt, must wait for one
                lda        gpib_int2       ; get int status2
                bita       #inte2_adsc     ; abort?
                beq        loc_F7F5        ; no, wait for another interrupt
                lda        gpib_int1       ; there was an ADSC interrupt
                                           ; get int status1
                beq        do_adsc         ; no other interrupt? handle adsc
                bita       #ints1_bo       ; byte out requested?
                beq        loc_F7EC        ; no, skip
                stb        gpio_dio        ; send the byte
loc_F7EC:       bita       #ints1_dec      ; device clear?
                bne        sendabort_dcas  ; yes, abort
                bra        loc_F836        ; no, continue
send_loop:      ldb        ,x+
wait_gpib_int:  sync                       ; wait for an interrupt from GPIB
loc_F7F5:       lda        gpib_int1       ; get int status1
                bita       #ints1_bo       ; byte out request?
                bne        loc_F854        ; yes, skip
                bita       #ints1_apt      ; verify secondary?
                lbne       loc_F8A8        ; yes, abort and leave
                bita       #ints1_dec      ; device clear?
                beq        loc_F816        ; no, continue
sendabort_dcas: clra
                sta        gpib_int2       ; disable additiona lGPIB ints
                lda        portctrl_state
                oraa       #ctrl_clridx    ; hold index counter
                sta        portctrl_state
                sta        portcc
                jmp        do_dcas         ; do device clear
loc_F816:       lda        gpib_int2       ; get int status2
                bita       #ints2_adsc     ; adsc interrupt?
                bne        do_adsc1        ; yes, handle it
do_adsc:        lda        gpib_am         ; get address status
                bita       #as_ta          ; is a talker?
                bne        loc_F826        ; yes, skip
                bita       #as_tpas        ; is primary talker?
                beq        do_adsc1
loc_F826:       bita       #as_mjmn        ; is major mode?
                beq        wait_gpib_int   ; yes, something else caused spurious int
do_adsc1:       lda        gpib_ad01       ; check interrupt
                bpl        loc_F83F        ; no, abort and leave
                lda        gpib_int1       ; get int status1
                bita       #ints1_bo       ; byte out requested?
                beq        loc_F836        ; no, other int
                stb        gpio_dio        ; send the byte
loc_F836:       bita       #ints1_apt      ; verify secondary?
                beq        loc_F83F        ; no, skip
                lda        #1              ; notify verify secondary
                sta        gpib_apt_flag
loc_F83F:       jsr        gpib_set_PP     ; set parallel poll
                clra
                sta        gpib_int2       ; disable ADSC interrupt
                sta        unbufread_flg
                lda        portctrl_state
                oraa       #ctrl_clridx    ; set clridx bit again
                sta        portctrl_state
                sta        portcc
                rts                        ; exit
loc_F854:       stb        gpio_dio        ; send data byte
                cmpx       #0x100          ; end of buffer? (256 bytes)
                bne        send_loop       ; no, loop
; finished sending the buffer
                lda        portctrl_state
                oraa       #ctrl_clridx    ; set clrindex bit
                sta        portctrl_state
                sta        portcc          ; in ctrl port
                clra
                sta        gpib_int2       ; disable ADSC interrupt
                tst        unbufread_flg   ; multisector read requested?
                bne        fdc_read_next_sector ; yes, skip
senddata_eoibyte:
                lda        #1              ; send a byte 1 with EOI
                jsr        gpib_sendbyte_with_eoi
loc_F873:       lda        #9              ; set parallel poll
                sta        gpib_auxm
                clra                       ; clear secondary interrupt word
                sta        gpib_int2
                sta        unbufread_flg   ; clear multisector read
                rts

				;=============================================
fdc_read_next_sector:
                jsr        fdc_waitrdy_select_curdrv ; select drive
                jsr        fdc_spinup      ; start motor
                tst        drvN_inactive,u ; is inactive?
                bne        loc_F896        ; yes, skip
                tst        drvN_tgtsector,u ; target sector not 0?
                bne        loc_F89B        ; yes, contiune
                tst        drvN_endofdisk,u ; end of disk reached?
                bne        senddata_eoibyte ; yes, send EOI and terminate read
loc_F896:       jsr        fdc_seek        ; reposition to next sector
                bne        loc_F8B0
loc_F89B:       jsr        fdc_readsector  ; read sector from floppy
                bne        loc_F8B0        ; disk error? skip
                andcc      #~cc_irq        ; enable index IRQ
                jsr        fdc_nextsector  ; advance to next sector
                jmp        loc_F7C2        ; continue sending data
loc_F8A8:       lda        #1              ; notify verify secondary
                sta        gpib_apt_flag
                jmp        loc_F873        ; abort and leave
loc_F8B0:       jsr        set_s2_error    ; decode S2 errors
                ldx        #diskbuf        ; gt disk buffer start
loc_F8B6:       lda        ,x+             ; load byte and avance
                jsr        gpib_sendbyte   ; send it
                bcs        loc_F8C7        ; if erorr, exit
                cmpx       #0xFF           ; last but one byte?
                bne        loc_F8B6        ; no, loop
                lda        ,x              ; get last byte
                jsr        gpib_sendbyte_with_eoi ; send byte with EOI
loc_F8C7:       lda        #1              ; set unsuccessful
                sta        dsj
                jsr        led_on          ; leave LED on
                bra        loc_F873        ; exit

;=============================================
amigo_sendstatus:
                ldx        #status_s1      ; get address of status1
loc_F8D4:       lda        ,x+             ; load byte
                jsr        gpib_sendbyte   ; send it
                bcs        loc_F8E5        ; if error, exit
                cmpx       #sys_state      ; 4 bytes sent?
                bne        loc_F8D4        ; no, loop
                lda        #1              ; send fifth dummy byte EOI
                jsr        gpib_sendbyte_with_eoi
loc_F8E5:       jsr        gpib_set_PP     ; set parallel poll
                rts

;=============================================
gpib_errorstate:
                lda        #1
                sta        dsj             ; notify DSJ error state
                jsr        led_on          ; switch on LED
                lda        s1_errcode      ; get status
                bne        loc_F8FB        ; if set, don't overwrite
                lda        #amigo_s1_ioprog_err ; set S1= 0a (I/O program error)
                sta        s1_errcode
loc_F8FB:       
                jsr        gpib_set_PP     ; set parallel poll
                tst        gpib_apt_flag   ; secondary pending?
                bne        loc_F91E        ; yes, skip
                lda        gpib_am         ; get address mode
                bita       #as_lpas        ; listener secondary requested?
                bne        drain_read      ; yes, continue
                bita       #as_la          ; listener addressed?
                bne        drain_read      ; yes, continue
                bita       #as_tpas        ; talker secondary requested?
                bne        loc_F915        ; yes, continue
                bita       #as_ta          ; talker addressed?
                beq        loc_F91E        ; no, ignore that
loc_F915:       bita       #as_mjmn        ; minor  mode?
                bne        loc_F91E        ; yes, ignore that
                lda        #1              ; send last byte EOI
                jsr        gpib_sendbyte_with_eoi
loc_F91E:       
                lds        #stacktop       ; reset stack
                jmp        mainloop
; ignore incoming bytes until EOI
drain_read:     jsr        gpib_readbyte   ; read byte
                bcs        loc_F92C        ; CY=1, error
                bvc        drain_read      ; not yet EOI, loop
loc_F92C:       lds        #stacktop       ; and reset
                jmp        mainloop

;=============================================
; check whether device is fully initialized
assert_dsj_not_2:
                ldb        dsj             ; get DSJ
                cmpb       #2              ; is it still in init state?
                beq        loc_F8FB        ; yes, signal error
                rts                        ; otherwise exit
				
;=============================================
amigo_illopcode: 
                lda        #1              ; set illegal opcode status
                sta        s1_errcode
				
;=============================================
amigo_errorexit:
                lda        #1              ; DSJ=error
                sta        dsj
                jsr        gpib_set_PP     ; parallel poll
                jsr        led_on          ; switch on LED
                jmp        drain_read      ; drain bytes to read

;=============================================
gpib_validsecondary:
                lda        #auxm_v_sa      ; signal valid secondary address
                sta        gpib_auxm
                rts

;=============================================
gpib_invalidsecondary:
                lda        #auxm_nv_sa     ; signal invalid secondary
                sta        gpib_auxm
                rts

;=============================================
; issue enable parallel poll
gpib_set_PP:    lda        #auxm_set_pp
                sta        gpib_auxm
                rts

;=============================================
; issue disable parallel poll
gpib_clr_PP:    lda        #auxm_clr_pp
                sta        gpib_auxm
                rts

;=============================================
; read from GPIB until UNLISTEN, EOI or SECONDARY
; set APT flag if secondary pending (return in A)
; set V if ended with EOI
; set C if byte in, but not listener (protocol or hardware error)
gpib_readbyte:  andcc      #~cc_c          ; clear CY
                lda        gpib_ad01       ; get interrupt flag
                bpl        loc_F979        ; no interrupt, skip
                lda        gpib_int1       ; get interrupt status
                bita       #ints1_bi       ; byte in interrupt?
                beq        loc_F983        ; no, skip
                bita       #ints1_apt      ; verify secondary?
                beq        loc_F9A3        ; no, skip
                lda        #1              ; notify APT pending
                sta        gpib_apt_flag
                bra        loc_F9A3        ; continue
; interrupt from GPIB pending
loc_F979:       lda        gpib_ad01       ; get interrupt again, was spurious?
                bpl        loc_F992        ; yes, ignore it
                lda        gpib_int1       ; no, this one's for real
                                           ; get interrupt status
                bita       #ints1_bi       ; byte in interrupt?
                bne        loc_F99A        ; yes, skip
loc_F983:       bita       #ints1_dec      ; device clear received?
                lbne       do_dcas         ; yes handle it
                bita       #ints1_apt      ; verify secondary?
                beq        loc_F992        ; no, ignore it
                lda        #1              ; notify APT pending
                sta        gpib_apt_flag
loc_F992:       lda        gpib_am         ; get address mode
                bita       #as_la          ; is listener addressed?
                beq        loc_F9B1        ; no, skip
                bne        loc_F979        ; loop to drain bytes to listen for
loc_F99A:       bita       #ints1_apt      ; verify secondary?
                beq        loc_F9A3        ; no, skip
                lda        #1              ; notify APT pending
                sta        gpib_apt_flag
; just BYTE IN interrupt, get byte
loc_F9A3:       lda        gpib_am         ; just byte in interrupt, get byte
                bita       #as_eoi         ; EOI seen? (clears V)
                beq        loc_F9AE        ; no, return data byte with V=0
                lda        gpio_dio        ; yes, return data byte with V=1
                orcc       #cc_v
                rts
loc_F9AE:       lda        gpio_dio        ; just return data byte with V=0
                rts
loc_F9B1:       orcc       #cc_c           ; set CY=1, end of listen
                lda        gpio_dio        ; read data byte
                rts

;=============================================
; send a databyte through GPIB
gpib_sendbyte:  pshs        a              ; save byte to send
gpib_sendbyte1: lda        gpib_ad01       ; get GPIB int flag
                bpl        loc_F9CD        ; no interrupt
                lda        gpib_int1       ; get interrupt status
                bita       #ints1_bo       ; byte out interrupt?
                beq        loc_F9D7        ; no, skip
                bita       #ints1_apt      ; APT interrupt?
                beq        loc_F9FA        ; no, go sending
                lda        #1              ; flag APT interrupt
                sta        gpib_apt_flag
                bra        loc_F9FA        ; go sending
loc_F9CD:       lda        gpib_ad01       ; get GPIB int flag
                bpl        loc_F9E8        ; no interrupt? skip
                lda        gpib_int1       ; get interrupt status
                bita       #ints1_bo       ; byte out interrupt?
                bne        loc_F9FA        ; yes, go sending
loc_F9D7:       bita       #ints1_dec      ; device clear interrupt?
                lbne       do_dcas         ; yes, reset
                bita       #ints1_apt      ; APT interrupt?
                beq        loc_F9E8        ; no, skip
                lda        #1              ; flag APT interrupt
                sta        gpib_apt_flag
                bra        loc_F9F2        ; send unsuccessful
loc_F9E8:       lda        gpib_am         ; get mode register
                bita       #as_ta          ; is device addressed as talker?
                beq        loc_F9F2        ; no, send unsuccessful
                bita       #as_mjmn        ; minor mode?
                beq        loc_F9CD        ; no, loop
; minor mode or other error: send unsuccessful
loc_F9F2:       puls       a               ; restore byte to send
                clr        eoi_flag
                orcc       #cc_c           ; set carry flag (unsuccessful)
                rts
loc_F9FA:       puls       a               ; restore byte to send
                tst        eoi_flag        ; check if send with EOI
                beq        loc_FA08        ; no, don't EOI
                ldb        #6              ; set EOI on data output
                stb        gpib_auxm
                clr        eoi_flag
loc_FA08:       sta        gpio_dio        ; put byte out on DIO
                andcc      #~cc_c          ; clear CY (success)
                rts

;=============================================
; send a byte with EOI set
gpib_sendbyte_with_eoi:
                pshs       a               ; save byte to send
                lda        #1
                sta        eoi_flag        ; send byte with EOI
                bra        gpib_sendbyte1  ; go send data

;=============================================
; delay, A is number of seconds to delay
delay:          ldx        #58824          ; load timing value
loc_FA19:       jsr        nullsub_1       ; waste time
                jsr        nullsub_1       ; waste more time
                leax       -1,x            ; decrement X
                bne        loc_FA19        ; loop
                deca                       ; decrement A
                bne        delay           ; more delay
                rts                        ; done

;=============================================
switch on LED for 5 seconds
led_on5sec:     ldb        portctrl_state  ; switch on LED
                orab       #ctrl_led
                stb        portctrl_state
                stb        portcc
                lda        #5              ; wait 5 seconds
                jsr        delay
                andb       #~ctrl_led      ; switch off LED
                stb        portctrl_state
                stb        portcc
                rts

;=============================================
; NMI is used for service initiated self test
; shorten jumper on board, select test to be done by
; address switches
nmi_vector:     lds        #stacktop       ; load stack
                lda        statuscc        ; get status port
                bita       #status_test    ; testsw set?
                lbne       loc_FADE        ; test jumper is 1
                jsr        led_on5sec      ; switch on LED for 5sec
selftest_loop:  lda        statuscc        ; get status port
                anda       #7              ; mask address switches
                bne        loc_FA63        ; not zero, skip
                jsr        selftest_chk_ram ; TEST 0: check RAM
                bcs        selftest_error_5sec ; signal failure
                ldb        #1              ; blink LED once
                jsr        blink_led
                bra        loc_FAD2        ; reinitialize
loc_FA63:       cmpa       #1              ; TEST 1: check ROM
                bne        loc_FA73        ; no, skip
                jsr        selftest_chk_rom ; do test
                bcs        selftest_error_5sec ; signal failure
                ldb        #2              ; blink twice for success
                jsr        blink_led
                bra        loc_FAD2        ; reinitialize
loc_FA73:       cmpa       #2              ; TEST 2: check GPIB chip
                bne        loc_FA83        ; no, skip
                jsr        selftest_check_gpib ; do test
                bcs        selftest_error_5sec ; signal failure
                ldb        #3              ; blink 3 times for success
                jsr        blink_led
                bra        loc_FAD2        ; reinitialize
loc_FA83:       cmpa       #3              ; TEST 3: check FDC chip
                bne        loc_FA93        ; no, skip
                jsr        selftest_chk_fdc ; do test
                bcs        selftest_error_5sec ; signal failure
                ldb        #4              ; blink 4 times
                jsr        blink_led
                bra        loc_FAD2        ; reinitialize
loc_FA93:       cmpa       #5              ; TEST 5: check motor speed
                bne        loc_FAA3        ; no, skip
                jsr        selftest_chk_motor ; do test
                bcs        selftest_error_5sec ; signal failure
                ldb        #6              ; blink 6 times
                jsr        blink_led
                bra        loc_FAD2        ; reinitialize
loc_FAA3:       cmpa       #4              ; TEST 4: seek test
                bne        loc_FAB3        ; no, skip
                jsr        selftest_chk_seek ; do test
                bcs        selftest_error_5sec ; signal failure
                ldb        #5              ; blink 5 times for success
                jsr        blink_led
                bra        loc_FAD2        ; reinitialize
loc_FAB3:       cmpa       #6              ; TEST 6: format test
                bne        loc_FAC3        ; no, skip
                jsr        selftest_chk_format ; do test
                bcs        selftest_error_5sec ; signal failure
                ldb        #7              ; blink 7 times
                jsr        blink_led
                bra        loc_FAD2        ; reinitialize
loc_FAC3:       jsr        selftest_chk_verify ; TEST 7: read/verify test
                bcs        selftest_error_5sec ; signal failure
                ldb        #8              ; blink 8 times
                jsr        blink_led
                bra        loc_FAD2        ; reinitialize
selftest_error_5sec:
                jsr        led_on5sec      ; switch on LED for 5 sec to signal failure of test
; arrive here after test, check  for loop mode
loc_FAD2:       lda        statuscc        ; read status port
                bita       #status_loop    ; loop mode?
                lbeq       selftest_loop   ; yes, repeat test
                jmp        initialize_all  ; reinitialize
loc_FADE:       jsr        selftest_chk_ram ; check the RAM
                bcs        loc_FB26        ; failed, skip
                jsr        selftest_chk_rom ; check the ROM
                bcs        loc_FB26        ; failed, skip
                jsr        selftest_check_gpib ; check GPIB chip
                bcs        loc_FB26        ; failed, skip
                jsr        selftest_chk_fdc ; check FDC chip
                bcs        loc_FB26        ; failed, skip
                ldu        #0              ; select drive 0
                stu        drive_ptr
                jsr        fdc_waitrdy_select_curdrv : waiting for being ready
                jsr        fdc_spinup      ; start motor
                lda        statuscc        ; get status port
                bmi        loc_FB1E        ; no disk in drive, skip
                jsr        selftest_chk_seek ; check seek
                bcs        loc_FB26        ; failed, skip
                jsr        selftest_chk_motor ; check motor speed
                bcs        loc_FB26        ; failed, skip
                jsr        selftest_chk_format ; check formatting
                bcc        loc_FB19        ; succeeded, continue
                lda        fdc_status      ; get drive status
                bita       #0x40           ; was write protect?
                beq        loc_FB26        ; no, failed, skip
loc_FB19:       jsr        selftest_chk_verify ; verify formatted disk
                bcs        loc_FB26        ; failed, skip
loc_FB1E:       ldb        #0xA            ; blink LED 10 times
                jsr        blink_led
                jmp        initialize_all  ; initialize
loc_FB26:       jsr        led_on5sec      ; failure: set LED on for 5 seconds
                jmp        initialize_all  ; initialize

;=============================================
; test RAM, if okay CY=0, if error CY=1
selftest_chk_ram:
                ldx        #diskbuf        ; load RAM base
                clra                       ; clear A
loc_FB30:       sta        ,x+             ; clear memory
                inca                       ; increment stored value
                cmpx       #chkram_top     ; check whether close to stack top
                bne        loc_FB30        ; no loop
                ldx        #diskbuf        ; load base of ram again
                clra                       ; clear A
loc_FB3C:       cmpa       ,x+             ; check whether RAM was set correctly
                bne        loc_FB72        ; no, error
                inca                       ; next cell value
                cmpx       #chkram_top     ; end of checked RAM
                bne        loc_FB3C        ; loop
                ldx        #diskbuf        ; load RAM base
                lda        #0xFF           ; load FF
loc_FB4B:       sta        ,x+             ; store in RAM
                deca                       ; decrement stored value
                cmpx       #chkram_top     ; end of checked ram reached?
                bne        loc_FB4B        ; no, loop
                ldx        #diskbuf        ; load RAM base
                lda        #0xFF           ; load check value
loc_FB58:       cmpa       ,x+             ; test if okay
                bne        loc_FB72        ; no error
                deca                       ; decrement value to check
                cmpx       #chkram_top     ; end of ram reached?
                bne        loc_FB58        ; no loop
                ldx        #gpio_dio       ; load memory base
loc_FB65:       clr        ,x+             ; clear memory
                cmpx       #chkram_top     ; until end of checked RAM
                bne        loc_FB65        ; loop
                andcc      #~1             ; clear CY
                jsr        led_off         ; switch off LED
                rts                        ; exit
loc_FB72:       orcc        #1             ; set carry
                rts                        ; exit

;=============================================
selftest_chk_rom:
                ldx        #0xE000         ; load ROM BASE
                ldd        #0              ; clear accu (checksum)
loc_FB7B:       addb       ,x+             ; add byte of ROM
                bcc        loc_FB80        ; if no carry, skip
                inca                       ; increment higher byte of checksum
loc_FB80:       cmpx       #0              ; end of ROM reached?
                bne        loc_FB7B        ; no loop
                std        temp1           ; store ROM checksum
                ldd        rom_checksum    ; check checksum to reach
                clra                       ; only low byte
                std        temp4           ; save it
                ldd        temp1           ; get checksum
                subd       temp4           ; subtract low byte
                std        temp1           ; store it again
                ldd        rom_checksum    ; get high byte of checksum to reach
                clrb
                exg        a, b            ; make it low value
                std        temp4           ; store it
                ldd        temp1           ; subtract it from checksum
                subd       temp4
                cmpd       rom_checksum    ; is it the correct ROM checksum?
                bne        loc_FBB0        ; no, set carry
                andcc      #~cc_c          ; clear CY
                rts                        ; exit
loc_FBB0:       orcc        #cc_c          ; set CY for error
                rts

;=============================================
selftest_check_gpib:
                lda        #0x55            ; store pattern in address
                sta        gpib_ad01
                cmpa       gpib_ad01        ; check  if it can be read out
                bne        loc_FBD9         ; no, exit with CY=1 (error)
                lda        #0x2A 
                sta        gpib_ad01        ; initialize LISTEN 0x0a
                cmpa       gpib_ad01        ; check if this can be set
                bne        loc_FBD9         ; no, exit with error
                lda        #0xC0            ; set mode to talk to itself
                sta        gpib_am
                clr        gpib_ad01        ; select address 0
                clr        gpib_auxm        ; set immediate PON
                clra
loc_FBCC:       sta        gpio_dio         ; send data byte 0
                nop                         ; wait
                cmpa       gpio_dio         ; check that it can be received again
                bne        loc_FBD9         ; no, exit with error
                inca                        ; increment byte
                bne        loc_FBCC         ; loop 256 times
                andcc      #~cc_c           ; exit okay
                rts
loc_FBD9:       orcc       #cc_c            ; exit error
                rts

;=============================================
; verify that FDC is present and some regs are writeable
selftest_chk_fdc:
                lda        #0x55            ; load some ill value into FDC track register
                sta        FDC_port.tr
                coma                        ; complement into sector register
                sta        FDC_port.scr
                jsr        nullsub_1        ; wait a bit
                cmpa       FDC_port.scr     ; compare it with stored value
                bne        loc_FC0B         ; not the same, exit CY=1 (error)
                coma                        ; complement
                cmpa       FDC_port.tr      ; compare with track register
                bne        loc_FC0B         ; not okayx, error
                sta        FDC_port.scr     ; do the same again with reversed bits
                coma
                sta        FDC_port.tr
                jsr        nullsub_1
                cmpa       FDC_port.tr
                bne        loc_FC0B         ; not okay, error
                coma
                cmpa       FDC_port.scr     ; not okay, error
                bne        loc_FC0B
                andcc      #~cc_c           ; clear CY (no error)
                rts
loc_FC0B:       orcc       #cc_c
                rts

;=============================================
; check        that track 0 can be correctly reached
; return CY=1 if error,        0 if success
selftest_chk_seek:
                ldu        #0              ; select drive 0
                stu        drive_ptr
loc_FC14:       jsr        fdc_waitrdy_select_curdrv ; wait for disk ready
                jsr        fdc_restore_trk0 ; move to track0
                bcs        loc_FC4F        ; failed, continue
                lda        #fdcmd_restore  ; issue RESTORE again
                sta        FDC_port.str_cr
                jsr        fdc_waitrdy     ; wait until done
                lda        FDC_port.str_cr ; get status
                bita       #fdc_status1_trk0
                bne        loc_FC62        ; is track 0 not reached? (negative logic!)
                                           ; goto error
                lda        #69             ; position to last track
                coma
                sta        FDC_port.dr     ; target position
                lda        #fdcmd_seek
                sta        FDC_port.str_cr ; issue SEEK command
                jsr        fdc_waitrdy     ; wait until done
                lda        FDC_port.str_cr ; get status
                bita       #fdc_status1_trk0 ; is track 0 reached?
                beq        loc_FC62        ; oops, this is reversed? error
                lda        #fdcmd_restore  ; issue RESTORE
                sta        FDC_port.str_cr
                jsr        fdc_waitrdy     ; wait until done
                lda        FDC_port.str_cr
                bita       #fdc_status1_trk0 ; is track 0 not reached?
                bne        loc_FC62        ; oops, this is reversed?
loc_FC4F:       inc        drive_ptr+1     ; increment drive pointer
                ldu        drive_ptr       ; select drive
                lda        #2              ; loop over all drives
                cmpa       drive_ptr+1
                bne        loc_FC14        ; loop
                jsr        fdc_select_drv0 ; select drive 0
                andcc      #~cc_c          ; return CY=0
                rts
loc_FC62:       jsr        fdc_select_drv0 ; select drive 0
                orcc       #cc_c           ; return with error
                rts

;=============================================
; check  if motor speed on track 36 is within valid limits
; exit CY=1 if error
; requires disk in drive to be checked,
; will return success if no disk in drive
; (power on selftest with no disks in drives)
selftest_chk_motor:
                ldu        #0              ; select drive 0
                stu        drive_ptr
loc_FC6E:       jsr        fdc_waitrdy_select_curdrv ; wait until drive ready
                jsr        fdc_spinup      ; start motor
                jsr        fdc_loadhead    ; load head
                jsr        fdc_restore_trk0 ; move to track 0
                bcs        loc_FC8F        ; if error, skip
                lda        #35             ; position to track 35
                coma
                sta        FDC_port.dr
                lda        #fdcmd_seek     ; issue SEEK
                sta        FDC_port.str_cr
                jsr        fdc_waitrdy     ; wait until done
                jsr        fdc_checkmotorspeed ; check speed of motor in valid range
                bcs        loc_FCA5        ; if not, exit failure
loc_FC8F:       inc        drive_ptr+1     ; advance to next drive
                ldu        drive_ptr
                lda        #2
                cmpa       drive_ptr+1     ; all drives done?
                bne        loc_FC6E        ; no, loop
                jsr        fdc_motoroff    ; switch off motor
                jsr        fdc_select_drv0 ; select drive 0
                andcc      #~cc_c          ; exit with success
                rts
loc_FCA5:       jsr        fdc_motoroff    ; switch off motor
                jsr        fdc_select_drv0 ; select drive 0
                orcc       #cc_c           ; exit with error
                rts

;=============================================
; return CY=0 if revolution speed is okay
fdc_checkmotorspeed:
                ldx        #0              ; set counter
loc_FCB1:       leax       -1,x            ; decrement X
                beq        loc_FCF2        ; if zero reached, return CY=1
                lda        FDC_port.str_cr ; get fdc status
                anda       #fdc_status1_index ; index pulse found?
                beq        loc_FCB1        ; no, loop
                ldx        #0              ; reset counter
loc_FCBF:       leax       -1,x            ; decrement
                beq        loc_FCF2        ; if zero reached, return CY=1
                lda        FDC_port.str_cr ; get fdc status
                anda       #fdc_status1_index ; still index pulse
                bne        loc_FCBF        ; yes, wait until zero
                ldx        #22500          ; load count for speed
loc_FCCD:       leax       -1,x            ; decrement
                bne        loc_FCCD        ; wait some time

;=============================================
fdc_measure_speed:
                ldx        #0              ; clear counter
loc_FCD4:       jsr        nullsub_1       ; wait some time
                jsr        nullsub_1
                leax       1,x             ; increment
                beq        loc_FCF2        ; overrun, exit CY=1
                lda        FDC_port.str_cr ; get fdc status
                anda       #fdc_status1_index ; check index pulse
                bne        loc_FCD4        ; is still one
                cmpx       #314            ; is time to index pulse between 314 and 584
                bls        loc_FCF2
                cmpx       #584
                bcc        loc_FCF2
                andcc      #~cc_c          ; yes, okay, return CY=0
                rts
loc_FCF2:       orcc       #cc_c           ; return CY=1
                rts

;=============================================
; format all disks in drives
; return CY=1 if failure, 0 if success
selftest_chk_format:
                ldu        #0              ; select drive 0
                stu        drive_ptr
loc_FCFB:       jsr        fdc_waitrdy_select_curdrv ; wait for disk ready
                jsr        fdc_spinup      ; start motor
                jsr        fdc_restore_trk0 ; go to track 0
                bcs        loc_FD19        ; if error, continue with next drive
                jsr        delay_5000      ; wait some time
                clr        format_filler   ; filler pattern = 0
                lda        #1
                sta        format_interleave ; set interleave=1
                sta        override_old_fmt ; set override flag
                jsr        fdc_format_disk ; format entire disk
                bne        loc_FD2F        ; if error, exit
loc_FD19:       inc        drive_ptr+1     ; increment drives to test
                ldu        drive_ptr
                lda        #2
                cmpa       drive_ptr+1     ; last drive?
                bne        loc_FCFB        ; no, loop
                jsr        fdc_motoroff    ; switch off motor
                jsr        fdc_select_drv0 ; select drive 0
                andcc      #~cc_c          ; return with success
                rts
loc_FD2F:       sta        fdc_status      ; store error data
                jsr        fdc_motoroff    ; switch off motor
                jsr        fdc_select_drv0 ; select drive 0
                orcc       #cc_c           ; exit with error
                rts

;=============================================
; ensure all sectors of disk are readable without error
selftest_chk_verify:
                ldu        #0              ; start with drive 0
                stu        drive_ptr
loc_FD41:       jsr        fdc_waitrdy_select_curdrv ; select drive
                jsr        fdc_spinup      ; start motor
                jsr        fdc_restore_trk0 ; go to track 0
                bcs        loc_FD66        ; if error, continue with next drive
                jsr        delay_5000      ; wait some time
                clr        drvN_tgthead,u  ; position target to 0,0,0
                clr        drvN_tgtcylinder,u
                clr        drvN_tgtsector,u
                clr        verify_seccnt   ; clear sector count
                clr        verify_seccnt+1
                jsr        verify_sectors  ; verify all sectors can be read
                bne        loc_FD7C        ; if error, exit
loc_FD66:       inc        drive_ptr+1     ; advance to next drive
                ldu        drive_ptr
                lda        #2
                cmpa       drive_ptr+1     ; last drive?
                bne        loc_FD41        ; no, loop
                jsr        fdc_motoroff    ; switch off motor
                jsr        fdc_select_drv0 ; select drive 0
                andcc      #~cc_c          ; return with CY=0, success
                rts
loc_FD7C:       jsr        fdc_motoroff    ; switch of motor
                jsr        fdc_select_drv0 ; select drive 0
                orcc       #cc_c           ; return with error
                rts

;=============================================
; undocumented command
; listen 0c 1a
; formats the boot track with various patterns
; to ensure it is correct
; then expects a listen 02 and a sector number
; and 256 data bytes to be written into the
; specified sector. Verifies correct writing.
; continue over all sectors of the disk until end of disk
;
; this is supposedly an HP internal command to
; fill an entire disk with data.
amigo_writedisk: 
                jsr        gpib_readbyte   ; get unit byte
                lbcs       gpib_errorstate ; if error, exit
                jsr        chk_validunit   ; check if valid unit
                sta        selected_unit   ; select it
                jsr        gpib_readbyte   ; get another byte
                lbcs       gpib_errorstate ; if error, exit
                clr        gpib_apt_flag   ; clear flag "secondary pending"
                cmpa       #0xF            ; is byte >15? yes error
                lbhi       gpib_errorstate
                sta        format_specinterleave ; store it
                jsr        gpib_clr_PP     ; disable parallel poll
                clr        dsj             ; set success flag
                jsr        fdc_waitrdy_select_curdrv ; wait for disk
                jsr        fdc_spinup      ; start motor
                clra
                sta        drvN_activehead,u ; clear known position
                sta        drvN_activecyl,u
                sta        drvN_curhead,u  ; clear reported position
                sta        drvN_curtrk,u
                sta        drvN_tgtcylinder,u ; clear target to go to
                sta        drvN_tgthead,u
                coma                       ; negative logic
                sta        FDC_port.tr     ; store into track regeister
                lda        #fdcmd_restore
                sta        FDC_port.str_cr ; issue RESTORE (to track 0) command
                jsr        fdc_waitrdy     ; wait until done
loc_FDD6:       jsr        delay_5000      ; wait some time
                lda        #1
                sta        fmtcnt          ; take filler 1 from table
                sta        sys_state       ; set state=1
loc_FDE1:       lda        drvN_tgtcylinder,u ; set parameters for formatting this track
                sta        format_cylinder
                lda        drvN_tgthead,u
                sta        format_head
                lda        #1              ; set interleave 1
                sta        format_interleave
                lda        fmtcnt          ; get pattern to use
                sta        trkpattern+1    ; make a 16 bit index
                clr        trkpattern
                ldx        trkpattern
                lda        track_fillers,x ; get filler byte
                sta        format_filler   ; set it as fill byte
                jsr        fdc_format      ; format the first track with interleave 1
                beq        loc_FE14        ; if no error, continue
                jsr        set_s2_error    ; set error codes
                lda        #1              ; set unsuccessful
                sta        dsj
loc_FE14:       clr        drvN_tgtsector,u ; :start at sector 0
loc_FE18:       jsr        fdc_readsector  ; verify sector
                beq        loc_FE25        ; was okay, skip
                jsr        set_s2_error    ; otherwise set error ocdes
                lda        #1              ; set unsuccessful
                sta        dsj
loc_FE25:       lda        drvN_tgtsector,u ; loop over alls sectors of track
                cmpa       #15
                beq        loc_FE33        ; done
                inc        drvN_tgtsector,u ; next sector
                bra        loc_FE18        ; loop
loc_FE33:       dec        fmtcnt          ; take next pattern
                bpl        loc_FDE1        ; still positive, retry with other pattern
                lda        format_specinterleave ; get actual interleave to use
                sta        format_interleave ; store it
                lda        drvN_tgtcylinder,u ; get target cylinder
                sta        format_cylinder ; set format parameters
                lda        drvN_tgthead,u
                sta        format_head
                jsr        fdc_format      ; format that track again
                beq        loc_FE59        ; if not error, continue
                jsr        set_s2_error    ; set error
                lda        #1
                sta        dsj
loc_FE59:       lda        #0xF            ; set count of 15
                sta        fmtcnt
                jsr        gpib_set_PP     ; set parallel poll
loc_FE61:       lda        gpib_ad01       ; check GPIB interrupt
                bpl        loc_FE61        ; wait until one happens
                lda        gpib_int1       ; get int status
                bita       #ints1_apt      ; verify other secondary?
                lbeq       gpib_errorstate ; no, error
                lda        gpib_auxm       ; get mode
                anda       #~as_ton        ; mask bits
                cmpa       #0x62           ; is it secondary 2?
                beq        loc_FE7B        ; yes, continue
                jsr        gpib_invalidsecondary ; no, do not accept more
                jmp        gpib_errorstate ; go to error state
loc_FE7B:       jsr        gpib_validsecondary ; acknowledge valid secondary
loc_FE7E:       lda        gpib_ad01       ; wait for GPIB interrupt
                bpl        loc_FE7E
                lda        gpib_int1       ; get interrupt status
                bita       #ints1_bi       ; byte in pending?
                lbeq       gpib_errorstate ; no, error
                lda        gpio_dio        ; get data byte
                sta        drvN_tgtsector,u ; set as target sector
                ldx        #0              ; set start of buffer
loc_FE93:       lda        gpib_ad01       ; wait for GPIB interrupt
                bpl        loc_FE93
                lda        gpib_int1       ; get int status
                bita       #ints1_bi       ; byte in pending?
                lbeq       gpib_errorstate ; no, error
                ldb        gpio_dio        ; Get data byte
                stb        ,x+             ; store in buffer
                cmpx       #end_buf        ; end of buffer reached?
                bne        loc_FE93        ; no, loop
                jsr        fdc_write_sector ; write this sector to disk
                beq        loc_FEB5        ; no error, continue
                jsr        set_s2_error    ; set error flags
                lda        #1              ; set unsuccessful
                sta        dsj
loc_FEB5:       dec        fmtcnt          ; decrement count of sectors
                bpl        loc_FE7E        ; loop over 16 sectors
                jsr        gpib_clr_PP     ; disable parallel poll
                clra
                ldb        #0xF
                std        cnt16           ; set 16bit counter to 16
loc_FEC3:       ldx        cnt16           ; get counter
                lda        sector_numbers,x ; get sector number table
                sta        drvN_tgtsector,u ; set as sector to verify
                jsr        fdc_readsector  ; verify the given sector
                beq        loc_FEDB        ; yes, continue
                jsr        set_s2_error    ; set error code
                lda        #1              ; set unsuccessful
                sta        dsj
loc_FEDB:       dec        cnt16+1         ; decrement loop
                bpl        loc_FEC3        ; loop over all sectors to verify
                jsr        fdc_nextsector1 ; Advance to next sector
                tst        drvN_endofdisk,u ; end of disk reached?
                bne        loc_FEFF        ; yes, skip
                jsr        fdc_stepin      ; go to next cylinder
                lda        drvN_tgtcylinder,u ; copy target position into track register
                adda       drvN_tgtcylinder,u
                adda       drvN_tgthead,u
                coma
                sta        FDC_port.tr
                jmp        loc_FDD6        ; loop over all tracks
loc_FEFF:       jsr        fdc_restore_trk0 ; return to track 0
                jsr        delay_5000      ; wait some time
                lda        #0xE7
                sta        drvN_wearh,u    ; set a high value into wear level
                clra
                sta        drvN_wearm,u
                sta        drvN_wearl,u
                sta        gpib_int1       ; clear GPIB interrupts
                sta        sys_state       ; set state = 0
                jsr        write_sector17  ; write wear level into sector 17
                jsr        gpib_set_PP     ; enable parallel poll
                tst        dsj             ; were there errors?
                beq        locret_FF2A     ; no, exit
                lda        #1              ; mark disk as worn out
                sta        drvN_disk_wornout,u
locret_FF2A:    rts                        ; exit

track_fillers:  .db        ~0xA4
                .db        ~0

;=============================================
delay_5000:     ldx        #5000           ; waste some time
loc_FF30:       leax       -1,x
                bne        loc_FF30
                rts

;=============================================
; undocumented
; listen 1d
; 00: go to trk0, then step in # of tracks
;     accept three pattern bytes and fill selected
;     track with it
;
; 01: go to trk0, then step in # of tracks
; 02: step in unit # of tracks
; 03: step out unit # of tracks
;
; supposedly HP internal diagnostic
amigo_diagnostic:
                jsr        gpib_validsecondary ; acknowledge valid secondary
                jsr        gpib_readbyte   ; get opcode byte
                lbcs       gpib_errorstate ; if error, exit
                cmpa       #3              ; is it >3? yes illegal opcode
                lbhi       amigo_illopcode
                sta        fdc_status      ; save temporary
                jsr        gpib_readbyte   ; get unit byte
                lbcs       gpib_errorstate ; if error, exit
                jsr        chk_validunit   ; check valid unit
                jsr        fdc_waitrdy_select_curdrv
                jsr        fdc_spinup      ; start motor
                lda        fdc_status      ; get opcode
                beq        stepdiag_op01   ; if 0 or 1, continue
                cmpa       #1
                beq        stepdiag_op01
                cmpa       #2              ; if opcode=2, skip
                beq        stepdiag_op2
                jsr        gpib_readbyte   ; opcode is 3, get step count
                lbcs       gpib_errorstate ; if error, exit
                tfr        a, b            ; move to B as count
loc_FF6E:       decb                       ; decrement count
                bmi        stepdiag_done   ; if negative, skip
                jsr        fdc_movetrack_out ; step towards track 0
                bra        loc_FF6E        ; loop
stepdiag_op2:   jsr        gpib_readbyte   ; opcode is 2, get step count
                lbcs       gpib_errorstate ; if error, exit
                tfr        a, b            ; move to B as count
loc_FF7F:       decb                       ; decrement count
                bmi        stepdiag_done   ; if negative, skip
                jsr        fdc_movetrack_in ; step towards track 69
                bra        loc_FF7F        ; loop
stepdiag_op01:  jsr        fdc_loadhead    ; load head
                jsr        fdc_restore_trk0 ; move to track 0
                bra        stepdiag_op2    ; step n steps forward
stepdiag_done:  lda        fdc_status      ; get opcode
                bne        loc_FFB5        ; is not zero?
                jsr        gpib_readbyte   ; get another byte
                lbcs       gpib_errorstate ; is error?
                sta        fdc_status      ; store 1st byte
                jsr        gpib_readbyte   ; get another byte
                lbcs       gpib_errorstate ; if error, exit
                sta        temp3           ; store 2nd byte
                jsr        gpib_readbyte   ; get 3rd byte
                lbcs       gpib_errorstate
                sta        temp1           ; save it
                jsr        write_diagpattern ; fill track with given diagnostic patterns
loc_FFB5:       jsr        gpib_readbyte   ; get a (EOI) byte from GPIB and exit
                rts

;=============================================
; diag is called from undocumented diagnostic function
; this fills the specified track with
; a triple of alignment patterns in fdc_status, temp3 and temp1
write_diagpattern:
                clra
                sta        gpib_int1       ; disable GPIB interrupt
                sta        sys_state       ; set state = 0
                lda        FDC_port.str_cr ; drain FDC status
                andcc      #~cc_firq       ; enable FIRQ
                lda        portctrl_state
                oraa       #ctrl_halten    ; enable HALTEN
                sta        portcc
                ldb        #fdcmd_writetrk ; issue write track command
                stb        FDC_port.str_cr
                sta        portctrl_state
loc_FFD4:       lda        fdc_status      ; get first data byte
                coma
                nop
                sta        FDC_port.dr     ; store into data register
                lda        temp3           ; get second data byte
                coma
                nop
                sta        FDC_port.dr     ; store into data register
                lda        temp1           ; get second data byte
                coma
                nop
                sta        FDC_port.dr     ; store into data register
                bra        loc_FFD4        ; loop until FIRQ

;=============================================
                .dw        0xFFFF
                .dw        0
                .dw        reset_vector        ; SWI3_vector (unused)
                .dw        reset_vector        ; SWI2_vector (unused)
                .dw        firq_vector         ; handles interrupts from GPIB and FDC
                .dw        irq_vector          ; counts index pulses of spinning disk
                .dw        reset_vector        ; SWI vector (unused)
                .dw        nmi_vector          ; self test
                .dw        reset_vector        ; standard powerup
