Sunday, February 23, 2014

A mini-project to display rectangles


Now let us move onto something more big…like displaying graphics.

Example: test5.c


/* generate 16 bit code                                     */
__asm__(".code16\n");
/* jump to main function or program code                    */
__asm__("jmpl $0x0000, $main\n");

#define MAX_COLS 320       /* maximum columns of the screen */
#define MAX_ROWS 200       /* maximum rows of the screen    */

/* function to print string onto the screen                 */
/* input ah = 0x0e                                          */
/* input al = <character to print>                          */
/* interrupt: 0x10                                          */
/* we use interrupt 0x10 with function code 0x0e to print   */
/* a byte in al onto the screen                             */
/* this function takes string as an argument and then       */
/* prints character by character until it founds null       */
/* character                                                */
void printString(const char* pStr) {
     while(*pStr) {
          __asm__ __volatile__ (
               "int $0x10" : : "a"(0x0e00 | *pStr), "b"(0x0007)
          );
          ++pStr;
     }
}

/* function to get a keystroke from the keyboard            */
/* input ah = 0x00                                          */
/* input al = 0x00                                          */
/* interrupt: 0x10                                          */
/* we use this function to hit a key to continue by the     */
/* user                                                     */
void getch() {
     __asm__ __volatile__ (
          "xorw %ax, %ax\n"
          "int $0x16\n"
     );
}

/* function to print a colored pixel onto the screen        */
/* at a given column and at a given row                     */
/* input ah = 0x0c                                          */
/* input al = desired color                                 */
/* input cx = desired column                                */
/* input dx = desired row                                   */
/* interrupt: 0x10                                          */
void drawPixel(unsigned char color, int col, int row) {
     __asm__ __volatile__ (
          "int $0x10" : : "a"(0x0c00 | color), "c"(col), "d"(row)
     );
}

/* function to clear the screen and set the video mode to   */
/* 320x200 pixel format                                     */
/* function to clear the screen as below                    */
/* input ah = 0x00                                          */
/* input al = 0x03                                          */
/* interrupt = 0x10                                         */
/* function to set the video mode as below                  */
/* input ah = 0x00                                          */
/* input al = 0x13                                          */
/* interrupt = 0x10                                         */
void initEnvironment() {
/* clear screen                                             */
     __asm__ __volatile__ (
          "int $0x10" : : "a"(0x03)
     );
     __asm__ __volatile__ (
          "int $0x10" : : "a"(0x0013)
     );
}

/* function to print rectangles in descending order of      */
/* their sizes                                              */
/* I follow the below sequence                              */
/* (left, top) to (left, bottom)                            */
/* (left, bottom) to (right, bottom)                        */
/* (right, bottom) to (right, top)                          */
/* (right, top) to (left, top)                              */
void initGraphics() {
     int i = 0, j = 0;
     int m = 0;
     int cnt1 = 0, cnt2 =0;
     unsigned char color = 10;

     for(;;) {
          if(m < (MAX_ROWS - m)) {
               ++cnt1;
          }
          if(m < (MAX_COLS - m - 3)) {
               ++cnt2;
          }

          if(cnt1 != cnt2) {
               cnt1 = 0;
               cnt2 = 0;
               m = 0;
               if(++color > 255) color= 0;
          }

          /* (left, top) to (left, bottom)                  */
          j = 0;
          for(i = m; i < MAX_ROWS - m; ++i) {
               drawPixel(color, j+m, i);
          }


          /* (left, bottom) to (right, bottom)              */
          for(j = m; j < MAX_COLS - m; ++j) {
               drawPixel(color, j, i);
          }

          /* (right, bottom) to (right, top)                */
          for(i = MAX_ROWS - m - 1 ; i >= m; --i) {
               drawPixel(color, MAX_COLS - m - 1, i);
          }
         
          /* (right, top) to (left, top)                    */
          for(j = MAX_COLS - m - 1; j >= m; --j) {
               drawPixel(color, j, m);
          }

          m += 6;
          if(++color > 255) color = 0;
     }
}  

/* function is boot code and it calls the below functions   */
/* print a message to the screen to make the user hit the   */
/* key to proceed further and then once the user hits then  */
/* it displays rectangles in the descending order           */
void main() {
     printString("Now in bootloader...hit a key to continue\n\r");
     getch();
     initEnvironment();
     initGraphics();
}



Now save the above code as test5.c and then follow the compilation instructions given by changing the input source file name and follow the instructions given to copy the compiled code to the boot sector of the floppy.

Now observe the result. You should see the below screen output if everything was fine.





Now hit a key to see what will happen further.











Observation:


If you closely look at the contents of the executable, you will observe that we were almost running out of space. As boot sector is only 512 bytes, we were able to embed only few functions into our program like intializing the environment and then printing colored rectangles but not more than that because it requires more than 512 bytes of space. Below is the snapshot for your reference.




That’s all for this article. Have fun and write more programs to explore the real mode and you will observe that it is real fun programming in real mode using bios Interrupts. In the next article, I will try to explain about Addressing modes used to access data, reading a floppy disk and its architecture and also why a boot loader is mostly written in Assembly than C and what are the constraints in writing a bootloader in C in terms of Code Generation

No comments:

Post a Comment