Laboration Datorteknik TSIU02 2. I/O-programmering Stefan Gustafsson version 1.1
1. Inledning Laboration: I/O-programmering Du skall i denna laboration programmera TUTOR till att signalera i Morsekod. Det är som alltid viktigt att du förbereder den här laborationen väl innan du kommer till lablokalen. En del av programmet du skall knappa in får du färdigskrivet i styckena nedan. En del får du dock skriva själv, och detta skall förberedas så noga du kan i förväg. På laborationen skall du sätta ihop alltsammans till en fungerande helhet och provköra det, eventuellt felsöka det tills det fungerar som avsett, samt slutligen göra vissa mindre ändringar. De egentliga laborationsuppgifterna finner du i avsnitten 11, 12 och 13. Resten av denna laborationshandledning är förberedelseuppgifter och information. 2. Tonsignalering med binära utgångar TUTOR-kortet har bara digitala utgångar, och man kan fråga sig hur man sänder ut en ton på en sådan. Svaret är att det går alldeles utmärkt att ansluta en örsnäcka till en digital utgång som man växlar mellan hög och låg nivå i snabb takt. Det blir ingen vacker ton, men det låter i alla fall. Du skall använda någon av utgångarna på parallellporten. Det finns många att välja bland, men du behöver bara en. Koppla in den ena av örsnäckans anslutningar till utgången du valt. Koppla den andra anslutningen till matningsspänningen (5V). 1 Stoppa inte in örsnäckan i örat när du senare skall provköra ditt program! En 5 volts fyrkantvåg ger en stark och rå ton som bäst avlyssnas på litet håll. Låt örsnäckan ligga på bordet så slipper du bli lomhörd. 3. Morsekoden Morsekoden är faktiskt ett mycket tidigt exempel på en kod för asynkron seriell dataöverföring. Den konstruerades redan på 1800-talet av amerikanen Samuel Morse. Morsekoden består av korta och långa tonsignaler som i olika kombinationer betecknar olika tecken. En kort signal ( prick ) är 1 tidsenhet lång, och en lång signal ( streck ) 3 tidsenheter. Mellan signalerna skall det vara 1 tidsenhets tystnad. Mellan tecken skall det vara 3 tidsenheter tystnad, och mellan ord skall det vara 7 tidsenheters tystnad. Tidsenheten det talas om här bör vara ganska lång med datormått mätt, omkring 50 ms, för att koden skall kunna uppfattas av människor. Morsekoden återges i tabellen på nästa sida, tillsammans med en lämplig binär kodning i 8 bitar av följden av prickar och streck. Binärkoden läses från vänster bitvis. En etta motsvarar ett streck, och en nolla motsvarar en prick. Morsetecknen är olika långa, så längden måste markeras på något sätt. I kodningen i tabellen är ettan längst till höger i byten en slutmarkering, som alltså inte ingår i själva Morsekoden. Tanken med denna kodning är att man skall läsa av bit för bit genom att skifta byten ett steg i taget åt vänster och låta innehållet i C-flaggan bestämma om man skall sända ut prick eller streck. När den sista ettan skiftats ut i C-flaggan är byten noll, och man kan på så sätt detektera när tecknet är slut. 1. Du kan koppla den andra anslutningen till jord om du vill, men digitala utgångar är oftast bättre på att leda mycket ström in i kretsen vid låg nivå än ut ur kretsen vid hög nivå, så du belastar inte PIA-kretsen lika hårt om du kopplar örsnäckan mellan utgången och 5V.
Tkn ASCII Morsekod Binärkod Hex Tkn ASCII Morsekod Binärkod Hex A $41.- 01100000 $60 Å $5D.--.- 01101100 $6C B $42 -... 10001000 $88 Ä $5B.-.- 01011000 $58 C $43 -.-. 10101000 $A8 Ö $5C ---. 11101000 $E8 D $44 -.. 10010000 $90 0 $30 ----- 11111100 $FC E $45. 01000000 $40 1 $31.---- 01111100 $7C F $46..-. 00101000 $28 2 $32..--- 00111100 $3C G $47 --. 11010000 $D0 3 $33...-- 00011100 $1C H $48... 00001000 $08 4 $34...- 00001100 $0C I $49.. 00100000 $20 5 $35... 00000100 $04 J $4A.--- 01111000 $78 6 $36 -... 10000100 $84 K $4B -.- 10110000 $B0 7 $37 --... 11000100 $C4 L $4C.-.. 01001000 $48 8 $38 ---.. 11100100 $E4 M $4D -- 11100000 $E0 9 $39 ----. 11110100 $F4 N $4E -. 10100000 $A0. $2E.-.-.- 01010110 $56 O $4F --- 11110000 $F0, $2C --..-- 11001110 $CE P $50.--. 01101000 $68 : $3A ---... 11100010 $E2 Q $51 --.- 11011000 $D8 ; $3B -.-.-. 10101010 $AA R $52.-. 01010000 $50? $3F..--.. 00110010 $32 S $53... 00010000 $10 $27.----. 01111010 $7A T $54-11000000 $C0 - $2D -...- 10000110 $86 U $55..- 00110000 $30 / $2F -..-. 10010100 $94 V $56...- 00011000 $18 ( $28 -.--. 10110100 $B4 W $57.-- 01110000 $70 ) $29 -.--.- 10110110 $B6 X $58 -..- 10011000 $98 $22.-..-. 01001010 $4A Y $59 -.-- 10111000 $B8 = $3D -...- 10001100 $8C Z $5A --.. 11001000 $C8 4. Programmets allmänna struktur Uppgiften sänd ut en sträng i Morsekod måste brytas ner i hanterligare deluppgifter om man skall kunna programmera TUTOR för detta. En lämplig uppdelning i successivt mindre deluppgifter visas i tabellen på nästa sida. Kolumnen längst till vänster beskriver huvudproblemet. För varje kolumn delas vissa komplicerade uppgifter upp i nya delproblem. Längst till höger i varje rad har vi brutit ner problemet i så pass enkla uppgifter att vi kan programmera dem direkt i assembler. Denna successiva uppdelning i delproblem lämpar sig mycket väl för att programmeras som en samling subrutiner i flera nivåer. Vi sätter namn på de subrutiner som behövs, och behandlar dem i tur och ordning i följande stycken. Översätt ett tecken till Morsekod Vänta en halv period Sänd ett streck eller en prick Sänd ett tecken Huvudprogram: starta, sänd ut sträng, avsluta LOOKUP DELAY BEEP SEND MAIN För att du och eventuellt också assistenten skall hitta i TUTOR:s minne rekommenderas att du lägger dina subrutiner, dina data och din stack på följande adresser. MAIN $2000 Sträng $6000 LOOKUP $2100 Morsetabell $7000 SEND $2200 T för DELAY $7100 BEEP $2300 N för BEEP $7104 DELAY $2400 Stackpekare $8000
Nivå 1 (sträng) Start Sänd ut en sträng i Morsekod Slut Nivå 2 (tecken) Hämta ett tecken. Om strängen är slut, sluta. Översätt tecknet till Morsekod Sänd ut tecknet Hämta nästa tecken Nivå 3 (streck/prick) Kolla om vi skall sända ut ett streck eller en prick. Om tecknet är färdigt, hoppa ur. Om prick: sänd kort ton och kort tystnad Om streck: sänd lång ton och kort tystnad. Sänd ut nästa streck/prick Nivå 4 (ton/paus) Ladda register med N. Om streck, multiplicera registret med 3. Sänd ut en ton vars längd bestäms av registrets innehåll. Ladda register med N. Sänd ut en paus vars längd bestäms av innehållet i registret. Nivå 5 (enstaka perioder) Sänd ut en period av tonen. Räkna ned i registret. Om noll, hoppa ur, annars upp och sänd en period till. Sänd ut en period tystnad. Räkna ned i registret. Om noll, hoppa ur, annars upp och sänd en period till. Nivå 6 (utbit 1/0) Sätt utgången till 1 Vänta en halv period T. Sätt utgången till 0 Vänta en halv period T. Vänta en hel period 2T.
5. LOOKUP: Översättning til Morsekod För att översätta ett tecken från ASCII till Morse använder man lämpligtvis tabellslagning. Med hjälp av en tabell med början på adress $7000 enligt nedan, så kan man enkelt översätta från ASCII till Morse genom att läsa talet på den position i tabellen som anges av ASCII-koden. Adress Innehåll Kommentar $7000 $00 Ej skrivbara tecken... $00 $7040 $00 $7041 $60 A $7042 $88 B $7043 $A8 C $7044 $90 D $7045 $40 E $7046 $28 F... $00 $705A $C8 Z $705B $58 Ä $705C $E8 Ö $705D $6C Å $705E $00 Tecken som inte är skrivbara eller inte har någon motsvarighet i Morsekoden, samt tecken som man helt enkelt inte orkar mata in, markeras lämpligen med värdet 0 i tabellen. Tabellen matas lämpligen in med kommandot MM, eventuellt med hjälp av kommandot BF för att nollställa hela tabellen (256 bytes) innan man börjar mata in data. För att slå i tabellen kan följande subrutin användas. ASCII-koden för ett tecken skall ligga i D0 (8 bitar) vid anropet. Morsekoden returneras i D0. LOOKUP: LEA $7000,A1 ; Peka på tabellen AND.W #$00FF,D0 ; Gör D0 till 16 bitar MOVE.B (A1,D0.W),D0 ; Hämta Morsekoden RTS 6. DELAY: Tidsfördröjning Följande programsnutt ger en tidsfördröjning vars längd beror på talet på adressen $7100. DELAY: MOVE.L $7100,D3 ; Hämta fördröjning T WAIT: SUBQ.L #1,D3 ; Räkna ned med 1 BNE WAIT ; Om D3<>0: räkna vidare RTS ; Hoppa tillbaka Du måste förstås byta ut den symboliska hopppadressen WAIT i hoppinstruktionen mot den faktiska adress som instruktionen SUBQ.L hamnar på när du skriver in programmet. Detsamma gäller för alla andra symboliska hopadresser ( labels ) i det följande. För kommentarer om hur man matar in hopp framåt till okända adresser, se häftet TUTOR M68008 system och assembler, avsnitt 5.9. Med talet $000000DA (218 decimalt) på adressen $7100 tar rutinen DELAY ungefär 1 ms. Vilken frekvens ger detta för tonen som sänds ut av BEEP?
7. BEEP: Sänd ut ton eller paus En ton sänds ut med hjälp av följande rutin. Ett antal perioder som anges av innehållet i registret D1 sänds. För att inte behöva skriva en nästan likadan subrutin till låter vi rutinen även kunna sända ut en stunds tystnad. För ton sätts D2 till 1 före anrop, för tystnad sätts D2 till 0. BEEP: BSR DELAY BSR DELAY SUBQ.L #1, D1 BNE BEEP RTS ; Om D2=1: ettställ utbiten, ; annars nollställ utbiten. ; (Tips: instruktionen Scc!) ; Vänta en halv period ; Nollställ utbiten ; Vänta en halv period ; Räkna ned D1 ; Om D1>0: fortsätt pipa Notera att den eller de instruktioner som krävs för att ettställa och nollställa utbiten utelämnats. Vilka instruktioner som skall användas beror på vilken utgång du väljer att använda. Det är din uppgift att välja en lämplig utgång och fylla i dessa instruktioner. 8. SEND: Sänd ut ett tecken För att sända ut ett tecken kan följande rutin användas. Morsetecknet i binär representation enligt tabellen i avsnitt 5 skall ligga i D0 vid anropet. SEND: MOVE.L $7104,D1 ; Hämta antal perioder N för prick LSL.B #1,D0 ; Skifta upp nästa symbol i Morsekoden BEQ READY ; Om D0=0 är tecknet slut BCC DOT ; C=0: prick, C=1: streck DASH: MULU #3,D1 ; Multiplicera D1 med 3 DOT: MOVE.B #1,D2 ; Ladda D2 med 1 för ton BSR.L BEEP ; Sänd ut prick/streck MOVE.L $7104,D1 ; Hämta N igen MOVE.B #0,D2 ; Ladda D2 med 0 för paus BSR.L BEEP ; Sänd paus efter prick/streck BRA SEND ; Sänd ut nästa prick/streck READY: ASL.L #1,D1 ; Öka D1 till 2N MOVE.B #0,D2 ; Ladda D2 med 0 för paus BSR.L BEEP ; Sänd ut extra paus efter tecknet RTS ; Hoppa tillbaka från subrutinen Ett tal N hämtas från adress $7104. Detta tal anger hur många perioder BEEP skall sända ut för en kort ton. Vilket tal skall lagras på denna adress om man vill ha en kort ton av längden 100 ms? 9. MAIN: Huvudprogrammet Huvudprogrammet skall börja med att sätta stackpekaren till $08000 och programmera parallellporten så att den utgång du valt verkligen fungerar som utgång. De anslutningar som inte används bör sättas till ingångar, om inte annat så för att kolla att du träffat rätt med bitadresserna i ditt program. För information om hur man programmerar parallellporten, se häftet TUTOR M68008 system och assembler, avsnitt 4.5.
Du skall själv skriva huvudprogrammet, men en del hjälp får du av skelettet nedan. MAIN: MOVE.B #$04,$10084 MOVE.B #$04,$10086 Som synes blir huvudprogrammet tämligen kort och överskådligt, eftersom det mesta av detaljarbetet sker i subrutiner, som i sin tur är tämligen lättbegripliga eftersom de också anropar subrutiner för nästa mer detaljerade nivå av databearbetning. De flesta subrutiner används visserligen bara på ett ställe i koden och skulle därför kunnat skrivas in i själva huvudprogrammet, men det blir betydligt lättare att få en överblick över programmet på det här sättet, och den extra tid som några subrutinanrop tar är i det här fallet helt försumbar. Med tanke på den mycket begränsade editorn i TUTOR är detta också det lämpligaste sättet att skriva in programmet: som små åtskilda snuttar som gör att man inte måste skriva om stora stycken om man behöver skjuta in en instruktion någonstans i efterhand. 10. Lagring av strängen som skall sändas Den sträng du skall sända ut skall lagras med början på adress $6000 och avslutas med tecknet NUL (ASCII-kod $00). Du kan exempelvis lagra strängen HEJ HOPP på detta format med TUTOR-kommandot: MS 6000 HEJ HOPP 00 11. Testkörning av programmet ; Sätt stackpekaren ; Programmera PIA: nollställ CRA ; och nollställ CRB ; Programmera DDRA och DDRB. ; En bit utgång, resten ingångar ; Inga avbrott skall genereras, ; varken från port A eller port B ; Sätt utbiten till 0 LEA $6000,A0 ; Sätt A0 till att peka på strängen NEXTCH: MOVE.B (A0)+,D0 ; Hämta nästa tecken ; Om D0=0: sträng slut, gå till END ; Annars: anropa LOOKUP ; Kolla det returnerade värdet i D0 BEQ NEXTCH ; Om D0=0: gå till NEXTCH ; Annars: anropa SEND ; Gå till NEXTCH för nästa tecken END: MOVE.B #228,D7 ; Gå tillbaka till TRAP #14 ; monitorn i TUTOR Mata in hela programmet och provkör det. Om det inte fungerar skall du leta upp felet själv. Kolla att programmet är rätt inmatat, och att du matat in alla data som behövs. Kolla att alla hopp sker till rätt adresser. Kolla att du valt rätt bit som utgång, att du kopplat in örsnäckan rätt till denna bit och att utgången verkligen byter värde när du sätter biten i utregistret till 1 respektive 0. Om du kör programmet med TR skall det höras ett klick i örsnäckan när utgången växlar från 0 till 1 eller tvärt om. Om du misstänker att örsnäckan är trasig, koppla in den till matningsspänningen (den går inte sönder av det). Precis när du kopplar in den skall du höra ett knäpp eller ett frasande. Annars är den trasig och behöver bytas ut. Om felet ligger i ditt program, använd kommandona GO och BR, eventuellt GT samt det mycket användbara kommandot TR för noggrann kontroll tills du hittar felet. Om du misslyckas, försök igen. Om du misslyckas trots upprepade försök, fråga assistenten om hjälp. Vänta dig emellertid inte att du skall få annat än allmän hjälp och goda råd. Det är inte assistentens uppgift att felsöka ditt program. När du fått ditt program att fungera, sänd ut några olika meddelanden.
12. Ändring av sändhastighet När du fått ditt program att fungera skall du göra följande enkla ändringar: 1. Ändra hastigheten för sändning, antingen till långsammare eller snabbare hastighet. Frekvensen hos den utsända tonen skall inte ändras. 2. Ändra frekvensen hos den utsända tonen till en något högre eller något lägre frekvens. Sändhastigheten skall inte ändras. För ovanstående ändringar behöver du inte ändra i någon programkod! 13. Hantering av mellanslag Tänk efter vad som händer när ditt program träffar på ett mellanslag i strängen. Hur lång blir pausen mellan två ord? Är detta rätt enligt Morsekodens definition i stycke 3? Lägg in Morsekoden $80 för mellanslag (ASCII-kod $20) i din tabell. Hur lång blir pausen mellan två ord nu? Är detta rätt längd? Är det bättre än förut? För att hantera mellanslag helt korrekt kan man antingen kolla tecknet innan man gör tabellslagningen med LOOKUP och särbehandla ett mellanslag, eller också kan man behandla alla okända tecken (Morsekod $00 efter tabellslagningen) som mellanslag. Den senare metoden är enklast. Du skall avslutningsvis i laborationen göra denna utökning av ditt program. Följande programsnutt skall skrivas och läggas till i slutet av ditt huvudprogram. Du kan lägga till den efter instruktionen TRAP #14 om du vill - du behöver alltså inte flytta några av de instruktioner du redan skrivit. Lägg den dock i omedelbar anslutning till ditt huvudprogram. Sedan behöver du bara ändra i en enda hoppinstruktion i det tidigare huvudprogrammet för att få mellanslag att hanteras korrekt. Vilken, och till vad, får du fundera ut själv. (Om du ändrade koden för mellanslag till $80 förut skall du dessutom ändra den tillbaka till $00 igen.) BLANK: ; Ladda D1 med N ; Öka D1 till 4N ; Ladda D2 med 0 för paus ; Sänd ut extra paus efter ord ; Gå till NEXTCH för nästa tecken