Desktop Projects
Japanese Full-Screen editor for MS DOS
This one dates back to 1997, shortly before I moved to Japan. It is
probably my first published little program: I submitted this
successfully for inclusion in the Simtel software archive,
so it will probably have found its way to quite a lot of CD-ROMs and
mirror websites. The lettering though.
The package has a .exe file that still runs fine in
dosbox-x on macOS. I canât even remember how I built this,
it must have been Borland C++, which you had to install from I think a
dozen of floppy-disks, I kid you not.
The heart of the project is probably this low-level C file with routines that talk to the VGA graphics card and blit the actual character bitmaps, and do all the visual stuff âby handâ.
For example, scrolling a line means that you have to copy up all the pixels that are below the line onto the deleted line, and then overwrite the last line on the screen with the characters of the line that has now become visible at the bottom.
/* EOUE640.C EOUE Project, 1997 by Michael Mangelsdorf */
/* THIS FILE IS PUBLIC DOMAIN.
LEAVE THIS MESSAGE IN PLACE AS COURTESY TO THE AUTHOR */
#include <dos.h>
#include <stdio.h>
extern unsigned char joyokanji_bitmap16x16[], kana_bitmap16x16[];
unsigned char TINV=0; /* text inverted */
unsigned char *eoue_font8x16ptr, *eoue_font8x8ptr,
*eoue_vidmem=MK_FP(0xA000,0);
unsigned eoue_visualpage;
unsigned char eoue_pal_colors [17][3]; /* Index in RGB order, hold color
components pointed to by palette
registers in palette_buf[],
used by eoue_pal() */
unsigned char eoue_palette_buf [17]; /* Holds copy of pre-process palette,
used by eoue_pal(). */
void eoue_pal (unsigned char op);
void eoue_clrstat (void);
void eoue_cpythru (void);
void eoue_clrscr (void); /*Forward prototypes*/
void eoue_pageflip (void);
/* Returns true if VGA detected.
Called by eoue_setgr() when argument 1.
*/
unsigned char eoue_hasvga (void) {
union REGS r;
r.x.ax=0x1A00; int86(0x10,&r,&r);
if (r.h.al==0x1A) return 1;
else return 0;
}
/* Get and store pointer to VGA 8x8 ASCII font.
Called during eoue_setgr() when argument 1.
*/
void eoue_get8x8ptr (void) {
static unsigned font8x8seg, font8x8off;
asm {
push bp
push es
mov ax,0x1130
mov bh,3
int 0x10
mov [font8x8seg],es
mov [font8x8off],bp
pop es
pop bp
}
eoue_font8x8ptr=MK_FP(font8x8seg,font8x8off);
}
/* Get and store pointer to VGA 8x16 ASCII font.
Called during eoue_setgr() when argument 1.
*/
void eoue_get8x16ptr (void) {
static unsigned font8x16seg, font8x16off;
asm {
push bp
mov ax,0x1130
mov bh,6
int 0x10
mov [font8x16seg],es
mov [font8x16off],bp
pop bp
}
eoue_font8x16ptr=MK_FP(font8x16seg,font8x16off);
}
/* Wait until (stale) retrace finished,
then wait until display end.
*/
void eoue_flyback (void) {
while (inportb(0x3DA)&8) /*3DA: VGA input status register 1*/
; /*NOP*/ /*Bit three indicates vertical retrace phase*/
while (!(inportb(0x3DA)&8))
; /*NOP*/
}
/* Switch VGA display mode, BIOS interrupt.
Argument: 0 sets textmode, 1 sets 640x480x16 mode.
We modify a CRT setting; display end is going to
be early, at raster line 400. This is to be able
to have to swappable display pages.
Failing function returns 1.
*/
unsigned char eoue_setgr (unsigned char grmode) {
union REGS r;
if (!eoue_hasvga()) return 1;
if (!grmode) {
/* blacken display for smooth palette restoration*/
r.h.ah=0x12; r.h.bl=0x36; r.h.al=1; int86(0x10,&r,&r);
eoue_pal(0);
}
/*set video mode*/
r.h.ah=0; r.h.al=grmode?0x12:0x03;
int86(0x10,&r,&r);
if (!grmode) return 0;
/* blacken display after setting mode*/
r.h.ah=0x12; r.h.bl=0x36; r.h.al=1; int86(0x10,&r,&r);
eoue_get8x16ptr();
eoue_get8x8ptr();
eoue_visualpage=0x0000;
eoue_pal(1);
outportb (0x3D4,0x12); /* select vertical display end register */
outportb (0x3D5,152); /* bits 0-7, bits 8,9 need not be changed. */
outportb (0x3D4,0x11); /* center the image better */
outportb (0x3D5,169); /* End Vertical retrace reg.*/
outportb (0x3D4,0x10);
outportb (0x3D5,0xC7); /* Start vertical retrace reg. -32 rasters */
eoue_clrscr();
eoue_clrstat();
eoue_pageflip();
eoue_clrstat();
/*re-enable display*/
r.h.ah=0x12; r.h.bl=0x36; r.h.al=0; int86(0x10,&r,&r);
return 0;
}
/* Change palette entry for one of the 4 colours (colcode 0-3).
*/
void eoue_modify_color (char colcode, char red, char g, char b) {
union REGS r;
r.h.ah=0x10; r.h.al=0x10;
r.x.bx=eoue_palette_buf[colcode]; r.h.dh=red; r.h.ch=g; r.h.cl=b;
int86(0x10,&r,&r);
}
/*
*/
void eoue_pal (unsigned char op) {
unsigned char i;
union REGS r; struct SREGS s;
/* Store indexes of affected colors first, if not already done.
Those are found in the palette registers. */
if (op) {
r.h.ah=0x10; r.h.al=0x09; r.x.dx=FP_OFF(eoue_palette_buf);
s.es=FP_SEG(eoue_palette_buf);
int86x(0x10,&r,&r,&s);
} else eoue_clrscr();
/* Store or retrieve corresponding color values.
Subfunction 10h writes RGB to VGA, 15h returns RGB values. */
for (i=0;i<17;i++) {
r.h.ah=0x10; r.h.al=op?0x15:0x10; r.x.bx=eoue_palette_buf[i];
if (!op) {
r.h.dh=eoue_pal_colors[i][0];
r.h.ch=eoue_pal_colors[i][1];
r.h.cl=eoue_pal_colors[i][2];
int86(0x10,&r,&r);
}
else {
int86(0x10,&r,&r);
eoue_pal_colors[i][0]=r.h.dh;
eoue_pal_colors[i][1]=r.h.ch;
eoue_pal_colors[i][2]=r.h.cl;
}
}
if (op) {
/* Now modify palette. */
r.h.ah=0x10; r.h.al=0x10;
r.x.bx=eoue_palette_buf[0]; r.h.dh=0; r.h.ch=0; r.h.cl=24;
int86(0x10,&r,&r);
r.x.bx=eoue_palette_buf[1]; r.h.dh=0; r.h.ch=0; r.h.cl=0;
int86(0x10,&r,&r);
r.x.bx=eoue_palette_buf[2]; r.h.dh=63; r.h.ch=63; r.h.cl=63;
int86(0x10,&r,&r);
r.x.bx=eoue_palette_buf[3]; r.h.dh=12; r.h.ch=12; r.h.cl=24;
int86(0x10,&r,&r);
r.x.bx=eoue_palette_buf[4]; r.h.dh=62; r.h.ch=48; r.h.cl=28;
int86(0x10,&r,&r);
r.x.bx=eoue_palette_buf[8]; r.h.dh=64; r.h.ch=12; r.h.cl=24;
int86(0x10,&r,&r);
/*load overscan register, too*/
r.h.ah=0x10; r.h.al=1; r.h.bh=1; int86 (0x10,&r,&r);
}
}
/* Set the entire screen region to zero.
*/
void eoue_clrscr (void) {
unsigned sweep;
outportb (0x3C4,2); /*select map mask register*/
outportb (0x3C5,0xF); /*plane 0, set to 1*/
sweep=0;
do *(eoue_vidmem+sweep)=0; while (2*0x7FD0!=sweep++);
}
/* Rubout a character. CHECK.
*/
void eoue_clr8 (char x, char y) {
unsigned char i;
unsigned voffset=80*16*y+eoue_visualpage+x;
for(i=0;i<16;i++) *(eoue_vidmem+80*i+voffset)=0;
}
/* Rubout a character. CHECK.
*/
void eoue_clr16 (char x, char y) {
unsigned char i;
unsigned voffset=80*16*y+eoue_visualpage+x;
for(i=0;i<16;i++) {
*(eoue_vidmem+80*i+voffset)=0;
*(eoue_vidmem+80*i+voffset+1)=0;
}
}
/* Rubout a character from status line. CHECK.
*/
void eoue_clrstat () {
unsigned char i,j;
unsigned voffset=80*16*25+eoue_visualpage;
/*Phase 'set'*/
outportb(0x3C4,2); /*select map mask register*/
outportb(0x3C5,3);
for (j=0;j<80;j++) *(eoue_vidmem+voffset+j)=255;
voffset+=80;
for (j=0;j<80;j++)
for(i=0;i<8;i++) *(eoue_vidmem+80*i+voffset+j)=255;
/*Phase 'reset'*/
outportb(0x3C4,2); /*select map mask register*/
outportb(0x3C5,12);
voffset=80*16*25+eoue_visualpage+80;
for (j=0;j<80;j++)
for(i=0;i<8;i++) *(eoue_vidmem+80*i+voffset+j)=0;
outportb(0x3C4,2); /*select map mask register*/
outportb(0x3C5,0xF); /*default*/
}
/* Write VGA ROM font character to status line. CHECK.
*/
void eoue_prstat (char *ch, char x, char color)
{
unsigned char i,j,l, *cbitmap;
unsigned voffset=80*16*25+80+eoue_visualpage+x;
/*phase 'set'*/
outportb(0x3C4,2); /*select map mask register*/
outportb(0x3C5,color);
l=strlen(ch);
for (j=0;j<l;j++) {
cbitmap=eoue_font8x8ptr+8*ch[j];
for(i=0;i<8;i++)
*(eoue_vidmem+80*i+voffset+j) = TINV?~*(cbitmap+i):*(cbitmap+i);
}
/*phase 'reset'*/
voffset=80*16*25+80+eoue_visualpage+x;
outportb(0x3C4,2); /*select map mask register*/
outportb(0x3C5,3);
l=strlen(ch);
for (j=0;j<l;j++) {
cbitmap=eoue_font8x8ptr+8*ch[j];
for(i=0;i<8;i++)
*(eoue_vidmem+80*i+voffset+j) = TINV?*(cbitmap+i):~*(cbitmap+i);
}
outportb(0x3C5,0x0F); /*back to default*/
}
/* Write VGA ROM font character. CHECK.
*/
void eoue_put8 (unsigned char ch, char x, char y, char color)
{
unsigned char i, *cbitmap=eoue_font8x16ptr+16*ch;
unsigned voffset=80*16*y+eoue_visualpage+x;
outportb(0x3C4,2); /*select map mask register*/
outportb(0x3C5,color);
for(i=0;i<16;i++)
*(eoue_vidmem+80*i+voffset) = TINV?~*(cbitmap+i):*(cbitmap+i);
outportb(0x3C5,0x0F); /*back to default*/
}
/* Write 16 bit wide JIS/KANA character. CHECK.
*/
void eoue_put16 (unsigned ch, char x, char y, char color)
{
unsigned char i, *cbitmap;
unsigned voffset=80*16*y+eoue_visualpage+x;
if (ch<2000) cbitmap=joyokanji_bitmap16x16+32*ch;
else cbitmap=kana_bitmap16x16+32*(ch-2000);
outportb(0x3C4,2); /*select map mask register*/
outportb(0x3C5,color);
for(i=0;i<16;i++) {
*(eoue_vidmem+80*i+voffset)=TINV?*(cbitmap+i*2):~*(cbitmap+i*2);
*(eoue_vidmem+80*i+voffset+1)=TINV?*(cbitmap+i*2+1):~*(cbitmap+i*2+1);
}
outportb(0x3C5,0x0F); /*back to default*/
}
/* Make working page visual page, and vice-versa. CHECK.
If argument 25, copy whole screen through, otherwise
only specified line 0-24.
*/
void eoue_pageflip (void) {
eoue_flyback();
outportb (0x3D4,0x0C); /* start address msb 409*80 */
outportb (0x3D5,eoue_visualpage?0x7F:0);
outportb (0x3D4,0x0D); /* lsb */
outportb (0x3D5,eoue_visualpage?0xD0:0);
eoue_visualpage=0x7FD0-eoue_visualpage; /*toggle*/
asm { /* Copy visual page through into working page */
mov dx, 0x3CE; mov ax, 0x0105; out dx,ax
mov cx,0x7FD0;
mov di, eoue_visualpage; mov si,cx; sub si, di;
push ds; mov ax,0xA000; mov ds, ax; mov es,ax
cld
rep movsb
pop ds
mov ax,0x0005; out dx,ax
}
}
/* Scrolls visual page, result in working page.
*/
void eoue_scrollthru (unsigned char direction, unsigned char line) {
unsigned sweep=direction?0x7FD0-25*80:25*80;
unsigned src=eoue_visualpage, dst=0x7FD0-eoue_visualpage;
outportb (0x3CE,5); /*select mode register, graphics controller*/
outportb (0x3CF,1); /*write mode 1 read mode 0*/
if (!direction)
do *(eoue_vidmem+src+sweep-16*80)=*(eoue_vidmem+dst+sweep);
while (line*16*80!=sweep++);
else do *(eoue_vidmem+src+sweep+16*80)=*(eoue_vidmem+dst+sweep);
while (line*16*80!=sweep--);
outportb (0x3CE,5); /*select mode register, graphics controller*/
outportb (0x3CF,0); /*default r/w mode*/
}
/* Clear one line in working page. CHECK.
*/
void eoue_linedel (unsigned char line, unsigned char fromc) {
static unsigned i,j,start;
start=16*80*line+eoue_visualpage;
for (i=0;i<16;i++)
for (j=fromc;j<80;j++) *(eoue_vidmem+start+i*80+j)=0;
}
/* Scoll workpage line left from lpos, result in workpage line. CHECK.
*/
void eoue_SLL (char lin, char lpos, char displ) {
unsigned pagebase=lin*80*16+eoue_visualpage,i,j;
outportb (0x3CE,5); /*select mode register, graphics controller*/
outportb (0x3CF,1); /*write mode 1 read mode 0*/
for (i=0;i<16;i++) for (j=lpos;j<80-displ;j++)
*(eoue_vidmem+pagebase+i*80+j) = *(eoue_vidmem+pagebase+i*80+j+displ);
outportb (0x3CE,5); /*select mode register, graphics controller*/
outportb (0x3CF,0); /*default r/w mode*/
}
/* Scoll workpage line right from lpos, result in workpage line. CHECK.
*/
void eoue_SLR (char lin, char lpos, char displ) {
unsigned pagebase=lin*80*16+eoue_visualpage,i,j;
outportb (0x3CE,5); /*select mode register, graphics controller*/
outportb (0x3CF,1); /*write mode 1 read mode 0*/
for (i=0;i<16;i++) for (j=79;j>lpos+displ;j--)
*(eoue_vidmem+pagebase+i*80+j) = *(eoue_vidmem+pagebase+i*80+j-displ);
outportb (0x3CE,5); /*select mode register, graphics controller*/
outportb (0x3CF,0); /*default r/w mode*/
}
/* Draw string of ASCII characters
*/
void eoue_fixprint (char *s, char x, char y, char color) {
unsigned char i,j;
j=strlen(s);
for (i=0;i<j;i++)
eoue_put8 (s[i],x+i,y,color);
}
/* Draw string of two-byte Japanese characters.
*/
void eoue_jprint (unsigned *codestr, char x, char y, char color) {
unsigned char i;
if (codestr==NULL) return;
for (i=1;i<codestr[0];i++) eoue_put16 (codestr[i],x+2*i,y,color);
}
/* Same as jprint, but vertically
*/
void eoue_tategaki (unsigned *codestr, char x, char y, char color) {
unsigned char i;
if (codestr==NULL) return;
for (i=1;i<codestr[0];i++) eoue_put16 (codestr[i],x,y+i,color);
}
void showbitmap (char* map, char x0, char y0, char color, char w, char h) {
char i,j;
unsigned k=0;
outportb(0x3C4,2); /*select map mask register*/
outportb(0x3C5,color);
for (i=y0;i<y0+h;i++)
for (j=x0;j<x0+w;j++)
*(eoue_vidmem+eoue_visualpage+i*80+j)=*(map+k++);
outportb(0x3C5,15);
}
Downloads
The âeditorâ executable is LQNT.exe. You can run it in an MS DOS
emulator such as dosbox-x. The way the editor works is
actually a (tiny) bit original. You type text (for example âGAIRAIGOâ)
then a space, followed by an inbuilt command (such as âkanaâ), and then
the editor does a contextual replacement or other action (transforms the
roman letters into katakana).
Words in ALLCAPS get replaced by their katakana, words in âlowercaseâ become hiragana.
Check LQNT.c to see how inserting kanji works. Itâs the istrcmp conditional that checks for an uppercase K, followed by a numeric code. I think you were supposed to enter K(JIS-page)-(CHARCODE). I must have ran out of time or something, because it would have been relatively easy and really cool to have it show a little kanji picker based on a kana word.
JIS (Japanese Industrial Standards) was the then-equivalent of Unicode, but for Japanese characters only. DOS or early Windows had absolutely no awareness yet of UTF-8 or anything like that. There were multiple competing encoding standards for kanji, it was a complete mess. You couldnât just âwrite Japaneseâ on your PC in those days.