Minnes-mappad I/O

Innehåll

  1. Förberedelser
  2. Uppgifter
  3. Redovisning
    • Tyck till

      I tidigare uppgifter har vi använt "fusket" syscall för att skriv ut strängar och heltal. Det har nu blivt dags att kolla under huven och se hur vi själva kan konstruera liknande rutiner med hjälp av minnes-mappad I/O.

      Senast uppdaterat: Tue Nov 29 17:24:26 CET 2005

      ~ karl

      Förberedelser

      Innan ni fortsätter skall ni läsa igenom avsnitt A.8 "Input and Output" i "Assemblers, Linkers, and the SPIM Simulator". I detta avsnitt beskrivs hur SPIM simulerar en så kallad minnes-mappad I/O-enhet. Titta även igenom era föreläsningsanteckningar.

      Minnes-mappad I/0

      Anledningen till att det kallas minnes-mappad I/O är att I/O-enheten kontrolleras av ett antal register. Men, till skillnad från riktiga register (t.ex. $t0) är inte detta några riktiga register utan varje kontrollregister representeras av ett ord i minnet.

      Hur går det egentligen till när man läser in en sträng som matas in via tangentbordet? I SPIM är I/0-enheten mycket enkel och kan endast lagra ett tecken åt gången. För att läsa in en hel sträng måste vi alltså läsa in ett tecken i taget.

      I spim kontrolleras den minnesmappade I/0-enheten av följande fyra minnesmappade register:

      Vid inläsning från tangentbordet använder vi oss av de minnesmappade registren Receiver Controll och Receiver Data. När en tangent trycks ner ändras den minst signifikanta biten i Receiver Controll från 0 till 1 och ASCII-värdet av det inlästa tecknet lagras i den minst signifikanta byten i Receiver Data.

      Pollad I/O

      Full rulle

      Innan det fans något som ens liknade en bildskärm gick det ändå att mata in data och erhålla resultat från dåtidens "data-maskiner". En vanlig metod var att lagra program och data på så kallde hålkort av papper. För varje punkt på pappret kan en bit lagras (hål eller inte hål). Resultat kunde sedan skrivas ut på någon typ av skrivare.

      Ett annat exempel på ett mekaniskt I/0-system är det självspelande pianot eller som det egentligen heter pianola. Indata (noter) kodas som en serie av hål på en pappersrulle. Utdata (musik) uppstår när pianolan avkodar (spelar) de på pappersrullen kodade noterna.

      I denna uppgift kommer vi inte att använda oss av avbrottstyrd I/0 och detta innbär att alla interrupt enable-bitar kommer att vara satta till 0.

      För att upptäcka när ett tecken hamnat i Receiver Data behöver vi endast kolla om innehållet i Receiver Controll är skillt från noll. Hur och när skall vi kolla om det hamnat ett tecken i Receiver Data?

      Om vi först ber användare att mata in en sträng kan vi göra en loop som kollar värdet i Receiver Controll. När detta blir skillt från noll vet vi att ett tecken lästs in. Att med jämna mellanrum kolla värdet av till exempel ett kontrollregister brukar kallas att polla (av engelskans poll = "undersöka").

      Stora tal

      Om vi till exempel vill lagra talet fyra i register $t0 kan vi göra så här:

           addi    $t0, $zero, 4
      	   

      Varje I/O kontrollregister representeras av ett ord i minnet. Till exempel motsvarar Transmitter Data-registret av innehållet på adress 0xffff000c.

      [ visa svar ]

      Fråga

      Varför går det inte att göra:
          addi    $t0, $zero, 0xffff000c       # Address to Transmitter Data Registret
      	   

      För att att kontrollera om det skett en tangenttryckning måste vi läsa innehåller i Receiver Controll-registret, dvs läsa innehållet på address 0xffff000c. För att läsa något från en adress behöver vi stoppa in adressen i ett register.

      [ visa svar ]

      Fråga

      Om det inte går att läsa in ett stort tal till ett register med hjälp av immediate-instruktioner hur gör vi då?

      Uppgifter

      Ni skall bygga vidare på koden nedan. OBS: För att det skall fungera att köra spim med minnesmappad I/O måste spim startas med en speciell flagga:

      	   xspim -mapped_io -file uppgift_fyra.s 
      	   
      Börja med att ladda ner koden och provkör, kontrollera att ni inte får några felmeddelanden. Eftersom subrutinerna inte gör något alls kommer programmet inte att göra något, notera att programmet inte skriver ut några stängar med syscal. Ni skall själva skriva klart subrutinerna som möjliggör utskrift och inläsning av strängar.

      [ download ][ maximera ]

      uppgift_fyra.s

      ##############################################################################
      ##
      ## DESCRIPTION: Computer Systems 1 - part 1 (DARK)
      ##
      ##              Assignment 4: Polled, memory mapped I/0.
      ##
      ##
      ## AUTHOR(s):   <student_1234@student.uu.se>
      ##
      ##              <student_6789@student.uu.se>
      ##
      ##
      ## RUN:		xspim -mapped_io -file uppgift_fyra.s                          
      ## 
      ##		or
      ##
      ##		spim -mapped_io -file uppgift_fyra.s                          
      ##
      ##         
      ## HISTORY:     November XX, 2004. First version.
      ##
      ##############################################################################
      
      
      	
      ##############################################################################
      ## Data Segment
      ##############################################################################
      
      
              .data
      
      # Memory mapped I/0, i.e., each I/O device register appears as a
      # special memory location. These addresses are to large to use
      # as an immediate value so we store them as "constants" in memory.
      	
      RECEIVER_CONTROL:	.word 0xffff0000	
      RECEIVER_DATA:		.word 0xffff0004	
      
      TRANSMITTER_CONTROL:	.word 0xffff0008
      TRANSMITTER_DATA:	.word 0xffff000c	
      
      # A 80 character string buffer.
      		
      BUFF:   .space 80       
      
      # Some predefined strings:
      	
      MSG_1:	.asciiz "Enter a string: "
      MSG_2:	.asciiz "You entered   : "	
      NL:	.asciiz "\n"
      
      	
      ##############################################################################
      ## Text Segment
      ##############################################################################
      
      
      	.text
      
      	.globl main
      
      	.globl dbg
      	
      #-----------------------------------------------------------------------------
      # Main Start
      #-----------------------------------------------------------------------------
      
      
      main:   addi    $sp, $sp, -4    # Push return address.
              sw      $ra, 0($sp)
      	
      start:	
      	# "Enter a string:"
      
      
      	la	$a0, NL
      	jal	ouputString
      	
      	la	$a0, MSG_1
      	jal	ouputString
      
      	la	$a0, BUFF
      	jal	inputString
      
      	la	$a0, NL
      	jal	ouputString
      
      	# "You entered:"
      	
      	la	$a0, MSG_2
      	jal	ouputString
      	
      	la	$a0, BUFF
      	jal	ouputString
      
      	la	$a0, NL
      	jal	ouputString
      	
      	j start			# This makes the program loop forever. 
      	
      	lw	$ra, 0($sp)	# Pop return adress and return to caller (exit)
      	addiu	$sp, $sp, 4
      
      	jr	$ra
      
      #-----------------------------------------------------------------------------
      # Main End
      #-----------------------------------------------------------------------------
      
      
      
      #------------------------------------------------------------------------------
      # DESCRIPTION:  Reads one character from the console using polled memory 
      #		mapped I/O.
      #
      # OUTPU:        $v0 - ASCII code of the character read from the console.
      #------------------------------------------------------------------------------
      inputCharacter:
      	
      	# Check if the receiver is ready, if not,
      	# simple spin and poll until ready.
      dbg:		
      	
      	jr	$ra
      	
      #------------------------------------------------------------------------------
      # DESCRIPTION:  Print one character to the display using polled memory 
      #		mapped I/O.
      #
      # INPUT:        $a0 - ASCII code to print
      #------------------------------------------------------------------------------
      printCharacter:
      
      	# Check if the transmitter is ready, if not,
      	# simple spin and poll until ready
      
      
      	
      	jr	$ra
      	
      #------------------------------------------------------------------------------
      # DESCRIPTION:  Reads a string of characters from the console and store it as
      #		a NULL-terminated string in the buffer pointed to by $a0.
      #		The input string ends when the user presses ENTER.
      #
      #		This subroutine works by polling the memory mapped input 
      #		device.
      #
      # DEPENDENCIES:	Uses inputCharacter to read characters from the console.
      #
      #		Uses printCharacter to echo each typed character to 
      #		the console. Otherwise the user will no see what he/she
      #		types.
      #
      # INPUT:        $a0 - Address to first byte in buffer.
      #------------------------------------------------------------------------------
      inputString:
      
      
      	jr	$ra
      	
      
      		
      #------------------------------------------------------------------------------
      # DESCRIPTION:  Output a NULL-terminated string to the console.
      #
      #		This subroutine works by polling the memory mapped output 
      #		device.		
      #
      # DEPENDENCIES: Uses printCharacter to print each character one by one. 
      #	
      # INPUT:        $a0 - address to first character in null-terminated 
      #		      string to print
      #------------------------------------------------------------------------------
      ouputString:
      
      	jr	$ra
      
      
      

      Om ni kollar i början main ser ni att outputString anropas två gånger. Först en gång med strängen NL som endast innehåller tecknet för ny rad och sedan med strängen MSG_1.

      Tips

      En bra början är fundera igenom hur subrutinen outputString kan tänkas se ut. En sträng är inget annat än en array av bytes där varje byte utgör ett ASCII-kodat tecken. Om vi bara har ett sätt att skriva ut ett tecken kan vi sedan skriva en loop som kollar på varje tecken i strängen och skriver ut ett i taget tills strängen är slut.

      För att skriva ut ett tecken anropar vi subrutinen printCharacter. Har vi fått detta att fungera kommer vi fortfarande inte att se något vid en testkörning. Genom att anväda en brytpunkt i loopen (och sedan steppa) kan vi dock kontrollera att loopen fungerar (kontrollera att vi läser rätt ASCII-värden).

      Nu är det dags att skriva subrutinen printCharacter. Ni skall polla Transmitter Controll-registret och när transmittern är redo är det dags att skriva ASCII-värdet för det tecken man vill skriva ut till Transmitter Data-registret. Även här kan det vara praktiskt att sätta en brytpunkt, steppa och kolla. Särskillt intressant är att sätta en brytpunkt och kontrollera värdet på innehållet i Transmitter Controll-registret (ready bit).

      Trick: Transmitter Controll

      För att simulera att det tar tid att skriva ut via I/0-enheten gör SPIM så att ready-biten för Transmitter Controll-registret inte blir 1 (ready) omedelbart efter det att ett tecken skrivits ut på konsollen. Tiden som det tar att skriva ut ett tecken mäts i antalet exekverade instruktioner. Om man steppar kan man alltså behöva utföra flera varv i poll-loopen innan ready-biten blir 1.

      Ibland uppför sig I/O-enheten i SPIM lite konstigt och ready-biten blir alrdig 1 (ready) oavsett hur länge man kör poll-loopen. Genom att klicka på konsollen och trycka på valfri tangent händer något magiskt inne i SPIM som gör att ready-biten sätts till 1, dvs ready. (bug?).

      När ni fått subrutinenrna outputString och printCharacter att fungera skall strängarna skrivas ut på konsollen vid testkörning. Ni blir kanske tvunga att använda tricket ovan.

      Har man lyckats skriva en fungerande printCharacter rutin är det en lätt mach att skriva klart inputCharacter, den fungerar i stort sett på samma sätt.

      Sedan är det dags att använda inputCharacter och printCharacter för att skriva klart inputString.

      Om ni kollar i main ser ni att innan anropet till inputString så sätts $a0 att innehålla adressen till den första byten i bufferten BUFF. Subrutinen inputString skall inte känna till något om main utan endast bero på indata. Alltså skall det fungera lika bra att anropa rutinen med någon annan buffer.

      Observera att all kod ni skriver skall vara försedd med lämpliga kommentarer.

      Redovisning

      En testkörning med det färdiga programmet bör se ut ungefär såhär:

            Enter a string: Hej
            You entered   : Hej
      
            Enter a string: Monica
            You entered   : Monica
      
            Enter a string:              
      

      Innan ni lämnar in, visst har ni kollat att allt funkar för några olika strängar, speciellt intressant är förstås fallet tom sträng.

      Det är inte tillåtet att använda några syscalls för att lösa uppgiften.

      Tyck till

      Som vanligt vill jag veta vad du tycker om denna uppgift. Detta är viktigt för att vi tillsammans skall kunna fortsätta att göra kursen bättre!

      Som helhet tycker jag att uppgiften var:

      Bra
      Hyffsad
      Dålig

      Kommentar:

      Som helhet tycker jag att svårighetsgraden var:

      Lätt
      Lagom
      Svår

      Kommentar:

      Anledningen till att färdiga kodskellet används är att det skall vara lätt att komma igång med en uppgift. Genom att det finns ett färdigt som anropar de olika subrutinerna är det också enkelt att börja testa programmet.

      En invändning mot att använda kodskelett är att det ibland kan vara svårt att förså vad koden gör samt att det vore roligare eller mer lärorikt att skriva allt själv.

      Jag tycker att kodskellet:

      hälper
      stjälper

      Kommentar:

      Vad kan göras bättre:

      Något som du uppskattade speciellt:

      Tidsuppskattning

      Det tog ungefär att göra klart uppgiften

      Kommentar:

      Övrigt: