// InterDisk.bcpl 
// Copyright Xerox Corporation 1979


//This is a little module that Lyle Ramshaw wrote to allow Oedit to
// reference disk files on any Diablo or Trident drive, without
// worrying about details like whether they had been Init'ed or not,
// and whether or not they needed fancy microcode.

//The primary exported procedures are SetUpInterDisk(zone), which 
// takes a zone for disk space as the argument, and sets up the
// InterDisk static data structures.  You should allow 1024*number
// of Trident files + (~125)*number of Trident drives + 256*number
// of Diablo files + (~100)*[DP1 open? 1, 0] + slop.
//After executing SetUpInterDisk, you can use InterDiskOpenFile instead
// of OpenFile, and it will parse the drive specifications on the
// file names, init the drive if necessary, and then open the file.
//InterDiskOpenFile only pays attention to its first three arguments.

//The zone argument to SetUpInterDisk is used for all space requirements.
// When you have either already opened a file on a Trident drive, or
// you know that you will never use any Trident drive, you may call
// the routine OverwriteLoadRam, and it will do a SetEndCode to
// get back the space taken up by LoadRam and TriconMC.  A system using this
// feature should end its load command with "LoadRam TriconMC" in that
// order.

get "streams.d"
get "altodefs.d"

external [		//the exported procedures
	SetUpInterDisk
	InterDiskOpenFile
	OverwriteLoadRam
	]

external [
//from BFS package
	BFSInit
	BFSClose

//from TFS package
        TFSInit
        TFSClose
        TFSSilentBoot
        LoadRam
        DiskRamImage
        TFSSwatContextProc

//from the OS
        lvUserFinishProc
        lvSwatContextProc

        SetEndCode
	CallSwat
	OpenFile
	Allocate
	sysDisk
	Wl
	Ws
        ]


static [ savedUFP; savedSCP ]
static [ triDisks;  diDisk1;  diskZone ]

structure UnPacked:
	[
	Type bit 1	//0 for Diablo, 1 for Trident
	Num bit 15
	]
manifest [ Diablo = 0;  Trident = 1 ]
structure
	[
	Left	byte
	Right	byte
	]
structure Packed:
	[
	blank bit 11
	FileSys bit 2	//For T-300's, says which file system
	Num bit 3
	]

structure Str:
	[
	Len		byte
	Char↑1,255	byte
	]

let SetUpInterDisk(zone) be
[
//assign zone to static variable, to remember it
	diskZone = zone
//set up disk objects to be 0, since no disks Init'ed
	triDisks = Allocate(zone, 32)
	for i=0 to 31 do triDisks!i=0
	diDisk1=0
]

and OverwriteLoadRam() be
  SetEndCode(LoadRam)

and InterDiskOpenFile(name, ksType, itemSize) = valof
[
let nameWordLength = (name>>Str.Len/2) + 1
let nameCopy = Allocate(diskZone, nameWordLength)
AssignStr(nameCopy, name)
let drive=ParseDriveSpec(nameCopy)
if drive eq -1 then resultis 0
if drive eq 0 then resultis	//means use DP0 = sysDisk
	  OpenFile(nameCopy,ksType,itemSize,0,0,0,diskZone,0,sysDisk)
let type = drive<<UnPacked.Type
let num = drive<<UnPacked.Num
switchon type into
	[
  case Diablo:
	if num ne 1 then resultis 0
	if diDisk1 eq 0 then InitDP1()
	resultis OpenFile(nameCopy,ksType,itemSize,0,0,0,diskZone,0,diDisk1)
  case Trident:
		[
		let triDrive, fileSys = num<<Right, num<<Left
		if (triDrive ls 0) % (triDrive gr 7) then resultis 0
		if (fileSys ls 0) % (fileSys gr 2) then resultis 0
		let packed = 0
		packed<<Packed.FileSys = fileSys
		packed<<Packed.Num = triDrive
		if triDisks!packed eq 0 then InitTridentDrive(num, packed)
		resultis
	   OpenFile(nameCopy,ksType,itemSize,0,0,0,diskZone,0,triDisks!packed)
		]
	]
]

and InitDP1(num) be
[
let res = BFSInit(diskZone, true, 1)
if res eq 0 then Bitch("Couldn't operate DP1.")
diDisk1 = res
]

and InitTridentDrive(unpacked, packed) be
[
test @lvUserFinishProc eq Finish
 ifso
	[
	let res = TFSInit(diskZone, true, unpacked)
	if res eq 0 then
		[
		let message = "Couldn't operate TPn."
		message>>Str.Char↑20 = (unpacked<<Right) + $0
		Bitch(message)
		]
	triDisks!packed = res
	]
  ifnot
	//Time to load the RAM with the Trident microcode
	[
	let load = LoadRam(DiskRamImage, true)
	let AltoVersion=(table [ #61014; #1401 ] )()
	let eng=AltoVersion<<VERS.eng
	if (eng le 3) & (load ls 0) then Bitch("Couldn't load the RAM.")
	//otherwise, assume that correct microcode is loaded on a
	//Dolphin or Dorado, and pluge on
	savedUFP = @lvUserFinishProc
	@lvUserFinishProc = Finish
	savedSCP = @lvSwatContextProc
	@lvSwatContextProc = TFSSwatContextProc
	InitTridentDrive(unpacked, packed)
	]
]

and Bitch(msg) be
[
	Wl(msg)
	let AltoVersion=(table [ #61014; #1401 ] )()
	test AltoVersion<<VERS.eng ge 4
	 ifso  Ws("Correct microcode loaded? ")
	 ifnot 	Ws("Therefore, farewell! ")
	abort
]

//The following routine reads the "DP1:" portion of the file name

and ParseDriveSpec(str) = valof
[
let colonIndex = Contains(str, $:)
test colonIndex
 ifnot
	[		//default drive is DP0=sysDisk
	resultis 0	
	]
 ifso
	[
	let res, num = nil, nil
	if colonIndex ls 3 then resultis -1
	if (str>>Str.Char↑2 ne $P)&(str>>Str.Char↑2 ne $p) then
		resultis -1
	num = 0
	for i=3 to colonIndex-1 do 
		num = (num lshift 3) + str>>Str.Char↑i - $0
	res<<UnPacked.Num = num
	switchon str>>Str.Char↑1 into
		[
	  case $D: case $d:
		res<<UnPacked.Type = Diablo
		RemoveDriveSpec(str, colonIndex)
		resultis res
	  case $T: case $t:
		res<<UnPacked.Type = Trident
		RemoveDriveSpec(str, colonIndex)
		resultis res
	  default:
		resultis -1
		]
	]
]

and RemoveDriveSpec(str, index) be
	[
	str>>Str.Len = str>>Str.Len - index
	for i=1 to str>>Str.Len do
		str>>Str.Char↑i = str>>Str.Char↑(i+index)
	]

and Contains(str, char) = valof
	[
	for i=1 to str>>Str.Len do
		if str>>Str.Char↑i eq char then resultis i
	resultis 0
	]
			 
and AssignStr(str1, str2) be
	[
	str1>>Str.Len = str2>>Str.Len
	for i=1 to str1>>Str.Len do
		str1>>Str.Char↑i = str2>>Str.Char↑i
	]

and Finish() be
	[
	for i=0 to 31 do
		if triDisks!i then TFSClose(triDisks!i)
	if diDisk1 then BFSClose(diDisk1)
	@lvUserFinishProc = savedUFP
	@lvSwatContextProc = savedSCP
	TFSSilentBoot()
	]