#include "MemMapDev.h" #include "Memory.h" #include "MKGenException.h" #include #include #if defined(WINDOWS) #include #endif //#define DBG 1 #if defined (DBG) #include using namespace std; #endif /* *-------------------------------------------------------------------- * Method: * Purpose: * Arguments: * Returns: *-------------------------------------------------------------------- */ namespace MKBasic { /* *-------------------------------------------------------------------- * Method: 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 #include #include 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