Performance, bug fixes, debug options, documentation updates.

Performance stats., refactoring, bug fixes, documentation updates,
cosmetic changes, debug options.
This commit is contained in:
Marek Karcz 2016-09-07 22:52:01 -04:00
parent 3cd0bc3f20
commit 9d6706df96
22 changed files with 1926 additions and 325 deletions

176
ConsoleIO.cpp Normal file
View File

@ -0,0 +1,176 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: ConsoleIO.cpp
*
* Purpose: Implementation of ConsoleIO class.
* The ConsoleIO class has methods helpful when
* UI is utilizing STDIO (DOS) console.
*
* Date: 8/26/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <string.h>
#include "system.h"
#include "ConsoleIO.h"
#include "MKGenException.h"
#if defined(WINDOWS)
#include <conio.h>
#endif
using namespace std;
namespace MKBasic {
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
ConsoleIO::ConsoleIO()
{
}
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
ConsoleIO::~ConsoleIO()
{
}
#if defined(WINDOWS)
/*
*--------------------------------------------------------------------
* Method: ClearScreen()
* Purpose: Clear the console screen.
* Arguments: n/a
* Returns: n/a
*--------------------------------------------------------------------
*/
void ConsoleIO::ClearScreen()
{
HANDLE hStdOut;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD count;
DWORD cellCount;
COORD homeCoords = { 0, 0 };
hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
if (hStdOut == INVALID_HANDLE_VALUE) return;
/* Get the number of cells in the current buffer */
if (!GetConsoleScreenBufferInfo( hStdOut, &csbi )) return;
cellCount = csbi.dwSize.X *csbi.dwSize.Y;
/* Fill the entire buffer with spaces */
if (!FillConsoleOutputCharacter(
hStdOut,
(TCHAR) ' ',
cellCount,
homeCoords,
&count
)) return;
/* Fill the entire buffer with the current colors and attributes */
if (!FillConsoleOutputAttribute(
hStdOut,
csbi.wAttributes,
cellCount,
homeCoords,
&count
)) return;
/* Move the cursor home */
SetConsoleCursorPosition( hStdOut, homeCoords );
}
/*
*--------------------------------------------------------------------
* Method: ScrHome()
* Purpose: Bring the console cursor to home position.
* Arguments: n/a
* Returns: n/a
*--------------------------------------------------------------------
*/
void ConsoleIO::ScrHome()
{
HANDLE hStdOut;
COORD homeCoords = { 0, 0 };
hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
if (hStdOut == INVALID_HANDLE_VALUE) return;
/* Move the cursor home */
SetConsoleCursorPosition( hStdOut, homeCoords );
}
#endif
#if defined(LINUX)
/*
*--------------------------------------------------------------------
* Method: ClearScreen()
* Purpose: Clear the console screen.
* Arguments: n/a
* Returns: n/a
*--------------------------------------------------------------------
*/
void ConsoleIO::ClearScreen()
{
system("clear");
}
/*
*--------------------------------------------------------------------
* Method: ScrHome()
* Purpose: Bring the console cursor to home position.
* Arguments: n/a
* Returns: n/a
*--------------------------------------------------------------------
*/
void ConsoleIO::ScrHome()
{
cout << "\033[1;1H";
}
#endif // #devine LINUX
} // END namespace MKBasic

61
ConsoleIO.h Normal file
View File

@ -0,0 +1,61 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: ConsoleIO.h
*
* Purpose: Prototype of ConsoleIO class.
*
* Date: 8/26/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#ifndef CONSOLEIO_H
#define CONSOLEIO_H
#include <string>
#include <queue>
#include <chrono>
#include "system.h"
//#define WINDOWS 1
#if defined (WINDOWS)
#include <windows.h>
#endif
namespace MKBasic {
class ConsoleIO
{
public:
ConsoleIO();
~ConsoleIO();
void ClearScreen();
void ScrHome();
};
} // namespace MKBasic
#endif // #ifndef CONSOLEIO_H

View File

@ -1,3 +1,39 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: Display.cpp
*
* Purpose: Implementation of Display class.
* The Display class emulates the character I/O device.
* It defines the abstract device. The actual
* input/output to the screen of the platform on which
* the emulator runs is defined in MemMapDev class
* or on higher level of abstraction.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#include "Display.h"
#include <ctype.h>
#include <cstdlib>

View File

@ -1,3 +1,35 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: Display.h
*
* Purpose: Prototype of Display class and all supportig data
* structures, enumerations, constants and macros.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#ifndef DISPLAY_H
#define DISPLAY_H

View File

@ -1,4 +1,40 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: GraphDisp.cpp
*
* Purpose: Implementation of GraphDisp class.
* The GraphDisp class emulates the graphics raster
* device. It handles displaying of the graphics, the
* events loop and the 'hardware' API to the device.
* The higher level abstraction layer - MemMapDev
* defines the actual behavior/response of the emulated
* device in the emulated system.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#include "GraphDisp.h"
#include "MKGenException.h"
#include "system.h"
@ -75,6 +111,8 @@ void GraphDisp::Initialize()
{
int desk_w, desk_h, winbd_top = 5, winbd_right = 5;
mPixelSizeX = GRAPHDISP_MAXW / mWidth;
mPixelSizeY = GRAPHDISP_MAXH / mHeight;
GetDesktopResolution(desk_w, desk_h);
// Available in version > 2.0.4
//SDL_GetWindowBordersSize(mpWindow, &winbd_top, NULL, NULL, &winbd_right);

View File

@ -1,3 +1,35 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: GraphDisp.h
*
* Purpose: Prototype of GraphDisp class and supporting data
* structures, enumerations, constants and macros.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#ifndef GRAPHDISP_H
#define GRAPHDISP_H
@ -12,8 +44,8 @@ using namespace std;
namespace MKBasic {
const int GRAPHDISP_MAXW = 640; // "real" display width or maximum virtual width
const int GRAPHDISP_MAXH = 400; // "real" display width or maximum virtual height
const int GRAPHDISP_MAXW = 960; // "real" display width or maximum virtual width
const int GRAPHDISP_MAXH = 600; // "real" display width or maximum virtual height
class GraphDisp {
@ -43,8 +75,8 @@ class GraphDisp {
int mWidth = 320; // virtual display width
int mHeight = 200; // virtual display height
int mPixelSizeX = 2; // virtual pixel width
int mPixelSizeY = 2; // virtual pixel height
int mPixelSizeX = 3; // virtual pixel width
int mPixelSizeY = 3; // virtual pixel height
int mWinPosX = 0; // SDL window position coordinate X
int mWinPosY = 0; // SDL window position coordinate Y
int mBgRgbR = 0; // bg color, RGB red intensity

View File

@ -1,13 +1,46 @@
#include "MKBasic.h"
namespace MKBasic {
MKBasic::MKBasic()
{
}
MKBasic::~MKBasic()
{
}
} // namespace MKBasic
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File:
*
* Purpose: Implementation of MKBasic class.
* NOTE: This is obsolete concept and will likely be
* removed from project.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#include "MKBasic.h"
namespace MKBasic {
MKBasic::MKBasic()
{
}
MKBasic::~MKBasic()
{
}
} // namespace MKBasic

View File

@ -1,18 +1,52 @@
#ifndef MKBASIC_H
#define MKBASIC_H
#include "VMachine.h"
namespace MKBasic {
class MKBasic : public VMachine
{
public:
MKBasic();
~MKBasic();
protected:
};
} // namespace MKBasic
#endif
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File:
*
* Purpose: Prototype of MKBasic class and all supporting data
* structures, enumerations, constants and macros.
* NOTE: This is obsolete concept and will likely be
* removed from project.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#ifndef MKBASIC_H
#define MKBASIC_H
#include "VMachine.h"
namespace MKBasic {
class MKBasic : public VMachine
{
public:
MKBasic();
~MKBasic();
protected:
};
} // namespace MKBasic
#endif

View File

@ -1,4 +1,38 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: MKCpu.cpp
*
* Purpose: Implementation of MKCpu class.
* MKCpu class implements the op-codes and other
* supporting functions of a virtual CPU. This one in
* particular emulates a real world microprocessor from
* 1970-s, the venerable MOS-6502.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and exercise common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#include <string.h>
#include "MKCpu.h"
#include "MKGenException.h"
@ -347,7 +381,7 @@ void MKCpu::InitCpu()
mReg.PtrStack = 0xFF; // top of stack
mReg.SoftIrq = false;
mReg.IrqPending = false;
mReg.CyclesLeft = 0;
mReg.CyclesLeft = 1;
mReg.PageBoundary = false;
mLocalMem = false;
mExitAtLastRTS = true;

34
MKCpu.h
View File

@ -1,3 +1,35 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: MKCpu.h
*
* Purpose: Prototype of MKCpu class. Data structures, enumerations
* and constants definitions supporting MKCpu class.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and exercise common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#ifndef MKCPU_H
#define MKCPU_H
@ -26,7 +58,7 @@ struct Regs {
bool SoftIrq; // true when interrupted with BRK or trapped opcode
bool LastRTS; // true if RTS encountered and stack empty.
unsigned short LastAddr; // PC at the time of previous op-code
string LastInstr; // instruction and argument executed in previous step
//string LastInstr; // instruction and argument executed in previous step
int LastOpCode; // op-code of last instruction
unsigned short LastArg; // argument to the last instruction
int LastAddrMode; // addressing mode of last instruction

View File

@ -1,4 +1,47 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: MemMapDev.cpp
*
* Purpose: Implementation of MemMapDev class.
* The MemMapDev class implements the highest abstraction
* layer for interfacing with emulated devices via memory
* addressing space, which is a typical way of
* interfacing the CPU with peripherals in the real world
* microprocessor systems.
* It also implements/emulates the behavior of the
* devices. The core implementation of the devices may
* be contained in separate classes or inside MemMapDev
* class. Note that MemMapDev class always contains
* at least partial (highest abstraction layer)
* implementation of the emulated device as it defines
* the methods triggered by corresponding memory address
* accesses.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#include "MemMapDev.h"
#include "Memory.h"
#include "MKGenException.h"
@ -83,6 +126,7 @@ void MemMapDev::Initialize()
mCharIOAddr = CHARIO_ADDR;
mGraphDispAddr = GRDISP_ADDR;
mpGraphDisp = NULL;
mpCharIODisp = NULL;
AddrRange addr_range(CHARIO_ADDR, CHARIO_ADDR+1);
DevPar dev_par("echo", "false");
MemAddrRanges addr_ranges_chario;
@ -112,6 +156,7 @@ void MemMapDev::Initialize()
&MemMapDev::GraphDispDevice_Write,
dev_params_grdisp);
mDevices.push_back(dev_grdisp);
mCharIOActive = false;
}
/*
@ -287,7 +332,7 @@ int MemMapDev::getch()
}
}
#endif
#endif // #define LINUX
/*
*--------------------------------------------------------------------
@ -304,10 +349,11 @@ unsigned char MemMapDev::ReadCharKb(bool nonblock)
#if defined(LINUX)
set_conio_terminal_mode();
#endif
static int c = ' ';
if (mIOEcho && isprint(c)) putchar(c);
fflush(stdout);
static int c = ' '; // static, initializes once, remembers prev.
// value
// checking mCharIOActive may be too much of a precaution since
// this method will not be called unless char I/O is enabled
if (mCharIOActive && mIOEcho && isprint(c)) putchar(c);
if (nonblock) {
// get a keystroke only if character is already in buffer
if (kbhit()) c = getch();
@ -375,10 +421,31 @@ char MemMapDev::GetCharOut()
return ret;
}
/*
*--------------------------------------------------------------------
* Method: CharIOFlush()
* Purpose: Flush the character I/O FIFO output buffer contents
* to the character I/O device's screen buffer.
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
void MemMapDev::CharIOFlush()
{
char cr = -1;
while ((cr = GetCharOut()) != -1) {
mpCharIODisp->PutChar(cr);
}
}
/*
*--------------------------------------------------------------------
* Method: PutCharIO()
* Purpose: Put character in the output char I/O FIFO buffer.
* If character I/O device emulation is enabled, print the
* character to the standard output, then flush the I/O
* FIFO output buffer to the character device screen
* buffer.
* Arguments: c - character
* Returns: n/a
*--------------------------------------------------------------------
@ -388,6 +455,10 @@ void MemMapDev::PutCharIO(char c)
mCharIOBufOut[mOutBufDataEnd] = c;
mOutBufDataEnd++;
if (mOutBufDataEnd >= CHARIO_BUF_SIZE) mOutBufDataEnd = 0;
if (mCharIOActive) {
putchar((int)c);
CharIOFlush();
}
}
/*
@ -451,6 +522,69 @@ bool MemMapDev::GetCharIOEchoOn()
return mIOEcho;
}
/*
*--------------------------------------------------------------------
* Method: IsCharIOActive()
* Purpose:
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
bool MemMapDev::IsCharIOActive()
{
return mCharIOActive;
}
/*
*--------------------------------------------------------------------
* Method: ActivateCharIO()
* Purpose: Activate character I/O device, create Display object.
* Arguments: n/a
* Returns: Pointer to Display object.
*--------------------------------------------------------------------
*/
Display *MemMapDev::ActivateCharIO()
{
if (NULL == mpCharIODisp) {
mpCharIODisp = new Display();
if (NULL == mpCharIODisp)
throw MKGenException(
"Out of memory while initializing Character I/O Display Device");
}
mCharIOActive = true;
mpCharIODisp->ClrScr();
return mpCharIODisp;
}
/*
*--------------------------------------------------------------------
* Method: GetDispPtr()
* Purpose:
* Arguments:
* Returns: Pointer to Display object.
*--------------------------------------------------------------------
*/
Display *MemMapDev::GetDispPtr()
{
return mpCharIODisp;
}
/*
*--------------------------------------------------------------------
* Method: DeactivateCharIO()
* Purpose:
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
void MemMapDev::DeactivateCharIO()
{
if (NULL != mpCharIODisp) delete mpCharIODisp;
mpCharIODisp = NULL;
mCharIOActive = false;
}
/*
*--------------------------------------------------------------------
* Method: GetGraphDispAddrBase()
@ -668,4 +802,20 @@ void MemMapDev::GraphDisp_Update()
if (NULL != mpGraphDisp) mpGraphDisp->Update();
}
/*
*--------------------------------------------------------------------
* Method: SetCharIODispPtr()
* Purpose: Set internal pointer to character I/O device object.
* Arguments: p - pointer to Display object.
* active - bool, true if character I/O is active
* Returns: n/a
*--------------------------------------------------------------------
*/
/*
void MemMapDev::SetCharIODispPtr(Display *p, bool active)
{
mpCharIODisp = p;
mCharIOActive = active;
}*/
} // namespace MKBasic

View File

@ -1,3 +1,35 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: MemMapDev.h
*
* Purpose: Prototype of MemMapDev class and all supporting
* data structures, enumarators, constants and macros.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#ifndef MEMMAPDEV_H
#define MEMMAPDEV_H
@ -6,6 +38,7 @@
#include "system.h"
//#include "Memory.h"
#include "GraphDisp.h"
#include "Display.h"
#if defined(LINUX)
#include <unistd.h>
@ -102,6 +135,7 @@ struct Device {
typedef vector<Device> MemMappedDevices;
// currently supported devices
enum DevNums {
DEVNUM_CHARIO = 0, // character I/O device
DEVNUM_GRDISP = 1, // raster graphics display device
@ -112,7 +146,7 @@ enum DevNums {
*
* The emulated device is a model of a electronic device that is connected
* to the CPU-s bus (address, memory, control signals).
* Depending on the device, the memory address range maybe as simple as
* Depending on the device, the memory address range may be as simple as
* a single memory address or it may contain multiple memory addresses
* or ranges of addresses positioned at various non-contiguous places.
* The functions of these addresses are handled internally by the MemMapDev
@ -184,8 +218,13 @@ class MemMapDev {
char GetCharIn();
char GetCharOut();
void CharIOFlush();
unsigned short GetCharIOAddr();
bool GetCharIOEchoOn();
bool IsCharIOActive();
Display* ActivateCharIO();
Display* GetDispPtr();
void DeactivateCharIO();
int CharIODevice_Read(int addr);
void CharIODevice_Write(int addr, int val);
@ -200,6 +239,8 @@ class MemMapDev {
void GraphDisp_ReadEvents();
void GraphDisp_Update();
//void SetCharIODispPtr(Display *p, bool active);
private:
Memory *mpMem; // pointer to memory object
@ -214,6 +255,8 @@ class MemMapDev {
bool mIOEcho;
unsigned int mGraphDispAddr;
GraphDisp *mpGraphDisp; // pointer to Graphics Device object
Display *mpCharIODisp; // pointer to character I/O device object
bool mCharIOActive; // indicate if character I/O is active
GraphDeviceRegs mGrDevRegs; // graphics display device registers
void Initialize();

View File

@ -1,4 +1,42 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: Memory.cpp.
*
* Purpose: Implementation of class Memory.
* The Memory class implements the highest abstraction
* layer of Visrtual Machine's memory. in this particular
* case the Virtual Machine emulates a computer system
* based on a 8-bit microprocessor. Therefore it
* implements image size and addressing space adequate
* for the specific architecture of such microprocessor.
* The Memory class also interfaces with the MemMapDev
* API (Memory Mapped Devices).
*
* Date:
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#include "Memory.h"
#include "MKGenException.h"
@ -322,6 +360,7 @@ void Memory::SetCharIO(unsigned short addr, bool echo)
SetupDevice(DEVNUM_CHARIO, memaddr_ranges, dev_params);
if (false == mCharIOActive) AddDevice(DEVNUM_CHARIO);
mCharIOActive = true;
mpMemMapDev->ActivateCharIO();
}
/*
@ -336,6 +375,7 @@ void Memory::DisableCharIO()
{
mCharIOActive = false;
DeleteDevice(DEVNUM_CHARIO);
mpMemMapDev->DeactivateCharIO();
}
/*
@ -405,10 +445,10 @@ unsigned short Memory::GetGraphDispAddr()
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
* Method: GetROMBegin()
* Purpose: Get starting address of read-only memory.
* Arguments:
* Returns: unsigned short - address ($0000-$FFFF)
*--------------------------------------------------------------------
*/
unsigned short Memory::GetROMBegin()
@ -418,10 +458,10 @@ unsigned short Memory::GetROMBegin()
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Method: GetROMEnd()
* Purpose: Get end address of read-only memory.
* Arguments:
* Returns:
* Returns: unsigned short - address ($0000-$FFFF)
*--------------------------------------------------------------------
*/
unsigned short Memory::GetROMEnd()
@ -431,10 +471,10 @@ unsigned short Memory::GetROMEnd()
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
* Method: IsROMEnabled()
* Purpose: Get status of ROM.
* Arguments:
* Returns: bool - true if enabled.
*--------------------------------------------------------------------
*/
bool Memory::IsROMEnabled()
@ -445,7 +485,7 @@ bool Memory::IsROMEnabled()
/*
*--------------------------------------------------------------------
* Method: AddDevice()
* Purpose: Add device number to active devices list.
* Purpose: Add device to active devices cache.
* Arguments: devnum - device number
* Returns: -1 if device is not supported OR already cached
* devnum - device number if it was found
@ -487,7 +527,7 @@ int Memory::AddDevice(int devnum)
}
}
} // END if (dev.num >= 0)
// else device with wuch number is not supported
// else device with such number is not supported
return ret;
}
@ -495,7 +535,7 @@ int Memory::AddDevice(int devnum)
/*
*--------------------------------------------------------------------
* Method: DeleteDevice()
* Purpose: Delete device number from active devices list.
* Purpose: Delete device from active devices cache.
* Arguments: devnum - device number
* Returns: >=0 if device was found in local cache and deleted
* -1 if device was not found
@ -607,10 +647,10 @@ char Memory::GetCharOut()
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
* Method: GraphDisp_ReadEvents()
* Purpose: Read events from the graphics display window.
* Arguments: n/a
* Returns: n/a
*--------------------------------------------------------------------
*/
void Memory::GraphDisp_ReadEvents()
@ -620,10 +660,10 @@ void Memory::GraphDisp_ReadEvents()
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
* Method: GraphDisp_Update()
* Purpose: Trigger update handler of the graphics display window.
* Arguments: n/a
* Returns: n/a
*--------------------------------------------------------------------
*/
void Memory::GraphDisp_Update()
@ -646,4 +686,17 @@ bool Memory::GraphDispOp()
return mDispOp;
}
/*
*--------------------------------------------------------------------
* Method: GetMemMapDevPtr()
* Purpose: Get the pointer to MemMapDev object.
* Arguments: n/a
* Returns: Pointer to MemMapDev object.
*--------------------------------------------------------------------
*/
MemMapDev *Memory::GetMemMapDevPtr()
{
return mpMemMapDev;
}
} // namespace MKBasic

View File

@ -1,3 +1,35 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: Memory.h
*
* Purpose: Prototype of Memory class and supporting data
* structures, constants, enumerations and macros.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and exercise common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#ifndef MEMORY_H
#define MEMORY_H
@ -49,6 +81,7 @@ class Memory
void GraphDisp_ReadEvents();
void GraphDisp_Update();
bool GraphDispOp();
MemMapDev *GetMemMapDevPtr();
protected:

View File

@ -82,11 +82,14 @@ provided image file and enter the debug console menu.
which will be referred to as Debug Console:
STOPPED at 0
Emulation performance stats is OFF.
*-------------*-----------------------*----------*----------*
| PC: $0000 | Acc: $00 (00000000) | X: $00 | Y: $00 |
*-------------*-----------------------*----------*----------*
| NV-BDIZC |
| 00100000 | Last instr.:
| NV-BDIZC | :
| 00100000 | :
*-------------*
Stack: $ff
@ -95,6 +98,7 @@ Stack: $ff
I/O status: disabled, at: $e000, local echo: OFF.
Graphics status: disabled, at: $e002
ROM: disabled. Range: $d000 - $dfff.
Op-code execute history: disabled.
------------------------------------+----------------------------------------
C - continue, S - step | A - set address for next step
G - go/cont. from new address | N - go number of steps, P - IRQ
@ -103,11 +107,38 @@ ROM: disabled. Range: $d000 - $dfff.
E - toggle I/O local echo | F - toggle registers animation
J - set animation delay | M - dump memory, W - write memory
K - toggle ROM emulation | R - show registers, Y - snapshot
L - load memory image | O - display op-codes history
L - load memory image | O - display op-code exec. history
D - disassemble code in memory | Q - quit, 0 - reset, H - help
V - toggle graphics emulation |
V - toggle graphics emulation | U - enable/disable exec. history
Z - enable/disable debug traces | 1 - enable/disable perf. stats
2 - display debug traces | ? - show this menu
------------------------------------+----------------------------------------
> _
>
Upper part of the screen consists of current CPU registers status.
If performance statistics are enabled, they are right above the CPU
registers.
PC shows address of currently executed op-code, while to the right of status
flags below PC is a disassembled (now empty, showing just the ':' signs)
instructions section. It shows 2 lines, 1-st line is the previously executed
instruction while the 2-nd line shows current instruction. Instructions are
shown in binary (hex) and symbolic (disassembled mnemonics, hex arguments)
format.
Accumulator and index registers X and Y values are also shown in the CPU
registers section.
Stack pointer and its contents are displayed below the CPU registers.
The contents of the Processor registers, disassembled executed instructions
and stack pointer and contents are updated dymanically during the multi-step
program debugging.
Below the stack data we have current status of various flags and parameters
that don't belong to emulated CPU.
Next goes the full list of available commands with short descriptions.
The menu of commands is not always displayed. It is usually skipped when
there were no screen actions that could potentially obstruct that menu.
The menu of commands can be always recalled to the screen by issuing
command '?' after the command prompt '>' and pressing ENTER.
Each of the commands in Debug Console is interactive and can be used in two
ways - user can issue command in the prompt, press enter and follow the
@ -116,9 +147,9 @@ ROM: disabled. Range: $d000 - $dfff.
To see full help for above commands, issue command H in the prompt and press
ENTER.
A quick overview of the some commands available in Debug Console:
A quick overview of some of the commands available in Debug Console:
* Code execution and debugging commands.
2.1. Code execution and debugging aid commands.
C - Continue
@ -126,11 +157,12 @@ ROM: disabled. Range: $d000 - $dfff.
S - Step
Executes single opcode at current address.
Executes single op-code at current address. The address for the next
step can be changed with command 'A'.
A - Set address for next step
Sets current address to one provided is argument (or prompts for one).
Sets current address to one provided as argument (or prompts for one).
G - Execute (continue) from address.
@ -138,6 +170,10 @@ ROM: disabled. Range: $d000 - $dfff.
N - Execute # of steps.
Useful when we want to skip ahead (e.g.: in a loop) and execute multiple
op-codes before next stop. Look also at commands 'F' and 'J' for
features and parameters associated with it.
X - Execute from address.
F - Toggle registers animation.
@ -159,7 +195,7 @@ ROM: disabled. Range: $d000 - $dfff.
Displays current status of CPU registers, flags and stack.
O - Display op-codes history.
O - Display op-codes execute history.
Show the history of last executed op-codes/instructions, full with
disassembled mnemonic and argument.
@ -181,7 +217,47 @@ ROM: disabled. Range: $d000 - $dfff.
opcode if stack is empty, so the VM will never return from opcodes
execution loop unless user interrupts with CTRL-C or CTRL-Break.
* Memory access commands.
Z - Toggle enable/disable debug traces.
Emulator may produce debug messages that can be helpful when
troubleshooting issues with 6502 code. Since the immediate output
to the same console where emulated program is connected is in many
cases undesired, the debug messages are remembered in an internal
log. The log holds last 200 messages. If enabled, this log can be
recalled with command '2'. When the log is disabled and then enabled
again, the old log messages will be deleted.
2 - Display debug traces.
Displays debug traces log (200 maximum) in 20 messages long pages.
U - Toggle enable/disable op-codes execute history.
See also option 'O'.
This debuggin aid is not needed during normal emulator run, therefore
option to disable this feature was added, mainly because it affects
performance. But in debugging mode, when we often trace code step by
step, the performance compromise may be justified as a trade off for
getting more internal data from the system.
1 - Toggle enable/disable emulation performance statistics.
MKCpu class includes code to measure the emulation speed.
The speed is measured as a % of model 1 MHz MOS-6502. Number of executed
cycles is divided by number of microseconds that passed and multiplied
by 100 to get this figure.
Performance is only measured in the execute/run modes.
It doesn't make much sense in debug/step by step modes, therefore even
when the feature is enabled, the stats are not calculated in these modes
of operation.
Emulation performance feature is useful mainly during the development
cycle of the emulator and since this is a programming framework for
buiding your own virtual machines/CPU emulators, then it is included.
However as with any debugging aid, it affects performance, therefore
option was added to disable it when we just want to run the 6502 code
and enjoy it at maximum possible speed.
2.2. Memory access commands.
M - Dump memory.
@ -191,7 +267,7 @@ ROM: disabled. Range: $d000 - $dfff.
Writes provided values to memory starting at specified address.
* I/O devices commands.
2.3. I/O devices commands.
I - Toggle char I/O emulation.
@ -251,6 +327,76 @@ ROM: disabled. Range: $d000 - $dfff.
NOTE: All addresses and memory values are to be entered in hexadecimal
format.
2.4. Typical debug session.
Your typical debug session, after starting VM65 is:
* Load the memory image with command 'L' (if not already loaded from
command line). E.g.:
L A 6502_func_test.bin
* Set the program start address with command 'A' (if not already set
in the memory image definition file). E.g.:
A 0400
* Enable/disable various debug and I/O facilities as required by the
loaded 6502 code. E.g.: to enable dymanic processor registers updates
and show the executed code dynamically while executing multiple steps
with command 'N', you need to issue command 'F' to enable registers
animation and optionally 'J' to change the speed (delay) of the
step-by-step animation:
F
J 100
N 1000
OR if command 'S' is used to manually step through 6502 instructions,
the animation of the CPU registers doesn't have top be enabled.
The registers values and disassembled instructions will be refreshed
after each step on the screen.
Each step is considered a single instructions (not a clock cycle).
If user wants to skip several (thousands perhaps) instructions, then
registers animation (command 'F') should be disabled, which will
make the steps to proceed much quicker. E.g.: assuming that currently
the animation is enabled, and user wants to skip 5000 instructions
quickly without looking at the changing registers and disassembled
instructions, user would issue commands:
F
N 5000
At any moment during multi-step execution (command 'N'), user can
interrupt before all the steps are concluded with CTRL-C and then
lool-up/alter memory content (commands 'M', 'W'), change the address
of the next executed instruction (command 'A') etc. and then continue
debugging with 'S', 'N', 'X', 'C', '0' or 'P' commands.
User can enable the history of executed op-codes with command 'U' and
display the history of the last 20 op-codes and CPU registers values
in the last 20 steps. E.g.:
L A 6502_func_test.bin
A 0400
U
N 5
O
Output from 'O':
> o
PC : INSTR ACC | X | Y | PS | SP
------------------------------------+-----+-----+-----+-----
$0400: CLD $00 | $00 | $00 | $20 | $ff
$0401: LDX #$FF $00 | $ff | $00 | $a0 | $ff
$0403: TXS $00 | $ff | $00 | $a0 | $ff
$0404: LDA #$00 $00 | $ff | $00 | $22 | $ff
$0406: STA $0200 $00 | $ff | $00 | $22 | $ff
Type '?' and press [ENTER] for Menu ...
3. Implementing the Virtual Machine.
The Virtual Machine (or Emulator) is implemented by the means of multiple
@ -565,3 +711,58 @@ ROM: disabled. Range: $d000 - $dfff.
See Memory::SetGraphDisp() and VMachine::SetGraphDisp() methods for
an example. The SetGraphDisp() method in Memory class defines the memory
mapped device on a lower level and calls SetupDevice().
5. Debug traces.
VMachine class implements debug messages queue which can be used during
development cycle.
void EnableDebugTrace();
void DisableDebugTrace();
bool IsDebugTraceActive();
queue<string> GetDebugTraces();
void AddDebugTrace(string msg);
This is a FIFO queue and it is maintained to not exceed the length as
defined:
#define DBG_TRACE_SIZE 200 // maximum size of debug messages queue
Therefore the next message added to queue that overfills the queue causes
the earliest added message to be removed.
Shortly put - the last 200 added messages only are remembered internally
in VMachine object and can be recalled with GetDebugTraces() method like
shown in this example:
VMachine *pvm;
[...]
queue<string> dbgtrc(pvm->GetDebugTraces());
while (dbgtrc.size()) {
cout << dbgtrc.front() << endl;
dbgtrc.pop();
}
Good example of AddDebugTrace() use is EnableROM() method:
void VMachine::EnableROM(unsigned short start, unsigned short end)
{
mpRAM->EnableROM(start, end);
if (mDebugTraceActive) {
stringstream sssa, ssea;
string msg, startaddr, endaddr;
sssa << start;
sssa >> startaddr;
ssea << end;
ssea >> endaddr;
msg = "ROM ENABLED, Start: " + startaddr + ", End: " + endaddr + ".";
AddDebugTrace(msg);
}
}
NOTE: The AddDebugTrace method checks mDebugTraceActive flag internally,
however for performance's sake we also check it in the higher level
code especially if there are intermediate objects to be created
to produce the debug string. This way we don't execute code related
to creation of debug message unnecessarily if the debugging messages
are disabled.

View File

@ -21,13 +21,15 @@ Graphics display emulation requires SDL2.
Makefile are included to build under Windows 32/64 (mingw compiler required)
and under Linux Ubuntu or Ubuntu based distro.
SDL2 library must be on your execution path in order to run program.
E.g.:
set PATH=C:\src\SDL\lib\x64;%PATH%
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.
* Set environment variable SDLDIR.
* Set environment variable SDLDIR. (E.g.: set SDLDIR=C:\src\SDL)
* Run: makeming.bat
To build under Linux:
@ -595,6 +597,12 @@ D - diassemble code in memory
U - enable/disable exec. history
Toggle enable/disable of op-codes execute history.
Disabling this feature improves performance.
Z - enable/disable debug traces
Toggle enable/disable of debug traces.
2 - display debug traces
Display recent debug traces.
1 - enable/disable performance stats
Toggle enable/disable emulation speed measurement.
NOTE:
1. If no arguments provided, each command will prompt user to enter
@ -603,7 +611,7 @@ NOTE:
by pressing CTRL-C or CTRL-Pause/Break, which will generate
a "Operator Interrupt". However in the character input mode
use CTRL-Y combination or CTRL-Break (DOS), CTRL-C (Linux).
You may need to press ENTER after that in input mode (DOS).
You may need to press ENTER after that in input mode (DOS).
7. Command line usage.
@ -770,7 +778,7 @@ With all peripherals disabled and op-code history enabled we are down to
411 % on PC1 and 312 % on PC2.
Enabling and adding the emulated memory mapped devices to the pool may cause
the emulation speed to drop as well. Hovever even with currently implemented
the emulation speed to drop as well. However even with currently implemented
peripherals (char I/O, graphics raster device) enabled and actively used and
op-codes execute history enabled the performance is still well above 300 %
on both PC1 and on PC2 (* see annotations for PC configurations/specs).

View File

@ -1,14 +1,52 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: VMachine.cpp
*
* Purpose: Implementation of VMachine class.
* The VMachine class implements the Virtual Machine
* in its entirety. It creates all the objects that
* emulate the component's of the whole system and
* implements the methods to execute the code on the
* emulated platform.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <string.h>
#include "system.h"
#include "VMachine.h"
#include "MKGenException.h"
/*
#if defined(WINDOWS)
#include <conio.h>
#endif
*/
using namespace std;
@ -62,10 +100,11 @@ VMachine::VMachine(string romfname, string ramfname)
*/
VMachine::~VMachine()
{
delete mpDisp;
//delete mpDisp;
delete mpCPU;
delete mpROM;
delete mpRAM;
delete mpConIO;
}
/*
@ -82,7 +121,6 @@ void VMachine::InitVM()
mpRAM = new Memory();
mPerfStats.cycles = 0;
mPerfStats.micro_secs = 0;
mPerfStats.perf_onemhz = 0;
mPerfStats.prev_cycles = 0;
mPerfStats.prev_usec = 0;
@ -93,6 +131,8 @@ void VMachine::InitVM()
mCharIOAddr = CHARIO_ADDR;
mCharIOActive = mCharIO = false;
mGraphDispActive = false;
mPerfStatsActive = false;
mDebugTraceActive = false;
if (NULL == mpRAM) {
throw MKGenException("Unable to initialize VM (RAM).");
}
@ -105,14 +145,18 @@ void VMachine::InitVM()
if (NULL == mpCPU) {
throw MKGenException("Unable to initialize VM (CPU).");
}
/*
mpDisp = new Display();
if (NULL == mpDisp) {
throw MKGenException("Unable to initialize VM (Display).");
}
} */
mpConIO = new ConsoleIO();
if (NULL == mpConIO) {
throw MKGenException("Unable to initialize VM (ConsoleIO)");
}
mBeginTime = high_resolution_clock::now();
}
#if defined(WINDOWS)
/*
*--------------------------------------------------------------------
* Method: ClearScreen()
@ -124,39 +168,7 @@ void VMachine::InitVM()
*/
void VMachine::ClearScreen()
{
HANDLE hStdOut;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD count;
DWORD cellCount;
COORD homeCoords = { 0, 0 };
hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
if (hStdOut == INVALID_HANDLE_VALUE) return;
/* Get the number of cells in the current buffer */
if (!GetConsoleScreenBufferInfo( hStdOut, &csbi )) return;
cellCount = csbi.dwSize.X *csbi.dwSize.Y;
/* Fill the entire buffer with spaces */
if (!FillConsoleOutputCharacter(
hStdOut,
(TCHAR) ' ',
cellCount,
homeCoords,
&count
)) return;
/* Fill the entire buffer with the current colors and attributes */
if (!FillConsoleOutputAttribute(
hStdOut,
csbi.wAttributes,
cellCount,
homeCoords,
&count
)) return;
/* Move the cursor home */
SetConsoleCursorPosition( hStdOut, homeCoords );
mpConIO->ClearScreen();
}
/*
@ -170,50 +182,9 @@ void VMachine::ClearScreen()
*/
void VMachine::ScrHome()
{
HANDLE hStdOut;
COORD homeCoords = { 0, 0 };
hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
if (hStdOut == INVALID_HANDLE_VALUE) return;
/* Move the cursor home */
SetConsoleCursorPosition( hStdOut, homeCoords );
mpConIO->ScrHome();
}
#endif
#if defined(LINUX)
/*
*--------------------------------------------------------------------
* Method: ClearScreen()
* Purpose: Clear the working are of the VM - Linux.
* This is not a part of virtual display emulation.
* Arguments: n/a
* Returns: n/a
*--------------------------------------------------------------------
*/
void VMachine::ClearScreen()
{
system("clear");
}
/*
*--------------------------------------------------------------------
* Method: ScrHome()
* Purpose: Bring the console cursor to home position - Linux.
* This is not a part of virtual display emulation.
* Arguments: n/a
* Returns: n/a
*--------------------------------------------------------------------
*/
void VMachine::ScrHome()
{
cout << "\033[1;1H";
}
#endif
/*
*--------------------------------------------------------------------
* Method: ShowDisp()
@ -224,7 +195,7 @@ void VMachine::ScrHome()
*/
void VMachine::ShowDisp()
{
if (mCharIOActive) {
if (mCharIOActive && NULL != mpDisp) {
ScrHome();
mpDisp->ShowScr();
}
@ -233,36 +204,67 @@ void VMachine::ShowDisp()
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
* Method: CalcCurrPerf()
* Purpose: Calculate CPU emulation performance based on 1 MHz model
* CPU.
* Arguments: n/a
* Returns: Integer, the % of speed as compared to 1 MHz CPU.
*--------------------------------------------------------------------
*/
int VMachine::CalcCurrPerf()
{
auto lap = high_resolution_clock::now();
auto beg = mPerfStats.begin_time;
mPerfStats.micro_secs = duration_cast<microseconds>(lap-beg).count();
if (!mPerfStatsActive) return 0;
if (mPerfStats.micro_secs > 0) {
int currperf = (int)
(((double)mPerfStats.cycles / (double)mPerfStats.micro_secs) * 100.0);
auto lap = high_resolution_clock::now();
long usec = duration_cast<microseconds>(lap-mPerfStats.begin_time).count();
if (usec > 0) {
int currperf = (int)(((double)mPerfStats.cycles / (double)usec) * 100.0);
if (mPerfStats.perf_onemhz == 0)
mPerfStats.perf_onemhz = currperf;
else
mPerfStats.perf_onemhz = (mPerfStats.perf_onemhz + currperf) / 2;
mPerfStats.prev_cycles = mPerfStats.cycles;
mPerfStats.prev_usec = mPerfStats.micro_secs;
mPerfStats.prev_usec = usec;
mPerfStats.cycles = 0;
mPerfStats.micro_secs = 0;
mPerfStats.begin_time = high_resolution_clock::now();
mPerfStats.begin_time = lap;
if (mDebugTraceActive) { // prepare and log some debug traces
stringstream sscp, ssap;
string msg, avgprf, curprf;
ssap << mPerfStats.perf_onemhz;
ssap >> avgprf;
sscp << currperf;
sscp >> curprf;
msg = "Perf. measured. Curr.: " + curprf + " %, Avg.: " + avgprf + " %";
AddDebugTrace(msg);
}
}
return mPerfStats.perf_onemhz;
}
/*
*--------------------------------------------------------------------
* Method: PERFSTAT_LAP (macro)
* Purpose: Calculate emulation performace at pre-defined interval
* of real time and clock ticks.
* Arguments: cycles - long : number of clock ticks executed so far
* begin - time_point<high_resolution_clock> : the moment
* when time measurement started
* Returns: n/a
* Remarks: Call inside emulation execute loop.
*--------------------------------------------------------------------
*/
#define PERFSTAT_LAP(cycles,begin) \
{ \
if (mPerfStatsActive && cycles%PERFSTAT_CYCLES == 0) { \
long usec = duration_cast<microseconds> \
(high_resolution_clock::now()-begin).count(); \
if (usec >= PERFSTAT_INTERVAL) CalcCurrPerf(); \
} \
}
/*
*--------------------------------------------------------------------
* Method: Run()
@ -275,18 +277,18 @@ Regs *VMachine::Run()
{
Regs *cpureg = NULL;
AddDebugTrace("Running code at: $" + Addr2HexStr(mRunAddr));
mOpInterrupt = false;
ClearScreen();
ShowDisp();
mPerfStats.cycles = 0;
mPerfStats.micro_secs = 0;
mPerfStats.begin_time = high_resolution_clock::now();
while (true) {
mPerfStats.cycles++;
cpureg = Step();
if (cpureg->CyclesLeft == 0 && mCharIO) ShowDisp();
if (cpureg->SoftIrq || mOpInterrupt) break;
//if (mPerfStats.cycles == PERFSTAT_INTERVAL) CalcCurrPerf();
PERFSTAT_LAP(mPerfStats.cycles,mPerfStats.begin_time);
}
CalcCurrPerf();
@ -323,21 +325,17 @@ Regs *VMachine::Exec()
{
Regs *cpureg = NULL;
AddDebugTrace("Executing code at: $" + Addr2HexStr(mRunAddr));
mOpInterrupt = false;
ClearScreen();
ShowDisp();
mPerfStats.cycles = 0;
mPerfStats.micro_secs = 0;
mPerfStats.begin_time = high_resolution_clock::now();
while (true) {
mPerfStats.cycles++;
cpureg = Step();
if (cpureg->CyclesLeft == 0 && mCharIO) {
cout << mpDisp->GetLastChar();
cout << flush;
}
if (cpureg->LastRTS || mOpInterrupt) break;
//if (mPerfStats.cycles == PERFSTAT_INTERVAL) CalcCurrPerf();
PERFSTAT_LAP(mPerfStats.cycles,mPerfStats.begin_time);
}
CalcCurrPerf();
@ -383,28 +381,13 @@ Regs *VMachine::Exec(unsigned short addr)
*/
Regs *VMachine::Step()
{
unsigned short addr = mRunAddr;
Regs *cpureg = NULL;
cpureg = mpCPU->ExecOpcode(addr);
cpureg = mpCPU->ExecOpcode(mRunAddr);
if (mGraphDispActive && cpureg->CyclesLeft == 0) {
mpRAM->GraphDisp_ReadEvents();
}
addr = cpureg->PtrAddr;
mRunAddr = addr;
if (cpureg->CyclesLeft == 0 && mCharIOActive && !mOpInterrupt) {
char c = -1;
mCharIO = false;
while ((c = mpRAM->GetCharOut()) != -1) {
mOpInterrupt = mOpInterrupt || (c == OPINTERRUPT);
if (!mOpInterrupt) {
mpDisp->PutChar(c);
mCharIO = true;
}
}
}
mRunAddr = cpureg->PtrAddr;
return cpureg;
}
@ -458,6 +441,15 @@ int VMachine::LoadRAM(string ramfname)
break;
}
mError = err;
if (mDebugTraceActive && err) {
stringstream sserr;
string msg, strerr;
sserr << err;
sserr >> strerr;
msg = "ERROR: LoadRAM, error code: " + strerr;
AddDebugTrace(msg);
}
return err;
}
@ -560,6 +552,8 @@ bool VMachine::HasHdrData(FILE *fp)
}
ret = (0 == strncmp(buf, HDRMAGICKEY, l));
AddDebugTrace(((ret) ? "HasHdrData: YES" : "HasHdrData: NO"));
return ret;
}
@ -590,6 +584,8 @@ bool VMachine::HasOldHdrData(FILE *fp)
}
ret = (0 == strncmp(buf, HDRMAGICKEY_OLD, l));
AddDebugTrace(((ret) ? "HasOldHdrData: YES" : "HasOldHdrData: NO"));
return ret;
}
@ -655,20 +651,26 @@ bool VMachine::LoadHdrData(FILE *fp)
switch (n)
{
case 1: mRunAddr = l + 256 * val;
ADD_DBG_LDMEMPARHEX("LoadHdrData : mRunAddr",mRunAddr);
break;
case 3: mCharIOAddr = l + 256 * val;
ADD_DBG_LDMEMPARHEX("LoadHdrData : mCharIOAddr",mCharIOAddr);
break;
case 5: rb = l + 256 * val;
break;
case 7: re = l + 256 * val;
break;
case 8: mCharIOActive = (val != 0);
ADD_DBG_LDMEMPARVAL("LoadHdrData : mCharIOActive",mCharIOActive);
break;
case 9: if (val != 0) {
mpRAM->EnableROM(rb, re);
} else {
mpRAM->SetROM(rb, re);
}
ADD_DBG_LDMEMPARHEX("LoadHdrData : ROM begin",rb);
ADD_DBG_LDMEMPARHEX("LoadHdrData : ROM end",re);
ADD_DBG_LDMEMPARVAL("LoadHdrData : ROM enable",((val!=0)?1:0));
break;
case 10: r.Acc = val;
break;
@ -682,9 +684,11 @@ bool VMachine::LoadHdrData(FILE *fp)
ret = true;
break;
case 15: mGraphDispActive = (val != 0);
ADD_DBG_LDMEMPARVAL("LoadHdrData : mGraphDispActive",mGraphDispActive);
break;
case 17: if (mGraphDispActive) SetGraphDisp(l + 256 * val);
else DisableGraphDisp();
ADD_DBG_LDMEMPARHEX("LoadHdrData : Graph. Disp. addr",(l + 256 * val));
break;
default: break;
}
@ -699,9 +703,6 @@ bool VMachine::LoadHdrData(FILE *fp)
return ret;
}
// Macro to save header data: v - value, fp - file pointer, n - data counter (dec)
#define SAVE_HDR_DATA(v,fp,n) {fputc(v, fp); n--;}
/*
*--------------------------------------------------------------------
* Method: SaveHdrData()
@ -788,6 +789,14 @@ int VMachine::SaveSnapshot(string fname)
fclose(fp);
}
if (0 != ret) mError = VMERR_SAVE_SNAPSHOT;
if (mDebugTraceActive && ret) {
stringstream sserr;
string msg, strerr;
sserr << ret;
sserr >> strerr;
msg = "ERROR: SaveSnapshot, error code: " + strerr;
AddDebugTrace(msg);
}
return ret;
}
@ -829,6 +838,7 @@ int VMachine::LoadRAMBin(string ramfname)
Memory *pm = mpRAM;
int ret = MEMIMGERR_RAMBIN_OPEN;
AddDebugTrace("LoadRAMBin : " + ramfname);
mOldStyleHeader = false;
if ((fp = fopen(ramfname.c_str(), "rb")) != NULL) {
if (HasHdrData(fp) || (mOldStyleHeader = HasOldHdrData(fp))) {
@ -1074,7 +1084,11 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
bool romendset = false, engraph = false, graphset = false;
Memory *pm = pmem;
int err = MEMIMGERR_OK;
if (mDebugTraceActive) {
string msg = "LoadMEM: " + memfname;
AddDebugTrace(msg);
}
if ((fp = fopen(memfname.c_str(), "r")) != NULL) {
DisableROM();
DisableCharIO();
@ -1100,8 +1114,10 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
} else {
err = MEMIMGERR_VM65_IGNPROCWRN;
errc++;
cout << "LINE #" << dec << lc << " WARNING: Run address was already set. Ignoring..." << endl;
ADD_DBG_LOADMEM(lc," WARNING: Run address was already set. Ignoring...");
//cout << "LINE #" << dec << lc << " WARNING: Run address was already set. Ignoring..." << endl;
}
ADD_DBG_LDMEMPARHEX("ADDR",addr);
continue;
}
// change address counter
@ -1115,6 +1131,7 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
} else {
addr = (unsigned short) atoi(line);
}
ADD_DBG_LDMEMPARHEX("ORG",addr);
continue;
}
// define I/O emulation address (once)
@ -1133,8 +1150,10 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
} else {
err = MEMIMGERR_VM65_IGNPROCWRN;
errc++;
cout << "LINE #" << dec << lc << " WARNING: I/O address was already set. Ignoring..." << endl;
ADD_DBG_LOADMEM(lc," WARNING: I/O address was already set. Ignoring...");
//cout << "LINE #" << dec << lc << " WARNING: I/O address was already set. Ignoring..." << endl;
}
ADD_DBG_LDMEMPARHEX("IOADDR",mCharIOAddr);
continue;
}
// define generic graphics display device base address (once)
@ -1153,23 +1172,28 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
} else {
err = MEMIMGERR_VM65_IGNPROCWRN;
errc++;
cout << "LINE #" << dec << lc << " WARNING: graphics device base address was already set. Ignoring..." << endl;
ADD_DBG_LOADMEM(lc," WARNING: graphics device base address was already set. Ignoring...");
//cout << "LINE #" << dec << lc << " WARNING: graphics device base address was already set. Ignoring..." << endl;
}
ADD_DBG_LDMEMPARHEX("GRAPHADDR",graphaddr);
continue;
}
// enable character I/O emulation
if (0 == strncmp(line, "ENIO", 4)) {
enio = true;
ADD_DBG_LDMEMPARVAL("ENIO",enio);
continue;
}
// enable generic graphics display emulation
if (0 == strncmp(line, "ENGRAPH", 7)) {
engraph = true;
ADD_DBG_LDMEMPARVAL("ENIO",engraph);
continue;
}
// enable ROM emulation
if (0 == strncmp(line, "ENROM", 5)) {
enrom = true;
ADD_DBG_LDMEMPARVAL("ENROM",enrom);
continue;
}
// auto execute from address
@ -1189,13 +1213,16 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
} else {
err = MEMIMGERR_VM65_IGNPROCWRN;
errc++;
cout << "LINE #" << dec << lc << " WARNING: auto-exec address was already set. Ignoring..." << endl;
ADD_DBG_LOADMEM(lc," WARNING: auto-exec address was already set. Ignoring...");
//cout << "LINE #" << dec << lc << " WARNING: auto-exec address was already set. Ignoring..." << endl;
}
ADD_DBG_LDMEMPARHEX("EXEC",mRunAddr);
continue;
}
// auto reset
if (0 == strncmp(line, "RESET", 5)) {
mAutoReset = true;
ADD_DBG_LDMEMPARVAL("RESET",mAutoReset);
continue;
}
// define ROM begin address
@ -1214,8 +1241,10 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
} else {
err = MEMIMGERR_VM65_IGNPROCWRN;
errc++;
cout << "LINE #" << dec << lc << " WARNING: ROM-begin address was already set. Ignoring..." << endl;
ADD_DBG_LOADMEM(lc," WARNING: ROM-begin address was already set. Ignoring...");
//cout << "LINE #" << dec << lc << " WARNING: ROM-begin address was already set. Ignoring..." << endl;
}
ADD_DBG_LDMEMPARHEX("ROMBEGIN",rombegin);
continue;
}
// define ROM end address
@ -1234,8 +1263,10 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
} else {
err = MEMIMGERR_VM65_IGNPROCWRN;
errc++;
cout << "LINE #" << dec << lc << " WARNING: ROM-end address was already set. Ignoring..." << endl;
ADD_DBG_LOADMEM(lc," WARNING: ROM-end address was already set. Ignoring...");
//cout << "LINE #" << dec << lc << " WARNING: ROM-end address was already set. Ignoring..." << endl;
}
ADD_DBG_LDMEMPARHEX("ROMEND",romend);
continue;
}
if (';' == *line) continue; // skip comment lines
@ -1269,7 +1300,8 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
}
else {
err = MEMIMGERR_VM65_OPEN;
cout << "WARNING: Unable to open memory definition file: " << memfname << endl;
cout << "WARNING: Unable to open memory definition file: ";
cout << memfname << endl;
errc++;
}
if (errc) {
@ -1341,7 +1373,13 @@ void VMachine::SetCharIO(unsigned short addr, bool echo)
mCharIOAddr = addr;
mCharIOActive = true;
mpRAM->SetCharIO(addr, echo);
mpDisp->ClrScr();
mpDisp = mpRAM->GetMemMapDevPtr()->GetDispPtr();
if (mDebugTraceActive) {
string msg;
msg = "Char I/O activated at: $" + Addr2HexStr(addr) + ", echo: "
+ ((echo) ? "ON" : "OFF");
AddDebugTrace(msg);
}
}
/*
@ -1356,6 +1394,8 @@ void VMachine::DisableCharIO()
{
mCharIOActive = false;
mpRAM->DisableCharIO();
AddDebugTrace("Char I/O DISABLED");
//mpRAM->GetMemMapDevPtr()->DeactivateCharIO();
}
/*
@ -1386,16 +1426,21 @@ bool VMachine::GetCharIOActive()
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
* Method: SetGraphDisp()
* Purpose: Set graphics device address and enable.
* Arguments: addr - unsigned short : device base address.
* Returns: n/a
*--------------------------------------------------------------------
*/
void VMachine::SetGraphDisp(unsigned short addr)
{
mGraphDispActive = true;
mpRAM->SetGraphDisp(addr);
if (mDebugTraceActive) {
string msg;
msg = "Graphics Device set at: $" + Addr2HexStr(addr) + ".";
AddDebugTrace(msg);
}
}
/*
@ -1410,6 +1455,7 @@ void VMachine::DisableGraphDisp()
{
mGraphDispActive = false;
mpRAM->DisableGraphDisp();
AddDebugTrace("Graphics Device DISABLED.");
}
/*
@ -1448,7 +1494,7 @@ bool VMachine::GetGraphDispActive()
*/
void VMachine::ShowIO()
{
if (mCharIOActive)
if (mCharIOActive && NULL != mpDisp)
mpDisp->ShowScr();
}
@ -1489,6 +1535,7 @@ bool VMachine::IsAutoReset()
void VMachine::EnableROM()
{
mpRAM->EnableROM();
AddDebugTrace("ROM ENABLED.");
}
/*
@ -1502,6 +1549,7 @@ void VMachine::EnableROM()
void VMachine::DisableROM()
{
mpRAM->DisableROM();
AddDebugTrace("ROM DISABLED.");
}
/*
@ -1515,19 +1563,32 @@ void VMachine::DisableROM()
void VMachine::SetROM(unsigned short start, unsigned short end)
{
mpRAM->SetROM(start, end);
if (mDebugTraceActive) {
string msg;
msg = "ROM SET, Start: $" + Addr2HexStr(start) + ", End: $";
msg += Addr2HexStr(end) + ".";
AddDebugTrace(msg);
}
}
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
* Method: EnableROM()
* Purpose: Sets and enables Read Only Memory range.
* Arguments: start, end - unsigned short : start and end addresses
* of the ROM.
* Returns: n/a
*--------------------------------------------------------------------
*/
void VMachine::EnableROM(unsigned short start, unsigned short end)
{
mpRAM->EnableROM(start, end);
if (mDebugTraceActive) {
string msg;
msg = "ROM ENABLED, Start: $" + Addr2HexStr(start) + ", End: $";
msg += Addr2HexStr(end) + ".";
AddDebugTrace(msg);
}
}
/*
@ -1650,6 +1711,7 @@ void VMachine::Reset()
mpCPU->Reset();
Exec(mpCPU->GetRegs()->PtrAddr);
mpCPU->mExitAtLastRTS = true;
AddDebugTrace("*** CPU RESET ***");
}
/*
@ -1692,6 +1754,13 @@ int VMachine::GetLastError()
void VMachine::EnableExecHistory(bool enexehist)
{
mpCPU->EnableExecHistory(enexehist);
if (mDebugTraceActive) {
string msg;
msg = "The op-code execute history "
+ (string)((enexehist) ? "ENABLED" : "DISABLED");
msg += ".";
AddDebugTrace(msg);
}
}
/*
@ -1707,4 +1776,173 @@ bool VMachine::IsExecHistoryActive()
return mpCPU->IsExecHistoryEnabled();
}
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
void VMachine::EnableDebugTrace()
{
if (!mDebugTraceActive) {
mDebugTraceActive = true;
while (!mDebugTraces.empty()) mDebugTraces.pop();
}
}
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
void VMachine::DisableDebugTrace()
{
mDebugTraceActive = false;
}
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
bool VMachine::IsDebugTraceActive()
{
return mDebugTraceActive;
}
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
queue<string> VMachine::GetDebugTraces()
{
return mDebugTraces;
}
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
void VMachine::EnablePerfStats()
{
if (!mPerfStatsActive) {
mPerfStatsActive = true;
mPerfStats.cycles = 0;
mPerfStats.prev_cycles = 0;
mPerfStats.prev_usec = 0;
mPerfStats.perf_onemhz = 0;
AddDebugTrace("Performance stats ENABLED.");
}
}
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
void VMachine::DisablePerfStats()
{
mPerfStatsActive = false;
AddDebugTrace("Performance stats DISABLED.");
}
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
bool VMachine::IsPerfStatsActive()
{
return mPerfStatsActive;
}
/*
*--------------------------------------------------------------------
* Method: AddDebugTrace()
* Purpose: Add string to the debug messages queue. String is
* prefixed with time stamp (number of microseconds since
* start of program. Queue is maintained to not exceed
* DBG_TRACE_SIZE. If the size is exceeded with the next
* added debug message, the first one in the queue is
* deleted (FIFO).
* Arguments: msg - string : debug message.
* Returns:
*--------------------------------------------------------------------
*/
void VMachine::AddDebugTrace(string msg)
{
if (mDebugTraceActive) {
stringstream ss;
string mmsg;
// add timestamp in front (micro seconds)
auto lap = high_resolution_clock::now();
unsigned long usec = duration_cast<microseconds>(lap-mBeginTime).count();
ss << usec;
ss >> mmsg;
mmsg += " : " + msg;
mDebugTraces.push(mmsg);
while (mDebugTraces.size() > DBG_TRACE_SIZE) mDebugTraces.pop();
}
}
/*
*--------------------------------------------------------------------
* Method: Addr2HexStr()
* Purpose: Convert 16-bit address to a hex notation string.
* Arguments: addr - 16-bit unsigned
* Returns: string
*--------------------------------------------------------------------
*/
string VMachine::Addr2HexStr(unsigned short addr)
{
stringstream ss;
string ret;
ss << hex << addr;
ss >> ret;
return ret;
}
/*
*--------------------------------------------------------------------
* Method: Addr2DecStr()
* Purpose: Convert 16-bit address to a decimal notation string.
* Arguments: addr - 16-bit unsigned
* Returns: string
*--------------------------------------------------------------------
*/
string VMachine::Addr2DecStr(unsigned short addr)
{
stringstream ss;
string ret;
ss << addr;
ss >> ret;
return ret;
}
} // namespace MKBasic

View File

@ -1,3 +1,35 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: VMachine.h
*
* Purpose: Prototype of VMachine class and all supporting data
* structures, enumerations, constants and macros.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#ifndef VMACHINE_H
#define VMACHINE_H
@ -8,6 +40,7 @@
#include "MKCpu.h"
#include "Memory.h"
#include "Display.h"
#include "ConsoleIO.h"
//#define WINDOWS 1
#if defined (WINDOWS)
@ -21,11 +54,46 @@
#define HDRDATALEN 128
#define HDRDATALEN_OLD 15
#define HEXEOF ":00000001FF"
//#define PERFSTAT_INTERVAL 30000000
// take emulation speed measurement every 2 minutes (120,000,000 usec)
#define PERFSTAT_INTERVAL 120000000
// but not more often than 30,000,000 clock ticks
#define PERFSTAT_CYCLES 30000000
#define DBG_TRACE_SIZE 200 // maximum size of debug messages queue
using namespace std;
using namespace chrono;
// Macros for debug log.
#define ADD_DBG_LOADMEM(lc,txt) \
if (mDebugTraceActive) \
{ \
stringstream ss; \
string msg, s; \
ss << lc; \
ss >> s; \
msg = "LINE #" + s + txt; \
AddDebugTrace(msg); \
}
#define ADD_DBG_LDMEMPARHEX(name,value) \
if (mDebugTraceActive) \
{ \
string msg; \
msg = (string)name + " = $" + Addr2HexStr(value); \
AddDebugTrace(msg); \
}
#define ADD_DBG_LDMEMPARVAL(name,value) \
if (mDebugTraceActive) \
{ \
string msg; \
msg = (string)name + " = " + Addr2DecStr(value); \
AddDebugTrace(msg); \
}
// Macro to save header data: v - value, fp - file pointer, n - data counter (dec)
#define SAVE_HDR_DATA(v,fp,n) {fputc(v, fp); n--;}
namespace MKBasic {
// Types of memory image definition file.
@ -70,7 +138,6 @@ struct PerfStats {
time_point<high_resolution_clock>
begin_time; // the moment of time count start
long cycles; // performance stats
long micro_secs; // performance stats
long prev_cycles; // previously measured stats
long prev_usec; // previously measured stats
int perf_onemhz; // avg. % perf. based on 1MHz CPU.
@ -131,16 +198,24 @@ class VMachine
// cycles per second (1 MHz CPU).
void EnableExecHistory(bool enexehist);
bool IsExecHistoryActive();
void EnableDebugTrace();
void DisableDebugTrace();
bool IsDebugTraceActive();
void EnablePerfStats();
void DisablePerfStats();
bool IsPerfStatsActive();
queue<string> GetDebugTraces();
protected:
private:
MKCpu *mpCPU;
Memory *mpROM;
Memory *mpRAM;
Display *mpDisp;
MKCpu *mpCPU; // object maintained locally
Memory *mpROM; // object maintained locally
Memory *mpRAM; // object maintained locally
Display *mpDisp; // just a pointer
ConsoleIO *mpConIO; // object maintained locally
unsigned short mRunAddr;
unsigned short mCharIOAddr;
bool mCharIOActive;
@ -152,6 +227,10 @@ class VMachine
bool mGraphDispActive;
bool mOldStyleHeader;
PerfStats mPerfStats;
queue<string> mDebugTraces;
bool mPerfStatsActive;
bool mDebugTraceActive;
time_point<high_resolution_clock> mBeginTime;
int LoadMEM(string memfname, Memory *pmem);
void ShowDisp();
@ -161,6 +240,9 @@ class VMachine
void SaveHdrData(FILE *fp);
eMemoryImageTypes GetMemoryImageType(string ramfname);
int CalcCurrPerf();
void AddDebugTrace(string msg);
string Addr2HexStr(unsigned short addr);
string Addr2DecStr(unsigned short addr);
};
} // namespace MKBasic

63
grdevdemo.bas Normal file
View File

@ -0,0 +1,63 @@
1 REM GRAPHICS DISPLAY DEVICE DEMO
2 REM BASE ADDRESS $FFE2
3 REM DRAW HORIZONTAL AND VERTICAL LINES
4 REM DRAW SINUSOID
10 GB=65506:REM SET BASE ADDRESS
12 REM INITIALIZE, SET COLORS
15 POKE GB+1,0:POKE GB+11,0
16 POKE GB+3,0:POKE GB+4,255:POKE GB+5,0
17 POKE GB+6,0:POKE GB+7,0:POKE GB+8,0
18 POKE GB+9,3:POKE GB+9,4:POKE GB+9,0
19 GOSUB 1120:REM DRAW SINUSOID
20 Y=100:REM X-AXIS
30 GOSUB 1000
50 X=100:REM Y-AXIS
60 GOSUB 1060
70 REM SOME EXTRA DOTTED LINES
80 Y=50:GOSUB 1200
90 Y=150:GOSUB 1200
100 X=50:GOSUB 1260
110 X=150:GOSUB 1260
120 PRINT "... HIT [SPACE] TO END ..."
125 GET K$:IF K$=" " THEN END
130 FOR I=1 TO 2000:NEXT I:REM SHORT PAUSE
140 GOTO 15
998 END
999 REM ------- SUBROUTINES SECTION -------
1000 REM DRAW HORIZONTAL LINE AT Y
1005 POKE GB+2,Y
1006 POKE GB+12,Y
1020 POKE GB,0
1025 POKE GB+10,199:POKE GB+9,5
1050 RETURN
1060 REM DRAW VERTICAL LINE AT X
1070 POKE GB,X
1075 POKE GB+10,X
1090 POKE GB+2,0
1095 POKE GB+12,199:POKE GB+9,5
1110 RETURN
1120 REM SINUSOID
1130 FOR X=0 TO 199-4 STEP 5
1140 XX=X*(6.28/200)
1145 XE=(X+5)*(6.28/200)
1150 YY=SIN(XX):YE=SIN(XE)
1160 Y=199-INT((YY+1)*100)
1165 Y2=199-INT((YE+1)*100)
1170 POKE GB,X:POKE GB+2,Y
1175 POKE GB+10,X+5:POKE GB+12,Y2:POKE GB+9,5
1180 NEXT X
1190 RETURN
1200 REM DRAW DOTTED HORIZONTAL LINE AT Y
1205 POKE GB+2,Y
1210 FOR X=0 TO 199 STEP 4
1220 POKE GB,X
1230 POKE GB+9,1
1240 NEXT X
1250 RETURN
1260 REM DRAW DOTTED VERTICAL LINE AT X
1270 POKE GB,X
1280 FOR Y=0 TO 199 STEP 4
1290 POKE GB+2,Y
1300 POKE GB+9,1
1310 NEXT Y
1320 RETURN

377
main.cpp
View File

@ -1,3 +1,35 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: main.cpp
*
* Purpose: Define User Interface, Debug Console and main loop
* of the app.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and exercise common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#include <cstdlib>
#include <iostream>
#include <bitset>
@ -17,9 +49,16 @@ using namespace std;
using namespace MKBasic;
#define ANIM_DELAY 250
#define PROMPT_ADDR "Address (0..FFFF): "
#define PROMPT_START_ADDR "Start address (0..FFFF): "
#define PROMPT_RANGE_ADDR "Enter address range (0..0xFFFF).."
#define PROMPT_END_ADDR "End address (0..FFFF): "
const bool ClsIfDirty = true;
char diss_buf[DISS_BUF_SIZE]; // last disassembled instruction buffer
char curr_buf[DISS_BUF_SIZE]; // current disassembled instruction buffer
VMachine *pvm = NULL;
Regs *preg = NULL;
bool ioecho = false, opbrk = false, needhelp = false;
@ -32,6 +71,26 @@ void ShowHelp();
void CmdArgHelp(string prgname);
void CopyrightBanner();
/*
*--------------------------------------------------------------------
* Method: PressEnter2Cont()
* Purpose: Print a message and wait for ENTER to be pressed.
* Arguments: msg - string : message
* Returns:
*--------------------------------------------------------------------
*/
void PressEnter2Cont(string msg)
{
string mesg = msg;
if (0 == msg.length()) mesg = "Press [ENTER]...";
cout << mesg;
fflush(stdin);
while (true) {
int c = getchar();
if ('\n' == c || EOF == c) break;
}
}
/*
*--------------------------------------------------------------------
* Method: RunSingleInstr()
@ -44,9 +103,14 @@ void CopyrightBanner();
{
Regs *ret = NULL;
pvm->Disassemble(addr, diss_buf);
// skip # cycles per op-code specs
do {
ret = pvm->Step(addr);
} while (ret->CyclesLeft > 0);
// and now execute the actual op-code
ret = pvm->Step(addr);
pvm->Disassemble(ret->PtrAddr, curr_buf);
return ret;
}
@ -64,13 +128,56 @@ void CopyrightBanner();
{
Regs *ret = NULL;
pvm->Disassemble(preg->PtrAddr, diss_buf);
// skip # cycles per op-code specs
do {
ret = pvm->Step();
} while (ret->CyclesLeft > 0);
// and now execute the actual op-code
ret = pvm->Step();
pvm->Disassemble(ret->PtrAddr, curr_buf);
return ret;
}
/*
*--------------------------------------------------------------------
* Method: VMErr
* Purpose: Data structure and macros supporting VM errors
* messages
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
#define WARN_UNEXPECTED_EOF "WARNING: Unexpected EOF (image shorter than 64kB)."
#define WARN_NOHDR_BINIMG "WARNING: No header found in binary image."
#define WARN_HDRPRBLM_BINIMG "WARNING: Problem with binary image header."
struct VMErr {
int id;
char text1[80];
char text2[80];
} g_vmerrtbl[] = {
{MEMIMGERR_RAMBIN_EOF, WARN_UNEXPECTED_EOF, ""},
{MEMIMGERR_RAMBIN_OPEN, "WARNING: Unable to open memory image file.", ""},
{MEMIMGERR_RAMBIN_HDR, WARN_HDRPRBLM_BINIMG, ""},
{MEMIMGERR_RAMBIN_NOHDR, WARN_NOHDR_BINIMG, ""},
{MEMIMGERR_RAMBIN_HDRANDEOF, WARN_HDRPRBLM_BINIMG, WARN_UNEXPECTED_EOF},
{MEMIMGERR_RAMBIN_NOHDRANDEOF,WARN_NOHDR_BINIMG, WARN_UNEXPECTED_EOF},
{MEMIMGERR_INTELH_OPEN, "WARNING: Unable to open Intel HEX file.", ""},
{MEMIMGERR_INTELH_SYNTAX, "ERROR: Syntax error.", ""},
{MEMIMGERR_INTELH_FMT, "ERROR: Intel HEX format error.", ""},
{MEMIMGERR_VM65_OPEN, "ERROR: Unable to open memory definition file.", ""},
{MEMIMGERR_VM65_IGNPROCWRN, "WARNING: There were problems while processing memory definition file.", ""},
{VMERR_SAVE_SNAPSHOT, "WARNING: There was a problem saving memory snapshot.", ""},
{-1, "", ""}
};
/*
*--------------------------------------------------------------------
* Method: PrintVMErr()
@ -81,57 +188,23 @@ void CopyrightBanner();
*/
void PrintVMErr(int err)
{
bool pressenter = true;
switch (err) {
case MEMIMGERR_RAMBIN_EOF:
cout << "WARNING: Unexpected EOF (image shorter than 64kB).";
cout << endl;
break;
case MEMIMGERR_RAMBIN_OPEN:
cout << "WARNING: Unable to open memory image file." << endl;
break;
case MEMIMGERR_RAMBIN_HDR:
cout << "WARNING: Problem with binary image header." << endl;
break;
case MEMIMGERR_RAMBIN_NOHDR:
cout << "WARNING: No header found in binary image." << endl;
break;
case MEMIMGERR_RAMBIN_HDRANDEOF:
cout << "WARNING: Problem with binary image header." << endl;
cout << "WARNING: Unexpected EOF (image shorter than 64kB).";
cout << endl;
break;
case MEMIMGERR_RAMBIN_NOHDRANDEOF:
cout << "WARNING: No header found in binary image." << endl;
cout << "WARNING: Unexpected EOF (image shorter than 64kB).";
cout << endl;
break;
case MEMIMGERR_INTELH_OPEN:
cout << "WARNING: Unable to open Intel HEX file." << endl;
break;
case MEMIMGERR_INTELH_SYNTAX:
cout << "ERROR: Syntax error." << endl;
break;
case MEMIMGERR_INTELH_FMT:
cout << "ERROR: Intel HEX format error." << endl;
bool pressenter = false;
for (int i=0; g_vmerrtbl[i].id >= 0; i++) {
if (g_vmerrtbl[i].id == err) {
pressenter = true;
if (strlen(g_vmerrtbl[i].text1)) {
cout << g_vmerrtbl[i].text1 << endl;
}
if (strlen(g_vmerrtbl[i].text2)) {
cout << g_vmerrtbl[i].text2 << endl;
}
break;
case MEMIMGERR_VM65_OPEN:
cout << "ERROR: Unable to open memory definition file.";
cout << endl;
break;
case MEMIMGERR_VM65_IGNPROCWRN:
cout << "WARNING: There were problems while processing";
cout << " memory definition file." << endl;
break;
case VMERR_SAVE_SNAPSHOT:
cout << "WARNING: There was a problem saving memory snapshot.";
cout << endl;
break;
default: pressenter = false; break;
}
}
}
if (pressenter) {
cout << "Press [ENTER]...";
getchar();
PressEnter2Cont("");
}
}
@ -156,7 +229,7 @@ void trap_signal(int signum)
pvm->SetOpInterrupt(true);
opbrk = true;
}
//exit(signum);
return;
}
@ -181,7 +254,7 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
switch( fdwCtrlType )
{
case CTRL_C_EVENT:
//Beep( 750, 300 );
if (NULL != pvm && NULL != preg) {
pvm->SetOpInterrupt(true);
opbrk = true;
@ -190,12 +263,12 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
case CTRL_CLOSE_EVENT:
//Beep( 600, 200 );
cout << "Ctrl-Close event" << endl;
return TRUE ;
case CTRL_BREAK_EVENT:
//Beep( 900, 200 );
if (NULL != pvm && NULL != preg) {
pvm->SetOpInterrupt(true);
opbrk = true;
@ -203,7 +276,7 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
return TRUE;
case CTRL_LOGOFF_EVENT:
//Beep( 1000, 200 );
cout << "Ctrl-Logoff event" << endl;
return FALSE;
@ -275,18 +348,24 @@ bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat)
bool ret = false;
char sBuf[80] = {0};
sprintf(sBuf, "| PC: $%04x | Acc: $%02x (" BYTETOBINARYPATTERN ") | X: $%02x | Y: $%02x |",
preg->PtrAddr, preg->Acc, BYTETOBINARY(preg->Acc), preg->IndX, preg->IndY);
cout << "*-------------*-----------------------*----------*----------*" << endl;
sprintf(sBuf, "| PC: $%04x | Acc: $%02x (" BYTETOBINARYPATTERN
") | X: $%02x | Y: $%02x |",
preg->PtrAddr, preg->Acc, BYTETOBINARY(preg->Acc),
preg->IndX, preg->IndY);
cout << "*-------------*-----------------------*----------*----------*";
cout << endl;
cout << sBuf << endl;
cout << "*-------------*-----------------------*----------*----------*" << endl;
cout << "| NV-BDIZC |" << endl;
cout << "*-------------*-----------------------*----------*----------*";
cout << endl;
cout << "| NV-BDIZC |";
cout << " : " << diss_buf << " " << endl;
cout << "| " << bitset<8>((int)preg->Flags) << " |";
cout << " Last instr.: " << preg->LastInstr << " " << endl;
cout << " : " << curr_buf << " " << endl;
cout << "*-------------*" << endl;
cout << endl;
cout << "Stack: $" << hex << (unsigned short)preg->PtrStack << " " << endl;
cout << " \r";
cout << " ";
cout << " \r";
// display stack contents
cout << " [";
int j = 0, stacklines = 1;
@ -310,14 +389,19 @@ bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat)
// end display stack contents
if (showiostat) {
cout << endl << "I/O status: " << (pvm->GetCharIOActive() ? "enabled" : "disabled") << ", ";
cout << endl << "I/O status: ";
cout << (pvm->GetCharIOActive() ? "enabled" : "disabled") << ", ";
cout << " at: $" << hex << pvm->GetCharIOAddr() << ", ";
cout << " local echo: " << (ioecho ? "ON" : "OFF") << "." << endl;
cout << "Graphics status: " << (pvm->GetGraphDispActive() ? "enabled" : "disabled") << ", ";
cout << "Graphics status: ";
cout << (pvm->GetGraphDispActive() ? "enabled" : "disabled") << ", ";
cout << " at: $" << hex << pvm->GetGraphDispAddr() << endl;
cout << "ROM: " << ((pvm->IsROMEnabled()) ? "enabled." : "disabled.") << " ";
cout << "Range: $" << hex << pvm->GetROMBegin() << " - $" << hex << pvm->GetROMEnd() << "." << endl;
cout << "Op-code execute history: " << (pvm->IsExecHistoryActive() ? "enabled" : "disabled");
cout << "ROM: ";
cout << ((pvm->IsROMEnabled()) ? "enabled." : "disabled.") << " ";
cout << "Range: $" << hex << pvm->GetROMBegin() << " - $";
cout << hex << pvm->GetROMEnd() << "." << endl;
cout << "Op-code execute history: ";
cout << (pvm->IsExecHistoryActive() ? "enabled" : "disabled");
cout << "." << endl;
}
cout << " \r";
@ -327,8 +411,8 @@ bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat)
/*
*--------------------------------------------------------------------
* Method:
* Purpose:
* Method: ShowMenu()
* Purpose: Print available commands on the console.
* Arguments:
* Returns:
*--------------------------------------------------------------------
@ -346,6 +430,8 @@ void ShowMenu()
cout << " L - load memory image | O - display op-code exec. history" << endl;
cout << " D - disassemble code in memory | Q - quit, 0 - reset, H - help" << endl;
cout << " V - toggle graphics emulation | U - enable/disable exec. history" << endl;
cout << " Z - enable/disable debug traces | 1 - enable/disable perf. stats" << endl;
cout << " 2 - display debug traces | ? - show this menu" << endl;
cout << "------------------------------------+----------------------------------------" << endl;
}
@ -377,9 +463,9 @@ void ShowMenu()
brk = preg->SoftIrq; \
lrts = preg->LastRTS; \
while(step && nsteps > 1 && !brk && !lrts && !opbrk) { \
preg = RunSingleCurrInstr(); \
cout << "addr: $" << hex << preg->PtrAddr << ", step: " << dec << stct; \
cout << " \r"; \
preg = RunSingleCurrInstr(); \
if (anim) { \
if (cls & ClsIfDirty) { pvm->ClearScreen(); cls = false; } \
pvm->ScrHome(); \
@ -404,13 +490,19 @@ void ShowMenu()
*/
void ShowSpeedStats()
{
if (pvm->IsPerfStatsActive()) {
cout << endl;
cout << dec;
cout << "CPU emulation speed stats: " << endl;
cout << "|-> Average speed based on 1MHz CPU: " << pvm->GetPerfStats().perf_onemhz << " %" << endl;
cout << "|-> Last measured # of cycles exec.: " << pvm->GetPerfStats().prev_cycles << endl;
cout << "|-> Last measured time of execution: " << pvm->GetPerfStats().prev_usec << " usec" << endl;
cout << endl;
cout << dec;
cout << "CPU emulation speed stats: " << endl;
cout << "|-> Average speed based on 1MHz CPU: " << pvm->GetPerfStats().perf_onemhz << " %" << endl;
cout << "|-> Last measured # of cycles exec.: " << pvm->GetPerfStats().prev_cycles << endl;
cout << "|-> Last measured time of execution: " << pvm->GetPerfStats().prev_usec << " usec" << endl;
cout << endl;
} else {
cout << endl;
cout << "Emulation performance stats is OFF." << endl;
cout << endl;
}
}
/*
@ -425,8 +517,10 @@ void ExecHistory()
{
if (pvm->IsExecHistoryActive()) {
queue<string> exechist(pvm->GetExecHistory());
cout << "PC : INSTR ACC | X | Y | PS | SP" << endl;
cout << "------------------------------------+-----+-----+-----+-----" << endl;
cout << "PC : INSTR ACC | X | Y | PS | SP";
cout << endl;
cout << "------------------------------------+-----+-----+-----+-----";
cout << endl;
while (exechist.size()) {
cout << exechist.front() << endl;
exechist.pop();
@ -478,6 +572,7 @@ unsigned int LoadImage(unsigned int newaddr)
if (pvm->IsAutoExec()) execvm = true;
if (newaddr == 0) newaddr = 0x10000;
}
PrintVMErr(pvm->GetLastError());
return newaddr;
}
@ -496,7 +591,7 @@ unsigned int ToggleIO(unsigned int ioaddr)
pvm->DisableCharIO();
cout << "I/O deactivated." << endl;
} else {
ioaddr = PromptNewAddress("Address (0..FFFF): ");
ioaddr = PromptNewAddress(PROMPT_ADDR);
cout << " [" << hex << ioaddr << "]" << endl;
pvm->SetCharIO(ioaddr, ioecho);
cout << "I/O activated." << endl;
@ -519,7 +614,7 @@ unsigned int ToggleGrDisp(unsigned int graddr)
pvm->DisableGraphDisp();
cout << "Graphics display deactivated." << endl;
} else {
graddr = PromptNewAddress("Address (0..FFFF): ");
graddr = PromptNewAddress(PROMPT_ADDR);
cout << " [" << hex << graddr << "]" << endl;
pvm->SetGraphDisp(graddr);
cout << "Graphics display activated." << endl;
@ -531,14 +626,14 @@ unsigned int ToggleGrDisp(unsigned int graddr)
/*
*--------------------------------------------------------------------
* Method: WriteToMemory()
* Purpose:
* Purpose: Take user input and write to memory.
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
void WriteToMemory()
{
unsigned int tmpaddr = PromptNewAddress("Address (0..FFFF): ");
unsigned int tmpaddr = PromptNewAddress(PROMPT_ADDR);
cout << " [" << hex << tmpaddr << "]" << endl;
cout << "Enter hex bytes [00..FF] values separated with NL or spaces, end with [100]:" << endl;
unsigned short v = 0;
@ -554,7 +649,7 @@ void WriteToMemory()
/*
*--------------------------------------------------------------------
* Method: DisassembleMemory()
* Purpose:
* Purpose: Disassemble machine code in memory to symbolic format.
* Arguments:
* Returns:
*--------------------------------------------------------------------
@ -562,10 +657,10 @@ void WriteToMemory()
void DisassembleMemory()
{
unsigned int addrbeg = 0x10000, addrend = 0x10000;
cout << "Enter address range (0..0xFFFF)..." << endl;
addrbeg = PromptNewAddress("Start address (0..FFFF): ");
cout << PROMPT_RANGE_ADDR << endl;
addrbeg = PromptNewAddress(PROMPT_START_ADDR);
cout << " [" << hex << addrbeg << "]" << endl;
addrend = PromptNewAddress("End address (0..FFFF): ");
addrend = PromptNewAddress(PROMPT_END_ADDR);
cout << " [" << hex << addrend << "]" << endl;
cout << endl;
for (unsigned int addr = addrbeg; addr <= addrend;) {
@ -578,7 +673,7 @@ void DisassembleMemory()
/*
*--------------------------------------------------------------------
* Method: DumpMemory()
* Purpose:
* Purpose: Display contents of memory, range entered by user.
* Arguments:
* Returns:
*--------------------------------------------------------------------
@ -586,10 +681,10 @@ void DisassembleMemory()
void DumpMemory()
{
unsigned int addrbeg = 0x10000, addrend = 0x10000;
cout << "Enter address range (0..0xFFFF)..." << endl;
addrbeg = PromptNewAddress("Start address (0..FFFF): ");
cout << PROMPT_RANGE_ADDR << endl;
addrbeg = PromptNewAddress(PROMPT_START_ADDR);
cout << " [" << hex << addrbeg << "]" << endl;
addrend = PromptNewAddress("End address (0..FFFF): ");
addrend = PromptNewAddress(PROMPT_END_ADDR);
cout << " [" << hex << addrend << "]" << endl;
cout << endl;
for (unsigned int addr = addrbeg; addr <= addrend; addr+=16) {
@ -615,6 +710,87 @@ void DumpMemory()
}
}
/*
*--------------------------------------------------------------------
* Method: ToggleDebugTraces()
* Purpose: Enable/disable debug traces.
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
void ToggleDebugTraces()
{
if (pvm->IsDebugTraceActive()) {
pvm->DisableDebugTrace();
cout << "Debug traces disabled." << endl;
} else {
pvm->EnableDebugTrace();
cout << "Debug traces enabled." << endl;
}
}
/*
*--------------------------------------------------------------------
* Macro: SCRDIV_xxCOL
* Purpose: Print line out of xx '-' signs, no NL.
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
#define SCRDIV_20COL cout << "--------------------";
#define SCRDIV_19COL cout << "-------------------";
#define SCRDIV_79COL SCRDIV_20COL; SCRDIV_20COL; SCRDIV_20COL; SCRDIV_19COL;
/*
*--------------------------------------------------------------------
* Method: DebugTraces()
* Purpose: Show debug traces.
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
void DebugTraces()
{
if (pvm->IsDebugTraceActive()) {
queue<string> dbgtrc(pvm->GetDebugTraces());
cout << "Time [usec] : Message" << endl;
SCRDIV_79COL; cout << endl;
int n=0;
while (dbgtrc.size()) {
cout << dbgtrc.front() << endl;
dbgtrc.pop();
if (n++ == 20) {
n = 0;
cout << endl;
PressEnter2Cont("Press [ENTER] for more...");
cout << endl;
}
}
SCRDIV_79COL; cout << endl;
} else {
cout << "Sorry. Debug traces are currently disabled." << endl;
}
}
/*
*--------------------------------------------------------------------
* Method: TogglePerfStats()
* Purpose: Enable/disable performance stats.
* Arguments:
* Returns:
*--------------------------------------------------------------------
*/
void TogglePerfStats()
{
if (pvm->IsPerfStatsActive()) {
pvm->DisablePerfStats();
cout << "Performance stats were disabled." << endl;
} else {
pvm->EnablePerfStats();
cout << "Performance stats were enabled." << endl;
}
}
/*
*--------------------------------------------------------------------
* Method: LoadArgs()
@ -769,7 +945,7 @@ int main(int argc, char *argv[]) {
} else {
cout << endl;
cout << "Type '?' and press ENTER for Menu ..." << endl;
cout << "Type '?' and press [ENTER] for Menu ..." << endl;
cout << endl;
}
@ -805,6 +981,7 @@ int main(int argc, char *argv[]) {
ExecHistory();
} else if (c == 'l') { // load memory image
newaddr = LoadImage(newaddr);
ioaddr = pvm->GetCharIOAddr();
} else if (c == 'k') { // toggle ROM emulation
if (!enrom) {
enrom = true;
@ -856,7 +1033,7 @@ int main(int argc, char *argv[]) {
WriteToMemory();
} else if (c == 'a') { // change run address
execaddr = stop = true;
newaddr = PromptNewAddress("Address (0..FFFF): ");
newaddr = PromptNewAddress(PROMPT_ADDR);
cout << " [" << hex << newaddr << "]" << endl;
} else if (c == 's') {
runvm = step = stop = true;
@ -875,13 +1052,13 @@ int main(int argc, char *argv[]) {
} else if (c == 'g') { // run from new address until BRK
runvm = true;
execaddr = true;
newaddr = PromptNewAddress("Address (0..FFFF): ");
newaddr = PromptNewAddress(PROMPT_ADDR);
cout << " [" << hex << newaddr << "]" << endl;
show_menu = true;
} else if (c == 'x') { // execute code at address
execvm = true;
execaddr = true;
newaddr = PromptNewAddress("Address (0..FFFF): ");
newaddr = PromptNewAddress(PROMPT_ADDR);
cout << " [" << hex << newaddr << "]" << endl;
show_menu = true;
} else if (c == 'q') { // quit
@ -895,6 +1072,14 @@ int main(int argc, char *argv[]) {
cout << "Op-code execute history has been ";
cout << (pvm->IsExecHistoryActive() ? "enabled" : "disabled") << ".";
cout << endl;
} else if (c == 'z') { // toggle enable/disable debug traces in VM
ToggleDebugTraces();
} else if (c == '2') { // show debug traces
DebugTraces();
} else if (c == '1') { // toggle enable/disable perf. stats
TogglePerfStats();
} else {
cout << "ERROR: Unknown command." << endl;
}
}
}
@ -1158,6 +1343,12 @@ D - diassemble code in memory
U - enable/disable exec. history
Toggle enable/disable of op-codes execute history.
Disabling this feature improves performance.
Z - enable/disable debug traces
Toggle enable/disable of debug traces.
2 - display debug traces
Display recent debug traces.
1 - enable/disable performance stats
Toggle enable/disable emulation speed measurement.
NOTE:
1. If no arguments provided, each command will prompt user to enter

View File

@ -1,4 +1,4 @@
# Project: MKBasic
# Project: VM65
# Makefile created by Dev-C++ 5.11
# and modified for standalone MINGW compiler installation.
@ -7,9 +7,9 @@ SDLBASE = $(SDLDIR)
CPP = g++.exe -D__DEBUG__
CC = gcc.exe -D__DEBUG__
WINDRES = windres.exe
OBJ = main.o VMachine.o MKBasic.o MKCpu.o Memory.o Display.o GraphDisp.o MemMapDev.o MKGenException.o
OBJ = main.o VMachine.o MKCpu.o Memory.o Display.o GraphDisp.o MemMapDev.o MKGenException.o ConsoleIO.o
OBJ2 = bin2hex.o
LINKOBJ = main.o VMachine.o MKBasic.o MKCpu.o Memory.o Display.o GraphDisp.o MemMapDev.o MKGenException.o
LINKOBJ = main.o VMachine.o MKCpu.o Memory.o Display.o GraphDisp.o MemMapDev.o MKGenException.o ConsoleIO.o
LINKOBJ2 = bin2hex.o
LIBS = -L"C:\mingw-w64\x86_64-5.3.0\mingw64\x86_64-w64-mingw32/lib" -L"C:\mingw-w64\x86_64-5.3.0\mingw64\x86_64-w64-mingw32/lib" -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic -lmingw32
SDLLIBS = -L"$(SDLBASE)\x86_64-w64-mingw32/lib" -lSDL2main -lSDL2
@ -40,9 +40,6 @@ main.o: main.cpp
VMachine.o: VMachine.cpp VMachine.h
$(CPP) -c VMachine.cpp -o VMachine.o $(CXXFLAGS) $(SDLINCS)
MKBasic.o: MKBasic.cpp MKBasic.h
$(CPP) -c MKBasic.cpp -o MKBasic.o $(CXXFLAGS) $(SDLINCS)
MKCpu.o: MKCpu.cpp MKCpu.h
$(CPP) -c MKCpu.cpp -o MKCpu.o $(CXXFLAGS) $(SDLINCS)
@ -61,6 +58,9 @@ MemMapDev.o: MemMapDev.cpp MemMapDev.h
MKGenException.o: MKGenException.cpp MKGenException.h
$(CPP) -c MKGenException.cpp -o MKGenException.o $(CXXFLAGS)
ConsoleIO.o: ConsoleIO.cpp ConsoleIO.h
$(CPP) -c ConsoleIO.cpp -o ConsoleIO.o $(CXXFLAGS)
$(BIN2): $(OBJ2)
$(CC) $(LINKOBJ2) -o $(BIN2) $(LIBS)

View File

@ -1,3 +1,34 @@
/*
*--------------------------------------------------------------------
* Project: VM65 - Virtual Machine/CPU emulator programming
* framework.
*
* File: system.h
*
* Purpose: Definitions related to platform portability.
*
* Date: 8/25/2016
*
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
*
* Contact: makarcz@yahoo.com
*
* License Agreement and Warranty:
This software is provided with No Warranty.
I (Marek Karcz) will not be held responsible for any damage to
computer systems, data or user's health resulting from use.
Please proceed responsibly and apply common sense.
This software is provided in hope that it will be useful.
It is 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.
*--------------------------------------------------------------------
*/
#if !defined(LINUX)
#define WINDOWS
#endif