A little recap!
VGA, introduced by IBM in 1987, is a display standard that allows various screen resolutions and color depths. In bootloaders, VGA is often used because it is widely supported by PCs and offers a simple interface for setting graphics modes.
- Text Mode: Displays characters using a grid, typically 80x25 characters with 16 colors.
- Graphics Mode: Allows pixel-by-pixel control, enabling more complex images and interfaces.
Graphics Modes in VGA
VGA offers several graphics modes, but the most commonly used in bootloaders are:
- 320x200 with 256 colors (Mode 13h): This is often preferred for its balance between resolution and color depth.
- 640x480 with 16 colors (Mode 12h): Provides higher resolution but with limited color depth.
- 640x480 with 256 colors (Mode X): An extension of standard VGA allowing for higher color depth at higher resolution.
Why Use Graphics Mode in Bootloaders?
While text mode is adequate for simple bootloaders, graphics mode provides several advantages:
- Enhanced User Interface: Create more visually appealing and user-friendly interfaces.
- Rich Graphics: Display logos, progress bars, and other graphical elements.
- Better Resolution: Higher resolutions allow more information to be displayed at once.
Setting Up VGA Graphics Mode
To set a graphics mode in VGA, we typically interact with the BIOS interrupt 0x10. Here’s a step-by-step guide to switch to 320x200 mode with 256 colors (Mode 13h):
Enter Assembly Mode: The bootloader code usually starts in assembly.
Invoke BIOS Interrupt 0x10: Use this interrupt to switch video modes.
mov ah, 0x00 ; Function: Set Video Mode
mov al, 0x13 ; Mode: 320x200 with 256 colors
int 0x10 ; Call BIOS Video Services
Setup Pixel Data: Once in the desired mode, you can manipulate pixel data directly in the VGA memory, starting at address 0xA0000
.
Displaying Graphics
1 Drawing Pixels
Once in graphics mode, you can directly manipulate video memory to draw pixels on the screen.

The framebuffer is essentially a linear array of bytes, where each byte represents the color of one pixel on the screen. The memory layout typically starts from the top-left corner of the screen and proceeds row by row until the bottom-right corner. This means that the byte at index 0 represents the color of the pixel at coordinates (0,0), the byte at index 1 represents the color of the pixel at coordinates (1,0), and so on, until the byte at index 63,999 represents the color of the pixel at coordinates (319,199).
Frame Buffer Layout:
- The frame buffer for mode 13h starts at the segment address
0xA0000
. - Each pixel is represented by a single byte.
- The total memory required is
320 x 200 = 64,000 bytes
or 64 KB.
To Plot a pixel at coordinates (x, y):
- The memory address for a pixel is calculated as:
offset = (y * Screen_Width) + x
for mode 13h, the Screen_Width = 320
- The video memory for the pixel is then:
- memory address =
0xA0000 + offset
- memory address =
The address of a pixel at coordinates (x, y)
can be calculated as:
Address = 0xA0000 + (y*320) + x
Color Representation:
- Each byte in the framebuffer corresponds to a pixel color, which is an index into a palette of 256 colors.
- The VGA palette defines the actual RGB values for these 256 indices, which can be modified to change the colors displayed on the screen.
Value | Color |
---|---|
0 | Black |
1 | Blue |
2 | Green |
3 | Cyan |
4 | Red |
5 | Magenta |
6 | Brown |
7 | Light Gray |
8 | Dark Gray |
9 | Light Blue |
10 | Light Green |
11 | Light Cyan |
12 | Light Red |
13 | Light Magenta |
14 | Yellow |
15 | White |
Table I. The first 16 VGA colors.
Palette Configuration:
- The VGA palette consists of 256 entries, each representing an RGB color.
- You can set the palette using VGA I/O ports to define which RGB color corresponds to each of the 256 possible pixel values.
Let's modify our barebone bootloader code to switch to graphics mode and then plot a pixel on the specified place on the screen.
; Set VGA Mode 13h (320x200, 256 colors)
mov ax, 0x13 ; Set AX to 0x13 (mode 13h)
int 0x10 ; Call BIOS interrupt to set the video mode
GRAPHICS_MODE_13_SCREEN_WIDTH equ 320
; Draw red pixels using the draw_pixel function
mov ax, 100 ; y coordinate
mov bx, 100 ; x coordinate
mov cl, 4 ; red color
call draw_pixel
; Function to draw a pixel at (x, y) with color index
; ax = y coordinate
; bx = x coordinate
; cl = color
draw_pixel:
push ax
push bx
push cx
push dx
push di
mov dx, 0xA000 ; Set segment to video memory
mov es, dx ; es now points to video memory
mov dx, GRAPHICS_MODE_13_SCREEN_WIDTH ; set dx to screen width
mul dx ; ax = y * 320 (multiplying ax by dx)
add ax, bx ; ax = y * 320 + bx
mov di, ax ; move offset to di
mov al, cl ; set al to the color for the pixel
stosb ; store al to ES:DI and increment di
pop di
pop dx
pop cx
pop bx
pop ax
ret
section .text
; Example usage: draw a red pixel at (100, 100)
mov cx, 100 ; x coordinate
mov dx, 100 ; y coordinate
mov bx, 4 ; color index (red)
call draw_pixel

2 Drawing Text in Graphics Mode
It involves rendering each character as a set of pixels on the screen. Each character can be represented by a bitmap, which is binary representation of the pixels that form the character. The simplest way is to create a bitmap [matrix of size 8x8] in which we have 0's and 1's of where 0 means draw pixel of background color while 1 means drawing pixel of foreground color. Thus making an text character.
Bitmap: also known as a raster graphic, is a type of digital image composed of a matrix of dots called pixels. Each pixel in a bitmap corresponds to a specific color and is stored as a binary value. Bitmaps are commonly used for images, icons, and graphical representations in computer systems.
In memory, a bitmap is typically stored as a series of bytes, where each byte (or group of bytes) represents the color of a pixel or group of pixels. For simplicity, we'll focus on monochrome (1-bit per pixel) and 8-bit color bitmaps.
Monochrome Bitmap (1-bit per Pixel)
In a monochrome bitmap, each bit represents one pixel. A 1
might represent a white pixel, and a 0
might represent a black pixel (or vice versa).
Example: 8x8 Monochrome Bitmap
0b00011000
0b00111100
0b01111110
0b11011011
0b11111111
0b01111110
0b00111100
0b00011000
This pattern could represent a simple 8x8 image, where each bit corresponds to one pixel.
8-bit Color Bitmap (256 Colors)
In an 8-bit color bitmap, each pixel is represented by one byte, allowing for 256 different colors.
Example: 4x4 8-bit Color Bitmap
0x01 0x02 0x03 0x04
0x05 0x06 0x07 0x08
0x09 0x0A 0x0B 0x0C
0x0D 0x0E 0x0F 0x10
Each value corresponds to a color in a color palette.
Steps to Implement Drawing Text
- Define Font Data: A simple
8x8
font can be defined using an array where each byte represents a row of the character. Each byte in the bitmap represents a row of 8 pixels. For example, the characterA
might be represented by an 8-byte array.
For example, let's represent the character A
:
.XX.
X..X
XXXX
X..X
X..X
This character can be stored as the following bitmap:
01111110 ; Row 0
10000001 ; Row 1
10000001 ; Row 2
11111111 ; Row 3
10000001 ; Row 4
10000001 ; Row 5
10000001 ; Row 6
00000000 ; Row 7
Here, 0 means black and 1 means white so when it is drawn on a black screen, it is printed as below:
111111 ; Row 0
1 1 ; Row 1
1 1 ; Row 2
11111111 ; Row 3
1 1 ; Row 4
1 1 ; Row 5
1 1 ; Row 6
; Row 7
isnt' it awesome!
- Draw Each Character: Convert each character to pixels and draw them on the screen.
We have the bitmap font mapping, which will store our all printable characters. Now we need a way to use that mapping decode them in a way that it prints as a character. We will need to write a function that can, Find the bitmap for the given character from the font table and a function Draw the bitmap on the screen at the desired coordinates.
Here are the steps to draw character on the screen:
1 Calculate Pixel Offset: Calculate the starting address for the character in video memory on the coordinates. For example: (x = 100, y = 50)
- Screen width in mode 13h: 320 pixels
- Starting Y coordinate (top row): 50
- Starting X coordinate (left column): 100
- Formula for offset:
offset = (y * screen_width) + x
= (50 * 320) + 100
= 16000 + 100
= 16100
2 Draw the Character: Loop through each row of the character bitmap and set the corresponding pixels in video memory.
– Drawing Each Row
Row 0
: 01111110
The starting address for the row: 16100
Bitmap for row 0 = 01111110
Pixel Calculation: For each bit in the row, determine if it's set (1) or not (0).
Bit 7 (most significant): 0 -> Skip
Bit 6: 1 -> Set pixel at address 16100
Bit 5: 1 -> Set pixel at address 16101
Bit 4: 1 -> Set pixel at address 16102
Bit 3: 1 -> Set pixel at address 16103
Bit 2: 1 -> Set pixel at address 16104
Bit 1: 1 -> Set pixel at address 16105
Bit 0 (least significant): 0 -> Skip
Row 1
: 10000001
Start address for row 1: 16100 (base address for row 0) + 320 (offset for each new row, given the screen width is 320 pixels).
The starting address is = 16100 + 320 = 16420
Bitmap for row 1 = 10000001
Pixel Calculation: For each bit in the row, determine if it's set (1) or not (0).
Bit 7 (most significant): 1 -> Set pixel at address 16420
Bit 6: 0 -> Skip
Bit 5: 0 -> Skip
Bit 4: 0 -> Skip
Bit 3: 0 -> Skip
Bit 2: 0 -> Skip
Bit 1: 0 -> Skip
Bit 0 (least significant): 1 -> Set pixel at address 16427
Similarly we traverse the complete bitmap for the particular character. Thus effectively draw character on the screen.
Our bitmap mapping would from 32 to 126 which is ASCII 32 (space) to ASCII 126 (tilde ~). So, how do we get the offset of particular character in the bitmap table.
font_table:
db row1 of ASCII 32
db row2 of ASCII 32
db row3 of ASCII 32
db row4 of ASCII 32
db row5 of ASCII 32
db row6 of ASCII 32
db row7 of ASCII 32
db row8 of ASCII 32
;; end of character bitmap
;; start of bitmap of next ASCII character
db row1 of ASCII 33
db row2 of ASCII 33
db row3 of ASCII 33
db row4 of ASCII 33
db row5 of ASCII 33
db row6 of ASCII 33
db row7 of ASCII 33
db row8 of ASCII 33
;; end of character bitmap
;; similarly upto ASCII 126
Computer stores characters using the ASCII (American Standard for Information Interchange) encoding, where each character is represented by a unique numerical value. In this encoding, for instance, the character A
is represented by the number 65. Since each bitmap is 8 bytes long then we can use this to get the effective offset of character into the font table.
Calculate Offset in font table:
Since out table start with the bitmap for character 32 (space), the offset for character A
is (A - 32) * 8
.
- 8 = one character bitmap is 8 bytes long.
Let's write the code:
Now we understood the basics, time to implement it.
This completes the 8x8 font table for all printable ASCII characters from 32 to 126. Each db
line represents the 8x8 bitmap of a character in binary form.
Implement the Character Drawing Function:
Implement the function to draw a character. The function draw_char
will use the font table to print characters on the screen.
section .text
global _start
_start:
; Set video mode to 13h
mov ax, 0x0013
int 0x10
; Print character 'A' at (10, 10)
mov al, 'A' ; Character to print
mov bx, 10 ; X position
mov cx, 10 ; Y position
call draw_char
; Hang the system
hlt
; Function: draw_char
; Inputs: AL = character, BX = X position, CX = Y position
draw_char:
pusha
mov ah, 0 ; Video page number (always 0 in mode 13h)
sub al, 32 ; Convert ASCII to font table index
shl ax, 3 ; Multiply index by 8 (each character is 8 bytes)
add ax, font_table ; Point AX to the start of the character bitmap
mov di, ax ; ES:DI -> character bitmap
mov dx, 320 ; Screen width in mode 13h (320 pixels)
mov si, cx ; SI = Y position
mul dx ; AX = Y * 320
add ax, bx ; AX += X position
mov di, ax ; DI = Screen position (Y * 320 + X)
mov es, 0xA000 ; Segment address of video memory
mov cx, 8 ; 8 rows per character
next_row:
lodsb ; Load next byte from font_table to AL
mov ah, al ; Copy to AH
mov al, 0 ; AL = 0
mov dx, 8 ; 8 bits per row
next_pixel:
shl ah, 1 ; Shift AH left
jnc skip_pixel ; If carry (bit 0) is 0, skip
mov byte [es:di], 0x0F ; Set pixel color to white (0x0F)
skip_pixel:
inc di ; Move to next pixel on screen
dec dx
jnz next_pixel ; Repeat for 8 bits (pixels)
add di, 320-8 ; Move to the start of the next row
loop next_row ; Repeat for 8 rows
popa
ret
Explanation:
1 Data Section:
The font_table
defines the 8x8 bitmap for the character A
. Each byte represents one row of the character.
The bitmap for A
is defined as follows:
section .data
font_table:
; Character 'A' in binary
db 0b01111110 ; Row 1: 0x7E
db 0b11000011 ; Row 2: 0xC3
db 0b11000011 ; Row 3: 0xC3
db 0b11000011 ; Row 4: 0xC3
db 0b11111111 ; Row 5: 0xFF
db 0b11000011 ; Row 6: 0xC3
db 0b11000011 ; Row 7: 0xC3
db 0b11000011 ; Row 8: 0xC3
Each byte represents one row of the character, with bits set to 1 representing pixels to be turned on.
Each byte in font_table
corresponds to a row in the 8x8 grid for the character 'A'. For example, the first byte 0b01111110
represents the top row of the character 'A'.
Draw
Function:
The draw_A
function is responsible for drawing the character 'A' on the screen at the specified position.
; Function: draw_A
; Inputs: BX = X position, CX = Y position
draw_A:
pusha ; Save all general-purpose registers
mov si, font_table ; SI = Pointer to the beginning of the font_table
mov dx, 0 ; Offset in the font table for character 'A'
add si, dx ; Add DX to SI to point to the correct character bitmap
mov dx, 320 ; Screen width in mode 13h (320 pixels)
mov di, cx ; DI = Y position
mul dx ; AX = Y * 320
add ax, bx ; AX += X position
mov di, ax ; DI = Screen position (Y * 320 + X)
mov es, 0xA000 ; Segment address of video memory
mov cx, 8 ; 8 rows per character
next_row:
lodsb ; Load next byte from font_table to AL
mov ah, al ; Copy to AH
mov al, 0 ; Clear AL
mov dx, 8 ; 8 bits per row
next_pixel:
shr ah, 1 ; Shift AH right
jnc skip_pixel ; If carry (bit 0) is 0, skip
mov byte [es:di], 0x0F ; Set pixel color to white (0x0F)
skip_pixel:
inc di ; Move to next pixel on screen
dec dx
jnz next_pixel ; Repeat for 8 bits (pixels)
add di, 320 - 8 ; Move to the start of the next row
loop next_row ; Repeat for 8 rows
popa ; Restore all general-purpose registers
ret
- Save Registers:
pusha
saves all general-purpose registers. - Set Source Index:
mov si, font_table
loads the address of the font table intoSI
. - Offset:
mov dx, 0
andadd si, dx
set the offset to zero for characterA
(no offset needed here, since we only have a single character as of now). - Set Screen Width:
mov dx, 320
sets the screen width in pixels. - Calculate Screen Position:
mov di, cx
Loads the Y position intoDI
.mul dx
multipliescx
by 320 to calculate the offset for the Y position.add ax, bx
adds the X position to the offset.mov di, ax
setsDI
to the final screen position.
- Set Video Memory Segment:
mov es, 0xA000
sets the segment address of the video memory. - Draw Character:
mov cx, 8
sets up a loop to handle 8 rows.next_row
: label marks the start of the row loop.lodsb
loads the next byte from the font table intoAL
.mov ah, al
copies the byte toAH
mov al, 0
clearsAL
.mov dx, 8
sets up a loop to handle 8 bits (pixels) per row.next_pixel:
label marks the start of the pixel loop.shl ah, 1
ShiftsAH
left, and it sets the carry flag with the leftmost shifted bit.jnc skip_pixel
skips setting pixels if the carry flag is not set.mov byte [es:di], 0x0F
sets the pixel color to white.skip_pixel:
label marks the skip position.inc di
moves di to the next pixel on the screen.dec dx
decrements the pixel counter.jnz next_pixel
jumps back tonext_pixel
if there are more pixels to process.add di, 320 -8
moves to the start of the next row.loop next_row
jumps back tonext_row
if there are more to process.
- Restore Registers:
popa
restores all general-purpose registers. - Return:
ret
returns from the function.
Note: This code is just for the character A only. we have to create bitmap for all ASCII characters (from 32 to 127), we will need to define the bitmaps for each character in a similar 8x8 format as we did for the character A
.
ASCII Character Bitmap Definition:
Below is a bitmap representation for each character. This example includes a few characters to illustrate the idea.
font_table:
; Offset 0x00 (ASCII 32: Space ' ')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00000000 ; Row 2: 0x00
db 0b00000000 ; Row 3: 0x00
db 0b00000000 ; Row 4: 0x00
db 0b00000000 ; Row 5: 0x00
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
; Offset 0x08 (ASCII 33: Exclamation Mark '!')
db 0b00011000 ; Row 0: 0x18
db 0b00011000 ; Row 1: 0x18
db 0b00011000 ; Row 2: 0x18
db 0b00011000 ; Row 3: 0x18
db 0b00011000 ; Row 4: 0x18
db 0b00000000 ; Row 5: 0x00
db 0b00011000 ; Row 6: 0x18
db 0b00000000 ; Row 7: 0x00
; Offset 0x10 (ASCII 34: Double Quote '"')
db 0b01100110 ; Row 0: 0x66
db 0b01100110 ; Row 1: 0x66
db 0b00100100 ; Row 2: 0x24
db 0b00000000 ; Row 3: 0x00
db 0b00000000 ; Row 4: 0x00
db 0b00000000 ; Row 5: 0x00
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
; Offset 0x18 (ASCII 35: Hash '#')
db 0b01100110 ; Row 0: 0x66
db 0b01100110 ; Row 1: 0x66
db 0b11111111 ; Row 2: 0xFF
db 0b01100110 ; Row 3: 0x66
db 0b11111111 ; Row 4: 0xFF
db 0b01100110 ; Row 5: 0x66
db 0b01100110 ; Row 6: 0x66
db 0b00000000 ; Row 7: 0x00
; Offset 0x20 (ASCII 36: Dollar Sign '$')
db 0b00011000 ; Row 0: 0x18
db 0b00111111 ; Row 1: 0x3F
db 0b01100000 ; Row 2: 0x60
db 0b00111110 ; Row 3: 0x3E
db 0b00000111 ; Row 4: 0x07
db 0b01111110 ; Row 5: 0x7E
db 0b00011000 ; Row 6: 0x18
db 0b00000000 ; Row 7: 0x00
; Offset 0x28 (ASCII 37: Percent '%')
db 0b11000001 ; Row 0: 0xC1
db 0b11000110 ; Row 1: 0xC6
db 0b00001100 ; Row 2: 0x0C
db 0b00011000 ; Row 3: 0x18
db 0b00110000 ; Row 4: 0x30
db 0b01100011 ; Row 5: 0x63
db 0b11000011 ; Row 6: 0xC3
db 0b00000000 ; Row 7: 0x00
; Offset 0x30 (ASCII 38: Ampersand '&')
db 0b00110000 ; Row 0: 0x30
db 0b01101100 ; Row 1: 0x6C
db 0b01100100 ; Row 2: 0x64
db 0b00111000 ; Row 3: 0x38
db 0b01101110 ; Row 4: 0x6E
db 0b11001100 ; Row 5: 0xCC
db 0b01110110 ; Row 6: 0x76
db 0b00000000 ; Row 7: 0x00
; Offset 0x38 (ASCII 39: Single Quote ''')
db 0b00011000 ; Row 0: 0x18
db 0b00011000 ; Row 1: 0x18
db 0b00110000 ; Row 2: 0x30
db 0b00000000 ; Row 3: 0x00
db 0b00000000 ; Row 4: 0x00
db 0b00000000 ; Row 5: 0x00
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
; Offset 0x40 (ASCII 40: Left Parenthesis '(')
db 0b00001100 ; Row 0: 0x0C
db 0b00011000 ; Row 1: 0x18
db 0b00110000 ; Row 2: 0x30
db 0b00110000 ; Row 3: 0x30
db 0b00110000 ; Row 4: 0x30
db 0b00011000 ; Row 5: 0x18
db 0b00001100 ; Row 6: 0x0C
db 0b00000000 ; Row 7: 0x00
; Offset 0x48 (ASCII 41: Right Parenthesis ')')
db 0b00110000 ; Row 0: 0x30
db 0b00011000 ; Row 1: 0x18
db 0b00001100 ; Row 2: 0x0C
db 0b00001100 ; Row 3: 0x0C
db 0b00001100 ; Row 4: 0x0C
db 0b00011000 ; Row 5: 0x18
db 0b00110000 ; Row 6: 0x30
db 0b00000000 ; Row 7: 0x00
; Offset 0x50 (ASCII 42: Asterisk '*')
db 0b00011000 ; Row 0: 0x18
db 0b01101100 ; Row 1: 0x6C
db 0b00111100 ; Row 2: 0x3C
db 0b11111111 ; Row 3: 0xFF
db 0b00111100 ; Row 4: 0x3C
db 0b01101100 ; Row 5: 0x6C
db 0b00011000 ; Row 6: 0x18
db 0b00000000 ; Row 7: 0x00
; Offset 0x58 (ASCII 43: Plus '+')
db 0b00011000 ; Row 0: 0x18
db 0b00011000 ; Row 1: 0x18
db 0b00011000 ; Row 2: 0x18
db 0b11111111 ; Row 3: 0xFF
db 0b00011000 ; Row 4: 0x18
db 0b00011000 ; Row 5: 0x18
db 0b00011000 ; Row 6: 0x18
db 0b00000000 ; Row 7: 0x00
; Offset 0x60 (ASCII 44: Comma ',')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00000000 ; Row 2: 0x00
db 0b00000000 ; Row 3: 0x00
db 0b00000000 ; Row 4: 0x00
db 0b00011000 ; Row 5: 0x18
db 0b00011000 ; Row 6: 0x18
db 0b00110000 ; Row 7: 0x30
; Offset 0x68 (ASCII 45: Hyphen '-')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00000000 ; Row 2: 0x00
db 0b11111111 ; Row 3: 0xFF
db 0b00000000 ; Row 4: 0x00
db 0b00000000 ; Row 5: 0x00
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
; Offset 0x70 (ASCII 46: Period '.')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00000000 ; Row 2: 0x00
db 0b00000000 ; Row 3: 0x00
db 0b00000000 ; Row 4: 0x00
db 0b00011000 ; Row 5: 0x18
db 0b00011000 ; Row 6: 0x18
db 0b00000000 ; Row 7: 0x00
; Offset 0x78 (ASCII 47: Slash '/')
db 0b00000001 ; Row 0: 0x01
db 0b00000010 ; Row 1: 0x02
db 0b00000110 ; Row 2: 0x06
db 0b00001100 ; Row 3: 0x0C
db 0b00011000 ; Row 4: 0x18
db 0b00110000 ; Row 5: 0x30
db 0b01100000 ; Row 6: 0x60
db 0b11000000 ; Row 7: 0xC0
; Offset 0x80 (ASCII 48: Zero '0')
db 0b00111100 ; Row 0: 0x3C
db 0b01100110 ; Row 1: 0x66
db 0b01101110 ; Row 2: 0x6E
db 0b01110110 ; Row 3: 0x76
db 0b01100110 ; Row 4: 0x66
db 0b01100110 ; Row 5: 0x66
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0x88 (ASCII 49: One '1')
db 0b00011000 ; Row 0: 0x18
db 0b00111000 ; Row 1: 0x38
db 0b01111000 ; Row 2: 0x78
db 0b00011000 ; Row 3: 0x18
db 0b00011000 ; Row 4: 0x18
db 0b00011000 ; Row 5: 0x18
db 0b01111110 ; Row 6: 0x7E
db 0b00000000 ; Row 7: 0x00
; Offset 0x90 (ASCII 50: Two '2')
db 0b00111100 ; Row 0: 0x3C
db 0b01100110 ; Row 1: 0x66
db 0b00000110 ; Row 2: 0x06
db 0b00001100 ; Row 3: 0x0C
db 0b00110000 ; Row 4: 0x30
db 0b01100000 ; Row 5: 0x60
db 0b01111110 ; Row 6: 0x7E
db 0b00000000 ; Row 7: 0x00
; Offset 0x98 (ASCII 51: Three '3')
db 0b00111100 ; Row 0: 0x3C
db 0b01100110 ; Row 1: 0x66
db 0b00000110 ; Row 2: 0x06
db 0b00011100 ; Row 3: 0x1C
db 0b00000110 ; Row 4: 0x06
db 0b01100110 ; Row 5: 0x66
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0xA0 (ASCII 52: Four '4')
db 0b00001100 ; Row 0: 0x0C
db 0b00011100 ; Row 1: 0x1C
db 0b00111100 ; Row 2: 0x3C
db 0b01101100 ; Row 3: 0x6C
db 0b11111110 ; Row 4: 0xFE
db 0b00001100 ; Row 5: 0x0C
db 0b00001100 ; Row 6: 0x0C
db 0b00000000 ; Row 7: 0x00
; Offset 0xA8 (ASCII 53: Five '5')
db 0b01111110 ; Row 0: 0x7E
db 0b01100000 ; Row 1: 0x60
db 0b01111100 ; Row 2: 0x7C
db 0b00000110 ; Row 3: 0x06
db 0b00000110 ; Row 4: 0x06
db 0b01100110 ; Row 5: 0x66
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0xB0 (ASCII 54: Six '6')
db 0b00111100 ; Row 0: 0x3C
db 0b01100000 ; Row 1: 0x60
db 0b01111100 ; Row 2: 0x7C
db 0b01100110 ; Row 3: 0x66
db 0b01100110 ; Row 4: 0x66
db 0b01100110 ; Row 5: 0x66
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0xB8 (ASCII 55: Seven '7')
db 0b01111110 ; Row 0: 0x7E
db 0b01100110 ; Row 1: 0x66
db 0b00000110 ; Row 2: 0x06
db 0b00001100 ; Row 3: 0x0C
db 0b00001100 ; Row 4: 0x0C
db 0b00001100 ; Row 5: 0x0C
db 0b00001100 ; Row 6: 0x0C
db 0b00000000 ; Row 7: 0x00
; Offset 0xC0 (ASCII 56: Eight '8')
db 0b00111100 ; Row 0: 0x3C
db 0b01100110 ; Row 1: 0x66
db 0b01100110 ; Row 2: 0x66
db 0b00111100 ; Row 3: 0x3C
db 0b01100110 ; Row 4: 0x66
db 0b01100110 ; Row 5: 0x66
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0xC8 (ASCII 57: Nine '9')
db 0b00111100 ; Row 0: 0x3C
db 0b01100110 ; Row 1: 0x66
db 0b01100110 ; Row 2: 0x66
db 0b00111110 ; Row 3: 0x3E
db 0b00000110 ; Row 4: 0x06
db 0b01100110 ; Row 5: 0x66
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0xD0 (ASCII 58: Colon ':')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00011000 ; Row 2: 0x18
db 0b00011000 ; Row 3: 0x18
db 0b00000000 ; Row 4: 0x00
db 0b00011000 ; Row 5: 0x18
db 0b00011000 ; Row 6: 0x18
db 0b00000000 ; Row 7: 0x00
; Offset 0xD8 (ASCII 59: Semicolon ';')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00011000 ; Row 2: 0x18
db 0b00011000 ; Row 3: 0x18
db 0b00000000 ; Row 4: 0x00
db 0b00011000 ; Row 5: 0x18
db 0b00011000 ; Row 6: 0x18
db 0b00110000 ; Row 7: 0x30
; Offset 0xE0 (ASCII 60: Less Than '<')
db 0b00000000 ; Row 0: 0x00
db 0b00000110 ; Row 1: 0x06
db 0b00011000 ; Row 2: 0x18
db 0b01100000 ; Row 3: 0x60
db 0b11000000 ; Row 4: 0xC0
db 0b01100000 ; Row 5: 0x60
db 0b00011000 ; Row 6: 0x18
db 0b00000110 ; Row 7: 0x06
; Offset 0xE8 (ASCII 61: Equals '=')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b11111111 ; Row 2: 0xFF
db 0b00000000 ; Row 3: 0x00
db 0b11111111 ; Row 4: 0xFF
db 0b00000000 ; Row 5: 0x00
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
; Offset 0xF0 (ASCII 62: Greater Than '>')
db 0b00000000 ; Row 0: 0x00
db 0b01100000 ; Row 1: 0x60
db 0b00011000 ; Row 2: 0x18
db 0b00000110 ; Row 3: 0x06
db 0b00000011 ; Row 4: 0x03
db 0b00000110 ; Row 5: 0x06
db 0b00011000 ; Row 6: 0x18
db 0b01100000 ; Row 7: 0x60
; Offset 0xF8 (ASCII 63: Question Mark '?')
db 0b00111100 ; Row 0: 0x3C
db 0b01100110 ; Row 1: 0x66
db 0b00000110 ; Row 2: 0x06
db 0b00001100 ; Row 3: 0x0C
db 0b00011000 ; Row 4: 0x18
db 0b00000000 ; Row 5: 0x00
db 0b00011000 ; Row 6: 0x18
db 0b00000000 ; Row 7: 0x00
; Offset 0x100 (ASCII 64: At Sign '@')
db 0b00111100 ; Row 0: 0x3C
db 0b01000010 ; Row 1: 0x42
db 0b01011010 ; Row 2: 0x5A
db 0b01011110 ; Row 3: 0x5E
db 0b01011110 ; Row 4: 0x5E
db 0b01000000 ; Row 5: 0x40
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0x108 (ASCII 65: Capital A 'A')
db 0b00111100 ; Row 0: 0x3C
db 0b01100110 ; Row 1: 0x66
db 0b01100110 ; Row 2: 0x66
db 0b01111110 ; Row 3: 0x7E
db 0b01100110 ; Row 4: 0x66
db 0b01100110 ; Row 5: 0x66
db 0b01100110 ; Row 6: 0x66
db 0b00000000 ; Row 7: 0x00
; Offset 0x110 (ASCII 66: Capital B 'B')
db 0b01111100 ; Row 0: 0x7C
db 0b01100110 ; Row 1: 0x66
db 0b01100110 ; Row 2: 0x66
db 0b01111100 ; Row 3: 0x7C
db 0b01100110 ; Row 4: 0x66
db 0b01100110 ; Row 5: 0x66
db 0b01111100 ; Row 6: 0x7C
db 0b00000000 ; Row 7: 0x00
; Offset 0x118 (ASCII 67: Capital C 'C')
db 0b00111100 ; Row 0: 0x3C
db 0b01100110 ; Row 1: 0x66
db 0b01100000 ; Row 2: 0x60
db 0b01100000 ; Row 3: 0x60
db 0b01100000 ; Row 4: 0x60
db 0b01100110 ; Row 5: 0x66
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0x120 (ASCII 68: Capital D 'D')
db 0b01111100 ; Row 0: 0x7C
db 0b01100110 ; Row 1: 0x66
db 0b01100010 ; Row 2: 0x62
db 0b01100010 ; Row 3: 0x62
db 0b01100010 ; Row 4: 0x62
db 0b01100110 ; Row 5: 0x66
db 0b01111100 ; Row 6: 0x7C
db 0b00000000 ; Row 7: 0x00
; Offset 0x128 (ASCII 69: Capital E 'E')
db 0b01111110 ; Row 0: 0x7E
db 0b01100000 ; Row 1: 0x60
db 0b01100000 ; Row 2: 0x60
db 0b01111100 ; Row 3: 0x7C
db 0b01100000 ; Row 4: 0x60
db 0b01100000 ; Row 5: 0x60
db 0b01111110 ; Row 6: 0x7E
db 0b00000000 ; Row 7: 0x00
; Offset 0x130 (ASCII 70: Capital F 'F')
db 0b01111110 ; Row 0: 0x7E
db 0b01100000 ; Row 1: 0x60
db 0b01100000 ; Row 2: 0x60
db 0b01111100 ; Row 3: 0x7C
db 0b01100000 ; Row 4: 0x60
db 0b01100000 ; Row 5: 0x60
db 0b01100000 ; Row 6: 0x60
db 0b00000000 ; Row 7: 0x00
; Offset 0x138 (ASCII 71: Capital G 'G')
db 0b00111100 ; Row 0: 0x3C
db 0b01100110 ; Row 1: 0x66
db 0b01100000 ; Row 2: 0x60
db 0b01100000 ; Row 3: 0x60
db 0b01101110 ; Row 4: 0x6E
db 0b01100110 ; Row 5: 0x66
db 0b00111110 ; Row 6: 0x3E
db 0b00000000 ; Row 7: 0x00
; Offset 0x140 (ASCII 72: Capital H 'H')
db 0b01100110 ; Row 0: 0x66
db 0b01100110 ; Row 1: 0x66
db 0b01100110 ; Row 2: 0x66
db 0b01111110 ; Row 3: 0x7E
db 0b01100110 ; Row 4: 0x66
db 0b01100110 ; Row 5: 0x66
db 0b01100110 ; Row 6: 0x66
db 0b00000000 ; Row 7: 0x00
; Offset 0x148 (ASCII 73: Capital I 'I')
db 0b00111100 ; Row 0: 0x3C
db 0b00011000 ; Row 1: 0x18
db 0b00011000 ; Row 2: 0x18
db 0b00011000 ; Row 3: 0x18
db 0b00011000 ; Row 4: 0x18
db 0b00011000 ; Row 5: 0x18
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0x150 (ASCII 74: Capital J 'J')
db 0b00011110 ; Row 0: 0x1E
db 0b00001100 ; Row 1: 0x0C
db 0b00001100 ; Row 2: 0x0C
db 0b00001100 ; Row 3: 0x0C
db 0b01101100 ; Row 4: 0x6C
db 0b01101100 ; Row 5: 0x6C
db 0b00111000 ; Row 6: 0x38
db 0b00000000 ; Row 7: 0x00
; Offset 0x158 (ASCII 75: Capital K 'K')
db 0b01100110 ; Row 0: 0x66
db 0b01101100 ; Row 1: 0x6C
db 0b01111000 ; Row 2: 0x78
db 0b01110000 ; Row 3: 0x70
db 0b01111000 ; Row 4: 0x78
db 0b01101100 ; Row 5: 0x6C
db 0b01100110 ; Row 6: 0x66
db 0b00000000 ; Row 7: 0x00
; Offset 0x160 (ASCII 76: Capital L 'L')
db 0b01100000 ; Row 0: 0x60
db 0b01100000 ; Row 1: 0x60
db 0b01100000 ; Row 2: 0x60
db 0b01100000 ; Row 3: 0x60
db 0b01100000 ; Row 4: 0x60
db 0b01100000 ; Row 5: 0x60
db 0b01111110 ; Row 6: 0x7E
db 0b00000000 ; Row 7: 0x00
; Offset 0x168 (ASCII 77: Capital M 'M')
db 0b01100011 ; Row 0: 0x63
db 0b01110111 ; Row 1: 0x77
db 0b01111111 ; Row 2: 0x7F
db 0b01101010 ; Row 3: 0x6A
db 0b01100011 ; Row 4: 0x63
db 0b01100011 ; Row 5: 0x63
db 0b01100011 ; Row 6: 0x63
db 0b00000000 ; Row 7: 0x00
; Offset 0x170 (ASCII 78: Capital N 'N')
db 0b01100011 ; Row 0: 0x63
db 0b01110011 ; Row 1: 0x73
db 0b01111011 ; Row 2: 0x7B
db 0b01111111 ; Row 3: 0x7F
db 0b01101111 ; Row 4: 0x6F
db 0b01100111 ; Row 5: 0x67
db 0b01100011 ; Row 6: 0x63
db 0b00000000 ; Row 7: 0x00
; Offset 0x178 (ASCII 79: Capital O 'O')
db 0b00111100 ; Row 0: 0x3C
db 0b01100110 ; Row 1: 0x66
db 0b01100110 ; Row 2: 0x66
db 0b01100110 ; Row 3: 0x66
db 0b01100110 ; Row 4: 0x66
db 0b01100110 ; Row 5: 0x66
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0x180 (ASCII 80: Capital P 'P')
db 0b01111100 ; Row 0: 0x7C
db 0b01100110 ; Row 1: 0x66
db 0b01100110 ; Row 2: 0x66
db 0b01111100 ; Row 3: 0x7C
db 0b01100000 ; Row 4: 0x60
db 0b01100000 ; Row 5: 0x60
db 0b01100000 ; Row 6: 0x60
db 0b00000000 ; Row 7: 0x00
; Offset 0x1B8 (ASCII 81: Capital Q 'Q')
db 0b00111100 ; Row 0: 0x3C
db 0b01100110 ; Row 1: 0x66
db 0b01100110 ; Row 2: 0x66
db 0b01100110 ; Row 3: 0x66
db 0b01100110 ; Row 4: 0x66
db 0b01100110 ; Row 5: 0x66
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0x190 (ASCII 82: Capital R 'R')
db 0b01111100 ; Row 0: 0x7C
db 0b01100110 ; Row 1: 0x66
db 0b01100110 ; Row 2: 0x66
db 0b01111100 ; Row 3: 0x7C
db 0b01101100 ; Row 4: 0x6C
db 0b01100110 ; Row 5: 0x66
db 0b01100110 ; Row 6: 0x66
db 0b00000000 ; Row 7: 0x00
; Offset 0x198 (ASCII 83: Capital S 'S')
db 0b00111100 ; Row 0: 0x3C
db 0b01100110 ; Row 1: 0x66
db 0b01100000 ; Row 2: 0x60
db 0b00111100 ; Row 3: 0x3C
db 0b00000110 ; Row 4: 0x06
db 0b01100110 ; Row 5: 0x66
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0x1A0 (ASCII 84: Capital T 'T')
db 0b01111110 ; Row 0: 0x7E
db 0b01011010 ; Row 1: 0x5A
db 0b00011000 ; Row 2: 0x18
db 0b00011000 ; Row 3: 0x18
db 0b00011000 ; Row 4: 0x18
db 0b00011000 ; Row 5: 0x18
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0x1A8 (ASCII 85: Capital U 'U')
db 0b01100110 ; Row 0: 0x66
db 0b01100110 ; Row 1: 0x66
db 0b01100110 ; Row 2: 0x66
db 0b01100110 ; Row 3: 0x66
db 0b01100110 ; Row 4: 0x66
db 0b01100110 ; Row 5: 0x66
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0x1B0 (ASCII 86: Capital V 'V')
db 0b01100110 ; Row 0: 0x66
db 0b01100110 ; Row 1: 0x66
db 0b01100110 ; Row 2: 0x66
db 0b01100110 ; Row 3: 0x66
db 0b00100100 ; Row 4: 0x24
db 0b00100100 ; Row 5: 0x24
db 0b00011000 ; Row 6: 0x18
db 0b00000000 ; Row 7: 0x00
; Offset 0x1B8 (ASCII 87: Capital W 'W')
db 0b01100011 ; Row 0: 0x63
db 0b01100011 ; Row 1: 0x63
db 0b01100011 ; Row 2: 0x63
db 0b01101011 ; Row 3: 0x6B
db 0b01101011 ; Row 4: 0x6B
db 0b01111111 ; Row 5: 0x7F
db 0b01100110 ; Row 6: 0x66
db 0b00000000 ; Row 7: 0x00
; Offset 0x1C0 (ASCII 88: Capital X 'X')
db 0b01100110 ; Row 0: 0x66
db 0b01100110 ; Row 1: 0x66
db 0b00111100 ; Row 2: 0x3C
db 0b00011000 ; Row 3: 0x18
db 0b00111100 ; Row 4: 0x3C
db 0b01100110 ; Row 5: 0x66
db 0b01100110 ; Row 6: 0x66
db 0b00000000 ; Row 7: 0x00
; Offset 0x1C8 (ASCII 89: Capital Y 'Y')
db 0b01100110 ; Row 0: 0x66
db 0b01100110 ; Row 1: 0x66
db 0b01100110 ; Row 2: 0x66
db 0b00111100 ; Row 3: 0x3C
db 0b00011000 ; Row 4: 0x18
db 0b00011000 ; Row 5: 0x18
db 0b00011000 ; Row 6: 0x18
db 0b00000000 ; Row 7: 0x00
; Offset 0x1D0 (ASCII 90: Capital Z 'Z')
db 0b01111110 ; Row 0: 0x7E
db 0b00000110 ; Row 1: 0x06
db 0b00001100 ; Row 2: 0x0C
db 0b00011000 ; Row 3: 0x18
db 0b00110000 ; Row 4: 0x30
db 0b01100000 ; Row 5: 0x60
db 0b01111110 ; Row 6: 0x7E
db 0b00000000 ; Row 7: 0x00
; Offset 0x1D8 (ASCII 91: Left Square Bracket '[')
db 0b00011110 ; Row 0: 0x1E
db 0b00011000 ; Row 1: 0x18
db 0b00011000 ; Row 2: 0x18
db 0b00011000 ; Row 3: 0x18
db 0b00011000 ; Row 4: 0x18
db 0b00011000 ; Row 5: 0x18
db 0b00011110 ; Row 6: 0x1E
db 0b00000000 ; Row 7: 0x00
; Offset 0x1E0 (ASCII 92: Backslash '\')
db 0b01100000 ; Row 0: 0x60
db 0b01100000 ; Row 1: 0x60
db 0b00110000 ; Row 2: 0x30
db 0b00110000 ; Row 3: 0x30
db 0b00011000 ; Row 4: 0x18
db 0b00011000 ; Row 5: 0x18
db 0b00001100 ; Row 6: 0x0C
db 0b00000000 ; Row 7: 0x00
; Offset 0x1E8 (ASCII 93: Right Square Bracket ']')
db 0b00011110 ; Row 0: 0x1E
db 0b00001100 ; Row 1: 0x0C
db 0b00001100 ; Row 2: 0x0C
db 0b00001100 ; Row 3: 0x0C
db 0b00001100 ; Row 4: 0x0C
db 0b00001100 ; Row 5: 0x0C
db 0b00011110 ; Row 6: 0x1E
db 0b00000000 ; Row 7: 0x00
; Offset 0x1F0 (ASCII 94: Caret '^')
db 0b00001000 ; Row 0: 0x08
db 0b00011100 ; Row 1: 0x1C
db 0b00110110 ; Row 2: 0x36
db 0b01100011 ; Row 3: 0x63
db 0b01100011 ; Row 4: 0x63
db 0b00000000 ; Row 5: 0x00
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
; Offset 0x1F8 (ASCII 95: Underscore '_')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00000000 ; Row 2: 0x00
db 0b00000000 ; Row 3: 0x00
db 0b00000000 ; Row 4: 0x00
db 0b00000000 ; Row 5: 0x00
db 0b01111111 ; Row 6: 0x7F
db 0b00000000 ; Row 7: 0x00
; Offset 0x200 (ASCII 96: Grave Accent '`')
db 0b00011000 ; Row 0: 0x18
db 0b00001100 ; Row 1: 0x0C
db 0b00000110 ; Row 2: 0x06
db 0b00000000 ; Row 3: 0x00
db 0b00000000 ; Row 4: 0x00
db 0b00000000 ; Row 5: 0x00
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
; Offset 0x208 (ASCII 97: Lowercase a 'a')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00011100 ; Row 2: 0x1C
db 0b00100010 ; Row 3: 0x22
db 0b00111110 ; Row 4: 0x3E
db 0b01000010 ; Row 5: 0x42
db 0b00111110 ; Row 6: 0x3E
db 0b00000000 ; Row 7: 0x00
; Offset 0x210 (ASCII 98: Lowercase b 'b')
db 0b01100000 ; Row 0: 0x60
db 0b01100000 ; Row 1: 0x60
db 0b01111100 ; Row 2: 0x7C
db 0b01100110 ; Row 3: 0x66
db 0b01100110 ; Row 4: 0x66
db 0b01111100 ; Row 5: 0x7C
db 0b01100000 ; Row 6: 0x60
db 0b01100000 ; Row 7: 0x60
; Offset 0x218 (ASCII 99: Lowercase c 'c')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00011100 ; Row 2: 0x1C
db 0b00100010 ; Row 3: 0x22
db 0b00100000 ; Row 4: 0x20
db 0b00111100 ; Row 5: 0x3C
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
; Offset 0x220 (ASCII 100: Lowercase d 'd')
db 0b00000110 ; Row 0: 0x06
db 0b00000110 ; Row 1: 0x06
db 0b00011110 ; Row 2: 0x1E
db 0b00110110 ; Row 3: 0x36
db 0b00100110 ; Row 4: 0x26
db 0b00011110 ; Row 5: 0x1E
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
; Offset 0x228 (ASCII 101: Lowercase e 'e')
db 0b00000000 ; Row 0: 0x00
db 0b00011100 ; Row 1: 0x1C
db 0b00100010 ; Row 2: 0x22
db 0b00111110 ; Row 3: 0x3E
db 0b00111110 ; Row 4: 0x3E
db 0b00100000 ; Row 5: 0x20
db 0b00011110 ; Row 6: 0x1E
db 0b00000000 ; Row 7: 0x00
; Offset 0x230 (ASCII 102: Lowercase f 'f')
db 0b00011100 ; Row 0: 0x1C
db 0b00100010 ; Row 1: 0x22
db 0b00111110 ; Row 2: 0x3E
db 0b00100010 ; Row 3: 0x22
db 0b00100000 ; Row 4: 0x20
db 0b00100000 ; Row 5: 0x20
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
; Offset 0x238 (ASCII 103: Lowercase g 'g')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00011110 ; Row 2: 0x1E
db 0b00100110 ; Row 3: 0x26
db 0b00111110 ; Row 4: 0x3E
db 0b00000110 ; Row 5: 0x06
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0x240 (ASCII 104: Lowercase h 'h')
db 0b01100000 ; Row 0: 0x60
db 0b01100000 ; Row 1: 0x60
db 0b01111100 ; Row 2: 0x7C
db 0b01100110 ; Row 3: 0x66
db 0b01100110 ; Row 4: 0x66
db 0b01100110 ; Row 5: 0x66
db 0b01100110 ; Row 6: 0x66
db 0b00000000 ; Row 7: 0x00
; Offset 0x248 (ASCII 105: Lowercase i 'i')
db 0b00011000 ; Row 0: 0x18
db 0b00000000 ; Row 1: 0x00
db 0b00011000 ; Row 2: 0x18
db 0b00011000 ; Row 3: 0x18
db 0b00011000 ; Row 4: 0x18
db 0b00011000 ; Row 5: 0x18
db 0b00011110 ; Row 6: 0x1E
db 0b00000000 ; Row 7: 0x00
; Offset 0x250 (ASCII 106: Lowercase j 'j')
db 0b00000110 ; Row 0: 0x06
db 0b00000000 ; Row 1: 0x00
db 0b00000110 ; Row 2: 0x06
db 0b00000110 ; Row 3: 0x06
db 0b00000110 ; Row 4: 0x06
db 0b00000110 ; Row 5: 0x06
db 0b00000110 ; Row 6: 0x06
db 0b00111100 ; Row 7: 0x3C
; Offset 0x258 (ASCII 107: Lowercase k 'k')
db 0b01100000 ; Row 0: 0x60
db 0b01100000 ; Row 1: 0x60
db 0b01100110 ; Row 2: 0x66
db 0b01101100 ; Row 3: 0x6C
db 0b01111000 ; Row 4: 0x78
db 0b01101100 ; Row 5: 0x6C
db 0b01100110 ; Row 6: 0x66
db 0b00000000 ; Row 7: 0x00
; Offset 0x260 (ASCII 108: Lowercase l 'l')
db 0b00011110 ; Row 0: 0x1E
db 0b00000110 ; Row 1: 0x06
db 0b00000110 ; Row 2: 0x06
db 0b00000110 ; Row 3: 0x06
db 0b00000110 ; Row 4: 0x06
db 0b00000110 ; Row 5: 0x06
db 0b00000110 ; Row 6: 0x06
db 0b00000000 ; Row 7: 0x00
; Offset 0x268 (ASCII 109: Lowercase m 'm')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b01101010 ; Row 2: 0x6A
db 0b01110101 ; Row 3: 0x75
db 0b01110101 ; Row 4: 0x75
db 0b01101010 ; Row 5: 0x6A
db 0b01100010 ; Row 6: 0x62
db 0b00000000 ; Row 7: 0x00
; Offset 0x270 (ASCII 110: Lowercase n 'n')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b01111100 ; Row 2: 0x7C
db 0b01100110 ; Row 3: 0x66
db 0b01100110 ; Row 4: 0x66
db 0b01100110 ; Row 5: 0x66
db 0b01100110 ; Row 6: 0x66
db 0b00000000 ; Row 7: 0x00
; Offset 0x278 (ASCII 111: Lowercase o 'o')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00011100 ; Row 2: 0x1C
db 0b00100010 ; Row 3: 0x22
db 0b00100010 ; Row 4: 0x22
db 0b00011100 ; Row 5: 0x1C
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
; Offset 0x280 (ASCII 112: Lowercase p 'p')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b01111100 ; Row 2: 0x7C
db 0b01100110 ; Row 3: 0x66
db 0b01100110 ; Row 4: 0x66
db 0b01111100 ; Row 5: 0x7C
db 0b01100000 ; Row 6: 0x60
db 0b01100000 ; Row 7: 0x60
; Offset 0x288 (ASCII 113: Lowercase q 'q')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00011110 ; Row 2: 0x1E
db 0b00110110 ; Row 3: 0x36
db 0b00100110 ; Row 4: 0x26
db 0b00011110 ; Row 5: 0x1E
db 0b00000110 ; Row 6: 0x06
db 0b00000110 ; Row 7: 0x06
; Offset 0x290 (ASCII 114: Lowercase r 'r')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b01111100 ; Row 2: 0x7C
db 0b01100110 ; Row 3: 0x66
db 0b01100000 ; Row 4: 0x60
db 0b01100000 ; Row 5: 0x60
db 0b01100000 ; Row 6: 0x60
db 0b00000000 ; Row 7: 0x00
; Offset 0x298 (ASCII 115: Lowercase s 's')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00011100 ; Row 2: 0x1C
db 0b00100010 ; Row 3: 0x22
db 0b00011100 ; Row 4: 0x1C
db 0b00000110 ; Row 5: 0x06
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0x2A0 (ASCII 116: Lowercase t 't')
db 0b00000000 ; Row 0: 0x00
db 0b00001000 ; Row 1: 0x08
db 0b00011100 ; Row 2: 0x1C
db 0b00001000 ; Row 3: 0x08
db 0b00001000 ; Row 4: 0x08
db 0b00001000 ; Row 5: 0x08
db 0b00000110 ; Row 6: 0x06
db 0b00000000 ; Row 7: 0x00
; Offset 0x2A8 (ASCII 117: Lowercase u 'u')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b01100110 ; Row 2: 0x66
db 0b01100110 ; Row 3: 0x66
db 0b01100110 ; Row 4: 0x66
db 0b00111110 ; Row 5: 0x3E
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
; Offset 0x2B0 (ASCII 118: Lowercase v 'v')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b01100110 ; Row 2: 0x66
db 0b01100110 ; Row 3: 0x66
db 0b00111100 ; Row 4: 0x3C
db 0b00111100 ; Row 5: 0x3C
db 0b00011000 ; Row 6: 0x18
db 0b00000000 ; Row 7: 0x00
; Offset 0x2B8 (ASCII 119: Lowercase w 'w')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b01100011 ; Row 2: 0x63
db 0b01101011 ; Row 3: 0x6B
db 0b01101011 ; Row 4: 0x6B
db 0b00111110 ; Row 5: 0x3E
db 0b00011000 ; Row 6: 0x18
db 0b00000000 ; Row 7: 0x00
; Offset 0x2C0 (ASCII 120: Lowercase x 'x')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b01100110 ; Row 2: 0x66
db 0b00111100 ; Row 3: 0x3C
db 0b00011000 ; Row 4: 0x18
db 0b00111100 ; Row 5: 0x3C
db 0b01100110 ; Row 6: 0x66
db 0b00000000 ; Row 7: 0x00
; Offset 0x2C8 (ASCII 121: Lowercase y 'y')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b01100110 ; Row 2: 0x66
db 0b01100110 ; Row 3: 0x66
db 0b00111110 ; Row 4: 0x3E
db 0b00000110 ; Row 5: 0x06
db 0b00111100 ; Row 6: 0x3C
db 0b00000000 ; Row 7: 0x00
; Offset 0x2D0 (ASCII 122: Lowercase z 'z')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b01111110 ; Row 2: 0x7E
db 0b00000110 ; Row 3: 0x06
db 0b00001100 ; Row 4: 0x0C
db 0b00011000 ; Row 5: 0x18
db 0b01111110 ; Row 6: 0x7E
db 0b00000000 ; Row 7: 0x00
; Offset 0x2D8 (ASCII 123: Left Curly Brace '{')
db 0b00011100 ; Row 0: 0x1C
db 0b00011000 ; Row 1: 0x18
db 0b00011000 ; Row 2: 0x18
db 0b00110000 ; Row 3: 0x30
db 0b00011000 ; Row 4: 0x18
db 0b00011000 ; Row 5: 0x18
db 0b00011100 ; Row 6: 0x1C
db 0b00000000 ; Row 7: 0x00
; Offset 0x2E0 (ASCII 124: Vertical Bar '|')
db 0b00011000 ; Row 0: 0x18
db 0b00011000 ; Row 1: 0x18
db 0b00011000 ; Row 2: 0x18
db 0b00011000 ; Row 3: 0x18
db 0b00011000 ; Row 4: 0x18
db 0b00011000 ; Row 5: 0x18
db 0b00011000 ; Row 6: 0x18
db 0b00000000 ; Row 7: 0x00
; Offset 0x2E8 (ASCII 125: Right Curly Brace '}')
db 0b01110000 ; Row 0: 0x70
db 0b00011000 ; Row 1: 0x18
db 0b00011000 ; Row 2: 0x18
db 0b00001100 ; Row 3: 0x0C
db 0b00011000 ; Row 4: 0x18
db 0b00011000 ; Row 5: 0x18
db 0b01110000 ; Row 6: 0x70
db 0b00000000 ; Row 7: 0x00
; Offset 0x2F0 (ASCII 126: Tilde '~')
db 0b00000000 ; Row 0: 0x00
db 0b00000000 ; Row 1: 0x00
db 0b00110010 ; Row 2: 0x32
db 0b01001100 ; Row 3: 0x4C
db 0b00000000 ; Row 4: 0x00
db 0b00000000 ; Row 5: 0x00
db 0b00000000 ; Row 6: 0x00
db 0b00000000 ; Row 7: 0x00
Now, we have our ASCII characters bitmap table from ASCII (32 to 126).
Now we need a way to get the offset in the table for able to read the particular character bitmap.
To get the offset in the table to read the particular character bitmap, you can calculate the offset based on the ASCII value of the character. Here's a simple formula to calculate the offset:
offset = (ASCII_value - 32) * bitmap_size
Where:
ASCII_value
is the ASCII value of the character.bitmap_size
is the size of each bitmap in bytes.32
is the starting ASCII character bitmap.
For example, if the ASCII value of the character is 65 ('A') and each bitmap is 8 bytes in size, the offset would be:
offset = (65 - 32) * 8
= 33 * 8
= 264
Note: Each bitmap is 8 bytes (one byte per row) that's why we multiplied by 8
This means the bitmap for the character 'A' would be located at offset 264 in your bitmap table. You can then use this offset to access the bitmap in your table.
Calculating the Offset in Code:
; Suppose ax carries the character to print
; let ax = A, which is ASCII 65
sub ax, 32 ; ASCII_value - 32 (first printable character)
mov bx, 8 ; Each character bitmap is 8 bytes
mul bx ; AX = (ASCII_value - 32) * 8
mov bx, ax ; BX = offset in bitmap table
;; We can do the multiplication by 8 with shifting
; Since each character bitmap is 8 bytes, we can multiply by 8 using left shift by 3 (equivalent to multiplying by 2^3)
; Here is the code
sub ax, 32 ; ASCII_value - 32 (first printable character)
shl ax, 3 ; Multiply by 8 using left shift
mov bx, ax ; BX = offset in bitmap table
; Now bx contains the offset of the charactet in bitmap table
; Just add the bx to bitmap_table starting address to reach to character offset.
mov esi, font_table
add esi, ebx
Here is the complete code:
; Draw the character on the screen
; eax = charaacter to print
; ebx = x position
; ecx = y position
draw_Char:
pusha ; Save all general-purpose registers
; Finding the offset of character in font table
sub eax, 32 ; ASCII_value - 32 (First Printable character)
shl eax, 3 ; Multiply by 8 using left shift (each character is 8 bytes long)
; pointing SI to the bitmap of character in the font_table by adding offset
mov esi, font_table ; SI point to the starting address of font_table
add esi, eax ; Add the offset, SI now points to the character's bitmap in the font_table
mov eax, ecx ; set Y position to ax
mov edx, MODE_13_SCREEN_WIDTH ; Screen width in mode 13h (320 pixels)
mul edx ; AX = Y * 320
add eax, ebx ; AX += X position
mov edi, eax ; DI = Screen position (Y * 320 + X)
add edi, FRAMEBUFFER_ADDRESS_MODE_13
mov cx, 8 ; 8 rows per character
next_row32:
lodsb ; Load next byte from font_A to AL
mov ah, al ; Copy to AH
mov al, 0 ; Clear AL
mov edx, 8 ; 8 bits per row
next_pixel32:
shl ah, 1 ; Shift AH right
jnc skip_pixel32 ; If carry (bit 0) is 0, skip
mov byte [edi], 0x0F ; Set pixel color to white (0x0F)
skip_pixel32:
inc edi ; Move to next pixel on screen
dec edx ; Move to next bit
jnz next_pixel32 ; Repeat for 8 bits (pixels)
add edi, MODE_13_SCREEN_WIDTH - 8 ; Move to the start of the next row
loop next_row32 ; Repeat for 8 rows
popa ; Restore all general-purpose registers
ret
3 Drawing String
Since we can draw a single character onto the screen. Now, we can use that function to draw the string of characters.
We will do it as follows:
print_string
Function:- Loads each character form the string.
- Checks for the end of the string (null terminator).
- Calls
draw_Char
for each character. - Moves the x position to the right by 8 pixels for the next character.
- draw_Char Function:
- Calculates the offset for the character in the font table.
- Points
esi
to the character's bitmap. - Calculates the screen position.
- Draws the character on the screen by setting pixels based on the bitmap.
section .data
font_table:
; Space (ASCII 32)
db 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000
; 'A' (ASCII 65)
db 0b00011000, 0b00100100, 0b00100100, 0b00111100, 0b00100100, 0b00100100, 0b00100100, 0b00000000
; 'B' (ASCII 66)
db 0b00111000, 0b00100100, 0b00100100, 0b00111000, 0b00100100, 0b00100100, 0b00111000, 0b00000000
; ... additional characters as needed
MODE_13_SCREEN_WIDTH equ 320
FRAMEBUFFER_ADDRESS_MODE_13 equ 0xA0000
section .bss
section .text
extern _start
_start:
; Set up GDT and enter protected mode as before
; Call print_string with the string to print, x position, y position, and color
mov esi, string_to_print ; ESI points to the string
mov ebx, 50 ; X position
mov ecx, 50 ; Y position
mov edx, 15 ; Color
call print_string
; Infinite loop
cli
hlt
string_to_print db 'AB', 0 ; String to print, null-terminated
print_string:
pusha ; Save all general-purpose registers
.next_char:
lodsb ; Load the next byte (character) from string into AL
test al, al ; Check if AL is zero (end of string)
jz .done ; If zero, we're done
; Draw the character at the current position
mov eax, al ; Character to print
push ebx ; Save X position
push ecx ; Save Y position
call draw_Char
pop ecx ; Restore Y position
pop ebx ; Restore X position
; Advance to the next character position
add ebx, 8 ; Move to the next character position (8 pixels to the right)
jmp .next_char ; Repeat for the next character
.done:
popa ; Restore all general-purpose registers
ret
; Draw the character on the screen
; eax = character to print
; ebx = x position
; ecx = y position
draw_Char:
pusha ; Save all general-purpose registers
; Finding the offset of character in font table
sub eax, 32 ; ASCII_value - 32 (First Printable character)
shl eax, 3 ; Multiply by 8 using left shift (each character is 8 bytes long)
; pointing SI to the bitmap of character in the font_table by adding offset
mov esi, font_table ; SI points to the starting address of font_table
add esi, eax ; Add the offset, SI now points to the character's bitmap in the font_table
mov eax, ecx ; set Y position to ax
mov edx, MODE_13_SCREEN_WIDTH ; Screen width in mode 13h (320 pixels)
mul edx ; AX = Y * 320
add eax, ebx ; AX += X position
mov edi, eax ; DI = Screen position (Y * 320 + X)
add edi, FRAMEBUFFER_ADDRESS_MODE_13
mov cx, 8 ; 8 rows per character
next_row32:
lodsb ; Load next byte from font_table to AL
mov ah, al ; Copy to AH
mov al, 0 ; Clear AL
mov edx, 8 ; 8 bits per row
next_pixel32:
shl ah, 1 ; Shift AH left
jnc skip_pixel32 ; If carry (bit 0) is 0, skip
mov byte [edi], 0x0F ; Set pixel color to white (0x0F)
skip_pixel32:
inc edi ; Move to next pixel on screen
dec edx ; Move to next bit
jnz next_pixel32 ; Repeat for 8 bits (pixels)
add edi, MODE_13_SCREEN_WIDTH - 8 ; Move to the start of the next row
loop next_row32 ; Repeat for 8 rows
popa ; Restore all general-purpose registers
ret
4 Drawing Line
%define FRAMEBUFFER_ADDRESS 0xA0000
%define SCREEN_WIDTH 320
%define SCREEN_HEIGHT 200
section .data
font_table: db 0 ; Placeholder for font table
section .text
extern _start
; set_pixel:
; Inputs:
; eax = x position
; ebx = y position
; dl = color
set_pixel:
push ebp
mov ebp, esp
; Calculate the framebuffer offset
mov ecx, SCREEN_WIDTH
mul ecx ; EAX = y * SCREEN_WIDTH
add eax, ebx ; EAX += x
add eax, FRAMEBUFFER_ADDRESS ; Add base address of framebuffer
mov [eax], dl ; Set the pixel color
pop ebp
ret
; draw_line:
; Inputs:
; eax = x1
; ebx = y1
; ecx = x2
; edx = y2
; [esp + 4] = color
draw_line:
pusha ; Save all general-purpose registers
push ebp
mov ebp, esp
sub esp, 4 ; Reserve space for local variables
; Load the color from the stack
mov dl, [ebp + 12]
; Calculate dx and dy
mov esi, ecx ; esi = x2
sub esi, eax ; esi = dx = x2 - x1
mov edi, edx ; edi = y2
sub edi, ebx ; edi = dy = y2 - y1
; Determine the direction of the line
mov ebp, esi
sar ebp, 31 ; Sign extend dx
mov ecx, edi
sar ecx, 31 ; Sign extend dy
; Absolute values of dx and dy
xor esi, ebp
sub esi, ebp
xor edi, ecx
sub edi, ecx
; Initialize decision parameter
cmp esi, edi
jge .not_steep ; If |dx| >= |dy|, the line is not steep
; Steep line
xchg eax, ebx ; Swap x1 and y1
xchg ecx, edx ; Swap x2 and y2
xchg esi, edi ; Swap dx and dy
.not_steep:
mov ebp, eax ; ebp = x
mov ecx, ebx ; ecx = y
mov edi, esi ; edi = dx
shr edi, 1 ; edi = dx / 2
.loop:
; Draw the pixel
push ecx ; Save y
push ebp ; Save x
call set_pixel
pop ebp ; Restore x
pop ecx ; Restore y
; Update the decision parameter
sub edi, edi ; edi -= dy
jge .no_step_y
add ecx, 1 ; y += 1
add edi, esi ; p += dx
.no_step_y:
add ebp, 1 ; x += 1
; Loop until we reach the end point
cmp ebp, eax ; Compare x1 with x2
jle .loop
.end:
add esp, 4 ; Deallocate local variable space
pop ebp
popa ; Restore all general-purpose registers
ret
section .bss