//
// Routine to read a MU binary into core
// last edited 21-MAR-75 12:15
//

external	// entry procedures
[	ReadMU
]

external	// entry statics
[	MuSeqNo
]

external
[	GETS; ENDOFS; BSTORE
]

structure [ lh byte; rh byte ]

structure BS: [ length byte; char↑1,255 byte ]

manifest
	nmems = 3

static
[	stream
	memnos
	MuSeqNo
	W2ram
	Wcon
	DefName
]


let ReadMU(strm, writeram, writecon, definename; numargs n) = valof
// strm is a word-oriented input stream
// writeram(addr, hi, lo) writes into RAM
// writecon(addr, value) writes constant
// definename(addr, string, memid) defines name
// returns 0 if OK, otherwise error string
[	stream = strm
	W2ram = ((n ls 2) % (writeram eq 0)? noop, writeram)
	Wcon = ((n ls 3) % (writecon eq 0)? noop, writecon)
	DefName = ((n ls 4) % (definename eq 0)? noop, definename)
	let curmemx, curaddr = -1, 0
	let memnames = vec nmems
	   memnames!0 = "R"
	   memnames!1 = "CONSTANT"
	   memnames!2 = "INSTRUCTION"
	let memwidths = table[ 16;16;32 ]
	let memsizes = table[ 63;255;-1 ]
	let mnos = vec nmems
	memnos = mnos
	BSTORE(memnos, -1, nmems)
  [	if ENDOFS(stream) then resultis "Unexpected end of stream"
	let blocktype = GETS(stream)
	switchon blocktype into
	 [ case 0:	// end of data
	      resultis 0
	   case 2:	// set address
	    [ let memno = GETS(stream)
	      let addr = GETS(stream)
	      let memx = findmem(memno)
	      switchon memx into
	       [ case 1: [ curaddr = fixCONaddr(addr); endcase ]
		 case 2: [ curaddr = fixINSaddr(addr); endcase ]
		 default: resultis "Bad memory #"
	       ]
	      curmemx = memx
	      endcase
	    ]
	   case 1:	// data word
	    [ MuSeqNo = GETS(stream)
	      switchon curmemx into
	       [ case 1: [ readCONdata(curaddr); endcase ]
		 case 2: [ readINSdata(curaddr); endcase ]
		 default: resultis "Data for undefined memory"
	       ]
	      curaddr = curaddr+1
	      endcase
	    ]
	   case 4:	// define memory
	    [ let memno = GETS(stream)
	      let width = GETS(stream)
	      let name = vec 128
	      readname(name)
	      for j = 0 to nmems-1 do
	         if strequal(name, memnames!j) then
		  [ if width ne memwidths!j then
		       resultis "Bad width"
		    memnos!j = memno
		    endcase
		  ]
	      resultis "Bad memory name"
	    ]
	   case 5:	// define symbol
	    [ let memno = GETS(stream)
	      let val = GETS(stream)
	      let name = vec 128
	      readname(name)
	      let memx = findmem(memno)
	      if memx eq -1 then resultis "Bad memory #"
	      DefName(val, name, (table[$R;$C;$I])!memx)
	      endcase
	    ]
	   default: resultis "Invalid block type"
	 ]
  ] repeat
]

and noop(x, y, z) be [ ]

and findmem(memno) = valof
[	for j = 0 to nmems-1 do
	   if memnos!j eq memno then resultis j
	resultis -1
]

and readname(strg) be
[	let j = 0
  [	let wd = GETS(stream)
	if wd<<lh eq 0 break
	j = j+1
	strg>>BS.char↑j = wd<<lh
	if wd<<rh eq 0 break
	j = j+1
	strg>>BS.char↑j = wd<<rh
  ] repeat
	strg>>BS.length = j
]

and strequal(str1, str2) = valof
[	for i = 1 to str1>>BS.length do
	   if str1>>BS.char↑i ne str2>>BS.char↑i resultis false
	resultis true
]

and revbits(x, n, mask) = valof	// reverse low n bits of x
[	let y = 0
	for j = 1 to n do
	 [ y = y lshift 1 + (x & mask)
	   x = x rshift 1
	 ]
	resultis y
]

and fixCONaddr(addr) = valof
[	structure CA: [ blank bit 8; a bit 3; b bit 4; c bit 1 ]
	resultis
	   revbits(addr<<CA.b, 4, 1) lshift 4 +
	   addr<<CA.a lshift 1 +
	   addr<<CA.c
]

and fixINSaddr(addr) = valof
[	resultis
	   (addr&#177400) +
	   revbits((not addr)&#377, 8, 1)
]

and readCONdata(addr) be
	Wcon(addr, not GETS(stream))

and readINSdata(addr) be
[	// data word is reversed by nibbles (ugh)
	let wd0 = revbits(GETS(stream), 4, #10421)
	let wd1 = revbits(GETS(stream), 4, #10421)
	W2ram(addr, wd0, wd1)
]