commit e5415c977ccbbfcb62219af0c744fab319ed8eb4 Author: gered Date: Sun Nov 26 13:18:33 2017 -0500 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0f996b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +*.exe +*.a +*.o +/dgl_test.gdt +/dgl_test.gpr diff --git a/BLIT.C b/BLIT.C new file mode 100644 index 0000000..34599a1 --- /dev/null +++ b/BLIT.C @@ -0,0 +1,204 @@ +#include "blit.h" +#include "clipping.h" +#include "internal.h" + +static inline boolean clip_blit(const RECT *dest_clip_region, + RECT *src_blit_region, + int *dest_x, + int *dest_y) { + int dest_clip_right = rect_right(dest_clip_region); + int dest_clip_bottom = rect_bottom(dest_clip_region); + int offset; + + // off the left edge? + if (*dest_x < dest_clip_region->x) { + // completely off the left edge? + if ((*dest_x + src_blit_region->width) < dest_clip_region->x) + return FALSE; + + offset = src_blit_region->x - *dest_x; + src_blit_region->x += offset; + src_blit_region->width -= offset; + *dest_x = dest_clip_region->x; + } + + // off the right edge? + if (*dest_x > (dest_clip_region->width - src_blit_region->width)) { + // completely off the right edge? + if (*dest_x > dest_clip_right) + return FALSE; + + offset = *dest_x + src_blit_region->width - dest_clip_region->width; + src_blit_region->width -= offset; + } + + // off the top edge? + if (*dest_y < dest_clip_region->y) { + // completely off the top edge? + if ((*dest_y + src_blit_region->height) < dest_clip_region->y) + return FALSE; + + offset = dest_clip_region->y - *dest_y; + src_blit_region->y += offset; + src_blit_region->height -= offset; + *dest_y = dest_clip_region->y; + } + + // off the bottom edge? + if (*dest_y > (dest_clip_region->height - src_blit_region->height)) { + // completely off the bottom edge? + if (*dest_y > dest_clip_bottom) + return FALSE; + + offset = *dest_y + src_blit_region->height - dest_clip_region->height; + src_blit_region->height -= offset; + } + + return TRUE; +} + +void surface_blit_region(const SURFACE *src, + SURFACE *dest, + int src_x, + int src_y, + int src_width, + int src_height, + int dest_x, + int dest_y) { + RECT src_region = rect(src_x, src_y, src_width, src_height); + boolean on_screen = clip_blit(&dest->clip_region, &src_region, &dest_x, &dest_y); + + if (!on_screen) + return; + + surface_blit_region_f(src, dest, + src_region.x, src_region.y, + src_region.width, src_region.height, + dest_x, dest_y); +} + +void surface_blit_region_f(const SURFACE *src, + SURFACE *dest, + int src_x, + int src_y, + int src_width, + int src_height, + int dest_x, + int dest_y) { + byte *psrc, *pdest; + int src_y_inc, dest_y_inc; + int width; + int lines_left; + int num_dwords; + + psrc = surface_pointer(src, src_x, src_y); + src_y_inc = src->width; + pdest = surface_pointer(dest, dest_x, dest_y); + dest_y_inc = dest->width; + width = src_width; + lines_left = src_height; + + if (width % 4 == 0) { + num_dwords = width / 4; + while (lines_left) { + REP_MOVSL(psrc, pdest, num_dwords); + psrc += src_y_inc; + pdest += dest_y_inc; + --lines_left; + } + } else { + while (lines_left) { + REP_MOVSB(psrc, pdest, width); + psrc += src_y_inc; + pdest += dest_y_inc; + --lines_left; + } + } +} + +void surface_blit_sprite_region(const SURFACE *src, + SURFACE *dest, + int src_x, + int src_y, + int src_width, + int src_height, + int dest_x, + int dest_y) { + RECT src_region = rect(src_x, src_y, src_width, src_height); + boolean on_screen = clip_blit(&dest->clip_region, &src_region, &dest_x, &dest_y); + + if (!on_screen) + return; + + surface_blit_sprite_region_f(src, dest, + src_region.x, src_region.y, + src_region.width, src_region.height, + dest_x, dest_y); +} + +void surface_blit_sprite_region_f(const SURFACE *src, + SURFACE *dest, + int src_x, + int src_y, + int src_width, + int src_height, + int dest_x, + int dest_y) { + byte *psrc, *pdest; + byte pixel; + int src_y_inc, dest_y_inc; + int width; + int lines_left; + int x; + + psrc = surface_pointer(src, src_x, src_y); + src_y_inc = src->width; + pdest = surface_pointer(dest, dest_x, dest_y); + dest_y_inc = dest->width; + width = src_width; + lines_left = src_height; + + // based on benchmarking on a DX2-66, there is VERY significant + // diminishing returns for loop unrolling beyond these sizes + // (in fact, even the one for 8 is a very small gain over 4) + if (width % 8 == 0) { + while (lines_left) { + for (x = 0; x < width; x += 8) { + if ((pixel = psrc[x + 0])) pdest[x + 0] = pixel; + if ((pixel = psrc[x + 1])) pdest[x + 1] = pixel; + if ((pixel = psrc[x + 2])) pdest[x + 2] = pixel; + if ((pixel = psrc[x + 3])) pdest[x + 3] = pixel; + if ((pixel = psrc[x + 4])) pdest[x + 4] = pixel; + if ((pixel = psrc[x + 5])) pdest[x + 5] = pixel; + if ((pixel = psrc[x + 6])) pdest[x + 6] = pixel; + if ((pixel = psrc[x + 7])) pdest[x + 7] = pixel; + } + psrc += src_y_inc; + pdest += dest_y_inc; + --lines_left; + } + } else if (width % 4 == 0) { + while (lines_left) { + for (x = 0; x < width; x += 4) { + if ((pixel = psrc[x + 0])) pdest[x + 0] = pixel; + if ((pixel = psrc[x + 1])) pdest[x + 1] = pixel; + if ((pixel = psrc[x + 2])) pdest[x + 2] = pixel; + if ((pixel = psrc[x + 3])) pdest[x + 3] = pixel; + } + psrc += src_y_inc; + pdest += dest_y_inc; + --lines_left; + } + } else { + while (lines_left) { + for (x = 0; x < width; ++x) { + if ((pixel = psrc[x])) + pdest[x] = pixel; + } + psrc += src_y_inc; + pdest += dest_y_inc; + --lines_left; + } + } +} + diff --git a/BLIT.H b/BLIT.H new file mode 100644 index 0000000..51cab6e --- /dev/null +++ b/BLIT.H @@ -0,0 +1,67 @@ +#ifndef DGL_BLIT_H_INCLUDED +#define DGL_BLIT_H_INCLUDED + +#include "gfx.h" + +void surface_blit_region(const SURFACE *src, + SURFACE *dest, + int src_x, + int src_y, + int src_width, + int src_height, + int dest_x, + int dest_y); + +void surface_blit_region_f(const SURFACE *src, + SURFACE *dest, + int src_x, + int src_y, + int src_width, + int src_height, + int dest_x, + int dest_y); + +static inline void surface_blit(const SURFACE *src, SURFACE *dest, int x, int y); +static inline void surface_blit_f(const SURFACE *src, SURFACE *dest, int x, int y); + +void surface_blit_sprite_region(const SURFACE *src, + SURFACE *dest, + int src_x, + int src_y, + int src_width, + int src_height, + int dest_x, + int dest_y); + +void surface_blit_sprite_region_f(const SURFACE *src, + SURFACE *dest, + int src_x, + int src_y, + int src_width, + int src_height, + int dest_x, + int dest_y); + +static inline void surface_blit_sprite(const SURFACE *src, SURFACE *dest, int x, int y); +static inline void surface_blit_sprite_f(const SURFACE *src, SURFACE *dest, int x, int y); + +// -------------------------------------------------------------------------- + +static inline void surface_blit(const SURFACE *src, SURFACE *dest, int x, int y) { + surface_blit_region(src, dest, 0, 0, src->width, src->height, x, y); +} + +static inline void surface_blit_f(const SURFACE *src, SURFACE *dest, int x, int y) { + surface_blit_region_f(src, dest, 0, 0, src->width, src->height, x, y); +} + +static inline void surface_blit_sprite(const SURFACE *src, SURFACE *dest, int x, int y) { + surface_blit_sprite_region(src, dest, 0, 0, src->width, src->height, x, y); +} + +static inline void surface_blit_sprite_f(const SURFACE *src, SURFACE *dest, int x, int y) { + surface_blit_sprite_region_f(src, dest, 0, 0, src->width, src->height, x, y); +} + +#endif + diff --git a/CLIPPING.C b/CLIPPING.C new file mode 100644 index 0000000..fae546b --- /dev/null +++ b/CLIPPING.C @@ -0,0 +1,111 @@ +#include "clipping.h" +#include "common.h" +#include "mathext.h" + +static inline boolean is_in_bounds(int clip_x, + int clip_y, + int clip_right, + int clip_bottom, + int x1, + int y1, + int x2, + int y2) { + if (y1 < clip_y && y2 < clip_y) + return FALSE; + if (y1 > clip_bottom && y2 > clip_bottom) + return FALSE; + if (x1 < clip_x && x2 < clip_x) + return FALSE; + if (x1 > clip_right && x2 > clip_right) + return FALSE; + + return TRUE; +} + +boolean is_point_in_bounds(const RECT *clip_region, int x, int y) { + return ( + x >= clip_region->x && + y >= clip_region->y && + x <= rect_right(clip_region) && + y <= rect_bottom(clip_region) + ); +} + +boolean clamp_to_region(const RECT *clip_region, + int *x1, + int *y1, + int *x2, + int *y2) { + int clip_x = clip_region->x; + int clip_y = clip_region->y; + int clip_right = rect_right(clip_region); + int clip_bottom = rect_bottom(clip_region); + + if (!is_in_bounds(clip_x, clip_y, clip_right, clip_bottom, *x1, *y1, *x2, *y2)) + return FALSE; + + // at least partially within bounds + + if (*y1 < clip_y) *y1 = clip_y; + if (*y1 > clip_bottom) *y1 = clip_bottom; + if (*y2 < clip_y) *y2 = clip_y; + if (*y2 > clip_bottom) *y2 = clip_bottom; + if (*x1 < clip_x) *x1 = clip_x; + if (*x1 > clip_right) *x1 = clip_right; + if (*x2 < clip_x) *x2 = clip_x; + if (*x2 > clip_right) *x2 = clip_right; + + return TRUE; +} + +boolean clip_to_region(const RECT *clip_region, RECT *r) { + int clip_right = rect_right(clip_region); + int clip_bottom = rect_bottom(clip_region); + int offset; + + // off the left edge? + if (r->x < clip_region->x) { + // completely off the left edge? + if (rect_right(r) < clip_region->x) + return FALSE; + + offset = clip_region->x - r->x; + r->x += offset; + r->width -= offset; + } + + // off the right edge? + if (r->x > (clip_region->width - r->width)) { + // completely off the right edge? + if (r->x > clip_right) + return FALSE; + + offset = r->x + r->width - clip_region->width; + r->width -= offset; + } + + // off the top edge? + if (r->y < clip_region->y) { + // completely off the top edge? + if (rect_bottom(r) < clip_region->y) + return FALSE; + + offset = clip_region->y - r->y; + r->y += offset; + r->height -= offset; + } + + // off the bottom edge? + if (r->y > (clip_region->height - r->height)) { + // completely off the bottom edge? + if (r->y > clip_bottom) + return FALSE; + + offset = r->y + r->height - clip_region->height; + r->height -= offset; + } + + return TRUE; +} + + diff --git a/CLIPPING.H b/CLIPPING.H new file mode 100644 index 0000000..1602a59 --- /dev/null +++ b/CLIPPING.H @@ -0,0 +1,39 @@ +#ifndef DGL_CLIPPING_H_INCLUDED +#define DGL_CLIPPING_H_INCLUDED + +#include "common.h" +#include "rect.h" + +/* + * Determines if the given point lies within the clipping region. + * @param clip_region the clipping region to check against + * @param x x coordinate of the point + * @param y y coordinate of the point + * @return TRUE if the point lies inside the clipping region + */ +boolean is_point_in_bounds(const RECT *clip_region, int x, int y); + +/* + * Clamps the coordinates given to the clipping region, assuming that the + * region defined by the coordinates lies at least partially within the + * clipping region. + * @param clip_region the clipping region to check against and clamp to + * @param x1 x coordinate of the top-left point of the region to clamp + * @param y1 y coordinate of the top-left point of the region to clamp + * @param x2 x coordinate of the bottom-right point of the region to clamp + * @param y2 y coordinate of the bottom-right point of the region to clamp + * @return TRUE if the given region was clamped and was at least partially + * within the clipping region to begin with. If the region was + * totally outside of the clipping region, returns FALSE and the + * coordinates will not be modified. + */ +boolean clamp_to_region(const RECT *clip_region, + int *x1, + int *y1, + int *x2, + int *y2); + +boolean clip_to_region(const RECT *clip_region, RECT *r); + +#endif + diff --git a/COMMON.H b/COMMON.H new file mode 100644 index 0000000..4ac384a --- /dev/null +++ b/COMMON.H @@ -0,0 +1,36 @@ +#ifndef DGL_COMMON_H_INCLUDED +#define DGL_COMMON_H_INCLUDED + +typedef int boolean; + +typedef char byte; +typedef short word; +typedef int dword; + +typedef unsigned char ubyte; +typedef unsigned short uword; +typedef unsigned int udword; + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; + +#define TRUE 1 +#define FALSE 0 + +#define BIT_0 0x1 +#define BIT_1 0x2 +#define BIT_2 0x4 +#define BIT_3 0x8 +#define BIT_4 0x10 +#define BIT_5 0x20 +#define BIT_6 0x40 +#define BIT_7 0x80 + +#define BIT_ISSET(bit, x) ((x) & (bit) != 0) +#define BIT_SET(bit, x) ((x) |= (bit)) +#define BIT_CLEAR(bit, x) ((x) &= ~(bit)) +#define BIT_TOGGLE(bit, x) ((x) ^= (bit)) + +#endif + diff --git a/DGL.C b/DGL.C new file mode 100644 index 0000000..ee45d94 --- /dev/null +++ b/DGL.C @@ -0,0 +1,106 @@ +#include "dgl.h" +#include "keyboard.h" +#include "mouse.h" +#include "gfx.h" +#include "util.h" +#include +#include +#include + +static boolean _initialized = FALSE; +static DGL_ERROR _last_error = DGL_NONE; + +boolean dgl_init(void) { + if (_initialized) { + dgl_set_error(DGL_ALREADY_INIT); + return FALSE; + } + if (__djgpp_nearptr_enable() == 0) { + dgl_set_error(DGL_NEARPTR_ENABLE_FAILURE); + return FALSE; + } + + srandom((int)sys_clock()); + + // first call will return zero, so call here just to "init" + sys_clock(); + sys_ticks(); + + if (!video_init()) + return FALSE; + if (!keyboard_init()) + return FALSE; + if (!mouse_init()) + return FALSE; + + _initialized = TRUE; + return TRUE; +} + +boolean dgl_shutdown(void) { + if (!_initialized) + return TRUE; // don't care + + // remove installed services + if (!mouse_shutdown()) + return FALSE; + if (!keyboard_shutdown()) + return FALSE; + if (!video_shutdown()) + return FALSE; + + __djgpp_nearptr_disable(); + + _initialized = FALSE; + return TRUE; +} + +DGL_ERROR dgl_last_error(void) { + return _last_error; +} + +const char* dgl_last_error_message(void) { + switch (_last_error) { + case DGL_NONE: + return "No error."; + case DGL_ALREADY_INIT: + return "DGL is already initialized."; + case DGL_NEARPTR_ENABLE_FAILURE: + return "Failed to enable DJGPP near pointers."; + case DGL_VIDEO_ALREADY_INITIALIZED: + return "Video subsystem is already initialized."; + case DGL_VIDEO_MODE_13H_INIT_FAILURE: + return "Failed to set VGA mode 13h."; + case DGL_VIDEO_TEXT_MODE_INIT_FAILURE: + return "Failed to set text mode."; + case DGL_KEYBOARD_ALREADY_INITIALIZED: + return "Keyboard subsystem is already initialized."; + case DGL_KEYBOARD_IRQ_INSTALL_FAILURE: + return "Failed to install IRQ handler for keyboard."; + case DGL_KEYBOARD_IRQ_RESTORE_FAILURE: + return "Failed to restore original keyboard IRQ handler."; + case DGL_KEYBOARD_UPDATE_LED_FAILURE: + return "Failed to update keyboard LED state."; + case DGL_MOUSE_ALREADY_INITIALIZED: + return "Mouse subsystem is already initialized."; + case DGL_MOUSE_ALLOCATE_CALLBACK_FAILURE: + return "Failed to allocate mouse handler callback."; + case DGL_MOUSE_FREE_CALLBACK_FAILURE: + return "Failed to free mouse handler callback."; + case DGL_MOUSE_INT_CALLBACK_SET_FAILURE: + return "Failed to set mouse interrupt handler callback."; + case DGL_MOUSE_INT_CALLBACK_RESTORE_FAILURE: + return "Failed to restore original mouse interrupt handler callback."; + case DGL_IO_ERROR: + return "File IO error."; + case DGL_PCX_BAD_FORMAT: + return "PCX file unsupported format or bad file."; + default: + return "Unknown error."; + } +} + +void dgl_set_error(DGL_ERROR error) { + _last_error = error; +} + diff --git a/DGL.H b/DGL.H new file mode 100644 index 0000000..712a2b3 --- /dev/null +++ b/DGL.H @@ -0,0 +1,24 @@ +#ifndef DGL_DGL_H_INCLUDED +#define DGL_DGL_H_INCLUDED + +#include "common.h" +#include "error.h" + +#include "keyboard.h" +#include "mouse.h" +#include "gfx.h" +#include "clipping.h" +#include "draw.h" +#include "blit.h" +#include "mathext.h" +#include "rect.h" +#include "vector2.h" +#include "matrix33.h" +#include "util.h" +#include "pcx.h" + +boolean dgl_init(void); +boolean dgl_shutdown(void); + +#endif + diff --git a/DRAW.C b/DRAW.C new file mode 100644 index 0000000..5b4f017 --- /dev/null +++ b/DRAW.C @@ -0,0 +1,365 @@ +#include "draw.h" +#include "gfx.h" +#include "mathext.h" +#include "util.h" +#include "internal.h" +#include +#include +#include + +static char printf_buffer[1024]; + +void surface_hline_f(SURFACE *surface, int x1, int x2, int y, int color) { + byte *p = surface_pointer(surface, x1, y); + memset(p, color, x2 - x1 + 1); +} + +void surface_vline_f(SURFACE *surface, int x, int y1, int y2, int color) { + byte *p = surface_pointer(surface, x, y1); + int line_inc = surface->width; + int y; + + for (y = y1; y <= y2; ++y, p += line_inc) { + *p = (byte)color; + } +} + +void surface_line(SURFACE *surface, int x1, int y1, int x2, int y2, int color) { + int delta_x, delta_y; + int delta_x_abs, delta_y_abs; + int delta_x_sign, delta_y_sign; + int x, y; + byte *p; + int p_x_inc; + int p_y_inc; + int i; + int dx = x1; + int dy = y1; + int clip_x1 = surface->clip_region.x; + int clip_y1 = surface->clip_region.y; + int clip_x2 = rect_right(&surface->clip_region); + int clip_y2 = rect_bottom(&surface->clip_region); + + delta_x = x2 - x1; + delta_y = y2 - y1; + delta_x_abs = abs(delta_x); + delta_y_abs = abs(delta_y); + delta_x_sign = SIGN(delta_x); + delta_y_sign = SIGN(delta_y); + x = delta_y_abs / 2; + y = delta_x_abs / 2; + + p = surface_pointer(surface, x1, y1); + p_x_inc = delta_x_sign; + p_y_inc = delta_y_sign * surface->width; + + if (dx >= clip_x1 && + dy >= clip_y1 && + dx <= clip_x2 && + dy <= clip_y2) + *p = (byte)color; + + if (delta_x_abs >= delta_y_abs) { + for (i = 0; i < delta_x_abs; ++i) { + y += delta_y_abs; + + if (y >= delta_x_abs) { + y -= delta_x_abs; + p += p_y_inc; + dy += delta_y_sign; + } + + p += p_x_inc; + dx += delta_x_sign; + + if (dx >= clip_x1 && + dy >= clip_y1 && + dx <= clip_x2 && + dy <= clip_y2) + *p = (byte)color; + } + } else { + for (i = 0; i < delta_y_abs; ++i) { + x += delta_x_abs; + + if (x >= delta_y_abs) { + x -= delta_y_abs; + p += p_x_inc; + dx += delta_x_sign; + } + + p += p_y_inc; + dy += delta_y_sign; + + if (dx >= clip_x1 && + dy >= clip_y1 && + dx <= clip_x2 && + dy <= clip_y2) + *p = (byte)color; + } + } +} + +void surface_line_f(SURFACE *surface, int x1, int y1, int x2, int y2, int color) { + int delta_x, delta_y; + int delta_x_abs, delta_y_abs; + int delta_x_sign, delta_y_sign; + int x, y; + byte *p; + int p_x_inc; + int p_y_inc; + int i; + + delta_x = x2 - x1; + delta_y = y2 - y1; + delta_x_abs = abs(delta_x); + delta_y_abs = abs(delta_y); + delta_x_sign = SIGN(delta_x); + delta_y_sign = SIGN(delta_y); + x = delta_y_abs / 2; + y = delta_x_abs / 2; + + p = surface_pointer(surface, x1, y1); + p_x_inc = delta_x_sign; + p_y_inc = delta_y_sign * surface->width; + + *p = (byte)color; + + if (delta_x_abs >= delta_y_abs) { + for (i = 0; i < delta_x_abs; ++i) { + y += delta_y_abs; + + if (y >= delta_x_abs) { + y -= delta_x_abs; + p += p_y_inc; + } + + p += p_x_inc; + *p = (byte)color; + } + } else { + for (i = 0; i < delta_y_abs; ++i) { + x += delta_x_abs; + + if (x >= delta_y_abs) { + x -= delta_y_abs; + p += p_x_inc; + } + + p += p_y_inc; + *p = (byte)color; + } + } +} + +void surface_rect(SURFACE *surface, int x1, int y1, int x2, int y2, int color) { + byte *p1, *p2; + int width; + int y; + int y_inc = surface->width; + int clipped_x1 = x1; + int clipped_y1 = y1; + int clipped_x2 = x2; + int clipped_y2 = y2; + int clip_right = rect_right(&surface->clip_region); + int clip_bottom = rect_bottom(&surface->clip_region); + + if (x1 < surface->clip_region.x) + clipped_x1 = surface->clip_region.x; + if (x2 > clip_right) + clipped_x2 = clip_right; + + width = clipped_x2 - clipped_x1 + 1; + + // top line, only if y1 was within bounds + if (y1 < surface->clip_region.y) + clipped_y1 = surface->clip_region.y; + else + memset(surface_pointer(surface, clipped_x1, clipped_y1), color, width); + + // bottom line, only if y2 was within bounds + if (y2 > clip_bottom) + clipped_y2 = clip_bottom; + else + memset(surface_pointer(surface, clipped_x1, clipped_y2), color, width); + + // draw both left and right lines if neither x1 nor x2 were clipped + if (x1 == clipped_x1 && x2 == clipped_x2) { + p1 = surface_pointer(surface, x1, y1); + p2 = surface_pointer(surface, x2, y1); + for (y = y1; y <= y2; ++y, p1 += y_inc, p2 += y_inc) { + *p1 = (byte)color; + *p2 = (byte)color; + } + + // draw left line if x1 was not clipped + } else if (x1 == clipped_x1) { + p1 = surface_pointer(surface, x1, y1); + for (y = y1; y <= y2; ++y, p1 += y_inc) + *p1 = (byte)color; + + // draw right line if x2 was not clipped + } else if (x2 == clipped_x2) { + p1 = surface_pointer(surface, x2, y1); + for (y = y1; y <= y2; ++y, p1 += y_inc) + *p1 = (byte)color; + } +} + +void surface_rect_f(SURFACE *surface, int x1, int y1, int x2, int y2, int color) { + byte *p1, *p2; + int width = x2 - x1 + 1; + int y; + int y_inc = surface->width; + + p1 = surface_pointer(surface, x1, y1); + p2 = surface_pointer(surface, x1, y2); + memset(p1, color, width); + memset(p2, color, width); + + p1 = surface_pointer(surface, x1, y1); + p2 = surface_pointer(surface, x2, y1); + for (y = y1; y <= y2; ++y, p1 += y_inc, p2 += y_inc) { + *p1 = (byte)color; + *p2 = (byte)color; + } +} + +void surface_filled_rect(SURFACE *surface, + int x1, + int y1, + int x2, + int y2, + int color) { + if (!clamp_to_region(&surface->clip_region, &x1, &y1, &x2, &y2)) + return; + + surface_filled_rect_f(surface, x1, y1, x2, y2, color); +} + +void surface_filled_rect_f(SURFACE *surface, + int x1, + int y1, + int x2, + int y2, + int color) { + byte *p; + int width = x2 - x1 + 1; + int y; + int y_inc = surface->width; + + p = surface_pointer(surface, x1, y1); + for (y = y1; y <= y2; ++y) { + memset(p, (byte)color, width); + p += y_inc; + } +} + +#define CHAR_WIDTH 8 +#define CHAR_HEIGHT 8 +#define CHAR_LINE_BITMASK(x) (1 << ((CHAR_WIDTH - 1) - (x))) +#define IS_CHAR_PIXEL(x, line) ((line) & CHAR_LINE_BITMASK(x)) + +// dest_x, dest_y - original unclipped render x,y coords +// dest_clipped - clipped destination render region +static inline void print_char(SURFACE *surface, + int dest_x, + int dest_y, + const RECT *dest_clipped, + int color, + char c) { + byte *p; + byte *rom_char; + char char_line_bits; + int cx, cy; + int offset_x, offset_y; + int width, height; + int y_inc = surface->width; + + p = surface_pointer(surface, dest_clipped->x, dest_clipped->y); + rom_char = map_dos_memory(0xffa6e) + (c * CHAR_HEIGHT); + + // get offset x,y to start rendering char from (will be in range 0-7) + offset_x = dest_clipped->x - dest_x; + offset_y = dest_clipped->y - dest_y; + width = dest_clipped->width + offset_x; + height = dest_clipped->height + offset_y; + + // cx,cy are always in "char coordinate space" (that is, 0-7) + for (cy = offset_y; cy < height; ++cy) { + char_line_bits = rom_char[cy]; + + for (cx = offset_x; cx < width; ++cx) { + if (IS_CHAR_PIXEL(cx, char_line_bits)) + p[cx - offset_x] = (byte)color; + } + + p += y_inc; + } +} + +static inline void print_text(SURFACE *surface, + int x, + int y, + int color, + boolean clip, + const char *text) { + const char *c; + RECT r; + RECT draw_r; + + r = rect(x, y, CHAR_WIDTH, CHAR_HEIGHT); + + for (c = text; *c; ++c) { + if (*c == '\n') { + r.x = x; + r.y += r.height; + } else if (*c == ' ') { + r.x += r.width; + } else { + if (clip) { + draw_r = r; + if (clip_to_region(&surface->clip_region, &draw_r)) + print_char(surface, r.x, r.y, &draw_r, color, *c); + } else { + print_char(surface, r.x, r.y, &r, color, *c); + } + r.x += r.width; + } + } +} + +void surface_text(SURFACE *surface, int x, int y, int color, const char *text) { + print_text(surface, x, y, color, TRUE, text); +} + +void surface_text_f(SURFACE *surface, int x, int y, int color, const char *text) { + print_text(surface, x, y, color, FALSE, text); +} + +void surface_printf(SURFACE *surface, + int x, + int y, + int color, + const char *format, ...) { + va_list args; + va_start(args, format); + vsprintf(printf_buffer, format, args); + va_end(args); + + print_text(surface, x, y, color, TRUE, printf_buffer); +} + +void surface_printf_f(SURFACE *surface, + int x, + int y, + int color, + const char *format, ...) { + va_list args; + va_start(args, format); + vsprintf(printf_buffer, format, args); + va_end(args); + + print_text(surface, x, y, color, FALSE, printf_buffer); +} + diff --git a/DRAW.H b/DRAW.H new file mode 100644 index 0000000..f1460ce --- /dev/null +++ b/DRAW.H @@ -0,0 +1,68 @@ +#ifndef DGL_DRAW_H_INCLUDED +#define DGL_DRAW_H_INCLUDED + +#include "gfx.h" +#include "clipping.h" +#include "util.h" + +static inline void surface_pset(SURFACE *surface, int x, int y, int color); +static inline void surface_pset_f(SURFACE *surface, int x, int y, int color); +static inline int surface_point(const SURFACE *surface, int x, int y); +static inline int surface_point_f(const SURFACE *surface, int x, int y); + +static inline void surface_hline(SURFACE *surface, int x1, int x2, int y, int color); +void surface_hline_f(SURFACE *surface, int x1, int x2, int y, int color); +static inline void surface_vline(SURFACE *surface, int x, int y1, int y2, int color); +void surface_vline_f(SURFACE *surface, int x, int y1, int y2, int color); +void surface_line(SURFACE *surface, int x1, int y1, int x2, int y2, int color); +void surface_line_f(SURFACE *surface, int x1, int y1, int x2, int y2, int color); + +void surface_rect(SURFACE *surface, int x1, int y1, int x2, int y2, int color); +void surface_rect_f(SURFACE *surface, int x1, int y1, int x2, int y2, int color); +void surface_filled_rect(SURFACE *surface, int x1, int y1, int x2, int y2, int color); +void surface_filled_rect_f(SURFACE *surface, int x1, int y1, int x2, int y2, int color); + +void surface_text(SURFACE *surface, int x, int y, int color, const char *text); +void surface_text_f(SURFACE *surface, int x, int y, int color, const char *text); +void surface_printf(SURFACE *surface, int x, int y, int color, const char *format, ...); +void surface_printf_f(SURFACE *surface, int x, int y, int color, const char *format, ...); + + +// -------------------------------------------------------------------------- + +static inline void surface_pset(SURFACE *surface, int x, int y, int color) { + if (is_point_in_bounds(&surface->clip_region, x, y)) + surface_pset_f(surface, x, y, color); +} + +static inline void surface_pset_f(SURFACE *surface, int x, int y, int color) { + *(surface_pointer(surface, x, y)) = (byte)color; +} + +static inline int surface_point(const SURFACE *surface, int x, int y) { + if (is_point_in_bounds(&surface->clip_region, x, y)) + return surface_point_f(surface, x, y); + else + return 0; +} + +static inline int surface_point_f(const SURFACE *surface, int x, int y) { + return (int)*(surface_pointer(surface, x, y)); +} + +static inline void surface_hline(SURFACE *surface, int x1, int x2, int y, int color) { + if (x2 < x1) + SWAP(int, x1, x2); + if (clamp_to_region(&surface->clip_region, &x1, &y, &x2, &y)) + surface_hline_f(surface, x1, x2, y, color); +} + +static inline void surface_vline(SURFACE *surface, int x, int y1, int y2, int color) { + if (y2 < y1) + SWAP(int, y1, y2); + if (clamp_to_region(&surface->clip_region, &x, &y1, &x, &y2)) + surface_vline_f(surface, x, y1, y2, color); +} + +#endif + diff --git a/ERROR.H b/ERROR.H new file mode 100644 index 0000000..bd03c07 --- /dev/null +++ b/ERROR.H @@ -0,0 +1,31 @@ +#ifndef DGL_ERROR_H_INCLUDED +#define DGL_ERROR_H_INCLUDED + +#include "common.h" + +typedef enum { + DGL_NONE = 0, + DGL_ALREADY_INIT, + DGL_NEARPTR_ENABLE_FAILURE, + DGL_VIDEO_ALREADY_INITIALIZED, + DGL_VIDEO_MODE_13H_INIT_FAILURE, + DGL_VIDEO_TEXT_MODE_INIT_FAILURE, + DGL_KEYBOARD_ALREADY_INITIALIZED, + DGL_KEYBOARD_IRQ_INSTALL_FAILURE, + DGL_KEYBOARD_IRQ_RESTORE_FAILURE, + DGL_KEYBOARD_UPDATE_LED_FAILURE, + DGL_MOUSE_ALREADY_INITIALIZED, + DGL_MOUSE_ALLOCATE_CALLBACK_FAILURE, + DGL_MOUSE_FREE_CALLBACK_FAILURE, + DGL_MOUSE_INT_CALLBACK_SET_FAILURE, + DGL_MOUSE_INT_CALLBACK_RESTORE_FAILURE, + DGL_IO_ERROR, + DGL_PCX_BAD_FORMAT +} DGL_ERROR; + +DGL_ERROR dgl_last_error(void); +const char* dgl_last_error_message(void); +void dgl_set_error(DGL_ERROR error); + +#endif + diff --git a/GFX.C b/GFX.C new file mode 100644 index 0000000..ad4e892 --- /dev/null +++ b/GFX.C @@ -0,0 +1,151 @@ +#include "gfx.h" +#include "blit.h" +#include "util.h" +#include "internal.h" +#include "error.h" +#include +#include +#include +#include + +static boolean _initialized = FALSE; + +SURFACE *screen = NULL; + +static SURFACE* surface_create_internal(int width, int height, byte *pixels) { + SURFACE *surface = malloc(sizeof(SURFACE)); + + surface->width = width; + surface->height = height; + surface->clip_region = rect(0, 0, width, height); + + if (pixels != NULL) { + surface->aliased = TRUE; + surface->pixels = pixels; + } else { + surface->aliased = FALSE; + surface->pixels = malloc(width * height); + memset(surface->pixels, 0, width * height); + } + + return surface; +} + +boolean video_init(void) { + __dpmi_regs regs; + void *framebuffer; + + if (_initialized) { + dgl_set_error(DGL_VIDEO_ALREADY_INITIALIZED); + return FALSE; + } + + memset(®s, 0, sizeof(__dpmi_regs)); + regs.h.ah = 0x00; + regs.h.al = 0x13; + if (__dpmi_int(0x10, ®s)) { + dgl_set_error(DGL_VIDEO_MODE_13H_INIT_FAILURE); + return FALSE; + } + + framebuffer = map_dos_memory(0xa0000); + screen = surface_create_internal(320, 200, framebuffer); + surface_clear(screen, 0); + + _initialized = TRUE; + return TRUE; +} + +boolean video_shutdown(void) { + __dpmi_regs regs; + + if (!_initialized) + return TRUE; // don't care + + memset(®s, 0, sizeof(__dpmi_regs)); + regs.h.ah = 0x00; + regs.h.al = 0x03; + if (__dpmi_int(0x10, ®s)) { + dgl_set_error(DGL_VIDEO_TEXT_MODE_INIT_FAILURE); + return FALSE; + } + + surface_free(screen); + screen = NULL; + + _initialized = FALSE; + return TRUE; +} + +boolean video_is_initialized(void) { + return _initialized; +} + +void video_wait_vsync(void) { + do {} while (inportb(0x3da) & 0x8); + do {} while (!(inportb(0x3da) & 0x8)); +} + +void video_set_color(ubyte color, ubyte r, ubyte g, ubyte b) { + outportb(0x3c6, 0xff); + outportb(0x3c8, color); + outportb(0x3c9, r); + outportb(0x3c9, g); + outportb(0x3c9, b); +} + +void video_get_color(ubyte color, ubyte *r, ubyte *g, ubyte *b) { + outportb(0x3c6, 0xff); + outportb(0x3c7, color); + *r = inportb(0x3c9); + *g = inportb(0x3c9); + *b = inportb(0x3c9); +} + +void video_set_palette(const byte *palette) { + int i = 0; + for (i = 0; i < 256; ++i) { + video_set_color(i, palette[0], palette[1], palette[2]); + palette += 3; + } +} + +void video_get_palette(byte *palette) { + int i = 0; + for (i = 0; i < 256; ++i) { + video_get_color(i, palette, palette + 1, palette + 2); + palette += 3; + } +} + +SURFACE* surface_create(int width, int height) { + return surface_create_internal(width, height, NULL); +} + +void surface_free(SURFACE *surface) { + if (!surface) + return; + + if (!surface->aliased) + free(surface->pixels); + free(surface); +} + +void surface_clear(SURFACE *surface, int color) { + int length = surface->width * surface->height; + if (length % 4 == 0) { + color *= 0x01010101; + REP_STOSL(color, surface->pixels, length / 4); + } else { + memset(surface->pixels, color, surface->width * surface->height); + } +} + +void surface_copy(const SURFACE *src, SURFACE *dest) { + if (src->width == dest->width && src->height == dest->height) { + memcpy(dest->pixels, src->pixels, src->width * src->height); + } else { + surface_blit(src, dest, 0, 0); + } +} + diff --git a/GFX.H b/GFX.H new file mode 100644 index 0000000..2bb6a1c --- /dev/null +++ b/GFX.H @@ -0,0 +1,47 @@ +#ifndef DGL_GFX_H_INCLUDED +#define DGL_GFX_H_INCLUDED + +#include "common.h" +#include "rect.h" + +typedef struct { + int width; + int height; + byte *pixels; + boolean aliased; + RECT clip_region; +} SURFACE; + +extern SURFACE *screen; + +boolean video_init(void); +boolean video_shutdown(void); +boolean video_is_initialized(void); + +void video_wait_vsync(void); + +void video_set_color(ubyte color, ubyte r, ubyte g, ubyte b); +void video_get_color(ubyte color, ubyte *r, ubyte *g, ubyte *b); +void video_set_palette(const byte *palette); +void video_get_palette(byte *palette); + +SURFACE* surface_create(int width, int height); +void surface_free(SURFACE *surface); +void surface_clear(SURFACE *surface, int color); +void surface_copy(const SURFACE *src, SURFACE *dest); + +static inline int surface_offset(const SURFACE *surface, int x, int y); +static inline byte* surface_pointer(const SURFACE *surface, int x, int y); + +// -------------------------------------------------------------------------- + +static inline int surface_offset(const SURFACE *surface, int x, int y) { + return surface->width * y + x; +} + +static inline byte* surface_pointer(const SURFACE *surface, int x, int y) { + return surface->pixels + surface_offset(surface, x, y); +} + +#endif + diff --git a/INTERNAL.C b/INTERNAL.C new file mode 100644 index 0000000..40ce76d --- /dev/null +++ b/INTERNAL.C @@ -0,0 +1,36 @@ +#include "internal.h" +#include +#include +#include + +boolean _install_irq(int irq, + void* irq_handler, + _go32_dpmi_seginfo* new_handler, + _go32_dpmi_seginfo* old_handler) { + memset((void*)new_handler, 0, sizeof(_go32_dpmi_seginfo)); + + if (_go32_dpmi_get_real_mode_interrupt_vector(irq, old_handler) != 0) + return FALSE; + + new_handler->pm_offset = (int)irq_handler; + new_handler->pm_selector = _go32_my_cs(); + + if (_go32_dpmi_allocate_iret_wrapper(new_handler) != 0) + return FALSE; + if (_go32_dpmi_set_protected_mode_interrupt_vector(irq, new_handler) != 0) + return FALSE; + + return TRUE; +} + +boolean _restore_irq(int irq, + _go32_dpmi_seginfo* new_handler, + _go32_dpmi_seginfo* old_handler) { + if (_go32_dpmi_set_real_mode_interrupt_vector(irq, old_handler) != 0) + return FALSE; + if (_go32_dpmi_free_iret_wrapper(new_handler) != 0) + return FALSE; + + return TRUE; +} + diff --git a/INTERNAL.H b/INTERNAL.H new file mode 100644 index 0000000..7960d56 --- /dev/null +++ b/INTERNAL.H @@ -0,0 +1,60 @@ +#ifndef DGL_INTERNAL_H_INCLUDED +#define DGL_INTERNAL_H_INCLUDED + +#include "common.h" +#include + +#define END_OF_FUNCTION(x) void x##_end() {} +#define LOCK_VARIABLE(x) _go32_dpmi_lock_data((void*)&x, sizeof(x)) +#define LOCK_MEMORY(ptr, len) _go32_dpmi_lock_data((void*)(ptr), (len)) +#define LOCK_FUNCTION(x) _go32_dpmi_lock_code(x, (long)x##_end - (long)x) + +static inline void _enable_interrupts(void) { + asm volatile ("sti"); +} +static inline void _disable_interrupts(void) { + asm volatile ("cli"); +} + +boolean _install_irq(int irq, + void* irq_handler, + _go32_dpmi_seginfo* new_handler, + _go32_dpmi_seginfo* old_handler); +boolean _restore_irq(int irq, + _go32_dpmi_seginfo* new_handler, + _go32_dpmi_seginfo* old_handler); + +#define REP_MOVSL(src, dest, num_dwords) \ + __asm__ __volatile__ ( \ + "cld\n\t" \ + "rep\n\t" \ + "movsl" \ + : : "S" (src), "D" (dest), "c" (num_dwords) \ + : "%ecx", "%esi", "%edi" ) + +#define REP_MOVSB(src, dest, num_bytes) \ + __asm__ __volatile__ ( \ + "cld\n\t" \ + "rep\n\t" \ + "movsb" \ + : : "S" (src), "D" (dest), "c" (num_bytes) \ + : "%ecx", "%esi", "%edi" ) + +#define REP_STOSL(value, dest, num_dwords) \ + __asm__ __volatile__ ( \ + "cld\n\t" \ + "rep\n\t" \ + "stosl" \ + : : "a" (value), "D" (dest), "c" (num_dwords) \ + : "%ecx", "%edi" ) + +#define REP_STOSB(value, dest, num_bytes) \ + __asm__ __volatile__ ( \ + "cld\n\t" \ + "rep\n\t" \ + "stosb" \ + : : "a" (value), "D" (dest), "c" (num_bytes) \ + : "%ecx", "%edi" ) + +#endif + diff --git a/KEYBOARD.C b/KEYBOARD.C new file mode 100644 index 0000000..f3ba4d1 --- /dev/null +++ b/KEYBOARD.C @@ -0,0 +1,194 @@ +#include "keyboard.h" +#include "internal.h" +#include "util.h" +#include "error.h" +#include +#include +#include +#include + +#define PIC_CTRL_PORT 0x20 +#define KEYBRD_DATA_PORT 0x60 +#define KEYBRD_CTRL_PORT 0x61 +#define KEYBRD_STATUS_PORT 0x64 + +#define KEYBRD_CMD_SET_LED 0xED + +#define KEYBRD_FLAGS_ADDR 0x417 + +#define KEYBRD_FLAGS_SCROLLOCK 0x10 +#define KEYBRD_FLAGS_NUMLOCK 0x20 +#define KEYBRD_FLAGS_CAPSLOCK 0x40 + +#define KEYBRD_LED_SCROLLOCK 0x1 +#define KEYBRD_LED_NUMLOCK 0x2 +#define KEYBRD_LED_CAPSLOCK 0x4 + +static boolean _installed = FALSE; + +volatile ubyte keys[128]; + +volatile KEY _key_last_scan; +volatile KEY _key_scan; + +uword _old_flags; + +_go32_dpmi_seginfo _old_handler; +_go32_dpmi_seginfo _new_handler; + +static inline void reset_key_states() { + memset((void*)keys, 0, 128); +} + +// waits until the keyboard status port indicates the data port +// can be read from once again +static inline void wait_kb_data_read() { + while ((inportb(KEYBRD_STATUS_PORT) & BIT_0) == 0) { + } +} + +// waits until the keyboard status port indicates the data port +// can be written to once again +static inline void wait_kb_data_write() { + while ((inportb(KEYBRD_STATUS_PORT) & BIT_1) != 0) { + } +} + +// sends data to the keyboard data port. checks for success +// and returns TRUE if the data write succeeded +static inline boolean send_kb_data(ubyte data) { + ubyte result; + + wait_kb_data_write(); + outportb(KEYBRD_DATA_PORT, data); + + wait_kb_data_read(); + result = inportb(KEYBRD_DATA_PORT); + return (result == 0xFA); +} + +// keyboard interrupt handler +void kb_int_handler(void) { + // read scan code of key that was just pressed + _key_scan = inportb(KEYBRD_DATA_PORT); + if (_key_scan & 0x80) { + // high bit set indicates key was released, clear high bit to get + // the actual key scan code + _key_scan &= 0x7f; + keys[(int)_key_scan] = 0; + } else { + keys[(int)_key_scan] = 1; + } + + _key_last_scan = _key_scan; + + // indicate key event was processed to keyboard controller + _key_scan = inportb(KEYBRD_CTRL_PORT) | 0x82; + outportb(KEYBRD_CTRL_PORT, _key_scan); + outportb(KEYBRD_CTRL_PORT, _key_scan & 0x7f); + outportb(PIC_CTRL_PORT, 0x20); +} +END_OF_FUNCTION(kb_int_handler) + +static uword get_kb_flags(void) { + return *((uword*)(map_dos_memory(KEYBRD_FLAGS_ADDR))); +} + +static void set_kb_flags(uword flags) { + *((uword*)(map_dos_memory(KEYBRD_FLAGS_ADDR))) = flags; +} + +// updates the keyboard indicator LEDs from the num/caps/scroll lock flags +// set in the passed keyboard flags. returns FALSE if the LEDs could not +// be updated (if keyboard data write did not succeed) +static boolean update_kb_led(byte flags) { + ubyte led = 0; + + if (flags & KEYBRD_FLAGS_SCROLLOCK) + led |= KEYBRD_LED_SCROLLOCK; + if (flags & KEYBRD_FLAGS_NUMLOCK) + led |= KEYBRD_LED_NUMLOCK; + if (flags & KEYBRD_FLAGS_CAPSLOCK) + led |= KEYBRD_LED_CAPSLOCK; + + if (!send_kb_data(KEYBRD_CMD_SET_LED)) { + dgl_set_error(DGL_KEYBOARD_UPDATE_LED_FAILURE); + return FALSE; + } + if (!send_kb_data(led)) { + dgl_set_error(DGL_KEYBOARD_UPDATE_LED_FAILURE); + return FALSE; + } + + return TRUE; +} + +boolean keyboard_init(void) { + if (_installed) { + dgl_set_error(DGL_KEYBOARD_ALREADY_INITIALIZED); + return FALSE; + } + + LOCK_MEMORY(keys, 128); + LOCK_VARIABLE(_key_scan); + LOCK_VARIABLE(_key_last_scan); + LOCK_FUNCTION(kb_int_handler); + + // preserve old flags + _old_flags = get_kb_flags(); + + reset_key_states(); + if (!_install_irq(9, kb_int_handler, &_new_handler, &_old_handler)) { + dgl_set_error(DGL_KEYBOARD_IRQ_INSTALL_FAILURE); + return FALSE; + } + + // turn off keyboard LEDs since our interrupt handler does not currently + // respect the num/caps/scroll lock statuses + _disable_interrupts(); + update_kb_led(0); + _enable_interrupts(); + + _installed = TRUE; + return TRUE; +} + +boolean keyboard_shutdown(void) { + if (!_installed) + return TRUE; // don't care + + // reset keyboard LEDs to previous state + _disable_interrupts(); + update_kb_led(_old_flags); + _enable_interrupts(); + + if (!_restore_irq(9, &_new_handler, &_old_handler)) { + dgl_set_error(DGL_KEYBOARD_IRQ_RESTORE_FAILURE); + return FALSE; + } + reset_key_states(); + + // restore keyboard flags to previous state + set_kb_flags(_old_flags); + + _installed = FALSE; + return TRUE; +} + +boolean keyboard_is_initialized(void) { + return _installed; +} + +KEY keyboard_read_key(void) { + _key_last_scan = 0; + while (_key_last_scan == 0) { + } + return _key_last_scan; +} + +void keyboard_wait_for_key(KEY key) { + _key_last_scan = 0; + while (_key_last_scan != key) { + } +} + diff --git a/KEYBOARD.H b/KEYBOARD.H new file mode 100644 index 0000000..372b518 --- /dev/null +++ b/KEYBOARD.H @@ -0,0 +1,43 @@ +#ifndef DGL_KEYBOARD_H_INCLUDED +#define DGL_KEYBOARD_H_INCLUDED + +#include "common.h" +#include "keys.h" + +typedef byte KEY; + +/* + * Current state of the keyboard. + */ +volatile extern ubyte keys[128]; + +/* + * Installs a custom keyboard interrupt handler. + * @return TRUE on success + */ +boolean keyboard_init(void); + +/* + * Removes a previously installed keyboard interrupt handler. + * @return TRUE on success + */ +boolean keyboard_shutdown(void); + +/* + * @return TRUE if the custom keyboard interrupt handler is installed. + */ +boolean keyboard_is_initialized(void); + +/* + * Waits indefinitely until any key is pressed. + * @return The key that was pressed. + */ +KEY keyboard_read_key(void); + +/* + * Waits indefinitely until the specified key is pressed. + */ +void keyboard_wait_for_key(KEY key); + +#endif + diff --git a/KEYS.H b/KEYS.H new file mode 100644 index 0000000..b448d69 --- /dev/null +++ b/KEYS.H @@ -0,0 +1,89 @@ +#ifndef DGL_KEYS_H_INCLUDED +#define DGL_KEYS_H_INCLUDED + +#define KEY_ESC 0x01 +#define KEY_1 0x02 +#define KEY_2 0x03 +#define KEY_3 0x04 +#define KEY_4 0x05 +#define KEY_5 0x06 +#define KEY_6 0x07 +#define KEY_7 0x08 +#define KEY_8 0x09 +#define KEY_9 0x0A +#define KEY_0 0x0B +#define KEY_MINUS 0x0C +#define KEY_EQUALS 0x0D +#define KEY_BACKSPACE 0x0E +#define KEY_TAB 0x0F +#define KEY_Q 0x10 +#define KEY_W 0x11 +#define KEY_E 0x12 +#define KEY_R 0x13 +#define KEY_T 0x14 +#define KEY_Y 0x15 +#define KEY_U 0x16 +#define KEY_I 0x17 +#define KEY_O 0x18 +#define KEY_P 0x19 +#define KEY_LEFTBRACKET 0x1A +#define KEY_RIGHTBRACKET 0x1B +#define KEY_ENTER 0x1C +#define KEY_CTRL 0x1D +#define KEY_A 0x1E +#define KEY_S 0x1F +#define KEY_D 0x20 +#define KEY_F 0x21 +#define KEY_G 0x22 +#define KEY_H 0x23 +#define KEY_J 0x24 +#define KEY_K 0x25 +#define KEY_L 0x26 +#define KEY_SEMICOLON 0x27 +#define KEY_APOSTROPHE 0x28 +#define KEY_TILDE 0x29 +#define KEY_LEFTSHIFT 0x2A +#define KEY_BACKSLASH 0x2B +#define KEY_Z 0x2C +#define KEY_X 0x2D +#define KEY_C 0x2E +#define KEY_V 0x2F +#define KEY_B 0x30 +#define KEY_N 0x31 +#define KEY_M 0x32 +#define KEY_COMMA 0x33 +#define KEY_PERIOD 0x34 +#define KEY_FORWARDSLASH 0x35 +#define KEY_RIGHTSHIFT 0x36 +#define KEY_ASTERISK 0x37 +#define KEY_ALT 0x38 +#define KEY_SPACE 0x39 +#define KEY_CAPSLOCK 0x3A +#define KEY_F1 0x3B +#define KEY_F2 0x3C +#define KEY_F3 0x3D +#define KEY_F4 0x3E +#define KEY_F5 0x3F +#define KEY_F6 0x40 +#define KEY_F7 0x41 +#define KEY_F8 0x42 +#define KEY_F9 0x43 +#define KEY_F10 0x44 +#define KEY_NUMLOCK 0x45 +#define KEY_SCROLLLOCK 0x46 +#define KEY_HOME 0x47 +#define KEY_UP 0x48 +#define KEY_PAGEUP 0x49 +#define KEY_NUM_MINUS 0x4A +#define KEY_LEFT 0x4B +#define KEY_NUM_5 0x4C +#define KEY_RIGHT 0x4D +#define KEY_NUM_PLUS 0x4E +#define KEY_END 0x4F +#define KEY_DOWN 0x50 +#define KEY_PAGEDOWN 0x51 +#define KEY_INSERT 0x52 +#define KEY_DELETE 0x53 + +#endif + diff --git a/LIBDGL.GDT b/LIBDGL.GDT new file mode 100644 index 0000000..e0b928a Binary files /dev/null and b/LIBDGL.GDT differ diff --git a/LIBDGL.GPR b/LIBDGL.GPR new file mode 100644 index 0000000..d0fc7e3 Binary files /dev/null and b/LIBDGL.GPR differ diff --git a/MATHEXT.C b/MATHEXT.C new file mode 100644 index 0000000..78227bb --- /dev/null +++ b/MATHEXT.C @@ -0,0 +1,34 @@ +#include "mathext.h" +#include + +float angle_between_i(int x1, int y1, int x2, int y2) { + int delta_x = x2 - x1; + int delta_y = y2 - y1; + if (delta_x == 0 && delta_y == 0) + return 0.0f; + else + return atan2(delta_y, delta_x); +} + +float angle_between_f(float x1, float y1, float x2, float y2) { + float delta_x = x2 - x1; + float delta_y = y2 - y1; + if (close_enough(delta_x, 0.0f, TOLERANCE) && close_enough(delta_y, 0.0f, TOLERANCE)) + return 0.0f; + else + return atan2(delta_y, delta_x); +} + +int next_power_of_2(int n) { + int i = n & (~n + 1); + while (i < n) { + i <<= 1; + } + return i; +} + +void point_on_circle(float radius, float radians, float *x, float *y) { + *x = radius * cos(radians); + *y = radius * sin(radians); +} + diff --git a/MATHEXT.H b/MATHEXT.H new file mode 100644 index 0000000..d2c7c4e --- /dev/null +++ b/MATHEXT.H @@ -0,0 +1,75 @@ +#ifndef DGL_MATH_H_INCLUDED +#define DGL_MATH_H_INCLUDED + +#include "common.h" +#include "vector2.h" +#include + +#define TOLERANCE 0.00001f + +#define PI_OVER_180 (PI / 180.0f) + +#define RADIANS_0 0.0f +#define RADIANS_45 (PI / 4.0f) +#define RADIANS_90 (PI / 2.0f) +#define RADIANS_135 ((3.0f * PI) / 4.0f) +#define RADIANS_180 PI +#define RADIANS_225 ((5.0f * PI) / 4.0f) +#define RADIANS_270 ((3.0f * PI) / 2.0f) +#define RADIANS_315 ((7.0f * PI) / 4.0f) +#define RADIANS_360 (PI * 2.0f) + +#define DEG_TO_RAD(degrees) ((degrees) * PI_OVER_180) +#define RAD_TO_DEG(radians) ((radians) * (1.0f / PI_OVER_180)) +#define CLAMP(value, low, high) (((value) < (low) ? (low) : ((value) > (high) ? (high) : (value)))) +#define LERP(a, b, t) ((a) + ((b) - (a)) * (t)) +#define INVERSE_LERP(a, b, lerped) (((lerped) - (a)) / ((b) - (a))) + +#define SCALE_RANGE(value, from_min, from_max, to_min, to_max) \ + (((value) / (((from_max) - (from_min)) / ((to_max) - (to_min)))) + (to_min)) + +float angle_between_i(int x1, int y1, int x2, int y2); +float angle_between_f(float x1, float y1, float x2, float y2); +int next_power_of_2(int n); +void point_on_circle(float radius, float radians, float *x, float *y); + +static inline VECTOR2F direction_from_angle(float radians); +static inline float round(float value); +static inline float symmetrical_round(float value); +static inline boolean close_enough(float a, float b, float tolerance); +static inline boolean power_of_2(int n); +static inline float smooth_step(float low, float high, float t); + +// -------------------------------------------------------------------------- + +static inline VECTOR2F direction_from_angle(float radians) { + VECTOR2F direction; + point_on_circle(1.0f, radians, &direction.x, &direction.y); + return direction; +} + +static inline float round(float value) { + return ceil(value + 0.5f); +} + +static inline float symmetrical_round(float value) { + if (value > 0.0f) + return floor(value + 0.5f); + else + return ceil(value - 0.5f); +} + +static inline boolean close_enough(float a, float b, float tolerance) { + return fabs((a - b) / ((b == 0.0f) ? 1.0f : b)) < tolerance; +} + +static inline boolean power_of_2(int n) { + return (n != 0) && !(n & (n - 1)); +} + +static inline float smooth_step(float low, float high, float t) { + float n = CLAMP(t, 0.0f, 1.0f); + return LERP(low, high, (n * n) * (3.0f - (2.0f * n))); +} + +#endif diff --git a/MATRIX33.H b/MATRIX33.H new file mode 100644 index 0000000..7ef0e31 --- /dev/null +++ b/MATRIX33.H @@ -0,0 +1,340 @@ +#ifndef DGL_MATRIX33_H_INCLUDED +#define DGL_MATRIX33_H_INCLUDED + +#include "common.h" +#include "mathext.h" +#include "vector2.h" +#include + +#define _M33_11 0 +#define _M33_12 3 +#define _M33_13 6 +#define _M33_21 1 +#define _M33_22 4 +#define _M33_23 7 +#define _M33_31 2 +#define _M33_32 5 +#define _M33_33 8 + +typedef struct { + float m[9]; +} MATRIX33; + +static inline MATRIX33 matrix33(float m11, float m12, float m13, + float m21, float m22, float m23, + float m31, float m32, float m33); +static inline void matrix33_set(MATRIX33 *m, + float m11, float m12, float m13, + float m21, float m22, float m23, + float m31, float m32, float m33); + +static inline MATRIX33 matrix33_from_euler_angles(float x, float y, float z); +static inline MATRIX33 matrix33_rotation_x(float radians); +static inline MATRIX33 matrix33_rotation_y(float radians); +static inline MATRIX33 matrix33_rotation_z(float radians); + +static inline MATRIX33 matrix33_add(MATRIX33 a, MATRIX33 b); +static inline MATRIX33 matrix33_sub(MATRIX33 a, MATRIX33 b); +static inline MATRIX33 matrix33_mul(MATRIX33 a, MATRIX33 b); +static inline MATRIX33 matrix33_scale(MATRIX33 m, float scale); + +static inline float matrix33_determinant(MATRIX33 m); +static inline MATRIX33 matrix33_inverse(MATRIX33 m); +static inline MATRIX33 matrix33_transpose(MATRIX33 m); +static inline VECTOR2F matrix33_transform(MATRIX33 m, VECTOR2F v); + +static inline MATRIX33 matrix33_translation_2d(float x, float y); +static inline MATRIX33 matrix33_scaling_2d(float x, float y); +static inline MATRIX33 matrix33_rotation_2d(float radians); +static inline VECTOR2F matrix33_transform_2d(MATRIX33 m, VECTOR2F v); + +#define IDENTITY_MATRIX33 matrix33(1.0f, 0.0f, 0.0f, \ + 0.0f, 1.0f, 0.0f, \ + 0.0f, 0.0f, 1.0f) + +// -------------------------------------------------------------------------- + +static inline MATRIX33 matrix33(float m11, float m12, float m13, + float m21, float m22, float m23, + float m31, float m32, float m33) { + MATRIX33 result; + result.m[_M33_11] = m11; + result.m[_M33_12] = m12; + result.m[_M33_13] = m13; + result.m[_M33_21] = m21; + result.m[_M33_22] = m22; + result.m[_M33_23] = m23; + result.m[_M33_31] = m31; + result.m[_M33_32] = m32; + result.m[_M33_33] = m33; + return result; +} + +static inline void matrix33_set(MATRIX33 *m, + float m11, float m12, float m13, + float m21, float m22, float m23, + float m31, float m32, float m33) { + m->m[_M33_11] = m11; + m->m[_M33_12] = m12; + m->m[_M33_13] = m13; + m->m[_M33_21] = m21; + m->m[_M33_22] = m22; + m->m[_M33_23] = m23; + m->m[_M33_31] = m31; + m->m[_M33_32] = m32; + m->m[_M33_33] = m33; +} + +static inline MATRIX33 matrix33_from_euler_angles(float x, float y, float z) { + MATRIX33 rx, ry, rz; + rx = matrix33_rotation_x(x); + ry = matrix33_rotation_y(y); + rz = matrix33_rotation_z(z); + return matrix33_mul(matrix33_mul(rz, ry), rx); +} + +static inline MATRIX33 matrix33_rotation_x(float radians) { + MATRIX33 result; + float s, c; + + s = sin(radians); + c = cos(radians); + + result.m[_M33_11] = 1.0f; + result.m[_M33_12] = 0.0f; + result.m[_M33_13] = 0.0f; + + result.m[_M33_21] = 0.0f; + result.m[_M33_22] = c; + result.m[_M33_23] = -s; + + result.m[_M33_31] = 0.0f; + result.m[_M33_32] = s; + result.m[_M33_33] = c; + + return result; +} + +static inline MATRIX33 matrix33_rotation_y(float radians) { + MATRIX33 result; + float s, c; + + s = sin(radians); + c = cos(radians); + + result.m[_M33_11] = c; + result.m[_M33_12] = 0.0f; + result.m[_M33_13] = s; + + result.m[_M33_21] = 0.0f; + result.m[_M33_22] = 1.0f; + result.m[_M33_23] = 0.0f; + + result.m[_M33_31] = -s; + result.m[_M33_32] = 0.0f; + result.m[_M33_33] = c; + + return result; +} + +static inline MATRIX33 matrix33_rotation_z(float radians) { + MATRIX33 result; + float s, c; + + s = sin(radians); + c = cos(radians); + + result.m[_M33_11] = c; + result.m[_M33_12] = -s; + result.m[_M33_13] = 0.0f; + + result.m[_M33_21] = s; + result.m[_M33_22] = c; + result.m[_M33_23] = 0.0f; + + result.m[_M33_31] = 0.0f; + result.m[_M33_32] = 0.0f; + result.m[_M33_33] = 1.0f; + + return result; +} + +static inline MATRIX33 matrix33_add(MATRIX33 a, MATRIX33 b) { + MATRIX33 result; + + result.m[_M33_11] = a.m[_M33_11] + b.m[_M33_11]; + result.m[_M33_12] = a.m[_M33_12] + b.m[_M33_12]; + result.m[_M33_13] = a.m[_M33_13] + b.m[_M33_13]; + result.m[_M33_21] = a.m[_M33_21] + b.m[_M33_21]; + result.m[_M33_22] = a.m[_M33_22] + b.m[_M33_22]; + result.m[_M33_23] = a.m[_M33_23] + b.m[_M33_23]; + result.m[_M33_31] = a.m[_M33_31] + b.m[_M33_31]; + result.m[_M33_32] = a.m[_M33_32] + b.m[_M33_32]; + result.m[_M33_33] = a.m[_M33_33] + b.m[_M33_33]; + + return result; +} + +static inline MATRIX33 matrix33_sub(MATRIX33 a, MATRIX33 b) { + MATRIX33 result; + + result.m[_M33_11] = a.m[_M33_11] - b.m[_M33_11]; + result.m[_M33_12] = a.m[_M33_12] - b.m[_M33_12]; + result.m[_M33_13] = a.m[_M33_13] - b.m[_M33_13]; + result.m[_M33_21] = a.m[_M33_21] - b.m[_M33_21]; + result.m[_M33_22] = a.m[_M33_22] - b.m[_M33_22]; + result.m[_M33_23] = a.m[_M33_23] - b.m[_M33_23]; + result.m[_M33_31] = a.m[_M33_31] - b.m[_M33_31]; + result.m[_M33_32] = a.m[_M33_32] - b.m[_M33_32]; + result.m[_M33_33] = a.m[_M33_33] - b.m[_M33_33]; + + return result; +} + +static inline MATRIX33 matrix33_mul(MATRIX33 a, MATRIX33 b) { + MATRIX33 result; + + result.m[_M33_11] = a.m[_M33_11] * b.m[_M33_11] + a.m[_M33_12] * b.m[_M33_21] + a.m[_M33_13] * b.m[_M33_31]; + result.m[_M33_12] = a.m[_M33_11] * b.m[_M33_12] + a.m[_M33_12] * b.m[_M33_22] + a.m[_M33_13] * b.m[_M33_32]; + result.m[_M33_13] = a.m[_M33_11] * b.m[_M33_13] + a.m[_M33_12] * b.m[_M33_23] + a.m[_M33_13] * b.m[_M33_33]; + + result.m[_M33_21] = a.m[_M33_21] * b.m[_M33_11] + a.m[_M33_22] * b.m[_M33_21] + a.m[_M33_23] * b.m[_M33_31]; + result.m[_M33_22] = a.m[_M33_21] * b.m[_M33_12] + a.m[_M33_22] * b.m[_M33_22] + a.m[_M33_23] * b.m[_M33_32]; + result.m[_M33_23] = a.m[_M33_21] * b.m[_M33_13] + a.m[_M33_22] * b.m[_M33_23] + a.m[_M33_23] * b.m[_M33_33]; + + result.m[_M33_31] = a.m[_M33_31] * b.m[_M33_11] + a.m[_M33_32] * b.m[_M33_21] + a.m[_M33_33] * b.m[_M33_31]; + result.m[_M33_32] = a.m[_M33_31] * b.m[_M33_12] + a.m[_M33_32] * b.m[_M33_22] + a.m[_M33_33] * b.m[_M33_32]; + result.m[_M33_33] = a.m[_M33_31] * b.m[_M33_13] + a.m[_M33_32] * b.m[_M33_23] + a.m[_M33_33] * b.m[_M33_33]; + + return result; +} + +static inline MATRIX33 matrix33_scale(MATRIX33 m, float scale) { + MATRIX33 result; + + result.m[_M33_11] = m.m[_M33_11] * scale; + result.m[_M33_12] = m.m[_M33_12] * scale; + result.m[_M33_13] = m.m[_M33_13] * scale; + result.m[_M33_21] = m.m[_M33_21] * scale; + result.m[_M33_22] = m.m[_M33_22] * scale; + result.m[_M33_23] = m.m[_M33_23] * scale; + result.m[_M33_31] = m.m[_M33_31] * scale; + result.m[_M33_32] = m.m[_M33_32] * scale; + result.m[_M33_33] = m.m[_M33_33] * scale; + + return result; +} + +static inline float matrix33_determinant(MATRIX33 m) { + return + m.m[_M33_11] * m.m[_M33_22] * m.m[_M33_33] + + m.m[_M33_12] * m.m[_M33_23] * m.m[_M33_31] + + m.m[_M33_13] * m.m[_M33_21] * m.m[_M33_32] - + m.m[_M33_11] * m.m[_M33_23] * m.m[_M33_32] - + m.m[_M33_12] * m.m[_M33_21] * m.m[_M33_33] - + m.m[_M33_13] * m.m[_M33_22] * m.m[_M33_31]; +} + +static inline MATRIX33 matrix33_inverse(MATRIX33 m) { + float d; + MATRIX33 result; + d = matrix33_determinant(m); + if (close_enough(d, 0.0f, TOLERANCE)) + return IDENTITY_MATRIX33; + else { + d = 1.0f / d; + + result.m[_M33_11] = d * (m.m[_M33_22] * m.m[_M33_33] - m.m[_M33_32] * m.m[_M33_23]); + result.m[_M33_21] = d * (m.m[_M33_31] * m.m[_M33_23] - m.m[_M33_21] * m.m[_M33_33]); + result.m[_M33_31] = d * (m.m[_M33_21] * m.m[_M33_32] - m.m[_M33_31] * m.m[_M33_22]); + result.m[_M33_21] = d * (m.m[_M33_32] * m.m[_M33_13] - m.m[_M33_12] * m.m[_M33_33]); + result.m[_M33_22] = d * (m.m[_M33_11] * m.m[_M33_33] - m.m[_M33_31] * m.m[_M33_13]); + result.m[_M33_23] = d * (m.m[_M33_31] * m.m[_M33_12] - m.m[_M33_11] * m.m[_M33_32]); + result.m[_M33_31] = d * (m.m[_M33_12] * m.m[_M33_23] - m.m[_M33_22] * m.m[_M33_13]); + result.m[_M33_32] = d * (m.m[_M33_21] * m.m[_M33_13] - m.m[_M33_11] * m.m[_M33_23]); + result.m[_M33_33] = d * (m.m[_M33_11] * m.m[_M33_22] - m.m[_M33_21] * m.m[_M33_12]); + + return result; + } +} + +static inline MATRIX33 matrix33_transpose(MATRIX33 m) { + MATRIX33 result; + + result.m[_M33_11] = m.m[_M33_11]; + result.m[_M33_12] = m.m[_M33_21]; + result.m[_M33_13] = m.m[_M33_31]; + + result.m[_M33_21] = m.m[_M33_12]; + result.m[_M33_22] = m.m[_M33_22]; + result.m[_M33_23] = m.m[_M33_32]; + + result.m[_M33_31] = m.m[_M33_13]; + result.m[_M33_32] = m.m[_M33_23]; + result.m[_M33_33] = m.m[_M33_33]; + + return result; +} + +static inline VECTOR2F matrix33_transform(MATRIX33 m, VECTOR2F v) { + VECTOR2F result; + + result.x = v.x * m.m[_M33_11] + v.y * m.m[_M33_12] + m.m[_M33_13]; + result.y = v.x * m.m[_M33_21] + v.y * m.m[_M33_22] + m.m[_M33_23]; + + return result; +} + +static inline MATRIX33 matrix33_translation_2d(float x, float y) { + MATRIX33 result; + + result.m[_M33_11] = 1.0f; + result.m[_M33_12] = 0.0f; + result.m[_M33_13] = 0.0f; + + result.m[_M33_21] = 0.0f; + result.m[_M33_22] = 1.0f; + result.m[_M33_23] = 0.0f; + + result.m[_M33_31] = x; + result.m[_M33_32] = y; + result.m[_M33_33] = 1.0f; + + return result; +} + +static inline MATRIX33 matrix33_scaling_2d(float x, float y) { + MATRIX33 result; + + result.m[_M33_11] = x; + result.m[_M33_12] = 0.0f; + result.m[_M33_13] = 0.0f; + + result.m[_M33_21] = 0.0f; + result.m[_M33_22] = y; + result.m[_M33_23] = 0.0f; + + result.m[_M33_31] = 0.0f; + result.m[_M33_32] = 0.0f; + result.m[_M33_33] = 1.0f; + + return result; +} + +static inline MATRIX33 matrix33_rotation_2d(float radians) { + return matrix33_rotation_z(radians); +} + +static inline VECTOR2F matrix33_transform_2d(MATRIX33 m, VECTOR2F v) { + VECTOR2F result; + + result.x = v.x * m.m[_M33_11] + v.y * m.m[_M33_12] + m.m[_M33_13]; + result.y = v.x * m.m[_M33_21] + v.y * m.m[_M33_22] + m.m[_M33_23]; + result.x += m.m[_M33_31]; + result.y += m.m[_M33_32]; + + return result; +} + +#endif + diff --git a/MOUSE.C b/MOUSE.C new file mode 100644 index 0000000..266e2d3 --- /dev/null +++ b/MOUSE.C @@ -0,0 +1,188 @@ +#include "mouse.h" +#include "internal.h" +#include "error.h" +#include +#include + +static boolean _installed = FALSE; +static boolean _has_mouse = FALSE; + +volatile int mouse_x; +volatile int mouse_y; +volatile int mouse_buttons; +volatile int mouse_delta_x; +volatile int mouse_delta_y; + +__dpmi_regs _mouse_regs; +_go32_dpmi_seginfo _mouse_seg_info; + +static void reset_mouse_state(void) { + mouse_x = 0; + mouse_y = 0; + mouse_buttons = 0; + mouse_delta_x = 0; + mouse_delta_y = 0; +} + +static boolean init_mouse_driver(void) { + __dpmi_regs regs; + + memset(®s, 0, sizeof(__dpmi_regs)); + regs.x.ax = 0x00; + __dpmi_int(0x33, ®s); + + return (regs.x.ax != 0); +} + +static void update_mouse_state(void) { + __dpmi_regs regs; + + memset(®s, 0, sizeof(__dpmi_regs)); + regs.x.ax = 0x03; + __dpmi_int(0x33, ®s); + mouse_x = (regs.x.cx / 2); + mouse_y = regs.x.dx; + mouse_buttons = regs.x.bx; + mouse_delta_x = 0; + mouse_delta_y = 0; +} + +void mouse_int_handler(__dpmi_regs* regs) { + mouse_delta_x = (regs->x.cx / 2) - mouse_x; + mouse_delta_y = regs->x.dx - mouse_y; + mouse_x = (regs->x.cx / 2); + mouse_y = regs->x.dx; + mouse_buttons = regs->x.bx; +} +END_OF_FUNCTION(mouse_int_handler) + +boolean mouse_init(void) { + __dpmi_regs regs; + + if (_installed) { + dgl_set_error(DGL_MOUSE_ALREADY_INITIALIZED); + return FALSE; + } + + reset_mouse_state(); + + _has_mouse = init_mouse_driver(); + if (!_has_mouse) { + _installed = TRUE; + return TRUE; + } + + LOCK_FUNCTION(mouse_int_handler); + LOCK_VARIABLE(_mouse_regs); + LOCK_VARIABLE(_mouse_seg_info); + LOCK_VARIABLE(mouse_x); + LOCK_VARIABLE(mouse_y); + LOCK_VARIABLE(mouse_buttons); + LOCK_VARIABLE(mouse_delta_x); + LOCK_VARIABLE(mouse_delta_y); + + memset(&_mouse_regs, 0, sizeof(__dpmi_regs)); + memset(&_mouse_seg_info, 0, sizeof(_go32_dpmi_seginfo)); + + _mouse_seg_info.pm_offset = (int)mouse_int_handler; + _mouse_seg_info.pm_selector = _go32_my_cs(); + if (_go32_dpmi_allocate_real_mode_callback_retf(&_mouse_seg_info, &_mouse_regs) != 0) { + dgl_set_error(DGL_MOUSE_ALLOCATE_CALLBACK_FAILURE); + return FALSE; + } + + update_mouse_state(); + + memset(®s, 0, sizeof(__dpmi_regs)); + regs.x.ax = 0x0c; + regs.x.cx = 0x7f; + regs.x.dx = _mouse_seg_info.rm_offset; + regs.x.es = _mouse_seg_info.rm_segment; + if (__dpmi_int(0x33, ®s) != 0) { + dgl_set_error(DGL_MOUSE_INT_CALLBACK_SET_FAILURE); + return FALSE; + } + + _installed = TRUE; + return TRUE; +} + +boolean mouse_shutdown(void) { + __dpmi_regs regs; + + if (!_installed) + return TRUE; // don't care + + if (!_has_mouse) { + _installed = FALSE; + return TRUE; + } + + memset(®s, 0, sizeof(__dpmi_regs)); + regs.x.ax = 0x0c; + regs.x.cx = 0; + regs.x.dx = 0; + regs.x.es = 0; + if (__dpmi_int(0x33, ®s) != 0) { + dgl_set_error(DGL_MOUSE_INT_CALLBACK_RESTORE_FAILURE); + return FALSE; + } + + if (_go32_dpmi_free_real_mode_callback(&_mouse_seg_info) != 0) + dgl_set_error(DGL_MOUSE_FREE_CALLBACK_FAILURE); + + reset_mouse_state(); + + _installed = FALSE; + return TRUE; +} + +boolean mouse_is_initialized(void) { + return _installed; +} + +boolean mouse_is_present(void) { + return _has_mouse; +} + +void mouse_show(void) { + __dpmi_regs regs; + + if (!_has_mouse) + return; + + memset(®s, 0, sizeof(__dpmi_regs)); + regs.x.ax = 0x01; + __dpmi_int(0x33, ®s); +} + +void mouse_hide(void) { + __dpmi_regs regs; + + if (!_has_mouse) + return; + + memset(®s, 0, sizeof(__dpmi_regs)); + regs.x.ax = 0x02; + __dpmi_int(0x33, ®s); +} + +void mouse_set_bounds(int min_x, int min_y, int max_x, int max_y) { + __dpmi_regs regs; + + if (!_has_mouse) + return; + + memset(®s, 0, sizeof(__dpmi_regs)); + + regs.x.ax = 0x07; + regs.x.cx = min_x; + regs.x.dx = max_x; + __dpmi_int(0x33, ®s); + + regs.x.ax = 0x08; + regs.x.cx = min_y; + regs.x.dx = max_y; + __dpmi_int(0x33, ®s); +} + diff --git a/MOUSE.H b/MOUSE.H new file mode 100644 index 0000000..dd40536 --- /dev/null +++ b/MOUSE.H @@ -0,0 +1,92 @@ +#ifndef DGL_MOUSE_H_INCLUDED +#define DGL_MOUSE_H_INCLUDED + +#include "common.h" + +#define MOUSE_LEFTBUTTON 0x01 +#define MOUSE_RIGHTBUTTON 0x02 +#define MOUSE_CENTERBUTTON 0x04 + +/* + * Current mouse cursor X position. + */ +volatile extern int mouse_x; + +/* + * Current mouse cursor Y position. + */ +volatile extern int mouse_y; + +/* + * Current state of mouse buttons. + */ +volatile extern int mouse_buttons; + +/* + * Amount the cursor moved along the X-axis since the last update. + */ +volatile extern int mouse_delta_x; + +/* + * Amount the cursor moved along the Y-axis since the last update. + */ +volatile extern int mouse_delta_y; + +/* + * Installs a custom mouse handler. + * @return TRUE on success + */ +boolean mouse_init(void); + +/* + * Removes a previously installed mouse handler. + * @return TRUE on success + */ +boolean mouse_shutdown(void); + +/* + * @return TRUE if the custom mouse handler is installed. + */ +boolean mouse_is_initialized(void); + +/* + * @return TRUE if the user's computer has a (recognized) mouse connected. + */ +boolean mouse_is_present(void); + +/* + * Shows the mouse cursor. If the mouse cursor is currently shown, this does + * nothing. + */ +void mouse_show(void); + +/* + * Hides the mouse cursor. If the mouse cursor is not currently shown, this + * does nothing. + */ +void mouse_hide(void); + +/* + * Sets the pixel boundaries for the mouse cursor. + * @param min_x left coordinate + * @param max_x right coordinate + * @param min_y top coordinate + * @param max_y bottom coordinate + */ +void mouse_set_bounds(int min_x, int min_y, int max_x, int max_y); + +/* + * Returns the current status of the specified button. + * @param button The button to check the status of. + * @return TRUE if the button is pressed. + */ +static inline boolean mouse_button(int button); + +// -------------------------------------------------------------------------- + +static inline boolean mouse_button(int button) { + return (mouse_buttons & button) != 0; +} + +#endif + diff --git a/PCX.C b/PCX.C new file mode 100644 index 0000000..b46ff7e --- /dev/null +++ b/PCX.C @@ -0,0 +1,223 @@ +#include "pcx.h" +#include "gfx.h" +#include "draw.h" +#include "error.h" +#include + +typedef struct { + byte manufacturer; + byte version; + byte encoding; + byte bpp; + word x; + word y; + word width; + word height; + word horizontal_dpi; + word vertical_dpi; + byte ega_palette[48]; + byte reserved; + byte num_color_planes; + word bytes_per_line; + word palette_type; + word horizontal_size; + word vertical_size; + byte padding[54]; +} PCX_HEADER; + +SURFACE* pcx_load(const char *filename, byte *pcx_palette) { + FILE *fp; + PCX_HEADER header; + int i, n, count; + SURFACE *pcx; + ubyte data; + + fp = fopen(filename, "rb"); + if (!fp) { + dgl_set_error(DGL_IO_ERROR); + return NULL; + } + + n = fread(&header, sizeof(PCX_HEADER), 1, fp); + if (n == -1) { + dgl_set_error(DGL_IO_ERROR); + fclose(fp); + return NULL; + } + + if (header.manufacturer != 10 || + header.version != 5 || + header.encoding != 1 || + header.bpp != 8) { + dgl_set_error(DGL_PCX_BAD_FORMAT); + fclose(fp); + return NULL; + } + + pcx = surface_create(header.width + 1, header.height + 1); + + // read pixel data + i = 0; + while (i < (header.width * header.height)) { + n = fread(&data, 1, 1, fp); + if (n == -1) + goto pcx_load_error; + + if (data >= 192) { + count = data - 192; + n = fread(&data, 1, 1, fp); + if (n == -1) + goto pcx_load_error; + + while (count > 0) { + pcx->pixels[i++] = data; + count--; + } + } else { + pcx->pixels[i++] = data; + } + } + + // read palette (only if needed) + if (pcx_palette) { + fseek(fp, -768, SEEK_END); + + n = fread(pcx_palette, 768, 1, fp); + if (n == -1) + goto pcx_load_error; + + for (i = 0; i < 768; ++i) + pcx_palette[i] >>= 2; + } + + fclose(fp); + return pcx; + +pcx_load_error: + dgl_set_error(DGL_PCX_BAD_FORMAT); + surface_free(pcx); + fclose(fp); + return NULL; +} + +static inline boolean write_pcx_data(FILE *fp, int run_count, byte pixel) { + int n; + + if ((run_count > 1) || ((pixel & 0xc0) == 0xc0)) { + n = fputc(0xc0 | run_count, fp); + if (n == -1) + return FALSE; + } + n = fputc(pixel, fp); + if (n == -1) + return FALSE; + + return TRUE; +} + +boolean pcx_save(const char *filename, const SURFACE *src, const byte *palette) { + FILE *fp; + int i, n, x, y; + int run_count; + byte pixel, run_pixel; + byte r, g, b; + boolean result; + PCX_HEADER header; + + fp = fopen(filename, "wb"); + if (!fp) { + dgl_set_error(DGL_IO_ERROR); + return FALSE; + } + + memset(&header, 0, sizeof(PCX_HEADER)); + header.manufacturer = 10; + header.version = 5; + header.encoding = 1; + header.bpp = 8; + header.x = 0; + header.y = 0; + header.width = src->width - 1; + header.height = src->height - 1; + header.horizontal_dpi = 0; + header.vertical_dpi = 0; + header.num_color_planes = 1; + header.bytes_per_line = src->width; + header.palette_type = 1; + header.horizontal_size = 320; + header.vertical_size = 200; + + n = fwrite(&header, sizeof(PCX_HEADER), 1, fp); + if (n == -1) + goto pcx_save_error; + + // write image data + i = 0; + for (y = 0; y < src->height; ++y) { + // write one scanline at a time (breaking runs that could have + // continue across scanlines in the process... as per pcx standard) + run_count = 0; + run_pixel = 0; + for (x = 0; x < src->width; ++x) { + pixel = src->pixels[i]; + ++i; + + if (run_count == 0) { + run_count = 1; + run_pixel = pixel; + + } else { + if ((pixel != run_pixel) || (run_count >= 63)) { + result = write_pcx_data(fp, run_count, run_pixel); + if (!result) + goto pcx_save_error; + + run_count = 1; + run_pixel = pixel; + + } else { + ++run_count; + } + } + } + + // end the scanline, writing out whatever run we might have had going + result = write_pcx_data(fp, run_count, run_pixel); + if (!result) + goto pcx_save_error; + } + + fputc(12, fp); + + // use provided palette, otherwise use current vga palette + if (palette) { + for (i = 0; i < 768; ++i) { + n = fputc(palette[i] << 2, fp); + if (n == -1) + goto pcx_save_error; + } + } else { + for (i = 0; i < 256; ++i) { + video_get_color(i, &r, &g, &b); + + n = fputc(r << 2, fp); + if (n == -1) + goto pcx_save_error; + n = fputc(g << 2, fp); + if (n == -1) + goto pcx_save_error; + n = fputc(b << 2, fp); + if (n == -1) + goto pcx_save_error; + } + } + + fclose(fp); + return TRUE; + +pcx_save_error: + dgl_set_error(DGL_IO_ERROR); + fclose(fp); + return FALSE; +} + diff --git a/PCX.H b/PCX.H new file mode 100644 index 0000000..2b7dc1f --- /dev/null +++ b/PCX.H @@ -0,0 +1,11 @@ +#ifndef DGL_PCX_H_INCLUDED +#define DGL_PCX_H_INCLUDED + +#include "common.h" +#include "gfx.h" + +SURFACE* pcx_load(const char *filename, byte *pcx_palette); +boolean pcx_save(const char *filename, const SURFACE *src, const byte *palette); + +#endif + diff --git a/README.md b/README.md new file mode 100644 index 0000000..4686d3f --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# libDGL + +libDGL is a "retro coding" C library aimed at MS-DOS [VGA Mode 13h](https://en.wikipedia.org/wiki/Mode_13h) game development. It is developed with DJGPP with 486 DX2 systems as a baseline target for performance. + +### Why? + +Because it's fun. + +If you were looking for something for "retro" MS-DOS game development that is fully featured, really performant and bug-tested that's ready right now, have a look at Allegro 3.x or possibly 4.x depending on what hardware you're targetting. + +### Status + +This library is currently under early development and is lacking many things and probably chock-full of bugs. + +**Done / Mostly Done** + +* Primitive drawing support +* Text rendering +* Blitting +* Graphics clipping +* Keyboard input +* Mouse input +* PCX loading/saving +* Math library (general helpers, 2D vector, 3x3 matrix) +* Basic error handling / results + +**TODO** + +* Scaled/rotated blitting support +* Blending +* "Mode 7" like support +* Custom font loading (BIOS-like format?) +* Joystick / Gravis GamePad support +* PC speaker sounds +* Sound Blaster compatible sound/music +* Gravis Ultrasound compatible sound/music +* Fixed point math +* Sine/cosine lookup table optimizations +* BMP, LBM, GIF image loading (and saving?) + +### DJGPP Toolchain + +Along the same theme as this library itself, I am using an older version of DJGPP from the late 90's. I have not tested with any other versions of the DJGPP toolchain other than: + +``` +15 Jan 1998 bnu281b.zip +24 Oct 1997 csdpmi4b.zip +31 Oct 1996 djdev201.zip +18 Jan 1997 faq210b.zip + 6 Jun 1998 gcc281b.zip +18 Oct 1996 gdb416b.zip + 6 Jun 1998 gpp281b.zip + 1 Mar 1998 mak3761b.zip +30 Sep 1997 rhide14b.zip +``` + +This is a pretty arbitrary selection that was based on a cut-off of about 1998 (which was when I first came across DJGPP when I was younger). + diff --git a/RECT.H b/RECT.H new file mode 100644 index 0000000..61c807c --- /dev/null +++ b/RECT.H @@ -0,0 +1,41 @@ +#ifndef DGL_RECT_H_INCLUDED +#define DGL_RECT_H_INCLUDED + +typedef struct { + int x; + int y; + int width; + int height; +} RECT; + +static inline RECT rect(int x, int y, int width, int height); +static inline int rect_right(const RECT *r); +static inline int rect_bottom(const RECT *r); + +// -------------------------------------------------------------------------- + +static inline RECT rect(int x, int y, int width, int height) { + RECT result; + result.x = x; + result.y = y; + result.width = width; + result.height = height; + return result; +} + +static inline int rect_right(const RECT *r) { + if (r->width) + return r->x + r->width - 1; + else + return r->x; +} + +static inline int rect_bottom(const RECT *r) { + if (r->height) + return r->y + r->height - 1; + else + return r->y; +} + +#endif + diff --git a/TEST.C b/TEST.C new file mode 100644 index 0000000..1e01ac6 --- /dev/null +++ b/TEST.C @@ -0,0 +1,35 @@ +#include +#include +#include "dgl.h" + +int main(void) { + SURFACE *backbuffer; + + if (!dgl_init()) { + printf("Failed to initialize DGL: %s\n", dgl_last_error_message()); + return 1; + } + + backbuffer = surface_create(320, 200); + + + while (!keys[KEY_ESC]) { + surface_clear(backbuffer, 0); + + surface_text(backbuffer, 100, 100, 15, "Hello, world!"); + + + video_wait_vsync(); + surface_copy(backbuffer, screen); + } + + surface_free(backbuffer); + + if (!dgl_shutdown()) { + printf("Failed to close DGL: %s\n", dgl_last_error_message()); + return 1; + } + + return 0; +} + diff --git a/UTIL.C b/UTIL.C new file mode 100644 index 0000000..6a776e5 --- /dev/null +++ b/UTIL.C @@ -0,0 +1,37 @@ +#include "util.h" +#include +#include +#include + +#define SYS_CLOCKS_PER_SEC (1000.0f / 55.0f) + +void* map_dos_memory(long physical_addr) { + return (void*)(__djgpp_conventional_base + physical_addr); +} + +int sys_clock() { + //return *((int*)(map_dos_memory(0x046c))); + return (int)clock(); +} + +int sys_ticks() { + return (int)uclock(); +} + +float clock_ticks_to_seconds(int clocks) { + //return clocks / SYS_CLOCKS_PER_SEC; + return clocks / (float)CLOCKS_PER_SEC; +} + +float ticks_to_seconds(int ticks) { + return (float)ticks / UCLOCKS_PER_SEC; +} + +int rnd_int(int low, int high) { + return rand() % ((high - low) + 1) + low; +} + +float rnd_float(float low, float high) { + return low + (rand() / (float)RAND_MAX) * (high - low); +} + diff --git a/UTIL.H b/UTIL.H new file mode 100644 index 0000000..2b1cf12 --- /dev/null +++ b/UTIL.H @@ -0,0 +1,26 @@ +#ifndef DGL_UTIL_H_INCLUDED +#define DGL_UTIL_H_INCLUDED + +#include "common.h" + +#define SWAP(type, a, b) \ + do { \ + type __tmp = a; \ + a = b; \ + b = __tmp; \ + } while (0) + +#define SIGN(x) (((x) < 0) ? -1 : (((x) > 0) ? 1 : 0)) + +void* map_dos_memory(long physical_addr); + +int sys_clock(); +int sys_ticks(); +float clock_ticks_to_seconds(int clocks); +float ticks_to_seconds(int ticks); + +int rnd_int(int low, int high); +float rnd_float(float low, float high); + +#endif + diff --git a/VECTOR2.H b/VECTOR2.H new file mode 100644 index 0000000..ab5f95d --- /dev/null +++ b/VECTOR2.H @@ -0,0 +1,284 @@ +#ifndef DGL_VECTOR2_H_INCLUDED +#define DGL_VECTOR2_H_INCLUDED + +#include +#include "common.h" + +typedef struct { + int x; + int y; +} VECTOR2I; + +static inline VECTOR2I vector2i(int x, int y); +static inline void vector2i_set(VECTOR2I *v, int x, int y); +static inline boolean vector2i_equals(VECTOR2I a, VECTOR2I b); +static inline VECTOR2I vector2i_add(VECTOR2I a, VECTOR2I b); +static inline VECTOR2I vector2i_sub(VECTOR2I a, VECTOR2I b); +static inline VECTOR2I vector2i_mul(VECTOR2I a, VECTOR2I b); +static inline VECTOR2I vector2i_muls(VECTOR2I v, int n); +static inline VECTOR2I vector2i_div(VECTOR2I a, VECTOR2I b); +static inline VECTOR2I vector2i_divs(VECTOR2I v, int n); +static inline int vector2i_distance(VECTOR2I a, VECTOR2I b); +static inline int vector2i_distancesq(VECTOR2I a, VECTOR2I b); +static inline int vector2i_dot(VECTOR2I a, VECTOR2I b); +static inline int vector2i_length(VECTOR2I v); +static inline int vector2i_lengthsq(VECTOR2I v); +static inline VECTOR2I vector2i_lerp(VECTOR2I a, VECTOR2I b, float lerp); + +#define ZERO_VECTOR2I vector2i(0, 0) +#define UP_VECTOR2I vector2i(0, -1); +#define DOWN_VECTOR2I vector2i(0, 1); +#define LEFT_VECTOR2I vector2i(-1, 0); +#define RIGHT_VECTOR2I vector2i(1, 0); +#define UNIT_X_VECTOR2I vector2i(1, 0); +#define UNIT_Y_VECTOR2I vector2i(0, 1); + +typedef struct { + float x; + float y; +} VECTOR2F; + +static inline VECTOR2F vector2f(float x, float y); +static inline void vector2f_set(VECTOR2F *v, float x, float y); +static inline boolean vector2f_equals(VECTOR2F a, VECTOR2F b); +static inline VECTOR2F vector2f_add(VECTOR2F a, VECTOR2F b); +static inline VECTOR2F vector2f_sub(VECTOR2F a, VECTOR2F b); +static inline VECTOR2F vector2f_mul(VECTOR2F a, VECTOR2F b); +static inline VECTOR2F vector2f_muls(VECTOR2F v, float n); +static inline VECTOR2F vector2f_div(VECTOR2F a, VECTOR2F b); +static inline VECTOR2F vector2f_divs(VECTOR2F v, float n); +static inline float vector2f_distance(VECTOR2F a, VECTOR2F b); +static inline float vector2f_distancesq(VECTOR2F a, VECTOR2F b); +static inline float vector2f_dot(VECTOR2F a, VECTOR2F b); +static inline float vector2f_length(VECTOR2F v); +static inline float vector2f_lengthsq(VECTOR2F v); +static inline VECTOR2F vector2f_normalize(VECTOR2F v); +static inline VECTOR2F vector2f_set_length(VECTOR2F v, float length); +static inline VECTOR2F vector2f_lerp(VECTOR2F a, VECTOR2F b, float lerp); + +#define ZERO_VECTOR2F vector2f(0.0f, 0.0f) +#define UP_VECTOR2F vector2f(0.0f, -1.0f); +#define DOWN_VECTOR2F vector2f(0.0f, 1.0f); +#define LEFT_VECTOR2F vector2f(-1.0f, 0.0f); +#define RIGHT_VECTOR2F vector2f(1.0f, 0.0f); +#define UNIT_X_VECTOR2F vector2f(1.0f, 0.0f); +#define UNIT_Y_VECTOR2F vector2f(0.0f, 1.0f); + +// -------------------------------------------------------------------------- + +static inline VECTOR2I vector2i(int x, int y) { + VECTOR2I v; + v.x = x; + v.y = y; + return v; +} + +static inline VECTOR2F vector2f(float x, float y) { + VECTOR2F v; + v.x = x; + v.y = y; + return v; +} + +static inline void vector2i_set(VECTOR2I *v, int x, int y) { + v->x = x; + v->y = y; +} + +static inline void vector2f_set(VECTOR2F *v, float x, float y) { + v->x = x; + v->y = y; +} + +static inline boolean vector2i_equals(VECTOR2I a, VECTOR2I b) { + return (a.x == b.x && a.y == b.y); +} + +static inline boolean vector2f_equals(VECTOR2F a, VECTOR2F b) { + return (a.x == b.x && a.y == b.y); +} + +static inline VECTOR2I vector2i_add(VECTOR2I a, VECTOR2I b) { + VECTOR2I result; + result.x = a.x + b.x; + result.y = a.y + b.y; + return result; +} + +static inline VECTOR2F vector2f_add(VECTOR2F a, VECTOR2F b) { + VECTOR2F result; + result.x = a.x + b.x; + result.y = a.y + b.y; + return result; +} + +static inline VECTOR2I vector2i_sub(VECTOR2I a, VECTOR2I b) { + VECTOR2I result; + result.x = a.x - b.x; + result.y = a.y - b.y; + return result; +} + +static inline VECTOR2F vector2f_sub(VECTOR2F a, VECTOR2F b) { + VECTOR2F result; + result.x = a.x - b.x; + result.y = a.y - b.y; + return result; +} + +static inline VECTOR2I vector2i_mul(VECTOR2I a, VECTOR2I b) { + VECTOR2I result; + result.x = a.x * b.x; + result.y = a.y * b.y; + return result; +} + +static inline VECTOR2F vector2f_mul(VECTOR2F a, VECTOR2F b) { + VECTOR2F result; + result.x = a.x * b.x; + result.y = a.y * b.y; + return result; +} + +static inline VECTOR2I vector2i_muls(VECTOR2I v, int n) { + VECTOR2I result; + result.x = v.x * n; + result.y = v.y * n; + return result; +} + +static inline VECTOR2F vector2f_muls(VECTOR2F v, float n) { + VECTOR2F result; + result.x = v.x * n; + result.y = v.y * n; + return result; +} + +static inline VECTOR2I vector2i_div(VECTOR2I a, VECTOR2I b) { + VECTOR2I result; + result.x = a.x / b.x; + result.y = a.y / b.y; + return result; +} + +static inline VECTOR2F vector2f_div(VECTOR2F a, VECTOR2F b) { + VECTOR2F result; + result.x = a.x / b.x; + result.y = a.y / b.y; + return result; +} + +static inline VECTOR2I vector2i_divs(VECTOR2I v, int n) { + VECTOR2I result; + result.x = v.x / n; + result.y = v.y / n; + return result; +} + +static inline VECTOR2F vector2f_divs(VECTOR2F v, float n) { + VECTOR2F result; + result.x = v.x / n; + result.y = v.y / n; + return result; +} + +static inline int vector2i_distance(VECTOR2I a, VECTOR2I b) { + return (int)sqrt( + ((b.x - a.x) * (b.x - a.x)) + + ((b.y - a.y) * (b.y - a.y)) + ); +} + +static inline float vector2f_distance(VECTOR2F a, VECTOR2F b) { + return (float)sqrt( + ((b.x - a.x) * (b.x - a.x)) + + ((b.y - a.y) * (b.y - a.y)) + ); +} + +static inline int vector2i_distancesq(VECTOR2I a, VECTOR2I b) { + return + ((b.x - a.x) * (b.x - a.x)) + + ((b.y - a.y) * (b.y - a.y)); +} + +static inline float vector2f_distancesq(VECTOR2F a, VECTOR2F b) { + return + ((b.x - a.x) * (b.x - a.x)) + + ((b.y - a.y) * (b.y - a.y)); +} + +static inline int vector2i_dot(VECTOR2I a, VECTOR2I b) { + return + (a.x * b.x) + + (a.y * b.y); +} + +static inline float vector2f_dot(VECTOR2F a, VECTOR2F b) { + return + (a.x * b.x) + + (a.y * b.y); +} + +static inline int vector2i_length(VECTOR2I v) { + return sqrt( + (v.x * v.x) + + (v.y * v.y) + ); +} + +static inline float vector2f_length(VECTOR2F v) { + return (float)sqrt( + (v.x * v.x) + + (v.y * v.y) + ); +} + +static inline int vector2i_lengthsq(VECTOR2I v) { + return + (v.x * v.x) + + (v.y * v.y); +} + +static inline float vector2f_lengthsq(VECTOR2F v) { + return + (v.x * v.x) + + (v.y * v.y); +} + +static inline VECTOR2F vector2f_normalize(VECTOR2F v) { + float inverse_length; + VECTOR2F result; + + inverse_length = 1.0f / vector2f_length(v); + result.x = v.x * inverse_length; + result.y = v.y * inverse_length; + return result; +} + +static inline VECTOR2F vector2f_set_length(VECTOR2F v, float length) { + float scale_factor; + VECTOR2F result; + + scale_factor = length / vector2f_length(v); + result.x = v.x * scale_factor; + result.y = v.y * scale_factor; + return result; +} + +static inline VECTOR2I vector2i_lerp(VECTOR2I a, VECTOR2I b, float lerp) { + VECTOR2I result; + result.x = a.x + (b.x - a.x) * lerp; + result.y = a.y + (b.y - a.y) * lerp; + return result; +} + +static inline VECTOR2F vector2f_lerp(VECTOR2F a, VECTOR2F b, float lerp) { + VECTOR2F result; + result.x = a.x + (b.x - a.x) * lerp; + result.y = a.y + (b.y - a.y) * lerp; + return result; +} + +#endif +