For more projects, click here
A 16-bits x86 DOS Assembly library that provides many useful functions for developing programs. It has both VGA grapics functions as well as general purpose utilities. The main purpose of this library was to be able to implement simple DOS games (in Assembly) using VGA (320x200, 256 colors) display.
Note The library was developed and tested using TASM 4.1 and DosBox
The library provides the following graphics capabilities:
- General purpose graphics utilities
- Manipulate color palettes
- Draw lines
- Draw rectangles (outline and filled)
- Draw closed polygons (outline only)
- Draw circles (outline and filled)
- Draw Bitmaps
- Images & screen manipulations
- Draw sprites and animation
- Support for Double buffering
The library contains an utility package with many useful macros and procedures such as:
- File management
- Keyboard management, including ISR support
- Mouse input
- Several Math and randomizer functions
- Memory management (allocation and deallocation on the heap)
- String operations
- Printing to the screen
- Support for playing sound
- Time functions for delays
To use the libraries, include the files in your code as follows:
CODESEG
include "UtilLib.inc"
include "GrLib.inc" Note that UtilLib.inc MUST come before GrLib.inc
Here is a sample program that draws a line and waits for a key press, using double buffering:
.486
IDEAL
MODEL small
STACK 256
DATASEG
CODESEG
include "UtilLib.inc"
include "GrLib.inc"
start:
mov ax, @data
mov ds,ax
; Init library with double buffering flag on
mov ax, TRUE
ut_init_lib ax
; Allocate double buffering memory space
call AllocateDblBuffer
; set display to VGA mode
gr_set_video_mode_vga
; Set initial pen color
gr_set_color GR_COLOR_GREEN
; your code here - we will draw a line...
grm_DrawLine 10, 10, 200, 200
exit:
call WaitForKeypress
call ReleaseDblBuffer
gr_set_video_mode_txt
return 0
END start Build the program using
tasm /zi main.asm
tlink /v mainThe library provides many Procedures and Macros that you can use directly in your code. To make life easier (and have the code look more like a high level language), a wrapper MACRO was provided to many of the PROCs. Both options yield the same outcome.
For example, you can draw a line using the PROC
push x1
push y1
push x2
push y2
call GR_DrawLineor using a wrapper MACRO
grm_DrawLine x1, y1, x2, y2Make sure you pass the arguments in the right order. All procedures preserve register values, unless stated otherwise. Macros (not including wrapper macros), on the other hand, may alter register values. Check out their documentation for details.
At the beginning of your code, you need to initialize the library by calling:
CODESEG
; Init library
mov ax, FALSE
ut_init_lib axThe flag shoule be set to TRUE if you intend to dynamically allocate memory in your program. See memory management
Note that TRUE and FALSE are defined by the library as 1 and 0.
The Tests folder includes a testing file that demonstrates the use of various parts of the library. the test program itself can be found at the root and is called main.asm
A simple TicTac game that uses many of the library's features, was added to the library. You can compile and run the game using:
tasm /zi tictac.asm
tlink /v tictacThe game itself can be found under Tests/tic folder and the main file is tictac.asm
The most basic macros handle drawing a single pixel on the screen, using direct memory access (not interrupt).
gr_set_pixel x, y, color - draws a pixel in the given (x,y) coordinates with the given color
gr_set_pixel_xy x,y - draws a pixel in the given (x,y) coordinates with the set colorNote that x, y and color cannot use the following registers: ax, bx, di, dx
This macro takes into account double buffering and will draw to the buffer (if set) instead of the video display. See Double Buffering for details.
It is highly recommended to use this macro whenever you want to draw to the VGA screen.
There are 3 ways to clear the screen, or a portion of the screen
clear_screen_vga - macro that very efficiently clears the entire screen (VGA mode)
GR_ClearRect - procedure that clears a rectangle on the screen (VGA mode)
clear_screen_txt - macro that very efficiently clears the entire screen (TXT mode)Drawing a line using a PROC:
push 50 ; x1
push 60 ; y1
push 120 ; x2
push 180 ; y2
call GR_DrawLineor using a MACRO:
grm_DrawLine 50, 60, 120, 180You can either draw a rectangle outline or a filled one.
Outline using a PROC:
push 50 ; x (top)
push 60 ; y (top)
push 120 ; width
push 180 ; height
call GR_DrawRector using a MACRO:
grm_DrawRect 50, 60, 120, 180Filled using a PROC:
push 50 ; x (top)
push 60 ; y (top)
push 120 ; width
push 180 ; height
call GR_FillRector using a MACRO:
grm_FillRect 50, 60, 120, 180You can either draw a circle outline or a filled one.
Outline using a PROC:
push 150 ; X center
push 100 ; Y center
push 40 ; Radius
call GR_DrawCircleor using a MACRO:
grm_DrawCircle 150, 100, 40Filled using a PROC:
push 150 ; X center
push 100 ; Y center
push 40 ; Radius
call GR_FillCircleor using a MACRO:
grm_FillCircle 150, 100, 40To draw a polygon, you need to define an array and pass its pointer along with the number of points in the array. The array should contain points (x and y) as follows:
DATASEG
_poly dw 5,30,100,50,200,100This is an array with the following points:
- _poly[0] = (5,30)
- _poly[1] = (100,50)
- _poly[2] = (200,100)
Using a PROC:
push 3 ; there are 3 points
push offset _poly ; pointer to _poly array
call GR_DrawPolygonor using a MACRO:
mov ax, offset _poly
grm_DrawPolygon 3, axThe library support only 8-bit bitmaps in v3. To load a bitmap, you need to define the following variable:
_bmp_file db "asset\\b.bmp",0
_bmp db BMP_STRUCT_SIZE dup(0)This will allocate the memory for the BMP headers (struct). Memory allocation for the pixels' data will be allocated in RAM when loading the data.
You must use "call FreeProgramMem" before trying to load images or you will get an out of memory situation and the allocation will fail. Note that you can implicitly call it by passing TRUE to ut_init_lib:
mov ax, TRUE
ut_init_lib TRUETo load a BMP image, call:
mov dx, offset _bmp_file
mov cx, offset _bmp
push dx ; path offset
push ds ; path segment
push cx ; bitmap struct offset
push ds ; bitmap struct segment
call LoadBMPImageor using a MACRO
mov dx, offset _bmp_file
mov cx, offset _bmp
grm_LoadBMPImage dx, ds, cx, dsAfter loading the bitmap, you can draw it on the screen
mov cx, offset _bmp
push cx ; bitmap struct offset
push ds ; bitmap struct segment
push 000Ah ; xTop = 10
push 000Bh ; yTop = 11
call DisplayBMP
; instead, you can tile the image on the screen using
push cx
push ds
call TileBmpor using MACROS:
mov cx, offset _bmp
grm_DisplayBMP cx, ds, 000Ah, 000Bh
; or
grm_TileBmp cx, dsYou must free the bitmap memory (when it is not needed anymore) by calling
mov cx, offset _bmp
push cx ; bitmap struct offset
push ds ; bitmap struct segment
call FreeBmpor using MACROS:
grm_FreeBmp cx, dsThe library provides methods for extracting color palettes of BMP files and storing them in a new file (binary) as well as load a palette file into a buffer (possibly the palette of a BMP structure).
Here is an example of saving a palette to a file:
mov dx, offset _bmp_file
mov ax, offset _bmp
grm_LoadBMPImage dx, [_dss], ax, [_dss]
mov ax, offset _bmp
mov bx, offset _paletteFile
grm_SavePalette ax, [_dss], bxand loading it to a buffer:
push offset _paletteFile
push ds
push offset _palette
push ds
call LoadPaletteThe library supports sprites, which is an image with multiple frames. Sprites are a great way to create animation or to hold multiple variants of an image. See an example.
Since sprites are standard BMP files, you load them normally and then can display any frame you select.
DATASEG
_sprite_w equ 30
_sprite_frames equ 6
_sprite_file db "asset\\sprite1.bmp",0
_sprite db BMP_STRUCT_SIZE dup(0)
CODESEG
mov dx, offset _sprite_file
mov ax, offset _sprite
push dx ; path address
push ds ; path segment
push ax ; struct address
push ds ; struct segment
call LoadBMPImage
push 0 ; frame index
push ax ; BMP struct address
push ds ; BMP struct segment
push 0064h ; x coordinate
push 0064h ; y coordinate
push _sprite_w ; width of a single frame
push _sprite_frames ; number of frames in BMP
call PlaySpriteInPlace or using macros:
CODESEG
mov dx, offset _sprite_file
mov ax, offset _sprite
grm_LoadBMPImage dx, ds, ax, ds
grm_PlaySpriteInPlace 0, ax, ds, 0064h, 0064h, _sprite_w, _sprite_framesTake a look at TestMySprite function as an example for playing animation using sprites.
Here is an example of a sprite image
The library provide functions for copying a portion of the screen to a buffer and visa versa (from a buffer to the screen).
grm_SaveScreen memAddress, memSeg, x, y, w, hgrm_WriteScreen memAddress, memSeg, x, y, w, hThe library includes other utilities that help developing assembly programs.
store_sp_bp - macro that stores and sets BP value. Called at the beginning of PROC
restore_sp_bp - macro that restores BP, SP values. Called at the end of PROC
return - macro for returning control to DOS with a code
cmpv - macro that compare two memory variables
movv - moves a WORD from one memory to another via the stackThe library stores the original data segment (DS register) using a global variable _dss that you can access at any time if you need to restore its value.
push [_dss]
pop dsThe library provides 3 different ways to delay your program:
If you want your program to halt for a given number of seconds call the Sleep procedure
push 3 ; sleeps for 3 seconds
call Sleepor using MACROS:
utm_Sleep 3Delay program execution by X number of milliseconds. Actually, the argument is the number of 1/18 of a second
push 3 ; Delay for 3 * 1/18 of a sec
call DelayOr:
utm_Delay 3This function delays execution for given number of microseconds which are specified in 2 variables, one for the high order and one for the low order. For example, a 1 second delay (1 * 1,000,000 msec) is 000F 4240 and therfore the values will be
push 000Fh
push 4240h
call DelayMSOr
utm_DelayMS 000Fh, 4240hAnother example, a 2 seconds delay is equal to (21,000,000) 001E 8480 and a 1 millisecond is (11000) 0000 03EB
Most keyboard functions are accessible via interrupts and the library focuses on some less trivial parts of handling the keyboard. A list of keyboard scan codes can be found at keymap.asm
Setting keyboard yypematic rate to defalt (repeat delay and rate)
call SetKeyboardRateDefaultHold program execution until a key is pressed
call WaitForKeypressGetting keyboard status
call GetKeyboardStatusReturns: ZF = 0 if a _Key pressed (even Ctrl-Break) AX = 0 if no scan code is available AH = scan code AL = ASCII character or zero if special function _Key
Taking the pressed key out of the keyboard buffer
call ConsumeKeycall GetKeyboardFlagsReturns AL:
|7|6|5|4|3|2|1|0| AL or BIOS Data Area 40:17
| | | | | | | ---- right shift key depressed | | | | | | ----- left shift key depressed
| | | | | ------ CTRL key depressed | | | | ------- ALT key depressed
| | | -------- scroll-lock is active | | --------- num-lock is active
| ---------- caps-lock is active ----------- insert is active
You can replace the default keyboard interrupt with your own by calling this procedure
push isr_address
call InstallKeyboardInterruptFor example:
lea dx,[my_interr]
push dx
call InstallKeyboardInterruptYou must restore the interrupt at the end of the program by calling
call RestoreKeyboardInterruptThe library contains 2 sample implementations of a keyboard ISR.
- KeyboardSampleISR - implements a FIFO buffer and allows retrieving keys using getcISR. Note that this is not a complete implementation but merely an example. For instance, it does not convert the scancode to ASCII and does not treat SHIFT, CTRL and ALT combinations.
- KeyboardISREvents - a simple implementation that illustrates how to call the default keyboard handler
See TestKeyboardISR and TestSimpleISR
Most mouse functions are accessible via interrupts and the library focuses on some less trivial parts of handling the mouse.
Use these macros to show / hide the mouse
ShowMouse
HideMouseMouse position and button status can be retrieved by
GetMouseStatusOn return: CX = horizontal (X) position (0..639) DX = vertical (Y) position (0..199) BX = button status:
|F-8|7|6|5|4|3|2|1|0| Button Status
| | | | | | | | `---- left button (1 = pressed)
| | | | | | | `----- right button (1 = pressed)
`------------------- unused
After getting mouse coordinates from GetMouseStatus, you can translate them to VGA coordinates by calling
TranslateMouseCoordsmov cx, 3 ; horizontal
mov dx, 5 ; vertical
SetMousePositionYou can replace the default mouse interrupt with your own by calling this procedure
push ISR address
push ISR segment
push mask
call InstallMouseInterruptYou must restore the interrupt at the end of the program by calling
call UninstallMouseInterruptThe library provide a set of functions to access and manipulate files. The library was designed to handle a single file at any time using the global variables
_fHandle - stores the handle of the openned file
_fErr - stores the error valuepush address_of_file_name
push segment_of_file_name
call fopenor using MACROS:
utm_fopen address_of_file_name, segment_of_file_namepush address_of_file_name
push segment_of_file_name
call fnewor using MACROS:
utm_fnew address_of_file_name, segment_of_file_namecall fcloseor using MACROS:
utm_fclosepush address_of_file_name
push segment_of_file_name
call fsizeor using MACROS:
utm_fsize address_of_file_name, segment_of_file_namepush length
push address_of_buffer
push segment_of_buffer
call freador using MACROS:
utm_fread address_of_buffer, segment_of_bufferpush length
push address_of_buffer
push segment_of_buffer
call fwriteor using MACROS:
utm_fwrite address_of_buffer, segment_of_bufferpush address_of_file_name
push segment_of_file_name
call fdeleteor using MACROS:
utm_fdelete address_of_file_name, segment_of_file_namepush attribute
push address_of_file_name
push segment_of_file_name
call fchangeAttror using MACROS:
utm_fchangeAttr attribute, address_of_file_name, segment_of_file_nameYou can do a fseek in a file very similar to the C function
grm_fseek SEEK_SET, 0, 40Or:
push SEEK_SET
push 0
push 40
call fseekThe first parameter can be SEEK_CUR, SEEK_SET or SEEK_END. The second parameter is the high order of the offset and the third is the low order.
The library provides a few directory services, including:
mkdir OR utm_mkdir - create a directory
rmdir OR utm_rmdir - delete a directory
chdir OR utm_chdir - change a directoryThe library provide some basic math related functions and macros
Before generating a random number, you need to initialize the number generator by calling
call RandomSeedand then you can use
call RandomWord
or
call RandomByte to get a random number in AX or AL
You can get the absoilute value of a number
gr_absolute numberThe library allows managing (allocating, releasing) RAM memory. If you need to allocate dynamic memory, you must free unused memory at the beginning of your program by calling:
call FreeProgramMemIf you forget to call it, all memory allocations will fail on "out of memory".
Note that you can implicitly call it by passing TRUE to ut_init_lib:
mov ax, TRUE
ut_init_lib TRUEAllocating a memory block is done by calling:
push size
call mallocNote that the size is measured in Paragraphs and therefore need to be divided by 16 (bytes)
Return value: AX = segment address of allocated memory block (MCB + 1para) 0 on error BX = size in paras of the largest block of memory available 0 on error CF = 0 if successful = 1 if error Allocated memory is at AX:0000
Pass the segment address of the allocated memory block to release it
push segment
call mfreeThe library maintains an internal list of up to 50 allocated blocks that can be freed by calling
call mfreeAllCopies memory from one address to another
push from_address
push from_seg
push to_address
push to_seg
push length_in_bytes
call MemCpyYou can set a byte or word value to an entire memory block
mov ax, offset _blcok
push 10 ; length_in_bytes
push ds ; block_segment
push ax ; block_offset
push 0 ; value
call SetMemByteand
mov ax, offset _blcok
push 5 ; length_in_words
push ds ; block_segment
push ax ; block_offset
push 0 ; value
call SetMemWordYou can make a beep with the following procedure. Note that the sound will continue until you stop it
push frequency
call Beep
utm_Sleep 2 ; wait 2 seconds
call StopBeepOr with macros:
utm_Beep freq
utm_Sleep 2
utm_StopBeepThis part of the library supports writting text to the screen
call PrintDecimal - prints a value as a decimal number
call PrintChar - prints DL as a char
PrintCharNewLine - macro to print a char with new line
call PrintByte - prints DL (number between 0 and 15)
PrintByteNewLine - macro to print a byte with a new line
call PrintStr - prints a string. DS:DX pointer to string ending in "$"
PrintStrNewLine - macro to print a string with a new line
call PrintNewLine - prints a new line
call PrintSpace - prints a space char
call PrintHexByte - prints the LSB BYTE in HEX
call PrintHexByte - prints the WORD in HEXcall PrintCharVGA - prints a character on VGA display (DL: char, BL: color)
call PrintStrVGA - prints a string to the VGA screen push x
push y
call SetCursorPositionThe library contains several basic string manipulations functions that accept null terminating string
Strlen - Calculates length of string ending with NULL
StrlenDollar - Calculates length of string ending with '$'
Strcpy - Copies string s2 into string s1
Strncpy - Copies given number of chars from string s2 into string s1.
Strcat - concatenate 2 strings
Strchr - Searches for the first occurrence of the character in the given string
Strcmp - Compares the string pointed to, by str1 to the string pointed to by str2.This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- Some ideas were taken from https://github.com/itay-grudev/assembly-dos-gx
- README file created using Dillinger