From: George Wright Date: Thu, 25 Jun 2026 02:03:19 +0000 (-0700) Subject: sa1100: add initial lcd controller implementation X-Git-Url: http://git.gwright.org.uk/?a=commitdiff_plain;h=ff5e3f804910de789bc72355e0717b1a9942efff;p=WindEmu.git sa1100: add initial lcd controller implementation --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 54da465..c81d37f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ set(WIND_CORE_SOURCES WindCore/series5mx/etna.cpp WindCore/series5mx/windermere.cpp WindCore/series7/gpio_controller.cpp + WindCore/series7/lcd_controller.cpp WindCore/series7/sa1100.cpp WindCore/series7/sa1100_memory.cpp ) diff --git a/WindCore/series7/lcd_controller.cpp b/WindCore/series7/lcd_controller.cpp new file mode 100644 index 0000000..9b4dac6 --- /dev/null +++ b/WindCore/series7/lcd_controller.cpp @@ -0,0 +1,202 @@ +// ARMware - an ARM emulator +// Copyright (C) <2007> Wei Hu +// +// 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 . +// + +#include "lcd_controller.h" + +#include "logger.h" + +namespace SA1100 { + +LCDController::LCDController() : mEnabled(false) { initRegisters(); } + +void LCDController::initRegisters() { + mLCCR0 = 0; + mLCSR = 0; + mDBAR1 = 0; + mDCAR1 = 0; + mDBAR2 = 0; + mDCAR2 = 0; + mLCCR1 = 0; + mLCCR2 = 0; + mLCCR3 = 0; +} + +void LCDController::reset() { + mLCCR0 = 0; + mLCCR1 = 0; + mLCCR2 = 0; + mLCCR3 = 0; + mLCSR = 0; + mEnabled = false; +} + +uint32_t LCDController::getFramebufferBase(Framebuffer fb) const { + switch (fb) { + case Framebuffer::Top: + return mDBAR1; + case Framebuffer::Bottom: + return mDBAR2; + } +} + +uint32_t LCDController::read32(const uint32_t address) const { + switch (address) { + case LCCR0: + LOG_REG_R("LCD LCCR0", address, mLCCR0); + return mLCCR0; + case LCCR1: + LOG_REG_R("LCD LCCR1", address, mLCCR1); + return mLCCR1; + case LCCR2: + LOG_REG_R("LCD LCCR2", address, mLCCR2); + return mLCCR2; + case LCCR3: + LOG_REG_R("LCD LCCR3", address, mLCCR3); + return mLCCR3; + case DBAR1: + LOG_REG_R("LCD DBAR1", address, mDBAR1); + return mDBAR1; + case DCAR1: + LOG_REG_R("LCD DCAR1", address, mDCAR1); + return mDCAR1; + case DBAR2: + LOG_REG_R("LCD DBAR2", address, mDBAR2); + return mDBAR2; + case DCAR2: + LOG_REG_R("LCD DCAR2", address, mDCAR2); + return mDCAR2; + case LCSR: + LOG_REG_R("LCD LCSR", address, mLCSR); + return mLCSR; + + default: + LOG_REG_R("LCD Unknown", address, 0); + return 0; + } +} + +void LCDController::write32(const uint32_t address, const uint32_t value) { + switch (address) { + case LCCR0: { + LOG_REG_W("LCD LCCR0", address, value); + const uint32_t diff = (mLCCR0 ^ value); + + if ((diff & LCCR0_LEN) != 0) { + if (0 == (value & LCCR0_LEN)) { + mEnabled = false; + mLCSR |= LCSR_LDD; + } else { + mEnabled = true; + // :SA-1110 Developer's Manual: Wei 2003-Dec-08: + // + // Value in DBAR1(or 2) is transferred to DCAR1(or 2) when LCD is first + // enabled (LEN = 0->1). + mDCAR1 = mDBAR1; + mDCAR2 = mDBAR2; + + // :SA-1110 Developer's Manual: p.249: Wei 2004-Jan-15: + // + // The base address update status (BAU) is a read/write status bit + // that is set after the contents of the DMA base address register 1 + // are transferred to the DMA current Address register 1. + mLCSR |= LCSR_BAU; + } + } + + mLCCR0 = (value & (0xFFFFF ^ (1 << 6))); + } break; + + case LCCR1: + LOG_REG_W("LCD LCCR1", address, value); + // :SA-1110 Developer's Manual: Wei 2004-Jan-13: + // + // Note that the bottom four bits of PPL are not implemented and + // therefore are not writable. Reads of these bits return zeros + // because the LCD controller only supports displays that are a + // multiple of 16 pixels wide. + mLCCR1 = (value & 0xFFFFFFF0); + break; + + case LCCR2: + LOG_REG_W("LCD LCCR2", address, value); + mLCCR2 = value; + break; + + case LCCR3: + LOG_REG_W("LCD LCCR3", address, value); + mLCCR3 = (value & 0xFFFFFF); + break; + + case DBAR1: + LOG_REG_W("LCD DBAR1", address, value); + // :SA-1110 Developer's Manual: p.245: Wei 2003-Dec-08: + // + // Addresses programmed in the base address register must be aligned + // on quadword boundaries; + // the least significant four bits (DBAR1[3:0]) must always be written with + // zeros. + + // :NOTE: Wei 2004-Jan-15: + // + // If we want to change the frame buffer address, + // we are likely need to update the whole screen. + mDBAR1 = value; + break; + + case DBAR2: + LOG_REG_W("LCD DBAR2", address, value); + // :SA-1110 Developer's Manual: p.245: Wei 2003-Dec-08: + // + // Addresses programmed in the base address register must be aligned + // on quadword boundaries; + // the least significant four bits (DBAR1[3:0]) must always be written with + // zeros. + + mDBAR2 = value; + break; + + case DCAR1: + LOG_REG_W("LCD DCAR1", address, value); + break; + + case DCAR2: + LOG_REG_W("LCD DCAR2", address, value); + // :SA-1110 Developer's Manual: p.247: Wei 2004-Jun-06: + // :SA-1110 Developer's Manual: p.248: Wei 2004-Jun-06: + // + // These are read-only registers. + break; + + case LCSR: + LOG_REG_W("LCD LCSR", address, value); + // :SA-1110 Developer's Manual: p.248: Wei 2004-Jun-06: + // + // Status bits are referred to as 'sticky' (once set by hardware, + // they must be cleared by software). Writing a 1 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. + + mLCSR &= ~(mLCSR & (value & LCSR_READ_WRITE_BITMASK)); + break; + + default: + LOG_REG_W("LCD Unknown", address, value); + break; + } +} + +} // namespace SA1100 diff --git a/WindCore/series7/lcd_controller.h b/WindCore/series7/lcd_controller.h new file mode 100644 index 0000000..f994f30 --- /dev/null +++ b/WindCore/series7/lcd_controller.h @@ -0,0 +1,165 @@ +// ARMware - an ARM emulator +// Copyright (C) <2007> Wei Hu +// +// 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 . +// + +#ifndef LCD_CONTROLLER_H +#define LCD_CONTROLLER_H + +#include + +namespace SA1100 { + +class IntController; + +class LCDController { +public: + LCDController(); + void reset(); + + bool isLCDEnabled() { return mEnabled; } + + unsigned int getWidth() { return (mLCCR1 & 0x3FF) + 16; } + unsigned int getHeight() { return (mLCCR2 & 0x3FF) + 1; } + + enum Framebuffer { Top, Bottom }; + + uint32_t getFramebufferBase(Framebuffer fb) const; + + inline void endFrame() { + mDCAR1 = mDBAR1; + mDCAR2 = mDBAR2; + + // :SA-1110 Developer's Manual: Wei 2003-Dec-08: + // + // Value in DBAR1(or 2) is transferred to DCAR1(or 2) when LCD is first + // enabled (LEN = 0->1) and when the current address pointer value equals + // the end-of-frame buffer. + + // :NOTE: Wei 2004-Jan-15: + // + // Althought I don't update the mDCAR1(or 2) when (mDBAR1 == mDCAR1) + // here, however, they are actually transfered in the real SA-1110, thus the + // following LCSR_BAU setting operation are always performed. + + // :SA-1110 Developer's Manual: p.249: Wei 2004-Jan-15: + // + // The base address update status (BAU) is a read/write status bit + // that is set after the contents of the DMA base address register 1 + // are transferred to the DMA current Address register 1 + mLCSR |= LCSR_BAU; + } + + inline uint32_t getInterruptStatus() const { return mLCSR; } + inline uint32_t getLcdCtrlRegister() const { return mLCCR0; } + + uint32_t read32(const uint32_t address) const; + void write32(const uint32_t address, const uint32_t value); + +private: + enum { + LCCR0_LEN = (1 << 0), // LCD controller enable + LCCR0_CMS = (1 << 1), // Color/monochrome select. + LCCR0_SDS = (1 << 2), // Single-/dual-panel display select. + LCCR0_LDM = (1 << 3), // LCD disable done mask + LCCR0_BAM = (1 << 4), // Base address update interrupt mask + LCCR0_ERM = (1 << 5), // Error mask. + LCCR0_PAS = (1 << 7), // Passive/active display select + LCCR0_BLE = (1 << 8), // Big/little endian select. + LCCR0_DPD = (1 << 9), // Double-pixel data pin mode. + LCCR0_VCS = ((1 << 10) | (1 << 11)), // Vertical slant line correction. + LCCR0_PDD = + ((1 << 12) | (1 << 13) | (1 << 14) | (1 << 15) | (1 << 16) | (1 << 17) | + (1 << 18) | (1 << 19)) // Palette DMA request delay. + }; + + enum { + LCSR_LDD = (1 << 0), // LCD disable done status. + LCSR_BAU = + (1 << 1), // Base address update flag (read-only, maskable interrupt) + LCSR_BER = (1 << 2), // Bus error status. + LCSR_ABC = (1 << 3), // AC bias count status. + + // :NOTE: Wei 2004-Jun-06: + // + // I think in ARMware, we shouldn't raise the following bits. + // As a result, no interrupts should be raised because of the following + // bits. + LCSR_IOL = (1 << 4), // Input FIFO overrun lower panel status. + LCSR_IUL = (1 << 5), // Input FIFO underrun lower panel status. + LCSR_IOU = (1 << 6), // Input FIFO overrun upper panel status. + LCSR_IUU = (1 << 7), // Input FIFO underrun upper panel status. + LCSR_OOL = (1 << 8), // Output FIFO overrun lower panel status. + LCSR_OUL = (1 << 9), // Output FIFO underrun lower panel status. + LCSR_OOU = (1 << 10), // Output FIFO overrun upper panel status. + LCSR_OUU = (1 << 11), // Output FIFO underrun upper panel status. + + // :NOTE: Wei 2004-Jun-30: + // + // Althought page 249 in the Intel SA-1110 Developer's Manual says that BAU + // flag is read-only, however, in the same page (p.249), it says that BAU + // flag can be cleared when it is written to a 1. After searching google, I + // find a post related this: + // + // �H��̡Gbratfi_t@my-deja.com (bratfi_t@my-deja.com) + // �D���GRe: BAU flag on SA1100 + // ���W�׾¡Gcomp.sys.arm + // ����G1999/08/31 + // + // ... + // According to the manual, the BAU flag is read-only, but it can be cleared + // like a status bit! + // ... + // + // Thus I include BAU in the following constant. + LCSR_READ_WRITE_BITMASK = + (LCSR_LDD | LCSR_BAU | LCSR_BER | LCSR_ABC | LCSR_IOL | LCSR_IUL | + LCSR_IOU | LCSR_IUU | LCSR_OOL | + // LCSR_OUL | + LCSR_OOU | LCSR_OUU) + }; + + enum { + LCCR0 = 0xB0100000, + LCSR = 0xB0100004, + DBAR1 = 0xB0100010, + DCAR1 = 0xB0100014, + DBAR2 = 0xB0100018, + DCAR2 = 0xB010001C, + LCCR1 = 0xB0100020, + LCCR2 = 0xB0100024, + LCCR3 = 0xB0100028 + }; + + bool mEnabled; + + uint32_t mLCCR0; // LCD controller control register 0 + uint32_t mLCSR; // LCD controller status register 1 + uint32_t mDBAR1; // DMA channel 1 base address register + uint32_t mDCAR1; // DMA channel 1 current address register + uint32_t mDBAR2; // DMA channel 2 base address register + uint32_t mDCAR2; // DMA channel 2 current address register + uint32_t mLCCR1; // LCD controller control register 1 + uint32_t mLCCR2; // LCD controller control register 2 + uint32_t mLCCR3; // LCD controller control register 3 + + void initRegisters(); + + friend class IntController; +}; + +} // namespace SA1100 + +#endif // LCD_CONTROLLER_H