// ResistMeasure.bcpl -- board tester hardware operation

// Last modified by McCreight June 25, 1984  1:35 PM
//  to accommodate a wider range of ohms by a hardware change
//  in the tester that produces a voltage proportional to
//  log of resistance.

get "Resist.decl"
get "AltoDefs.d"
get "SysDefs.d"

external
[
// outgoing procedures
MeasureOhms; CalibrateOhms; PulseChannel; Wait; SignalOn; TesterInterrupt

// incoming procedures
DecodeOhms; Max

// incoming statics
OsBuffer
]

static
[
testerOut; ticks
signalOn = false
signalState = false
signalTimer = 0
lastSwitchState = 1
signalOnInterval = 3  // times 17 milliseconds
signalOffInterval = 20
]

//-------------------------------------
let MeasureOhms(channel) = valof
//-------------------------------------
// Returns resistance of channel in ohms
// Returns the special value "open" if greater than 5k
[
let cal5k = SampleChannel(chanCal5k) // 4990+50 ohms
let cal100 = SampleChannel(chanCal100) // 100+50 ohms
let cal0 = SampleChannel(chanCal0) // 0+50 ohms
let unknown = SampleChannel(channel) // x+50 ohms
if unknown ls cal5k then resultis open
if unknown gr cal0 then resultis 0
// The calibration goes in two spans: 0 to 400 ohms, and 400 to 4990.
// The first span is for unknown in [cal0-2*(cal0-cal100)..cal0],
//  and the second span covers the rest.
let n, d, logCalRatio, delta = 0, 0, 0, 0
test unknown ge 2*cal100-cal0
ifnot
  [
  n = unknown-cal5k
  d = cal100-cal5k
  logCalRatio = 162 // 32*lg((4990+50)/(100+50))
  delta = 0
  ]
ifso
  [
  n = unknown-cal100
  d = cal0-cal100
  logCalRatio = 51 // 32*lg((100+50)/(0+50))
  delta = 162 // 32*lg((4990+50)/(100+50))
  ]
let an = Max(n, -n) // Abs(n)
let logRatio = 2*(((logCalRatio/2)*an)/d)+
  (2*(((logCalRatio/2)*an) rem d)+(logCalRatio rem 2)*an)/d
 // logRatio = (logCalRatio*an)/d, without overflow
if n<0 then logRatio = -logRatio
resultis DecodeOhms(logRatio+delta)
]

//-------------------------------------
and CalibrateOhms() = valof
//-------------------------------------
// Calibrates ohmmeter using built-in 0-
// and 4990-ohm resistors. If all appears
// well, returns result of measuring built-in 50-ohm
// resistor.  Otherwise returns an error code
// that is out of range.
[
let cal5k = SampleChannel(chanCal5k)
let cal0 = SampleChannel(chanCal0)
if cal5k ls 10 then resultis 10000
if cal0 gr 245 then resultis 10001
if cal0 le cal5k+100 then resultis 10002
resultis MeasureOhms(chanCal50)
]

// ---------------------------------------------------------------------------
and SampleChannel(channel) = valof
// ---------------------------------------------------------------------------
[
testerOut<<TesterOut.channel = channel
for i = 1 to 100 do loop  // 1 ms or so
let dac = 128
let delta = 64

   [ // repeat
   testerOut<<TesterOut.dac = dac
   @utilOut = not testerOut  // output bits are inverted
   for i = 1 to 10 do loop  // 100 microseconds or so
   test (@utilIn)<<TesterIn.comparator ne 0
      ifso dac = dac - delta
      ifnot dac = dac + delta
   delta = delta rshift 1
   ] repeatuntil delta eq 0

resultis dac
]

// ---------------------------------------------------------------------------
and PulseChannel(channel, time) be
// ---------------------------------------------------------------------------
// Sends high-frequency pulses out channel for specified time (17 ms units).
// At the end, leaves channel deselected (precisely, leaves the 0-ohm
// calibration channel selected instead).
// This is to aid use of an external current tracer.
[
testerOut<<TesterOut.dac = 128  // any constant value
let endTime = ticks+time
let chan = chanCal0
until ticks-endTime gr 0 & chan eq chanCal0 do
   [
   chan = chan eq chanCal0? channel, chanCal0
   testerOut<<TesterOut.channel = chan
   @utilOut = not testerOut  // output bits are inverted
   ]
]

// ---------------------------------------------------------------------------
and Wait(time) be
// ---------------------------------------------------------------------------
// Busy-waits for time*17 ms.
[
let endTime = ticks+time
until ticks-endTime gr 0 do loop
]

// ---------------------------------------------------------------------------
and SignalOn(value) be
// ---------------------------------------------------------------------------
// Turns on or off the audible signal
[
if value ne signalOn then
   [
   signalOn = value
   signalTimer = 1
   ]
]

// ---------------------------------------------------------------------------
and TesterInterrupt() be
// ---------------------------------------------------------------------------
[
ticks = ticks+1
if signalTimer gr 0 then
   [
   signalTimer = signalTimer-1
   if signalTimer eq 0 then
      test signalOn
         ifso
            [
            signalState = not signalState
            testerOut<<TesterOut.signal = signalState
            signalTimer = signalState? signalOnInterval, signalOffInterval
            ]
         ifnot
            [
            testerOut<<TesterOut.signal = false
            signalState = false
            ]
   @utilOut = not testerOut
   ]

let switchState = (@utilIn)<<TesterIn.switch
if switchState ne lastSwitchState then
   [
   lastSwitchState = switchState
   if switchState eq 0 then CramKeyboardCharacter($*s)
   ]
]

// ---------------------------------------------------------------------------
and CramKeyboardCharacter(char) be
// ---------------------------------------------------------------------------
[
let newIn = OsBuffer>>OsBUF.In+1
if newIn eq OsBuffer>>OsBUF.Last then newIn = OsBuffer>>OsBUF.First
if newIn ne OsBuffer>>OsBUF.Out then
   [
   @(OsBuffer>>OsBUF.In) = char
   OsBuffer>>OsBUF.In = newIn
   ]
]