mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-05-08 19:32:39 +08:00
963 lines
38 KiB
C++
963 lines
38 KiB
C++
/*
|
|
* Copyright (C) 2002-2015 The DOSBox Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
|
|
#include "dos_inc.h"
|
|
#include "../ints/int10.h"
|
|
#include <string.h>
|
|
#include "inout.h"
|
|
#include "shiftjis.h"
|
|
#include "callback.h"
|
|
|
|
#define NUMBER_ANSI_DATA 10
|
|
|
|
extern bool DOS_BreakFlag;
|
|
|
|
Bitu INT10_Handler(void);
|
|
Bitu INT16_Handler_Wrap(void);
|
|
|
|
ShiftJISDecoder con_sjis;
|
|
|
|
Bit16u last_int16_code = 0;
|
|
|
|
static size_t dev_con_pos=0,dev_con_max=0;
|
|
static char dev_con_readbuf[64];
|
|
|
|
Bit8u DefaultANSIAttr() {
|
|
return IS_PC98_ARCH ? 0xE1 : 0x07;
|
|
}
|
|
|
|
class device_CON : public DOS_Device {
|
|
public:
|
|
device_CON();
|
|
bool Read(Bit8u * data,Bit16u * size);
|
|
bool Write(const Bit8u * data,Bit16u * size);
|
|
bool Seek(Bit32u * pos,Bit32u type);
|
|
bool Close();
|
|
void ClearAnsi(void);
|
|
Bit16u GetInformation(void);
|
|
bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { (void)bufptr; (void)size; (void)retcode; return false; }
|
|
bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { (void)bufptr; (void)size; (void)retcode; return false; }
|
|
private:
|
|
Bit8u readcache;
|
|
Bit8u lastwrite;
|
|
struct ansi { /* should create a constructor, which would fill them with the appropriate values */
|
|
bool esc;
|
|
bool sci;
|
|
bool pc98rab; // PC-98 ESC [ > ... (right angle bracket) I will rename this variable if MS-DOS ANSI.SYS also supports this sequence
|
|
bool enabled;
|
|
Bit8u attr; // machine-specific
|
|
Bit8u data[NUMBER_ANSI_DATA];
|
|
Bit8u numberofarg;
|
|
Bit16u nrows;
|
|
Bit16u ncols;
|
|
Bit8u savecol;
|
|
Bit8u saverow;
|
|
bool warned;
|
|
|
|
void Disable() {
|
|
if (!IS_PC98_ARCH)
|
|
enabled = false;
|
|
|
|
attr = DefaultANSIAttr();
|
|
}
|
|
} ansi;
|
|
|
|
static void Real_INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page) {
|
|
Bit16u oldax,oldbx,olddx;
|
|
|
|
oldax=reg_ax;
|
|
oldbx=reg_bx;
|
|
olddx=reg_dx;
|
|
|
|
reg_ah=0x2;
|
|
reg_dh=row;
|
|
reg_dl=col;
|
|
reg_bh=page;
|
|
|
|
/* FIXME: PC-98 emulation should eventually use CONIO emulation that
|
|
* better emulates the actual platform. The purpose of this
|
|
* hack is to allow our code to call into INT 10h without
|
|
* setting up an INT 10h vector */
|
|
if (IS_PC98_ARCH)
|
|
INT10_Handler();
|
|
else
|
|
CALLBACK_RunRealInt(0x10);
|
|
|
|
reg_ax=oldax;
|
|
reg_bx=oldbx;
|
|
reg_dx=olddx;
|
|
}
|
|
|
|
/* Common function to turn specific scan codes into ANSI codes.
|
|
* This is a separate function so that both Read() and GetInformation() can use it.
|
|
* GetInformation needs to handle the scan code on entry in order to correctly
|
|
* assert whether Read() will return data or not. Some scan codes are ignored by
|
|
* the CON driver, therefore even though the BIOS says there is key data, Read()
|
|
* will not return anything and will block. */
|
|
bool CommonPC98ExtScanConversionToReadBuf(unsigned char code) {
|
|
switch (code) {
|
|
case 0x38: // INS
|
|
dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x50; dev_con_pos=0; dev_con_max=2;
|
|
break;
|
|
case 0x39: // DEL
|
|
dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x44; dev_con_pos=0; dev_con_max=2;
|
|
return true;
|
|
case 0x3A: // up arrow
|
|
dev_con_readbuf[0] = 0x0B; dev_con_pos=0; dev_con_max=1;
|
|
return true;
|
|
case 0x3B: // left arrow
|
|
dev_con_readbuf[0] = 0x08; dev_con_pos=0; dev_con_max=1;
|
|
return true;
|
|
case 0x3C: // right arrow
|
|
dev_con_readbuf[0] = 0x0C; dev_con_pos=0; dev_con_max=1;
|
|
return true;
|
|
case 0x3D: // down arrow
|
|
dev_con_readbuf[0] = 0x0A; dev_con_pos=0; dev_con_max=1;
|
|
return true;
|
|
case 0x62: // F1
|
|
dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x53; dev_con_pos=0; dev_con_max=2;
|
|
return true;
|
|
case 0x63: // F2
|
|
dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x54; dev_con_pos=0; dev_con_max=2;
|
|
return true;
|
|
case 0x64: // F3
|
|
dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x55; dev_con_pos=0; dev_con_max=2;
|
|
return true;
|
|
case 0x65: // F4
|
|
dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x56; dev_con_pos=0; dev_con_max=2;
|
|
return true;
|
|
case 0x66: // F5
|
|
dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x57; dev_con_pos=0; dev_con_max=2;
|
|
return true;
|
|
case 0x67: // F6
|
|
dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x45; dev_con_pos=0; dev_con_max=2;
|
|
return true;
|
|
case 0x68: // F7
|
|
dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x4A; dev_con_pos=0; dev_con_max=2;
|
|
return true;
|
|
case 0x69: // F8
|
|
dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x50; dev_con_pos=0; dev_con_max=2;
|
|
return true;
|
|
case 0x6A: // F9
|
|
dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x51; dev_con_pos=0; dev_con_max=2;
|
|
return true;
|
|
case 0x6B: // F10
|
|
dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x5A; dev_con_pos=0; dev_con_max=2;
|
|
return true;
|
|
#if 0
|
|
// ROLL UP -- -- --
|
|
// POLL DOWN-- -- --
|
|
// COPY -- -- --
|
|
// HOME/CLR 0x1A 0x1E --
|
|
// HELP -- -- --
|
|
#endif
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void Real_INT10_TeletypeOutput(Bit8u xChar,Bit8u xAttr) {
|
|
Bit16u oldax,oldbx;
|
|
|
|
if (IS_PC98_ARCH) {
|
|
if (con_sjis.take(xChar)) {
|
|
BIOS_NCOLS;
|
|
Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
|
|
Bit8u cur_row=CURSOR_POS_ROW(page);
|
|
Bit8u cur_col=CURSOR_POS_COL(page);
|
|
unsigned char cw = con_sjis.doublewide ? 2 : 1;
|
|
|
|
/* FIXME: I'm not sure what NEC's ANSI driver does if a doublewide character is printed at column 79 */
|
|
if ((cur_col+cw) > ncols) {
|
|
cur_col = (Bit8u)ncols;
|
|
AdjustCursorPosition(cur_col,cur_row);
|
|
}
|
|
|
|
/* JIS conversion to WORD value appropriate for text RAM */
|
|
if (con_sjis.b2 != 0) con_sjis.b1 -= 0x20;
|
|
|
|
INT10_WriteChar((con_sjis.b2 << 8) + con_sjis.b1,xAttr,0,1,true);
|
|
|
|
cur_col += cw;
|
|
AdjustCursorPosition(cur_col,cur_row);
|
|
Real_INT10_SetCursorPos(cur_row,cur_col,page);
|
|
}
|
|
}
|
|
else {
|
|
oldax=reg_ax;
|
|
oldbx=reg_bx;
|
|
|
|
reg_ah=0xE;
|
|
reg_al=xChar;
|
|
reg_bl=xAttr;
|
|
|
|
CALLBACK_RunRealInt(0x10);
|
|
|
|
reg_ax=oldax;
|
|
reg_bx=oldbx;
|
|
}
|
|
}
|
|
|
|
|
|
static void Real_WriteChar(Bit8u cur_col,Bit8u cur_row,
|
|
Bit8u page,Bit8u chr,Bit8u attr,Bit8u useattr) {
|
|
//Cursor position
|
|
Real_INT10_SetCursorPos(cur_row,cur_col,page);
|
|
|
|
//Write the character
|
|
Bit16u oldax,oldbx,oldcx;
|
|
oldax=reg_ax;
|
|
oldbx=reg_bx;
|
|
oldcx=reg_cx;
|
|
|
|
reg_al=chr;
|
|
reg_bl=attr;
|
|
reg_bh=page;
|
|
reg_cx=1;
|
|
if(useattr)
|
|
reg_ah=0x9;
|
|
else reg_ah=0x0A;
|
|
|
|
/* FIXME: PC-98 emulation should eventually use CONIO emulation that
|
|
* better emulates the actual platform. The purpose of this
|
|
* hack is to allow our code to call into INT 10h without
|
|
* setting up an INT 10h vector */
|
|
if (IS_PC98_ARCH)
|
|
INT10_Handler();
|
|
else
|
|
CALLBACK_RunRealInt(0x10);
|
|
|
|
reg_ax=oldax;
|
|
reg_bx=oldbx;
|
|
reg_cx=oldcx;
|
|
}//static void Real_WriteChar(cur_col,cur_row,page,chr,attr,useattr)
|
|
|
|
|
|
static void AdjustCursorPosition(Bit8u& cur_col,Bit8u& cur_row) {
|
|
BIOS_NCOLS;BIOS_NROWS;
|
|
auto defattr = DefaultANSIAttr();
|
|
//Need a new line?
|
|
if(cur_col==ncols)
|
|
{
|
|
cur_col=0;
|
|
cur_row++;
|
|
|
|
if (!IS_PC98_ARCH)
|
|
Real_INT10_TeletypeOutput('\r',defattr);
|
|
}
|
|
|
|
//Reached the bottom?
|
|
if(cur_row==nrows)
|
|
{
|
|
if (IS_PC98_ARCH)
|
|
INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,defattr,0);
|
|
else
|
|
Real_INT10_TeletypeOutput('\n',defattr); //Scroll up
|
|
|
|
cur_row--;
|
|
}
|
|
}
|
|
|
|
|
|
void Real_INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr) {
|
|
//TODO Check if this page thing is correct
|
|
Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
|
|
// BIOS_NCOLS;BIOS_NROWS;
|
|
Bit8u cur_row=CURSOR_POS_ROW(page);
|
|
Bit8u cur_col=CURSOR_POS_COL(page);
|
|
switch (chr)
|
|
{
|
|
case 7: {
|
|
// set timer (this should not be needed as the timer already is programmed
|
|
// with those values, but the speaker stays silent without it)
|
|
IO_Write(0x43,0xb6);
|
|
IO_Write(0x42,1320&0xff);
|
|
IO_Write(0x42,1320>>8);
|
|
// enable speaker
|
|
IO_Write(0x61,IO_Read(0x61)|0x3);
|
|
for(Bitu i=0; i < 333; i++) CALLBACK_Idle();
|
|
IO_Write(0x61,IO_Read(0x61)&~0x3);
|
|
break;
|
|
}
|
|
case 8:
|
|
if(cur_col>0)
|
|
cur_col--;
|
|
break;
|
|
case '\r':
|
|
cur_col=0;
|
|
break;
|
|
case '\n':
|
|
cur_col=0;
|
|
cur_row++;
|
|
break;
|
|
case '\t':
|
|
do {
|
|
Real_INT10_TeletypeOutputAttr(' ',attr,useattr);
|
|
cur_row=CURSOR_POS_ROW(page);
|
|
cur_col=CURSOR_POS_COL(page);
|
|
} while(cur_col%8);
|
|
break;
|
|
default:
|
|
//* Draw the actual Character
|
|
if (IS_PC98_ARCH) {
|
|
if (con_sjis.take(chr)) {
|
|
BIOS_NCOLS;
|
|
unsigned char cw = con_sjis.doublewide ? 2 : 1;
|
|
|
|
/* FIXME: I'm not sure what NEC's ANSI driver does if a doublewide character is printed at column 79 */
|
|
if ((cur_col+cw) > ncols) {
|
|
cur_col = (Bit8u)ncols;
|
|
AdjustCursorPosition(cur_col,cur_row);
|
|
}
|
|
|
|
/* JIS conversion to WORD value appropriate for text RAM */
|
|
if (con_sjis.b2 != 0) con_sjis.b1 -= 0x20;
|
|
|
|
INT10_WriteChar((con_sjis.b2 << 8) + con_sjis.b1,attr,0,1,true);
|
|
|
|
cur_col += cw;
|
|
}
|
|
}
|
|
else {
|
|
Real_WriteChar(cur_col,cur_row,page,chr,attr,useattr);
|
|
cur_col++;
|
|
}
|
|
}
|
|
|
|
AdjustCursorPosition(cur_col,cur_row);
|
|
Real_INT10_SetCursorPos(cur_row,cur_col,page);
|
|
}//void Real_INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr)
|
|
};
|
|
|
|
// NEC-PC98 keyboard input notes
|
|
//
|
|
// on a system with KKCFUNC.SYS, NECAIK1.SYS, NECAIK2.SYS, NECAI.SYS loaded
|
|
//
|
|
// Key Normal Shift CTRL
|
|
// -------------------------------------
|
|
// ESC 0x1B 0x1B 0x1B
|
|
// TAB 0x09 0x09 0x09
|
|
// F1 0x1B 0x53 <shortcut> --
|
|
// F2 0x1B 0x54 <shortcut> --
|
|
// F3 0x1B 0x55 <shortcut> --
|
|
// F4 0x1B 0x56 <shortcut> Toggles 'g'
|
|
// F5 0x1B 0x57 <shortcut> --
|
|
// F6 0x1B 0x45 <shortcut> Toggle 20/25-line text mode
|
|
// F7 0x1B 0x4A <shortcut> Toggle function row (C1/CU/etc, shortcuts, or off)
|
|
// F8 0x1B 0x50 <shortcut> Clear screen, home cursor
|
|
// F9 0x1B 0x51 <shortcut> --
|
|
// F10 0x1B 0x5A <shortcut> --
|
|
// INS 0x1B 0x50 0x1B 0x50 0x1B 0x50
|
|
// DEL 0x1B 0x44 0x1B 0x44 0x1B 0x44
|
|
// ROLL UP -- -- --
|
|
// POLL DOWN-- -- --
|
|
// COPY -- -- --
|
|
// HOME/CLR 0x1A 0x1E --
|
|
// HELP -- -- --
|
|
// UP ARROW 0x0B 0x0B 0x0B
|
|
// LF ARROW 0x08 0x08 0x08
|
|
// RT ARROW 0x0C 0x0C 0x0C
|
|
// DN ARROW 0x0A 0x0A 0x0A
|
|
// VF1 -- -- --
|
|
// VF2 -- -- --
|
|
// VF3 -- -- --
|
|
// VF4 -- -- --
|
|
// VF5 -- -- --
|
|
|
|
// TODO for PC-98 mode:
|
|
//
|
|
// According to:
|
|
//
|
|
// http://hackipedia.org/browse.cgi/Computer/Platform/PC%2c%20NEC%20PC%2d98/Collections/PC%2d9801%20Bible%20%e6%9d%b1%e4%ba%ac%e7%90%86%e7%a7%91%e5%a4%a7%e5%ad%a6EIC%20%281994%29%2epdf
|
|
//
|
|
// Section 4-8.
|
|
//
|
|
// The PDF documents ANSI codes defined on PC-98, which may or may not be a complete listing.
|
|
|
|
bool device_CON::Read(Bit8u * data,Bit16u * size) {
|
|
Bit16u oldax=reg_ax;
|
|
Bit16u count=0;
|
|
auto defattr=DefaultANSIAttr();
|
|
INT10_SetCurMode();
|
|
if ((readcache) && (*size)) {
|
|
data[count++]=readcache;
|
|
if(dos.echo) Real_INT10_TeletypeOutput(readcache,defattr);
|
|
readcache=0;
|
|
}
|
|
while (*size>count) {
|
|
if (dev_con_pos < dev_con_max) {
|
|
data[count++] = (Bit8u)dev_con_readbuf[dev_con_pos++];
|
|
continue;
|
|
}
|
|
|
|
reg_ah=(IS_EGAVGA_ARCH)?0x10:0x0;
|
|
|
|
/* FIXME: PC-98 emulation should eventually use CONIO emulation that
|
|
* better emulates the actual platform. The purpose of this
|
|
* hack is to allow our code to call into INT 16h without
|
|
* setting up an INT 16h vector */
|
|
if (IS_PC98_ARCH)
|
|
INT16_Handler_Wrap();
|
|
else
|
|
CALLBACK_RunRealInt(0x16);
|
|
|
|
/* hack for DOSKEY emulation */
|
|
last_int16_code = reg_ax;
|
|
|
|
switch(reg_al) {
|
|
case 13:
|
|
data[count++]=0x0D;
|
|
if (*size>count) data[count++]=0x0A; // it's only expanded if there is room for it. (NO cache)
|
|
*size=count;
|
|
reg_ax=oldax;
|
|
if(dos.echo) {
|
|
Real_INT10_TeletypeOutput(13,defattr); //maybe don't do this ( no need for it actually ) (but it's compatible)
|
|
Real_INT10_TeletypeOutput(10,defattr);
|
|
}
|
|
return true;
|
|
break;
|
|
case 8:
|
|
if(*size==1) data[count++]=reg_al; //one char at the time so give back that BS
|
|
else if(count) { //Remove data if it exists (extended keys don't go right)
|
|
data[count--]=0;
|
|
Real_INT10_TeletypeOutput(8,defattr);
|
|
Real_INT10_TeletypeOutput(' ',defattr);
|
|
} else {
|
|
continue; //no data read yet so restart whileloop.
|
|
}
|
|
break;
|
|
case 0xe0: /* Extended keys in the int 16 0x10 case */
|
|
if(!reg_ah) { /*extended key if reg_ah isn't 0 */
|
|
data[count++] = reg_al;
|
|
} else {
|
|
data[count++] = 0;
|
|
if (*size>count) data[count++] = reg_ah;
|
|
else readcache = reg_ah;
|
|
}
|
|
break;
|
|
case 0: /* Extended keys in the int 16 0x0 case */
|
|
if (IS_PC98_ARCH) {
|
|
/* PC-98 does NOT return scan code, but instead returns nothing or
|
|
* control/escape code */
|
|
CommonPC98ExtScanConversionToReadBuf(reg_ah);
|
|
}
|
|
else {
|
|
/* IBM PC/XT/AT signals extended code by entering AL, AH.
|
|
* Arrow keys for example become 0x00 0x48, 0x00 0x50, etc. */
|
|
data[count++]=reg_al;
|
|
if (*size>count) data[count++]=reg_ah;
|
|
else readcache=reg_ah;
|
|
}
|
|
break;
|
|
default:
|
|
data[count++]=reg_al;
|
|
break;
|
|
}
|
|
if(dos.echo) { //what to do if *size==1 and character is BS ?????
|
|
// TODO: If CTRL+C checking is applicable do not echo (reg_al == 3)
|
|
Real_INT10_TeletypeOutput(reg_al,defattr);
|
|
}
|
|
}
|
|
*size=count;
|
|
reg_ax=oldax;
|
|
return true;
|
|
}
|
|
|
|
bool log_dev_con = false;
|
|
std::string log_dev_con_str;
|
|
|
|
bool device_CON::Write(const Bit8u * data,Bit16u * size) {
|
|
Bit16u count=0;
|
|
Bitu i;
|
|
Bit8u col,row;
|
|
Bit8u tempdata;
|
|
INT10_SetCurMode();
|
|
|
|
if (IS_PC98_ARCH) {
|
|
ansi.enabled = true; // ANSI is enabled at all times
|
|
ansi.attr = mem_readb(0x71D); // 60:11D
|
|
}
|
|
|
|
while (*size>count) {
|
|
if (log_dev_con) {
|
|
if (log_dev_con_str.size() >= 255 || data[count] == '\n' || data[count] == 27) {
|
|
LOG_MSG("DOS CON: %s",log_dev_con_str.c_str());
|
|
log_dev_con_str.clear();
|
|
}
|
|
|
|
if (data[count] != '\n' && data[count] != '\r')
|
|
log_dev_con_str += (char)data[count];
|
|
}
|
|
|
|
if (!ansi.esc){
|
|
// TODO: PC-98 MS-DOS ANSI driver accepts 0x1E (RECORD SEPARATOR) as a single char command
|
|
// to put the cursor in home position.
|
|
// TODO: PC-98 MS-DOS ANSI driver accepts CTRL+Z as a single char command to clear the
|
|
// screeen and move the cursor to home (upper left).
|
|
if(data[count]=='\033') {
|
|
/*clear the datastructure */
|
|
ClearAnsi();
|
|
/* start the sequence */
|
|
ansi.esc=true;
|
|
count++;
|
|
continue;
|
|
} else {
|
|
/* Some sort of "hack" now that '\n' doesn't set col to 0 (int10_char.cpp old chessgame) */
|
|
if((data[count] == '\n') && (lastwrite != '\r')) Real_INT10_TeletypeOutputAttr('\r',ansi.attr,ansi.enabled);
|
|
/* ansi attribute will be set to the default if ansi is disabled */
|
|
Real_INT10_TeletypeOutputAttr(data[count],ansi.attr,true);
|
|
lastwrite = data[count++];
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if(!ansi.sci){
|
|
|
|
switch(data[count]){
|
|
case '[':
|
|
ansi.sci=true;
|
|
break;
|
|
case '*':/* PC-98: clear screen */
|
|
if (IS_PC98_ARCH) {
|
|
/* NTS: Some reverse engineering of INT DCh ANSI handling shows that
|
|
* ESC * handling does nothing but execute the same code path
|
|
* as CTRL+Z handling, which clears the screen including removal
|
|
* of the function key row, then calls into the code path for
|
|
* 0x1E (RECORD SEPARATOR) handling which then positions the
|
|
* cursor to home position (upper left corner of the screen).
|
|
*
|
|
* boot144.dsk ref NOTES
|
|
*
|
|
* 0ADC:0B84 ESC * handling
|
|
* 0ADC:117D CTRL+Z handling
|
|
* 0ADC:1516 Fill (clear) the screen
|
|
* 0ADC:13FF Remove function key row
|
|
* 0ADC:116B 0x1E RECORD SEPARATOR handling
|
|
*/
|
|
Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
|
|
|
|
/* reverse engineering of a bootdisk shows that ESC * (and CTRL+Z) also remove the function key row */
|
|
void update_pc98_function_row(bool enable);
|
|
update_pc98_function_row(false);
|
|
|
|
INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
|
|
Real_INT10_SetCursorPos(0,0,page);
|
|
ClearAnsi();
|
|
}
|
|
else {
|
|
LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unknown char %c after a esc",data[count]); /*prob () */
|
|
ClearAnsi();
|
|
}
|
|
break;
|
|
case '7': /* save cursor pos + attr TODO */
|
|
case '8': /* restore this TODO */
|
|
case 'D':/* scrolling DOWN TODO */
|
|
case 'M':/* scrolling UP TODO */
|
|
default:
|
|
LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unknown char %c after a esc",data[count]); /*prob () */
|
|
ClearAnsi();
|
|
break;
|
|
}
|
|
count++;
|
|
continue;
|
|
}
|
|
/*ansi.esc and ansi.sci are true */
|
|
Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
|
|
if (isdigit(data[count])) {
|
|
assert(ansi.numberofarg < NUMBER_ANSI_DATA);
|
|
ansi.data[ansi.numberofarg]=10*ansi.data[ansi.numberofarg]+(data[count]-'0');
|
|
}
|
|
else if (data[count] == ';') {
|
|
if ((ansi.numberofarg+1) < NUMBER_ANSI_DATA)
|
|
ansi.numberofarg++;
|
|
}
|
|
else if (ansi.pc98rab) {
|
|
assert(IS_PC98_ARCH);
|
|
|
|
switch(data[count]){
|
|
case 'h': /* SET MODE (if code =7 enable linewrap) */
|
|
case 'l': /* RESET MODE */
|
|
switch (ansi.data[0]) {
|
|
case 1: // show/hide function key row
|
|
void update_pc98_function_row(bool enable);
|
|
update_pc98_function_row(data[count] == 'l');
|
|
ansi.nrows = real_readb(0x60,0x112)+1;
|
|
break;
|
|
case 3: // clear screen (doesn't matter if l or h)
|
|
INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
|
|
Real_INT10_SetCursorPos(0,0,page);
|
|
break;
|
|
case 5: // show/hide cursor
|
|
void PC98_show_cursor(bool show);
|
|
PC98_show_cursor(data[count] == 'l');
|
|
mem_writeb(0x71B,data[count] == 'l' ? 0x01 : 0x00); /* 60:11B cursor display state */
|
|
break;
|
|
default:
|
|
LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled esc [ > %d %c",ansi.data[0],data[count]);
|
|
break;
|
|
};
|
|
|
|
ClearAnsi();
|
|
break;
|
|
default:
|
|
LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled char %c in esc [ >",data[count]);
|
|
ClearAnsi();
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch(data[count]){
|
|
case 'm': /* SGR */
|
|
// NEC's ANSI driver always resets at the beginning
|
|
if(IS_PC98_ARCH) {
|
|
ansi.attr = DefaultANSIAttr();
|
|
}
|
|
for(i=0;i<=ansi.numberofarg;i++){
|
|
const Bit8u COLORFLAGS[][8] = {
|
|
// Black Red Green Yellow Blue Pink Cyan White
|
|
{ 0x0, 0x4, 0x2, 0x6, 0x1, 0x5, 0x3, 0x7 }, /* IBM */
|
|
{ 0x0, 0x40, 0x80, 0xC0, 0x20, 0x60, 0xA0, 0xE0 }, /* PC-98 */
|
|
};
|
|
const auto &flagset = COLORFLAGS[IS_PC98_ARCH];
|
|
|
|
if(IS_PC98_ARCH) {
|
|
// Convert alternate color codes to regular ones
|
|
if(ansi.data[i] >= 17 && ansi.data[i] <= 23) {
|
|
const Bit8u convtbl[] = {
|
|
31, 34, 35, 32, 33, 36, 37
|
|
};
|
|
ansi.data[i] = convtbl[ansi.data[i] - 17];
|
|
}
|
|
}
|
|
|
|
ansi.enabled=true;
|
|
switch(ansi.data[i]){
|
|
case 0: /* normal */
|
|
//Real ansi does this as well. (should do current defaults)
|
|
ansi.Disable();
|
|
break;
|
|
case 1: /* bold mode on*/
|
|
// FIXME: According to http://www.ninton.co.jp/?p=11, this
|
|
// should set some sort of "highlight" flag in monochrome
|
|
// mode, but I have no idea how to even enter that mode.
|
|
ansi.attr |= IS_PC98_ARCH ? 0 : 0x08;
|
|
break;
|
|
case 2: /* PC-98 "Bit 4" */
|
|
ansi.attr |= IS_PC98_ARCH ? 0x10 : 0;
|
|
break;
|
|
case 4: /* underline */
|
|
if(IS_PC98_ARCH) {
|
|
ansi.attr |= 0x08;
|
|
} else {
|
|
LOG(LOG_IOCTL, LOG_NORMAL)("ANSI:no support for underline yet");
|
|
}
|
|
break;
|
|
case 5: /* blinking */
|
|
ansi.attr |= IS_PC98_ARCH ? 0x02 : 0x80;
|
|
break;
|
|
case 7: /* reverse */
|
|
//Just like real ansi. (should do use current colors reversed)
|
|
if(IS_PC98_ARCH) {
|
|
ansi.attr |= 0x04;
|
|
} else {
|
|
ansi.attr = 0x70;
|
|
}
|
|
break;
|
|
case 8: /* PC-98 secret */
|
|
case 16:
|
|
ansi.attr &= IS_PC98_ARCH ? 0xFE : 0xFF;
|
|
break;
|
|
case 30: /* fg color black */
|
|
case 31: /* fg color red */
|
|
case 32: /* fg color green */
|
|
case 33: /* fg color yellow */
|
|
case 34: /* fg color blue */
|
|
case 35: /* fg color magenta */
|
|
case 36: /* fg color cyan */
|
|
case 37: /* fg color white */
|
|
ansi.attr &= ~(flagset[7]);
|
|
ansi.attr |= (flagset[ansi.data[i] - 30]);
|
|
break;
|
|
case 40:
|
|
case 41:
|
|
case 42:
|
|
case 43:
|
|
case 44:
|
|
case 45:
|
|
case 46:
|
|
case 47: {
|
|
Bit8u shift = IS_PC98_ARCH ? 0 : 4;
|
|
ansi.attr &= ~(flagset[7] << shift);
|
|
ansi.attr |= (flagset[ansi.data[i] - 40] << shift);
|
|
ansi.attr |= IS_PC98_ARCH ? 0x04 : 0;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (IS_PC98_ARCH) mem_writeb(0x71D,ansi.attr); // 60:11D
|
|
ClearAnsi();
|
|
break;
|
|
case 'f':
|
|
case 'H':/* Cursor Pos*/
|
|
if(!ansi.warned) { //Inform the debugger that ansi is used.
|
|
ansi.warned = true;
|
|
LOG(LOG_IOCTL,LOG_WARN)("ANSI SEQUENCES USED");
|
|
}
|
|
/* Turn them into positions that are on the screen */
|
|
if(ansi.data[0] == 0) ansi.data[0] = 1;
|
|
if(ansi.data[1] == 0) ansi.data[1] = 1;
|
|
if(ansi.data[0] > ansi.nrows) ansi.data[0] = (Bit8u)ansi.nrows;
|
|
if(ansi.data[1] > ansi.ncols) ansi.data[1] = (Bit8u)ansi.ncols;
|
|
Real_INT10_SetCursorPos(--(ansi.data[0]),--(ansi.data[1]),page); /*ansi=1 based, int10 is 0 based */
|
|
ClearAnsi();
|
|
break;
|
|
/* cursor up down and forward and backward only change the row or the col not both */
|
|
case 'A': /* cursor up*/
|
|
col=CURSOR_POS_COL(page) ;
|
|
row=CURSOR_POS_ROW(page) ;
|
|
tempdata = (ansi.data[0]? ansi.data[0] : 1);
|
|
if(tempdata > row) { row=0; }
|
|
else { row-=tempdata;}
|
|
Real_INT10_SetCursorPos(row,col,page);
|
|
ClearAnsi();
|
|
break;
|
|
case 'B': /*cursor Down */
|
|
col=CURSOR_POS_COL(page) ;
|
|
row=CURSOR_POS_ROW(page) ;
|
|
tempdata = (ansi.data[0]? ansi.data[0] : 1);
|
|
if(tempdata + static_cast<Bitu>(row) >= ansi.nrows)
|
|
{ row = ansi.nrows - 1;}
|
|
else { row += tempdata; }
|
|
Real_INT10_SetCursorPos(row,col,page);
|
|
ClearAnsi();
|
|
break;
|
|
case 'C': /*cursor forward */
|
|
col=CURSOR_POS_COL(page);
|
|
row=CURSOR_POS_ROW(page);
|
|
tempdata=(ansi.data[0]? ansi.data[0] : 1);
|
|
if(tempdata + static_cast<Bitu>(col) >= ansi.ncols)
|
|
{ col = ansi.ncols - 1;}
|
|
else { col += tempdata;}
|
|
Real_INT10_SetCursorPos(row,col,page);
|
|
ClearAnsi();
|
|
break;
|
|
case 'D': /*Cursor Backward */
|
|
col=CURSOR_POS_COL(page);
|
|
row=CURSOR_POS_ROW(page);
|
|
tempdata=(ansi.data[0]? ansi.data[0] : 1);
|
|
if(tempdata > col) {col = 0;}
|
|
else { col -= tempdata;}
|
|
Real_INT10_SetCursorPos(row,col,page);
|
|
ClearAnsi();
|
|
break;
|
|
case 'J': /*erase screen and move cursor home*/
|
|
if(ansi.data[0]==0) ansi.data[0]=2;
|
|
if(ansi.data[0]!=2) {/* every version behaves like type 2 */
|
|
LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: esc[%dJ called : not supported handling as 2",ansi.data[0]);
|
|
}
|
|
INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
|
|
ClearAnsi();
|
|
Real_INT10_SetCursorPos(0,0,page);
|
|
break;
|
|
case 'h': /* SET MODE (if code =7 enable linewrap) */
|
|
case 'I': /* RESET MODE */
|
|
LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: set/reset mode called(not supported)");
|
|
ClearAnsi();
|
|
break;
|
|
case 'u': /* Restore Cursor Pos */
|
|
Real_INT10_SetCursorPos(ansi.saverow,ansi.savecol,page);
|
|
ClearAnsi();
|
|
break;
|
|
case 's': /* SAVE CURSOR POS */
|
|
ansi.savecol=CURSOR_POS_COL(page);
|
|
ansi.saverow=CURSOR_POS_ROW(page);
|
|
ClearAnsi();
|
|
break;
|
|
case 'K': /* erase till end of line (don't touch cursor) */
|
|
col = CURSOR_POS_COL(page);
|
|
row = CURSOR_POS_ROW(page);
|
|
INT10_WriteChar(' ',ansi.attr,page,ansi.ncols-col,true); //Real_WriteChar(ansi.ncols-col,row,page,' ',ansi.attr,true);
|
|
|
|
//for(i = col;i<(Bitu) ansi.ncols; i++) INT10_TeletypeOutputAttr(' ',ansi.attr,true);
|
|
Real_INT10_SetCursorPos(row,col,page);
|
|
ClearAnsi();
|
|
break;
|
|
case 'M': /* delete line (NANSI) */
|
|
col = CURSOR_POS_COL(page);
|
|
row = CURSOR_POS_ROW(page);
|
|
INT10_ScrollWindow(row,0,ansi.nrows-1,ansi.ncols-1,ansi.data[0]? -ansi.data[0] : -1,ansi.attr,0xFF);
|
|
ClearAnsi();
|
|
break;
|
|
case '>':/* proprietary NEC PC-98 MS-DOS codes (??) */
|
|
if (IS_PC98_ARCH) {
|
|
ansi.pc98rab = true;
|
|
}
|
|
else {
|
|
LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: ESC [ > not supported outside PC-98 mode");
|
|
ClearAnsi();
|
|
}
|
|
break;
|
|
case 'l':/* (if code =7) disable linewrap */
|
|
case 'p':/* reassign keys (needs strings) */
|
|
case 'i':/* printer stuff */
|
|
default:
|
|
LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled char %c in esc[",data[count]);
|
|
ClearAnsi();
|
|
break;
|
|
}
|
|
}
|
|
count++;
|
|
}
|
|
*size=count;
|
|
return true;
|
|
}
|
|
|
|
bool device_CON::Seek(Bit32u * pos,Bit32u type) {
|
|
(void)pos; // UNUSED
|
|
(void)type; // UNUSED
|
|
// seek is valid
|
|
*pos = 0;
|
|
return true;
|
|
}
|
|
|
|
bool device_CON::Close() {
|
|
return true;
|
|
}
|
|
|
|
extern bool dos_con_use_int16_to_detect_input;
|
|
|
|
Bit16u device_CON::GetInformation(void) {
|
|
if (dos_con_use_int16_to_detect_input || IS_PC98_ARCH) {
|
|
Bit16u ret = 0x80D3; /* No Key Available */
|
|
|
|
/* DOSBox-X behavior: Use INT 16h AH=0x11 Query keyboard status/preview key.
|
|
* The reason we do this is some DOS programs actually rely on hooking INT 16h
|
|
* to manipulate, hide, or transform what the DOS CON driver sees as well as
|
|
* itself. Perhaps the most disgusting example of this behavior would be the
|
|
* SCANDISK.EXE utility in Microsoft MS-DOS 6.22, which apparently relies on
|
|
* hooking INT 16h in this way to catch the Escape, CTRL+C, and some other
|
|
* scan codes in order to "eat" the scan codes before they get back to DOS.
|
|
* The reason they can get away with it apparently and still respond properly
|
|
* to those keys, is because the MS-DOS 6.22 CON driver always calls INT 16h
|
|
* AH=0x11 first before calling INT 16h AH=0x10 to fetch the scan code.
|
|
*
|
|
* Without this fix, SCANDISK.EXE does not respond properly to Escape and
|
|
* a few other keys. Pressing Escape will do nothing until you hit any other
|
|
* key, at which point it suddenly acts upon the Escape key.
|
|
*
|
|
* Since Scandisk is using INT 21h AH=0x0B to query STDIN during this time,
|
|
* this implementation is a good "halfway" compromise in that this call
|
|
* will trigger the INT 16h AH=0x11 hook it relies on. */
|
|
if (readcache || dev_con_pos < dev_con_max) return 0x8093; /* key available */
|
|
|
|
Bitu saved_ax = reg_ax;
|
|
|
|
reg_ah = (IS_EGAVGA_ARCH)?0x11:0x1; // check for keystroke
|
|
|
|
/* FIXME: PC-98 emulation should eventually use CONIO emulation that
|
|
* better emulates the actual platform. The purpose of this
|
|
* hack is to allow our code to call into INT 16h without
|
|
* setting up an INT 16h vector */
|
|
if (IS_PC98_ARCH)
|
|
INT16_Handler_Wrap();
|
|
else
|
|
CALLBACK_RunRealInt(0x16);
|
|
|
|
if (!GETFLAG(ZF)) { /* key is present, waiting to be returned on AH=0x10 or AH=0x00 */
|
|
if (IS_PC98_ARCH && reg_al == 0) {
|
|
/* some scan codes are ignored by CON, and wouldn't read anything.
|
|
* while we're at it, take the scan code and convert it into ANSI here
|
|
* so that Read() returns it immediately instead of doing this conversion itself.
|
|
* This way we never block when we SAID a key was available that gets ignored. */
|
|
if (CommonPC98ExtScanConversionToReadBuf(reg_ah))
|
|
ret = 0x8093; /* Key Available */
|
|
else
|
|
ret = 0x80D3; /* No Key Available */
|
|
|
|
/* need to consume the key. if it generated anything it will be returned to Read()
|
|
* through dev_con_readbuf[] */
|
|
reg_ah=0x0;
|
|
|
|
/* FIXME: PC-98 emulation should eventually use CONIO emulation that
|
|
* better emulates the actual platform. The purpose of this
|
|
* hack is to allow our code to call into INT 16h without
|
|
* setting up an INT 16h vector */
|
|
INT16_Handler_Wrap();
|
|
}
|
|
else {
|
|
ret = 0x8093; /* Key Available */
|
|
}
|
|
}
|
|
|
|
reg_ax = saved_ax;
|
|
return ret;
|
|
}
|
|
else {
|
|
/* DOSBox mainline behavior: alternate "fast" way through direct manipulation of keyboard scan buffer */
|
|
Bit16u head=mem_readw(BIOS_KEYBOARD_BUFFER_HEAD);
|
|
Bit16u tail=mem_readw(BIOS_KEYBOARD_BUFFER_TAIL);
|
|
|
|
if ((head==tail) && !readcache) return 0x80D3; /* No Key Available */
|
|
if (readcache || real_readw(0x40,head)) return 0x8093; /* Key Available */
|
|
|
|
/* remove the zero from keyboard buffer */
|
|
Bit16u start=mem_readw(BIOS_KEYBOARD_BUFFER_START);
|
|
Bit16u end =mem_readw(BIOS_KEYBOARD_BUFFER_END);
|
|
head+=2;
|
|
if (head>=end) head=start;
|
|
mem_writew(BIOS_KEYBOARD_BUFFER_HEAD,head);
|
|
}
|
|
|
|
return 0x80D3; /* No Key Available */
|
|
}
|
|
|
|
device_CON::device_CON() {
|
|
SetName("CON");
|
|
readcache=0;
|
|
lastwrite=0;
|
|
ansi.Disable();
|
|
if (IS_PC98_ARCH) {
|
|
// NTS: On real hardware, the BIOS does NOT manage the console at all.
|
|
// TTY handling is entirely handled by MS-DOS.
|
|
ansi.ncols=80;
|
|
ansi.nrows=25 - 1;
|
|
// the DOS kernel will call on this function to disable, and SDLmain
|
|
// will call on to enable
|
|
}
|
|
else {
|
|
ansi.ncols=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); //should be updated once set/reset mode is implemented
|
|
ansi.nrows=real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1;
|
|
}
|
|
ansi.saverow=0;
|
|
ansi.savecol=0;
|
|
ansi.warned=false;
|
|
ClearAnsi();
|
|
}
|
|
|
|
void device_CON::ClearAnsi(void){
|
|
for(Bit8u i=0; i<NUMBER_ANSI_DATA;i++) ansi.data[i]=0;
|
|
ansi.pc98rab=false;
|
|
ansi.esc=false;
|
|
ansi.sci=false;
|
|
ansi.numberofarg=0;
|
|
}
|
|
|