! Mouse disc process for old eightinchester portable filestore
!
! Requests are sent via the standard send/receive message operations.
! P1: request code -- 0 = get disc status, 1 = read, 2 = write
! P2: block number (0 <= P2 < disc size)
! P3: buffer address

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

%include "MOUSE:MOUSE.INC"

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

%externalroutinespec FS insert(%string(31) name, %integer value)
%externalroutinespec FS lookup(%string(31) name, %integername value)

%ownrecord(queue fm) disc queue = 0
%ownrecord(semaphore fm) disc sema = 0
%ownrecord(mailbox fm) disc mailbox = 0

%owninteger disc reads  = 0
%owninteger disc writes = 0
%owninteger disc HW     = 0

%ownrecord(semaphore fm) disc interrupt = 0
%Ownrecord(interrupthandlerfm)   ih
%Owninteger waiting = 0
%owninteger direction = 0

{ disk characteristics }
%constant %integer restore step rate   =     3,       { 1.5ms }
                   seek step rate      =     0,       { 10us }
                   sectors per block   = 2,
                   blocks per track    = 16,
                   tracks per cylinder = 4,
                   cylinders per drive = 256,
                   number of drives    = 1,
                   size = 1*256*4*16

{ disk controller commands }
%constant %integer restore      = 16_10 + restore step rate,
                   seek         = 16_70 + seek step rate,
                   read sector  = 16_20,               { + 8 for DMA }
                   write sector = 16_30,
                   format track = 16_50

{ status values }
%constant %integer busy          = 16_80,
                   ready         = 16_40,
                   write fault   = 16_20,
                   seek complete = 16_10,
                   data request  = 16_08,
                   error         = 16_01
 
{ disk controller task file }
%record %format mfsr (%short status,
                      %short size drive head,
                      %short cylinder high,
                      %short cylinder low,
                      %short sector number,
                      %short sector count,
                      %short error register,
                      %short data register)

%record %format mfsw (%short command,
                      %short size drive head,
                      %short cylinder high,
                      %short cylinder low,
                      %short sector number,
                      %short sector count,
                      %short precomp,
                      %short data register)

@ 16_7ffe0 %record (mfsr) read
@ 16_7ffe0 %record (mfsw) writer

%routine Reset Drive
   %integer drive=0
   writer_precomp = 16_20             { precomp from track 128 onward }
!   %for drive = 0,1,1 %cycle
     waiting=1
     writer_size drive head = drive << 3
     writer_command = restore
     semaphore wait (disc interrupt)
     waiting=1
     writer_cylinder high = 0
     writer_cylinder low = 0
     writer_command = seek            { subsequent seeks at 10us step rate }
     semaphore wait (disc interrupt)
!   %repeat
   Putstring("Drive reset");putsym(10)
%End

%predicate report error (%integer block)
   %if read_status & error # 0 %start
      putstring("Disk err:");putlong(read_errorregister);
      putsym(':');putlong(block);putsym(10)
      %true
   %Finish
   %false
%End

%routine initialise disc
   @0(a6)%Record(Interrupthandlerfm) nih
   %label x

   ih_pc = addr(x)
   ih_a4 = a4
   Add Interrupt Handler (ih,4)
   Reset Drive
   %Return

x: !putsym('?')
   %if read_status & busy = 0 %start
     ! putsym('=')
      %if waiting = 1 %start
         !putsym('*')
         signal Semaphore (disc interrupt) 
         waiting = 0
      %finish
   %Finish
   *movem.l nih_pc,a1/a4/a6
   *jmp (a1)
%end

%routine setup task file(%integer block number)
  %integer drive, cylinder, head, block, remainder

  block     = rem(block number, blocks per track)
  remainder = block number // blocks per track
  head      = rem(remainder, tracks per cylinder)
  remainder = remainder // tracks per cylinder
  cylinder  = rem(remainder, cylinders per drive)
  drive     = 0;! remainder // cylinders per drive
!  %signal %event 9,3 %if drive >= number of drives
  writer_sector number   = block * sectors per block
  writer_size drive head = drive << 3 + head
  writer_cylinder high   = cylinder >> 8
  writer_cylinder low    = cylinder
%end

%integerfn disc read(%integer block, n, %bytename buffer)
   %Integer sector ,byte
   %label r1,r2,loop
   disc reads = disc reads + 1
   %while n > 0 %cycle
      setup task file (block)
      !putsym('R');putlong(block);putsym(10)
      %for sector = 0,1,1 %cycle
         waiting = 1
         writer_command = read sector
         semaphore wait (disc interrupt)
         %result=0 %if report error(block)
!         %for byte = 1,1,256 %cycle
!            buffer = read_data register
!            buffer == buffer[1]
!         %Repeat
         *movea.l buffer,a0
         *move.l  sector,d0
         *lsl     #8,d0
         *adda.l  d0,a0
         *move.l  #63,d0
         *lea     16_7ffef,a1
loop:
         *move.b  (a1),(a0)+
         *move.b  (a1),(a0)+
         *move.b  (a1),(a0)+
         *move.b  (a1),(a0)+
         *dbra    d0,loop
         writer_sectornumber=read_sectornumber + 1
      %Repeat

!      writer_command=readsector
!      semaphore wait (disc interrupt)
!      d0 = 255
!      a0 = addr(buffer)
!      a1 = addr(read_data register)
!r1:   *move.b (a1), (a0)+
!      *dbra d0,r1
!
!      buffer == buffer[256]
!      writer_sectornumber=read_sectornumber + 1
!      writer_command=readsector
!      semaphore wait (disc interrupt)
!      d0 = 255
!      a0 = addr(buffer)
!      a1 = addr(read_data register)
!r2:   *move.b (a1), (a0)+
!      *dbra d0,r2

      buffer == buffer[512]
      n = n - 1; block = block + 1
   %Repeat   
   %result = 0
%end

%integerfn disc write(%integer block, n, %bytename buffer)
   %integer sector, byte
   %label loop

   disc writes = disc writes + 1

   %while n > 0 %cycle
      setup task file (block)
      !putsym('W');putlong(blocK);putsym(10)
      %for sector = 0, 1, 1 %cycle
         waiting = 1
         writer_command = write sector
         *movea.l buffer, a0
         *move.l  sector, d0
         *lsl     #8,d0
         *adda.l  d0, a0
         *move.l  #63, d0
         *lea     16_7ffef, a1
loop:         
         *move.b (a0)+, (a1)
         *move.b (a0)+, (a1)
         *move.b (a0)+, (a1)
         *move.b (a0)+, (a1)
         *dbra    d0, loop
         semaphore wait (disc interrupt)
         %result = -1 %if report error (block)
         writer_sectornumber=read_sector number + 1
      %Repeat
      buffer == buffer[512]
      n = n - 1 ; block = block + 1
   %Repeat
   %Result = 0
%end

%owninteger requests pending = 0
%ownrecord(queue fm) pending queue = 0

!! %routine dump queue(%string(63) where)
!!    %record(message fm)%name m
!!       printstring(where)
!!       m == record(addr(pending queue))
!!       %cycle
!!          %if addr(m) = addr(pending queue) %start
!!             printstring("*TAIL*")
!!             newline
!!             %return
!!          %finish
!!          phex(addr(m))
!!          printstring(" (")
!!          writer(m_i(2), 0)
!!          printstring(")  ")
!!       %repeat
!! %end

%routine change direction
   direction = \direction
!   %if direction = 0 %then putstring("->") %else putstring("<-")
!   putlong(disc reads)
!   putsym(10)
%End

%routine do pending
   %ownrecord(queue fm)%name next == nil
   %record(message fm)%name x
      requests pending = requests pending - 1
      %if requests pending = 0 %start
         ! Only one thing on the queue
         x == record(addr(pending queue_forward))
         next == nil
!         direction = \direction;  !?
         change direction
      %else
         x == record(addr(next))
         %if direction = 0 %start
            x == record(addr(pending queue_forward)) %if x == nil
            x_queue_backward_forward == x_queue_forward
            x_queue_forward_backward == x_queue_backward
            next == x_queue_forward
         %else
            x == record(addr(pending queue_backward)) %if x == nil
            x_queue_backward_forward == x_queue_forward
            x_queue_forward_backward == x_queue_backward
            next == x_queue_backward
         %finish
!         next == nil %and direction = \direction %if next == pending queue
         next == nil %and change direction %if next == pending queue
      %finish
      %if x_i(1) = 1 %start
         ! Read buffer
         x_i(1) = disc read(x_i(2), x_i(4), byteinteger(x_i(3)))
      %else
         ! writebuffer
         x_i(1) = disc write(x_i(2), x_i(4), byteinteger(x_i(3)))
      %finish
      send message(x, x_reply, nil)
%end

%routine add pending(%record(message fm)%name x)
   %record(message fm)%name m
      %if requests pending <= 0 %start
         ! Queue is empty
         pending queue_forward == x_queue
         x_queue_backward == pending queue
         x_queue_forward == pending queue
         pending queue_backward == x_queue
         requests pending = 1
         disc HW = 1 %if disc HW = 0
         %return
      %finish
      m == record(addr(pending queue))
      %cycle
         m == record(addr(m_queue_forward))
         %if addr(m) = addr(pending queue) %or m_i(2) > x_i(2) %start
            x_queue_forward == m_queue
            x_queue_backward == m_queue_backward
            m_queue_backward_forward == x_queue
            m_queue_backward == x_queue
            requests pending = requests pending + 1
            disc HW = requests pending %if disc HW < requests pending
            %return
         %finish
      %repeat
%end

%routine pending request(%record(message fm)%name x)
   %if x_i(1) = 0 %start
      ! Status enquiry
      x_i(1) = 0
      x_i(2) = size
      x_i(3) = 0;  ! Meantime
      send message(x, x_reply, nil)
   %else %if x_i(1) = 1 %or x_i(1) = 2
      add pending(x)
   %else %if x_i(1) = 3
      x_i(1) = 0
      x_i(2) = disc reads
      x_i(3) = disc writes
      x_i(4) = disc HW;  disc HW = 0;  ! Clear?
      send message(x, x_reply, nil)
   %else
      ! Unrecognised request
      x_i(1) = -1
      send message(x, x_reply, nil)
   %finish
%end

%routine disc process
   %record(message fm)%name x
   %integer lasttime
   %label end

      initialise disc
      Putstring ("Disc Process (Mini) running");putsym(10)
      %cycle
            x == receive message(disc mailbox)
            pending request(x)
            %cycle
               %cycle
                  x == dequeue(disc mailbox_queue)
                  %exit %if x == nil
                  semaphore wait(disc mailbox_semaphore)
                  pending request(x)
               %repeat
               %if requests pending > 0 %then do pending %c
                                        %else %exit
            %repeat
      %repeat
%end

%externalroutine start disc
   %record(process fm)%name p
   %ownrecord(runqueuefm) r=0
  %integer i
   %label z
!      i = read_status {just in case}
!      i = set SR(0)
      putstring("Starting disc");putsym(10)
      disc mailbox_semaphore == disc sema

      p == current process
      r_interrupter == p_target_interrupter
      r_processes_forward == r_processes
      r_processes_backward == r_processes
      requeue(r, p_target_queue_header)
      p == create process(8192, addr(z), r, r_processes)
      FS insert("FUJI2243_REQUESTS", addr(disc mailbox))

      %return
z:    disc process
%end

%end %of %file
