module Memory

  implicit NONE

  public

  type :: Instruction_t
    integer :: Index   ! 0..9
    integer :: Command
    integer :: Channel ! 00..99 unless I/O, which allows 00..1000
    integer :: Word
  end type Instruction_t

  double precision, save, target :: Double(9:18,0:49)

  type(instruction_t) :: Instructions(7:19,0:99) ! channel X word

  real, save, target :: Single(7:18,0:99)

  integer, parameter :: S_None=0, S_I_O = 1, S_Sqrt = 2
  integer, parameter :: S_Log=3, S_Exp = 4, S_Trig=5, S_Arctan=6
  integer :: Subroutines(7:18) = s_none ! indexed by channel number
  character(6*(s_arctan-s_none+1)), parameter :: Names = &
    & 'none  io    sqrt  log   exp   trig  arctan'

  ! Math subroutine entry points
  integer, parameter :: E_sqrt =  97  ! sqrt
  integer, parameter :: E_log10 = 71  ! log 10
  integer, parameter :: E_log =   17  ! log e
  integer, parameter :: E_log2 =   8  ! log 2
  integer, parameter :: E_10_x =  72  ! 10**x
  integer, parameter :: E_e_x =   22  ! exp
  integer, parameter :: E_2_x =    8  ! 2**x
  integer, parameter :: E_sin_r = 42  ! sin(radians)
  integer, parameter :: E_sin_d = 39  ! sin(degrees)
  integer, parameter :: E_cos_r = 26  ! cos(radians)
  integer, parameter :: E_cos_d = 23  ! cos(degrees)
  integer, parameter :: E_atan =  24  ! atan => radians

  integer, parameter :: W_None = 0, W_Data = 1, W_Instr = 2
  integer, save :: What(7:19,0:99) = w_none

contains

  subroutine Format_Instruction ( Instruction, Text )
    type(instruction_t), intent(in) :: Instruction
    character(7), intent(out) :: Text
    integer :: Instr
    instr = instruction%channel + 100 * (instruction%command + &
      &     100 * instruction%channel )
    write ( text(1:5), '(i5)' ) instr
    if ( instruction%word == 100 ) then
      text(6:7) = 'u0'
    else
      write ( text(6:7), '(i2.2)' ) instruction%word
    end if
    text = adjustl(text)
  end subroutine Format_Instruction

  subroutine Read_Instruction ( Text, Instruction, Error )
    character(7), intent(in) :: Text
    type(instruction_t), intent(out) :: Instruction
    logical, intent(out) :: Error
    integer :: Instr, IOSTAT, L
    error = .false.
    l = len_trim(text)
    if ( text(l-1:l) == 'u0' .or. text(l-1:l) == 'U0' ) then
      instruction%word = 100
    else
      read ( text(l-1:l), '(i2)', iostat=iostat ) instruction%word
      error = iostat /= 0
    end if
    if ( l > 2 ) then
      read ( text(1:l-2), *, iostat=iostat ) instr
      error = iostat /= 0
    else
      instr = 0
    end if
    error = error .or. instr < 0
    instruction%channel = mod(instr,100)
    instr = instr / 100
    instruction%command = mod(instr,100)
    instr = instr / 100
    instruction%index = instr
    error = error .or. instr > 9
    if ( error ) &
      & print '(3a)', 'Instruction "', trim(adjustl(text)), '" is invalid'
  end subroutine Read_Instruction

end module Memory
