// CHAT.BCPL - Bob Sproull - Pup User Telnet - BCPL
// Copyright Xerox Corporation 1979, 1980
// modified: September 26, 1980  5:21 PM (E. Taft)

get "Chat.d"
get "ChatBSP.d"
get "SysDefs.d"
get "BcplFiles.d"

//outgoing procedures:
external [
	Sti
	SendMarkData

	BigStack
	SmallStack
	CheckShiftSwat
	SendScreenParams
	]

//incoming procedures
external [
	ChatReadParams
	ChatComInit
	ChatTTY
	ChatDIS
	ChatDISInit

//BSP
	BSPPutMark

//OS
	Puts
	CallersFrame
	GotoLabel
	EnableInterrupts
	DisableInterrupts
	Junta
	MyFrame
	InitializeZone
	MoveBlock

//ALTOTIME
	SetTimer
	TimerHasExpired

//CONTEXT
	CallContextList

//EtherBoot
	EtherBoot
	]

//global statics
external [
//Chat state
	Parm			//vector of structure PARM
	ctxQ			//List of processes
	Running			//0 => stopped; 1 => running; 2=> please quit
	makeBootFile

//Connections:
	TTYSoc			//Telnet Socket
	TTYStr			// (stream version)
	DISSoc			//Display protocol socket
	DISStr

//Screen
	ScreenBuffer		//Buffer to use.
	ScreenBufferLength	// and its length
	YMax

//Free storage stuff
	ChatZone		//Normal zone
	ChatZoneSize
	ComZone			//For communications stuff (below code!)
	ComZoneLeft
	]

static [
	Parm
	ctxQ
	Running = 0
	savedUFP
	makeBootFile
	TTYSoc
	TTYStr
	DISSoc
	DISStr
	ScreenBuffer
	ScreenBufferLength
	YMax=YMaxDefault
	ChatZone
	ChatZoneSize
	ComZone
	ComZoneLeft
	]


//incoming statics
external [
	OsBuffer
	UserName
	UserPassword
	lvUserFinishProc
	lvAbortFlag
	]


// local statics
static [
	bigStack
	saved335
	savedFrame
	]

manifest kbdAd = #177034


let CHAT(blv) be
[CHAT
//First, set up parm vector and read parameters in:
	Parm=#1000
	bigStack=#1000+lPARM

	ChatReadParams(Parm)

//Now start up communications stuff:
	savedUFP = @lvUserFinishProc
	@lvUserFinishProc = ChatHandleFinish
	let zoneFirst=#1000+lPARM+bigStackSize
	ChatComInit(zoneFirst, blv>>BLV.startOfStatics-zoneFirst-1)

//Now decide which of the two "programs" we are running!
	if Parm>>PARM.DisplayProtocol then
		[
//Display -- move display code down over TTY and Init code:
		let len=@#335-ChatDIS+2
		MoveBlock(ChatTTY, ChatDIS, len)
		let dif=ChatDIS-ChatTTY
		let p=blv>>BLV.relPairTable
		for i=1 to p!0 do
			[
			@(p!1)=@(p!1)-dif	//Relocate statics
			p=p+2
			]
		Junta(levKeyboard, ChatDISInit)
		]

//Must be simple TTY if we ever get here!!

	ChatTTY()				//Go do the work!
]CHAT

and ChatHandleFinish() be if makeBootFile then EtherBoot(10B)  // NetExec

and CheckShiftSwat() be
// Note: do not simply "abort" here, because when the OS "finish" code
// re-enables shift-swat aborts, the abort will take a second time
// and may cause the OS initialization to malfunction.
	if kbdAd!2 eq #177677 & (kbdAd!3 & #177577) eq #177573 then
		[
		if makeBootFile then abort  // do not want OS to see this
		@lvAbortFlag = @lvAbortFlag-1
		for i = 1 to 10000 do loop
		@lvAbortFlag = @lvAbortFlag+1
		]

//SendMarkData(soc, markbyte, databyte)
// Sends a mark of type "markbyte", optionally followed by a single
// data byte "databyte"

and SendMarkData(S, markbyte, data; numargs n) be
[
	BSPPutMark(S, markbyte)
	if n gr 2 then Puts(lv S>>BSPSoc.bspStr, data)
]

and SendScreenParams(Soc) be
[SSP
   SendMarkData(Soc, MarkPageLength, Parm>>PARM.ScreenLines)	//Page length
   SendMarkData(Soc, MarkTerminalType, #12)	//Terminal type scope
   SendMarkData(Soc, MarkLineWidth, Parm>>PARM.ScreenChars)	//Line width
]SSP

// Sti(byte1) -- stuff the character in the type-in buffer!

and Sti(c) be
[STI
	DisableInterrupts()
	let oldIn=OsBuffer>>OsBUF.In
	let newIn=oldIn+1
	if newIn eq OsBuffer>>OsBUF.Last then
		newIn=OsBuffer>>OsBUF.First
	if newIn ne OsBuffer>>OsBUF.Out then
		[
		@oldIn=c
		OsBuffer>>OsBUF.In=newIn
		]
	EnableInterrupts()
]STI

// Routines for letting procedures run in a bigger-than-usual
// stack.  Users must beware that no Blocks() are done within
// the big stack, because another process may use the same stack.
// Note: local variables are not the same (accessible) after a call
// on BigStack.
// Both BigStack and SmallStack return their arguments.

and BigStack(val) be
[BS
	savedFrame=CallersFrame()	//Including return address
	let cfsize=(savedFrame!0)-savedFrame
	saved335=@#335
	@#335=bigStack
	GotoLabel(bigStack+bigStackSize-cfsize, savedFrame!1+1, val)
]BS

and SmallStack(val) be
[SS
	let l=CallersFrame()
	@#335=saved335
	GotoLabel(savedFrame, l!1+1, val)
]SS