aboutsummaryrefslogtreecommitdiff
path: root/SrcShared/Hardware/EmRegs328.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SrcShared/Hardware/EmRegs328.cpp')
-rw-r--r--SrcShared/Hardware/EmRegs328.cpp2803
1 files changed, 2803 insertions, 0 deletions
diff --git a/SrcShared/Hardware/EmRegs328.cpp b/SrcShared/Hardware/EmRegs328.cpp
new file mode 100644
index 0000000..0f33398
--- /dev/null
+++ b/SrcShared/Hardware/EmRegs328.cpp
@@ -0,0 +1,2803 @@
+/* -*- mode: C++; tab-width: 4 -*- */
+/* ===================================================================== *\
+ Copyright (c) 2000-2001 Palm, Inc. or its subsidiaries.
+ All rights reserved.
+
+ This file is part of the Palm OS Emulator.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+\* ===================================================================== */
+
+#include "EmCommon.h"
+#include "EmRegs328.h"
+#include "EmRegs328Prv.h"
+
+#include "Byteswapping.h" // Canonical
+#include "EmHAL.h" // EmHAL
+#include "EmMemory.h" // gMemAccessFlags, EmMem_memcpy
+#include "EmPixMap.h" // SetSize, SetRowBytes, etc.
+#include "EmScreen.h" // EmScreenUpdateInfo
+#include "EmSession.h" // GetDevice
+#include "Hordes.h" // Hordes::IsOn
+#include "Logging.h" // LogAppendMsg
+#include "Miscellaneous.h" // GetHostTime
+#include "PreferenceMgr.h" // Preference
+#include "SessionFile.h" // WriteHwrDBallType, etc.
+#include "UAE.h" // regs, SPCFLAG_INT
+
+#include "PalmPack.h"
+#define NON_PORTABLE
+ #include "HwrMiscFlags.h" // hwrMiscFlagID1
+
+ // Some platform-specific -- yet fairly portable -- defines.
+ #define hwrTD1PortENoBacklight 0x80 // (H) high if no backlight present
+
+#undef NON_PORTABLE
+#include "PalmPackPop.h"
+
+/*
+ This file controls the emulation of the Dragonball registers.
+
+ As a subclass of EmRegs, EmRegs328 registers with EmBankRegs
+ for control of the memory range 0xFFFFF000 to 0xFFFFFB14. If
+ an application accesses a memory location in that range,
+ EmBankRegs calls on of the Set/GetLong/Word/Byte methods of
+ this class.
+
+ EmRegs328 provides handlers when particular memory addresses
+ are accessed. For instance, if the UART TX register is written
+ to, EmRegs328 will arrange for any data byte to be sent out
+ the host computer's serial port. If the UART RX register is
+ read, EmRegs328 will respond with any byte in the buffer that
+ contains data received from the host computer's serial port.
+
+ Not all Dragonball registers are emulated the same way as on
+ an actual device. Some registers control hardware that exists
+ on the device only, there being no analog on the host computer.
+ For those registers, simple handlers are installed that write
+ the specified value to the register and return that value later
+ when the register is read.
+
+ Other registers require extensive support. The UART registers
+ are examples of that, as the above text indicates.
+
+ In the Palm OS source code, the Dragonball registers are
+ represented by th HwrM68328Type data type. We use this same type
+ in Poser to provide the "backing memory" for the registers. That
+ is, if some emulated code writes to a Dragonball register, we
+ store that value in a variable of type HwrM68328Type.
+
+ (Note that there's really no guarantee that the compiler used to
+ build Poser will lay out the fields of HwrM68328Type in the same
+ way expected by the Palm OS. Poser has a mechanism for creating
+ buffers with fields that have the same layout as Palm OS-defined
+ structs (see EmPalmStructs.h). However, using that mechanism in
+ this module is deadly to performance. In particular, the Cycle
+ method is called after every opcode is executed, and making that
+ method go through the indirection and byteswapping required by
+ the EmPalmStructs facilities totally kills performance. In one
+ test, a Gremlins run increased from 1m 24s to 1m 56s, or by about
+ 30%. With targetted caching of the fields used in Cycle, we can
+ regain most of that performance. However, not all the performance
+ is regained, and the resulting code is not very maintainable.)
+*/
+
+ #define hwr328chipID328 0x33
+ #define hwr328maskID1H58B 0x30
+
+static const uint32 ADDRESS_MASK = 0x0000FFF0;
+
+#define PRINTF if (1) ; else LogAppendMsg
+
+// Values used to initialize the DragonBall registers.
+
+const HwrM68328Type kInitial68328RegisterValues =
+{
+ 0x0C, // Byte scr; // $000: System Control Register
+ { 0 }, // Byte ___filler0[0x004-0x001];
+
+ // The following ID stuff is not present on earlier chips (before ??)
+ hwr328chipID328, // Byte chipID; // $004: Chip ID Register
+ hwr328maskID1H58B, // Byte maskID; // $005: Mask ID Register
+ 0x00, // Word swID; // $006: Software ID Register
+ { 0 }, // Byte ___filler1[0x100-0x008];
+
+ 0x0000, // Word csAGroupBase; // $100: Chip Select Group A Base Register
+ 0x0000, // Word csBGroupBase; // $102: Chip Select Group B Base Register
+ 0x0000, // Word csCGroupBase; // $104: Chip Select Group C Base Register
+ 0x0000, // Word csDGroupBase; // $106: Chip Select Group D Base Register
+
+ 0x0000, // Word csAGroupMask; // $108: Chip Select Group A Mask Register
+ 0x0000, // Word csBGroupMask; // $10A: Chip Select Group B Mask Register
+ 0x0000, // Word csCGroupMask; // $10C: Chip Select Group C Mask Register
+ 0x0000, // Word csDGroupMask; // $10E: Chip Select Group D Mask Register
+
+ 0x00010006, // DWord csASelect0; // $110: Group A Chip Select 0 Register
+ 0x00010006, // DWord csASelect1; // $114: Group A Chip Select 1 Register
+ 0x00010006, // DWord csASelect2; // $118: Group A Chip Select 2 Register
+ 0x00010006, // DWord csASelect3; // $11C: Group A Chip Select 3 Register
+
+ 0x00000000, // DWord csBSelect0; // $120: Group B Chip Select 0 Register
+ 0x00000000, // DWord csBSelect1; // $124: Group B Chip Select 1 Register
+ 0x00000000, // DWord csBSelect2; // $128: Group B Chip Select 2 Register
+ 0x00000000, // DWord csBSelect3; // $12C: Group B Chip Select 3 Register
+
+ 0x00010006, // DWord csCSelect0; // $130: Group C Chip Select 0 Register
+ 0x00010006, // DWord csCSelect1; // $134: Group C Chip Select 1 Register
+ 0x00010006, // DWord csCSelect2; // $138: Group C Chip Select 2 Register
+ 0x00010006, // DWord csCSelect3; // $13C: Group C Chip Select 3 Register
+
+ 0x00000000, // DWord csDSelect0; // $140: Group D Chip Select 0 Register
+ 0x00000000, // DWord csDSelect1; // $144: Group D Chip Select 1 Register
+ 0x00000000, // DWord csDSelect2; // $148: Group D Chip Select 2 Register
+ 0x00000000, // DWord csDSelect3; // $14C: Group D Chip Select 3 Register
+
+ 0x0000, // Word csDebug; // $150: Chip Select debug register
+ { 0 }, // Byte ___filler2[0x200-0x152];
+
+ 0x2400, // Word pllControl; // $200: PLL Control Register
+ 0x0123, // Word pllFreqSel; // $202: PLL Frequency Select Register
+ 0x0000, // Word pllTest; // $204: PLL Test Register
+ { 0 }, // Byte __filler44;
+ 0x1F, // Byte pwrControl; // $207: Power Control Register
+
+ { 0 }, // Byte ___filler3[0x300-0x208];
+
+ 0x00, // Byte intVector; // $300: Interrupt Vector Register
+ { 0 }, // Byte ___filler4;
+ 0x0000, // Word intControl; // $302: Interrupt Control Register
+ 0x00FF, // Word intMaskHi; // $304: Interrupt Mask Register/HIGH word
+ 0xFFFF, // Word intMaskLo; // $306: Interrupt Mask Register/LOW word
+ 0x00FF, // Word intWakeupEnHi; // $308: Interrupt Wakeup Enable Register
+ 0xFFFF, // Word intWakeupEnLo; // $30A: Interrupt Wakeup Enable Register
+ 0x0000, // Word intStatusHi; // $30C: Interrupt Status Register/HIGH word
+ 0x0000, // Word intStatusLo; // $30E: Interrupt Status Register/LOW word
+ 0x0000, // Word intPendingHi; // $310: Interrupt Pending Register
+ 0x0000, // Word intPendingLo; // $312: Interrupt Pending Register
+
+ { 0 }, // Byte ___filler4a[0x400-0x314];
+
+ 0x00, // Byte portADir; // $400: Port A Direction Register
+ 0x00, // Byte portAData; // $401: Port A Data Register
+ { 0 }, // Byte ___filler5;
+ 0x00, // Byte portASelect; // $403: Port A Select Register
+
+ { 0 }, // Byte ___filler6[4];
+
+ 0x00, // Byte portBDir; // $408: Port B Direction Register
+ 0x00, // Byte portBData; // $409: Port B Data Register
+ { 0 }, // Byte ___filler7;
+ 0x00, // Byte portBSelect; // $40B: Port B Select Register
+
+ { 0 }, // Byte ___filler8[4];
+
+ 0x00, // Byte portCDir; // $410: Port C Direction Register
+ 0x00, // Byte portCData; // $411: Port C Data Register
+ { 0 }, // Byte ___filler9;
+ 0x00, // Byte portCSelect; // $413: Port C Select Register
+
+ { 0 }, // Byte ___filler10[4];
+
+ 0x00, // Byte portDDir; // $418: Port D Direction Register
+ 0x00, // Byte portDData; // $419: Port D Data Register
+ 0xFF, // Byte portDPullupEn; // $41A: Port D Pull-up Enable
+ { 0 }, // Byte ___filler11;
+ 0x00, // Byte portDPolarity; // $41C: Port D Polarity Register
+ 0x00, // Byte portDIntReqEn; // $41D: Port D Interrupt Request Enable
+ { 0 }, // Byte ___filler12;
+ 0x00, // Byte portDIntEdge; // $41F: Port D IRQ Edge Register
+
+ 0x00, // Byte portEDir; // $420: Port E Direction Register
+ 0x00, // Byte portEData; // $421: Port E Data Register
+ 0x80, // Byte portEPullupEn; // $422: Port E Pull-up Enable
+ 0x80, // Byte portESelect; // $423: Port E Select Register
+
+ { 0 }, // Byte ___filler14[4];
+
+ 0x00, // Byte portFDir; // $428: Port F Direction Register
+ 0x00, // Byte portFData; // $429: Port F Data Register
+ 0xFF, // Byte portFPullupEn; // $42A: Port F Pull-up Enable
+ 0xFF, // Byte portFSelect; // $42B: Port F Select Register
+
+ { 0 }, // Byte ___filler16[4];
+
+ 0x00, // Byte portGDir; // $430: Port G Direction Register
+ 0x00, // Byte portGData; // $431: Port G Data Register
+ 0xFF, // Byte portGPullupEn; // $432: Port G Pull-up Enable
+ 0xFF, // Byte portGSelect; // $433: Port G Select Register
+
+ { 0 }, // Byte ___filler18[4];
+
+ 0x00, // Byte portJDir; // $438: Port J Direction Register
+ 0x00, // Byte portJData; // $439: Port J Data Register
+ { 0 }, // Byte ___filler19;
+ 0x00, // Byte portJSelect; // $43B: Port J Select Register
+
+ { 0 }, // Byte ___filler19a[4];
+
+ 0x00, // Byte portKDir; // $440: Port K Direction Register
+ 0x00, // Byte portKData; // $441: Port K Data Register
+ 0x3F, // Byte portKPullupEn; // $442: Port K Pull-up Enable
+ 0x3F, // Byte portKSelect; // $443: Port K Select Register
+
+ { 0 }, // Byte ___filler21[4];
+
+ 0x00, // Byte portMDir; // $448: Port M Direction Register
+ 0x00, // Byte portMData; // $449: Port M Data Register
+ 0xFF, // Byte portMPullupEn; // $44A: Port M Pull-up Enable Register
+ 0x02, // Byte portMSelect; // $44B: Port M Select Register
+
+ { 0 }, // Byte ___filler22[4];
+
+ { 0 }, // Byte ___filler23[0x500-0x450];
+
+ 0x0000, // Word pwmControl; // $500: PWM Control Register
+ 0x0000, // Word pwmPeriod; // $502: PWM Period Register
+ 0x0000, // Word pwmWidth; // $504: PWM Width Register
+ 0x0000, // Word pwmCounter; // $506: PWM Counter
+
+ { 0 }, // Byte ___filler24[0x600-0x508];
+
+ 0x0000, // Word tmr1Control; // $600: Timer 1 Control Register
+ 0x0000, // Word tmr1Prescaler; // $602: Timer 1 Prescaler Register
+ 0xFFFF, // Word tmr1Compare; // $604: Timer 1 Compare Register
+ 0x0000, // Word tmr1Capture; // $606: Timer 1 Capture Register
+ 0x0000, // Word tmr1Counter; // $608: Timer 1 Counter Register
+ 0x0000, // Word tmr1Status; // $60A: Timer 1 Status Register
+
+ 0x0000, // Word tmr2Control; // $60C: Timer 2 Control Register
+ 0x0000, // Word tmr2Prescaler; // $60E: Timer 2 Prescaler Register
+ 0xFFFF, // Word tmr2Compare; // $610: Timer 2 Compare Register
+ 0x0000, // Word tmr2Capture; // $612: Timer 2 Capture Register
+ 0x0000, // Word tmr2Counter; // $614: Timer 2 Counter Register
+ 0x0000, // Word tmr2Status; // $616: Timer 2 Status Register
+
+ 0x0000, // Word wdControl; // $618: Watchdog Control Register
+ 0x0000, // Word wdReference; // $61A: Watchdog Reference Register
+ 0x0000, // Word wdCounter; // $61C: Watchdog Counter
+
+ { 0 }, // Byte ___filler25[0x700-0x61E];
+
+ 0x0000, // Word spiSlave; // $700: SPI Slave Register
+
+ { 0 }, // Byte ___filler26[0x800-0x702];
+
+ 0x0000, // Word spiMasterData; // $800: SPI Master Data Register
+ 0x0000, // Word spiMasterControl; // $802: SPI Master Control Register
+
+ { 0 }, // Byte ___filler27[0x900-0x804];
+
+ 0x0000, // Word uControl; // $900: Uart Control Register
+ 0x003F, // Word uBaud; // $902: Uart Baud Control Register
+ 0x0000, // Word uReceive; // $904: Uart Receive Register
+ 0x0000, // Word uTransmit; // $906: Uart Transmit Register
+ 0x0000, // Word uMisc; // $908: Uart Miscellaneous Register
+
+ { 0 }, // Byte ___filler28[0xA00-0x90A];
+
+ 0x00000000, // DWord lcdStartAddr; // $A00: Screen Starting Address Register
+ { 0 }, // Byte ___filler29;
+ 0xFF, // Byte lcdPageWidth; // $A05: Virtual Page Width Register
+ { 0 }, // Byte ___filler30[2];
+ 0x03FF, // Word lcdScreenWidth; // $A08: Screen Width Register
+ 0x01FF, // Word lcdScreenHeight; // $A0A: Screen Height Register
+ { 0 }, // Byte ___filler31[0xA18-0xA0C];
+ 0x0000, // Word lcdCursorXPos; // $A18: Cursor X Position
+ 0x0000, // Word lcdCursorYPos; // $A1A: Cursor Y Position
+ 0x0101, // Word lcdCursorWidthHeight; // $A1C: Cursor Width and Height
+ { 0 }, // Byte ___filler32;
+ 0x7F, // Byte lcdBlinkControl; // $A1F: Blink Control Register
+ 0x00, // Byte lcdPanelControl; // $A20: Panel Interface Control Register
+ 0x00, // Byte lcdPolarity; // $A21: Polarity Config Register
+ 0x00, // Byte ___filler33;
+ 0x00, // Byte lcdACDRate; // $A23: ACD (M) Rate Control Register
+ 0x00, // Byte ___filler34;
+ 0x00, // Byte lcdPixelClock; // $A25: Pixel Clock Divider Register
+ 0x00, // Byte ___filler35;
+ 0x40, // Byte lcdClockControl; // $A27: Clocking Control Register
+ 0x00, // Byte ___filler36;
+ 0x3E, // Byte lcdLastBufferAddr; // $A29: Last Buffer Address Register
+ 0x00, // Byte ___filler37;
+ 0x3F, // Byte lcdOctetTermCount; // $A2B: Octet Terminal Count Register
+ 0x00, // Byte ___filler38;
+ 0x00, // Byte lcdPanningOffset; // $A2D: Panning Offset Register
+ { 0 }, // Byte ___filler39[3];
+ 0xB9, // Byte lcdFrameRate; // $A31: Frame Rate Control Modulation Register
+ 0x1073, // Word lcdGrayPalette; // $A32: Gray Palette Mapping Register
+ 0x00, // Byte lcdReserved; // $A34: Reserved
+
+ { 0 }, // Byte ___filler40[0xB00-0xA35];
+
+ 0x00000000, // DWord rtcHourMinSec; // $B00: RTC Hours, Minutes, Seconds Register
+ 0x00000000, // DWord rtcAlarm; // $B04: RTC Alarm Register
+ 0x00000000, // DWord rtcReserved; // $B08: RTC Reserved
+ 0x0000, // Word rtcControl; // $B0C: RTC Control Register
+ 0x0000, // Word rtcIntStatus; // $B0E: RTC Interrupt Status Register
+ 0x0000, // Word rtcIntEnable; // $B10: RTC Interrupt Enable Register
+ 0x0000 // Word stopWatch; // $B12: Stopwatch Minutes
+};
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::EmRegs328
+// ---------------------------------------------------------------------------
+
+EmRegs328::EmRegs328 (void) :
+ EmRegs (),
+ f68328Regs (),
+ fHotSyncButtonDown (0),
+ fTmr2CurrentMilliseconds (0),
+ fTmr2StartMilliseconds (0),
+ fKeyBits (0),
+ fLastTmr1Status (0),
+ fLastTmr2Status (0),
+ fPortDEdge (0),
+ fPortDDataCount (0),
+ fHour (0),
+ fMin (0),
+ fSec (0),
+ fTick (0),
+ fCycle (0),
+ fUART (NULL)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::~EmRegs328
+// ---------------------------------------------------------------------------
+
+EmRegs328::~EmRegs328 (void)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::Initialize
+// ---------------------------------------------------------------------------
+
+void EmRegs328::Initialize (void)
+{
+ EmRegs::Initialize ();
+
+ fUART = new EmUARTDragonball (EmUARTDragonball::kUART_Dragonball, 0);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::Reset
+// ---------------------------------------------------------------------------
+
+void EmRegs328::Reset (Bool hardwareReset)
+{
+ EmRegs::Reset (hardwareReset);
+
+ if (hardwareReset)
+ {
+ f68328Regs = kInitial68328RegisterValues;
+
+ // Byteswap all the words in the Dragonball registers (if necessary).
+
+ Canonical (f68328Regs);
+ ByteswapWords (&f68328Regs, sizeof(f68328Regs));
+
+ fKeyBits = 0;
+ fLastTmr1Status = 0;
+ fLastTmr2Status = 0;
+ fPortDEdge = 0;
+ fPortDDataCount = 0;
+
+ // React to the new data in the UART registers.
+
+ Bool sendTxData = false;
+ EmRegs328::UARTStateChanged (sendTxData);
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::Save
+// ---------------------------------------------------------------------------
+
+void EmRegs328::Save (SessionFile& f)
+{
+ EmRegs::Save (f);
+
+ StWordSwapper swapper1 (&f68328Regs, sizeof(f68328Regs));
+ f.WriteHwrDBallType (f68328Regs);
+ f.FixBug (SessionFile::kBugByteswappedStructs);
+
+ const long kCurrentVersion = 4;
+
+ Chunk chunk;
+ EmStreamChunk s (chunk);
+
+ s << kCurrentVersion;
+ s << fHotSyncButtonDown;
+ s << fLastTmr1Status;
+ s << fLastTmr2Status;
+ s << fPortDEdge;
+
+ // Added in version 2.
+
+ s << fKeyBits;
+
+ // Added in version 3.
+
+ s << fHour;
+ s << fMin;
+ s << fSec;
+ s << fTick;
+ s << fCycle;
+
+ // Added in version 4.
+
+ s << fPortDDataCount;
+
+ f.WriteDBallState (chunk);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::Load
+// ---------------------------------------------------------------------------
+
+void EmRegs328::Load (SessionFile& f)
+{
+ EmRegs::Load (f);
+
+ if (f.ReadHwrDBallType (f68328Regs))
+ {
+ // The Windows version of Poser 2.1d29 and earlier did not write
+ // out structs in the correct format. The fields of the struct
+ // were written out in Little-Endian format, not Big-Endian. To
+ // address this problem, the bug has been fixed, and a new field
+ // is added to the file format indicating that the bug has been
+ // fixed. With the new field (the "bug bit"), Poser can identify
+ // old files from new files and read them in accordingly.
+ //
+ // With the bug fixed, the .psf files should now be interchangeable
+ // across platforms (modulo other bugs...).
+
+ if (!f.IncludesBugFix (SessionFile::kBugByteswappedStructs))
+ {
+ Canonical (f68328Regs);
+ }
+ ByteswapWords (&f68328Regs, sizeof(f68328Regs));
+
+ // React to the new data in the UART registers.
+
+ Bool sendTxData = false;
+ EmRegs328::UARTStateChanged (sendTxData);
+
+ // Reset gMemAccessFlags.fProtect_SRAMSet
+
+ gMemAccessFlags.fProtect_SRAMSet = (READ_REGISTER (csASelect1) & 0x0008) != 0;
+ }
+ else
+ {
+ f.SetCanReload (false);
+ }
+
+ Chunk chunk;
+ if (f.ReadDBallState (chunk))
+ {
+ long version;
+ EmStreamChunk s (chunk);
+
+ s >> version;
+
+ if (version >= 1)
+ {
+ s >> fHotSyncButtonDown;
+ s >> fTmr2CurrentMilliseconds;
+ s >> fTmr2StartMilliseconds;
+ s >> fLastTmr1Status;
+ s >> fLastTmr2Status;
+ s >> fPortDEdge;
+ }
+
+ if (version >= 2)
+ {
+ s >> fKeyBits;
+ }
+
+ if (version >= 3)
+ {
+ s >> fHour;
+ s >> fMin;
+ s >> fSec;
+ s >> fTick;
+ s >> fCycle;
+ }
+
+ if (version >= 4)
+ {
+ s >> fPortDDataCount;
+ }
+ }
+ else
+ {
+ f.SetCanReload (false);
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::Dispose
+// ---------------------------------------------------------------------------
+
+void EmRegs328::Dispose (void)
+{
+ delete fUART;
+ fUART = NULL;
+
+ EmRegs::Dispose ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::SetSubBankHandlers
+// ---------------------------------------------------------------------------
+
+void EmRegs328::SetSubBankHandlers (void)
+{
+ // Install base handlers.
+
+ EmRegs::SetSubBankHandlers ();
+
+ // Now add standard/specialized handers for the defined registers.
+
+ INSTALL_HANDLER (StdRead, StdWrite, scr);
+
+ INSTALL_HANDLER (StdRead, NullWrite, chipID);
+ INSTALL_HANDLER (StdRead, NullWrite, maskID);
+ INSTALL_HANDLER (StdRead, NullWrite, swID);
+
+ INSTALL_HANDLER (StdRead, StdWrite, csAGroupBase);
+ INSTALL_HANDLER (StdRead, StdWrite, csBGroupBase);
+ INSTALL_HANDLER (StdRead, StdWrite, csCGroupBase);
+ INSTALL_HANDLER (StdRead, StdWrite, csDGroupBase);
+
+ INSTALL_HANDLER (StdRead, StdWrite, csAGroupMask);
+ INSTALL_HANDLER (StdRead, StdWrite, csBGroupMask);
+ INSTALL_HANDLER (StdRead, StdWrite, csCGroupMask);
+ INSTALL_HANDLER (StdRead, StdWrite, csDGroupMask);
+
+ INSTALL_HANDLER (StdRead, StdWrite, csASelect0);
+ INSTALL_HANDLER (StdRead, csASelect1Write, csASelect1);
+ INSTALL_HANDLER (StdRead, StdWrite, csASelect2);
+ INSTALL_HANDLER (StdRead, StdWrite, csASelect3);
+
+ INSTALL_HANDLER (StdRead, StdWrite, csBSelect0);
+ INSTALL_HANDLER (StdRead, StdWrite, csBSelect1);
+ INSTALL_HANDLER (StdRead, StdWrite, csBSelect2);
+ INSTALL_HANDLER (StdRead, StdWrite, csBSelect3);
+
+ INSTALL_HANDLER (StdRead, csCSelect0Write, csCSelect0);
+ INSTALL_HANDLER (StdRead, csCSelect1Write, csCSelect1);
+ INSTALL_HANDLER (StdRead, StdWrite, csCSelect2);
+ INSTALL_HANDLER (StdRead, StdWrite, csCSelect3);
+
+ INSTALL_HANDLER (StdRead, StdWrite, csDSelect0);
+ INSTALL_HANDLER (StdRead, StdWrite, csDSelect1);
+ INSTALL_HANDLER (StdRead, StdWrite, csDSelect2);
+ INSTALL_HANDLER (StdRead, StdWrite, csDSelect3);
+ INSTALL_HANDLER (StdRead, StdWrite, csDebug);
+
+ INSTALL_HANDLER (StdRead, StdWrite, pllControl);
+ INSTALL_HANDLER (pllFreqSelRead, StdWrite, pllFreqSel);
+ INSTALL_HANDLER (StdRead, StdWrite, pllTest);
+ INSTALL_HANDLER (StdRead, StdWrite, pwrControl);
+
+ INSTALL_HANDLER (StdRead, StdWrite, intVector);
+ INSTALL_HANDLER (StdRead, StdWrite, intControl);
+ INSTALL_HANDLER (StdRead, intMaskHiWrite, intMaskHi);
+ INSTALL_HANDLER (StdRead, intMaskLoWrite, intMaskLo);
+ INSTALL_HANDLER (StdRead, StdWrite, intWakeupEnHi);
+ INSTALL_HANDLER (StdRead, StdWrite, intWakeupEnLo);
+ INSTALL_HANDLER (StdRead, intStatusHiWrite, intStatusHi);
+ INSTALL_HANDLER (StdRead, NullWrite, intStatusLo);
+ INSTALL_HANDLER (StdRead, NullWrite, intPendingHi);
+ INSTALL_HANDLER (StdRead, NullWrite, intPendingLo);
+
+ INSTALL_HANDLER (StdRead, StdWrite, portADir);
+ INSTALL_HANDLER (portXDataRead, portXDataWrite, portAData);
+ INSTALL_HANDLER (StdRead, StdWrite, portASelect);
+
+ INSTALL_HANDLER (StdRead, StdWrite, portBDir);
+ INSTALL_HANDLER (portXDataRead, portXDataWrite, portBData);
+ INSTALL_HANDLER (StdRead, StdWrite, portBSelect);
+
+ INSTALL_HANDLER (StdRead, StdWrite, portCDir);
+ INSTALL_HANDLER (portXDataRead, portXDataWrite, portCData);
+ INSTALL_HANDLER (StdRead, StdWrite, portCSelect);
+
+ INSTALL_HANDLER (StdRead, StdWrite, portDDir);
+ INSTALL_HANDLER (portXDataRead, portXDataWrite, portDData);
+ INSTALL_HANDLER (StdRead, StdWrite, portDPullupEn);
+ INSTALL_HANDLER (StdRead, StdWrite, portDPolarity);
+ INSTALL_HANDLER (StdRead, portDIntReqEnWrite, portDIntReqEn);
+ INSTALL_HANDLER (StdRead, StdWrite, portDIntEdge);
+
+ INSTALL_HANDLER (StdRead, StdWrite, portEDir);
+ INSTALL_HANDLER (portXDataRead, portXDataWrite, portEData);
+ INSTALL_HANDLER (StdRead, StdWrite, portEPullupEn);
+ INSTALL_HANDLER (StdRead, StdWrite, portESelect);
+
+ INSTALL_HANDLER (StdRead, StdWrite, portFDir);
+ INSTALL_HANDLER (portXDataRead, portXDataWrite, portFData);
+ INSTALL_HANDLER (StdRead, StdWrite, portFPullupEn);
+ INSTALL_HANDLER (StdRead, StdWrite, portFSelect);
+
+ INSTALL_HANDLER (StdRead, StdWrite, portGDir);
+ INSTALL_HANDLER (portXDataRead, portXDataWrite, portGData);
+ INSTALL_HANDLER (StdRead, StdWrite, portGPullupEn);
+ INSTALL_HANDLER (StdRead, StdWrite, portGSelect);
+
+ INSTALL_HANDLER (StdRead, StdWrite, portJDir);
+ INSTALL_HANDLER (portXDataRead, portXDataWrite, portJData);
+ INSTALL_HANDLER (StdRead, StdWrite, portJSelect);
+
+ INSTALL_HANDLER (StdRead, StdWrite, portKDir);
+ INSTALL_HANDLER (portXDataRead, portXDataWrite, portKData);
+ INSTALL_HANDLER (StdRead, StdWrite, portKPullupEn);
+ INSTALL_HANDLER (StdRead, StdWrite, portKSelect);
+
+ INSTALL_HANDLER (StdRead, StdWrite, portMDir);
+ INSTALL_HANDLER (portXDataRead, portXDataWrite, portMData);
+ INSTALL_HANDLER (StdRead, StdWrite, portMPullupEn);
+ INSTALL_HANDLER (StdRead, StdWrite, portMSelect);
+
+ INSTALL_HANDLER (StdRead, StdWrite, pwmControl);
+ INSTALL_HANDLER (StdRead, StdWrite, pwmPeriod);
+ INSTALL_HANDLER (StdRead, StdWrite, pwmWidth);
+ INSTALL_HANDLER (StdRead, NullWrite, pwmCounter);
+
+ INSTALL_HANDLER (StdRead, StdWrite, tmr1Control);
+ INSTALL_HANDLER (StdRead, StdWrite, tmr1Prescaler);
+ INSTALL_HANDLER (StdRead, StdWrite, tmr1Compare);
+ INSTALL_HANDLER (StdRead, StdWrite, tmr1Capture);
+ INSTALL_HANDLER (StdRead, NullWrite, tmr1Counter);
+ INSTALL_HANDLER (tmr1StatusRead, tmr1StatusWrite, tmr1Status);
+
+ INSTALL_HANDLER (StdRead, StdWrite, tmr2Control);
+ INSTALL_HANDLER (StdRead, StdWrite, tmr2Prescaler);
+ INSTALL_HANDLER (StdRead, StdWrite, tmr2Compare);
+ INSTALL_HANDLER (StdRead, StdWrite, tmr2Capture);
+ INSTALL_HANDLER (StdRead, NullWrite, tmr2Counter);
+ INSTALL_HANDLER (tmr2StatusRead, tmr2StatusWrite, tmr2Status);
+
+ INSTALL_HANDLER (StdRead, StdWrite, wdControl);
+ INSTALL_HANDLER (StdRead, StdWrite, wdReference);
+ INSTALL_HANDLER (StdRead, wdCounterWrite, wdCounter);
+
+ INSTALL_HANDLER (StdRead, StdWrite, spiSlave);
+ INSTALL_HANDLER (StdRead, StdWrite, spiMasterData);
+ INSTALL_HANDLER (StdRead, spiMasterControlWrite, spiMasterControl);
+
+ INSTALL_HANDLER (uartRead, uartWrite, uControl);
+ INSTALL_HANDLER (uartRead, uartWrite, uBaud);
+ INSTALL_HANDLER (uartRead, uartWrite, uReceive);
+ INSTALL_HANDLER (uartRead, uartWrite, uTransmit);
+ INSTALL_HANDLER (uartRead, uartWrite, uMisc);
+
+ INSTALL_HANDLER (StdRead, lcdRegisterWrite, lcdStartAddr);
+ INSTALL_HANDLER (StdRead, lcdRegisterWrite, lcdPageWidth);
+ INSTALL_HANDLER (StdRead, lcdRegisterWrite, lcdScreenWidth);
+ INSTALL_HANDLER (StdRead, lcdRegisterWrite, lcdScreenHeight);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdCursorXPos);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdCursorYPos);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdCursorWidthHeight);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdBlinkControl);
+ INSTALL_HANDLER (StdRead, lcdRegisterWrite, lcdPanelControl);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdPolarity);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdACDRate);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdPixelClock);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdClockControl);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdLastBufferAddr);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdOctetTermCount);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdPanningOffset);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdFrameRate);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdGrayPalette);
+ INSTALL_HANDLER (StdRead, StdWrite, lcdReserved);
+
+ INSTALL_HANDLER (rtcHourMinSecRead, StdWrite, rtcHourMinSec);
+ INSTALL_HANDLER (StdRead, StdWrite, rtcAlarm);
+ INSTALL_HANDLER (StdRead, StdWrite, rtcReserved);
+ INSTALL_HANDLER (StdRead, rtcControlWrite, rtcControl);
+ INSTALL_HANDLER (StdRead, rtcIntStatusWrite, rtcIntStatus);
+ INSTALL_HANDLER (StdRead, rtcIntEnableWrite, rtcIntEnable);
+ INSTALL_HANDLER (StdRead, StdWrite, stopWatch);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetRealAddress
+// ---------------------------------------------------------------------------
+
+uint8* EmRegs328::GetRealAddress (emuptr address)
+{
+ uint8* loc = ((uint8*) &f68328Regs) + (address - kMemoryStart);
+
+ return loc;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetAddressStart
+// ---------------------------------------------------------------------------
+
+emuptr EmRegs328::GetAddressStart (void)
+{
+ return kMemoryStart;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetAddressRange
+// ---------------------------------------------------------------------------
+
+uint32 EmRegs328::GetAddressRange (void)
+{
+ COMPILE_TIME_ASSERT (kMemorySize == 0x0B14);
+
+ return kMemorySize;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::Cycle
+// ---------------------------------------------------------------------------
+// Handles periodic events that need to occur when the processor cycles (like
+// updating timer registers). This function is called in two places from
+// Emulator::Execute. Interestingly, the loop runs 3% FASTER if this function
+// is in its own separate function instead of being inline.
+
+#if 0
+static int calibrated;
+static int increment;
+static int timesCalled;
+static uint32 startingTime;
+
+static void PrvCalibrate (uint16 tmrCompare)
+{
+ // Calibrate the the value by which we increment the counter.
+ // The counter is set up so that it times out after 10 milliseconds
+ // so that it can increment the Palm OS's tick counter 100 times
+ // a second. We would like tmrCounter to surpass tmrCompare
+ // after 10 milliseconds. So figure out by how much we need to
+ // increment it in order for that to happen.
+
+ // If timer is disabled; reset calibration.
+
+ if (tmrCompare == 0xFFFF)
+ {
+ startingTime = 0;
+ }
+
+ // If timer is enabled, restart calibration.
+
+ else if (startingTime == 0)
+ {
+ startingTime = Platform::GetMilliseconds();
+ timesCalled = 0;
+ increment = 1;
+ }
+
+ // If calibration is started, continue it.
+
+ else
+ {
+ timesCalled++;
+
+ uint32 now = Platform::GetMilliseconds();
+ if (now - startingTime > 100)
+ {
+ calibrated = true;
+ increment = tmrCompare / (timesCalled / 10);
+ }
+ }
+}
+#endif
+
+void EmRegs328::Cycle (Bool sleeping)
+{
+#if 0
+ // Cycle is *very* sensitive to timing issue. With this section
+ // of code, a Gremlins run can slow down by 5%.
+ if (!calibrated)
+ {
+ ::PrvCalibrate (READ_REGISTER (tmr2Compare));
+ }
+#else
+ #if _DEBUG
+ #define increment 20
+ #else
+ #define increment 4
+ #endif
+#endif
+
+ // Determine whether timer 2 is enabled.
+
+ if ((READ_REGISTER (tmr2Control) & hwr328TmrControlEnable) != 0)
+ {
+ // If so, increment the timer.
+
+ WRITE_REGISTER (tmr2Counter, READ_REGISTER (tmr2Counter) + (sleeping ? 1 : increment));
+
+ // Determine whether the timer has reached the specified count.
+
+ if (sleeping || READ_REGISTER (tmr2Counter) > READ_REGISTER (tmr2Compare))
+ {
+ // Flag the occurrence of the successful comparison.
+
+ WRITE_REGISTER (tmr2Status, READ_REGISTER (tmr2Status) | hwr328TmrStatusCompare);
+
+ // If the Free Run/Restart flag is not set, clear the counter.
+
+ if ((READ_REGISTER (tmr2Control) & hwr328TmrControlFreeRun) == 0)
+ {
+ WRITE_REGISTER (tmr2Counter, 0);
+ }
+
+ // If the timer interrupt is enabled, post an interrupt.
+
+ if ((READ_REGISTER (tmr2Control) & hwr328TmrControlEnInterrupt) != 0)
+ {
+ WRITE_REGISTER (intPendingLo, READ_REGISTER (intPendingLo) | hwr328IntLoTimer2);
+ EmRegs328::UpdateInterrupts ();
+ }
+ }
+ }
+
+ if ((fCycle += increment) > READ_REGISTER (tmr2Compare))
+ {
+ fCycle = 0;
+
+ if (++fTick >= 100)
+ {
+ fTick = 0;
+
+ if (++fSec >= 60)
+ {
+ fSec = 0;
+
+ if (++fMin >= 60)
+ {
+ fMin = 0;
+
+ if (++fHour >= 24)
+ {
+ fHour = 0;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::CycleSlowly
+// ---------------------------------------------------------------------------
+// Handles periodic events that need to occur when the processor cycles (like
+// updating timer registers). This function is called in two places from
+// Emulator::Execute. Interestingly, the loop runs 3% FASTER if this function
+// is in its own separate function instead of being inline.
+
+void EmRegs328::CycleSlowly (Bool sleeping)
+{
+ UNUSED_PARAM(sleeping)
+
+ // See if a hard button is pressed.
+
+ EmAssert (gSession);
+ if (gSession->HasButtonEvent ())
+ {
+ EmButtonEvent event = gSession->GetButtonEvent ();
+ if (event.fButton == kElement_CradleButton)
+ {
+ EmRegs328::HotSyncEvent (event.fButtonIsDown);
+ }
+ else
+ {
+ EmRegs328::ButtonEvent (event.fButton, event.fButtonIsDown);
+ }
+ }
+
+ // See if there's anything new ("Put the data on the bus")
+
+ EmRegs328::UpdateUARTState (false);
+
+ // Check to see if the RTC alarm is ready to go off. First see
+ // if the RTC is enabled, and that the alarm event isn't already
+ // registered (the latter check is just an optimization).
+
+ if ((READ_REGISTER (rtcIntEnable) & hwr328RTCIntEnableAlarm) != 0 &&
+ (READ_REGISTER (rtcIntStatus) & hwr328RTCIntStatusAlarm) == 0)
+ {
+ uint32 rtcAlarm = READ_REGISTER (rtcAlarm);
+
+ long almHour = (rtcAlarm & hwr328RTCAlarmHoursMask) >> hwr328RTCAlarmHoursOffset;
+ long almMin = (rtcAlarm & hwr328RTCAlarmMinutesMask) >> hwr328RTCAlarmMinutesOffset;
+ long almSec = (rtcAlarm & hwr328RTCAlarmSecondsMask) >> hwr328RTCAlarmSecondsOffset;
+ long almInSeconds = (almHour * 60 * 60) + (almMin * 60) + almSec;
+
+ long nowHour;
+ long nowMin;
+ long nowSec;
+ ::GetHostTime (&nowHour, &nowMin, &nowSec);
+ long nowInSeconds = (nowHour * 60 * 60) + (nowMin * 60) + nowSec;
+
+ if (almInSeconds <= nowInSeconds)
+ {
+ WRITE_REGISTER (rtcIntStatus, READ_REGISTER (rtcIntStatus) | hwr328RTCIntStatusAlarm);
+ EmRegs328::UpdateRTCInterrupts ();
+ }
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::TurnSoundOff
+// ---------------------------------------------------------------------------
+
+void EmRegs328::TurnSoundOff (void)
+{
+ uint16 pwmControl = READ_REGISTER (pwmControl);
+ WRITE_REGISTER (pwmControl, pwmControl & ~hwr328PWMControlEnable);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::ResetTimer
+// ---------------------------------------------------------------------------
+
+void EmRegs328::ResetTimer (void)
+{
+ WRITE_REGISTER (tmr2Counter, 0);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::ResetRTC
+// ---------------------------------------------------------------------------
+
+void EmRegs328::ResetRTC (void)
+{
+ fHour = 15;
+ fMin = 0;
+ fSec = 0;
+ fTick = 0;
+ fCycle = 0;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetInterruptLevel
+// ---------------------------------------------------------------------------
+
+int32 EmRegs328::GetInterruptLevel (void)
+{
+ uint16 intStatusHi = READ_REGISTER (intStatusHi);
+ uint16 intStatusLo = READ_REGISTER (intStatusLo);
+
+ // Level 7 = IRQ7.
+
+ if ((intStatusHi & hwr328IntHiNMI) != 0)
+ return 7;
+
+ // Level 6 = SPIS, TMR1, IRQ6.
+
+ if ((intStatusHi & (hwr328IntHiTimer1 | hwr328IntHiSPIS | hwr328IntHiIRQ6)) != 0)
+ return 6;
+
+ // Level 5 = PEN.
+
+ if ((intStatusHi & hwr328IntHiPen) != 0)
+ return 5;
+
+ // Level 4 = SPIM, TMR2, UART, WDT, RTC, KB, PWM, INT0 - INT7.
+
+ if ((intStatusLo & ( hwr328IntLoAllKeys |
+ hwr328IntLoPWM |
+ hwr328IntLoKbd |
+ // hwr328IntLoLCDC |
+ hwr328IntLoRTC |
+ hwr328IntLoWDT |
+ hwr328IntLoTimer2 |
+ hwr328IntLoSPIM)) != 0)
+ return 4;
+
+ // Level 3 = IRQ3.
+
+ if ((intStatusHi & hwr328IntHiIRQ3) != 0)
+ return 3;
+
+ // Level 2 = IRQ2.
+
+ if ((intStatusHi & hwr328IntHiIRQ2) != 0)
+ return 2;
+
+ // Level 1 = IRQ1.
+
+ if ((intStatusHi & hwr328IntHiIRQ1) != 0)
+ return 1;
+
+ // Level 0.
+
+ return -1;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetInterruptBase
+// ---------------------------------------------------------------------------
+
+int32 EmRegs328::GetInterruptBase (void)
+{
+ return READ_REGISTER (intVector) & 0xF8;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetLCDHasFrame
+// ---------------------------------------------------------------------------
+
+Bool EmRegs328::GetLCDHasFrame (void)
+{
+ return false;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetLCDBeginEnd
+// ---------------------------------------------------------------------------
+
+void EmRegs328::GetLCDBeginEnd (emuptr& begin, emuptr& end)
+{
+ emuptr baseAddr = READ_REGISTER (lcdStartAddr);
+ int rowBytes = READ_REGISTER (lcdPageWidth) * 2;
+ int height = READ_REGISTER (lcdScreenHeight) + 1;
+
+ begin = baseAddr;
+ end = baseAddr + rowBytes * height;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetLCDScanlines
+// ---------------------------------------------------------------------------
+
+void EmRegs328::GetLCDScanlines (EmScreenUpdateInfo& info)
+{
+ // Get the screen metrics.
+
+ int32 bpp = 1 << (READ_REGISTER (lcdPanelControl) & 0x01);
+ int32 width = READ_REGISTER (lcdScreenWidth) + 1;
+ int32 height = READ_REGISTER (lcdScreenHeight) + 1;
+ int32 rowBytes = READ_REGISTER (lcdPageWidth) * 2;
+ emuptr baseAddr = READ_REGISTER (lcdStartAddr);
+
+ info.fLeftMargin = READ_REGISTER (lcdPanningOffset) & 0x0F;
+
+ EmPixMapFormat format = bpp == 1 ? kPixMapFormat1 :
+ bpp == 2 ? kPixMapFormat2 :
+ bpp == 4 ? kPixMapFormat4 :
+ kPixMapFormat8;
+
+ RGBList colorTable;
+ this->PrvGetPalette (colorTable);
+
+ // Set format, size, and color table of EmPixMap.
+
+ info.fImage.SetSize (EmPoint (width, height));
+ info.fImage.SetFormat (format);
+ info.fImage.SetRowBytes (rowBytes);
+ info.fImage.SetColorTable (colorTable);
+
+ // Determine first and last scanlines to fetch, and fetch them.
+
+ info.fFirstLine = (info.fScreenLow - baseAddr) / rowBytes;
+ info.fLastLine = (info.fScreenHigh - baseAddr - 1) / rowBytes + 1;
+
+ long firstLineOffset = info.fFirstLine * rowBytes;
+ long lastLineOffset = info.fLastLine * rowBytes;
+
+ EmMem_memcpy (
+ (void*) ((uint8*) info.fImage.GetBits () + firstLineOffset),
+ baseAddr + firstLineOffset,
+ lastLineOffset - firstLineOffset);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetUARTDevice
+// ---------------------------------------------------------------------------
+// Return what sort of device is hooked up to the given UART.
+
+EmUARTDeviceType EmRegs328::GetUARTDevice (int /*uartNum*/)
+{
+ Bool serEnabled = this->GetLineDriverState (kUARTSerial);
+ Bool irEnabled = this->GetLineDriverState (kUARTIR);
+
+ // It's probably an error to have them both enabled at the same
+ // time. !!! TBD: make this an error message.
+
+ EmAssert (!(serEnabled && irEnabled));
+
+ if (serEnabled)
+ return kUARTSerial;
+
+ if (irEnabled)
+ return kUARTIR;
+
+ return kUARTNone;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetDynamicHeapSize
+// ---------------------------------------------------------------------------
+
+int32 EmRegs328::GetDynamicHeapSize (void)
+{
+ uint32 result = 0;
+
+ uint32 csCSelect0 = READ_REGISTER (csCSelect0) & ADDRESS_MASK;
+ uint32 csCSelect1 = READ_REGISTER (csCSelect1) & ADDRESS_MASK;
+
+ if (csCSelect0 == 0x0000 && csCSelect1 == 0x0000)
+ {
+ result = 0 * 1024L;
+ }
+ else if (csCSelect0 == 0x0070 && csCSelect1 == 0x0000)
+ {
+ result = 32 * 1024L;
+ }
+ else if (csCSelect0 == 0x00F0 && csCSelect1 == 0x0000)
+ {
+ result = 64 * 1024L;
+ }
+ else if (csCSelect0 == 0x0070 && csCSelect1 == 0x0070)
+ {
+ // This one's odd, but the Symbol seems to (temporarily)
+ // set up this configuration when running with 2Meg of RAM.
+ result = 96 * 1024L;
+ }
+ else if (csCSelect0 == 0x00F0 && csCSelect1 == 0x0070)
+ {
+ result = 96 * 1024L;
+ }
+ else if (csCSelect0 == 0x01F0 && csCSelect1 == 0x0000)
+ {
+ result = 128 * 1024L;
+ }
+ else if (csCSelect0 == 0x03F0 && csCSelect1 == 0x0000)
+ {
+ result = 256 * 1024L;
+ }
+ else
+ {
+ EmAssert (false);
+ }
+
+ if (!ChipSelectsConfigured())
+ {
+ result = 16 * 1024L * 1024L;
+ }
+
+ return result;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetROMSize
+// ---------------------------------------------------------------------------
+
+int32 EmRegs328::GetROMSize (void)
+{
+ uint32 result = 2 * 1024L * 1024L;
+
+ return result;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetROMBaseAddress
+// ---------------------------------------------------------------------------
+
+emuptr EmRegs328::GetROMBaseAddress (void)
+{
+ if (!this->ChipSelectsConfigured())
+ {
+ return 0xFFFFFFFF;
+ }
+
+ return 0x10C00000; // use known value
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::ChipSelectsConfigured
+// ---------------------------------------------------------------------------
+
+Bool EmRegs328::ChipSelectsConfigured (void)
+{
+ return READ_REGISTER (csAGroupBase) & 0x0001;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetSystemClockFrequency
+// ---------------------------------------------------------------------------
+
+int32 EmRegs328::GetSystemClockFrequency (void)
+{
+ uint16 pllControl = READ_REGISTER (pllControl);
+ uint16 pllFreqSel = READ_REGISTER (pllFreqSel);
+
+ uint16 PC = (pllFreqSel & 0x00FF);
+ uint16 QC = (pllFreqSel & 0x0F00) >> 8;
+
+ uint32 result = 32768L * (14 * (PC + 1) + QC + 1);
+
+ // Divide by the system clock scaler, if needed.
+
+ switch (pllControl & 0x0F00)
+ {
+ case hwr328PLLControlSysVCODiv2:
+ result /= 2;
+ break;
+
+ case hwr328PLLControlSysVCODiv4:
+ result /= 4;
+ break;
+
+ case hwr328PLLControlSysVCODiv8:
+ result /= 8;
+ break;
+
+ case hwr328PLLControlSysVCODiv16:
+ result /= 16;
+ break;
+ }
+
+ return result;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetCanStop
+// ---------------------------------------------------------------------------
+
+Bool EmRegs328::GetCanStop (void)
+{
+ // Make sure Timer 2 is enabled or the RTC interrupt is enabled.
+
+ if ((READ_REGISTER (tmr2Control) & hwr328TmrControlEnable) != 0)
+ return true;
+
+ if ((READ_REGISTER (rtcIntEnable) & hwr328RTCIntEnableAlarm) != 0)
+ return true;
+
+ return false;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetAsleep
+// ---------------------------------------------------------------------------
+
+Bool EmRegs328::GetAsleep (void)
+{
+ return ((READ_REGISTER (pllControl) & hwr328PLLControlDisable) != 0);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetPortInputValue
+// ---------------------------------------------------------------------------
+// Return the GPIO values for the pins on the port. These values are used
+// if the select pins are high.
+
+uint8 EmRegs328::GetPortInputValue (int port)
+{
+ uint8 result = 0;
+
+ if (port == 'D')
+ {
+ result = this->GetPortInternalValue (port);
+ }
+
+ return result;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetPortInternalValue
+// ---------------------------------------------------------------------------
+// Return the dedicated values for the pins on the port. These values are
+// used if the select pins are low.
+
+uint8 EmRegs328::GetPortInternalValue (int port)
+{
+ uint8 result = 0;
+
+ if (port == 'C')
+ {
+ // This makes the power on key work. If the signal is not asserted, the
+ // unit will not transition between asleep and awake (cf. HwrSleep, HwrWake).
+
+ result |= hwr328PortCNMI;
+ }
+
+ else if (port == 'D')
+ {
+ // If the ID_DETECT pin is asserted, load the data lines with the
+ // hardware ID.
+
+ if (EmRegs328::IDDetectAsserted ())
+ {
+ result |= EmRegs328::GetHardwareID ();
+ }
+
+ // Otherwise, load the lines with keyboard information.
+
+ else
+ {
+ // Get the INT bits that need to be set.
+
+ result |= this->GetKeyBits ();
+ }
+ }
+
+ return result;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::PortDataChanged
+// ---------------------------------------------------------------------------
+
+void EmRegs328::PortDataChanged (int port, uint8, uint8 newValue)
+{
+ if (port == 'D')
+ {
+ // Clear the interrupt bits that are having a 1 written to them.
+ // Only clear them if they're configured as edge-senstive.
+
+ uint8 portDIntEdge = READ_REGISTER (portDIntEdge);
+
+ PRINTF ("EmRegs328::PortDataChanged (D): fPortDEdge = 0x%02lX", (uint32) (uint8) fPortDEdge);
+ PRINTF ("EmRegs328::PortDataChanged (D): portDIntEdge = 0x%02lX", (uint32) (uint8) portDIntEdge);
+ PRINTF ("EmRegs328::PortDataChanged (D): newValue = 0x%02lX", (uint32) (uint8) newValue);
+
+ fPortDEdge &= ~(newValue & portDIntEdge);
+
+ PRINTF ("EmRegs328::PortDataChanged (D): fPortDEdge = 0x%02lX", (uint32) (uint8) fPortDEdge);
+
+ // Set the new interrupt state.
+
+ EmRegs328::UpdatePortDInterrupts ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::pllFreqSelRead
+// ---------------------------------------------------------------------------
+
+uint32 EmRegs328::pllFreqSelRead (emuptr address, int size)
+{
+ // Simulate the rising and falling of the CLK32 signal so that functions
+ // like HwrPreRAMInit, HwrShutDownPLL, PrvSetPLL, and PrvShutDownPLL
+ // won't hang.
+
+ uint16 pllFreqSel = READ_REGISTER (pllFreqSel) ^ 0x8000;
+ WRITE_REGISTER (pllFreqSel, pllFreqSel);
+
+ // Finish up by doing a standard read.
+
+ return EmRegs328::StdRead (address, size);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::portXDataRead
+// ---------------------------------------------------------------------------
+
+uint32 EmRegs328::portXDataRead (emuptr address, int)
+{
+ // The value read can come from three different places:
+ //
+ // - the value what was written to the data register
+ // - any dedicated inputs
+ // - any GPIO inputs
+ //
+ // The value returned depends on the settings of the SEL and DIR
+ // registers. So let's get those settings, the values from the three
+ // input sources, and build up a return value based on those.
+
+ int port = GetPort (address);
+
+ uint8 sel = StdRead (address + 2, 1);
+ uint8 dir = StdRead (address - 1, 1);
+ uint8 output = StdRead (address + 0, 1);
+ uint8 input = EmHAL::GetPortInputValue (port);
+ uint8 intFn = EmHAL::GetPortInternalValue (port);
+
+ uint8 xsel = sel;
+ uint8 xdir = dir;
+ uint8 xoutput = output;
+ uint8 xinput = input;
+ uint8 xintFn = intFn;
+
+ if (port == 'D')
+ {
+ sel = 0xFF; // No "select" bit in low nybble, so set for IO values.
+
+ // The system will poll portD twice in KeyBootKeys to see
+ // if any keys are down. Wait at least that long before
+ // letting up any boot keys maintained by the session. When we
+ // do call ReleaseBootKeys, set our counter to -1 as a flag not
+ // to call it any more.
+
+ if (fPortDDataCount != 0xFFFFFFFF && ++fPortDDataCount >= 2 * 2)
+ {
+ fPortDDataCount = 0xFFFFFFFF;
+ gSession->ReleaseBootKeys ();
+ }
+ }
+
+ // Use the internal chip function bits if the "sel" bits are zero.
+
+ intFn &= ~sel;
+
+ // Otherwise, use the I/O bits.
+
+ output &= sel & dir; // Use the output bits if the "dir" is one.
+ input &= sel & ~dir; // Use the input bits if the "dir" is zero.
+
+ // Assert that there are no overlaps.
+
+ EmAssert ((output & input) == 0);
+ EmAssert ((output & intFn) == 0);
+ EmAssert ((input & intFn) == 0);
+
+ // Mush everything together.
+
+ uint8 result = output | input | intFn;
+
+ // If this is port D, flip the bits if the POLARITY register says to.
+ // (!!! Does this inversion apply only to input bits? That is, the
+ // bits where the "dir" register has 0 bits?)
+
+ if (0 && port == 'D')
+ {
+ uint8 polarity = READ_REGISTER (portDPolarity);
+ PRINTF ("EmRegs328::portXDataRead: polarity = 0x%02lX", (uint32) polarity);
+ result ^= polarity;
+ }
+
+ PRINTF ("EmRegs328::port%cDataRead: sel dir output input intFn result", (char) port);
+ PRINTF ("EmRegs328::port%cDataRead: 0x%02lX 0x%02lX 0x%02lX 0x%02lX 0x%02lX 0x%02lX",
+ (char) port, (uint32) xsel, (uint32) xdir, (uint32) xoutput, (uint32) xinput, (uint32) xintFn, (uint32) result);
+
+ return result;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::tmr1StatusRead
+// ---------------------------------------------------------------------------
+
+uint32 EmRegs328::tmr1StatusRead (emuptr address, int size)
+{
+ uint16 tmr1Counter = READ_REGISTER (tmr1Counter) + 16;
+ uint16 tmr1Compare = READ_REGISTER (tmr1Compare);
+ uint16 tmr1Control = READ_REGISTER (tmr1Control);
+
+ // Increment the timer.
+
+ WRITE_REGISTER (tmr1Counter, tmr1Counter);
+
+ // If the timer has passed the specified value...
+
+ if ( (tmr1Counter - tmr1Compare) < 16 )
+ {
+ // Set the flag saying the timer timed out.
+
+ uint16 tmr1Status = READ_REGISTER (tmr1Status) | hwr328TmrStatusCompare;
+ WRITE_REGISTER (tmr1Status, tmr1Status);
+
+ // If it's not a free-running timer, reset it to zero.
+
+ if ((tmr1Control & hwr328TmrControlFreeRun) == 0)
+ {
+ WRITE_REGISTER (tmr1Counter, 0);
+ }
+ }
+
+ // Remember this guy for later (see EmRegs328::tmr1StatusWrite())
+
+ fLastTmr1Status |= READ_REGISTER (tmr1Status);
+
+ // Finish up by doing a standard read.
+
+ return EmRegs328::StdRead (address, size);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::tmr2StatusRead
+// ---------------------------------------------------------------------------
+
+uint32 EmRegs328::tmr2StatusRead (emuptr address, int size)
+{
+#if 0 // (Greg doesn't do this for Timer 2...I wonder why)
+
+ /*
+ ram_rom.cpp: DBReg::tmr2StatusRead: Hmm, I don't update the TMR2 count
+ value. As with everything else that's missing in my DragonBall
+ emulation, it's probably because I never found anything that needed it.
+ If you know otherwise, by all means implement it. Also, the magic value
+ of 16 counts per status read seemed to be about right for the programmed
+ timer 1 frequency. It isn't likely to be right for timer 2.
+
+ -- Greg
+ */
+
+ uint16 tmr2Counter = READ_REGISTER (tmr2Counter) + 16;
+ uint16 tmr2Compare = READ_REGISTER (tmr2Compare);
+ uint16 tmr2Control = READ_REGISTER (tmr2Control);
+
+ // Increment the timer.
+
+ WRITE_REGISTER (tmr2Counter, tmr2Counter);
+
+ // If the timer has passed the specified value...
+
+ if ( (tmr2Counter - tmr2Compare) < 16 )
+ {
+ // Set the flag saying the timer timed out.
+
+ uint16 tmr2Status = READ_REGISTER (tmr1Status) | hwr328TmrStatusCompare;
+ WRITE_REGISTER (tmr2Status, tmr2Status);
+
+ // If it's not a free-running timer, reset it to zero.
+
+ if ( (tmr2Control & hwr328TmrControlFreeRun) == 0 )
+ WRITE_REGISTER (tmr2Counter, 0);
+ }
+#endif
+
+ fLastTmr2Status |= READ_REGISTER (tmr2Status); // remember this guy for later (see EmRegs328::tmr2StatusWrite())
+
+ // Finish up by doing a standard read.
+
+ return EmRegs328::StdRead (address, size);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::uartRead
+// ---------------------------------------------------------------------------
+
+uint32 EmRegs328::uartRead (emuptr address, int size)
+{
+ // If this is a full read, get the next byte from the FIFO.
+
+ Bool refreshRxData = (address == addressof (uReceive)) && (size == 2);
+
+ // See if there's anything new ("Put the data on the bus")
+
+ EmRegs328::UpdateUARTState (refreshRxData);
+
+ // Finish up by doing a standard read.
+
+ return EmRegs328::StdRead (address, size);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::rtcHourMinSecRead
+// ---------------------------------------------------------------------------
+
+uint32 EmRegs328::rtcHourMinSecRead (emuptr address, int size)
+{
+ // Get the desktop machine's time.
+
+ long hour, min, sec;
+
+ if (Hordes::IsOn ())
+ {
+ hour = fHour;
+ min = fMin;
+ sec = fSec;
+ }
+ else
+ {
+ ::GetHostTime (&hour, &min, &sec);
+ }
+
+ // Update the register.
+
+ WRITE_REGISTER (rtcHourMinSec, (hour << hwr328RTCHourMinSecHoursOffset)
+ | (min << hwr328RTCHourMinSecMinutesOffset)
+ | (sec << hwr328RTCHourMinSecSecondsOffset));
+
+ // Finish up by doing a standard read.
+
+ return EmRegs328::StdRead (address, size);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::csASelect1Write
+// ---------------------------------------------------------------------------
+
+void EmRegs328::csASelect1Write (emuptr address, int size, uint32 value)
+{
+ // Do a standard update of the register.
+
+ EmRegs328::StdWrite (address, size, value);
+
+ // Check its new state and update our ram-protect flag.
+
+ gMemAccessFlags.fProtect_SRAMSet = (READ_REGISTER (csASelect1) & 0x0008) != 0;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::csCSelect0Write
+// ---------------------------------------------------------------------------
+
+void EmRegs328::csCSelect0Write (emuptr address, int size, uint32 value)
+{
+ uint32 csCSelect0 = READ_REGISTER (csCSelect0);
+
+ // Do a standard update of the register.
+
+ EmRegs328::StdWrite (address, size, value);
+
+ // Check to see if the unprotected memory range changed.
+
+ if ((csCSelect0 & ADDRESS_MASK) != (READ_REGISTER (csCSelect0) & ADDRESS_MASK))
+ {
+ EmAssert (gSession);
+ gSession->ScheduleResetBanks ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::csCSelect1Write
+// ---------------------------------------------------------------------------
+
+void EmRegs328::csCSelect1Write (emuptr address, int size, uint32 value)
+{
+ uint32 csCSelect1 = READ_REGISTER (csCSelect1);
+
+ // Do a standard update of the register.
+
+ EmRegs328::StdWrite (address, size, value);
+
+ // Check to see if the unprotected memory range changed.
+
+ if ((csCSelect1 & ADDRESS_MASK) != (READ_REGISTER (csCSelect1) & ADDRESS_MASK))
+ {
+ EmAssert (gSession);
+ gSession->ScheduleResetBanks ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::intMaskHiWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::intMaskHiWrite (emuptr address, int size, uint32 value)
+{
+ // Do a standard update of the register.
+
+ EmRegs328::StdWrite (address, size, value);
+
+ // Respond to the new interrupt state.
+
+ EmRegs328::UpdateInterrupts ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::intMaskLoWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::intMaskLoWrite (emuptr address, int size, uint32 value)
+{
+ // Do a standard update of the register.
+
+ EmRegs328::StdWrite (address, size, value);
+
+ // Respond to the new interrupt state.
+
+ EmRegs328::UpdateInterrupts ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::intStatusHiWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::intStatusHiWrite (emuptr address, int size, uint32 value)
+{
+ // IRQ1, IRQ2, IRQ3, IRQ6 and IRQ7 are cleared by writing to their
+ // respective status bits. We handle those there. Since there are
+ // no interrupt status bits like this in intStatusLo, we don't need
+ // a handler for that register; we only handle intStatusHi.
+
+ // Even though this is a 16-bit register as defined by the Palm headers,
+ // it's a 32-bit register according to Dragonball docs, and is in fact
+ // accessed that way in the kernal files (cf. HwrIRQ4Handler). In those
+ // cases, we're still only interested in access to the IRQ# bits, so we
+ // can turn 4-byte accesses into 2-byte accesses.
+
+ if (size == 4)
+ value >>= 16;
+
+ // Take into account the possibility of 1-byte accesses, too. If we're
+ // accessing the upper byte, just return. If we're accessing the lower
+ // byte, we can treat it as a 2-byte access.
+
+ else if (size == 1 && address == addressof (intStatusHi))
+ return;
+
+ // Now we can treat the rest of this function as a word-write to intStatusHi.
+
+ uint16 intPendingHi = READ_REGISTER (intPendingHi);
+
+ // For each interrupt:
+ // If we're writing to that interrupt's status bit and its edge bit is set:
+ // - clear the interrupt's pending bit
+ // - respond to the new interrupt state.
+
+ #undef CLEAR_PENDING_INTERRUPT
+ #define CLEAR_PENDING_INTERRUPT(edge, irq) \
+ if ( (READ_REGISTER (intControl) & edge) && (value & (irq)) ) \
+ { \
+ intPendingHi &= ~(irq); \
+ }
+
+ CLEAR_PENDING_INTERRUPT (hwr328IntCtlEdge1, hwr328IntHiIRQ1);
+ CLEAR_PENDING_INTERRUPT (hwr328IntCtlEdge2, hwr328IntHiIRQ2);
+ CLEAR_PENDING_INTERRUPT (hwr328IntCtlEdge3, hwr328IntHiIRQ3);
+ CLEAR_PENDING_INTERRUPT (hwr328IntCtlEdge6, hwr328IntHiIRQ6);
+
+ // IRQ7 is not edge-programmable, so clear it if we're merely writing to it.
+
+ if (value & hwr328IntHiNMI)
+ {
+ intPendingHi &= ~(hwr328IntHiNMI);
+ }
+
+ // If we're emulating the user pressing the hotsync button, make sure the
+ // interrupt stays asserted. (!!! Should we use the same technique for
+ // other buttons, too? It doesn't seem to be needed right now, but doing
+ // that may more closely mirror the hardware.)
+
+ if (fHotSyncButtonDown)
+ {
+ intPendingHi |= hwr328IntHiIRQ1;
+ }
+ else
+ {
+ intPendingHi &= ~hwr328IntHiIRQ1;
+ }
+
+ WRITE_REGISTER (intPendingHi, intPendingHi);
+ EmRegs328::UpdateInterrupts ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::portXDataWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::portXDataWrite (emuptr address, int size, uint32 value)
+{
+ // Get the old value before updating it.
+
+ uint8 oldValue = StdRead (address, size);
+
+ // Take a snapshot of the line driver states.
+
+ Bool driverStates[kUARTEnd];
+ EmHAL::GetLineDriverStates (driverStates);
+
+ // Now update the value with a standard write.
+
+ StdWrite (address, size, value);
+
+ // Let anyone know that it's changed.
+
+ int port = GetPort (address);
+ PRINTF ("EmRegs328::port%cDataWrite: oldValue = 0x%02lX", (char) port, (uint32) (uint8) oldValue);
+ PRINTF ("EmRegs328::port%cDataWrite: newValue = 0x%02lX", (char) port, (uint32) (uint8) value);
+
+ EmHAL::PortDataChanged (port, oldValue, value);
+
+ // Respond to any changes in the line driver states.
+
+ EmHAL::CompareLineDriverStates (driverStates);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::portDIntReqEnWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::portDIntReqEnWrite (emuptr address, int size, uint32 value)
+{
+ // Do a standard update of the register.
+
+ EmRegs328::StdWrite (address, size, value);
+
+ // Set the new interrupt state.
+
+ EmRegs328::UpdatePortDInterrupts ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::tmr1StatusWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::tmr1StatusWrite (emuptr address, int size, uint32 value)
+{
+ UNUSED_PARAM(address)
+ UNUSED_PARAM(size)
+
+ EmAssert (size == 2); // This function's a hell of a lot easier to write if
+ // we assume only full-register access.
+
+ // Get the current value.
+
+ uint16 tmr1Status = READ_REGISTER (tmr1Status);
+
+ // If the user had previously read the status bits while they
+ // were set, then it's OK for them to be clear now. Otherwise,
+ // we have to merge any set status bits back in.
+
+ tmr1Status &= value | ~fLastTmr1Status; // fLastTmr1Status was set in EmRegs328::tmr1StatusRead()
+
+ WRITE_REGISTER (tmr1Status, tmr1Status);
+
+ fLastTmr1Status = 0;
+ if ((tmr1Status & hwr328TmrStatusCompare) == 0)
+ {
+ uint16 intPendingHi = READ_REGISTER (intPendingHi) & ~hwr328IntHiTimer1;
+ WRITE_REGISTER (intPendingHi, intPendingHi);
+
+ // Respond to the new interrupt state.
+
+ EmRegs328::UpdateInterrupts ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::tmr2StatusWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::tmr2StatusWrite (emuptr address, int size, uint32 value)
+{
+ UNUSED_PARAM(address)
+ UNUSED_PARAM(size)
+
+ EmAssert (size == 2); // This function's a hell of a lot easier to write if
+ // we assume only full-register access.
+
+ // Get the current value.
+
+ uint16 tmr2Status = READ_REGISTER (tmr2Status);
+
+ // If the user had previously read the status bits while they
+ // were set, then it's OK for them to be clear now. Otherwise,
+ // we have to merge any set status bits back in.
+
+ tmr2Status &= value | ~fLastTmr2Status; // fLastTmr2Status was set in EmRegs328::tmr2StatusRead()
+
+ WRITE_REGISTER (tmr2Status, tmr2Status);
+
+ fLastTmr2Status = 0;
+ if ( (tmr2Status & hwr328TmrStatusCompare) == 0 )
+ {
+ uint16 intPendingLo = READ_REGISTER (intPendingLo) & ~hwr328IntLoTimer2;
+ WRITE_REGISTER (intPendingLo, intPendingLo);
+
+ // Respond to the new interrupt state.
+
+ EmRegs328::UpdateInterrupts ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::wdCounterWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::wdCounterWrite (emuptr address, int size, uint32 value)
+{
+ UNUSED_PARAM(address)
+ UNUSED_PARAM(size)
+ UNUSED_PARAM(value)
+
+ // Always set it to zero (a write to this register always resets it).
+
+ WRITE_REGISTER (wdCounter, 0);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::spiMasterControlWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::spiMasterControlWrite (emuptr address, int size, uint32 value)
+{
+ // Do a standard update of the register.
+
+ EmRegs328::StdWrite (address, size, value);
+
+ // Get the current value.
+
+ uint16 spiMasterControl = READ_REGISTER (spiMasterControl);
+
+ // Check to see if data exchange and interrupts are enabled.
+
+ #define BIT_MASK (hwr328SPIMControlExchange | hwr328SPIMControlIntEnable)
+ if ((spiMasterControl & BIT_MASK) != 0)
+ {
+ // If so, assert the interrupt and clear the exchange bit.
+
+ spiMasterControl |= hwr328SPIMControlIntStatus;
+ spiMasterControl &= ~hwr328SPIMControlExchange;
+
+ WRITE_REGISTER (spiMasterControl, spiMasterControl);
+
+/*
+ // If we wanted digitizer data, load it into the SPIM data register.
+
+ switch (READ_REGISTER (portFData) & hwrTD1PortFPanelMask)
+ {
+ case (hwrTD1PortFPanelCfgXMeas):
+ WRITE_REGISTER (spiMasterData, (0xFF - Hardware::fgPen_HorzLocation) * 2);
+ break;
+
+ case (hwrTD1PortFPanelCfgYMeas):
+ WRITE_REGISTER (spiMasterData, (0xFF - Hardware::fgPen_VertLocation) * 2);
+ break;
+ }
+*/
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::uartWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::uartWrite(emuptr address, int size, uint32 value)
+{
+ // Do a standard update of the register.
+
+ EmRegs328::StdWrite (address, size, value);
+
+ // If this write included the TX_DATA field, signal that it needs to
+ // be transmitted.
+
+ Bool sendTxData =
+ ((address == addressof (uTransmit)) && (size == 2)) ||
+ ((address == addressof (uTransmit) + 1) && (size == 1));
+
+ // React to any changes.
+
+ EmRegs328::UARTStateChanged (sendTxData);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::lcdRegisterWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::lcdRegisterWrite(emuptr address, int size, uint32 value)
+{
+ // First, get the old value in case we need to see what changed.
+
+ uint32 oldValue = EmRegs328::StdRead (address, size);
+
+ // Do a standard update of the register.
+
+ EmRegs328::StdWrite (address, size, value);
+
+ // Note what changed.
+
+ if (address == addressof (lcdScreenWidth))
+ {
+ EmScreen::InvalidateAll ();
+ }
+ else if (address == addressof (lcdScreenHeight))
+ {
+ EmScreen::InvalidateAll ();
+ }
+ else if (address == addressof (lcdPanelControl))
+ {
+ if (((value ^ oldValue) & hwr328LcdPanelControlGrayScale) != 0)
+ {
+ EmScreen::InvalidateAll ();
+ }
+ }
+ else if (address == addressof (lcdStartAddr))
+ {
+ // Make sure the low-bit is always zero.
+
+ uint32 lcdStartAddr = READ_REGISTER (lcdStartAddr) & 0xFFFFFFFE;
+ WRITE_REGISTER (lcdStartAddr, lcdStartAddr);
+
+ EmScreen::InvalidateAll ();
+ }
+ else if (address == addressof (lcdPageWidth))
+ {
+ if (value != oldValue)
+ {
+ EmScreen::InvalidateAll ();
+ }
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::rtcControlWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::rtcControlWrite(emuptr address, int size, uint32 value)
+{
+ // Do a standard update of the register.
+
+ EmRegs328::StdWrite (address, size, value);
+
+ // Respond to the new interrupt state.
+
+ EmRegs328::UpdateRTCInterrupts ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::rtcIntStatusWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::rtcIntStatusWrite(emuptr address, int size, uint32 value)
+{
+ // Status bits are cleared by writing ones to them.
+
+ // If we're doing a byte-write to the upper byte, shift the byte
+ // so that we can treat the operation as a word write. If we're
+ // doing a byte-write to the lower byte, this extension will happen
+ // automatically.
+
+ if (address == addressof (rtcIntStatus) && size == 1)
+ value <<= 8;
+
+ // Get the current value.
+
+ uint16 rtcIntStatus = READ_REGISTER (rtcIntStatus);
+
+ // Clear the requested bits.
+
+ rtcIntStatus &= ~value;
+
+ // Update the register.
+
+ WRITE_REGISTER (rtcIntStatus, rtcIntStatus);
+
+ // Respond to the new interrupt state.
+
+ EmRegs328::UpdateRTCInterrupts ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::rtcIntEnableWrite
+// ---------------------------------------------------------------------------
+
+void EmRegs328::rtcIntEnableWrite(emuptr address, int size, uint32 value)
+{
+ // Do a standard update of the register.
+
+ EmRegs328::StdWrite (address, size, value);
+
+ // Respond to the new interrupt state.
+
+ EmRegs328::UpdateRTCInterrupts ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::ButtonEvent
+// ---------------------------------------------------------------------------
+// Handles a Palm device button event by updating the appropriate registers.
+
+void EmRegs328::ButtonEvent (SkinElementType button, Bool buttonIsDown)
+{
+ uint16 bitNumber = this->ButtonToBits (button);
+
+ // Get the bits that should have been set with the previous set
+ // of pressed keys. We use this old value to update the port D interrupts.
+
+ uint8 oldBits = this->GetKeyBits ();
+
+ // Update the set of keys that are currently pressed.
+
+ if (buttonIsDown)
+ {
+ fKeyBits |= bitNumber; // Remember the key bit
+ }
+ else
+ {
+ fKeyBits &= ~bitNumber; // Forget the key bit
+ }
+
+ // Now get the new set of bits that should be set.
+
+ uint8 newBits = this->GetKeyBits ();
+
+ PRINTF ("EmRegs328::ButtonEvent: fKeyBits = 0x%04lX", (uint32) fKeyBits);
+ PRINTF ("EmRegs328::ButtonEvent: oldBits = 0x%02lX", (uint32) oldBits);
+ PRINTF ("EmRegs328::ButtonEvent: newBits = 0x%02lX", (uint32) newBits);
+
+ // Set the interrupt bits for the bits that went from off to on.
+ // These get cleared when portDData is written to.
+
+ fPortDEdge |= newBits & ~oldBits;
+
+ PRINTF ("EmRegs328::ButtonEvent: fPortDEdge = 0x%02lX", (uint32) fPortDEdge);
+
+ // Set the new interrupt state.
+
+ EmRegs328::UpdatePortDInterrupts ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::HotSyncEvent
+// ---------------------------------------------------------------------------
+// Handles a HotSync button event by updating the appropriate registers.
+
+void EmRegs328::HotSyncEvent (Bool iButton_IsDown)
+{
+ // If the button changes state, set or clear the HotSync interrupt.
+
+ uint16 intPendingHi = READ_REGISTER (intPendingHi);
+
+ if (iButton_IsDown)
+ {
+ intPendingHi |= hwr328IntHiIRQ1;
+ fHotSyncButtonDown = true;
+ }
+ else
+ {
+ intPendingHi &= ~hwr328IntHiIRQ1;
+ fHotSyncButtonDown = false;
+ }
+
+ WRITE_REGISTER (intPendingHi, intPendingHi);
+
+ EmRegs328::UpdateInterrupts ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetKeyBits
+// ---------------------------------------------------------------------------
+
+uint8 EmRegs328::GetKeyBits (void)
+{
+ // Return the key bits
+
+ uint8 portDPullupEn = READ_REGISTER (portDPullupEn); // Interested where bits are one
+ uint8 keyBits = portDPullupEn & fKeyBits;
+
+ PRINTF ("EmRegs328::GetKeyBits: keyBits = 0x%02lX", (uint32) keyBits);
+
+ return keyBits;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::ButtonToBits
+// ---------------------------------------------------------------------------
+
+uint16 EmRegs328::ButtonToBits (SkinElementType button)
+{
+ uint16 bitNumber = 0;
+ switch (button)
+ {
+ case kElement_None: break;
+
+ case kElement_PowerButton: bitNumber = keyBitPower; break;
+ case kElement_UpButton: bitNumber = keyBitPageUp; break;
+ case kElement_DownButton: bitNumber = keyBitPageDown; break;
+ case kElement_App1Button: bitNumber = keyBitHard1; break;
+ case kElement_App2Button: bitNumber = keyBitHard2; break;
+ case kElement_App3Button: bitNumber = keyBitHard3; break;
+ case kElement_App4Button: bitNumber = keyBitHard4; break;
+ case kElement_CradleButton: bitNumber = keyBitCradle; break;
+ case kElement_Antenna: bitNumber = keyBitAntenna; break;
+ case kElement_ContrastButton: bitNumber = keyBitContrast; break;
+
+/*
+ // Symbol-specific
+ case kElement_TriggerLeft: bitNumber = keyBitTrigLeft; break;
+ case kElement_TriggerCenter: bitNumber = keyBitTrigCenter; break;
+ case kElement_TriggerRight: bitNumber = keyBitTrigRight; break;
+ case kElement_UpButtonLeft: bitNumber = keyBitPageUpLeft; break;
+ case kElement_UpButtonRight: bitNumber = keyBitPageUpRight; break;
+ case kElement_DownButtonLeft: bitNumber = keyBitPageDownLeft; break;
+ case kElement_DownButtonRight: bitNumber = keyBitPageDownRight; break;
+*/
+ default: EmAssert (false);
+ }
+
+ return bitNumber;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::UpdateInterrupts
+// ---------------------------------------------------------------------------
+// Determines whether an interrupt has occurred by copying the Interrupt
+// Pending Register to the Interrupt Status Register.
+
+void EmRegs328::UpdateInterrupts (void)
+{
+ // Copy the Interrupt Pending Register to the Interrupt Status
+ // Register, but ignore interrupts that are being masked.
+
+ // Note: this function is not sensitive to the byte ordering of the registers,
+ // so their contents don't need to be accessed via READ_REGISTER or WRITE_REGISTER.
+
+ f68328Regs.intStatusHi = f68328Regs.intPendingHi & ~f68328Regs.intMaskHi;
+ f68328Regs.intStatusLo = f68328Regs.intPendingLo & ~f68328Regs.intMaskLo;
+
+ PRINTF ("EmRegs328::UpdateInterrupts: intMask = 0x%04lX %04lX",
+ (uint32) f68328Regs.intMaskHi, (uint32) f68328Regs.intMaskLo);
+
+ PRINTF ("EmRegs328::UpdateInterrupts: intPending = 0x%04lX %04lX",
+ (uint32) f68328Regs.intPendingHi, (uint32) f68328Regs.intPendingLo);
+
+ // If the Interrupt Status Register isn't clear, flag an interrupt.
+
+ if (f68328Regs.intStatusHi || f68328Regs.intStatusLo)
+ {
+ regs.spcflags |= SPCFLAG_INT;
+
+ PRINTF ("EmRegs328::UpdateInterrupts: intStatus = 0x%04lX %04lX",
+ (uint32) f68328Regs.intStatusHi, (uint32) f68328Regs.intStatusLo);
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::UpdatePortDInterrupts
+// ---------------------------------------------------------------------------
+// Determine what interrupts need to be generated based on the current
+// settings in portDData and fPortDEdge.
+
+void EmRegs328::UpdatePortDInterrupts (void)
+{
+ // Update INT0-INT7 of the Interrupt-Pending register (bits 8-15 of the low word).
+
+ // First, get those bits and clear them out.
+
+ uint16 intPendingLo = READ_REGISTER (intPendingLo) & ~hwr328IntLoAllKeys;
+
+
+ // Initialize the variable to hold the new interrupt settings.
+
+ uint8 newBits = 0;
+
+
+ // Get some other values we're going to need:
+
+ uint8 portDDir = READ_REGISTER (portDDir); // Interrupt on inputs only (when pin is low)
+ uint8 portDData = EmHAL::GetPortInputValue ('D');
+ uint8 portDPolarity = READ_REGISTER (portDPolarity);
+ uint8 portDIntReqEn = READ_REGISTER (portDIntReqEn);
+ uint8 portDIntEdge = READ_REGISTER (portDIntEdge);
+
+
+ // We have a line-level interrupt if:
+ //
+ // - line-level interrupts are requested
+ // - the GPIO bit matches the polarity bit
+
+ newBits |= ~portDIntEdge & portDData & portDPolarity;
+ newBits |= ~portDIntEdge & ~portDData & ~portDPolarity;
+
+
+ // We have an edge interrupt if:
+ //
+ // - edge interrupts are requested
+ // - an edge has been recorded
+ //
+ // Note that we should distinguish between rising and falling edges.
+ // For historical reasons, that's not done, and the Palm OS doesn't
+ // look for them, so it's OK for now.
+
+ newBits |= portDIntEdge & fPortDEdge & portDPolarity;
+ newBits |= portDIntEdge & 0 & ~portDPolarity;
+
+
+ // Only have interrupts if they're enabled and the pin is configured for input.
+
+ newBits &= portDIntReqEn & ~portDDir;
+
+ PRINTF ("EmRegs328::UpdatePortDInterrupts: Dir Data Pol Req Edg PDE bits");
+ PRINTF ("EmRegs328::UpdatePortDInterrupts: 0x%02lX 0x%02lX 0x%02lX 0x%02lX 0x%02lX 0x%02lX 0x%02lX",
+ (uint32) portDDir, (uint32) portDData, (uint32) portDPolarity, (uint32) portDIntReqEn, (uint32) portDIntEdge,
+ (uint32) fPortDEdge, (uint32) newBits);
+
+
+ // Merge in the new values and write out the result.
+
+ intPendingLo |= (((uint16) newBits) << 8) & hwr328IntLoAllKeys;
+ WRITE_REGISTER (intPendingLo, intPendingLo);
+
+
+ // Respond to the new interrupt state.
+
+ EmRegs328::UpdateInterrupts ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::UpdateRTCInterrupts
+// ---------------------------------------------------------------------------
+// Determine whether to set or clear the RTC bit in the interrupt pending
+// register based on the current RTC register values.
+
+void EmRegs328::UpdateRTCInterrupts (void)
+{
+ // See if the RTC is enabled.
+
+ Bool rtcEnabled = (READ_REGISTER (rtcControl) & hwr328RTCControlRTCEnable) != 0;
+
+ // See if there are any RTC events that need to trigger an interrupt.
+
+#define BITS_TO_CHECK ( \
+ hwr328RTCIntEnableSec | \
+ hwr328RTCIntEnable24Hr | \
+ hwr328RTCIntEnableAlarm | \
+ hwr328RTCIntEnableMinute | \
+ hwr328RTCIntEnableStopWatch )
+
+ uint16 rtcIntStatus = READ_REGISTER (rtcIntStatus);
+ uint16 rtcIntEnable = READ_REGISTER (rtcIntEnable);
+ uint16 rtcIntPending = rtcIntStatus & rtcIntEnable & BITS_TO_CHECK;
+
+ Bool havePendingEvents = rtcIntPending != 0;
+
+ // If the RTC is enabled and there are pending events, set the interrupt.
+ // Otherwise, clear the interrupt.
+
+ uint16 intPendingLo = READ_REGISTER (intPendingLo);
+
+ if (rtcEnabled && havePendingEvents)
+ {
+ intPendingLo |= hwr328IntLoRTC; // have events, so set interrupt
+ }
+ else
+ {
+ intPendingLo &= ~hwr328IntLoRTC; // no events, so clear interrupt
+ }
+
+ // Update the interrupt pending register.
+
+ WRITE_REGISTER (intPendingLo, intPendingLo);
+
+ // Respond to the new interrupt state.
+
+ EmRegs328::UpdateInterrupts ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::IDDetectAsserted
+// ---------------------------------------------------------------------------
+// cf. HwrIdentifyFeatures and HwrPreRAMInit.
+
+Bool EmRegs328::IDDetectAsserted (void)
+{
+ uint8 portEDir = READ_REGISTER (portEDir);
+ uint8 portEData = READ_REGISTER (portEData);
+ uint8 portEPullupEn = READ_REGISTER (portEPullupEn);
+ const uint8 kMask = hwrTD1PortENoBacklight;
+
+ return (portEDir & kMask) == kMask &&
+ (portEData & kMask) == 0 &&
+ (portEPullupEn & kMask) == 0;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetHardwareID
+// ---------------------------------------------------------------------------
+
+UInt8 EmRegs328::GetHardwareID (void)
+{
+ // Determine the hardware ID.
+
+ // Note: Because of a Poser bug, I don't think any of the following actually
+ // gets executed. 328 ROMs first check to see if they're on a PalmPilot
+ // by executing the following:
+ //
+ // if (!(baseP->portEData & hwrTD1PortENoBacklight)) {
+ // ...on PalmPilot...
+ // } else {
+ // ...execute ID DETECT...
+ // }
+ //
+ // In Poser, hwrTD1PortENoBacklight is always zero (that's the bug), and
+ // so we always think we're on a PalmPilot, regardless of what 328 device
+ // we selected.
+
+ EmAssert (gSession);
+
+ EmDevice device = gSession->GetDevice ();
+ long miscFlags = device.HardwareID ();
+
+ // Reverse map the following:
+// GHwrMiscFlags = 0;
+// if ( (keyState & keyBitHard1) == 0) GHwrMiscFlags |= hwrMiscFlagID1;
+// if ( (keyState & keyBitHard2) == 0) GHwrMiscFlags |= hwrMiscFlagID2;
+// if ( (keyState & keyBitHard3) == 0) GHwrMiscFlags |= hwrMiscFlagID3;
+// if ( (keyState & keyBitHard4) == 0) GHwrMiscFlags |= hwrMiscFlagID4;
+
+ uint8 keyState = ~0;
+
+ if ((miscFlags & hwrMiscFlagID1) != 0) keyState &= ~keyBitHard1;
+ if ((miscFlags & hwrMiscFlagID2) != 0) keyState &= ~keyBitHard2;
+ if ((miscFlags & hwrMiscFlagID3) != 0) keyState &= ~keyBitHard3;
+ if ((miscFlags & hwrMiscFlagID4) != 0) keyState &= ~keyBitHard4;
+
+ return keyState;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::UARTStateChanged
+// ---------------------------------------------------------------------------
+
+void EmRegs328::UARTStateChanged (Bool sendTxData)
+{
+ EmUARTDragonball::State state (EmUARTDragonball::kUART_Dragonball);
+
+ EmRegs328::MarshalUARTState (state);
+ fUART->StateChanged (state, sendTxData);
+ EmRegs328::UnmarshalUARTState (state);
+
+ EmRegs328::UpdateUARTInterrupts (state);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::UpdateUARTState
+// ---------------------------------------------------------------------------
+
+void EmRegs328::UpdateUARTState (Bool refreshRxData)
+{
+ EmUARTDragonball::State state (EmUARTDragonball::kUART_Dragonball);
+
+ EmRegs328::MarshalUARTState (state);
+ fUART->UpdateState (state, refreshRxData);
+ UnmarshalUARTState (state);
+
+ EmRegs328::UpdateUARTInterrupts (state);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::UpdateUARTInterrupts
+// ---------------------------------------------------------------------------
+
+void EmRegs328::UpdateUARTInterrupts (const EmUARTDragonball::State& state)
+{
+ // Generate the appropriate interrupts.
+
+ if (state.RX_FULL_ENABLE && state.RX_FIFO_FULL ||
+ state.RX_HALF_ENABLE && state.RX_FIFO_HALF ||
+ state.RX_RDY_ENABLE && state.DATA_READY ||
+ state.TX_EMPTY_ENABLE && state.TX_FIFO_EMPTY ||
+ state.TX_HALF_ENABLE && state.TX_FIFO_HALF ||
+ state.TX_AVAIL_ENABLE && state.TX_AVAIL)
+ {
+ // Set the UART interrupt.
+
+ WRITE_REGISTER (intPendingLo, READ_REGISTER (intPendingLo) | hwr328IntLoUART);
+ }
+ else
+ {
+ // Clear the UART interrupt.
+
+ WRITE_REGISTER (intPendingLo, READ_REGISTER (intPendingLo) & ~hwr328IntLoUART);
+ }
+
+ // Respond to the new interrupt state.
+
+ EmRegs328::UpdateInterrupts ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::MarshalUARTState
+// ---------------------------------------------------------------------------
+
+void EmRegs328::MarshalUARTState (EmUARTDragonball::State& state)
+{
+ uint16 uControl = READ_REGISTER (uControl);
+ uint16 uBaud = READ_REGISTER (uBaud);
+ uint16 uReceive = READ_REGISTER (uReceive);
+ uint16 uTransmit = READ_REGISTER (uTransmit);
+ uint16 uMisc = READ_REGISTER (uMisc);
+
+ state.UART_ENABLE = (uControl & hwr328UControlUARTEnable) != 0;
+ state.RX_ENABLE = (uControl & hwr328UControlRxEnable) != 0;
+ state.TX_ENABLE = (uControl & hwr328UControlTxEnable) != 0;
+ state.RX_CLK_CONT = (uControl & hwr328UControlRxClock1x) != 0;
+ state.PARITY_EN = (uControl & hwr328UControlParityEn) != 0;
+ state.ODD_EVEN = (uControl & hwr328UControlParityOdd) != 0;
+ state.STOP_BITS = (uControl & hwr328UControlStopBits2) != 0;
+ state.CHAR8_7 = (uControl & hwr328UControlDataBits8) != 0;
+ state.GPIO_DELTA_ENABLE = (uControl & hwr328UControlGPIODeltaEn) != 0; // 68328 only
+// state.OLD_ENABLE = (uControl & hwrEZ328UControlOldDataEn) != 0; // 68EZ328 only
+ state.CTS_DELTA_ENABLE = (uControl & hwr328UControlCTSDeltaEn) != 0;
+ state.RX_FULL_ENABLE = (uControl & hwr328UControlRxFullEn) != 0;
+ state.RX_HALF_ENABLE = (uControl & hwr328UControlRxHalfEn) != 0;
+ state.RX_RDY_ENABLE = (uControl & hwr328UControlRxRdyEn) != 0;
+ state.TX_EMPTY_ENABLE = (uControl & hwr328UControlTxEmptyEn) != 0;
+ state.TX_HALF_ENABLE = (uControl & hwr328UControlTxHalfEn) != 0;
+ state.TX_AVAIL_ENABLE = (uControl & hwr328UControlTxAvailEn) != 0;
+
+ // Baud control register bits
+ // These are all values the user sets; we just look at them.
+
+ state.GPIO_DELTA = (uBaud & hwr328UBaudGPIODelta) != 0; // 68328 only
+ state.GPIO = (uBaud & hwr328UBaudGPIOData) != 0; // 68328 only
+ state.GPIO_DIR = (uBaud & hwr328UBaudGPIODirOut) != 0; // 68328 only
+ state.GPIO_SRC = (uBaud & hwr328UBaudGPIOSrcBaudGen) != 0; // 68328 only
+// state.UCLK_DIR = (uBaud & hwrEZ328UBaudUCLKDirOut) != 0; // 68EZ328 only
+ state.BAUD_SRC = (uBaud & hwr328UBaudBaudSrcGPIO) != 0;
+ state.DIVIDE = (uBaud & hwr328UBaudDivider) >> hwr328UBaudDivideBitOffset;
+ state.PRESCALER = (uBaud & hwr328UBaudPrescaler);
+
+ // Receive register bits
+ // These are all input bits; we set them, not the user.
+
+ state.RX_FIFO_FULL = (uReceive & hwr328UReceiveFIFOFull) != 0;
+ state.RX_FIFO_HALF = (uReceive & hwr328UReceiveFIFOHalf) != 0;
+ state.DATA_READY = (uReceive & hwr328UReceiveDataRdy) != 0;
+// state.OLD_DATA = (uReceive & hwrEZ328UReceiveOldData) != 0; // 68EZ328 only
+ state.OVRUN = (uReceive & hwr328UReceiveOverrunErr) != 0;
+ state.FRAME_ERROR = (uReceive & hwr328UReceiveFrameErr) != 0;
+ state.BREAK = (uReceive & hwr328UReceiveBreakErr) != 0;
+ state.PARITY_ERROR = (uReceive & hwr328UReceiveParityErr) != 0;
+ state.RX_DATA = (uReceive & hwr328UReceiveData);
+
+ // Transmitter register bits
+ // We set everything except TX_DATA; the user sets that
+ // value and ONLY that value.
+
+ state.TX_FIFO_EMPTY = (uTransmit & hwr328UTransmitFIFOEmpty) != 0;
+ state.TX_FIFO_HALF = (uTransmit & hwr328UTransmitFIFOHalf) != 0;
+ state.TX_AVAIL = (uTransmit & hwr328UTransmitTxAvail) != 0;
+ state.SEND_BREAK = (uTransmit & hwr328UTransmitSendBreak) != 0;
+ state.IGNORE_CTS = (uTransmit & hwr328UTransmitIgnoreCTS) != 0;
+// state.BUSY = (uTransmit & hwrEZ328UTransmitBusy) != 0; // 68EZ328 only
+ state.CTS_STATUS = (uTransmit & hwr328UTransmitCTSStatus) != 0;
+ state.CTS_DELTA = (uTransmit & hwr328UTransmitCTSDelta) != 0;
+ state.TX_DATA = (uTransmit & hwr328UTransmitData);
+
+ // Misc register bits
+ // These are all values the user sets; we just look at them.
+
+// state.BAUD_TEST = (uMisc & hwrEZ328UMiscBaudTest) != 0; // 68EZ328 only
+ state.CLK_SRC = (uMisc & hwr328UMiscClkSrcGPIO) != 0;
+ state.FORCE_PERR = (uMisc & hwr328UMiscForceParityErr) != 0;
+ state.LOOP = (uMisc & hwr328UMiscLoopback) != 0;
+// state.BAUD_RESET = (uMisc & hwrEZ328UMiscBaudReset) != 0; // 68EZ328 only
+// state.IR_TEST = (uMisc & hwrEZ328UMiscIRTestEn) != 0; // 68EZ328 only
+ state.RTS_CONT = (uMisc & hwr328UMiscRTSThruFIFO) != 0;
+ state.RTS = (uMisc & hwr328UMiscRTSOut) != 0;
+ state.IRDA_ENABLE = (uMisc & hwr328UMiscIRDAEn) != 0;
+ state.IRDA_LOOP = (uMisc & hwr328UMiscLoopIRDA) != 0;
+// state.RX_POL = (uMisc & hwrEZ328UMiscRXPolarityInv) != 0; // 68EZ328 only
+// state.TX_POL = (uMisc & hwrEZ328UMiscTXPolarityInv) != 0; // 68EZ328 only
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::UnmarshalUARTState
+// ---------------------------------------------------------------------------
+
+void EmRegs328::UnmarshalUARTState (const EmUARTDragonball::State& state)
+{
+ uint16 uControl = 0;
+ uint16 uBaud = 0;
+ uint16 uReceive = 0;
+ uint16 uTransmit = 0;
+ uint16 uMisc = 0;
+
+ if (state.UART_ENABLE) uControl |= hwr328UControlUARTEnable;
+ if (state.RX_ENABLE) uControl |= hwr328UControlRxEnable;
+ if (state.TX_ENABLE) uControl |= hwr328UControlTxEnable;
+ if (state.RX_CLK_CONT) uControl |= hwr328UControlRxClock1x;
+ if (state.PARITY_EN) uControl |= hwr328UControlParityEn;
+ if (state.ODD_EVEN) uControl |= hwr328UControlParityOdd;
+ if (state.STOP_BITS) uControl |= hwr328UControlStopBits2;
+ if (state.CHAR8_7) uControl |= hwr328UControlDataBits8;
+ if (state.GPIO_DELTA_ENABLE)uControl |= hwr328UControlGPIODeltaEn; // 68328 only
+// if (state.OLD_ENABLE) uControl |= hwrEZ328UControlOldDataEn; // 68EZ328 only
+ if (state.CTS_DELTA_ENABLE) uControl |= hwr328UControlCTSDeltaEn;
+ if (state.RX_FULL_ENABLE) uControl |= hwr328UControlRxFullEn;
+ if (state.RX_HALF_ENABLE) uControl |= hwr328UControlRxHalfEn;
+ if (state.RX_RDY_ENABLE) uControl |= hwr328UControlRxRdyEn;
+ if (state.TX_EMPTY_ENABLE) uControl |= hwr328UControlTxEmptyEn;
+ if (state.TX_HALF_ENABLE) uControl |= hwr328UControlTxHalfEn;
+ if (state.TX_AVAIL_ENABLE) uControl |= hwr328UControlTxAvailEn;
+
+ // Baud control register bits
+ // These are all values the user sets; we just look at them.
+
+ if (state.GPIO_DELTA) uBaud |= hwr328UBaudGPIODelta; // 68328 only
+ if (state.GPIO) uBaud |= hwr328UBaudGPIOData; // 68328 only
+ if (state.GPIO_DIR) uBaud |= hwr328UBaudGPIODirOut; // 68328 only
+ if (state.GPIO_SRC) uBaud |= hwr328UBaudGPIOSrcBaudGen; // 68328 only
+// if (state.UCLK_DIR) uBaud |= hwrEZ328UBaudUCLKDirOut; // 68EZ328 only
+ if (state.BAUD_SRC) uBaud |= hwr328UBaudBaudSrcGPIO;
+
+ uBaud |= (state.DIVIDE << hwr328UBaudDivideBitOffset) & hwr328UBaudDivider;
+ uBaud |= (state.PRESCALER) & hwr328UBaudPrescaler;
+
+ // Receive register bits
+ // These are all input bits; we set them, not the user.
+
+ if (state.RX_FIFO_FULL) uReceive |= hwr328UReceiveFIFOFull;
+ if (state.RX_FIFO_HALF) uReceive |= hwr328UReceiveFIFOHalf;
+ if (state.DATA_READY) uReceive |= hwr328UReceiveDataRdy;
+// if (state.OLD_DATA) uReceive |= hwrEZ328UReceiveOldData; // 68EZ328 only
+ if (state.OVRUN) uReceive |= hwr328UReceiveOverrunErr;
+ if (state.FRAME_ERROR) uReceive |= hwr328UReceiveFrameErr;
+ if (state.BREAK) uReceive |= hwr328UReceiveBreakErr;
+ if (state.PARITY_ERROR) uReceive |= hwr328UReceiveParityErr;
+
+ uReceive |= (state.RX_DATA) & hwr328UReceiveData;
+
+ // Transmitter register bits
+ // We set everything except TX_DATA; the user sets that
+ // value and ONLY that value.
+
+ if (state.TX_FIFO_EMPTY) uTransmit |= hwr328UTransmitFIFOEmpty;
+ if (state.TX_FIFO_HALF) uTransmit |= hwr328UTransmitFIFOHalf;
+ if (state.TX_AVAIL) uTransmit |= hwr328UTransmitTxAvail;
+ if (state.SEND_BREAK) uTransmit |= hwr328UTransmitSendBreak;
+ if (state.IGNORE_CTS) uTransmit |= hwr328UTransmitIgnoreCTS;
+// if (state.BUSY) uTransmit |= hwrEZ328UTransmitBusy; // 68EZ328 only
+ if (state.CTS_STATUS) uTransmit |= hwr328UTransmitCTSStatus;
+ if (state.CTS_DELTA) uTransmit |= hwr328UTransmitCTSDelta;
+
+ uTransmit |= (state.TX_DATA) & hwr328UTransmitData;
+
+ // Misc register bits
+ // These are all values the user sets; we just look at them.
+
+// if (state.BAUD_TEST) uMisc |= hwrEZ328UMiscBaudTest; // 68EZ328 only
+ if (state.CLK_SRC) uMisc |= hwr328UMiscClkSrcGPIO;
+ if (state.FORCE_PERR) uMisc |= hwr328UMiscForceParityErr;
+ if (state.LOOP) uMisc |= hwr328UMiscLoopback;
+// if (state.BAUD_RESET) uMisc |= hwrEZ328UMiscBaudReset; // 68EZ328 only
+// if (state.IR_TEST) uMisc |= hwrEZ328UMiscIRTestEn; // 68EZ328 only
+ if (state.RTS_CONT) uMisc |= hwr328UMiscRTSThruFIFO;
+ if (state.RTS) uMisc |= hwr328UMiscRTSOut;
+ if (state.IRDA_ENABLE) uMisc |= hwr328UMiscIRDAEn;
+ if (state.IRDA_LOOP) uMisc |= hwr328UMiscLoopIRDA;
+// if (state.RX_POL) uMisc |= hwrEZ328UMiscRXPolarityInv; // 68EZ328 only
+// if (state.TX_POL) uMisc |= hwrEZ328UMiscTXPolarityInv; // 68EZ328 only
+
+ WRITE_REGISTER (uControl, uControl);
+ WRITE_REGISTER (uBaud, uBaud);
+ WRITE_REGISTER (uReceive, uReceive);
+ WRITE_REGISTER (uTransmit, uTransmit);
+ WRITE_REGISTER (uMisc, uMisc);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::GetPort
+// ---------------------------------------------------------------------------
+// Given an address, return a value indicating what port it is associated with.
+
+int EmRegs328::GetPort (emuptr address)
+{
+ const long MASK = 0x00000FF8;
+
+ switch (address & MASK)
+ {
+ case 0x0400: return 'A';
+ case 0x0408: return 'B';
+ case 0x0410: return 'C';
+ case 0x0418: return 'D';
+ case 0x0420: return 'E';
+ case 0x0428: return 'F';
+ case 0x0430: return 'G';
+ case 0x0438: return 'J';
+ case 0x0440: return 'K';
+ case 0x0448: return 'M';
+ }
+
+ EmAssert (false);
+ return 0;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmRegs328::PrvGetPalette
+// ---------------------------------------------------------------------------
+
+void EmRegs328::PrvGetPalette (RGBList& thePalette)
+{
+ // !!! TBD
+ Preference<RGBType> pref1 (kPrefKeyBackgroundColor);
+ Preference<RGBType> pref2 (kPrefKeyHighlightColor);
+
+ RGBType foreground (0, 0, 0);
+ RGBType background;
+
+ if (this->GetLCDBacklightOn ())
+ {
+ if (pref2.Loaded ())
+ background = *pref2;
+ else
+ background = ::SkinGetHighlightColor ();
+ }
+ else
+ {
+ if (pref1.Loaded ())
+ background = *pref1;
+ else
+ background = ::SkinGetBackgroundColor ();
+ }
+
+ long br = ((long) background.fRed);
+ long bg = ((long) background.fGreen);
+ long bb = ((long) background.fBlue);
+
+ long dr = ((long) foreground.fRed) - ((long) background.fRed);
+ long dg = ((long) foreground.fGreen) - ((long) background.fGreen);
+ long db = ((long) foreground.fBlue) - ((long) background.fBlue);
+
+ int32 bpp = 1 << (READ_REGISTER (lcdPanelControl) & 0x01);
+ int32 numColors = 1 << bpp;
+ thePalette.resize (numColors);
+
+ for (int color = 0; color < numColors; ++color)
+ {
+ thePalette[color].fRed = (UInt8) (br + dr * color / (numColors - 1));
+ thePalette[color].fGreen = (UInt8) (bg + dg * color / (numColors - 1));
+ thePalette[color].fBlue = (UInt8) (bb + db * color / (numColors - 1));
+ }
+}
+
+