//bldr xjas jasmc jasswat loadram

get "bcpl.head"

//outgoing procedures
external
[
JasmineInit
JasmineSetDelay//(delay=0 to 15)
JasmineSetResolution//(skips=0 to 15)
JasmineSetTime//(ticks = 38usecond units)
JasmineLoadRam//(array, len)
JasmineScanInit//(block,nWords) returns scanHead
JasmineScanClose//(scanHead)
JasmineReadLine//(scanHead) returns pointer to scan line
JasmineSetReadMode//readOnly=1, readAndStep=2, readStepAndDelay=3
JasmineStep//(nSteps,forward)
JasmineSetWindow//(xstart,xlen)in full resolution coords (250 microns)
JasmineCoord//(coord) returns # samples
JasmineNewPage//(pageLen) (>100 = 250u, <100=inches)
JasmineEject //eject current page
JasmineMotorOff
]

//incoming procedures
external
[
LoadRam
JasmineSwatContextProc
]

//incoming statics
external RamImage

//internal statics
static
[
SetBLV
savedEmulatorX;savedDWTX
]

manifest
[ SafeLoc=#177022
StepLoc=#177020
Outport=#177016
ITQUAN=#422
ITIBITS=#423
ITTIME=#525
ScanTime=#526
ScanCBHead=#736
StartCommand=#737
]

manifest
[
START=7
LOAD=6
MOTORCTL=5
WE1=3
WE2=2
WE3=1
SETDELAY=0

StatusINUSE = 1//in general, all positive numbers are "in use" flags
StatusFREE = 0
StatusDONE = -1
StatusDATALATE = -2

CommandREAD = 0
CommandDELAY = 1
CommandFORWARD = 2
CommandBACK = 3
]

structure dataOut :
[ blank
byte
enablebit
commandbit 3
databit 4
]

structure ScanCB :
[
linkword
commandword
statusword
bufferword
]

structure ScanHead :
[
firstCBword
nBlocksword
nextBlockword
nCBsPerBlockword
nScanLinesword
]

structure ramVal :
[
q1bit 4
q2bit 4
q3bit 4
q4bit 4
]

structure Array :
[ pixel↑0,1000
byte
]

static [ DelaySetting=13;SkipSamples=0;ArraySize=1024
Time=656//38 usec ticks = 1/40 sec
forward = true; StepCommand
Late=0;Bad=0
Bug=0
maxNumBlocks=10
ReadMode=2//Read,Step,Delay
savedUFP;savedSCP
ScannerTaskBits=#177376
StepTime=130//200 steps/second
//StepperSlip=6//direction reversal causes this many slip steps on old model
StepperSlip=0
TopBlank=120
nDiscardElements=0
nElements=1024
currentScanLine=-1//not active
firstScanLine=-1
nScanLines=#77777
]

manifest Reverse=false
//mirror reversal; to switch Reverse, also use different ucode

let JasmineInit() be
[
savedUFP=@lvUserFinishProc
@lvUserFinishProc=JasmineFinish
savedSCP=@lvSwatContextProc
@lvSwatContextProc=JasmineSwatContextProc
Init()
SendCommand(SETDELAY,DelaySetting)
@ScanCBHead=0
LoadRam(RamImage,true)
@#335 = RamImage
InitStartCommand()
@ScanTime=Time
SetBLV=table [ #70002;#1401]
]

and JasmineSetDelay(delay) be
[
DelaySetting=delay
ScannerOff()
SendCommand(SETDELAY,DelaySetting)
ScannerOn()
]

and JasmineSetResolution(skips) be
[
SkipSamples=skips
InitStartCommand()
]

and JasmineSetReadMode(mode) be
[
ReadMode=mode
]

and JasmineNewPage(length) be
[
JasmineStep(TopBlank,true)
forward=true
currentScanLine=0
]

and JasmineEject() be
[
if (firstScanLine ge 0) then
[JasmineStep(currentScanLine,false)
JasmineStep(TopBlank+50,false)
firstScanLine=-1
]
]

and JasmineCoord(c) = c/(SkipSamples+1)

and JasmineSetWindow(xStart,xLen,yStart,yLen;numargs na) be
[
if na ge 4 then
[firstScanLine=yStart
nScanLines=yLen
]
compiletest Reverse then
[
nDiscardElements=ArraySize-(xStart+xLen)
]
or
[
nDiscardElements=xStart
]
nElements=xLen
]

and JasmineSetTime(ticks) be
[
Time=ticks
@ScanTime=Time
]

and JasmineLoadRam(ramVec,nElements;numargs n) be
[ if n ls 2 then nElements=1024
ScannerOff()
SendCommand(SETDELAY,0)//enable addr counting during load
Init()
//for i=1 to 32 do SendCommand(LOAD);Init()//to fix delay count??
//bug in jasminepromgen: never get to first ram loc
//for i=1 to 1000 do [ ] //wait for start pulse to finish (4 msec)
let load=0;load<<dataOut.command=LOAD
let we1=0;we1<<dataOut.command=WE1
let we2=0;we2<<dataOut.command=WE2
let we3=0;we3<<dataOut.command=WE3
compiletest Reverse then
[
for i=nElements-1 to 0 by -1 do
[Pulse(we1+(ramVec!i)<<ramVal.q2)
Pulse(we2+(ramVec!i)<<ramVal.q3)
Pulse(we3+(ramVec!i)<<ramVal.q4)
Pulse(load)
]
]
or
[
for i=0 to nElements-1 do
[Pulse(we1+(ramVec!i)<<ramVal.q2)
Pulse(we2+(ramVec!i)<<ramVal.q3)
Pulse(we3+(ramVec!i)<<ramVal.q4)
Pulse(load)
]
]
SendCommand(SETDELAY,DelaySetting)
Init()
ScannerOn()
]

and JasmineScanInit(block,nWords) = valof
[
let nBytes = (nElements+SkipSamples)/(SkipSamples + 1)
let nDiscardCommands=((nDiscardElements/(SkipSamples+1)) le 1)?0,1
let wordsPerBlock=
(nBytes+1)/2 +
(size ScanCB/16)*(ReadMode+SkipSamples+nDiscardCommands)
//*ReadMode for: read,forward,delay commands
let nBlocks=(nWords - (size ScanHead/16))/wordsPerBlock
if nBlocks gr maxNumBlocks then nBlocks = maxNumBlocks
block>>ScanHead.firstCB=block+(size ScanHead/16)
block>>ScanHead.nBlocks=nBlocks
block>>ScanHead.nextBlock=0
block>>ScanHead.nCBsPerBlock=ReadMode+SkipSamples+nDiscardCommands
block>>ScanHead.nScanLines=nScanLines

if (firstScanLine ge 0) & (currentScanLine ne firstScanLine) then
[JasmineStep(firstScanLine-currentScanLine,forward)
currentScanLine=firstScanLine
]
let blockBase=block+(size ScanHead/16)
@ScanCBHead=0
@ScanTime=Time
StepCommand=forward?CommandFORWARD,CommandBACK
let scanHead=table [ 0;CommandDELAY;0] //funny first time bug
scanHead!0=0
scanHead!2=StatusINUSE
for i=0 to nBlocks-1 do NewReadCommand(block,i,lv scanHead)
@ScanCBHead=scanHead
resultis block
]

and JasmineScanClose(s) be
[
@ScanTime=0
[ @ScanCBHead=0] repeatuntil JasmineReadLine(s,false) eq 0
@ScanCBHead=0//this is foolish, but be safe
@ScanTime=Time
]

and JasmineReadLine(s,continue;numargs na) = valof
[
if na ls 2 then continue=true
if s>>ScanHead.nScanLines le 0 then continue=false

let thisBlock=s>>ScanHead.nextBlock
let lastBlock=(thisBlock eq 0)?(s>>ScanHead.nBlocks-1),thisBlock-1
let nCBsPerBlock=s>>ScanHead.nCBsPerBlock
let lastBlockptr=s>>ScanHead.firstCB+lastBlock*nCBsPerBlock*(size ScanCB/16)
if lastBlockptr>>ScanCB.status eq StatusFREE then //queue up new command
if continue then NewReadCommand(s,lastBlock)
let thisBlockptr=s>>ScanHead.firstCB+thisBlock*nCBsPerBlock*(size ScanCB/16)
let thisBlockend=thisBlockptr+(nCBsPerBlock-1)*(size ScanCB/16)
while (thisBlockend>>ScanCB.status ge 0)&
(@ScanCBHead ne 0) do Idle()

let nDiscards = 0
if thisBlockptr>>ScanCB.buffer eq 0 then//was a discard
[thisBlockptr>>ScanCB.status=StatusFREE
thisBlockptr=thisBlockptr + (size ScanCB/16)
nDiscards=1
]
//if user has changed ReadMode or SkipSamples or Window without calling
//JasmineScanInit, then we’re in trouble
if nCBsPerBlock ne (ReadMode+SkipSamples+nDiscards) then
CallSwat("missing JasmineScanInit")

let blockStatus=thisBlockptr>>ScanCB.status

s>>ScanHead.nextBlock=(thisBlock eq (s>>ScanHead.nBlocks-1))?0,thisBlock+1
thisBlockptr>>ScanCB.status = StatusFREE

if blockStatus eq StatusDONE then
[if firstScanLine ge 0 then currentScanLine=currentScanLine+SkipSamples+1
s>>ScanHead.nScanLines=s>>ScanHead.nScanLines-(SkipSamples+1)
resultis thisBlockptr>>ScanCB.buffer
]

unless continue do resultis 0

if blockStatus eq StatusDATALATE then
[Late=Late+1//CallSwat("DataLate")
//
@ScanCBHead=thisBlockend>>ScanCB.link
let dummy = table[ 0;CommandDELAY;0] //funny first time bug
dummy!0=thisBlockend>>ScanCB.link
dummy!2=StatusINUSE
@ScanCBHead=dummy
resultis JasmineReadLine(s)//thisBlockptr>>ScanCB.buffer
//
resultis thisBlockptr>>ScanCB.buffer
]

Bad=Bad+1//CallSwat("Bad")
resultis 0//NextScanLine(s)
]

and ScannerOn() be
[
let savedEmu=@#177740;let savedDWT=@#177751
SetBLV(ScannerTaskBits);StartIO(#100000)
@#177740=savedEmu;@#177751=savedDWT
]
and ScannerOff() be
[
let savedEmu=@#177740;let savedDWT=@#177751
SetBLV(#177776);StartIO(#100000)
@#177740=savedEmu;@#177751=savedDWT
]
and JasmineFinish(code) be
[
ScannerOff()
@lvUserFinishProc=savedUFP
@lvSwatContextProc=savedSCP
for i=0 to 15 do #177740!i = 0
finish
]

and NewReadCommand(block,num,scanHead;numargs na) be
[
if na ls 3 then scanHead = ScanCBHead

let nDiscardBytes=(nDiscardElements/(SkipSamples+1))𫙰//even
let nBytes = ((nElements+SkipSamples)/(SkipSamples + 1)+1)𫙰//even
let nWords = (nBytes+1)/2
let nCBsPerBlock=block>>ScanHead.nCBsPerBlock
let base=block>>ScanHead.firstCB + num*nCBsPerBlock*(size ScanCB/16)
let bufferBase = block>>ScanHead.firstCB +
(block>>ScanHead.nBlocks)*nCBsPerBlock*(size ScanCB/16)

let thisCB=base
if nDiscardBytes gr 0 then
[thisCB>>ScanCB.link = thisCB + (size ScanCB/16)
thisCB>>ScanCB.command = CommandREAD
thisCB>>ScanCB.status = nDiscardBytes
thisCB>>ScanCB.buffer = 0
thisCB=thisCB + (size ScanCB/16)
]

thisCB>>ScanCB.link = thisCB + (size ScanCB/16)
thisCB>>ScanCB.command = CommandREAD
thisCB>>ScanCB.status = nBytes
thisCB>>ScanCB.buffer = bufferBase + num*nWords

if ReadMode gr 1 then
[for y=0 to SkipSamples do
[thisCB=thisCB + (size ScanCB/16)
thisCB>>ScanCB.link = thisCB + (size ScanCB/16)
thisCB>>ScanCB.command = StepCommand
thisCB>>ScanCB.status = StatusINUSE
]

if ReadMode gr 2 then
[thisCB=thisCB + (size ScanCB/16)
thisCB>>ScanCB.link = thisCB + (size ScanCB/16)
thisCB>>ScanCB.command = CommandDELAY
thisCB>>ScanCB.status = StatusINUSE
]
]
thisCB>>ScanCB.link = 0

let link=scanHead
//the following code doesn’t work:
//
until @link eq 0 do link = @link
//because if link eq scanHead, then
//between the test for "eq 0" and the assignment, scanHead may be zeroed
//by the ucode, causing us to add a chain through 77400 (@0)
[let newLink=@link
if newLink eq 0 then break
if newLink!2 ls 0 then //possible infinite loop bug
[
let first=@scanHead
let link = first
[let newLink = @link
if newLink eq 0 then break
if newLink eq first then
[CallSwat("Bug")
Bug=Bug+1
@link=0
break
]
link = newLink
] repeat
]
link=newLink
] repeat
@link = base
if @scanHead eq 0 then @scanHead=base
]

and JasmineStep(nSteps,forwardBoolean) be
[
let reversed=nSteps ls 0
if reversed then
[forwardBoolean = not forwardBoolean
nSteps=-nSteps
]
let v=vec (size ScanCB/16)
v>>ScanCB.link=0
if forward ne forwardBoolean then nSteps=nSteps+StepperSlip
v>>ScanCB.command=forwardBoolean?CommandFORWARD,CommandBACK
until @ScanCBHead eq 0 do [ ]
@ScanTime=StepTime
for i=1 to nSteps do
[v>>ScanCB.status = StatusINUSE
@ScanCBHead=v
until v>>ScanCB.status eq StatusDONE do [ ]
]
if reversed then //set forward, take up slack
for i=1 to StepperSlip do
[v>>ScanCB.command=forwardBoolean?CommandBACK,CommandFORWARD
v>>ScanCB.status = StatusINUSE
@ScanCBHead=v
until v>>ScanCB.status eq StatusDONE do [ ]
]
forward=reversed?(not forwardBoolean),forwardBoolean
@ScanTime=Time
]

and Init() be [ SendCommand(LOAD);SendCommand(START)]

and JasmineMotorOff() be SendCommand(MOTORCTL,4)

and InitStartCommand() be
[ @StartCommand=0
StartCommand>>dataOut.command=START
StartCommand>>dataOut.data=SkipSamples
]

and SendCommand(command,value;numargs na) be
[
if na ls 2 then value=0
let dataWord=0
dataWord<<dataOut.command=command
dataWord<<dataOut.data=value
Pulse(dataWord)
]
and Pulse(data) be
[ let t=table
[#42406//sta 0,@.+6
#24406//lda 1,.+6
#107000//add 0,1
#46403//sta 1,@.+3
#42402//sta 0,@.+2
#1401//jmp 1,3
#177016//dataout
#200
]
Pulse=t
Pulse(data)
]