! Mouse/filestore disc process for Fuji 2243 80 Mb Winchester with Ambit
! controller and RWT interface.  New Mouse version.

! Requests are sent via the standard send/receive message operations.
! P1: request code -- 0 = get disc status, 1 = read, 2 = write
! P2: block number
! P3: buffer address
! P4: number of blocks
!     (0 <= P2 < (P2 + P4) <= disc size)

%externalstring(47) copyright %alias "GDMR_(C)_2243" = %c
   "Copyright (C) 1987 George D.M. Ross"

%option "-low-nonstandard-nocheck-nodiag-noline"

%include "Moose:Mouse.Inc"

%conststring(31) request mailbox name = "FUJI2243_REQUESTS"

%systemroutinespec phex2(%integer what)
%systemroutinespec phex(%integer what)

%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

%constinteger heads      =  11
%constinteger sectors    =  16, sector mask = sectors - 1
%constinteger cylinders  = 754
%constinteger size       = heads * sectors * cylinders

%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 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

%routine analyse error(%integer command, %record(disc address fm)%name a)
   printstring("Disc error ");  phex2(disc status)
   space;  phex2(command)
   %if a ## nil %start
      space
      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, intwait)
   ! bytes +  -> write to controller
   ! bytes 0  -> no parameter part
   ! bytes -  -> read from controller
   ! intwait # 0 iff interrupts are to be used
   %integer i, junk
      i = disc r %until i = 16_A0
      disc w = op
      i = disc r %until i = 16_A1
      disc w = 16_FF
      %for i = 1, 1, 50 %cycle; %repeat;  ! Placate disc controller
      %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 w, D1
            *dbra D0, rf
         %finish
      %finish
      disc w = 16_FF
      %for i = 1, 1, 50 %cycle;  %repeat;  ! Placate disc controller
      %if intwait = 0 %start
         i = disc r %until i = 16_FF
      %else
         %cycle
            disc a = 255;  ! Arm interrupts
            !! put sym('W');  !put sym(13)
            semaphore wait(disc interrupt)
            !! put sym('C');  !put sym(13)
            i = disc r
         %repeat %until i = 16_FF
      %finish
      disc w = 16_FE
      i = disc r %until i & 16_80 = 0
      disc status = disc r
      disc w = 0
%end

%routine initialise disc
   %ownbytearray b(0 : 15) =
         255, 0, 0,        { Pre-compensation }
         0, 0, 0,          { RWC }
         4,                { Buffered step }
         (cylinders - 1) & 255,
         (cylinders - 1) >> 8,
                           { Max track }
         0, 0,             { Last user track (disabled) }
         0,                { Bad track mapping }
         0,                { Removable drive }
         0, 0, 0           { Reserved, mbz }
   %ownrecord(interrupt handler fm) handler = 0
   %integer i
   %label h
      !! printstring("Initialise disc: ")
      !! write(sectors, 0);  printstring("s ")
      !! write(heads, 0);  printstring("h ")
      !! write(cylinders, 0);  print symbol('c')
      !! newline
      setup semaphore(disc interrupt)
      setup interrupt handler(handler, addr(h))
      add interrupt handler(handler, 3)
      disc w = 0   
      command(dc write buffer, 16, b(0), 512 - 16, 0)
      %if disc status # 0 %start
         printstring("Disc init: write buffer status ")
         phex2(disc status);  newline
         %stop
      %finish
      command(dc initialise, 0, null byte, 0, 1)
      %if disc status # 0 %start
         printstring("Disc init: init status ");  phex2(disc status)
         newline
         %stop
      %finish
      %return

      ! Interrupt handler -- merely reawaken the disc process
   h: *move.b disc d, D0;  ! Disarm the interface
      !! put sym('I');  !put sym(13)
      int signal semaphore(disc interrupt)
      !! put sym('R');  !put sym(13)
      return from interrupt
      *move.l D0, D0;  ! In case of "optimisations"
%end

%routine split address(%integer block, %record(disc address fm)%name address)
   %integer c, h, s
      address_unit = block >> unit shift;  block = block & block mask
      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
      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
      %result = -2 %if n <= 0
      %result = -2 %unless 0 <= block %and block + n <= size
      disc reads = disc reads + 1
      %cycle
         split address(block, a)
         %cycle
            command(dc read disc, 8, a_unit, 0, 1)
            %if disc status = 0 %start
               command(dc read buffer, -512, buffer, 0, 0)
               analyse error(dc read buffer, a) %if disc status # 0
            %else
               analyse error(dc read disc, a)
            %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
      %result = -2 %if n <= 0
      %result = -2 %unless 0 <= block %and block + n <= size
      !! printstring("Disc write to ");  write(block, 0);  newline
      disc writes = disc writes + 1
      %cycle
         split address(block, a)
         %cycle
            command(dc write buffer, 512, buffer, 0, 0)
            %if disc status = 0 %start
               command(dc write disc, 8, a_unit, 0, 1)
               analyse error(dc write disc, a) %if disc status # 0
            %else
               analyse error(dc write buffer, a)
            %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


! The part above is drive-dependent.  The part below is drive-independent.

%include "GDMR_H:Disc.Indep"


! Start it off as an independent process

%begin
   start disc
%end %of %program
