Assume I have a variable called Block_Size and without initialization.
Would
Block_Size db ?
mov DS:Block_Size, 1
be equal to
Block_Size db 1
No, Block_Size db ? has to go in the BSS or data section, not mixed in with your code.
If you wrote
my_function:
Block_Size db ?
mov DS:Block_Size, 1
...
ret
your code would crash. ? isn't really uninitialized, it's actually zeroed. So then the CPU decoded the instructions starting at my_function (e.g. after some other code ran call my_function), it would actually decode the 0 as code. (IIRC, opcode 0 is add, and then the opcode of the mov instruction would be decoded as the operand byte of add (ModR/M).)
Try assembling it, and then use a disassembler to show you how it would decode, along with the hex dump of the machine code.
db assembles a byte into the output file at the current position, just like add eax, 2 assembles 83 c0 02 into the output file.
You can't use db the way you declare variable in C
void foo() {
unsigned char Block_size = 1;
}
A non-optimizing compiler would reserve space on the stack for Block_size. Look at compiler asm output if you're curious. (But it will be more readable if you enable optimization. You can use volatile to force the compiler to actually store to memory so you can see that part of the asm in optimized code.)
Maybe related: Assembly - .data, .code, and registers...?
If you wrote
.data
Block_size db ?
.code
set_blocksize:
mov [Block_size], 1
ret
it would be somewhat like this C:
unsigned char Block_size;
void set_blocksize(void) {
Block_size = 1;
}
If you don't need something to live in memory, don't use db or dd for it. Keep it in registers. Or use Block_size equ 1 to define a constant, so you can do stuff like mov eax, Block_size + 4 instead of mov eax, 5.
Variables are a high-level concept that assembly doesn't really have. In asm, data you're working with can be in a register or in memory somewhere. Reserving static storage for it is usually unnecessary, especially for small programs. Use comments to keep track of what you put in which register.
db literally stands for "define byte" so it will put the byte there, where the move command can have you place a particular value in a register overwriting whatever else was there.
Related
I'm starting to study assembly for PIC18f4550 and I've been trying to do some activities and I don't know how to solve it. According to the activity, Using MPLABX, I'm supposed to sum 2 16bit variables and store the result on a third 16 bit variable.
I was able to sum and store the result on the third variable but I have no idea how to declare these variables in 16bit.
; TODO INSERT CONFIG CODE HERE USING CONFIG BITS GENERATOR
INCLUDE
RES_VECT CODE 0x0000 ; processor reset vector GOTO START ; go to beginning of program
; TODO ADD INTERRUPTS HERE IF USED
MAIN_PROG CODE ; let linker place main program
START
clrw ;clear the w register
num1 equ 00000 ;declares 3 variables and their initial values
num2 equ 00001
result equ 00002
movlw H'4F'
movwf num1
movlw H'8A'
movwf num2
movf num1,W ;moves num1 value to w register
addwf num2,W ;sums num2 and w and stores it in w itself
movwf resultado ;moves w to the result register
END
I need to check if my code is actually correct (Im totally new on assembly) and how to declare these 3 variables in 16bit format. Thanks in advance!
The PIC18 is a 8 bit controller. If you want to add two 16 bit variables you had to do it byte by byte.
Maybe you don't want to work with an absolue address and work with the linker:
udata_acs H'000'
num1_LSB RES 1 ;reserve one byte on the access bank
num1_MSB RES 1 ;
You also could reserve two bytes for a name:
udata_acs H'000'
num1 RES 2 ;reserve two bytes on the access bank
Know you could access the second byte with :
movwf num1+1
And always remember to check the carry bit to get the MSB of an addition.
I wrote an 8086 program, and as far as I can tell it runs fine, but when it gets to the part where I declare the variables, the emulator gives me an error. When trying to run the line temp db 0x0F, the emulator says:
unknown opcode skipped: 32
not 8086 instruction - not supported yet.
Here's my full program:
org 100h
mov ah, temp ;put variables into registers
mov al, changed
mov dx, result
lea bx, temp ;get address of temp and put into bx
add dx, [bx] ;add value at the address in bx to result
lea bx, changed ;get address of changed and put into bx
add dx, [bx] ;add value at the address in bx to result
temp db 0x0F ;declare and initialize variables
changed db 32h
result dw 0
Is this consequential to how the program functions, and how do I fix it?
EDIT: sigjuice solved the problem, as you can see in the comments. Here's the final version of the program that runs correctly:
.CODE
org 100h
mov ah, temp ;put variables into registers
mov al, changed
mov dx, result
lea bx, temp ;get address of temp and put into bx
add dx, [bx] ;add value at the address in bx to result
lea bx, changed ;get address of changed and put into bx
add dx, [bx] ;add value at the address in bx to result
.DATA
temp db 0x0F ;declare and initialize variables
changed db 32h
result dw 0
add dx, [bx] ;add value at the address in bx to result
temp db 0x0F ;declare and initialize variables
In this part of your program there's nothing that stops the CPU from executing the data at the temp label as if it were an instruction.
Although adding the .CODE and .DATA assembler directives (perhaps suggested by #sigjuice) seemingly solves the problem, this is typically not what you use when writing a .COM executable. It's a .COM executable because you used the org 100h directive.
What your program really needs is a way to return to the operating system. Since this is EMU8086 the preferred way is using the DOS.TerminateWithReturncode function.
add dx, [bx] ;add value at the address in bx to result
; Exit to the operating system
mov ax, 4C00h ;AH=4Ch function number, AL=0 exitcode (0 most often means OK)
int 21h ;DOS system call
; Now beyond this point nothing gets executed inadvertently
temp db 0Fh ;declare and initialize variables
I can't really advice to return to the operating system using a mere ret instruction, because this method requires that the SS:SP registers are set as they were when the program started. This will not always be the case. Better use this DOS function that does not rely on any specific register setting.
lea bx, temp ;get address of temp and put into bx
add dx, [bx] ;add value at the address in bx to result
lea bx, changed ;get address of changed and put into bx
add dx, [bx] ;add value at the address in bx to result
Nothing to do with your original problem but as a bonus:
Because temp and changed are both byte-sized variables, the word-sized additions don't just add the variables alone but also the byte that happens to follow them in memory! Sometimes this is intentional (I sincerily doubt this is the case here!), but you need to make sure that you understand this.
I try to implement a low power "deep sleep" functionality into uboot on button press. Button press is handled by linux and a magic code is set to make u-boot aware of the stay asleep do not reboot"
printf ("\nDisable interrupts to restore them later\n");
rupts = disable_interrupts();
printf ("\nEnable interrupts to enable magic wakeup later\n");
enable_interrupts();
printf ("\nSuspending. Press button to restart\n");
while(probe_button()/*gpio probe*/){
#if 1
//FIXME recheck if that one actually needs an unmasked interrupt or any is ok
__asm__ __volatile__(
"mcr p15, 0, %0, c7, c0, 4\n" /* read cp15 */
"mov %0, %0"
: "=r" (tmp)
:
: "memory"
);
#else
udelay (10000);
#endif
}
if (rupts) {
printf ("\nRe-Enabling interrupts\n");
enable_interrupts();
}
Unfortunatly the power dissipation does not change at all (got power dissipation measurment tied to the chip), no matter if hotspinning is used or not. Beyond that, if I use the Wait-For-Interrupt CP15 instruction, it never wakes up. The button is attached to one of the GPIOs. The plattform is Marvell Kirkwood ARM9EJ-S based.
I enabled some CONFIG_IRQ_* manually, and create implementation for arch_init_irq() aswell as do_irq(), I think there is my issue.
According to the CP15 instruction docs it should be just enough that a interrupt gets triggered (no matter if masked or not!).
Can anyone tell me what I am doing wrong or what needs to be done beyond the code above?
Thanks a lot in advance!
I'm not sure if it is the only reason your aproach isn't working on power saving but your inline assembly isn't correct. According to this article you need to execute:
MOV R0, #0
MCR p15, 0, r0, c7, c0, 4
but your inline assembly
__asm__ __volatile__(
"mcr p15, 0, %0, c7, c0, 4\n" /* read cp15 */
"mov %0, %0"
: "=r" (tmp)
:
: "memory"
);
produces
0: ee073f90 mcr 15, 0, r3, cr7, cr0, {4}
4: e1a03003 mov r3, r3
8: e12fff1e bx lr
I am not sure what's your intent but mov r3, r3 doesn'αΊ— have any effect. So you are making coprocessor call with a random value. You also need to set r3 (ARM source register for mcr) before mcr call. Btw when you put 'memory' in clobber list it means
... will cause GCC to not keep memory values cached in registers across the assembler instruction and not optimize stores or loads to that memory.
Try this line,
asm("MOV R0, #0\n MCR p15, 0, r0, c7, c0, 4" : : : "r0");
it produces
c: e3a00000 mov r0, #0 ; 0x0
10: ee070f90 mcr 15, 0, r0, cr7, cr0, {4}
For power saving in general, I would recommend this article at ARM's web site.
Bonus section:
A small answer to your claim on backward compability of this coprocessor supplied WFI:
ARMv7 processors (including Cortex-A8, Cortex-A9, Cortex-R4 and Cortex-M3) all implement the WFI instruction to enter "wait for interrupt" mode. On these processors, the coprocessor write used on earlier processors will always execute as a NOP. It is therefore possible to write code that will work across ARMv6K, ARMv6T2 and all profiles of ARMv7 by executing both the MCR and WFI instruction, though on ARM11MPCore this will cause "wait for interrupt" mode to be entered twice. To write fully portable code that enters "wait for interrupt" mode, the CPUID register must be read at runtime to determine whether "wait for interrupt" is available and the instruction needed to enter it.
I'm trying to understand this code:
inline SInt32 smul32by16(SInt32 i32, SInt16 i16)
{
register SInt32 r;
asm volatile("smulwb %0, %1, %2" : "=r"(r) : "r"(i32), "r"(i16));
return r;
}
Does anybody know what this assembly instruction does?
Update:
P.S. I use objective C. and I should understand some code from assembly. That's why it's difficult for me to understand this code.
It does signed 32 bit by signed 16 bit multiplication and returns the top 32 bit of 48 bit result. The b specifies to use the bottom 16bit of the third operand.
So, translating it into pseudo code:
int_48 temp;
temp = i32*i16;
result = temp >> 16;
See here for the description of the ARM SMUL and SMULW instructions:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/CHDIABBH.html
by using asm you can give assembler commands.
and using volatile for the reason,
volatile for the asm construct, to prevent GCC from deleting the asm statement as unused
see this link for better understanding
command inside ask instruction means :
SMULWB R4, R5, R3 ; Multiplies R5 with the bottom halfword of R3,
; extracts top 32 bits and writes to R4.
I would like to come up with the byte code in assembler (assembly?) for Windows machines to add two 32-bit longs and throw away the carry bit. I realize the "Windows machines" part is a little vague, but I'm assuming that the bytes for ADD are pretty much the same in all modern Intel instruction sets.
I'm just trying to abuse VB a little and make some things faster. So as an example of running direct assembly in VB, the hex string "8A4C240833C0F6C1E075068B442404D3E0C20800" is the assembly code for SHL that can be "injected" into a VB6 program for a fast SHL operation expecting two Long parameters (we're ignoring here that 32-bit longs in VB6 are signed, just pretend they are unsigned).
Along those same lines, what is the hex string of bytes representing assembler instructions that will do the same thing to return the sum of two 32-bit unsigned integers?
The hex code above for SHL is, according to the author:
mov eax, [esp+4]
mov cl, [esp+8]
shl eax, cl
ret 8
I spit those bytes into a file and tried unassembling them in a windows command prompt using the old debug utility, but I figured out it's not working with the newer instruction set because it didn't like EAX when I tried assembling something but it was happy with AX.
I know from comments in the source code that SHL EAX, CL is D3E0, but I don't have any reference to know what the bytes are for instruction ADD EAX, CL or I'd try it. (Though I know now that the operands have to be the same size.)
I tried flat assembler and am not getting anything I can figure out how to use. I used it to assemble the original SHL code and got a very different result, not the same bytes. Help?
I disassembled the bytes you provided and got the following code:
(__TEXT,__text) section
f:
00000000 movb 0x08(%esp),%cl
00000004 xorl %eax,%eax
00000006 testb $0xe0,%cl
00000009 jne 0x00000011
0000000b movl 0x04(%esp),%eax
0000000f shll %cl,%eax
00000011 retl $0x0008
Which is definitely more complicated than the source code the author provided. It checks that the second operand isn't too large, for example, which isn't in the code you showed at all (see Edit 2, below, for a more complete analysis). Here's a simple stdcall function that adds two arguments together and returns the result:
mov 4(%esp), %eax
add 8(%esp), %eax
ret $8
Assembling that gives me this output:
(__TEXT,__text) section
00000000 8b 44 24 04 03 44 24 08 c2 08 00
I hope those bytes do what you want them to!
Edit: Perhaps more usefully, I just did the same in C:
__attribute__((__stdcall__))
int f(int a, int b)
{
return a + b;
}
Compiled with -Oz and -fomit-frame-pointer it generates exactly the same code (well, functionally equivalent, anyway):
$ gcc -arch i386 -fomit-frame-pointer -Oz -c -o example.o example.c
$ otool -tv example.o
example.o:
(__TEXT,__text) section
_f:
00000000 movl 0x08(%esp),%eax
00000004 addl 0x04(%esp),%eax
00000008 retl $0x0008
The machine code output:
$ otool -t example.o
example.o:
(__TEXT,__text) section
00000000 8b 44 24 08 03 44 24 04 c2 08 00
Sure beats hand-writing assembly code!
Edit 2:
#ErikE asked in the comments below what would happen if a shift of 32 bits or greater was attempted. The disassembled code at the top of this answer (for the bytes provided in the original question) can be represented by the following higher-level code:
unsigned int shift_left(unsigned int a, unsigned char b)
{
if (b > 32)
return 0;
else
return a << b;
}
From this logic it's pretty easy to see that if you pass a value greater than 32 as the second parameter to the shift function, you'll just get 0 back.