DIRECTORY Basics USING [BITAND, LowHalf], BasicTime USING [Now], Buttons USING [ReLabel], IO, Labels USING [Set], LarkControl, LarkPrograms USING [AddOrReplaceProgram, FetchProgram, FindVariable, PatchItem, Program, ReadProgramFromDisk, ReadProgramFromMBFile, STObject], NamesGV USING [GVIsAuthenticated, GVGetAttribute], ProcessExtras USING [CheckForAbort], Rope USING [Concat, Fetch, Find, FromChar, Length, ROPE, Substr], TeleLoad USING [Advice, Call, CallPkt, CallPktObject, CoreAddress, CoreBlock, CoreBlockObject, EventRecordObject, Failed, Fetch, FetchState, FlushWrites, GetEventData, Go, GoFromBreak, GoToDebugger, Handle, ReadWord, Registers8086, ResetCache, SetCacheSize, SingleStep, SlaveFetch, SlaveStore, State8086, State8086Object, Store, SwabState, TeleLoadProc, Write, WriteWord]; LarkCommImpl: CEDAR PROGRAM IMPORTS Buttons, Basics, BasicTime, IO, Labels, LarkControl, LarkPrograms, NamesGV, ProcessExtras, Rope, TeleLoad EXPORTS LarkControl = { loadWindowLow: PUBLIC TeleLoad.CoreAddress _ 0; loadWindowHigh: PUBLIC TeleLoad.CoreAddress _ 57344; -- 0E000H LoadProgram: PUBLIC PROC [lark: LarkControl.LarkData, go: BOOL, main: BOOL] = { ENABLE TeleLoad.Failed => GOTO BadComm; ok: BOOL; tries: NAT; h: TeleLoad.Handle _ lark.h; log: IO.STREAM _ h.log; program: LarkPrograms.Program _ IF main THEN lark.program ELSE lark.slaveProgram; IF program = NIL THEN { log.PutRope["No program specified\n"]; RETURN; }; TeleLoad.FlushWrites[h]; TeleLoad.SetCacheSize[h, 0]; IF lark.breakList # NIL THEN { LarkControl.LogEntry[lark: lark, rope: "Clearing breakpoints\n"]; lark.breakList _ NIL; }; LarkControl.LogEntry[lark: lark, rope: IO.PutFR["Loading %g: ", IO.rope[program.programName]]]; IF NOT FetchState[h].ok THEN { log.PutRope["\n"]; RETURN; }; FOR l: LIST OF TeleLoad.CoreBlock _ program.program, l.rest WHILE l # NIL DO IF l.first.address >= LarkControl.loadWindowLow AND (l.first.address + l.first.count) < LarkControl.loadWindowHigh THEN { l.first.advice _ [FALSE, FALSE, 0]; [ok, tries] _ IF program.addressSpace = main THEN TeleLoad.Store[h, l.first] ELSE TeleLoad.SlaveStore[h, l.first]; IF ok THEN log.PutChar[IF tries = 1 THEN '! ELSE ('0 + MIN[tries, 9])] ELSE { log.PutRope["\nTeleload Error\n"]; EXIT; }; } ELSE { FOR partial: TeleLoad.CoreAddress IN [0..l.first.count) DO IF (l.first.address + partial) >= LarkControl.loadWindowLow AND (l.first.address + partial) < LarkControl.loadWindowHigh THEN TeleLoad.Write[h: h, addr: l.first.address + partial, value: l.first[partial], addressSpace: program.addressSpace]; ENDLOOP; TeleLoad.FlushWrites[h]; log.PutChar['s]; }; ENDLOOP; FOR pl: LIST OF LarkPrograms.PatchItem _ program.patchList, pl.rest WHILE pl # NIL DO TeleLoad.WriteWord[h: h, addr: pl.first.address, value: pl.first.wordValue, addressSpace: program.addressSpace]; log.PutChar['p]; ENDLOOP; TeleLoad.FlushWrites[h]; lark.state _ program.startState; IF go THEN { [] _ SetState[h: h, state: lark.state, tp: TeleLoad.Go]; Labels.Set[lark.status, LarkControl.PollLark[lark, FALSE, FALSE]]; lark.lastBoot _ BasicTime.Now[]; lark.lastBootTimeValid _ TRUE; }; log.PutRope["\n"]; EXITS BadComm => NULL; }; PaintMode: PUBLIC PROC [lark: LarkControl.LarkData] = { Buttons.ReLabel[button: lark.mode, newName: Rope.Concat["Mode ", Rope.FromChar[lark.larkMode]]]; }; SetState: PUBLIC PROC [h: TeleLoad.Handle, state: TeleLoad.State8086Object, tp: TeleLoad.TeleLoadProc, print: BOOL _ TRUE] RETURNS [ok: BOOL] = TRUSTED { cb: TeleLoad.CoreBlock; tries: NAT; pp: TeleLoad.State8086; size: CARDINAL = SIZE[TeleLoad.State8086Object]*2; IF h = NIL THEN ERROR; cb _ NEW[TeleLoad.CoreBlockObject[size]]; cb.address _ 1; cb.advice _ [FALSE, FALSE, 0]; pp _ LOOPHOLE[BASE[DESCRIPTOR[cb.data]], TeleLoad.State8086]; pp^ _ TeleLoad.SwabState[state]; pp _ NIL; [ok, tries] _ tp[h, cb]; IF NOT ok AND print THEN { reason: Rope.ROPE _ SELECT tp FROM TeleLoad.Store => "Store", TeleLoad.Fetch => "Fetch", TeleLoad.Go => "Go", TeleLoad.FetchState => "FetchState", TeleLoad.SingleStep => "SingleStep", TeleLoad.GoFromBreak => "GoFromBreak", TeleLoad.GoToDebugger => "GoToDebugger", TeleLoad.SlaveStore => "SlaveStore", TeleLoad.SlaveFetch => "SlaveFetch", TeleLoad.Call => "Call" ENDCASE => "SetState"; IF h.log # NIL THEN h.log.PutF["%g: %g failed (%d attempts)\n", IO.rope[h.host], IO.rope[reason], IO.int[tries]]; }; }; FetchState: PUBLIC PROC[h: TeleLoad.Handle, adv: TeleLoad.Advice _ [FALSE, FALSE, 0], print: BOOL _ TRUE] RETURNS [ok: BOOL, state: TeleLoad.State8086Object] = TRUSTED { cb: TeleLoad.CoreBlock; tries: NAT; pp: TeleLoad.State8086; size: CARDINAL = SIZE[TeleLoad.State8086Object]*2; IF h = NIL THEN ERROR; cb _ NEW[TeleLoad.CoreBlockObject[size]]; cb.address _ 1; cb.advice _ adv; [ok, tries] _ TeleLoad.FetchState[h, cb]; IF NOT ok AND print THEN { IF h.log # NIL THEN h.log.PutF["%g: FetchState failed (%d attempts)\n", IO.rope[h.host], IO.int[tries]]; state _ [ALL[0]]; RETURN; }; pp _ LOOPHOLE[BASE[DESCRIPTOR[cb.data]], TeleLoad.State8086]; state _ TeleLoad.SwabState[pp^]; pp _ NIL; }; SetValue: PUBLIC PROC [lark: LarkControl.LarkData, name: Rope.ROPE, value: CARDINAL, offset: NAT _ 0, print: BOOL _ TRUE] RETURNS [ok: BOOL] = {{ ENABLE TeleLoad.Failed => GOTO BadComm; ob: LarkPrograms.STObject; address: TeleLoad.CoreAddress; found: BOOL; oldValue: CARDINAL; out: IO.STREAM; IF lark = NIL OR lark.h = NIL THEN RETURN[FALSE]; out _ lark.h.log; TeleLoad.ResetCache[lark.h]; [item: ob, found: found] _ LarkPrograms.FindVariable[lark.program, name]; IF NOT found THEN { IF print THEN out.PutF[" %g: not found\n", IO.rope[name]]; RETURN[FALSE]; } ELSE { -- fetch data and print it address _ ob.addr; IF print THEN out.PutF[" %g + %04xH (%04xH^): ", IO.rope[name], IO.card[offset], IO.card[address]]; oldValue _ TeleLoad.ReadWord[lark.h, address + offset]; IF ob.type = variable THEN { TeleLoad.SetCacheSize[lark.h, 2]; -- one word at a time! TeleLoad.WriteWord[h: lark.h, addr: address + offset, value: value]; TeleLoad.FlushWrites[lark.h]; TeleLoad.SetCacheSize[lark.h, 0]; IF print THEN out.PutF["%04xH _ %04xH\n", IO.card[oldValue], IO.card[value]]; } ELSE { IF print THEN out.PutF["%04xH . . . not a variable\n", IO.card[oldValue]]; RETURN[FALSE]; }; }; }; RETURN[TRUE]; EXITS BadComm => RETURN[FALSE]; }; DisplayState: PUBLIC PROC[s: IO.STREAM, state: TeleLoad.State8086Object, indent: NAT _ 0] = { THROUGH [0..indent) DO s.PutChar[' ]; ENDLOOP; FOR i: TeleLoad.Registers8086 IN [AX..SI] DO PutReg[s, i, state.Regs[i]]; ENDLOOP; s.PutChar['\n]; THROUGH [0..indent) DO s.PutChar[' ]; ENDLOOP; FOR i: TeleLoad.Registers8086 IN [DI..FL] DO PutReg[s, i, state.Regs[i]]; ENDLOOP; s.PutChar['\n]; }; PutReg: PROC [s: IO.STREAM, i: TeleLoad.Registers8086, v: CARDINAL] = { s.PutF[" %2s=%04xH", IO.rope[RegNames[i]], IO.card[v]]; }; RegNames: ARRAY TeleLoad.Registers8086 OF Rope.ROPE = [ "AX", "BX", "CX", "DX", "SP", "BP", "SI", "DI", "CS", "DS", "SS", "ES", "IP", "FL"]; LocalGo: PUBLIC PROC [lark: LarkControl.LarkData] = { found: BOOL _ FALSE; breakpoint: LarkControl.Breakpoint; FOR l: LIST OF LarkControl.Breakpoint _ lark.breakList, l.rest WHILE l # NIL DO IF l.first.address = lark.state.Regs[IP] THEN { found _ TRUE; breakpoint _ l.first; EXIT; }; ENDLOOP; IF found THEN { RepairBreakpoint[lark.h, breakpoint]; [] _ LarkControl.SetState[h: lark.h, state: lark.state, tp: TeleLoad.GoFromBreak, print: TRUE]; } ELSE [] _ LarkControl.SetState[h: lark.h, state: lark.state, tp: TeleLoad.Go, print: TRUE]; IF lark.status # NIL THEN Labels.Set[lark.status, LarkControl.PollLark[lark, FALSE, FALSE]]; }; PollLark: PUBLIC PROC [lark: LarkControl.LarkData, print: BOOL _ FALSE, setPointers: BOOL _ TRUE] RETURNS [reason: Rope.ROPE] = { event: TeleLoad.EventRecordObject; ok: BOOL; [event: event, ok: ok] _ TeleLoad.GetEventData[h: lark.h, setPointers: setPointers]; IF NOT ok THEN { reason _ "Down"; lark.event.reason _ LarkControl.cDown; } ELSE { reason _ LarkControl.BootReason[reason: event.reason, BX: event.regs.Regs[BX]]; IF lark.lastBootTimeValid THEN reason _ IO.PutFR["%-20g booted: %g", IO.rope[reason], IO.time[lark.lastBoot]]; lark.event _ event; }; IF print THEN { IF ok THEN LarkControl.DisplayEvent[lark: lark, event: event, out: lark.h.log, reasonRope: reason, preRope: "Status of", short: TRUE] ELSE LogEntry[lark: lark, rope: reason, endWithCR: TRUE]; }; }; RepairBreakpoint: PUBLIC PROC [h: TeleLoad.Handle, breakpoint: LarkControl.Breakpoint] = { TeleLoad.FlushWrites[h]; TeleLoad.ResetCache[h]; TeleLoad.SetCacheSize[h, 1]; TeleLoad.Write[h, breakpoint.address, breakpoint.codeByte]; TeleLoad.FlushWrites[h]; TeleLoad.SetCacheSize[h, 0]; }; BootLark: PUBLIC PROC [lark: LarkControl.LarkData] = TRUSTED { ok: BOOL; cb: TeleLoad.CoreBlock _ NEW[TeleLoad.CoreBlockObject[SIZE[TeleLoad.CallPktObject]*2]]; cp: TeleLoad.CallPkt _ LOOPHOLE[BASE[DESCRIPTOR[cb.data]]]; sym: LarkPrograms.STObject; tries: NAT; cb.address _ 0; cb.advice _ [FALSE, FALSE, 0]; [item: sym, found: ok] _ LarkPrograms.FindVariable[lark.program, "Boot"]; IF NOT ok THEN RETURN; cp.proc _ Basics.LowHalf[sym.addr]; cp.args _ ALL[0]; cp.nargs _ 0; cp.returnArg _ 0; [ok, tries] _ TeleLoad.Call[lark.h, cb, 1]; }; LarkGVSet: PUBLIC PROC [lark: LarkControl.LarkData, setMode: BOOL, setProgram: BOOL] = { programName: Rope.ROPE_NIL; found: BOOL_FALSE; IF lark.larkMode = 'd THEN { lark.h.log.PutF["%g: local mode override\n", IO.rope[lark.rName]]; RETURN; }; SELECT NamesGV.GVIsAuthenticated[lark.rName] FROM authentic, perhaps => found_TRUE; bogus, nonexistent => lark.h.log.PutF["%g: Not known\n", IO.rope[lark.rName]]; unknown => lark.h.log.PutF["%g: Grapevine failure\n", IO.rope[lark.rName]]; ENDCASE => ERROR; IF found THEN { larkPos: INT; programName _ NamesGV.GVGetAttribute[lark.rName, $program, "Lark"]; lark.h.log.PutF["%g program: %g\n", IO.rope[lark.rName], IO.rope[programName]]; lark.userRName _ NamesGV.GVGetAttribute[lark.rName, $connect, "Unknown"]; larkPos _ Rope.Find[s1: lark.userRName, s2: ".lark", case: FALSE]; IF larkPos >= 0 THEN lark.userRName _ Rope.Substr[base: lark.userRName, len: larkPos]; }; Labels.Set[lark.userRNameViewer, lark.userRName]; IF setMode THEN { larkMode: Rope.ROPE; larkMode _ NamesGV.GVGetAttribute[lark.rName, $mode, "U"]; IF larkMode.Length[]=0 THEN larkMode_"U"; lark.larkMode _ larkMode.Fetch[0]; }; IF setProgram THEN { program: LarkPrograms.Program; IF programName.Length[] > 0 THEN { program _ LarkPrograms.FetchProgram[programName]; IF program = NIL THEN { program _ LarkPrograms.ReadProgramFromDisk[objectFileName: programName, log: lark.h.log, addressSpace: main]; IF program # NIL THEN LarkPrograms.AddOrReplaceProgram[program]; }; IF program # NIL THEN { lark.program _ program; LarkControl.LogEntry[lark: lark]; lark.h.log.PutF["setting program to %g\n", IO.rope[lark.program.programName]]; } ELSE LarkControl.LogEntry[lark: lark, rope: "setting program to NIL.\n"]; programName _ Rope.Concat[programName, "SlaveRam.mb"]; program _ LarkPrograms.FetchProgram[programName]; IF program = NIL THEN { program _ LarkPrograms.ReadProgramFromMBFile[mbFileName: programName, baseAddress: 02000H, log: NIL, addressSpace: slave]; IF program # NIL THEN LarkPrograms.AddOrReplaceProgram[program]; }; IF program # NIL THEN { lark.slaveProgram _ program; LarkControl.LogEntry[lark: lark]; lark.h.log.PutF[" setting slave program to %g\n", IO.rope[lark.slaveProgram.programName]]; }; }; }; }; LogEntry: PUBLIC PROC [lark: LarkControl.LarkData, rope: Rope.ROPE _ NIL, endWithCR: BOOL _ FALSE] = { lark.h.log.PutF["%g, %g", IO.time[], IO.rope[lark.nameRope]]; IF rope # NIL THEN { lark.h.log.PutRope[": "]; lark.h.log.PutRope[rope]; } ELSE lark.h.log.PutChar[' ]; IF endWithCR THEN lark.h.log.PutRope["\n"]; }; DisplayEvent: PUBLIC PROC [lark: LarkControl.LarkData, event: TeleLoad.EventRecordObject, out: IO.STREAM, reasonRope: Rope.ROPE, preRope: Rope.ROPE, short: BOOL _ FALSE] = { longClock: LONG CARDINAL; relays: Rope.ROPE; LogEntry[lark: lark, rope: preRope]; out.PutF[" %04xH (%g)\n", IO.card[event.reason], IO.rope[reasonRope]]; longClock _ event.clockHigh; longClock _ longClock * 65536; longClock _ longClock + event.clockLow; out.PutF[" clock %08x, switches: %04xH, advice: %04xH\n", IO.card[longClock], IO.card[event.bootSwitches], IO.card[event.advice]]; SELECT Basics.BITAND[event.monRelays, 9] FROM 0 => relays _ "TeleSet and Hookswitch active "; 1 => relays _ "TeleSet active "; 8 => relays _ "Hookswitch active "; ENDCASE => relays _ NIL; SELECT Basics.BITAND[event.monRelays, 6] FROM 2 => relays _ relays.Concat["A relay active "]; 4 => relays _ relays.Concat["Off hook "]; 6 => relays _ relays.Concat["Off hook and A relay active "]; ENDCASE => NULL; out.PutRope[" "]; IF relays.Length[] > 0 THEN { out.PutRope["Previously "]; out.PutRope[relays]; out.PutRope[", "]; }; out.PutF["TeleLoad Host: %b#%b# via %b\n", IO.card[event.tlNet], IO.card[event.tlHost], IO.card[event.tlImHost]]; IF NOT short THEN LarkControl.DisplayState[s: out, state: event.regs, indent: 6]; out.PutRope["% "]; IF lark.status # NIL THEN Labels.Set[lark.status, reasonRope]; }; VerifyInternal: PUBLIC PROC [lark: LarkControl.LarkData, program: LarkPrograms.Program, startAddress, stopAddress: TeleLoad.CoreAddress] = { ENABLE TeleLoad.Failed => GOTO BadComm; log: IO.STREAM _ lark.h.log; h: TeleLoad.Handle _ lark.h; blockCount: NAT; blockAddress: TeleLoad.CoreAddress; tries: NAT; tcb: TeleLoad.CoreBlock _ NIL; ok: BOOL; endCode: TeleLoad.CoreAddress; item: LarkPrograms.STObject; found: BOOL; IF stopAddress <= startAddress THEN { [item: item, found: found] _ LarkPrograms.FindVariable[program, "endCode", FALSE]; endCode _ item.addr; IF NOT found THEN { [item: item, found: found] _ LarkPrograms.FindVariable[program, "monEndCode", FALSE]; endCode _ item.addr; IF found THEN { startAddress _ program.startState.Regs[IP]; stopAddress _ endCode; } ELSE { log.PutRope["Can't find endCode symbol.\n"]; log.PutRope["Checking entire image\n"]; startAddress _ LAST[TeleLoad.CoreAddress]; stopAddress _ FIRST[TeleLoad.CoreAddress]; FOR l: LIST OF TeleLoad.CoreBlock _ program.program, l.rest WHILE l # NIL DO IF l.first.address < startAddress THEN startAddress _ l.first.address; IF l.first.address + l.first.count > stopAddress THEN stopAddress _ l.first.address + l.first.count; ENDLOOP; }; } ELSE { startAddress _ program.startState.Regs[IP]; stopAddress _ endCode; }; }; log.PutF["%g: Checking %s between %04x and %04x: ", IO.rope[lark.nameRope], IO.rope[program.programName], IO.card[startAddress], IO.card[stopAddress]]; TeleLoad.FlushWrites[h]; FOR l: LIST OF TeleLoad.CoreBlock _ program.program, l.rest WHILE l # NIL DO IF lark.world.pleaseStop THEN EXIT; blockCount _ l.first.count; blockAddress _ l.first.address; IF l.first.address < startAddress THEN { IF l.first.address + blockCount < startAddress THEN LOOP; blockCount _ blockCount - (startAddress - l.first.address); blockAddress _ startAddress; }; IF l.first.address + blockCount > stopAddress THEN { IF l.first.address > stopAddress THEN LOOP; blockCount _ stopAddress - l.first.address; }; IF tcb = NIL OR tcb.count # blockCount THEN tcb _ NEW[TeleLoad.CoreBlockObject[blockCount]]; tcb.address _ blockAddress; tcb.advice _ [FALSE, FALSE, 0]; [ok, tries] _ IF program.addressSpace = main THEN TeleLoad.Fetch[h, tcb] ELSE TeleLoad.SlaveFetch[h, tcb]; IF ok THEN { log.PutChar[IF tries # 1 THEN ('0 + MIN[tries, 9]) ELSE IF program.addressSpace = main THEN '! ELSE 's]; } ELSE { log.PutRope["\nTeleload Read Error\r"]; EXIT; }; FOR i: CARDINAL IN [0..tcb.count) DO IF lark.world.pleaseStop THEN EXIT; IF tcb[i] # l.first[tcb.address + i - l.first.address] THEN { log.PutF["addr: %04x is: %02x was %02x\n", IO.card[tcb.address+i], IO.card[tcb[i]], IO.card[l.first[tcb.address + i - l.first.address]]]; ProcessExtras.CheckForAbort[]; }; ENDLOOP; ENDLOOP; lark.world.pleaseStop _ FALSE; log.PutRope["\n"]; EXITS BadComm => NULL; }; }. April 25, 1983 12:13 pm, LCS, created from old LarkPrograms.mesa April 29, 1983 12:06 pm, LCS, new Event stuff December 21, 1983 4:08 pm, LCS, new Cedar 5 $LarkCommImpl.mesa Stewart, December 21, 1983 4:08 pm Last Edited by: Swinehart, June 15, 1984 1:46:31 am PDT changes the word at offset from an identifier, use for patch decks Force a GV lookup next time. Peel off the .lark, if there is one Now install a standard slave program, if any ÊɘJšœ™Jšœ"™"J™7J˜šÏk ˜ Jšœœœ ˜Jšœ œ˜Jšœœ ˜Jšœ˜Jšœœ˜J˜ Jšœ œ}˜Jšœœ%˜2Jšœœ˜$Jšœœ)œ ˜AJšœ œæ˜ôJ˜—Jšœœ˜JšœœK˜qJšœ˜J˜J˜Jšœœ˜/JšœœÏc ˜>J˜š Ïn œœœ"œœ˜OJšœœ ˜'Jšœœ˜ Jšœœ˜ J˜Jšœœœ ˜Jšœ œœœ˜Qšœ œœ˜J˜&Jšœ˜J˜—Jšœ˜Jšœ˜šœœœ˜JšœA˜AJšœœ˜J˜—Jšœ'œœ˜_šœœœ˜Jšœ˜Jšœ˜J˜—š œœœ.œœ˜Lšœ.œ@œ˜yJšœœœ˜#Jšœœœœ!˜rJš œœ œ œœœ ˜Fšœ˜J˜"Jšœ˜J˜—J˜—šœ˜šœœ˜:Jšœ:œ:œt˜ñJšœ˜—Jšœ˜J˜J˜—Jšœ˜—š œœœ5œœ˜UJšœp˜pJ˜Jšœ˜—Jšœ˜J˜ šœœ˜ Jšœ8˜8Jšœ3œœ˜BJ˜ Jšœœ˜J˜—Jšœ˜Jš˜Jšœ œ˜J˜—J˜šŸ œœœ!˜7Jšœ`˜`J˜—J˜šŸœœœYœœœœœ˜™J˜Jšœœ˜ J˜Jšœœœ˜2Jšœœœœ˜Jšœœ!˜)J˜Jšœ œœ˜Jšœœœ œ ˜=J˜ Jšœœ˜ J˜šœœœœ˜šœ œœ˜"J˜J˜J˜J˜$J˜$J˜&J˜(J˜$J˜$J˜Jšœ˜—Jš œ œœ-œœœ ˜qJ˜—J˜—J˜šŸ œœœ-œœ œœœœ%œ˜©J˜Jšœœ˜ J˜Jšœœœ˜2Jšœœœœ˜Jšœœ!˜)J˜J˜J˜)šœœœœ˜Jš œ œœ5œœ ˜hJšœ œ˜Jšœ˜J˜—Jšœœœ œ ˜=J˜ Jšœœ˜ J˜—J˜JšœB™BšŸœœœ)œ œ œ œœœœ˜‘Jšœœ ˜'J˜J˜Jšœœ˜ Jšœ œ˜Jšœœœ˜Jšœœœ œœœœ˜1Jšœ˜J˜J˜Išœœœ˜Jšœœœ ˜;Jšœœ˜J˜—šœž˜!J˜Jš œœ%œ œœ˜dJ˜7šœœ˜Jšœ#ž˜9J˜DJ˜J˜!Jšœœœœ˜MJ˜—šœ˜Jšœœ*œ˜JJšœœ˜J˜—J˜—J˜Jšœœ˜ Jš˜Jšœ œœ˜J˜—J˜š Ÿ œœœœœ+œ ˜^Jšœ œœ˜.Jš œœœœœœ˜RJ˜Jšœ œœ˜.Jš œœœœœœ˜RJ˜J˜—J˜š Ÿœœœœ œ˜GJšœœœ ˜7J˜J˜—šœ œœœ˜7J˜T—J˜šŸœœœ!˜5Jšœœœ˜J˜#š œœœ1œœ˜Ošœ#œœ˜/Jšœœ˜ Jšœ˜Jšœ˜J˜—Jšœ˜—šœœ˜Jšœ%˜%JšœYœ˜_J˜—JšœQœ˜[Jš œœœ4œœ˜\J˜—J˜šŸœœœ%œœœœœœ˜Jšœ"˜"Jšœœ˜ JšœT˜Tšœœœ˜Jšœ˜Jšœ&˜&J˜—šœ˜Jšœ6œœ˜OJš œœ œœœ˜nJ˜J˜—šœœ˜Jšœœvœ˜…Jšœ/œ˜9J˜—J˜—J˜šŸœœœ=˜ZJšœ˜Jšœ˜Jšœ˜Jšœ;˜;Jšœ˜Jšœ˜J˜—J˜šŸœœœ œ˜>Jšœœ˜ Jšœœœ˜WJšœœœ œ ˜;J˜Jšœœ˜ J˜Jšœ œœ˜J˜IJšœœœœ˜Jšœ#˜#Jšœ œ˜Jšœ ˜ Jšœ˜J˜+J˜—J˜š Ÿ œœœ'œœ˜XJšœœœ˜Jšœœœ˜šœœ˜Jšœ-œ˜BJ™Jšœ˜Jšœ˜—šœ'˜1Jšœœ˜!Jšœ9œ˜NJšœ6œ˜KJšœœ˜—šœœ˜Jšœ œ˜ JšœC˜CJšœ$œœ˜OJ™#JšœI˜IJ•StartOfExpansion>[s1: ROPE, s2: ROPE, pos1: INT _ 0, case: BOOL _ TRUE]šœ;œ˜BJšœœB˜VJ˜—Jšœ1˜1šœ œ˜Jšœœ˜Jšœ:˜:Jšœœ˜)Jšœ"˜"J˜—šœ œ˜J˜šœœ˜"Jšœ1˜1šœ œœ˜Jšœm˜mJšœ œœ+˜@J˜—šœ œœ˜Jšœ˜Jšœ!˜!Jšœ+œ!˜NJ˜—JšœE˜IJšœ,™,Jšœ6˜6Jšœ1˜1šœ œœ˜Jšœ`œ˜zJšœ œœ+˜@J˜—šœ œœ˜Jšœ˜Jšœ!˜!Jšœ3œ&˜[J˜—J˜—J˜—J˜—J˜šŸœœœ)œœ œœ˜fJšœœ œ˜=šœœœ˜Jšœ˜Jšœ˜J˜—Jšœ˜Jšœ œ˜+J˜—J˜šŸ œœœFœœœœ œœ˜­Jšœ œœ˜Jšœ œ˜Jšœ$˜$Jšœœœ˜FJšœ˜J˜Jšœ'˜'Jšœ?œœœ˜‡šœœ˜-J˜/J˜ J˜#Jšœ œ˜—šœœ˜-J˜/J˜)J˜J˜—J˜šŸœœœq˜ŒJšœœ ˜'Jšœœœ˜Jšœ˜Jšœ œ˜J˜#Jšœœ˜ Jšœœ˜Jšœœ˜ Jšœ˜Jšœ˜Jšœœ˜ šœœ˜%JšœKœ˜RJ˜šœœœ˜JšœNœ˜UJ˜šœœ˜Jšœ'œ˜+Jšœ˜J˜—šœ˜Jšœ,˜,Jšœ'˜'Jšœœ˜*Jšœœ˜*š œœœ.œœ˜LJšœ œ ˜FJšœ/œ/˜dJšœ˜—J˜—J˜—šœ˜Jšœ'œ˜+Jšœ˜J˜—J˜—Jš œ5œœœœ˜˜Jšœ˜š œœœ.œœ˜LJšœœœ˜#Jšœ˜Jšœ˜šœ œ˜(Jšœ-œœ˜9Jšœ;˜;Jšœ˜J˜—šœ,œ˜4Jšœœœ˜+Jšœ+˜+J˜—Jš œœœœœ'˜\Jšœ˜Jšœœœ˜Jšœœœœ˜jšœœ˜ Jšœ œ œœ œœœœ˜hJ˜—šœ˜Jšœ'˜'Jšœ˜J˜—šœœœ˜$Jšœœœ˜#šœ5œ˜=Jšœ+œœœ3˜‰Jšœ˜J˜—Jšœ˜—Jšœ˜—Jšœœ˜Jšœ˜Jš˜Jšœ œ˜J˜—J˜J˜Jšœœ$˜@Jšœœ˜-Jšœœ ˜+—…—>ªQ—