// PLMaker1.bcpl

// utility routines for PLMaker

// edited by Wyatt, February 15, 1979  10:14 AM

external
[
// outgoing
Str; Chr; Cr; Dec; UDec; Oct; DNum; ONum; XNum; Char; RNumOver10; Begin; End;
SetPos; GetPos; IncPos; DoubleSub; MulDiv;
WordsForString; CopyString; StringMatch; AppendChar; AppendNum; AppendStr;
DefaultExtension; StripExtension;

// from PLMaker
outstream
lev

// from OS
Ws; Wo; Wns; Wss;
Gets; Puts; Endofs; Resets; Closes;
SetFilePos; FilePos;
DoubleAdd; MoveBlock; SetBlock; Usc;
];

manifest cr=$*n;

structure S: // BCPL string
[
length byte
body↑0,255 byte
];

let Str(string) be Wss(outstream, string);
and Chr(c) be Puts(outstream, c);
and Cr() be [ Puts(outstream, cr); for i=1 to lev do Puts(outstream, $*T) ]
and Dec(n) be Wns(outstream, n, 0, -10); // signed decimal
and UDec(n) be Wns(outstream, n, 0, 10); // unsigned decimal
and Oct(n) be Wns(outstream, n, 0, 8); // (unsigned) octal
and DNum(n) be [ Str(" D "); Dec(n)];
and ONum(n) be [ Str(" O "); Oct(n)];
and XNum(p) be [ Str(" X "); Dec(p!0); Chr($ ); UDec(p!1) ];
and Char(c) be
[
test ($0 le c & c le $9)
 % ($A le c & c le $Z)
 % ($a le c & c le $z)
	ifso [ Str(" C "); Chr(c) ]
	ifnot ONum(c);
];
and RNumOver10(n) be // n/10 as a real number
[
Str(" R ");
if n ls 0 do [ Chr($-); n=-n];
UDec(n/10); Chr($.); UDec(n rem 10)
];

and Begin() be [ Chr($(); lev=lev+1 ]
and End() be [ lev=lev-1; Chr($)); Cr() ];


// routines for dealing with file positions in words

and
SetPos(stream, fp) be
[
let fph, fpl=fp!0, fp!1; // don't disturb given fp
fph=(fph lshift 1)+(fpl ls 0?1,0)
fpl=fpl lshift 1
SetFilePos(stream, fph, fpl)
]

and
GetPos(stream, fp) be
[
FilePos(stream, fp)
fp!1=(fp!1 rshift 1)+((fp!0 & 1) eq 1?#100000,0);
fp!0=fp!0 rshift 1
]

and
IncPos(stream, nwords) be // increment file position by nwords
[
let fp=vec 1;
GetPos(stream, fp);
let t=vec 1;
t!0=nwords ls 0?-1,0;
t!1=nwords;
DoubleAdd(fp, t);
SetPos(stream, fp);
]

and
DoubleSub(a, b) be
[
// does a ← a - b
let minusb=vec 1;
minusb!0, minusb!1 = not b!0, not b!1;
DoubleAdd(minusb, table [ 0; 1 ]);
DoubleAdd(a, minusb);
];

and
MulDiv(a,b,c) = valof [
// Returns a*b/c  using unsigned arithmetic.
  MulDiv=table [
	#55001	// STA 3,1,2
	#155000 // MOV 2,3  save stack pointer
	#111000 // MOV 0,2  a
	#21403	// LDA 0,3,3
	#101220	// MOVZR 0,0	c/2
	#61020  // MUL
	#31403  // LDA 2,3,3 c
	#61021  // DIV
	#101010 //  MOV# 0,0
	#121000 // MOV 1,0
	#171000 // MOV 3,2
	#35001	// LDA 3,1,2
	#1401	// JMP 1,3
	]
	resultis MulDiv(a,b,c)
]

and
WordsForString(s) = valof
[
let b=s>>S.length+1; // number of bytes
resultis (b+1)/2; // number of words required
];

and
CopyString(dest, source) be
	MoveBlock(dest, source, WordsForString(source));

and
StringMatch(s1,s2) = valof
[
let Uc(c) = (c ge $a & c le $z)?(c-($a-$A)),c;
let n=s1>>S.length;
if s2>>S.length ne n resultis false;
for i=0 to n-1 do
	if Uc(s1>>S.body↑i) ne Uc(s2>>S.body↑i) resultis false;
resultis true;
];

and
AppendChar(s, c) be
[
let i=s>>S.length;
s>>S.body↑i=c;
s>>S.length=i+1;
];

and
AppendNum(s, n) be
[
// append the number n, in decimal, to string s
if n gr 9 do [ AppendNum(s, n/10); n=n rem 10 ];
AppendChar(s,$0+n);
];

and
AppendStr(s, s2) be
[
// append the string s2 to string s
let i=s>>S.length;
for j=0 to s2>>S.length-1 do [ s>>S.body↑i=s2>>S.body↑j; i=i+1 ];
s>>S.length=i;
];

and
DefaultExtension(name, ext) be
[
let n=name>>S.length;
for i=0 to n-1 do if name>>S.body↑i eq $. return;
AppendChar(name,$.);
AppendStr(name,ext);
];

and
StripExtension(s) be
[
for i=0 to s>>S.length-1 do
	if s>>S.body↑i eq $. do [ s>>S.length=i; return ];
];