// MDinit.bcpl -- process command line
// last edited March 25, 1981  4:01 PM

	get "mddecl.d"
	get "streams.d"
	get "altofilesys.d"
	get "altodefs.d"

external [	// defined here
	Init	// () -> sources
	// Switches and parameters from command line
	DebugFlag
	DebugFirstLoc
	DebugLastLoc
	IgnoreOnPage
	Kludge	// see MDSCAN
	ListAbs
	ListingLevel
	MapChart
	MapIM
	MapRM
	MapOccupied
	ListSymbols
	TraceStorage
	Version
	Xternal
	// Other statics
	VersionString
	StartDTString
	OutputSource
	MBSource
	ScratchSource
	DeleteScratch
]

external [
// OS
	CreateDisplayStream; ShowDisplayStream
	OpenFile
	CreateDiskStream
	Closes; Endofs; Gets; Puts; Resets; Wss
	Allocate; Free; MoveBlock; Zero
	fpComCm; fpSysDir
	dsp
	sysFont; sysZone
// GP
	SetupReadParam; ReadParam; EvalParam
// MDI
	LookupEntries
// TimeIO
	CONVUDT
// Template
	PutTemplate
// MDmain
	Err
	StoreString
	MaxBlock
	@OutputS; @MBS
	@TempZone; @Zone
]


manifest [
	nOutputs = 8
	ldspLines = 6
	ldspLinesSmall = 3
	ldspBits = 5000b
	ldspBitsSmall = 2000b
]


static [
	// Switches and parameters from command line
	DebugFlag = 0
	DebugFirstLoc = 0
	DebugLastLoc = IMsize-1
	IgnoreOnPage = false
	Kludge = false
	LargeProgram = false
	ListAbs = false
	ListingLevel = listFull
	MapChart = false
	MapIM = false
	MapRM = false
	MapOccupied = false
	ListSymbols = false
	TraceStorage = false
	Version = -1
	Xternal = false
	// Other statics
	VersionString
	StartDTString
	OutputSource = 0
	MBSource = 0
	ScratchSource = 0
	DeleteScratch = false
]

let Init() = valof
[	AllocateDisplay(ldspLines, ldspBits, TempZone)

	VersionString = StoreString("*NMicroD 9.14 (OS 16) of March 25, 1981*N", Zone)
	Wss(dsp, VersionString)
	StartDTString = Allocate(Zone, 10)
	CONVUDT(StartDTString, 0)

//Read and parse command line
	let inExt = ".DIB"
	// Following must be kept in order, with scratchExt last
	let lstExt = ".DLS"
	let outExt = ".MB"
	let mapExt = ".csmap"
	let regsExt = ".regs"
	let occExt = "Occupied.mc"
	let chartExt = ".cschart"
	let absExt = ".absDLS"
	let scratchExt = ""

	let scratchName = "SWATEE"

	let ComCmVec = vec 120
	let StrVec,FlagVec = vec 100, vec 16
	let OutVec = vec 100
	@OutVec = 0
	let ScratchVec = vec 100
	@ScratchVec = 0
	let S = OpenFile("Com.Cm",ksTypeReadOnly,charItem,0,fpComCm,0,TempZone)
	let firstSource = 0
	let lastSource = lv firstSource-(offset Source.next/16)
	let NS = 0

//Read and parse global flags
	SetupReadParam(StrVec, FlagVec, S, FlagVec)
	GlobalFlags(FlagVec)

//Now handle names with local flags
	let lastname = nil
	while ReadParam(0, -1, StrVec, FlagVec) ne -1 do
	[ if (StrVec!0 eq 1) & (StrVec!1 eq $~) then	// additional global flags, not a file
	  [ GlobalFlags(FlagVec); loop ]
	  let lflag = ListingLevel
	  let nf = FlagVec!0
	  for i = 1 to nf do
	   switchon FlagVec!i & 137B into
	  [ case $A: lflag = listAbsOnly; endcase
	    case $C: lflag = listConcise; endcase
	    case $D: switchon (i eq nf? $*S, valof [ i = i+1; resultis FlagVec!i & 137B ]) into
		[ case $*S: DebugFlag = EvalParam(StrVec, $B, "Debug: "); endcase
		  case $F: DebugFirstLoc = EvalParam(StrVec, $B, "First loc: "); endcase
		  case $L: DebugLastLoc = EvalParam(StrVec, $B, "Last loc: "); endcase
		  default: Err(NonFatal, "Undefined switch: $US/D$C", StrVec, FlagVec!i)
		]
		goto notsource
	    case $L: lflag = listFull; endcase
	    case $N: lflag = listNotIM; endcase
	    case $O:
	      if nf ne 1 then Err(Fatal, "/O with another switch")
	      EvalParam(StrVec, $P, 0, OutVec)
	      goto notsource
	    case $V:
	      if nf ne 1 then Err(Fatal, "/V with another switch")
	      Version = EvalParam(StrVec, $B, "Version: ")
	      goto notsource
	    case $Z:
	      if nf ne 1 then Err(Fatal, "/Z with another switch")
	      EvalParam(StrVec, $P, 0, ScratchVec)
	      goto notsource
	    default: Err(NonFatal, "Undefined switch: $US/$C", StrVec, FlagVec!i)
	  ]
 	  [ EvalParam(StrVec, $P)
 	    let source = Extend(StrVec, inExt, false)
 	    lastname = source>>Source.pName
	    source>>Source.lflag = lflag
 	    lastSource>>Source.next = source
	    lastSource = source
 	    NS = NS+1
	  ]
notsource:
	]

	if NS eq 0 then Err(Fatal, "No input files")
	if @OutVec eq 0 then MoveBlock(OutVec, lastname, 100)
	if @ScratchVec eq 0 then MoveBlock(ScratchVec, scratchName, 100)
	let nfiles = nOutputs+NS
	let ff = firstSource
	let names = Allocate(TempZone, nfiles)
	let osv = vec nOutputs
	for i = nOutputs-1 by -1 to 0 do
	[ let s = Extend((i eq nOutputs-1? ScratchVec, OutVec), (lv lstExt)!i, true)
	  osv!i = s
	  s>>Source.next = ff
	  ff = s
	]
	let prvec = Allocate(Zone, nfiles*(lFP+1))
	let i = 0
	while ff ne 0 do
	[ names!i = ff>>Source.pName
	  ff>>Source.pFP = prvec+i*(lFP+1)+1
	  i, ff = i+1, ff>>Source.next
	]
	OutputSource = osv!0
	test ListingLevel eq -2  ifso names!1 = 0  ifnot MBSource = osv!1
	test MapIM  ifso MapIM = osv!2  ifnot names!2 = 0
	test MapRM  ifso MapRM = osv!3  ifnot names!3 = 0
	test MapOccupied  ifso MapOccupied = osv!4  ifnot names!4 = 0
	test MapChart  ifso MapChart = osv!5  ifnot names!5 = 0
	test ListAbs  ifso ListAbs = osv!6  ifnot names!6 = 0
	test ListingLevel eq -2  ifso names!7 = 0  ifnot ScratchSource = osv!7
	let DirS = CreateDiskStream(fpSysDir, ksTypeReadOnly, wordItem, 0, 0, TempZone)
	let bufsize = MaxBlock(TempZone)
	let buf = Allocate(TempZone, bufsize)
	LookupEntries(DirS, names, prvec, nfiles, true, buf, bufsize)
	Free(TempZone, buf, bufsize)
	Closes(DirS)
	let s = firstSource
	while s ne 0 do
	[ let pDE = s>>Source.pFP-1
	  if @pDE eq 0 then Err(PassFatal, "Can't open input file $S", s>>Source.pName)
	  s = s>>Source.next
	]
	OutputS = CheckOutput(osv!0, charItem, Zone)
	MBS = (names!1 eq 0? 0, CheckOutput(osv!1, wordItem, Zone))
	for i = 2 to nOutputs-1 do
	 if names!i ne 0 then
	  CheckOutput(osv!i, (i eq nOutputs-1? wordItem, charItem), 0)

	ShowDisplayStream(dsp, DSdelete)
	Free(sysZone, dsp)	// Free the temporary display stream
	test LargeProgram
	 ifso AllocateDisplay(ldspLinesSmall, ldspBitsSmall, Zone)
	 ifnot AllocateDisplay(ldspLines, ldspBits, Zone)
	Wss(dsp, VersionString)
	PutTemplate(OutputS, "$S  at $S*N*N", VersionString, StartDTString)
	Resets(S)
	until Endofs(S) do Puts(OutputS, Gets(S))
	Wss(OutputS, "*N*N")
	resultis firstSource
]

and GlobalFlags(flagVec) be
// A subroutine so that one can add ~/f onto a command line
[	for i = 1 to flagVec!0 do
	 switchon flagVec!i & 137B into
	 [ case $A: ListingLevel = listAbsOnly; endcase	// Absolute only
	   case $C: ListingLevel = listConcise; endcase	// Concise, no octal
	   case $D: DebugFlag = -1; endcase	// Debug MicroD
	   case $E: MapChart = true; endcase	// chart Every location
	   case $H: ListAbs = true; endcase	// abs listing for Hardware debugging
	   case $I: IgnoreOnPage = true; endcase	// Ignore OnPage directives
	   case $K: Kludge = true; endcase
	   case $L: LargeProgram = true; endcase	// large program, small display
	   case $M: MapIM = true; endcase	// Map of IM by page
	   case $N: ListingLevel = listNotIM; endcase	// No IM listing
	   case $O: MapOccupied = true; endcase	// write Occupied location map
	   case $P: ListingLevel = listPrintMB; endcase	// just Print the .MBs
	   case $R: MapRM = true; endcase	// RM map
	   case $S: ListSymbols = true; endcase	// Symbols for all memories
	   case $T: TraceStorage = true; endcase	// Trace calls on allocator
	   case $X: Xternal = true; endcase	// allow eXternal references
	   default: Err(NonFatal, "Undefined global switch /$C", flagVec!i)
	 ]
]


and AllocateDisplay(nl, lbits, zone) be
[	let bits = Allocate(zone, lbits)
	let ds = CreateDisplayStream(nl, bits, lbits, sysFont)
	ShowDisplayStream(ds, DSalone)
	dsp = ds
]

and Extend(name, ext, force) = valof
[	let v = vec 100
	test ext>>BS.length eq 0
	ifso v = name	// scratch name
	ifnot
	[ MoveBlock(v, name, 100)
	  SetExt(v, ext, force)
	]
	let s = Allocate(Zone, lSource)
	s>>Source.pName = StoreString(v, Zone)
	resultis s
]

and SetExt(Str, Ext, force) be
[	let I = Str>>STRING.length
	for J = 1 to I do
	 if Str>>STRING.char↑J eq $. then
	[ unless force return
	  I = J-1; break
	]
	for J = 1 to Ext>>STRING.length do
	[ I = I+1; Str>>STRING.char↑I = Ext>>STRING.char↑J
	]
	Str>>STRING.length = I
]

and CheckOutput(s, item, zone) = valof
[	let name, dv = s>>Source.pName, s>>Source.pFP-1
	if @dv eq 0 then Closes(OpenFile(name, ksTypeWriteOnly, item, verLatestCreate, dv+1, 0, TempZone))
	if zone eq 0 resultis 0
	let st = CreateDiskStream(dv+1, ksTypeWriteOnly, item, 0, 0, zone)
	if st eq 0 then Err(Fatal, "Can't open output file $S", name)
	resultis st
]