871 lines
22 KiB
ArmAsm
871 lines
22 KiB
ArmAsm
/*
|
|
/* @(#)fd_asm.s 1.1 94/10/31 SMI */
|
|
/*
|
|
* Copyright (c) 1989 by Sun Microsystems, Inc.
|
|
*/
|
|
/*
|
|
* Campus 1 low level floppy hardware interrupt handler.
|
|
*
|
|
* This is hardwired onto level 11 all by itself. It is needed to handle
|
|
* the floppy data transfers without DMA. The basic flow of operations is:
|
|
* device driver calculates parameters
|
|
* device driver sets up fdintr_* values for this interrupt handler
|
|
* device driver starts operation
|
|
* device driver sleeps on someplace where its software interrupt handler
|
|
* will wake it.
|
|
* this interrupt handler does the data/result xfer for read/write/formats
|
|
* (or sense status for recalibrate/seek)
|
|
* then this interrupt handler sets a flag for the floppy soft interrupt
|
|
* handler and sets its bit in the interrupt control register
|
|
* the floppy software interrupt handler does whatever it needs to,
|
|
* (like retries, wake of upper levels, ... )
|
|
* and clears the opmode value to "reset" the hardware level.
|
|
*
|
|
*
|
|
* NOTES ON TRAPS
|
|
* This code is called directly out of the trap table and
|
|
* RUNS WITH TRAPS DISABLED! Therefore it must be PERFECTLY ROBUST!
|
|
*
|
|
* Some things to remember:
|
|
* when a trap is taken, traps are disabled: SO we must be *VERY CAREFUL*
|
|
* when a trap is taken, PC and nPC go into r17/%l1 and r18/%l2, respectively
|
|
* the TRAP() macro is: #define TRAP(H) b (H); mov %psr,%l0; nop; nop;
|
|
* I have only the registers %l0-%l7 to work with.
|
|
* - and %l0 - %l2 are already used.
|
|
* the two instructions to return from trap are: jmpl ; rett
|
|
* - ie: jmp %l1 ! return
|
|
* rett %l2
|
|
*
|
|
* TODO:
|
|
* () decide on status returned, now it is just 0 = ok, !0 = !ok
|
|
*/
|
|
|
|
#define I82077
|
|
|
|
#include <machine/auxio.h>
|
|
#include <machine/intreg.h>
|
|
#include <machine/asm_linkage.h>
|
|
|
|
#include <machine/mmu.h>
|
|
|
|
/* these defines are always the same for 82072 controller chip */
|
|
#define RQM 0x80
|
|
#define DIO 0x40
|
|
#define NDM 0x20
|
|
#define CB 0x10
|
|
#define SENSE_INTERRUPT_STATUS 0x08
|
|
|
|
|
|
#ifdef COMMENT /* just a (longish) comment of pseudocode */
|
|
fdc_hardintr()
|
|
{
|
|
int timeoutcntr; /* timeout counter NYD XXX NOT USED? TBD */
|
|
register u_char *fdc; /* pointer to fdc registers */
|
|
register int len;
|
|
register char *addr;
|
|
|
|
fdc = fdctlr_reg;
|
|
|
|
switch( fdintr_opmode ) {
|
|
case 1:
|
|
/* read/write/format data xfer - during data xfer phase */
|
|
/***timeoutcntr = fdintr_timeout; /* XXX NYD TBD NOT USED? */
|
|
len = fdintr_len;
|
|
addr = fdintr_addr;
|
|
|
|
/* if the fifo ready bit set, then data available */
|
|
while( *fdc & RQM ) {
|
|
if( (*fdc & NDM) == 0 ) {
|
|
/* whoops! things blew up, abandon it */
|
|
/* DON'T send terminal count, it clears status*/
|
|
fdintr_len = len;
|
|
fdintr_addr = addr;
|
|
fdintr_opmode = 3;
|
|
goto results;
|
|
}
|
|
/* NDM is set, that means data */
|
|
/* if input data, then input; else output */
|
|
if( *fdc & DIO )
|
|
*addr++ = *fifo;
|
|
else
|
|
*fifo = *addr++;
|
|
len--;
|
|
/* if length went to 0, then send the TC */
|
|
if( len == 0 )
|
|
goto end_xfr;
|
|
}
|
|
/* no more data, stash updated len, addr */
|
|
fdintr_len = len;
|
|
fdintr_addr = addr;
|
|
break;
|
|
/*****/
|
|
end_xfr:
|
|
/* start send terminal count pulse */
|
|
*(char *)AUXIO_REG |= (AUX_TC|AUX_MBO);
|
|
/* save updated len, addr (is also a DELAY for TC) */
|
|
fdintr_len = len;
|
|
fdintr_addr = addr;
|
|
/* clear the terminal count pulse */
|
|
*(char *)AUXIO_REG =
|
|
AUX_MBO | ((*(char *)AUXIO_REG) & ~AUX_TC);
|
|
/* set "this is status phase" */
|
|
fdintr_opmode = 3;
|
|
return;
|
|
break;
|
|
case 2:
|
|
/* recal,seek - no result phase, must do sense interrupt status */
|
|
sense_int_stat:
|
|
while( *fdc & CB ) ;
|
|
while( ! (*fdc & RQM) ) ;
|
|
*fifo = SENSE_INTERRUPT_STATUS;
|
|
while( ! (*fdc & RQM) ) ;
|
|
fdintr_statbuf[0] = *fifo;
|
|
while( ! (*fdc & RQM) ) ;
|
|
fdintr_statbuf[1] = *fifo;
|
|
goto notify; /* now tell upper layer that things are done */
|
|
break;
|
|
|
|
case 3:
|
|
results:
|
|
/* result phase of regular ops, gather status */
|
|
addr = fdintr_statp; /* get ptr to status buf */
|
|
while( *fdc & CB ) { /* while chip is still "busy" */
|
|
while( (*fdc & (RQM|DIO)) != (RQM|DIO) )
|
|
;
|
|
*addr++ = *fifo;
|
|
}
|
|
fdintr_statp = addr; /* save updated ptr */
|
|
goto notify; /* now tell upper layer that things are done */
|
|
break;
|
|
|
|
default:
|
|
/* no drive, no operation, ... */
|
|
fdintr_spurious++;
|
|
if( fdc != 0 )
|
|
goto sense_int_stat;
|
|
/* shall we do a reset to the FDC ? XXX NYD TBD */
|
|
}
|
|
goto out;
|
|
/*******/
|
|
|
|
notify:
|
|
/* set bit in soft interrupt register */
|
|
#ifndef sun4m
|
|
*(u_char *)INTREG_ADDR |= IR_SOFT_INT4;
|
|
#else sun4m
|
|
send_dirint(get_processorid(), 4);
|
|
#endif sun4m
|
|
/* set software interrupt flag */
|
|
fdintr_opmode = 4;
|
|
fdintr_status = 0;
|
|
|
|
out:
|
|
restore_psr_with_delay;
|
|
return_from_trap;
|
|
}
|
|
#endif COMMENT
|
|
|
|
|
|
/* start of assembler source */
|
|
/*
|
|
* private data for this interrupt handler
|
|
* I = input, set before starting operation
|
|
* O = output, available *after* this invokes the software level
|
|
*/
|
|
.seg "data"
|
|
.align 4
|
|
|
|
/* I: 1 = read/write/format, 2 = seek/recal; O: 4 = done */
|
|
/* 3 = internal state -waiting for results */
|
|
/* only set if operation is actually pending - KEEP CLEAR OTHERWISE! */
|
|
.global _fdintr_opmode
|
|
_fdintr_opmode:
|
|
.word 0x0
|
|
|
|
/* I/O: (current) data xfer address */
|
|
.global _fdintr_addr
|
|
_fdintr_addr:
|
|
.word 0x0
|
|
|
|
/* I: data xfer length; O: remainder */
|
|
.global _fdintr_len
|
|
_fdintr_len:
|
|
.word 0x0
|
|
|
|
/* I: n/a; O: 0 = ok, TBD XXX NYD errors */
|
|
.global _fdintr_status
|
|
_fdintr_status:
|
|
.word 0x0
|
|
|
|
/* timeout value for ??? */
|
|
/* NOT USED YET/EVER??? XXX TBD NYD NEVER MAYBE */
|
|
.global _fdintr_timeout
|
|
_fdintr_timeout:
|
|
.word 0x0
|
|
|
|
/* count of spurious interrupts */
|
|
.global _fdintr_spurious
|
|
_fdintr_spurious:
|
|
.word 0x0
|
|
|
|
/* I: pointer to fdintr_statbuf; O: n/a */
|
|
.global _fdintr_statp
|
|
_fdintr_statp:
|
|
.word _fdintr_statbuf + 0x0
|
|
|
|
/*XXX status of number of interrupts! */
|
|
.global _fdintr_nints
|
|
_fdintr_nints:
|
|
.word 0
|
|
|
|
|
|
/*
|
|
* WARNING, DANGER - if there is ever an interrupt and this address is
|
|
* not * setup (by fdattach) then things may/will hang! (depends on
|
|
* what's at 0)
|
|
*/
|
|
/* pointer to fdc registers, set by fdattach() */
|
|
.global _fdctlr_reg
|
|
_fdctlr_reg:
|
|
.word 0x0
|
|
#ifdef I82077
|
|
.global _fd_msr
|
|
_fd_msr:
|
|
.word 0x0
|
|
.global _fd_dsr
|
|
_fd_dsr:
|
|
.word 0x0
|
|
.global _fd_dor
|
|
_fd_dor:
|
|
.word 0x0
|
|
.global _fd_dir
|
|
_fd_dir:
|
|
.word 0x0
|
|
.global _fd_fifo
|
|
_fd_fifo:
|
|
.word 0x0
|
|
#endif I82077
|
|
|
|
/* I: n/a; O: 10 bytes of status buffer */
|
|
.global _fdintr_statbuf
|
|
_fdintr_statbuf:
|
|
.word 0xAAAAAAAA
|
|
.word 0xBBBBBBBB
|
|
.word 0xCCCCCCCC
|
|
.word 0xDDDDDDDD
|
|
|
|
/*
|
|
* fdc_hardintr - interrupt function to do DMA-like operation for floppy
|
|
* Since it is the only thing on level 11 on a Campus, we can just
|
|
* call this thing directly from trap table.
|
|
* WE RUN WITH TRAPS DISABLED! - BE CAREFUL!
|
|
* I have only the registers %l0-%l7 to work with! (and %l{0-2} are already used)
|
|
*/
|
|
/*
|
|
* CODING CONVENTIONS
|
|
* REGISTER USAGE - is as shown by the following defines
|
|
* INSTRUCTIONS following BRANCH - have a "!!" comment delimiter
|
|
*/
|
|
#define Fdc %l3 /* csr - the pointer to the chip's registers */
|
|
#define Adr %l4 /* addr - the data address pointer */
|
|
#define Len %l5 /* len - length of data */
|
|
#define Ptr %l6 /* misc ptr */
|
|
#define Tmp %l7 /* scratch */
|
|
|
|
.seg "text"
|
|
.global _fdc_hardintr
|
|
_fdc_hardintr:
|
|
#ifdef MEASURE_TIME
|
|
/* START: set the LED */
|
|
sethi %hi(AUXIO_REG), Ptr
|
|
ldub [Ptr + %lo(AUXIO_REG)], Tmp
|
|
or Tmp, AUX_LED|AUX_MBO, Tmp
|
|
stb Tmp, [Ptr + %lo(AUXIO_REG)]
|
|
#endif MEASURE_TIME
|
|
|
|
/*XXX DEBUG ??? */
|
|
sethi %hi(_fdintr_nints), Ptr ! load ptr to count of ints
|
|
ld [Ptr + %lo(_fdintr_nints)], Tmp
|
|
add Tmp, 0x1, Tmp
|
|
st Tmp, [Ptr + %lo(_fdintr_nints)]
|
|
#ifdef I82077
|
|
/*
|
|
* This routine only uses 2 8207[27] registers, the msr and fifo
|
|
* registers. In the two configurations, the addresses of the msr
|
|
* are different, but the fifo always directly follows the msr.
|
|
* For this reason, we stuff the address of msr into Fdc instead
|
|
* of the base address of the chip's registers.
|
|
*/
|
|
sethi %hi(_fd_msr), Ptr ! load ptr to msr into Fdc
|
|
ld [Ptr + %lo(_fd_msr)], Fdc
|
|
#else
|
|
sethi %hi(_fdctlr_reg), Ptr ! load ptr to csr into Fdc
|
|
ld [Ptr + %lo(_fdctlr_reg)], Fdc
|
|
#endif I82077
|
|
|
|
/*
|
|
* switch( opmode ) {
|
|
*/
|
|
sethi %hi(_fdintr_opmode), Ptr
|
|
ld [Ptr + %lo(_fdintr_opmode)], Tmp
|
|
cmp Tmp, 1 !case 1: read/write data-xfr
|
|
be L10
|
|
cmp Tmp, 2 !case 2: seek/recalibrate ?
|
|
be L21
|
|
cmp Tmp, 3 !case 3: results ?
|
|
be L30
|
|
|
|
! default: SPURIOUS INTERRUPT! XXX NYD hope we NEVER GET HERE!
|
|
sethi %hi(_fdintr_spurious), Ptr !! spurious++
|
|
ld [Ptr + %lo(_fdintr_spurious)], Tmp
|
|
add Tmp, 0x1, Tmp
|
|
st Tmp, [Ptr + %lo(_fdintr_spurious)] !!
|
|
! pray and test that the controller is mapped in!
|
|
tst Fdc
|
|
bne L21 ! yes, do a sense interrupt status
|
|
nop
|
|
b L900 ! no, just exit
|
|
nop
|
|
/**********/
|
|
|
|
/*
|
|
* case 1:
|
|
* read/write/format data-xfer case - they have a result phase
|
|
*/
|
|
L10:
|
|
#ifdef NEVER /* no timeout stuff just yet */
|
|
sethi %hi(_fdintr_timeout), %__
|
|
ld [%__ + %lo(_fdintr_timeout)], %__
|
|
st %__, [%fp + -0x4]
|
|
#endif NEVER
|
|
sethi %hi(_fdintr_len), Ptr ! len into Len
|
|
ld [Ptr + %lo(_fdintr_len)], Len
|
|
sethi %hi(_fdintr_addr), Ptr ! addr into Adr
|
|
ld [Ptr + %lo(_fdintr_addr)], Adr
|
|
|
|
/* while the fifo ready bit set, then data/status available */
|
|
/* while( *csr & RQM ) { */
|
|
L11: ldub [Fdc], Tmp ! get msr
|
|
andcc Tmp, RQM, %g0
|
|
be L19 ! branch if bit clear
|
|
|
|
/* if NDM is set, that means data */
|
|
andcc Tmp, NDM, %g0 !! if( *csr & NDM ) {
|
|
be L803 ! if ! set, is status!
|
|
|
|
/* check for input vs. output data */
|
|
andcc Tmp, DIO, %g0 !! _if( *csr & DIO ) {
|
|
be L13
|
|
sub Len, 0x1, Len !! len--
|
|
|
|
ldub [Fdc + 0x1], Tmp ! DIO set, *addr = *fifo
|
|
b L12
|
|
stb Tmp, [Adr] !!
|
|
|
|
L13: ldsb [Adr], Tmp ! _}else{ DIO clear, *fifo = *addr
|
|
stb Tmp, [Fdc + 0x1]
|
|
! _} end if *csr & DIO
|
|
/* addr++; len--; if(len==0) send TC */
|
|
L12: ! moved len-- up beneath test DIO branch
|
|
tst Len ! if( len == 0 ) send TC
|
|
bne L11 ! branch if not, back to while( ..RQM )
|
|
add Adr, 0x1, Adr !! addr++ } /* end of *csr & NDM */
|
|
/* if ( Len == 0 ) goto end_xfr */
|
|
b L801
|
|
nop
|
|
|
|
/* } end while( *csr & RQM ) */
|
|
L19: /* save updated len, addr */
|
|
sethi %hi(_fdintr_len), Ptr
|
|
st Len, [Ptr + %lo(_fdintr_len)]
|
|
sethi %hi(_fdintr_addr), Ptr
|
|
b L900 ! not done yet, return
|
|
st Adr, [Ptr + %lo(_fdintr_addr)] !!
|
|
/**********/
|
|
|
|
/*
|
|
* END OF TRANSFER - if read/write, toggle the TC bit in AUXIO_REG
|
|
* then save status and set state = 3.
|
|
*/
|
|
/* end_xfr: */
|
|
L801: sethi %hi(AUXIO_REG), Ptr
|
|
ldub [Ptr + %lo(AUXIO_REG)], Tmp
|
|
or Tmp, AUX_MBO|AUX_TC, Tmp
|
|
stb Tmp, [Ptr + %lo(AUXIO_REG)]
|
|
/* begin TC delay */
|
|
! five nops provide 100ns of delay at 10MIPS to ensure
|
|
! TC is wide enough at slowest possible floppy clock (500ns @ 250Kbps)
|
|
/*
|
|
* (do something usefull in delay)
|
|
* stash len and addr before they get lost
|
|
*/
|
|
L802: sethi %hi(_fdintr_len), Ptr
|
|
st Len, [Ptr + %lo(_fdintr_len)]
|
|
sethi %hi(_fdintr_addr), Ptr
|
|
st Adr, [Ptr + %lo(_fdintr_addr)]
|
|
nop
|
|
/* end TC delay */
|
|
! now clear the TC bit
|
|
sethi %hi(AUXIO_REG), Ptr
|
|
ldub [Ptr + %lo(AUXIO_REG)], Tmp
|
|
and Tmp, ~AUX_TC, Tmp
|
|
or Tmp, AUX_MBO, Tmp
|
|
stb Tmp, [Ptr + %lo(AUXIO_REG)]
|
|
|
|
/* opmode = 3 to indicate going into status mode */
|
|
mov 0x3, Tmp
|
|
sethi %hi(_fdintr_opmode), Ptr
|
|
b L900
|
|
st Tmp, [Ptr + %lo(_fdintr_opmode)] !!
|
|
/***********/
|
|
|
|
/*
|
|
* error status state: save old pointers, go direct to result snarfing
|
|
*/
|
|
L803: sethi %hi(_fdintr_len), Ptr
|
|
st Len, [Ptr + %lo(_fdintr_len)]
|
|
sethi %hi(_fdintr_addr), Ptr
|
|
st Adr, [Ptr + %lo(_fdintr_addr)]
|
|
mov 0x3, Tmp
|
|
sethi %hi(_fdintr_opmode), Ptr
|
|
b L30
|
|
st Tmp, [Ptr + %lo(_fdintr_opmode)] !!
|
|
/*******************************/
|
|
|
|
/*
|
|
* case 2:
|
|
* recalibrate/seek - no result phase, must do sense interrupt status
|
|
*/
|
|
/* sense_int_stat: */
|
|
L21: ldub [Fdc], Tmp ! Tmp = *csr
|
|
L22: andcc Tmp, CB, Tmp ! is CB set?
|
|
bne L22 ! yes, keep waiting
|
|
ldub [Fdc], Tmp !! Tmp = *csr
|
|
|
|
/* wait!!! should we check rqm first??? ABSOLUTELY YES!!!! */
|
|
L23: ldub [Fdc], Tmp ! busy wait until RQM set
|
|
andcc Tmp, RQM, Tmp
|
|
be L23 ! branch if bit clear
|
|
nop
|
|
mov 0x8, Tmp ! cmd for SENSE_INTERRUPT_STATUS
|
|
stb Tmp, [Fdc + 0x1]
|
|
|
|
/* NOTE: we ignore DIO here, assume it is set before RQM! */
|
|
L24: ldub [Fdc], Tmp ! busy wait until RQM set
|
|
andcc Tmp, RQM, Tmp
|
|
be L24 ! branch if bit clear
|
|
nop
|
|
|
|
! fdintr_statbuf[0] = *fifo;
|
|
ldub [Fdc + 0x1], Tmp
|
|
sethi %hi(_fdintr_statbuf), Ptr
|
|
stb Tmp, [Ptr + %lo(_fdintr_statbuf)]
|
|
|
|
L26: ldub [Fdc], Tmp ! busy wait until RQM set
|
|
andcc Tmp, RQM, Tmp
|
|
be L26 ! branch if bit clear
|
|
nop
|
|
|
|
/* fdintr_statbuf[1] = *fifo; */
|
|
ldub [Fdc + 0x1], Tmp
|
|
sethi %hi(_fdintr_statbuf + 0x1), Ptr
|
|
b L40
|
|
stb Tmp, [Ptr + %lo(_fdintr_statbuf + 0x1)] !!
|
|
|
|
|
|
/*
|
|
* case 3: result mode
|
|
* are in result mode make sure all status bytes are read out
|
|
*/
|
|
L30: sethi %hi(_fdintr_statp), Adr ! &statp in Adr
|
|
ld [Adr + %lo(_fdintr_statp)], Ptr ! statp is Ptr
|
|
sethi %hi(_fdintr_statbuf+10), Len ! Len is limit for status bytes
|
|
or Len, %lo(_fdintr_statbuf+10), Len
|
|
ldub [Fdc], Tmp !
|
|
L34: andcc Tmp, CB, %g0 ! is CB set?
|
|
be L32 ! no, jump around, must be fini
|
|
nop
|
|
! are *both* RQM and DIO set?
|
|
andcc Tmp, RQM, %g0
|
|
be L33
|
|
nop
|
|
andcc Tmp, DIO, %g0
|
|
be L33
|
|
nop
|
|
ldub [Fdc + 0x1], Tmp ! *fifo into Tmp
|
|
stb Tmp, [Ptr]
|
|
! check to see if status buffer ptr is at/past limit
|
|
/*XXX limits? */
|
|
cmp Ptr, Len
|
|
bge L33 ! if past, don't increment!
|
|
nop
|
|
add Ptr, 0x1, Ptr ! statp++ in Ptr
|
|
L33: b L34 ! back to while( *Fdc & CB )
|
|
ldub [Fdc], Tmp !
|
|
|
|
L32: ! stash updated statp and go to notify
|
|
b L40
|
|
st Ptr, [Adr + %lo(_fdintr_statp)] !!
|
|
|
|
|
|
/* notify: */ /* set bit in software interrupt register */
|
|
L40: /* *(uchar *)INTREG_ADDR |= IR_SOFT_INT4 */
|
|
#ifndef sun4m
|
|
sethi %hi(INTREG_ADDR), Ptr
|
|
ldub [Ptr + %lo(INTREG_ADDR)], Tmp
|
|
or Tmp, IR_SOFT_INT4, Tmp
|
|
stb Tmp, [Ptr + %lo(INTREG_ADDR)]
|
|
#else sun4m
|
|
/*
|
|
* set a pending level 4 software interrupt
|
|
* we are probably going to be the master,
|
|
* and if we are not then it will be vectored
|
|
* to the proper master.
|
|
*/
|
|
GETCPU(Tmp) ! get module id, 8..11
|
|
and Tmp, 3, Tmp ! mask to 0..3
|
|
sll Tmp, 12, Tmp ! shift for +0x1000 per mid
|
|
set V_INT_REGS, Ptr
|
|
add Ptr, Tmp, Ptr ! calculate location of OUR int reg
|
|
set IR_SOFT_INT4, Tmp
|
|
st Tmp, [Ptr+8] ! send the softint
|
|
#endif sun4m
|
|
|
|
/* opmode = 4; status = 0; */
|
|
mov 0x4, Tmp
|
|
sethi %hi(_fdintr_opmode), Ptr
|
|
st Tmp, [Ptr + %lo(_fdintr_opmode)]
|
|
mov 0x0, Tmp
|
|
sethi %hi(_fdintr_status), Ptr
|
|
st Tmp, [Ptr + %lo(_fdintr_status)]
|
|
|
|
/* out: */
|
|
L900:
|
|
#ifdef MEASURE_TIME
|
|
/* END: clear the LED */
|
|
sethi %hi(AUXIO_REG), Ptr
|
|
ldub [Ptr + %lo(AUXIO_REG)], Tmp
|
|
and Tmp, ~AUX_LED, Tmp
|
|
or Tmp, AUX_MBO, Tmp
|
|
stb Tmp, [Ptr + %lo(AUXIO_REG)]
|
|
#endif MEASURE_TIME
|
|
mov %l0, %psr ! restore psr - and user's ccodes
|
|
nop
|
|
nop
|
|
jmp %l1 ! return from interrupt
|
|
rett %l2
|
|
.empty
|
|
|
|
.global _fdc_intr
|
|
_fdc_intr:
|
|
save %sp, -96, %sp
|
|
#ifdef MEASURE_TIME
|
|
/* START: set the LED */
|
|
sethi %hi(AUXIO_REG), Ptr
|
|
ldub [Ptr + %lo(AUXIO_REG)], Tmp
|
|
or Tmp, AUX_LED|AUX_MBO, Tmp
|
|
stb Tmp, [Ptr + %lo(AUXIO_REG)]
|
|
#endif MEASURE_TIME
|
|
|
|
! Assume interrupt is from Floppy, unless otherwise overwritten (%i0)
|
|
mov 0x1, %i0
|
|
|
|
/*XXX DEBUG ??? */
|
|
sethi %hi(_fdintr_nints), Ptr ! load ptr to count of ints
|
|
ld [Ptr + %lo(_fdintr_nints)], Tmp
|
|
add Tmp, 0x1, Tmp
|
|
st Tmp, [Ptr + %lo(_fdintr_nints)]
|
|
#ifdef I82077
|
|
/*
|
|
* This routine only uses 2 8207[27] registers, the msr and fifo
|
|
* registers. In the two configurations, the addresses of the msr
|
|
* are different, but the fifo always directly follows the msr.
|
|
* For this reason, we stuff the address of msr into Fdc instead
|
|
* of the base address of the chip's registers.
|
|
*/
|
|
sethi %hi(_fd_msr), Ptr ! load ptr to msr into Fdc
|
|
ld [Ptr + %lo(_fd_msr)], Fdc
|
|
#else
|
|
sethi %hi(_fdctlr_reg), Ptr ! load ptr to csr into Fdc
|
|
ld [Ptr + %lo(_fdctlr_reg)], Fdc
|
|
#endif I82077
|
|
|
|
/*
|
|
* switch( opmode ) {
|
|
*/
|
|
sethi %hi(_fdintr_opmode), Ptr
|
|
ld [Ptr + %lo(_fdintr_opmode)], Tmp
|
|
cmp Tmp, 1 !case 1: read/write data-xfr
|
|
be L100
|
|
cmp Tmp, 2 !case 2: seek/recalibrate ?
|
|
be L210
|
|
cmp Tmp, 3 !case 3: results ?
|
|
be L300
|
|
nop
|
|
|
|
|
|
! do a sense interrupt status
|
|
|
|
tst Fdc
|
|
bne L210
|
|
nop
|
|
|
|
! Return 0, interrupt is not from Floppy.
|
|
b L9000 ! no, just exit with Zero return
|
|
mov 0x0, %i0
|
|
/**********/
|
|
|
|
/*
|
|
* case 1:
|
|
* read/write/format data-xfer case - they have a result phase
|
|
*/
|
|
L100:
|
|
#ifdef NEVER /* no timeout stuff just yet */
|
|
sethi %hi(_fdintr_timeout), %__
|
|
ld [%__ + %lo(_fdintr_timeout)], %__
|
|
st %__, [%fp + -0x4]
|
|
#endif NEVER
|
|
sethi %hi(_fdintr_len), Ptr ! len into Len
|
|
ld [Ptr + %lo(_fdintr_len)], Len
|
|
sethi %hi(_fdintr_addr), Ptr ! addr into Adr
|
|
ld [Ptr + %lo(_fdintr_addr)], Adr
|
|
|
|
/* while the fifo ready bit set, then data/status available */
|
|
/* while( *csr & RQM ) { */
|
|
L110: ldub [Fdc], Tmp ! get msr
|
|
andcc Tmp, RQM, %g0
|
|
be L190 ! branch if bit clear
|
|
|
|
/* if NDM is set, that means data */
|
|
andcc Tmp, NDM, %g0 !! if( *csr & NDM ) {
|
|
be L8030 ! if ! set, is status!
|
|
|
|
/* check for input vs. output data */
|
|
andcc Tmp, DIO, %g0 !! _if( *csr & DIO ) {
|
|
be L130
|
|
sub Len, 0x1, Len !! len--
|
|
|
|
ldub [Fdc + 0x1], Tmp ! DIO set, *addr = *fifo
|
|
b L120
|
|
stb Tmp, [Adr] !!
|
|
|
|
L130: ldsb [Adr], Tmp ! _}else{ DIO clear, *fifo = *addr
|
|
stb Tmp, [Fdc + 0x1]
|
|
! _} end if *csr & DIO
|
|
/* addr++; len--; if(len==0) send TC */
|
|
L120: ! moved len-- up beneath test DIO branch
|
|
tst Len ! if( len == 0 ) send TC
|
|
bne L110 ! branch if not, back to while( ..RQM )
|
|
add Adr, 0x1, Adr !! addr++ } /* end of *csr & NDM */
|
|
/* if ( Len == 0 ) goto end_xfr */
|
|
b L8010
|
|
nop
|
|
|
|
/* } end while( *csr & RQM ) */
|
|
L190: /* save updated len, addr */
|
|
sethi %hi(_fdintr_len), Ptr
|
|
st Len, [Ptr + %lo(_fdintr_len)]
|
|
sethi %hi(_fdintr_addr), Ptr
|
|
b L9000 ! not done yet, return
|
|
st Adr, [Ptr + %lo(_fdintr_addr)] !!
|
|
/**********/
|
|
|
|
/*
|
|
* END OF TRANSFER - if read/write, toggle the TC bit in AUXIO_REG
|
|
* then save status and set state = 3.
|
|
*/
|
|
/* end_xfr: */
|
|
L8010: sethi %hi(AUXIO_REG), Ptr
|
|
ldub [Ptr + %lo(AUXIO_REG)], Tmp
|
|
or Tmp, AUX_MBO|AUX_TC, Tmp
|
|
stb Tmp, [Ptr + %lo(AUXIO_REG)]
|
|
/* begin TC delay */
|
|
! five nops provide 100ns of delay at 10MIPS to ensure
|
|
! TC is wide enough at slowest possible floppy clock (500ns @ 250Kbps)
|
|
/*
|
|
* (do something usefull in delay)
|
|
* stash len and addr before they get lost
|
|
*/
|
|
L8020: sethi %hi(_fdintr_len), Ptr
|
|
st Len, [Ptr + %lo(_fdintr_len)]
|
|
sethi %hi(_fdintr_addr), Ptr
|
|
st Adr, [Ptr + %lo(_fdintr_addr)]
|
|
nop
|
|
/* end TC delay */
|
|
! now clear the TC bit
|
|
sethi %hi(AUXIO_REG), Ptr
|
|
ldub [Ptr + %lo(AUXIO_REG)], Tmp
|
|
and Tmp, ~AUX_TC, Tmp
|
|
or Tmp, AUX_MBO, Tmp
|
|
stb Tmp, [Ptr + %lo(AUXIO_REG)]
|
|
|
|
/* opmode = 3 to indicate going into status mode */
|
|
mov 0x3, Tmp
|
|
sethi %hi(_fdintr_opmode), Ptr
|
|
b L9000
|
|
st Tmp, [Ptr + %lo(_fdintr_opmode)] !!
|
|
/***********/
|
|
|
|
/*
|
|
* error status state: save old pointers, go direct to result snarfing
|
|
*/
|
|
L8030: sethi %hi(_fdintr_len), Ptr
|
|
st Len, [Ptr + %lo(_fdintr_len)]
|
|
sethi %hi(_fdintr_addr), Ptr
|
|
st Adr, [Ptr + %lo(_fdintr_addr)]
|
|
mov 0x3, Tmp
|
|
sethi %hi(_fdintr_opmode), Ptr
|
|
b L300
|
|
st Tmp, [Ptr + %lo(_fdintr_opmode)] !!
|
|
/*******************************/
|
|
|
|
/*
|
|
* case 2:
|
|
* recalibrate/seek - no result phase, must do sense interrupt status
|
|
*/
|
|
/* sense_int_stat: */
|
|
L210: ldub [Fdc], Tmp ! Tmp = *csr
|
|
L220: andcc Tmp, CB, Tmp ! is CB set?
|
|
bne L220 ! yes, keep waiting
|
|
ldub [Fdc], Tmp !! Tmp = *csr
|
|
|
|
/* wait!!! should we check rqm first??? ABSOLUTELY YES!!!! */
|
|
L230: ldub [Fdc], Tmp ! busy wait until RQM set
|
|
andcc Tmp, RQM, Tmp
|
|
be L230 ! branch if bit clear
|
|
nop
|
|
mov 0x8, Tmp ! cmd for SENSE_INTERRUPT_STATUS
|
|
stb Tmp, [Fdc + 0x1]
|
|
|
|
/* NOTE: we ignore DIO here, assume it is set before RQM! */
|
|
L240: ldub [Fdc], Tmp ! busy wait until RQM set
|
|
andcc Tmp, RQM, Tmp
|
|
be L240 ! branch if bit clear
|
|
nop
|
|
|
|
! fdintr_statbuf[0] = *fifo;
|
|
ldub [Fdc + 0x1], Tmp
|
|
sethi %hi(_fdintr_statbuf), Ptr
|
|
stb Tmp, [Ptr + %lo(_fdintr_statbuf)]
|
|
|
|
L260: ldub [Fdc], Tmp ! busy wait until RQM set
|
|
andcc Tmp, RQM, Tmp
|
|
be L260 ! branch if bit clear
|
|
nop
|
|
|
|
/* fdintr_statbuf[1] = *fifo; */
|
|
ldub [Fdc + 0x1], Tmp
|
|
sethi %hi(_fdintr_statbuf + 0x1), Ptr
|
|
b L400
|
|
stb Tmp, [Ptr + %lo(_fdintr_statbuf + 0x1)] !!
|
|
|
|
|
|
/*
|
|
* case 3: result mode
|
|
* are in result mode make sure all status bytes are read out
|
|
*/
|
|
L300: sethi %hi(_fdintr_statp), Adr ! &statp in Adr
|
|
ld [Adr + %lo(_fdintr_statp)], Ptr ! statp is Ptr
|
|
sethi %hi(_fdintr_statbuf+10), Len ! Len is limit for status bytes
|
|
or Len, %lo(_fdintr_statbuf+10), Len
|
|
ldub [Fdc], Tmp !
|
|
L340: andcc Tmp, CB, %g0 ! is CB set?
|
|
be L320 ! no, jump around, must be fini
|
|
nop
|
|
! are *both* RQM and DIO set?
|
|
andcc Tmp, RQM, %g0
|
|
be L330
|
|
nop
|
|
andcc Tmp, DIO, %g0
|
|
be L330
|
|
nop
|
|
ldub [Fdc + 0x1], Tmp ! *fifo into Tmp
|
|
stb Tmp, [Ptr]
|
|
! check to see if status buffer ptr is at/past limit
|
|
/*XXX limits? */
|
|
cmp Ptr, Len
|
|
bge L330 ! if past, don't increment!
|
|
nop
|
|
add Ptr, 0x1, Ptr ! statp++ in Ptr
|
|
L330: b L340 ! back to while( *Fdc & CB )
|
|
ldub [Fdc], Tmp !
|
|
|
|
L320: ! stash updated statp and go to notify
|
|
b L400
|
|
st Ptr, [Adr + %lo(_fdintr_statp)] !!
|
|
|
|
|
|
/* notify: */ /* set bit in software interrupt register */
|
|
L400: /* *(uchar *)INTREG_ADDR |= IR_SOFT_INT4 */
|
|
#ifndef sun4m
|
|
sethi %hi(INTREG_ADDR), Ptr
|
|
ldub [Ptr + %lo(INTREG_ADDR)], Tmp
|
|
or Tmp, IR_SOFT_INT4, Tmp
|
|
stb Tmp, [Ptr + %lo(INTREG_ADDR)]
|
|
#else sun4m
|
|
/*
|
|
* set a pending level 4 software interrupt
|
|
* we are probably going to be the master,
|
|
* and if we are not then it will be vectored
|
|
* to the proper master.
|
|
*/
|
|
GETCPU(Tmp) ! get module id, 8..11
|
|
and Tmp, 3, Tmp ! mask to 0..3
|
|
sll Tmp, 12, Tmp ! shift for +0x1000 per mid
|
|
set V_INT_REGS, Ptr
|
|
add Ptr, Tmp, Ptr ! calculate location of OUR int reg
|
|
set IR_SOFT_INT4, Tmp
|
|
st Tmp, [Ptr+8] ! send the softint
|
|
#endif sun4m
|
|
|
|
/* opmode = 4; status = 0; */
|
|
mov 0x4, Tmp
|
|
sethi %hi(_fdintr_opmode), Ptr
|
|
st Tmp, [Ptr + %lo(_fdintr_opmode)]
|
|
mov 0x0, Tmp
|
|
sethi %hi(_fdintr_status), Ptr
|
|
st Tmp, [Ptr + %lo(_fdintr_status)]
|
|
|
|
/* out: */
|
|
L9000:
|
|
#ifdef MEASURE_TIME
|
|
/* END: clear the LED */
|
|
sethi %hi(AUXIO_REG), Ptr
|
|
ldub [Ptr + %lo(AUXIO_REG)], Tmp
|
|
and Tmp, ~AUX_LED, Tmp
|
|
or Tmp, AUX_MBO, Tmp
|
|
stb Tmp, [Ptr + %lo(AUXIO_REG)]
|
|
#endif MEASURE_TIME
|
|
ret
|
|
restore
|
|
.empty
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef I82077
|
|
/*
|
|
* Turn on or off bits in the digital output register.
|
|
*
|
|
* fd_set_dor(bit, flag)
|
|
* int bit; bit mask in the dor
|
|
* int flag; 0 = off, otherwise on
|
|
*/
|
|
ENTRY(fd_set_dor)
|
|
|
|
tst %o1
|
|
set _fd_dor, %o2 ! get address of address of dor
|
|
ld [%o2], %o2 ! get address of dor
|
|
ldub [%o2], %g1 ! read dor
|
|
bnz,a 1f
|
|
bset %o0, %g1 ! on
|
|
bclr %o0, %g1 ! off
|
|
1:
|
|
stb %g1, [%o2] ! write dor
|
|
|
|
retl
|
|
nop
|
|
#endif I82077
|