if (insnFault != NoFault) {
// Raise a prefetch error
// These do not set FSR or FAR
+ log("prefetch error!");
raiseException(Abort32, GPRs[15] - 8, 0xC);
} else {
clocks += executeInstruction(insn);
uint32_t ARM710a::executeInstruction(uint32_t i) {
uint32_t cycles = 1;
+// log("executing insn %08x @ %08x", i, GPRs[15] - 0xC);
// a big old dispatch thing here
// but first, conditions!
op2 -= 4;
}
- switch (extract(Operand2, 6, 5)) {
- case 0: // Logical Left (LSL)
- if (shiftBy == 0) {
- shifterCarryOutput = flagC();
- // no change to op2!
- } else if (shiftBy <= 31) {
- shifterCarryOutput = extract1(op2, 31 - shiftBy);
- op2 <<= shiftBy;
- } else if (shiftBy == 32) {
- shifterCarryOutput = extract1(op2, 0);
- op2 = 0;
- } else /*if (shiftBy >= 33)*/ {
- shifterCarryOutput = false;
- op2 = 0;
- }
- break;
- case 1: // Logical Right (LSR)
- if (shiftBy == 0 || shiftBy == 32) {
- shifterCarryOutput = extract1(op2, 31);
- op2 = 0;
- } else if (shiftBy <= 31) {
- shifterCarryOutput = extract1(op2, shiftBy - 1);
- op2 >>= shiftBy;
- } else /*if (shiftBy >= 33)*/ {
- shifterCarryOutput = false;
- op2 = 0;
- }
- break;
- case 2: // Arithmetic Right (ASR)
- if (shiftBy == 0 || shiftBy >= 32) {
- shifterCarryOutput = extract1(op2, 31);
- op2 = (int32_t)op2 >> 31;
- } else /*if (shiftBy <= 31)*/ {
- shifterCarryOutput = extract1(op2, shiftBy - 1);
- op2 = (int32_t)op2 >> shiftBy;
- }
- break;
- case 3: // Rotate Right (ROR)
- if (shiftBy == 0) { // treated as RRX
- shifterCarryOutput = op2 & 1;
- op2 >>= 1;
- op2 |= flagC() ? 0x80000000 : 0;
- } else {
- shiftBy %= 32;
- if (shiftBy == 0) { // like 32
+ if (extract(Operand2, 4, 4) && (shiftBy == 0)) {
+ // register shift by 0 never does anything
+ shifterCarryOutput = flagC();
+ } else {
+ switch (extract(Operand2, 6, 5)) {
+ case 0: // Logical Left (LSL)
+ if (shiftBy == 0) {
+ shifterCarryOutput = flagC();
+ // no change to op2!
+ } else if (shiftBy <= 31) {
+ shifterCarryOutput = extract1(op2, 31 - shiftBy);
+ op2 <<= shiftBy;
+ } else if (shiftBy == 32) {
+ shifterCarryOutput = extract1(op2, 0);
+ op2 = 0;
+ } else /*if (shiftBy >= 33)*/ {
+ shifterCarryOutput = false;
+ op2 = 0;
+ }
+ break;
+ case 1: // Logical Right (LSR)
+ if (shiftBy == 0 || shiftBy == 32) {
shifterCarryOutput = extract1(op2, 31);
- // no change to op2
- } else {
+ op2 = 0;
+ } else if (shiftBy <= 31) {
shifterCarryOutput = extract1(op2, shiftBy - 1);
- op2 = ROR(op2, shiftBy);
+ op2 >>= shiftBy;
+ } else /*if (shiftBy >= 33)*/ {
+ shifterCarryOutput = false;
+ op2 = 0;
+ }
+ break;
+ case 2: // Arithmetic Right (ASR)
+ if (shiftBy == 0 || shiftBy >= 32) {
+ shifterCarryOutput = extract1(op2, 31);
+ op2 = (int32_t)op2 >> 31;
+ } else /*if (shiftBy <= 31)*/ {
+ shifterCarryOutput = extract1(op2, shiftBy - 1);
+ op2 = (int32_t)op2 >> shiftBy;
+ }
+ break;
+ case 3: // Rotate Right (ROR)
+ if (shiftBy == 0) { // treated as RRX
+ shifterCarryOutput = op2 & 1;
+ op2 >>= 1;
+ op2 |= flagC() ? 0x80000000 : 0;
+ } else {
+ shiftBy %= 32;
+ if (shiftBy == 0) { // like 32
+ shifterCarryOutput = extract1(op2, 31);
+ // no change to op2
+ } else {
+ shifterCarryOutput = extract1(op2, shiftBy - 1);
+ op2 = ROR(op2, shiftBy);
+ }
}
+ break;
}
- break;
}
} else {
// IMMEDIATE
uint32_t Rotate = extract(Operand2, 11, 8);
uint32_t Imm = extract(Operand2, 7, 0);
- Imm = (uint32_t)(int8_t)Imm;
op2 = ROR(Imm, Rotate * 2);
shifterCarryOutput = flagC(); // correct? unsure...
}
flags |= (CPSR & CPSR_V);
#define ADD_OP(a, b, c) \
- result = a + b + (uint32_t)(c); \
+ result = (uint64_t)(a) + (uint64_t)(b) + (uint64_t)(c); \
flags |= (result & 0xFFFFFFFF) ? 0 : CPSR_Z; \
flags |= (result & 0x80000000) ? CPSR_N : 0; \
flags |= (result & 0x100000000) ? CPSR_C : 0; \
// Output-less opcodes: special behaviour
if (S) {
CPSR = (CPSR & ~CPSR_FlagMask) | flags;
+// log("CPSR setflags=%08x results in CPSR=%08x", flags, CPSR);
} else if (Opcode == 8) {
// MRS, CPSR -> Reg
GPRs[Rd] = CPSR;
+ log("r%d <- CPSR(%08x)", Rd, GPRs[Rd]);
} else if (Opcode == 9) {
// MSR, Reg -> CPSR
bool canChangeMode = extract1(Rn, 0);
auto newCPSR = GPRs[extract(Operand2, 3, 0)];
switchMode(modeFromCPSR(newCPSR));
CPSR = newCPSR;
+ log("CPSR change privileged: %08x", CPSR);
} else {
// for the flag-only version, immediates are allowed
// so we just re-use what was calculated earlier...
auto newFlag = I ? op2 : GPRs[extract(Operand2, 3, 0)];
CPSR &= ~CPSR_FlagMask;
CPSR |= (newFlag & CPSR_FlagMask);
+ log("CPSR change unprivileged: new=%08x result=%08x", newFlag, CPSR);
}
} else if (Opcode == 0xA) {
// MRS, SPSR -> Reg
- if (isPrivileged())
+ if (isPrivileged()) {
GPRs[Rd] = SPSRs[currentBank()];
+ log("r%d <- SPSR(%08x)", Rd, GPRs[Rd]);
+ }
} else /*if (Opcode == 0xB)*/ {
bool canChangeMode = extract1(Rn, 0);
if (isPrivileged()) {
if (canChangeMode) {
SPSRs[currentBank()] = GPRs[extract(Operand2, 3, 0)];
+ log("SPSR change privileged: %08x", SPSRs[currentBank()]);
} else {
// same hat
auto newFlag = I ? op2 : GPRs[extract(Operand2, 3, 0)];
SPSRs[currentBank()] &= ~CPSR_FlagMask;
SPSRs[currentBank()] |= (newFlag & CPSR_FlagMask);
+ log("SPSR change unprivileged: new=%08x result=%08x", newFlag, SPSRs[currentBank()]);
}
}
}
auto saved = SPSRs[currentBank()];
switchMode(modeFromCPSR(saved));
CPSR = saved;
+ log("dataproc restore CPSR: %08x", CPSR);
}
} else if (S) {
CPSR = (CPSR & ~CPSR_FlagMask) | flags;
+// log("dataproc flag change: flags=%08x CPSR=%08x", flags, CPSR);
}
}
auto valueSize = extract1(IPUBWL, 2) ? V8 : V32;
bool up = extract1(IPUBWL, 3);
bool preIndex = extract1(IPUBWL, 4);
- bool immediate = extract1(IPUBWL, 5);
+ bool immediate = !extract1(IPUBWL, 5);
// calculate the offset
uint32_t calcOffset;
}
} else {
// IMMEDIATE
- uint32_t Rotate = extract(offset, 11, 8);
- uint32_t Imm = extract(offset, 7, 0);
- Imm = (uint32_t)(int8_t)Imm;
- calcOffset = ROR(Imm, Rotate * 2);
+ // No rotation or anything here
+ calcOffset = offset;
}
uint32_t base = GPRs[Rn];
if (changeModes) switchMode(User32);
auto readResult = readVirtual(transferAddr, valueSize);
if (changeModes) switchMode(saveMode);
- if (readResult.first.has_value())
+ if (readResult.first.has_value()) {
GPRs[Rd] = readResult.first.value();
+ if (Rd == 15) prefetchCount = 0;
+ }
fault = readResult.second;
} else {
uint32_t value = GPRs[Rd];
}
}
+ if (registerList & 0x8000)
+ prefetchCount = 0;
+
// datasheet specifies that base register must be
// restored if an error occurs during LDM
if (load && fault != NoFault)
if (auto v = readPhysical(virtAddr, valueSize); v.has_value())
return make_pair(v.value(), NoFault);
else
- return make_pair(MaybeU32(), NonMMUError);
+ return make_pair(MaybeU32(), encodeFault(NonMMUError, 0, virtAddr));
}
auto translated = translateAddressUsingTlb(virtAddr);
if (!isMMUEnabled()) {
// direct virtual -> physical mapping, sans MMU
if (!writePhysical(value, virtAddr, valueSize))
- return NonMMUError;
+ return encodeFault(NonMMUError, 0, virtAddr);
} else {
auto translated = translateAddressUsingTlb(virtAddr);
if (holds_alternative<MMUFault>(translated))
void ARM710a::reportFault(MMUFault fault) {
if (fault != NoFault) {
if ((fault & 0xF) != NonMMUError) {
- cp15_faultStatus = fault & 0xFFFF;
- cp15_faultAddress = fault >> 32;
+ cp15_faultStatus = fault & (MMUFaultTypeMask | MMUFaultDomainMask);
+ cp15_faultAddress = fault >> MMUFaultAddressShift;
}
+ static const char *faultTypes[] = {
+ "NoFault",
+ "AlignmentFault",
+ "???",
+ "NonMMUError",
+ "SectionLinefetchError",
+ "SectionTranslationFault",
+ "PageLinefetchError",
+ "PageTranslationFault",
+ "SectionOtherBusError",
+ "SectionDomainFault",
+ "PageOtherBusError",
+ "PageDomainFault",
+ "Lv1TranslationError",
+ "SectionPermissionFault",
+ "Lv2TranslationError",
+ "PagePermissionFault"
+ };
+ log("⚠️ Fault type=%s domain=%d address=%08x pc=%08x lr=%08x",
+ faultTypes[fault & MMUFaultTypeMask],
+ (fault & MMUFaultDomainMask) >> MMUFaultDomainShift,
+ fault >> MMUFaultAddressShift,
+ GPRs[15], GPRs[14]);
+
// this signals a branch to DataAbort after the
// instruction is done executing
faultTriggeredThisCycle = true;
}
}
+
+
+void ARM710a::log(const char *format, ...) {
+ if (logger) {
+ char buffer[1024];
+
+ va_list vaList;
+ va_start(vaList, format);
+ vsnprintf(buffer, sizeof(buffer), format, vaList);
+ va_end(vaList);
+
+ logger(buffer);
+ }
+}
+
+
+void ARM710a::test() {
+ uint64_t result;
+ uint32_t flags = 0;
+ uint32_t v = 0x10000000;
+
+ SUB_OP(v, v, 1);
+
+ log("RESULT:%llx FLAGS:%08x", result, flags);
+}
class ARM710a
{
public:
+ void test();
+
enum ValueSize { V8 = 0, V32 = 1 };
enum MMUFault : uint64_t {
// so we are reusing it for nefarious purposes
NonMMUError = 3,
+ MMUFaultTypeMask = 0xF,
MMUFaultDomainMask = 0xF0,
- MMUFaultAddressMask = 0xFFFFFFFF00000000
+ MMUFaultDomainShift = 4,
+ MMUFaultAddressMask = 0xFFFFFFFF00000000,
+ MMUFaultAddressShift = 32
};
void requestIRQ(); // pull nIRQ low
void reset(); // pull nRESET low
+ bool instructionReady() const { return (prefetchCount == 2); }
uint32_t tick(); // run the chip for at least 1 clock cycle
MaybeU32 readVirtualDebug(uint32_t virtAddr, ValueSize valueSize);
virtual bool writePhysical(uint32_t value, uint32_t physAddr, ARM710a::ValueSize valueSize) = 0;
uint32_t getGPR(int index) const { return GPRs[index]; }
+ uint32_t getCPSR() const { return CPSR; }
+ void setLogger(std::function<void(const char *)> newLogger) { logger = newLogger; }
+protected:
+ void log(const char *format, ...);
private:
+ std::function<void(const char *)> logger;
+
enum { Nop = 0xE1A00000 };
enum Mode : uint8_t {
bool flagN() const { return CPSR & CPSR_N; }
bool checkCondition(int cond) const {
switch (cond) {
- case 0: return flagZ();
- case 1: return !flagZ();
- case 2: return flagC();
- case 3: return !flagC();
- case 4: return flagN();
- case 5: return !flagN();
- case 6: return flagV();
- case 7: return !flagV();
- case 8: return flagC() && !flagZ();
- case 9: return !flagC() || flagZ();
- case 0xA: return flagN() == flagV();
- case 0xB: return flagN() != flagV();
- case 0xC: return !flagZ() && (flagN() == flagV());
- case 0xD: return flagZ() || (flagN() != flagV());
- case 0xE: return true;
- /*case 0xF:*/
+ /*EQ*/ case 0: return flagZ();
+ /*NE*/ case 1: return !flagZ();
+ /*CS*/ case 2: return flagC();
+ /*CC*/ case 3: return !flagC();
+ /*MI*/ case 4: return flagN();
+ /*PL*/ case 5: return !flagN();
+ /*VS*/ case 6: return flagV();
+ /*VC*/ case 7: return !flagV();
+ /*HI*/ case 8: return flagC() && !flagZ();
+ /*LS*/ case 9: return !flagC() || flagZ();
+ /*GE*/ case 0xA: return flagN() == flagV();
+ /*LT*/ case 0xB: return flagN() != flagV();
+ /*GT*/ case 0xC: return !flagZ() && (flagN() == flagV());
+ /*LE*/ case 0xD: return flagZ() || (flagN() != flagV());
+ /*AL*/ case 0xE: return true;
+ /*NV*/ /*case 0xF:*/
default: return false;
}
}
else if (region == 0xD1)
return MemoryBlockD1[physAddr & MemoryBlockMask];
#endif
+ else if (region >= 0xC0)
+ return 0xFF; // just throw accesses to unmapped RAM away
} else {
uint32_t result;
if (region == 0)
else if (region == 0xD1)
LOAD_32LE(result, physAddr & MemoryBlockMask, MemoryBlockD1);
#endif
+ else if (region >= 0xC0)
+ return 0xFFFFFFFF; // just throw accesses to unmapped RAM away
else
return {};
return result;
else if (region == 0xD1)
MemoryBlockD1[physAddr & MemoryBlockMask] = (uint8_t)value;
#endif
+ else if (region >= 0xC0)
+ return true; // just throw accesses to unmapped RAM away
else if (region == 0x20 && physAddr <= 0x20000FFF)
etna.writeReg8(physAddr & 0xFFF, value);
else if (region == 0x80 && physAddr <= 0x80000FFF)
else if (region == 0xD1)
STORE_32LE(value, physAddr & MemoryBlockMask, MemoryBlockD1);
#endif
+ else if (region >= 0xC0)
+ return true; // just throw accesses to unmapped RAM away
else if (region == 0x20 && physAddr <= 0x20000FFF)
etna.writeReg32(physAddr & 0xFFF, value);
else if (region == 0x80 && physAddr <= 0x80000FFF)
nextTickAt = TICK_INTERVAL;
rtc = getRTC();
+ setProcessorID(0x41807100);
reset();
}
// keep the clock moving
passedCycles++;
} else {
+ if (auto v = virtToPhys(getGPR(15) - 0xC); v.has_value() && instructionReady())
+ debugPC(v.value());
passedCycles += tick();
uint32_t new_pc = getGPR(15) - 0xC;
- if (_breakpoints.find(new_pc) != _breakpoints.end())
+ if (_breakpoints.find(new_pc) != _breakpoints.end()) {
+ log("⚠️ Breakpoint triggered at %08x!\n", new_pc);
return;
+ }
}
}
}
const char *wut = identifyObjectCon(container);
if (wut) {
fetchName(obj, objName);
- printf("OBJS: added %s at %08x <%s>", wut, obj, objName);
if (strcmp(wut, "process") == 0) {
- fetchProcessFilename(obj, objName);
- printf(" <%s>", objName);
+ char procName[1000];
+ fetchProcessFilename(obj, procName);
+ log("OBJS: added %s at %08x <%s> <%s>", wut, obj, objName, procName);
+ } else {
+ log("OBJS: added %s at %08x <%s>", wut, obj, objName);
}
- printf("\n");
}
}
+
+ if (pc == 0x6D8) {
+ uint32_t virtAddr = getGPR(0);
+ uint32_t physAddr = getGPR(1);
+ uint32_t btIndex = getGPR(2);
+ uint32_t regionSize = getGPR(3);
+ log("KERNEL MMU SECTION: v:%08x p:%08x size:%08x idx:%02x",
+ virtAddr, physAddr, regionSize, btIndex);
+ }
+ if (pc == 0x710) {
+ uint32_t virtAddr = getGPR(0);
+ uint32_t physAddr = getGPR(1);
+ uint32_t btIndex = getGPR(2);
+ uint32_t regionSize = getGPR(3);
+ uint32_t pageTableA = getGPR(4);
+ uint32_t pageTableB = getGPR(5);
+ log("KERNEL MMU PAGES: v:%08x p:%08x size:%08x idx:%02x tableA:%08x tableB:%08x",
+ virtAddr, physAddr, regionSize, btIndex, pageTableA, pageTableB);
+ }
}
ui(new Ui::MainWindow)
{
ui->setupUi(this);
+ ui->logView->setMaximumBlockCount(1000);
emu = new Emu;
emu->loadROM("/Users/ash/src/psion/Sys$rom.bin");
+ emu->setLogger([&](const char *str) {
+ ui->logView->appendPlainText(str);
+ });
+ emu->test();
timer = new QTimer(this);
timer->setInterval(1000/64);
updateMemory();
+ char flagDisplay[] = {
+ (emu->getCPSR() & 0x80000000) ? 'N' : '-',
+ (emu->getCPSR() & 0x40000000) ? 'Z' : '-',
+ (emu->getCPSR() & 0x20000000) ? 'C' : '-',
+ (emu->getCPSR() & 0x10000000) ? 'V' : '-',
+ 0
+ };
+ const char *modeName = "???";
+ switch (emu->getCPSR() & 0x1F) {
+ case 0x10: modeName = "User"; break;
+ case 0x11: modeName = "FIQ"; break;
+ case 0x12: modeName = "IRQ"; break;
+ case 0x13: modeName = "Supervisor"; break;
+ case 0x17: modeName = "Abort"; break;
+ case 0x1B: modeName = "Undefined"; break;
+ }
+
ui->regsLabel->setText(
- QString("R0: %1 / R1: %2 / R2: %3 / R3: %4 / R4: %5 / R5: %6 / R6: %7 / R7: %8\nR8: %9 / R9: %10 / R10:%11 / R11:%12 / R12:%13 / SP: %14 / LR: %15 / PC: %16")
+ QString("R0: %1 / R1: %2 / R2: %3 / R3: %4 / R4: %5 / R5: %6 / R6: %7 / R7: %8\nR8: %9 / R9: %10 / R10:%11 / R11:%12 / R12:%13 / SP: %14 / LR: %15 / PC: %16\n%17 / Mode: %18")
.arg(emu->getGPR(0), 8, 16)
.arg(emu->getGPR(1), 8, 16)
.arg(emu->getGPR(2), 8, 16)
.arg(emu->getGPR(13), 8, 16)
.arg(emu->getGPR(14), 8, 16)
.arg(emu->getGPR(15), 8, 16)
+ .arg(flagDisplay)
+ .arg(modeName)
);
// show a crude disassembly
const int context = 8 * 4;
- uint32_t pc = emu->getGPR(15) - 4;
+ uint32_t pc = emu->getGPR(15) - 8;
uint32_t minCode = pc - context;
if (minCode >= (UINT32_MAX - context))
minCode = 0;
QStringList codeLines;
for (uint32_t addr = minCode; addr >= minCode && addr <= maxCode; addr += 4) {
- const char *prefix = (addr == pc) ? "==>" : " ";
+ const char *prefix = (addr == pc) ? (emu->instructionReady() ? "==>" : "...") : " ";
struct ARMInstructionInfo info;
char buffer[512];
void MainWindow::on_stepTickButton_clicked()
{
- emu->executeUntil(emu->currentCycles() + (CLOCK_SPEED * 2));
- updateScreen();
+// emu->executeUntil(emu->currentCycles() + (CLOCK_SPEED * 2));
+ emu->executeUntil(emu->currentCycles() + 25000);
+ updateScreen();
}
void MainWindow::on_stepInsnButton_clicked()
</property>
</widget>
</item>
- <item row="3" column="0" colspan="5">
- <widget class="QLabel" name="regsLabel">
- <property name="font">
- <font>
- <family>Courier New</family>
- </font>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="1" column="3">
- <widget class="QPushButton" name="startButton">
- <property name="text">
- <string>Start</string>
- </property>
- </widget>
- </item>
- <item row="2" column="4">
- <widget class="QPushButton" name="stepTickButton">
- <property name="text">
- <string>Step (Tick)</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0" colspan="6">
- <widget class="QLabel" name="screen">
- <property name="focusPolicy">
- <enum>Qt::ClickFocus</enum>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QPushButton" name="stepInsnButton">
- <property name="text">
- <string>Step (Insn)</string>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="4">
- <widget class="QPushButton" name="stopButton">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="text">
- <string>Stop</string>
- </property>
- </widget>
- </item>
<item row="4" column="0" colspan="5">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
</widget>
</widget>
</item>
+ <item row="3" column="0" colspan="5">
+ <widget class="QLabel" name="regsLabel">
+ <property name="font">
+ <font>
+ <family>Courier New</family>
+ </font>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QPushButton" name="startButton">
+ <property name="text">
+ <string>Start</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="4">
+ <widget class="QPushButton" name="stepTickButton">
+ <property name="text">
+ <string>Step (Tick)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="6">
+ <widget class="QLabel" name="screen">
+ <property name="focusPolicy">
+ <enum>Qt::ClickFocus</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QPushButton" name="stepInsnButton">
+ <property name="text">
+ <string>Step (Insn)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="4">
+ <widget class="QPushButton" name="stopButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Stop</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" colspan="5">
+ <widget class="QPlainTextEdit" name="logView"/>
+ </item>
</layout>
</widget>
</widget>