1
0
mirror of https://github.com/makarcz/vm6502.git synced 2024-05-31 14:41:28 +00:00
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:
Marek Karcz 2016-03-15 01:09:40 -04:00
parent d99ed03232
commit dce9babd36
8 changed files with 1161 additions and 1099 deletions

View File

@ -1,312 +1,335 @@
#include "Display.h" #include "Display.h"
#include <ctype.h> #include <ctype.h>
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <string.h> #include <string.h>
using namespace std; using namespace std;
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
namespace MKBasic { namespace MKBasic {
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
Display::Display() Display::Display()
{ {
InitScr(); InitScr();
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
Display::~Display() Display::~Display()
{ {
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: InitScr() * Method: InitScr()
* Purpose: Initialize screen. * Purpose: Initialize screen.
* Arguments: n/a * Arguments: n/a
* Returns: n/a * Returns: n/a
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
void Display::InitScr() void Display::InitScr()
{ {
mScrLines = SCREENDIM_ROW; mLastChar = 0;
mScrColumns = SCREENDIM_COL; mScrLines = SCREENDIM_ROW;
mShellConsoleWidth = GetConsoleWidth(); mScrColumns = SCREENDIM_COL;
if (mScrColumns > mShellConsoleWidth) { mShellConsoleWidth = GetConsoleWidth();
mScrColumns = mShellConsoleWidth; if (mScrColumns > mShellConsoleWidth) {
} mScrColumns = mShellConsoleWidth;
ClrScr(); }
} ClrScr();
}
#if defined(WINDOWS)
#if defined(WINDOWS)
#include <windows.h>
#include <conio.h> #include <windows.h>
#include <conio.h>
/*
*-------------------------------------------------------------------- /*
* Method: GetConsoleWidth() *--------------------------------------------------------------------
* Purpose: Obtain the width of shell console (the real one, not * Method: GetConsoleWidth()
* the emulated one) on Windows. * Purpose: Obtain the width of shell console (the real one, not
* Arguments: n/a * the emulated one) on Windows.
* Returns: int - width of the shell console. * Arguments: n/a
*-------------------------------------------------------------------- * Returns: int - width of the shell console.
*/ *--------------------------------------------------------------------
int Display::GetConsoleWidth() */
{ int Display::GetConsoleWidth()
HANDLE hStdOut; {
CONSOLE_SCREEN_BUFFER_INFO csbi; HANDLE hStdOut;
CONSOLE_SCREEN_BUFFER_INFO csbi;
hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
if (hStdOut == INVALID_HANDLE_VALUE) return -1; hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
if (hStdOut == INVALID_HANDLE_VALUE) return -1;
if (!GetConsoleScreenBufferInfo( hStdOut, &csbi )) return -2;
if (!GetConsoleScreenBufferInfo( hStdOut, &csbi )) return -2;
return csbi.dwSize.X;
} return csbi.dwSize.X;
}
#endif
#endif
#if defined(LINUX)
#if defined(LINUX)
#include <termcap.h>
#include <termcap.h>
/*
*-------------------------------------------------------------------- /*
* Method: GetConsoleWidth() *--------------------------------------------------------------------
* Purpose: Obtain the width of shell console (the real one, not * Method: GetConsoleWidth()
* the emulated one) on Linux. * Purpose: Obtain the width of shell console (the real one, not
* Arguments: n/a * the emulated one) on Linux.
* Returns: int - width of the shell console. * Arguments: n/a
*-------------------------------------------------------------------- * Returns: int - width of the shell console.
*/ *--------------------------------------------------------------------
int Display::GetConsoleWidth() */
{ int Display::GetConsoleWidth()
unsigned int conwidth = SCREENDIM_COL; {
char *termtype = getenv("TERM"); unsigned int conwidth = SCREENDIM_COL;
static char termbuf[2048]; char *termtype = getenv("TERM");
static char termbuf[2048];
if (tgetent(termbuf, termtype) < 0) {
cout << "WARNING: Could not access the termcap data base." << endl; if (tgetent(termbuf, termtype) < 0) {
cout << " Unable to determine console width." << endl; cout << "WARNING: Could not access the termcap data base." << endl;
} else { cout << " Unable to determine console width." << endl;
conwidth = tgetnum("co"); } else {
} conwidth = tgetnum("co");
}
return conwidth;
} return conwidth;
}
#endif
#endif
/*
*-------------------------------------------------------------------- /*
* Method: *--------------------------------------------------------------------
* Purpose: * Method:
* Arguments: * Purpose:
* Returns: * Arguments:
*-------------------------------------------------------------------- * Returns:
*/ *--------------------------------------------------------------------
void Display::ScrollUp() */
{ void Display::ScrollUp()
for (unsigned int row=0; row<mScrLines-1; row++) { {
for (unsigned int col=0; col<mScrColumns; col++) { for (unsigned int row=0; row<mScrLines-1; row++) {
mScreen[col][row] = mScreen[col][row+1]; for (unsigned int col=0; col<mScrColumns; col++) {
} mScreen[row][col] = mScreen[row+1][col];
} }
for (unsigned int col=0; col<mScrColumns; col++) { }
mScreen[col][mScrLines-1] = ' '; for (unsigned int col=0; col<mScrColumns; col++) {
} mScreen[mScrLines-1][col] = ' ';
} }
}
/*
*-------------------------------------------------------------------- /*
* Method: GotoXY() *--------------------------------------------------------------------
* Purpose: Move cursor to new coordinates. * Method: GotoXY()
* Arguments: col, row - integer values, new cursor coordinates * Purpose: Move cursor to new coordinates.
* Returns: n/a * Arguments: col, row - integer values, new cursor coordinates
*-------------------------------------------------------------------- * Returns: n/a
*/ *--------------------------------------------------------------------
void Display::GotoXY(unsigned int col, unsigned int row) */
{ void Display::GotoXY(unsigned int col, unsigned int row)
if (col < mScrColumns && row < mScrLines) { {
mCursorCoord.col = col; if (col < mScrColumns && row < mScrLines) {
mCursorCoord.row = row; mCursorCoord.col = col;
} mCursorCoord.row = row;
} }
}
/*
*-------------------------------------------------------------------- /*
* Method: *--------------------------------------------------------------------
* Purpose: * Method:
* Arguments: * Purpose:
* Returns: * Arguments:
*-------------------------------------------------------------------- * Returns:
*/ *--------------------------------------------------------------------
bool Display::IsSpecChar(char c) */
{ bool Display::IsSpecChar(char c)
bool ret = false; {
char sct[] = {SCREENSPECCHARS_NL, bool ret = false;
SCREENSPECCHARS_CR, char sct[] = {SCREENSPECCHARS_NL,
SCREENSPECCHARS_TB, SCREENSPECCHARS_CR,
SCREENSPECCHARS_BS, SCREENSPECCHARS_TB,
SCREENSPECCHARS_BE, SCREENSPECCHARS_BS,
0}; SCREENSPECCHARS_BE,
0};
for (unsigned int i=0; i<strlen(sct); i++) {
if (c == sct[i]) { for (unsigned int i=0; i<strlen(sct); i++) {
ret = true; if (c == sct[i]) {
break; ret = true;
} break;
} }
}
return ret;
} return ret;
}
/*
*-------------------------------------------------------------------- /*
* Method: PutChar() *--------------------------------------------------------------------
* Purpose: Output character to console. If going outside * Method: PutChar()
* lower-right corner, scroll the contents up. * Purpose: Output character to console. If going outside
* Arguments: c - character to output. * lower-right corner, scroll the contents up.
* Returns: n/a * Arguments: c - character to output.
*-------------------------------------------------------------------- * Returns: n/a
*/ *--------------------------------------------------------------------
void Display::PutChar(char c) */
{ void Display::PutChar(char c)
if (isalnum(c) || ispunct(c) || isspace(c) || IsSpecChar(c)) {
{ if (isalnum(c) || ispunct(c) || isspace(c) || IsSpecChar(c))
if (c == SCREENSPECCHARS_NL) { {
//mCursorCoord.col = 0; if (c == SCREENSPECCHARS_NL) {
mCursorCoord.row++; mLastChar = SCREENSPECCHARS_NL;
if (mCursorCoord.row >= mScrLines) { //mCursorCoord.col = 0;
ScrollUp(); mCursorCoord.row++;
mCursorCoord.row = mScrLines-1; if (mCursorCoord.row >= mScrLines) {
} ScrollUp();
} else if (c == SCREENSPECCHARS_CR) { mCursorCoord.row = mScrLines-1;
mCursorCoord.col = 0; }
} else if (c == SCREENSPECCHARS_TB) { } else if (c == SCREENSPECCHARS_CR) {
mCursorCoord.col += TABSIZE; mLastChar = SCREENSPECCHARS_CR;
if (mCursorCoord.col >= mScrColumns) { mCursorCoord.col = 0;
mCursorCoord.col = mScrColumns-1; // must work on it some more } else if (c == SCREENSPECCHARS_TB) {
} mLastChar = SCREENSPECCHARS_TB;
} else if (c == SCREENSPECCHARS_BS) { mCursorCoord.col += TABSIZE;
if (mCursorCoord.col > 0) mCursorCoord.col--; if (mCursorCoord.col >= mScrColumns) {
} else if (c == SCREENSPECCHARS_BE) { mCursorCoord.col = mScrColumns-1; // must work on it some more
// no action }
} } else if (c == SCREENSPECCHARS_BS) {
else { mLastChar = SCREENSPECCHARS_BS;
mScreen[mCursorCoord.col][mCursorCoord.row] = c; if (mCursorCoord.col > 0) mCursorCoord.col--;
mCursorCoord.col++; } else if (c == SCREENSPECCHARS_BE) {
if (mCursorCoord.col >= mScrColumns) { mLastChar = SCREENSPECCHARS_BE;
mCursorCoord.col = 0; // no action
mCursorCoord.row++; }
if (mCursorCoord.row >= mScrLines) { else {
ScrollUp(); mScreen[mCursorCoord.row][mCursorCoord.col] = c;
mCursorCoord.row = mScrLines-1; 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() * Method: ClrScr()
{ * Purpose: Fill the screen with spaces. Set cursor in left-upper
for (unsigned int col=0; col<mScrColumns; col++) { * corner.
for (unsigned int row=0; row<mScrLines; row++) { * Arguments: n/a
mScreen[col][row] = ' '; * Returns: n/a
} *--------------------------------------------------------------------
} */
mCursorCoord.col = mCursorCoord.row = 0; void Display::ClrScr()
} {
for (unsigned int col=0; col<mScrColumns; col++) {
/* for (unsigned int row=0; row<mScrLines; row++) {
*-------------------------------------------------------------------- mScreen[row][col] = ' ';
* Method: }
* Purpose: }
* Arguments: mCursorCoord.col = mCursorCoord.row = 0;
* Returns: }
*--------------------------------------------------------------------
*/ /*
char Display::GetCharAt(unsigned int col, unsigned int row) *--------------------------------------------------------------------
{ * Method:
char c = -1; * Purpose:
* Arguments:
if (col < mScrColumns && row < mScrLines) * Returns:
c = mScreen[col][row]; *--------------------------------------------------------------------
*/
return c; char Display::GetCharAt(unsigned int col, unsigned int row)
} {
char c = -1;
/*
*-------------------------------------------------------------------- if (col < mScrColumns && row < mScrLines)
* Method: ShowScr() c = mScreen[row][col];
* Purpose: Display contents of the emulated console on a... well,
* real console. return c;
* Arguments: n/a }
* Returns: n/a
*-------------------------------------------------------------------- /*
*/ *--------------------------------------------------------------------
void Display::ShowScr() * Method: ShowScr()
{ * Purpose: Display contents of the emulated console on a... well,
for (unsigned int row=0; row<mScrLines; row++) { * real console.
string line; * Arguments: n/a
line.clear(); * Returns: n/a
for (unsigned int col=0; col<mScrColumns; col++) { *--------------------------------------------------------------------
char c = mScreen[col][row]; */
if (mCursorCoord.col == col && mCursorCoord.row == row) { void Display::ShowScr()
c = '_'; {
} char buf[SCREENDIM_COL] = {0};
line = line + c; string scr;
} scr.clear();
cout << line; for (unsigned int row=0; row<mScrLines; row++) {
// add extra NL if the real console is wider than emulated one char *linebuf = &(mScreen[row][0]);
if (mShellConsoleWidth > mScrColumns) cout << endl; memset(buf, 0, mScrColumns);
} strncpy(buf, linebuf, mScrColumns);
} buf[mScrColumns] = 0;
if (mCursorCoord.row == row) {
/* buf[mCursorCoord.col] = '_';
*-------------------------------------------------------------------- }
* Method: GetCursorCoord() string line(buf);
* Purpose: Get cursor coordinates. // add extra NL if the real console is wider than emulated one
* Arguments: n/a if (mShellConsoleWidth > mScrColumns) line = line + "\n";
* Returns: pointer to cursor coordinates scr = scr + line;
*-------------------------------------------------------------------- }
*/ cout << scr;
CursorCoord *Display::GetCursorCoord() }
{
return &mCursorCoord; /*
} *--------------------------------------------------------------------
* Method: GetCursorCoord()
} // namespace MKBasic * 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
View File

@ -1,60 +1,62 @@
#ifndef DISPLAY_H #ifndef DISPLAY_H
#define DISPLAY_H #define DISPLAY_H
#include "system.h" #include "system.h"
#define TABSIZE 4 #define TABSIZE 4
namespace MKBasic { namespace MKBasic {
enum eScreenDimensions { enum eScreenDimensions {
SCREENDIM_COL = 80, SCREENDIM_COL = 80,
SCREENDIM_ROW = 24 SCREENDIM_ROW = 24
}; };
enum eScreenSpecChars { enum eScreenSpecChars {
SCREENSPECCHARS_NL = (int)'\n', // new line SCREENSPECCHARS_NL = (int)'\n', // new line
SCREENSPECCHARS_CR = (int)'\r', // caret SCREENSPECCHARS_CR = (int)'\r', // caret
SCREENSPECCHARS_TB = (int)'\t', // tab SCREENSPECCHARS_TB = (int)'\t', // tab
SCREENSPECCHARS_BS = (int)'\b', // backspace SCREENSPECCHARS_BS = (int)'\b', // backspace
SCREENSPECCHARS_BE = (int)'\a' // bell SCREENSPECCHARS_BE = (int)'\a' // bell
}; };
struct CursorCoord { struct CursorCoord {
unsigned int row; unsigned int row;
unsigned int col; unsigned int col;
}; };
class Display class Display
{ {
public: public:
Display(); Display();
~Display(); ~Display();
void GotoXY(unsigned int col, unsigned int row); void GotoXY(unsigned int col, unsigned int row);
void PutChar(char c); void PutChar(char c);
void ClrScr(); void ClrScr();
char GetCharAt(unsigned int col, unsigned int row); char GetCharAt(unsigned int col, unsigned int row);
void ShowScr(); void ShowScr();
CursorCoord *GetCursorCoord(); CursorCoord *GetCursorCoord();
char GetLastChar();
protected:
protected:
private:
private:
char mScreen[SCREENDIM_COL][SCREENDIM_ROW];
CursorCoord mCursorCoord; char mScreen[SCREENDIM_ROW][SCREENDIM_COL];
unsigned int mShellConsoleWidth; char mLastChar;
unsigned int mScrLines; CursorCoord mCursorCoord;
unsigned int mScrColumns; unsigned int mShellConsoleWidth;
unsigned int mScrLines;
void InitScr(); unsigned int mScrColumns;
void ScrollUp();
bool IsSpecChar(char c); void InitScr();
int GetConsoleWidth(); void ScrollUp();
bool IsSpecChar(char c);
}; int GetConsoleWidth();
} // namespace MKBasic };
#endif } // namespace MKBasic
#endif

View File

@ -1,463 +1,463 @@
#include "Memory.h" #include "Memory.h"
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
#if defined(WINDOWS) #if defined(WINDOWS)
#include <conio.h> #include <conio.h>
#endif #endif
//#define DBG 1 //#define DBG 1
#if defined (DBG) #if defined (DBG)
#include <iostream> #include <iostream>
using namespace std; using namespace std;
#endif #endif
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
namespace MKBasic { namespace MKBasic {
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
Memory::Memory() Memory::Memory()
{ {
Initialize(); Initialize();
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
Memory::~Memory() Memory::~Memory()
{ {
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
void Memory::Initialize() void Memory::Initialize()
{ {
unsigned short addr = 0; unsigned short addr = 0;
for (int i=0; i < 0xFFFF; i++) { for (int i=0; i < 0xFFFF; i++) {
m8bitMem[addr++] = 0; m8bitMem[addr++] = 0;
} }
mCharIOAddr = CHARIO_ADDR; mCharIOAddr = CHARIO_ADDR;
mCharIOActive = false; mCharIOActive = false;
mIOEcho = false; mIOEcho = false;
mInBufDataBegin = mInBufDataEnd = 0; mInBufDataBegin = mInBufDataEnd = 0;
mOutBufDataBegin = mOutBufDataEnd = 0; mOutBufDataBegin = mOutBufDataEnd = 0;
mROMBegin = ROM_BEGIN; mROMBegin = ROM_BEGIN;
mROMEnd = ROM_END; mROMEnd = ROM_END;
mROMActive = false; mROMActive = false;
} }
#if defined(LINUX) #if defined(LINUX)
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
struct termios orig_termios; struct termios orig_termios;
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
void reset_terminal_mode() void reset_terminal_mode()
{ {
tcsetattr(0, TCSANOW, &orig_termios); tcsetattr(0, TCSANOW, &orig_termios);
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
void Memory::set_conio_terminal_mode() void Memory::set_conio_terminal_mode()
{ {
struct termios new_termios; struct termios new_termios;
/* take two copies - one for now, one for later */ /* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios); tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios)); memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */ /* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode); atexit(reset_terminal_mode);
cfmakeraw(&new_termios); cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios); tcsetattr(0, TCSANOW, &new_termios);
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
int Memory::kbhit() int Memory::kbhit()
{ {
struct timeval tv = { 0L, 0L }; struct timeval tv = { 0L, 0L };
fd_set fds; fd_set fds;
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(0, &fds); FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv); return select(1, &fds, NULL, NULL, &tv);
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
int Memory::getch() int Memory::getch()
{ {
int r; int r;
unsigned char c; unsigned char c;
if ((r = read(0, &c, sizeof(c))) < 0) { if ((r = read(0, &c, sizeof(c))) < 0) {
return r; return r;
} else { } else {
return c; return c;
} }
} }
#endif #endif
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
void Memory::EnableROM() void Memory::EnableROM()
{ {
mROMActive = true; mROMActive = true;
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
void Memory::DisableROM() void Memory::DisableROM()
{ {
mROMActive = false; mROMActive = false;
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
void Memory::SetROM(unsigned short start, unsigned short end) void Memory::SetROM(unsigned short start, unsigned short end)
{ {
if (mROMEnd > mROMBegin) { if (mROMEnd > mROMBegin) {
mROMBegin = start; mROMBegin = start;
mROMEnd = end; mROMEnd = end;
} }
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
void Memory::EnableROM(unsigned short start, unsigned short end) void Memory::EnableROM(unsigned short start, unsigned short end)
{ {
SetROM(start,end); SetROM(start,end);
EnableROM(); EnableROM();
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: ReadCharKb() * Method: ReadCharKb()
* Purpose: If char I/O active, read character from console * Purpose: If char I/O active, read character from console
* (non-blocking) and put in an input FIFO buffer. * (non-blocking) and put in an input FIFO buffer.
* Arguments: nonblock - if true, works in non-blocking mode * Arguments: nonblock - if true, works in non-blocking mode
* Returns: n/a * Returns: n/a
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
unsigned char Memory::ReadCharKb(bool nonblock) unsigned char Memory::ReadCharKb(bool nonblock)
{ {
unsigned char ret = 0; unsigned char ret = 0;
if (mCharIOActive) { if (mCharIOActive) {
#if defined(LINUX) #if defined(LINUX)
set_conio_terminal_mode(); set_conio_terminal_mode();
#endif #endif
static int c = ' '; static int c = ' ';
putchar('\n'); //putchar('\n');
if (mIOEcho && isprint(c)) putchar(c); if (mIOEcho && isprint(c)) putchar(c);
else putchar(' '); //else putchar(' ');
fputs("<-Character Input (CTRL-Y to BREAK) ?\r",stdout); //fputs("<-Character Input (CTRL-Y to BREAK) ?\r",stdout);
fflush(stdout); fflush(stdout);
if (!nonblock) while(!kbhit()); if (!nonblock) while(!kbhit());
else c = 0; else c = 0;
c = getch(); c = getch();
#if defined(LINUX) #if defined(LINUX)
if (c == 3) { // capture CTRL-C in CONIO mode if (c == 3) { // capture CTRL-C in CONIO mode
reset_terminal_mode(); reset_terminal_mode();
kill(getpid(),SIGINT); kill(getpid(),SIGINT);
} }
#endif #endif
fputs(" \r",stdout); //fputs(" \r",stdout);
fflush(stdout); //fflush(stdout);
mCharIOBufIn[mInBufDataEnd] = c; mCharIOBufIn[mInBufDataEnd] = c;
mInBufDataEnd++; mInBufDataEnd++;
if (mInBufDataEnd >= CHARIO_BUF_SIZE) mInBufDataEnd = 0; if (mInBufDataEnd >= CHARIO_BUF_SIZE) mInBufDataEnd = 0;
ret = c; ret = c;
#if defined(LINUX) #if defined(LINUX)
reset_terminal_mode(); reset_terminal_mode();
#endif #endif
} }
return ret; return ret;
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: GetCharIn() * Method: GetCharIn()
* Purpose: Return character from the emulated character I/O FIFO * Purpose: Return character from the emulated character I/O FIFO
* input buffer or -1 if FIFO empty or char I/O not active. * input buffer or -1 if FIFO empty or char I/O not active.
* Arguments: n/a * Arguments: n/a
* Returns: character * Returns: character
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
char Memory::GetCharIn() char Memory::GetCharIn()
{ {
char ret = -1; char ret = -1;
if (mCharIOActive) { if (mCharIOActive) {
if (mInBufDataEnd != mInBufDataBegin) { if (mInBufDataEnd != mInBufDataBegin) {
ret = mCharIOBufIn[mInBufDataBegin]; ret = mCharIOBufIn[mInBufDataBegin];
mInBufDataBegin++; mInBufDataBegin++;
if (mInBufDataBegin >= CHARIO_BUF_SIZE) mInBufDataBegin = 0; if (mInBufDataBegin >= CHARIO_BUF_SIZE) mInBufDataBegin = 0;
} }
} }
return ret; return ret;
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: GetCharOut() * Method: GetCharOut()
* Purpose: Return character from the emulated character I/O FIFO * Purpose: Return character from the emulated character I/O FIFO
* output buffer or -1 if FIFO empty or char I/O not * output buffer or -1 if FIFO empty or char I/O not
* active. * active.
* Arguments: n/a * Arguments: n/a
* Returns: character * Returns: character
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
char Memory::GetCharOut() char Memory::GetCharOut()
{ {
char ret = -1; char ret = -1;
if (mCharIOActive) { if (mCharIOActive) {
if (mOutBufDataEnd != mOutBufDataBegin) { if (mOutBufDataEnd != mOutBufDataBegin) {
ret = mCharIOBufOut[mOutBufDataBegin]; ret = mCharIOBufOut[mOutBufDataBegin];
mOutBufDataBegin++; mOutBufDataBegin++;
if (mOutBufDataBegin >= CHARIO_BUF_SIZE) mOutBufDataBegin = 0; if (mOutBufDataBegin >= CHARIO_BUF_SIZE) mOutBufDataBegin = 0;
} }
} }
return ret; return ret;
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: PutCharIO() * Method: PutCharIO()
* Purpose: Put character in the output char I/O FIFO buffer. * Purpose: Put character in the output char I/O FIFO buffer.
* Arguments: c - character * Arguments: c - character
* Returns: n/a * Returns: n/a
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
void Memory::PutCharIO(char c) void Memory::PutCharIO(char c)
{ {
if (mCharIOActive) { if (mCharIOActive) {
mCharIOBufOut[mOutBufDataEnd] = c; mCharIOBufOut[mOutBufDataEnd] = c;
mOutBufDataEnd++; mOutBufDataEnd++;
if (mOutBufDataEnd >= CHARIO_BUF_SIZE) mOutBufDataEnd = 0; if (mOutBufDataEnd >= CHARIO_BUF_SIZE) mOutBufDataEnd = 0;
} }
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
unsigned char Memory::Peek8bit(unsigned short addr) unsigned char Memory::Peek8bit(unsigned short addr)
{ {
if (mCharIOActive && addr == mCharIOAddr) { if (mCharIOActive && addr == mCharIOAddr) {
m8bitMem[addr] = ReadCharKb(false); // blocking mode input m8bitMem[addr] = ReadCharKb(false); // blocking mode input
} else if (mCharIOActive && addr == mCharIOAddr+1) { } else if (mCharIOActive && addr == mCharIOAddr+1) {
m8bitMem[addr] = ReadCharKb(true); // non-blocking mode input m8bitMem[addr] = ReadCharKb(true); // non-blocking mode input
} }
return m8bitMem[addr]; return m8bitMem[addr];
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
unsigned short Memory::Peek16bit(unsigned short addr) unsigned short Memory::Peek16bit(unsigned short addr)
{ {
unsigned short ret = 0; unsigned short ret = 0;
if (mCharIOActive && addr == mCharIOAddr) { if (mCharIOActive && addr == mCharIOAddr) {
m8bitMem[addr] = ReadCharKb(false); // blocking mode input m8bitMem[addr] = ReadCharKb(false); // blocking mode input
} else if (mCharIOActive && addr == mCharIOAddr+1) { } else if (mCharIOActive && addr == mCharIOAddr+1) {
m8bitMem[addr] = ReadCharKb(true); // non-blocking mode input m8bitMem[addr] = ReadCharKb(true); // non-blocking mode input
} }
ret = m8bitMem[addr++]; ret = m8bitMem[addr++];
ret += m8bitMem[addr] * 256; ret += m8bitMem[addr] * 256;
return ret; return ret;
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: Poke8bit() * Method: Poke8bit()
* Purpose: Write byte to specified memory location. * Purpose: Write byte to specified memory location.
* If the memory location is mapped to character I/O, * If the memory location is mapped to character I/O,
* write value to output buffer and memory location. * write value to output buffer and memory location.
* If the memory location is protected (ROM), do not * If the memory location is protected (ROM), do not
* write the value. * write the value.
* Arguments: addr - (0x0000..0xffff) memory address, * Arguments: addr - (0x0000..0xffff) memory address,
* val - value (0x00..0xff) * val - value (0x00..0xff)
* Returns: n/a * Returns: n/a
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
void Memory::Poke8bit(unsigned short addr, unsigned char val) void Memory::Poke8bit(unsigned short addr, unsigned char val)
{ {
if (mCharIOActive && addr == mCharIOAddr) if (mCharIOActive && addr == mCharIOAddr)
PutCharIO(val); PutCharIO(val);
if (!mROMActive || (addr < mROMBegin || addr > mROMEnd)) { if (!mROMActive || (addr < mROMBegin || addr > mROMEnd)) {
m8bitMem[addr] = val; m8bitMem[addr] = val;
} }
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: SetCharIO() * Method: SetCharIO()
* Purpose: Activates and sets an address of basic character I/O * Purpose: Activates and sets an address of basic character I/O
* emulation (console). * emulation (console).
* Arguments: addr - address of I/O area (0x0000..0xFFFF) * Arguments: addr - address of I/O area (0x0000..0xFFFF)
* Returns: n/a * Returns: n/a
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
void Memory::SetCharIO(unsigned short addr, bool echo) void Memory::SetCharIO(unsigned short addr, bool echo)
{ {
mCharIOAddr = addr; mCharIOAddr = addr;
mCharIOActive = true; mCharIOActive = true;
mIOEcho = echo; mIOEcho = echo;
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: DisableCharIO() * Method: DisableCharIO()
* Purpose: Deactivates basic character I/O emulation (console). * Purpose: Deactivates basic character I/O emulation (console).
* Arguments: n/a * Arguments: n/a
* Returns: n/a * Returns: n/a
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
void Memory::DisableCharIO() void Memory::DisableCharIO()
{ {
mCharIOActive = false; mCharIOActive = false;
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: GetCharIOAddr() * Method: GetCharIOAddr()
* Purpose: Returns current address of basic character I/O area. * Purpose: Returns current address of basic character I/O area.
* Arguments: n/a * Arguments: n/a
* Returns: address of I/O area * Returns: address of I/O area
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
unsigned short Memory::GetCharIOAddr() unsigned short Memory::GetCharIOAddr()
{ {
return mCharIOAddr; return mCharIOAddr;
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
unsigned short Memory::GetROMBegin() unsigned short Memory::GetROMBegin()
{ {
return mROMBegin; return mROMBegin;
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
unsigned short Memory::GetROMEnd() unsigned short Memory::GetROMEnd()
{ {
return mROMEnd; return mROMEnd;
} }
/* /*
*-------------------------------------------------------------------- *--------------------------------------------------------------------
* Method: * Method:
* Purpose: * Purpose:
* Arguments: * Arguments:
* Returns: * Returns:
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
bool Memory::IsROMEnabled() bool Memory::IsROMEnabled()
{ {
return mROMActive; return mROMActive;
} }
} // namespace MKBasic } // namespace MKBasic

View File

@ -1,262 +1,293 @@
Project: MKBasic (VM6502). Project: MKBasic (VM6502).
Author: Copyright (C) Marek Karcz 2016. All rights reserved. Author: Copyright (C) Marek Karcz 2016. All rights reserved.
Free for personal and non-commercial use. Free for personal and non-commercial use.
Code can be distributed and included in derivative work under Code can be distributed and included in derivative work under
condition that the original copyright notice is preserved. condition that the original copyright notice is preserved.
For use in commercial product, please contact me to obtain For use in commercial product, please contact me to obtain
permission and discuss possible fees, at: makarcz@yahoo.com permission and discuss possible fees, at: makarcz@yahoo.com
This software is provided with no warranty. This software is provided with no warranty.
Purpose: Purpose:
MOS 6502 emulator, Virtual CPU/Machine and potentially retro-style 8-bit MOS 6502 emulator, Virtual CPU/Machine and potentially retro-style 8-bit
computer emulator. computer emulator.
MOS-6502-compatible virtual computer featuring BASIC interpreter, machine code MOS-6502-compatible virtual computer featuring BASIC interpreter, machine code
monitor, input/output device emulation etc. monitor, input/output device emulation etc.
Program works in DOS/shell console (text mode) only. Program works in DOS/shell console (text mode) only.
Makefile are included to build under Windows 32/64 (mingw compiler required) Makefile are included to build under Windows 32/64 (mingw compiler required)
and under Linux Ubuntu or Ubuntu based. and under Linux Ubuntu or Ubuntu based.
To build under Windows 32/64: To build under Windows 32/64:
* Install MINGW64 under C:\mingw-w64\x86_64-5.3.0 folder. * Install MINGW64 under C:\mingw-w64\x86_64-5.3.0 folder.
* Run mingw terminal. * Run mingw terminal.
* Change current directory to that of this project. * Change current directory to that of this project.
* Run: makeming.bat * Run: makeming.bat
To build under Linux: To build under Linux:
* Make sure C++11 compliant version of GCC compiler is installed. * Make sure C++11 compliant version of GCC compiler is installed.
* Change current directory to that of this project. * Change current directory to that of this project.
* Run: make clean all * Run: make clean all
Program passed following tests: Program passed following tests:
* 6502 functional test by Klaus Dormann * 6502 functional test by Klaus Dormann
* AllSuiteA.asm from project hmc-6502 * AllSuiteA.asm from project hmc-6502
1. Credits/attributions: 1. Credits/attributions:
Parts of this project is based on or contains 3-rd party work: Parts of this project is based on or contains 3-rd party work:
- Tiny Basic. - Tiny Basic.
- Enhanced Basic by Lee Davison. - Enhanced Basic by Lee Davison.
- Microchess by Peter Jennings (http://www.benlo.com/microchess/index.html). - Microchess by Peter Jennings (http://www.benlo.com/microchess/index.html).
- 6502 functional test by Klaus Dormann. - 6502 functional test by Klaus Dormann.
- All Suite test from project hmc-6502. - All Suite test from project hmc-6502.
2. Format of the memory image definition file. 2. Format of the memory image definition file.
Program can load raw binary image of MOS 6502 opcodes. 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, 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. so the code must be properly located inside that image.
Depending on your favorite 6502 assembler, you may need to use proper command Depending on your favorite 6502 assembler, you may need to use proper command
line arguments or configuration to achieve properly formatted binary file. line arguments or configuration to achieve properly formatted binary file.
E.g.: if using CL65 from CC65 package, create configuration file that defines 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 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 last one) must have attribute 'fill' set to 'yes' so the unsused areas are
filled with 0-s. filled with 0-s.
Two CFG files, one for microchess and one for All Suite from hmc-6502 project 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 are supplied with this project and assembler source code adapted to be
compiled with CL65. compiled with CL65.
Other assemblers may need a different approach or may not be able to generate Other assemblers may need a different approach or may not be able to generate
binary images that are required for this emulator. binary images that are required for this emulator.
Program can also load memory image definition file (plain text), which is Program can also load memory image definition file (plain text), which is
a format developed especially for this project. a format developed especially for this project.
The format of the plain text memory image definition file is described below: The format of the plain text memory image definition file is described below:
; comments ; comments
ADDR ADDR
address address
data data
ORG ORG
address address
data data
IOADDR IOADDR
address address
ROMBEGIN ROMBEGIN
address address
ROMEND ROMEND
address address
ENROM ENROM
ENIO ENIO
EXEC EXEC
address address
Where: Where:
ADDR - label indicating that starting and run address will follow in ADDR - label indicating that starting and run address will follow in
the next line the next line
ORG - label indicating that the address counter will change to the ORG - label indicating that the address counter will change to the
value provided in next line value provided in next line
IOADDR - label indicating that character I/O emulation trap address will IOADDR - label indicating that character I/O emulation trap address will
follow in the next line follow in the next line
ROMBEGIN - label indicating that the emulated read-only memory start address ROMBEGIN - label indicating that the emulated read-only memory start address
will follow in the next line will follow in the next line
ROMEND - label indicating that the emulated read-only memory end address ROMEND - label indicating that the emulated read-only memory end address
will follow in the next line will follow in the next line
ENROM - enable read-only memory emulation ENROM - enable read-only memory emulation
ENIO - enable character I/O emulation ENIO - enable character I/O emulation
EXEC - label indicating that the auto-execute address will follow EXEC - label indicating that the auto-execute address will follow
in the next line in the next line
address - decimal or hexadecimal (prefix $) address in memory address - decimal or hexadecimal (prefix $) address in memory
E.g: E.g:
ADDR ADDR
$0200 $0200
or or
ADDR ADDR
512 512
changes the default start address (256) to 512. changes the default start address (256) to 512.
ORG ORG
49152 49152
moves address counter to address 49152, following data will be moves address counter to address 49152, following data will be
loaded from that address forward loaded from that address forward
data - the multi-line stream of decimal of hexadecimal ($xx) values data - the multi-line stream of decimal of hexadecimal ($xx) values
of size unsigned char (byte: 0-255) separated with spaces of size unsigned char (byte: 0-255) separated with spaces
or commas. or commas.
E.g.: E.g.:
$00 $00 $00 $00 $00 $00 $00 $00
$00 $00 $00 $00 $00 $00 $00 $00
or or
$00,$00,$00,$00 $00,$00,$00,$00
or or
0 0 0 0 0 0 0 0
or or
0,0,0,0 0,0,0,0
0 0 0 0 0 0 0 0
Each described above element of the memory image definition file is optional. Each described above element of the memory image definition file is optional.
3. Character I/O emulation. 3. Character I/O emulation.
Emulator has ability to simulate a 80x25 text output display device and Emulator has ability to simulate a 80x25 text output display device and
rudimentary character I/O functions. The emulation is implemented by the means rudimentary character I/O functions. The emulation is implemented by the means
of trapping memory locations defined to be designated I/O emulation addresses. 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 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: is disabled. It can be enabled from the debug console with 'I' command:
I hexaddr I hexaddr
E.g.: E.g.:
I E000 I E000
or or
I FE00 I FE00
or by putting optional statements in the memory image dedinition file: or by putting optional statements in the memory image dedinition file:
ENIO ENIO
or or
IOADDR IOADDR
address address
ENIO ENIO
Where: Where:
address - decimal or hexadecimal (with prefix '$') address in memory address - decimal or hexadecimal (with prefix '$') address in memory
$0000 - $FFFF. $0000 - $FFFF.
The same address is used for both, input and output operations. The same address is used for both, input and output operations.
Reading from IOADDR inside the 6502 code invokes a blocking character Reading from IOADDR inside the 6502 code invokes a blocking character
input function from user's DOS/shell session. input function from user's DOS/shell session.
After user enters the character, the memory location contains the character After user enters the character, the memory location contains the character
code and also emulated CPU Acc register contains the same code. code and also emulated CPU Acc register contains the same code.
Reading from IOADDR+1 inside 6502 code invokes a non-blocking character Reading from IOADDR+1 inside 6502 code invokes a non-blocking character
input function from user's DOS/shell session. input function from user's DOS/shell session.
This function is different than blocking one in one respect. This function is different than blocking one in one respect.
This function will return value 0 in the memory location and Acc register 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 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. 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 Note that there is no clearly distinguished prompt generated by emulator
being put in the IOADDR memory location and also written to the character when there is character input operation performed. It is designed like that
output buffer of the emulated display device. That character is not to avoid interfering with the character I/O performed by native 6502 code.
immediately transferred to the user's DOS/shell session. It is written to the Therefore if user performs multi-step debugging in the debug console and
emulated display's text memory instead. Depending on the mode in which program suddenly stops, it is likely waiting for character input.
emulator is currently working (continuous or step-by-step code execution), This is more clear when running the native 6502 code in non-debug execute
the emulated display device contents may or may not be updated on the user's mode. In this case the I/O operations are represented on the screen instantly
screen in real time fashion. Remember that this is a DOS/shell console and 6502 code may also produce prompts so user is aware when to enter data
application. The user's console is shared among various functions of the to the program.
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 Writing to IOADDR inside the 6502 code will result in character code
corresponding debug console command: 'T'. being put in the IOADDR memory location and also written to the character
output buffer of the emulated display device.
4. ROM (read-only memory) emulation.
When VM is running in one of the debug modes, like step-by-step mode
This facility provides very basic means for memory mapping of the read-only (S - step, N - go number of steps) or one of the debug code execution modes
area. This may be required by some 6502 programs that check for non-writable (C- continue or G - go/cont. from new address), that character is not
memory to establish the bounds of memory that can be used for data and code. immediately transferred to the user's DOS/shell session.
One good example is Tiny Basic. It is only written to the emulated display's text memory.
By default the ROM emulation is disabled and the memory range of ROM is
defined as $D000 - $DFFF. When VM is running in non-debug code execution mode (X - execute from new
ROM emulation can be enabled (and the memory range defined) using debug address), the character is also output to the native DOS/shell console
console's command 'K': (user's screen).
The character output history is therefore always kept in the memory of the
K [rombegin] [romend] - to enable emulated text display device and can be recalled to the screen in debug
console with command 'T'.
or
There are 2 reasons for this:
K - to disable
* Performance.
The ROM emulation can also be defined and enabled in the memory image In previous version only the emulated text display device approach was used.
definition file with following statements: 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
ROMBEGIN the DOS/shell console. That was slow and caused screen flicker when characters
address were output at high rate of speed.
ROMEND
address * Record of character I/O operation.
ENROM During step-by-step debugging or multiple-step animated registers mode, any
characters output is immediately replaced by the registers and stack status
5. Additional comments and remarks. 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
IOADDR is permitted to be located in the emulated ROM memory range. history of the characters output to the emulated text display device. This is
The writing to IOADDR is trapped first before checking ROM range and writing when shadow copy of character I/O comes handy.
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, 4. ROM (read-only memory) emulation.
otherwise memory scanning routines like the one in Tiny Basic may trigger
unexpected character input because of the reading from IOADDR during the scan. This facility provides very basic means for memory mapping of the read-only
If you experience unexpected character input prompt while emulating area. This may be required by some 6502 programs that check for non-writable
6502 code, this may be the case. Reconfigure your IOADDR to be inside ROM in memory to establish the bounds of memory that can be used for data and code.
such case and try again. One good example is Tiny Basic.
By default the ROM emulation is disabled and the memory range of ROM is
6. Warranty and License Agreement. defined as $D000 - $DFFF.
ROM emulation can be enabled (and the memory range defined) using debug
This software is provided with No Warranty. console's command 'K':
I (The Author) will not be held responsible for any damage to computer
systems, data or user's health resulting from using this software. K [rombegin] [romend] - to enable
Please use responsibly.
This software is provided in hope that it will be be useful and free of or
charge for non-commercial and educational use.
Distribution of this software in non-commercial and educational derivative K - to disable
work is permitted under condition that original copyright notices and
comments are preserved. Some 3-rd party work included with this project The ROM emulation can also be defined and enabled in the memory image
may require separate application for permission from their respective definition file with following statements:
authors/copyright owners.
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.

View File

@ -280,7 +280,8 @@ Regs *VMachine::Exec()
while (true) { while (true) {
cpureg = Step(); cpureg = Step();
if (mCharIO) { if (mCharIO) {
ShowDisp(); cout << mpDisp->GetLastChar();
cout << flush;
} }
if (cpureg->LastRTS || mOpInterrupt) break; if (cpureg->LastRTS || mOpInterrupt) break;
} }

View File

@ -1,5 +1,5 @@
10 LET A=0 10 LET A=0
20 PR A;") HELLO WORLD FROM MKHBC!" 20 PRINT A;") HELLO WORLD FROM MKHBC!"
30 LET A=A+1 30 LET A=A+1
40 IF A>100 THEN END 40 IF A>100 THEN END
50 GOTO 20 50 GOTO 20

View File

@ -346,6 +346,8 @@ int main(int argc, char** argv) {
newaddr = 0x10000; newaddr = 0x10000;
} }
if (brk || opbrk || stop || lrts) { if (brk || opbrk || stop || lrts) {
pvm->ClearScreen();
pvm->ShowIO();
cout << endl; cout << endl;
if (opbrk) { if (opbrk) {
cout << "Interrupted at " << hex << preg->PtrAddr << endl; cout << "Interrupted at " << hex << preg->PtrAddr << endl;

View File

@ -13,3 +13,6 @@ $F0 $06 $8D $00 $E0 $E8 $D0 $F5
$00 $00 $EA $4C $00 $02 $45 $6E $00 $00 $EA $4C $00 $02 $45 $6E
$74 $65 $72 $20 $74 $65 $78 $74 $74 $65 $72 $20 $74 $65 $78 $74
$3A $00 $00 $00 $00 $00 $00 $00 $3A $00 $00 $00 $00 $00 $00 $00
ENIO
EXEC
$0200