mirror of
https://github.com/makarcz/vm6502.git
synced 2025-05-13 06:38:11 +00:00
Char I/O
Improvements to character I/O emulation: * Performance. * Use native console STDIO in execute mode, text display emulation in debug mode. * Always shadow character I/O with text device emulation, even when native STDIO is used.
This commit is contained in:
parent
d99ed03232
commit
dce9babd36
647
Display.cpp
647
Display.cpp
@ -1,312 +1,335 @@
|
||||
#include "Display.h"
|
||||
#include <ctype.h>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
Display::Display()
|
||||
{
|
||||
InitScr();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
Display::~Display()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: InitScr()
|
||||
* Purpose: Initialize screen.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Display::InitScr()
|
||||
{
|
||||
mScrLines = SCREENDIM_ROW;
|
||||
mScrColumns = SCREENDIM_COL;
|
||||
mShellConsoleWidth = GetConsoleWidth();
|
||||
if (mScrColumns > mShellConsoleWidth) {
|
||||
mScrColumns = mShellConsoleWidth;
|
||||
}
|
||||
ClrScr();
|
||||
}
|
||||
|
||||
#if defined(WINDOWS)
|
||||
|
||||
#include <windows.h>
|
||||
#include <conio.h>
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetConsoleWidth()
|
||||
* Purpose: Obtain the width of shell console (the real one, not
|
||||
* the emulated one) on Windows.
|
||||
* Arguments: n/a
|
||||
* Returns: int - width of the shell console.
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int Display::GetConsoleWidth()
|
||||
{
|
||||
HANDLE hStdOut;
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
|
||||
hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
|
||||
if (hStdOut == INVALID_HANDLE_VALUE) return -1;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo( hStdOut, &csbi )) return -2;
|
||||
|
||||
return csbi.dwSize.X;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(LINUX)
|
||||
|
||||
#include <termcap.h>
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetConsoleWidth()
|
||||
* Purpose: Obtain the width of shell console (the real one, not
|
||||
* the emulated one) on Linux.
|
||||
* Arguments: n/a
|
||||
* Returns: int - width of the shell console.
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int Display::GetConsoleWidth()
|
||||
{
|
||||
unsigned int conwidth = SCREENDIM_COL;
|
||||
char *termtype = getenv("TERM");
|
||||
static char termbuf[2048];
|
||||
|
||||
if (tgetent(termbuf, termtype) < 0) {
|
||||
cout << "WARNING: Could not access the termcap data base." << endl;
|
||||
cout << " Unable to determine console width." << endl;
|
||||
} else {
|
||||
conwidth = tgetnum("co");
|
||||
}
|
||||
|
||||
return conwidth;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Display::ScrollUp()
|
||||
{
|
||||
for (unsigned int row=0; row<mScrLines-1; row++) {
|
||||
for (unsigned int col=0; col<mScrColumns; col++) {
|
||||
mScreen[col][row] = mScreen[col][row+1];
|
||||
}
|
||||
}
|
||||
for (unsigned int col=0; col<mScrColumns; col++) {
|
||||
mScreen[col][mScrLines-1] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GotoXY()
|
||||
* Purpose: Move cursor to new coordinates.
|
||||
* Arguments: col, row - integer values, new cursor coordinates
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Display::GotoXY(unsigned int col, unsigned int row)
|
||||
{
|
||||
if (col < mScrColumns && row < mScrLines) {
|
||||
mCursorCoord.col = col;
|
||||
mCursorCoord.row = row;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
bool Display::IsSpecChar(char c)
|
||||
{
|
||||
bool ret = false;
|
||||
char sct[] = {SCREENSPECCHARS_NL,
|
||||
SCREENSPECCHARS_CR,
|
||||
SCREENSPECCHARS_TB,
|
||||
SCREENSPECCHARS_BS,
|
||||
SCREENSPECCHARS_BE,
|
||||
0};
|
||||
|
||||
for (unsigned int i=0; i<strlen(sct); i++) {
|
||||
if (c == sct[i]) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: PutChar()
|
||||
* Purpose: Output character to console. If going outside
|
||||
* lower-right corner, scroll the contents up.
|
||||
* Arguments: c - character to output.
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Display::PutChar(char c)
|
||||
{
|
||||
if (isalnum(c) || ispunct(c) || isspace(c) || IsSpecChar(c))
|
||||
{
|
||||
if (c == SCREENSPECCHARS_NL) {
|
||||
//mCursorCoord.col = 0;
|
||||
mCursorCoord.row++;
|
||||
if (mCursorCoord.row >= mScrLines) {
|
||||
ScrollUp();
|
||||
mCursorCoord.row = mScrLines-1;
|
||||
}
|
||||
} else if (c == SCREENSPECCHARS_CR) {
|
||||
mCursorCoord.col = 0;
|
||||
} else if (c == SCREENSPECCHARS_TB) {
|
||||
mCursorCoord.col += TABSIZE;
|
||||
if (mCursorCoord.col >= mScrColumns) {
|
||||
mCursorCoord.col = mScrColumns-1; // must work on it some more
|
||||
}
|
||||
} else if (c == SCREENSPECCHARS_BS) {
|
||||
if (mCursorCoord.col > 0) mCursorCoord.col--;
|
||||
} else if (c == SCREENSPECCHARS_BE) {
|
||||
// no action
|
||||
}
|
||||
else {
|
||||
mScreen[mCursorCoord.col][mCursorCoord.row] = c;
|
||||
mCursorCoord.col++;
|
||||
if (mCursorCoord.col >= mScrColumns) {
|
||||
mCursorCoord.col = 0;
|
||||
mCursorCoord.row++;
|
||||
if (mCursorCoord.row >= mScrLines) {
|
||||
ScrollUp();
|
||||
mCursorCoord.row = mScrLines-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ClrScr()
|
||||
* Purpose: Fill the screen with spaces. Set cursor in left-upper
|
||||
* corner.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Display::ClrScr()
|
||||
{
|
||||
for (unsigned int col=0; col<mScrColumns; col++) {
|
||||
for (unsigned int row=0; row<mScrLines; row++) {
|
||||
mScreen[col][row] = ' ';
|
||||
}
|
||||
}
|
||||
mCursorCoord.col = mCursorCoord.row = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char Display::GetCharAt(unsigned int col, unsigned int row)
|
||||
{
|
||||
char c = -1;
|
||||
|
||||
if (col < mScrColumns && row < mScrLines)
|
||||
c = mScreen[col][row];
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ShowScr()
|
||||
* Purpose: Display contents of the emulated console on a... well,
|
||||
* real console.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Display::ShowScr()
|
||||
{
|
||||
for (unsigned int row=0; row<mScrLines; row++) {
|
||||
string line;
|
||||
line.clear();
|
||||
for (unsigned int col=0; col<mScrColumns; col++) {
|
||||
char c = mScreen[col][row];
|
||||
if (mCursorCoord.col == col && mCursorCoord.row == row) {
|
||||
c = '_';
|
||||
}
|
||||
line = line + c;
|
||||
}
|
||||
cout << line;
|
||||
// add extra NL if the real console is wider than emulated one
|
||||
if (mShellConsoleWidth > mScrColumns) cout << endl;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCursorCoord()
|
||||
* Purpose: Get cursor coordinates.
|
||||
* Arguments: n/a
|
||||
* Returns: pointer to cursor coordinates
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
CursorCoord *Display::GetCursorCoord()
|
||||
{
|
||||
return &mCursorCoord;
|
||||
}
|
||||
|
||||
} // namespace MKBasic
|
||||
#include "Display.h"
|
||||
#include <ctype.h>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
Display::Display()
|
||||
{
|
||||
InitScr();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
Display::~Display()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: InitScr()
|
||||
* Purpose: Initialize screen.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Display::InitScr()
|
||||
{
|
||||
mLastChar = 0;
|
||||
mScrLines = SCREENDIM_ROW;
|
||||
mScrColumns = SCREENDIM_COL;
|
||||
mShellConsoleWidth = GetConsoleWidth();
|
||||
if (mScrColumns > mShellConsoleWidth) {
|
||||
mScrColumns = mShellConsoleWidth;
|
||||
}
|
||||
ClrScr();
|
||||
}
|
||||
|
||||
#if defined(WINDOWS)
|
||||
|
||||
#include <windows.h>
|
||||
#include <conio.h>
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetConsoleWidth()
|
||||
* Purpose: Obtain the width of shell console (the real one, not
|
||||
* the emulated one) on Windows.
|
||||
* Arguments: n/a
|
||||
* Returns: int - width of the shell console.
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int Display::GetConsoleWidth()
|
||||
{
|
||||
HANDLE hStdOut;
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
|
||||
hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
|
||||
if (hStdOut == INVALID_HANDLE_VALUE) return -1;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo( hStdOut, &csbi )) return -2;
|
||||
|
||||
return csbi.dwSize.X;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(LINUX)
|
||||
|
||||
#include <termcap.h>
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetConsoleWidth()
|
||||
* Purpose: Obtain the width of shell console (the real one, not
|
||||
* the emulated one) on Linux.
|
||||
* Arguments: n/a
|
||||
* Returns: int - width of the shell console.
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int Display::GetConsoleWidth()
|
||||
{
|
||||
unsigned int conwidth = SCREENDIM_COL;
|
||||
char *termtype = getenv("TERM");
|
||||
static char termbuf[2048];
|
||||
|
||||
if (tgetent(termbuf, termtype) < 0) {
|
||||
cout << "WARNING: Could not access the termcap data base." << endl;
|
||||
cout << " Unable to determine console width." << endl;
|
||||
} else {
|
||||
conwidth = tgetnum("co");
|
||||
}
|
||||
|
||||
return conwidth;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Display::ScrollUp()
|
||||
{
|
||||
for (unsigned int row=0; row<mScrLines-1; row++) {
|
||||
for (unsigned int col=0; col<mScrColumns; col++) {
|
||||
mScreen[row][col] = mScreen[row+1][col];
|
||||
}
|
||||
}
|
||||
for (unsigned int col=0; col<mScrColumns; col++) {
|
||||
mScreen[mScrLines-1][col] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GotoXY()
|
||||
* Purpose: Move cursor to new coordinates.
|
||||
* Arguments: col, row - integer values, new cursor coordinates
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Display::GotoXY(unsigned int col, unsigned int row)
|
||||
{
|
||||
if (col < mScrColumns && row < mScrLines) {
|
||||
mCursorCoord.col = col;
|
||||
mCursorCoord.row = row;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
bool Display::IsSpecChar(char c)
|
||||
{
|
||||
bool ret = false;
|
||||
char sct[] = {SCREENSPECCHARS_NL,
|
||||
SCREENSPECCHARS_CR,
|
||||
SCREENSPECCHARS_TB,
|
||||
SCREENSPECCHARS_BS,
|
||||
SCREENSPECCHARS_BE,
|
||||
0};
|
||||
|
||||
for (unsigned int i=0; i<strlen(sct); i++) {
|
||||
if (c == sct[i]) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: PutChar()
|
||||
* Purpose: Output character to console. If going outside
|
||||
* lower-right corner, scroll the contents up.
|
||||
* Arguments: c - character to output.
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Display::PutChar(char c)
|
||||
{
|
||||
if (isalnum(c) || ispunct(c) || isspace(c) || IsSpecChar(c))
|
||||
{
|
||||
if (c == SCREENSPECCHARS_NL) {
|
||||
mLastChar = SCREENSPECCHARS_NL;
|
||||
//mCursorCoord.col = 0;
|
||||
mCursorCoord.row++;
|
||||
if (mCursorCoord.row >= mScrLines) {
|
||||
ScrollUp();
|
||||
mCursorCoord.row = mScrLines-1;
|
||||
}
|
||||
} else if (c == SCREENSPECCHARS_CR) {
|
||||
mLastChar = SCREENSPECCHARS_CR;
|
||||
mCursorCoord.col = 0;
|
||||
} else if (c == SCREENSPECCHARS_TB) {
|
||||
mLastChar = SCREENSPECCHARS_TB;
|
||||
mCursorCoord.col += TABSIZE;
|
||||
if (mCursorCoord.col >= mScrColumns) {
|
||||
mCursorCoord.col = mScrColumns-1; // must work on it some more
|
||||
}
|
||||
} else if (c == SCREENSPECCHARS_BS) {
|
||||
mLastChar = SCREENSPECCHARS_BS;
|
||||
if (mCursorCoord.col > 0) mCursorCoord.col--;
|
||||
} else if (c == SCREENSPECCHARS_BE) {
|
||||
mLastChar = SCREENSPECCHARS_BE;
|
||||
// no action
|
||||
}
|
||||
else {
|
||||
mScreen[mCursorCoord.row][mCursorCoord.col] = c;
|
||||
mLastChar = c;
|
||||
mCursorCoord.col++;
|
||||
if (mCursorCoord.col >= mScrColumns) {
|
||||
mCursorCoord.col = 0;
|
||||
mCursorCoord.row++;
|
||||
if (mCursorCoord.row >= mScrLines) {
|
||||
ScrollUp();
|
||||
mCursorCoord.row = mScrLines-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ClrScr()
|
||||
* Purpose: Fill the screen with spaces. Set cursor in left-upper
|
||||
* corner.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Display::ClrScr()
|
||||
{
|
||||
for (unsigned int col=0; col<mScrColumns; col++) {
|
||||
for (unsigned int row=0; row<mScrLines; row++) {
|
||||
mScreen[row][col] = ' ';
|
||||
}
|
||||
}
|
||||
mCursorCoord.col = mCursorCoord.row = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char Display::GetCharAt(unsigned int col, unsigned int row)
|
||||
{
|
||||
char c = -1;
|
||||
|
||||
if (col < mScrColumns && row < mScrLines)
|
||||
c = mScreen[row][col];
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ShowScr()
|
||||
* Purpose: Display contents of the emulated console on a... well,
|
||||
* real console.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Display::ShowScr()
|
||||
{
|
||||
char buf[SCREENDIM_COL] = {0};
|
||||
string scr;
|
||||
scr.clear();
|
||||
for (unsigned int row=0; row<mScrLines; row++) {
|
||||
char *linebuf = &(mScreen[row][0]);
|
||||
memset(buf, 0, mScrColumns);
|
||||
strncpy(buf, linebuf, mScrColumns);
|
||||
buf[mScrColumns] = 0;
|
||||
if (mCursorCoord.row == row) {
|
||||
buf[mCursorCoord.col] = '_';
|
||||
}
|
||||
string line(buf);
|
||||
// add extra NL if the real console is wider than emulated one
|
||||
if (mShellConsoleWidth > mScrColumns) line = line + "\n";
|
||||
scr = scr + line;
|
||||
}
|
||||
cout << scr;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCursorCoord()
|
||||
* Purpose: Get cursor coordinates.
|
||||
* Arguments: n/a
|
||||
* Returns: pointer to cursor coordinates
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
CursorCoord *Display::GetCursorCoord()
|
||||
{
|
||||
return &mCursorCoord;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char Display::GetLastChar()
|
||||
{
|
||||
return mLastChar;
|
||||
}
|
||||
|
||||
} // namespace MKBasic
|
||||
|
122
Display.h
122
Display.h
@ -1,60 +1,62 @@
|
||||
#ifndef DISPLAY_H
|
||||
#define DISPLAY_H
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#define TABSIZE 4
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
enum eScreenDimensions {
|
||||
SCREENDIM_COL = 80,
|
||||
SCREENDIM_ROW = 24
|
||||
};
|
||||
|
||||
enum eScreenSpecChars {
|
||||
SCREENSPECCHARS_NL = (int)'\n', // new line
|
||||
SCREENSPECCHARS_CR = (int)'\r', // caret
|
||||
SCREENSPECCHARS_TB = (int)'\t', // tab
|
||||
SCREENSPECCHARS_BS = (int)'\b', // backspace
|
||||
SCREENSPECCHARS_BE = (int)'\a' // bell
|
||||
};
|
||||
|
||||
struct CursorCoord {
|
||||
unsigned int row;
|
||||
unsigned int col;
|
||||
};
|
||||
|
||||
class Display
|
||||
{
|
||||
public:
|
||||
|
||||
Display();
|
||||
~Display();
|
||||
void GotoXY(unsigned int col, unsigned int row);
|
||||
void PutChar(char c);
|
||||
void ClrScr();
|
||||
char GetCharAt(unsigned int col, unsigned int row);
|
||||
void ShowScr();
|
||||
CursorCoord *GetCursorCoord();
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
|
||||
char mScreen[SCREENDIM_COL][SCREENDIM_ROW];
|
||||
CursorCoord mCursorCoord;
|
||||
unsigned int mShellConsoleWidth;
|
||||
unsigned int mScrLines;
|
||||
unsigned int mScrColumns;
|
||||
|
||||
void InitScr();
|
||||
void ScrollUp();
|
||||
bool IsSpecChar(char c);
|
||||
int GetConsoleWidth();
|
||||
|
||||
};
|
||||
|
||||
} // namespace MKBasic
|
||||
|
||||
#endif
|
||||
#ifndef DISPLAY_H
|
||||
#define DISPLAY_H
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#define TABSIZE 4
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
enum eScreenDimensions {
|
||||
SCREENDIM_COL = 80,
|
||||
SCREENDIM_ROW = 24
|
||||
};
|
||||
|
||||
enum eScreenSpecChars {
|
||||
SCREENSPECCHARS_NL = (int)'\n', // new line
|
||||
SCREENSPECCHARS_CR = (int)'\r', // caret
|
||||
SCREENSPECCHARS_TB = (int)'\t', // tab
|
||||
SCREENSPECCHARS_BS = (int)'\b', // backspace
|
||||
SCREENSPECCHARS_BE = (int)'\a' // bell
|
||||
};
|
||||
|
||||
struct CursorCoord {
|
||||
unsigned int row;
|
||||
unsigned int col;
|
||||
};
|
||||
|
||||
class Display
|
||||
{
|
||||
public:
|
||||
|
||||
Display();
|
||||
~Display();
|
||||
void GotoXY(unsigned int col, unsigned int row);
|
||||
void PutChar(char c);
|
||||
void ClrScr();
|
||||
char GetCharAt(unsigned int col, unsigned int row);
|
||||
void ShowScr();
|
||||
CursorCoord *GetCursorCoord();
|
||||
char GetLastChar();
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
|
||||
char mScreen[SCREENDIM_ROW][SCREENDIM_COL];
|
||||
char mLastChar;
|
||||
CursorCoord mCursorCoord;
|
||||
unsigned int mShellConsoleWidth;
|
||||
unsigned int mScrLines;
|
||||
unsigned int mScrColumns;
|
||||
|
||||
void InitScr();
|
||||
void ScrollUp();
|
||||
bool IsSpecChar(char c);
|
||||
int GetConsoleWidth();
|
||||
|
||||
};
|
||||
|
||||
} // namespace MKBasic
|
||||
|
||||
#endif
|
||||
|
926
Memory.cpp
926
Memory.cpp
@ -1,463 +1,463 @@
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#if defined(WINDOWS)
|
||||
#include <conio.h>
|
||||
#endif
|
||||
|
||||
//#define DBG 1
|
||||
#if defined (DBG)
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
#endif
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
Memory::Memory()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
Memory::~Memory()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::Initialize()
|
||||
{
|
||||
unsigned short addr = 0;
|
||||
for (int i=0; i < 0xFFFF; i++) {
|
||||
m8bitMem[addr++] = 0;
|
||||
}
|
||||
mCharIOAddr = CHARIO_ADDR;
|
||||
mCharIOActive = false;
|
||||
mIOEcho = false;
|
||||
mInBufDataBegin = mInBufDataEnd = 0;
|
||||
mOutBufDataBegin = mOutBufDataEnd = 0;
|
||||
mROMBegin = ROM_BEGIN;
|
||||
mROMEnd = ROM_END;
|
||||
mROMActive = false;
|
||||
}
|
||||
|
||||
#if defined(LINUX)
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
struct termios orig_termios;
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void reset_terminal_mode()
|
||||
{
|
||||
tcsetattr(0, TCSANOW, &orig_termios);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::set_conio_terminal_mode()
|
||||
{
|
||||
struct termios new_termios;
|
||||
|
||||
/* take two copies - one for now, one for later */
|
||||
tcgetattr(0, &orig_termios);
|
||||
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
|
||||
|
||||
/* register cleanup handler, and set the new terminal mode */
|
||||
atexit(reset_terminal_mode);
|
||||
cfmakeraw(&new_termios);
|
||||
tcsetattr(0, TCSANOW, &new_termios);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int Memory::kbhit()
|
||||
{
|
||||
struct timeval tv = { 0L, 0L };
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(0, &fds);
|
||||
return select(1, &fds, NULL, NULL, &tv);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int Memory::getch()
|
||||
{
|
||||
int r;
|
||||
unsigned char c;
|
||||
if ((r = read(0, &c, sizeof(c))) < 0) {
|
||||
return r;
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::EnableROM()
|
||||
{
|
||||
mROMActive = true;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::DisableROM()
|
||||
{
|
||||
mROMActive = false;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::SetROM(unsigned short start, unsigned short end)
|
||||
{
|
||||
if (mROMEnd > mROMBegin) {
|
||||
mROMBegin = start;
|
||||
mROMEnd = end;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::EnableROM(unsigned short start, unsigned short end)
|
||||
{
|
||||
SetROM(start,end);
|
||||
EnableROM();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ReadCharKb()
|
||||
* Purpose: If char I/O active, read character from console
|
||||
* (non-blocking) and put in an input FIFO buffer.
|
||||
* Arguments: nonblock - if true, works in non-blocking mode
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned char Memory::ReadCharKb(bool nonblock)
|
||||
{
|
||||
unsigned char ret = 0;
|
||||
if (mCharIOActive) {
|
||||
#if defined(LINUX)
|
||||
set_conio_terminal_mode();
|
||||
#endif
|
||||
static int c = ' ';
|
||||
putchar('\n');
|
||||
if (mIOEcho && isprint(c)) putchar(c);
|
||||
else putchar(' ');
|
||||
fputs("<-Character Input (CTRL-Y to BREAK) ?\r",stdout);
|
||||
fflush(stdout);
|
||||
if (!nonblock) while(!kbhit());
|
||||
else c = 0;
|
||||
c = getch();
|
||||
#if defined(LINUX)
|
||||
if (c == 3) { // capture CTRL-C in CONIO mode
|
||||
reset_terminal_mode();
|
||||
kill(getpid(),SIGINT);
|
||||
}
|
||||
#endif
|
||||
fputs(" \r",stdout);
|
||||
fflush(stdout);
|
||||
mCharIOBufIn[mInBufDataEnd] = c;
|
||||
mInBufDataEnd++;
|
||||
if (mInBufDataEnd >= CHARIO_BUF_SIZE) mInBufDataEnd = 0;
|
||||
ret = c;
|
||||
#if defined(LINUX)
|
||||
reset_terminal_mode();
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharIn()
|
||||
* Purpose: Return character from the emulated character I/O FIFO
|
||||
* input buffer or -1 if FIFO empty or char I/O not active.
|
||||
* Arguments: n/a
|
||||
* Returns: character
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char Memory::GetCharIn()
|
||||
{
|
||||
char ret = -1;
|
||||
if (mCharIOActive) {
|
||||
if (mInBufDataEnd != mInBufDataBegin) {
|
||||
ret = mCharIOBufIn[mInBufDataBegin];
|
||||
mInBufDataBegin++;
|
||||
if (mInBufDataBegin >= CHARIO_BUF_SIZE) mInBufDataBegin = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharOut()
|
||||
* Purpose: Return character from the emulated character I/O FIFO
|
||||
* output buffer or -1 if FIFO empty or char I/O not
|
||||
* active.
|
||||
* Arguments: n/a
|
||||
* Returns: character
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char Memory::GetCharOut()
|
||||
{
|
||||
char ret = -1;
|
||||
if (mCharIOActive) {
|
||||
if (mOutBufDataEnd != mOutBufDataBegin) {
|
||||
ret = mCharIOBufOut[mOutBufDataBegin];
|
||||
mOutBufDataBegin++;
|
||||
if (mOutBufDataBegin >= CHARIO_BUF_SIZE) mOutBufDataBegin = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: PutCharIO()
|
||||
* Purpose: Put character in the output char I/O FIFO buffer.
|
||||
* Arguments: c - character
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::PutCharIO(char c)
|
||||
{
|
||||
if (mCharIOActive) {
|
||||
mCharIOBufOut[mOutBufDataEnd] = c;
|
||||
mOutBufDataEnd++;
|
||||
if (mOutBufDataEnd >= CHARIO_BUF_SIZE) mOutBufDataEnd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned char Memory::Peek8bit(unsigned short addr)
|
||||
{
|
||||
if (mCharIOActive && addr == mCharIOAddr) {
|
||||
m8bitMem[addr] = ReadCharKb(false); // blocking mode input
|
||||
} else if (mCharIOActive && addr == mCharIOAddr+1) {
|
||||
m8bitMem[addr] = ReadCharKb(true); // non-blocking mode input
|
||||
}
|
||||
|
||||
return m8bitMem[addr];
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short Memory::Peek16bit(unsigned short addr)
|
||||
{
|
||||
unsigned short ret = 0;
|
||||
|
||||
if (mCharIOActive && addr == mCharIOAddr) {
|
||||
m8bitMem[addr] = ReadCharKb(false); // blocking mode input
|
||||
} else if (mCharIOActive && addr == mCharIOAddr+1) {
|
||||
m8bitMem[addr] = ReadCharKb(true); // non-blocking mode input
|
||||
}
|
||||
|
||||
ret = m8bitMem[addr++];
|
||||
ret += m8bitMem[addr] * 256;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: Poke8bit()
|
||||
* Purpose: Write byte to specified memory location.
|
||||
* If the memory location is mapped to character I/O,
|
||||
* write value to output buffer and memory location.
|
||||
* If the memory location is protected (ROM), do not
|
||||
* write the value.
|
||||
* Arguments: addr - (0x0000..0xffff) memory address,
|
||||
* val - value (0x00..0xff)
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::Poke8bit(unsigned short addr, unsigned char val)
|
||||
{
|
||||
if (mCharIOActive && addr == mCharIOAddr)
|
||||
PutCharIO(val);
|
||||
if (!mROMActive || (addr < mROMBegin || addr > mROMEnd)) {
|
||||
m8bitMem[addr] = val;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: SetCharIO()
|
||||
* Purpose: Activates and sets an address of basic character I/O
|
||||
* emulation (console).
|
||||
* Arguments: addr - address of I/O area (0x0000..0xFFFF)
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::SetCharIO(unsigned short addr, bool echo)
|
||||
{
|
||||
mCharIOAddr = addr;
|
||||
mCharIOActive = true;
|
||||
mIOEcho = echo;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: DisableCharIO()
|
||||
* Purpose: Deactivates basic character I/O emulation (console).
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::DisableCharIO()
|
||||
{
|
||||
mCharIOActive = false;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharIOAddr()
|
||||
* Purpose: Returns current address of basic character I/O area.
|
||||
* Arguments: n/a
|
||||
* Returns: address of I/O area
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short Memory::GetCharIOAddr()
|
||||
{
|
||||
return mCharIOAddr;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short Memory::GetROMBegin()
|
||||
{
|
||||
return mROMBegin;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short Memory::GetROMEnd()
|
||||
{
|
||||
return mROMEnd;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
bool Memory::IsROMEnabled()
|
||||
{
|
||||
return mROMActive;
|
||||
}
|
||||
|
||||
} // namespace MKBasic
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#if defined(WINDOWS)
|
||||
#include <conio.h>
|
||||
#endif
|
||||
|
||||
//#define DBG 1
|
||||
#if defined (DBG)
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
#endif
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
Memory::Memory()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
Memory::~Memory()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::Initialize()
|
||||
{
|
||||
unsigned short addr = 0;
|
||||
for (int i=0; i < 0xFFFF; i++) {
|
||||
m8bitMem[addr++] = 0;
|
||||
}
|
||||
mCharIOAddr = CHARIO_ADDR;
|
||||
mCharIOActive = false;
|
||||
mIOEcho = false;
|
||||
mInBufDataBegin = mInBufDataEnd = 0;
|
||||
mOutBufDataBegin = mOutBufDataEnd = 0;
|
||||
mROMBegin = ROM_BEGIN;
|
||||
mROMEnd = ROM_END;
|
||||
mROMActive = false;
|
||||
}
|
||||
|
||||
#if defined(LINUX)
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
struct termios orig_termios;
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void reset_terminal_mode()
|
||||
{
|
||||
tcsetattr(0, TCSANOW, &orig_termios);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::set_conio_terminal_mode()
|
||||
{
|
||||
struct termios new_termios;
|
||||
|
||||
/* take two copies - one for now, one for later */
|
||||
tcgetattr(0, &orig_termios);
|
||||
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
|
||||
|
||||
/* register cleanup handler, and set the new terminal mode */
|
||||
atexit(reset_terminal_mode);
|
||||
cfmakeraw(&new_termios);
|
||||
tcsetattr(0, TCSANOW, &new_termios);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int Memory::kbhit()
|
||||
{
|
||||
struct timeval tv = { 0L, 0L };
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(0, &fds);
|
||||
return select(1, &fds, NULL, NULL, &tv);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int Memory::getch()
|
||||
{
|
||||
int r;
|
||||
unsigned char c;
|
||||
if ((r = read(0, &c, sizeof(c))) < 0) {
|
||||
return r;
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::EnableROM()
|
||||
{
|
||||
mROMActive = true;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::DisableROM()
|
||||
{
|
||||
mROMActive = false;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::SetROM(unsigned short start, unsigned short end)
|
||||
{
|
||||
if (mROMEnd > mROMBegin) {
|
||||
mROMBegin = start;
|
||||
mROMEnd = end;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::EnableROM(unsigned short start, unsigned short end)
|
||||
{
|
||||
SetROM(start,end);
|
||||
EnableROM();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ReadCharKb()
|
||||
* Purpose: If char I/O active, read character from console
|
||||
* (non-blocking) and put in an input FIFO buffer.
|
||||
* Arguments: nonblock - if true, works in non-blocking mode
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned char Memory::ReadCharKb(bool nonblock)
|
||||
{
|
||||
unsigned char ret = 0;
|
||||
if (mCharIOActive) {
|
||||
#if defined(LINUX)
|
||||
set_conio_terminal_mode();
|
||||
#endif
|
||||
static int c = ' ';
|
||||
//putchar('\n');
|
||||
if (mIOEcho && isprint(c)) putchar(c);
|
||||
//else putchar(' ');
|
||||
//fputs("<-Character Input (CTRL-Y to BREAK) ?\r",stdout);
|
||||
fflush(stdout);
|
||||
if (!nonblock) while(!kbhit());
|
||||
else c = 0;
|
||||
c = getch();
|
||||
#if defined(LINUX)
|
||||
if (c == 3) { // capture CTRL-C in CONIO mode
|
||||
reset_terminal_mode();
|
||||
kill(getpid(),SIGINT);
|
||||
}
|
||||
#endif
|
||||
//fputs(" \r",stdout);
|
||||
//fflush(stdout);
|
||||
mCharIOBufIn[mInBufDataEnd] = c;
|
||||
mInBufDataEnd++;
|
||||
if (mInBufDataEnd >= CHARIO_BUF_SIZE) mInBufDataEnd = 0;
|
||||
ret = c;
|
||||
#if defined(LINUX)
|
||||
reset_terminal_mode();
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharIn()
|
||||
* Purpose: Return character from the emulated character I/O FIFO
|
||||
* input buffer or -1 if FIFO empty or char I/O not active.
|
||||
* Arguments: n/a
|
||||
* Returns: character
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char Memory::GetCharIn()
|
||||
{
|
||||
char ret = -1;
|
||||
if (mCharIOActive) {
|
||||
if (mInBufDataEnd != mInBufDataBegin) {
|
||||
ret = mCharIOBufIn[mInBufDataBegin];
|
||||
mInBufDataBegin++;
|
||||
if (mInBufDataBegin >= CHARIO_BUF_SIZE) mInBufDataBegin = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharOut()
|
||||
* Purpose: Return character from the emulated character I/O FIFO
|
||||
* output buffer or -1 if FIFO empty or char I/O not
|
||||
* active.
|
||||
* Arguments: n/a
|
||||
* Returns: character
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char Memory::GetCharOut()
|
||||
{
|
||||
char ret = -1;
|
||||
if (mCharIOActive) {
|
||||
if (mOutBufDataEnd != mOutBufDataBegin) {
|
||||
ret = mCharIOBufOut[mOutBufDataBegin];
|
||||
mOutBufDataBegin++;
|
||||
if (mOutBufDataBegin >= CHARIO_BUF_SIZE) mOutBufDataBegin = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: PutCharIO()
|
||||
* Purpose: Put character in the output char I/O FIFO buffer.
|
||||
* Arguments: c - character
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::PutCharIO(char c)
|
||||
{
|
||||
if (mCharIOActive) {
|
||||
mCharIOBufOut[mOutBufDataEnd] = c;
|
||||
mOutBufDataEnd++;
|
||||
if (mOutBufDataEnd >= CHARIO_BUF_SIZE) mOutBufDataEnd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned char Memory::Peek8bit(unsigned short addr)
|
||||
{
|
||||
if (mCharIOActive && addr == mCharIOAddr) {
|
||||
m8bitMem[addr] = ReadCharKb(false); // blocking mode input
|
||||
} else if (mCharIOActive && addr == mCharIOAddr+1) {
|
||||
m8bitMem[addr] = ReadCharKb(true); // non-blocking mode input
|
||||
}
|
||||
|
||||
return m8bitMem[addr];
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short Memory::Peek16bit(unsigned short addr)
|
||||
{
|
||||
unsigned short ret = 0;
|
||||
|
||||
if (mCharIOActive && addr == mCharIOAddr) {
|
||||
m8bitMem[addr] = ReadCharKb(false); // blocking mode input
|
||||
} else if (mCharIOActive && addr == mCharIOAddr+1) {
|
||||
m8bitMem[addr] = ReadCharKb(true); // non-blocking mode input
|
||||
}
|
||||
|
||||
ret = m8bitMem[addr++];
|
||||
ret += m8bitMem[addr] * 256;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: Poke8bit()
|
||||
* Purpose: Write byte to specified memory location.
|
||||
* If the memory location is mapped to character I/O,
|
||||
* write value to output buffer and memory location.
|
||||
* If the memory location is protected (ROM), do not
|
||||
* write the value.
|
||||
* Arguments: addr - (0x0000..0xffff) memory address,
|
||||
* val - value (0x00..0xff)
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::Poke8bit(unsigned short addr, unsigned char val)
|
||||
{
|
||||
if (mCharIOActive && addr == mCharIOAddr)
|
||||
PutCharIO(val);
|
||||
if (!mROMActive || (addr < mROMBegin || addr > mROMEnd)) {
|
||||
m8bitMem[addr] = val;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: SetCharIO()
|
||||
* Purpose: Activates and sets an address of basic character I/O
|
||||
* emulation (console).
|
||||
* Arguments: addr - address of I/O area (0x0000..0xFFFF)
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::SetCharIO(unsigned short addr, bool echo)
|
||||
{
|
||||
mCharIOAddr = addr;
|
||||
mCharIOActive = true;
|
||||
mIOEcho = echo;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: DisableCharIO()
|
||||
* Purpose: Deactivates basic character I/O emulation (console).
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::DisableCharIO()
|
||||
{
|
||||
mCharIOActive = false;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharIOAddr()
|
||||
* Purpose: Returns current address of basic character I/O area.
|
||||
* Arguments: n/a
|
||||
* Returns: address of I/O area
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short Memory::GetCharIOAddr()
|
||||
{
|
||||
return mCharIOAddr;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short Memory::GetROMBegin()
|
||||
{
|
||||
return mROMBegin;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short Memory::GetROMEnd()
|
||||
{
|
||||
return mROMEnd;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
bool Memory::IsROMEnabled()
|
||||
{
|
||||
return mROMActive;
|
||||
}
|
||||
|
||||
} // namespace MKBasic
|
||||
|
555
ReadMe.txt
555
ReadMe.txt
@ -1,262 +1,293 @@
|
||||
|
||||
Project: MKBasic (VM6502).
|
||||
|
||||
Author: Copyright (C) Marek Karcz 2016. All rights reserved.
|
||||
Free for personal and non-commercial use.
|
||||
Code can be distributed and included in derivative work under
|
||||
condition that the original copyright notice is preserved.
|
||||
For use in commercial product, please contact me to obtain
|
||||
permission and discuss possible fees, at: makarcz@yahoo.com
|
||||
This software is provided with no warranty.
|
||||
|
||||
Purpose:
|
||||
|
||||
MOS 6502 emulator, Virtual CPU/Machine and potentially retro-style 8-bit
|
||||
computer emulator.
|
||||
MOS-6502-compatible virtual computer featuring BASIC interpreter, machine code
|
||||
monitor, input/output device emulation etc.
|
||||
Program works in DOS/shell console (text mode) only.
|
||||
Makefile are included to build under Windows 32/64 (mingw compiler required)
|
||||
and under Linux Ubuntu or Ubuntu based.
|
||||
|
||||
To build under Windows 32/64:
|
||||
|
||||
* Install MINGW64 under C:\mingw-w64\x86_64-5.3.0 folder.
|
||||
* Run mingw terminal.
|
||||
* Change current directory to that of this project.
|
||||
* Run: makeming.bat
|
||||
|
||||
To build under Linux:
|
||||
|
||||
* Make sure C++11 compliant version of GCC compiler is installed.
|
||||
* Change current directory to that of this project.
|
||||
* Run: make clean all
|
||||
|
||||
Program passed following tests:
|
||||
|
||||
* 6502 functional test by Klaus Dormann
|
||||
* AllSuiteA.asm from project hmc-6502
|
||||
|
||||
1. Credits/attributions:
|
||||
|
||||
Parts of this project is based on or contains 3-rd party work:
|
||||
|
||||
- Tiny Basic.
|
||||
- Enhanced Basic by Lee Davison.
|
||||
- Microchess by Peter Jennings (http://www.benlo.com/microchess/index.html).
|
||||
- 6502 functional test by Klaus Dormann.
|
||||
- All Suite test from project hmc-6502.
|
||||
|
||||
2. Format of the memory image definition file.
|
||||
|
||||
Program can load raw binary image of MOS 6502 opcodes.
|
||||
Binary image is always loaded from address $0000 and can be up to 64 kB long,
|
||||
so the code must be properly located inside that image.
|
||||
Depending on your favorite 6502 assembler, you may need to use proper command
|
||||
line arguments or configuration to achieve properly formatted binary file.
|
||||
E.g.: if using CL65 from CC65 package, create configuration file that defines
|
||||
memory segments that your 6502 code uses, then all of the segments (except the
|
||||
last one) must have attribute 'fill' set to 'yes' so the unsused areas are
|
||||
filled with 0-s.
|
||||
Two CFG files, one for microchess and one for All Suite from hmc-6502 project
|
||||
are supplied with this project and assembler source code adapted to be
|
||||
compiled with CL65.
|
||||
Other assemblers may need a different approach or may not be able to generate
|
||||
binary images that are required for this emulator.
|
||||
|
||||
Program can also load memory image definition file (plain text), which is
|
||||
a format developed especially for this project.
|
||||
|
||||
The format of the plain text memory image definition file is described below:
|
||||
|
||||
; comments
|
||||
ADDR
|
||||
address
|
||||
data
|
||||
ORG
|
||||
address
|
||||
data
|
||||
IOADDR
|
||||
address
|
||||
ROMBEGIN
|
||||
address
|
||||
ROMEND
|
||||
address
|
||||
ENROM
|
||||
ENIO
|
||||
EXEC
|
||||
address
|
||||
|
||||
Where:
|
||||
ADDR - label indicating that starting and run address will follow in
|
||||
the next line
|
||||
ORG - label indicating that the address counter will change to the
|
||||
value provided in next line
|
||||
IOADDR - label indicating that character I/O emulation trap address will
|
||||
follow in the next line
|
||||
ROMBEGIN - label indicating that the emulated read-only memory start address
|
||||
will follow in the next line
|
||||
ROMEND - label indicating that the emulated read-only memory end address
|
||||
will follow in the next line
|
||||
ENROM - enable read-only memory emulation
|
||||
ENIO - enable character I/O emulation
|
||||
EXEC - label indicating that the auto-execute address will follow
|
||||
in the next line
|
||||
|
||||
|
||||
address - decimal or hexadecimal (prefix $) address in memory
|
||||
|
||||
E.g:
|
||||
ADDR
|
||||
$0200
|
||||
|
||||
or
|
||||
|
||||
ADDR
|
||||
512
|
||||
|
||||
changes the default start address (256) to 512.
|
||||
|
||||
ORG
|
||||
49152
|
||||
|
||||
moves address counter to address 49152, following data will be
|
||||
loaded from that address forward
|
||||
|
||||
data - the multi-line stream of decimal of hexadecimal ($xx) values
|
||||
of size unsigned char (byte: 0-255) separated with spaces
|
||||
or commas.
|
||||
|
||||
E.g.:
|
||||
$00 $00 $00 $00
|
||||
$00 $00 $00 $00
|
||||
|
||||
or
|
||||
|
||||
$00,$00,$00,$00
|
||||
|
||||
or
|
||||
|
||||
0 0 0 0
|
||||
|
||||
or
|
||||
|
||||
0,0,0,0
|
||||
0 0 0 0
|
||||
|
||||
Each described above element of the memory image definition file is optional.
|
||||
|
||||
3. Character I/O emulation.
|
||||
|
||||
Emulator has ability to simulate a 80x25 text output display device and
|
||||
rudimentary character I/O functions. The emulation is implemented by the means
|
||||
of trapping memory locations defined to be designated I/O emulation addresses.
|
||||
The default memory location is $E000 and also by default, the character I/O
|
||||
is disabled. It can be enabled from the debug console with 'I' command:
|
||||
|
||||
I hexaddr
|
||||
|
||||
E.g.:
|
||||
|
||||
I E000
|
||||
|
||||
or
|
||||
|
||||
I FE00
|
||||
|
||||
or by putting optional statements in the memory image dedinition file:
|
||||
|
||||
ENIO
|
||||
|
||||
or
|
||||
|
||||
IOADDR
|
||||
address
|
||||
ENIO
|
||||
|
||||
Where:
|
||||
|
||||
address - decimal or hexadecimal (with prefix '$') address in memory
|
||||
$0000 - $FFFF.
|
||||
|
||||
The same address is used for both, input and output operations.
|
||||
|
||||
Reading from IOADDR inside the 6502 code invokes a blocking character
|
||||
input function from user's DOS/shell session.
|
||||
After user enters the character, the memory location contains the character
|
||||
code and also emulated CPU Acc register contains the same code.
|
||||
|
||||
Reading from IOADDR+1 inside 6502 code invokes a non-blocking character
|
||||
input function from user's DOS/shell session.
|
||||
This function is different than blocking one in one respect.
|
||||
This function will return value 0 in the memory location and Acc register
|
||||
if there was no key pressed by the user (no character waiting in buffer).
|
||||
If there was a key typed, the function will act as the blocking counterpart.
|
||||
|
||||
Writing to IOADDR inside the 6502 code will result in character code
|
||||
being put in the IOADDR memory location and also written to the character
|
||||
output buffer of the emulated display device. That character is not
|
||||
immediately transferred to the user's DOS/shell session. It is written to the
|
||||
emulated display's text memory instead. Depending on the mode in which
|
||||
emulator is currently working (continuous or step-by-step code execution),
|
||||
the emulated display device contents may or may not be updated on the user's
|
||||
screen in real time fashion. Remember that this is a DOS/shell console
|
||||
application. The user's console is shared among various functions of the
|
||||
program. In step-by-step mode, if the character I/O emulation is enabled, the
|
||||
current contents of the emulated display device can be displayed with
|
||||
corresponding debug console command: 'T'.
|
||||
|
||||
4. ROM (read-only memory) emulation.
|
||||
|
||||
This facility provides very basic means for memory mapping of the read-only
|
||||
area. This may be required by some 6502 programs that check for non-writable
|
||||
memory to establish the bounds of memory that can be used for data and code.
|
||||
One good example is Tiny Basic.
|
||||
By default the ROM emulation is disabled and the memory range of ROM is
|
||||
defined as $D000 - $DFFF.
|
||||
ROM emulation can be enabled (and the memory range defined) using debug
|
||||
console's command 'K':
|
||||
|
||||
K [rombegin] [romend] - to enable
|
||||
|
||||
or
|
||||
|
||||
K - to disable
|
||||
|
||||
The ROM emulation can also be defined and enabled in the memory image
|
||||
definition file with following statements:
|
||||
|
||||
ROMBEGIN
|
||||
address
|
||||
ROMEND
|
||||
address
|
||||
ENROM
|
||||
|
||||
5. Additional comments and remarks.
|
||||
|
||||
IOADDR is permitted to be located in the emulated ROM memory range.
|
||||
The writing to IOADDR is trapped first before checking ROM range and writing
|
||||
to it is permitted when character I/O emulation and ROM are enabled at the
|
||||
same time. It is a good idea in fact to put the IOADDR inside ROM range,
|
||||
otherwise memory scanning routines like the one in Tiny Basic may trigger
|
||||
unexpected character input because of the reading from IOADDR during the scan.
|
||||
If you experience unexpected character input prompt while emulating
|
||||
6502 code, this may be the case. Reconfigure your IOADDR to be inside ROM in
|
||||
such case and try again.
|
||||
|
||||
6. Warranty and License Agreement.
|
||||
|
||||
This software is provided with No Warranty.
|
||||
I (The Author) will not be held responsible for any damage to computer
|
||||
systems, data or user's health resulting from using this software.
|
||||
Please use responsibly.
|
||||
This software is provided in hope that it will be be useful and free of
|
||||
charge for non-commercial and educational use.
|
||||
Distribution of this software in non-commercial and educational derivative
|
||||
work is permitted under condition that original copyright notices and
|
||||
comments are preserved. Some 3-rd party work included with this project
|
||||
may require separate application for permission from their respective
|
||||
authors/copyright owners.
|
||||
|
||||
|
||||
|
||||
|
||||
Project: MKBasic (VM6502).
|
||||
|
||||
Author: Copyright (C) Marek Karcz 2016. All rights reserved.
|
||||
Free for personal and non-commercial use.
|
||||
Code can be distributed and included in derivative work under
|
||||
condition that the original copyright notice is preserved.
|
||||
For use in commercial product, please contact me to obtain
|
||||
permission and discuss possible fees, at: makarcz@yahoo.com
|
||||
This software is provided with no warranty.
|
||||
|
||||
Purpose:
|
||||
|
||||
MOS 6502 emulator, Virtual CPU/Machine and potentially retro-style 8-bit
|
||||
computer emulator.
|
||||
MOS-6502-compatible virtual computer featuring BASIC interpreter, machine code
|
||||
monitor, input/output device emulation etc.
|
||||
Program works in DOS/shell console (text mode) only.
|
||||
Makefile are included to build under Windows 32/64 (mingw compiler required)
|
||||
and under Linux Ubuntu or Ubuntu based.
|
||||
|
||||
To build under Windows 32/64:
|
||||
|
||||
* Install MINGW64 under C:\mingw-w64\x86_64-5.3.0 folder.
|
||||
* Run mingw terminal.
|
||||
* Change current directory to that of this project.
|
||||
* Run: makeming.bat
|
||||
|
||||
To build under Linux:
|
||||
|
||||
* Make sure C++11 compliant version of GCC compiler is installed.
|
||||
* Change current directory to that of this project.
|
||||
* Run: make clean all
|
||||
|
||||
Program passed following tests:
|
||||
|
||||
* 6502 functional test by Klaus Dormann
|
||||
* AllSuiteA.asm from project hmc-6502
|
||||
|
||||
1. Credits/attributions:
|
||||
|
||||
Parts of this project is based on or contains 3-rd party work:
|
||||
|
||||
- Tiny Basic.
|
||||
- Enhanced Basic by Lee Davison.
|
||||
- Microchess by Peter Jennings (http://www.benlo.com/microchess/index.html).
|
||||
- 6502 functional test by Klaus Dormann.
|
||||
- All Suite test from project hmc-6502.
|
||||
|
||||
2. Format of the memory image definition file.
|
||||
|
||||
Program can load raw binary image of MOS 6502 opcodes.
|
||||
Binary image is always loaded from address $0000 and can be up to 64 kB long,
|
||||
so the code must be properly located inside that image.
|
||||
Depending on your favorite 6502 assembler, you may need to use proper command
|
||||
line arguments or configuration to achieve properly formatted binary file.
|
||||
E.g.: if using CL65 from CC65 package, create configuration file that defines
|
||||
memory segments that your 6502 code uses, then all of the segments (except the
|
||||
last one) must have attribute 'fill' set to 'yes' so the unsused areas are
|
||||
filled with 0-s.
|
||||
Two CFG files, one for microchess and one for All Suite from hmc-6502 project
|
||||
are supplied with this project and assembler source code adapted to be
|
||||
compiled with CL65.
|
||||
Other assemblers may need a different approach or may not be able to generate
|
||||
binary images that are required for this emulator.
|
||||
|
||||
Program can also load memory image definition file (plain text), which is
|
||||
a format developed especially for this project.
|
||||
|
||||
The format of the plain text memory image definition file is described below:
|
||||
|
||||
; comments
|
||||
ADDR
|
||||
address
|
||||
data
|
||||
ORG
|
||||
address
|
||||
data
|
||||
IOADDR
|
||||
address
|
||||
ROMBEGIN
|
||||
address
|
||||
ROMEND
|
||||
address
|
||||
ENROM
|
||||
ENIO
|
||||
EXEC
|
||||
address
|
||||
|
||||
Where:
|
||||
ADDR - label indicating that starting and run address will follow in
|
||||
the next line
|
||||
ORG - label indicating that the address counter will change to the
|
||||
value provided in next line
|
||||
IOADDR - label indicating that character I/O emulation trap address will
|
||||
follow in the next line
|
||||
ROMBEGIN - label indicating that the emulated read-only memory start address
|
||||
will follow in the next line
|
||||
ROMEND - label indicating that the emulated read-only memory end address
|
||||
will follow in the next line
|
||||
ENROM - enable read-only memory emulation
|
||||
ENIO - enable character I/O emulation
|
||||
EXEC - label indicating that the auto-execute address will follow
|
||||
in the next line
|
||||
|
||||
|
||||
address - decimal or hexadecimal (prefix $) address in memory
|
||||
|
||||
E.g:
|
||||
ADDR
|
||||
$0200
|
||||
|
||||
or
|
||||
|
||||
ADDR
|
||||
512
|
||||
|
||||
changes the default start address (256) to 512.
|
||||
|
||||
ORG
|
||||
49152
|
||||
|
||||
moves address counter to address 49152, following data will be
|
||||
loaded from that address forward
|
||||
|
||||
data - the multi-line stream of decimal of hexadecimal ($xx) values
|
||||
of size unsigned char (byte: 0-255) separated with spaces
|
||||
or commas.
|
||||
|
||||
E.g.:
|
||||
$00 $00 $00 $00
|
||||
$00 $00 $00 $00
|
||||
|
||||
or
|
||||
|
||||
$00,$00,$00,$00
|
||||
|
||||
or
|
||||
|
||||
0 0 0 0
|
||||
|
||||
or
|
||||
|
||||
0,0,0,0
|
||||
0 0 0 0
|
||||
|
||||
Each described above element of the memory image definition file is optional.
|
||||
|
||||
3. Character I/O emulation.
|
||||
|
||||
Emulator has ability to simulate a 80x25 text output display device and
|
||||
rudimentary character I/O functions. The emulation is implemented by the means
|
||||
of trapping memory locations defined to be designated I/O emulation addresses.
|
||||
The default memory location is $E000 and also by default, the character I/O
|
||||
is disabled. It can be enabled from the debug console with 'I' command:
|
||||
|
||||
I hexaddr
|
||||
|
||||
E.g.:
|
||||
|
||||
I E000
|
||||
|
||||
or
|
||||
|
||||
I FE00
|
||||
|
||||
or by putting optional statements in the memory image dedinition file:
|
||||
|
||||
ENIO
|
||||
|
||||
or
|
||||
|
||||
IOADDR
|
||||
address
|
||||
ENIO
|
||||
|
||||
Where:
|
||||
|
||||
address - decimal or hexadecimal (with prefix '$') address in memory
|
||||
$0000 - $FFFF.
|
||||
|
||||
The same address is used for both, input and output operations.
|
||||
|
||||
Reading from IOADDR inside the 6502 code invokes a blocking character
|
||||
input function from user's DOS/shell session.
|
||||
After user enters the character, the memory location contains the character
|
||||
code and also emulated CPU Acc register contains the same code.
|
||||
|
||||
Reading from IOADDR+1 inside 6502 code invokes a non-blocking character
|
||||
input function from user's DOS/shell session.
|
||||
This function is different than blocking one in one respect.
|
||||
This function will return value 0 in the memory location and Acc register
|
||||
if there was no key pressed by the user (no character waiting in buffer).
|
||||
If there was a key typed, the function will act as the blocking counterpart.
|
||||
|
||||
Note that there is no clearly distinguished prompt generated by emulator
|
||||
when there is character input operation performed. It is designed like that
|
||||
to avoid interfering with the character I/O performed by native 6502 code.
|
||||
Therefore if user performs multi-step debugging in the debug console and
|
||||
program suddenly stops, it is likely waiting for character input.
|
||||
This is more clear when running the native 6502 code in non-debug execute
|
||||
mode. In this case the I/O operations are represented on the screen instantly
|
||||
and 6502 code may also produce prompts so user is aware when to enter data
|
||||
to the program.
|
||||
|
||||
Writing to IOADDR inside the 6502 code will result in character code
|
||||
being put in the IOADDR memory location and also written to the character
|
||||
output buffer of the emulated display device.
|
||||
|
||||
When VM is running in one of the debug modes, like step-by-step mode
|
||||
(S - step, N - go number of steps) or one of the debug code execution modes
|
||||
(C- continue or G - go/cont. from new address), that character is not
|
||||
immediately transferred to the user's DOS/shell session.
|
||||
It is only written to the emulated display's text memory.
|
||||
|
||||
When VM is running in non-debug code execution mode (X - execute from new
|
||||
address), the character is also output to the native DOS/shell console
|
||||
(user's screen).
|
||||
The character output history is therefore always kept in the memory of the
|
||||
emulated text display device and can be recalled to the screen in debug
|
||||
console with command 'T'.
|
||||
|
||||
There are 2 reasons for this:
|
||||
|
||||
* Performance.
|
||||
In previous version only the emulated text display device approach was used.
|
||||
That meant that each time there was a new character in the emulated display
|
||||
buffer, the entire emulated text output device screen had to be refreshed on
|
||||
the DOS/shell console. That was slow and caused screen flicker when characters
|
||||
were output at high rate of speed.
|
||||
|
||||
* Record of character I/O operation.
|
||||
During step-by-step debugging or multiple-step animated registers mode, any
|
||||
characters output is immediately replaced by the registers and stack status
|
||||
on the screen and is not visible on the screen. However user must be able to
|
||||
debug applications that perform character I/O operations and recall the
|
||||
history of the characters output to the emulated text display device. This is
|
||||
when shadow copy of character I/O comes handy.
|
||||
|
||||
4. ROM (read-only memory) emulation.
|
||||
|
||||
This facility provides very basic means for memory mapping of the read-only
|
||||
area. This may be required by some 6502 programs that check for non-writable
|
||||
memory to establish the bounds of memory that can be used for data and code.
|
||||
One good example is Tiny Basic.
|
||||
By default the ROM emulation is disabled and the memory range of ROM is
|
||||
defined as $D000 - $DFFF.
|
||||
ROM emulation can be enabled (and the memory range defined) using debug
|
||||
console's command 'K':
|
||||
|
||||
K [rombegin] [romend] - to enable
|
||||
|
||||
or
|
||||
|
||||
K - to disable
|
||||
|
||||
The ROM emulation can also be defined and enabled in the memory image
|
||||
definition file with following statements:
|
||||
|
||||
ROMBEGIN
|
||||
address
|
||||
ROMEND
|
||||
address
|
||||
ENROM
|
||||
|
||||
5. Additional comments and remarks.
|
||||
|
||||
IOADDR is permitted to be located in the emulated ROM memory range.
|
||||
The writing to IOADDR is trapped first before checking ROM range and writing
|
||||
to it is permitted when character I/O emulation and ROM are enabled at the
|
||||
same time. It is a good idea in fact to put the IOADDR inside ROM range,
|
||||
otherwise memory scanning routines like the one in Tiny Basic may trigger
|
||||
unexpected character input because of the reading from IOADDR during the scan.
|
||||
If you experience unexpected character input prompt while emulating
|
||||
6502 code, this may be the case. Reconfigure your IOADDR to be inside ROM in
|
||||
such case and try again.
|
||||
|
||||
6. Warranty and License Agreement.
|
||||
|
||||
This software is provided with No Warranty.
|
||||
I (The Author) will not be held responsible for any damage to computer
|
||||
systems, data or user's health resulting from using this software.
|
||||
Please use responsibly.
|
||||
This software is provided in hope that it will be be useful and free of
|
||||
charge for non-commercial and educational use.
|
||||
Distribution of this software in non-commercial and educational derivative
|
||||
work is permitted under condition that original copyright notices and
|
||||
comments are preserved. Some 3-rd party work included with this project
|
||||
may require separate application for permission from their respective
|
||||
authors/copyright owners.
|
||||
|
||||
|
||||
|
||||
|
@ -280,7 +280,8 @@ Regs *VMachine::Exec()
|
||||
while (true) {
|
||||
cpureg = Step();
|
||||
if (mCharIO) {
|
||||
ShowDisp();
|
||||
cout << mpDisp->GetLastChar();
|
||||
cout << flush;
|
||||
}
|
||||
if (cpureg->LastRTS || mOpInterrupt) break;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
10 LET A=0
|
||||
20 PR A;") HELLO WORLD FROM MKHBC!"
|
||||
20 PRINT A;") HELLO WORLD FROM MKHBC!"
|
||||
30 LET A=A+1
|
||||
40 IF A>100 THEN END
|
||||
50 GOTO 20
|
||||
|
2
main.cpp
2
main.cpp
@ -346,6 +346,8 @@ int main(int argc, char** argv) {
|
||||
newaddr = 0x10000;
|
||||
}
|
||||
if (brk || opbrk || stop || lrts) {
|
||||
pvm->ClearScreen();
|
||||
pvm->ShowIO();
|
||||
cout << endl;
|
||||
if (opbrk) {
|
||||
cout << "Interrupted at " << hex << preg->PtrAddr << endl;
|
||||
|
@ -13,3 +13,6 @@ $F0 $06 $8D $00 $E0 $E8 $D0 $F5
|
||||
$00 $00 $EA $4C $00 $02 $45 $6E
|
||||
$74 $65 $72 $20 $74 $65 $78 $74
|
||||
$3A $00 $00 $00 $00 $00 $00 $00
|
||||
ENIO
|
||||
EXEC
|
||||
$0200
|
Loading…
x
Reference in New Issue
Block a user