/*
* This file is part of the uHex project.
* Copyright (C) 2013, 2014, 2015 Mateusz Viste
*
* This is the (original) I/O driver for DOS in real mode.
* Supported compilers: Borland Turbo C, Open Watcom C.
* For non-DOS support, see the io-curse.c and io-win32 files.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*/
#include <dos.h> /* provides int86() along with the union REGS type */
#include <stdio.h> /* this one contains the NULL definition */
#include "io.h" /* include self headers for control */
/* a few global variables used to maintain some stateful data in memory */
int term_width = 0, term_height = 0;
int savedattr = 0; /* used to restore the same video mode before quitting */
int cursor_start = 0, cursor_end = 0; /* remember the cursor's shape */
int cursor_x = 0, cursor_y = 0; /* current cursor coordinates (cached) */
unsigned char far *vmem; /* video memory pointer (beginning of page 0) */
/* looks for an installed EGA card (or compatible). returns 0 if no EGA found,
* non-zero otherwise. */
static int detect_ega(void) {
union REGS regs;
regs.h.ah = 0x12;
regs.h.bl = 0x10;
/* check for the presence of an EGA by executing an interrupt 10h with AH
* set to 12h and BL set to 10h */
int86(0x10, ®s, ®s);
/* if BL is still equal to 10h on return, then assume there is no EGA */
if (regs.h.bl == 0x10) return(0);
return(1);
}
/* get current attribute value under cursor and returns it */
static int getcurattr(void) {
union REGS regs;
regs.h.ah = 0x08; /* Read character and attribute at cursor position */
regs.h.bh = 0; /* display page */
int86(0x10, ®s, ®s);
return(regs.h.ah);
}
static void cursor_set(int startscanline, int endscanline) {
union REGS regs;
regs.h.ah = 0x01;
regs.h.ch = startscanline;
regs.h.cl = endscanline;
int86(0x10, ®s, ®s);
}
/* gets cursor's position and size */
static void cursor_getprops(int *x, int *y, int *start, int *end) {
union REGS regs;
regs.h.ah = 3; /* get cursor position and shape */
regs.h.bh = 0; /* page number (pretty much always 0) */
int86(0x10, ®s, ®s);
if (x != NULL) *x = regs.h.dl; /* column */
if (y != NULL) *y = regs.h.dh; /* row */
if (start != NULL) *start = regs.h.ch; /* start scan line */
if (end != NULL) *end = regs.h.cl; /* end scan line */
}
/* inits the IO subsystem */
void io_init(void) {
int color_flag;
savedattr = getcurattr(); /* there we save current color attributes for later restoration */
getcurvideomode(&term_width, &term_height, &color_flag);
cursor_getprops(&cursor_x, &cursor_y, &cursor_start, &cursor_end); /* save the current cursor's shape */
}
void io_close(void) {
cls(savedattr); /* clears the screen and restore the original colors of the shell */
}
void cursor_hide(void) {
cursor_set(0x0F, 0x0E); /* hide the cursor */
}
void cursor_show(void) {
cursor_set(cursor_start, cursor_end); /* unhide the cursor */
}
void locate(int row, int column) {
union REGS regs;
/* cache new cursor coordinates*/
cursor_y = row;
cursor_x = column;
/* and set them */
regs.h.ah = 0x02;
regs.h.bh = 0;
regs.h.dh = row;
regs.h.dl = column;
int86(0x10, ®s, ®s);
}
void printchar(char c, int attr) {
unsigned char far *p;
p = vmem + ((cursor_y * term_width + cursor_x) << 1);
*p++ = c;
*p = attr;
}
void printattrstringyx(int y, int x, char *sym, int *attr, int len) {
unsigned char far *p;
p = vmem + ((y * term_width + x) << 1);
while (len-- > 0) {
*p++ = *sym++;
*p++ = (unsigned char) *attr++;
}
}
/* waits for a keypress and return it. Returns 0 for extended keystroke, then
function must be called again to return scan code. */
int getkey(void) {
union REGS regs;
regs.h.ah = 0x08;
int86(0x21, ®s, ®s);
return(regs.h.al);
}
void cls(int colattr) {
int termwidth, termheight, colorflag;
union REGS regs;
getcurvideomode(&termwidth, &termheight, &colorflag);
regs.x.ax = 0x0600; /* Scroll window up - entire window */
regs.x.bx = (colattr << 8); /* Attribute to write */
regs.x.cx = 0x0000; /* Upper left */
regs.x.dx = ((termheight - 1) << 8) | (termwidth - 1); /* Lower right */
int86(0x10, ®s, ®s);
locate(0, 0);
}
void getcurvideomode(int *termwidth, int *termheight, int *colorflag) {
union REGS regs;
regs.h.ah = 0x0F; /* get current video mode */
int86(0x10, ®s, ®s);
*termwidth = regs.h.ah; /* int10,F provides number of columns in AH */
/* if EGA+ detected, fetch number of rows, otherwise (CGA...) assume 25 */
if (detect_ega() != 0) {
*termheight = (*(unsigned char far *) MK_FP(0x40, 0x84)) + 1;
} else {
*termheight = 25;
}
/* select the most appropriate color scheme */
if ((regs.h.al == 0) || (regs.h.al == 2) || (regs.h.al == 7)) { /* mono modes */
*colorflag = 0;
} else { /* else we are in color */
*colorflag = 1;
}
/* select the correct VRAM address */
if (regs.h.al == 7) { /* MDA/HERC mode */
vmem = (unsigned char far *) 0xB0000000l; /* B000:0000 video memory addess */
} else {
vmem = (unsigned char far *) 0xB8000000l; /* B800:0000 video memory address */
}
}
int computecolor(int doscol) {
return(doscol);
}