I do not achieve to control keypad of the Digital Lab Sim tool of Mars 4.2.
According to the directions in the help "Byte value at address 0xFFFF0014 : receive row and column of the key pressed, 0 if not key pressed ". Nevertheless, if you read that memory position it does not work. Next simple sequence should read the keypad, but it does not work
ini:
lui $1,0xffff
lw $2,0x0014($1)
j ini
It always returns a 0 in spite of pressing any key of the tools.
I have no problem with the seven segment digit of this tool at all. It is quite easy to use through the address 0xFFFF0012. Nevertheless, no way with the keypad of this tool
Does anybody know how to read the keypad of the Digital Lab Sim of Mars 4.2?
Thank you very much
I answer myself.
Before reading a key, you have to select the row. The next sequence check the three rows (and it works):
sub $0,$0,$0
lui $2,0xffff # Base address IO
main:
addi $3,$0,0x01 # bit 0 =1 for row 1
sb $3,0x0012($2) # Selection row 1 (write on port 0xFFFF 0012)
lb $4,0x0014($2) # $4= code of pressed key of row 1 ($4=0 if not pressed)
bne $4,$0,row1 # if some key of row 1 was pressed -> go row1
# test of row 2
addi $3,$0,0x02 # bit 1 =1 for row 2
sb $3,0x0012($2)
lb $4,0x0014($2)
bne $4,$0,row2
# test of row 3
addi $3,$0,0x04 # bit 2 =1 for row 3
sb $3,0x0012($2)
lb $4,0x0014($2)
bne $4,$0,row3
# test of row 4
addi $3,$0,0x08 # bit 3 =1 for row 4
sb $3,0x0012($2)
lb $4,0x0014($2)
bne $4,$0,row4
j main
row1: # $4 has the code of the pressed key of this row
row2: # $4 has the code of the pressed key of this row
row3: # $4 has the code of the pressed key of this row
row4: # $4 has the code of the pressed key of this row
Basically, I have 8 pieces of data, 2 bits each (4 states), being stored in the 16 LSBs of a 32-bit integer. I want to reverse the order of the data pieces to do some pattern matching.
I am given a reference integer and 8 candidates, and I need to match one of the candidates to the reference. However, the matching candidate may be transformed in some predictable way.
If the reference data is in the form [0,1,2,3,4,5,6,7], then the possible matches can be in one of these 8 forms:
[0,1,2,3,4,5,6,7], [0,7,6,5,4,3,2,1]
[6,7,0,1,2,3,4,5], [2,1,0,7,6,5,4,3]
[4,5,6,7,0,1,2,3], [4,3,2,1,0,7,6,5]
[2,3,4,5,6,7,0,1], [6,5,4,3,2,1,0,7]
The pattern is that the data is always in order, but can be reversed and rotated.
I am implementing this in C and MIPS. I have both working, but they seem bulky. My current approach is to mask each piece from the original, shift it to its new position, and OR it with the new variable (initialized to 0).
I did more hard coding in C:
int ref = 4941; // reference value, original order [1,3,0,1,3,0,1,0], (encoded as 0b0001001101001101)
int rev = 0;
rev |= ((ref & 0x0003) << 14) | ((ref & 0x000C) << 10) | ((ref & 0x0030) << 6) | ((ref & 0x00C0) << 2); // move bottom 8 bits to top
rev |= ((ref & 0xC000) >> 14) | ((ref & 0x3000) >> 10) | ((ref & 0x0C00) >> 6) | ((ref & 0x0300) >> 2); // move top 8 bits to bottom
// rev = 29124 reversed order [0,1,0,3,1,0,3,1], (0b0111000111000100)
I implemented a loop in MIPS to try to reduce the static instructions:
lw $01, Reference($00) # load reference value
addi $04, $00, 4 # initialize $04 as Loop counter
addi $05, $00, 14 # initialize $05 to hold shift value
addi $06, $00, 3 # initialize $06 to hold mask (one piece of data)
# Reverse the order of data in Reference and store it in $02
Loop: addi $04, $04, -1 # decrement Loop counter
and $03, $01, $06 # mask out one piece ($03 = Reference & $06)
sllv $03, $03, $05 # shift piece to new position ($03 <<= $05)
or $02, $02, $03 # put piece into $02 ($02 |= $03)
sllv $06, $06, $05 # shift mask for next piece
and $03, $01, $06 # mask out next piece (#03 = Reference & $06)
srlv $03, $03, $05 # shift piece to new position ($03 >>= $05)
or $02, $02, $03 # put new piece into $02 ($02 |= $03)
srlv $06, $06, $05 # shift mask back
addi $05, $05, -4 # decrease shift amount by 4
sll $06, $06, 2 # shift mask for next loop
bne $04, $00, Loop # keep looping while $04 != 0
Is there a way to implement this that is simpler or at least fewer instructions?
To reverse your bits, you can use the following code.
static int rev(int v){
// swap adjacent pairs of bits
v = ((v >> 2) & 0x3333) | ((v & 0x3333) << 2);
// swap nibbles
v = ((v >> 4) & 0x0f0f) | ((v & 0x0f0f) << 4);
// swap bytes
v = ((v >> 8) & 0x00ff) | ((v & 0x00ff) << 8);
return v;
}
MIPS implementation is 15 instructions.
rev: # value to reverse in $01
# uses $02 reg
srli $02, $01, 2
andi $02, $02, 0x3333
andi $01, $01, 0x3333
slli $01, $01, 2
or $01, $01, $02
srli $02, $01, 4
andi $02, $02, 0x0f0f
andi $01, $01, 0x0f0f
slli $01, $01, 4
or $01, $01, $02
srli $02, $01, 8
andi $02, $02, 0xff
andi $01, $01, 0xff
slli $01, $01, 8
or $01, $01, $02
# result in $01
Note that you can simultaneously reverse 2x16bits by just doubling the constants (and even 4 on 64 bits machines). But I am not sure it is useful in you case.
Note: Be-careful with handwritten optimized assembly, there are really processor specific optimization keep them if you really have a strugle with your compiler generation in a tight loop.
You can improve the pipeline, (if you code in C the compiler do it for you) and use the delay slot of the bne instruction. This will improve your instruction level parallelism.
Assuming you have something like a Mips Processor with a 1 delay slot and 5 stage pipeline (Instruction Fetch, Decode, Execute, Memory, Writeback).
This pipeline introduce Read After Write Hazards on data dependence most were on $3 register.
A RaW hasard cause your pipeline to stall.
# Reverse the order of data in Reference and store it in $02
Loop: and $03, $01, $06 # mask out one piece ($03 = Reference & $06)
addi $04, $04, -1 # decrement Loop counter (RaW on $3)
sllv $03, $03, $05 # shift piece to new position ($03 <<= $05)
sllv $06, $06, $05 # shift mask for next piece
or $02, $02, $03 # put piece into $02 ($02 |= $03)
and $03, $01, $06 # mask out next piece (#03 = Reference & $06)
srlv $06, $06, $05 # shift mask back
srlv $03, $03, $05 # shift piece to new position ($03 >>= $05)
addi $05, $05, -4 # decrease shift amount by 4
or $02, $02, $03 # put new piece into $02 ($02 |= $03)
bne $04, $00, Loop # keep looping while $04 != 0
sll $06, $06, 2 # shift mask for next loop
If you have a Superscalar processor the solution need some changes.
For a very simple and effective approach, use a 256-byte lookup table and perform 2 lookups:
extern unsigned char const xtable[256];
unsigned int ref = 4149;
unsigned int rev = (xtable[ref & 0xFF] << 8) | xtable[ref >> 8];
The xtable array can be initialized statically via a set of macros:
#define S(x) ((((x) & 0x0003) << 14) | (((x) & 0x000C) << 10) | \
(((x) & 0x0030) << 6) | (((x) & 0x00C0) << 2) | \
(((x) & 0xC000) >> 14) | (((x) & 0x3000) >> 10) | \
(((x) & 0x0C00) >> 6) | (((x) & 0x0300) >> 2))
#define X8(m,n) m((n)+0), m((n)+1), m((n)+2), m((n)+3), \
m((n)+4), m((n)+5), m((n)+6), m((n)+7)
#define X32(m,n) X8(m,(n)), X8(m,(n)+8), X8(m,(n)+16), X8(m,(n)+24)
unsigned char const xtable[256] = {
X32(S, 0), X32(S, 32), X32(S, 64), X32(S, 96),
X32(S, 128), X32(S, 160), X32(S, 192), X32(S, 224),
};
#undef S
#undef X8
#undef X32
If space is not expensive, you could use a single lookup into a 128K-byte table, which you would compute at startup time or generate with a script and include at compile time, but it is somewhat wasteful and not cache-friendly.
Okay, so this might be a really silly question but I don't quite have the hang of Assembly yet. I have to write a program that calculates the summation of a series of numbers. It should behave like so:
Enter the first integer in the series: 5
Enter the number of integers in the series: 3
Enter the offset between integers in the series: 4
The series is: 5, 9, 13.
The summation of the series is 27.
Would you like to calculate another summation (Y/N)? y
Enter the first integer in the series: 4
Enter the number of integers in the series: 5
Enter the offset between integers in the series: 27
The series is 4, 31, 58, 85, 112.
The summation of the series is 290.
Would you like to calculate another summation (Y/N)? Y
Enter the first integer in the series: -16
Enter the number of integers in the series: -22
There must be a positive number of integers in the series.
Would you like to calculate another summation (Y/N)? n
This is what I have so far:
li $v0, 4 #put 4 in as main parameter in v0
la $a0, Q1 #syscall will print string query 1
syscall
Store first integer in series in s0
li $v0, 5 #put 5 in as main parameter in v0
syscall #syscall will read integer from Q1
move $s0, $v0 #move integer in v0 to s0
Request number of integers in series
li $v0, 4 #put 4 in as main parameter in v0
la $a0, Q2 #syscall will print string query 2
syscall
Store number of integers in series in s1
li $v0, 5 #put 5 in as main parameter in v0
syscall #syscall will read integer from Q2
move $s1, $v0 #move integer in v0 to s1
Request offset of integers
li $v0, 4 #put 4 in as main parameter in v0
la $a0, Q3 #syscall will print string query 3
syscall
Store offset of integers in s2
li $v0, 5 #put 5 in as main parameter in v0
syscall #syscall will read integer from Q3
move $s2, $v0 #move integer in v0 to s1
Set counter
li $s3, 1 #Set counter to zero
li $t0, 1 #iterator count is in t0
I'm just curious as to where to start my loop? And how exactly would I go about printing the entire series? Any advice would be greatly appreciated.
I'm not sure what you do in "set counter" part, as the s1 already contains number of series members.
And s0 contains current element.
All you need (additional information) is to clear current_sum, let's say as s3:
add $s3, $zero, $zero ; or "move $s3, $zero" if you prefer the pseudo-ops
So for your first example s0-s3 will be set to [5, 3, 4, 0] before first loop iteration. This is everything ("world state") you need, any other value you will set up is probably some temporary for particular sub-task like displaying some value and similar, but as long as the core computation goes, these four values represent all you need, and everything else can be based upon them.
If s1 is already less than 1 (<=0 test), report wrong input.
Then the loop algorithm:
add current element (s0) to current sum (s3) ; summing up all numbers
display current element (s0)
; handle the correct count of series' members
decrement counter s1
if s1 is zero, then goto exit_loop
; preparing for next loop iteration
add offset (s2) to current element (s0)
display ", "
jump to first step
exit_loop: logic will involve printing end of line, summation text description, current sum value (s3), another new line(s) and asking for repeat y/n.
For example that 4 core values will evolve during iterations like this:
[5, 3, 4, 0] => displays "5, " + all the updating of values
[9, 2, 4, 5] => displays "9, " + all the updating of values
[13, 1, 4, 14] => modifies s3 to 27, displays "13", --s1, jumps to exit_loop
At exit_loop the core values are [13, 0, 4, 27], code will display 27 as series sum.
I am a beginner in MIPS and I am trying to write a simple code that runs over a given array in memory that is smaller than 10 cells, lets say 9 cells, and prints on screen the biggest number.
I wrote a C code that solves this issue, but I don't know how to convert it (without mips gcc) to a working MIPS assembly code.
The code I wrote:
int N = 9 , i = 0 , biggest = 0 ;
int arr [N] = -2 , 3 , 9 , -1 , 5 , 6 , 10 , 52 , 9 ;
while ( i <= N )
{
if ( arr [i] > biggest )
biggest = arr [i] ;
i++ ;
}
printf ( "biggest number is: %d" , biggest ) ;
I will be more than happy if someone can write that code in MIPS assembly, and explain it to me.
Thank you !
Just focusing on the loop, try something like that:
.text
.set noreorder
.global get_max
get_max:
li $4, array // start pointer
li $5, array_end-array-4 // end pointer
li $2, 0 // 'biggest' as result
lw $6, 0($4) // load first table entry
1: slt $3, $2, $6 // boolean flag (biggest<arr[i])
movn $2, $6, $3 // update 'biggest' when flag is set
lw $6, 4($4) // load next table entry
bne $4, $5, 1b // continue until we hit end of array
addiu $4, 4 // advance to next cell (using bne delay slot)
jr $31 // return to the caller
nop // safely fill the delay slot
.data
array: .long -2 , 3 , 9 , -1 , 5 , 6 , 10 , 52 , 9
array_end: .long 0
Compile this into a separate assembly source file and link with your main C code.
Don't forget to call the function from your C code:
printf("biggest=%d\n",get_max());
You have a problem with your initialization...