// ReadMB.bcpl -- Read MB file
// Last modified June 26, 1985  3:38 PM by Rumph
  // taken from LoadMB.bcpl
  // Last modified June 25, 1982  6:18 PM by Taft

get "Streams.d"
get "AltoFileSys.d"

external
[
// incoming procedures
SetupReadParam; ReadParam; EvalParam
OpenFile; Closes; ReadBlock; WriteBlock; FilePos; SetFilePos; GetCompleteFa
Ws; Gets; Puts; Resets; ReadLeaderPage; TruncateDiskStream
PutTemplate; Enqueue
Zero; MoveBlock; Usc; CallSwat; ReadCalendar; Allocate

// incoming statics
keys; dsp; sysZone
]


structure MBName ↑0,0 byte  // terminates on null byte
structure String [ length byte; char ↑1,1 byte ]

//----------------------------------------------------------------------------
structure MB:		// block format in MB file
//----------------------------------------------------------------------------
[
word0 word =
   [
   blank byte		// used in Midas MB files, but not in normal ones
   type byte		// block type
   ]
data:
   [
   sourceLine word	// source line number
   value ↑0,0 word	// left-adjusted data
   ] =
memory:
   [
   memoryNum word	// memory number
   location word	// first location in memory
   ] =
fixup:
   [
   memoryNum word	// memory number
   location word	// location in memory
   firstBit byte	// field to be stored into
   lastBit byte
   value word		// value to be stored
   ] =
memoryName:
   [
   memoryNum word	// memory number
   width word		// memory width
   name @MBName		// memory name
   ] =
address:
   [
   memoryNum word	// memory number
   value word		// address value
   name @MBName		// address symbol name
   ] =
undefined:
   [
   memoryNum word	// memory number
   location word	// location in memory
   firstBit byte	// field to be stored into
   lastBit byte
   name @MBName		// external symbol name
   ]
]

manifest
[
// block types
mbEnd = 0		// end of MB file
mbData = 1		// store memory data and increment location
mbMemory = 2		// set memory number and location
mbFixup = 3		// forward reference fixup
mbMemoryName = 4	// correlate memory name and number
mbAddress = 5		// define address symbol
mbUndefined = 6		// external reference

// block lengths
lenMBData = offset MB.data.value/16  // + size of value
lenMBMemory = offset MB.memory.location/16 +1
lenMBFixup = offset MB.fixup.value/16 +1
lenMBMemoryName = offset MB.memoryName.name/16  // + size of name
lenMBAddress = offset MB.address.name/16  // + size of name
lenMBUndefined = offset MB.undefined.name/16  // + size of name

maxMemoryNum = 50
maxLenName = 127
]

//----------------------------------------------------------------------------
structure IM:		// IM word, in MB file only
//----------------------------------------------------------------------------
[
word0 word =
   [ RSTK bit 4; ALUF bit 4; BSEL bit 3; LC bit 3; ASEL01 bit 2 ] =
   [ RSTK0 bit 1; rest0 bit 15 ]
word1 word =
   [ ASEL2 bit 1; BLK bit 1; FF bit 8; JCN05 bit 6 ] =
   [ ASEL2BLK bit 2; rest1 bit 14 ]
word2 word =
   [ JCN67 bit 2; PE020 bit 1; PE2131 bit 1; blank bit 12 ]
word3 word =
   [ blank bit 4; absoluteAddr bit 12 ]
]

//----------------------------------------------------------------------------
structure IFUM:		// IFUM word, in MB file and in Item
//----------------------------------------------------------------------------
[
word0 word =
   [ blank bit 5; PA bit 1; IFAD bit 10 = [ IFAD01 bit 2; IFAD29 bit 8 ] ]
word1 word =
   [
   SGN bit 1; IPAR bit 3 = [ P0 bit; P1 bit; P2 bit ]; LEN bit 2;
   RBASEB bit 1; MEMB bit 3 = [ MEMB0 bit 1; MEMB12 bit 2 ];
   TYPE bit 2; N bit 4
   ]
]

manifest
[
// masks defining contribution of each word to each parity bit
ifumW0P0 = 1400B; ifumW0P1 = 377B; ifumW0P2 = 2000B
ifumW1P0 = 317B; ifumW1P1 = 0B; ifumW1P2 = 107460B
]

//----------------------------------------------------------------------------
structure RM:		// RM word, in MB file and in Item
//----------------------------------------------------------------------------
[
word0 word
]

//----------------------------------------------------------------------------
structure MesaString: [ length word; maxLength word; char↑0,1 byte ]
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
structure InFile:	// Input file descriptor
//----------------------------------------------------------------------------
[
next word
name word		// -> complete file name string
cfa @CFA
startAddr word		// microcode starting address for this overlay
]
manifest lenInFile = size InFile/16

manifest
[
maxItems = 4096+1024+256+1  // IM+IFUM+RM+End, enough for one full image
]


//----------------------------------------------------------------------------
let ReadMB() be
//----------------------------------------------------------------------------
[
Ws("*nReadMB of June 26, 1985")

let stringVec, switchVec = vec 50, vec 50
SetupReadParam(stringVec, switchVec)

let streamOut = 0
let outName = vec 50
let inFileQ = vec 1; inFileQ!0 = 0

   [ // repeat
   if ReadParam(0, 0, 0, 0, true) eq -1 then break
   switchon switchVec!0 into
      [
      case 0:
         switchVec!1 = $I  // fall thru (treat as input file name)
      case 1:
         switchon switchVec!1 into
            [
            case $I: case $i:
               [
               let inName = vec 50
               let s = EvalParam(stringVec, "IW", "Input MB file name: ", inName)
               let inFile = Allocate(sysZone, lenInFile)
               let l = inName>>String.length rshift 1 +1
               inFile>>InFile.name = Allocate(sysZone, l)
               MoveBlock(inFile>>InFile.name, inName, l)
               GetCompleteFa(s, lv inFile>>InFile.cfa)
               Closes(s)
               inFile>>InFile.startAddr = -1  // haven't seen start address yet
               Enqueue(inFileQ, inFile)
               endcase
               ]
            case $O: case $o:
               streamOut = EvalParam(stringVec, "OW", "Output file name: ", outName)
               endcase
            default:
               Fail("undefined local switch /$C", switchVec!1)
               ]
         endcase
      default:
         Fail("multiple local switches")
      ]
   ] repeat

if inFileQ!0 eq 0 then Fail("no input file specified")

if streamOut eq 0 then
   [  // default output filename to match last input filename
   MoveBlock(outName, (inFileQ!1)>>InFile.name, 50)
   for i = 1 to outName>>String.length do
      if outName>>String.char↑i eq $. then outName>>String.length = i-1
   let i = outName>>String.length
   outName>>String.char↑(i+1) = $.
   outName>>String.char↑(i+2) = $l
   outName>>String.char↑(i+3) = $s
   outName>>String.length = i+3
   streamOut = OpenFile(outName, ksTypeWriteOnly, charItem)
   ]

let inFile = inFileQ!0
while inFile ne 0 do
   [
   let streamMB = OpenFile(inFile>>InFile.name, ksTypeReadOnly, charItem, 0,
    lv inFile>>InFile.cfa.fp)
   if streamMB eq 0 then CallSwat("Failed to open existing file")
   ReadMBFile(streamMB, streamOut)
   Closes(streamMB)
   inFile = inFile>>InFile.next
   ]

let pos = vec 1; FilePos(streamOut, pos)
Closes(streamOut)
PutTemplate(dsp, "*n$ED bytes written on $S.", pos, outName)
finish
]

//----------------------------------------------------------------------------
and Fail(string, par1, par2, par3) be
//----------------------------------------------------------------------------
[
Ws("*nCommand error: ")
PutTemplate(dsp, string, par1, par2, par3)
Ws("*n")
abort
]

//----------------------------------------------------------------------------
and ReadMBFile(stream, streamOut) be
//----------------------------------------------------------------------------
[
manifest lenBuf = 1024
manifest minLenBuf = 128
let mbBuf = vec lenBuf
let mb = mbBuf
let endBuf = mbBuf
let memoryNumIM = -2
let memoryNumIFUM = -2
let memoryNumRM = -2
let memoryNum = -1
let addr = nil
let memoryWidths = vec maxMemoryNum; Zero(memoryWidths, maxMemoryNum)
let name = vec maxLenName
let addrValue = 0

   [ // repeat
   if (endBuf-mb) ls minLenBuf then
      [
      let wordsRemaining = endBuf-mb
      MoveBlock(mbBuf, mb, wordsRemaining)
      mb = mbBuf
      wordsRemaining = wordsRemaining +
       ReadBlock(stream, mb+wordsRemaining, lenBuf-wordsRemaining)
      if wordsRemaining le 0 then CallSwat("Missing End block in MB file")
      endBuf = mbBuf+wordsRemaining
      ]

   switchon mb>>MB.type into
      [
      case mbEnd:
         break

      case mbData:
         [
         addr = addr+1
         mb = mb + lenMBData + memoryWidths!memoryNum
         endcase
         ]

      case mbMemory:
         memoryNum = mb>>MB.memory.memoryNum
         if memoryWidths!memoryNum eq 0 then
            CallSwat("Undefined memory")
         addr = mb>>MB.memory.location
         mb = mb + lenMBMemory
         endcase

      case mbFixup:
         CallSwat("Fixup block encountered in MB file")

      case mbMemoryName:
         [
         let newMemoryNum = mb>>MB.memoryName.memoryNum
         if Usc(newMemoryNum, maxMemoryNum) gr 0 then
            CallSwat("Memory number out of bounds")
         memoryWidths!newMemoryNum = (mb>>MB.memoryName.width +15) rshift 4
         let lenName = ReadName(lv mb>>MB.memoryName.name, name)
         PutTemplate(streamOut, "$S*n", name)
         mb = mb + lenMBMemoryName + lenName
         endcase
         ]

      case mbAddress:
         addrValue = mb>>MB.address.value
         mb = mb + lenMBAddress + ReadName(lv mb>>MB.address.name, name)
         PutTemplate(streamOut, "   $S:*t$UO*n", name, addrValue)
         endcase

      case mbUndefined:
         CallSwat("Undefined symbol block encountered in MB file")

      default:
         CallSwat("Unknown block type in MB file")
      ]
   ] repeat
]

//----------------------------------------------------------------------------
and ReadName(mbName, stringName) = valof
//----------------------------------------------------------------------------
// Converts from the MB name format to a normal string.
// Returns the number of words occupied by mbName.
[
let i = 0
   [ // repeat
   let char = mbName>>MBName↑i
   if char eq 0 then break
   stringName>>String.char↑(i+1) = char
   i = i+1
   ] repeat
stringName>>String.length = i
resultis i rshift 1 +1
]

//----------------------------------------------------------------------------
and StringEq(s1, s2) = valof
//----------------------------------------------------------------------------
[
let length = s1>>String.length
if length ne s2>>String.length resultis false
for i = 1 to length do
   [
   let c1, c2 = s1>>String.char↑i, s2>>String.char↑i
   if c1 ne c2 then
      unless (c1 xor 40B) eq c2 &
       Usc((c1 & 137B)-$A, $Z-$A) le 0 resultis false
   ]
resultis true
]

//----------------------------------------------------------------------------
and MesaFromBCPLString(mesaString, bcplString) = valof
//----------------------------------------------------------------------------
// Returns the number of words occupied by the mesaString
[
let length = bcplString>>String.length
mesaString>>MesaString.length = length
mesaString>>MesaString.maxLength = (length+1) & -2
for i = 1 to length do mesaString>>MesaString.char↑(i-1) = bcplString>>String.char↑i
resultis (offset MesaString.char↑0 / 16) + (length+1) rshift 1
]