! SMD disc controller module, speaking to RWT-style/NEC interface. ! Included before disc independent part. %externalstring(47) copyright smd %alias "GDMR_(C)_DISC.SMD" = %c "Copyright (C) 1987 George D.M. Ross" %constinteger size = heads * sectors * cylinders %constinteger read bit = 16_01 %constinteger write bit = 16_02 %constinteger intwait bit = 16_04 %constinteger int bit = 16_08 %constinteger doing bit = 16_10 %constinteger adding bit = 16_20 %owninteger disc reads = 0 %owninteger disc writes = 0 %owninteger disc HW = 0 %ownrecord(semaphore fm) disc interrupt = 0 %ownrecord(semaphore fm) disc initialised = 0 %ownrecord(semaphore fm) seek complete = 0 %constinteger unit shift = 28 %constinteger block mask = \(16_FFFFFFFF << unit shift) %owninteger current cylinder = -1 ! Board control %constinteger bc reset = -16_8000 %constinteger bc int enable = 16_4000 %constinteger bc dma out = 16_2000 %constinteger bc dma in = 16_1000 %constinteger bc idle = 0 @16_71002 %writeonly %short bcr ! Controller commands %constinteger dc sense int status = 16_10 %constinteger dc specify = 16_20 %constinteger dc sense unit status = 16_30 %constinteger dc detect error = 16_40 %constinteger dc recalibrate = 16_50 %constinteger dc seek = 16_60 !constinteger dc write ID = 16_70 !constinteger dc verify ID = 16_80 !constinteger dc read ID = 16_90 !constinteger dc read diagnostic = 16_A0 %constinteger dc read data = 16_B0 !constinteger dc check = 16_C0 !constinteger dc scan = 16_D0 %constinteger dc verify data = 16_E0 %constinteger dc write data = 16_F0 %constinteger dc clce = 16_08 %constinteger dc hsrq = 16_04 %constinteger dc clb = 16_02 %constinteger dc rst = 16_01 @16_71001 %writeonly %byte dcr ! Controller status %constinteger ds busy = 16_80 %constinteger ds command end = 16_60 %constinteger ds command end abnormal = 16_20 %constinteger ds command end normal = 16_40 %constinteger ds command end invalid = 16_60 %constinteger ds sense request = 16_10 %constinteger ds reset request = 16_08 %constinteger ds ID error = 16_04 %constinteger ds not coincident = 16_02 %constinteger ds data request = 16_01 %constinteger busy bit = 7 %constinteger CEH bit = 6 %constinteger CEL bit = 5 %constinteger SRQ bit = 4 @16_71001 %readonly %volatile %byte dsr ! Disc data register. Note that we split this into the read ! half and the write half in order to prevent "optimisations" ! such as clr.b being used. @16_71000 %readonly %volatile %byte rdr @16_71000 %writeonly %byte wdr ! Interrupt status bits %constinteger is seek error = 16_20 %constinteger seek done bit = 7 ! Error status bits %constinteger es enc = 16_80 %constinteger es ovr = 16_40 %constinteger es der = 16_20 %constinteger es eqc = 16_10 %constinteger es nr = 16_08 %constinteger es nd = 16_04 %constinteger es nwr = 16_02 %constinteger es mdm = 16_01 ! Resultant status bytes %ownbyte controller status = 0 %ownbyte interrupt status = 0 ! Disc buffer %constinteger disc buffer address = 16_70000 ! Parameter & result records %recordformat parameter fm((%byte pcnh, pcnl) %c %or (%byte phn, flag, lcnh, lcnl, lhn, lsn, scnt)) %recordformat result fm((%byte est, phn, flag, lcnh, lcnl, lhn, lsn, scnt) %c %or %byte ust) %ownrecord(result fm) results = 0 %owninteger expecting = -1 ! Error codes %constinteger transfer size error = -11 %constinteger off cylinder end error = -12 %constinteger seek failed error = -13 %constinteger read failed error = -14 %constinteger write failed error = -15 %constinteger verify failed error = -16 ! Interrupt handler %constinteger interrupts off = 16_0400 %routine initialise interrupt handler %ownrecord(interrupt handler fm) handler = 0 %ownbyte sensing = 0 %label h, CE, rl, no r, sense done, seek done, SRQ, do SRQ ! Semaphores set up in 'initialise disc' setup interrupt handler(handler, addr(h)) add interrupt handler(handler, 4) %return ! Interrupt handler h: ! Get the controller status *move.b dsr, D0 *btst.b #CEH bit, D0 *bne CE *btst.b #CEL bit, D0 *bne CE *btst.b #SRQ bit, D0 *bne SRQ ! Else not us, so.... return from interrupt CE: ! Command ended. ! Was it a sense int status? *move.b sensing, D1 *bne sense done ! Stop any DMA, save the status. *move.w #bc int enable, bcr *move.b D0, controller status ! Save the results, if any expected. *move.l expecting, D1 *blt no r *lea results, A0 rl: *move.b rdr, (A0)+ *dbra D1, rl *move.l D1, expecting no r: ! Signal done int signal semaphore(disc interrupt) ! Sense int status needed? !? *move.b controller status, D0 *btst.b #SRQ bit, D0 *bne do SRQ *move.b #dc clce, dcr return from interrupt sense done: ! Sense interrupt has finished. Read the result *move.b rdr, D1 *move.b D1, interrupt status *clr.b sensing *move.b #dc clce, dcr ! Check if it's a seek completed *btst.b #seek done bit, D1 *bne seek done return from interrupt seek done: ! A seek operation has terminated. Signal the ! semaphore, then return. int signal semaphore(seek complete) return from interrupt SRQ: ! Request to sense interrupt status. Hush it if we're ! busy -- it'll come back again. *btst.b #busy bit, D0 *beq do SRQ ! We're busy, so hush it. *move.b #dc hsrq, dcr return from interrupt do SRQ: ! Controller is idle now, so issue the request *move.b #dc sense int status, dcr *move.b #1, sensing return from interrupt ! HMD-trap *move.l D0, D0 %end ! Command execution %predicate command(%integer op, %record(parameter fm)%name parameters, %integer n parameters, %integer n results, board control) %integer old SR, status, i %label pl ! Interrupts off for most of this, to lock out the SRQ section ! of the interrupt handler.... ! First wait for the controller to go idle %cycle old SR = or to SR(interrupts off) status = dsr %exit %if status & ds busy = 0 ! Still busy, ints on again and wait a bit old SR = set SR(old SR) %for i = 1, 1, 1000 %cycle; %repeat %repeat ! The controller is idle and interrupts are off. Set up ! the parameters %if n parameters > 0 %start A0 = addr(parameters) D0 = n parameters - 1 pl: *move.b (A0)+, wdr *dbra D0, pl %finish ! Note how many results we're expecting expecting = n results - 1 ! Now issue the command, re-enable interrupts, and wait... dcr = op bcr = board control old SR = set SR(old SR) semaphore wait(disc interrupt) !! printstring("Done "); phex2(op) !! printstring(", status "); phex2(controller status) !! newline %if controller status & ds command end = ds command end normal %start %true %else %if controller status & (ds reset request ! %c ds ID error ! %c ds not coincident) # 0 %start printstring("SMD controller: ") printstring(" reset request") %if controller status & ds reset request # 0 printstring(" ID error") %if controller status & ds ID error # 0 printstring(" not coincident") %if controller status & ds not coincident # 0 newline %finish %false %finish %end ! Reset %routine reset everything %integer i current cylinder = -1; ! Force a seek dcr = dc rst %for i = 1, 1, 10 000 %cycle; %repeat %if command(dc recalibrate, nil, 0, 0, bc int enable) %start semaphore wait(seek complete) %else printstring("Recalibrate failed"); newline %finish %end ! Initialisation %routine initialise controller %integer i bcr = bc reset %for i = 1, 1, 10 000 %cycle; %repeat bcr = bc int enable %for i = 1, 1, 10 000 %cycle; %repeat wdr = 16_40 wdr = 16_82 wdr = 0 wdr = heads - 1 wdr = sectors - 1 wdr = 16 wdr = 10 dcr = dc specify semaphore wait(disc interrupt) !! printstring("Specify: "); phex2(controller status); newline %for i = 1, 1, 300 000 %cycle; %repeat; ! Wait for drives to wake... %end ! Disc status enquiry %routine disc enquiry %if command(dc sense unit status, nil, 0, 1, bc int enable) %start printstring("SMD drive: ") write(sectors, 0); printstring("s ") write(heads, 0); printstring("h ") write(cylinders, 0); print symbol('c') printstring(" selected") %if results_ust & 16_80 # 0 printstring(" sk-end") %if results_ust & 16_40 # 0 printstring(" protected") %if results_ust & 16_20 # 0 printstring(" (am)") %if results_ust & 16_10 # 0 printstring(" ready") %if results_ust & 16_08 # 0 printstring(" on-cyl") %if results_ust & 16_04 # 0 printstring(" sk-err") %if results_ust & 16_02 # 0 printstring(" fault") %if results_ust & 16_01 # 0 !! space; phex2(results_ust) newline %else reset everything %if controller status & ds reset request # 0 %finish %end ! Address resolution %routine split address(%integer block, %integername cylinder, %record(parameter fm)%name p) %integer c, h, s h = block // sectors; s = block - h * sectors c = h // heads; h = h - c * heads !! printstring("Split address "); write(block, 0) !! printstring(" -> ") !! write(c, 0); printstring("c ") !! write(h, 0); printstring("h ") !! write(s, 0); print symbol('s'); newline cylinder = c p_phn = h; p_lhn = h p_lcnh = cylinder >> 8 p_lcnl = cylinder & 255 p_lsn = s p_flag = 0 %end ! Address reporting %routine show address(%record(result fm)%name r) write(r_lcnh << 8 + r_lcnl, 0); printstring("c ") write(r_lhn, 0); printstring("h ") write(r_lsn, 0); print symbol('s') %end ! Disc transfers %routine show error(%string(31) doing) printstring("SMD: "); printstring(doing) printstring(": "); show address(results) printstring(" ENC") %if results_est & es enc # 0 printstring(" OVR") %if results_est & es ovr # 0 printstring(" DER") %if results_est & es der # 0 printstring(" EQC") %if results_est & es eqc # 0 printstring(" NR") %if results_est & es nr # 0 printstring(" ND") %if results_est & es nd # 0 printstring(" NWR") %if results_est & es nwr # 0 printstring(" MDM") %if results_est & es mdm # 0 newline reset everything %if controller status & ds reset request # 0 %end %predicate do seek(%integer cylinder) %record(parameter fm) seek p current cylinder = -1 seek p_pcnh = cylinder >> 8 seek p_pcnl = cylinder & 255 %unless command(dc seek, seek p, 2, 0, bc int enable) %start show error("initiate seek") %false %finish !L! lights or B(lights seek) semaphore wait(seek complete) !L! lights and B(\ lights seek) !! printstring("Seek done status: ") !! phex2(interrupt status); newline %if interrupt status & is seek error # 0 %start show error("seek") %false %finish current cylinder = cylinder %true %end %predicate do verify(%record(parameter fm)%name p) %true %if command(dc verify data, p, 7, 8, bc int enable ! bc dma out) show error("verify") %false %end %integerfn do disc read(%integer block, n, %bytename buffer) %record(parameter fm) seek p, transfer p %integer cylinder %result = transfer size error %unless 0 < n <= 8 split address(block, cylinder, transfer p) ! Check we won't be crossing heads %result = off cylinder end error %if transfer p_lsn + n > sectors disc reads = disc reads + 1 %result = seek failed error %unless cylinder = current cylinder %or do seek(cylinder) ! Do the transfer transfer p_scnt = n %unless command(dc read data, transfer p, 7, 8, bc int enable ! bc dma in) %start show error("read transfer") %result = read failed error %finish ! Verify, if required %if read verify # 0 %start %result = verify failed error %unless do verify(transfer p) %finish ! Copy the resulting data D0 = n << 8 - 1 A0 = addr(buffer) A1 = disc buffer address cl: *move.w (A1)+, (A0)+ *dbra D0, cl %result = 0 %end %integerfn do disc write(%integer block, n, %bytename buffer) %record(parameter fm) seek p, transfer p %integer cylinder %result = transfer size error %unless 0 < n <= 8 split address(block, cylinder, transfer p) ! Check we won't be crossing heads %result = off cylinder end error %if transfer p_lsn + n > sectors disc writes = disc writes + 1 %result = seek failed error %unless cylinder = current cylinder %or do seek(cylinder) ! Copy in the data D0 = n << 8 - 1 A0 = addr(buffer) A1 = disc buffer address cl: *move.w (A0)+, (A1)+ *dbra D0, cl ! Do the transfer transfer p_scnt = n %unless command(dc write data, transfer p, 7, 8, bc int enable ! bc dma out) %start show error("write transfer") %result = write failed error %finish ! Verify, if required %if write verify # 0 %start %result = verify failed error %unless do verify(transfer p) %finish %result = 0 %end %routine initialise disc setup semaphore(disc interrupt) setup semaphore(disc initialised) setup semaphore(seek complete) initialise interrupt handler initialise controller disc enquiry %end %end %of %file