What does an Unsigned Long in C look like in PIC24 Assembly Language? - c

I have a variable defined in C on a PIC24
Let's say the name of the variable (in the C file) is The_Number_Of_Bytes
In the PIC24 C code, it is defined like this....
unsigned long The_Number_Of_Bytes=0; // number of bytes in buffer
I'm going to be called when an array of bytes named DATABUF01 will contain The_Number_Of_Bytes bytes (sorry for the implied redundancy) and I will need to make sure that I do that many bytes, exactly, and then stop.
I'm pretty confident that the number will be less than 65535, so why it is an unsigned long is for other discussions. For now, I want to know; which is the high order word and which is the low order word ? (For that matter, is long a 32 bit number in PIC24 C ?)
Could somebody tell me What will be in W2 and W3 in this example ?
Mov.W #_The_Number_Of_Bytes, W1 ;From the dispatcher
Mov.W [W1++], W2 ;My question: Hi Order or Low Order ?
Mov.W [W1], W3 ;My question: Hi Order or Low ?
Mov.W #_DATABUF01, W4 ;The Start
Mov.B [W4++], W5 ;First byte...
: ;Whatever
: ;Whatever
: ;Whatever
Could someone please confirm or correct my thinking about Hi / Low order of the unsigned long int ?
EDIT commenter requested this. May answer the question (comments are mine)
Requested comment, The size appears to be 4 bytes, from this...
45: unsigned long i=0;
0AB6A B80060 mul.uu 0x0000,#0,0x0000 ;make a zero
0AB6C 980710 mov.w 0x0000,[0x001c+2] ;no clue which word
0AB6E 980721 mov.w 0x0002,[0x001c+4] ;aha, if I made it 1 or 2, I'd know
46: unsigned int Fischer;
47:
48: Fischer = sizeof(i);
0AB70 200040 mov.w #0x4,0x0000 ;So it must be 4
0AB72 780F00 mov.w 0x0000,[0x001c] ;okay, it's a 4 byte number
49:

Here's a snippet from MPLAB C Compiler for PIC24 MCU's User's Guide:
5.3 DATA REPRESENTATION
Multibyte quantities are stored in “little endian” format, which means:
The least significant byte is stored at the lowest address
The least significant bit is stored at the lowest-numbered bit position
As an example, the long value of 0x12345678 is stored at address 0x100 as follows:
0x1000x1010x1020x103
0x78 0x56 0x34 0x12
As another example, the long value of 0x12345678 is stored in registers w4 and w5:
w4 w5
0x56780x1234

you can also view the assembly for a program to try to get an idea of how their compiler does it... for instance:
int main(int argc, char** argv)
{
long i = 56;
long j = i;
return 0;
}
becomes... ( xc16-gcc -S main.c )
.file "/Users/grady/MPLABXProjects/testpic24.X/main.c"
.section .text,code
.align 2
.global _main ; export
.type _main,#function
_main:
.set ___PA___,1
lnk #12
mov w0,[w14+8]
mov w1,[w14+10]
mov #56,w4
mov #0,w5
mov.d w4,[w14]
mov.d [w14],w4
mov w4,[w14+4]
mov w5,[w14+6]
clr w4
mov w4,w0
ulnk
return
.set ___PA___,0
.section __c30_signature, info, data
.word 0x0001
.word 0x0000
.word 0x0000
; MCHP configuration words
.set ___PA___,0
.end

Related

Function that takes a char array and 2 indices; swapping the chars in those indices

This is my function prototype:
char* swap(char* array, int index1, int index2);
This is my assembly code:
segment .text
global swap
swap:
mov r14,[rdi+rsi]
mov r15,[rdi+rdx]
mov [rdi+rsi],r15 ;this line segfaults
mov [rdi+rdx],r14
mov rax,rdi
ret
The lines mov [rdi+rsi],r15 and mov [rdi+rdx],r14 give me a segfault; I'm not sure where I'm going wrong
The calling function:
#include <stdio.h>
#include <stdlib.h>
extern char* swapLetters(char* str, int indexA, int indexB);
int main()
{
char* st= "E X A M P L E";
printf("Before swap: \t%s\n", st);
char * res = swap(st, 2 ,10);
printf("After swap: \t%s\n", res);
return 0;
}
Expected output:
Before swap: E X A M P L E
After swap: E L A M P X E
The primary problem is that your st variable is defined as a pointer to a string literal.
char* st= "E X A M P L E";
String literals in the C language are considered read-only. To modify such a string is undefined behaviour. What happens is unknown and will be specific to the compiler and the environment it runs in. Your environment is raising an exception when you go to write that memory in the assembly code. On most modern OSes using modern compilers the string literals are placed in memory that isn't writeable so that it will generate an exception, which is what happened in your case.
If you wish to create a character array in writeable memory you can define st this way:
char st[] = "E X A M P L E";
Issues with the Assembly Code
One issue is that your indices to the function swap are int. In 64-bit GCC/CLANG int is 32-bits. If you pass 32-bit signed int to the assembly code the top 32-bits may have garbage in them. Given that your indices are never negative you should use an unsigned type and preferably one that is 64-bit. I would recommend the size_t type instead. size_t will be unsigned and 64-bit in size in x86-64 code, so when passed to the assembly code you don't need to sign/zero extend the index values to 64-bits before using them. I'm recommending changing swap to be:
char* swap(char* array, size_t index1, size_t index2)
If you keep index1 and index2 as signed integers (int) the beginning of your assembly code would have to use MOVSX on both ESI and EDX registers. That code would look like:
swap:
movsx rsi, esi ; Sign extend 32-bit index1 parm in ESI to 64-bits
movsx rdx, edx ; Sign extend 32-bit index2 parm in EDX to 64-bits
; rest of function here
If you were to have used 32-bit unsigned int for index and index2 you would have had to zero extend the 32-bit values with:
mov esi, esi ; Zero extend 32-bit index1 parm in ESI to 64-bits
mov edx, edx ; Zero extend 32-bit index2 parm in EDX to 64-bits
; rest of function here
When the destination of an operation is a 32-bit register in 64-bit mode, the CPU automatically zeros the upper 32-bits of the destination register. Moving a 32-bit register like ESI to itself will clear the upper 32-bits of RSI. This is the same for all the general purpose registers.
RBX, RBP, and R12–R15 are non-volatile registers according to the x86-64 System V ABI. If your function modifies them their contents have to be preserved. You can push them on the stack and pop their original values off the stack when finished. The preferred way is to use one of the volatile registers that don't need to preserved like R8-R11, RAX, RCX, RDX, RDI, RSI.
When you move data to/from memory using a 64-bit register then 64 bits (8 bytes) will be transferred. As an example:
mov r14,[rdi+rsi]
Moves the 8 bytes starting at memory address [rdi+rsi] and moves it to 64-bit register R14. The write later on does something similar but updates 8 bytes in memory rather than one byte. Updating 8 bytes of data could smash the stack if the array of characters were placed on the stack, which happens to be the case in your code and environment.
When using the numbered registers R8 to R15 you can reference the low 8 bits by placing a b suffix on the end of the register name (w is for 16-bit word, d is for 32-bit double word). A complete chart of all the registers names in NASM/YASM syntax for 64-bit mode are:
mov r14,[rdi+rsi] would be written as mov mov r14b,[rdi+rsi] to move a single byte. You would have to make that change to each of the other moves as well.
Assuming you change index1 and index2 to have type size_t (or uin64_t) your assembly code could have been written as :
segment .text
global swap
swap:
push r14 ; Save non-volatile registers we overwrite
push r15
mov r14b,[rdi+rsi] ; Move one byte from [rdi+rsi] to R14B. R14B is lower 8 bits of R14
mov r15b,[rdi+rdx] ; Move one byte from [rdi+rdx] to R15B. R15B is lower 8 bits of R15
mov [rdi+rsi],r15b ; Move the byte in R15B to [rdi+rsi]
mov [rdi+rdx],r14b ; Move the byte in R14B to [rdi+rdx]
mov rax,rdi
pop r15 ; Restore non-volatile registers
pop r14
ret
If you were to use the other volatile registers rather than the non-volatile ones the code could have been simplified to:
segment .text
global swap
swap:
mov al,[rdi+rsi] ; Move one byte from [rdi+rsi] to AL. AL is lower 8 bits of RAX
mov cl,[rdi+rdx] ; Move one byte from [rdi+rdx] to CL. CL is lower 8 bits of RCX
mov [rdi+rsi],cl ; Move the byte in CL to [rdi+rsi]
mov [rdi+rdx],al ; Move the byte in AL to [rdi+rdx]
mov rax,rdi
ret
In this case we use the lower 8 bits of the volatile registers RAX(AL) and RCX(CL) to do the swap. Since we don't have to preserve these registers there is no need to save and restore them.
Part of the problem here is that an area of non-writable memory is being used to write to, it will not work. (There are also other correctness problems with the asm, see #MichaelPetch's answer.)
When this is created:
char* st= "E X A M P L E";
Because it creates a string literal, the pointer st refers to a memory location that is not writable.
If created as:
char st[] = "E X A M P L E";
st stored in writable memory and its contents are the characters, instead of just holding a pointer to a read-only string literal.

How do I compute the 16-bit sum of the 8-bit values of an array in assembly?

Feel like I've been asking a lot of these questions lately lol, but assembly is still pretty foreign to me.
Using an Arduino, I have to write a function in Atmel AVR Assembly for my computer science class that calculates the sum of the 8-bit values in an array and returns it as a 16-bit integer. The function is supposed to take in an array of bytes and a byte representing the length of the array as arguments, with those arguments stored in r24 and r22, respectively, when the function is called. I am allowed to use branching instructions and such.
The code is in this format:
.global sumArray
sumArray:
//magic happens
ret
I know how to make loops and increment the counter and things like that, but I am really lost as to how I would do this.
I am unsure as to how I would do this. Does anyone know how to write this function in Atmel AVR Assembly? Any help would be much appreciated!
Why don't you ask the question to your compiler?
#include <stdint.h>
uint16_t sumArray(uint8_t *val, uint8_t count)
{
uint16_t sum = 0;
for (uint8_t i = 0; i < count; i++)
sum += val[i];
return sum;
}
Compiling with avr-gcc -std=c99 -mmcu=avr5 -Os -S sum8-16.c generates
the following assembly:
.global sumArray
sumArray:
mov r19, r24
movw r30, r24
ldi r24, 0
ldi r25, 0
.L2:
mov r18, r30
sub r18, r19
cp r18, r22
brsh .L5
ld r18, Z+
add r24, r18
adc r25,__zero_reg__
rjmp .L2
.L5:
ret
This may not be the most straight-forward solution, but if you study
this code, you can understand how it works and, hopefully, come with
your own version.
Iif you want something quick and dirty, add the two 8-bit values into an 8-bit register. If the sum is less than the inputs, then make a second 8-bit register equal to 1, otherwise 0. That's how you can do the carry.
The processor should already have something called a carry flag that you can use to this end.
with pencil and paper how do I add two two digit decimal numbers when I was only taught to add two single digit numbers at a time? 12 + 49? I can add the 2+9 = 11 then what do I do? (search for the word carry)

Comparing signed integer in arm assembly

Loading a global variable and checking to see if it's equal to -1 but my code will not recognize it as equal to -1. It only pass the comparison with local variables.
//setting global variable
.data
.global top_m
top_m: .word -1
//loading global variable into x21
adrp x28, top_m
add x28, x28, :lo12:top_m
ldr x21, [x28]
//checking value of global variable, it does not branch to exit_stackEmpty
cmp x21, -1
b.eq exit_stackEmpty
//but if I compare with a local variable then it does branch
mov x23, -1
cmp x23, -1
b.eq exit_stackEmpty
EDIT:
In gdb it shows this for x21 and x23
x21 0xffffffff 4294967295 //after loading -1 to x21
x23 0xffffffffffffffff -1 //after mov x23, -1
The data that you're loading is from a .word directive, and thus is 32 bits - 0xffffffff. If you then load 64 bits from that address you get 0x00000000ffffffff, which indeed is not a 64-bit -1 however you look at it. Incidentally, that load also runs off the end of the data section, which is a bad idea, but does means you'll happen to get zeros in the MSBs (or a segfault) - if you had more data following top_m, it would be more obvious what's happening.
If you want to load a 32-bit word and sign-extend it to 64 bits, use the "load a 32-bit word and sign-extend it to 64 bits" instruction: LDRSW.

Using Int (32 bits) over char (8 bits) to 'help' processor

In C, often we use char for small number representations. However Processor always uses Int( or 32 bit) values for read from(or fetch from) registers. So every time we need to use a char or 8 bits in our program processor need to fetch 32 bits from regsiter and 'parse' 8 bits out of it.
Hence does it sense to use Int more often in place of char if memory is not the limitation?
Will it 'help' processor?
There's the compiler part and the cpu part.
If you tell the compiler you're using a char instead of an int, during static analysis it will know the bounds of the variable is between 0-255 instead of 0-(2^32-1). This will allow it to optimize your program better.
On the cpu side, your assumption isn't always correct. Take x86 as an example, it has registers eax and al for 32 bit and 8 bit register access. If you want to use chars only, using al is sufficient. There is no performance loss.
I did some simple benchmarks in response to below comments:
al:
format PE GUI 4.0
xor ecx, ecx
dec ecx
loop_start:
inc al
add al, al
dec al
dec al
loopd short loop_start
ret
eax:
format PE GUI 4.0
xor ecx, ecx
dec ecx
loop_start:
inc eax
add eax, eax
dec eax
dec eax
loopd short loop_start
ret
times:
$ time ./test_al.exe
./test_al.exe 0.01s user 0.00s system 0% cpu 7.102 total
$ time ./test_eax.exe
./test_eax.exe 0.01s user 0.01s system 0% cpu 7.120 total
So in this case, al is slightly faster, but sometimes eax came out faster. The difference is really negligible. But cpus aren't so simple, there might be code alignment issues, caches, and other things going on, so it's best to benchmark your own code to see if there's any performance improvement. But imo, if your code is not super tight, it's best to trust the compiler to optimize things.
I'd stick to int if I were you as that is probably the most native integral type for your platform. Internally you could expect shorter types to be converted to int so actually degrading performance.
You should never use char and expect it to be consistent across platforms. Although the C standard defines sizeof(char) to be 1, char itself could be signed or unsigned. The choice is down to the compiler.
If you believe that you can squeeze some performance gain in using an 8 bit type then be explicit and use signed char or unsigned char.
From ARM system developers guide
"most ARM data processing operations are 32-bit only. For this reason, you should use
a 32-bit datatype, int or long, for local variables wherever possible. Avoid using char and
short as local variable types, even if you are manipulating an 8- or 16-bit value"
an example code from the book to prove the point. note the wrap around handling for char as opposed to unsigned int.
int checksum_v1(int *data)
{
char i;
int sum = 0;
for (i = 0; i < 64; i++)
{
sum += data[i];
}
return sum;
}
ARM7 assembly when using i as a char
checksum_v1
MOV r2,r0 ; r2 = data
MOV r0,#0 ; sum = 0
MOV r1,#0 ; i = 0
checksum_v1_loop
LDR r3,[r2,r1,LSL #2] ; r3 = data[i]
ADD r1,r1,#1 ; r1 = i+1
AND r1,r1,#0xff ; i = (char)r1
CMP r1,#0x40 ; compare i, 64
ADD r0,r3,r0 ; sum += r3
BCC checksum_v1_loop ; if (i<64) loop
MOV pc,r14 ; return sum
ARM7 assembly when i is an unsigned int.
checksum_v2
MOV r2,r0 ; r2 = data
MOV r0,#0 ; sum = 0
MOV r1,#0 ; i = 0
checksum_v2_loop
LDR r3,[r2,r1,LSL #2] ; r3 = data[i]
ADD r1,r1,#1 ; r1++
CMP r1,#0x40 ; compare i, 64
ADD r0,r3,r0 ; sum += r3
BCC checksum_v2_loop ; if (i<64) goto loop
MOV pc,r14 ; return sum
If your program is simple enough, the optimizer can do the right thing without you having to worry about it. In this case, plain int would be the simplest (and forward-proof) solution.
However, if you want really much to combine specific bit width and speed, you can use 7.18.1.3 Fastest minimum-width integer types from the C99 standard (requires C99-compliant compiler).
For example:
int_fast8_t x;
uint_fast8_t y;
are the signed and unsigned types that are guaranteed to be able to store at least 8 bits of data and use the usually faster underlying type. Of course, it all depends on what you are doing with the data afterwards.
For example, on all systems I have tested (see: standard type sizes in C++) the fast types were 8-bit long.

How to find largest number in an array using NASM

i was doing a program in NASM(x86 assembly), in which user is asked to enter three 32 bit hex numbers(8 digit), which are further stored in an array and the program shows the number which is largest of them all. The program works fine, i.e. it shows the largest of the three numbers. But the problem is, that it shows only 16 bit (4 digit number) as output. For example, if i give three numbers as 11111111h,22222222h and 10000000h, the output comes out to be only 2222. This is the code.
section .data
msg db "Enter the number : ",10d,13d
msglen equ $-msg
show db "The greatest number is : ",10d,13d
showlen equ $-show
%macro display 2
mov eax,4
mov ebx,1
mov ecx,%1
mov edx,%2
int 80h
%endmacro
%macro input 2
mov eax,3
mov ebx,0
mov ecx,%1
mov edx,%2
int 80h
%endmacro
section .bss
large resd 12
num resd 3
section .text
global _start
_start:
mov esi,num
mov edi,3
; Now taking input
nxt_num:
display msg,msglen
input esi,12
add esi,12
dec edi
jnz nxt_num
mov esi,num
mov edi,3
add: mov eax,[esi]
jmp check
next: add esi,12
mov ebx,[esi]
CMP ebx,eax
jg add
check: dec edi
jnz next
mov [large],eax
display show,showlen
display large,12
;exit
mov eax,1
mov ebx,0
int 80h
I even tried changing reserved size of array from doubly byte to quad byte. But the result remains the same.
Also, when i execute the same code in NASM x86_64 assembly, only with the registers and the system calls changed (i.e. eax to rax, ebx to rcx, int 80h to syscall, etc) the output comes out to of 32 bits(8 digits). Why so?
I need help. Thank you. :)
In you little program , you're trying to move the Qword into a 32-bit register which can hold just 4bytes (DWord). Based on your response to Gunner I guess you're misunderstanding this concept.
Actually each byte is represented by 8bits.
a word is 2 bytes (16 bits)
a dword is 4 bytes (32 bits) which is the size of a register in a x86 arch.
So whenever you take a byte , its binary equivalent has always an 8bits size.
So the binary equivalent of "FF" in hex is 00001111.
In your program just try to print your number as a string instead of printing it through a register, you can simply do that by using the pointer to the memory address where you number is stored or simply by printing the input using printf.
P.S : the string should be in ASCII , so to display 11111111 it should be in memory as following 3131313131313131 .
The output 2222 is correct for a 32 bit register. Each number is 8 bits, 4 numbers = 8 * 4 = 32, the max a 32 bit register can hold. This is why if you change to 64 bit registers, the full number is printed. You will need to change the displayed number into a string to display the full number.

Resources