// SCI.bcpl -- Translates Route output to SCI input format and writes a tape.
// Last modified April 28, 1983  7:33 PM by Boggs

get "SCI.decl"
get "Streams.d"

external
[
// outgoing procedure -- SCI
TSP

// incoming procedures -- SCI
WLFile; OtherFile
ReadToken; OutPut; FlushBlock

// incoming procedures -- packages
InitializeContext; Block; CallContextList
ExtractSubstring; ConcatenateStrings
InitPupLevel1; GetPartner; Enqueue; PutTemplate; ByteBlt
OpenLevel1Socket; OpenRTPSocket; CreateBSPStream
BSPWriteBlock; BSPReadBlock; BSPForceOutput

// incoming procedures -- OS
OpenFile; Closes; Puts; Gets
InitializeZone; AddToZone; Allocate; Free
Noop; CallSwat; MyFrame; Wss

// outgoing statics
lf; in; out; cpuType
token; board; numBlocks
block; blockCnt; record; recordCnt

// incoming statics -- OS
sysZone; dsp; keys
lvSysZone; lvIdle; lvUserFinishProc

// incoming statics -- Pup package
offsetBSPStr; lBSPSoc
]

manifest stackLimit = 335b

static
[
lf; in; out; cpuType
token; board; numBlocks
block; blockCnt; record; recordCnt

ts; savedUFP; versionText
]

//----------------------------------------------------------------------------------------
let SCI() be
//----------------------------------------------------------------------------------------
[
versionText = "SCI of April 28, 1983"
Wss(dsp, versionText)

savedUFP = @lvUserFinishProc; @lvUserFinishProc = FinishProc

// free storage
let freeBegin = @stackLimit
let freeEnd = MyFrame() -2000
let length = freeEnd-freeBegin
if length ugr 77776b then length = 77776b
@stackLimit = freeBegin + length
sysZone = InitializeZone(freeBegin, length)
@lvSysZone = sysZone

token = Allocate(sysZone, 128)		// input is parsed into tokens
record = Allocate(sysZone, 128)		// tokens are packed into records
block = Allocate(sysZone, 2048)		// records are packed into blocks

// get board name
in = keys
   [
   Wss(dsp, "*NBoard name (e.g. DCCP-rev-H (no extension)): ")
   ReadToken()
   ] repeatuntil token>>String.length ne 0
board = ExtractSubstring(token)

// open listing file
let lfName = ConcatenateStrings(token, ".ls")
lf = OpenFile(lfName, ksTypeWriteOnly, charItem)
if lf eq 0 then CallSwat("Failed to open listing file")
PutTemplate(dsp, "*NListing to file $S", lfName)
Free(sysZone, lfName)

let ctxQ = vec 1; ctxQ!0 = 0
@lvIdle = Block
InitPupLevel1(sysZone, ctxQ, 10)
Enqueue(ctxQ, InitializeContext(Allocate(sysZone, 1000), 1000, MainCtx))
CallContextList(ctxQ!0) repeat  //forever
]

//----------------------------------------------------------------------------------------
and FinishProc() be
//----------------------------------------------------------------------------------------
[
if lf ne 0 then Closes(lf)
@lvIdle = Noop
@lvUserFinishProc = savedUFP
]

//----------------------------------------------------------------------------------------
and MainCtx() be  //a context
//----------------------------------------------------------------------------------------
[
// open tape
let port = vec 3
   [
   Wss(dsp, "*NTape server name (<DEL> => no tape): ")
   ReadToken()
   test token>>String.length eq 0
      ifso [ port = 0; break ]
      ifnot if GetPartner(token, dsp, port, 0, tapeSocket) break
   ] repeat
if port ne 0 then
   [
   Wss(dsp, "*NVAX or PRIME format (V or P): ")
   cpuType = Gets(keys) & 137b; Puts(dsp, cpuType)
   let soc = Allocate(sysZone, lBSPSoc)
   OpenLevel1Socket(soc, 0, port)
   if OpenRTPSocket(soc) then ts = CreateBSPStream(soc)
   ]

// initialize the tape
TSP(cmdVersion, 1, versionText)
if ts ne 0 then
   [
   Wss(dsp, "*NTape unit number: ")
   ReadToken()
   let unit = token>>String.char↑1 - $0
   if token>>String.length eq 1 & unit ge 0 & unit le 1 then
      [ TSP(cmdOpenDrive, unit, ""); break ]
   ] repeat
TSP(cmdSetStatus, 1, 1600)

let v1 = "VOL1SCI                              D%C          1                            3"
let h1 = "HDR1SCI.DAT          SCI   00010001000100 83000 00000 000000DECFILE11A          "
let h2 = "HDR2D0204800079                     A             00                            "
let h3 = "HDR3004B010200000000000100000000000000000000000000000000000000000000            "
if cpuType eq $V then  //write ANSI header labels
   [
   TSP(cmdWriteRecord, v1, 1, 80)
   TSP(cmdWriteRecord, h1, 1, 80)
   TSP(cmdWriteRecord, h2, 1, 80)
   TSP(cmdWriteRecord, h3, 1, 80)
   TSP(cmdWriteEOF)
   ]

// do the work
out = lv OutPut - offset ST.puts/16
WLFile()
OtherFile()
FlushBlock()

if cpuType eq $V then  //write ANSI trailer labels
   [
   TSP(cmdWriteEOF)
   ByteBlt(h1, 1, "EOF", 1, 3)
   ByteBlt(h2, 1, "EOF", 1, 3)
   ByteBlt(h3, 1, "EOF", 1, 3)
   h1>>String.char↑59 = numBlocks/10 +$0
   h1>>String.char↑60 = numBlocks rem 10 +$0
   TSP(cmdWriteRecord, h1, 1, 80)
   TSP(cmdWriteRecord, h2, 1, 80)
   TSP(cmdWriteRecord, h3, 1, 80)
   ]

TSP(cmdWriteEOF)
TSP(cmdWriteEOF)
TSP(cmdUnload)
TSP(cmdCloseDrive)
if ts ne 0 then Closes(ts)
finish
]

//----------------------------------------------------------------------------------------
and TSP(cmd, arg0, arg1, arg2, arg3; numargs na) = valof
//----------------------------------------------------------------------------------------
// Tape Server Protocol
[
if ts eq 0 resultis true

// send command
let msg = vec 10; msg!0 = 2; msg!1 = cmd
switchon cmd into
   [
   case cmdRewind: case cmdUnload: case cmdCloseDrive: case cmdWriteEOF:
      [
      BSPWriteBlock(ts, msg, 0, 4)
      endcase
      ]
   case cmdVersion: case cmdOpenDrive:
      [
      let sl = arg1>>String.length rshift 1 +1
      msg!0 = 3 + sl
      msg!2 = arg0
      BSPWriteBlock(ts, msg, 0, 6)
      BSPWriteBlock(ts, arg1, 0, sl lshift 1)
      endcase
      ]
   case cmdWriteRecord:
      [
      msg!0 = 3 + arg2 rshift 1
      msg!2 = arg2
      BSPWriteBlock(ts, msg, 0, 6)
      BSPWriteBlock(ts, arg0, arg1, arg2)
      endcase
      ]
   case cmdSetStatus:
      [
      msg!0 = 4
      msg!2 = arg0
      msg!3 = arg1
      BSPWriteBlock(ts, msg, 0, 8)
      endcase
      ]
   default: CallSwat("TSP: unknown command")
   ]
BSPForceOutput(ts-offsetBSPStr)

// get response
let yesNo = vec (4 + 128)  //length+type+code+status+string
BSPReadBlock(ts, yesNo, 0, 2)
BSPReadBlock(ts, yesNo+1, 0, (yesNo!0-1) lshift 1)

if cmd eq cmdVersion then
   [
   if yesNo!1 ne cmdVersion % yesNo!2 ne 0 then CallSwat("TSP version incompatible")
   PutTemplate(dsp, "*N$S", yesNo+3)
   resultis true
   ]
if yesNo!1 eq cmdYes resultis true
if yesNo!1 eq cmdNo then
   PutTemplate(dsp, "*N[No] cmd $O cause $O status $O $S", cmd, yesNo!2, yesNo!3, yesNo+4)
resultis false
]