! Ambit disc controller module, speaking to RWT-style interface. ! Include before disc independent part. %externalstring(47) copyright ambit %alias "GDMR_(C)_DISC.AMBIT" = %c "Copyright (C) 1987, 1988 George D.M. Ross" ! Because of the utterly crazy way the hardware operates, an interrupt-driven ! controller cannot be made to work in any sensible manner whatsoever: we get ! interrupts to tell us that things which happen more-or-less instantaneously ! have happened, but we have to busy-wait for things which do take a while, ! such as seeks and disc transfers! Interrupt mode is therefore considered ! useless with this hardware, which leaves us a choice of ways to poll the ! device: straightforward polling in the command routine interacts non-optimally ! with a reasonable choice of disc process priority; busy-waiting in a lower ! priority process could be most responsive, but would soak up valuable CPU ! time and bus bandwidth; while polling every clock tick would use least other ! resources, but could add noticeable latency. This version of the driver ! uses option 2, as the clock ticks too slowly for option 3. ! Disc controller definitions %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 %constinteger sector 0 mask = sectors 0 - 1 %constinteger sector 1 mask = sectors 1 - 1 %constinteger unit shift = 28 %constinteger block mask = \(16_FFFFFFFF << unit shift) @16_ED0000 %readonly %volatile %byte disc r; ! Read data @16_ED0000 %writeonly %byte disc w; ! Write data !16_ED0001 %writeonly %byte disc a; ! Arm interrupts !16_ED0001 %readonly %volatile %byte disc d; ! Disarm interrupts %constinteger dc initialise = 16_02 %constinteger dc initialise 1 = 16_07 %constinteger dc read disc = 16_21 %constinteger dc write disc = 16_22 %constinteger dc drive status = 16_25 %constinteger dc seek = 16_28 %constinteger dc read buffer = 16_41 %constinteger dc write buffer = 16_42 %recordformat disc address fm(%byte unit, head, %byte track l, track h, %byte sector, %byte z1, z2, z3) %ownbyte null byte = 0 %ownbyte disc status = 0 %constinteger fatal = 16_40 %owninteger soft errors = 0 %owninteger hard errors = 0 ! Disc poller, runs at a lower priority than the disc process proper. %constinteger poller priority = 2 %constinteger poller size = 4096 %ownrecord(semaphore fm) poll wait = 0 %ownrecord(semaphore fm) poll request = 0 %routine poller %owninteger x = 0 %cycle !L! lights and A(\lights wait) semaphore wait(poll request) !L! lights or A(lights wait) %cycle !! put sym('p') D0 = 31 L: *move.l x, D1 *move.l x, D1 *dbra D0, L %repeat %until disc r = 16_FF signal semaphore(poll wait) %repeat %end ! Disc handler proper starts here.... %routine analyse error(%integer command, %record(disc address fm)%name a) printstring("Disc error: status "); phex2(disc status) printstring(", command "); phex2(command) %if a ## nil %start printstring(", address ") write(a_unit, 0); printstring("u ") phex2(a_track h); phex2(a_track l) printstring("t ") phex2(a_head); printstring("h ") phex2(a_sector); print symbol('s') %finish %if disc status & fatal = 0 %start soft errors = soft errors + 1 printstring(" (soft)") %else hard errors = hard errors + 1 printstring(" (*HARD*)") %finish newline %end %routine command(%integer op, bytes, %bytename buffer, %integer fill) ! bytes + -> write to controller ! bytes 0 -> no parameter part ! bytes - -> read from controller ! intwait # 0 iff interrupts are to be used %integer i, junk !! put sym('<'); put long(op); put sym('>') !! put sym('1') %while disc r # 16_A0 %cycle !! put sym('.') %repeat !! put sym('2') disc w = op !! put sym('3') %while disc r # 16_A1 %cycle !! put sym('.') %repeat !! put sym('4') disc w = 16_FF !! put sym('5') %for i = 1, 1, 20 %cycle; %repeat; ! Placate disc controller !! put sym('6') %if bytes > 0 %start ! Write to controller D0 = bytes - 1 A0 = addr(buffer) wl:*move.b (A0)+, disc w *dbra D0, wl %if fill > 0 %start D0 = fill - 1 wf:*move.b #0, disc w *dbra D0, wf %finish %else %if bytes < 0 D0 = -bytes - 1 A0 = addr(buffer) rl:*move.b disc r, (A0)+ *dbra D0, rl %if fill > 0 %start D0 = fill - 1 rf:*move.b disc r, D1 *dbra D0, rf %finish %finish !! put sym('7') disc w = 16_FF !! put sym('8') %for i = 1, 1, 20 %cycle; %repeat; ! Placate disc controller %if op & 16_20 = 0 %start ! Controller-only operation. Poll for completion here %cycle !! put sym('9') %repeat %until disc r = 16_FF %else ! Operation involves a disc transfer. Rather than polling ! here we let the clock tick handler do it for us. !! put sym('P') signal semaphore(poll request) semaphore wait(poll wait) %finish !! put sym('A') disc w = 16_FE !! put sym('B') %while disc r & 16_80 # 0 %cycle !! put sym('.') %repeat !! put sym('C') disc status = disc r !! put sym('D') disc w = 0 !! put sym('#') %end %routine initialise disc %ownbytearray b0(0 : 15) = 255, 0, 0, { Pre-compensation } 0, 0, 0, { RWC } 4, { Buffered step } (cylinders 0 - 1) & 255, (cylinders 0 - 1) >> 8, { Max track } 0, 0, { Last user track (disabled) } 0, { Bad track mapping } 0, { Removable drive } 0, 0, 0 { Reserved, mbz } %ownbytearray b1(0 : 15) = 255, 0, 0, { Pre-compensation } 0, 0, 0, { RWC } 4, { Buffered step } (cylinders 1 - 1) & 255, (cylinders 1 - 1) >> 8, { Max track } 0, 0, { Last user track (disabled) } 0, { Bad track mapping } 0, { Removable drive } 0, 0, 0 { Reserved, mbz } %ownrecord(disc address fm) da = 0 %record(process fm)%name p %integer i %label poller process !! printstring("Initialise disc: ") !! write(sectors0, 0); printstring("s ") !! write(heads0, 0); printstring("h ") !! write(cylinders0, 0); print symbol('c') !! newline setup semaphore(poll wait) setup semaphore(poll request) p == create process(poller size, addr(poller process), poller priority, nil) disc w = 0; ! Initialise the protocol !! printstring("Disc protocol initialised"); newline ! Set up drive 0 command(dc write buffer, 16, b0(0), 512 - 16) %if disc status # 0 %start printstring("Disc init 0: write buffer status ") phex2(disc status); newline %stop %finish !! printstring("Init buffer written"); newline command(dc initialise, 0, null byte, 0) %if disc status # 0 %start printstring("Disc init 0: init status "); phex2(disc status) newline %stop %finish {} printstring("Initialised 0, seeking...."); newline command(dc seek, 8, da_unit, 0) {} printstring("0 seek status "); phex2(disc status); newline drive 0 size = heads 0 * sectors 0 * cylinders 0 %if disc status = 0 ! Now for drive 1 {} printstring("Write drive 1 parameters"); newline %if heads 1 # 0 %start ! May be configured, so probe command(dc write buffer, 16, b1(0), 512 - 16) %if disc status # 0 %start printstring("Disc init 1: write buffer status ") phex2(disc status); newline %stop %finish {} printstring("Initialise 1"); newline command(dc initialise 1, 0, null byte, 0) %if disc status # 0 %start printstring("Disc init 1: init status "); phex2(disc status) newline %stop %finish da_unit = 1 {} printstring("Seek 1"); newline command(dc seek, 8, da_unit, 0) {} printstring("1 seek status "); phex2(disc status); newline drive 1 size = heads 1 * sectors 1 * cylinders 1 %if disc status = 0 %finish %return poller process: poller %end %routine split address(%integer block, unit, %record(disc address fm)%name address) %integer c, h, s address_unit = unit %if unit = 0 %start h = block // sectors 0; s = block - h * sectors 0 c = h // heads 0; h = h - c * heads 0 h = head table0(h) %else {%if unit = 1 h = block // sectors 1; s = block - h * sectors 1 c = h // heads 1; h = h - c * heads 1 h = head table1(h) %finish !! printstring("Split address "); write(block, 0) !! printstring(" -> ") !! write(unit, 0); printstring("u ") !! write(c, 0); printstring("c ") !! write(h, 0); printstring("h ") !! write(s, 0); print symbol('s'); newline address_head = h address_sector = s address_track l = c & 255; address_track h = c >> 8 address_z1 = 0; address_z2 = 0; address_z3 = 0 %end %integerfn do disc read(%integer block, n, %bytename buffer) %record(disc address fm) a %integer unit, sector mask %result = -2 %if n <= 0 unit = block >> unit shift; block = block & block mask !! printstring("Unit "); write(unit, 0) !! printstring(" block "); write(block, 0); newline %if unit = 0 %start %result = -2 %unless 0 <= block %and block + n <= drive 0 size sector mask = sector 0 mask %else {%if unit = 1 %result = -2 %unless 0 <= block %and block + n <= drive 1 size sector mask = sector 1 mask %finish disc reads = disc reads + 1 %cycle split address(block, unit, a) %cycle command(dc read disc, 8, a_unit, 0) analyse error(dc read disc, a) %if disc status # 0 %if disc status & fatal = 0 %start command(dc read buffer, -512, buffer, 0) analyse error(dc read buffer, a) %if disc status # 0 %finish %result = disc status ! 16_80000000 %if disc status & fatal # 0 n = n - 1; %result = 0 %if n <= 0 block = block + 1 buffer == buffer [512] a_sector = (a_sector + 1) & sector mask %repeat %until a_sector = 0 %repeat %end %integerfn do disc write(%integer block, n, %bytename buffer) %record(disc address fm) a %integer unit, sector mask %result = -2 %if n <= 0 unit = block >> unit shift; block = block & block mask %if unit = 0 %start %result = -2 %unless 0 <= block %and block + n <= drive 0 size sector mask = sector 0 mask %else {%if unit = 1 %result = -2 %unless 0 <= block %and block + n <= drive 1 size sector mask = sector 1 mask %finish !! printstring("Disc write to "); write(block, 0); newline disc writes = disc writes + 1 %cycle split address(block, unit, a) %cycle command(dc write buffer, 512, buffer, 0) analyse error(dc write buffer, a) %if disc status # 0 %if disc status & fatal = 0 %start command(dc write disc, 8, a_unit, 0) analyse error(dc write disc, a) %if disc status # 0 %finish %result = disc status ! 16_80000000 %if disc status & fatal # 0 n = n - 1; %result = 0 %if n <= 0 block = block + 1 buffer == buffer [512] a_sector = (a_sector + 1) & sector mask %repeat %until a_sector = 0 %repeat %end %end %of %file