Reading input into a BIOS level assembly?

I am building a very low level application and I have a bootloader ready. I essentially need to rebuild the scanf () function as I cannot use any libraries in the project. What would be a good basic assembly application that can read input? I can handle reading it to C char [] or whatever data type I want, I just need to figure out how to read the input without any system calls.

+3


source to share


1 answer


You seem to be writing code for real mode. You don't say which C compiler you are using, but your own answer assumes that you are indeed in real mode. I recommend the WATCOM C compiler as it can generate true 16-bit code. If you are using GCC with an option -m16

, I do not recommend it for 16-bit code. I have another Stackoverflow answer that discusses some of the GCC issues .

The best source for information on DOS and BIOS interrupts is the Ralph Brown Interrupt List . Information about receiving one keystroke through Int 16h / AH = 00 :

KEYBOARD - GET KEYSTROKE

AH = 00h

      

Return:

AH = BIOS scan code
AL = ASCII character

      

This BIOS feature will not echo characters, so another useful BIOS feature Int 10h / AH = 0eh displays one character to the terminal:

VIDEO - OUTPUT TELETICS

AH = 0Eh
AL = character to write
BH = page number
BL = foreground color (graphics modes only)

      

Return:

Nothing

      

Desc: Display a character on the screen, move the cursor and scroll the screen as needed

To print a character in text mode, you can put BX at 0, the character to print in AL and trigger an interrupt.


Using the information above, you can write some simple wrappers around both BIOS interrupts using inline assembly. In GCC, you can use Extended Inline Assembly templates . They might look like this:



#include <stdint.h>

static inline void
printch (char outchar, uint16_t attr)
{
   __asm__ ("int $0x10\n\t"
            :
            : "a"((0x0e << 8) | outchar),
              "b"(attr));
}

static inline char
getch (void)
{
   uint16_t inchar;

   __asm__ __volatile__ ("int $0x16\n\t"
            : "=a"(inchar)
            : "0"(0x0));

   return ((char)inchar);
}

      

In Watcom C, you can create functions with #pragma aux

:

#include <stdint.h>

void printch(char c, uint16_t pageattr);
char getch(void);

#pragma aux printch = \
    "mov ah, 0x0e" \
    "int 0x10" \
     parm [al] [bx] nomemory \
     modify [ah] nomemory

#pragma aux getch = \
    "xor ah, ah" \
    "int 0x16" \
     parm [] nomemory \
     modify [ah] nomemory \
     value [al]

      

With these basic functions, you only need to write a function that receives a string from the user, sends characters back as they are entered, and stores them in a buffer. ASCII character getch

returns for newline: carriage return \r

(0x0d). When we reach the maximum number of characters requested or a newline character is encountered, we stop and NUL terminates the line. Such a function might look like this:

/* Get string up to maxchars. NUL terminate string.
   Ensure inbuf has enough space for NUL.
   Carriage return is stripped from string.
   Return a pointer to the buffer passed in.
*/
char *getstr_echo(char *inbuf, int maxchars)
{
    char *bufptr = inbuf;

    while(bufptr < (inbuf + maxchars) && (*bufptr = getch()) != '\r')
        printch(*bufptr++, 0);

    *bufptr = '\0';
    return inbuf;
}

      

If you don't want to use inline assembly, you can create a build module with getch

and printch

, executed in a clean assembly. This is less efficient than code that can be generated with inline assembly, but much less error prone.


The function is getstr_echo

not complete and is intended to be used as a starting point for your own code. It handles things like backspace.

+1


source







All Articles