mirror of https://github.com/makarcz/vm6502.git
Performance, bug fixes, debug options, documentation updates.
Performance stats., refactoring, bug fixes, documentation updates, cosmetic changes, debug options.
This commit is contained in:
parent
3cd0bc3f20
commit
9d6706df96
|
@ -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
|
|
@ -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
|
36
Display.cpp
36
Display.cpp
|
@ -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 "Display.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
32
Display.h
32
Display.h
|
@ -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
|
#ifndef DISPLAY_H
|
||||||
#define DISPLAY_H
|
#define DISPLAY_H
|
||||||
|
|
||||||
|
|
|
@ -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 "GraphDisp.h"
|
||||||
#include "MKGenException.h"
|
#include "MKGenException.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
@ -75,6 +111,8 @@ void GraphDisp::Initialize()
|
||||||
{
|
{
|
||||||
int desk_w, desk_h, winbd_top = 5, winbd_right = 5;
|
int desk_w, desk_h, winbd_top = 5, winbd_right = 5;
|
||||||
|
|
||||||
|
mPixelSizeX = GRAPHDISP_MAXW / mWidth;
|
||||||
|
mPixelSizeY = GRAPHDISP_MAXH / mHeight;
|
||||||
GetDesktopResolution(desk_w, desk_h);
|
GetDesktopResolution(desk_w, desk_h);
|
||||||
// Available in version > 2.0.4
|
// Available in version > 2.0.4
|
||||||
//SDL_GetWindowBordersSize(mpWindow, &winbd_top, NULL, NULL, &winbd_right);
|
//SDL_GetWindowBordersSize(mpWindow, &winbd_top, NULL, NULL, &winbd_right);
|
||||||
|
|
40
GraphDisp.h
40
GraphDisp.h
|
@ -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
|
#ifndef GRAPHDISP_H
|
||||||
#define GRAPHDISP_H
|
#define GRAPHDISP_H
|
||||||
|
|
||||||
|
@ -12,8 +44,8 @@ using namespace std;
|
||||||
|
|
||||||
namespace MKBasic {
|
namespace MKBasic {
|
||||||
|
|
||||||
const int GRAPHDISP_MAXW = 640; // "real" display width or maximum virtual width
|
const int GRAPHDISP_MAXW = 960; // "real" display width or maximum virtual width
|
||||||
const int GRAPHDISP_MAXH = 400; // "real" display width or maximum virtual height
|
const int GRAPHDISP_MAXH = 600; // "real" display width or maximum virtual height
|
||||||
|
|
||||||
class GraphDisp {
|
class GraphDisp {
|
||||||
|
|
||||||
|
@ -43,8 +75,8 @@ class GraphDisp {
|
||||||
|
|
||||||
int mWidth = 320; // virtual display width
|
int mWidth = 320; // virtual display width
|
||||||
int mHeight = 200; // virtual display height
|
int mHeight = 200; // virtual display height
|
||||||
int mPixelSizeX = 2; // virtual pixel width
|
int mPixelSizeX = 3; // virtual pixel width
|
||||||
int mPixelSizeY = 2; // virtual pixel height
|
int mPixelSizeY = 3; // virtual pixel height
|
||||||
int mWinPosX = 0; // SDL window position coordinate X
|
int mWinPosX = 0; // SDL window position coordinate X
|
||||||
int mWinPosY = 0; // SDL window position coordinate Y
|
int mWinPosY = 0; // SDL window position coordinate Y
|
||||||
int mBgRgbR = 0; // bg color, RGB red intensity
|
int mBgRgbR = 0; // bg color, RGB red intensity
|
||||||
|
|
59
MKBasic.cpp
59
MKBasic.cpp
|
@ -1,13 +1,46 @@
|
||||||
#include "MKBasic.h"
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
namespace MKBasic {
|
* Project: VM65 - Virtual Machine/CPU emulator programming
|
||||||
|
* framework.
|
||||||
MKBasic::MKBasic()
|
*
|
||||||
{
|
* File:
|
||||||
}
|
*
|
||||||
|
* Purpose: Implementation of MKBasic class.
|
||||||
MKBasic::~MKBasic()
|
* NOTE: This is obsolete concept and will likely be
|
||||||
{
|
* removed from project.
|
||||||
}
|
*
|
||||||
|
* Date: 8/25/2016
|
||||||
} // namespace MKBasic
|
*
|
||||||
|
* 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
|
||||||
|
|
70
MKBasic.h
70
MKBasic.h
|
@ -1,18 +1,52 @@
|
||||||
#ifndef MKBASIC_H
|
/*
|
||||||
#define MKBASIC_H
|
*--------------------------------------------------------------------
|
||||||
|
* Project: VM65 - Virtual Machine/CPU emulator programming
|
||||||
#include "VMachine.h"
|
* framework.
|
||||||
|
*
|
||||||
namespace MKBasic {
|
* File:
|
||||||
|
*
|
||||||
class MKBasic : public VMachine
|
* Purpose: Prototype of MKBasic class and all supporting data
|
||||||
{
|
* structures, enumerations, constants and macros.
|
||||||
public:
|
* NOTE: This is obsolete concept and will likely be
|
||||||
MKBasic();
|
* removed from project.
|
||||||
~MKBasic();
|
*
|
||||||
protected:
|
* Date: 8/25/2016
|
||||||
};
|
*
|
||||||
|
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
|
||||||
} // namespace MKBasic
|
*
|
||||||
|
* Contact: makarcz@yahoo.com
|
||||||
#endif
|
*
|
||||||
|
* 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
|
||||||
|
|
36
MKCpu.cpp
36
MKCpu.cpp
|
@ -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 <string.h>
|
||||||
#include "MKCpu.h"
|
#include "MKCpu.h"
|
||||||
#include "MKGenException.h"
|
#include "MKGenException.h"
|
||||||
|
@ -347,7 +381,7 @@ void MKCpu::InitCpu()
|
||||||
mReg.PtrStack = 0xFF; // top of stack
|
mReg.PtrStack = 0xFF; // top of stack
|
||||||
mReg.SoftIrq = false;
|
mReg.SoftIrq = false;
|
||||||
mReg.IrqPending = false;
|
mReg.IrqPending = false;
|
||||||
mReg.CyclesLeft = 0;
|
mReg.CyclesLeft = 1;
|
||||||
mReg.PageBoundary = false;
|
mReg.PageBoundary = false;
|
||||||
mLocalMem = false;
|
mLocalMem = false;
|
||||||
mExitAtLastRTS = true;
|
mExitAtLastRTS = true;
|
||||||
|
|
34
MKCpu.h
34
MKCpu.h
|
@ -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
|
#ifndef MKCPU_H
|
||||||
#define MKCPU_H
|
#define MKCPU_H
|
||||||
|
|
||||||
|
@ -26,7 +58,7 @@ struct Regs {
|
||||||
bool SoftIrq; // true when interrupted with BRK or trapped opcode
|
bool SoftIrq; // true when interrupted with BRK or trapped opcode
|
||||||
bool LastRTS; // true if RTS encountered and stack empty.
|
bool LastRTS; // true if RTS encountered and stack empty.
|
||||||
unsigned short LastAddr; // PC at the time of previous op-code
|
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
|
int LastOpCode; // op-code of last instruction
|
||||||
unsigned short LastArg; // argument to the last instruction
|
unsigned short LastArg; // argument to the last instruction
|
||||||
int LastAddrMode; // addressing mode of last instruction
|
int LastAddrMode; // addressing mode of last instruction
|
||||||
|
|
160
MemMapDev.cpp
160
MemMapDev.cpp
|
@ -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 "MemMapDev.h"
|
||||||
#include "Memory.h"
|
#include "Memory.h"
|
||||||
#include "MKGenException.h"
|
#include "MKGenException.h"
|
||||||
|
@ -83,6 +126,7 @@ void MemMapDev::Initialize()
|
||||||
mCharIOAddr = CHARIO_ADDR;
|
mCharIOAddr = CHARIO_ADDR;
|
||||||
mGraphDispAddr = GRDISP_ADDR;
|
mGraphDispAddr = GRDISP_ADDR;
|
||||||
mpGraphDisp = NULL;
|
mpGraphDisp = NULL;
|
||||||
|
mpCharIODisp = NULL;
|
||||||
AddrRange addr_range(CHARIO_ADDR, CHARIO_ADDR+1);
|
AddrRange addr_range(CHARIO_ADDR, CHARIO_ADDR+1);
|
||||||
DevPar dev_par("echo", "false");
|
DevPar dev_par("echo", "false");
|
||||||
MemAddrRanges addr_ranges_chario;
|
MemAddrRanges addr_ranges_chario;
|
||||||
|
@ -112,6 +156,7 @@ void MemMapDev::Initialize()
|
||||||
&MemMapDev::GraphDispDevice_Write,
|
&MemMapDev::GraphDispDevice_Write,
|
||||||
dev_params_grdisp);
|
dev_params_grdisp);
|
||||||
mDevices.push_back(dev_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)
|
#if defined(LINUX)
|
||||||
set_conio_terminal_mode();
|
set_conio_terminal_mode();
|
||||||
#endif
|
#endif
|
||||||
static int c = ' ';
|
static int c = ' '; // static, initializes once, remembers prev.
|
||||||
if (mIOEcho && isprint(c)) putchar(c);
|
// value
|
||||||
fflush(stdout);
|
// 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) {
|
if (nonblock) {
|
||||||
// get a keystroke only if character is already in buffer
|
// get a keystroke only if character is already in buffer
|
||||||
if (kbhit()) c = getch();
|
if (kbhit()) c = getch();
|
||||||
|
@ -375,10 +421,31 @@ char MemMapDev::GetCharOut()
|
||||||
return ret;
|
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()
|
* Method: PutCharIO()
|
||||||
* Purpose: Put character in the output char I/O FIFO buffer.
|
* 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
|
* Arguments: c - character
|
||||||
* Returns: n/a
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
|
@ -388,6 +455,10 @@ void MemMapDev::PutCharIO(char c)
|
||||||
mCharIOBufOut[mOutBufDataEnd] = c;
|
mCharIOBufOut[mOutBufDataEnd] = c;
|
||||||
mOutBufDataEnd++;
|
mOutBufDataEnd++;
|
||||||
if (mOutBufDataEnd >= CHARIO_BUF_SIZE) mOutBufDataEnd = 0;
|
if (mOutBufDataEnd >= CHARIO_BUF_SIZE) mOutBufDataEnd = 0;
|
||||||
|
if (mCharIOActive) {
|
||||||
|
putchar((int)c);
|
||||||
|
CharIOFlush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -451,6 +522,69 @@ bool MemMapDev::GetCharIOEchoOn()
|
||||||
return mIOEcho;
|
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()
|
* Method: GetGraphDispAddrBase()
|
||||||
|
@ -668,4 +802,20 @@ void MemMapDev::GraphDisp_Update()
|
||||||
if (NULL != mpGraphDisp) mpGraphDisp->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
|
} // namespace MKBasic
|
||||||
|
|
45
MemMapDev.h
45
MemMapDev.h
|
@ -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
|
#ifndef MEMMAPDEV_H
|
||||||
#define MEMMAPDEV_H
|
#define MEMMAPDEV_H
|
||||||
|
|
||||||
|
@ -6,6 +38,7 @@
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
//#include "Memory.h"
|
//#include "Memory.h"
|
||||||
#include "GraphDisp.h"
|
#include "GraphDisp.h"
|
||||||
|
#include "Display.h"
|
||||||
|
|
||||||
#if defined(LINUX)
|
#if defined(LINUX)
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -102,6 +135,7 @@ struct Device {
|
||||||
|
|
||||||
typedef vector<Device> MemMappedDevices;
|
typedef vector<Device> MemMappedDevices;
|
||||||
|
|
||||||
|
// currently supported devices
|
||||||
enum DevNums {
|
enum DevNums {
|
||||||
DEVNUM_CHARIO = 0, // character I/O device
|
DEVNUM_CHARIO = 0, // character I/O device
|
||||||
DEVNUM_GRDISP = 1, // raster graphics display 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
|
* The emulated device is a model of a electronic device that is connected
|
||||||
* to the CPU-s bus (address, memory, control signals).
|
* 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
|
* a single memory address or it may contain multiple memory addresses
|
||||||
* or ranges of addresses positioned at various non-contiguous places.
|
* or ranges of addresses positioned at various non-contiguous places.
|
||||||
* The functions of these addresses are handled internally by the MemMapDev
|
* The functions of these addresses are handled internally by the MemMapDev
|
||||||
|
@ -184,8 +218,13 @@ class MemMapDev {
|
||||||
|
|
||||||
char GetCharIn();
|
char GetCharIn();
|
||||||
char GetCharOut();
|
char GetCharOut();
|
||||||
|
void CharIOFlush();
|
||||||
unsigned short GetCharIOAddr();
|
unsigned short GetCharIOAddr();
|
||||||
bool GetCharIOEchoOn();
|
bool GetCharIOEchoOn();
|
||||||
|
bool IsCharIOActive();
|
||||||
|
Display* ActivateCharIO();
|
||||||
|
Display* GetDispPtr();
|
||||||
|
void DeactivateCharIO();
|
||||||
|
|
||||||
int CharIODevice_Read(int addr);
|
int CharIODevice_Read(int addr);
|
||||||
void CharIODevice_Write(int addr, int val);
|
void CharIODevice_Write(int addr, int val);
|
||||||
|
@ -200,6 +239,8 @@ class MemMapDev {
|
||||||
void GraphDisp_ReadEvents();
|
void GraphDisp_ReadEvents();
|
||||||
void GraphDisp_Update();
|
void GraphDisp_Update();
|
||||||
|
|
||||||
|
//void SetCharIODispPtr(Display *p, bool active);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Memory *mpMem; // pointer to memory object
|
Memory *mpMem; // pointer to memory object
|
||||||
|
@ -214,6 +255,8 @@ class MemMapDev {
|
||||||
bool mIOEcho;
|
bool mIOEcho;
|
||||||
unsigned int mGraphDispAddr;
|
unsigned int mGraphDispAddr;
|
||||||
GraphDisp *mpGraphDisp; // pointer to Graphics Device object
|
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
|
GraphDeviceRegs mGrDevRegs; // graphics display device registers
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
|
|
97
Memory.cpp
97
Memory.cpp
|
@ -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 "Memory.h"
|
||||||
#include "MKGenException.h"
|
#include "MKGenException.h"
|
||||||
|
|
||||||
|
@ -322,6 +360,7 @@ void Memory::SetCharIO(unsigned short addr, bool echo)
|
||||||
SetupDevice(DEVNUM_CHARIO, memaddr_ranges, dev_params);
|
SetupDevice(DEVNUM_CHARIO, memaddr_ranges, dev_params);
|
||||||
if (false == mCharIOActive) AddDevice(DEVNUM_CHARIO);
|
if (false == mCharIOActive) AddDevice(DEVNUM_CHARIO);
|
||||||
mCharIOActive = true;
|
mCharIOActive = true;
|
||||||
|
mpMemMapDev->ActivateCharIO();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -336,6 +375,7 @@ void Memory::DisableCharIO()
|
||||||
{
|
{
|
||||||
mCharIOActive = false;
|
mCharIOActive = false;
|
||||||
DeleteDevice(DEVNUM_CHARIO);
|
DeleteDevice(DEVNUM_CHARIO);
|
||||||
|
mpMemMapDev->DeactivateCharIO();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -405,10 +445,10 @@ unsigned short Memory::GetGraphDispAddr()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: GetROMBegin()
|
||||||
* Purpose:
|
* Purpose: Get starting address of read-only memory.
|
||||||
* Arguments:
|
* Arguments:
|
||||||
* Returns:
|
* Returns: unsigned short - address ($0000-$FFFF)
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
unsigned short Memory::GetROMBegin()
|
unsigned short Memory::GetROMBegin()
|
||||||
|
@ -418,10 +458,10 @@ unsigned short Memory::GetROMBegin()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: GetROMEnd()
|
||||||
* Purpose:
|
* Purpose: Get end address of read-only memory.
|
||||||
* Arguments:
|
* Arguments:
|
||||||
* Returns:
|
* Returns: unsigned short - address ($0000-$FFFF)
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
unsigned short Memory::GetROMEnd()
|
unsigned short Memory::GetROMEnd()
|
||||||
|
@ -431,10 +471,10 @@ unsigned short Memory::GetROMEnd()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: IsROMEnabled()
|
||||||
* Purpose:
|
* Purpose: Get status of ROM.
|
||||||
* Arguments:
|
* Arguments:
|
||||||
* Returns:
|
* Returns: bool - true if enabled.
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
bool Memory::IsROMEnabled()
|
bool Memory::IsROMEnabled()
|
||||||
|
@ -445,7 +485,7 @@ bool Memory::IsROMEnabled()
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method: AddDevice()
|
* Method: AddDevice()
|
||||||
* Purpose: Add device number to active devices list.
|
* Purpose: Add device to active devices cache.
|
||||||
* Arguments: devnum - device number
|
* Arguments: devnum - device number
|
||||||
* Returns: -1 if device is not supported OR already cached
|
* Returns: -1 if device is not supported OR already cached
|
||||||
* devnum - device number if it was found
|
* devnum - device number if it was found
|
||||||
|
@ -487,7 +527,7 @@ int Memory::AddDevice(int devnum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // END if (dev.num >= 0)
|
} // END if (dev.num >= 0)
|
||||||
// else device with wuch number is not supported
|
// else device with such number is not supported
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -495,7 +535,7 @@ int Memory::AddDevice(int devnum)
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method: DeleteDevice()
|
* Method: DeleteDevice()
|
||||||
* Purpose: Delete device number from active devices list.
|
* Purpose: Delete device from active devices cache.
|
||||||
* Arguments: devnum - device number
|
* Arguments: devnum - device number
|
||||||
* Returns: >=0 if device was found in local cache and deleted
|
* Returns: >=0 if device was found in local cache and deleted
|
||||||
* -1 if device was not found
|
* -1 if device was not found
|
||||||
|
@ -607,10 +647,10 @@ char Memory::GetCharOut()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: GraphDisp_ReadEvents()
|
||||||
* Purpose:
|
* Purpose: Read events from the graphics display window.
|
||||||
* Arguments:
|
* Arguments: n/a
|
||||||
* Returns:
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void Memory::GraphDisp_ReadEvents()
|
void Memory::GraphDisp_ReadEvents()
|
||||||
|
@ -620,10 +660,10 @@ void Memory::GraphDisp_ReadEvents()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: GraphDisp_Update()
|
||||||
* Purpose:
|
* Purpose: Trigger update handler of the graphics display window.
|
||||||
* Arguments:
|
* Arguments: n/a
|
||||||
* Returns:
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void Memory::GraphDisp_Update()
|
void Memory::GraphDisp_Update()
|
||||||
|
@ -646,4 +686,17 @@ bool Memory::GraphDispOp()
|
||||||
return mDispOp;
|
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
|
} // namespace MKBasic
|
||||||
|
|
33
Memory.h
33
Memory.h
|
@ -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
|
#ifndef MEMORY_H
|
||||||
#define MEMORY_H
|
#define MEMORY_H
|
||||||
|
|
||||||
|
@ -49,6 +81,7 @@ class Memory
|
||||||
void GraphDisp_ReadEvents();
|
void GraphDisp_ReadEvents();
|
||||||
void GraphDisp_Update();
|
void GraphDisp_Update();
|
||||||
bool GraphDispOp();
|
bool GraphDispOp();
|
||||||
|
MemMapDev *GetMemMapDevPtr();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
|
@ -82,11 +82,14 @@ provided image file and enter the debug console menu.
|
||||||
which will be referred to as Debug Console:
|
which will be referred to as Debug Console:
|
||||||
|
|
||||||
STOPPED at 0
|
STOPPED at 0
|
||||||
|
|
||||||
|
Emulation performance stats is OFF.
|
||||||
|
|
||||||
*-------------*-----------------------*----------*----------*
|
*-------------*-----------------------*----------*----------*
|
||||||
| PC: $0000 | Acc: $00 (00000000) | X: $00 | Y: $00 |
|
| PC: $0000 | Acc: $00 (00000000) | X: $00 | Y: $00 |
|
||||||
*-------------*-----------------------*----------*----------*
|
*-------------*-----------------------*----------*----------*
|
||||||
| NV-BDIZC |
|
| NV-BDIZC | :
|
||||||
| 00100000 | Last instr.:
|
| 00100000 | :
|
||||||
*-------------*
|
*-------------*
|
||||||
|
|
||||||
Stack: $ff
|
Stack: $ff
|
||||||
|
@ -95,6 +98,7 @@ Stack: $ff
|
||||||
I/O status: disabled, at: $e000, local echo: OFF.
|
I/O status: disabled, at: $e000, local echo: OFF.
|
||||||
Graphics status: disabled, at: $e002
|
Graphics status: disabled, at: $e002
|
||||||
ROM: disabled. Range: $d000 - $dfff.
|
ROM: disabled. Range: $d000 - $dfff.
|
||||||
|
Op-code execute history: disabled.
|
||||||
------------------------------------+----------------------------------------
|
------------------------------------+----------------------------------------
|
||||||
C - continue, S - step | A - set address for next step
|
C - continue, S - step | A - set address for next step
|
||||||
G - go/cont. from new address | N - go number of steps, P - IRQ
|
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
|
E - toggle I/O local echo | F - toggle registers animation
|
||||||
J - set animation delay | M - dump memory, W - write memory
|
J - set animation delay | M - dump memory, W - write memory
|
||||||
K - toggle ROM emulation | R - show registers, Y - snapshot
|
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
|
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
|
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
|
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
|
To see full help for above commands, issue command H in the prompt and press
|
||||||
ENTER.
|
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
|
C - Continue
|
||||||
|
|
||||||
|
@ -126,11 +157,12 @@ ROM: disabled. Range: $d000 - $dfff.
|
||||||
|
|
||||||
S - Step
|
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
|
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.
|
G - Execute (continue) from address.
|
||||||
|
|
||||||
|
@ -138,6 +170,10 @@ ROM: disabled. Range: $d000 - $dfff.
|
||||||
|
|
||||||
N - Execute # of steps.
|
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.
|
X - Execute from address.
|
||||||
|
|
||||||
F - Toggle registers animation.
|
F - Toggle registers animation.
|
||||||
|
@ -159,7 +195,7 @@ ROM: disabled. Range: $d000 - $dfff.
|
||||||
|
|
||||||
Displays current status of CPU registers, flags and stack.
|
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
|
Show the history of last executed op-codes/instructions, full with
|
||||||
disassembled mnemonic and argument.
|
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
|
opcode if stack is empty, so the VM will never return from opcodes
|
||||||
execution loop unless user interrupts with CTRL-C or CTRL-Break.
|
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.
|
M - Dump memory.
|
||||||
|
|
||||||
|
@ -191,7 +267,7 @@ ROM: disabled. Range: $d000 - $dfff.
|
||||||
|
|
||||||
Writes provided values to memory starting at specified address.
|
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.
|
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
|
NOTE: All addresses and memory values are to be entered in hexadecimal
|
||||||
format.
|
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.
|
3. Implementing the Virtual Machine.
|
||||||
|
|
||||||
The Virtual Machine (or Emulator) is implemented by the means of multiple
|
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
|
See Memory::SetGraphDisp() and VMachine::SetGraphDisp() methods for
|
||||||
an example. The SetGraphDisp() method in Memory class defines the memory
|
an example. The SetGraphDisp() method in Memory class defines the memory
|
||||||
mapped device on a lower level and calls SetupDevice().
|
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.
|
||||||
|
|
||||||
|
|
14
ReadMe.txt
14
ReadMe.txt
|
@ -21,13 +21,15 @@ Graphics display emulation requires SDL2.
|
||||||
Makefile are included to build under Windows 32/64 (mingw compiler required)
|
Makefile are included to build under Windows 32/64 (mingw compiler required)
|
||||||
and under Linux Ubuntu or Ubuntu based distro.
|
and under Linux Ubuntu or Ubuntu based distro.
|
||||||
SDL2 library must be on your execution path in order to run program.
|
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:
|
To build under Windows 32/64:
|
||||||
|
|
||||||
* Install MINGW64 under C:\mingw-w64\x86_64-5.3.0 folder.
|
* Install MINGW64 under C:\mingw-w64\x86_64-5.3.0 folder.
|
||||||
* Run mingw terminal.
|
* Run mingw terminal.
|
||||||
* Change current directory to that of this project.
|
* Change current directory to that of this project.
|
||||||
* Set environment variable SDLDIR.
|
* Set environment variable SDLDIR. (E.g.: set SDLDIR=C:\src\SDL)
|
||||||
* Run: makeming.bat
|
* Run: makeming.bat
|
||||||
|
|
||||||
To build under Linux:
|
To build under Linux:
|
||||||
|
@ -595,6 +597,12 @@ D - diassemble code in memory
|
||||||
U - enable/disable exec. history
|
U - enable/disable exec. history
|
||||||
Toggle enable/disable of op-codes execute history.
|
Toggle enable/disable of op-codes execute history.
|
||||||
Disabling this feature improves performance.
|
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:
|
NOTE:
|
||||||
1. If no arguments provided, each command will prompt user to enter
|
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
|
by pressing CTRL-C or CTRL-Pause/Break, which will generate
|
||||||
a "Operator Interrupt". However in the character input mode
|
a "Operator Interrupt". However in the character input mode
|
||||||
use CTRL-Y combination or CTRL-Break (DOS), CTRL-C (Linux).
|
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.
|
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.
|
411 % on PC1 and 312 % on PC2.
|
||||||
|
|
||||||
Enabling and adding the emulated memory mapped devices to the pool may cause
|
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
|
peripherals (char I/O, graphics raster device) enabled and actively used and
|
||||||
op-codes execute history enabled the performance is still well above 300 %
|
op-codes execute history enabled the performance is still well above 300 %
|
||||||
on both PC1 and on PC2 (* see annotations for PC configurations/specs).
|
on both PC1 and on PC2 (* see annotations for PC configurations/specs).
|
||||||
|
|
518
VMachine.cpp
518
VMachine.cpp
|
@ -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 <stdio.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "VMachine.h"
|
#include "VMachine.h"
|
||||||
#include "MKGenException.h"
|
#include "MKGenException.h"
|
||||||
|
|
||||||
|
/*
|
||||||
#if defined(WINDOWS)
|
#if defined(WINDOWS)
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#endif
|
#endif
|
||||||
|
*/
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -62,10 +100,11 @@ VMachine::VMachine(string romfname, string ramfname)
|
||||||
*/
|
*/
|
||||||
VMachine::~VMachine()
|
VMachine::~VMachine()
|
||||||
{
|
{
|
||||||
delete mpDisp;
|
//delete mpDisp;
|
||||||
delete mpCPU;
|
delete mpCPU;
|
||||||
delete mpROM;
|
delete mpROM;
|
||||||
delete mpRAM;
|
delete mpRAM;
|
||||||
|
delete mpConIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -82,7 +121,6 @@ void VMachine::InitVM()
|
||||||
mpRAM = new Memory();
|
mpRAM = new Memory();
|
||||||
|
|
||||||
mPerfStats.cycles = 0;
|
mPerfStats.cycles = 0;
|
||||||
mPerfStats.micro_secs = 0;
|
|
||||||
mPerfStats.perf_onemhz = 0;
|
mPerfStats.perf_onemhz = 0;
|
||||||
mPerfStats.prev_cycles = 0;
|
mPerfStats.prev_cycles = 0;
|
||||||
mPerfStats.prev_usec = 0;
|
mPerfStats.prev_usec = 0;
|
||||||
|
@ -93,6 +131,8 @@ void VMachine::InitVM()
|
||||||
mCharIOAddr = CHARIO_ADDR;
|
mCharIOAddr = CHARIO_ADDR;
|
||||||
mCharIOActive = mCharIO = false;
|
mCharIOActive = mCharIO = false;
|
||||||
mGraphDispActive = false;
|
mGraphDispActive = false;
|
||||||
|
mPerfStatsActive = false;
|
||||||
|
mDebugTraceActive = false;
|
||||||
if (NULL == mpRAM) {
|
if (NULL == mpRAM) {
|
||||||
throw MKGenException("Unable to initialize VM (RAM).");
|
throw MKGenException("Unable to initialize VM (RAM).");
|
||||||
}
|
}
|
||||||
|
@ -105,14 +145,18 @@ void VMachine::InitVM()
|
||||||
if (NULL == mpCPU) {
|
if (NULL == mpCPU) {
|
||||||
throw MKGenException("Unable to initialize VM (CPU).");
|
throw MKGenException("Unable to initialize VM (CPU).");
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
mpDisp = new Display();
|
mpDisp = new Display();
|
||||||
if (NULL == mpDisp) {
|
if (NULL == mpDisp) {
|
||||||
throw MKGenException("Unable to initialize VM (Display).");
|
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()
|
* Method: ClearScreen()
|
||||||
|
@ -124,39 +168,7 @@ void VMachine::InitVM()
|
||||||
*/
|
*/
|
||||||
void VMachine::ClearScreen()
|
void VMachine::ClearScreen()
|
||||||
{
|
{
|
||||||
HANDLE hStdOut;
|
mpConIO->ClearScreen();
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -170,50 +182,9 @@ void VMachine::ClearScreen()
|
||||||
*/
|
*/
|
||||||
void VMachine::ScrHome()
|
void VMachine::ScrHome()
|
||||||
{
|
{
|
||||||
HANDLE hStdOut;
|
mpConIO->ScrHome();
|
||||||
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 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()
|
* Method: ShowDisp()
|
||||||
|
@ -224,7 +195,7 @@ void VMachine::ScrHome()
|
||||||
*/
|
*/
|
||||||
void VMachine::ShowDisp()
|
void VMachine::ShowDisp()
|
||||||
{
|
{
|
||||||
if (mCharIOActive) {
|
if (mCharIOActive && NULL != mpDisp) {
|
||||||
ScrHome();
|
ScrHome();
|
||||||
mpDisp->ShowScr();
|
mpDisp->ShowScr();
|
||||||
}
|
}
|
||||||
|
@ -233,36 +204,67 @@ void VMachine::ShowDisp()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: CalcCurrPerf()
|
||||||
* Purpose:
|
* Purpose: Calculate CPU emulation performance based on 1 MHz model
|
||||||
* Arguments:
|
* CPU.
|
||||||
* Returns:
|
* Arguments: n/a
|
||||||
|
* Returns: Integer, the % of speed as compared to 1 MHz CPU.
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
int VMachine::CalcCurrPerf()
|
int VMachine::CalcCurrPerf()
|
||||||
{
|
{
|
||||||
auto lap = high_resolution_clock::now();
|
if (!mPerfStatsActive) return 0;
|
||||||
auto beg = mPerfStats.begin_time;
|
|
||||||
mPerfStats.micro_secs = duration_cast<microseconds>(lap-beg).count();
|
|
||||||
|
|
||||||
if (mPerfStats.micro_secs > 0) {
|
auto lap = high_resolution_clock::now();
|
||||||
int currperf = (int)
|
long usec = duration_cast<microseconds>(lap-mPerfStats.begin_time).count();
|
||||||
(((double)mPerfStats.cycles / (double)mPerfStats.micro_secs) * 100.0);
|
|
||||||
|
if (usec > 0) {
|
||||||
|
int currperf = (int)(((double)mPerfStats.cycles / (double)usec) * 100.0);
|
||||||
if (mPerfStats.perf_onemhz == 0)
|
if (mPerfStats.perf_onemhz == 0)
|
||||||
mPerfStats.perf_onemhz = currperf;
|
mPerfStats.perf_onemhz = currperf;
|
||||||
else
|
else
|
||||||
mPerfStats.perf_onemhz = (mPerfStats.perf_onemhz + currperf) / 2;
|
mPerfStats.perf_onemhz = (mPerfStats.perf_onemhz + currperf) / 2;
|
||||||
|
|
||||||
mPerfStats.prev_cycles = mPerfStats.cycles;
|
mPerfStats.prev_cycles = mPerfStats.cycles;
|
||||||
mPerfStats.prev_usec = mPerfStats.micro_secs;
|
mPerfStats.prev_usec = usec;
|
||||||
mPerfStats.cycles = 0;
|
mPerfStats.cycles = 0;
|
||||||
mPerfStats.micro_secs = 0;
|
mPerfStats.begin_time = lap;
|
||||||
mPerfStats.begin_time = high_resolution_clock::now();
|
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;
|
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()
|
* Method: Run()
|
||||||
|
@ -275,18 +277,18 @@ Regs *VMachine::Run()
|
||||||
{
|
{
|
||||||
Regs *cpureg = NULL;
|
Regs *cpureg = NULL;
|
||||||
|
|
||||||
|
AddDebugTrace("Running code at: $" + Addr2HexStr(mRunAddr));
|
||||||
mOpInterrupt = false;
|
mOpInterrupt = false;
|
||||||
ClearScreen();
|
ClearScreen();
|
||||||
ShowDisp();
|
ShowDisp();
|
||||||
mPerfStats.cycles = 0;
|
mPerfStats.cycles = 0;
|
||||||
mPerfStats.micro_secs = 0;
|
|
||||||
mPerfStats.begin_time = high_resolution_clock::now();
|
mPerfStats.begin_time = high_resolution_clock::now();
|
||||||
while (true) {
|
while (true) {
|
||||||
mPerfStats.cycles++;
|
mPerfStats.cycles++;
|
||||||
cpureg = Step();
|
cpureg = Step();
|
||||||
if (cpureg->CyclesLeft == 0 && mCharIO) ShowDisp();
|
if (cpureg->CyclesLeft == 0 && mCharIO) ShowDisp();
|
||||||
if (cpureg->SoftIrq || mOpInterrupt) break;
|
if (cpureg->SoftIrq || mOpInterrupt) break;
|
||||||
//if (mPerfStats.cycles == PERFSTAT_INTERVAL) CalcCurrPerf();
|
PERFSTAT_LAP(mPerfStats.cycles,mPerfStats.begin_time);
|
||||||
}
|
}
|
||||||
CalcCurrPerf();
|
CalcCurrPerf();
|
||||||
|
|
||||||
|
@ -323,21 +325,17 @@ Regs *VMachine::Exec()
|
||||||
{
|
{
|
||||||
Regs *cpureg = NULL;
|
Regs *cpureg = NULL;
|
||||||
|
|
||||||
|
AddDebugTrace("Executing code at: $" + Addr2HexStr(mRunAddr));
|
||||||
mOpInterrupt = false;
|
mOpInterrupt = false;
|
||||||
ClearScreen();
|
ClearScreen();
|
||||||
ShowDisp();
|
ShowDisp();
|
||||||
mPerfStats.cycles = 0;
|
mPerfStats.cycles = 0;
|
||||||
mPerfStats.micro_secs = 0;
|
|
||||||
mPerfStats.begin_time = high_resolution_clock::now();
|
mPerfStats.begin_time = high_resolution_clock::now();
|
||||||
while (true) {
|
while (true) {
|
||||||
mPerfStats.cycles++;
|
mPerfStats.cycles++;
|
||||||
cpureg = Step();
|
cpureg = Step();
|
||||||
if (cpureg->CyclesLeft == 0 && mCharIO) {
|
|
||||||
cout << mpDisp->GetLastChar();
|
|
||||||
cout << flush;
|
|
||||||
}
|
|
||||||
if (cpureg->LastRTS || mOpInterrupt) break;
|
if (cpureg->LastRTS || mOpInterrupt) break;
|
||||||
//if (mPerfStats.cycles == PERFSTAT_INTERVAL) CalcCurrPerf();
|
PERFSTAT_LAP(mPerfStats.cycles,mPerfStats.begin_time);
|
||||||
}
|
}
|
||||||
CalcCurrPerf();
|
CalcCurrPerf();
|
||||||
|
|
||||||
|
@ -383,28 +381,13 @@ Regs *VMachine::Exec(unsigned short addr)
|
||||||
*/
|
*/
|
||||||
Regs *VMachine::Step()
|
Regs *VMachine::Step()
|
||||||
{
|
{
|
||||||
unsigned short addr = mRunAddr;
|
|
||||||
Regs *cpureg = NULL;
|
Regs *cpureg = NULL;
|
||||||
|
|
||||||
cpureg = mpCPU->ExecOpcode(addr);
|
cpureg = mpCPU->ExecOpcode(mRunAddr);
|
||||||
if (mGraphDispActive && cpureg->CyclesLeft == 0) {
|
if (mGraphDispActive && cpureg->CyclesLeft == 0) {
|
||||||
mpRAM->GraphDisp_ReadEvents();
|
mpRAM->GraphDisp_ReadEvents();
|
||||||
}
|
}
|
||||||
addr = cpureg->PtrAddr;
|
mRunAddr = 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cpureg;
|
return cpureg;
|
||||||
}
|
}
|
||||||
|
@ -458,6 +441,15 @@ int VMachine::LoadRAM(string ramfname)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mError = err;
|
mError = err;
|
||||||
|
if (mDebugTraceActive && err) {
|
||||||
|
stringstream sserr;
|
||||||
|
string msg, strerr;
|
||||||
|
sserr << err;
|
||||||
|
sserr >> strerr;
|
||||||
|
msg = "ERROR: LoadRAM, error code: " + strerr;
|
||||||
|
AddDebugTrace(msg);
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,6 +552,8 @@ bool VMachine::HasHdrData(FILE *fp)
|
||||||
}
|
}
|
||||||
ret = (0 == strncmp(buf, HDRMAGICKEY, l));
|
ret = (0 == strncmp(buf, HDRMAGICKEY, l));
|
||||||
|
|
||||||
|
AddDebugTrace(((ret) ? "HasHdrData: YES" : "HasHdrData: NO"));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -590,6 +584,8 @@ bool VMachine::HasOldHdrData(FILE *fp)
|
||||||
}
|
}
|
||||||
ret = (0 == strncmp(buf, HDRMAGICKEY_OLD, l));
|
ret = (0 == strncmp(buf, HDRMAGICKEY_OLD, l));
|
||||||
|
|
||||||
|
AddDebugTrace(((ret) ? "HasOldHdrData: YES" : "HasOldHdrData: NO"));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -655,20 +651,26 @@ bool VMachine::LoadHdrData(FILE *fp)
|
||||||
switch (n)
|
switch (n)
|
||||||
{
|
{
|
||||||
case 1: mRunAddr = l + 256 * val;
|
case 1: mRunAddr = l + 256 * val;
|
||||||
|
ADD_DBG_LDMEMPARHEX("LoadHdrData : mRunAddr",mRunAddr);
|
||||||
break;
|
break;
|
||||||
case 3: mCharIOAddr = l + 256 * val;
|
case 3: mCharIOAddr = l + 256 * val;
|
||||||
|
ADD_DBG_LDMEMPARHEX("LoadHdrData : mCharIOAddr",mCharIOAddr);
|
||||||
break;
|
break;
|
||||||
case 5: rb = l + 256 * val;
|
case 5: rb = l + 256 * val;
|
||||||
break;
|
break;
|
||||||
case 7: re = l + 256 * val;
|
case 7: re = l + 256 * val;
|
||||||
break;
|
break;
|
||||||
case 8: mCharIOActive = (val != 0);
|
case 8: mCharIOActive = (val != 0);
|
||||||
|
ADD_DBG_LDMEMPARVAL("LoadHdrData : mCharIOActive",mCharIOActive);
|
||||||
break;
|
break;
|
||||||
case 9: if (val != 0) {
|
case 9: if (val != 0) {
|
||||||
mpRAM->EnableROM(rb, re);
|
mpRAM->EnableROM(rb, re);
|
||||||
} else {
|
} else {
|
||||||
mpRAM->SetROM(rb, re);
|
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;
|
break;
|
||||||
case 10: r.Acc = val;
|
case 10: r.Acc = val;
|
||||||
break;
|
break;
|
||||||
|
@ -682,9 +684,11 @@ bool VMachine::LoadHdrData(FILE *fp)
|
||||||
ret = true;
|
ret = true;
|
||||||
break;
|
break;
|
||||||
case 15: mGraphDispActive = (val != 0);
|
case 15: mGraphDispActive = (val != 0);
|
||||||
|
ADD_DBG_LDMEMPARVAL("LoadHdrData : mGraphDispActive",mGraphDispActive);
|
||||||
break;
|
break;
|
||||||
case 17: if (mGraphDispActive) SetGraphDisp(l + 256 * val);
|
case 17: if (mGraphDispActive) SetGraphDisp(l + 256 * val);
|
||||||
else DisableGraphDisp();
|
else DisableGraphDisp();
|
||||||
|
ADD_DBG_LDMEMPARHEX("LoadHdrData : Graph. Disp. addr",(l + 256 * val));
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
@ -699,9 +703,6 @@ bool VMachine::LoadHdrData(FILE *fp)
|
||||||
return ret;
|
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()
|
* Method: SaveHdrData()
|
||||||
|
@ -788,6 +789,14 @@ int VMachine::SaveSnapshot(string fname)
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
if (0 != ret) mError = VMERR_SAVE_SNAPSHOT;
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -829,6 +838,7 @@ int VMachine::LoadRAMBin(string ramfname)
|
||||||
Memory *pm = mpRAM;
|
Memory *pm = mpRAM;
|
||||||
int ret = MEMIMGERR_RAMBIN_OPEN;
|
int ret = MEMIMGERR_RAMBIN_OPEN;
|
||||||
|
|
||||||
|
AddDebugTrace("LoadRAMBin : " + ramfname);
|
||||||
mOldStyleHeader = false;
|
mOldStyleHeader = false;
|
||||||
if ((fp = fopen(ramfname.c_str(), "rb")) != NULL) {
|
if ((fp = fopen(ramfname.c_str(), "rb")) != NULL) {
|
||||||
if (HasHdrData(fp) || (mOldStyleHeader = HasOldHdrData(fp))) {
|
if (HasHdrData(fp) || (mOldStyleHeader = HasOldHdrData(fp))) {
|
||||||
|
@ -1074,7 +1084,11 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||||
bool romendset = false, engraph = false, graphset = false;
|
bool romendset = false, engraph = false, graphset = false;
|
||||||
Memory *pm = pmem;
|
Memory *pm = pmem;
|
||||||
int err = MEMIMGERR_OK;
|
int err = MEMIMGERR_OK;
|
||||||
|
|
||||||
|
if (mDebugTraceActive) {
|
||||||
|
string msg = "LoadMEM: " + memfname;
|
||||||
|
AddDebugTrace(msg);
|
||||||
|
}
|
||||||
if ((fp = fopen(memfname.c_str(), "r")) != NULL) {
|
if ((fp = fopen(memfname.c_str(), "r")) != NULL) {
|
||||||
DisableROM();
|
DisableROM();
|
||||||
DisableCharIO();
|
DisableCharIO();
|
||||||
|
@ -1100,8 +1114,10 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||||
} else {
|
} else {
|
||||||
err = MEMIMGERR_VM65_IGNPROCWRN;
|
err = MEMIMGERR_VM65_IGNPROCWRN;
|
||||||
errc++;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
// change address counter
|
// change address counter
|
||||||
|
@ -1115,6 +1131,7 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||||
} else {
|
} else {
|
||||||
addr = (unsigned short) atoi(line);
|
addr = (unsigned short) atoi(line);
|
||||||
}
|
}
|
||||||
|
ADD_DBG_LDMEMPARHEX("ORG",addr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// define I/O emulation address (once)
|
// define I/O emulation address (once)
|
||||||
|
@ -1133,8 +1150,10 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||||
} else {
|
} else {
|
||||||
err = MEMIMGERR_VM65_IGNPROCWRN;
|
err = MEMIMGERR_VM65_IGNPROCWRN;
|
||||||
errc++;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
// define generic graphics display device base address (once)
|
// define generic graphics display device base address (once)
|
||||||
|
@ -1153,23 +1172,28 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||||
} else {
|
} else {
|
||||||
err = MEMIMGERR_VM65_IGNPROCWRN;
|
err = MEMIMGERR_VM65_IGNPROCWRN;
|
||||||
errc++;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
// enable character I/O emulation
|
// enable character I/O emulation
|
||||||
if (0 == strncmp(line, "ENIO", 4)) {
|
if (0 == strncmp(line, "ENIO", 4)) {
|
||||||
enio = true;
|
enio = true;
|
||||||
|
ADD_DBG_LDMEMPARVAL("ENIO",enio);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// enable generic graphics display emulation
|
// enable generic graphics display emulation
|
||||||
if (0 == strncmp(line, "ENGRAPH", 7)) {
|
if (0 == strncmp(line, "ENGRAPH", 7)) {
|
||||||
engraph = true;
|
engraph = true;
|
||||||
|
ADD_DBG_LDMEMPARVAL("ENIO",engraph);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// enable ROM emulation
|
// enable ROM emulation
|
||||||
if (0 == strncmp(line, "ENROM", 5)) {
|
if (0 == strncmp(line, "ENROM", 5)) {
|
||||||
enrom = true;
|
enrom = true;
|
||||||
|
ADD_DBG_LDMEMPARVAL("ENROM",enrom);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// auto execute from address
|
// auto execute from address
|
||||||
|
@ -1189,13 +1213,16 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||||
} else {
|
} else {
|
||||||
err = MEMIMGERR_VM65_IGNPROCWRN;
|
err = MEMIMGERR_VM65_IGNPROCWRN;
|
||||||
errc++;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
// auto reset
|
// auto reset
|
||||||
if (0 == strncmp(line, "RESET", 5)) {
|
if (0 == strncmp(line, "RESET", 5)) {
|
||||||
mAutoReset = true;
|
mAutoReset = true;
|
||||||
|
ADD_DBG_LDMEMPARVAL("RESET",mAutoReset);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// define ROM begin address
|
// define ROM begin address
|
||||||
|
@ -1214,8 +1241,10 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||||
} else {
|
} else {
|
||||||
err = MEMIMGERR_VM65_IGNPROCWRN;
|
err = MEMIMGERR_VM65_IGNPROCWRN;
|
||||||
errc++;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
// define ROM end address
|
// define ROM end address
|
||||||
|
@ -1234,8 +1263,10 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||||
} else {
|
} else {
|
||||||
err = MEMIMGERR_VM65_IGNPROCWRN;
|
err = MEMIMGERR_VM65_IGNPROCWRN;
|
||||||
errc++;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
if (';' == *line) continue; // skip comment lines
|
if (';' == *line) continue; // skip comment lines
|
||||||
|
@ -1269,7 +1300,8 @@ int VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
err = MEMIMGERR_VM65_OPEN;
|
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++;
|
errc++;
|
||||||
}
|
}
|
||||||
if (errc) {
|
if (errc) {
|
||||||
|
@ -1341,7 +1373,13 @@ void VMachine::SetCharIO(unsigned short addr, bool echo)
|
||||||
mCharIOAddr = addr;
|
mCharIOAddr = addr;
|
||||||
mCharIOActive = true;
|
mCharIOActive = true;
|
||||||
mpRAM->SetCharIO(addr, echo);
|
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;
|
mCharIOActive = false;
|
||||||
mpRAM->DisableCharIO();
|
mpRAM->DisableCharIO();
|
||||||
|
AddDebugTrace("Char I/O DISABLED");
|
||||||
|
//mpRAM->GetMemMapDevPtr()->DeactivateCharIO();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1386,16 +1426,21 @@ bool VMachine::GetCharIOActive()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: SetGraphDisp()
|
||||||
* Purpose:
|
* Purpose: Set graphics device address and enable.
|
||||||
* Arguments:
|
* Arguments: addr - unsigned short : device base address.
|
||||||
* Returns:
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void VMachine::SetGraphDisp(unsigned short addr)
|
void VMachine::SetGraphDisp(unsigned short addr)
|
||||||
{
|
{
|
||||||
mGraphDispActive = true;
|
mGraphDispActive = true;
|
||||||
mpRAM->SetGraphDisp(addr);
|
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;
|
mGraphDispActive = false;
|
||||||
mpRAM->DisableGraphDisp();
|
mpRAM->DisableGraphDisp();
|
||||||
|
AddDebugTrace("Graphics Device DISABLED.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1448,7 +1494,7 @@ bool VMachine::GetGraphDispActive()
|
||||||
*/
|
*/
|
||||||
void VMachine::ShowIO()
|
void VMachine::ShowIO()
|
||||||
{
|
{
|
||||||
if (mCharIOActive)
|
if (mCharIOActive && NULL != mpDisp)
|
||||||
mpDisp->ShowScr();
|
mpDisp->ShowScr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1489,6 +1535,7 @@ bool VMachine::IsAutoReset()
|
||||||
void VMachine::EnableROM()
|
void VMachine::EnableROM()
|
||||||
{
|
{
|
||||||
mpRAM->EnableROM();
|
mpRAM->EnableROM();
|
||||||
|
AddDebugTrace("ROM ENABLED.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1502,6 +1549,7 @@ void VMachine::EnableROM()
|
||||||
void VMachine::DisableROM()
|
void VMachine::DisableROM()
|
||||||
{
|
{
|
||||||
mpRAM->DisableROM();
|
mpRAM->DisableROM();
|
||||||
|
AddDebugTrace("ROM DISABLED.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1515,19 +1563,32 @@ void VMachine::DisableROM()
|
||||||
void VMachine::SetROM(unsigned short start, unsigned short end)
|
void VMachine::SetROM(unsigned short start, unsigned short end)
|
||||||
{
|
{
|
||||||
mpRAM->SetROM(start, end);
|
mpRAM->SetROM(start, end);
|
||||||
|
if (mDebugTraceActive) {
|
||||||
|
string msg;
|
||||||
|
msg = "ROM SET, Start: $" + Addr2HexStr(start) + ", End: $";
|
||||||
|
msg += Addr2HexStr(end) + ".";
|
||||||
|
AddDebugTrace(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: EnableROM()
|
||||||
* Purpose:
|
* Purpose: Sets and enables Read Only Memory range.
|
||||||
* Arguments:
|
* Arguments: start, end - unsigned short : start and end addresses
|
||||||
* Returns:
|
* of the ROM.
|
||||||
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void VMachine::EnableROM(unsigned short start, unsigned short end)
|
void VMachine::EnableROM(unsigned short start, unsigned short end)
|
||||||
{
|
{
|
||||||
mpRAM->EnableROM(start, 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();
|
mpCPU->Reset();
|
||||||
Exec(mpCPU->GetRegs()->PtrAddr);
|
Exec(mpCPU->GetRegs()->PtrAddr);
|
||||||
mpCPU->mExitAtLastRTS = true;
|
mpCPU->mExitAtLastRTS = true;
|
||||||
|
AddDebugTrace("*** CPU RESET ***");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1692,6 +1754,13 @@ int VMachine::GetLastError()
|
||||||
void VMachine::EnableExecHistory(bool enexehist)
|
void VMachine::EnableExecHistory(bool enexehist)
|
||||||
{
|
{
|
||||||
mpCPU->EnableExecHistory(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();
|
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
|
} // namespace MKBasic
|
||||||
|
|
94
VMachine.h
94
VMachine.h
|
@ -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
|
#ifndef VMACHINE_H
|
||||||
#define VMACHINE_H
|
#define VMACHINE_H
|
||||||
|
|
||||||
|
@ -8,6 +40,7 @@
|
||||||
#include "MKCpu.h"
|
#include "MKCpu.h"
|
||||||
#include "Memory.h"
|
#include "Memory.h"
|
||||||
#include "Display.h"
|
#include "Display.h"
|
||||||
|
#include "ConsoleIO.h"
|
||||||
|
|
||||||
//#define WINDOWS 1
|
//#define WINDOWS 1
|
||||||
#if defined (WINDOWS)
|
#if defined (WINDOWS)
|
||||||
|
@ -21,11 +54,46 @@
|
||||||
#define HDRDATALEN 128
|
#define HDRDATALEN 128
|
||||||
#define HDRDATALEN_OLD 15
|
#define HDRDATALEN_OLD 15
|
||||||
#define HEXEOF ":00000001FF"
|
#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 std;
|
||||||
using namespace chrono;
|
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 {
|
namespace MKBasic {
|
||||||
|
|
||||||
// Types of memory image definition file.
|
// Types of memory image definition file.
|
||||||
|
@ -70,7 +138,6 @@ struct PerfStats {
|
||||||
time_point<high_resolution_clock>
|
time_point<high_resolution_clock>
|
||||||
begin_time; // the moment of time count start
|
begin_time; // the moment of time count start
|
||||||
long cycles; // performance stats
|
long cycles; // performance stats
|
||||||
long micro_secs; // performance stats
|
|
||||||
long prev_cycles; // previously measured stats
|
long prev_cycles; // previously measured stats
|
||||||
long prev_usec; // previously measured stats
|
long prev_usec; // previously measured stats
|
||||||
int perf_onemhz; // avg. % perf. based on 1MHz CPU.
|
int perf_onemhz; // avg. % perf. based on 1MHz CPU.
|
||||||
|
@ -131,16 +198,24 @@ class VMachine
|
||||||
// cycles per second (1 MHz CPU).
|
// cycles per second (1 MHz CPU).
|
||||||
void EnableExecHistory(bool enexehist);
|
void EnableExecHistory(bool enexehist);
|
||||||
bool IsExecHistoryActive();
|
bool IsExecHistoryActive();
|
||||||
|
void EnableDebugTrace();
|
||||||
|
void DisableDebugTrace();
|
||||||
|
bool IsDebugTraceActive();
|
||||||
|
void EnablePerfStats();
|
||||||
|
void DisablePerfStats();
|
||||||
|
bool IsPerfStatsActive();
|
||||||
|
queue<string> GetDebugTraces();
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
MKCpu *mpCPU;
|
MKCpu *mpCPU; // object maintained locally
|
||||||
Memory *mpROM;
|
Memory *mpROM; // object maintained locally
|
||||||
Memory *mpRAM;
|
Memory *mpRAM; // object maintained locally
|
||||||
Display *mpDisp;
|
Display *mpDisp; // just a pointer
|
||||||
|
ConsoleIO *mpConIO; // object maintained locally
|
||||||
unsigned short mRunAddr;
|
unsigned short mRunAddr;
|
||||||
unsigned short mCharIOAddr;
|
unsigned short mCharIOAddr;
|
||||||
bool mCharIOActive;
|
bool mCharIOActive;
|
||||||
|
@ -152,6 +227,10 @@ class VMachine
|
||||||
bool mGraphDispActive;
|
bool mGraphDispActive;
|
||||||
bool mOldStyleHeader;
|
bool mOldStyleHeader;
|
||||||
PerfStats mPerfStats;
|
PerfStats mPerfStats;
|
||||||
|
queue<string> mDebugTraces;
|
||||||
|
bool mPerfStatsActive;
|
||||||
|
bool mDebugTraceActive;
|
||||||
|
time_point<high_resolution_clock> mBeginTime;
|
||||||
|
|
||||||
int LoadMEM(string memfname, Memory *pmem);
|
int LoadMEM(string memfname, Memory *pmem);
|
||||||
void ShowDisp();
|
void ShowDisp();
|
||||||
|
@ -161,6 +240,9 @@ class VMachine
|
||||||
void SaveHdrData(FILE *fp);
|
void SaveHdrData(FILE *fp);
|
||||||
eMemoryImageTypes GetMemoryImageType(string ramfname);
|
eMemoryImageTypes GetMemoryImageType(string ramfname);
|
||||||
int CalcCurrPerf();
|
int CalcCurrPerf();
|
||||||
|
void AddDebugTrace(string msg);
|
||||||
|
string Addr2HexStr(unsigned short addr);
|
||||||
|
string Addr2DecStr(unsigned short addr);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace MKBasic
|
} // namespace MKBasic
|
||||||
|
|
|
@ -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
377
main.cpp
|
@ -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 <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
|
@ -17,9 +49,16 @@ using namespace std;
|
||||||
using namespace MKBasic;
|
using namespace MKBasic;
|
||||||
|
|
||||||
#define ANIM_DELAY 250
|
#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;
|
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;
|
VMachine *pvm = NULL;
|
||||||
Regs *preg = NULL;
|
Regs *preg = NULL;
|
||||||
bool ioecho = false, opbrk = false, needhelp = false;
|
bool ioecho = false, opbrk = false, needhelp = false;
|
||||||
|
@ -32,6 +71,26 @@ void ShowHelp();
|
||||||
void CmdArgHelp(string prgname);
|
void CmdArgHelp(string prgname);
|
||||||
void CopyrightBanner();
|
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()
|
* Method: RunSingleInstr()
|
||||||
|
@ -44,9 +103,14 @@ void CopyrightBanner();
|
||||||
{
|
{
|
||||||
Regs *ret = NULL;
|
Regs *ret = NULL;
|
||||||
|
|
||||||
|
pvm->Disassemble(addr, diss_buf);
|
||||||
|
// skip # cycles per op-code specs
|
||||||
do {
|
do {
|
||||||
ret = pvm->Step(addr);
|
ret = pvm->Step(addr);
|
||||||
} while (ret->CyclesLeft > 0);
|
} while (ret->CyclesLeft > 0);
|
||||||
|
// and now execute the actual op-code
|
||||||
|
ret = pvm->Step(addr);
|
||||||
|
pvm->Disassemble(ret->PtrAddr, curr_buf);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -64,13 +128,56 @@ void CopyrightBanner();
|
||||||
{
|
{
|
||||||
Regs *ret = NULL;
|
Regs *ret = NULL;
|
||||||
|
|
||||||
|
pvm->Disassemble(preg->PtrAddr, diss_buf);
|
||||||
|
// skip # cycles per op-code specs
|
||||||
do {
|
do {
|
||||||
ret = pvm->Step();
|
ret = pvm->Step();
|
||||||
} while (ret->CyclesLeft > 0);
|
} while (ret->CyclesLeft > 0);
|
||||||
|
// and now execute the actual op-code
|
||||||
|
ret = pvm->Step();
|
||||||
|
pvm->Disassemble(ret->PtrAddr, curr_buf);
|
||||||
|
|
||||||
return ret;
|
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()
|
* Method: PrintVMErr()
|
||||||
|
@ -81,57 +188,23 @@ void CopyrightBanner();
|
||||||
*/
|
*/
|
||||||
void PrintVMErr(int err)
|
void PrintVMErr(int err)
|
||||||
{
|
{
|
||||||
bool pressenter = true;
|
bool pressenter = false;
|
||||||
switch (err) {
|
|
||||||
case MEMIMGERR_RAMBIN_EOF:
|
for (int i=0; g_vmerrtbl[i].id >= 0; i++) {
|
||||||
cout << "WARNING: Unexpected EOF (image shorter than 64kB).";
|
if (g_vmerrtbl[i].id == err) {
|
||||||
cout << endl;
|
pressenter = true;
|
||||||
break;
|
if (strlen(g_vmerrtbl[i].text1)) {
|
||||||
case MEMIMGERR_RAMBIN_OPEN:
|
cout << g_vmerrtbl[i].text1 << endl;
|
||||||
cout << "WARNING: Unable to open memory image file." << endl;
|
}
|
||||||
break;
|
if (strlen(g_vmerrtbl[i].text2)) {
|
||||||
case MEMIMGERR_RAMBIN_HDR:
|
cout << g_vmerrtbl[i].text2 << endl;
|
||||||
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;
|
|
||||||
break;
|
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) {
|
if (pressenter) {
|
||||||
cout << "Press [ENTER]...";
|
PressEnter2Cont("");
|
||||||
getchar();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +229,7 @@ void trap_signal(int signum)
|
||||||
pvm->SetOpInterrupt(true);
|
pvm->SetOpInterrupt(true);
|
||||||
opbrk = true;
|
opbrk = true;
|
||||||
}
|
}
|
||||||
//exit(signum);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +254,7 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
|
||||||
switch( fdwCtrlType )
|
switch( fdwCtrlType )
|
||||||
{
|
{
|
||||||
case CTRL_C_EVENT:
|
case CTRL_C_EVENT:
|
||||||
//Beep( 750, 300 );
|
|
||||||
if (NULL != pvm && NULL != preg) {
|
if (NULL != pvm && NULL != preg) {
|
||||||
pvm->SetOpInterrupt(true);
|
pvm->SetOpInterrupt(true);
|
||||||
opbrk = true;
|
opbrk = true;
|
||||||
|
@ -190,12 +263,12 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
|
||||||
|
|
||||||
|
|
||||||
case CTRL_CLOSE_EVENT:
|
case CTRL_CLOSE_EVENT:
|
||||||
//Beep( 600, 200 );
|
|
||||||
cout << "Ctrl-Close event" << endl;
|
cout << "Ctrl-Close event" << endl;
|
||||||
return TRUE ;
|
return TRUE ;
|
||||||
|
|
||||||
case CTRL_BREAK_EVENT:
|
case CTRL_BREAK_EVENT:
|
||||||
//Beep( 900, 200 );
|
|
||||||
if (NULL != pvm && NULL != preg) {
|
if (NULL != pvm && NULL != preg) {
|
||||||
pvm->SetOpInterrupt(true);
|
pvm->SetOpInterrupt(true);
|
||||||
opbrk = true;
|
opbrk = true;
|
||||||
|
@ -203,7 +276,7 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case CTRL_LOGOFF_EVENT:
|
case CTRL_LOGOFF_EVENT:
|
||||||
//Beep( 1000, 200 );
|
|
||||||
cout << "Ctrl-Logoff event" << endl;
|
cout << "Ctrl-Logoff event" << endl;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
@ -275,18 +348,24 @@ bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat)
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
char sBuf[80] = {0};
|
char sBuf[80] = {0};
|
||||||
|
|
||||||
sprintf(sBuf, "| PC: $%04x | Acc: $%02x (" BYTETOBINARYPATTERN ") | X: $%02x | Y: $%02x |",
|
sprintf(sBuf, "| PC: $%04x | Acc: $%02x (" BYTETOBINARYPATTERN
|
||||||
preg->PtrAddr, preg->Acc, BYTETOBINARY(preg->Acc), preg->IndX, preg->IndY);
|
") | X: $%02x | Y: $%02x |",
|
||||||
cout << "*-------------*-----------------------*----------*----------*" << endl;
|
preg->PtrAddr, preg->Acc, BYTETOBINARY(preg->Acc),
|
||||||
|
preg->IndX, preg->IndY);
|
||||||
|
cout << "*-------------*-----------------------*----------*----------*";
|
||||||
|
cout << endl;
|
||||||
cout << sBuf << endl;
|
cout << sBuf << endl;
|
||||||
cout << "*-------------*-----------------------*----------*----------*" << endl;
|
cout << "*-------------*-----------------------*----------*----------*";
|
||||||
cout << "| NV-BDIZC |" << endl;
|
cout << endl;
|
||||||
|
cout << "| NV-BDIZC |";
|
||||||
|
cout << " : " << diss_buf << " " << endl;
|
||||||
cout << "| " << bitset<8>((int)preg->Flags) << " |";
|
cout << "| " << bitset<8>((int)preg->Flags) << " |";
|
||||||
cout << " Last instr.: " << preg->LastInstr << " " << endl;
|
cout << " : " << curr_buf << " " << endl;
|
||||||
cout << "*-------------*" << endl;
|
cout << "*-------------*" << endl;
|
||||||
cout << endl;
|
cout << endl;
|
||||||
cout << "Stack: $" << hex << (unsigned short)preg->PtrStack << " " << endl;
|
cout << "Stack: $" << hex << (unsigned short)preg->PtrStack << " " << endl;
|
||||||
cout << " \r";
|
cout << " ";
|
||||||
|
cout << " \r";
|
||||||
// display stack contents
|
// display stack contents
|
||||||
cout << " [";
|
cout << " [";
|
||||||
int j = 0, stacklines = 1;
|
int j = 0, stacklines = 1;
|
||||||
|
@ -310,14 +389,19 @@ bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat)
|
||||||
// end display stack contents
|
// end display stack contents
|
||||||
|
|
||||||
if (showiostat) {
|
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 << " at: $" << hex << pvm->GetCharIOAddr() << ", ";
|
||||||
cout << " local echo: " << (ioecho ? "ON" : "OFF") << "." << endl;
|
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 << " at: $" << hex << pvm->GetGraphDispAddr() << endl;
|
||||||
cout << "ROM: " << ((pvm->IsROMEnabled()) ? "enabled." : "disabled.") << " ";
|
cout << "ROM: ";
|
||||||
cout << "Range: $" << hex << pvm->GetROMBegin() << " - $" << hex << pvm->GetROMEnd() << "." << endl;
|
cout << ((pvm->IsROMEnabled()) ? "enabled." : "disabled.") << " ";
|
||||||
cout << "Op-code execute history: " << (pvm->IsExecHistoryActive() ? "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 << "." << endl;
|
||||||
}
|
}
|
||||||
cout << " \r";
|
cout << " \r";
|
||||||
|
@ -327,8 +411,8 @@ bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: ShowMenu()
|
||||||
* Purpose:
|
* Purpose: Print available commands on the console.
|
||||||
* Arguments:
|
* Arguments:
|
||||||
* Returns:
|
* Returns:
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
|
@ -346,6 +430,8 @@ void ShowMenu()
|
||||||
cout << " L - load memory image | O - display op-code exec. history" << endl;
|
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 << " D - disassemble code in memory | Q - quit, 0 - reset, H - help" << endl;
|
||||||
cout << " V - toggle graphics emulation | U - enable/disable exec. history" << 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;
|
cout << "------------------------------------+----------------------------------------" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,9 +463,9 @@ void ShowMenu()
|
||||||
brk = preg->SoftIrq; \
|
brk = preg->SoftIrq; \
|
||||||
lrts = preg->LastRTS; \
|
lrts = preg->LastRTS; \
|
||||||
while(step && nsteps > 1 && !brk && !lrts && !opbrk) { \
|
while(step && nsteps > 1 && !brk && !lrts && !opbrk) { \
|
||||||
|
preg = RunSingleCurrInstr(); \
|
||||||
cout << "addr: $" << hex << preg->PtrAddr << ", step: " << dec << stct; \
|
cout << "addr: $" << hex << preg->PtrAddr << ", step: " << dec << stct; \
|
||||||
cout << " \r"; \
|
cout << " \r"; \
|
||||||
preg = RunSingleCurrInstr(); \
|
|
||||||
if (anim) { \
|
if (anim) { \
|
||||||
if (cls & ClsIfDirty) { pvm->ClearScreen(); cls = false; } \
|
if (cls & ClsIfDirty) { pvm->ClearScreen(); cls = false; } \
|
||||||
pvm->ScrHome(); \
|
pvm->ScrHome(); \
|
||||||
|
@ -404,13 +490,19 @@ void ShowMenu()
|
||||||
*/
|
*/
|
||||||
void ShowSpeedStats()
|
void ShowSpeedStats()
|
||||||
{
|
{
|
||||||
|
if (pvm->IsPerfStatsActive()) {
|
||||||
cout << endl;
|
cout << endl;
|
||||||
cout << dec;
|
cout << dec;
|
||||||
cout << "CPU emulation speed stats: " << endl;
|
cout << "CPU emulation speed stats: " << endl;
|
||||||
cout << "|-> Average speed based on 1MHz CPU: " << pvm->GetPerfStats().perf_onemhz << " %" << 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 # of cycles exec.: " << pvm->GetPerfStats().prev_cycles << endl;
|
||||||
cout << "|-> Last measured time of execution: " << pvm->GetPerfStats().prev_usec << " usec" << endl;
|
cout << "|-> Last measured time of execution: " << pvm->GetPerfStats().prev_usec << " usec" << endl;
|
||||||
cout << endl;
|
cout << endl;
|
||||||
|
} else {
|
||||||
|
cout << endl;
|
||||||
|
cout << "Emulation performance stats is OFF." << endl;
|
||||||
|
cout << endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -425,8 +517,10 @@ void ExecHistory()
|
||||||
{
|
{
|
||||||
if (pvm->IsExecHistoryActive()) {
|
if (pvm->IsExecHistoryActive()) {
|
||||||
queue<string> exechist(pvm->GetExecHistory());
|
queue<string> exechist(pvm->GetExecHistory());
|
||||||
cout << "PC : INSTR ACC | X | Y | PS | SP" << endl;
|
cout << "PC : INSTR ACC | X | Y | PS | SP";
|
||||||
cout << "------------------------------------+-----+-----+-----+-----" << endl;
|
cout << endl;
|
||||||
|
cout << "------------------------------------+-----+-----+-----+-----";
|
||||||
|
cout << endl;
|
||||||
while (exechist.size()) {
|
while (exechist.size()) {
|
||||||
cout << exechist.front() << endl;
|
cout << exechist.front() << endl;
|
||||||
exechist.pop();
|
exechist.pop();
|
||||||
|
@ -478,6 +572,7 @@ unsigned int LoadImage(unsigned int newaddr)
|
||||||
if (pvm->IsAutoExec()) execvm = true;
|
if (pvm->IsAutoExec()) execvm = true;
|
||||||
if (newaddr == 0) newaddr = 0x10000;
|
if (newaddr == 0) newaddr = 0x10000;
|
||||||
}
|
}
|
||||||
|
PrintVMErr(pvm->GetLastError());
|
||||||
|
|
||||||
return newaddr;
|
return newaddr;
|
||||||
}
|
}
|
||||||
|
@ -496,7 +591,7 @@ unsigned int ToggleIO(unsigned int ioaddr)
|
||||||
pvm->DisableCharIO();
|
pvm->DisableCharIO();
|
||||||
cout << "I/O deactivated." << endl;
|
cout << "I/O deactivated." << endl;
|
||||||
} else {
|
} else {
|
||||||
ioaddr = PromptNewAddress("Address (0..FFFF): ");
|
ioaddr = PromptNewAddress(PROMPT_ADDR);
|
||||||
cout << " [" << hex << ioaddr << "]" << endl;
|
cout << " [" << hex << ioaddr << "]" << endl;
|
||||||
pvm->SetCharIO(ioaddr, ioecho);
|
pvm->SetCharIO(ioaddr, ioecho);
|
||||||
cout << "I/O activated." << endl;
|
cout << "I/O activated." << endl;
|
||||||
|
@ -519,7 +614,7 @@ unsigned int ToggleGrDisp(unsigned int graddr)
|
||||||
pvm->DisableGraphDisp();
|
pvm->DisableGraphDisp();
|
||||||
cout << "Graphics display deactivated." << endl;
|
cout << "Graphics display deactivated." << endl;
|
||||||
} else {
|
} else {
|
||||||
graddr = PromptNewAddress("Address (0..FFFF): ");
|
graddr = PromptNewAddress(PROMPT_ADDR);
|
||||||
cout << " [" << hex << graddr << "]" << endl;
|
cout << " [" << hex << graddr << "]" << endl;
|
||||||
pvm->SetGraphDisp(graddr);
|
pvm->SetGraphDisp(graddr);
|
||||||
cout << "Graphics display activated." << endl;
|
cout << "Graphics display activated." << endl;
|
||||||
|
@ -531,14 +626,14 @@ unsigned int ToggleGrDisp(unsigned int graddr)
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method: WriteToMemory()
|
* Method: WriteToMemory()
|
||||||
* Purpose:
|
* Purpose: Take user input and write to memory.
|
||||||
* Arguments:
|
* Arguments:
|
||||||
* Returns:
|
* Returns:
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void WriteToMemory()
|
void WriteToMemory()
|
||||||
{
|
{
|
||||||
unsigned int tmpaddr = PromptNewAddress("Address (0..FFFF): ");
|
unsigned int tmpaddr = PromptNewAddress(PROMPT_ADDR);
|
||||||
cout << " [" << hex << tmpaddr << "]" << endl;
|
cout << " [" << hex << tmpaddr << "]" << endl;
|
||||||
cout << "Enter hex bytes [00..FF] values separated with NL or spaces, end with [100]:" << endl;
|
cout << "Enter hex bytes [00..FF] values separated with NL or spaces, end with [100]:" << endl;
|
||||||
unsigned short v = 0;
|
unsigned short v = 0;
|
||||||
|
@ -554,7 +649,7 @@ void WriteToMemory()
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method: DisassembleMemory()
|
* Method: DisassembleMemory()
|
||||||
* Purpose:
|
* Purpose: Disassemble machine code in memory to symbolic format.
|
||||||
* Arguments:
|
* Arguments:
|
||||||
* Returns:
|
* Returns:
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
|
@ -562,10 +657,10 @@ void WriteToMemory()
|
||||||
void DisassembleMemory()
|
void DisassembleMemory()
|
||||||
{
|
{
|
||||||
unsigned int addrbeg = 0x10000, addrend = 0x10000;
|
unsigned int addrbeg = 0x10000, addrend = 0x10000;
|
||||||
cout << "Enter address range (0..0xFFFF)..." << endl;
|
cout << PROMPT_RANGE_ADDR << endl;
|
||||||
addrbeg = PromptNewAddress("Start address (0..FFFF): ");
|
addrbeg = PromptNewAddress(PROMPT_START_ADDR);
|
||||||
cout << " [" << hex << addrbeg << "]" << endl;
|
cout << " [" << hex << addrbeg << "]" << endl;
|
||||||
addrend = PromptNewAddress("End address (0..FFFF): ");
|
addrend = PromptNewAddress(PROMPT_END_ADDR);
|
||||||
cout << " [" << hex << addrend << "]" << endl;
|
cout << " [" << hex << addrend << "]" << endl;
|
||||||
cout << endl;
|
cout << endl;
|
||||||
for (unsigned int addr = addrbeg; addr <= addrend;) {
|
for (unsigned int addr = addrbeg; addr <= addrend;) {
|
||||||
|
@ -578,7 +673,7 @@ void DisassembleMemory()
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method: DumpMemory()
|
* Method: DumpMemory()
|
||||||
* Purpose:
|
* Purpose: Display contents of memory, range entered by user.
|
||||||
* Arguments:
|
* Arguments:
|
||||||
* Returns:
|
* Returns:
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
|
@ -586,10 +681,10 @@ void DisassembleMemory()
|
||||||
void DumpMemory()
|
void DumpMemory()
|
||||||
{
|
{
|
||||||
unsigned int addrbeg = 0x10000, addrend = 0x10000;
|
unsigned int addrbeg = 0x10000, addrend = 0x10000;
|
||||||
cout << "Enter address range (0..0xFFFF)..." << endl;
|
cout << PROMPT_RANGE_ADDR << endl;
|
||||||
addrbeg = PromptNewAddress("Start address (0..FFFF): ");
|
addrbeg = PromptNewAddress(PROMPT_START_ADDR);
|
||||||
cout << " [" << hex << addrbeg << "]" << endl;
|
cout << " [" << hex << addrbeg << "]" << endl;
|
||||||
addrend = PromptNewAddress("End address (0..FFFF): ");
|
addrend = PromptNewAddress(PROMPT_END_ADDR);
|
||||||
cout << " [" << hex << addrend << "]" << endl;
|
cout << " [" << hex << addrend << "]" << endl;
|
||||||
cout << endl;
|
cout << endl;
|
||||||
for (unsigned int addr = addrbeg; addr <= addrend; addr+=16) {
|
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()
|
* Method: LoadArgs()
|
||||||
|
@ -769,7 +945,7 @@ int main(int argc, char *argv[]) {
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
cout << endl;
|
cout << endl;
|
||||||
cout << "Type '?' and press ENTER for Menu ..." << endl;
|
cout << "Type '?' and press [ENTER] for Menu ..." << endl;
|
||||||
cout << endl;
|
cout << endl;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -805,6 +981,7 @@ int main(int argc, char *argv[]) {
|
||||||
ExecHistory();
|
ExecHistory();
|
||||||
} else if (c == 'l') { // load memory image
|
} else if (c == 'l') { // load memory image
|
||||||
newaddr = LoadImage(newaddr);
|
newaddr = LoadImage(newaddr);
|
||||||
|
ioaddr = pvm->GetCharIOAddr();
|
||||||
} else if (c == 'k') { // toggle ROM emulation
|
} else if (c == 'k') { // toggle ROM emulation
|
||||||
if (!enrom) {
|
if (!enrom) {
|
||||||
enrom = true;
|
enrom = true;
|
||||||
|
@ -856,7 +1033,7 @@ int main(int argc, char *argv[]) {
|
||||||
WriteToMemory();
|
WriteToMemory();
|
||||||
} else if (c == 'a') { // change run address
|
} else if (c == 'a') { // change run address
|
||||||
execaddr = stop = true;
|
execaddr = stop = true;
|
||||||
newaddr = PromptNewAddress("Address (0..FFFF): ");
|
newaddr = PromptNewAddress(PROMPT_ADDR);
|
||||||
cout << " [" << hex << newaddr << "]" << endl;
|
cout << " [" << hex << newaddr << "]" << endl;
|
||||||
} else if (c == 's') {
|
} else if (c == 's') {
|
||||||
runvm = step = stop = true;
|
runvm = step = stop = true;
|
||||||
|
@ -875,13 +1052,13 @@ int main(int argc, char *argv[]) {
|
||||||
} else if (c == 'g') { // run from new address until BRK
|
} else if (c == 'g') { // run from new address until BRK
|
||||||
runvm = true;
|
runvm = true;
|
||||||
execaddr = true;
|
execaddr = true;
|
||||||
newaddr = PromptNewAddress("Address (0..FFFF): ");
|
newaddr = PromptNewAddress(PROMPT_ADDR);
|
||||||
cout << " [" << hex << newaddr << "]" << endl;
|
cout << " [" << hex << newaddr << "]" << endl;
|
||||||
show_menu = true;
|
show_menu = true;
|
||||||
} else if (c == 'x') { // execute code at address
|
} else if (c == 'x') { // execute code at address
|
||||||
execvm = true;
|
execvm = true;
|
||||||
execaddr = true;
|
execaddr = true;
|
||||||
newaddr = PromptNewAddress("Address (0..FFFF): ");
|
newaddr = PromptNewAddress(PROMPT_ADDR);
|
||||||
cout << " [" << hex << newaddr << "]" << endl;
|
cout << " [" << hex << newaddr << "]" << endl;
|
||||||
show_menu = true;
|
show_menu = true;
|
||||||
} else if (c == 'q') { // quit
|
} else if (c == 'q') { // quit
|
||||||
|
@ -895,6 +1072,14 @@ int main(int argc, char *argv[]) {
|
||||||
cout << "Op-code execute history has been ";
|
cout << "Op-code execute history has been ";
|
||||||
cout << (pvm->IsExecHistoryActive() ? "enabled" : "disabled") << ".";
|
cout << (pvm->IsExecHistoryActive() ? "enabled" : "disabled") << ".";
|
||||||
cout << endl;
|
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
|
U - enable/disable exec. history
|
||||||
Toggle enable/disable of op-codes execute history.
|
Toggle enable/disable of op-codes execute history.
|
||||||
Disabling this feature improves performance.
|
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:
|
NOTE:
|
||||||
1. If no arguments provided, each command will prompt user to enter
|
1. If no arguments provided, each command will prompt user to enter
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Project: MKBasic
|
# Project: VM65
|
||||||
# Makefile created by Dev-C++ 5.11
|
# Makefile created by Dev-C++ 5.11
|
||||||
# and modified for standalone MINGW compiler installation.
|
# and modified for standalone MINGW compiler installation.
|
||||||
|
|
||||||
|
@ -7,9 +7,9 @@ SDLBASE = $(SDLDIR)
|
||||||
CPP = g++.exe -D__DEBUG__
|
CPP = g++.exe -D__DEBUG__
|
||||||
CC = gcc.exe -D__DEBUG__
|
CC = gcc.exe -D__DEBUG__
|
||||||
WINDRES = windres.exe
|
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
|
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
|
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
|
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
|
SDLLIBS = -L"$(SDLBASE)\x86_64-w64-mingw32/lib" -lSDL2main -lSDL2
|
||||||
|
@ -40,9 +40,6 @@ main.o: main.cpp
|
||||||
VMachine.o: VMachine.cpp VMachine.h
|
VMachine.o: VMachine.cpp VMachine.h
|
||||||
$(CPP) -c VMachine.cpp -o VMachine.o $(CXXFLAGS) $(SDLINCS)
|
$(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
|
MKCpu.o: MKCpu.cpp MKCpu.h
|
||||||
$(CPP) -c MKCpu.cpp -o MKCpu.o $(CXXFLAGS) $(SDLINCS)
|
$(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
|
MKGenException.o: MKGenException.cpp MKGenException.h
|
||||||
$(CPP) -c MKGenException.cpp -o MKGenException.o $(CXXFLAGS)
|
$(CPP) -c MKGenException.cpp -o MKGenException.o $(CXXFLAGS)
|
||||||
|
|
||||||
|
ConsoleIO.o: ConsoleIO.cpp ConsoleIO.h
|
||||||
|
$(CPP) -c ConsoleIO.cpp -o ConsoleIO.o $(CXXFLAGS)
|
||||||
|
|
||||||
$(BIN2): $(OBJ2)
|
$(BIN2): $(OBJ2)
|
||||||
$(CC) $(LINKOBJ2) -o $(BIN2) $(LIBS)
|
$(CC) $(LINKOBJ2) -o $(BIN2) $(LIBS)
|
||||||
|
|
||||||
|
|
31
system.h
31
system.h
|
@ -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)
|
#if !defined(LINUX)
|
||||||
#define WINDOWS
|
#define WINDOWS
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue