Inline C assembler macros for an AVR RTOS - c

I am trying to write a preemptive scheduler for AVR and therefore I need some assembler code ... but I have no experience with assembler. However, I wrote all the assembler code I think I need in some C macros. At compilation I get some errors related to assembler (constant value required and garbage at and of line), which makes me think that something is not correct in my macros ...
The following macro, load_SP(some_unsigned_char, some_unsigned_char), sets the stack pointer to a known memory location ... I am keeping the location in the global struct aux_SP;
Something similar is with load_PC(...) which is loading on the stack, a program counter: "func_pointer" which is actually, as the name suggest, a pointer to a function. I assume here that the program counter as well as the function pointer are represented on 2 bytes (because the flash is small enough)
For this I am using processor register R16. In order to leave this register untouched, I am saving its value first with the macro "save_R16(tempR)" and the restoring its value with the macro "load_R16(tempR)" where "tempR" as can be seen is a global C variable.
This is simply written in a header file. This along with another two macros (not written here because of their size) "pushRegs()" and "popRegs()" which are basically pushing and then popping all processors registers is ALL my assembler code ...
What should I do to correct my macros?
// used to store the current StackPointer when creating a new task until it is restored at the
// end of createNewTask function.
struct auxSP
{
unsigned char auxSPH;
unsigned char auxSPL;
};
struct auxSP cSP = {0,0};
// used to restore processor register when using load_SP or load_PC macros to perform
// a Stack Pointer or Program Counter load.
unsigned char tempReg = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////// assembler macros begin ////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// save processor register R16
#define save_R16(tempR) \
asm volatile( \
"STS tempR, R16 \n\t" \
);
// load processor register R16
#define load_R16(tempR) \
asm volatile( \
"LDS R16, tempR \n\t" \
);
// load the Stack Pointer. Warning: Alters the processor registers
#define load_SP(new_SP_H, new_SP_L) \
asm volatile( \
"LDI R16, new_SP_H \n\t" \
"OUT SPH, R16 \n\t" \
"LDI R16, new_SP_L \n\t" \
"OUT SPL, R16 \n\t" \
);
// load the Program Counter on stack. Warning: Alters the processor registers
#define load_PC(func_pointer) \
asm volatile( \
"LDS r16, LOW(func_pointer) \n\t" \
"PUSH r16 \n\t" \
"LDS r16, HIGH(func_pointer) \n\t" \
"PUSH r16 \n\t" \
);

Your main source of reference for this should be http://www.nongnu.org/avr-libc/user-manual/inline_asm.html
Avoid using "unsigned char" - use "uint8_t", as it is shorter and more explicit. Avoid macros - use static inline functions instead whenever possible. Don't invent your own struct for auxSP, especially not using a different endian ordering than the target normally uses - just use uint16_t. Don't write things in assembly when you can write them in C. And don't split up asm statements that need to be combined together (such as preserving R16 in one statement, then using it in a second statement).
Where does that leave us?
It's a long time since I have done much AVR programming, but this might get you started:
static inline uint16_t read_SP(void) {
uint16_t sp;
asm volatile(
"in %A[sp], __SP_L__ \n\t"
"in %B[sp], __SP_H__ \n\t"
: [sp] "=r" (sp) :: );
return sp;
}
static inline void write_SP(uint16_t sp) {
asm volatile(
"out __SP_L__, %A[sp] \n\t"
"out __SP_H__, %B[sp] \n\t"
:: [sp] "r" (sp) : );
}
typedef void (*FVoid)(void);
static inline void load_PC(FVoid f) __attribute__((noreturn));
static inline void load_PC(FVoid f) {
asm volatile(
"ijmp"
:: "z" (f) );
__builtin_unreachable();
}
You will probably also want to make sure you disable interrupts before using any of these.

Here is an example of C code I did on an AVR platform. It is not macros but functions because it was more adapted for me.
void call_xxx(uint32_t address, uint16_t data)
{
asm volatile("push r15"); //has to be saved
asm volatile("ldi r18, 0x01"); //r15 will be copied to SPMCSR
asm volatile("mov r15, r18"); //copy r18 to r15 (cannot be done directly)
asm volatile("movw r0, r20"); //r1:r0 <= r21:r20 //should conatain "data" parameter
asm volatile("movw r30, r22"); //31:r30<=r23:r22 // should contain "address" parameter ...
asm volatile("sts 0x5b, r24"); //RAMPZ
asm volatile("rcall .+0"); //push PC on top of stack and never pop it
asm volatile("jmp 0x3ecb7"); //secret function
asm volatile("eor r1, r1"); //null r1
asm volatile("pop r15"); //restore value
return;
}
Also try without your \n\t this may be the "garbage at and of line"
The problem of constant value required may come from here :
#define save_R16(tempR) \
asm volatile( \
"STS tempR, R16 \n\t" \
);
For this I am less sure but STS (and other) requires an address that may need to be fixed at compile time. So depending on how you use the macro it may not compile. If tempR is not fixed, you may use functions instead of macro.

Related

arm inline assembly - store C variable in arm register

Trying to save a variable in an arm register using inline assembly.
unsigned int lma_offset = 0x1234; // typically calculated, hardcoded for example
__asm volatile ("MOV R10, %[input]"
: [input] "=r" (lma_offset)
);
This changes lma_offset to 0xd100 in my case, instead of setting the register. What am I doing wrong?
PS: when I declare lma_offset as const it gives a compiler error because lma_offset is used as output. So obviously something is wrong, still I cant find the correct syntax for this.
For future reference, according to Erics comment
const unsigned int lma_offset = 0x10000;
__asm__ volatile ("MOV R10, %[input]"
: // no C variable outputs
: [input] "r" (lma_offset)
: "R10" // tell the compiler R10 is modified
);
using double : and replacing the "=r" with "r" indeed solves the problem.
It would also be possible to ask the compiler to have that constant already in R10 for an asm statement, by using a register local variable to force the "r" input to pick r10. (Then we can omit the redundant mov r10, r10).
register unsigned r10 __asm__("r10") = lma_offset; // picks r10 for "r" constraints, no other *guaranteed* effects
__asm__ volatile ("# inline asm went here" // empty template, actually just a comment you can see if looking at the compiler's asm output
: // no C variable outputs
: [input] "r" (lma_offset)
: // no clobbers needed
);
When writing a register to some output C variable it would result in
unsigned int lma_offset = 0x0;
__asm__ volatile ("MOV %[output], R11"
: [output] "=r" (lma_offset)
// no clobbers needed; reading a register doesn't step on the compiler's toes
);

How to mark as clobbered input operands (C register variables) in extended GCC inline assembly?

Problem description
I'm trying to design the C code unpacking array A of uint32_t elements to array B of uint32_t elements where each element of A is unpacked to two consecutive elements of B so that B[2*i] contains low 16 bits of A[i] and B[2*i + 1] contains high 16 bits of A[i] shifted right, i.e.,
B[2*i] = A[i] & 0xFFFFul;
B[2*i+1] = A[i] >> 16u;
Note the arrays are aligned to 4, have variable length, but A always contains multiple of 4 of uint32_t and the size is <= 32, B has sufficient space for unpacking and we are on ARM Cortex-M3.
Current bad solution in GCC inline asm
As the GCC is not good in optimizing this unpacking, I wrote unrolled C & inline asm to make it speed optimized with acceptable code size and register usage. The unrolled code looks like this:
static void unpack(uint32_t * src, uint32_t * dst, uint8_t nmb8byteBlocks)
{
switch(nmb8byteBlocks) {
case 8:
UNPACK(src, dst)
case 7:
UNPACK(src, dst)
...
case 1:
UNPACK(src, dst)
default:;
}
}
where
#define UNPACK(src, dst) \
asm volatile ( \
"ldm %0!, {r2, r4} \n\t" \
"lsrs r3, r2, #16 \n\t" \
"lsrs r5, r4, #16 \n\t" \
"stm %1!, {r2-r5} \n\t" \
: \
: "r" (src), "r" (dst) \
: "r2", "r3", "r4", "r5" \
);
It works until the GCC's optimizer decides to inline the function (wanted property) and reuse register variables src and dst in the next code. Clearly, due to the ldm %0! and stm %1! instructions the src and dst contain different addresses when leaving switch statement.
How to solve it?
I do not know how to inform GCC that registers used for src and dst are invalid after the last UNPACK macro in last case 1:.
I tried to pass them as output operands in all or only last macro ("=r" (mem), "=r" (pma)) or somehow (how) to include them in inline asm clobbers but it only make the register handling worse with bad code again.
Only one solution is to disable function inlining (__attribute__ ((noinline))), but in this case I lose the advantage of GCC which can cut the proper number of macros and inline it if the nmb8byteBlocks is known in compile time. (The same drawback holds for rewriting code to pure assembly.)
Is there any possibility how to solve this in inline assembly?
I think you are looking for the + constraint modifier, which means "this operand is both read and written". (See the "Modifiers" section of GCC's inline-assembly documentation.)
You also need to tell GCC that this asm reads and writes memory; the easiest way to do that is by adding "memory" to the clobber list. And that you clobber the "condition codes" with lsrs, so a "cc" clobber is also necessary. Try this:
#define UNPACK(src, dst) \
asm volatile ( \
"ldm %0!, {r2, r4} \n\t" \
"lsrs r3, r2, #16 \n\t" \
"lsrs r5, r4, #16 \n\t" \
"stm %1!, {r2-r5} \n\t" \
: "+r" (src), "+r" (dst) \
: /* no input-only operands */ \
: "r2", "r3", "r4", "r5", "memory", "cc" \
);
(Micro-optimization: since you don't use the condition codes from the shifts, it's better to use lsr instead of lsrs. It also makes the code easier to read months later; future you won't be scratching your head wondering if there's some reason why the condition codes are actually needed here. EDIT: I've been reminded that lsrs has a more compact encoding than lsr in Thumb format, which is enough of a reason to use it even though the condition codes aren't needed.)
(I would like to say that you'd get better register allocator behavior if you let GCC pick the scratch registers, but I don't know how to tell it to pick scratch registers in a particular numeric order as required by ldm and stm, or how to tell it to use only the registers accessible to 2-byte Thumb instructions.)
(It is possible to specify exactly what memory is read and written with "m"-type input and output operands, but it's complicated and may not improve things much. If you discover that this code works but causes a bunch of unrelated stuff to get reloaded from memory into registers unnecessarily, consult How can I indicate that the memory *pointed* to by an inline ASM argument may be used?)
(You may get better code generation for what unpack is inlined into, if you change its function signature to
static void unpack(const uint32_t *restrict src,
uint32_t *restrict dst,
unsigned int nmb8byteBlocks)
I would also experiment with adding if (nmb8byteBlocks > 8) __builtin_trap(); as the first line of the function.)
Many thanks zwol, this is exactly what I was looking for but couldn't find it in GCC inline assembly pages. It solved the problem perfectly - now the GCC makes a copy of src and dst in different registers and uses them correctly after the last UNPACK macro.Two remarks:
I use lsrs because it compiles to 2-bytes Cortex-M3 native lsrs. If I use flags untouching lsr version, it compiles to 4-bytes mov.w r3, r2, lsr #16 -> the 16-bit Thumb 2 lsr is with 's' by default. Without the 's', the 32-bit Thumb 2 must be used (I have to check it). Anyway, I should add "cc" in clobbers in this case.
In code above, I removed the nmb8byteBlocks value range check to make it clear. But of course, your last sentence is a good point not only for all C programmers.

Performing syscalls in inline C assembly causes a segfault

I recently dabbled into low level programming, and want to make a function somesyscall that accepts (CType rax, CType rbx, CType rcx, CType rdx). struct CType looks like:
/*
TYPES:
0 int
1 string
2 bool
*/
typedef struct {
void* val;
int typev;
} CType;
the function is a bit messy, but in theory should work:
#include <errno.h>
#include <stdbool.h>
#include "ctypes.h"
//define functions to set registers
#define seteax(val) asm("mov %0, %%rax" :: "g" (val) : "%rax")
#define setebx(val) asm("mov %0, %%rbx" :: "g" (val) : "%rbx")
#define setecx(val) asm("mov %0, %%rcx" :: "g" (val) : "%rcx")
#define setedx(val) asm("mov %0, %%rdx" :: "g" (val) : "%rdx")
///////////////////////////////////
#define setregister(value, register) \
switch (value.typev) { \
case 0: { \
register(*((double*)value.val)); \
break; \
} \
case 1: { \
register(*((char**)value.val)); \
break; \
} \
case 2: { \
register(*((bool*)value.val)); \
break; \
} \
}
static inline long int somesyscall(CType a0, CType a1, CType a2, CType a3) {
//set the registers
setregister(a0, seteax);
setregister(a1, setebx);
setregister(a2, setecx);
setregister(a3, setedx);
///////////////////
asm("int $0x80"); //interrupt
//fetch back the rax
long int raxret;
asm("mov %%rax, %0" : "=r" (raxret));
return raxret;
}
when I run with:
#include "syscall_unix.h"
int main() {
CType rax;
rax.val = 39;
rax.typev = 0;
CType rbx;
rbx.val = 0;
rbx.typev = 0;
CType rcx;
rcx.val = 0;
rcx.typev = 0;
CType rdx;
rdx.val = 0;
rdx.typev = 0;
printf("%ld", somesyscall(rax, rbx, rcx, rdx));
}
and compile (and run binary) with
clang test.c
./a.out
I get a segfault. However, everything seems to look correct. Am I doing anything wrong here?
After macro expansion you will have something like
long int raxret;
asm("mov %0, %%rax" :: "g" (a0) : "%rax");
asm("mov %0, %%rbx" :: "g" (a1) : "%rbx");
asm("mov %0, %%rcx" :: "g" (a2) : "%rcx");
asm("mov %0, %%rdx" :: "g" (a3) : "%rdx");
asm("int $0x80");
asm("mov %%rax, %0" : "=r" (raxret));
This doesn't work because you haven't told the compiler that it's not allowed to reuse rax, rbx, rcx, and rdx for something else during the sequence of asm statements. For instance, the register allocator might decide to copy a2 from the stack to rax and then use rax as the input operand for the mov %0, %%rcx instruction -- clobbering the value you put in rax.
(asm statements with no outputs are implicitly volatile so the first 5 can't reorder relative to each other, but the final one can move anywhere. For example, be moved after later code to where the compiler finds it convenient to generate raxret in a register of its choice. RAX might no longer have the system call return value at that point - you need to tell the compiler that the output comes from the asm statement that actually produces it, without assuming any registers survive between asm statements.)
There are two different ways to tell the compiler not to do that:
Put only the int instruction in an asm, and express all of the requirements for what goes in what register with constraint letters:
asm volatile ("int $0x80"
: "=a" (raxret) // outputs
: "a" (a0), "b" (a1), "c" (a2), "d" (a3) // pure inputs
: "memory", "r8", "r9", "r10", "r11" // clobbers
// 32-bit int 0x80 system calls in 64-bit code zero R8..R11
// for native "syscall", clobber "rcx", "r11".
);
This is possible for this simple example but not always possible in general, because there aren't constraint letters for every single register, especially not on CPUs other than x86.
// use the native 64-bit syscall ABI
// remove the r8..r11 clobbers for 32-bit mode
Put only the int instruction in an asm, and express the requirements for what goes in what register with explicit register variables:
register long rax asm("rax") = a0;
register long rbx asm("rbx") = a1;
register long rcx asm("rcx") = a2;
register long rdx asm("rdx") = r3;
// Note that int $0x80 only looks at the low 32 bits of input regs
// so `uint32_t` would be more appropriate than long
// but really you should just use "syscall" in 64-bit code.
asm volatile ("int $0x80"
: "+r" (rax) // read-write: in=call num, out=retval
: "r" (rbx), "r" (rcx), "r" (rdx) // read-only inputs
: "memory", "r8", "r9", "r10", "r11"
);
return rax;
This will work regardless of which registers you need to use. It's also probably more compatible with the macros you're trying to use to erase types.
Incidentally, if this is 64-bit x86/Linux then you should be using syscall rather than int $0x80, and the arguments belong in the ABI-standard incoming-argument registers (rdi, rsi, rdx, rcx, r8, r9 in that order), not in rbx, rcx, rdx etc. The system call number still goes in rax, though. (Use call numbers from #include <asm/unistd.h> or <sys/syscall.h>, which will be appropriate for the native ABI of the mode you're compiling for, another reason not to use int $0x80 in 64-bit mode.)
Also, the asm statement for the system-call instruction should have a "memory" clobber and be declared volatile; almost all system calls access memory somehow.
(As a micro-optimization, I suppose you could have a list of system calls that don't read memory, write memory, or modify the virtual address space, and avoid the memory clobber for them. It would be a pretty short list and I'm not sure it would be worth the trouble. Or use the syntax shown in How can I indicate that the memory *pointed* to by an inline ASM argument may be used? to tell GCC which memory might be read or written, instead of a "memory" clobber, if you write wrappers for specific syscalls.
Some of the no-pointer cases include getpid where it would be a lot faster to call into the VDSO to avoid a round trip to kernel mode and back, like glibc does for the appropriate syscalls. That also applies to clock_gettime which does take pointers.)
Incidentally, beware of the actual kernel interfaces not matching up with the interfaces presented by the C library's wrappers. This is generally documented in the NOTES section of the man page, e.g. for brk(2) and getpriority(2)

What ensures reads/writes of operands occurs at desired timed with extended ASM?

According to GCC's Extended ASM and Assembler Template, to keep instructions consecutive, they must be in the same ASM block. I'm having trouble understanding what provides the scheduling or timings of reads and writes to the operands in a block with multiple statements.
As an example, EBX or RBX needs to be preserved when using CPUID because, according to the ABI, the caller owns it. There are some open questions with respect to the use of EBX and RBX, so we want to preserve it unconditionally (its a requirement). So three instructions need to be encoded into a single ASM block to ensure the consecutive-ness of the instructions (re: the assembler template discussed in the first paragraph):
unsigned int __FUNC = 1, __SUBFUNC = 0;
unsigned int __EAX, __EBX, __ECX, __EDX;
__asm__ __volatile__ (
"push %ebx;"
"cpuid;"
"pop %ebx"
: "=a"(__EAX), "=b"(__EBX), "=c"(__ECX), "=d"(__EDX)
: "a"(__FUNC), "c"(__SUBFUNC)
);
If the expression representing the operands is interpreted at the wrong point in time, then __EBX will be the saved EBX (and not the CPUID's EBX), which will likely be a pointer to the Global Offset Table (GOT) if PIC is enabled.
Where, exactly, does the expression specify that the store of CPUID's %EBX into __EBX should happen (1) after the PUSH %EBX; (2) after the CPUID; but (3) before the POP %EBX?
In your question you present some code that does a push and pop of ebx. The idea of saving ebx in the event that you compile with gcc using -fPIC (position independent code) is correct. It is up to our function not to clobber ebx upon return in that situation. Unfortunately the way you have defined the constraints you explicitly use ebx. Generally the compiler will warn you (error: inconsistent operand constraints in an 'asm') if you are using PIC code and you specify =b as an output constraint. Why it doesn't produce a warning for you is unusual.
To get around this problem you can let the assembler template choose a register for you. Instead of pushing and popping we simply exchange %ebx with an unused register chosen by the compiler and restore it by exchanging it back after. Since we don't wish to have the compiler clobber our input registers during the exchange we specify early clobber modifier, thus ending up with a constraint of =&r (instead of =b in the OPs code). More on modifiers can be found here. Your code (for 32 bit) would look something like:
unsigned int __FUNC = 1, __SUBFUNC = 0;
unsigned int __EAX, __EBX, __ECX, __EDX;
__asm__ __volatile__ (
"xchgl\t%%ebx, %k1\n\t" \
"cpuid\n\t" \
"xchgl\t%%ebx, %k1\n\t"
: "=a"(__EAX), "=&r"(__EBX), "=c"(__ECX), "=d"(__EDX)
: "a"(__FUNC), "c"(__SUBFUNC));
If you intend to compile for X86_64 (64 bit) you'll need to save the entire contents of %rbx. The code above will not quite work. You'd have to use something like:
uint32_t __FUNC = 1, __SUBFUNC = 0;
uint32_t __EAX, __ECX, __EDX;
uint64_t __BX; /* Big enough to hold a 64 bit value */
__asm__ __volatile__ (
"xchgq\t%%rbx, %q1\n\t" \
"cpuid\n\t" \
"xchgq\t%%rbx, %q1\n\t"
: "=a"(__EAX), "=&r"(__BX), "=c"(__ECX), "=d"(__EDX)
: "a"(__FUNC), "c"(__SUBFUNC));
You could code this up using conditional compilation to deal with both X86_64 and i386:
uint32_t __FUNC = 1, __SUBFUNC = 0;
uint32_t __EAX, __ECX, __EDX;
uint64_t __BX; /* Big enough to hold a 64 bit value */
#if defined(__i386__)
__asm__ __volatile__ (
"xchgl\t%%ebx, %k1\n\t" \
"cpuid\n\t" \
"xchgl\t%%ebx, %k1\n\t"
: "=a"(__EAX), "=&r"(__BX), "=c"(__ECX), "=d"(__EDX)
: "a"(__FUNC), "c"(__SUBFUNC));
#elif defined(__x86_64__)
__asm__ __volatile__ (
"xchgq\t%%rbx, %q1\n\t" \
"cpuid\n\t" \
"xchgq\t%%rbx, %q1\n\t"
: "=a"(__EAX), "=&r"(__BX), "=c"(__ECX), "=d"(__EDX)
: "a"(__FUNC), "c"(__SUBFUNC));
#else
#error "Unknown architecture."
#endif
GCC has a __cpuid macro defined in cpuid.h. It defined the macro so that it only saves the ebx and rbx register when required. You can find the GCC 4.8.1 macro definition here to get an idea of how they handle cpuid in cpuid.h.
The astute reader may ask the question - what stops the compiler from choosing ebx or rbx as the scratch register to use for the exchange. The compiler knows about ebx and rbx in the context of PIC, and will not allow it to be used as a scratch register. This is based on my personal observations over the years and reviewing the assembler (.s) files generated from C code. I can't say for certain how more ancient versions of gcc handled it so it could be a problem.
I think you understand, but to be clear, the "consecutive" rule means that this:
asm ("a");
asm ("b");
asm ("c");
... might get other instructions interposed, so if that's not desirable then it must be rewritten like this:
asm ("a\n"
"b\n"
"c");
... and now it will be inserted as a whole.
As for the cpuid snippet, we have two problems:
The cpuid instruction will overwrite ebx, and hence clobber the data that PIC code must keep there.
We want to extract the value that cpuid places in ebx while never returning to compiled code with the "wrong" ebx value.
One possible solution would be this:
unsigned int __FUNC = 1, __SUBFUNC = 0;
unsigned int __EAX, __EBX, __ECX, __EDX;
__asm__ __volatile__ (
"push %ebx;"
"cpuid;"
"mov %ebx, %ecx"
"pop %ebx"
: "=c"(__EBX)
: "a"(__FUNC), "c"(__SUBFUNC)
: "eax", "edx"
);
__asm__ __volatile__ (
"push %ebx;"
"cpuid;"
"pop %ebx"
: "=a"(__EAX), "=c"(__ECX), "=d"(__EDX)
: "a"(__FUNC), "c"(__SUBFUNC)
);
There's no need to mark ebx as clobbered as you're putting it back how you found it.
(I don't do much Intel programming, so I may have some of the assembler-specific details off there, but this is how asm works.)

Inline ASM in C, compiled with MinGW/GCC using "-masm=intel": "undefined reference"

I've been looking around a lot for examples of using inline ASM and I've seen seen a few different approaches.
I've gone with the -masm=intel option when compiling. As I understand it, when using this option you can just write the inline ASM as you would with intel syntax.
I have also seen approaches where people use ".intel_syntax"
When I compile I get the following message.
i586-mingw32msvc-gcc -masm=intel -o KDOS.exe KDOS.c
/tmp/ccVIXhRF.o:KDOS.c:(.text+0x5f): undefined reference to `address'
/tmp/ccVIXhRF.o:KDOS.c:(.text+0x6a): undefined reference to `ipAddr'
/tmp/ccVIXhRF.o:KDOS.c:(.text+0x79): undefined reference to `csAddr'
/tmp/ccVIXhRF.o:KDOS.c:(.text+0x11d): undefined reference to `address'
collect2: ld returned 1 exit status
I've looked around for a solution but I can't seem to find one. I've seen threads saying you can't pass C variables into inline ASM, but I've also seen some stuff saying there are workarounds. They didn't quite apply to what I was doing though so I wasn't really sure what to make of them. Sorry if it is an obvious answer but this is my first time using inline ASM much less fooling around with converting the syntax.
Here is my code. I am working through a book and this is some sample code within it. It was not compiled with gcc in the book so this is why I need to convert to intel syntax, because I need it to run on windows obviously. This is my modified version of the code:
// KDOS.c
// Chapter 2
#include<stdio.h>
#define WORD unsigned short
#define IDT_001_ADDR 0 //start address of first IVT vector
#define IDT_255_ADDR 1020 //start address of last IVT vector
#define IDT_VECTOR_SZ 4 //size of each IVT Vector (in bytes)
#define BP __asm{ int 0x3 } //break point
void main()
{
WORD csAddr; //Code segment of given interrupt
WORD ipAddr; //Starting IP for given interrupt
short address; //address in memory (0-1020)
WORD vector; //IVT entry ID (i.e., 0..255)
char dummy; //strictly to help pause program execution
vector = 0x0;
printf("\n---Dumping IVT from bottom up---\n");
printf("Vector\tAddress\t\n");
for
(
address=IDT_001_ADDR;
address<=IDT_255_ADDR;
address=address+IDT_VECTOR_SZ,vector++
)
{
printf("%03d\t%08p\t",vector,address);
//IVT starts at bottom of memory, so CS is alway 0x0
__asm__
(
".intel_syntax;"
"PUSH ES;"
"MOV AX, 0;"
"MOV ES,AX;"
"MOV BX,address;"
"MOV AX,ES:[BX];"
"MOV ipAddr,AX;"
"INC BX;"
"INC BX;"
"MOV AX,ES:[BX];"
"MOV csAddr,AX;"
"POP ES;"
);
printf("[CS:IP]=[%04X,%04X]\n",csAddr,ipAddr);
}
printf("press [ENTER] key to continue:");
scanf("%c",&dummy);
printf("\n---Overwrite IVT from top down---\n");
/*
Program will die somwhere around 0x4*
Note: can get same results via DOS debug.exe -e command
*/
for
(
address=IDT_255_ADDR;
address>=IDT_001_ADDR;
address=address-IDT_VECTOR_SZ,vector--
)
{
printf("Nulling %03d\t%08p\n",vector,address);
__asm__
(
".intel_syntax;"
"PUSH ES;"
"MOV AX,0;"
"MOV ES,AX;"
"MOV BX,address;"
"MOV ES:[BX],AX;"
"INC BX;"
"INC BX;"
"MOV ES:[BX],AX;"
"POP ES;"
);
}
return;
}/*end main()------------------------------------------------------------*/
Any help would be greatly appreciated. Once again my apologies if it is something obvious.
Actually you can pass C arguments to inline asm. But You have to define it after the asm code part.
In Your case something like this could work (You should add -masm=intel to the gcc's command line):
asm(
".intel_syntax noprefix;\n\t"
...
"MOV BX,%[address];\n\t"
...
".intel_syntax prefix;\n\t"
:: [address] "m" address, ...
: "AX", "BX", /* all changed registers to inform compiler to save them if needed */
);
See examples in a similar question.

Resources