From dce9babd368de6ceb2b62ee73797bcc61fd43c6c Mon Sep 17 00:00:00 2001 From: Marek Karcz Date: Tue, 15 Mar 2016 01:09:40 -0400 Subject: [PATCH] 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. --- Display.cpp | 647 ++++++++++++++++--------------- Display.h | 122 +++--- Memory.cpp | 926 ++++++++++++++++++++++---------------------- ReadMe.txt | 555 +++++++++++++------------- VMachine.cpp | 3 +- hello_world.bas | 2 +- main.cpp | 2 + test_char_io_01.dat | 3 + 8 files changed, 1161 insertions(+), 1099 deletions(-) diff --git a/Display.cpp b/Display.cpp index d0ec1bf..94cd094 100644 --- a/Display.cpp +++ b/Display.cpp @@ -1,312 +1,335 @@ -#include "Display.h" -#include -#include -#include -#include - -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 -#include - -/* - *-------------------------------------------------------------------- - * 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 - -/* - *-------------------------------------------------------------------- - * 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) { - 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) 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 +#include +#include +#include + +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 +#include + +/* + *-------------------------------------------------------------------- + * 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 + +/* + *-------------------------------------------------------------------- + * 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) { + 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) 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 diff --git a/Display.h b/Display.h index 56993f5..7c9dd15 100644 --- a/Display.h +++ b/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 diff --git a/Memory.cpp b/Memory.cpp index 2ea11f7..698eb64 100644 --- a/Memory.cpp +++ b/Memory.cpp @@ -1,463 +1,463 @@ - -#include "Memory.h" - -#include -#include -#if defined(WINDOWS) -#include -#endif - -//#define DBG 1 -#if defined (DBG) -#include -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 -#include -#include - -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 +#include +#if defined(WINDOWS) +#include +#endif + +//#define DBG 1 +#if defined (DBG) +#include +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 +#include +#include + +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 diff --git a/ReadMe.txt b/ReadMe.txt index 8a2d3b2..809a5d1 100644 --- a/ReadMe.txt +++ b/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. + + + diff --git a/VMachine.cpp b/VMachine.cpp index 15a0ffa..69f79a9 100644 --- a/VMachine.cpp +++ b/VMachine.cpp @@ -280,7 +280,8 @@ Regs *VMachine::Exec() while (true) { cpureg = Step(); if (mCharIO) { - ShowDisp(); + cout << mpDisp->GetLastChar(); + cout << flush; } if (cpureg->LastRTS || mOpInterrupt) break; } diff --git a/hello_world.bas b/hello_world.bas index 6d8c396..6bb65a5 100644 --- a/hello_world.bas +++ b/hello_world.bas @@ -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 diff --git a/main.cpp b/main.cpp index b5b08c5..c01e2ea 100644 --- a/main.cpp +++ b/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; diff --git a/test_char_io_01.dat b/test_char_io_01.dat index 7fdc22b..392c34c 100644 --- a/test_char_io_01.dat +++ b/test_char_io_01.dat @@ -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 \ No newline at end of file