mirror of
https://github.com/makarcz/vm6502.git
synced 2025-04-22 07:37:26 +00:00
Memory Mapped Device Layer
Implemented MemMapDev layer, added User Manual and Programmers Reference.
This commit is contained in:
parent
0d47565b9d
commit
20d12b5eae
464
GraphDisp.cpp
Normal file
464
GraphDisp.cpp
Normal file
@ -0,0 +1,464 @@
|
||||
|
||||
#include "GraphDisp.h"
|
||||
#include "MKGenException.h"
|
||||
#include "system.h"
|
||||
|
||||
#if defined(WINDOWS)
|
||||
#include "wtypes.h"
|
||||
#endif
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GraphDisp()
|
||||
* Purpose: Class default constructor.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
GraphDisp::GraphDisp()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GraphDisp()
|
||||
* Purpose: Class custom constructor.
|
||||
* Arguments: width, height - integer, graphical display dimensions
|
||||
* (virtual dimensions as opposed to
|
||||
* to actual dimensions GRAPHDISP_MAXW,
|
||||
* GRAPHDISP_MAXH)
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
GraphDisp::GraphDisp(int width, int height)
|
||||
{
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ~GraphDisp()
|
||||
* Purpose: Class destructor.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
GraphDisp::~GraphDisp()
|
||||
{
|
||||
mContLoop = false;
|
||||
while (mMainLoopActive);
|
||||
SDL_DestroyRenderer(mpRenderer);
|
||||
SDL_DestroyWindow(mpWindow);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: Initialize()
|
||||
* Purpose: Initialize class members, objects.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
* Problems:
|
||||
* SDL_CreateWindow with flags:
|
||||
* SDL_WINDOW_SHOWN|SDL_WINDOW_BORDERLESS|SDL_WINDOW_RESIZABLE
|
||||
* creates window with no title/icon/system buttons, but with
|
||||
* a frame, which can be used to resize it. The problem is, when
|
||||
* it is resized, it stops updating correctly.
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::Initialize()
|
||||
{
|
||||
int desk_w, desk_h, winbd_top = 5, winbd_right = 5;
|
||||
|
||||
GetDesktopResolution(desk_w, desk_h);
|
||||
// Available in version > 2.0.4
|
||||
//SDL_GetWindowBordersSize(mpWindow, &winbd_top, NULL, NULL, &winbd_right);
|
||||
mWinPosX = desk_w - GRAPHDISP_MAXW - winbd_right;
|
||||
mWinPosY = winbd_top;
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
|
||||
mpWindow = SDL_CreateWindow(
|
||||
"GraphDisp",
|
||||
mWinPosX,
|
||||
mWinPosY,
|
||||
GRAPHDISP_MAXW,
|
||||
GRAPHDISP_MAXH,
|
||||
SDL_WINDOW_SHOWN|SDL_WINDOW_BORDERLESS|SDL_WINDOW_RESIZABLE
|
||||
);
|
||||
|
||||
if (NULL == mpWindow) {
|
||||
throw MKGenException(SDL_GetError());
|
||||
}
|
||||
|
||||
// Get window surface
|
||||
mpSurface = SDL_GetWindowSurface(mpWindow);
|
||||
|
||||
// Create renderer for window
|
||||
mpRenderer = SDL_CreateRenderer(mpWindow, -1, SDL_RENDERER_SOFTWARE);
|
||||
//mpRenderer = SDL_GetRenderer(mpWindow);
|
||||
|
||||
if (NULL == mpRenderer) {
|
||||
throw MKGenException(SDL_GetError());
|
||||
}
|
||||
|
||||
Clear();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ClearScreen()
|
||||
* Purpose: Clear the surface. Update screen.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::ClearScreen()
|
||||
{
|
||||
Clear();
|
||||
UpdateSurface();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: Clear()
|
||||
* Purpose: Clear the surface.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::Clear()
|
||||
{
|
||||
//Fill the surface with background color
|
||||
SDL_FillRect(mpSurface, NULL, SDL_MapRGB(mpSurface->format,
|
||||
mBgRgbR,
|
||||
mBgRgbG,
|
||||
mBgRgbB));
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: UpdateSurface()
|
||||
* Purpose: Update window surface.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::UpdateSurface()
|
||||
{
|
||||
//Update the surface
|
||||
SDL_UpdateWindowSurface(mpWindow);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: Update()
|
||||
* Purpose: Update window surface (public).
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::Update()
|
||||
{
|
||||
//Update the surface
|
||||
UpdateSurface();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: RenderPixel()
|
||||
* Purpose: Set or unset pixel (scaled) on graphics display
|
||||
* surface.
|
||||
* Arguments: x, y - integer, virtual pixel coordinates
|
||||
* set - boolean, set or unset pixel
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::RenderPixel(int x, int y, bool set)
|
||||
{
|
||||
SDL_Rect rtf;
|
||||
|
||||
rtf.x = x * mPixelSizeX; rtf.y = y * mPixelSizeY;
|
||||
rtf.w = mPixelSizeX;
|
||||
rtf.h = mPixelSizeY;
|
||||
|
||||
int rgb_r = 0, rgb_g = 0, rgb_b = 0;
|
||||
|
||||
if (set) {
|
||||
rgb_r = mFgRgbR;
|
||||
rgb_g = mFgRgbG;
|
||||
rgb_b = mFgRgbB;
|
||||
} else {
|
||||
rgb_r = mBgRgbR;
|
||||
rgb_g = mBgRgbG;
|
||||
rgb_b = mBgRgbB;
|
||||
}
|
||||
|
||||
// set or unset pixel
|
||||
SDL_FillRect(mpSurface, &rtf, SDL_MapRGB(mpSurface->format, rgb_r, rgb_g, rgb_b));
|
||||
|
||||
//Update the surface
|
||||
SDL_UpdateWindowSurface(mpWindow);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: SetPixel()
|
||||
* Purpose: Set pixel (scaled) on graphics display surface.
|
||||
* Arguments: x, y - pixel coordinates
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::SetPixel(int x, int y)
|
||||
{
|
||||
RenderPixel(x, y, true);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ErasePixel()
|
||||
* Purpose: Unset (erase) pixel (paint with BG color) on graphics
|
||||
* display surface.
|
||||
* Arguments: x, y - pixel coordinates
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::ErasePixel(int x, int y)
|
||||
{
|
||||
RenderPixel(x, y, false);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: DrawLine()
|
||||
* Purpose: Draw or erase line between specified points.
|
||||
* Arguments: x1, y1 - coordinates of first point
|
||||
* x2, y2 - coordinates of second point
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::DrawLine(int x1, int y1, int x2, int y2, bool draworerase)
|
||||
{
|
||||
int colR = mFgRgbR, colG = mFgRgbG, colB = mFgRgbB;
|
||||
|
||||
if (false == draworerase) {
|
||||
colR = mBgRgbR;
|
||||
colG = mBgRgbG;
|
||||
colB = mBgRgbB;
|
||||
}
|
||||
|
||||
SDL_SetRenderDrawColor(mpRenderer, colR, colG, colB, SDL_ALPHA_OPAQUE);
|
||||
SDL_RenderSetLogicalSize(mpRenderer, mWidth, mHeight);
|
||||
SDL_RenderSetScale(mpRenderer, mPixelSizeX, mPixelSizeY);
|
||||
SDL_RenderDrawLine(mpRenderer, x1, y1, x2, y2);
|
||||
SDL_RenderPresent(mpRenderer);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: DrawLine()
|
||||
* Purpose: Draw line between specified points.
|
||||
* Arguments: x1, y1 - coordinates of first point
|
||||
* x2, y2 - coordinates of second point
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::DrawLine(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
DrawLine(x1, y1, x2, y2, true);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: EraseLine()
|
||||
* Purpose: Erase line between specified points (draw with BG color)
|
||||
* Arguments: x1, y1 - coordinates of first point
|
||||
* x2, y2 - coordinates of second point
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::EraseLine(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
DrawLine(x1, y1, x2, y2, false);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: SetBgColor()
|
||||
* Purpose: Set background color.
|
||||
* Arguments: r, g, b - integer, red, green and blue intensities
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::SetBgColor(int r, int g, int b)
|
||||
{
|
||||
mBgRgbR = r;
|
||||
mBgRgbG = g;
|
||||
mBgRgbB = b;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: SetFgColor()
|
||||
* Purpose: Set foreground color.
|
||||
* Arguments: r, g, b - integer, red, green and blue intensities
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::SetFgColor(int r, int g, int b)
|
||||
{
|
||||
mFgRgbR = r;
|
||||
mFgRgbG = g;
|
||||
mFgRgbB = b;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: MainLoop()
|
||||
* Purpose: The main loop to process SDL events and update window.
|
||||
* This is a global function meant to run in a thread.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void MainLoop(GraphDisp *pgd)
|
||||
{
|
||||
pgd->mMainLoopActive = true;
|
||||
while (pgd->mContLoop) {
|
||||
pgd->ReadEvents();
|
||||
pgd->Update();
|
||||
}
|
||||
pgd->mMainLoopActive = false;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: Start()
|
||||
* Purpose: Starts MainLoop in a thread.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::Start(GraphDisp *pgd)
|
||||
{
|
||||
mMainLoopThread = thread(MainLoop,pgd);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: Stop()
|
||||
* Purpose: Stop MainLoop thread.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::Stop()
|
||||
{
|
||||
mContLoop = false;
|
||||
mMainLoopThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
bool GraphDisp::IsMainLoopActive()
|
||||
{
|
||||
return mMainLoopActive;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ReadEvents()
|
||||
* Purpose: Read events and perform actions when needed.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
* Problems:
|
||||
* By blitting (copying) surface I wanted to avoid loosing already
|
||||
* drawn pixels during windows resize, but this doesn't work.
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::ReadEvents()
|
||||
{
|
||||
SDL_Event e;
|
||||
SDL_Surface *pTmpSurface;
|
||||
|
||||
while (SDL_PollEvent(&e)) {
|
||||
|
||||
switch (e.type) {
|
||||
|
||||
case SDL_QUIT:
|
||||
mContLoop = false;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
if (SDL_WINDOWEVENT_RESIZED == e.window.event) {
|
||||
|
||||
pTmpSurface = SDL_CreateRGBSurface(0,
|
||||
mpSurface->w,
|
||||
mpSurface->h,
|
||||
mpSurface->format->BitsPerPixel,
|
||||
mpSurface->format->Rmask,
|
||||
mpSurface->format->Gmask,
|
||||
mpSurface->format->Bmask,
|
||||
mpSurface->format->Amask);
|
||||
SDL_SetWindowSize(mpWindow, GRAPHDISP_MAXW, GRAPHDISP_MAXH);
|
||||
mpSurface = SDL_GetWindowSurface(mpWindow);
|
||||
SDL_SetWindowPosition(mpWindow, mWinPosX, mWinPosY);
|
||||
SDL_SetSurfaceAlphaMod(pTmpSurface, 0);
|
||||
SDL_BlitSurface(pTmpSurface, 0, mpSurface, 0);
|
||||
UpdateSurface();
|
||||
SDL_FreeSurface(pTmpSurface);
|
||||
|
||||
} else if (SDL_WINDOWEVENT_FOCUS_GAINED == e.window.event) {
|
||||
|
||||
SDL_RaiseWindow(mpWindow);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void GraphDisp::GetDesktopResolution(int& horizontal, int& vertical)
|
||||
{
|
||||
#if defined(WINDOWS)
|
||||
RECT desktop;
|
||||
// Get a handle to the desktop window
|
||||
const HWND hDesktop = GetDesktopWindow();
|
||||
// Get the size of screen to the variable desktop
|
||||
GetWindowRect(hDesktop, &desktop);
|
||||
// The top left corner will have coordinates (0,0)
|
||||
// and the bottom right corner will have coordinates
|
||||
// (horizontal, vertical)
|
||||
horizontal = desktop.right;
|
||||
vertical = desktop.bottom;
|
||||
#else
|
||||
horizontal = GRAPHDISP_MAXW;
|
||||
vertical = GRAPHDISP_MAXH;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace MKBasic
|
72
GraphDisp.h
Normal file
72
GraphDisp.h
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef GRAPHDISP_H
|
||||
#define GRAPHDISP_H
|
||||
|
||||
/*
|
||||
* Rudimentary emulation of generic raster graphics RGB display device.
|
||||
*/
|
||||
|
||||
#include <thread>
|
||||
#include <SDL.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
const int GRAPHDISP_MAXW = 640; // "real" display width or maximum virtual width
|
||||
const int GRAPHDISP_MAXH = 400; // "real" display width or maximum virtual height
|
||||
|
||||
class GraphDisp {
|
||||
|
||||
public:
|
||||
|
||||
bool mContLoop = true;
|
||||
bool mMainLoopActive = false;
|
||||
|
||||
GraphDisp();
|
||||
GraphDisp(int width, int height);
|
||||
~GraphDisp();
|
||||
void Start(GraphDisp *pgd);
|
||||
void Stop();
|
||||
void SetPixel(int x, int y);
|
||||
void ErasePixel(int x, int y);
|
||||
void DrawLine(int x1, int y1, int x2, int y2);
|
||||
void EraseLine(int x1, int y1, int x2, int y2);
|
||||
void SetBgColor(int r, int g, int b);
|
||||
void SetFgColor(int r, int g, int b);
|
||||
void Update();
|
||||
void ReadEvents();
|
||||
void ClearScreen();
|
||||
//void MainLoop();
|
||||
bool IsMainLoopActive();
|
||||
|
||||
private:
|
||||
|
||||
int mWidth = 320; // virtual display width
|
||||
int mHeight = 200; // virtual display height
|
||||
int mPixelSizeX = 2; // virtual pixel width
|
||||
int mPixelSizeY = 2; // virtual pixel height
|
||||
int mWinPosX = 0; // SDL window position coordinate X
|
||||
int mWinPosY = 0; // SDL window position coordinate Y
|
||||
int mBgRgbR = 0; // bg color, RGB red intensity
|
||||
int mBgRgbG = 0; // bg color, RGB green intensity
|
||||
int mBgRgbB = 0; // bg color, RGB blue intensity
|
||||
int mFgRgbR = 0xFF; // fg color, RGB red intensity
|
||||
int mFgRgbG = 0xFF; // fg color, RGB green intensity
|
||||
int mFgRgbB = 0xFF; // fg color, RGB blue intensity
|
||||
SDL_Window *mpWindow = NULL;
|
||||
SDL_Surface *mpSurface = NULL;
|
||||
SDL_Renderer *mpRenderer = NULL;
|
||||
thread mMainLoopThread;
|
||||
|
||||
void Initialize();
|
||||
void UpdateSurface();
|
||||
void Clear();
|
||||
void GetDesktopResolution(int& horizontal, int& vertical);
|
||||
void DrawLine(int x1, int y1, int x2, int y2, bool draworerase);
|
||||
void RenderPixel(int x, int y, bool set);
|
||||
|
||||
}; // class GraphDisp
|
||||
|
||||
} // namespace MKBasic
|
||||
|
||||
#endif
|
663
MemMapDev.cpp
Normal file
663
MemMapDev.cpp
Normal file
@ -0,0 +1,663 @@
|
||||
|
||||
#include "MemMapDev.h"
|
||||
#include "Memory.h"
|
||||
#include "MKGenException.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#if defined(WINDOWS)
|
||||
#include <conio.h>
|
||||
#endif
|
||||
|
||||
//#define DBG 1
|
||||
#if defined (DBG)
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
#endif
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: MemMapDev()
|
||||
* Purpose: Class constructor.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
MemMapDev::MemMapDev()
|
||||
{
|
||||
mpMem = NULL;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: MemMapDev()
|
||||
* Purpose: Custom class constructor.
|
||||
* Arguments: pmem - Pointer to Memory, pointer to Memory object.
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
MemMapDev::MemMapDev(Memory *pmem)
|
||||
{
|
||||
mpMem = pmem;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
MemMapDev::~MemMapDev()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: Initialize()
|
||||
* Purpose: Initialize class and all supported devices.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void MemMapDev::Initialize()
|
||||
{
|
||||
mInBufDataBegin = mInBufDataEnd = 0;
|
||||
mOutBufDataBegin = mOutBufDataEnd = 0;
|
||||
mIOEcho = false;
|
||||
mCharIOAddr = CHARIO_ADDR;
|
||||
mGraphDispAddr = GRDISP_ADDR;
|
||||
mpGraphDisp = NULL;
|
||||
AddrRange addr_range(CHARIO_ADDR, CHARIO_ADDR+1);
|
||||
DevPar dev_par("echo", "false");
|
||||
MemAddrRanges addr_ranges_chario;
|
||||
DevParams dev_params_chario;
|
||||
addr_ranges_chario.push_back(addr_range);
|
||||
dev_params_chario.push_back(dev_par);
|
||||
Device dev_chario(DEVNUM_CHARIO,
|
||||
"Character I/O",
|
||||
addr_ranges_chario,
|
||||
&MemMapDev::CharIODevice_Read,
|
||||
&MemMapDev::CharIODevice_Write,
|
||||
dev_params_chario);
|
||||
mDevices.push_back(dev_chario);
|
||||
|
||||
addr_range.start_addr = GRDISP_ADDR;
|
||||
addr_range.end_addr = GRDISP_ADDR + GRAPHDEVREG_END - 1;
|
||||
dev_par.name = "nil";
|
||||
dev_par.value = "nil";
|
||||
MemAddrRanges addr_ranges_grdisp;
|
||||
DevParams dev_params_grdisp;
|
||||
addr_ranges_grdisp.push_back(addr_range);
|
||||
dev_params_grdisp.push_back(dev_par);
|
||||
Device dev_grdisp(DEVNUM_GRDISP,
|
||||
"Graphics Display",
|
||||
addr_ranges_grdisp,
|
||||
&MemMapDev::GraphDispDevice_Read,
|
||||
&MemMapDev::GraphDispDevice_Write,
|
||||
dev_params_grdisp);
|
||||
mDevices.push_back(dev_grdisp);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetDevice()
|
||||
* Purpose: Get device by specified number.
|
||||
* Arguments: devnum - device number
|
||||
* Returns: Device structure value.
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
Device MemMapDev::GetDevice(int devnum)
|
||||
{
|
||||
Device ret;
|
||||
for (MemMappedDevices::iterator devit = mDevices.begin();
|
||||
devit != mDevices.end();
|
||||
++devit
|
||||
) {
|
||||
|
||||
Device dev = *devit;
|
||||
if (dev.num == devnum) {
|
||||
ret = dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: SetupDevice()
|
||||
* Purpose: Configure device memory ranges and parameters.
|
||||
* Arguments: devnum - device number
|
||||
* memranges - memory address ranges vector
|
||||
* params - parameters vector
|
||||
* Returns: integer, 0 if OK, error number otherwise
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int MemMapDev::SetupDevice(int devnum,
|
||||
MemAddrRanges memranges,
|
||||
DevParams params)
|
||||
{
|
||||
#if defined(DBG)
|
||||
cout << "DBG: MemMapDev::SetupDevice()" << endl;
|
||||
#endif
|
||||
int ret = 0;
|
||||
Device dev = GetDevice(devnum);
|
||||
if (dev.num >= 0) {
|
||||
dev.addr_ranges = memranges;
|
||||
dev.params = params;
|
||||
MemMappedDevices devices_new;
|
||||
for (MemMappedDevices::iterator it = mDevices.begin();
|
||||
it != mDevices.end();
|
||||
++it
|
||||
) {
|
||||
if ((*it).num != devnum) devices_new.push_back(*it);
|
||||
}
|
||||
devices_new.push_back(dev);
|
||||
mDevices = devices_new;
|
||||
// device specific post-processing
|
||||
if (DEVNUM_CHARIO == devnum) {
|
||||
MemAddrRanges::iterator it = memranges.begin();
|
||||
mCharIOAddr = (*it).start_addr;
|
||||
for (DevParams::iterator it = params.begin();
|
||||
it != params.end();
|
||||
++it
|
||||
) {
|
||||
string par_name = (*it).name;
|
||||
string par_val = (*it).value;
|
||||
if (0 == par_name.compare("echo")) {
|
||||
if (0 == par_val.compare("true")) mIOEcho = true;
|
||||
else mIOEcho = false;
|
||||
}
|
||||
}
|
||||
#if defined(DBG)
|
||||
cout << "DBG: MemMapDev::SetupDevice() mCharIOAddr = $" << hex << mCharIOAddr << endl;
|
||||
cout << "DBG: MemMapDev::SetupDevice() mIOEcho = " << (mIOEcho?"true":"false") << endl;
|
||||
#endif
|
||||
} else if (DEVNUM_GRDISP == devnum) {
|
||||
MemAddrRanges::iterator it = memranges.begin();
|
||||
mGraphDispAddr = (*it).start_addr;
|
||||
}
|
||||
// finished device specific post-processing
|
||||
} else {
|
||||
ret++; // error
|
||||
#if defined(DBG)
|
||||
cout << "DBG: MemMapDev::SetupDevice() ERROR!" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(DBG)
|
||||
while (!getchar());
|
||||
cout << "Press [ENTER]...";
|
||||
getchar();
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(LINUX)
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
struct termios orig_termios;
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void reset_terminal_mode()
|
||||
{
|
||||
tcsetattr(0, TCSANOW, &orig_termios);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void MemMapDev::set_conio_terminal_mode()
|
||||
{
|
||||
struct termios new_termios;
|
||||
|
||||
/* take two copies - one for now, one for later */
|
||||
tcgetattr(0, &orig_termios);
|
||||
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
|
||||
|
||||
/* register cleanup handler, and set the new terminal mode */
|
||||
atexit(reset_terminal_mode);
|
||||
cfmakeraw(&new_termios);
|
||||
tcsetattr(0, TCSANOW, &new_termios);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int MemMapDev::kbhit()
|
||||
{
|
||||
struct timeval tv = { 0L, 0L };
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(0, &fds);
|
||||
return select(1, &fds, NULL, NULL, &tv);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int MemMapDev::getch()
|
||||
{
|
||||
int r;
|
||||
unsigned char c;
|
||||
if ((r = read(0, &c, sizeof(c))) < 0) {
|
||||
return r;
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ReadCharKb()
|
||||
* Purpose: If char I/O active, read character from console
|
||||
* (non-blocking) and put in an input FIFO buffer.
|
||||
* Arguments: nonblock - if true, works in non-blocking mode
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned char MemMapDev::ReadCharKb(bool nonblock)
|
||||
{
|
||||
unsigned char ret = 0;
|
||||
#if defined(LINUX)
|
||||
set_conio_terminal_mode();
|
||||
#endif
|
||||
static int c = ' ';
|
||||
if (mIOEcho && isprint(c)) putchar(c);
|
||||
fflush(stdout);
|
||||
if (!nonblock) while(!kbhit());
|
||||
else c = 0;
|
||||
c = getch();
|
||||
#if defined(LINUX)
|
||||
if (c == 3) { // capture CTRL-C in CONIO mode
|
||||
reset_terminal_mode();
|
||||
kill(getpid(),SIGINT);
|
||||
}
|
||||
#endif
|
||||
mCharIOBufIn[mInBufDataEnd] = c;
|
||||
mInBufDataEnd++;
|
||||
if (mInBufDataEnd >= CHARIO_BUF_SIZE) mInBufDataEnd = 0;
|
||||
ret = c;
|
||||
#if defined(LINUX)
|
||||
reset_terminal_mode();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharIn()
|
||||
* Purpose: Return character from the emulated character I/O FIFO
|
||||
* input buffer or -1 if FIFO empty or char I/O not active.
|
||||
* Arguments: n/a
|
||||
* Returns: character
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char MemMapDev::GetCharIn()
|
||||
{
|
||||
char ret = -1;
|
||||
if (mInBufDataEnd != mInBufDataBegin) {
|
||||
ret = mCharIOBufIn[mInBufDataBegin];
|
||||
mInBufDataBegin++;
|
||||
if (mInBufDataBegin >= CHARIO_BUF_SIZE) mInBufDataBegin = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharOut()
|
||||
* Purpose: Return character from the emulated character I/O FIFO
|
||||
* output buffer or -1 if FIFO empty or char I/O not
|
||||
* active.
|
||||
* Arguments: n/a
|
||||
* Returns: character
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char MemMapDev::GetCharOut()
|
||||
{
|
||||
char ret = -1;
|
||||
if (mOutBufDataEnd != mOutBufDataBegin) {
|
||||
ret = mCharIOBufOut[mOutBufDataBegin];
|
||||
mOutBufDataBegin++;
|
||||
if (mOutBufDataBegin >= CHARIO_BUF_SIZE) mOutBufDataBegin = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: PutCharIO()
|
||||
* Purpose: Put character in the output char I/O FIFO buffer.
|
||||
* Arguments: c - character
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void MemMapDev::PutCharIO(char c)
|
||||
{
|
||||
mCharIOBufOut[mOutBufDataEnd] = c;
|
||||
mOutBufDataEnd++;
|
||||
if (mOutBufDataEnd >= CHARIO_BUF_SIZE) mOutBufDataEnd = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: CharIODevice_Write()
|
||||
* Purpose: Write value to Char I/O device register.
|
||||
* Arguments: addr - address, val - value (character)
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void MemMapDev::CharIODevice_Write(int addr, int val)
|
||||
{
|
||||
if ((unsigned int)addr == mCharIOAddr) {
|
||||
PutCharIO ((char) val);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: CharIODevice_Read()
|
||||
* Purpose: Read value from Char I/O device register.
|
||||
* Arguments: addr - address
|
||||
* Returns: value
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int MemMapDev::CharIODevice_Read(int addr)
|
||||
{
|
||||
int ret = 0;
|
||||
if ((unsigned int)addr == mCharIOAddr) {
|
||||
ret = ReadCharKb(false); // blocking mode input
|
||||
} else if ((unsigned int)addr == mCharIOAddr+1) {
|
||||
ret = ReadCharKb(true); // non-blocking mode input
|
||||
}
|
||||
mpMem->Poke8bitImg((unsigned short)addr, (unsigned char)ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharIOAddr()
|
||||
* Purpose: Returns current address of basic character I/O area.
|
||||
* Arguments: n/a
|
||||
* Returns: address of I/O area
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short MemMapDev::GetCharIOAddr()
|
||||
{
|
||||
return mCharIOAddr;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharIOEchoOn()
|
||||
* Purpose: Returns current status of char I/O local echo flag.
|
||||
* Arguments: n/a
|
||||
* Returns: true if local echo is enabled, false if disabled
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
bool MemMapDev::GetCharIOEchoOn()
|
||||
{
|
||||
return mIOEcho;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetGraphDispAddrBase()
|
||||
* Purpose: Return base address of graphics display device.
|
||||
* Arguments: n/a
|
||||
* Returns: unsigned short - address ($0000 - $FFFF)
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short MemMapDev::GetGraphDispAddrBase()
|
||||
{
|
||||
return mGraphDispAddr;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GraphDispDevice_Read()
|
||||
* Purpose: Read from specified graphics display device register.
|
||||
* Arguments: int - address of the register in memory.
|
||||
* Returns: int - read value.
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int MemMapDev::GraphDispDevice_Read(int addr)
|
||||
{
|
||||
// this device has no read registers, return 0
|
||||
GraphDisp_ReadEvents();
|
||||
GraphDisp_Update();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GraphDispDevice_Write()
|
||||
* Purpose: Write a value to specified graphics display device
|
||||
* register.
|
||||
* Arguments: addr - address of the register in memory
|
||||
* val - value to write
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void MemMapDev::GraphDispDevice_Write(int addr, int val)
|
||||
{
|
||||
if (NULL != mpGraphDisp) { // only if device is active
|
||||
if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_X) {
|
||||
// setup X coordinate of the pixel or beginning of line,
|
||||
// less significant part
|
||||
mGrDevRegs.mGraphDispLoX = (unsigned char)val;
|
||||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_X + 1) {
|
||||
// setup X coordinate of the pixel, more significant part
|
||||
mGrDevRegs.mGraphDispHiX = (unsigned char)val;
|
||||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_Y) {
|
||||
// setup Y coordinate of the pixel
|
||||
mGrDevRegs.mGraphDispY = (unsigned char)val;
|
||||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_PXCOL_R) {
|
||||
// setup pixel RGB color Red component
|
||||
mGrDevRegs.mGraphDispPixColR = (unsigned char)val;
|
||||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_PXCOL_G) {
|
||||
// setup pixel RGB color Green component
|
||||
mGrDevRegs.mGraphDispPixColG = (unsigned char)val;
|
||||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_PXCOL_B) {
|
||||
// setup pixel RGB color Blue component
|
||||
mGrDevRegs.mGraphDispPixColB = (unsigned char)val;
|
||||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_BGCOL_R) {
|
||||
// setup background RGB color Red component
|
||||
mGrDevRegs.mGraphDispBgColR = (unsigned char)val;
|
||||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_BGCOL_G) {
|
||||
// setup background RGB color Green component
|
||||
mGrDevRegs.mGraphDispBgColG = (unsigned char)val;
|
||||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_BGCOL_B) {
|
||||
// setup background RGB color Blue component
|
||||
mGrDevRegs.mGraphDispBgColB = (unsigned char)val;
|
||||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_X2) {
|
||||
// setup X coordinate of the end of line, less significant part
|
||||
mGrDevRegs.mGraphDispLoX2 = (unsigned char)val;
|
||||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_X2 + 1) {
|
||||
// setup X coordinate of the end of line, more significant part
|
||||
mGrDevRegs.mGraphDispHiX2 = (unsigned char)val;
|
||||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_Y2) {
|
||||
// setup Y coordinate of the end of line
|
||||
mGrDevRegs.mGraphDispY2 = (unsigned char)val;
|
||||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_CMD) {
|
||||
// execute command
|
||||
switch (val) {
|
||||
|
||||
case GRAPHDEVCMD_CLRSCR:
|
||||
mpGraphDisp->ClearScreen();
|
||||
break;
|
||||
|
||||
case GRAPHDEVCMD_SETPXL:
|
||||
mpGraphDisp->SetPixel(mGrDevRegs.mGraphDispLoX
|
||||
+ 256 * mGrDevRegs.mGraphDispHiX,
|
||||
mGrDevRegs.mGraphDispY);
|
||||
break;
|
||||
|
||||
case GRAPHDEVCMD_CLRPXL:
|
||||
mpGraphDisp->ErasePixel(mGrDevRegs.mGraphDispLoX
|
||||
+ 256 * mGrDevRegs.mGraphDispHiX,
|
||||
mGrDevRegs.mGraphDispY);
|
||||
break;
|
||||
|
||||
case GRAPHDEVCMD_SETBGC:
|
||||
mpGraphDisp->SetBgColor(mGrDevRegs.mGraphDispBgColR,
|
||||
mGrDevRegs.mGraphDispBgColG,
|
||||
mGrDevRegs.mGraphDispBgColB);
|
||||
break;
|
||||
|
||||
case GRAPHDEVCMD_SETFGC:
|
||||
mpGraphDisp->SetFgColor(mGrDevRegs.mGraphDispPixColR,
|
||||
mGrDevRegs.mGraphDispPixColG,
|
||||
mGrDevRegs.mGraphDispPixColB);
|
||||
break;
|
||||
|
||||
case GRAPHDEVCMD_DRAWLN:
|
||||
mpGraphDisp->DrawLine(mGrDevRegs.mGraphDispLoX
|
||||
+ 256 * mGrDevRegs.mGraphDispHiX,
|
||||
mGrDevRegs.mGraphDispY,
|
||||
mGrDevRegs.mGraphDispLoX2
|
||||
+ 256 * mGrDevRegs.mGraphDispHiX2,
|
||||
mGrDevRegs.mGraphDispY2);
|
||||
break;
|
||||
|
||||
case GRAPHDEVCMD_ERASLN:
|
||||
mpGraphDisp->EraseLine(mGrDevRegs.mGraphDispLoX
|
||||
+ 256 * mGrDevRegs.mGraphDispHiX,
|
||||
mGrDevRegs.mGraphDispY,
|
||||
mGrDevRegs.mGraphDispLoX2
|
||||
+ 256 * mGrDevRegs.mGraphDispHiX2,
|
||||
mGrDevRegs.mGraphDispY2);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
GraphDisp_ReadEvents();
|
||||
GraphDisp_Update();
|
||||
//mpGraphDisp->Update();
|
||||
} // if (NULL != mpGraphDisp)
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ActivateGraphDisp()
|
||||
* Purpose: Activate graphics display.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void MemMapDev::ActivateGraphDisp()
|
||||
{
|
||||
if (NULL == mpGraphDisp) {
|
||||
mpGraphDisp = new GraphDisp();
|
||||
if (NULL == mpGraphDisp)
|
||||
throw MKGenException("Out of memory while initializing Graphics Display Device");
|
||||
mGrDevRegs.mGraphDispBgColR = 0;
|
||||
mGrDevRegs.mGraphDispBgColG = 0;
|
||||
mGrDevRegs.mGraphDispBgColB = 0;
|
||||
mGrDevRegs.mGraphDispPixColR = 0xFF;
|
||||
mGrDevRegs.mGraphDispPixColG = 0xFF;
|
||||
mGrDevRegs.mGraphDispPixColB = 0xFF;
|
||||
mpGraphDisp->SetBgColor(mGrDevRegs.mGraphDispBgColR,
|
||||
mGrDevRegs.mGraphDispBgColG,
|
||||
mGrDevRegs.mGraphDispBgColB);
|
||||
mpGraphDisp->SetFgColor(mGrDevRegs.mGraphDispPixColR,
|
||||
mGrDevRegs.mGraphDispPixColG,
|
||||
mGrDevRegs.mGraphDispPixColB);
|
||||
GraphDisp_Update();
|
||||
//mpGraphDisp->Start(mpGraphDisp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: DeactivateGraphDisp()
|
||||
* Purpose: Inactivate graphics display.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void MemMapDev::DeactivateGraphDisp()
|
||||
{
|
||||
#if defined(DBG)
|
||||
if (false == mpGraphDisp->IsMainLoopActive()) {
|
||||
cout << "DBG: ERROR: Main Loop is already inactive in Graphics Display." << endl;
|
||||
}
|
||||
#endif
|
||||
//mpGraphDisp->Stop();
|
||||
if (NULL != mpGraphDisp) delete mpGraphDisp;
|
||||
mpGraphDisp = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void MemMapDev::GraphDisp_ReadEvents()
|
||||
{
|
||||
if (NULL != mpGraphDisp) mpGraphDisp->ReadEvents();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void MemMapDev::GraphDisp_Update()
|
||||
{
|
||||
if (NULL != mpGraphDisp) mpGraphDisp->Update();
|
||||
}
|
||||
|
||||
} // namespace MKBasic
|
236
MemMapDev.h
Normal file
236
MemMapDev.h
Normal file
@ -0,0 +1,236 @@
|
||||
#ifndef MEMMAPDEV_H
|
||||
#define MEMMAPDEV_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "system.h"
|
||||
//#include "Memory.h"
|
||||
#include "GraphDisp.h"
|
||||
|
||||
#if defined(LINUX)
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
// some default definitions
|
||||
#define CHARIO_ADDR 0xE000
|
||||
#define GRDISP_ADDR 0xE002
|
||||
#define CHARIO_BUF_SIZE 256
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
class Memory;
|
||||
|
||||
// Definitions for memory mapped devices handling.
|
||||
|
||||
// memory address ranges
|
||||
struct AddrRange {
|
||||
unsigned short start_addr, end_addr; // inclusive address range
|
||||
|
||||
AddrRange(unsigned short staddr, unsigned short endaddr)
|
||||
{
|
||||
start_addr = staddr;
|
||||
end_addr = endaddr;
|
||||
}
|
||||
};
|
||||
|
||||
// device parameters
|
||||
struct DevPar {
|
||||
string name, value;
|
||||
|
||||
DevPar(string n, string v)
|
||||
{
|
||||
name = n;
|
||||
value = v;
|
||||
}
|
||||
};
|
||||
|
||||
typedef vector<AddrRange> MemAddrRanges;
|
||||
typedef vector<DevPar> DevParams;
|
||||
|
||||
class MemMapDev;
|
||||
|
||||
// Class MemMapDev implements functionality of all
|
||||
// memory mapped devices.
|
||||
|
||||
// device register read, arguments: address
|
||||
typedef int (MemMapDev::*ReadFunPtr)(int);
|
||||
// device register write, arguments: address, value
|
||||
typedef void (MemMapDev::*WriteFunPtr)(int,int);
|
||||
|
||||
// Definition of device.
|
||||
struct Device {
|
||||
int num; // device number
|
||||
string name; // device name
|
||||
MemAddrRanges addr_ranges; // vector of memory address ranges for this device
|
||||
ReadFunPtr read_fun_ptr; // pointer to memory register read function
|
||||
WriteFunPtr write_fun_ptr; // pointer to memory register write function
|
||||
DevParams params; // list of device parameters
|
||||
|
||||
Device()
|
||||
{
|
||||
num = -1;
|
||||
}
|
||||
|
||||
Device(int dnum,
|
||||
string dname,
|
||||
MemAddrRanges addrranges,
|
||||
ReadFunPtr prdfun,
|
||||
WriteFunPtr pwrfun,
|
||||
DevParams parms)
|
||||
{
|
||||
num = dnum;
|
||||
name = dname;
|
||||
addr_ranges = addrranges;
|
||||
read_fun_ptr = prdfun;
|
||||
write_fun_ptr = pwrfun;
|
||||
params = parms;
|
||||
}
|
||||
};
|
||||
|
||||
typedef vector<Device> MemMappedDevices;
|
||||
|
||||
enum DevNums {
|
||||
DEVNUM_CHARIO = 0, // character I/O device
|
||||
DEVNUM_GRDISP = 1, // raster graphics display device
|
||||
};
|
||||
|
||||
/*
|
||||
* NOTE regarding device address ranges.
|
||||
*
|
||||
* The emulated device is a model of a electronic device that is connected
|
||||
* to the CPU-s bus (address, memory, control signals).
|
||||
* Depending on the device, the memory address range maybe as simple as
|
||||
* a single memory address or it may contain multiple memory addresses
|
||||
* or ranges of addresses positioned at various non-contiguous places.
|
||||
* The functions of these addresses are handled internally by the MemMapDev
|
||||
* class. The client (user) initializing the device, if providing custom
|
||||
* memory ranges must know more details about device in order to provide
|
||||
* accurate data. E.g.: if device requires a single address as an entry
|
||||
* point, just one memory range is needed with start_addr == end_addr.
|
||||
* If device is more complicated and requires range of addresses to access
|
||||
* its control registers, another range of addresses to map memory access
|
||||
* etc., device should be setup accordingly by calling SetupDevice method
|
||||
* with proper arguments. Device setup is not mandatory, each device that
|
||||
* is mainained is initialized wit default parameters.
|
||||
*/
|
||||
|
||||
// offsets of raster graphics device registers
|
||||
enum GraphDevRegs {
|
||||
GRAPHDEVREG_X = 0,
|
||||
GRAPHDEVREG_Y = 2,
|
||||
GRAPHDEVREG_PXCOL_R = 3,
|
||||
GRAPHDEVREG_PXCOL_G = 4,
|
||||
GRAPHDEVREG_PXCOL_B = 5,
|
||||
GRAPHDEVREG_BGCOL_R = 6,
|
||||
GRAPHDEVREG_BGCOL_G = 7,
|
||||
GRAPHDEVREG_BGCOL_B = 8,
|
||||
GRAPHDEVREG_CMD = 9,
|
||||
GRAPHDEVREG_X2 = 10,
|
||||
GRAPHDEVREG_Y2 = 12,
|
||||
//---------------------------
|
||||
GRAPHDEVREG_END
|
||||
};
|
||||
|
||||
// graphics display commands
|
||||
enum GraphDevCmds {
|
||||
GRAPHDEVCMD_CLRSCR = 0,
|
||||
GRAPHDEVCMD_SETPXL = 1,
|
||||
GRAPHDEVCMD_CLRPXL = 2,
|
||||
GRAPHDEVCMD_SETBGC = 3,
|
||||
GRAPHDEVCMD_SETFGC = 4,
|
||||
GRAPHDEVCMD_DRAWLN = 5,
|
||||
GRAPHDEVCMD_ERASLN = 6
|
||||
};
|
||||
|
||||
struct GraphDeviceRegs {
|
||||
unsigned char mGraphDispLoX;
|
||||
unsigned char mGraphDispHiX;
|
||||
unsigned char mGraphDispY;
|
||||
unsigned char mGraphDispLoX2;
|
||||
unsigned char mGraphDispHiX2;
|
||||
unsigned char mGraphDispY2;
|
||||
unsigned char mGraphDispPixColR;
|
||||
unsigned char mGraphDispPixColG;
|
||||
unsigned char mGraphDispPixColB;
|
||||
unsigned char mGraphDispBgColR;
|
||||
unsigned char mGraphDispBgColG;
|
||||
unsigned char mGraphDispBgColB;
|
||||
};
|
||||
|
||||
// Functionality of memory mapped devices
|
||||
class MemMapDev {
|
||||
|
||||
public:
|
||||
|
||||
MemMapDev();
|
||||
MemMapDev(Memory *pmem);
|
||||
~MemMapDev();
|
||||
|
||||
Device GetDevice(int devnum);
|
||||
int SetupDevice(int devnum, MemAddrRanges memranges, DevParams params);
|
||||
|
||||
char GetCharIn();
|
||||
char GetCharOut();
|
||||
unsigned short GetCharIOAddr();
|
||||
bool GetCharIOEchoOn();
|
||||
|
||||
int CharIODevice_Read(int addr);
|
||||
void CharIODevice_Write(int addr, int val);
|
||||
|
||||
unsigned short GetGraphDispAddrBase();
|
||||
void ActivateGraphDisp();
|
||||
void DeactivateGraphDisp();
|
||||
|
||||
int GraphDispDevice_Read(int addr);
|
||||
void GraphDispDevice_Write(int addr, int val);
|
||||
|
||||
void GraphDisp_ReadEvents();
|
||||
void GraphDisp_Update();
|
||||
|
||||
private:
|
||||
|
||||
Memory *mpMem; // pointer to memory object
|
||||
MemMappedDevices mDevices;
|
||||
char mCharIOBufIn[CHARIO_BUF_SIZE];
|
||||
char mCharIOBufOut[CHARIO_BUF_SIZE];
|
||||
unsigned int mInBufDataBegin;
|
||||
unsigned int mInBufDataEnd;
|
||||
unsigned int mOutBufDataBegin;
|
||||
unsigned int mOutBufDataEnd;
|
||||
unsigned int mCharIOAddr;
|
||||
bool mIOEcho;
|
||||
unsigned int mGraphDispAddr;
|
||||
GraphDisp *mpGraphDisp; // pointer to Graphics Device object
|
||||
GraphDeviceRegs mGrDevRegs; // graphics display device registers
|
||||
|
||||
void Initialize();
|
||||
|
||||
unsigned char ReadCharKb(bool nonblock);
|
||||
void PutCharIO(char c);
|
||||
|
||||
#if defined(LINUX)
|
||||
|
||||
void set_conio_terminal_mode();
|
||||
int kbhit();
|
||||
int getch();
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
} // namespace MKBasic
|
||||
|
||||
#endif // MEMMAPDEV_H
|
540
Memory.cpp
540
Memory.cpp
@ -1,12 +1,6 @@
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#if defined(WINDOWS)
|
||||
#include <conio.h>
|
||||
#endif
|
||||
|
||||
//#define DBG 1
|
||||
#if defined (DBG)
|
||||
#include <iostream>
|
||||
@ -47,6 +41,7 @@ Memory::Memory()
|
||||
*/
|
||||
Memory::~Memory()
|
||||
{
|
||||
if (NULL != mpMemMapDev) delete mpMemMapDev;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -66,92 +61,13 @@ void Memory::Initialize()
|
||||
mCharIOAddr = CHARIO_ADDR;
|
||||
mCharIOActive = false;
|
||||
mIOEcho = false;
|
||||
mInBufDataBegin = mInBufDataEnd = 0;
|
||||
mOutBufDataBegin = mOutBufDataEnd = 0;
|
||||
mROMBegin = ROM_BEGIN;
|
||||
mROMEnd = ROM_END;
|
||||
mROMActive = false;
|
||||
mpMemMapDev = new MemMapDev(this);
|
||||
mGraphDispActive = false;
|
||||
}
|
||||
|
||||
#if defined(LINUX)
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
struct termios orig_termios;
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void reset_terminal_mode()
|
||||
{
|
||||
tcsetattr(0, TCSANOW, &orig_termios);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::set_conio_terminal_mode()
|
||||
{
|
||||
struct termios new_termios;
|
||||
|
||||
/* take two copies - one for now, one for later */
|
||||
tcgetattr(0, &orig_termios);
|
||||
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
|
||||
|
||||
/* register cleanup handler, and set the new terminal mode */
|
||||
atexit(reset_terminal_mode);
|
||||
cfmakeraw(&new_termios);
|
||||
tcsetattr(0, TCSANOW, &new_termios);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int Memory::kbhit()
|
||||
{
|
||||
struct timeval tv = { 0L, 0L };
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(0, &fds);
|
||||
return select(1, &fds, NULL, NULL, &tv);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int Memory::getch()
|
||||
{
|
||||
int r;
|
||||
unsigned char c;
|
||||
if ((r = read(0, &c, sizeof(c))) < 0) {
|
||||
return r;
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
@ -210,141 +126,115 @@ void Memory::EnableROM(unsigned short start, unsigned short end)
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ReadCharKb()
|
||||
* Purpose: If char I/O active, read character from console
|
||||
* (non-blocking) and put in an input FIFO buffer.
|
||||
* Arguments: nonblock - if true, works in non-blocking mode
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned char Memory::ReadCharKb(bool nonblock)
|
||||
{
|
||||
unsigned char ret = 0;
|
||||
if (mCharIOActive) {
|
||||
#if defined(LINUX)
|
||||
set_conio_terminal_mode();
|
||||
#endif
|
||||
static int c = ' ';
|
||||
if (mIOEcho && isprint(c)) putchar(c);
|
||||
fflush(stdout);
|
||||
if (!nonblock) while(!kbhit());
|
||||
else c = 0;
|
||||
c = getch();
|
||||
#if defined(LINUX)
|
||||
if (c == 3) { // capture CTRL-C in CONIO mode
|
||||
reset_terminal_mode();
|
||||
kill(getpid(),SIGINT);
|
||||
}
|
||||
#endif
|
||||
mCharIOBufIn[mInBufDataEnd] = c;
|
||||
mInBufDataEnd++;
|
||||
if (mInBufDataEnd >= CHARIO_BUF_SIZE) mInBufDataEnd = 0;
|
||||
ret = c;
|
||||
#if defined(LINUX)
|
||||
reset_terminal_mode();
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharIn()
|
||||
* Purpose: Return character from the emulated character I/O FIFO
|
||||
* input buffer or -1 if FIFO empty or char I/O not active.
|
||||
* Arguments: n/a
|
||||
* Returns: character
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char Memory::GetCharIn()
|
||||
{
|
||||
char ret = -1;
|
||||
if (mCharIOActive) {
|
||||
if (mInBufDataEnd != mInBufDataBegin) {
|
||||
ret = mCharIOBufIn[mInBufDataBegin];
|
||||
mInBufDataBegin++;
|
||||
if (mInBufDataBegin >= CHARIO_BUF_SIZE) mInBufDataBegin = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharOut()
|
||||
* Purpose: Return character from the emulated character I/O FIFO
|
||||
* output buffer or -1 if FIFO empty or char I/O not
|
||||
* active.
|
||||
* Arguments: n/a
|
||||
* Returns: character
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char Memory::GetCharOut()
|
||||
{
|
||||
char ret = -1;
|
||||
if (mCharIOActive) {
|
||||
if (mOutBufDataEnd != mOutBufDataBegin) {
|
||||
ret = mCharIOBufOut[mOutBufDataBegin];
|
||||
mOutBufDataBegin++;
|
||||
if (mOutBufDataBegin >= CHARIO_BUF_SIZE) mOutBufDataBegin = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: PutCharIO()
|
||||
* Purpose: Put character in the output char I/O FIFO buffer.
|
||||
* Arguments: c - character
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::PutCharIO(char c)
|
||||
{
|
||||
if (mCharIOActive) {
|
||||
mCharIOBufOut[mOutBufDataEnd] = c;
|
||||
mOutBufDataEnd++;
|
||||
if (mOutBufDataEnd >= CHARIO_BUF_SIZE) mOutBufDataEnd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
* Method: Peek8bit()
|
||||
* Purpose: Get/read 8-bit value from memory. If the memory address
|
||||
* is within the range of any active devices, call
|
||||
* handling method for this device.
|
||||
* Arguments: addr - memory address
|
||||
* Returns: unsigned char value read from specified memory address
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned char Memory::Peek8bit(unsigned short addr)
|
||||
{
|
||||
// if memory address is in range of any active memory mapped
|
||||
// devices, call corresponding device handling function
|
||||
bool cont_loop = true;
|
||||
for (vector<int>::iterator it = mActiveDevNumVec.begin();
|
||||
it != mActiveDevNumVec.end() && cont_loop;
|
||||
++it
|
||||
) {
|
||||
|
||||
Device dev = mpMemMapDev->GetDevice(*it);
|
||||
if (dev.num >= 0) {
|
||||
for (MemAddrRanges::iterator memrangeit = dev.addr_ranges.begin();
|
||||
memrangeit != dev.addr_ranges.end();
|
||||
++memrangeit
|
||||
) {
|
||||
AddrRange addr_range = *memrangeit;
|
||||
if (addr >= addr_range.start_addr && addr <= addr_range.end_addr) {
|
||||
ReadFunPtr pfun = dev.read_fun_ptr;
|
||||
if (pfun != NULL) {
|
||||
cont_loop = false;
|
||||
(mpMemMapDev->*pfun)((int)addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mDispOp = (DEVNUM_GRDISP == dev.num);
|
||||
}
|
||||
/*
|
||||
if (mCharIOActive && addr == mCharIOAddr) {
|
||||
m8bitMem[addr] = ReadCharKb(false); // blocking mode input
|
||||
} else if (mCharIOActive && addr == mCharIOAddr+1) {
|
||||
m8bitMem[addr] = ReadCharKb(true); // non-blocking mode input
|
||||
}
|
||||
}*/
|
||||
|
||||
return m8bitMem[addr];
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
* Method: Peek8bitImg()
|
||||
* Purpose: Get/read 8-bit value from memory image only.
|
||||
* Memory mapped devices are not affected.
|
||||
* Arguments: addr - memory address
|
||||
* Returns: unsigned char value read from specified memory address
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned char Memory::Peek8bitImg(unsigned short addr)
|
||||
{
|
||||
return m8bitMem[addr];
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: Peek16bit()
|
||||
* Purpose: Get/read 16-bit value from memory. If the memory address
|
||||
* is within the range of any active devices, call
|
||||
* handling method for this device.
|
||||
* Arguments: addr - memory address
|
||||
* Returns: unsigned short value read secified from memory address
|
||||
* and next memory address (16-bit, little endian)
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short Memory::Peek16bit(unsigned short addr)
|
||||
{
|
||||
unsigned short ret = 0;
|
||||
|
||||
// if memory address is in range of any active memory mapped
|
||||
// devices, call corresponding device handling function
|
||||
bool cont_loop = true;
|
||||
for (vector<int>::iterator it = mActiveDevNumVec.begin();
|
||||
it != mActiveDevNumVec.end() && cont_loop;
|
||||
++it
|
||||
) {
|
||||
Device dev = mpMemMapDev->GetDevice(*it);
|
||||
if (dev.num >= 0) {
|
||||
for (MemAddrRanges::iterator memrangeit = dev.addr_ranges.begin();
|
||||
memrangeit != dev.addr_ranges.end();
|
||||
++memrangeit
|
||||
) {
|
||||
AddrRange addr_range = *memrangeit;
|
||||
if (addr >= addr_range.start_addr && addr <= addr_range.end_addr) {
|
||||
ReadFunPtr pfun = dev.read_fun_ptr;
|
||||
if (pfun != NULL) {
|
||||
cont_loop = false;
|
||||
(mpMemMapDev->*pfun)((int)addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mDispOp = (DEVNUM_GRDISP == dev.num);
|
||||
}
|
||||
|
||||
/*
|
||||
if (mCharIOActive && addr == mCharIOAddr) {
|
||||
m8bitMem[addr] = ReadCharKb(false); // blocking mode input
|
||||
} else if (mCharIOActive && addr == mCharIOAddr+1) {
|
||||
m8bitMem[addr] = ReadCharKb(true); // non-blocking mode input
|
||||
}
|
||||
}*/
|
||||
|
||||
ret = m8bitMem[addr++];
|
||||
ret += m8bitMem[addr] * 256;
|
||||
@ -356,8 +246,8 @@ unsigned short Memory::Peek16bit(unsigned short addr)
|
||||
*--------------------------------------------------------------------
|
||||
* Method: Poke8bit()
|
||||
* Purpose: Write byte to specified memory location.
|
||||
* If the memory location is mapped to character I/O,
|
||||
* write value to output buffer and memory location.
|
||||
* If the memory location is mapped to an active device,
|
||||
* call corresponding handling function.
|
||||
* If the memory location is protected (ROM), do not
|
||||
* write the value.
|
||||
* Arguments: addr - (0x0000..0xffff) memory address,
|
||||
@ -367,13 +257,56 @@ unsigned short Memory::Peek16bit(unsigned short addr)
|
||||
*/
|
||||
void Memory::Poke8bit(unsigned short addr, unsigned char val)
|
||||
{
|
||||
// if memory address is in range of any active memory mapped
|
||||
// devices, call corresponding device handling function
|
||||
bool cont_loop = true;
|
||||
for (vector<int>::iterator it = mActiveDevNumVec.begin();
|
||||
it != mActiveDevNumVec.end() && cont_loop;
|
||||
++it
|
||||
) {
|
||||
Device dev = mpMemMapDev->GetDevice(*it);
|
||||
if (dev.num >= 0) {
|
||||
for (MemAddrRanges::iterator memrangeit = dev.addr_ranges.begin();
|
||||
memrangeit != dev.addr_ranges.end();
|
||||
++memrangeit
|
||||
) {
|
||||
AddrRange addr_range = *memrangeit;
|
||||
if (addr >= addr_range.start_addr && addr <= addr_range.end_addr) {
|
||||
WriteFunPtr pfun = dev.write_fun_ptr;
|
||||
if (pfun != NULL) {
|
||||
cont_loop = false;
|
||||
(mpMemMapDev->*pfun)((int)addr,(int)val);
|
||||
m8bitMem[addr] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mDispOp = (DEVNUM_GRDISP == dev.num);
|
||||
}
|
||||
/*
|
||||
if (mCharIOActive && addr == mCharIOAddr)
|
||||
PutCharIO(val);
|
||||
*/
|
||||
if (!mROMActive || (addr < mROMBegin || addr > mROMEnd)) {
|
||||
m8bitMem[addr] = val;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: Poke8bitImg()
|
||||
* Purpose: Write byte to specified memory location.
|
||||
* Arguments: addr - (0x0000..0xffff) memory address,
|
||||
* val - value (0x00..0xff)
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::Poke8bitImg(unsigned short addr, unsigned char val)
|
||||
{
|
||||
m8bitMem[addr] = val;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: SetCharIO()
|
||||
@ -386,8 +319,20 @@ void Memory::Poke8bit(unsigned short addr, unsigned char val)
|
||||
void Memory::SetCharIO(unsigned short addr, bool echo)
|
||||
{
|
||||
mCharIOAddr = addr;
|
||||
mCharIOActive = true;
|
||||
//mCharIOActive = true;
|
||||
mIOEcho = echo;
|
||||
|
||||
AddrRange addr_range(addr, addr+1);
|
||||
MemAddrRanges memaddr_ranges;
|
||||
DevPar dev_par("echo",(echo?"true":"false"));
|
||||
DevParams dev_params;
|
||||
|
||||
dev_params.push_back(dev_par);
|
||||
memaddr_ranges.push_back(addr_range);
|
||||
|
||||
if (false == mCharIOActive) AddDevice(DEVNUM_CHARIO);
|
||||
mCharIOActive = true;
|
||||
SetupDevice(DEVNUM_CHARIO, memaddr_ranges, dev_params);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -401,6 +346,7 @@ void Memory::SetCharIO(unsigned short addr, bool echo)
|
||||
void Memory::DisableCharIO()
|
||||
{
|
||||
mCharIOActive = false;
|
||||
DeleteDevice(DEVNUM_CHARIO);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -416,6 +362,58 @@ unsigned short Memory::GetCharIOAddr()
|
||||
return mCharIOAddr;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: SetGraphDisp()
|
||||
* Purpose: Setup and activate graphics display device.
|
||||
* Arguments: addr - base address of display device
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::SetGraphDisp(unsigned short addr)
|
||||
{
|
||||
AddrRange addr_range(addr, addr + GRAPHDEVREG_END - 1);
|
||||
MemAddrRanges memaddr_ranges;
|
||||
DevPar dev_par("nil","nil");
|
||||
DevParams dev_params;
|
||||
|
||||
dev_params.push_back(dev_par);
|
||||
memaddr_ranges.push_back(addr_range);
|
||||
|
||||
if (false == mGraphDispActive) AddDevice(DEVNUM_GRDISP);
|
||||
mGraphDispActive = true;
|
||||
SetupDevice(DEVNUM_GRDISP, memaddr_ranges, dev_params);
|
||||
mpMemMapDev->ActivateGraphDisp();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: DisableGraphDisp()
|
||||
* Purpose: Inactivate graphics display device.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::DisableGraphDisp()
|
||||
{
|
||||
mGraphDispActive = false;
|
||||
mpMemMapDev->DeactivateGraphDisp();
|
||||
DeleteDevice(DEVNUM_GRDISP);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetGraphDispAddr()
|
||||
* Purpose: Return base address of graphics display device.
|
||||
* Arguments: n/a
|
||||
* Returns: unsigned short - address ($0000 - $FFFF)
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short Memory::GetGraphDispAddr()
|
||||
{
|
||||
return mpMemMapDev->GetGraphDispAddrBase();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
@ -455,4 +453,144 @@ bool Memory::IsROMEnabled()
|
||||
return mROMActive;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: AddDevice()
|
||||
* Purpose: Add device number to active devices list.
|
||||
* Arguments: devnum - device number
|
||||
* Returns: 0
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int Memory::AddDevice(int devnum)
|
||||
{
|
||||
mActiveDevNumVec.push_back(devnum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: DeleteDevice()
|
||||
* Purpose: Delete device number from active devices list.
|
||||
* Arguments: devnum - device number
|
||||
* Returns: 0
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int Memory::DeleteDevice(int devnum)
|
||||
{
|
||||
vector<int> active_new;
|
||||
|
||||
for (vector<int>::iterator it = mActiveDevNumVec.begin();
|
||||
it != mActiveDevNumVec.end();
|
||||
++it
|
||||
) {
|
||||
if (*it != devnum) {
|
||||
active_new.push_back(*it);
|
||||
}
|
||||
}
|
||||
mActiveDevNumVec.clear();
|
||||
mActiveDevNumVec = active_new;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: SetupDevice()
|
||||
* Purpose: Configure device address ranges and parameters.
|
||||
* Arguments: devnum - device number, must be on active dev. list
|
||||
* memranges - memory address ranges vector
|
||||
* params - parameters vector
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::SetupDevice(int devnum,
|
||||
MemAddrRanges memranges,
|
||||
DevParams params)
|
||||
{
|
||||
for (vector<int>::iterator it = mActiveDevNumVec.begin();
|
||||
it != mActiveDevNumVec.end();
|
||||
++it
|
||||
) {
|
||||
if (devnum == *it) {
|
||||
mpMemMapDev->SetupDevice(devnum, memranges, params);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (DEVNUM_CHARIO == devnum) {
|
||||
mCharIOAddr = mpMemMapDev->GetCharIOAddr();
|
||||
mIOEcho = mpMemMapDev->GetCharIOEchoOn();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharIn()
|
||||
* Purpose: Return character from the emulated character I/O FIFO
|
||||
* input buffer or -1 if FIFO empty or char I/O not active.
|
||||
* Arguments: n/a
|
||||
* Returns: character
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char Memory::GetCharIn()
|
||||
{
|
||||
return mpMemMapDev->GetCharIn();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetCharOut()
|
||||
* Purpose: Return character from the emulated character I/O FIFO
|
||||
* output buffer or -1 if FIFO empty or char I/O not
|
||||
* active.
|
||||
* Arguments: n/a
|
||||
* Returns: character
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
char Memory::GetCharOut()
|
||||
{
|
||||
return mpMemMapDev->GetCharOut();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::GraphDisp_ReadEvents()
|
||||
{
|
||||
mpMemMapDev->GraphDisp_ReadEvents();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void Memory::GraphDisp_Update()
|
||||
{
|
||||
mpMemMapDev->GraphDisp_Update();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GraphDispOp()
|
||||
* Purpose: Status of last operation being perf. on Graphics Display
|
||||
* or not.
|
||||
* Arguments: n/a
|
||||
* Returns: bool, true if last operation was performed on Graphics
|
||||
* Display device.
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
bool Memory::GraphDispOp()
|
||||
{
|
||||
return mDispOp;
|
||||
}
|
||||
|
||||
} // namespace MKBasic
|
||||
|
151
Memory.h
151
Memory.h
@ -1,77 +1,74 @@
|
||||
#ifndef MEMORY_H
|
||||
#define MEMORY_H
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#if defined(LINUX)
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
#define MAX_8BIT_ADDR 0xFFFF
|
||||
#define CHARIO_ADDR 0xE000
|
||||
#define CHARIO_BUF_SIZE 256
|
||||
#define ROM_BEGIN 0xD000
|
||||
#define ROM_END 0xDFFF
|
||||
#define MIN_ROM_BEGIN 0x0200
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
class Memory
|
||||
{
|
||||
public:
|
||||
|
||||
Memory();
|
||||
~Memory();
|
||||
|
||||
void Initialize();
|
||||
unsigned char Peek8bit(unsigned short addr);
|
||||
unsigned short Peek16bit(unsigned short addr);
|
||||
void Poke8bit(unsigned short addr, unsigned char val);
|
||||
void SetCharIO(unsigned short addr, bool echo);
|
||||
void DisableCharIO();
|
||||
unsigned short GetCharIOAddr();
|
||||
char GetCharIn();
|
||||
char GetCharOut();
|
||||
void EnableROM();
|
||||
void DisableROM();
|
||||
void SetROM(unsigned short start, unsigned short end);
|
||||
void EnableROM(unsigned short start, unsigned short end);
|
||||
unsigned short GetROMBegin();
|
||||
unsigned short GetROMEnd();
|
||||
bool IsROMEnabled();
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
|
||||
unsigned char m8bitMem[MAX_8BIT_ADDR+1];
|
||||
char mCharIOBufIn[CHARIO_BUF_SIZE];
|
||||
char mCharIOBufOut[CHARIO_BUF_SIZE];
|
||||
unsigned int mInBufDataBegin;
|
||||
unsigned int mInBufDataEnd;
|
||||
unsigned int mOutBufDataBegin;
|
||||
unsigned int mOutBufDataEnd;
|
||||
unsigned short mCharIOAddr;
|
||||
bool mCharIOActive;
|
||||
bool mIOEcho;
|
||||
unsigned short mROMBegin;
|
||||
unsigned short mROMEnd;
|
||||
bool mROMActive;
|
||||
|
||||
unsigned char ReadCharKb(bool nonblock);
|
||||
void PutCharIO(char c);
|
||||
|
||||
#if defined(LINUX)
|
||||
|
||||
void set_conio_terminal_mode();
|
||||
int kbhit();
|
||||
int getch();
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace MKBasic
|
||||
|
||||
#endif
|
||||
#ifndef MEMORY_H
|
||||
#define MEMORY_H
|
||||
|
||||
#include "system.h"
|
||||
#include "MemMapDev.h"
|
||||
|
||||
#define MAX_8BIT_ADDR 0xFFFF
|
||||
#define ROM_BEGIN 0xD000
|
||||
#define ROM_END 0xDFFF
|
||||
#define MIN_ROM_BEGIN 0x0200
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
class Memory
|
||||
{
|
||||
public:
|
||||
|
||||
Memory();
|
||||
~Memory();
|
||||
|
||||
void Initialize();
|
||||
unsigned char Peek8bit(unsigned short addr);
|
||||
unsigned char Peek8bitImg(unsigned short addr);
|
||||
unsigned short Peek16bit(unsigned short addr);
|
||||
void Poke8bit(unsigned short addr, unsigned char val); // write to memory and call memory mapped device handle
|
||||
void Poke8bitImg(unsigned short addr, unsigned char val); // write to memory image only
|
||||
void SetCharIO(unsigned short addr, bool echo);
|
||||
void DisableCharIO();
|
||||
unsigned short GetCharIOAddr();
|
||||
char GetCharIn();
|
||||
char GetCharOut();
|
||||
void EnableROM();
|
||||
void DisableROM();
|
||||
void SetROM(unsigned short start, unsigned short end);
|
||||
void EnableROM(unsigned short start, unsigned short end);
|
||||
unsigned short GetROMBegin();
|
||||
unsigned short GetROMEnd();
|
||||
bool IsROMEnabled();
|
||||
int AddDevice(int devnum);
|
||||
int DeleteDevice(int devnum);
|
||||
void SetupDevice(int devnum, MemAddrRanges memranges, DevParams params);
|
||||
|
||||
void SetGraphDisp(unsigned short addr);
|
||||
void DisableGraphDisp();
|
||||
unsigned short GetGraphDispAddr();
|
||||
void GraphDisp_ReadEvents();
|
||||
void GraphDisp_Update();
|
||||
bool GraphDispOp();
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
|
||||
unsigned char m8bitMem[MAX_8BIT_ADDR+1];
|
||||
unsigned short mCharIOAddr;
|
||||
bool mCharIOActive;
|
||||
bool mIOEcho;
|
||||
unsigned short mROMBegin;
|
||||
unsigned short mROMEnd;
|
||||
bool mROMActive;
|
||||
vector<int> mActiveDevNumVec; // active devices numbers
|
||||
MemMapDev *mpMemMapDev; // pointer to MemMapDev object
|
||||
bool mGraphDispActive;
|
||||
bool mDispOp;
|
||||
|
||||
unsigned char ReadCharKb(bool nonblock);
|
||||
void PutCharIO(char c);
|
||||
};
|
||||
|
||||
} // namespace MKBasic
|
||||
|
||||
#endif
|
||||
|
567
ProgrammersReferenceManual.txt
Normal file
567
ProgrammersReferenceManual.txt
Normal file
@ -0,0 +1,567 @@
|
||||
|
||||
|
||||
User Manual and Programmers Reference Manual for VM65.
|
||||
|
||||
by
|
||||
|
||||
Marek Karcz (C) 2016.
|
||||
|
||||
|
||||
|
||||
1. Introduction.
|
||||
|
||||
VM65 is a simulator which can be expanded to become an emulator of more
|
||||
complex computer system either a real one or completely abstract virtual
|
||||
machine.
|
||||
VM65 acronym stands for Virtual Machine 65 and is designed for MOS-6502
|
||||
simulation. In current form it allows to simulate a MOS-6502 CPU in
|
||||
MS Windows environment using MS-DOS command line UI.
|
||||
Software emulates all original MOS-6502 legal opcodes. The illegal opcodes
|
||||
are not emulated and are opened for expansion. It is up to programmer to
|
||||
implement illegal opcodes accurate emulation or replace them with extensions
|
||||
or traps. In current version they do nothing except to consume CPU cycles.
|
||||
The 6502 emulation is not purely abstract. The basic components of a real
|
||||
hardware CPU system are emulated: microprocessor, memory and memory mapped
|
||||
devices abstract layer - about which more in section 4.
|
||||
Using this software user will be able to load plain text memory definition
|
||||
file, Intel HEX format file, raw binary image file or binary snapshot image
|
||||
created with this software and execute or debug code within it in
|
||||
a continuous or in step-by-step mode.
|
||||
The file will be loaded and translated to the emulated memory space and
|
||||
optional emulation parameters included in the image will be interpreted and
|
||||
adequately translated into values of the corresponding internal flags.
|
||||
It is possible to automatically run program starting at specified address,
|
||||
perform automatic reset of the CPU after memory image upload or perform
|
||||
these actions from command line prompt of the debug console.
|
||||
|
||||
2. Command Line Interface and Debug Console.
|
||||
|
||||
Usage:
|
||||
|
||||
vm65 [-h] | [ramdeffile] [-b | -x] [-r]
|
||||
|
||||
|
||||
Where:
|
||||
|
||||
ramdeffile - RAM definition file name
|
||||
-b - specify input format as binary
|
||||
-x - specify input format as Intel HEX
|
||||
-r - after loading, perform CPU RESET
|
||||
-h - print this help screen
|
||||
|
||||
|
||||
When ran with no arguments, program will load default memory
|
||||
definition files: default.rom, default.ram and will enter the debug
|
||||
console menu.
|
||||
When ramdeffile argument is provided with no input format specified,
|
||||
program will attempt to automatically detect input format and load the
|
||||
memory definition from the file, set the flags and parameters depending
|
||||
on the contents of the memory definition file and enter the corresponding
|
||||
mode of operation as defined in that file.
|
||||
If input format is specified (-b|-x), program will load memory from the
|
||||
provided image file and enter the debug console menu.
|
||||
|
||||
To start program, navigate to the program directory in MS-DOS prompt console
|
||||
and type:
|
||||
|
||||
vm65
|
||||
|
||||
Above will run the emulator. Since no memory image is specified, program
|
||||
will attempt to load dummy.rom and dummy.ram files, which supposed to be
|
||||
plain text memory image definition files. The simplest you can create can
|
||||
look like this:
|
||||
|
||||
ORG
|
||||
$0000
|
||||
$00
|
||||
|
||||
NOTE: To see the full usage help for vm65, use flag -h:
|
||||
vm65 -h
|
||||
|
||||
Program loaded with no arguments will present user with following UI
|
||||
which will be referred to as Debug Console:
|
||||
|
||||
STOPPED at 0
|
||||
*-------------*-----------------------*----------*----------*
|
||||
| PC: $0000 | Acc: $00 (00000000) | X: $00 | Y: $00 |
|
||||
*-------------*-----------------------*----------*----------*
|
||||
| NV-BDIZC |
|
||||
| 00100000 | Last instr.:
|
||||
*-------------*
|
||||
|
||||
Stack: $ff
|
||||
[]
|
||||
|
||||
I/O status: disabled, at: $e000, local echo: OFF.
|
||||
Graphics status: disabled, at: $e002
|
||||
ROM: disabled. Range: $d000 - $dfff.
|
||||
------------------------------------+----------------------------------------
|
||||
C - continue, S - step | A - set address for next step
|
||||
G - go/cont. from new address | N - go number of steps, P - IRQ
|
||||
I - toggle char I/O emulation | X - execute from new address
|
||||
T - show I/O console | B - blank (clear) screen
|
||||
E - toggle I/O local echo | F - toggle registers animation
|
||||
J - set animation delay | M - dump memory, W - write memory
|
||||
K - toggle ROM emulation | R - show registers, Y - snapshot
|
||||
L - load memory image | O - display op-codes history
|
||||
D - disassemble code in memory | Q - quit, 0 - reset, H - help
|
||||
V - toggle graphics emulation |
|
||||
------------------------------------+----------------------------------------
|
||||
> _
|
||||
|
||||
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
|
||||
instructions/prompts to enter remaining parameters OR if user already knows
|
||||
the format of the command, all parameters can be entered in a single line.
|
||||
To see full help for above commands, issue command H in the prompt and press
|
||||
ENTER.
|
||||
|
||||
A quick overview of the some commands available in Debug Console:
|
||||
|
||||
* Code execution and debugging commands.
|
||||
|
||||
C - Continue
|
||||
|
||||
Execute code from current address after it was interrupted.
|
||||
|
||||
S - Step
|
||||
|
||||
Executes single opcode at current address.
|
||||
|
||||
A - Set address for next step
|
||||
|
||||
Sets current address to one provided is argument (or prompts for one).
|
||||
|
||||
G - Execute (continue) from address.
|
||||
|
||||
Asks for (if not provided as argument) address and then executes code.
|
||||
|
||||
N - Execute # of steps.
|
||||
|
||||
X - Execute from address.
|
||||
|
||||
F - Toggle registers animation.
|
||||
|
||||
When in multi-step debug mode (command: N), displaying registers
|
||||
can be suppressed or, when animation mode is enabled - they will
|
||||
be continuously displayed after each executed step.
|
||||
|
||||
J - Set registers animation delay.
|
||||
|
||||
Sets the time added at the end of each execution step in multi
|
||||
step mode (command: N). The default value is 250 ms. The change
|
||||
of this parameter will basically set the speed of multi-step code
|
||||
execution. Greater delay will slow down the execution but improve
|
||||
readability of the registers in Debug Console as they will not change
|
||||
as often as if this delay was smaller.
|
||||
|
||||
R - Show registers.
|
||||
|
||||
Displays current status of CPU registers, flags and stack.
|
||||
|
||||
O - Display op-codes history.
|
||||
|
||||
Show the history of last executed op-codes/instructions, full with
|
||||
disassembled mnemonic and argument.
|
||||
|
||||
D - Disassemble code in memory.
|
||||
|
||||
Attempt to disassemble code in specified address range and display
|
||||
the results (print) on the screen in symbolic form.
|
||||
|
||||
0 - Reset.
|
||||
|
||||
Run the processor initialization sequence, just like the real CPU
|
||||
when its RTS signal is set to LOW and HIGH again. CPU will disable
|
||||
interrupts, copy address from vector $FFFC to processors PC and will
|
||||
start executing code. Programmer must put initialization routine
|
||||
under address pointed by $FFFC vector, which will set the arithmetic
|
||||
mode, initialize stack, I/O devices and enable IRQ if needed before
|
||||
jumping to main loop. The reset routine disables trapping last RTS
|
||||
opcode if stack is empty, so the VM will never return from opcodes
|
||||
execution loop unless user interrupts with CTRL-C or CTRL-Break.
|
||||
|
||||
* Memory access commands.
|
||||
|
||||
M - Dump memory.
|
||||
|
||||
Dumps contents of memory, hexadecimal and ASCII formats.
|
||||
|
||||
W - Write memory.
|
||||
|
||||
Writes provided values to memory starting at specified address.
|
||||
|
||||
* I/O devices commands.
|
||||
|
||||
I - Toggle char I/O emulation.
|
||||
|
||||
Enables/disables basic character I/O emulation. When enabled, all writes
|
||||
to the specified memory address also writes a character code to
|
||||
to a virtual console. All reads from specified memory address
|
||||
are interpreted as console character input.
|
||||
The base address corresponds to blocking mode character I/O device,
|
||||
while base address + 1 corresponds to non-blocking character I/O device.
|
||||
The blocking device waits for the input character - code execution
|
||||
stops until user enters the keystroke, while in non-blocking mode
|
||||
the emulator takes the recently entered keystroke from the system
|
||||
keyboard buffer. That means that implementing the character input
|
||||
in 6502 assembly/machine code requires a loop reading from non-blocking
|
||||
address until the character code is different than 0, while in blocking
|
||||
mode it is enough to just read from I/O address. The emulator will only
|
||||
proceed when the key was pressed by user.
|
||||
|
||||
Examples of 6502 code:
|
||||
|
||||
- a 'waiting' character input implemented using non-blocking char I/O
|
||||
|
||||
readchar: LDA $E001
|
||||
BEQ readchar ; A = NULL, keep reading char I/O
|
||||
RTS ; A = char code
|
||||
|
||||
- a 'waiting' character inpute implemented using blocking char I/O
|
||||
|
||||
readchar: LDA $E000
|
||||
RTS ; A = char code
|
||||
|
||||
T - Show I/O console.
|
||||
|
||||
Displays/prints the contents of the virtual console screen.
|
||||
Note that in run mode (commands X, G or C), virtual screen is
|
||||
displayed automatically in real-time if I/O emulation is enabled.
|
||||
|
||||
V - Toggle graphics emulation.
|
||||
|
||||
Enables/disables basic raster (pixel) based RGB graphics display
|
||||
emulation.
|
||||
When enabled, window with graphics screen will open and several
|
||||
registers are available to control the device starting at provided
|
||||
base address. Detailed description of the device registers os provided
|
||||
later in this document.
|
||||
|
||||
The difference between 'G' and 'X' is that code executed with command 'G'
|
||||
will return to Debug Console when BRK or last RTS (empty stack) opcode is
|
||||
encountered, while code executed with 'X' goes to full CPU emulation mode
|
||||
where BRK will invoke proper interrupt routine as setup by IRQ vector in
|
||||
memory. The last RTS opcode (empty stack) will return to Debug
|
||||
Console in both cases unless such behavior is disabled (and this is only
|
||||
true in case of Reset function, see command '0').
|
||||
Entering keyboard interrupt codes (CTRL-C, CTRL-BREAK) will return to Debug
|
||||
Console.
|
||||
|
||||
NOTE: All addresses and memory values are to be entered in hexadecimal
|
||||
format.
|
||||
|
||||
3. Implementing the Virtual Machine.
|
||||
|
||||
The Virtual Machine (or Emulator) is implemented by the means of multiple
|
||||
layers of abstraction. The higher the layer, the less code it contains
|
||||
that is virtual hardware dependent.
|
||||
|
||||
On the highest level we have main.cpp which implements the UI and Debug
|
||||
Console. Programmer can replace this code with custom designed UI or GUI.
|
||||
|
||||
The definition of the emulated system layer begins in VMachine class
|
||||
(VMachine.h and VMachine.cpp) which provides the implementation of the
|
||||
entire emulated system. It is a template upon which programmer can expand.
|
||||
Several important methods are defined here that allow to execute the 6502
|
||||
code in many different modes:
|
||||
|
||||
Regs *Run();
|
||||
Regs *Run(unsigned short addr);
|
||||
Regs *Exec();
|
||||
Regs *Exec(unsigned short addr);
|
||||
Regs *Step();
|
||||
Regs *Step(unsigned short addr);
|
||||
void Reset();
|
||||
|
||||
Programmer should use this class to implement all the pieces of the
|
||||
emulated virtual computer system on the highest abstraction level.
|
||||
In current version the VMachine class initializes the basic system with CPU
|
||||
and memory at its core and character and raster graphics display devices.
|
||||
|
||||
Going one step down the abstraction layer are MKCPu and Memory classes.
|
||||
|
||||
The MKCpu class is the most important part of the emulator. It defines
|
||||
the whole system's architecture. Based on the type of the CPU device we
|
||||
know how to implement the rest of the core components by knowing the
|
||||
maximum addressable memory space, size of the data bus (8-bit, 16-bit) etc.
|
||||
The MKCpu class defines all the internal registers of the virtual CPU,
|
||||
the op-codes interpreter and its interface to outside components.
|
||||
The most important API methods are:
|
||||
|
||||
Regs *ExecOpcode(unsigned short memaddr);
|
||||
Regs *GetRegs();
|
||||
void SetRegs(Regs r);
|
||||
void Reset(); // reset CPU
|
||||
void Interrupt(); // Interrupt ReQuest (IRQ)
|
||||
|
||||
The Memory class (Memory.h and Memory.cpp) implements essential concept
|
||||
in any microprocessor based system, which is the memory. The assumption is
|
||||
that such a system requires some sort of program storage to be able to
|
||||
execute code. This is not entirely true in the real world, where we can
|
||||
make the CPU 'think' that it executes the real code with a tricky circuit
|
||||
and get away without any memory to make the CPU run. This however has
|
||||
limited usefulness (diagnostics, testing the basic operation of CPU) and
|
||||
has no use in the emulation domain where we have no need to test the CPU
|
||||
chip for basic operation on electrical level. The Memory class is also an
|
||||
important entry level for another concept of microprocessor based system:
|
||||
a memory mapped device. This brings us to the lower level of abstraction
|
||||
which is the memory mapped device class: MemMapDev.
|
||||
|
||||
Class MemMapDev is a core or hub for implementing all the devices that are
|
||||
connected to the virtual CPU via its memory address space. This is
|
||||
explained in more detail in chapter 4.
|
||||
|
||||
All remaining classes are the implementation of the actual devices on the
|
||||
lowest level of abstraction (from the emulator point of view) and other
|
||||
helper classes and methods.
|
||||
Examples of virtual devices emulation implementation are: Display and
|
||||
GraphDisp.
|
||||
|
||||
Programmer can add more code and more classes and integrate them into the
|
||||
emulator using this architecture. This is the starting point of the
|
||||
emulator of a real microprocessor based system or completely abstract
|
||||
virtual machine that has no hardware counterpart.
|
||||
|
||||
4. Memory Mapped Device Abstraction Layer/API.
|
||||
|
||||
In microprocessor based systems in majority of cases communication with
|
||||
peripheral devices is done via registers which in turn are located under
|
||||
specific memory addresses.
|
||||
Programming API responsible for modeling this functionality is implemented
|
||||
in Memory and MemMapDev classes. The Memory class implements access to
|
||||
specific memory locations and maintains the memory image.
|
||||
The MemMapDev class implements specific device address spaces and handling
|
||||
methods that are triggered when addresses of the device are accessed by the
|
||||
microprocessor.
|
||||
Programmers can expand the functionality of this emulator by adding
|
||||
necessary code emulating specific devices in MemMapDev and Memory classes
|
||||
implementation and header files. In current version, two basic devices are
|
||||
implemented:
|
||||
- character I/O and
|
||||
- raster (pixel based) graphics display.
|
||||
Character I/O device uses 2 memory locations, one for non-blocking I/O
|
||||
and one for blocking I/O. Writing to location causes character output, while
|
||||
reading from location waits for character input (blocking mode) or reads the
|
||||
character from keyboard buffer if available (non-blocking mode).
|
||||
The graphics display can be accessed by writing to multiple memory locations.
|
||||
|
||||
If we assume that GRDEVBASE is the base address of the Graphics Device, there
|
||||
are following registers:
|
||||
|
||||
Offset Register Description
|
||||
----------------------------------------------------------------------------
|
||||
0 GRAPHDEVREG_X_LO Least significant part of pixel's X (column)
|
||||
coordinate or begin of line coord. (0-255)
|
||||
1 GRAPHDEVREG_X_HI Most significant part of pixel's X (column)
|
||||
coordinate or begin of line coord. (0-1)
|
||||
2 GRAPHDEVREG_Y Pixel's Y (row) coordinate (0-199)
|
||||
3 GRAPHDEVREG_PXCOL_R Pixel's RGB color component - Red (0-255)
|
||||
4 GRAPHDEVREG_PXCOL_G Pixel's RGB color component - Green (0-255)
|
||||
5 GRAPHDEVREG_PXCOL_B Pixel's RGB color component - Blue (0-255)
|
||||
6 GRAPHDEVREG_BGCOL_R Backgr. RGB color component - Red (0-255)
|
||||
7 GRAPHDEVREG_BGCOL_G Backgr. RGB color component - Green (0-255)
|
||||
8 GRAPHDEVREG_BGCOL_B Backgr. RGB color component - Blue (0-255)
|
||||
9 GRAPHDEVREG_CMD Command code
|
||||
10 GRAPHDEVREG_X2_LO Least significant part of end of line's X
|
||||
coordinate
|
||||
11 GRAPHDEVREG_X2_HI Most significant part of end of line's X
|
||||
coordinate
|
||||
12 GRAPHDEVREG_Y2 End of line's Y (row) coordinate (0-199)
|
||||
|
||||
Writing values to above memory locations when Graphics Device is enabled
|
||||
allows to set the corresponding parameters of the device, while writing to
|
||||
command register executes corresponding command (performs action) per codes
|
||||
listed below:
|
||||
|
||||
Command code Command description
|
||||
----------------------------------------------------------------------------
|
||||
GRAPHDEVCMD_CLRSCR = 0 Clear screen
|
||||
GRAPHDEVCMD_SETPXL = 1 Set the pixel location to pixel color
|
||||
GRAPHDEVCMD_CLRPXL = 2 Clear the pixel location (set to bg color)
|
||||
GRAPHDEVCMD_SETBGC = 3 Set the background color
|
||||
GRAPHDEVCMD_SETFGC = 4 Set the foreground (pixel) color
|
||||
GRAPHDEVCMD_DRAWLN = 5 Draw line
|
||||
GRAPHDEVCMD_ERASLN = 6 Erase line
|
||||
|
||||
Reading from registers has no effect (returns 0).
|
||||
|
||||
Above method of interfacing GD requires no dedicated graphics memory space
|
||||
in VM's RAM. It is also simple to implement.
|
||||
The downside - slow performance (multiple memory writes to select/unselect
|
||||
a pixel or set color).
|
||||
I plan to add graphics frame buffer in the VM's RAM address space in future
|
||||
release.
|
||||
|
||||
Simple demo program written in EhBasic that shows how to drive the graphics
|
||||
screen:
|
||||
|
||||
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
|
||||
990 PRINT "DONE"
|
||||
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
|
||||
|
||||
4.1. Adding new device implementation.
|
||||
|
||||
MemMapDev.h and MemMapDev.cpp define the higher abstraction layer for memory
|
||||
mapped devices. Memory address range class: AddrRange and device class:
|
||||
Device are implemented to provide core facilities and class: MemMapDev for
|
||||
future expansion of the emulated system.
|
||||
|
||||
To add a new device to the memory mapped devices, programmer must:
|
||||
|
||||
* Implement the device behavior in a separate class.
|
||||
|
||||
See GraphDisp.h, GraphDisp.cpp as example.
|
||||
This is the actual device driver, code that defines how the device works.
|
||||
|
||||
Device driver class may define the raster graphics display functions,
|
||||
a real time clock chip functions, sound chip functions, Disk Drive
|
||||
functions etc.
|
||||
This code does not contain definitions related to how the device is mapped
|
||||
to the memory space of emulated computer system. That would be the
|
||||
responsibility of MemMapDev class, providing the middle layer of
|
||||
abstraction for connecting the device to the memory space of the CPU.
|
||||
|
||||
* Add necessary definitions, enumerators and methods to the MemMapDev.h
|
||||
and MemMapDev.cpp.
|
||||
|
||||
The minimal set should include methods to initialize the device and
|
||||
methods executed when memory mapped device registers are read from and
|
||||
written to.
|
||||
Other methods may be needed to perform device implementation specific
|
||||
functions.
|
||||
See example set of methods for simple raster graphics device:
|
||||
|
||||
unsigned short GetGraphDispAddrBase();
|
||||
void ActivateGraphDisp();
|
||||
void DeactivateGraphDisp();
|
||||
|
||||
int GraphDispDevice_Read(int addr);
|
||||
void GraphDispDevice_Write(int addr, int val);
|
||||
|
||||
void GraphDisp_ReadEvents();
|
||||
void GraphDisp_Update();
|
||||
|
||||
Important concept at the core of the memory mapped device is the method
|
||||
handling the action to be performed when certain memory register is being
|
||||
read from or written to. These are defined as function pointers in Device
|
||||
class: read_fun_ptr and write_fun_ptr.
|
||||
|
||||
struct Device {
|
||||
int num; // device number
|
||||
string name; // device name
|
||||
MemAddrRanges addr_ranges; // vector of memory address ranges for
|
||||
// this device
|
||||
ReadFunPtr read_fun_ptr; // pointer to memory register read
|
||||
// function
|
||||
WriteFunPtr write_fun_ptr; // pointer to memory register write
|
||||
// function
|
||||
DevParams params; // list of device parameters
|
||||
|
||||
[...]
|
||||
|
||||
Programmer implements these handler methods inside MemMapDev class.
|
||||
|
||||
Programmer should not forget to add the new device to the pool in the
|
||||
MemMapDev::Initialize() method which adds all implemented devices to the
|
||||
devices list with their corresponding default memory bases/ranges and
|
||||
parameters. These devices can be later re-defined with
|
||||
MemMapDev::SetupDevice() method.
|
||||
|
||||
typedef vector<Device> MemMappedDevices;
|
||||
|
||||
[...]
|
||||
|
||||
MemMappedDevices mDevices;
|
||||
|
||||
[...]
|
||||
|
||||
void MemMapDev::Initialize()
|
||||
{
|
||||
|
||||
[...]
|
||||
|
||||
Device dev_grdisp(DEVNUM_GRDISP,
|
||||
"Graphics Display",
|
||||
addr_ranges_grdisp,
|
||||
&MemMapDev::GraphDispDevice_Read,
|
||||
&MemMapDev::GraphDispDevice_Write,
|
||||
dev_params_grdisp);
|
||||
mDevices.push_back(dev_grdisp);
|
||||
|
||||
[...]
|
||||
|
||||
* Add necessary ports in Memory.h and Memory.cpp.
|
||||
|
||||
Memory class already implements the code handling memory mapped devices
|
||||
in all read/write memory methods. Some local methods and flags specific
|
||||
to the devices may be needed for implementation convenience.
|
||||
|
||||
Important relevant methods in Memory class:
|
||||
|
||||
int AddDevice(int devnum);
|
||||
int DeleteDevice(int devnum);
|
||||
void SetupDevice(int devnum, MemAddrRanges memranges, DevParams params);
|
||||
|
||||
are the API to be called from higher level abstraction layer. Some proxy
|
||||
methods can be implemented in Memory class for this purpose.
|
||||
See Memory::SetGraphDisp() and VMachine::SetGraphDisp() methods for
|
||||
an example. The SetGraphDisp() method in Memory class defines the memory
|
||||
mapped device on a lower level and calls SetupDevice().
|
202
ReadMe.txt
202
ReadMe.txt
@ -1,5 +1,6 @@
|
||||
|
||||
Project: MKBasic (VM6502).
|
||||
Project: MKBasic (a.k.a. VM6502, a.k.a. VM65, I just can't decide
|
||||
how to name it :-)).
|
||||
|
||||
Author: Copyright (C) Marek Karcz 2016. All rights reserved.
|
||||
Free for personal and non-commercial use.
|
||||
@ -15,21 +16,25 @@ MOS 6502 emulator, Virtual CPU/Machine and potentially retro-style 8-bit
|
||||
computer emulator.
|
||||
MOS-6502-compatible virtual computer featuring BASIC interpreter, machine code
|
||||
monitor, input/output device emulation etc.
|
||||
Program works in DOS/shell console (text mode) only.
|
||||
Main UI of the program works in DOS/shell console.
|
||||
Graphics display emulation requires SDL2.
|
||||
Makefile are included to build under Windows 32/64 (mingw compiler required)
|
||||
and under Linux Ubuntu or Ubuntu based.
|
||||
and under Linux Ubuntu or Ubuntu based distro.
|
||||
SDL2 library must be on your execution path in order to run program.
|
||||
|
||||
To build under Windows 32/64:
|
||||
|
||||
* Install MINGW64 under C:\mingw-w64\x86_64-5.3.0 folder.
|
||||
* Run mingw terminal.
|
||||
* Change current directory to that of this project.
|
||||
* Set environment variable SDLDIR.
|
||||
* Run: makeming.bat
|
||||
|
||||
To build under Linux:
|
||||
|
||||
* Make sure C++11 compliant version of GCC compiler is installed.
|
||||
* Change current directory to that of this project.
|
||||
* Set environment variable SDLDIR.
|
||||
* Run: make clean all
|
||||
|
||||
Program passed following tests:
|
||||
@ -63,12 +68,44 @@ Binary image is always loaded from address $0000 and can be up to 64 kB long,
|
||||
so the code must be properly located inside that image. Image can be shorter
|
||||
than 64 kB, user will receive warning in such case, but it will be loaded.
|
||||
Binary image may have header attached at the top.
|
||||
It consists of magic keyword 'SNAPSHOT' followed by 15 bytes of data
|
||||
(subject to change in future versions).
|
||||
Older version of header consists of magic keyword 'SNAPSHOT' followed by 15
|
||||
bytes of data - this format had no space for expansion and will be removed
|
||||
in future version. All new snapshots are saved in newest format.
|
||||
Current version of header consists of magic keyword 'SNAPSHOT2' followed by
|
||||
128 bytes of data. Not all of the 128 bytes are used, so there is a space
|
||||
for expansion without the need of changing the file format.
|
||||
The header data saves the status of CPU and emulation facilities like
|
||||
character I/O address and enable flag, ROM boundaries and enable flag etc.
|
||||
This header is added when user saves snapshot of the VM from debug console
|
||||
menu with command: Y [file_name].
|
||||
Below is the full detailed description of the header format:
|
||||
|
||||
* MAGIC_KEYWORD
|
||||
* aabbccddefghijklmm[remaining unused bytes]
|
||||
*
|
||||
* Where:
|
||||
* MAGIC_KEYWORD - text string indicating header, may vary between
|
||||
* versions thus rendering headers from previous
|
||||
* versions incompatible - currently: "SNAPSHOT2"
|
||||
*
|
||||
* Data:
|
||||
*
|
||||
* aa - low and hi bytes of execute address (PC)
|
||||
* bb - low and hi bytes of char IO address
|
||||
* cc - low and hi bytes of ROM begin address
|
||||
* dd - low and hi bytes of ROM end address
|
||||
* e - 0 if char IO is disabled, 1 if enabled
|
||||
* f - 0 if ROM is disabled, 1 if enabled
|
||||
* g - value in CPU Acc (accumulator) register
|
||||
* h - value in CPU X (X index) register
|
||||
* i - value in CPU Y (Y index) register
|
||||
* j - value in CPU PS (processor status/flags)
|
||||
* k - value in CPU SP (stack pointer) register
|
||||
* l - 0 if generic graphics display device is disabled,
|
||||
* 1 if graphics display is enabled
|
||||
* mm - low and hi bytes of graphics display base address
|
||||
* [remaining unused bytes are filled with 0-s]
|
||||
|
||||
Header is not mandatory, so the binary image created outside application can
|
||||
also be used. User will receive warning at startup during image load if
|
||||
header is missing, but image will be loaded. In this case, user may need
|
||||
@ -131,24 +168,32 @@ ENROM
|
||||
ENIO
|
||||
EXEC
|
||||
address
|
||||
ENGRAPH
|
||||
GRAPHADDR
|
||||
address
|
||||
RESET
|
||||
|
||||
Where:
|
||||
ADDR - label indicating that starting and run address will follow in
|
||||
the next line
|
||||
the next line
|
||||
ORG - label indicating that the address counter will change to the
|
||||
value provided in next line
|
||||
value provided in next line
|
||||
IOADDR - label indicating that character I/O emulation trap address will
|
||||
follow in the next line
|
||||
follow in the next line
|
||||
ROMBEGIN - label indicating that the emulated read-only memory start
|
||||
address will follow in the next line
|
||||
address will follow in the next line
|
||||
ROMEND - label indicating that the emulated read-only memory end address
|
||||
will follow in the next line
|
||||
will follow in the next line
|
||||
ENROM - enable read-only memory emulation
|
||||
ENIO - enable character I/O emulation
|
||||
EXEC - label indicating that the auto-execute address will follow
|
||||
in the next line, 6502 program will auto-execute from that
|
||||
address after memory definition file is done loading
|
||||
in the next line, 6502 program will auto-execute from that
|
||||
address after memory definition file is done loading
|
||||
ENGRAPH - enable generic graphics display device emulation with default
|
||||
base address
|
||||
GRAPHADDR - label indicating that base address for generic graphics display
|
||||
device will follow in next line, also enables generic graphics
|
||||
device emulation, but with the customized base address
|
||||
RESET - initiate CPU reset sequence after loading memory definition file
|
||||
|
||||
|
||||
@ -327,9 +372,9 @@ Emulator is "cycle accurate" but not time or speed accurate.
|
||||
This means that each call to MKCpu::ExecOpcode() method is considered a single
|
||||
CPU cycle, so depending on the executed opcode, multiple calls (# varies per
|
||||
opcode and other conditions) are needed to complete the opcode execution and
|
||||
proceed to the next one. Method returns pointer to the the virtual CPU
|
||||
registers. One of the members of this structure is named CyclesLeft. When this
|
||||
variable reaches 0, the opcode execution is considered complete.
|
||||
proceed to the next one. Method returns pointer to the virtual CPU registers.
|
||||
One of the members of this structure is named CyclesLeft. When this variable
|
||||
reaches 0, the opcode execution is considered complete.
|
||||
|
||||
The VMachine class calls the ExecOpcode() method as fast as possible, so it is
|
||||
not real-time accurate, as already mentioned. To implement real-time accurate
|
||||
@ -380,6 +425,14 @@ I - toggle char I/O emulation
|
||||
to the specified memory address also writes a character code to
|
||||
to a virtual console. All reads from specified memory address
|
||||
are interpreted as console character input.
|
||||
V - toggle graphics display (video) emulation
|
||||
Usage: V [address]
|
||||
Where: address - memory addr. in hexadecimal format [0000.FFFF],
|
||||
Toggles basic raster (pixel) based RGB graphics display emulation.
|
||||
When enabled, window with graphics screen will open and several
|
||||
registers are available to control the device starting at provided
|
||||
base address. Read programmers reference for detailed documentation
|
||||
regarding the available registers and their functions.
|
||||
R - show registers
|
||||
Displays CPU registers, flags and stack.
|
||||
Y - snapshot
|
||||
@ -415,11 +468,13 @@ K - toggle ROM emulation
|
||||
(read-only memory) will be mapped. Default range: $D000-$DFFF.
|
||||
L - load memory image
|
||||
Usage: L [image_type] [image_name]
|
||||
Where:
|
||||
image_type - B (binary), H (Intel HEX) OR D (definition),
|
||||
Where:
|
||||
image_type - A - (auto), B (binary), H (Intel HEX) OR D (definition),
|
||||
image_name - name of the image file.
|
||||
This function allows to load new memory image from either binary
|
||||
image file, Intel HEX format file or the ASCII definition file.
|
||||
With option 'A' selected, automatic input format detection will be
|
||||
attempted.
|
||||
The binary image is always loaded from address 0x0000 and can be up to
|
||||
64kB long. The definition file format is a plain text file that can
|
||||
contain following keywords and data:
|
||||
@ -500,35 +555,35 @@ NOTE:
|
||||
|
||||
7. Command line usage.
|
||||
|
||||
D:\src\wrk\mkbasic>mkbasic -h
|
||||
C:\src\devcppprj\mkbasic>mkbasic -h
|
||||
Virtual Machine/CPU Emulator (MOS 6502) and Debugger.
|
||||
Copyright (C) by Marek Karcz 2016. All rights reserved.
|
||||
|
||||
|
||||
Usage:
|
||||
|
||||
mkbasic [-h] | [ramdeffile] | [-b ramimage] | [-r ramimage]
|
||||
OR
|
||||
mkbasic [-x intelheximage]
|
||||
mkbasic [-h] | [ramdeffile] [-b | -x] [-r]
|
||||
|
||||
|
||||
Where:
|
||||
|
||||
ramdeffile - RAM definition file name
|
||||
intelheximage - Intel HEX format file
|
||||
ramimage - RAM binary image file name
|
||||
-b - specify input format as binary
|
||||
-x - specify input format as Intel HEX
|
||||
-r - after loading, perform CPU RESET
|
||||
-h - print this help screen
|
||||
|
||||
|
||||
When ran with no arguments, program will load default memory
|
||||
definition files: default.rom, default.ram and will enter the debug
|
||||
console menu.
|
||||
When ramdeffile argument is provided, program will load the memory
|
||||
definition from the file, set the flags and parameters depending on the
|
||||
contents of the memory definition file and enter the corresponding mode
|
||||
of operation as defined in that file.
|
||||
If used with flag -b or -x, program will load memory from the provided image
|
||||
file and enter the debug console menu.
|
||||
If used with flag -r, program will load memory from the provided image
|
||||
file and execute CPU reset sequence.
|
||||
When ramdeffile argument is provided with no input format specified,
|
||||
program will attempt to automatically detect input format and load the
|
||||
memory definition from the file, set the flags and parameters depending
|
||||
on the contents of the memory definition file and enter the corresponding
|
||||
mode of operation as defined in that file.
|
||||
If input format is specified (-b|-x), program will load memory from the
|
||||
provided image file and enter the debug console menu.
|
||||
|
||||
8. Utilities.
|
||||
|
||||
@ -564,7 +619,90 @@ Where:
|
||||
addr = 0, exec is not set and data blocks with 0-s only
|
||||
are always suppressed.
|
||||
|
||||
9. Warranty and License Agreement.
|
||||
9. Memory Mapped Device abstraction layer.
|
||||
|
||||
In microprocessor based systems in majority of cases communication with
|
||||
peripheral devices is done via registers which in turn are located under
|
||||
specific memory addresses.
|
||||
Programming API responsible for modeling this functionality is implemented
|
||||
in Memory and MemMapDev classes. The Memory class implements access to
|
||||
specific memory locations and maintains the memory image.
|
||||
The MemMapDev class implements specific device address spaces and handling
|
||||
methods that are triggered when addresses of the device are accessed by the
|
||||
microprocessor.
|
||||
Programmers can expand the functionality of this emulator by adding necessary
|
||||
code emulating specific devices in MemMapDev and Memory classes implementation
|
||||
and header files. In current version, two basic devices are implemented:
|
||||
character I/O and raster (pixel based) graphics display. Both can be activated
|
||||
or inactivated at will and provide simple register based interface that
|
||||
requires no extra memory space use for data.
|
||||
E.g.:
|
||||
Character I/O device uses just 2 memory locations, one for non-blocking I/O
|
||||
and one for blocking I/O. Writing to location causes character output, while
|
||||
reading from location waits for character input (blocking mode) or reads the
|
||||
character from keyboard buffer if available (non-blocking mode).
|
||||
The graphics display can be accessed by writing to multiple memory locations.
|
||||
|
||||
If we assume that GRDEVBASE is the base address of the Graphics Device, there
|
||||
are following registers:
|
||||
|
||||
Offset Register Description
|
||||
------------------------------------------------------------------------------
|
||||
0 GRAPHDEVREG_X_LO Least significant part of pixel's X (column)
|
||||
coordinate or begin of line coord. (0-255)
|
||||
1 GRAPHDEVREG_X_HI Most significant part of pixel's X (column)
|
||||
coordinate or begin of line coord. (0-1)
|
||||
2 GRAPHDEVREG_Y Pixel's Y (row) coordinate (0-199)
|
||||
3 GRAPHDEVREG_PXCOL_R Pixel's RGB color component - Red (0-255)
|
||||
4 GRAPHDEVREG_PXCOL_G Pixel's RGB color component - Green (0-255)
|
||||
5 GRAPHDEVREG_PXCOL_B Pixel's RGB color component - Blue (0-255)
|
||||
6 GRAPHDEVREG_BGCOL_R Background RGB color component - Red (0-255)
|
||||
7 GRAPHDEVREG_BGCOL_G Background RGB color component - Green (0-255)
|
||||
8 GRAPHDEVREG_BGCOL_B Background RGB color component - Blue (0-255)
|
||||
9 GRAPHDEVREG_CMD Command code
|
||||
10 GRAPHDEVREG_X2_LO Least significant part of end of line's X
|
||||
coordinate
|
||||
11 GRAPHDEVREG_X2_HI Most significant part of end of line's X
|
||||
coordinate
|
||||
12 GRAPHDEVREG_Y2 End of line's Y (row) coordinate (0-199)
|
||||
|
||||
Writing values to above memory locations when Graphics Device is enabled
|
||||
allows to set the corresponding parameters of the device, while writing to
|
||||
command register executes corresponding command (performs action) per codes
|
||||
listed below:
|
||||
|
||||
Command code Command description
|
||||
------------------------------------------------------------------------------
|
||||
GRAPHDEVCMD_CLRSCR = 0 Clear screen
|
||||
GRAPHDEVCMD_SETPXL = 1 Set the pixel location to pixel color
|
||||
GRAPHDEVCMD_CLRPXL = 2 Clear the pixel location (set to bg color)
|
||||
GRAPHDEVCMD_SETBGC = 3 Set the background color
|
||||
GRAPHDEVCMD_SETFGC = 4 Set the foreground (pixel) color
|
||||
GRAPHDEVCMD_DRAWLN = 5 Draw line
|
||||
GRAPHDEVCMD_ERASLN = 6 Erase line
|
||||
|
||||
Reading from registers has no effect (returns 0).
|
||||
|
||||
Above method of interfacing GD requires no dedicated graphics memory space
|
||||
in VM's RAM. It is also simple to implement.
|
||||
The downside - slow performance (multiple memory writes to select/unselect
|
||||
a pixel or set color).
|
||||
I plan to add graphics frame buffer in the VM's RAM address space in future
|
||||
release.
|
||||
|
||||
Simple demo program written in EhBasic that shows how to drive the graphics
|
||||
screen is included: grdevdemo.bas.
|
||||
|
||||
10. Problems, issues, bugs.
|
||||
|
||||
* Regaining focus of the graphics window when it is not being written to by the
|
||||
6502 code is somewhat flakey. Since the window has no title bar, user can
|
||||
only switch to it by ALT-TAB (windows) or clicking on the corresponding icon
|
||||
on the task bar. However it doesn't always work. Switching to the DOS console
|
||||
of emulator while in emulation mode should bring the graphics window back
|
||||
to front.
|
||||
|
||||
11. Warranty and License Agreement.
|
||||
|
||||
This software is provided with No Warranty.
|
||||
I (The Author) will not be held responsible for any damage to computer
|
||||
|
424
VMachine.cpp
424
VMachine.cpp
@ -81,10 +81,13 @@ void VMachine::InitVM()
|
||||
mOpInterrupt = false;
|
||||
mpRAM = new Memory();
|
||||
|
||||
mOldStyleHeader = false;
|
||||
mError = VMERR_OK;
|
||||
mAutoExec = false;
|
||||
mAutoReset = false;
|
||||
mCharIOAddr = CHARIO_ADDR;
|
||||
mCharIOActive = mCharIO = false;
|
||||
mGraphDispActive = false;
|
||||
if (NULL == mpRAM) {
|
||||
throw MKGenException("Unable to initialize VM (RAM).");
|
||||
}
|
||||
@ -324,12 +327,14 @@ Regs *VMachine::Step()
|
||||
Regs *cpureg = NULL;
|
||||
|
||||
cpureg = mpCPU->ExecOpcode(addr);
|
||||
if (mGraphDispActive) mpRAM->GraphDisp_ReadEvents();
|
||||
addr = cpureg->PtrAddr;
|
||||
mRunAddr = addr;
|
||||
|
||||
if (cpureg->CyclesLeft == 0 && mCharIOActive && !mOpInterrupt) {
|
||||
char c = -1;
|
||||
mCharIO = false;
|
||||
|
||||
while ((c = mpRAM->GetCharOut()) != -1) {
|
||||
mOpInterrupt = mOpInterrupt || (c == OPINTERRUPT);
|
||||
if (!mOpInterrupt) {
|
||||
@ -374,13 +379,97 @@ void VMachine::LoadROM(string romfname)
|
||||
* Method: LoadRAM()
|
||||
* Purpose: Load data from memory definition file to the memory.
|
||||
* Arguments: ramfname - name of the RAM file definition
|
||||
* Returns: n/a
|
||||
* Returns: int - error code
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void VMachine::LoadRAM(string ramfname)
|
||||
int VMachine::LoadRAM(string ramfname)
|
||||
{
|
||||
LoadMEM(ramfname, mpRAM);
|
||||
//mpRAM->EnableROM();
|
||||
int err = 0;
|
||||
eMemoryImageTypes memimg_type = GetMemoryImageType(ramfname);
|
||||
switch (memimg_type) {
|
||||
case MEMIMG_VM65DEF: err = LoadMEM(ramfname, mpRAM); break;
|
||||
case MEMIMG_INTELHEX: err = LoadRAMHex(ramfname); break;
|
||||
case MEMIMG_BIN:
|
||||
default: // unknown type, try to read as binary
|
||||
// and hope for the best
|
||||
err = LoadRAMBin(ramfname);
|
||||
break;
|
||||
}
|
||||
mError = err;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetMemoryImageType()
|
||||
* Purpose: Detect format of memory image file.
|
||||
* Arguments: ramfname - name of the RAM file definition
|
||||
* Returns: eMemoryImageTypes - code of the memory image format
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
eMemoryImageTypes VMachine::GetMemoryImageType(string ramfname)
|
||||
{
|
||||
eMemoryImageTypes ret = MEMIMG_UNKNOWN;
|
||||
char buf[256] = {0};
|
||||
FILE *fp = NULL;
|
||||
int n = 0;
|
||||
|
||||
if ((fp = fopen(ramfname.c_str(), "rb")) != NULL) {
|
||||
memset(buf, 0, 256);
|
||||
while (0 == feof(fp) && 0 == ferror(fp)) {
|
||||
unsigned char val = fgetc(fp);
|
||||
buf[n++] = val;
|
||||
if (n >= 256) break;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
bool possibly_intelhex = true;
|
||||
for (int i=0; i<256; i++) {
|
||||
char *pc = buf+i;
|
||||
if (isspace(buf[i])) continue;
|
||||
if (i < 256-9 // 256 less the length of the longest expected keyword
|
||||
&&
|
||||
(!strncmp(pc, "ADDR", 4)
|
||||
|| !strncmp(pc, "ORG", 3)
|
||||
|| !strncmp(pc, "IOADDR", 6)
|
||||
|| !strncmp(pc, "ROMBEGIN", 8)
|
||||
|| !strncmp(pc, "ROMEND", 6)
|
||||
|| !strncmp(pc, "ENROM", 5)
|
||||
|| !strncmp(pc, "ENIO", 4)
|
||||
|| !strncmp(pc, "EXEC", 4)
|
||||
|| !strncmp(pc, "RESET", 5)
|
||||
|| !strncmp(pc, "ENGRAPH", 7)
|
||||
|| !strncmp(pc, "GRAPHADDR", 9))
|
||||
)
|
||||
{
|
||||
ret = MEMIMG_VM65DEF;
|
||||
break;
|
||||
}
|
||||
if (buf[i] != ':'
|
||||
&& buf[i] != '0'
|
||||
&& buf[i] != '1'
|
||||
&& buf[i] != '2'
|
||||
&& buf[i] != '3'
|
||||
&& buf[i] != '4'
|
||||
&& buf[i] != '5'
|
||||
&& buf[i] != '6'
|
||||
&& buf[i] != '7'
|
||||
&& buf[i] != '8'
|
||||
&& buf[i] != '9'
|
||||
&& tolower(buf[i]) != 'a'
|
||||
&& tolower(buf[i]) != 'b'
|
||||
&& tolower(buf[i]) != 'c'
|
||||
&& tolower(buf[i]) != 'd'
|
||||
&& tolower(buf[i]) != 'e'
|
||||
&& tolower(buf[i]) != 'f')
|
||||
{
|
||||
possibly_intelhex = false;
|
||||
}
|
||||
}
|
||||
if (ret == MEMIMG_UNKNOWN && possibly_intelhex)
|
||||
ret = MEMIMG_INTELHEX;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -400,6 +489,7 @@ bool VMachine::HasHdrData(FILE *fp)
|
||||
|
||||
memset(buf, 0, 20);
|
||||
|
||||
rewind(fp);
|
||||
while (0 == feof(fp) && 0 == ferror(fp)) {
|
||||
unsigned char val = fgetc(fp);
|
||||
buf[n] = val;
|
||||
@ -411,6 +501,36 @@ bool VMachine::HasHdrData(FILE *fp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: HasOldHdrData()
|
||||
* Purpose: Check for previous version header in the binary memory
|
||||
* image.
|
||||
* Arguments: File pointer.
|
||||
* Returns: true if magic keyword found at the beginning of the
|
||||
* memory image file, false otherwise
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
bool VMachine::HasOldHdrData(FILE *fp)
|
||||
{
|
||||
bool ret = false;
|
||||
int n = 0, l = strlen(HDRMAGICKEY_OLD);
|
||||
char buf[20];
|
||||
|
||||
memset(buf, 0, 20);
|
||||
|
||||
rewind(fp);
|
||||
while (0 == feof(fp) && 0 == ferror(fp)) {
|
||||
unsigned char val = fgetc(fp);
|
||||
buf[n] = val;
|
||||
n++;
|
||||
if (n >= l) break;
|
||||
}
|
||||
ret = (0 == strncmp(buf, HDRMAGICKEY_OLD, l));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: LoadHdrData()
|
||||
@ -420,16 +540,25 @@ bool VMachine::HasHdrData(FILE *fp)
|
||||
*
|
||||
* Details:
|
||||
* Header of the binary memory image consists of magic keyword
|
||||
* string followed by the data (unsigned char values).
|
||||
* string followed by the 128 bytes of data (unsigned char values).
|
||||
* It has following format:
|
||||
*
|
||||
* MAGIC_KEYWORD
|
||||
* aabbccddefghijk
|
||||
* aabbccddefghijklmm[remaining unused bytes]
|
||||
*
|
||||
* Where:
|
||||
* MAGIC_KEYWORD - text string indicating header, may vary between
|
||||
* versions thus rendering headers from previous
|
||||
* versions incompatible - currently: "SNAPSHOT"
|
||||
* versions incompatible - currently: "SNAPSHOT2"
|
||||
* NOTE: Previous version of header is currently
|
||||
* recognized and can be read, the magic
|
||||
* keyword of previous version: "SNAPSHOT".
|
||||
* Old header had only 15 bytes of data.
|
||||
* This backwards compatibility will be
|
||||
* removed in next version as the new
|
||||
* format of header with 128 bytes for
|
||||
* data leaves space for expansion without
|
||||
* the need of changing file format.
|
||||
* aa - low and hi bytes of execute address (PC)
|
||||
* bb - low and hi bytes of char IO address
|
||||
* cc - low and hi bytes of ROM begin address
|
||||
@ -441,6 +570,9 @@ bool VMachine::HasHdrData(FILE *fp)
|
||||
* i - value in CPU Y (Y index) register
|
||||
* j - value in CPU PS (processor status/flags)
|
||||
* k - value in CPU SP (stack pointer) register
|
||||
* l - 0 if generic graphics display device is disabled,
|
||||
* 1 if graphics display is enabled
|
||||
* mm - low and hi bytes of graphics display base address
|
||||
*
|
||||
* NOTE:
|
||||
* If magic keyword was detected, this part is already read and file
|
||||
@ -450,12 +582,13 @@ bool VMachine::HasHdrData(FILE *fp)
|
||||
*/
|
||||
bool VMachine::LoadHdrData(FILE *fp)
|
||||
{
|
||||
int n = 0, l = 0;
|
||||
int n = 0, l = 0, hdrdtlen = HDRDATALEN;
|
||||
unsigned short rb = 0, re = 0;
|
||||
Regs r;
|
||||
bool ret = false;
|
||||
|
||||
while (0 == feof(fp) && 0 == ferror(fp) && n < HDRDATALEN) {
|
||||
if (mOldStyleHeader) hdrdtlen = HDRDATALEN_OLD;
|
||||
while (0 == feof(fp) && 0 == ferror(fp) && n < hdrdtlen) {
|
||||
unsigned char val = fgetc(fp);
|
||||
switch (n)
|
||||
{
|
||||
@ -486,6 +619,11 @@ bool VMachine::LoadHdrData(FILE *fp)
|
||||
case 14: r.PtrStack = val;
|
||||
ret = true;
|
||||
break;
|
||||
case 15: mGraphDispActive = (val != 0);
|
||||
break;
|
||||
case 17: if (mGraphDispActive) SetGraphDisp(l + 256 * val);
|
||||
else DisableGraphDisp();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
l = val;
|
||||
@ -499,6 +637,9 @@ bool VMachine::LoadHdrData(FILE *fp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Macro to save header data: v - value, fp - file pointer, n - data counter (dec)
|
||||
#define SAVE_HDR_DATA(v,fp,n) {fputc(v, fp); n--;}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: SaveHdrData()
|
||||
@ -510,6 +651,8 @@ bool VMachine::LoadHdrData(FILE *fp)
|
||||
void VMachine::SaveHdrData(FILE *fp)
|
||||
{
|
||||
char buf[20] = {0};
|
||||
int n = HDRDATALEN;
|
||||
|
||||
strcpy(buf, HDRMAGICKEY);
|
||||
for (unsigned int i = 0; i < strlen(HDRMAGICKEY); i++) {
|
||||
fputc(buf[i], fp);
|
||||
@ -518,32 +661,40 @@ void VMachine::SaveHdrData(FILE *fp)
|
||||
unsigned char lo = 0, hi = 0;
|
||||
lo = (unsigned char) (reg->PtrAddr & 0x00FF);
|
||||
hi = (unsigned char) ((reg->PtrAddr & 0xFF00) >> 8);
|
||||
fputc(lo, fp);
|
||||
fputc(hi, fp);
|
||||
SAVE_HDR_DATA(lo,fp,n);
|
||||
SAVE_HDR_DATA(hi,fp,n);
|
||||
lo = (unsigned char) (mCharIOAddr & 0x00FF);
|
||||
hi = (unsigned char) ((mCharIOAddr & 0xFF00) >> 8);
|
||||
fputc(lo, fp);
|
||||
fputc(hi, fp);
|
||||
SAVE_HDR_DATA(lo,fp,n);
|
||||
SAVE_HDR_DATA(hi,fp,n);
|
||||
lo = (unsigned char) (GetROMBegin() & 0x00FF);
|
||||
hi = (unsigned char) ((GetROMBegin() & 0xFF00) >> 8);
|
||||
fputc(lo, fp);
|
||||
fputc(hi, fp);
|
||||
SAVE_HDR_DATA(lo,fp,n);
|
||||
SAVE_HDR_DATA(hi,fp,n);
|
||||
lo = (unsigned char) (GetROMEnd() & 0x00FF);
|
||||
hi = (unsigned char) ((GetROMEnd() & 0xFF00) >> 8);
|
||||
fputc(lo, fp);
|
||||
fputc(hi, fp);
|
||||
SAVE_HDR_DATA(lo,fp,n);
|
||||
SAVE_HDR_DATA(hi,fp,n);
|
||||
lo = (mCharIOActive ? 1 : 0);
|
||||
fputc(lo, fp);
|
||||
SAVE_HDR_DATA(lo,fp,n);
|
||||
lo = (IsROMEnabled() ? 1 : 0);
|
||||
fputc(lo, fp);
|
||||
SAVE_HDR_DATA(lo,fp,n);
|
||||
Regs *pregs = mpCPU->GetRegs();
|
||||
if (pregs != NULL) {
|
||||
fputc(pregs->Acc, fp);
|
||||
fputc(pregs->IndX, fp);
|
||||
fputc(pregs->IndY, fp);
|
||||
fputc(pregs->Flags, fp);
|
||||
fputc(pregs->PtrStack, fp);
|
||||
SAVE_HDR_DATA(pregs->Acc,fp,n);
|
||||
SAVE_HDR_DATA(pregs->IndX,fp,n);
|
||||
SAVE_HDR_DATA(pregs->IndY,fp,n);
|
||||
SAVE_HDR_DATA(pregs->Flags,fp,n);
|
||||
SAVE_HDR_DATA(pregs->PtrStack,fp,n);
|
||||
}
|
||||
lo = (mGraphDispActive ? 1 : 0);
|
||||
SAVE_HDR_DATA(lo,fp,n);
|
||||
lo = (unsigned char) (GetGraphDispAddr() & 0x00FF);
|
||||
hi = (unsigned char) ((GetGraphDispAddr() & 0xFF00) >> 8);
|
||||
SAVE_HDR_DATA(lo,fp,n);
|
||||
SAVE_HDR_DATA(hi,fp,n);
|
||||
// fill up the unused slots of header data with 0-s
|
||||
for (int i = n; i > 0; i--) fputc(0, fp);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -564,7 +715,7 @@ int VMachine::SaveSnapshot(string fname)
|
||||
SaveHdrData(fp);
|
||||
for (int addr = 0; addr < MAX_8BIT_ADDR+1; addr++) {
|
||||
if (addr != mCharIOAddr && addr != mCharIOAddr+1) {
|
||||
unsigned char b = mpRAM->Peek8bit((unsigned short)addr);
|
||||
unsigned char b = mpRAM->Peek8bitImg((unsigned short)addr);
|
||||
if (EOF != fputc(b, fp)) ret--;
|
||||
else break;
|
||||
} else {
|
||||
@ -574,6 +725,7 @@ int VMachine::SaveSnapshot(string fname)
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
if (0 != ret) mError = VMERR_SAVE_SNAPSHOT;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -584,15 +736,21 @@ int VMachine::SaveSnapshot(string fname)
|
||||
* Purpose: Load data from binary image file to the memory.
|
||||
* Arguments: ramfname - name of the RAM file definition
|
||||
* Returns: int - error code
|
||||
* 0 - OK
|
||||
* 1 - WARNING: Unexpected EOF (image shorter than 64kB).
|
||||
* 2 - WARNING: Unable to open memory image file.
|
||||
* 3 - WARNING: Problem with binary image header.
|
||||
* 4 - WARNING: No header found in binary image.
|
||||
* 5 - WARNING: Problem with binary image header and
|
||||
* Unexpected EOF (image shorter than 64kB).
|
||||
* 6 - WARNING: No header found in binary image and
|
||||
* Unexpected EOF (image shorter than 64kB).
|
||||
* MEMIMGERR_OK - OK
|
||||
* MEMIMGERR_RAMBIN_EOF
|
||||
* - WARNING: Unexpected EOF (image shorter than 64kB).
|
||||
* MEMIMGERR_RAMBIN_OPEN
|
||||
* - WARNING: Unable to open memory image file.
|
||||
* MEMIMGERR_RAMBIN_HDR
|
||||
* - WARNING: Problem with binary image header.
|
||||
* MEMIMGERR_RAMBIN_NOHDR
|
||||
* - WARNING: No header found in binary image.
|
||||
* MEMIMGERR_RAMBIN_HDRANDEOF
|
||||
* - WARNING: Problem with binary image header and
|
||||
* Unexpected EOF (image shorter than 64kB).
|
||||
* MEMIMGERR_RAMBIN_NOHDRANDEOF
|
||||
* - WARNING: No header found in binary image and
|
||||
* Unexpected EOF (image shorter than 64kB).
|
||||
* TO DO:
|
||||
* - Add fixed size header to binary image with emulator
|
||||
* configuration data. Presence of the header will be detected
|
||||
@ -607,13 +765,14 @@ int VMachine::LoadRAMBin(string ramfname)
|
||||
unsigned short addr = 0x0000;
|
||||
int n = 0;
|
||||
Memory *pm = mpRAM;
|
||||
int ret = 2;
|
||||
|
||||
int ret = MEMIMGERR_RAMBIN_OPEN;
|
||||
|
||||
mOldStyleHeader = false;
|
||||
if ((fp = fopen(ramfname.c_str(), "rb")) != NULL) {
|
||||
if (HasHdrData(fp)) {
|
||||
ret = (LoadHdrData(fp) ? 0 : 3);
|
||||
if (HasHdrData(fp) || (mOldStyleHeader = HasOldHdrData(fp))) {
|
||||
ret = (LoadHdrData(fp) ? MEMIMGERR_OK : MEMIMGERR_RAMBIN_HDR);
|
||||
} else {
|
||||
ret = 4;
|
||||
ret = MEMIMGERR_RAMBIN_NOHDR;
|
||||
rewind(fp);
|
||||
}
|
||||
// temporarily disable emulation facilities to allow
|
||||
@ -623,7 +782,7 @@ int VMachine::LoadRAMBin(string ramfname)
|
||||
DisableROM();
|
||||
while (0 == feof(fp) && 0 == ferror(fp)) {
|
||||
unsigned char val = fgetc(fp);
|
||||
pm->Poke8bit(addr, val);
|
||||
pm->Poke8bitImg(addr, val);
|
||||
addr++; n++;
|
||||
}
|
||||
fclose(fp);
|
||||
@ -632,13 +791,24 @@ int VMachine::LoadRAMBin(string ramfname)
|
||||
if (tmp2) EnableROM();
|
||||
if (n <= 0xFFFF) {
|
||||
switch (ret) {
|
||||
case 0: ret = 1; break;
|
||||
case 3: ret = 5; break;
|
||||
case 4: ret = 6; break;
|
||||
|
||||
case MEMIMGERR_OK:
|
||||
ret = MEMIMGERR_RAMBIN_EOF;
|
||||
break;
|
||||
|
||||
case MEMIMGERR_RAMBIN_HDR:
|
||||
ret = MEMIMGERR_RAMBIN_HDRANDEOF;
|
||||
break;
|
||||
|
||||
case MEMIMGERR_RAMBIN_NOHDR:
|
||||
ret = MEMIMGERR_RAMBIN_NOHDRANDEOF;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mError = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -648,10 +818,10 @@ int VMachine::LoadRAMBin(string ramfname)
|
||||
* Method: LoadRAMHex()
|
||||
* Purpose: Load data from Intel HEX file format to memory.
|
||||
* Arguments: hexfname - name of the HEX file
|
||||
* Returns: int, 0 if OK, >0 - error code:
|
||||
* 1 - unable to open file
|
||||
* 2 - syntax error
|
||||
* 3 - hex format error
|
||||
* Returns: int, MEMIMGERR_OK if OK, otherwise error code:
|
||||
* MEMIMGERR_INTELH_OPEN - unable to open file
|
||||
* MEMIMGERR_INTELH_SYNTAX - syntax error
|
||||
* MEMIMGERR_INTELH_FMT - hex format error
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int VMachine::LoadRAMHex(string hexfname)
|
||||
@ -694,7 +864,7 @@ int VMachine::LoadRAMHex(string hexfname)
|
||||
if (rectype != 0) continue; // not a data record, next!
|
||||
for (unsigned int i=9; i<reclen*2+9; i+=2,addr++) {
|
||||
if (i>=strlen(line)-3) {
|
||||
ret = 3; // hex format error
|
||||
ret = MEMIMGERR_INTELH_FMT; // hex format error
|
||||
break;
|
||||
}
|
||||
char dbuf[3] = {0,0,0};
|
||||
@ -704,30 +874,46 @@ int VMachine::LoadRAMHex(string hexfname)
|
||||
dbuf[1] = line[i+1];
|
||||
dbuf[2] = 0;
|
||||
sscanf(dbuf, "%02x", &byteval);
|
||||
pm->Poke8bit(addr, (unsigned char)byteval&0x00FF);
|
||||
pm->Poke8bitImg(addr, (unsigned char)byteval&0x00FF);
|
||||
}
|
||||
} else {
|
||||
ret = 2; // syntax error
|
||||
ret = MEMIMGERR_INTELH_SYNTAX; // syntax error
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
} else {
|
||||
ret = 1; // unable to open file
|
||||
ret = MEMIMGERR_INTELH_OPEN; // unable to open file
|
||||
}
|
||||
if (tmp1) SetCharIO(mCharIOAddr, false);
|
||||
if (tmp2) EnableROM();
|
||||
|
||||
mError = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: LoadRAMDef()
|
||||
* Purpose: Load RAM from VM65 memory definition file.
|
||||
* Arguments: memfname - file name
|
||||
* Returns: int - error code.
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int VMachine::LoadRAMDef(string memfname)
|
||||
{
|
||||
return LoadMEM(memfname, mpRAM);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: LoadMEM()
|
||||
* Purpose: Load data from memory definition file to the memory.
|
||||
* Purpose: Load data from VM65 memory definition file to the
|
||||
* provided memory device.
|
||||
* Arguments: memfname - name of memory definition file
|
||||
* pmem - pointer to memory object
|
||||
* Returns: n/a
|
||||
* Returns: int - error code
|
||||
* Details:
|
||||
* Format of the memory definition file:
|
||||
* [; comment]
|
||||
@ -747,6 +933,10 @@ int VMachine::LoadRAMHex(string hexfname)
|
||||
* [ENROM]
|
||||
* [EXEC
|
||||
* addrress]
|
||||
* [ENGRAPH]
|
||||
* [GRAPHADDR
|
||||
* address]
|
||||
* [RESET]
|
||||
*
|
||||
* Where:
|
||||
* [] - optional token
|
||||
@ -764,6 +954,13 @@ int VMachine::LoadRAMHex(string hexfname)
|
||||
* ENROM - label enabling ROM emulation
|
||||
* EXEC - label enabling auto-execute of code, address follows in the
|
||||
* next line
|
||||
* ENGRAPH - enable generic graphics display device emulation with
|
||||
* default base address
|
||||
* GRAPHADDR - label indicating that base address for generic graphics
|
||||
* display device will follow in next line,
|
||||
* also enables generic graphics device emulation, but
|
||||
* with the customized base address
|
||||
* RESET - initiate CPU reset sequence after loading memory definition file
|
||||
* address - decimal or hexadecimal (prefix $) address in memory
|
||||
* E.g:
|
||||
* ADDR
|
||||
@ -803,21 +1000,20 @@ int VMachine::LoadRAMHex(string hexfname)
|
||||
* 0 0 0 0
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||
int VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
char line[256] = "\0";
|
||||
int lc = 0, errc = 0;
|
||||
unsigned short addr = 0, rombegin = 0, romend = 0;
|
||||
unsigned int nAddr;
|
||||
unsigned int nAddr, graphaddr = GRDISP_ADDR;
|
||||
bool enrom = false, enio = false, runset = false;
|
||||
bool ioset = false, execset = false, rombegset = false;
|
||||
bool romendset = false;
|
||||
bool romendset = false, engraph = false, graphset = false;
|
||||
Memory *pm = pmem;
|
||||
int err = MEMIMGERR_OK;
|
||||
|
||||
if ((fp = fopen(memfname.c_str(), "r")) != NULL) {
|
||||
// to ensure proper memory initialization, disable emulation
|
||||
// of char I/O and ROM
|
||||
DisableROM();
|
||||
DisableCharIO();
|
||||
while (0 == feof(fp) && 0 == ferror(fp))
|
||||
@ -840,6 +1036,7 @@ void VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||
mRunAddr = addr;
|
||||
runset = true;
|
||||
} else {
|
||||
err = MEMIMGERR_VM65_IGNPROCWRN;
|
||||
errc++;
|
||||
cout << "LINE #" << dec << lc << " WARNING: Run address was already set. Ignoring..." << endl;
|
||||
}
|
||||
@ -872,16 +1069,42 @@ void VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||
}
|
||||
ioset = true;
|
||||
} else {
|
||||
err = MEMIMGERR_VM65_IGNPROCWRN;
|
||||
errc++;
|
||||
cout << "LINE #" << dec << lc << " WARNING: I/O address was already set. Ignoring..." << endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// define generic graphics display device base address (once)
|
||||
if (0 == strncmp(line, "GRAPHADDR", 9)) {
|
||||
line[0] = '\0';
|
||||
fgets(line, 256, fp);
|
||||
lc++;
|
||||
if (!graphset) {
|
||||
if (*line == '$') {
|
||||
sscanf(line+1, "%04x", &nAddr);
|
||||
graphaddr = nAddr;
|
||||
} else {
|
||||
graphaddr = (unsigned short) atoi(line);
|
||||
}
|
||||
graphset = true;
|
||||
} else {
|
||||
err = MEMIMGERR_VM65_IGNPROCWRN;
|
||||
errc++;
|
||||
cout << "LINE #" << dec << lc << " WARNING: graphics device base address was already set. Ignoring..." << endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// enable character I/O emulation
|
||||
if (0 == strncmp(line, "ENIO", 4)) {
|
||||
enio = true;
|
||||
continue;
|
||||
}
|
||||
// enable generic graphics display emulation
|
||||
if (0 == strncmp(line, "ENGRAPH", 7)) {
|
||||
engraph = true;
|
||||
continue;
|
||||
}
|
||||
// enable ROM emulation
|
||||
if (0 == strncmp(line, "ENROM", 5)) {
|
||||
enrom = true;
|
||||
@ -902,6 +1125,7 @@ void VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||
}
|
||||
execset = true;
|
||||
} else {
|
||||
err = MEMIMGERR_VM65_IGNPROCWRN;
|
||||
errc++;
|
||||
cout << "LINE #" << dec << lc << " WARNING: auto-exec address was already set. Ignoring..." << endl;
|
||||
}
|
||||
@ -926,6 +1150,7 @@ void VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||
}
|
||||
rombegset = true;
|
||||
} else {
|
||||
err = MEMIMGERR_VM65_IGNPROCWRN;
|
||||
errc++;
|
||||
cout << "LINE #" << dec << lc << " WARNING: ROM-begin address was already set. Ignoring..." << endl;
|
||||
}
|
||||
@ -945,6 +1170,7 @@ void VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||
}
|
||||
romendset = true;
|
||||
} else {
|
||||
err = MEMIMGERR_VM65_IGNPROCWRN;
|
||||
errc++;
|
||||
cout << "LINE #" << dec << lc << " WARNING: ROM-end address was already set. Ignoring..." << endl;
|
||||
}
|
||||
@ -956,9 +1182,9 @@ void VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||
unsigned int nVal;
|
||||
if (*s == '$') {
|
||||
sscanf(s+1, "%02x", &nVal);
|
||||
pm->Poke8bit(addr++, (unsigned short)nVal);
|
||||
pm->Poke8bitImg(addr++, (unsigned short)nVal);
|
||||
} else {
|
||||
pm->Poke8bit(addr++, (unsigned short)atoi(s));
|
||||
pm->Poke8bitImg(addr++, (unsigned short)atoi(s));
|
||||
}
|
||||
s = strtok(NULL, " ,");
|
||||
}
|
||||
@ -975,8 +1201,12 @@ void VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||
if (enio) {
|
||||
SetCharIO(mCharIOAddr, false);
|
||||
}
|
||||
if (engraph || graphset) {
|
||||
SetGraphDisp(graphaddr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
err = MEMIMGERR_VM65_OPEN;
|
||||
cout << "WARNING: Unable to open memory definition file: " << memfname << endl;
|
||||
errc++;
|
||||
}
|
||||
@ -985,6 +1215,10 @@ void VMachine::LoadMEM(string memfname, Memory *pmem)
|
||||
cout << "Press [ENTER] to continue...";
|
||||
getchar();
|
||||
}
|
||||
|
||||
mError = err;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1088,6 +1322,60 @@ bool VMachine::GetCharIOActive()
|
||||
return mCharIOActive;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void VMachine::SetGraphDisp(unsigned short addr)
|
||||
{
|
||||
mGraphDispActive = true;
|
||||
mpRAM->SetGraphDisp(addr);
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Arguments:
|
||||
* Returns:
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void VMachine::DisableGraphDisp()
|
||||
{
|
||||
mGraphDispActive = false;
|
||||
mpRAM->DisableGraphDisp();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetGraphDispAddr()
|
||||
* Purpose: Return base address of graphics display.
|
||||
* Arguments: n/a
|
||||
* Returns: unsigned short - address ($0000 - $FFFF)
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
unsigned short VMachine::GetGraphDispAddr()
|
||||
{
|
||||
return mpRAM->GetGraphDispAddr();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetGraphDispActive()
|
||||
* Purpose: Returns status of graphics display emulation.
|
||||
* Arguments: n/a
|
||||
* Returns: true if graphics display emulation is active
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
bool VMachine::GetGraphDispActive()
|
||||
{
|
||||
return mGraphDispActive;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: ShowIO()
|
||||
@ -1315,4 +1603,20 @@ void VMachine::Interrupt()
|
||||
mpCPU->Interrupt();
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: GetLastError()
|
||||
* Purpose: Return error code set by last operation. Reset error
|
||||
* code to OK.
|
||||
* Arguments: n/a
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
int VMachine::GetLastError()
|
||||
{
|
||||
int ret = mError;
|
||||
mError = MEMIMGERR_OK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace MKBasic
|
||||
|
59
VMachine.h
59
VMachine.h
@ -15,14 +15,54 @@
|
||||
|
||||
#define IOREFRESH 32
|
||||
#define OPINTERRUPT 25 // operator interrupt code (CTRL-Y)
|
||||
#define HDRMAGICKEY "SNAPSHOT"
|
||||
#define HDRDATALEN 15
|
||||
#define HDRMAGICKEY "SNAPSHOT2"
|
||||
#define HDRMAGICKEY_OLD "SNAPSHOT"
|
||||
#define HDRDATALEN 128
|
||||
#define HDRDATALEN_OLD 15
|
||||
#define HEXEOF ":00000001FF"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace MKBasic {
|
||||
|
||||
// Types of memory image definition file.
|
||||
enum eMemoryImageTypes {
|
||||
MEMIMG_UNKNOWN = 0,
|
||||
MEMIMG_VM65DEF,
|
||||
MEMIMG_INTELHEX,
|
||||
MEMIMG_BIN
|
||||
};
|
||||
|
||||
// Types of memory image load errors
|
||||
enum eMemImgLoadErrors {
|
||||
MEMIMGERR_OK = 0, // all is good
|
||||
// binary format
|
||||
MEMIMGERR_RAMBIN_OPEN, // unable to open file
|
||||
MEMIMGERR_RAMBIN_EOF, // unexpected EOF (image shorter then 64 kB)
|
||||
MEMIMGERR_RAMBIN_HDR, // header problem
|
||||
MEMIMGERR_RAMBIN_NOHDR, // no header found
|
||||
MEMIMGERR_RAMBIN_HDRANDEOF, // header problem and unexpected EOF
|
||||
MEMIMGERR_RAMBIN_NOHDRANDEOF, // header not found and unexoected EOF
|
||||
// Intel HEX format
|
||||
MEMIMGERR_INTELH_OPEN, // unable to open file
|
||||
MEMIMGERR_INTELH_SYNTAX, // syntax error
|
||||
MEMIMGERR_INTELH_FMT, // format error
|
||||
// VM65 memory definition
|
||||
MEMIMGERR_VM65_OPEN, // unable to open file
|
||||
MEMIMGERR_VM65_IGNPROCWRN, // processing warnings (ignored, not critical)
|
||||
//-------------------------------------------------------------------------
|
||||
MEMIMGERR_UNKNOWN
|
||||
};
|
||||
|
||||
// Types of other errors
|
||||
enum eVMErrors {
|
||||
VMERR_OK = 0, // all is good
|
||||
VMERR_SAVE_SNAPSHOT = MEMIMGERR_UNKNOWN+1, // problem saving memory image
|
||||
// snapshot
|
||||
//-------------------------------------------------------------------------
|
||||
VMERR_UNKNOWN // unknown error
|
||||
};
|
||||
|
||||
class VMachine
|
||||
{
|
||||
public:
|
||||
@ -38,9 +78,10 @@ class VMachine
|
||||
Regs *Step();
|
||||
Regs *Step(unsigned short addr);
|
||||
void LoadROM(string romfname);
|
||||
void LoadRAM(string ramfname);
|
||||
int LoadRAM(string ramfname);
|
||||
int LoadRAMBin(string ramfname);
|
||||
int LoadRAMHex(string hexfname);
|
||||
int LoadRAMDef(string memfname);
|
||||
unsigned short MemPeek8bit(unsigned short addr);
|
||||
void MemPoke8bit(unsigned short addr, unsigned char v);
|
||||
Regs *GetRegs();
|
||||
@ -48,6 +89,7 @@ class VMachine
|
||||
void DisableCharIO();
|
||||
unsigned short GetCharIOAddr();
|
||||
bool GetCharIOActive();
|
||||
bool GetGraphDispActive();
|
||||
void ShowIO();
|
||||
void ClearScreen();
|
||||
void ScrHome();
|
||||
@ -68,6 +110,10 @@ class VMachine
|
||||
void Reset();
|
||||
void Interrupt();
|
||||
int SaveSnapshot(string fname);
|
||||
int GetLastError();
|
||||
void SetGraphDisp(unsigned short addr);
|
||||
void DisableGraphDisp();
|
||||
unsigned short GetGraphDispAddr();
|
||||
|
||||
protected:
|
||||
|
||||
@ -84,12 +130,17 @@ class VMachine
|
||||
bool mOpInterrupt; // operator interrupt from console
|
||||
bool mAutoExec;
|
||||
bool mAutoReset;
|
||||
int mError; // last error code
|
||||
bool mGraphDispActive;
|
||||
bool mOldStyleHeader;
|
||||
|
||||
void LoadMEM(string memfname, Memory *pmem);
|
||||
int LoadMEM(string memfname, Memory *pmem);
|
||||
void ShowDisp();
|
||||
bool HasHdrData(FILE *fp);
|
||||
bool HasOldHdrData(FILE *fp);
|
||||
bool LoadHdrData(FILE *fp);
|
||||
void SaveHdrData(FILE *fp);
|
||||
eMemoryImageTypes GetMemoryImageType(string ramfname);
|
||||
};
|
||||
|
||||
} // namespace MKBasic
|
||||
|
630
dummy.ram
630
dummy.ram
@ -1,315 +1,317 @@
|
||||
;
|
||||
; test program #1
|
||||
; address: $0200
|
||||
; load Acc with value 12
|
||||
; write Acc to address $c000 (49152)
|
||||
;
|
||||
; nop
|
||||
; nop
|
||||
; lda #$0c
|
||||
; sta $c000
|
||||
; brk
|
||||
;
|
||||
$EA $EA $A9 $0c $8D $00 $c0 $00 $00
|
||||
;
|
||||
; test program #2
|
||||
; address: $0400
|
||||
; copy 0-terminated string from
|
||||
; address $d000 to $0200
|
||||
; "Hello World!"
|
||||
;
|
||||
; ORG=$0400
|
||||
; hello:
|
||||
; ldx #0
|
||||
; loop:
|
||||
; lda $d000,x
|
||||
; beq $06 ;branch to end (+6) if A=0
|
||||
; sta $0200,x
|
||||
; inx
|
||||
; bne $f5 ; branch to loop (-11) if X<>0
|
||||
; end:
|
||||
; brk
|
||||
ORG
|
||||
$0400
|
||||
$A2 $00
|
||||
$BD $00 $d0
|
||||
$F0 $06
|
||||
$9D $00 $02
|
||||
$E8
|
||||
$D0 $F5
|
||||
$00 $00
|
||||
; data
|
||||
; address: $d000
|
||||
ORG
|
||||
$D000
|
||||
;DEC: 53248
|
||||
; "Hello World!"
|
||||
72 101 108 108 111 32 87 111 114 108 100 33 0
|
||||
;
|
||||
; test program #3 - copy Hello World! string to $0300
|
||||
; using different assembly instructions
|
||||
; address: $0500
|
||||
;
|
||||
; ORG=$0500 ;dec: 1280
|
||||
; hello:
|
||||
; lda #0
|
||||
; sta $05
|
||||
; ldx $05
|
||||
; loop:
|
||||
; lda $d000,x
|
||||
; sta $0300,x
|
||||
; beq end ;(+6)
|
||||
; inx
|
||||
; beq end ;(+3)
|
||||
; jmp loop
|
||||
; end:
|
||||
; brk
|
||||
ORG
|
||||
$0500
|
||||
;DEC: 1280
|
||||
$A9 $00
|
||||
$85 $05
|
||||
$A6 $05
|
||||
$BD $00 $d0
|
||||
$9D $00 $03
|
||||
$F0 $06
|
||||
$E8
|
||||
$F0 $03
|
||||
$4C $06 $05
|
||||
$00 $00
|
||||
;
|
||||
; test program #4
|
||||
; left-shift memory location $05 at zero page,
|
||||
; then location $06 using zero page indexed addressing,
|
||||
; then memory location $c001 (outside zero page) using absolute addressing
|
||||
; then location $c002 using indexed absolute addressing
|
||||
; and finally left-shift Acc.
|
||||
; stop after each step for debugging
|
||||
; exit loop when Acc=0
|
||||
;
|
||||
; start:
|
||||
; lda #$ff
|
||||
; ldx #$01
|
||||
; sta $05
|
||||
; sta $05,x
|
||||
; sta $c000,x
|
||||
; inx
|
||||
; sta $c000,x
|
||||
; ldx #$01
|
||||
; loop2:
|
||||
; brk
|
||||
; asl $05
|
||||
; asl $05,x
|
||||
; asl $c001
|
||||
; asl $c001,x
|
||||
; asl
|
||||
; bne loop2 ;(-15 or $f1)
|
||||
; brk
|
||||
ORG
|
||||
$0600
|
||||
$A9 $FF
|
||||
$A2 $01
|
||||
$85 $05
|
||||
$95 $05
|
||||
$9D $00 $C0
|
||||
$E8
|
||||
$9D $00 $C0
|
||||
$A2 $01
|
||||
$00 $00
|
||||
$06 $05
|
||||
$16 $05
|
||||
$0E $01 $C0
|
||||
$1E $01 $C0
|
||||
$0A
|
||||
$D0 $F1
|
||||
$00 $00
|
||||
;
|
||||
; test program #5
|
||||
; Test ORA opcode with various arguments and addressing modes.
|
||||
; At each break, the contents of Acc should equal $AA.
|
||||
;
|
||||
; start:
|
||||
; lda #$aa ;%10101010
|
||||
; sta $05
|
||||
; sta $aa
|
||||
; lda #$00
|
||||
; tax
|
||||
; ora ($05,x)
|
||||
; brk
|
||||
; lda #$00
|
||||
; ora $05
|
||||
; brk
|
||||
; lda #$00
|
||||
; ora #$aa
|
||||
; brk
|
||||
; lda #$00
|
||||
; ora $0005
|
||||
; brk
|
||||
; lda #$05
|
||||
; sta $06
|
||||
; lda #$00
|
||||
; sta $07
|
||||
; tay
|
||||
; ora ($06),y
|
||||
; brk
|
||||
; lda #$00
|
||||
; tax
|
||||
; ora $05,x
|
||||
; brk
|
||||
; lda #$00
|
||||
; tay
|
||||
; ora $0005,y
|
||||
; brk
|
||||
; lda #$00
|
||||
; tax
|
||||
; ora $0005,x
|
||||
; brk
|
||||
ORG
|
||||
$0700
|
||||
$A9 $AA
|
||||
$85 $05
|
||||
$85 $AA
|
||||
$A9 $00
|
||||
$AA
|
||||
$01 $05
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$05 $05
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$09 $AA
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$0D $05 $00
|
||||
$00 $00
|
||||
$A9 $05
|
||||
$85 $06
|
||||
$A9 $00
|
||||
$85 $07
|
||||
$A8
|
||||
$11 $06
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$AA
|
||||
$15 $05
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$A8
|
||||
$19 $05 $00
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$AA
|
||||
$1D $05 $00
|
||||
$00 $00
|
||||
;
|
||||
; test program #6
|
||||
; Test JSR opcode.
|
||||
; After each break examine memory at $c000 and $c001.
|
||||
; After 1-st break, $c000 should equal $dd.
|
||||
; Return address-1 ($0802) should be on stack.
|
||||
; After 2-nd break, PC counter should be at $0805.
|
||||
; After 3-rd break, $c000 should equal $ee.
|
||||
; Return address-1 ($0807) should be on stack.
|
||||
; After 4-th break, PC counter should be at $080a.
|
||||
;
|
||||
; start:
|
||||
; jsr sub1
|
||||
; brk
|
||||
; jsr sub2
|
||||
; brk
|
||||
; brk
|
||||
; brk
|
||||
; sub1:
|
||||
; lda #$dd
|
||||
; sta $c000
|
||||
; brk
|
||||
; rts
|
||||
; sub2:
|
||||
; lda #$ee
|
||||
; sta $c000
|
||||
; brk
|
||||
; rts
|
||||
;
|
||||
ORG
|
||||
$0800
|
||||
$20 $0B $08
|
||||
$00 $00
|
||||
$20 $13 $08
|
||||
$00
|
||||
$00
|
||||
$00
|
||||
$A9 $DD
|
||||
$8D $00 $C0
|
||||
$00 $00
|
||||
$60
|
||||
$A9 $EE
|
||||
$8D $00 $C0
|
||||
$00 $00
|
||||
$60
|
||||
;
|
||||
; test program #7
|
||||
; Test ADC opcode.
|
||||
; Expected results:
|
||||
; First break: Acc=$01, Carry=1
|
||||
; 2-nd break: Acc=$02, Carry=1
|
||||
; 3-rd break: Acc=$22, Carry=0
|
||||
; 4-th break: Acc=$23, Carry=0
|
||||
;
|
||||
; start:
|
||||
; clc
|
||||
; lda #$ff
|
||||
; adc #$02
|
||||
; brk
|
||||
; sec
|
||||
; lda #$ff
|
||||
; adc #$02
|
||||
; brk
|
||||
; clc
|
||||
; lda #$20
|
||||
; adc #$02
|
||||
; brk
|
||||
; sec
|
||||
; lda #$20
|
||||
; adc #$02
|
||||
; brk
|
||||
;
|
||||
ORG
|
||||
$0900
|
||||
$18
|
||||
$A9 $FF
|
||||
$69 $02
|
||||
$00 $00
|
||||
$38
|
||||
$A9 $FF
|
||||
$69 $02
|
||||
$00 $00
|
||||
$18
|
||||
$A9 $20
|
||||
$69 $02
|
||||
$00 $00
|
||||
$38
|
||||
$A9 $20
|
||||
$69 $02
|
||||
$00 $00
|
||||
;
|
||||
; test program #8
|
||||
; Test ROR opcode.
|
||||
;
|
||||
; start:
|
||||
; sec
|
||||
; lda #$00
|
||||
; loop:
|
||||
; ror
|
||||
; brk
|
||||
; bcc loop ;(-5 -> $FB)
|
||||
; brk
|
||||
;
|
||||
ORG
|
||||
$0920
|
||||
$38
|
||||
$A9 $00
|
||||
$6A
|
||||
$00 $00
|
||||
$90 $FB
|
||||
$00 $00
|
||||
ORG
|
||||
$0200
|
||||
;
|
||||
; test program #1
|
||||
; address: $0200
|
||||
; load Acc with value 12
|
||||
; write Acc to address $c000 (49152)
|
||||
;
|
||||
; nop
|
||||
; nop
|
||||
; lda #$0c
|
||||
; sta $c000
|
||||
; brk
|
||||
;
|
||||
$EA $EA $A9 $0c $8D $00 $c0 $00 $00
|
||||
;
|
||||
; test program #2
|
||||
; address: $0400
|
||||
; copy 0-terminated string from
|
||||
; address $d000 to $0200
|
||||
; "Hello World!"
|
||||
;
|
||||
; ORG=$0400
|
||||
; hello:
|
||||
; ldx #0
|
||||
; loop:
|
||||
; lda $d000,x
|
||||
; beq $06 ;branch to end (+6) if A=0
|
||||
; sta $0200,x
|
||||
; inx
|
||||
; bne $f5 ; branch to loop (-11) if X<>0
|
||||
; end:
|
||||
; brk
|
||||
ORG
|
||||
$0400
|
||||
$A2 $00
|
||||
$BD $00 $d0
|
||||
$F0 $06
|
||||
$9D $00 $02
|
||||
$E8
|
||||
$D0 $F5
|
||||
$00 $00
|
||||
; data
|
||||
; address: $d000
|
||||
ORG
|
||||
$D000
|
||||
;DEC: 53248
|
||||
; "Hello World!"
|
||||
72 101 108 108 111 32 87 111 114 108 100 33 0
|
||||
;
|
||||
; test program #3 - copy Hello World! string to $0300
|
||||
; using different assembly instructions
|
||||
; address: $0500
|
||||
;
|
||||
; ORG=$0500 ;dec: 1280
|
||||
; hello:
|
||||
; lda #0
|
||||
; sta $05
|
||||
; ldx $05
|
||||
; loop:
|
||||
; lda $d000,x
|
||||
; sta $0300,x
|
||||
; beq end ;(+6)
|
||||
; inx
|
||||
; beq end ;(+3)
|
||||
; jmp loop
|
||||
; end:
|
||||
; brk
|
||||
ORG
|
||||
$0500
|
||||
;DEC: 1280
|
||||
$A9 $00
|
||||
$85 $05
|
||||
$A6 $05
|
||||
$BD $00 $d0
|
||||
$9D $00 $03
|
||||
$F0 $06
|
||||
$E8
|
||||
$F0 $03
|
||||
$4C $06 $05
|
||||
$00 $00
|
||||
;
|
||||
; test program #4
|
||||
; left-shift memory location $05 at zero page,
|
||||
; then location $06 using zero page indexed addressing,
|
||||
; then memory location $c001 (outside zero page) using absolute addressing
|
||||
; then location $c002 using indexed absolute addressing
|
||||
; and finally left-shift Acc.
|
||||
; stop after each step for debugging
|
||||
; exit loop when Acc=0
|
||||
;
|
||||
; start:
|
||||
; lda #$ff
|
||||
; ldx #$01
|
||||
; sta $05
|
||||
; sta $05,x
|
||||
; sta $c000,x
|
||||
; inx
|
||||
; sta $c000,x
|
||||
; ldx #$01
|
||||
; loop2:
|
||||
; brk
|
||||
; asl $05
|
||||
; asl $05,x
|
||||
; asl $c001
|
||||
; asl $c001,x
|
||||
; asl
|
||||
; bne loop2 ;(-15 or $f1)
|
||||
; brk
|
||||
ORG
|
||||
$0600
|
||||
$A9 $FF
|
||||
$A2 $01
|
||||
$85 $05
|
||||
$95 $05
|
||||
$9D $00 $C0
|
||||
$E8
|
||||
$9D $00 $C0
|
||||
$A2 $01
|
||||
$00 $00
|
||||
$06 $05
|
||||
$16 $05
|
||||
$0E $01 $C0
|
||||
$1E $01 $C0
|
||||
$0A
|
||||
$D0 $F1
|
||||
$00 $00
|
||||
;
|
||||
; test program #5
|
||||
; Test ORA opcode with various arguments and addressing modes.
|
||||
; At each break, the contents of Acc should equal $AA.
|
||||
;
|
||||
; start:
|
||||
; lda #$aa ;%10101010
|
||||
; sta $05
|
||||
; sta $aa
|
||||
; lda #$00
|
||||
; tax
|
||||
; ora ($05,x)
|
||||
; brk
|
||||
; lda #$00
|
||||
; ora $05
|
||||
; brk
|
||||
; lda #$00
|
||||
; ora #$aa
|
||||
; brk
|
||||
; lda #$00
|
||||
; ora $0005
|
||||
; brk
|
||||
; lda #$05
|
||||
; sta $06
|
||||
; lda #$00
|
||||
; sta $07
|
||||
; tay
|
||||
; ora ($06),y
|
||||
; brk
|
||||
; lda #$00
|
||||
; tax
|
||||
; ora $05,x
|
||||
; brk
|
||||
; lda #$00
|
||||
; tay
|
||||
; ora $0005,y
|
||||
; brk
|
||||
; lda #$00
|
||||
; tax
|
||||
; ora $0005,x
|
||||
; brk
|
||||
ORG
|
||||
$0700
|
||||
$A9 $AA
|
||||
$85 $05
|
||||
$85 $AA
|
||||
$A9 $00
|
||||
$AA
|
||||
$01 $05
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$05 $05
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$09 $AA
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$0D $05 $00
|
||||
$00 $00
|
||||
$A9 $05
|
||||
$85 $06
|
||||
$A9 $00
|
||||
$85 $07
|
||||
$A8
|
||||
$11 $06
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$AA
|
||||
$15 $05
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$A8
|
||||
$19 $05 $00
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$AA
|
||||
$1D $05 $00
|
||||
$00 $00
|
||||
;
|
||||
; test program #6
|
||||
; Test JSR opcode.
|
||||
; After each break examine memory at $c000 and $c001.
|
||||
; After 1-st break, $c000 should equal $dd.
|
||||
; Return address-1 ($0802) should be on stack.
|
||||
; After 2-nd break, PC counter should be at $0805.
|
||||
; After 3-rd break, $c000 should equal $ee.
|
||||
; Return address-1 ($0807) should be on stack.
|
||||
; After 4-th break, PC counter should be at $080a.
|
||||
;
|
||||
; start:
|
||||
; jsr sub1
|
||||
; brk
|
||||
; jsr sub2
|
||||
; brk
|
||||
; brk
|
||||
; brk
|
||||
; sub1:
|
||||
; lda #$dd
|
||||
; sta $c000
|
||||
; brk
|
||||
; rts
|
||||
; sub2:
|
||||
; lda #$ee
|
||||
; sta $c000
|
||||
; brk
|
||||
; rts
|
||||
;
|
||||
ORG
|
||||
$0800
|
||||
$20 $0B $08
|
||||
$00 $00
|
||||
$20 $13 $08
|
||||
$00
|
||||
$00
|
||||
$00
|
||||
$A9 $DD
|
||||
$8D $00 $C0
|
||||
$00 $00
|
||||
$60
|
||||
$A9 $EE
|
||||
$8D $00 $C0
|
||||
$00 $00
|
||||
$60
|
||||
;
|
||||
; test program #7
|
||||
; Test ADC opcode.
|
||||
; Expected results:
|
||||
; First break: Acc=$01, Carry=1
|
||||
; 2-nd break: Acc=$02, Carry=1
|
||||
; 3-rd break: Acc=$22, Carry=0
|
||||
; 4-th break: Acc=$23, Carry=0
|
||||
;
|
||||
; start:
|
||||
; clc
|
||||
; lda #$ff
|
||||
; adc #$02
|
||||
; brk
|
||||
; sec
|
||||
; lda #$ff
|
||||
; adc #$02
|
||||
; brk
|
||||
; clc
|
||||
; lda #$20
|
||||
; adc #$02
|
||||
; brk
|
||||
; sec
|
||||
; lda #$20
|
||||
; adc #$02
|
||||
; brk
|
||||
;
|
||||
ORG
|
||||
$0900
|
||||
$18
|
||||
$A9 $FF
|
||||
$69 $02
|
||||
$00 $00
|
||||
$38
|
||||
$A9 $FF
|
||||
$69 $02
|
||||
$00 $00
|
||||
$18
|
||||
$A9 $20
|
||||
$69 $02
|
||||
$00 $00
|
||||
$38
|
||||
$A9 $20
|
||||
$69 $02
|
||||
$00 $00
|
||||
;
|
||||
; test program #8
|
||||
; Test ROR opcode.
|
||||
;
|
||||
; start:
|
||||
; sec
|
||||
; lda #$00
|
||||
; loop:
|
||||
; ror
|
||||
; brk
|
||||
; bcc loop ;(-5 -> $FB)
|
||||
; brk
|
||||
;
|
||||
ORG
|
||||
$0920
|
||||
$38
|
||||
$A9 $00
|
||||
$6A
|
||||
$00 $00
|
||||
$90 $FB
|
||||
$00 $00
|
||||
;
|
630
dummy.rom
630
dummy.rom
@ -1,315 +1,317 @@
|
||||
;
|
||||
; test program #1
|
||||
; address: $0200
|
||||
; load Acc with value 12
|
||||
; write Acc to address $c000 (49152)
|
||||
;
|
||||
; nop
|
||||
; nop
|
||||
; lda #$0c
|
||||
; sta $c000
|
||||
; brk
|
||||
;
|
||||
$EA $EA $A9 $0c $8D $00 $c0 $00 $00
|
||||
;
|
||||
; test program #2
|
||||
; address: $0400
|
||||
; copy 0-terminated string from
|
||||
; address $d000 to $0200
|
||||
; "Hello World!"
|
||||
;
|
||||
; ORG=$0400
|
||||
; hello:
|
||||
; ldx #0
|
||||
; loop:
|
||||
; lda $d000,x
|
||||
; beq $06 ;branch to end (+6) if A=0
|
||||
; sta $0200,x
|
||||
; inx
|
||||
; bne $f5 ; branch to loop (-11) if X<>0
|
||||
; end:
|
||||
; brk
|
||||
ORG
|
||||
$0400
|
||||
$A2 $00
|
||||
$BD $00 $d0
|
||||
$F0 $06
|
||||
$9D $00 $02
|
||||
$E8
|
||||
$D0 $F5
|
||||
$00 $00
|
||||
; data
|
||||
; address: $d000
|
||||
ORG
|
||||
$D000
|
||||
;DEC: 53248
|
||||
; "Hello World!"
|
||||
72 101 108 108 111 32 87 111 114 108 100 33 0
|
||||
;
|
||||
; test program #3 - copy Hello World! string to $0300
|
||||
; using different assembly instructions
|
||||
; address: $0500
|
||||
;
|
||||
; ORG=$0500 ;dec: 1280
|
||||
; hello:
|
||||
; lda #0
|
||||
; sta $05
|
||||
; ldx $05
|
||||
; loop:
|
||||
; lda $d000,x
|
||||
; sta $0300,x
|
||||
; beq end ;(+6)
|
||||
; inx
|
||||
; beq end ;(+3)
|
||||
; jmp loop
|
||||
; end:
|
||||
; brk
|
||||
ORG
|
||||
$0500
|
||||
;DEC: 1280
|
||||
$A9 $00
|
||||
$85 $05
|
||||
$A6 $05
|
||||
$BD $00 $d0
|
||||
$9D $00 $03
|
||||
$F0 $06
|
||||
$E8
|
||||
$F0 $03
|
||||
$4C $06 $05
|
||||
$00 $00
|
||||
;
|
||||
; test program #4
|
||||
; left-shift memory location $05 at zero page,
|
||||
; then location $06 using zero page indexed addressing,
|
||||
; then memory location $c001 (outside zero page) using absolute addressing
|
||||
; then location $c002 using indexed absolute addressing
|
||||
; and finally left-shift Acc.
|
||||
; stop after each step for debugging
|
||||
; exit loop when Acc=0
|
||||
;
|
||||
; start:
|
||||
; lda #$ff
|
||||
; ldx #$01
|
||||
; sta $05
|
||||
; sta $05,x
|
||||
; sta $c000,x
|
||||
; inx
|
||||
; sta $c000,x
|
||||
; ldx #$01
|
||||
; loop2:
|
||||
; brk
|
||||
; asl $05
|
||||
; asl $05,x
|
||||
; asl $c001
|
||||
; asl $c001,x
|
||||
; asl
|
||||
; bne loop2 ;(-15 or $f1)
|
||||
; brk
|
||||
ORG
|
||||
$0600
|
||||
$A9 $FF
|
||||
$A2 $01
|
||||
$85 $05
|
||||
$95 $05
|
||||
$9D $00 $C0
|
||||
$E8
|
||||
$9D $00 $C0
|
||||
$A2 $01
|
||||
$00 $00
|
||||
$06 $05
|
||||
$16 $05
|
||||
$0E $01 $C0
|
||||
$1E $01 $C0
|
||||
$0A
|
||||
$D0 $F1
|
||||
$00 $00
|
||||
;
|
||||
; test program #5
|
||||
; Test ORA opcode with various arguments and addressing modes.
|
||||
; At each break, the contents of Acc should equal $AA.
|
||||
;
|
||||
; start:
|
||||
; lda #$aa ;%10101010
|
||||
; sta $05
|
||||
; sta $aa
|
||||
; lda #$00
|
||||
; tax
|
||||
; ora ($05,x)
|
||||
; brk
|
||||
; lda #$00
|
||||
; ora $05
|
||||
; brk
|
||||
; lda #$00
|
||||
; ora #$aa
|
||||
; brk
|
||||
; lda #$00
|
||||
; ora $0005
|
||||
; brk
|
||||
; lda #$05
|
||||
; sta $06
|
||||
; lda #$00
|
||||
; sta $07
|
||||
; tay
|
||||
; ora ($06),y
|
||||
; brk
|
||||
; lda #$00
|
||||
; tax
|
||||
; ora $05,x
|
||||
; brk
|
||||
; lda #$00
|
||||
; tay
|
||||
; ora $0005,y
|
||||
; brk
|
||||
; lda #$00
|
||||
; tax
|
||||
; ora $0005,x
|
||||
; brk
|
||||
ORG
|
||||
$0700
|
||||
$A9 $AA
|
||||
$85 $05
|
||||
$85 $AA
|
||||
$A9 $00
|
||||
$AA
|
||||
$01 $05
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$05 $05
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$09 $AA
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$0D $05 $00
|
||||
$00 $00
|
||||
$A9 $05
|
||||
$85 $06
|
||||
$A9 $00
|
||||
$85 $07
|
||||
$A8
|
||||
$11 $06
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$AA
|
||||
$15 $05
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$A8
|
||||
$19 $05 $00
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$AA
|
||||
$1D $05 $00
|
||||
$00 $00
|
||||
;
|
||||
; test program #6
|
||||
; Test JSR opcode.
|
||||
; After each break examine memory at $c000 and $c001.
|
||||
; After 1-st break, $c000 should equal $dd.
|
||||
; Return address-1 ($0802) should be on stack.
|
||||
; After 2-nd break, PC counter should be at $0805.
|
||||
; After 3-rd break, $c000 should equal $ee.
|
||||
; Return address-1 ($0807) should be on stack.
|
||||
; After 4-th break, PC counter should be at $080a.
|
||||
;
|
||||
; start:
|
||||
; jsr sub1
|
||||
; brk
|
||||
; jsr sub2
|
||||
; brk
|
||||
; brk
|
||||
; brk
|
||||
; sub1:
|
||||
; lda #$dd
|
||||
; sta $c000
|
||||
; brk
|
||||
; rts
|
||||
; sub2:
|
||||
; lda #$ee
|
||||
; sta $c000
|
||||
; brk
|
||||
; rts
|
||||
;
|
||||
ORG
|
||||
$0800
|
||||
$20 $0B $08
|
||||
$00 $00
|
||||
$20 $13 $08
|
||||
$00
|
||||
$00
|
||||
$00
|
||||
$A9 $DD
|
||||
$8D $00 $C0
|
||||
$00 $00
|
||||
$60
|
||||
$A9 $EE
|
||||
$8D $00 $C0
|
||||
$00 $00
|
||||
$60
|
||||
;
|
||||
; test program #7
|
||||
; Test ADC opcode.
|
||||
; Expected results:
|
||||
; First break: Acc=$01, Carry=1
|
||||
; 2-nd break: Acc=$02, Carry=1
|
||||
; 3-rd break: Acc=$22, Carry=0
|
||||
; 4-th break: Acc=$23, Carry=0
|
||||
;
|
||||
; start:
|
||||
; clc
|
||||
; lda #$ff
|
||||
; adc #$02
|
||||
; brk
|
||||
; sec
|
||||
; lda #$ff
|
||||
; adc #$02
|
||||
; brk
|
||||
; clc
|
||||
; lda #$20
|
||||
; adc #$02
|
||||
; brk
|
||||
; sec
|
||||
; lda #$20
|
||||
; adc #$02
|
||||
; brk
|
||||
;
|
||||
ORG
|
||||
$0900
|
||||
$18
|
||||
$A9 $FF
|
||||
$69 $02
|
||||
$00 $00
|
||||
$38
|
||||
$A9 $FF
|
||||
$69 $02
|
||||
$00 $00
|
||||
$18
|
||||
$A9 $20
|
||||
$69 $02
|
||||
$00 $00
|
||||
$38
|
||||
$A9 $20
|
||||
$69 $02
|
||||
$00 $00
|
||||
;
|
||||
; test program #8
|
||||
; Test ROR opcode.
|
||||
;
|
||||
; start:
|
||||
; sec
|
||||
; lda #$00
|
||||
; loop:
|
||||
; ror
|
||||
; brk
|
||||
; bcc loop ;(-5 -> $FB)
|
||||
; brk
|
||||
;
|
||||
ORG
|
||||
$0920
|
||||
$38
|
||||
$A9 $00
|
||||
$6A
|
||||
$00 $00
|
||||
$90 $FB
|
||||
$00 $00
|
||||
ORG
|
||||
$0200
|
||||
;
|
||||
; test program #1
|
||||
; address: $0200
|
||||
; load Acc with value 12
|
||||
; write Acc to address $c000 (49152)
|
||||
;
|
||||
; nop
|
||||
; nop
|
||||
; lda #$0c
|
||||
; sta $c000
|
||||
; brk
|
||||
;
|
||||
$EA $EA $A9 $0c $8D $00 $c0 $00 $00
|
||||
;
|
||||
; test program #2
|
||||
; address: $0400
|
||||
; copy 0-terminated string from
|
||||
; address $d000 to $0200
|
||||
; "Hello World!"
|
||||
;
|
||||
; ORG=$0400
|
||||
; hello:
|
||||
; ldx #0
|
||||
; loop:
|
||||
; lda $d000,x
|
||||
; beq $06 ;branch to end (+6) if A=0
|
||||
; sta $0200,x
|
||||
; inx
|
||||
; bne $f5 ; branch to loop (-11) if X<>0
|
||||
; end:
|
||||
; brk
|
||||
ORG
|
||||
$0400
|
||||
$A2 $00
|
||||
$BD $00 $d0
|
||||
$F0 $06
|
||||
$9D $00 $02
|
||||
$E8
|
||||
$D0 $F5
|
||||
$00 $00
|
||||
; data
|
||||
; address: $d000
|
||||
ORG
|
||||
$D000
|
||||
;DEC: 53248
|
||||
; "Hello World!"
|
||||
72 101 108 108 111 32 87 111 114 108 100 33 0
|
||||
;
|
||||
; test program #3 - copy Hello World! string to $0300
|
||||
; using different assembly instructions
|
||||
; address: $0500
|
||||
;
|
||||
; ORG=$0500 ;dec: 1280
|
||||
; hello:
|
||||
; lda #0
|
||||
; sta $05
|
||||
; ldx $05
|
||||
; loop:
|
||||
; lda $d000,x
|
||||
; sta $0300,x
|
||||
; beq end ;(+6)
|
||||
; inx
|
||||
; beq end ;(+3)
|
||||
; jmp loop
|
||||
; end:
|
||||
; brk
|
||||
ORG
|
||||
$0500
|
||||
;DEC: 1280
|
||||
$A9 $00
|
||||
$85 $05
|
||||
$A6 $05
|
||||
$BD $00 $d0
|
||||
$9D $00 $03
|
||||
$F0 $06
|
||||
$E8
|
||||
$F0 $03
|
||||
$4C $06 $05
|
||||
$00 $00
|
||||
;
|
||||
; test program #4
|
||||
; left-shift memory location $05 at zero page,
|
||||
; then location $06 using zero page indexed addressing,
|
||||
; then memory location $c001 (outside zero page) using absolute addressing
|
||||
; then location $c002 using indexed absolute addressing
|
||||
; and finally left-shift Acc.
|
||||
; stop after each step for debugging
|
||||
; exit loop when Acc=0
|
||||
;
|
||||
; start:
|
||||
; lda #$ff
|
||||
; ldx #$01
|
||||
; sta $05
|
||||
; sta $05,x
|
||||
; sta $c000,x
|
||||
; inx
|
||||
; sta $c000,x
|
||||
; ldx #$01
|
||||
; loop2:
|
||||
; brk
|
||||
; asl $05
|
||||
; asl $05,x
|
||||
; asl $c001
|
||||
; asl $c001,x
|
||||
; asl
|
||||
; bne loop2 ;(-15 or $f1)
|
||||
; brk
|
||||
ORG
|
||||
$0600
|
||||
$A9 $FF
|
||||
$A2 $01
|
||||
$85 $05
|
||||
$95 $05
|
||||
$9D $00 $C0
|
||||
$E8
|
||||
$9D $00 $C0
|
||||
$A2 $01
|
||||
$00 $00
|
||||
$06 $05
|
||||
$16 $05
|
||||
$0E $01 $C0
|
||||
$1E $01 $C0
|
||||
$0A
|
||||
$D0 $F1
|
||||
$00 $00
|
||||
;
|
||||
; test program #5
|
||||
; Test ORA opcode with various arguments and addressing modes.
|
||||
; At each break, the contents of Acc should equal $AA.
|
||||
;
|
||||
; start:
|
||||
; lda #$aa ;%10101010
|
||||
; sta $05
|
||||
; sta $aa
|
||||
; lda #$00
|
||||
; tax
|
||||
; ora ($05,x)
|
||||
; brk
|
||||
; lda #$00
|
||||
; ora $05
|
||||
; brk
|
||||
; lda #$00
|
||||
; ora #$aa
|
||||
; brk
|
||||
; lda #$00
|
||||
; ora $0005
|
||||
; brk
|
||||
; lda #$05
|
||||
; sta $06
|
||||
; lda #$00
|
||||
; sta $07
|
||||
; tay
|
||||
; ora ($06),y
|
||||
; brk
|
||||
; lda #$00
|
||||
; tax
|
||||
; ora $05,x
|
||||
; brk
|
||||
; lda #$00
|
||||
; tay
|
||||
; ora $0005,y
|
||||
; brk
|
||||
; lda #$00
|
||||
; tax
|
||||
; ora $0005,x
|
||||
; brk
|
||||
ORG
|
||||
$0700
|
||||
$A9 $AA
|
||||
$85 $05
|
||||
$85 $AA
|
||||
$A9 $00
|
||||
$AA
|
||||
$01 $05
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$05 $05
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$09 $AA
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$0D $05 $00
|
||||
$00 $00
|
||||
$A9 $05
|
||||
$85 $06
|
||||
$A9 $00
|
||||
$85 $07
|
||||
$A8
|
||||
$11 $06
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$AA
|
||||
$15 $05
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$A8
|
||||
$19 $05 $00
|
||||
$00 $00
|
||||
$A9 $00
|
||||
$AA
|
||||
$1D $05 $00
|
||||
$00 $00
|
||||
;
|
||||
; test program #6
|
||||
; Test JSR opcode.
|
||||
; After each break examine memory at $c000 and $c001.
|
||||
; After 1-st break, $c000 should equal $dd.
|
||||
; Return address-1 ($0802) should be on stack.
|
||||
; After 2-nd break, PC counter should be at $0805.
|
||||
; After 3-rd break, $c000 should equal $ee.
|
||||
; Return address-1 ($0807) should be on stack.
|
||||
; After 4-th break, PC counter should be at $080a.
|
||||
;
|
||||
; start:
|
||||
; jsr sub1
|
||||
; brk
|
||||
; jsr sub2
|
||||
; brk
|
||||
; brk
|
||||
; brk
|
||||
; sub1:
|
||||
; lda #$dd
|
||||
; sta $c000
|
||||
; brk
|
||||
; rts
|
||||
; sub2:
|
||||
; lda #$ee
|
||||
; sta $c000
|
||||
; brk
|
||||
; rts
|
||||
;
|
||||
ORG
|
||||
$0800
|
||||
$20 $0B $08
|
||||
$00 $00
|
||||
$20 $13 $08
|
||||
$00
|
||||
$00
|
||||
$00
|
||||
$A9 $DD
|
||||
$8D $00 $C0
|
||||
$00 $00
|
||||
$60
|
||||
$A9 $EE
|
||||
$8D $00 $C0
|
||||
$00 $00
|
||||
$60
|
||||
;
|
||||
; test program #7
|
||||
; Test ADC opcode.
|
||||
; Expected results:
|
||||
; First break: Acc=$01, Carry=1
|
||||
; 2-nd break: Acc=$02, Carry=1
|
||||
; 3-rd break: Acc=$22, Carry=0
|
||||
; 4-th break: Acc=$23, Carry=0
|
||||
;
|
||||
; start:
|
||||
; clc
|
||||
; lda #$ff
|
||||
; adc #$02
|
||||
; brk
|
||||
; sec
|
||||
; lda #$ff
|
||||
; adc #$02
|
||||
; brk
|
||||
; clc
|
||||
; lda #$20
|
||||
; adc #$02
|
||||
; brk
|
||||
; sec
|
||||
; lda #$20
|
||||
; adc #$02
|
||||
; brk
|
||||
;
|
||||
ORG
|
||||
$0900
|
||||
$18
|
||||
$A9 $FF
|
||||
$69 $02
|
||||
$00 $00
|
||||
$38
|
||||
$A9 $FF
|
||||
$69 $02
|
||||
$00 $00
|
||||
$18
|
||||
$A9 $20
|
||||
$69 $02
|
||||
$00 $00
|
||||
$38
|
||||
$A9 $20
|
||||
$69 $02
|
||||
$00 $00
|
||||
;
|
||||
; test program #8
|
||||
; Test ROR opcode.
|
||||
;
|
||||
; start:
|
||||
; sec
|
||||
; lda #$00
|
||||
; loop:
|
||||
; ror
|
||||
; brk
|
||||
; bcc loop ;(-5 -> $FB)
|
||||
; brk
|
||||
;
|
||||
ORG
|
||||
$0920
|
||||
$38
|
||||
$A9 $00
|
||||
$6A
|
||||
$00 $00
|
||||
$90 $FB
|
||||
$00 $00
|
||||
;
|
@ -15,6 +15,11 @@ $FFFF
|
||||
; Enable char IO and ROM
|
||||
ENIO
|
||||
ENROM
|
||||
; Enable generic graphics display
|
||||
; and set its base address.
|
||||
ENGRAPH
|
||||
GRAPHADDR
|
||||
$FFE2
|
||||
; Auto-execute
|
||||
;EXEC
|
||||
;$C000
|
||||
|
BIN
ehbas_grdemo.snap
Normal file
BIN
ehbas_grdemo.snap
Normal file
Binary file not shown.
276
main.cpp
276
main.cpp
@ -9,6 +9,8 @@
|
||||
#include "Memory.h"
|
||||
#include "Display.h"
|
||||
#include "VMachine.h"
|
||||
#include "GraphDisp.h"
|
||||
#include "MemMapDev.h"
|
||||
#include "MKGenException.h"
|
||||
|
||||
using namespace std;
|
||||
@ -20,8 +22,10 @@ const bool ClsIfDirty = true;
|
||||
|
||||
VMachine *pvm = NULL;
|
||||
Regs *preg = NULL;
|
||||
bool ioecho = false, opbrk = false;
|
||||
bool ioecho = false, opbrk = false, needhelp = false;
|
||||
bool loadbin = false, loadhex = false, reset = false, execvm = false;
|
||||
int g_stackdisp_lines = 1;
|
||||
string ramfile = "dummy.ram";
|
||||
|
||||
bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat);
|
||||
void ShowHelp();
|
||||
@ -69,58 +73,60 @@ void CopyrightBanner();
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: PrintLoadBinImgErr()
|
||||
* Purpose: Print the warning/error message after loading binary
|
||||
* image.
|
||||
* Method: PrintVMErr()
|
||||
* Purpose: Print the warning/error message.
|
||||
* Arguments: err - integer, error code
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void PrintLoadBinImgErr(int err)
|
||||
void PrintVMErr(int err)
|
||||
{
|
||||
bool pressenter = true;
|
||||
switch (err) {
|
||||
case 1: cout << "WARNING: Unexpected EOF (image shorter than 64kB)." << endl;
|
||||
break;
|
||||
case 2: cout << "WARNING: Unable to open memory image file." << endl;
|
||||
break;
|
||||
case 3: cout << "WARNING: Problem with binary image header." << endl;
|
||||
break;
|
||||
case 4: cout << "WARNING: No header found in binary image." << endl;
|
||||
break;
|
||||
case 5: cout << "WARNING: Problem with binary image header." << endl;
|
||||
cout << "WARNING: Unexpected EOF (image shorter than 64kB)." << endl;
|
||||
break;
|
||||
case 6: cout << "WARNING: No header found in binary image." << endl;
|
||||
cout << "WARNING: Unexpected EOF (image shorter than 64kB)." << endl;
|
||||
break;
|
||||
default: pressenter = false; break;
|
||||
}
|
||||
if (pressenter) {
|
||||
cout << "Press [ENTER]...";
|
||||
getchar();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: PrintLoadHexImgErr()
|
||||
* Purpose: Print the warning/error message after loading Intel HEX
|
||||
* image.
|
||||
* Arguments: err - integer, error code
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void PrintLoadHexImgErr(int err)
|
||||
{
|
||||
bool pressenter = true;
|
||||
switch (err) {
|
||||
case 1: cout << "WARNING: Unable to open file." << endl;
|
||||
break;
|
||||
case 2: cout << "ERROR: Syntax error." << endl;
|
||||
break;
|
||||
case 3: cout << "ERROR: Intel HEX format error." << endl;
|
||||
break;
|
||||
case MEMIMGERR_RAMBIN_EOF:
|
||||
cout << "WARNING: Unexpected EOF (image shorter than 64kB).";
|
||||
cout << endl;
|
||||
break;
|
||||
case MEMIMGERR_RAMBIN_OPEN:
|
||||
cout << "WARNING: Unable to open memory image file." << endl;
|
||||
break;
|
||||
case MEMIMGERR_RAMBIN_HDR:
|
||||
cout << "WARNING: Problem with binary image header." << endl;
|
||||
break;
|
||||
case MEMIMGERR_RAMBIN_NOHDR:
|
||||
cout << "WARNING: No header found in binary image." << endl;
|
||||
break;
|
||||
case MEMIMGERR_RAMBIN_HDRANDEOF:
|
||||
cout << "WARNING: Problem with binary image header." << endl;
|
||||
cout << "WARNING: Unexpected EOF (image shorter than 64kB).";
|
||||
cout << endl;
|
||||
break;
|
||||
case MEMIMGERR_RAMBIN_NOHDRANDEOF:
|
||||
cout << "WARNING: No header found in binary image." << endl;
|
||||
cout << "WARNING: Unexpected EOF (image shorter than 64kB).";
|
||||
cout << endl;
|
||||
break;
|
||||
case MEMIMGERR_INTELH_OPEN:
|
||||
cout << "WARNING: Unable to open Intel HEX file." << endl;
|
||||
break;
|
||||
case MEMIMGERR_INTELH_SYNTAX:
|
||||
cout << "ERROR: Syntax error." << endl;
|
||||
break;
|
||||
case MEMIMGERR_INTELH_FMT:
|
||||
cout << "ERROR: Intel HEX format error." << endl;
|
||||
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) {
|
||||
@ -278,17 +284,7 @@ bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat)
|
||||
cout << "| " << bitset<8>((int)preg->Flags) << " |";
|
||||
cout << " Last instr.: " << preg->LastInstr << " " << endl;
|
||||
cout << "*-------------*" << endl;
|
||||
//cout << "Last instr.: " << preg->LastInstr << " " << endl;
|
||||
cout << endl;
|
||||
/*
|
||||
cout << "Registers:" << endl;
|
||||
cout << " Acc: $" << hex << (unsigned short)preg->Acc << "\t(%" << bitset<8>((int)preg->Acc) << ")" << endl;
|
||||
cout << " X: $" << hex << (unsigned short)preg->IndX << " " << endl;
|
||||
cout << " Y: $" << hex << (unsigned short)preg->IndY << " " << endl;
|
||||
cout << " PC: $" << hex << preg->PtrAddr << " " << endl;
|
||||
//cout << " Acc16: $" << hex << preg->Acc16 << " " << endl;
|
||||
//cout << " Ptr16: $" << hex << preg->Ptr16 << " " << endl;
|
||||
*/
|
||||
cout << "Stack: $" << hex << (unsigned short)preg->PtrStack << " " << endl;
|
||||
cout << " \r";
|
||||
// display stack contents
|
||||
@ -317,11 +313,13 @@ bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat)
|
||||
cout << endl << "I/O status: " << (pvm->GetCharIOActive() ? "enabled" : "disabled") << ", ";
|
||||
cout << " at: $" << hex << pvm->GetCharIOAddr() << ", ";
|
||||
cout << " local echo: " << (ioecho ? "ON" : "OFF") << "." << endl;
|
||||
cout << "Graphics status: " << (pvm->GetGraphDispActive() ? "enabled" : "disabled") << ", ";
|
||||
cout << " at: $" << hex << pvm->GetGraphDispAddr() << endl;
|
||||
cout << "ROM: " << ((pvm->IsROMEnabled()) ? "enabled." : "disabled.") << " ";
|
||||
cout << "Range: $" << hex << pvm->GetROMBegin() << " - $" << hex << pvm->GetROMEnd() << "." << endl;
|
||||
}
|
||||
cout << " \r";
|
||||
// cout << "-------------------------------------------------------------------------------" << endl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -345,6 +343,7 @@ void ShowMenu()
|
||||
cout << " K - toggle ROM emulation | R - show registers, Y - snapshot" << endl;
|
||||
cout << " L - load memory image | O - display op-codes history" << endl;
|
||||
cout << " D - disassemble code in memory | Q - quit, 0 - reset, H - help" << endl;
|
||||
cout << " V - toggle graphics emulation |" << endl;
|
||||
cout << "------------------------------------+----------------------------------------" << endl;
|
||||
}
|
||||
|
||||
@ -393,10 +392,54 @@ void ShowMenu()
|
||||
} \
|
||||
}
|
||||
|
||||
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: LoadArgs()
|
||||
* Purpose: Parse command line arguments.
|
||||
* Arguments: int argc, char *argv[], standard C command line args.
|
||||
* Returns: n/a
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
void LoadArgs(int argc, char *argv[])
|
||||
{
|
||||
for (int i=1; i<argc; i++) {
|
||||
if (!strcmp(argv[i], "-r")) {
|
||||
reset = true;
|
||||
execvm = true;
|
||||
} else if (!strcmp(argv[i], "-b")) {
|
||||
loadbin = true;
|
||||
} else if (!strcmp(argv[i], "-x")) {
|
||||
loadhex = true;
|
||||
} else if (!strcmp(argv[i], "-h")) {
|
||||
needhelp = true;
|
||||
} else {
|
||||
ramfile = argv[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
bool loadbin = false, loadhex = false, reset = false, execvm = false;
|
||||
|
||||
/************ corrected in makefile
|
||||
|
||||
// Quick and dirty SDL2 workaround to 'undefined reference to WinMain'
|
||||
|
||||
#ifdef main
|
||||
#undef main
|
||||
#endif
|
||||
|
||||
*****************/
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------
|
||||
* Method: main()
|
||||
* Purpose: Application entry point/main loop.
|
||||
* Arguments: int argc, char *argv[], standard C command line args.
|
||||
* Returns: int - general principle is to return 0 if OK, non-zero
|
||||
* otherwise
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
#if defined(LINUX)
|
||||
signal(SIGINT, trap_signal);
|
||||
signal(SIGTERM, trap_signal);
|
||||
@ -404,41 +447,29 @@ int main(int argc, char** argv) {
|
||||
#if defined(WINDOWS)
|
||||
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE );
|
||||
#endif
|
||||
string romfile("dummy.rom"), ramfile("dummy.ram");
|
||||
if (argc > 1) {
|
||||
if (argc > 2) {
|
||||
reset = execvm = loadbin = (0 == strcmp(argv[1], "-r")); // load binary image and reset
|
||||
if (!loadbin) loadbin = (0 == strcmp(argv[1], "-b")); // just load binary image
|
||||
if (!loadbin) loadhex = (0 == strcmp(argv[1], "-x")); // just load Intel HEX image
|
||||
if (loadbin && loadhex) {
|
||||
cout << "ERROR: Can't load both formats at the same time." << endl;
|
||||
exit(-1);
|
||||
}
|
||||
ramfile = argv[2];
|
||||
} else {
|
||||
if (0 == strcmp(argv[1], "-h")) {
|
||||
CmdArgHelp(argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
ramfile = argv[1];
|
||||
}
|
||||
}
|
||||
string romfile("dummy.rom");
|
||||
LoadArgs(argc, argv);
|
||||
if (needhelp) { CmdArgHelp(argv[0]); exit(0); }
|
||||
if (loadbin && loadhex) {
|
||||
cout << "ERROR: Can't load both formats at the same time." << endl;
|
||||
exit(-1);
|
||||
}
|
||||
try {
|
||||
cout << endl;
|
||||
if (loadbin) {
|
||||
pvm = new VMachine(romfile, "dummy.ram");
|
||||
if (NULL != pvm) {
|
||||
PrintLoadBinImgErr (pvm->LoadRAMBin(ramfile));
|
||||
if (!reset && !execvm)
|
||||
reset = execvm = pvm->IsAutoReset();
|
||||
PrintVMErr (pvm->LoadRAMBin(ramfile));
|
||||
if (!reset) { reset = execvm = pvm->IsAutoReset(); }
|
||||
}
|
||||
} else if (loadhex) {
|
||||
pvm = new VMachine(romfile, "dummy.ram");
|
||||
if (NULL != pvm) PrintLoadHexImgErr (pvm->LoadRAMHex(ramfile));
|
||||
if (NULL != pvm) PrintVMErr (pvm->LoadRAMHex(ramfile));
|
||||
}
|
||||
else {
|
||||
pvm = new VMachine(romfile, ramfile);
|
||||
reset = execvm = pvm->IsAutoReset();
|
||||
if (NULL != pvm) PrintVMErr(pvm->GetLastError());
|
||||
if (NULL != pvm && !reset) { reset = execvm = pvm->IsAutoReset(); }
|
||||
}
|
||||
if (NULL == pvm) {
|
||||
throw MKGenException("Out of memory");
|
||||
@ -449,6 +480,7 @@ int main(int argc, char** argv) {
|
||||
bool runvm = false, step = false, brk = false, execaddr = false, stop = true;
|
||||
bool lrts = false, anim = false, enrom = pvm->IsROMEnabled();
|
||||
unsigned int newaddr = pvm->GetRunAddr(), ioaddr = pvm->GetCharIOAddr(), tmpaddr = 0x0000;
|
||||
unsigned int graddr = pvm->GetGraphDispAddr();
|
||||
unsigned int rombegin = pvm->GetROMBegin(), romend = pvm->GetROMEnd(), delay = ANIM_DELAY;
|
||||
int nsteps = 0;
|
||||
if (pvm->IsAutoExec()) {
|
||||
@ -538,18 +570,33 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
} else if (c == 'l') { // load memory image
|
||||
char typ = 0;
|
||||
while (tolower(typ) != 'b' && tolower(typ) != 'd' && tolower(typ) != 'h') {
|
||||
cout << "Type (B - binary/H - Intel HEX/D - definition): ";
|
||||
for (char c = tolower(typ);
|
||||
c != 'a' && c != 'b' && c != 'h' && c != 'd';
|
||||
c = tolower(typ)) {
|
||||
cout << "Type (A - auto/B - binary/H - Intel HEX/D - definition): ";
|
||||
cin >> typ;
|
||||
}
|
||||
cout << " [" << ((tolower(typ) == 'b') ? "binary" : "definition") << "]" << endl;
|
||||
cout << " [";
|
||||
switch (tolower(typ)) {
|
||||
case 'a': cout << "auto"; break;
|
||||
case 'b': cout << "binary"; break;
|
||||
case 'h': cout << "Intel HEX"; break;
|
||||
case 'd': cout << "definition"; break;
|
||||
default: break; // should never happen
|
||||
}
|
||||
cout << "]" << endl;
|
||||
string name;
|
||||
cout << "Memory Image File Name: ";
|
||||
cin >> name;
|
||||
cout << " [" << name << "]" << endl;
|
||||
if (typ == 'b') PrintLoadBinImgErr (pvm->LoadRAMBin(name));
|
||||
else if (typ == 'h') PrintLoadHexImgErr (pvm->LoadRAMHex(name));
|
||||
else {
|
||||
if (typ == 'b') PrintVMErr (pvm->LoadRAMBin(name));
|
||||
else if (typ == 'h') PrintVMErr (pvm->LoadRAMHex(name));
|
||||
else if (typ == 'd') {
|
||||
PrintVMErr (pvm->LoadRAMDef(name));
|
||||
if (pvm->IsAutoExec()) execvm = true;
|
||||
if (newaddr == 0) newaddr = 0x10000;
|
||||
}
|
||||
else { // automatic file format detection
|
||||
pvm->LoadRAM(name);
|
||||
if (pvm->IsAutoExec()) execvm = true;
|
||||
if (newaddr == 0) newaddr = 0x10000;
|
||||
@ -603,10 +650,20 @@ int main(int argc, char** argv) {
|
||||
cout << "I/O deactivated." << endl;
|
||||
} else {
|
||||
ioaddr = PromptNewAddress("Address (0..FFFF): ");
|
||||
cout << " [" << hex << ioaddr << "]" << endl;
|
||||
cout << " [" << hex << ioaddr << "]" << endl;
|
||||
pvm->SetCharIO(ioaddr, ioecho);
|
||||
cout << "I/O activated." << endl;
|
||||
}
|
||||
} else if (c == 'v') { // toggle graphics display
|
||||
if (pvm->GetGraphDispActive()) {
|
||||
pvm->DisableGraphDisp();
|
||||
cout << "Graphics display deactivated." << endl;
|
||||
} else {
|
||||
graddr = PromptNewAddress("Address (0..FFFF): ");
|
||||
cout << " [" << hex << graddr << "]" << endl;
|
||||
pvm->SetGraphDisp(graddr);
|
||||
cout << "Graphics display activated." << endl;
|
||||
}
|
||||
} else if (c == 'w') { // write to memory
|
||||
tmpaddr = PromptNewAddress("Address (0..FFFF): ");
|
||||
cout << " [" << hex << tmpaddr << "]" << endl;
|
||||
@ -730,27 +787,26 @@ void CmdArgHelp(string prgname)
|
||||
cout << endl << endl;
|
||||
cout << "Usage:" << endl << endl;
|
||||
cout << "\t" << prgname;
|
||||
cout << " [-h] | [ramdeffile] | [-b ramimage] | [-r ramimage]" << endl;
|
||||
cout << "\tOR" << endl;
|
||||
cout << "\t" << prgname << " [-x intelheximage]";
|
||||
cout << " [-h] | [ramdeffile] [-b | -x] [-r]" << endl;
|
||||
cout << endl << endl;
|
||||
cout << "Where:" << endl << endl;
|
||||
cout << "\tramdeffile - RAM definition file name" << endl;
|
||||
cout << "\tintelheximage - Intel HEX format file" << endl;
|
||||
cout << "\tramimage - RAM binary image file name" << endl;
|
||||
cout << "\t-b - specify input format as binary" << endl;
|
||||
cout << "\t-x - specify input format as Intel HEX" << endl;
|
||||
cout << "\t-r - after loading, perform CPU RESET" << endl;
|
||||
cout << "\t-h - print this help screen" << endl;
|
||||
cout << R"(
|
||||
|
||||
When ran with no arguments, program will load default memory
|
||||
definition files: default.rom, default.ram and will enter the debug
|
||||
console menu.
|
||||
When ramdeffile argument is provided, program will load the memory
|
||||
definition from the file, set the flags and parameters depending on the
|
||||
contents of the memory definition file and enter the corresponding mode
|
||||
of operation as defined in that file.
|
||||
If used with flag -b or -x, program will load memory from the provided image
|
||||
file and enter the debug console menu.
|
||||
If used with flag -r, program will load memory from the provided image
|
||||
file and execute CPU reset sequence.
|
||||
When ramdeffile argument is provided with no input format specified,
|
||||
program will attempt to automatically detect input format and load the
|
||||
memory definition from the file, set the flags and parameters depending
|
||||
on the contents of the memory definition file and enter the corresponding
|
||||
mode of operation as defined in that file.
|
||||
If input format is specified (-b|-x), program will load memory from the
|
||||
provided image file and enter the debug console menu.
|
||||
|
||||
)";
|
||||
cout << endl;
|
||||
@ -809,6 +865,14 @@ I - toggle char I/O emulation
|
||||
to the specified memory address also writes a character code to
|
||||
to a virtual console. All reads from specified memory address
|
||||
are interpreted as console character input.
|
||||
V - toggle graphics display (video) emulation
|
||||
Usage: V [address]
|
||||
Where: address - memory addr. in hexadecimal format [0000.FFFF],
|
||||
Toggles basic raster (pixel) based RGB graphics display emulation.
|
||||
When enabled, window with graphics screen will open and several
|
||||
registers are available to control the device starting at provided
|
||||
base address. Read programmers reference for detailed documentation
|
||||
regarding the available registers and their functions.
|
||||
R - show registers
|
||||
Displays CPU registers, flags and stack.
|
||||
Y - snapshot
|
||||
@ -845,10 +909,12 @@ K - toggle ROM emulation
|
||||
L - load memory image
|
||||
Usage: L [image_type] [image_name]
|
||||
Where:
|
||||
image_type - B (binary), H (Intel HEX) OR D (definition),
|
||||
image_type - A - (auto), B (binary), H (Intel HEX) OR D (definition),
|
||||
image_name - name of the image file.
|
||||
This function allows to load new memory image from either binary
|
||||
image file, Intel HEX format file or the ASCII definition file.
|
||||
With option 'A' selected, automatic input format detection will be
|
||||
attempted.
|
||||
The binary image is always loaded from address 0x0000 and can be up to
|
||||
64kB long. The definition file format is a plain text file that can
|
||||
contain following keywords and data:
|
||||
|
@ -2,19 +2,23 @@
|
||||
# Makefile created by Dev-C++ 5.11
|
||||
# and modified for standalone MINGW compiler installation.
|
||||
|
||||
#SDLBASE = "C:\src\SDL"
|
||||
SDLBASE = $(SDLDIR)
|
||||
CPP = g++.exe -D__DEBUG__
|
||||
CC = gcc.exe -D__DEBUG__
|
||||
WINDRES = windres.exe
|
||||
OBJ = main.o VMachine.o MKBasic.o MKCpu.o Memory.o Display.o MKGenException.o
|
||||
OBJ = main.o VMachine.o MKBasic.o MKCpu.o Memory.o Display.o GraphDisp.o MemMapDev.o MKGenException.o
|
||||
OBJ2 = bin2hex.o
|
||||
LINKOBJ = main.o VMachine.o MKBasic.o MKCpu.o Memory.o Display.o MKGenException.o
|
||||
LINKOBJ = main.o VMachine.o MKBasic.o MKCpu.o Memory.o Display.o GraphDisp.o MemMapDev.o MKGenException.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
|
||||
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
|
||||
INCS = -I"C:\mingw-w64\x86_64-5.3.0\mingw64/include" -I"C:\mingw-w64\x86_64-5.3.0\mingw64\x86_64-w64-mingw32/include" -I"C:\mingw-w64\x86_64-5.3.0\mingw64\lib\gcc\x86_64-w64-mingw32\5.3.0/include"
|
||||
CXXINCS = -I"C:\mingw-w64\x86_64-5.3.0\mingw64/include" -I"C:\mingw-w64\x86_64-5.3.0\mingw64\x86_64-w64-mingw32/include" -I"C:\mingw-w64\x86_64-5.3.0\mingw64\lib\gcc\x86_64-w64-mingw32\5.3.0/include"
|
||||
BIN = mkbasic.exe
|
||||
BIN = vm65.exe
|
||||
BIN2 = bin2hex.exe
|
||||
CXXFLAGS = $(CXXINCS) -std=c++11 -Wall -Wextra -pedantic -g3
|
||||
SDLINCS = -I"$(SDLBASE)/include"
|
||||
CFLAGS = $(INCS) -std=c++11 -Wall -Wextra -pedantic -g3
|
||||
CXXFLAGS2 = $(CXXINCS)
|
||||
CFLAGS2 = $(INCS)
|
||||
@ -28,27 +32,33 @@ clean: clean-custom
|
||||
${RM} $(OBJ) $(OBJ2) $(BIN) $(BIN2)
|
||||
|
||||
$(BIN): $(OBJ)
|
||||
$(CPP) $(LINKOBJ) -o $(BIN) $(LIBS)
|
||||
$(CPP) $(LINKOBJ) -o $(BIN) $(LIBS) $(SDLLIBS)
|
||||
|
||||
main.o: main.cpp
|
||||
$(CPP) -c main.cpp -o main.o $(CXXFLAGS)
|
||||
$(CPP) -c main.cpp -o main.o $(CXXFLAGS) $(SDLINCS)
|
||||
|
||||
VMachine.o: VMachine.cpp
|
||||
$(CPP) -c VMachine.cpp -o VMachine.o $(CXXFLAGS)
|
||||
VMachine.o: VMachine.cpp VMachine.h
|
||||
$(CPP) -c VMachine.cpp -o VMachine.o $(CXXFLAGS) $(SDLINCS)
|
||||
|
||||
MKBasic.o: MKBasic.cpp
|
||||
$(CPP) -c MKBasic.cpp -o MKBasic.o $(CXXFLAGS)
|
||||
MKBasic.o: MKBasic.cpp MKBasic.h
|
||||
$(CPP) -c MKBasic.cpp -o MKBasic.o $(CXXFLAGS) $(SDLINCS)
|
||||
|
||||
MKCpu.o: MKCpu.cpp
|
||||
$(CPP) -c MKCpu.cpp -o MKCpu.o $(CXXFLAGS)
|
||||
MKCpu.o: MKCpu.cpp MKCpu.h
|
||||
$(CPP) -c MKCpu.cpp -o MKCpu.o $(CXXFLAGS) $(SDLINCS)
|
||||
|
||||
Memory.o: Memory.cpp
|
||||
$(CPP) -c Memory.cpp -o Memory.o $(CXXFLAGS)
|
||||
Memory.o: Memory.cpp Memory.h
|
||||
$(CPP) -c Memory.cpp -o Memory.o $(CXXFLAGS) $(SDLINCS)
|
||||
|
||||
Display.o: Display.cpp
|
||||
Display.o: Display.cpp Display.h
|
||||
$(CPP) -c Display.cpp -o Display.o $(CXXFLAGS)
|
||||
|
||||
MKGenException.o: MKGenException.cpp
|
||||
GraphDisp.o: GraphDisp.cpp GraphDisp.h
|
||||
$(CPP) -c GraphDisp.cpp -o GraphDisp.o $(CXXFLAGS) $(SDLINCS)
|
||||
|
||||
MemMapDev.o: MemMapDev.cpp MemMapDev.h
|
||||
$(CPP) -c MemMapDev.cpp -o MemMapDev.o $(CXXFLAGS) $(SDLINCS)
|
||||
|
||||
MKGenException.o: MKGenException.cpp MKGenException.h
|
||||
$(CPP) -c MKGenException.cpp -o MKGenException.o $(CXXFLAGS)
|
||||
|
||||
$(BIN2): $(OBJ2)
|
||||
|
BIN
tb.snap
Normal file
BIN
tb.snap
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user