MODULE OCH; (*NW 7.6.87 / 15.2.91*)
	IMPORT OCS, OCT, OCC;

	CONST (*instruction format prefixes*)
			F6 = 4EH; F7 = 0CEH; F9 = 3EH; F11 = 0BEH;

		(*object and item modes*)
			Var = 1; VarX = 2; Ind = 3; IndX = 4; RegI = 5;
			RegX = 6; Abs = 7; Con = 8; Stk = 9; Coc = 10;
			Reg = 11; Fld = 12; LProc = 14; XProc = 15;
			CProc = 17; IProc = 18; Mod = 19;

		(*structure forms*)
			Undef = 0; Byte = 1; Bool = 2; Char = 3; SInt = 4; Int = 5; LInt = 6;
			Real = 7; LReal = 8; Set = 9; String = 10; NilTyp = 11; NoTyp = 12;
			Pointer = 13; ProcTyp = 14; Array = 15; DynArr = 16; Record = 17;

	TYPE LabelRange* = RECORD low*, high*: INTEGER; label*: INTEGER END ;
	VAR lengcode: ARRAY 18 OF INTEGER;

	PROCEDURE setCC(VAR x: OCT.Item; cc: LONGINT);
	BEGIN x.typ := OCT.booltyp; x.mode := Coc; x.a0 := cc; x.a1 := 0; x.a2 := 0
	END setCC;

	PROCEDURE AdjustSP(n: LONGINT);
	BEGIN (*ADJSPB n*)
		IF n <= 127 THEN OCC.PutF3(-5A84H); OCC.PutByte(n)
		ELSE OCC.PutF3(-5A83H); OCC.PutWord(n)
		END
	END AdjustSP;

	PROCEDURE move(L: INTEGER; VAR x, y: OCT.Item);
	BEGIN
		IF (y.mode = Con) & (y.a0 <= 7) & (y.a0 >= -8) THEN
			OCC.PutF2(L+5CH, y.a0, x) (*MOVQi*)
		ELSE OCC.PutF4(L+14H, x, y) (*MOVi*)
		END
	END move;

	PROCEDURE load(VAR x: OCT.Item);
		VAR y: OCT.Item;
	BEGIN
		IF x.mode # Reg THEN
			y := x; OCC.GetReg(x); move(lengcode[x.typ.form], x, y)
		END
	END load;

	PROCEDURE moveBW(VAR x, y: OCT.Item);
	BEGIN
		IF (y.mode = Con) & (y.a0 <= 7) & (y.a0 >= -8) THEN
			OCC.PutF2(5DH, y.a0, x)
		ELSE OCC.Put(F7, 10H, x, y) (*MOVXBW*)
		END
	END moveBW;

	PROCEDURE moveBD(VAR x, y: OCT.Item);
	BEGIN
		IF (y.mode = Con) & (y.a0 <= 7) & (y.a0 >= -8) THEN
			OCC.PutF2(5FH, y.a0, x)
		ELSE OCC.Put(F7, 1CH, x, y) (*MOVXBD*)
		END
	END moveBD;

	PROCEDURE moveWD(VAR x, y: OCT.Item);
	BEGIN
		IF (y.mode = Con) & (y.a0 <= 7) & (y.a0 >= -8) THEN
			OCC.PutF2(5FH, y.a0, x)
		ELSE OCC.Put(F7, 1DH, x, y) (*MOVXWD*)
		END
	END moveWD;

	PROCEDURE Leng(VAR x: OCT.Item; L: LONGINT);
		VAR y: OCT.Item;
	BEGIN
		IF L <= 7 THEN OCC.PutF2(5FH, L, x)  (*MOVQD*)
		ELSE y.mode := Con; y.a0 := L;  (*MOVZBD*)
			IF L <= 255 THEN y.typ := OCT.sinttyp; OCC.Put(F7, 18H, x, y)
			ELSE y.typ := OCT.inttyp; OCC.Put(F7, 19H, x, y)
			END
		END
	END Leng;

	PROCEDURE MoveBlock(VAR x, y: OCT.Item; s: LONGINT; param: BOOLEAN);
		VAR L: INTEGER; z: OCT.Item;
	BEGIN
		IF s > 0 THEN
			IF param THEN
				s := (s+3) DIV 4 * 4; AdjustSP(s)
			END ;
			IF s <= 16 THEN
				OCC.Put(F7, 0, x, y); OCC.PutDisp(s-1) (*MOVMB*)
			ELSE
				z.mode := Reg; z.a0 := 1; OCC.PutF4(27H, z, y); (*ADDR y,R1*)
				z.a0 := 2; OCC.PutF4(27H, z, x); z.a0 := 0;
				(*ADDR x,R2*)
				IF s MOD 4 = 0 THEN L := 3; s := s DIV 4
				ELSIF s MOD 2 = 0 THEN L := 1; s := s DIV 2
				ELSE L := 0
				END ;
				Leng(z, s);
				OCC.PutF1(14); OCC.PutByte(L); OCC.PutByte(0) (*MOVS*)
			END
		END
	END MoveBlock;

	PROCEDURE DynArrBnd(ftyp, atyp: OCT.Struct; lev: INTEGER;
			adr: LONGINT; varpar: BOOLEAN);
		VAR f, s: INTEGER; x, y, z: OCT.Item;
	BEGIN (* ftyp.form = DynArr *)
		x.mode := Stk; y.mode := Var;
		IF varpar & (ftyp.BaseTyp = OCT.bytetyp) THEN
			IF atyp.form # DynArr THEN Leng(x, atyp.size-1)
			ELSE y.lev := lev; y.a0 := adr + atyp.adr; y.typ := OCT.linttyp;
				atyp := atyp.BaseTyp;
				IF atyp.form # DynArr THEN
					IF atyp.size > 1 THEN
						z.mode := Con; z.typ := OCT.linttyp; z.a0 := atyp.size;
						load(y); OCC.Put(F7, 23H, y, z); (* MULD z, Ry *)
						z.mode := Con; z.typ := OCT.linttyp; z.a0 := atyp.size-1;
						IF z.a0 < 8 THEN OCC.PutF2(0FH, z.a0, y)  (* ADDQD size-1, Ry *)
						ELSE OCC.PutF4(3, y, z) (* ADDD size-1, Ry *)
						END
					END
				ELSE load(y); OCC.PutF2(0FH, 1, y);
					REPEAT z.mode := Var; z.lev := lev; z.a0 := atyp.adr + adr; z.typ := OCT.linttyp;
						load(z); OCC.PutF2(0FH, 1, z); (* ADDQD 1, Rz *)
						OCC.Put(F7, 23H, y, z); (* MULD Rz, Ry *)
						atyp := atyp.BaseTyp
					UNTIL atyp.form # DynArr;
					IF atyp.size > 1 THEN
						z.mode := Con; z.typ := OCT.linttyp; z.a0 := atyp.size;
						OCC.Put(F7, 23H, y, z) (* MULD z, Ry *)
					END ;
					OCC.PutF2(0FH, -1, y)  (* ADDQD -1, Ry *)
				END ;
				OCC.PutF4(17H, x, y)  (* MOVD apdynarrlen-1, TOS *)
			END
		ELSE
			LOOP f := atyp.form;
				IF f = Array THEN y.lev := -atyp.mno; y.a0 := atyp.adr
				ELSIF f = DynArr THEN y.lev := lev; y.a0 := atyp.adr + adr
				ELSE OCS.Mark(66); EXIT
				END ;
				OCC.PutF4(17H, x, y); ftyp := ftyp.BaseTyp; atyp := atyp.BaseTyp;
				IF ftyp.form # DynArr THEN
					IF ftyp # atyp THEN OCS.Mark(67) END ;
					EXIT
				END
			END
		END
	END DynArrBnd;

	PROCEDURE Trap*(n: INTEGER);
	BEGIN OCC.PutF1(0F2H); OCC.PutByte(n) (*BPT n*)
	END Trap;

	PROCEDURE CompareParLists*(x, y: OCT.Object);
		VAR xt, yt: OCT.Struct;
	BEGIN
		WHILE x # NIL DO
			IF y # NIL THEN
				xt := x.typ; yt := y.typ;
				WHILE (xt.form = DynArr) & (yt.form = DynArr) DO
					xt := xt.BaseTyp; yt := yt.BaseTyp
				END ;
				IF x.mode # y.mode THEN OCS.Mark(115)
				ELSIF xt # yt THEN
					IF (xt.form = ProcTyp) & (yt.form = ProcTyp) THEN
						CompareParLists(xt.link, yt.link)
					ELSE OCS.Mark(115)
					END
				END ;
				y := y.next
			ELSE OCS.Mark(116)
			END ;
			x := x.next
		END ;
		IF (y # NIL) & (y.mode <= Ind) & (y.a0 > 0) THEN OCS.Mark(117) END
	END CompareParLists;

	PROCEDURE Assign*(VAR x, y: OCT.Item; param: BOOLEAN);
		VAR f, g, L, u: INTEGER; s, vsz: LONGINT;
			p, q: OCT.Struct;
			xp, yp: OCT.Object;
			tag, tdes: OCT.Item;
	BEGIN f := x.typ.form; g := y.typ.form;
		IF x.mode = Con THEN OCS.Mark(56) END ;
		CASE f OF
				Undef, String:
			| Byte: IF g IN {Undef, Byte, Char, SInt} THEN
					IF param THEN moveBD(x, y) ELSE move(0, x, y) END
				ELSE OCS.Mark(113)
				END
			| Bool: IF param THEN u := 3 ELSE u := 0 END ;
				IF y.mode = Coc THEN
					IF (y.a1 = 0) & (y.a2 = 0) THEN OCC.PutF2(u+3CH, y.a0, x)
					ELSE
						IF ODD(y.a0) THEN OCC.PutF0(y.a0-1) ELSE OCC.PutF0(y.a0+1) END ;
						OCC.PutWord(y.a2); y.a2 := OCC.pc-2;
						OCC.FixLink(y.a1); OCC.PutF2(u+5CH, 1, x);
						OCC.PutF0(14); L := OCC.pc; OCC.PutWord(0);
						OCC.FixLink(y.a2); OCC.PutF2(u+5CH, 0, x); OCC.fixup(L)
					END
				ELSIF g = Bool THEN
					IF y.mode = Con THEN OCC.PutF2(u+5CH, y.a0, x)
					ELSIF param THEN OCC.Put(F7, 18H, x, y) (*MOVZBD*)
					ELSE OCC.PutF4(14H, x, y)
					END
				ELSE OCS.Mark(113)
				END
			| Char, SInt:
				IF g = f THEN
					IF param THEN moveBD(x, y) ELSE move(0, x, y) END
				ELSE OCS.Mark(113)
				END
			| Int: IF g = Int THEN
					IF param THEN moveWD(x, y) ELSE move(1, x, y) END
				ELSIF g = SInt THEN
					IF param THEN moveBD(x, y) ELSE moveBW(x, y) END
				ELSE OCS.Mark(113)
				END
			| LInt: IF g = LInt THEN move(3, x, y)
				ELSIF g = Int THEN moveWD(x, y)
				ELSIF g = SInt THEN moveBD(x, y)
				ELSE OCS.Mark(113)
				END
			| Real: IF g = Real THEN OCC.Put(F11, 5, x, y)
				ELSIF (SInt <= g) & (g <= LInt) THEN OCC.Put(F9, lengcode[g]+4, x, y)
				ELSE OCS.Mark(113)
				END
			| LReal: IF g = LReal THEN OCC.Put(F11, 4, x, y)
				ELSIF g = Real THEN OCC.Put(F9, 1BH, x, y)
				ELSIF (SInt <= g) & (g <= LInt) THEN OCC.Put(F9, lengcode[g], x, y)
				ELSE OCS.Mark(113)
				END
			| Set: IF g = f THEN move(3, x, y) ELSE OCS.Mark(113) END
			| Pointer:
				IF x.typ = y.typ THEN move(3, x, y)
				ELSIF g = NilTyp THEN OCC.PutF2(5FH, 0, x)
				ELSIF g = Pointer THEN
					p := x.typ.BaseTyp; q := y.typ.BaseTyp;
					IF (p.form = Record) & (q.form = Record) THEN
						WHILE (q # p) & (q # NIL) DO q := q.BaseTyp END ;
						IF q # NIL THEN move(3, x, y) ELSE OCS.Mark(113) END
					ELSE OCS.Mark(113)
					END
				ELSE OCS.Mark(113)
				END
			| Array: s := x.typ.size;
				IF x.typ = y.typ THEN MoveBlock(x, y, s, param)
				ELSIF (g = String) & (x.typ.BaseTyp = OCT.chartyp) THEN
					s := y.a1; vsz := x.typ.n; (*check length of string*)
					IF s > vsz THEN OCS.Mark(114) END ;
					IF param THEN
						vsz := (vsz+3) DIV 4 - (s+3) DIV 4;
						IF vsz > 0 THEN AdjustSP(vsz*4) END
					END ;
					MoveBlock(x, y, s, param)
				ELSE OCS.Mark(113)
				END
			| DynArr: s := x.typ.size;
				IF param THEN (*formal parameter is open array*)
					IF (g = String) & (x.typ.BaseTyp.form = Char) THEN Leng(x, y.a1-1)
					ELSIF y.mode >= Abs THEN OCS.Mark(59)
					ELSE DynArrBnd(x.typ, y.typ, y.lev, y.a0, FALSE)
					END ;
					IF g = DynArr THEN OCC.DynArrAdr(x, y)
					ELSE OCC.PutF4(27H, x, y)
					END
				ELSE OCS.Mark(113)
				END
			| Record: s := x.typ.size;
				IF x.typ # y.typ THEN
					IF g = Record THEN
						q := y.typ.BaseTyp;
						WHILE (q # NIL) & (q # x.typ) DO q := q.BaseTyp END ;
						IF q = NIL THEN OCS.Mark(113) END
					ELSE OCS.Mark(113)
					END
				END ;
				IF OCC.typchk & ~param &
						( ((x.mode = Ind) OR (x.mode = RegI)) & (x.obj = OCC.wasderef) (* p^ := *)
						OR (x.mode = Ind) & (x.obj # NIL) & (x.obj # OCC.wasderef) ) THEN
					tag := x; tdes.mode := Var; tdes.lev := -x.typ.mno; tdes.a0 := x.typ.adr;
					IF x.obj = OCC.wasderef THEN tag.a1 := - 4
					ELSE tag.mode := Var; INC(tag.a0, 4)
					END;
					OCC.PutF4(7, tdes, tag);  (* CMPD tag, tdes *)
					OCC.PutF0(0); OCC.PutDisp(4);  (* BEQ continue *)
					OCC.PutF1(0F2H); OCC.PutByte(19)  (* BPT 19 *)
				END ;
				MoveBlock(x, y, s, param)
			| ProcTyp:
				IF (x.typ = y.typ) OR (y.typ = OCT.niltyp) THEN OCC.PutF4(17H, x, y)
				ELSIF (y.mode = XProc) OR (y.mode = IProc) THEN
					(*procedure y to proc. variable x; check compatibility*)
					IF x.typ.BaseTyp = y.typ THEN
						CompareParLists(x.typ.link, y.obj.dsc);
						IF y.a1 = 0 THEN
							y.a1 := OCC.LinkAdr(-y.lev, y.a0); y.obj.a1 := y.a1
						END ;
						y.mode := Var; y.lev := SHORT(-y.a1); y.a0 := 0;
						OCC.PutF4(27H, x, y) (*LXPD*)
					ELSE OCS.Mark(118)
					END
				ELSIF y.mode = LProc THEN OCS.Mark(119)
				ELSE OCS.Mark(111)
				END
			| NoTyp, NilTyp: OCS.Mark(111)
		END
	END Assign;

	PROCEDURE FJ*(VAR loc: INTEGER);
	BEGIN OCC.PutF0(14); OCC.PutWord(loc); loc := OCC.pc-2
	END FJ;

	PROCEDURE CFJ*(VAR x: OCT.Item; VAR loc: INTEGER);
	BEGIN
		IF x.typ.form = Bool THEN
			IF x.mode # Coc THEN OCC.PutF2(1CH, 1, x); setCC(x, 0) END
		ELSE OCS.Mark(120); setCC(x, 0)
		END ;
		IF ODD(x.a0) THEN OCC.PutF0(x.a0-1) ELSE OCC.PutF0(x.a0+1) END ;
		loc := OCC.pc; OCC.PutWord(x.a2); OCC.FixLink(x.a1)
	END CFJ;

	PROCEDURE BJ*(loc: INTEGER);
	BEGIN OCC.PutF0(14); OCC.PutDisp(loc - OCC.pc + 1)
	END BJ;

	PROCEDURE CBJ*(VAR x: OCT.Item; loc: INTEGER);
	BEGIN
		IF x.typ.form = Bool THEN
			IF x.mode # Coc THEN OCC.PutF2(1CH, 1, x); setCC(x,0) END
		ELSE OCS.Mark(120); setCC(x, 0)
		END ;
		IF ODD(x.a0) THEN OCC.PutF0(x.a0-1) ELSE OCC.PutF0(x.a0+1) END ;
		OCC.PutDisp(loc - OCC.pc + 1);
		OCC.FixLinkWith(x.a2, loc); OCC.FixLink(x.a1)
	END CBJ;

	PROCEDURE LFJ*(VAR loc: INTEGER);
	BEGIN OCC.PutF0(14); OCC.PutWord(-4000H); OCC.PutWord(0); loc := OCC.pc-4
	END LFJ;

	PROCEDURE PrepCall*(VAR x: OCT.Item; VAR fpar: OCT.Object);
	BEGIN
		IF (x.mode = LProc) OR (x.mode = XProc) OR (x.mode = CProc) THEN
			fpar := x.obj.dsc
		ELSIF (x.typ # NIL) & (x.typ.form = ProcTyp) THEN
			fpar := x.typ.link
		ELSE OCS.Mark(121); fpar := NIL; x.typ := OCT.undftyp
		END
	END PrepCall;

	PROCEDURE Param*(VAR ap: OCT.Item; f: OCT.Object);
		VAR q: OCT.Struct; fp, tag: OCT.Item;
	BEGIN fp.mode := Stk; fp.typ := f.typ;
		IF f.mode = Ind THEN (*VAR parameter*)
			IF ap.mode >= Con THEN OCS.Mark(122) END ;
			IF fp.typ.form = DynArr THEN
				DynArrBnd(fp.typ, ap.typ, ap.lev, ap.a0, TRUE);
				IF ap.typ.form = DynArr THEN OCC.DynArrAdr(fp, ap)
				ELSE OCC.PutF4(27H, fp, ap)
				END
			ELSIF (fp.typ.form = Record) & (ap.typ.form = Record) THEN
				q := ap.typ;
				WHILE (q # fp.typ) & (q # NIL) DO q := q.BaseTyp END ;
				IF q # NIL THEN
					IF (ap.mode = Ind) & (ap.obj # NIL) & (ap.obj # OCC.wasderef) THEN
						(*actual par is VAR-par*) ap.mode := Var; ap.a0 := ap.a0 + 4;
						OCC.PutF4(17H, fp, ap); ap.a0 := ap.a0 - 4; OCC.PutF4(17H, fp, ap)
					ELSIF ((ap.mode = Ind) OR (ap.mode = RegI)) & (ap.obj = OCC.wasderef) THEN
						(*actual par is p^*) ap.a1 := - 4; OCC.PutF4(17H, fp, ap);
						IF ap.mode = Ind THEN ap.mode := Var ELSE ap.mode := Reg END;
						OCC.PutF4(17H, fp, ap)
					ELSE
						tag.mode := Var; tag.lev := -ap.typ.mno; tag.a0 := ap.typ.adr;
						OCC.PutF4(17H, fp, tag); OCC.PutF4(27H, fp, ap)
					END
				ELSE OCS.Mark(111)
				END
			ELSIF (ap.typ = fp.typ) OR ((fp.typ.form = Byte)&(ap.typ.form IN {Char, SInt})) THEN
				IF (ap.mode = Ind) & (ap.a1 = 0) THEN (*actual var par*)
					ap.mode := Var; OCC.PutF4(17H, fp, ap)
				ELSE OCC.PutF4(27H, fp, ap)
				END
			ELSE OCS.Mark(123)
			END
		ELSE Assign(fp, ap, TRUE)
		END
	END Param;

	PROCEDURE Call*(VAR x: OCT.Item);
		VAR stk, sL: OCT.Item;
	BEGIN
		IF x.mode = LProc THEN
			IF x.lev > 0 THEN
				sL.mode := Var; sL.typ := OCT.linttyp; sL.lev := x.lev; sL.a0 := 0;
				stk.mode := Stk; OCC.PutF4(27H, stk, sL) (*static link*)
			END ;
			OCC.PutF1(2); OCC.PutDisp(x.a0 - OCC.pc + 1) (*BSR*)
		ELSIF x.mode = XProc THEN
			IF x.a1 = 0 THEN
				x.a1 := OCC.LinkAdr(-x.lev, x.a0); x.obj.a1 := x.a1
			END ;
			OCC.PutF1(22H); OCC.PutDisp(SHORT(x.a1)) (*CXP*)
		ELSIF (x.mode < Con) & (x.typ # OCT.undftyp) THEN (*CXPD*)
			OCC.PutF2(7FH, 0, x); x.typ := x.typ.BaseTyp
		ELSIF x.mode = CProc THEN
			OCC.PutF1(0E2H); OCC.PutByte(x.a0)  (*SVC n*)
		ELSE OCS.Mark(121)
		END
		(*function result is marked when restoring registers*)
	END Call;

	PROCEDURE Enter*(mode: SHORTINT; pno: LONGINT; VAR L: INTEGER);
	BEGIN
		IF mode # LProc THEN OCC.SetEntry(SHORT(pno)) END ;
		OCC.PutF1(82H); (*ENTER*)
		IF mode = IProc THEN OCC.PutByte(0C0H) ELSE OCC.PutByte(0) END ;
		IF mode # Mod THEN L := OCC.pc; OCC.PutWord(0) ELSE OCC.PutByte(0) END
	END Enter;

	PROCEDURE CopyDynArray*(adr: LONGINT; typ: OCT.Struct);
		VAR size, ptr, m2, tos: OCT.Item; add: SHORTINT;

		PROCEDURE DynArrSize(typ: OCT.Struct);
			VAR len: OCT.Item;
		BEGIN
			IF typ.form = DynArr THEN DynArrSize(typ.BaseTyp);
				len.mode := Var; len.lev := OCC.level; len.typ := OCT.linttyp;
				len.a0 := adr + typ.adr; load(len);
				IF (size.mode # Con) OR (size.a0 # 1) THEN
					IF add = 4 THEN OCC.PutF2(0FH, 1, size) END; (* ADDQD 1, size *)
					OCC.PutF2(0FH, 1, len); add := 3; (* ADDQD 1, len *)
					OCC.Put(F7, 23H, len, size)  (* MULD size, len *)
				ELSE add := 4
				END;
				size := len
			ELSE size.mode := Con; size.typ := OCT.linttyp; size.a0 := typ.size
			END
		END DynArrSize;

	BEGIN add := 3;
		DynArrSize(typ);  (* load total byte size of dyn array *)
		OCC.PutF2(0FH, add, size);  (* ADDQD 3 or 4, size *)
		m2.mode := Con; m2.typ := OCT.sinttyp;
		m2.a0 := -2; OCC.Put(F6, 7, size, m2); (* ASHD -2, size *)
		ptr.mode := Var; ptr.lev := OCC.level; ptr.typ := OCT.linttyp;
		ptr.a0 := adr; load(ptr);
		ptr.mode := RegX; ptr.a1 := -4; ptr.a2 := size.a0; tos.mode := Stk;
		OCC.PutF4(17H, tos, ptr);  (* loop: MOVD -4(ptr)[size:D], TOS *)
		OCC.PutF2(4FH, -1, size); OCC.PutDisp(-4);  (* ACBD -1, size, loop *)
		OCC.PutF3(-31D9H); OCC.PutDisp(0); OCC.PutDisp(adr);  (* ADDR adr(FP) *)
		OCC.FreeRegs({})
	END CopyDynArray;

	PROCEDURE Result*(VAR x: OCT.Item; typ: OCT.Struct);
		VAR res: OCT.Item;
	BEGIN res.mode := Reg; res.typ := typ; res.a0 := 0;
		Assign(res, x, FALSE)
	END Result;

	PROCEDURE Return*(mode: INTEGER; psize: LONGINT);
	BEGIN OCC.PutF1(92H);  (*EXIT*)
		IF mode = LProc THEN
			OCC.PutByte(0); OCC.PutF1(12H); OCC.PutDisp(psize-8) (*RET*)
		ELSIF mode = XProc THEN
			OCC.PutByte(0); OCC.PutF1(32H); OCC.PutDisp(psize-12) (*RXP*)
		ELSIF mode = IProc THEN
			OCC.PutByte(3); OCC.PutF1(42H); OCC.PutDisp(0)  (*RETT 0*)
		END
	END Return;

	PROCEDURE CaseIn*(VAR x: OCT.Item; VAR L0, L1: INTEGER);
		VAR f: INTEGER; r, x0, lim: OCT.Item;
	BEGIN f := x.typ.form;
		IF f # Int THEN
			IF f = Char THEN
				x0 := x; OCC.GetReg(x); OCC.Put(F7, 14H, x, x0) (*MOVZBW*)
			ELSIF f = SInt THEN
				x0 := x; OCC.GetReg(x); OCC.Put(F7, 10H, x, x0) (*MOVXBW*)
			ELSIF f # LInt THEN OCS.Mark(125)
			END ;
			f := Int
		END ;
		IF (x.mode IN {VarX, IndX, RegX}) OR
				(x.mode # Reg) & (x.lev > 0) & (x.lev < OCC.level) THEN
			x0 := x; OCC.GetReg(x); OCC.PutF4(15H, x, x0) (*MOVW*)
		END ;
		L0 := OCC.pc+3; (*fixup loc for bounds adr*)
		lim.mode := Var; lim.typ := OCT.inttyp; lim.lev := 0; lim.a0 := 100H;
		OCC.GetReg(r); OCC.Put(0EEH, SHORT(r.a0)*8 + 1, x, lim); (*CHECK*)
		OCC.PutF0(8); OCC.PutWord(0); L1 := OCC.pc;
		(*BFS*)
		lim.mode := VarX; lim.a2 := r.a0; OCC.PutF2(7DH, 14, lim) (*CASE*)
	END CaseIn;

	PROCEDURE CaseOut*(L0, L1, L2, L3, n: INTEGER;
			VAR tab: ARRAY OF LabelRange);
		VAR i, j, lim: INTEGER; k: LONGINT;
	BEGIN (*generate jump table*)
		IF n > 0 THEN OCC.AllocBounds(tab[0].low, tab[n-1].high, k)
		ELSE (*no cases*) OCC.AllocBounds(1, 0, k)
		END ;
		j := SHORT(k);
		OCC.FixupWith(L0, j);      (*bounds address in check*)
		OCC.FixupWith(L1-2, L2-L1+3); (*out of bounds jump addr*)
		OCC.FixupWith(L1+3, j+4); (*jump address to table*)
		i := 0; j := tab[0].low;
		WHILE i < n DO
			lim := tab[i].high;
			WHILE j < tab[i].low DO
				OCC.AllocInt(L2-L1); INC(j) (*else*)
			END ;
			WHILE j <= lim DO
				OCC.AllocInt(tab[i].label-L1); INC(j)
			END ;
			INC(i)
		END ;
		OCC.FixLink(L3)
	END CaseOut;

BEGIN lengcode[Undef] := 0;
	lengcode[Byte] := 0; lengcode[Bool] := 0; lengcode[Char] := 0; lengcode[SInt] := 0;
	lengcode[Int] := 1; lengcode[LInt] := 3; lengcode[Real] := 1; lengcode[LReal] := 0;
	lengcode[Set] := 3; lengcode[String] := 0; lengcode[NilTyp] := 3; lengcode[ProcTyp] := 3;
	lengcode[Pointer] := 3; lengcode[Array] := 1; lengcode[DynArr] := 1; lengcode[Record] := 1
END OCH.