Update to v087r25 release.

byuu says:

(r24 was a point release during merging of changes.)

This release is almost entirely Cydrak's direct work:
- Added ARM::sequential() and some WAITCNT timings
- Added Bus::io(uint32 pc), intended for prefetch timing
- Added ARM::load() with data rotation (fixed Mother 3 graphics)
- Added ARM::store() for data mirroring
- LDM, STM, and instruction fetch still use read/write()
- ARM::vector() no longer unmasks FIQs
- Set THUMB shifter flags via bit(), consistent with ARM
- Replace shifter loops with conditional tests

My changes:
- fixed sprite clipping on left-edge of screen
- added first system folder, GBA.system
- sudo make install is now make install
- make install will create GBA.system for you in your home folder

Windows users, take data/GBA.system and put it in the same folder as
bsnes.exe, and give it a BIOS named bios.rom
Or place it in your home folder (%APPDATA%/bsnes)
Also note that this is highly experimental, I'll probably be changing
things a lot before release.

EDIT: I botched the cartridge timing change. Will fix in r26. It'll
still run a bit too fast for now, unfortunately.
This commit is contained in:
Tim Allen 2012-04-15 16:49:56 +10:00
parent 28885db586
commit 1c18812f47
22 changed files with 276 additions and 216 deletions

View File

@ -1,7 +1,7 @@
#ifndef BASE_HPP #ifndef BASE_HPP
#define BASE_HPP #define BASE_HPP
static const char Version[] = "087.23"; static const char Version[] = "087.25";
#include <nall/platform.hpp> #include <nall/platform.hpp>
#include <nall/algorithm.hpp> #include <nall/algorithm.hpp>

View File

@ -0,0 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<system type="GBA">
<bios firmware="bios.rom" sha256="fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570"/>
</system>

View File

@ -45,6 +45,11 @@ void CPU::step(unsigned clocks) {
if(apu.clock < 0) co_switch(apu.thread); if(apu.clock < 0) co_switch(apu.thread);
} }
void CPU::bus_idle(uint32 addr) {
step(1);
return bus.idle(addr);
}
uint32 CPU::bus_read(uint32 addr, uint32 size) { uint32 CPU::bus_read(uint32 addr, uint32 size) {
step(bus.speed(addr, size)); step(bus.speed(addr, size));
return bus.read(addr, size); return bus.read(addr, size);

View File

@ -8,6 +8,7 @@ struct CPU : Processor::ARM, Thread, MMIO {
void enter(); void enter();
void step(unsigned clocks); void step(unsigned clocks);
void bus_idle(uint32 addr);
uint32 bus_read(uint32 addr, uint32 size); uint32 bus_read(uint32 addr, uint32 size);
void bus_write(uint32 addr, uint32 size, uint32 word); void bus_write(uint32 addr, uint32 size, uint32 word);

View File

@ -112,13 +112,13 @@ uint16 CPU::Registers::Interrupt::operator=(uint16 source) {
CPU::Registers::WaitControl::operator uint16() const { CPU::Registers::WaitControl::operator uint16() const {
return ( return (
(sram << 0) (nwait[3] << 0)
| (wait0n << 2) | (nwait[0] << 2)
| (wait0s << 4) | (swait[0] << 4)
| (wait1n << 5) | (nwait[1] << 5)
| (wait1s << 7) | (swait[1] << 7)
| (wait2n << 8) | (nwait[2] << 8)
| (wait2s << 10) | (swait[2] << 10)
| (phi << 11) | (phi << 11)
| (prefetch << 14) | (prefetch << 14)
| (gametype << 15) | (gametype << 15)
@ -126,16 +126,17 @@ CPU::Registers::WaitControl::operator uint16() const {
} }
uint16 CPU::Registers::WaitControl::operator=(uint16 source) { uint16 CPU::Registers::WaitControl::operator=(uint16 source) {
sram = (source >> 0) & 3; nwait[3] = (source >> 0) & 3;
wait0n = (source >> 2) & 3; nwait[0] = (source >> 2) & 3;
wait0s = (source >> 4) & 1; swait[0] = (source >> 4) & 1;
wait1n = (source >> 5) & 3; nwait[1] = (source >> 5) & 3;
wait1s = (source >> 7) & 1; swait[1] = (source >> 7) & 1;
wait2n = (source >> 8) & 3; nwait[2] = (source >> 8) & 3;
wait2s = (source >> 10) & 1; swait[2] = (source >> 10) & 1;
phi = (source >> 11) & 3; phi = (source >> 11) & 3;
prefetch = (source >> 14) & 1; prefetch = (source >> 14) & 1;
gametype = (source >> 15) & 1; gametype = (source >> 15) & 1;
swait[3] = nwait[3];
return operator uint16(); return operator uint16();
} }

View File

@ -91,13 +91,8 @@ struct Registers {
} irq; } irq;
struct WaitControl { struct WaitControl {
uint2 sram; uint2 nwait[4];
uint2 wait0n; uint2 swait[4];
uint1 wait0s;
uint2 wait1n;
uint1 wait1s;
uint2 wait2n;
uint1 wait2s;
uint2 phi; uint2 phi;
uint1 prefetch; uint1 prefetch;
uint1 gametype; uint1 gametype;

View File

@ -74,62 +74,77 @@ uint32 Bus::mirror(uint32 addr, uint32 size) {
} }
uint32 Bus::speed(uint32 addr, uint32 size) { uint32 Bus::speed(uint32 addr, uint32 size) {
return 2; if(addr & 0x08000000) {
static unsigned timing[] = { 5, 4, 3, 9 };
unsigned n = cpu.regs.wait.control.nwait[addr >> 25 & 3];
unsigned s = cpu.regs.wait.control.swait[addr >> 25 & 3];
//B B E I M P V O R R R R R R S S bool sequential = cpu.sequential();
//I I R R M R R A O O O O O O R R if((addr & 0xffff << 1) == 0) sequential = false;
//O O A A I A A M M M M M M M A A if(idleflag) sequential = false;
//S S M M O M M M M
static unsigned byte[] = { 1, 1, 3, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5 };
static unsigned half[] = { 1, 1, 3, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5 };
static unsigned word[] = { 1, 1, 6, 1, 1, 2, 2, 1, 8, 8, 8, 8, 8, 8, 8, 8 };
addr = (addr >> 24) & 15; if(sequential) return s << (size == Word); //16-bit bus
switch(size) { if(size == Word) n += s;
case Byte: return byte[addr]; return n;
case Half: return half[addr]; }
case Word: return word[addr];
switch(addr >> 24 & 7) {
case 0: return 1;
case 1: return 1;
case 2: return 3 << (size == Word);
case 3: return 1;
case 4: return 1;
case 5: return 1 << (size == Word);
case 6: return 1 << (size == Word);
case 7: return 1;
} }
} }
void Bus::idle(uint32 addr) {
if(addr & 0x08000000) idleflag = true;
}
uint32 Bus::read(uint32 addr, uint32 size) { uint32 Bus::read(uint32 addr, uint32 size) {
idleflag = false;
if(addr & 0x08000000) return cartridge.read(addr, size); if(addr & 0x08000000) return cartridge.read(addr, size);
switch(addr & 0x07000000) { switch(addr >> 24 & 7) {
case 0x00000000: return bios.read(addr, size); case 0: return bios.read(addr, size);
case 0x01000000: return bios.read(addr, size); case 1: return bios.read(addr, size);
case 0x02000000: return cpu.ewram.read(addr & 0x3ffff, size); case 2: return cpu.ewram.read(addr & 0x3ffff, size);
case 0x03000000: return cpu.iwram.read(addr & 0x7fff, size); case 3: return cpu.iwram.read(addr & 0x7fff, size);
case 0x04000000: case 4:
if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->read(addr, size); if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->read(addr, size);
if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).read(0x04000800 | (addr & 3), size); if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).read(0x04000800 | (addr & 3), size);
return 0u; return 0u;
case 0x05000000: return ppu.pram_read(addr, size); case 5: return ppu.pram_read(addr, size);
case 0x06000000: return ppu.vram_read(addr, size); case 6: return ppu.vram_read(addr, size);
case 0x07000000: return ppu.oam_read(addr, size); case 7: return ppu.oam_read(addr, size);
} }
} }
void Bus::write(uint32 addr, uint32 size, uint32 word) { void Bus::write(uint32 addr, uint32 size, uint32 word) {
idleflag = false;
if(addr & 0x08000000) return cartridge.write(addr, size, word); if(addr & 0x08000000) return cartridge.write(addr, size, word);
switch(addr & 0x07000000) { switch(addr >> 24 & 7) {
case 0x00000000: return; case 0: return;
case 0x01000000: return; case 1: return;
case 0x02000000: return cpu.ewram.write(addr & 0x3ffff, size, word); case 2: return cpu.ewram.write(addr & 0x3ffff, size, word);
case 0x03000000: return cpu.iwram.write(addr & 0x7fff, size, word); case 3: return cpu.iwram.write(addr & 0x7fff, size, word);
case 0x04000000: case 4:
if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->write(addr, size, word); if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->write(addr, size, word);
if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).write(0x04000800 | (addr & 3), size, word); if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).write(0x04000800 | (addr & 3), size, word);
return; return;
case 0x05000000: return ppu.pram_write(addr, size, word); case 5: return ppu.pram_write(addr, size, word);
case 0x06000000: return ppu.vram_write(addr, size, word); case 6: return ppu.vram_write(addr, size, word);
case 0x07000000: return ppu.oam_write(addr, size, word); case 7: return ppu.oam_write(addr, size, word);
} }
} }
void Bus::power() { void Bus::power() {
for(unsigned n = 0; n < 0x400; n++) mmio[n] = &unmappedMemory; for(unsigned n = 0; n < 0x400; n++) mmio[n] = &unmappedMemory;
idleflag = false;
} }
} }

View File

@ -23,9 +23,11 @@ struct MMIO : Memory {
struct Bus : Memory { struct Bus : Memory {
Memory *mmio[0x400]; Memory *mmio[0x400];
bool idleflag;
static uint32 mirror(uint32 addr, uint32 size); static uint32 mirror(uint32 addr, uint32 size);
uint32 speed(uint32 addr, uint32 size); uint32 speed(uint32 addr, uint32 size);
void idle(uint32 addr);
uint32 read(uint32 addr, uint32 size); uint32 read(uint32 addr, uint32 size);
void write(uint32 addr, uint32 size, uint32 word); void write(uint32 addr, uint32 size, uint32 word);
void power(); void power();

View File

@ -56,7 +56,7 @@ void PPU::render_object(Object &obj) {
x = (x / (1 + regs.mosaic.objhsize)) * (1 + regs.mosaic.objhsize); x = (x / (1 + regs.mosaic.objhsize)) * (1 + regs.mosaic.objhsize);
} }
unsigned ox = obj.x + px; uint9 ox = obj.x + px;
if(ox < 240 && x < obj.width && y < obj.height) { if(ox < 240 && x < obj.width && y < obj.height) {
unsigned offset = (y / 8) * rowsize + (x / 8); unsigned offset = (y / 8) * rowsize + (x / 8);
offset = offset * 64 + (y & 7) * 8 + (x & 7); offset = offset * 64 + (y & 7) * 8 + (x & 7);

View File

@ -55,63 +55,40 @@ uint32 ARM::mul(uint32 product, uint32 multiplicand, uint32 multiplier) {
return product; return product;
} }
uint32 ARM::lsl(uint32 source, uint32 shift) { uint32 ARM::lsl(uint32 source, uint8 shift) {
while(shift--) { carryout() = cpsr().c;
carryout() = source >> 31; if(shift == 0) return source;
source <<= 1;
}
if(cpsr().t) {
cpsr().n = source >> 31;
cpsr().z = source == 0;
cpsr().c = carryout();
}
carryout() = shift > 32 ? 0 : source & (1 << 32 - shift);
source = shift > 31 ? 0 : source << shift;
return source; return source;
} }
uint32 ARM::lsr(uint32 source, uint32 shift) { uint32 ARM::lsr(uint32 source, uint8 shift) {
while(shift--) { carryout() = cpsr().c;
carryout() = source & 1; if(shift == 0) return source;
source >>= 1;
}
if(cpsr().t) {
cpsr().n = source >> 31;
cpsr().z = source == 0;
cpsr().c = carryout();
}
carryout() = shift > 32 ? 0 : source & (1 << shift - 1);
source = shift > 31 ? 0 : source >> shift;
return source; return source;
} }
uint32 ARM::asr(uint32 source, uint32 shift) { uint32 ARM::asr(uint32 source, uint8 shift) {
while(shift--) { carryout() = cpsr().c;
carryout() = source & 1; if(shift == 0) return source;
source = (int32)source >> 1;
}
if(cpsr().t) {
cpsr().n = source >> 31;
cpsr().z = source == 0;
cpsr().c = carryout();
}
carryout() = shift > 32 ? source & (1 << 31) : source & (1 << shift - 1);
source = shift > 31 ? (int32)source >> 31 : (int32)source >> shift;
return source; return source;
} }
uint32 ARM::ror(uint32 source, uint32 shift) { uint32 ARM::ror(uint32 source, uint8 shift) {
while(shift--) { carryout() = cpsr().c;
carryout() = source & 1; if(shift == 0) return source;
source = (source << 31) | (source >> 1);
}
if(cpsr().t) {
cpsr().n = source >> 31;
cpsr().z = source == 0;
cpsr().c = carryout();
}
if(shift &= 31)
source = source << 32 - shift | source >> shift;
carryout() = source & (1 << 31);
return source; return source;
} }

View File

@ -26,16 +26,43 @@ void ARM::exec() {
cpsr().t ? thumb_step() : arm_step(); cpsr().t ? thumb_step() : arm_step();
} }
void ARM::idle() {
bus_idle(r(15));
}
uint32 ARM::read(uint32 addr, uint32 size) { uint32 ARM::read(uint32 addr, uint32 size) {
uint32 word = bus_read(addr, size); uint32 word = bus_read(addr, size);
//uint32 rotate = (addr & 3) << 3; sequential() = true;
//word = (word >> rotate) | (word << (32 - rotate)); return word;
//word = word & (~0u >> (32 - size)); }
uint32 ARM::load(uint32 addr, uint32 size) {
sequential() = false;
uint32 word = read(addr, size);
if(size == Half) { word &= 0xffff; word |= word << 16; }
if(size == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
word = ror(word, 8 * (addr & 3));
idle();
if(size == Half) word &= 0xffff;
if(size == Byte) word &= 0xff;
return word; return word;
} }
void ARM::write(uint32 addr, uint32 size, uint32 word) { void ARM::write(uint32 addr, uint32 size, uint32 word) {
return bus_write(addr, size, word); bus_write(addr, size, word);
sequential() = true;
}
void ARM::store(uint32 addr, uint32 size, uint32 word) {
if(size == Half) { word &= 0xffff; word |= word << 16; }
if(size == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
sequential() = false;
write(addr, size, word);
sequential() = false;
} }
void ARM::vector(uint32 addr, Processor::Mode mode) { void ARM::vector(uint32 addr, Processor::Mode mode) {
@ -43,7 +70,7 @@ void ARM::vector(uint32 addr, Processor::Mode mode) {
processor.setMode(mode); processor.setMode(mode);
spsr() = psr; spsr() = psr;
cpsr().i = 1; cpsr().i = 1;
cpsr().f = mode == Processor::Mode::FIQ; cpsr().f |= mode == Processor::Mode::FIQ;
cpsr().t = 0; cpsr().t = 0;
r(14) = pipeline.decode.address; r(14) = pipeline.decode.address;
r(15) = addr; r(15) = addr;

View File

@ -3,7 +3,8 @@
namespace Processor { namespace Processor {
//ARMv3, ARMv4TM //ARMv3
//ARMv4TDMI
struct ARM { struct ARM {
enum : unsigned { Byte = 8, Half = 16, Word = 32 }; enum : unsigned { Byte = 8, Half = 16, Word = 32 };
@ -12,13 +13,17 @@ struct ARM {
#include "instructions-thumb.hpp" #include "instructions-thumb.hpp"
#include "disassembler.hpp" #include "disassembler.hpp"
virtual void step(unsigned clocks) = 0; virtual void step(unsigned clocks) = 0;
virtual void bus_idle(uint32 addr) = 0;
virtual uint32 bus_read(uint32 addr, uint32 size) = 0; virtual uint32 bus_read(uint32 addr, uint32 size) = 0;
virtual void bus_write(uint32 addr, uint32 size, uint32 word) = 0; virtual void bus_write(uint32 addr, uint32 size, uint32 word) = 0;
void power(); void power();
void exec(); void exec();
void idle();
uint32 read(uint32 addr, uint32 size); uint32 read(uint32 addr, uint32 size);
uint32 load(uint32 addr, uint32 size);
void write(uint32 addr, uint32 size, uint32 word); void write(uint32 addr, uint32 size, uint32 word);
void store(uint32 addr, uint32 size, uint32 word);
void vector(uint32 addr, Processor::Mode mode); void vector(uint32 addr, Processor::Mode mode);
bool condition(uint4 condition); bool condition(uint4 condition);
@ -26,10 +31,10 @@ struct ARM {
uint32 add(uint32 source, uint32 modify, bool carry); uint32 add(uint32 source, uint32 modify, bool carry);
uint32 sub(uint32 source, uint32 modify, bool carry); uint32 sub(uint32 source, uint32 modify, bool carry);
uint32 mul(uint32 product, uint32 multiplicand, uint32 multiplier); uint32 mul(uint32 product, uint32 multiplicand, uint32 multiplier);
uint32 lsl(uint32 source, uint32 shift); uint32 lsl(uint32 source, uint8 shift);
uint32 lsr(uint32 source, uint32 shift); uint32 lsr(uint32 source, uint8 shift);
uint32 asr(uint32 source, uint32 shift); uint32 asr(uint32 source, uint8 shift);
uint32 ror(uint32 source, uint32 shift); uint32 ror(uint32 source, uint8 shift);
uint32 rrx(uint32 source); uint32 rrx(uint32 source);
void serialize(serializer&); void serialize(serializer&);

View File

@ -5,6 +5,7 @@ void ARM::arm_step() {
pipeline.reload = false; pipeline.reload = false;
r(15).data &= ~3; r(15).data &= ~3;
sequential() = false;
pipeline.fetch.address = r(15) & ~3; pipeline.fetch.address = r(15) & ~3;
pipeline.fetch.instruction = read(pipeline.fetch.address, Word); pipeline.fetch.instruction = read(pipeline.fetch.address, Word);
@ -195,8 +196,8 @@ void ARM::arm_op_memory_swap() {
uint4 d = instruction() >> 12; uint4 d = instruction() >> 12;
uint4 m = instruction(); uint4 m = instruction();
uint32 word = read(r(n), byte ? Byte : Word); uint32 word = load(r(n), byte ? Byte : Word);
write(r(n), byte ? Byte : Word, r(m)); store(r(n), byte ? Byte : Word, r(m));
r(d) = word; r(d) = word;
} }
@ -215,7 +216,7 @@ void ARM::arm_op_move_half_register() {
uint1 pre = instruction() >> 24; uint1 pre = instruction() >> 24;
uint1 up = instruction() >> 23; uint1 up = instruction() >> 23;
uint1 writeback = instruction() >> 21; uint1 writeback = instruction() >> 21;
uint1 load = instruction() >> 20; uint1 l = instruction() >> 20;
uint4 n = instruction() >> 16; uint4 n = instruction() >> 16;
uint4 d = instruction() >> 12; uint4 d = instruction() >> 12;
uint4 m = instruction(); uint4 m = instruction();
@ -224,8 +225,8 @@ void ARM::arm_op_move_half_register() {
uint32 rm = r(m); uint32 rm = r(m);
if(pre == 1) rn = up ? rn + rm : rn - rm; if(pre == 1) rn = up ? rn + rm : rn - rm;
if(load == 1) r(d) = read(rn, Half); if(l == 1) r(d) = load(rn, Half);
if(load == 0) write(rn, Half, r(d)); if(l == 0) store(rn, Half, r(d));
if(pre == 0) rn = up ? rn + rm : rn - rm; if(pre == 0) rn = up ? rn + rm : rn - rm;
if(pre == 0 || writeback == 1) r(n) = rn; if(pre == 0 || writeback == 1) r(n) = rn;
@ -247,7 +248,7 @@ void ARM::arm_op_move_half_immediate() {
uint1 pre = instruction() >> 24; uint1 pre = instruction() >> 24;
uint1 up = instruction() >> 23; uint1 up = instruction() >> 23;
uint1 writeback = instruction() >> 21; uint1 writeback = instruction() >> 21;
uint1 load = instruction() >> 20; uint1 l = instruction() >> 20;
uint4 n = instruction() >> 16; uint4 n = instruction() >> 16;
uint4 d = instruction() >> 12; uint4 d = instruction() >> 12;
uint4 ih = instruction() >> 8; uint4 ih = instruction() >> 8;
@ -257,8 +258,8 @@ void ARM::arm_op_move_half_immediate() {
uint8 immediate = (ih << 4) + (il << 0); uint8 immediate = (ih << 4) + (il << 0);
if(pre == 1) rn = up ? rn + immediate : rn - immediate; if(pre == 1) rn = up ? rn + immediate : rn - immediate;
if(load == 1) r(d) = read(rn, Half); if(l == 1) r(d) = load(rn, Half);
if(load == 0) write(rn, Half, r(d)); if(l == 0) store(rn, Half, r(d));
if(pre == 0) rn = up ? rn + immediate : rn - immediate; if(pre == 0) rn = up ? rn + immediate : rn - immediate;
if(pre == 0 || writeback == 1) r(n) = rn; if(pre == 0 || writeback == 1) r(n) = rn;
@ -288,7 +289,7 @@ void ARM::arm_op_load_register() {
uint32 rm = r(m); uint32 rm = r(m);
if(pre == 1) rn = up ? rn + rm : rn - rm; if(pre == 1) rn = up ? rn + rm : rn - rm;
uint32 word = read(rn, half ? Half : Byte); uint32 word = load(rn, half ? Half : Byte);
r(d) = half ? (int16)word : (int8)word; r(d) = half ? (int16)word : (int8)word;
if(pre == 0) rn = up ? rn + rm : rn - rm; if(pre == 0) rn = up ? rn + rm : rn - rm;
@ -321,7 +322,7 @@ void ARM::arm_op_load_immediate() {
uint8 immediate = (ih << 4) + (il << 0); uint8 immediate = (ih << 4) + (il << 0);
if(pre == 1) rn = up ? rn + immediate : rn - immediate; if(pre == 1) rn = up ? rn + immediate : rn - immediate;
uint32 word = read(rn, half ? Half : Byte); uint32 word = load(rn, half ? Half : Byte);
r(d) = half ? (int16)word : (int8)word; r(d) = half ? (int16)word : (int8)word;
if(pre == 0) rn = up ? rn + immediate : rn - immediate; if(pre == 0) rn = up ? rn + immediate : rn - immediate;
@ -486,7 +487,7 @@ void ARM::arm_op_move_immediate_offset() {
uint1 up = instruction() >> 23; uint1 up = instruction() >> 23;
uint1 byte = instruction() >> 22; uint1 byte = instruction() >> 22;
uint1 writeback = instruction() >> 21; uint1 writeback = instruction() >> 21;
uint1 load = instruction() >> 20; uint1 l = instruction() >> 20;
uint4 n = instruction() >> 16; uint4 n = instruction() >> 16;
uint4 d = instruction() >> 12; uint4 d = instruction() >> 12;
uint12 rm = instruction(); uint12 rm = instruction();
@ -495,8 +496,8 @@ void ARM::arm_op_move_immediate_offset() {
auto &rd = r(d); auto &rd = r(d);
if(pre == 1) rn = up ? rn + rm : rn - rm; if(pre == 1) rn = up ? rn + rm : rn - rm;
if(load == 1) rd = read(rn, byte ? Byte : Word); if(l == 1) rd = load(rn, byte ? Byte : Word);
if(load == 0) write(rn, byte ? Byte : Word, rd); if(l == 0) store(rn, byte ? Byte : Word, rd);
if(pre == 0) rn = up ? rn + rm : rn - rm; if(pre == 0) rn = up ? rn + rm : rn - rm;
if(pre == 0 || writeback == 1) r(n) = rn; if(pre == 0 || writeback == 1) r(n) = rn;
@ -521,7 +522,7 @@ void ARM::arm_op_move_register_offset() {
uint1 up = instruction() >> 23; uint1 up = instruction() >> 23;
uint1 byte = instruction() >> 22; uint1 byte = instruction() >> 22;
uint1 writeback = instruction() >> 21; uint1 writeback = instruction() >> 21;
uint1 load = instruction() >> 20; uint1 l = instruction() >> 20;
uint4 n = instruction() >> 16; uint4 n = instruction() >> 16;
uint4 d = instruction() >> 12; uint4 d = instruction() >> 12;
uint5 immediate = instruction() >> 7; uint5 immediate = instruction() >> 7;
@ -540,8 +541,8 @@ void ARM::arm_op_move_register_offset() {
if(mode == 3) rm = rs ? ror(rm, rs) : rrx(rm); if(mode == 3) rm = rs ? ror(rm, rs) : rrx(rm);
if(pre == 1) rn = up ? rn + rm : rn - rm; if(pre == 1) rn = up ? rn + rm : rn - rm;
if(load == 1) rd = read(rn, byte ? Byte : Word); if(l == 1) rd = load(rn, byte ? Byte : Word);
if(load == 0) write(rn, byte ? Byte : Word, rd); if(l == 0) store(rn, byte ? Byte : Word, rd);
if(pre == 0) rn = up ? rn + rm : rn - rm; if(pre == 0) rn = up ? rn + rm : rn - rm;
if(pre == 0 || writeback == 1) r(n) = rn; if(pre == 0 || writeback == 1) r(n) = rn;
@ -562,7 +563,7 @@ void ARM::arm_op_move_multiple() {
uint1 up = instruction() >> 23; uint1 up = instruction() >> 23;
uint1 s = instruction() >> 22; uint1 s = instruction() >> 22;
uint1 writeback = instruction() >> 21; uint1 writeback = instruction() >> 21;
uint1 load = instruction() >> 20; uint1 l = instruction() >> 20;
uint4 n = instruction() >> 16; uint4 n = instruction() >> 16;
uint16 list = instruction(); uint16 list = instruction();
@ -574,25 +575,29 @@ void ARM::arm_op_move_multiple() {
Processor::Mode pmode = mode(); Processor::Mode pmode = mode();
bool usr = false; bool usr = false;
if(s && load == 1 && (list & 0x8000) == 0) usr = true; if(s && l == 1 && (list & 0x8000) == 0) usr = true;
if(s && load == 0) usr = true; if(s && l == 0) usr = true;
if(usr) processor.setMode(Processor::Mode::USR); if(usr) processor.setMode(Processor::Mode::USR);
for(unsigned n = 0; n < 16; n++) { sequential() = false;
if(list & (1 << n)) { for(unsigned m = 0; m < 16; m++) {
if(load == 1) r(n) = read(rn, Word); if(list & (1 << m)) {
if(load == 0) write(rn, Word, r(n)); if(l == 1) r(m) = read(rn, Word);
if(l == 0) write(rn, Word, r(m));
rn += 4; rn += 4;
} }
} }
if(usr) processor.setMode(pmode); if(usr) processor.setMode(pmode);
if(load == 1 && s && (list & 0x8000)) { if(l == 1) {
if(mode() != Processor::Mode::USR && mode() != Processor::Mode::SYS) { idle();
cpsr() = spsr(); if(s && (list & 0x8000)) {
processor.setMode((Processor::Mode)cpsr().m); if(mode() != Processor::Mode::USR && mode() != Processor::Mode::SYS) {
cpsr() = spsr();
processor.setMode((Processor::Mode)cpsr().m);
}
} }
} }

View File

@ -5,6 +5,7 @@ void ARM::thumb_step() {
pipeline.reload = false; pipeline.reload = false;
r(15).data &= ~1; r(15).data &= ~1;
sequential() = false;
pipeline.fetch.address = r(15) & ~1; pipeline.fetch.address = r(15) & ~1;
pipeline.fetch.instruction = read(pipeline.fetch.address, Half); pipeline.fetch.instruction = read(pipeline.fetch.address, Half);
@ -62,12 +63,12 @@ void ARM::thumb_opcode(uint4 opcode, uint4 d, uint4 m) {
switch(opcode) { switch(opcode) {
case 0: r(d) = bit(r(d) & r(m)); break; //AND case 0: r(d) = bit(r(d) & r(m)); break; //AND
case 1: r(d) = bit(r(d) ^ r(m)); break; //EOR case 1: r(d) = bit(r(d) ^ r(m)); break; //EOR
case 2: r(d) = lsl(r(d), r(m) & 0xff); break; //LSL case 2: r(d) = bit(lsl(r(d), r(m))); break; //LSL
case 3: r(d) = lsr(r(d), r(m) & 0xff); break; //LSR case 3: r(d) = bit(lsr(r(d), r(m))); break; //LSR
case 4: r(d) = asr(r(d), r(m) & 0xff); break; //ASR case 4: r(d) = bit(asr(r(d), r(m))); break; //ASR
case 5: r(d) = add(r(d), r(m), cpsr().c); break; //ADC case 5: r(d) = add(r(d), r(m), cpsr().c); break; //ADC
case 6: r(d) = sub(r(d), r(m), cpsr().c); break; //SBC case 6: r(d) = sub(r(d), r(m), cpsr().c); break; //SBC
case 7: r(d) = ror(r(d), r(m) & 0xff); break; //ROR case 7: r(d) = bit(ror(r(d), r(m))); break; //ROR
case 8: bit(r(d) & r(m)); break; //TST case 8: bit(r(d) & r(m)); break; //TST
case 9: r(d) = sub(0, r(m), 1); break; //NEG case 9: r(d) = sub(0, r(m), 1); break; //NEG
case 10: sub(r(d), r(m), 1); break; //CMP case 10: sub(r(d), r(m), 1); break; //CMP
@ -128,9 +129,9 @@ void ARM::thumb_op_shift_immediate() {
uint3 d = instruction() >> 0; uint3 d = instruction() >> 0;
switch(opcode) { switch(opcode) {
case 0: r(d) = lsl(r(m), immediate); break; case 0: r(d) = bit(lsl(r(m), immediate)); break;
case 1: r(d) = lsr(r(m), immediate == 0 ? 32u : (unsigned)immediate); break; case 1: r(d) = bit(lsr(r(m), immediate == 0 ? 32u : (unsigned)immediate)); break;
case 2: r(d) = asr(r(m), immediate == 0 ? 32u : (unsigned)immediate); break; case 2: r(d) = bit(asr(r(m), immediate == 0 ? 32u : (unsigned)immediate)); break;
} }
} }
@ -203,7 +204,7 @@ void ARM::thumb_op_load_literal() {
uint8 displacement = instruction(); uint8 displacement = instruction();
unsigned rm = (r(15) & ~3) + displacement * 4; unsigned rm = (r(15) & ~3) + displacement * 4;
r(d) = read(rm, Word); r(d) = load(rm, Word);
} }
//(ld(r,s),str){b,h} rd,[rn,rm] //(ld(r,s),str){b,h} rd,[rn,rm]
@ -219,14 +220,14 @@ void ARM::thumb_op_move_register_offset() {
uint3 d = instruction() >> 0; uint3 d = instruction() >> 0;
switch(opcode) { switch(opcode) {
case 0: write(r(n) + r(m), Word, r(d)); break; //STR case 0: store(r(n) + r(m), Word, r(d)); break; //STR
case 1: write(r(n) + r(m), Half, r(d)); break; //STRH case 1: store(r(n) + r(m), Half, r(d)); break; //STRH
case 2: write(r(n) + r(m), Byte, r(d)); break; //STRB case 2: store(r(n) + r(m), Byte, r(d)); break; //STRB
case 3: r(d) = (int8)read(r(n) + r(m), Byte); break; //LDSB case 3: r(d) = (int8)load(r(n) + r(m), Byte); break; //LDSB
case 4: r(d) = read(r(n) + r(m), Word); break; //LDR case 4: r(d) = load(r(n) + r(m), Word); break; //LDR
case 5: r(d) = read(r(n) + r(m), Half); break; //LDRH case 5: r(d) = load(r(n) + r(m), Half); break; //LDRH
case 6: r(d) = read(r(n) + r(m), Byte); break; //LDRB case 6: r(d) = load(r(n) + r(m), Byte); break; //LDRB
case 7: r(d) = (int16)read(r(n) + r(m), Half); break; //LDSH case 7: r(d) = (int16)load(r(n) + r(m), Half); break; //LDSH
} }
} }
@ -237,13 +238,13 @@ void ARM::thumb_op_move_register_offset() {
//n = rn //n = rn
//d = rd //d = rd
void ARM::thumb_op_move_word_immediate() { void ARM::thumb_op_move_word_immediate() {
uint1 load = instruction() >> 11; uint1 l = instruction() >> 11;
uint5 offset = instruction() >> 6; uint5 offset = instruction() >> 6;
uint3 n = instruction() >> 3; uint3 n = instruction() >> 3;
uint3 d = instruction() >> 0; uint3 d = instruction() >> 0;
if(load == 1) r(d) = read(r(n) + offset * 4, Word); if(l == 1) r(d) = load(r(n) + offset * 4, Word);
if(load == 0) write(r(n) + offset * 4, Word, r(d)); if(l == 0) store(r(n) + offset * 4, Word, r(d));
} }
//(ldr,str)b rd,[rn,#offset] //(ldr,str)b rd,[rn,#offset]
@ -253,13 +254,13 @@ void ARM::thumb_op_move_word_immediate() {
//n = rn //n = rn
//d = rd //d = rd
void ARM::thumb_op_move_byte_immediate() { void ARM::thumb_op_move_byte_immediate() {
uint1 load = instruction() >> 11; uint1 l = instruction() >> 11;
uint5 offset = instruction() >> 6; uint5 offset = instruction() >> 6;
uint3 n = instruction() >> 3; uint3 n = instruction() >> 3;
uint3 d = instruction() >> 0; uint3 d = instruction() >> 0;
if(load == 1) r(d) = read(r(n) + offset, Byte); if(l == 1) r(d) = load(r(n) + offset, Byte);
if(load == 0) write(r(n) + offset, Byte, r(d)); if(l == 0) store(r(n) + offset, Byte, r(d));
} }
//(ldr,str)h rd,[rn,#offset] //(ldr,str)h rd,[rn,#offset]
@ -269,27 +270,27 @@ void ARM::thumb_op_move_byte_immediate() {
//n = rn //n = rn
//d = rd //d = rd
void ARM::thumb_op_move_half_immediate() { void ARM::thumb_op_move_half_immediate() {
uint1 load = instruction() >> 11; uint1 l = instruction() >> 11;
uint5 offset = instruction() >> 6; uint5 offset = instruction() >> 6;
uint3 n = instruction() >> 3; uint3 n = instruction() >> 3;
uint3 d = instruction() >> 0; uint3 d = instruction() >> 0;
if(load == 1) r(d) = read(r(n) + offset * 2, Half); if(l == 1) r(d) = load(r(n) + offset * 2, Half);
if(load == 0) write(r(n) + offset * 2, Half, r(d)); if(l == 0) store(r(n) + offset * 2, Half, r(d));
} }
//(ldr,str) rd,[sp,#immediate] //(ldr,str) rd,[sp,#immediate]
//1001 oddd iiii iiii //1001 oddd iiii iiii
//o = opcode //l = load
//d = rd //d = rd
//i = immediate //i = immediate
void ARM::thumb_op_move_stack() { void ARM::thumb_op_move_stack() {
uint1 opcode = instruction() >> 11; uint1 l = instruction() >> 11;
uint3 d = instruction() >> 8; uint3 d = instruction() >> 8;
uint8 immediate = instruction(); uint8 immediate = instruction();
if(opcode == 0) write(r(13) + immediate * 4, Word, r(d)); if(l == 1) r(d) = load(r(13) + immediate * 4, Word);
if(opcode == 1) r(d) = read(r(13) + immediate * 4, Word); if(l == 0) store(r(13) + immediate * 4, Word, r(d));
} }
//add rd,{pc,sp},#immediate //add rd,{pc,sp},#immediate
@ -325,31 +326,33 @@ void ARM::thumb_op_adjust_stack() {
//r = push lr -or- pop pc //r = push lr -or- pop pc
//l = register list //l = register list
void ARM::thumb_op_stack_multiple() { void ARM::thumb_op_stack_multiple() {
uint1 load = instruction() >> 11; uint1 l = instruction() >> 11;
uint1 branch = instruction() >> 8; uint1 branch = instruction() >> 8;
uint8 list = instruction(); uint8 list = instruction();
uint32 sp = 0; uint32 sp = 0;
if(load == 1) sp = r(13); if(l == 1) sp = r(13);
if(load == 0) sp = r(13) - (bit::count(list) + branch) * 4; if(l == 0) sp = r(13) - (bit::count(list) + branch) * 4;
for(unsigned l = 0; l < 8; l++) { sequential() = false;
if(list & (1 << l)) { for(unsigned m = 0; m < 8; m++) {
if(load == 1) r(l) = read(sp, Word); //POP if(list & (1 << m)) {
if(load == 0) write(sp, Word, r(l)); //PUSH if(l == 1) r(m) = read(sp, Word); //POP
if(l == 0) write(sp, Word, r(m)); //PUSH
sp += 4; sp += 4;
} }
} }
if(branch) { if(branch) {
//note: ARMv5+ POP sets cpsr().t //note: ARMv5+ POP sets cpsr().t
if(load == 1) r(15) = read(sp, Word); //POP if(l == 1) r(15) = read(sp, Word); //POP
if(load == 0) write(sp, Word, r(14)); //PUSH if(l == 0) write(sp, Word, r(14)); //PUSH
sp += 4; sp += 4;
} }
if(load == 1) r(13) += (bit::count(list) + branch) * 4; if(l == 1) idle();
if(load == 0) r(13) -= (bit::count(list) + branch) * 4; if(l == 1) r(13) += (bit::count(list) + branch) * 4;
if(l == 0) r(13) -= (bit::count(list) + branch) * 4;
} }
//(ldmia,stmia) rn!,{r...} //(ldmia,stmia) rn!,{r...}
@ -358,17 +361,20 @@ void ARM::thumb_op_stack_multiple() {
//n = rn //n = rn
//l = register list //l = register list
void ARM::thumb_op_move_multiple() { void ARM::thumb_op_move_multiple() {
uint1 load = instruction() >> 11; uint1 l = instruction() >> 11;
uint3 n = instruction() >> 8; uint3 n = instruction() >> 8;
uint8 list = instruction(); uint8 list = instruction();
for(unsigned l = 0; l < 8; l++) { sequential() = false;
if(list & (1 << l)) { for(unsigned m = 0; m < 8; m++) {
if(load == 1) r(l) = read(r(n), Word); //LDMIA if(list & (1 << m)) {
if(load == 0) write(r(n), Word, r(l)); //STMIA if(l == 1) r(m) = read(r(n), Word); //LDMIA
if(l == 0) write(r(n), Word, r(m)); //STMIA
r(n) += 4; r(n) += 4;
} }
} }
if(l == 1) idle();
} }
//swi #immediate //swi #immediate

View File

@ -11,6 +11,7 @@ void ARM::Processor::power() {
pc = 0; pc = 0;
carryout = false; carryout = false;
sequential = false;
irqline = false; irqline = false;
cpsr = 0; cpsr = 0;

View File

@ -104,6 +104,7 @@ struct Processor {
GPR pc; GPR pc;
PSR cpsr; PSR cpsr;
bool carryout; bool carryout;
bool sequential;
bool irqline; bool irqline;
GPR *r[16]; GPR *r[16];
@ -123,6 +124,7 @@ alwaysinline GPR& r(unsigned n) { return *processor.r[n]; }
alwaysinline PSR& cpsr() { return processor.cpsr; } alwaysinline PSR& cpsr() { return processor.cpsr; }
alwaysinline PSR& spsr() { return *processor.spsr; } alwaysinline PSR& spsr() { return *processor.spsr; }
alwaysinline bool& carryout() { return processor.carryout; } alwaysinline bool& carryout() { return processor.carryout; }
alwaysinline bool& sequential() { return processor.sequential; }
alwaysinline uint32 instruction() { return pipeline.execute.instruction; } alwaysinline uint32 instruction() { return pipeline.execute.instruction; }
alwaysinline Processor::Mode mode() { return (Processor::Mode)processor.cpsr.m; } alwaysinline Processor::Mode mode() { return (Processor::Mode)processor.cpsr.m; }
alwaysinline bool privilegedmode() const { return (Processor::Mode)processor.cpsr.m != Processor::Mode::USR; } alwaysinline bool privilegedmode() const { return (Processor::Mode)processor.cpsr.m != Processor::Mode::USR; }

View File

@ -12,6 +12,7 @@ struct ArmDSP : Processor::ARM, public Coprocessor {
void enter(); void enter();
void step(unsigned clocks); void step(unsigned clocks);
void bus_idle(uint32 addr);
uint32 bus_read(uint32 addr, uint32 size); uint32 bus_read(uint32 addr, uint32 size);
void bus_write(uint32 addr, uint32 size, uint32 word); void bus_write(uint32 addr, uint32 size, uint32 word);

View File

@ -1,6 +1,12 @@
#ifdef ARMDSP_CPP #ifdef ARMDSP_CPP
void ArmDSP::bus_idle(uint32 addr) {
step(1);
}
uint32 ArmDSP::bus_read(uint32 addr, uint32 size) { uint32 ArmDSP::bus_read(uint32 addr, uint32 size) {
step(1);
static auto memory = [&](const uint8 *memory, uint32 addr, uint32 size) { static auto memory = [&](const uint8 *memory, uint32 addr, uint32 size) {
memory += addr & ~3; memory += addr & ~3;
return (memory[0] << 0) | (memory[1] << 8) | (memory[2] << 16) | (memory[3] << 24); return (memory[0] << 0) | (memory[1] << 8) | (memory[2] << 16) | (memory[3] << 24);
@ -34,6 +40,8 @@ uint32 ArmDSP::bus_read(uint32 addr, uint32 size) {
} }
void ArmDSP::bus_write(uint32 addr, uint32 size, uint32 word) { void ArmDSP::bus_write(uint32 addr, uint32 size, uint32 word) {
step(1);
static auto memory = [](uint8 *memory, uint32 addr, uint32 size, uint32 word) { static auto memory = [](uint8 *memory, uint32 addr, uint32 size, uint32 word) {
switch(size) { switch(size) {
case Word: case Word:

View File

@ -69,18 +69,23 @@ else
endif endif
install: install:
ifeq ($(platform),x) ifeq ($(USER),root)
install -D -m 755 out/$(name) $(DESTDIR)$(prefix)/bin/$(name) @echo Please do not run make install as root.
@echo The installer needs to know your home directory to install important files.
else ifeq ($(platform),x)
sudo install -D -m 755 out/$(name) $(DESTDIR)$(prefix)/bin/$(name)
sudo install -D -m 644 data/$(name).png $(DESTDIR)$(prefix)/share/pixmaps/$(name).png
sudo install -D -m 644 data/$(name).desktop $(DESTDIR)$(prefix)/share/applications/$(name).desktop
mkdir -p ~/.config/$(name) mkdir -p ~/.config/$(name)
install -D -m 644 data/$(name).png $(DESTDIR)$(prefix)/share/pixmaps/$(name).png
install -D -m 644 data/$(name).desktop $(DESTDIR)$(prefix)/share/applications/$(name).desktop
cp data/cheats.xml ~/.config/$(name)/cheats.xml cp data/cheats.xml ~/.config/$(name)/cheats.xml
chmod 777 ~/.config/$(name) ~/.config/$(name)/cheats.xml cp -R data/GBA.system ~/.config/$(name)
chmod -R 777 ~/.config/$(name)
endif endif
uninstall: uninstall:
ifeq ($(platform),x) ifeq ($(platform),x)
rm $(DESTDIR)$(prefix)/bin/$(name) sudo rm $(DESTDIR)$(prefix)/bin/$(name)
rm $(DESTDIR)$(prefix)/share/pixmaps/$(name).png sudo rm $(DESTDIR)$(prefix)/share/pixmaps/$(name).png
rm $(DESTDIR)$(prefix)/share/applications/$(name).desktop sudo rm $(DESTDIR)$(prefix)/share/applications/$(name).desktop
endif endif

View File

@ -1,4 +1,25 @@
void InterfaceGBA::initialize() { void InterfaceGBA::initialize() {
string filename = application->path("GBA.system/manifest.xml");
string markup;
markup.readfile(filename);
XML::Document document(markup);
if(document["system"]["bios"].exists()) {
auto &bios = document["system"]["bios"];
string firmware = bios["firmware"].data;
string hash = bios["sha256"].data;
uint8_t *data;
unsigned size;
if(file::read({dir(filename),firmware}, data, size) == true) {
if(nall::sha256(data, size) == hash) {
GBA::bios.load(data, size);
} else {
MessageWindow::information(Window::None, "Warning: GBA BIOS SHA256 sum is incorrect.");
}
}
}
GBA::interface = this; GBA::interface = this;
GBA::system.init(); GBA::system.init();
} }
@ -10,42 +31,27 @@ bool InterfaceGBA::cartridgeLoaded() {
bool InterfaceGBA::loadCartridge(const string &filename) { bool InterfaceGBA::loadCartridge(const string &filename) {
interface->unloadCartridge(); interface->unloadCartridge();
uint8_t *biosdata; uint8_t *data;
unsigned biossize; unsigned size;
uint8_t *cartdata;
unsigned cartsize;
if(filename.endswith("/")) { if(filename.endswith("/")) {
if(file::exists({filename, "bios.rom"}) == false) { if(file::read({filename, "program.rom"}, data, size) == false) return false;
message("Error: Game Boy Advance BIOS (bios.rom) not found.");
return false;
}
if(file::read({filename, "bios.rom"}, biosdata, biossize) == false) return false;
if(file::read({filename, "program.rom"}, cartdata, cartsize) == false) return false;
interface->base = {true, filename}; interface->base = {true, filename};
} else { } else {
if(file::exists({dir(filename), "gbabios.rom"}) == false) { if(file::read(filename, data, size) == false) return false;
message("Error: Game Boy Advance BIOS (gbabios.rom) not found.");
return false;
}
if(file::read({dir(filename), "gbabios.rom"}, biosdata, biossize) == false) return false;
if(file::read(filename, cartdata, cartsize) == false) return false;
interface->base = {false, filename}; interface->base = {false, filename};
} }
interface->game = interface->base; interface->game = interface->base;
interface->cartridgeTitle = interface->base.title(); interface->cartridgeTitle = interface->base.title();
interface->applyPatch(interface->base, cartdata, cartsize); interface->applyPatch(interface->base, data, size);
string markup; string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml")); markup.readfile(interface->base.filename("manifest.xml", ".xml"));
GBA::bios.load(biosdata, biossize); GBA::cartridge.load(markup, data, size);
GBA::cartridge.load(markup, cartdata, cartsize);
GBA::system.power(); GBA::system.power();
delete[] biosdata; delete[] data;
delete[] cartdata;
GBA::video.generate(GBA::Video::Format::RGB30); GBA::video.generate(GBA::Video::Format::RGB30);
interface->loadCartridge(::Interface::Mode::GBA); interface->loadCartridge(::Interface::Mode::GBA);

View File

@ -15,8 +15,6 @@ AdvancedSettings::AdvancedSettings() {
focusPolicy[2].setText("Pause emulation"); focusPolicy[2].setText("Pause emulation");
RadioBox::group(focusPolicy[0], focusPolicy[1], focusPolicy[2]); RadioBox::group(focusPolicy[0], focusPolicy[1], focusPolicy[2]);
focusPolicy[config->input.focusPolicy].setChecked(); focusPolicy[config->input.focusPolicy].setChecked();
aboutLabel.setFont(application->boldFont);
aboutLabel.setText("bsnes author: byuu license: GPLv3 website: byuu.org");
lstring list; lstring list;
@ -55,8 +53,6 @@ AdvancedSettings::AdvancedSettings() {
focusPolicyLayout.append(focusPolicy[0], { ~0, 0 }, 5); focusPolicyLayout.append(focusPolicy[0], { ~0, 0 }, 5);
focusPolicyLayout.append(focusPolicy[1], { ~0, 0 }, 5); focusPolicyLayout.append(focusPolicy[1], { ~0, 0 }, 5);
focusPolicyLayout.append(focusPolicy[2], { ~0, 0 }, 0); focusPolicyLayout.append(focusPolicy[2], { ~0, 0 }, 0);
append(spacer, { ~0, ~0 }, 0);
append(aboutLabel, { ~0, 0 }, 0);
videoDriver.onChange = [&] { videoDriver.onChange = [&] {
lstring list; lstring list;

View File

@ -11,8 +11,6 @@ struct AdvancedSettings : SettingsLayout {
Label focusPolicyLabel; Label focusPolicyLabel;
HorizontalLayout focusPolicyLayout; HorizontalLayout focusPolicyLayout;
RadioBox focusPolicy[3]; RadioBox focusPolicy[3];
Widget spacer;
Label aboutLabel;
AdvancedSettings(); AdvancedSettings();
}; };