3. Hur man använder räckor i MISSE

3.1 Allmänt om räckor

Räckor (eng. array) kan visualiseras som N stycken konsekutiva dataceller i minnet, där alla celler är av samma storlek. I MIPS-assembler är det lätt att deklarera en array där cellstorleken är 8, 16 eller 32 bitar. Värdet för varje cell kan direkt anges iform av antingen decimala tal, hexadecimala tal eller tom. som flyttal i både enkel- och dubbel-precision (dock stödes ej flyttal ännu i MISSE).

Man allokerar celler med direktiven .byte, .half och .word, beroende på om man vill allokera 8, 16 eller 32 bitar resp. Dessa direktiv kan ta emot en eller flera kommaseparerade argument. Nu måste man ännu komma ihåg att .byte, .half och .word -direktiven är endast möjliga inne i Data (eller KData) segmentet.

	.data

	.byte	5				# upptar 1 byte
	.byte	1, 0xff, -7, 255, -128, 0xc	# upptar 6 byte

	.half	0xffff, 147, 42, -199		# upptar 8 byte
	.half	-44				# upptar 2 byte

	.word	0xffffeeee, -9, 0xf		# upptar 12 byte
	.word	9				# upptar 4 byte

Ovannämnda exempel allokerar en 7 elements byte-array, en 5 elements halvords-array och en 4 elements ord-array och som dessutom är organiserade efter varandra i minnet. Nu är det förståss inget som explicit säger att vi har skapat just skillda byte-, halvord- och ord-arrayer, utan det är helt upp till programmeraren att avgöra hur han tolkar datan i minnet.
Det är alltså inget i assemblerspråket som förbjuder oss att tolka allting som en enda stor byte/halvord/word array.

3.2 Hämtning av data från minnet

Nu är det på sin plats att vi tittar på hur man kan accessera datan vi lagrat i Data segmentet. Som du säkert är bekant med så har MIPS-assemblern 5* instruktioner för att hämta data från minnet. Dessa är :

  • lb och lbu -- hämtar en byte från minnet.
  • lh och lhu -- hämtar ett halvord från minnet.
  • lw -- hämtar ett ord från minnet.
    Instruktionerna ovan som slutar med u utför ingen teckenförlängning till 32-bitar. u står förövrigt för unsigned, dvs. utan förtecken.
    (* Det finns ytterligare instruktioner för hämtning av data ifrån minnet, men dessa faller utanför DatorTeknik-kursen.

    Instruktionerna för att lagra data i minnet är :
  • sb -- lagrar en byte i minnet.
  • sh -- lagrar ett halvord i minnet.
  • lw -- lagrar ett ord i minnet.
    Märkväl att instruktionerna för lagring av data i minnet inte har några u -varianter.


    Följande exempelkod illustrerar hur man hämtar data från minnet :
    	.data				# Data segmentet kommer automatiskt att starta från adress 0x10000000
    
    	.byte	10, -1, -5, 7, 99, 0
    	.word	0x1234abcd
    
    	.text				# Text segmentet börjar från 0x00400000
    	.globl main
    main:
    	lui	$a0, 0x1000		# Övre 16 bitarna av adressen 0x10000000 hamnar i register $a0.
    	ori	$a0, $a0, 0x0000	# Denna instruktion är helt onödig i detta fall eftersom lägre halvan
    					# av 0x10000000 ju är 0x0000 och togs med bara för att illustrera lagringen av
    					# de 16 lägsta bitarna av adressen 0x10000000.
    					# Noteras bör dock att lui -instruktionen automatiskt tömmer lägre halvan
    					# av registret.
    
    	lbu	$t0, 0($a0)		# Hämtar första byten ur räckan, dvs. värdet 10, till register $t0.
    	lbu	$t1, 1($a0)		# Hämtar andra byten till $t1.
    
    	addi	$a0, $a0, 2		# Nu ökar vi adressen i register $a0 med 2 byte framåt
    	lbu	$t2, 0($a0)		# så att lbu nu kommer att läsa tredje byten ur vår räcka, dvs. värdet -5.
    
    	addi	$a0, $a0, 4		# Vi ökar igen pekaren med 4 så att vi hamnar på adressen 0x10000006, dvs. dit vårt ord är lagrat.
    	lw	$t3, 0($a0)		# Vi kunde också ha skippat instruktionen på raden ovanför och på denna rad istället 
    					# använt lw $t3, 4($a0) för att nå ordet.
    	.
    	.
    	.
    

    Som du säkert märkte så var det väldigt krångligt att nå ordet 0x1234abcd, eftersom vi måste veta exakt vilken adress det finns lagrat på. Förståss kan en erfaren programmerare se på vilken adress ett visst ord finns på, men i längden blir det ändå jobbigt.

    3.3 Labels som hjälpmedel

    Nu ska vi ta upp ett par finesser i MIPS-assemblern som underlättar åtkomst till godtycklig data i minnet.
    Dels ska vi deklarera en label på raden vi har allokerat vår data på och dels ska vi använda två macros, %hi_addr() och %lo_addr(), för att hämta adressen för en label.

    	.data
    
    	.byte	10, -1, -5, 7, 99, 0		# En godtycklig byte-array för att göra adressen för nedanstående
    						# ord jobbigare att leta fram.
    
    ordet:	.word	0x1234abcd			# Vi har nu döpt adressen för vårt ord till "ordet".
    
    	.text
    	.globl main
    main:
    	# Vi börjar med att hämta adressen för vårt ord med följande två rader :
    
    	lui	$a0, %hi_addr(ordet)		# Lagrar övre 16 bitarna ...
    	ori	$a0, $a0, %lo_addr(ordet)	# och nedre 16 bitarna av adressen till $a0.
    
    	# Sedan hämtar vi ordet från minnet till register $t0
    
    	lw	$t0, 0($a0)
    
    	.
    	.
    	.
    

    Som du märker så behöver vi nu inte alls veta vilken adress ordet finns på, tackvare användningen av %hi_addr och %lo_addr.

    3.4 Teckensträngar

    Nu har vi bara diskuterat hur man deklarerar heltal i minnet. Det finns dock gånger då man också vill kunna skapa textsträngar. Förståss kan vi göra det obekvämt genom att lagra varje tecken med sitt respektiva ASCII-värde i en byte-räcka, men det blir väldigt jobbigt i längden och vi skulle då också behöva ha tillgång till en ASCII-tabell.
    Som tur så finns det direktiv i MIPS-assembler för just detta ändamål, nämligen .ascii och .asciiz. Skillnaden mellan dessa två är att .asciiz kommer att lagra en extra nolla i slutet av strängen.
    Du är säkert bekant med att t.ex. C-språket terminerar sina strängar med en nolla i slutet.

    Följande exempel illustrerar allokering av strängar i MIPS-assembler :
    	.data
    
    	.byte	72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 0	# Ett väldigt jobbigt sätt att deklarera en sträng.
    
    	.asciiz	"Hello World"						# Samma textsträng mera utförligt.
    
    	.ascii	"Hello World"						# Ett tredje sätt, bara att nu kommer strängen att
    	.byte	0							# inte termineras med en nolla, utan vi måste explicit
    									# använda .byte 0 för detta.
    	.
    	.
    	.
    

    3.5 Reservering av ett tomt minnesområde

    Sist men inte minst: vi har inte ännu nämnt hur man bekvämt kan skapa tomma arrayer. Ett sätt är ju att man helt enkelt använder .byte, .half eller .word -direktiven med N antal nollor som argument, men detta blir väldigt krångligt om vi vill allokera en tom array på t.ex. 1000 element.
    Vad vi istället kan göra är att använda direktivet .space, som tar emot ett heltalsargument och anger antalet bytes som skall allokeras. Det allokerade området kommer att nollställas.

    	.data
    
    	.byte	99		# Vi antar att denna byte hamnar på adressen 0x10000000.
    
    	.space	5		# Allokerar fem bytes fr.o.m. adressen 0x10000001.
    
    	.byte	-1		# Denna byte kommer att hamna på adressen 0x10000006
    
    	.
    	.
    	.
    



    <--- Tillbaka

    MIPS is a registered trademark of MIPS Technologies, Inc.             Senast uppdaterad 23.11.2004