]> localhost Git - WindEmu.git/commitdiff
sa1100: add initial uart implementation
authorGeorge Wright <gw@gwright.org.uk>
Thu, 25 Jun 2026 02:38:10 +0000 (19:38 -0700)
committerGeorge Wright <gw@gwright.org.uk>
Fri, 26 Jun 2026 03:44:07 +0000 (20:44 -0700)
CMakeLists.txt
WindCore/series7/uart.cpp [new file with mode: 0644]
WindCore/series7/uart.h [new file with mode: 0644]

index 5428ba94fab8379bac54fd7a010acf5a900087ce..586305b08d500d1080ee130bfcaa3a26ba7e3b2f 100644 (file)
@@ -32,6 +32,7 @@ set(WIND_CORE_SOURCES
     WindCore/series7/rtc.cpp
     WindCore/series7/sa1100.cpp
     WindCore/series7/sa1100_memory.cpp
+    WindCore/series7/uart.cpp
 )
 
 add_library(WindCore STATIC ${WIND_CORE_SOURCES})
diff --git a/WindCore/series7/uart.cpp b/WindCore/series7/uart.cpp
new file mode 100644 (file)
index 0000000..2ef30e8
--- /dev/null
@@ -0,0 +1,356 @@
+// ARMware - an ARM emulator
+// Copyright (C) <2007>  Wei Hu <wei.hu.tw@gmail.com>
+//
+// 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 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "uart.h"
+
+namespace SA1100 {
+
+Uart::Uart(std::function<void(const char*)>& output) : mSink(output) {
+  initRegisters();
+}
+
+void Uart::initRegisters() {
+  mUTCR0 = 0;
+  mUTCR1 = 0;
+  mUTCR2 = 0;
+  mUTCR3 = 0;
+  mUTDR = 0;
+
+  mUTSR0 = UTSR0_TFS; // Transmit FIFO service request
+  mUTSR1 = UTSR1_TNF; // Transmit FIFO not full
+  mTxFifo.clear();
+  mRxFifo.clear();
+  mNextTxCycle = 0;
+  mTxBuffer.clear();
+}
+
+void Uart::reset() {
+  // bit 7 sets 0 on reset.
+  mUTCR0 &= ~(1 << 7);
+
+  // bit 4, 5, 6, 7 set 0 on reset.
+  mUTCR1 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4));
+
+  // bit 0, 1, 6, 7 set 0 on reset.
+  mUTCR3 &= ~((1 << 7) | (1 << 6) | (1 << 1) | (1 << 0));
+
+  // bit 0, 1, 5, 6, 7 set 0 on reset.
+  mUTSR0 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 1) | (1 << 0));
+
+  // bit 0, 1, 5, 6, 7 set 0 on reset.
+  mUTSR1 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 1) | (1 << 0));
+
+  mTxFifo.clear();
+  mRxFifo.clear();
+  mNextTxCycle = 0;
+  mTxBuffer.clear();
+}
+
+void Uart::run(int64_t passedCycles) {
+  if (mUTCR3 & UTCR3_TXE) {
+    if (!mTxFifo.empty()) {
+      if (mNextTxCycle == 0) {
+        mNextTxCycle = passedCycles + 16000;
+      }
+      while (!mTxFifo.empty() && passedCycles >= mNextTxCycle) {
+        uint8_t ch = mTxFifo.front();
+        mTxFifo.erase(mTxFifo.begin());
+
+        if (ch & 0x80) {
+          ch = 0x20;
+        }
+        mTxBuffer.push_back(static_cast<char>(ch));
+        if (ch == '\n' || ch == '\r' || mTxBuffer.size() >= 120) {
+          flushTxBuffer();
+        }
+
+        if (mTxFifo.size() <= 4) {
+          mUTSR0 |= UTSR0_TFS;
+        }
+        if (mTxFifo.size() < 8) {
+          mUTSR1 |= UTSR1_TNF;
+        }
+
+        if (!mTxFifo.empty()) {
+          mNextTxCycle += 16000;
+          if (mNextTxCycle <= passedCycles) {
+            mNextTxCycle = passedCycles + 16000;
+          }
+        } else {
+          mNextTxCycle = 0;
+        }
+      }
+    }
+  }
+
+  if (mUTCR3 & UTCR3_RXE) {
+    switch (mRxFifo.size()) {
+    case 0:
+      mUTSR1 &= ~UTSR1_RNE;
+      break;
+
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+      // :SA-1110 Developer's Manual: p.338: Wei 2003-Jun-29:
+      //
+      // If receiver is enabled, receive FIFO not empty, 3 frame times elapsed
+      // without receiving data, request interrupt.
+      //
+      // :NOTE: Wei 2004-Jun-29:
+      //
+      // However, I don't see the reasone that I should implement a
+      // time-critical operation like this (3 frame times), thus, whenever the
+      // rx fifo isn't empty, ARMware will set the RID bit in UTSR0 up.
+      mUTSR0 |= UTSR0_RID;
+
+      mUTSR1 |= UTSR1_RNE;
+      break;
+
+    case 8:
+    case 9:
+    case 10:
+    case 11:
+    case 12:
+    default:
+      // :SA-1110 Developer's Manual: p.338: Wei 2003-Jun-29:
+      //
+      // Receive FIFO is one- to two-thirds full (contains 5, 6, 7, or 8 entries
+      // of data) or more, and receiver operation is enabled, DMA service
+      // request signalled, and interrupt request signalled if not masked (if
+      // RIE=1).
+      //
+      // :NOTE: Wei 2004-Jun-29:
+      //
+      // And I choice 8 elements as the delimiter.
+      mUTSR0 |= (UTSR0_RFS | UTSR0_RID);
+
+      mUTSR1 |= UTSR1_RNE;
+      break;
+    }
+  }
+}
+
+inline void Uart::txData() {
+  // :NOTE: Wei 2004-Jun-10:
+  //
+  // Becuase of using fifo[0], fifo has to reside in a consecutive memory
+  // blocks. This is why I use std::vector rather than std::deque for mTxFifo.
+  std::string output;
+
+  for (std::vector<uint8_t>::iterator iter = mTxFifo.begin();
+       iter != mTxFifo.end(); ++iter) {
+    if ((*iter) & 0x80) {
+      (*iter) = 0x20;
+    }
+  }
+
+  if (0xd == mTxFifo.back()) {
+    output.append(reinterpret_cast<char const*>(&(mTxFifo[0])), mTxFifo.size());
+    mNeedAppendMore = true;
+  } else {
+    if (true == mNeedAppendMore) {
+      output.append(reinterpret_cast<char const*>(&(mTxFifo[0])),
+                    mTxFifo.size());
+      mSink(output.c_str());
+      mNeedAppendMore = false;
+    } else {
+      if (false == output.empty()) {
+        mSink(output.c_str());
+      }
+
+      std::string lala = std::string(
+          reinterpret_cast<char const*>(&(mTxFifo[0])), mTxFifo.size());
+      mSink(lala.c_str());
+    }
+  }
+}
+
+void Uart::flushTxBuffer() {
+  if (!mTxBuffer.empty()) {
+    mSink(mTxBuffer.c_str());
+    mTxBuffer.clear();
+  }
+}
+
+uint32_t Uart::read32(const uint32_t address) {
+  switch (address) {
+  case UTCR0:
+    return static_cast<uint32_t>(mUTCR0);
+
+  case UTCR1:
+    return static_cast<uint32_t>(mUTCR1);
+
+  case UTCR2:
+    return static_cast<uint32_t>(mUTCR2);
+
+  case UTCR3:
+    return static_cast<uint32_t>(mUTCR3);
+
+  case UTDR: {
+    if (true == mRxFifo.empty()) {
+      assert(0 == (mUTSR1 & UTSR1_RNE));
+      return static_cast<uint32_t>(0);
+    } else {
+      assert(UTSR1_RNE == (mUTSR1 & UTSR1_RNE));
+      uint8_t const ch = mRxFifo.front();
+
+      mRxFifo.pop_front();
+
+      switch (mRxFifo.size()) {
+      case 0:
+        mUTSR0 &= ~(UTSR0_RID | UTSR0_RFS);
+
+        mUTSR1 &= ~UTSR1_RNE;
+        break;
+
+      case 1:
+      case 2:
+      case 3:
+      case 4:
+      case 5:
+      case 6:
+      case 7:
+        mUTSR0 &= ~UTSR0_RFS;
+        break;
+
+      case 8:
+      case 9:
+      case 10:
+      case 11:
+      case 12:
+      default:
+        break;
+      }
+      return static_cast<uint32_t>(ch);
+    }
+  }
+
+  case UTSR0:
+    return static_cast<uint32_t>(mUTSR0);
+
+  case UTSR1:
+    // :NOTE: Wei 2004-Jan-03:
+    //
+    // Bit 6, 7 are reserved.
+    //
+    // However, this is a read-only register, thus I don't have to do the mask
+    // operation.
+    return static_cast<uint32_t>(mUTSR1);
+
+  default:
+    return 0;
+  }
+}
+
+void Uart::write32(const uint32_t address, const uint32_t value) {
+  switch (address) {
+  case UTCR0:
+    // :NOTE: Wei 2004-Jan-03:
+    //
+    // Bit 7 is reserved.
+    mUTCR0 = static_cast<uint8_t>(value & ~(1 << 7));
+    break;
+
+  case UTCR1:
+    // :NOTE: Wei 2004-Jan-03:
+    //
+    // Bit 4, 5, 6, 7 are reserved.
+    mUTCR1 = static_cast<uint8_t>(value &
+                                  ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4)));
+    break;
+
+  case UTCR2:
+    mUTCR2 = static_cast<uint8_t>(value);
+    break;
+
+  case UTCR3: {
+    uint8_t const diff = (mUTCR3 ^ value);
+
+    if ((diff & UTCR3_TXE) && (0 == (value & UTCR3_TXE))) {
+      if (mTxFifo.size() != 0) {
+        // :SA-1110 Developer's Manual: p.332: Wei 2003-Jun-29:
+        //
+        // If the TXE bit is cleared to zero, all entries within the transmit
+        // FIFO are reset.
+        mTxFifo.clear();
+
+        mUTSR0 |= UTSR0_TFS; // Enable Transmit FIFO service request
+        mUTSR1 |= UTSR1_TNF; // Transmit FIFO not full
+        mNextTxCycle = 0;
+        mTxBuffer.clear();
+      }
+    }
+
+    if ((diff & UTCR3_RXE) && (0 == (value & UTCR3_RXE))) {
+      // :SA-1110 Developer's Manual: p.332: Wei 2003-Jun-29:
+      //
+      // If the RXE bit is cleared to zero, all entries within the receive FIFO
+      // are reset
+      if (false == mRxFifo.empty()) {
+        mRxFifo.clear();
+
+        mUTSR0 &= ~(UTSR0_RID | UTSR0_RFS);
+        mUTSR1 &= ~UTSR1_RNE;
+      }
+    }
+
+    // :NOTE: Wei 2004-Jan-03:
+    //
+    // Bit 6, 7 are reserved.
+    mUTCR3 = static_cast<uint8_t>(value & ~((1 << 7) | (1 << 6)));
+  } break;
+
+  case UTDR:
+    mTxFifo.push_back(static_cast<uint8_t>(value & 0xFF));
+    if (mTxFifo.size() > 4) {
+      mUTSR0 &= ~UTSR0_TFS; // clear TFS (transmit FIFO is more than half full)
+    }
+    if (mTxFifo.size() >= 8) {
+      mUTSR1 &= ~UTSR1_TNF; // clear TNF (transmit FIFO is full)
+    }
+    break;
+
+  case UTSR0:
+    // :SA-1110 Developer's Manual: p.336: Wei 2003-Jun-07:
+    //
+    // Writing a one to a sticky status bit clears it; writing a zero has no
+    // effect. Read-only flags are set and cleared by hardware; writes have no
+    // effect.
+
+    // :NOTE: Wei 2004-Jan-03:
+    //
+    // Bit 6, 7 are reserved.
+    // bits 0, 1, 5 are read-only.
+    mUTSR0 &= ~(value & UTSR0_READ_WRITE_BITS);
+    break;
+
+  case UTSR1:
+    // :NOTE: Wei 2004-Jan-01:
+    //
+    // read-only register.
+    return;
+
+  default:
+    break;
+  }
+}
+} // namespace SA1100
diff --git a/WindCore/series7/uart.h b/WindCore/series7/uart.h
new file mode 100644 (file)
index 0000000..334fa97
--- /dev/null
@@ -0,0 +1,115 @@
+// ARMware - an ARM emulator
+// Copyright (C) <2007>  Wei Hu <wei.hu.tw@gmail.com>
+//
+// 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 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef UART_H
+#define UART_H
+
+#include <cassert>
+#include <cstdint>
+#include <deque>
+#include <functional>
+#include <string>
+#include <vector>
+
+namespace SA1100 {
+
+class IntController;
+
+class Uart {
+public:
+  Uart(std::function<void(const char*)>& output);
+  void reset();
+
+  void run(int64_t passedCycles);
+  void flushTxBuffer();
+
+  inline int64_t getNextTxEventCycle() const {
+    return (mUTCR3 & UTCR3_TXE) && !mTxFifo.empty() ? mNextTxCycle : 0;
+  }
+
+  inline uint32_t getInterruptStatus() const { return mUTSR0; }
+  inline uint32_t getCtrlRegister() const { return mUTCR3; }
+
+  uint32_t read32(const uint32_t address);
+  void write32(const uint32_t address, const uint32_t value);
+
+private:
+  void initRegisters();
+
+  void txData();
+  void rxData();
+
+  enum {
+    UTCR3_RXE = (1 << 0), // Receiver enable.
+    UTCR3_TXE = (1 << 1), // Transmitter enable.
+    UTCR3_BRK = (1 << 2), // Break.
+    UTCR3_RIE = (1 << 3), // Receive FIFO interrupt enable.
+    UTCR3_TIE = (1 << 4), // Transmit FIFO interrupt enable.
+    UTCR3_LBM = (1 << 5)  // Loopback mode.
+  };
+
+  enum {
+    UTSR0_TFS = (1 << 0), // Transmit FIFO service request
+    UTSR0_RFS = (1 << 1), // Receive FIFO service request
+    UTSR0_RID = (1 << 2), // Receiver idle.
+    UTSR0_RBB = (1 << 3), // Receiver begin of break.
+    UTSR0_REB = (1 << 4), // Receiver end of break.
+    UTSR0_EIF = (1 << 5), // Error in FIFO (read-only).
+
+    UTSR0_READ_WRITE_BITS = (UTSR0_RID | UTSR0_RBB | UTSR0_REB),
+  };
+
+  enum {
+    UTSR1_TBY = (1 << 0), // Transmitter busy flag
+    UTSR1_RNE = (1 << 1), // Receive FIFO not empty
+    UTSR1_TNF = (1 << 2), // Transmit FIFO not full
+    UTSR1_PRE = (1 << 3), // Parity error
+    UTSR1_FRE = (1 << 4), // Framing error
+    UTSR1_ROR = (1 << 5)  // Receive FIFO overrun
+  };
+
+  enum {
+    UTCR0 = 0x80050000,
+    UTCR1 = 0x80050004,
+    UTCR2 = 0x80050008,
+    UTCR3 = 0x8005000C,
+    UTDR = 0x80050014,
+    UTSR0 = 0x8005001C,
+    UTSR1 = 0x80050020
+  };
+
+  uint8_t mUTCR0; // UART control register 0
+  uint8_t mUTCR1; // UART control register 1
+  uint8_t mUTCR2; // UART control register 2
+  uint8_t mUTCR3; // UART control register 3
+  uint8_t mUTDR;  // UART data register
+  uint8_t mUTSR0; // UART status register 0
+  uint8_t mUTSR1; // UART status register 1
+
+  std::vector<uint8_t> mTxFifo;
+  std::deque<uint8_t> mRxFifo;
+  bool mNeedAppendMore = false;
+  std::function<void(const char*)>& mSink;
+  std::string mTxBuffer;
+  int64_t mNextTxCycle = 0;
+
+  friend class IntController;
+};
+
+} // namespace SA1100
+
+#endif // UART_H