root/drivers/block/swim_asm.S
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * low-level functions for the SWIM floppy controller
 *
 * needs assembly language because is very timing dependent
 * this controller exists only on macintosh 680x0 based
 *
 * Copyright (C) 2004,2008 Laurent Vivier <Laurent@lvivier.info>
 *
 * based on Alastair Bridgewater SWIM analysis, 2001
 * based on netBSD IWM driver (c) 1997, 1998 Hauke Fath.
 *
 * 2004-08-21 (lv) - Initial implementation
 * 2008-11-05 (lv) - add get_swim_mode
 */

        .equ    write_data,     0x0000
        .equ    write_mark,     0x0200
        .equ    write_CRC,      0x0400
        .equ    write_parameter,0x0600
        .equ    write_phase,    0x0800
        .equ    write_setup,    0x0a00
        .equ    write_mode0,    0x0c00
        .equ    write_mode1,    0x0e00
        .equ    read_data,      0x1000
        .equ    read_mark,      0x1200
        .equ    read_error,     0x1400
        .equ    read_parameter, 0x1600
        .equ    read_phase,     0x1800
        .equ    read_setup,     0x1a00
        .equ    read_status,    0x1c00
        .equ    read_handshake, 0x1e00

        .equ    o_side, 0
        .equ    o_track, 1
        .equ    o_sector, 2
        .equ    o_size, 3
        .equ    o_crc0, 4
        .equ    o_crc1, 5

        .equ    seek_time, 30000
        .equ    max_retry, 40
        .equ    sector_size, 512

        .global swim_read_sector_header
swim_read_sector_header:
        link    %a6, #0
        moveml  %d1-%d5/%a0-%a4,%sp@-
        movel   %a6@(0x0c), %a4
        bsr     mfm_read_addrmark
        moveml  %sp@+, %d1-%d5/%a0-%a4
        unlk    %a6
        rts

sector_address_mark:
        .byte   0xa1, 0xa1, 0xa1, 0xfe
sector_data_mark:
        .byte   0xa1, 0xa1, 0xa1, 0xfb

mfm_read_addrmark:
        movel   %a6@(0x08), %a3
        lea     %a3@(read_handshake), %a2
        lea     %a3@(read_mark), %a3
        moveq   #-1, %d0
        movew   #seek_time, %d2

wait_header_init:
        tstb    %a3@(read_error - read_mark)
        moveb   #0x18, %a3@(write_mode0 - read_mark)
        moveb   #0x01, %a3@(write_mode1 - read_mark)
        moveb   #0x01, %a3@(write_mode0 - read_mark)
        tstb    %a3@(read_error - read_mark)
        moveb   #0x08, %a3@(write_mode1 - read_mark)

        lea     sector_address_mark, %a0
        moveq   #3, %d1

wait_addr_mark_byte:

        tstb    %a2@
        dbmi    %d2, wait_addr_mark_byte
        bpl     header_exit

        moveb   %a3@, %d3
        cmpb    %a0@+, %d3
        dbne    %d1, wait_addr_mark_byte
        bne     wait_header_init

        moveq   #max_retry, %d2

amark0: tstb    %a2@
        dbmi    %d2, amark0
        bpl     signal_nonyb

        moveb   %a3@, %a4@(o_track)

        moveq   #max_retry, %d2

amark1: tstb    %a2@
        dbmi    %d2, amark1
        bpl     signal_nonyb

        moveb   %a3@, %a4@(o_side)

        moveq   #max_retry, %d2

amark2: tstb    %a2@
        dbmi    %d2, amark2
        bpl     signal_nonyb

        moveb   %a3@, %a4@(o_sector)

        moveq   #max_retry, %d2

amark3: tstb    %a2@
        dbmi    %d2, amark3
        bpl     signal_nonyb

        moveb   %a3@, %a4@(o_size)

        moveq   #max_retry, %d2

crc0:   tstb    %a2@
        dbmi    %d2, crc0
        bpl     signal_nonyb

        moveb   %a3@, %a4@(o_crc0)

        moveq   #max_retry, %d2

crc1:   tstb    %a2@
        dbmi    %d2, crc1
        bpl     signal_nonyb

        moveb   %a3@, %a4@(o_crc1)

        tstb    %a3@(read_error - read_mark)

header_exit:
        moveq   #0, %d0
        moveb   #0x18, %a3@(write_mode0 - read_mark)
        rts
signal_nonyb:
        moveq   #-1, %d0
        moveb   #0x18, %a3@(write_mode0 - read_mark)
        rts

        .global swim_read_sector_data
swim_read_sector_data:
        link    %a6, #0
        moveml  %d1-%d5/%a0-%a5,%sp@-
        movel   %a6@(0x0c), %a4
        bsr     mfm_read_data
        moveml  %sp@+, %d1-%d5/%a0-%a5
        unlk    %a6
        rts

mfm_read_data:
        movel   %a6@(0x08), %a3
        lea     %a3@(read_handshake), %a2
        lea     %a3@(read_data), %a5
        lea     %a3@(read_mark), %a3
        movew   #seek_time, %d2

wait_data_init:
        tstb    %a3@(read_error - read_mark)
        moveb   #0x18, %a3@(write_mode0 - read_mark)
        moveb   #0x01, %a3@(write_mode1 - read_mark)
        moveb   #0x01, %a3@(write_mode0 - read_mark)
        tstb    %a3@(read_error - read_mark)
        moveb   #0x08, %a3@(write_mode1 - read_mark)

        lea     sector_data_mark, %a0
        moveq   #3, %d1

        /* wait data address mark */

wait_data_mark_byte:

        tstb    %a2@
        dbmi    %d2, wait_data_mark_byte
        bpl     data_exit

        moveb   %a3@, %d3
        cmpb    %a0@+, %d3
        dbne    %d1, wait_data_mark_byte
        bne     wait_data_init

        /* read data */

        tstb    %a3@(read_error - read_mark)

        movel   #sector_size-1, %d4             /* sector size */
read_new_data:
        movew   #max_retry, %d2
read_data_loop:
        moveb   %a2@, %d5
        andb    #0xc0, %d5
        dbne    %d2, read_data_loop
        beq     data_exit
        moveb   %a5@, %a4@+
        andb    #0x40, %d5
        dbne    %d4, read_new_data
        beq     exit_loop
        moveb   %a5@, %a4@+
        dbra    %d4, read_new_data
exit_loop:

        /* read CRC */

        movew   #max_retry, %d2
data_crc0:

        tstb    %a2@
        dbmi    %d2, data_crc0
        bpl     data_exit

        moveb   %a3@, %d5

        moveq   #max_retry, %d2

data_crc1:

        tstb    %a2@
        dbmi    %d2, data_crc1
        bpl     data_exit

        moveb   %a3@, %d5

        tstb    %a3@(read_error - read_mark)

        moveb   #0x18, %a3@(write_mode0 - read_mark)

        /* return number of bytes read */

        movel   #sector_size, %d0
        addw    #1, %d4
        subl    %d4, %d0
        rts
data_exit:
        moveb   #0x18, %a3@(write_mode0 - read_mark)
        moveq   #-1, %d0
        rts