What is the difference between these two forms of inline assembler in C?

Background: I was tasked with writing a data collection program for the Unitech HT630 that runs its own DOS operating system, which can run executable files compiled for 16-bit MS DOS, although with some limitations. I am using the Digital Mars C / C ++ compiler, which seems to work very well.

For some things, I can use the standard C libraries, but for other things, such as drawing on the device’s screen, I need assembly code. The assembly examples provided in the device documentation differ from how I was taught to use the built-in assembler code in C / C ++. For reference, BYTE in the examples below is of type unsigned char .

Example Code Example I:

 #include <dos.h> /* Set the state of a pixel */ void LCD_setpixel(BYTE x, BYTE y, BYTE status) { if(status > 1 || x > 63 || y > 127) { /* out of range, return */ return; } /* good data, set the pixel */ union REGS regs; regs.h.ah = 0x41; regs.h.al = status; regs.h.dh = x; regs.h.dl = y; int86(0x10, &regs, &regs); } 

As I was always taught to use the built-in assembly:

 /* Set the state of a pixel */ void LCD_setpixel(BYTE x, BYTE y, BYTE status) { if(status > 1 || x > 63 || y > 127) { /* out of range, return */ return; } /* good data, set the pixel */ asm { mov AH, 41H mov AL, status mov DH, x mov DL, y int 10H } } 

Both forms seem to work, I have not yet encountered a problem with any of the approaches. Is one form better than another for DOS programming? Does int86 function int86 something for me that I don’t process myself in my own build code in the second example?

Thanks in advance for any help.

+7
c assembly inline-assembly dos
source share
6 answers

When you use the int86 function int86 , this is a C time library call that sets the registers and issues a DOS int errupt function. Both methods are really the same with one exception, when you use the built-in assembler, the code is really embedded in the object code when compiling and binding.

The built-in assembly will be considered faster since you do not have the overhead associated with calling the C runtime library to call the DOS interrupt for you. It is up to you to ensure sufficient stack space when using the built-in assembly, while the C runtime library takes care of the allocation of stack space when setting registers before calling the int86 function.

int86 is a way to make calling DOS interrupts easier. It was extremely popular among the old compilers Borland Turbo C and Microsoft, I talk about old compilers before the release of Win 3.1.

Speaking of the 0x10 interrupt, which is responsible for video output, if I remember correctly, at that time some BIOSes destroyed the bp register, and a workaround was to do this:

 __asm{ push bp; } /* set up the registers */ int86(0x10, &regs, &regs); __asm{ pop bp; } 

You can find advanced BIOS features in the Ralph Brown interrupt list here . Also HelpPC v2.1 can also help find here .

+9
source share

the first form is more readable, which also takes into account something; -)

if you want to know if int86 is doing something behind your back, just compile your program and view the generated assembly code

+1
source share

By calling int86, your code remains in C. In any case, it writes a pixel, performing a system interrupt.

If you have a lot of pixels for recording, and you are starting to seriously encounter speed problems, there may be a more direct (and much less secure, but perhaps advisable) way to write directly to the pixel memory.

+1
source share

Both pieces of code do the same thing. The great advantage of the 1st is that there is a chance that you can still use it when switching compilers. And that you don’t stomp in the register that the "C" compiler code generator used for another purpose. Something you probably forget to take care of in your asm snippet.

+1
source share

You should check the compiler guide to find out who is responsible for restoring register values ​​after the built-in build section. Since your variables are assigned to registers, inadvertent changes in values ​​can lead to hard-to-reach errors. int86 (0x10,? regs and regs); saves registers and restores them after a program interrupt is executed.

Some compilers accept instructions for defining a clobber list (registers that should be saved and restored). Typically, the assembler section should save the registers and flags that will be changed using push and restore them using pop, either by the compiler or by ourselves. Therefore, the first example should be preferred.

+1
source share

This is not an inline assembly, it is C. A very low-level C using an interrupt function, but still C.

This page contains some documentation (for the DJGPP compiler, yours may work differently), including the structure used to represent registers. He also notes:

Please note that unlike __dpmi_int functions, requests that pass int86 and similar functions are specially processed to make them suitable for calling the real interrupt mode from the protected mode of the program. For example, if a particular routine takes a pointer to BX, int86 expects you to set the (protected mode) pointer to EBX. Therefore, int86 must have specific support for each interrupt and function you call this path. Currently, it only supports a subset of all available interrupts and functions [...]

0
source share

All Articles