// AltIOInit.bcpl -- Initialization for AltIO

//	Last modified March 14, 1981  3:25 PM

get "altio.decl"
get "altiomaxc2.decl"
get "pup0.decl"
get "streams.d"
get "AltoDefs.d"

external
[
//outgoing procedures
AltIOInit

//incoming procedures
GetFixed; MyFrame; CallersFrame
InitializeZone; Allocate; Free; Zero; MoveBlock
LogOpen; AltIOReset; AltIOFinish; OpenFile; CallSwat; FileLength
SysErr; Resets; ReadBlock; Closes; SetupReadParam; ReadParam
CreateDisplayStream; ShowDisplayStream; Enqueue
InitializeInterrupt; FindInterruptMask; Timer
InitializeContext; AltIOCommand; UpdateRegisterDisplay; KeySwitch
MaxcKeyboard; MaxcTerminal; MaxcWatcher; PutsWithCursor
MaxcTerminal1; DiabloError; CreateDiabloStream
LoadRam; Ws; Wss; Ding; Dismiss; PutTemplate; InitBcplRuntime
OutputSMI; OutputSMITest; StopMaxcForcefully; ClockInterrupt
ResetMemory; InputSMI; InitAltoEther; InitAltoImp; AltIOParityHandler
AltIOSwatProc; ZeroMaxcMem; ConfigureMemory

//outgoing statics
numPBI; debug; savedParityHandler; altIOSavedSCP
ctxQ; altIOSavedUFP; tDspLines; numMemCabs; stdMemConf; topStack; lenTopStack

//incoming statics
dsp; sysZone; lvSysZone; sysFont; fpSysFont; lvUserFinishProc
fpComCm; switchStartTenex; switchBoot; switchHalt; protected
loadFilename; silentBoot; sysDisk; versionText; lvAbortFlag
tDsp; hDsp; hShow; hNoShow; commandCtx; maxcKeysCtx
keysCtx; cursorDsp; DisplayPuts; diabloStr; bootDiskUnit
adrMTBS; adrDLSBS; RamImage; dlsOutputChar; lvSwatContextProc
maxcClock; lastTime; clockMask; ClockSecond
lenPup; lenPBI; pbiIQ; pbiFreeQ; ndbQ; pupCtx; lenPupCtx
etherNDB; impNDB
]

static
[
ctxQ; tDspLines; numMemCabs; stdMemConf
altIOSavedUFP; altIOSavedSCP
savedParityHandler = 0
lastShownStream = 0
numPBI = 0  //number of pbis to allocate initially
spyBuffer = 0
debug = false
switchZero = false
topStack; lenTopStack
]

manifest
[
endCode = #335
lenCDsp = 5000  //space for command display bitmap
lenTDsp = 17000  //space for maxc tty display bitmap
]

structure String: [ length byte; char↑1,255 byte ]

// ---------------------------------------------------------------------------
let AltIOInit() be
// ---------------------------------------------------------------------------
[
// Load the special Ram microcode
let res = LoadRam(RamImage, true)  //load and boot
if res ne 0 then CallSwat("LoadRam failed")
silentBoot = true
InitBcplRuntime()
@endCode = LoadRam  // Throw away LoadRam and RamImage

// First allocate some big blocks using GetFixed.  This ensures
// that we will have less than 32k left to pass to InitializeZone.
dsp = GetFixed(lenCDsp)  //command display window
tDsp = GetFixed(lenTDsp)  //maxc tty display window

// Turn remaining storage into a zone
topStack = MyFrame()-1000  //top-level stack during initialization
lenTopStack = CallersFrame()-topStack
sysZone = InitializeZone(@endCode, topStack-@endCode, SysErr,
 (debug? SysErr, 0))
@lvSysZone = sysZone
@endCode = topStack

// Reinitialize some things we Junta'ed away
LogOpen(sysZone)  //Junta closed the log but we want it open
altIOSavedUFP = @lvUserFinishProc
@lvUserFinishProc = AltIOFinish
altIOSavedSCP = @lvSwatContextProc
@lvSwatContextProc = AltIOSwatProc

// Read in the system font
let fontStr = OpenFile("SysFont.al", ksTypeReadOnly, 0, 0, fpSysFont)
if fontStr eq 0 then CallSwat("Can't open SysFont.al")
let fontSize = (FileLength(fontStr)+1) rshift 1
Resets(fontStr)
sysFont = Allocate(sysZone, fontSize)
ReadBlock(fontStr,sysFont, fontSize)
Closes(fontStr)
let fontHeight = (sysFont!0+1) & #177776
let lineWords = lDCB + 38*fontHeight
sysFont = sysFont+2

// Initialize the display
ShowNext(MakeBar(5, 0))  //10 scan lines of white at top
//2 lines for register display header
hDsp = CreateDisplayStream(2, Allocate(sysZone, 2*lineWords+20),
 2*lineWords+10, sysFont, 0, DSstopright)
ShowNext(hDsp)
//2 one-liners for updated display (show only the first)
hShow = CreateDisplayStream(1,
 Allocate(sysZone, lineWords+20), lineWords+10, sysFont, 0, DSstopright)
hNoShow = CreateDisplayStream(1,
 Allocate(sysZone, lineWords+20), lineWords+10, sysFont, 0, DSstopright)
ShowNext(hShow)
ShowNext(MakeBar(2, 0))  //4 scan lines of white
ShowNext(MakeBar(2, 1))  //4 scan lines of black
hNoShow>>DS.ldcb>>DCB.next = hShow>>DS.ldcb>>DCB.next

// AltIOInit (cont'd)

// Command display stream doesn't need a full bitmap
let cmdScanLines = 808-(10+2+3*fontHeight+8+4+lenTDsp/38+4)
dsp = CreateDisplayStream(cmdScanLines/fontHeight, dsp, lenCDsp-10, sysFont)
DisplayPuts = dsp>>ST.puts
dsp>>ST.puts = PutsWithCursor
ShowNext(dsp)
ShowNext(MakeBar(2,1))  //4 scan lines of black

// Maxc tty display stream does need a full bitmap
tDspLines = (lenTDsp-20)/lineWords
tDsp = CreateDisplayStream(tDspLines, tDsp, lenTDsp-10, sysFont, 0, DSnone)
ShowNext(tDsp)
ShowNext(MakeBar(2, 1))  //4 scan lines of black
PutTemplate(dsp, "$S - command window", versionText)
Wss(tDsp, "Maxc console terminal window*n")

// Initialize contexts
ctxQ = Allocate(sysZone, 2)
ctxQ!0 = 0
commandCtx = MakeContext(AltIOCommand, 1000)
maxcKeysCtx = MakeContext(MaxcKeyboard, 100)
keysCtx = commandCtx  //initially type to command window
cursorDsp = dsp
MakeContext(MaxcTerminal, 200)
MakeContext(MaxcTerminal1, 200)
MakeContext(UpdateRegisterDisplay, 200)
MakeContext(KeySwitch, 100)
MakeContext(MaxcWatcher, 500)

//initialize Diablo printer
diabloStr = CreateDiabloStream(0, 0, 0, 0, 25*6)
diabloStr>>ST.error = DiabloError

//initialize Pup package
InitAltIOPup()

//check for Maxc being powered on, and find out how much
//memory there is.
OutputSMITest(#16)  //put SMI in normal mode
StopMaxcForcefully()  //make sure the processor isn't running
if InputSMI(smiPPSTAT) ne 3 then
   Punt("*nProcessor or port not turned on, can't do anything")
let lastModuleOff = false
stdMemConf, numMemCabs = 0, 0
for physMod = 0 to 3 do
   [
   let logMod = nil
   test InputSMI(smiCABST+2*physMod) eq #17
      ifso
         [
         logMod = table [ 4; 0; 6; 2 ]!numMemCabs
         numMemCabs = numMemCabs+1
         if lastModuleOff then
            [
            Ws("*nUnusual memory configuration:  ")
            PutTemplate(dsp, "Cabinet $O off but $O on", physMod-1, physMod)
            Ding(dsp)
            lastModuleOff = false
            ]
         ]
      ifnot
         [
         lastModuleOff = true
         logMod = 3
         ]
   stdMemConf = stdMemConf + logMod lshift (12-4*physMod)
   ]
if numMemCabs eq 0 then
   Punt("*nNo memories turned on, can't do anything")

// AltIOInit (cont'd)

//prepare to scan command line
let comStr = OpenFile("Com.cm", ksTypeReadOnly, charItem, 0, fpComCm)
if comStr eq 0 then Punt("*Can't open Com.cm")
let stringVec = Allocate(sysZone, 128)
let switchVec = Allocate(sysZone, 128)

//read program name and global switches
SetupReadParam(0, 0, comStr, switchVec)
for i = 1 to switchVec!0 do
   switchon switchVec!i into
      [
      case $S: case $s:
         switchStartTenex = true  //fall into case $B
      case $B: case $b:
         switchBoot = true  //fall into case $Z
      case $Z: case $z:
         [ switchZero = true; endcase ]
      case $H: case $h:
         [ switchHalt = true; endcase ]
      case $P: case $p:
         [ protected = true; endcase ]
      case $R: case $r:
         [ ResetMemory(); endcase ]  //full memory reset
      case $D: case $d:
         [ debug = true; endcase ]
      default:
         PutTemplate(dsp,"*nBad global switch /$C, ignored", switchVec!i)
      ]

//read parameters and local switches
   [
   if ReadParam($P, -1, stringVec, switchVec) eq -1 then break
   if switchVec!0 ne 1 then [ CantParse(stringVec, switchVec); loop]
   switchon switchVec!1 into
      [
      case $L: case $l:  //save file from which to load memory
         [
         let length = stringVec>>String.length rshift 1 +1
         loadFilename = Allocate(sysZone, length+2)  //room for .Sav
         MoveBlock(loadFilename, stringVec, length)
         endcase
         ]
      case $U: case $u:  //unit number for booting Micro-Exec
         [
         let char = stringVec>>String.char↑1
         test stringVec>>String.length eq 1 &
          char ge $0 & char le $7
            ifso bootDiskUnit = char-$0
            ifnot CantParse(stringVec, switchVec)
         endcase
         ]
      default:
         CantParse(stringVec, switchVec)
      ]
   ] repeat
Free(sysZone, stringVec)
Free(sysZone, switchVec)
Closes(comStr)

// AltIOInit (cont'd)

//put hardware and software in standard initial state
adrMTBS,adrDLSBS = Allocate(sysZone, 2),Allocate(sysZone, 2)
dlsOutputChar = Allocate(sysZone, numDLSLines)
if switchZero then [ ConfigureMemory(0, 0); ZeroMaxcMem() ]
AltIOReset()  //simulate i/o reset

//initialize clock interrupt
maxcClock = Allocate(sysZone, 3)
lastTime = Allocate(sysZone, 2)
Timer(lastTime)
clockMask = FindInterruptMask(1)
InitializeInterrupt(Allocate(sysZone, 75), 75, clockMask, ClockInterrupt)
ClockInterrupt()  //get clock going

//the Maxc2 Alto's clock is a bit slow.
//set clock constants in OS to compensate.
if ClockSecond ne 0 then
   MoveBlock(ClockSecond, table [ #31; #120162 ], 2)  //1679474

//install own parity handler
savedParityHandler = @#501
@#501 = AltIOParityHandler

//diable shift-swat abort
@lvAbortFlag = 1

// Install own cursor pattern
MoveBlock(#431, table [
   #110000; #170000; #110000; #113000; #114400; #007400; #004620; #004540
   #000157; #000150; #074230; #004010; #034017; #040000; #074000; #000000
   ], 16)

// *** for performance debugging ***
if debug then spyBuffer = Allocate(sysZone, 1000)
]

// ---------------------------------------------------------------------------
and MakeBar(height, color) = valof
// ---------------------------------------------------------------------------
//make a bar 2*height scan lines high of specified color (black=1)
[
structure Bar: [ firstDCB word; lastDCB word; dcb: @DCB ]
manifest lenBar = size Bar/16
let bar = Allocate(sysZone, lenBar, false, true)  //even word
Zero(bar, lenBar)
bar>>Bar.firstDCB = lv bar>>Bar.dcb
bar>>Bar.lastDCB = lv bar>>Bar.dcb
bar>>Bar.dcb.background = color
bar>>Bar.dcb.height = height
resultis bar
]

// ---------------------------------------------------------------------------
and ShowNext(str) be
// ---------------------------------------------------------------------------
//show display stream immediately below lastShownStream
[
ShowDisplayStream(str, (lastShownStream eq 0? DSalone, DSbelow),
 lastShownStream)
lastShownStream = str
]

// ---------------------------------------------------------------------------
and MakeContext(proc, len) = valof
// ---------------------------------------------------------------------------
[
let ctx = InitializeContext(Allocate(sysZone, len), len, proc)
Enqueue(ctxQ, ctx)
resultis ctx
]

// ---------------------------------------------------------------------------
and InitAltIOPup() be
// ---------------------------------------------------------------------------
[
// Since we are not using Pup level 1, we have to do a lot of
// initialization ourselves
// The Pup portion of a PBI must be a multiple of 4 bytes.
lenPup = (pupOvBytes+defaultPupDataBytes)/2+1 & #177776
lenPBI = lenPBIOverhead+lenPup
let level1Qs = Allocate(sysZone, 6)
Zero(level1Qs, 6)
pbiIQ = level1Qs
pbiFreeQ = level1Qs+2
ndbQ = level1Qs+4
for i = 1 to numPBI do Enqueue(pbiFreeQ, Allocate(sysZone, lenPBI))
InitAltoEther(sysZone, ctxQ, 0)
etherNDB = ndbQ!0  // the one and only Ethernet NDB
InitAltoImp(sysZone, ctxQ)

// Wire-in the local net numbers (yuk)
etherNDB>>NDB.localNet = netEther
impNDB>>NDB.localNet = netArpa

// Allocate but do not initialize context for Pup process.
// It will get reset by later AltIOReset
pupCtx = Allocate(sysZone, lenPupCtx)
]

// ---------------------------------------------------------------------------
and Punt(string) be
// ---------------------------------------------------------------------------
[
Ws(string)
Ding(dsp)
Dismiss(500)  //5 seconds to let user see message
finish
]

// ---------------------------------------------------------------------------
and CantParse(stringVec,switchVec) be
// ---------------------------------------------------------------------------
   PutTemplate(dsp, "*nCan't parse *"$S$C$US*" -- ignored",
    stringVec, (switchVec!0 eq 0? $*s,$/), switchVec)