// AltIOImp.bcpl -- Alto Imp driver for Maxc2
// This version supports 96-bit leaders, NCP, Internet/TCP, Pup

//	Last modified December 13, 1982  9:35 AM

get "AltIO.decl"
get "Pup0.decl"
get "AltIOImp.decl"

external
[
// Outgoing procedures
ImpReset; EncapsulateImpPup; SendImpPacket
ImpInputInterrupt; ImpOutputInterrupt; ImpProcess

// Incoming procedures
Enqueue; Dequeue; CauseInterrupt; DisableInterrupts; EnableInterrupts
MemReadRelative; MemWriteRelative; MemReadBlock40A; MemWriteBlock40A
MemReadBlock32A; MemWriteBlock32A; RMWBitAbsolute; SignalMaxc
ConvertNBP; NBBlockTransfer; NBReadWord; NBWriteWord
ImpConvTo32; ImpConvTo36; ImpConvFrom32; ImpConvFrom36
SetTimer; TimerHasExpired; Dismiss; Block
CallSwat; Usc; StartIO; MoveBlock; Zero; Min

// Outgoing statics
impNDB; impMaxcBuf; impInputRequest; impOutputRequest; icb

// Incoming statics
pbiFreeQ; pbiIQ; lenPup
]

static
[
impNDB; impMaxcBuf; icb
impInputRequest = false
impOutputRequest = false
lastStatus
]


//----------------------------------------------------------------------------
let ImpReset() be
//----------------------------------------------------------------------------
// Called when an I/O Reset is done
[
ResetImpInterface(true)
impInputRequest = false
impOutputRequest = false

// Set "power on" bit in status word if an interface seems to be installed.
icb = impNDB>>ImpNDB.icb
impNDB>>ImpNDB.powerOn = icb>>ICB.controlPost ne 0
lastStatus = -1  // force status update in Maxc
]

//----------------------------------------------------------------------------
and ResetImpInterface(dropHostReady) be
//----------------------------------------------------------------------------
// Shuts off the interface and flushes all buffers.
[
IssueControlCommand(icMasterReset)
if dropHostReady then IssueControlCommand(icHostReadyOff)
IssueControlCommand(icLoopBackOff)
UpdateStatus(impNDB>>ImpNDB.icb>>ICB.controlPost)
FlushImpInput()
FlushImpOutput()
impNDB>>ImpNDB.oActive = false
]

// Pup interface procedures

//----------------------------------------------------------------------------
and EncapsulateImpPup(pbi, pdh) be
//----------------------------------------------------------------------------
// Performs Arpanet-dependent encapsulation.
// pbi points at a PBI containing a Pup.
// pdh is physical destination host for Pup.
[
pbi>>ImpPBI.pupHost = pdh  // just remember this for later
]

//----------------------------------------------------------------------------
and SendImpPacket(pbi) be
//----------------------------------------------------------------------------
[
test pbi>>ImpPBI.pupHost eq 0 %  // For now, discard broadcast
 impNDB>>ImpNDB.hostNotReady ne 0 %  // Discard if host not ready
 impNDB>>ImpNDB.impNotReady ne 0  // Discard if Imp not ready
   ifso
      Enqueue(pbi>>PBI.queue, pbi)
   ifnot
      [
      Enqueue(lv impNDB>>ImpNDB.pupOQ, pbi)
      unless impNDB>>ImpNDB.oActive do
         CauseInterrupt(impNDB>>ImpNDB.icb>>ICB.outputChanMask)
      ]
]

//----------------------------------------------------------------------------
and ImpProcess() be
//----------------------------------------------------------------------------
// Process that transfers Imp messages to and from Maxc main memory.
// Also watches status and maintains various timers.
[
let maxcWord = vec 2
let maxcNBP = vec 2
let nbp = vec lenNBP
let icb = impNDB>>ImpNDB.icb
let statusTimer, maxcInputTimer = nil, nil
SetTimer(lv statusTimer, 0)

// The main loop of the process
   [
   Block()
   if impNDB>>ImpNDB.pupInputIMBQ.head ne 0 then
      [  // Transfer received Pup to a normal Pup PBI and hand it to AltIOPup
      let imb = Dequeue(lv impNDB>>ImpNDB.pupInputIMBQ)
      // Accept only if source address will fit in a Pup host number
      if Usc(imb>>IMB.imp, 64) ls 0 & imb>>IMB.host ls 4 then
         [
         let host = imb>>IMB.host lshift 6 + imb>>IMB.imp
         let pbi = Dequeue(pbiFreeQ)
         if pbi ne 0 then
            [
            pbi>>PBI.packetLength = 2 + (imb>>IMB.pup.length+1) rshift 1
            pbi>>ImpPBI.pupHost = host  // "encapsulation"
            MoveBlock(lv pbi>>PBI.pup, lv imb>>IMB.pup,
             pbi>>PBI.packetLength-2)
            pbi>>PBI.ndb = impNDB
            Enqueue(pbiIQ, pbi)
            ]
         ]
      Enqueue(lv impNDB>>ImpNDB.freeInputIMBQ, imb)
      ]

// ImpProcess (cont'd)

   if impInputRequest & impNDB>>ImpNDB.hostInputIMBQ.head ne 0 then
      [  // Ready to hand received Host-Host protocol message to Maxc
      impInputRequest = false

      // Read the NBP from XIMPIB and see if Maxc is really ready
      MemReadRelative(xIMPIB, maxcNBP)
      unless ConvertNBP(maxcNBP, nbp) loop
      SetTimer(lv maxcInputTimer, 1500)

      // Convert the Imp leader (96 bits) to 36-bit Maxc memory format
      let imb = Dequeue(lv impNDB>>ImpNDB.hostInputIMBQ)
      let format = MessageFormat(imb)
      let message = lv imb>>IMB.message
      impMaxcBuf!0 = message!0  // 0-15
      impMaxcBuf!1 = message!1  // 16-31
      impMaxcBuf!2 = message!2  // 32-35
      impMaxcBuf!3 = message!2 lshift 4 + message!3 rshift 12  // 36-51
      impMaxcBuf!4 = message!3 lshift 4 + message!4 rshift 12  // 52-67
      impMaxcBuf!5 = message!4 lshift 4  // 68-71
      impMaxcBuf!6 = message!4 lshift 8 + message!5 rshift 8  // 72-87
      impMaxcBuf!7 = message!5 lshift 8  // 88-95
      // Low-order 12 bits of last Maxc word get garbage

      // Convert Host-Host protocol leader if appropriate, and
      // compute parameters for body transfer.
      let nMaxcWordsLeader, nMaxcWordsData = nil, nil
      switchon format into
         [
         case mfHost36:
            nMaxcWordsData = (16*imb>>IMB.length - size HostLeader +35)/36
            docase -1
         case mfHost32:
            nMaxcWordsData = (16*imb>>IMB.length - size HostLeader +31)/32
            docase -1
         case -1:  // tail of cases mfHost36 and mfHost32
            // Convert 40-bit Host-Host leader by discarding first 4 bits
            // and putting the other 36 in one Maxc word.
            impMaxcBuf!9 = message!6 lshift 4 + message!7 rshift 12
            impMaxcBuf!10 = message!7 lshift 4 + message!8 rshift 12
            impMaxcBuf!11 = message!8 lshift 4
            nMaxcWordsLeader = 4
            endcase
         case mfRaw32:
            nMaxcWordsData = (16*imb>>IMB.length - size ImpLeader +31)/32
            nMaxcWordsLeader = 3
            endcase
         default:
            CallSwat("[ImpProcess] Wrong-format message on hostInputIMBQ")
         ]

      if nMaxcWordsLeader+nMaxcWordsData gr maxMaxcWords then
         CallSwat("[ImpProcess] Incoming message too large")

// ImpProcess (cont'd)
// Imp input to Maxc (cont'd)

      // Transfer leader(s) to Maxc
      // Must put message length (Maxc words) in bits 0-15 of word 0 of
      // the Maxc buffer (without disturbing the rest of the word)
      // and the message itself starting at word 1.
      NBReadWord(nbp, 0, maxcWord)
      maxcWord!0 = nMaxcWordsLeader+nMaxcWordsData
      NBWriteWord(nbp, 0, maxcWord)
      NBBlockTransfer(MemWriteBlock40A, nbp, 1, impMaxcBuf,
       nMaxcWordsLeader, 3)

      // Convert body to appropriate Maxc memory format (if necessary)
      // and transfer it to Maxc.
      if nMaxcWordsData gr 0 then switchon format into
         [
         case mfHost36:
            ImpConvTo36(impMaxcBuf, message+8, nMaxcWordsData)
            NBBlockTransfer(MemWriteBlock40A, nbp, 5, impMaxcBuf,
             nMaxcWordsData, 3)
            endcase
         case mfHost32:
            ImpConvTo32(impMaxcBuf, message+8, nMaxcWordsData)
            NBBlockTransfer(MemWriteBlock40A, nbp, 5, impMaxcBuf,
             nMaxcWordsData, 3)
            endcase
         case mfRaw32:
            NBBlockTransfer(MemWriteBlock32A, nbp, 4, message+6,
             nMaxcWordsData, 2)
            endcase
         ]

      // Don't need the IMB any more.  Free it and restart input if
      // it was stopped due to lack of buffers.
      Enqueue(lv impNDB>>ImpNDB.freeInputIMBQ, imb)
      if impNDB>>ImpNDB.inputIMB eq 0 then
         CauseInterrupt(icb>>ICB.inputChanMask)

      // Signal Maxc that we have given it an Imp message
      MemWriteRelative(xIMPIB, maxcNBP)  //set use flag
      RMWBitAbsolute(aNVMAX, nmIMPIDN)
      SignalMaxc()
      ]

// ImpProcess (cont'd)

   if impOutputRequest & impNDB>>ImpNDB.freeOutputIMBQ.head ne 0 then
      [  // Ready to get Host-Host protocol message from Maxc and send it
      impOutputRequest = false

      // Read the NBP from XIMPOB and see if Maxc is really ready
      MemReadRelative(xIMPOB, maxcNBP)
      unless ConvertNBP(maxcNBP, nbp) loop

      // If host is now down, raise it
      if impNDB>>ImpNDB.hostNotReady then
         [
         IssueControlCommand(icHostReadyOn)
         Dismiss(50)
         IssueControlCommand(icNoop)  // just get updated status
         UpdateStatus(icb>>ICB.controlPost)
         ]

      // Copy the overhead word and the leader(s) from Maxc memory.
      // Copy enough words to encompass both Imp and Host-Host leaders,
      // though the latter may not actually be needed.
      // Word 0 has the message length (Maxc words) in bits 0-15.
      // The message itself starts in word 1.
      NBReadWord(nbp, 0, maxcWord)
      let nMaxcWords = maxcWord!0
      if Usc(nMaxcWords, maxMaxcWords) gr 0 then
         CallSwat("[ImpProcess] Maxc Imp message too large")
      NBBlockTransfer(MemReadBlock40A, nbp, 1, impMaxcBuf,
       Min(nMaxcWords, 4), 3)

      // Convert the Imp leader (96 bits) from 36-bit Maxc memory format.
      let imb = Dequeue(lv impNDB>>ImpNDB.freeOutputIMBQ)
      let message = lv imb>>IMB.message
      message!0 = impMaxcBuf!0  // 0-15
      message!1 = impMaxcBuf!1  // 16-31
      message!2 = (impMaxcBuf!2 & #170000) + impMaxcBuf!3 rshift 4  // 32-47
      message!3 = impMaxcBuf!3 lshift 12 + impMaxcBuf!4 rshift 4  // 48-63
      message!4 = impMaxcBuf!4 lshift 12 + (impMaxcBuf!5 & #170000) rshift 4 +
       impMaxcBuf!6 rshift 8 // 64-79
      message!5 = impMaxcBuf!6 lshift 8 + impMaxcBuf!7 rshift 8  // 80-95
      // Discard 12 garbage bits of Maxc data

      // Convert the 36-bit Maxc host leader to a 40-bit Host-Host leader
      // by prefixing 4 zero bits.  Do this whether or not it's
      // appropriate to do so, because we don't yet know whether or not
      // MessageFormat will need to look at the byte size.
      message!6 = impMaxcBuf!9 rshift 4
      message!7 = impMaxcBuf!9 lshift 12 + impMaxcBuf!10 rshift 4
      message!8 = impMaxcBuf!10 lshift 12 + impMaxcBuf!11 rshift 4

// ImpProcess (cont'd)
// Imp output from Maxc (cont'd)

      // Compute parameters for body transfer
      let format = MessageFormat(imb)
      let nAltoWords, nMaxcWordsData = nil, nil
      switchon format into
         [
         case mfHost36:
            nMaxcWordsData = nMaxcWords-4
            nAltoWords = (size HostLeader + 36*nMaxcWordsData +15)/16
            endcase
         case mfHost32:
            nMaxcWordsData = nMaxcWords-4
            nAltoWords = (size HostLeader + 32*nMaxcWordsData +15)/16
            endcase
         case mfRaw32: case mfPup:
            nMaxcWordsData = nMaxcWords-3
            nAltoWords = (size ImpLeader + 32*nMaxcWordsData +15)/16
            endcase
         ]

      if nAltoWords gr maxImpMessWords then  // Consistency check
         CallSwat("[ImpProcess] Outgoing message too large")

      // Transfer body from Maxc and
      // convert it from the appropriate Maxc memory format (if necessary)
      if nMaxcWordsData gr 0 then switchon MessageFormat(imb) into
         [
         case mfHost36:
            NBBlockTransfer(MemReadBlock40A, nbp, 5, impMaxcBuf,
             nMaxcWordsData, 3)
            ImpConvFrom36(message+8, impMaxcBuf, nMaxcWordsData+1)
            endcase
         case mfHost32:
            NBBlockTransfer(MemReadBlock40A, nbp, 5, impMaxcBuf,
             nMaxcWordsData, 3)
            ImpConvFrom32(message+8, impMaxcBuf, nMaxcWordsData+1)
            endcase
         case mfRaw32:
         case mfPup:  // Someone trying to send a Pup thru IMPDV??
            NBBlockTransfer(MemReadBlock32A, nbp, 4, message+6,
             nMaxcWordsData, 2)
            endcase
         ]

      // Queue message for transmission to Imp
      imb>>IMB.length = nAltoWords
      Enqueue(lv impNDB>>ImpNDB.hostOutputIMBQ, imb)
      unless impNDB>>ImpNDB.oActive do
         CauseInterrupt(impNDB>>ImpNDB.icb>>ICB.outputChanMask)

      // Signal Maxc that we have taken the Imp message
      MemWriteRelative(xIMPOB, maxcNBP)  //set use flag
      RMWBitAbsolute(aNVMAX, nmIMPODN)
      SignalMaxc()
      ]

// ImpProcess (cont'd)

   if TimerHasExpired(lv statusTimer) then
      [  // Do once-per-second housekeeping
      SetTimer(lv statusTimer, 100)
      IssueControlCommand(icNoop)  // Noop, just to get updated status
      UpdateStatus(icb>>ICB.controlPost)

      // If Maxc fails to accept an input Host-Host protocol packet
      // within 15 seconds, drop the Host ready line and flush all buffers.
      test impNDB>>ImpNDB.hostInputIMBQ.head eq 0
         ifso SetTimer(lv maxcInputTimer, 1500)
         ifnot if TimerHasExpired(lv maxcInputTimer) then
            ResetImpInterface(true)

      // If status has changed, update status word in Maxc memory
      if impNDB>>ImpNDB.status ne lastStatus then
         [
         lastStatus = impNDB>>ImpNDB.status
         maxcWord!0, maxcWord!1, maxcWord!2 = 0, lastStatus, 0
         MemWriteRelative(xIMPSI, maxcWord)
         // If Imp has gone off, flap the interface and flush buffers.
         // This is to recover from lost interrupts and such.
         if impNDB>>ImpNDB.impNotReady then ResetImpInterface(false)
         ]

      // If Imp and Host are now up and there is no input buffer set up,
      // attempt to start input.
      if impNDB>>ImpNDB.impNotReady eq 0 & impNDB>>ImpNDB.hostNotReady eq 0 &
       impNDB>>ImpNDB.inputIMB eq 0 &
       impNDB>>ImpNDB.freeInputIMBQ.head ne 0 then
         CauseInterrupt(icb>>ICB.inputChanMask)
      ]
   ] repeat
]

//----------------------------------------------------------------------------
and MessageFormat(imb) = valof
//----------------------------------------------------------------------------
// Returns the appropriate message format (mfHost36, mfHost32, mfPup) for
// supplied Imp Message Buffer.
[
let type = imb>>IMB.messageType

// Link number may or may not be used for addressing the message,
// depending on message type
manifest [ y = true; n = false ]
let linkAddressed = type gr 9? true,
 (table [ y; n; n; n; n; y; n; y; y; y ])!type

// All non-link-addressed messages are handled as 36-bit Host-Host
unless linkAddressed resultis mfHost36

// Link-addressed messages on the Pup link are given to the Pup handler.
// Note that this does not necessarily mean that they are Pups -- further
// discrimination is required.
let link = imb>>IMB.link
if link eq linkPup resultis mfPup

// Not on the Pup link.
// All irregular messages, regular messages on non-NCP links, and
// regular messages to/from fake hosts are handled as raw 32-bit messages.
if type ne 0 % link gr maxLinkNCP %
 imb>>IMB.host ge firstFakeHost resultis mfRaw32

// Regular messages on Host-Host protocol links and with byte size 36
// are handled as 36-bit Host-Host, all others as 32-bit Host-Host
resultis imb>>IMB.byteSize eq 36? mfHost36, mfHost32
]

//----------------------------------------------------------------------------
and ImpInputInterrupt() be
//----------------------------------------------------------------------------
[
let icb = impNDB>>ImpNDB.icb
let imb = impNDB>>ImpNDB.inputIMB
if imb ne 0 then
   [  // Dispose of incoming message
   let queue = lv impNDB>>ImpNDB.freeInputIMBQ
   UpdateStatus(icb>>ICB.inputPost)
   switchon icb>>ICB.inputPost.microcode into
      [
      case isDone:
         [  // Normal completion
         if impNDB>>ImpNDB.iError then  // Clear error, discard this message
            [ impNDB>>ImpNDB.iError = false; endcase ]
         if imb>>IMB.format ne 17B then endcase  // Discard if not new format
         imb>>IMB.length = icb>>ICB.inputPointer - lv imb>>IMB.message
         test MessageFormat(imb) eq mfPup
            ifso  // Pups must also be type 0 (regular)
               if imb>>IMB.messageType eq 0 &
                imb>>IMB.length ge (size ImpLeader/16)+pupOvWords &
                imb>>IMB.pup.length le imb>>IMB.length lshift 1 &
                imb>>IMB.pup.length le lenPup lshift 1 then
                  queue = lv impNDB>>ImpNDB.pupInputIMBQ
            ifnot  // Host-host or irregular
               queue = lv impNDB>>ImpNDB.hostInputIMBQ
         endcase
         ]
      case isInputOverflow:
         [  // Input overflow, cause remainder of message to be discarded
         impNDB>>ImpNDB.iError = true
         endcase
         ]
      default:
         CallSwat("[ImpInputInterrupt] Improper microcode status")
      ]
   Enqueue(queue, imb)
   imb = 0
   ]

unless impNDB>>ImpNDB.impNotReady % impNDB>>ImpNDB.hostNotReady do
   [  // Try to start up input
   imb = Dequeue(lv impNDB>>ImpNDB.freeInputIMBQ)
   if imb ne 0 then
      [
      icb>>ICB.inputPointer = lv imb>>IMB.message
      icb>>ICB.inputEnd = imb+lenIMB
      icb>>ICB.inputPost = 0
      StartIO(impStartInput)
      ]
   ]
impNDB>>ImpNDB.inputIMB = imb
]

//----------------------------------------------------------------------------
and FlushImpInput() be
//----------------------------------------------------------------------------
[
   [ // repeat
   let imb = Dequeue(lv impNDB>>ImpNDB.hostInputIMBQ)
   if imb eq 0 break
   Enqueue(lv impNDB>>ImpNDB.freeInputIMBQ, imb)
   ] repeat
if impNDB>>ImpNDB.inputIMB ne 0 then
   [
   Enqueue(lv impNDB>>ImpNDB.freeInputIMBQ, impNDB>>ImpNDB.inputIMB)
   impNDB>>ImpNDB.inputIMB = 0
   ]
]

//----------------------------------------------------------------------------
and ImpOutputInterrupt() be
//----------------------------------------------------------------------------
[
let icb = impNDB>>ImpNDB.icb
if impNDB>>ImpNDB.oActive then
   [  // Dispose of completed output message
   unless icb>>ICB.outputPost.microcode eq isDone do
      CallSwat("[ImpOutputInterrupt] Improper microcode status")
   UpdateStatus(icb>>ICB.outputPost)
   test impNDB>>ImpNDB.sendingNop
      ifso impNDB>>ImpNDB.sendingNop = false
      ifnot unless impNDB>>ImpNDB.oError do ImpOutputDispose()
   ]

impNDB>>ImpNDB.oActive = false

// If the Imp is down, flush all output
if impNDB>>ImpNDB.impNotReady then [ FlushImpOutput(); return ]

test impNDB>>ImpNDB.oError
   ifso
      [  // Error flop was set.  Send a Nop
      impNDB>>ImpNDB.oError = false
      impNDB>>ImpNDB.sendingNop = true
      icb>>ICB.outputPointer = table [  // Nop message
         17B lshift 8; 4; 0; 0; 0; 0 ]
      icb>>ICB.outputEnd = icb>>ICB.outputPointer+6
      ]
   ifnot
      test SetupImpOutput()
         ifnot return
         ifso  // IMB pending
            [
            let imb = impNDB>>ImpNDB.outputIMB
            icb>>ICB.outputPointer = lv imb>>IMB.message
            icb>>ICB.outputEnd = icb>>ICB.outputPointer+imb>>IMB.length
            ]

StartIO(impStartOutput)
impNDB>>ImpNDB.oActive = true
]

//----------------------------------------------------------------------------
and ImpOutputDispose() be
//----------------------------------------------------------------------------
[
if impNDB>>ImpNDB.outputIMB ne 0 then
   [
   Enqueue(lv impNDB>>ImpNDB.freeOutputIMBQ, impNDB>>ImpNDB.outputIMB)
   impNDB>>ImpNDB.outputIMB = 0
   ]
]

//----------------------------------------------------------------------------
and SetupImpOutput() = valof
//----------------------------------------------------------------------------
// Returns true iff ready to send an IMB.
[
if impNDB>>ImpNDB.outputIMB eq 0 then
   impNDB>>ImpNDB.outputIMB = Dequeue(lv impNDB>>ImpNDB.hostOutputIMBQ)
if impNDB>>ImpNDB.outputIMB eq 0 & impNDB>>ImpNDB.pupOQ.head ne 0 then
   [
   let pbi = Dequeue(lv impNDB>>ImpNDB.pupOQ)
   let imb = Dequeue(lv impNDB>>ImpNDB.freeOutputIMBQ)
   if imb eq 0 then
      CallSwat("[SetupImpOutput] Output idle but freeOutputIMBQ empty")
   MoveBlock(lv imb>>IMB.pup, lv pbi>>PBI.pup,
    (pbi>>PBI.pup.length +1) rshift 1)
   Zero(lv imb>>IMB.message, size ImpLeader/16)
   imb>>IMB.format = 17B
   imb>>IMB.host = pbi>>ImpPBI.pupHost rshift 6
   imb>>IMB.imp = pbi>>ImpPBI.pupHost & 77B
   imb>>IMB.link = linkPup
   imb>>IMB.length = (size ImpLeader/16)+(pbi>>PBI.pup.length +1) rshift 1
   impNDB>>ImpNDB.outputIMB = imb
   Enqueue(pbi>>PBI.queue, pbi)
   ]
resultis impNDB>>ImpNDB.outputIMB ne 0
]

//----------------------------------------------------------------------------
and FlushImpOutput() be while SetupImpOutput() do ImpOutputDispose()
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and UpdateStatus(status) be
//----------------------------------------------------------------------------
// Update software status from hardware.
[
impNDB>>ImpNDB.impNotReady = status<<ImpStatus.impNotReady
impNDB>>ImpNDB.hostNotReady = status<<ImpStatus.hostNotReady
if status<<ImpStatus.impWasDown then
   [  // Imp was down.  Attempt to clear it and record software errors
   IssueControlCommand(icClearImpWasDown)
   impNDB>>ImpNDB.iError = true
   impNDB>>ImpNDB.oError = true
   ]
]

//----------------------------------------------------------------------------
and IssueControlCommand(command) be
//----------------------------------------------------------------------------
// All control commands must be issued via this subroutine, which must
// disable interrupts since the control portion of the ICB is shared
// between input and output.
[
let icb = impNDB>>ImpNDB.icb
DisableInterrupts()
icb>>ICB.controlPost = 0
icb>>ICB.control = command
StartIO(impControlStatus, icb)
EnableInterrupts()
]