TECO-kurs (del 3) Pär Emanuelsson Lysator Hej hoppsan och God Jul! Här är jag tillbaka igen med del 3 av TECO-kursen. Beklagar att den inte kom med i förra numret av GARB. Den här gången tänker jag försöka reda ut begreppen kring TECOs hantering av terminalens tangentbord, samt lite om initieringsfiler. För alla försoffade själar som inte vet vad TECO är kan jag avslöja att det är EMACS interna språk. Jag talar här om Den Sanna EMACS, som bl.a. går under TOPS-20 och ITS. I den här delen kommer jag också in i gränslandet mellan EMACS och TECO. Egentligen var det Digital Equipment som hittade på TECO, men ett gäng hackers på MITs Al-labb märkte snart att det gick utmärkt att använda som programmeringsspråk, med vissa utvidgningar, och de byggde en skärmorienterad editor, EMACS, på sin version av TECO. Se GARB maj -87 för en utförligare historia. Rättelse Till att börja med: en rättelse. I del 2 fanns ett fakultetsprogram inuti en (ful) box. Där fattas det två stycken "\"; de ska stå bakom de ensamma "A" så att det blir kontrolltecknet "A\". Inledning Eftersom det var ett tag sen sist kan det kanske vara lämpligt med en kort repetition av allmänna principer. Ett kontrolltecken (ASCII-kod 0-31 decimalt) i TECO skrivs "^A". Tangenttryckningen Ctrl-A skrivs "C-A". Det är viktigt att skilja på dessa två termer. Ett escape-tecken (ASCII-kod 27 decimalt) skrivs "$". TECO-funktionernas namn består oftast av endast ett tecken; en mycket trevlig egenskap som gör att man slipper få COBOL-fingrar. En populär övning är att skriva sitt namn och se vad TECO hittar på för roligt med det. För bästa effekt bör man även ha med några escape-tecken i sitt namn... Normalt är dock inte TECO-program SÅ läsbara. För oinvigda ser det mer ut som krypterad text. De flesta TECO-funktioner returnerar ett, eller flera, argument som skickas vidare till funktionen som kommer efter på raden. Funktioner tar alltså in argument "till vänster" och lämnar ut resultat "till höger". Ett utelämnat argument räknas som "1". Anropet "L" ger alltså samma resultat som "IL". Normalt bör man specificera argumentet explicit om man inte riktigt vet vad framförliggande funktion lämnar efter sig. Alternativt kan man göra ett radbyte, eftersom alla argument försvinner vid radbyte. Slutligen finns också funktionen W, som sväljer sitt argument utan att lämna nåt efter sig. De flesta små TECO-snuttar kan direkt testas i en minibuffert (som man får med Escape). Varför inte prova följande klassiker från del 1, ett makro (samling av TECO-kod) som raderar de första 15 tecknen på varje rad i hela bufferten: BJ < 15D 1L.-Z; > B returnerar första positionen i bufferten (normalt 0). J aktuell position i bufferten sätts till J:s argument. <...> Oändlig repetition (eftersom < saknar argument). 15D raderar 15 tecken åt höger. IL går till början på raden 1 steg under nuvarande. returnerar "aktuell" "position. " - vanlig subtraktion (jo, det är säkert! !). Z returnerar positionen för sista tecknet i bufferten. ; Avbryter repetitionen om argumentet (.-Z) >= 0. Egentligen hade jag inte behövt skriva BJ eftersom enbart J utan argument ger samma resultat. Tangentbordsavkodning Hittills i kursen har jag lämnat EMACS utanför det hela, men nu är det dags att gå in på samspelet mellan EMACS och TECO. Tangentbordet har nämligen inte så mycket med TECO att göra, men eftersom man nästan alltid programmerar TECO för att göra nåt roligt i EMACS måste man känna till den interna representationen. Konvertering. Du tror väl inte att man får in ASCII-kod? Nej, så lätt är det inte. Det beror på att EMACS ursprungligen utvecklades med tanke på terminalen "TV". Den hade en ctrl-knapp till vänster och en meta-knapp till höger. Det enda som hände när man höll ner en av de skiftangenterna och tryckte på en annan tangent var att en speciell bit sattes i den utmatade teckenkoden. Det innebär alltså att termlnalen gav niobitars-kod. Det gällde för ALLA tangenter, så man kunde t.ex. slå Ctrl-return och få en rolig kod. Och givetvis var då inte return och kontroll-M samma sak. Därför finns det en mycket krånglig konvertering av ASCII-koderna från vanliga terminaler till TECOs interna kod, som är nio bitar (vilket passar bra, eftersom fyra tecken blir ett 36-bitars ord). Vad är det då som händer när man trycker ner valfri tangent på terminalen? Teckenkoden skickas förstås över till datorn och så småningom till TECO. TECO känner till om terminalen har 7-, 8- eller 9-bitars kod och omvandlar den mottagna koden till den interna 9-bitarsrepresentationen. Den interna kod som tecknen från en "vanlig" terminal får är följande: Alla "speciella" tecken, som return, linefeed, escape osv har de koder som vi är vana (normal ASCII-representation). Likaså alla bokstäver och vanliga tecken. MEN: alla kontrolltecken som inte är speciella (se ovan) är konverterade, så att en extrabit är satt. Motsvarande för meta. Det här innebär alltså att om jag trycker ctrl-A på tangentbordet representeras det som ASCII-koden för bokstaven A med åttonde biten satt. Men trycker jag ctrl-M blir den interna representationen samma som för return, dvs 13 decimalt! TECO kan ju inte veta om vi tryckte på ctrl-M eller tangenten return, eftersom terminalen ger samma kod ut. Eftersom våra vanliga terminaler inte har någon meta-tangent har man infört konventionen att ett tryck på tangenten escape följt av ett annat tecken representeras som det sista tecknet med den nionde biten satt. Det finns också ett tecken som fungerar som ctrl-prefix och det är normalt C-^. "C-^A" är alltså samma som "C-A". Kanske kan verka lite onödigt, men med den kan man generera t.ex. C-return, som annars inte går på vanliga terminaler. Det tecken som har både ctrl- och meta-biten satta kallas för kontrollmeta-tecken. Prefixet för C-M- är C-Z. Således: C_^ Sätter åttonde biten på efterföljande tecken (ctrl-prefix). escape Sätter nionde biten på efterföljande tecken (meta-prefix). C-Z Sätter både bit 8 & 9 på efterföljande tecken (ctrlmeta-prefix). Vad händer sedan? När tecknet nu har konverterats anropar TECO den definition som finns för tangenten. Definitionen är TECO-kod (eller en pekare till TECO-kod) och den exekveras. De TECO-makron som anropas brukar kallas ^R-makron, eftersom de exekveras i "skärmmod", vilket EMACS-TECO alltid befinner sig i. ^R-beteckningen lever kvar från DEC-TECO-tiden, då kommandot för att gå över i skärmmod var just ^R. Du kan titta på vilken TECO-funktion (namngivet makro) som exekveras för en viss tangent genom att ge hjälptecknet (normalt C-_ eller C-?) följt av bokstaven C. Normalt börjar namnen på dessa funktioner med "^R", men det är bara en konvention och inget krav. Övningsuppgift: försök lista ut vilken funktion som anropas när man trycker på escape-tangenten, dvs den funktion som sköter om att nionde biten sätts på efterföljande tecken! Ledning: namnet innehåller strängen "prefix". Omdefinition Varje "tangent" (även C-, M-definitionerna, etc) är åtkomlig som ett Q-register Man kan alltså lägga in sin egen TECO-kod på vilken tangent man vill. Tyvärr är det lite knepigt att referera till rätt Q-register. Q-registernamnen innehåller normalt ett "^R", dvs tecknet som man får genom att skriva "C-Q C-R" i EMACS. En punkt framför ^R indikerar ett C-tecken, två punkter ett M-tecken och tre punkter ett C-M-tecken. Exempel: ^RA Den "vanliga" bokstaven A. .^RF "C-F" ..^R* "M-*" ...^RZ "C-M-Z" Den vakne iakttagaren märker kanske att man inte kan komma åt t.ex. tecknen return och tab på det här sättet. För att kunna definiera om alla tecken med koder under 32 decimalt KAN man använda t.ex. ^R^M för return, men det är vanligare att man använder tecknet ^^ i stället för ^R. ^^ gör en exklusiv eller (XOR) med tecknet och 100 oktalt, vilket fimpar sjunde biten. Fler exempel: ^^I Tab. ^^M Return. ^^I C-Tab (kan genereras endast med C-prefixet C-^ i EMACS). Observera också att man kan definiera om C-p och att det INTE är samma som C- P! ! Kontroll-"lilla P" kan man ju inte slå på vanliga terminaler. När man definierar om kontroll-"stora P" definieras samtidigt kontroll-"lilla P" om, men inte vice versa. Man definierar därför normalt endast med stora bokstäver. Exempel Vi ska väl ha lite roligt! Vi kan t.ex. definiera om tangenten "f' till att skriva in tecknet "j" i bufferten! Prova det här i en minibuffert: @:I^Rf/ Ij$ / Jag lägger alltså in TECO-kommandot "Ij$" i Q-registret ^Rf, vilket är just tangent "f'. Tryck på tangent "f' och njut. Jaså, inte bra? EMACS skriver om hela raden som markören står på? Jo, det beror på att EMACS inte vet exakt hur mycket av raden som din nya definition ändrar på. Därför finns en möjlighet att returnera två parametrar som anger mellan vilka teckenpositioner som ändringar har gjorts i bufferten. EMACS litar ganska blint på detta, så det gäller att veta vad man returnerar. Du kommer väl ihåg att man returnerar från en funktion (eller makro) med "^\"? Argumenten som ges till ^\ kommer att returneras från den aktuella funktionen (makrot). Exemplet blir då i stället: @:I^Rf/ Ij$ .-1,. ^\ / Nu ser det väl bättre ut? Tänk på att om du vill återställa "f' så kan du göra "M-X SetKey^R Self Insert" och sedan svara med ett 'f' och ett "Y". Initieringsfiller När man kör TECO under EMACS läser den ingen egen initieringsfil. Däremot läser den EMACS initieringsfiler. Det finns två stycken: EMACSINIT Innehåller godtycklig TECO-kod och exekveras som sådan. EMACS.VARS "Enklare" format; varje rad "interpreteras". Som nybörjare bör man inte experimentera med EMACS.INIT. EMACS.VARS räcker för många enklare tillämpningar och jag tänkte därför beskriva den. EMACS.VARS Är närmast avsedd för definition av variabler (vilket syns på namnet). Att sätta en variabel till ett värde görs genom att lägga in rader enligt följande exempel: Display Mode Line Inverse: 1 Echo Area Height: 1 Om variabeln redan existerar sätts värdet, annars skapas den dessutom. En del av de variabler man sätter kan sedan påverka EMACS funktion, som de två ovan. För att titta på de "standardvariabler" som påverkar vissa grundläggande funktioner i EMACS kan du göra "M-X Edit Options$", som du går ur med C-M-Z. Man kan också exekvera TECO-kod, om man kan få plats med den på en rad. Då börjar man med variabeln "*". Om vi t.ex. alltid vill ha "f' omdefinierad: *: @:I^Rf/ I j$ .-1,. ^\ / Det vanligaste är väl att man alltid vill ha några bibliotek inladdade när man startar EMACS: *: M(M.m Load Library$)TMACS$ *: M(M.m Load Library$)VT100$ Man kanske alltid vill börja med Swedish Mode och svensk teckenuppsättning: *: M(M.m Swedish Mode$) *: M(M.m VT100 S$) Det finns också en möjlighet att direkt definiera tangenter. Man anger då tangentens Q-registernamn i stället för variabelnamn. Det som står som definition kommer först att exekveras av TECO och resultatet av den exekveringen läggs i tangenten. Det görs för att den vanligaste användningen är att man lägger någon fördefinierad funktion på en tangent, så här: ..^R+: M. m Push to EXEC$ Först exekveras "M.m Push to EXEC$". Som du kommer ihåg (del 2) returnerar M.m en pekare till funktionen. Denna pekare läggs alltså sedan in i tangenten. Resultatet blir att funktionen "Push to EXEC" kommer att anropas vid tryck på M-+. Det hade fungerat även om TECO inte hade exekverat M.m ovan, men då hade man varit tvungen att skriva "M(M.m Push to EXEC$)"i stället. Nackdelen med det är att det i så fall lagras en pekare till strängen "M (M. m Push to EXEC$)"i tangenten. Vid ett tryck på M-+ kommer alltså först "M(M.m Push to EXEC$)" att exekveras, som sedan i sin tur exekverar Push To EXEC. Slött!! Slutledning Det var allt för den här gången. Det finns mycket roligt kvar att berätta om, så det blir säkert en del till av TECO-kursen. Jag hoppas att du får både glädje och nytta av det jag har presenterat. Alltid välkommen med frågor! Seriestripp: Dippy digits by LEON finns längst ner på samma sida sidan 10. (Historie-redak. anm.)