├── sans ├── tests ├── asm │ ├── bin │ │ ├── gang.sav │ │ ├── red-screen.sav │ │ ├── red-screen.defsym │ │ ├── gang.gba │ │ ├── mode4.gba │ │ ├── arm-opcodes.gba │ │ ├── arm-opcodes.sav │ │ ├── red-screen.gba │ │ ├── thumb-simple.gba │ │ ├── thumb-simple.sav │ │ ├── arm-addressing-mode-1.gba │ │ ├── arm-addressing-mode-1.sav │ │ ├── arm-addressing-mode-2.gba │ │ ├── arm-addressing-mode-2.sav │ │ ├── arm-addressing-mode-3.gba │ │ └── arm-addressing-mode-3.sav │ ├── bios.bin │ ├── src │ │ ├── pic.bin │ │ ├── zuyd.bin │ │ ├── bill-cipher.S │ │ ├── pc-relative.S │ │ ├── red-screen.S │ │ ├── texan-frog.S │ │ ├── mode4.S │ │ ├── megalovania.S │ │ ├── arm-addressing-mode-3.S │ │ └── arm-addressing-mode-1.S │ ├── vba.sh │ ├── config.toml │ └── Makefile └── out │ └── catchmain.o ├── source ├── emu │ ├── jumptable │ │ ├── source │ │ │ ├── .gitkeep │ │ │ └── barrel_shifter.d │ │ └── .gitignore │ ├── common │ │ ├── source │ │ │ ├── abstracthw │ │ │ │ ├── gba.d │ │ │ │ ├── cputrace.d │ │ │ │ ├── memory.d │ │ │ │ └── cpu.d │ │ │ ├── util │ │ │ │ ├── pointerpool.d │ │ │ │ ├── ringbuffer.d │ │ │ │ └── package.d │ │ │ └── diag │ │ │ │ └── logger.d │ │ ├── dub.sdl │ │ └── .gitignore │ └── core │ │ ├── hw │ │ ├── ppu │ │ │ ├── point.d │ │ │ ├── layer │ │ │ │ ├── package.d │ │ │ │ ├── layer.d │ │ │ │ └── background_layer.d │ │ │ ├── package.d │ │ │ ├── pixel.d │ │ │ ├── palette.d │ │ │ └── background.d │ │ ├── apu │ │ │ ├── package.d │ │ │ ├── fifo.d │ │ │ └── channels │ │ │ │ ├── tone_channel.d │ │ │ │ ├── wave_channel.d │ │ │ │ └── noise_channel.d │ │ ├── cpu │ │ │ └── package.d │ │ ├── memory │ │ │ ├── package.d │ │ │ └── rom.d │ │ ├── beancomputer.d │ │ ├── keyinput.d │ │ ├── sio │ │ │ └── sio.d │ │ ├── interrupts.d │ │ ├── gba.d │ │ └── gpio │ │ │ └── rtc.d │ │ ├── save │ │ ├── package.d │ │ ├── backups │ │ │ ├── package.d │ │ │ ├── backup.d │ │ │ ├── sram.d │ │ │ ├── eeprom.d │ │ │ └── flash.d │ │ ├── savetype.d │ │ └── savetype_factory.d │ │ ├── shaders │ │ ├── colorcorrection.vert │ │ └── colorcorrection.frag │ │ ├── diag │ │ ├── reformat-gamebean.py │ │ ├── analyze-audio-data.py │ │ ├── reformat-vba-m.py │ │ ├── cputrace.d │ │ ├── log.d │ │ ├── compare-logs.py │ │ └── compare.py │ │ └── scheduler.d ├── ui │ ├── package.d │ ├── scene.d │ ├── reng │ │ ├── package.d │ │ ├── console.d │ │ ├── rengcore.d │ │ ├── emuscene.d │ │ ├── runner.d │ │ ├── gbavideo.d │ │ └── device.d │ ├── core.d │ └── device.d ├── xcat │ ├── gba_bios.x │ ├── .gitignore │ ├── README.md │ └── xcat.c ├── tools │ └── profiler │ │ ├── functiontree.d │ │ └── profiler.d └── app.d ├── media ├── icon.png ├── Roboto.ttf └── DroidSans.ttf ├── imgui └── dimgui │ ├── examples │ ├── colors │ │ ├── dub.selections.json │ │ ├── dub.json │ │ └── window.d │ ├── demo │ │ ├── dub.selections.json │ │ ├── dub.json │ │ └── window.d │ └── memory │ │ ├── dub.selections.json │ │ ├── SciTEDirectory.properties │ │ ├── dub.json │ │ └── window.d │ ├── DroidSans.ttf │ ├── screenshot │ └── imgui.png │ ├── src │ ├── glwtf │ │ ├── glfw.d │ │ ├── exception.d │ │ ├── input.d │ │ └── window.d │ ├── glad │ │ └── gl │ │ │ ├── all.d │ │ │ ├── types.d │ │ │ └── ext.d │ └── imgui │ │ ├── package.d │ │ └── util.d │ ├── .gitignore │ ├── license.txt │ ├── dub.json │ └── readme.md ├── .gitmodules ├── .gitignore ├── dub.selections.json ├── dub.sdl └── README.md /sans: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/asm/bin/gang.sav: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/asm/bin/red-screen.sav: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/asm/bin/red-screen.defsym: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /source/emu/jumptable/source/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /media/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/media/icon.png -------------------------------------------------------------------------------- /media/Roboto.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/media/Roboto.ttf -------------------------------------------------------------------------------- /imgui/dimgui/examples/colors/dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": {} 4 | } -------------------------------------------------------------------------------- /imgui/dimgui/examples/demo/dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": {} 4 | } -------------------------------------------------------------------------------- /media/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/media/DroidSans.ttf -------------------------------------------------------------------------------- /tests/asm/bios.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bios.bin -------------------------------------------------------------------------------- /imgui/dimgui/examples/memory/dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": {} 4 | } 5 | -------------------------------------------------------------------------------- /source/ui/package.d: -------------------------------------------------------------------------------- 1 | module ui; 2 | 3 | public { 4 | import ui.device; 5 | import ui.reng; 6 | } -------------------------------------------------------------------------------- /tests/asm/src/pic.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/src/pic.bin -------------------------------------------------------------------------------- /tests/out/catchmain.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/out/catchmain.o -------------------------------------------------------------------------------- /source/emu/common/source/abstracthw/gba.d: -------------------------------------------------------------------------------- 1 | module abstracthw.gba; 2 | 3 | interface IGBA { 4 | 5 | } -------------------------------------------------------------------------------- /source/xcat/gba_bios.x: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/source/xcat/gba_bios.x -------------------------------------------------------------------------------- /tests/asm/bin/gang.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/gang.gba -------------------------------------------------------------------------------- /tests/asm/bin/mode4.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/mode4.gba -------------------------------------------------------------------------------- /tests/asm/src/zuyd.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/src/zuyd.bin -------------------------------------------------------------------------------- /imgui/dimgui/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/imgui/dimgui/DroidSans.ttf -------------------------------------------------------------------------------- /source/emu/core/hw/ppu/point.d: -------------------------------------------------------------------------------- 1 | module hw.ppu.point; 2 | 3 | struct Point { 4 | int x; 5 | int y; 6 | } -------------------------------------------------------------------------------- /source/xcat/.gitignore: -------------------------------------------------------------------------------- 1 | xcat 2 | *.bin 3 | *.exe 4 | *dSYM 5 | pkg 6 | src 7 | *.pkg.tar.xz 8 | test 9 | gba_bios.bin -------------------------------------------------------------------------------- /tests/asm/bin/arm-opcodes.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/arm-opcodes.gba -------------------------------------------------------------------------------- /tests/asm/bin/arm-opcodes.sav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/arm-opcodes.sav -------------------------------------------------------------------------------- /tests/asm/bin/red-screen.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/red-screen.gba -------------------------------------------------------------------------------- /tests/asm/bin/thumb-simple.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/thumb-simple.gba -------------------------------------------------------------------------------- /tests/asm/bin/thumb-simple.sav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/thumb-simple.sav -------------------------------------------------------------------------------- /imgui/dimgui/screenshot/imgui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/imgui/dimgui/screenshot/imgui.png -------------------------------------------------------------------------------- /source/emu/core/hw/apu/package.d: -------------------------------------------------------------------------------- 1 | module hw.apu; 2 | 3 | public { 4 | import hw.apu.fifo; 5 | import hw.apu.apu; 6 | } 7 | -------------------------------------------------------------------------------- /tests/asm/bin/arm-addressing-mode-1.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/arm-addressing-mode-1.gba -------------------------------------------------------------------------------- /tests/asm/bin/arm-addressing-mode-1.sav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/arm-addressing-mode-1.sav -------------------------------------------------------------------------------- /tests/asm/bin/arm-addressing-mode-2.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/arm-addressing-mode-2.gba -------------------------------------------------------------------------------- /tests/asm/bin/arm-addressing-mode-2.sav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/arm-addressing-mode-2.sav -------------------------------------------------------------------------------- /tests/asm/bin/arm-addressing-mode-3.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/arm-addressing-mode-3.gba -------------------------------------------------------------------------------- /tests/asm/bin/arm-addressing-mode-3.sav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/GameBeanAdvance/HEAD/tests/asm/bin/arm-addressing-mode-3.sav -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "source/ui/imgui/dimgui"] 2 | path = source/ui/imgui/dimgui 3 | url = https://github.com/d-gamedev-team/dimgui 4 | -------------------------------------------------------------------------------- /source/emu/common/source/abstracthw/cputrace.d: -------------------------------------------------------------------------------- 1 | module abstracthw.cputrace; 2 | 3 | interface ICpuTrace { 4 | void print_trace(); 5 | void capture(); 6 | } -------------------------------------------------------------------------------- /source/emu/core/hw/cpu/package.d: -------------------------------------------------------------------------------- 1 | module hw.cpu; 2 | 3 | import std.meta; 4 | 5 | public { 6 | import hw.cpu.arm7tdmi; 7 | import abstracthw.cpu; 8 | } 9 | -------------------------------------------------------------------------------- /source/emu/core/hw/ppu/layer/package.d: -------------------------------------------------------------------------------- 1 | module hw.ppu.layer; 2 | 3 | public { 4 | import hw.ppu.layer.layer; 5 | import hw.ppu.layer.background_layer; 6 | } 7 | -------------------------------------------------------------------------------- /tests/asm/vba.sh: -------------------------------------------------------------------------------- 1 | # get arguments 2 | FILE=$(readlink -f $1) 3 | NUM_INSTRUCTIONS=$2 4 | 5 | # run the modified version of visualboyadvance 6 | visualboyadvance-m $1 $2 -------------------------------------------------------------------------------- /source/ui/scene.d: -------------------------------------------------------------------------------- 1 | module ui.debugger.scene; 2 | 3 | import re; 4 | 5 | class DebugScene : Scene2D { 6 | override void on_start() { 7 | // do nothing 8 | } 9 | } -------------------------------------------------------------------------------- /imgui/dimgui/src/glwtf/glfw.d: -------------------------------------------------------------------------------- 1 | module glwtf.glfw; 2 | 3 | version(DynamicGLFW) { 4 | public import derelict.glfw3.glfw3; 5 | } else { 6 | public import deimos.glfw.glfw3; 7 | } -------------------------------------------------------------------------------- /source/emu/common/dub.sdl: -------------------------------------------------------------------------------- 1 | name "gamebean-common" 2 | description "common" 3 | authors "bean" 4 | copyright "Copyright © 2021, bean" 5 | license "proprietary" 6 | targetType "library" 7 | -------------------------------------------------------------------------------- /source/emu/core/save/package.d: -------------------------------------------------------------------------------- 1 | module save; 2 | 3 | public { 4 | import save.save_detector; 5 | import save.savetype_factory; 6 | import save.savetype; 7 | import save.backups; 8 | } -------------------------------------------------------------------------------- /source/emu/core/hw/memory/package.d: -------------------------------------------------------------------------------- 1 | module hw.memory; 2 | 3 | public { 4 | import hw.memory.memory; 5 | import hw.memory.mmio; 6 | import hw.memory.prefetch_buffer; 7 | import hw.memory.rom; 8 | } -------------------------------------------------------------------------------- /source/emu/common/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /common 6 | *.so 7 | *.dylib 8 | *.dll 9 | *.a 10 | *.lib 11 | *-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | *.a 17 | *.so -------------------------------------------------------------------------------- /source/emu/core/save/backups/package.d: -------------------------------------------------------------------------------- 1 | module save.backups; 2 | 3 | public { 4 | import save.backups.backup; 5 | import save.backups.eeprom; 6 | import save.backups.flash; 7 | import save.backups.sram; 8 | } -------------------------------------------------------------------------------- /source/emu/jumptable/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /jumptable 6 | *.so 7 | *.dylib 8 | *.dll 9 | *.a 10 | *.lib 11 | *-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | *.a 17 | *.so -------------------------------------------------------------------------------- /source/ui/reng/package.d: -------------------------------------------------------------------------------- 1 | module ui.reng; 2 | 3 | public { 4 | import ui.reng.device; 5 | import ui.reng.gbavideo; 6 | import ui.reng.emuscene; 7 | import ui.reng.rengcore; 8 | import ui.reng.runner; 9 | } -------------------------------------------------------------------------------- /tests/asm/src/bill-cipher.S: -------------------------------------------------------------------------------- 1 | .arm 2 | .text 3 | .global main 4 | 5 | main: 6 | mov r7, #0x4000000 @ base I/O address 7 | mov r8, #0x3000000 @ work RAM - audio will be stored here 8 | 9 | infin: 10 | b infin 11 | 12 | audio: -------------------------------------------------------------------------------- /source/xcat/README.md: -------------------------------------------------------------------------------- 1 | 2 | # xcat 3 | 4 | ## usage 5 | 6 | ```sh 7 | gcc -O3 xcat.c -o xcat 8 | ./xcat '!676261' gba_bios.x > gba_bios.bin 9 | ``` 10 | 11 | target hash: `fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570` (`gba_bios.bin`) -------------------------------------------------------------------------------- /source/emu/core/shaders/colorcorrection.vert: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in vec2 vPosition; 4 | out vec2 tex_coord; 5 | 6 | void main() { 7 | gl_Position = vec4(vPosition, 0.0, 1.0); 8 | tex_coord = (vPosition + 1.0) / 2.0; 9 | tex_coord.y *= -1.0; 10 | } -------------------------------------------------------------------------------- /imgui/dimgui/examples/memory/SciTEDirectory.properties: -------------------------------------------------------------------------------- 1 | # This is a SciTE[1] configuration file. You can ignore this file if you're not using SciTE. 2 | # [1] : http://www.scintilla.org/SciTE.html 3 | command.go.subsystem.*.d=0 4 | command.go.*.d=dub -q --root=$(SciteDirectoryHome) 5 | -------------------------------------------------------------------------------- /source/emu/core/hw/ppu/package.d: -------------------------------------------------------------------------------- 1 | module hw.ppu; 2 | 3 | public { 4 | import hw.ppu.background; 5 | import hw.ppu.canvas; 6 | import hw.ppu.pixel; 7 | import hw.ppu.lcd; 8 | import hw.ppu.point; 9 | import hw.ppu.layer; 10 | import hw.ppu.palette; 11 | } 12 | -------------------------------------------------------------------------------- /source/emu/core/hw/ppu/pixel.d: -------------------------------------------------------------------------------- 1 | module hw.ppu.pixel; 2 | 3 | import hw.ppu; 4 | 5 | import ui.device; 6 | 7 | Pixel get_pixel_from_color(ushort color) { 8 | return Pixel((color >> 0) & 0x1F, 9 | (color >> 5) & 0x1F, 10 | (color >> 10) & 0x1F); 11 | } -------------------------------------------------------------------------------- /tests/asm/src/pc-relative.S: -------------------------------------------------------------------------------- 1 | @ from: https://patater.com/gbaguy/gba/ch2.htm 2 | 3 | .arm 4 | .text 5 | .global main 6 | 7 | main: 8 | b test 9 | nop 10 | nop 11 | nop 12 | nop 13 | nop 14 | nop 15 | nop 16 | nop 17 | 18 | test: 19 | ldr r0, [pc, #-0x4] 20 | 21 | infin: 22 | b infin -------------------------------------------------------------------------------- /imgui/dimgui/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /bin/* 3 | /examples/bin/* 4 | /data 5 | /todo/* 6 | *.a 7 | *.c 8 | *.dat 9 | *.h 10 | *.o 11 | *.7z 12 | *.di 13 | *.cpp 14 | *.dat 15 | *.log 16 | *.lib 17 | !implib/*.lib 18 | *.dll 19 | *.exe 20 | *.map 21 | *.obj 22 | *.rar 23 | *.xml 24 | *.deps 25 | *.modTime 26 | .sandbox 27 | .dub 28 | -------------------------------------------------------------------------------- /imgui/dimgui/src/glwtf/exception.d: -------------------------------------------------------------------------------- 1 | module glwtf.exception; 2 | 3 | 4 | class GLFWException : Exception { 5 | this(string s, string f=__FILE__, size_t l=__LINE__) { 6 | super(s, f, l); 7 | } 8 | } 9 | 10 | class WindowException : GLFWException { 11 | this(string s, string f=__FILE__, size_t l=__LINE__) { 12 | super(s, f, l); 13 | } 14 | } -------------------------------------------------------------------------------- /tests/asm/config.toml: -------------------------------------------------------------------------------- 1 | [audio] 2 | m4a_xq_enable = false 3 | interpolate_fifo = true 4 | resampler = "cosine" 5 | [video] 6 | shader_fs = "" 7 | scale = 2 8 | shader_vs = "" 9 | fullscreen = false 10 | 11 | [cartridge] 12 | force_rtc = false 13 | save_type = "detect" 14 | 15 | [general] 16 | sync_to_audio = false 17 | bios_skip = false 18 | bios_path = "bios.bin" 19 | 20 | -------------------------------------------------------------------------------- /source/emu/core/diag/reformat-gamebean.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | lines = open(sys.argv[1], 'r').readlines() 4 | 5 | output = [] 6 | 7 | for line in lines: 8 | elements = line.split(" ") 9 | elements = elements[:-2] 10 | elements.append('\n') 11 | output.append(elements) 12 | 13 | f = open(sys.argv[2], 'w+') 14 | for line in output: 15 | f.write(' '.join(line)) -------------------------------------------------------------------------------- /tests/asm/src/red-screen.S: -------------------------------------------------------------------------------- 1 | @ from: https://patater.com/gbaguy/gba/ch2.htm 2 | 3 | .arm 4 | .text 5 | .global main 6 | 7 | main: 8 | mov r0, #0x4000000 9 | mov r1, #0x400 10 | add r1, r1, #3 11 | str r1, [r0] 12 | 13 | mov r0, #0x6000000 14 | mov r1, #0xFF 15 | mov r2, #0x9600 16 | loop1: 17 | strh r1, [r0], #2 18 | subs r2, r2, #1 19 | bne loop1 20 | 21 | infin: 22 | b infin -------------------------------------------------------------------------------- /source/emu/core/diag/analyze-audio-data.py: -------------------------------------------------------------------------------- 1 | a = open('temp.log', 'r').readlines() 2 | b = list(map(lambda x : x.strip(), a)) 3 | 4 | c = list(filter(lambda x : all(c in 'abcdef0123456789' for c in x), b)) 5 | 6 | from itertools import groupby 7 | d = [list(j) for i, j in groupby(c)] 8 | 9 | g = [] 10 | 11 | for f in d: 12 | g.append((f[0], len(f))) 13 | 14 | print(list(filter(lambda x : (x[1] != 4 and x[1] != 5), g))) 15 | -------------------------------------------------------------------------------- /source/emu/core/hw/ppu/palette.d: -------------------------------------------------------------------------------- 1 | module hw.ppu.palette; 2 | 3 | import hw.ppu; 4 | 5 | import ui.device; 6 | 7 | import std.stdio; 8 | 9 | Pixel[] color_palette = new Pixel[0x200]; 10 | 11 | pragma(inline, true) void set_color(int index, ushort value) { 12 | color_palette[index] = get_pixel_from_color(value); 13 | } 14 | 15 | pragma(inline, true) Pixel get_color(int index) { 16 | return color_palette[index]; 17 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | roms/ 2 | 3 | gba 4 | test 5 | 6 | src/d-jump/.dub 7 | src/d-jump/source/__pycache__ 8 | src/d-jump/parser.out 9 | src/d-jump/parsetab.py 10 | 11 | src/jumptable/jumptable-arm.cpp 12 | src/jumptable/jumptable-arm.h 13 | src/jumptable/jumptable-thumb.cpp 14 | src/jumptable/jumptable-thumb.h 15 | 16 | src/emu/*.log 17 | gamebean-emu 18 | 19 | .dub/ 20 | *.dSYM/ 21 | gba_bios.bin 22 | gba.dSYM/ 23 | .vscode/ 24 | out/ 25 | vgcore.* 26 | *.log 27 | -------------------------------------------------------------------------------- /source/emu/core/hw/ppu/layer/layer.d: -------------------------------------------------------------------------------- 1 | module hw.ppu.layer.layer; 2 | 3 | // import ppu; 4 | // import memory; 5 | 6 | // abstract class Layer { 7 | // public uint priority = -1; 8 | 9 | // protected Memory memory; 10 | 11 | // this(Memory memory) { 12 | // this.memory = memory; 13 | // } 14 | 15 | // abstract Pixel calculate_next_pixel(); 16 | // abstract void skip_pixel(); 17 | // abstract void on_vblank(); 18 | // } -------------------------------------------------------------------------------- /source/emu/core/save/savetype.d: -------------------------------------------------------------------------------- 1 | module save.savetype; 2 | 3 | enum Savetype { 4 | EEPROM_4k, 5 | EEPROM_4k_alt, 6 | EEPROM_8k, 7 | EEPROM_8k_alt, 8 | Flash_512k_Atmel_RTC, 9 | Flash_512k_Atmel, 10 | Flash_512k_SST_RTC, 11 | Flash_512k_SST, 12 | Flash_512k_Panasonic_RTC, 13 | Flash_512k_Panasonic, 14 | Flash_1M_Macronix_RTC, 15 | Flash_1M_Macronix, 16 | Flash_1M_Sanyo_RTC, 17 | Flash_1M_Sanyo, 18 | SRAM_256K, 19 | NONE, 20 | UNKNOWN 21 | } -------------------------------------------------------------------------------- /source/emu/common/source/util/pointerpool.d: -------------------------------------------------------------------------------- 1 | module util.pointerpool; 2 | 3 | final class PointerPool(T, size_t POOL_SIZE) { 4 | alias Pool = T*; 5 | 6 | Pool[] pools; 7 | Pool current_pool; 8 | 9 | size_t pointer_index = 0; 10 | 11 | this() { 12 | create_new_pool(); 13 | } 14 | 15 | void create_new_pool() { 16 | pools ~= new T(POOL_SIZE); 17 | current_pool = pools[pools.length - 1]; 18 | } 19 | 20 | T* get_pointer() { 21 | if (pointer_index > POOL_SIZE) create_new_pool(); 22 | return ¤t_pool[pointer_index++]; 23 | } 24 | } -------------------------------------------------------------------------------- /source/ui/core.d: -------------------------------------------------------------------------------- 1 | module ui.debugger.core; 2 | 3 | import ui.debugger.scene; 4 | 5 | import core.sync.mutex; 6 | 7 | import re; 8 | import re.math; 9 | 10 | final class DebuggerCore : Core { 11 | enum WIDTH = 1000; 12 | enum HEIGHT = 1000; 13 | 14 | Mutex render_mutex; 15 | 16 | this(Mutex render_mutex) { 17 | this.render_mutex = render_mutex; 18 | 19 | super(WIDTH, HEIGHT, "Debugger"); 20 | } 21 | 22 | override void initialize() { 23 | default_resolution = Vector2(1000 / 4, 1000 / 4); 24 | load_scenes([new DebugScene()]); 25 | } 26 | 27 | override protected void draw() { 28 | super.draw(); 29 | } 30 | } -------------------------------------------------------------------------------- /dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": { 4 | "bindbc-loader": "1.0.3", 5 | "bindbc-opengl": "1.0.5", 6 | "bindbc-sdl": "1.0.1", 7 | "bolts": "1.3.1", 8 | "colorize": "1.0.5", 9 | "commandr": "0.2.0", 10 | "ddmp": "0.0.1-0.dev.3", 11 | "derelict-imgui": "0.10.0", 12 | "derelict-util": "2.0.6", 13 | "dray": "5.0.0-r5", 14 | "elf-d": "0.2.5", 15 | "fluent-asserts": "0.13.3", 16 | "gperftools_d": "0.1.0", 17 | "libdparse": "0.14.0", 18 | "minlog": "2.0.0", 19 | "optional": "1.3.0", 20 | "reng": "0.20.0-beta3", 21 | "silly": "1.1.1", 22 | "stdx-allocator": "2.77.5", 23 | "typetips": "0.1.4", 24 | "witchcraft": "0.1.9" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /imgui/dimgui/examples/colors/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "colors", 3 | 4 | "description": "colors", 5 | 6 | "authors": [ 7 | "Adrien Herubel", 8 | "Andrej Mitrovic" 9 | ], 10 | 11 | "homepage": "https://github.com/d-gamedev-team/dimgui", 12 | 13 | "copyright": "Copyright (C) 2012 - 2013 Adrien Herubel", 14 | 15 | "license": "zlib", 16 | 17 | "targetName" : "colors", 18 | 19 | "targetPath" : "../../bin", 20 | 21 | "targetType": "executable", 22 | 23 | "sourcePaths": ["."], 24 | 25 | "mainSourceFile": "colors.d", 26 | 27 | "dependencies": { 28 | "dimgui": {"path": "../../", "version": "~master"} 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /imgui/dimgui/examples/demo/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | 4 | "description": "demo", 5 | 6 | "authors": [ 7 | "Adrien Herubel", 8 | "Andrej Mitrovic" 9 | ], 10 | 11 | "homepage": "https://github.com/d-gamedev-team/dimgui", 12 | 13 | "copyright": "Copyright (C) 2012 - 2013 Adrien Herubel", 14 | 15 | "license": "zlib", 16 | 17 | "targetName" : "demo", 18 | 19 | "targetPath" : "../../bin", 20 | 21 | "targetType": "executable", 22 | 23 | "sourceFiles": ["demo.d", "window.d"], 24 | 25 | "mainSourceFile": "demo.d", 26 | 27 | "dependencies": { 28 | "dimgui": {"path": "../../", "version": "~master"} 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /imgui/dimgui/examples/memory/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memory", 3 | 4 | "description": "memory", 5 | 6 | "authors": [ 7 | "Adrien Herubel", 8 | "Andrej Mitrovic" 9 | ], 10 | 11 | "homepage": "https://github.com/d-gamedev-team/dimgui", 12 | 13 | "copyright": "Copyright (C) 2012 - 2013 Adrien Herubel", 14 | 15 | "license": "zlib", 16 | 17 | "targetName" : "memory", 18 | 19 | "targetPath" : "../../bin", 20 | 21 | "targetType": "executable", 22 | 23 | "sourceFiles": ["memory.d", "window.d"], 24 | 25 | "mainSourceFile": "memory.d", 26 | 27 | "dependencies": { 28 | "dimgui": {"path": "../../", "version": "~master"} 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /source/emu/common/source/util/ringbuffer.d: -------------------------------------------------------------------------------- 1 | module util.ringbuffer; 2 | 3 | final class RingBuffer(T) { 4 | T[] buffer; 5 | int current_index; 6 | 7 | this(int size) { 8 | current_index = 0; 9 | buffer = new T[size]; 10 | } 11 | 12 | void add(T element) { 13 | buffer[current_index] = element; 14 | current_index++; 15 | 16 | if (current_index >= buffer.length) current_index = 0; 17 | } 18 | 19 | T[] get() { 20 | T[] return_buffer = new T[buffer.length]; 21 | 22 | for (int i = 0; i < buffer.length; i++) { 23 | return_buffer[i] = buffer[(i + current_index) % buffer.length]; 24 | } 25 | 26 | return return_buffer; 27 | } 28 | } -------------------------------------------------------------------------------- /tests/asm/src/texan-frog.S: -------------------------------------------------------------------------------- 1 | .arm 2 | .text 3 | .global main 4 | main: 5 | ldr r0, =0x4000000 6 | ldr r1, =0x6000000 7 | ldr r2, yoshi 8 | ldr r3, =0xA0 9 | ldr r4, =0xa600 10 | mov r5, #0x400 11 | add r5, r5, #3 12 | ldr r6, =0x00 13 | 14 | ldr r7, =0x10 15 | str r7, [r0, #4] 16 | 17 | str r5, [r0] 18 | 19 | str r1, [r0 , #b0] 20 | str r2, [r0 , #b4] 21 | strh r3, [r0, #b8] 22 | strh r4, [r0, #ba] 23 | 24 | b loop 25 | ldrh r7, [r0, #BA] 26 | ldrh r8, =#0x7fff 27 | and r7, r7, r8 28 | strh r7, [r0, #BA] 29 | .pool 30 | 31 | loop: 32 | ldr r6, [r0, #6] 33 | cmp r6, #120 34 | bne loop 35 | 36 | yoshi: 37 | .incbin "yoshi_image.bin" -------------------------------------------------------------------------------- /source/ui/reng/console.d: -------------------------------------------------------------------------------- 1 | module ui.reng.console; 2 | 3 | import re.core; 4 | import re.ng.diag.console; 5 | 6 | import hw.gba; 7 | 8 | class EmuConsole { 9 | GBA gba; 10 | 11 | void register() { 12 | Core.inspector_overlay.console.reset(); 13 | 14 | // get the gba instance 15 | gba = Core.jar.resolve!GBA().get; 16 | 17 | // add emu commands 18 | Core.inspector_overlay.console.add_command(ConsoleCommand("gamebean", &cmd_gamebean, "gamebean")); 19 | } 20 | 21 | void unregister() { 22 | Core.inspector_overlay.console.reset(); 23 | } 24 | 25 | void cmd_gamebean(string[] args) { 26 | Core.log.info("GameBeanAdvance Emulator Console"); 27 | Core.log.info("GBA: %s", gba); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/emu/common/source/diag/logger.d: -------------------------------------------------------------------------------- 1 | module diag.logger; 2 | 3 | import std.stdio; 4 | import std.conv; 5 | 6 | import abstracthw.gba; 7 | import abstracthw.cpu; 8 | 9 | import util; 10 | 11 | import abstracthw.cputrace; 12 | 13 | final class Logger { 14 | static Logger instance; 15 | 16 | static Logger singleton(ICpuTrace cpu_trace) { 17 | if (!instance) { 18 | instance = new Logger(cpu_trace); 19 | } 20 | 21 | return instance; 22 | } 23 | 24 | void print() { 25 | cpu_trace.print_trace(); 26 | } 27 | 28 | void capture_cpu() { 29 | cpu_trace.capture(); 30 | } 31 | 32 | private: 33 | this(ICpuTrace cpu_trace) { 34 | this.cpu_trace = cpu_trace; 35 | } 36 | 37 | ICpuTrace cpu_trace; 38 | } -------------------------------------------------------------------------------- /imgui/dimgui/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2010 Mikko Mononen memon@inside.org 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | Permission is granted to anyone to use this software for any purpose, 7 | including commercial applications, and to alter it and redistribute it 8 | freely, subject to the following restrictions: 9 | 1. The origin of this software must not be misrepresented; you must not 10 | claim that you wrote the original software. If you use this software 11 | in a product, an acknowledgment in the product documentation would be 12 | appreciated but is not required. 13 | 2. Altered source versions must be plainly marked as such, and must not be 14 | misrepresented as being the original software. 15 | 3. This notice may not be removed or altered from any source distribution. 16 | -------------------------------------------------------------------------------- /imgui/dimgui/src/glad/gl/all.d: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | OpenGL loader generated by glad 0.1.28 on 11/05/18 20:34:08. 4 | 5 | Language/Generator: D 6 | Specification: gl 7 | APIs: gl=4.6 8 | Profile: compatibility 9 | Extensions: 10 | GL_ARB_debug_output, 11 | GL_KHR_debug 12 | Loader: True 13 | Local files: False 14 | Omit khrplatform: False 15 | Reproducible: False 16 | 17 | Commandline: 18 | --profile="compatibility" --api="gl=4.6" --generator="d" --spec="gl" --extensions="GL_ARB_debug_output,GL_KHR_debug" 19 | Online: 20 | http://glad.dav1d.de/#profile=compatibility&language=d&specification=gl&loader=on&api=gl%3D4.6&extensions=GL_ARB_debug_output&extensions=GL_KHR_debug 21 | */ 22 | 23 | module glad.gl.all; 24 | 25 | 26 | public import glad.gl.funcs; 27 | public import glad.gl.ext; 28 | public import glad.gl.enums; 29 | public import glad.gl.types; 30 | -------------------------------------------------------------------------------- /source/emu/core/diag/reformat-vba-m.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | lines = open(sys.argv[1], 'r').readlines() 4 | 5 | output = [] 6 | 7 | for line in lines: 8 | elements = line.split(" ") 9 | if elements[0] == 'THM': 10 | elements[0] = "THUMB" 11 | elements[1] = ' ' + elements[1][2:].rjust(4, '0') 12 | elements[17] = hex(int(elements[17], 16) - 2)[2:] 13 | else: # elements[0] == 'ARM' 14 | elements[0] = 'ARM ' 15 | elements[1] = elements[1][2:].rjust(8, '0') 16 | elements[17] = hex(int(elements[17], 16) - 4)[2:] 17 | 18 | for i in range(2, len(elements)): 19 | elements[i] = elements[i].rjust(8, '0') 20 | 21 | elements.insert(2, '|') 22 | elements = elements[:-2] 23 | 24 | elements = elements[0:2] + list(map(lambda a : a.strip(), elements[2:])) 25 | elements.append(' \n') 26 | output.append(elements) 27 | 28 | f = open(sys.argv[2], 'w+') 29 | for line in output: 30 | f.write(' '.join(line)) -------------------------------------------------------------------------------- /source/emu/core/save/backups/backup.d: -------------------------------------------------------------------------------- 1 | module save.backups.backup; 2 | 3 | import std.mmfile; 4 | 5 | class Backup { 6 | uint read_word (uint address) { return 0x0;} 7 | ushort read_half (uint address) { return 0x0;} 8 | ubyte read_byte (uint address) { return 0x0;} 9 | void write_word (uint address, uint data) { return; } 10 | void write_half(uint address, ushort data) { return; } 11 | void write_byte (uint address, ubyte data) { return; } 12 | 13 | ubyte[] serialize() { return [];} 14 | void deserialize(ubyte[] data) { return; } 15 | 16 | BackupType get_backup_type() { return BackupType.NONE; } 17 | int get_backup_size() { return 0; } 18 | 19 | MmFile backup_file; 20 | void set_backup_file(MmFile backup_file) { 21 | this.backup_file = backup_file; 22 | } 23 | } 24 | 25 | enum BackupType { 26 | FLASH, 27 | SRAM, 28 | EEPROM, 29 | NONE 30 | } -------------------------------------------------------------------------------- /source/emu/core/shaders/colorcorrection.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | in vec2 fragTexCoord; 4 | uniform sampler2D texture0; 5 | 6 | // algorithm developed by Near and Talarubi: 7 | // https://near.sh/articles/video/color-emulation 8 | vec4 color_correct(vec4 color) { 9 | float lcdGamma = 4.0, outGamma = 2.2; 10 | float lb = pow(color.b / 31.0, lcdGamma); 11 | float lg = pow(color.g / 31.0, lcdGamma); 12 | float lr = pow(color.r / 31.0, lcdGamma); 13 | 14 | vec3 color_without_gamma = vec3( 15 | pow(( 0.0 * lb + 50.0 * lg + 255.0 * lr) / 255.0, 1.0 / outGamma) * (65535.0 / 280.0), 16 | pow(( 30.0 * lb + 230.0 * lg + 10.0 * lr) / 255.0, 1.0 / outGamma) * (65535.0 / 280.0), 17 | pow((220.0 * lb + 10.0 * lg + 50.0 * lr) / 255.0, 1.0 / outGamma) * (65535.0 / 280.0) 18 | ); 19 | 20 | vec3 color_with_gamma = pow(color_without_gamma, vec3(1.0 / outGamma)); 21 | return vec4(color_with_gamma, 1.0); 22 | } 23 | 24 | out vec4 color_out; 25 | 26 | void main() { 27 | color_out = color_correct(texture(texture0, fragTexCoord.xy)); 28 | } -------------------------------------------------------------------------------- /imgui/dimgui/src/imgui/package.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2010 Mikko Mononen memon@inside.org 3 | * 4 | * This software is provided 'as-is', without any express or implied 5 | * warranty. In no event will the authors be held liable for any damages 6 | * arising from the use of this software. 7 | * Permission is granted to anyone to use this software for any purpose, 8 | * including commercial applications, and to alter it and redistribute it 9 | * freely, subject to the following restrictions: 10 | * 1. The origin of this software must not be misrepresented; you must not 11 | * claim that you wrote the original software. If you use this software 12 | * in a product, an acknowledgment in the product documentation would be 13 | * appreciated but is not required. 14 | * 2. Altered source versions must be plainly marked as such, and must not be 15 | * misrepresented as being the original software. 16 | * 3. This notice may not be removed or altered from any source distribution. 17 | */ 18 | module imgui; 19 | 20 | public 21 | { 22 | import imgui.api; 23 | } 24 | -------------------------------------------------------------------------------- /source/emu/core/hw/beancomputer.d: -------------------------------------------------------------------------------- 1 | module hw.beancomputer; 2 | 3 | import std.stdio; 4 | 5 | final class BeanComputer { 6 | enum SUPPORT_MAGIC = 0xBEA7; 7 | enum SUPPORT_VERSION = 1; 8 | 9 | this() { 10 | this.keyinput = 0x0FFF_FFFF_FFFF_FFFF; 11 | } 12 | 13 | private ulong keyinput; 14 | 15 | ubyte read_SUPPORT(int target_byte) { 16 | final switch (target_byte) { 17 | case 0b00: return (SUPPORT_VERSION >> 0) & 0xFF; 18 | case 0b01: return (SUPPORT_VERSION >> 8) & 0xFF; 19 | case 0b10: return (SUPPORT_MAGIC >> 0) & 0xFF; 20 | case 0b11: return (SUPPORT_MAGIC >> 8) & 0xFF; 21 | } 22 | } 23 | 24 | ubyte read_KEYBOARD1(int target_byte) { 25 | return (keyinput >> (8 * target_byte)) & 0xFF; 26 | } 27 | 28 | ubyte read_KEYBOARD2(int target_byte) { 29 | return (keyinput >> (8 * target_byte + 32)) & 0xFF; 30 | } 31 | 32 | void set_key(int code, bool pressed) { 33 | if (pressed) keyinput &= ~(0b1 << code); 34 | else keyinput |= (0b1 << code); 35 | } 36 | } -------------------------------------------------------------------------------- /source/ui/reng/rengcore.d: -------------------------------------------------------------------------------- 1 | module ui.reng.rengcore; 2 | 3 | import hw.gba; 4 | 5 | import ui.reng; 6 | 7 | import re; 8 | import re.math; 9 | 10 | class RengCore : Core { 11 | GBA gba; 12 | int width; 13 | int height; 14 | int screen_scale; 15 | 16 | this(GBA gba, int screen_scale) { 17 | this.gba = gba; 18 | this.width = 240 * screen_scale; 19 | this.height = 160 * screen_scale; 20 | this.screen_scale = screen_scale; 21 | 22 | super(width, height, "GameBeanAdvance"); 23 | } 24 | 25 | override void initialize() { 26 | // store gba in jar 27 | Core.jar.register(gba); 28 | 29 | // use custom console 30 | this.inspector_overlay.enabled = true; 31 | this.inspector_overlay.console.reset(); 32 | 33 | default_resolution = Vector2(width, height); 34 | content.paths ~= ["../content/", "content/"]; 35 | 36 | load_scenes([new EmuScene(screen_scale)]); 37 | } 38 | 39 | pragma(inline, true) { 40 | void update_pub() { 41 | update(); 42 | } 43 | 44 | void draw_pub() { 45 | draw(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /dub.sdl: -------------------------------------------------------------------------------- 1 | name "gamebean-emu" 2 | description "gamebeanadvance emu" 3 | authors "bean" 4 | copyright "Copyright © 2021, bean" 5 | license "proprietary" 6 | dependency "elf-d" version="~>0.2.5" 7 | dependency "bindbc-opengl" version="~>1.0.0" 8 | dependency "silly" version="~>1.1.1" 9 | dependency "reng" version="~>0.20.0-beta3" 10 | dependency "bindbc-sdl" version="~>1.0.1" 11 | dependency "commandr" version="~>0.2.0" 12 | targetType "executable" 13 | 14 | subConfiguration "reng" "lib-minimal" 15 | 16 | versions "GL_AllowDeprecated" 17 | 18 | configuration "default" { 19 | targetType "executable" 20 | versions "default" 21 | } 22 | 23 | configuration "gperf" { 24 | dependency "gperftools_d" version="~>0.1.0" 25 | targetType "executable" 26 | dflags "-O1" "--boundscheck=off" 27 | versions "gperf" 28 | } 29 | 30 | configuration "debug" { 31 | dependency "derelict-imgui" version="~>0.10.0" 32 | targetType "executable" 33 | libs "cimgui" 34 | versions "Imgui" 35 | } 36 | 37 | configuration "diag" { 38 | targetType "executable" 39 | versions "diag" 40 | } 41 | 42 | configuration "optimize" { 43 | targetType "executable" 44 | dflags "--boundscheck=off" "-O3" 45 | } 46 | -------------------------------------------------------------------------------- /source/ui/reng/emuscene.d: -------------------------------------------------------------------------------- 1 | module ui.reng.emuscene; 2 | 3 | import ui.reng; 4 | import ui.reng.console; 5 | 6 | import re; 7 | import re.gfx; 8 | import re.gfx.effects.frag; 9 | import re.util.hotreload; 10 | 11 | class EmuScene : Scene2D { 12 | int screen_scale; 13 | EmuConsole console; 14 | 15 | this(int screen_scale) { 16 | this.screen_scale = screen_scale; 17 | super(); 18 | 19 | console = new EmuConsole(); 20 | } 21 | 22 | override void on_start() { 23 | auto gba_screen = create_entity("gba_display"); 24 | auto gba_video = gba_screen.add_component(new GBAVideo(screen_scale)); 25 | Core.jar.register(gba_video); 26 | 27 | auto draw_shd_path = "source/emu/core/shaders/colorcorrection.frag"; 28 | auto shd_draw = new FragEffect(this, new ReloadableShader(null, draw_shd_path)); 29 | auto draw_p = new PostProcessor(resolution, shd_draw); 30 | postprocessors ~= draw_p; 31 | 32 | // set up console 33 | console.register(); 34 | } 35 | 36 | override void on_unload() { 37 | console.unregister(); 38 | } 39 | 40 | override void update() { 41 | super.update(); 42 | } 43 | } -------------------------------------------------------------------------------- /imgui/dimgui/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dimgui", 3 | 4 | "description": "dimgui is a D port of the imgui OpenGL GUI library", 5 | 6 | "authors": [ 7 | "Mikko Mononen", 8 | "Adrien Herubel", 9 | "Andrej Mitrovic" 10 | ], 11 | 12 | "homepage": "https://github.com/d-gamedev-team/dimgui", 13 | 14 | "copyright": "Copyright (c) 2009-2010 Mikko Mononen memon@inside.org", 15 | 16 | "license": "zlib", 17 | 18 | "targetName": "imgui", 19 | 20 | "targetType": "staticLibrary", 21 | 22 | "targetPath" : "bin", 23 | 24 | "sourcePaths": [ 25 | "src" 26 | ], 27 | 28 | "libs-posix": [ 29 | "dl", 30 | "glfw" 31 | ], 32 | 33 | "libs-linux": [ 34 | "GL", 35 | "Xrandr", 36 | "Xext", 37 | "Xxf86vm", 38 | "Xi", 39 | "Xcursor", 40 | "Xinerama", 41 | "X11" 42 | ], 43 | 44 | "libs-windows": ["glfw3dll"], 45 | 46 | "lflags-windows-x86": ["/LIBPATH:$PACKAGE_DIR\\lib\\x86"], 47 | "lflags-windows-x86_64": ["/LIBPATH:$PACKAGE_DIR\\lib\\x86-64"], 48 | 49 | "subPackages": [ 50 | "examples/colors", 51 | "examples/demo", 52 | "examples/memory" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /source/emu/core/diag/cputrace.d: -------------------------------------------------------------------------------- 1 | module diag.cputrace; 2 | 3 | import hw.cpu; 4 | 5 | import abstracthw.cputrace; 6 | 7 | import util.ringbuffer; 8 | 9 | import std.stdio; 10 | import std.format; 11 | 12 | final class CpuTrace : ICpuTrace { 13 | ARM7TDMI cpu; 14 | RingBuffer!CpuState ringbuffer; 15 | 16 | this(ARM7TDMI cpu, int length) { 17 | this.cpu = cpu; 18 | this.ringbuffer = new RingBuffer!CpuState(length); 19 | } 20 | 21 | void capture() { 22 | ringbuffer.add(get_cpu_state(cpu)); 23 | } 24 | 25 | void print_trace() { 26 | CpuState[] trace = ringbuffer.get(); 27 | for (int i = 0; i < trace.length; i++) { 28 | writef("[%04d] ", trace.length - i); 29 | 30 | if (trace[i].instruction_set == InstructionSet.THUMB) { 31 | write("THUMB "); 32 | write(format("%04x | ", trace[i].opcode)); 33 | } else { 34 | write("ARM "); 35 | write(format("%08x | ", trace[i].opcode)); 36 | } 37 | 38 | for (int j = 0; j < 16; j++) 39 | write(format("%08x ", trace[i].regs[j])); 40 | 41 | write(format(" | %08x", trace[i].mode)); 42 | writeln(); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /source/ui/device.d: -------------------------------------------------------------------------------- 1 | module ui.device; 2 | 3 | import hw.gba; 4 | import hw.keyinput; 5 | 6 | alias SetKey = void delegate(GBAKeyVanilla key, bool value); 7 | alias UpdateTouchscreenPosition = void delegate(int x_position, int y_position); 8 | 9 | struct Sample { 10 | short L; 11 | short R; 12 | } 13 | 14 | struct Pixel { 15 | int r; 16 | int g; 17 | int b; 18 | } 19 | 20 | abstract class MultiMediaDevice { 21 | SetKey update_key; 22 | UpdateTouchscreenPosition update_touchscreen_position; 23 | 24 | final void set_update_key_callback(SetKey update_key) { 25 | this.update_key = update_key; 26 | } 27 | 28 | final void set_update_touchscreen_position(UpdateTouchscreenPosition update_touchscreen_position) { 29 | this.update_touchscreen_position = update_touchscreen_position; 30 | } 31 | 32 | abstract { 33 | void update(); 34 | void draw(); 35 | bool should_cycle_gba(); 36 | void update_rom_title(string title); 37 | 38 | // video stuffs 39 | void present_videobuffers(Pixel[160][240] buffer); 40 | void set_fps(int fps); 41 | void update_icon(Pixel[32][32] texture); 42 | 43 | // audio stuffs 44 | void push_sample(Sample s); 45 | 46 | // input stuffs 47 | void handle_input(); 48 | } 49 | 50 | bool should_fast_forward(); 51 | } -------------------------------------------------------------------------------- /imgui/dimgui/src/imgui/util.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Andrej Mitrovic 2014. 3 | * Distributed under the Boost Software License, Version 1.0. 4 | * (See accompanying file LICENSE_1_0.txt or copy at 5 | * http://www.boost.org/LICENSE_1_0.txt) 6 | */ 7 | module imgui.util; 8 | 9 | /** 10 | Note: This unfortunately doesn't work with more complex structures 11 | due to a DMD bug with an infinite loop problem. This isn't reported yet. 12 | */ 13 | 14 | import std.range; 15 | import std.stdio; 16 | 17 | auto ref fieldRange(S, T)(auto ref T sym) 18 | { 19 | static if (is(T == struct) && !is(T == S)) 20 | return fieldRange!S(sym.tupleof); 21 | else 22 | return only(sym); 23 | } 24 | 25 | auto ref fieldRange(S, T...)(auto ref T syms) if (T.length > 1) 26 | { 27 | return chain(fieldRange!S(syms[0]), 28 | fieldRange!S(syms[1 .. $])); 29 | } 30 | 31 | auto addrFieldRange(S, T)(ref T sym) 32 | { 33 | static if (is(T == struct) && !is(T == S)) 34 | return addrFieldRange!S(sym.tupleof); 35 | else 36 | return only(&sym); 37 | } 38 | 39 | auto addrFieldRange(S, T...)(ref T syms) if (T.length > 1) 40 | { 41 | return chain(addrFieldRange!S(syms[0]), 42 | addrFieldRange!S(syms[1 .. $])); 43 | } 44 | 45 | auto refFieldRange(S, T)(ref T sym) 46 | { 47 | alias Type = typeof(sym.fieldRange!S.front); 48 | 49 | static ref Type getRef(Type* elem) { return *elem; } 50 | 51 | return sym.addrFieldRange!S.map!getRef; 52 | } 53 | -------------------------------------------------------------------------------- /source/emu/core/diag/log.d: -------------------------------------------------------------------------------- 1 | module diag.log; 2 | 3 | import std.stdio; 4 | import hw.gba; 5 | 6 | enum LogSource { 7 | INIT, 8 | MEMORY, 9 | LCD, 10 | DEBUG, 11 | SAVE 12 | } 13 | 14 | GBA g_log_gba; 15 | 16 | static immutable ulong logsource_padding = get_largest_logsource_length!(); 17 | 18 | static ulong get_largest_logsource_length()(){ 19 | import std.algorithm; 20 | import std.conv; 21 | 22 | ulong largest_logsource_length = 0; 23 | foreach (source; LogSource.min .. LogSource.max) { 24 | largest_logsource_length = max(to!string(source).length, largest_logsource_length); 25 | } 26 | 27 | return largest_logsource_length; 28 | } 29 | 30 | // thanks https://github.com/dlang/phobos/blob/4239ed8ebd3525206453784908f5d37c82d338ee/std/outbuffer.d 31 | void log(LogSource log_source, Char, A...)(scope const(Char)[] fmt, A args) { 32 | // import ui.device.video.sdl.sdl; 33 | import std.format.write : formattedWrite; 34 | import std.conv; 35 | 36 | // static if (log_source == LogSource.DEBUG) return; 37 | // else { 38 | // ulong timestamp = g_log_gba ? g_log_gba.scheduler.get_current_time_relative_to_cpu() : 0; 39 | // writef("[%016x] %s: ", timestamp, pad_string_right!(to!string(log_source), logsource_padding)); 40 | // writefln(fmt, args); 41 | // } 42 | } 43 | 44 | static string pad_string_right(string s, ulong pad)() { 45 | import std.array; 46 | 47 | static assert(s.length <= pad); 48 | return s ~ (replicate(" ", pad - s.length)); 49 | } -------------------------------------------------------------------------------- /source/emu/core/hw/memory/rom.d: -------------------------------------------------------------------------------- 1 | module hw.memory.rom; 2 | 3 | import std.stdio; 4 | import util; 5 | 6 | final class ROM { 7 | ushort[] data; 8 | uint rom_mask; 9 | bool mirrored; 10 | 11 | this(ubyte[] rom_data) { 12 | ulong num_bytes = rom_data.length; 13 | ulong num_halfs = num_bytes / 2; 14 | 15 | uint rom_mask_size = 0; 16 | while (num_halfs > 1) { 17 | num_halfs >>= 1; 18 | rom_mask_size++; 19 | } 20 | 21 | uint rom_length = 1 << (rom_mask_size + 1); 22 | this.rom_mask = rom_length - 1; 23 | data = new ushort[rom_length]; 24 | 25 | // this.data = new ushort[this.rom_mask + 1]; 26 | for (int i = 0; i < rom_data.length / 2; i++) { 27 | this.data[i] = (cast(ushort[]) rom_data)[i]; 28 | } 29 | 30 | this.mirrored = false; 31 | 32 | } 33 | 34 | ushort read(uint address) { 35 | if (mirrored) { 36 | return data[address & rom_mask]; 37 | } else { 38 | if (address & ~rom_mask) { 39 | return calculate_unmapped_rom_value(address); 40 | } else { 41 | return data[address]; 42 | } 43 | } 44 | } 45 | 46 | // https://problemkaputt.de/gbatek.htm#gbaunpredictablethings 47 | pragma(inline, true) ushort calculate_unmapped_rom_value(uint address) { 48 | return address & 0xFFFF; 49 | } 50 | 51 | ubyte[] get_bytes() { 52 | return cast(ubyte[]) data; 53 | } 54 | } -------------------------------------------------------------------------------- /imgui/dimgui/src/glad/gl/types.d: -------------------------------------------------------------------------------- 1 | module glad.gl.types; 2 | 3 | 4 | alias GLvoid = void; 5 | alias GLintptr = ptrdiff_t; 6 | alias GLsizei = int; 7 | alias GLchar = char; 8 | alias GLcharARB = byte; 9 | alias GLushort = ushort; 10 | alias GLint64EXT = long; 11 | alias GLshort = short; 12 | alias GLuint64 = ulong; 13 | alias GLhalfARB = ushort; 14 | alias GLubyte = ubyte; 15 | alias GLdouble = double; 16 | alias GLhandleARB = uint; 17 | alias GLint64 = long; 18 | alias GLenum = uint; 19 | alias GLeglImageOES = void*; 20 | alias GLintptrARB = ptrdiff_t; 21 | alias GLsizeiptr = ptrdiff_t; 22 | alias GLint = int; 23 | alias GLboolean = ubyte; 24 | alias GLbitfield = uint; 25 | alias GLsizeiptrARB = ptrdiff_t; 26 | alias GLfloat = float; 27 | alias GLuint64EXT = ulong; 28 | alias GLclampf = float; 29 | alias GLbyte = byte; 30 | alias GLclampd = double; 31 | alias GLuint = uint; 32 | alias GLvdpauSurfaceNV = ptrdiff_t; 33 | alias GLfixed = int; 34 | alias GLhalf = ushort; 35 | alias GLclampx = int; 36 | alias GLhalfNV = ushort; 37 | struct ___GLsync; alias __GLsync = ___GLsync*; 38 | alias GLsync = __GLsync*; 39 | struct __cl_context; alias _cl_context = __cl_context*; 40 | struct __cl_event; alias _cl_event = __cl_event*; 41 | extern(System) { 42 | alias GLDEBUGPROC = void function(GLenum, GLenum, GLuint, GLenum, GLsizei, in GLchar*, GLvoid*); 43 | alias GLDEBUGPROCARB = GLDEBUGPROC; 44 | alias GLDEBUGPROCKHR = GLDEBUGPROC; 45 | alias GLDEBUGPROCAMD = void function(GLuint, GLenum, GLenum, GLsizei, in GLchar*, GLvoid*); 46 | } 47 | -------------------------------------------------------------------------------- /source/ui/reng/runner.d: -------------------------------------------------------------------------------- 1 | module ui.reng.runner; 2 | 3 | import ui.device; 4 | import ui.reng; 5 | 6 | import hw.gba; 7 | 8 | import std.datetime.stopwatch; 9 | 10 | import core.sync.mutex; 11 | 12 | final class Runner { 13 | GBA gba; 14 | bool fast_forward; 15 | 16 | Mutex should_cycle_gba_mutex; 17 | bool should_cycle_gba; 18 | 19 | MultiMediaDevice frontend; 20 | 21 | size_t sync_to_audio_lower; 22 | size_t sync_to_audio_upper; 23 | 24 | StopWatch stopwatch; 25 | 26 | bool running; 27 | 28 | int fps = 0; 29 | 30 | this(GBA gba, MultiMediaDevice frontend) { 31 | this.gba = gba; 32 | 33 | this.should_cycle_gba_mutex = new Mutex(); 34 | 35 | this.frontend = frontend; 36 | 37 | this.should_cycle_gba = true; 38 | this.running = true; 39 | } 40 | 41 | void tick() { 42 | if (stopwatch.peek.total!"msecs" > 1000) { 43 | frontend.set_fps(fps); 44 | stopwatch.reset(); 45 | fps = 0; 46 | } 47 | 48 | frontend.update(); 49 | frontend.draw(); 50 | } 51 | 52 | void run() { 53 | stopwatch = StopWatch(AutoStart.yes); 54 | 55 | while (running) { 56 | if (frontend.should_cycle_gba() || frontend.should_fast_forward()) { 57 | if (gba.enabled) { 58 | gba.cycle_at_least_n_times(16_780_000 / 60); 59 | } 60 | 61 | fps++; 62 | } 63 | 64 | tick(); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /source/ui/reng/gbavideo.d: -------------------------------------------------------------------------------- 1 | module ui.reng.gbavideo; 2 | 3 | import re; 4 | import re.math; 5 | import re.gfx; 6 | 7 | import std.format; 8 | import std.string; 9 | 10 | import raylib; 11 | 12 | enum SCREEN_SEPARATION_HEIGHT = 0; 13 | 14 | class GBAVideo : Component, Updatable, Renderable2D { 15 | int screen_scale; 16 | 17 | RenderTarget render_target_top; 18 | 19 | uint[240 * 160] videobuffer; 20 | 21 | this(int screen_scale) { 22 | this.screen_scale = screen_scale; 23 | 24 | render_target_top = RenderExt.create_render_target( 25 | 240, 26 | 160 27 | ); 28 | } 29 | 30 | override void setup() { 31 | 32 | } 33 | 34 | void update() { 35 | 36 | } 37 | 38 | void update_icon(uint[32 * 32] icon_bitmap) { 39 | 40 | } 41 | 42 | void update_title(string title) { 43 | SetWindowTitle(toStringz(title)); 44 | } 45 | 46 | void render() { 47 | UpdateTexture(render_target_top.texture, cast(const void*) videobuffer); 48 | 49 | raylib.DrawTexturePro( 50 | render_target_top.texture, 51 | Rectangle(0, 0, 256, 192), 52 | Rectangle(0, 0, 256 * screen_scale, 192 * screen_scale), 53 | Vector2(0, 0), 54 | 0, 55 | Colors.WHITE 56 | ); 57 | } 58 | 59 | void debug_render() { 60 | // leave this blank 61 | } 62 | 63 | @property Rectangle bounds() { 64 | // if we're not using culling who cares 65 | // problem solved lol 66 | return Rectangle(0, 0, 1000, 1080); 67 | } 68 | } -------------------------------------------------------------------------------- /source/emu/core/hw/apu/fifo.d: -------------------------------------------------------------------------------- 1 | module hw.apu.fifo; 2 | 3 | import std.stdio; 4 | 5 | final class Fifo(T) { 6 | 7 | // size must be power of 2 8 | this(int size, T reset_value) { 9 | this.fifo_data = new T[size]; 10 | this.fifo_data[0..size] = reset_value; 11 | 12 | 13 | this.offset_mask = size - 1; 14 | this.reset_value = reset_value; 15 | 16 | reset(); 17 | } 18 | 19 | // pushing to a full fifo does nothing 20 | void push(T new_data) { 21 | if (size == fifo_data.length) { 22 | return; 23 | } 24 | 25 | size++; 26 | fifo_data[push_offset] = new_data; 27 | push_offset++; 28 | push_offset &= offset_mask; 29 | } 30 | 31 | // popping from an empty fifo returns null 32 | T pop() { 33 | if (size == 0) 34 | return reset_value; 35 | 36 | size--; 37 | T return_value = fifo_data[pop_offset]; 38 | fifo_data[pop_offset] = reset_value; 39 | pop_offset++; 40 | pop_offset &= offset_mask; 41 | return return_value; 42 | } 43 | 44 | bool is_full() { 45 | return size == fifo_data.length; 46 | } 47 | 48 | void reset() { 49 | this.fifo_data[0..fifo_data.length] = reset_value; 50 | 51 | this.push_offset = 0; 52 | this.pop_offset = 0; 53 | this.size = 0; 54 | } 55 | 56 | public: 57 | uint size; 58 | 59 | private: 60 | T[] fifo_data; 61 | uint push_offset; 62 | uint pop_offset; 63 | 64 | uint offset_mask; 65 | T reset_value; 66 | } -------------------------------------------------------------------------------- /source/emu/core/diag/compare-logs.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import itertools 3 | 4 | expected = open(sys.argv[1], 'r').readlines() 5 | actual = open(sys.argv[2], 'r').readlines() 6 | 7 | # do a pc analysis. easiest bugs to find are the ones where we simply dont run a set of code that 8 | # the expected output shows should be run. 9 | expected_pcs = list(map(lambda x : x.strip().split(" ")[-1], expected)) 10 | actual_pcs = list(map(lambda x : x.strip().split(" ")[-1], actual)) 11 | 12 | expected_pcs = list(set(expected_pcs)) 13 | actual_pcs = list(set(actual_pcs)) 14 | 15 | print("Running PC Analysis...") 16 | pcs_not_found = [] 17 | for pc in expected_pcs: 18 | if not pc in actual_pcs: 19 | # personal constraints: 20 | if (int(pc, 16) & 0xFF000000) == 0x00000000: 21 | continue 22 | 23 | if (int(pc, 16) & 0xFF000000) == 0x03000000: 24 | continue 25 | 26 | pcs_not_found.append(int(pc, 16)) 27 | 28 | pcs_not_found.sort() 29 | 30 | ranged_pcs_not_found = [] 31 | 32 | previous_pc = -1 33 | start_range = -1 34 | range_len = -1 35 | for pc in pcs_not_found: 36 | if pc == previous_pc + 2 or pc == previous_pc + 4 or pc == previous_pc: 37 | range_len += (pc - previous_pc) 38 | else: 39 | ranged_pcs_not_found.append((start_range, range_len)) 40 | 41 | range_len = 0 42 | start_range = pc 43 | 44 | previous_pc = pc 45 | pcs_not_found = pcs_not_found[1:] 46 | 47 | print("\nResults:") 48 | for r in ranged_pcs_not_found: 49 | start = '{0:#010x}'.format(r[0]) 50 | end = '{0:#010x}'.format(r[0] + r[1]) 51 | 52 | print("PCs in Range: {} to {} were not found.".format(start, end)) 53 | 54 | print("PC Analysis Complete") -------------------------------------------------------------------------------- /source/emu/core/save/savetype_factory.d: -------------------------------------------------------------------------------- 1 | module save.savetype_factory; 2 | 3 | import save; 4 | import util; 5 | 6 | public Backup create_savetype(Savetype savetype) { 7 | switch (savetype) { 8 | case Savetype.Flash_512k_Atmel_RTC: return new Flash(65536, false, 1, 0x32, 0x1B); 9 | case Savetype.Flash_512k_Atmel: return new Flash(65536, false, 1, 0x32, 0x1B); 10 | case Savetype.Flash_512k_SST_RTC: return new Flash(65536, false, 1, 0x32, 0x1B); 11 | case Savetype.Flash_512k_SST: return new Flash(65536, false, 1, 0x32, 0x1B); 12 | case Savetype.Flash_512k_Panasonic_RTC: return new Flash(65536, false, 1, 0x32, 0x1B); 13 | case Savetype.Flash_512k_Panasonic: return new Flash(65536, false, 1, 0x32, 0x1B); 14 | case Savetype.Flash_1M_Macronix_RTC: return new Flash(65536 * 2, true, 2, 0x62, 0x13); 15 | case Savetype.Flash_1M_Macronix: return new Flash(65536 * 2, true, 2, 0x62, 0x13); 16 | case Savetype.Flash_1M_Sanyo_RTC: return new Flash(65536 * 2, true, 2, 0x62, 0x13); 17 | case Savetype.Flash_1M_Sanyo: return new Flash(65536 * 2, true, 2, 0x62, 0x13); 18 | case Savetype.SRAM_256K: return new SRAM (32768); 19 | case Savetype.EEPROM_4k: return new EEPROM(65536); 20 | case Savetype.EEPROM_4k_alt: return new EEPROM(65536); 21 | case Savetype.EEPROM_8k: return new EEPROM(512); 22 | case Savetype.EEPROM_8k_alt: return new EEPROM(512); 23 | default: return new Backup(); // this will never trigger when all saves are finally implemented 24 | } 25 | } -------------------------------------------------------------------------------- /source/emu/core/hw/ppu/background.d: -------------------------------------------------------------------------------- 1 | module hw.ppu.background; 2 | 3 | import hw.ppu; 4 | 5 | struct Background { 6 | int id; 7 | int priority; // 0 - 3 8 | int character_base_block; // 0 - 3 (units of 16 KBytes) 9 | bool is_mosaic; 10 | bool doesnt_use_color_palettes; // 0 = 16/16, 1 = 256/1 11 | int screen_base_block; // 0 - 31 (units of 2 KBytes) 12 | bool does_display_area_overflow; 13 | int screen_size; // 0 - 3 14 | 15 | // these aren't used (except in NDS mode, according to GBATek) 16 | // yet, the GBA still saves their value and returns them. therefore 17 | // my emulator must do so too. 18 | uint bgcnt_bits_4_and_5; 19 | 20 | ushort x_offset; 21 | ushort y_offset; 22 | bool enabled; 23 | 24 | ushort transformation_dx; 25 | ushort transformation_dmx; 26 | ushort transformation_dy; 27 | ushort transformation_dmy; 28 | uint reference_x; 29 | uint reference_y; 30 | 31 | int x_offset_rotation; 32 | int y_offset_rotation; 33 | 34 | long internal_reference_x; 35 | long internal_reference_y; 36 | 37 | BackgroundMode mode; 38 | 39 | Layer layer; 40 | 41 | short[4] p; 42 | } 43 | 44 | enum BackgroundMode { 45 | TEXT, 46 | ROTATION_SCALING, 47 | NONE 48 | } 49 | 50 | static __gshared Background[] backgrounds = [ 51 | Background(), 52 | Background(), 53 | Background(), 54 | Background() 55 | ]; 56 | 57 | 58 | // the backgrounds may get sorted by priority at some point 59 | static this() { 60 | for (int i = 0; i < 4; i++) { 61 | backgrounds[i].id = i; 62 | backgrounds[i].p[0] = 0x100; 63 | backgrounds[i].p[3] = 0x100; 64 | } 65 | } -------------------------------------------------------------------------------- /source/emu/core/diag/compare.py: -------------------------------------------------------------------------------- 1 | mine_t = open("mine.log").read().split("\n") 2 | nba_t = open("nba.log") .read().split("\n") 3 | 4 | # look for pc 5 | pc_start = "80002e8" 6 | pc_end = "80002ea" 7 | 8 | def get_next_line_with_pc_start(l, start_index): 9 | i = start_index 10 | while i < len(l): 11 | try: 12 | if l[i].split(" ")[-2] == pc_start: 13 | return i 14 | i += 1 15 | except: 16 | print(f"fuck word {i}") 17 | print(l[i]) 18 | exit(-1) 19 | return -1 20 | 21 | current_mine_index = 0 22 | current_nba_index = 0 23 | 24 | current_nba_index = get_next_line_with_pc_start(nba_t, current_nba_index ) 25 | current_mine_index = get_next_line_with_pc_start(mine_t, current_mine_index) 26 | 27 | print(f"begun comparison at {current_nba_index} and {current_mine_index}") 28 | while (nba_t[current_nba_index].split(" ")[-2] != pc_end): 29 | if nba_t[current_nba_index] != mine_t[current_mine_index]: 30 | print(f"Error at lines {current_nba_index} and {current_mine_index}") 31 | exit(-1) 32 | current_nba_index += 1 33 | current_mine_index += 1 34 | print(f"ended comparison at {current_nba_index} and {current_mine_index}") 35 | 36 | current_nba_index = get_next_line_with_pc_start(nba_t, current_nba_index ) 37 | current_mine_index = get_next_line_with_pc_start(mine_t, current_mine_index) 38 | 39 | print(f"begun comparison at {current_nba_index} and {current_mine_index}") 40 | while (nba_t[current_nba_index].split(" ")[-2] != pc_end): 41 | if nba_t[current_nba_index] != mine_t[current_mine_index]: 42 | print(f"Error at lines {current_nba_index} and {current_mine_index}") 43 | exit(-1) 44 | current_nba_index += 1 45 | current_mine_index += 1 46 | print(f"ended comparison at {current_nba_index} and {current_mine_index}") -------------------------------------------------------------------------------- /source/emu/core/save/backups/sram.d: -------------------------------------------------------------------------------- 1 | module save.backups.sram; 2 | 3 | import save.backups; 4 | 5 | import std.stdio; 6 | 7 | final class SRAM : Backup { 8 | private ubyte[] data; 9 | private uint address_mask; 10 | 11 | this(uint size) { 12 | assert((size & (size - 1)) == 0); // size must be a power of 2 13 | 14 | this.data = new ubyte[size]; 15 | this.address_mask = size - 1; 16 | } 17 | 18 | override uint read_word(uint address) { 19 | ubyte read_data = read(address); 20 | return (read_data << 24) | (read_data << 16) | (read_data << 8) | read_data; 21 | } 22 | 23 | override ushort read_half(uint address) { 24 | ubyte read_data = read(address); 25 | return (read_data << 8) | read_data; 26 | } 27 | 28 | override ubyte read_byte(uint address) { 29 | return read(address); 30 | } 31 | 32 | override void write_word(uint address, uint data) { 33 | ubyte written_data = (data >> ((address & 3) * 8)) & 0xFF; 34 | write(address, written_data); 35 | } 36 | 37 | override void write_half(uint address, ushort data) { 38 | ubyte written_data = (data >> ((address & 1) * 8)) & 0xFF; 39 | write(address, written_data); 40 | } 41 | 42 | override void write_byte(uint address, ubyte data) { 43 | import std.stdio; 44 | write(address, data); 45 | } 46 | 47 | override ubyte[] serialize() { 48 | return data; 49 | } 50 | 51 | override void deserialize(ubyte[] data) { 52 | this.data = data; 53 | } 54 | 55 | override BackupType get_backup_type() { 56 | return BackupType.SRAM; 57 | } 58 | 59 | private ubyte read(uint address) { 60 | return this.data[address & address_mask]; 61 | } 62 | 63 | private void write(uint address, ubyte data) { 64 | this.data[address & address_mask] = data; 65 | } 66 | } -------------------------------------------------------------------------------- /tests/asm/src/mode4.S: -------------------------------------------------------------------------------- 1 | @ https://patater.com/gbaguy/gba/ch4.htm 2 | 3 | .arm 4 | .text 5 | .global main 6 | main: 7 | mov r0, #0x4000000 @ the usual set up routine 8 | mov r1, #0x400 @ 0x403 is BG 2 enable, and mode 3. 9 | add r1, r1, #4 10 | strh r1, [r0] @ the memory I/O value we're setting is actually 16bits, let's not mess 11 | @ something else up by writting 32. 12 | 13 | mov r0, #0x6000000 @ address of VRAM 14 | mov r5, #0x5000000 @ address of palette 15 | ldr r1, =pic @ using this form of LDR with a label will put the address of the label into r1. 16 | mov r2, #0x200 @ the amount of 32 BYTE writes to fill the screen (we'll be using a new instruction) 17 | 18 | loop1: 19 | ldrb r6, [r1], #1 20 | strb r6, [r5], #1 @ will store the 16bit value in r1 into address in r0, then 21 | @ add 2 to r0. (16bit values are 2 bytes long). 22 | subs r2, r2, #1 @ subtract 1 from r2 (r2 = r2 - 1). the S after SUB means to set the 23 | @ flags in CPSR based on the result. 24 | bne loop1 @ If the status doesn't reflect the result of an instruction being zero, jump to loop1. 25 | 26 | mov r2, #0x12c00 @ the amount of 32 BYTE writes to fill the screen (we'll be using a new instruction) 27 | 28 | loop2: 29 | ldrb r6, [r1], #1 30 | strb r6, [r0], #1 @ will store the 16bit value in r1 into address in r0, then 31 | @ add 2 to r0. (16bit values are 2 bytes long). 32 | subs r2, r2, #1 @ subtract 1 from r2 (r2 = r2 - 1). the S after SUB means to set the 33 | @ flags in CPSR based on the result. 34 | bne loop2 @ If the status doesn't reflect the result of an instruction being zero, jump to loop1. 35 | 36 | infin: 37 | b infin @ infinite loop 38 | 39 | .ltorg @ give the assembler a place to put the immediate value "pool", needed for the ldr REG,= (s). 40 | pic: @ a label to indicate the address of the included data. 41 | .incbin "src/zuyd.bin" @ include the binary file 42 | -------------------------------------------------------------------------------- /source/emu/core/hw/apu/channels/tone_channel.d: -------------------------------------------------------------------------------- 1 | module hw.apu.channels.tone_channel; 2 | 3 | final class ToneChannel { 4 | 5 | bool enabled; 6 | int volume; 7 | uint interval; 8 | 9 | uint cycles_elapsed; 10 | int cycles_remaining; 11 | 12 | bool length_flag; 13 | 14 | uint lut_index; 15 | uint duty; 16 | 17 | immutable bool[8][4] wave_duty_lut = [ 18 | [1, 0, 0, 0, 0, 0, 0, 0], 19 | [1, 1, 0, 0, 0, 0, 0, 0], 20 | [1, 1, 1, 1, 0, 0, 0, 0], 21 | [1, 1, 1, 1, 1, 1, 0, 0] 22 | ]; 23 | 24 | this() { 25 | enabled = false; 26 | cycles_elapsed = 0; 27 | cycles_remaining = 0; 28 | length_flag = false; 29 | interval = 8 * 2048; 30 | } 31 | 32 | short sample(int delta_cycles) { 33 | if (!enabled) return 0; 34 | 35 | cycles_elapsed += delta_cycles; 36 | while (cycles_elapsed > interval) { 37 | lut_index++; 38 | cycles_elapsed -= interval; 39 | } 40 | lut_index &= 7; 41 | 42 | if (length_flag) { 43 | cycles_remaining -= delta_cycles; 44 | if (cycles_remaining < 0) enabled = false; 45 | } 46 | 47 | return cast(short) wave_duty_lut[duty][lut_index]; 48 | } 49 | 50 | void set_duty(int duty) { 51 | this.duty = duty; 52 | } 53 | 54 | void set_enabled(bool enabled) { 55 | if (!this.enabled && enabled) { 56 | restart(); 57 | } 58 | 59 | this.enabled = enabled; 60 | } 61 | 62 | void set_length(uint n) { 63 | this.cycles_remaining = 65547 * (64 - n); 64 | }// 65 | 66 | void set_length_flag(bool length_flag) { 67 | this.length_flag = length_flag; 68 | }// 69 | 70 | void restart() { 71 | cycles_remaining = 0; 72 | length_flag = false; 73 | }// 74 | 75 | void set_frequency(int n) { 76 | this.interval = 32 * (2048 - n); 77 | }// 78 | } -------------------------------------------------------------------------------- /source/emu/common/source/abstracthw/memory.d: -------------------------------------------------------------------------------- 1 | module abstracthw.memory; 2 | 3 | enum Region { 4 | BIOS = 0x0, 5 | WRAM_BOARD = 0x2, 6 | WRAM_CHIP = 0x3, 7 | IO_REGISTERS = 0x4, 8 | PALETTE_RAM = 0x5, 9 | VRAM = 0x6, 10 | OAM = 0x7, 11 | 12 | ROM_WAITSTATE_0_L = 0x8, 13 | ROM_WAITSTATE_0_H = 0x9, 14 | ROM_WAITSTATE_1_L = 0xA, 15 | ROM_WAITSTATE_1_H = 0xB, 16 | ROM_WAITSTATE_2_L = 0xC, 17 | ROM_WAITSTATE_2_H = 0xD, 18 | ROM_SRAM_L = 0xE, 19 | ROM_SRAM_H = 0xF, 20 | } 21 | 22 | enum OpenBusBiosState { 23 | STARTUP, 24 | SOFT_RESET, 25 | DURING_IRQ, 26 | AFTER_IRQ, 27 | AFTER_SWI 28 | } 29 | 30 | enum AccessType { 31 | NONSEQUENTIAL = 0, 32 | SEQUENTIAL = 1 33 | } 34 | 35 | enum AccessSize { 36 | BYTE = 0, 37 | HALFWORD = 1, 38 | WORD = 2 39 | } 40 | 41 | interface IMemory { 42 | pragma(inline, true) ubyte read_byte(uint address, AccessType access_type = AccessType.SEQUENTIAL, bool instruction_access = false); 43 | pragma(inline, true) ushort read_half(uint address, AccessType access_type = AccessType.SEQUENTIAL, bool instruction_access = false); 44 | pragma(inline, true) uint read_word(uint address, AccessType access_type = AccessType 45 | .SEQUENTIAL, bool instruction_access = false); 46 | 47 | pragma(inline, true) void write_byte(uint address, ubyte value, 48 | AccessType access_type = AccessType.SEQUENTIAL, bool instruction_access = false); 49 | pragma(inline, true) void write_half(uint address, ushort value, AccessType access_type = AccessType.SEQUENTIAL, bool instruction_access = false); 50 | pragma(inline, true) void write_word(uint address, uint value, 51 | AccessType access_type = AccessType.SEQUENTIAL, bool instruction_access = false); 52 | 53 | void start_new_prefetch(uint address, AccessSize prefetch_access_size); 54 | void run_prefetcher(int number_of_times); 55 | void invalidate_prefetch_buffer(); 56 | void idle(); 57 | bool can_start_new_prefetch(); 58 | 59 | @property uint cycles(); 60 | @property uint cycles(uint cycles); 61 | } 62 | -------------------------------------------------------------------------------- /tests/asm/src/megalovania.S: -------------------------------------------------------------------------------- 1 | .arm 2 | .text 3 | .global main 4 | 5 | main: 6 | mov r7, #0x4000000 @ base I/O address 7 | mov r8, #0x3000000 @ work RAM - audio will be stored here 8 | 9 | fill_up_audio: 10 | mov r6, #0x56 @ 0x5622: 1 second worth of audio 11 | lsl r6, #0x8 12 | orr r6, #0x22 13 | 14 | mov r9, #0x80 @ we want waves of this frequency 15 | mov r4, #0x0 @ audio offset = 0 16 | ldr r5, =audio 17 | 18 | fill_up_audio_loop: 19 | mov r0, r4 20 | mov r1, r9 21 | swi #0x60000 22 | strb r1, [r8, r4] @ should produce a nice saw wave 23 | add r4, #0x1 24 | strb r1, [r8, r4] 25 | add r4, #0x1 26 | 27 | cmp r4, r6 28 | beq setup_sound 29 | 30 | b fill_up_audio_loop 31 | 32 | setup_sound: 33 | mov r1, #0x80 @ I/O offset for SOUNDCNT_L 34 | mov r2, #0x0 @ audio channels 1-4 shouldn't play 35 | strh r2, [r7, r1] 36 | 37 | mov r1, #0x82 @ I/O offset for SOUNDCNT_H 38 | mov r2, #0xB @ 0xB04: connect Timer 0 to DMA, set volume to 100%, reset FIFO 39 | lsl r2, #0x8 40 | orr r2, #0x04 41 | strh r2, [r7, r1] 42 | 43 | setup_timers: 44 | mov r1, #0x0 45 | sub r1, #0x1 @ 0xFFFF, the max timer value 46 | 47 | mov r3, #0x2 48 | lsl r3, #0x8 49 | orr r3, #0xF9 50 | sub r1, r3 @ 0x2F9: ticks per sample 51 | 52 | mov r2, #0x100 @ I/O offset for TM0CNT_L 53 | strh r1, [r7, r2] 54 | 55 | mov r2, #0x1 @ 0x102: I/O offset for TM0CNT_H 56 | lsl r2, #0x8 57 | orr r2, #0x2 58 | mov r1, #0x80 @ Timer Enable 59 | strh r1, [r7, r2] 60 | 61 | setup_audio_dma: 62 | 63 | mov r1, #0xBC @ I/O offset for DMA1SAD 64 | str r8, [r7, r1] 65 | 66 | mov r1, #0xC0 @ I/O offset for DMA1DAD 67 | mov r2, #0xA0 @ I/O offset for FIFO_A 68 | add r2, r7, r2 69 | str r2, [r7, r1] 70 | 71 | mov r1, #0xC4 @ I/O offset for DMA1CNT 72 | mov r2, #0xB6000000 @ DMA enable, Audio Mode 73 | str r2, [r7, r1] 74 | 75 | infin: 76 | b infin 77 | 78 | audio: -------------------------------------------------------------------------------- /imgui/dimgui/src/glad/gl/ext.d: -------------------------------------------------------------------------------- 1 | module glad.gl.ext; 2 | 3 | 4 | private import glad.gl.types; 5 | private import glad.gl.enums; 6 | private import glad.gl.funcs; 7 | bool GL_ARB_debug_output; 8 | bool GL_KHR_debug; 9 | nothrow @nogc extern(System) { 10 | alias fp_glDebugMessageControlARB = void function(GLenum, GLenum, GLenum, GLsizei, const(GLuint)*, GLboolean); 11 | alias fp_glDebugMessageInsertARB = void function(GLenum, GLenum, GLuint, GLenum, GLsizei, const(GLchar)*); 12 | alias fp_glDebugMessageCallbackARB = void function(GLDEBUGPROCARB, const(void)*); 13 | alias fp_glGetDebugMessageLogARB = GLuint function(GLuint, GLsizei, GLenum*, GLenum*, GLuint*, GLenum*, GLsizei*, GLchar*); 14 | alias fp_glDebugMessageControlKHR = void function(GLenum, GLenum, GLenum, GLsizei, const(GLuint)*, GLboolean); 15 | alias fp_glDebugMessageInsertKHR = void function(GLenum, GLenum, GLuint, GLenum, GLsizei, const(GLchar)*); 16 | alias fp_glDebugMessageCallbackKHR = void function(GLDEBUGPROCKHR, const(void)*); 17 | alias fp_glGetDebugMessageLogKHR = GLuint function(GLuint, GLsizei, GLenum*, GLenum*, GLuint*, GLenum*, GLsizei*, GLchar*); 18 | alias fp_glPushDebugGroupKHR = void function(GLenum, GLuint, GLsizei, const(GLchar)*); 19 | alias fp_glPopDebugGroupKHR = void function(); 20 | alias fp_glObjectLabelKHR = void function(GLenum, GLuint, GLsizei, const(GLchar)*); 21 | alias fp_glGetObjectLabelKHR = void function(GLenum, GLuint, GLsizei, GLsizei*, GLchar*); 22 | alias fp_glObjectPtrLabelKHR = void function(const(void)*, GLsizei, const(GLchar)*); 23 | alias fp_glGetObjectPtrLabelKHR = void function(const(void)*, GLsizei, GLsizei*, GLchar*); 24 | alias fp_glGetPointervKHR = void function(GLenum, void**); 25 | } 26 | __gshared { 27 | fp_glDebugMessageCallbackARB glDebugMessageCallbackARB; 28 | fp_glGetPointervKHR glGetPointervKHR; 29 | fp_glObjectPtrLabelKHR glObjectPtrLabelKHR; 30 | fp_glDebugMessageCallbackKHR glDebugMessageCallbackKHR; 31 | fp_glDebugMessageControlARB glDebugMessageControlARB; 32 | fp_glGetDebugMessageLogARB glGetDebugMessageLogARB; 33 | fp_glGetObjectPtrLabelKHR glGetObjectPtrLabelKHR; 34 | fp_glDebugMessageInsertARB glDebugMessageInsertARB; 35 | fp_glDebugMessageControlKHR glDebugMessageControlKHR; 36 | fp_glObjectLabelKHR glObjectLabelKHR; 37 | fp_glGetDebugMessageLogKHR glGetDebugMessageLogKHR; 38 | fp_glDebugMessageInsertKHR glDebugMessageInsertKHR; 39 | fp_glPopDebugGroupKHR glPopDebugGroupKHR; 40 | fp_glGetObjectLabelKHR glGetObjectLabelKHR; 41 | fp_glPushDebugGroupKHR glPushDebugGroupKHR; 42 | } 43 | -------------------------------------------------------------------------------- /imgui/dimgui/readme.md: -------------------------------------------------------------------------------- 1 | # dimgui 2 | 3 | ![dimgui](https://raw.github.com/d-gamedev-team/dimgui/master/screenshot/imgui.png) 4 | 5 | This is a D port of the [imgui] OpenGL GUI library. 6 | 7 | **dimgui** is an [immediate-mode] GUI library. 8 | 9 | Homepage: https://github.com/d-gamedev-team/dimgui 10 | 11 | ## Supported compilers 12 | 13 | Currently requires DMD v2.071.0, not tested with other compilers, 14 | and not frequently tested on Windows. 15 | 16 | ## Examples 17 | 18 | Use [dub] to build and run the example project: 19 | 20 | ``` 21 | # Shows a nice demo of the various UI elements. 22 | $ dub run dimgui:demo 23 | 24 | # Shows how to properly handle memory management. 25 | $ dub run dimgui:memory 26 | ``` 27 | 28 | Note: You will need to install the [glfw] shared library in order to run the example. 29 | 30 | ## Real-world examples 31 | 32 | **dimgui** is used in the following projects: 33 | 34 | - [dbox] - The 2D physics library uses **dimgui** for its interactive test-suite. 35 | 36 | ## Documentation 37 | 38 | The public API is available in the [imgui.api] module. 39 | 40 | ## Memory Management 41 | 42 | For efficiency reasons [imgui] will batch all commands and will render the current frame 43 | once **imguiRender** is called. Calls to UI-defining functions such as **imguiLabel** will 44 | store a reference to the passed-in string and will not draw the string immediately. 45 | 46 | This means you should not pass in memory allocated on the stack unless you can guarantee that: 47 | 48 | - The memory on the stack will live up to the point **imguiRender** is called. 49 | - The memory passed to the UI-defining functions is unique for each call. 50 | 51 | An example of both improper and proper memory management is shown in the [memory] example. 52 | 53 | ## Building dimgui as a static library 54 | 55 | Run [dub] alone in the root project directory to build **dimgui** as a static library: 56 | 57 | ``` 58 | $ dub 59 | ``` 60 | 61 | ## Links 62 | 63 | - The original [imgui] github repository. 64 | 65 | ## License 66 | 67 | Distributed under the [zlib] license. 68 | 69 | See the accompanying file [license.txt][zlib]. 70 | 71 | [dub]: http://code.dlang.org/ 72 | [immediate-mode]: http://sol.gfxile.net/imgui/ 73 | [imgui]: https://github.com/AdrienHerubel/imgui 74 | [imgui.api]: https://github.com/d-gamedev-team/dimgui/blob/master/src/imgui/api.d 75 | [zlib]: https://raw.github.com/d-gamedev-team/dimgui/master/license.txt 76 | [glfw]: http://www.glfw.org/ 77 | [memory]: https://github.com/d-gamedev-team/dimgui/blob/master/examples/memory/memory.d 78 | [dbox]: https://github.com/d-gamedev-team/dbox 79 | -------------------------------------------------------------------------------- /source/emu/core/hw/keyinput.d: -------------------------------------------------------------------------------- 1 | module hw.keyinput; 2 | 3 | import hw.memory; 4 | import hw.cpu; 5 | import hw.interrupts; 6 | 7 | import std.stdio; 8 | 9 | import util; 10 | 11 | enum GBAKeyVanilla { 12 | A = 0, 13 | B = 1, 14 | SELECT = 2, 15 | START = 3, 16 | RIGHT = 4, 17 | LEFT = 5, 18 | UP = 6, 19 | DOWN = 7, 20 | R = 8, 21 | L = 9 22 | } 23 | 24 | final class KeyInput { 25 | Memory memory; 26 | void delegate(uint) interrupt_cpu; 27 | 28 | this(Memory memory) { 29 | this.memory = memory; 30 | this.keyinput = 0x03FF; 31 | } 32 | 33 | void set_interrupt_cpu(void delegate(uint) interrupt_cpu) { 34 | this.interrupt_cpu = interrupt_cpu; 35 | } 36 | 37 | enum IRQCondition { 38 | OR = 0, 39 | AND = 1 40 | } 41 | 42 | private ushort keycnt; 43 | private IRQCondition irq_condition; 44 | private bool irq_enabled; 45 | 46 | private ushort keyinput; 47 | 48 | void write_KEYCNT(int target_byte, ubyte data) { 49 | if (target_byte == 0) { 50 | keycnt = (keycnt & 0xFF00) | data; 51 | } else { 52 | keycnt = (keycnt & 0x00FF) | ((data & 3) << 8); 53 | 54 | irq_condition = cast(IRQCondition) get_nth_bit(data, 6); 55 | irq_enabled = cast(IRQCondition) get_nth_bit(data, 6); 56 | } 57 | 58 | if (should_interrupt()) interrupt_cpu(Interrupt.KEYPAD); 59 | } 60 | 61 | ubyte read_KEYINPUT(int target_byte) { 62 | if (target_byte == 0) { 63 | return (keyinput & 0x00FF) >> 0; 64 | } else { 65 | return (keyinput & 0xFF00) >> 8; 66 | } 67 | } 68 | 69 | ubyte read_KEYCNT(int target_byte) { 70 | if (target_byte == 0) { 71 | return (keycnt & 0x00FF) >> 0; 72 | } else { 73 | return cast(ubyte) (((keycnt & 0xFF00) >> 8) | (cast(ubyte) irq_condition << 6) | (irq_enabled << 7)); 74 | } 75 | } 76 | 77 | void set_key(GBAKeyVanilla code, bool pressed) { 78 | if (pressed) { 79 | keyinput &= ~(0b1 << code); 80 | } else { 81 | keyinput |= (0b1 << code); 82 | } 83 | 84 | if (should_interrupt()) interrupt_cpu(Interrupt.KEYPAD); 85 | } 86 | 87 | import std.stdio; 88 | 89 | bool should_interrupt() { 90 | uint inverted_keyinput = ~keyinput & 0x3FF; 91 | final switch (irq_condition) { 92 | case IRQCondition.OR: 93 | return (keycnt & inverted_keyinput) != 0; 94 | case IRQCondition.AND: 95 | return (keycnt & inverted_keyinput) == keycnt; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /source/emu/core/hw/sio/sio.d: -------------------------------------------------------------------------------- 1 | module hw.sio.sio; 2 | 3 | import scheduler; 4 | import util; 5 | import hw.interrupts; 6 | 7 | final class SIO { 8 | bool shift_clock; 9 | bool internal_shift_clock; 10 | bool si_state; 11 | bool so_during_inactivity; 12 | bool start_bit; 13 | bool transfer_length; 14 | bool irq_enable; 15 | 16 | uint num_bits_to_transfer; 17 | 18 | void delegate(uint) interrupt_cpu; 19 | Scheduler scheduler; 20 | 21 | this(void delegate(uint) interrupt_cpu, Scheduler scheduler) { 22 | this.interrupt_cpu = interrupt_cpu; 23 | this.scheduler = scheduler; 24 | 25 | this.num_bits_to_transfer = 0; 26 | } 27 | 28 | void write_SIOCNT(uint target_byte, ubyte data) { 29 | final switch (target_byte) { 30 | case 0b0: 31 | this.shift_clock = get_nth_bit(data, 0); 32 | this.internal_shift_clock = get_nth_bit(data, 1); 33 | this.si_state = get_nth_bit(data, 2); 34 | this.so_during_inactivity = get_nth_bit(data, 3); 35 | this.start_bit = get_nth_bit(data, 7); 36 | 37 | if (this.start_bit) start_transfer(); 38 | break; 39 | 40 | case 0b1: 41 | this.transfer_length = get_nth_bit(data, 0); 42 | this.irq_enable = get_nth_bit(data, 6); 43 | break; 44 | } 45 | } 46 | 47 | ubyte read_SIOCNT(uint target_byte) { 48 | final switch (target_byte) { 49 | case 0b0: 50 | return cast(ubyte) 51 | (this.shift_clock << 0) | 52 | (this.internal_shift_clock << 1) | 53 | (this.si_state << 2) | 54 | (this.so_during_inactivity << 3) | 55 | (this.start_bit << 7); 56 | 57 | case 0b1: 58 | return cast(ubyte) 59 | (this.transfer_length << 0) | 60 | (this.irq_enable << 6); 61 | } 62 | } 63 | 64 | void start_transfer() { 65 | this.num_bits_to_transfer = this.transfer_length ? 32 : 8; 66 | schedule_next_value(); 67 | } 68 | 69 | void schedule_next_value() { 70 | uint cpu_frequency = 16780000; 71 | uint cycles_to_wait = cpu_frequency / (this.internal_shift_clock ? 2000000 : 256000); 72 | scheduler.add_event_relative_to_clock(&transfer_one_value, cycles_to_wait); 73 | } 74 | 75 | void transfer_one_value() { 76 | this.num_bits_to_transfer--; 77 | 78 | if (this.num_bits_to_transfer > 0) { 79 | schedule_next_value(); 80 | } else { 81 | this.start_bit = false; 82 | if (irq_enable) { 83 | interrupt_cpu(Interrupt.SERIAL_COMMUNICATION); 84 | } 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /source/emu/jumptable/source/barrel_shifter.d: -------------------------------------------------------------------------------- 1 | module barrel_shifter; 2 | 3 | import abstracthw.cpu; 4 | import ops; 5 | import util; 6 | 7 | struct BarrelShifter { 8 | Word result; 9 | bool carry; 10 | } 11 | 12 | BarrelShifter barrel_shift(int shift_type, bool is_immediate, T : IARM7TDMI)(T cpu, Word operand, Word shift) { 13 | Word result; 14 | bool carry; 15 | 16 | // "static switch" doesnt exist, sadly 17 | static if (shift_type == 0) { // LSL 18 | if (shift == 0) { 19 | result = operand; 20 | carry = cpu.get_flag(Flag.C); 21 | } else if (shift < 32) { 22 | result = operand << shift; 23 | carry = get_nth_bit(operand, 32 - shift); 24 | } else if (shift == 32) { 25 | result = 0; 26 | carry = get_nth_bit(operand, 0); 27 | } else { 28 | result = 0; 29 | carry = (result == 32) ? get_nth_bit(operand, 0) : 0; 30 | } 31 | } 32 | 33 | static if (shift_type == 1) { // LSR 34 | static if (is_immediate) if (shift == 0) shift = 32; 35 | 36 | if (shift == 0) { 37 | result = operand; 38 | carry = cpu.get_flag(Flag.C); 39 | } else if (shift < 32) { 40 | result = operand >> shift; 41 | carry = get_nth_bit(operand, shift - 1); 42 | } else if (shift == 32) { 43 | result = 0; 44 | carry = get_nth_bit(operand, 31); 45 | } else { 46 | result = 0; 47 | carry = 0; 48 | } 49 | } 50 | 51 | static if (shift_type == 2) { // ASR 52 | static if (is_immediate) if (shift == 0) shift = 32; 53 | 54 | if (shift == 0) { 55 | result = operand; 56 | carry = cpu.get_flag(Flag.C); 57 | } else if (shift < 32) { 58 | result = cpu.sext_32(operand >> shift, 32 - shift); 59 | carry = get_nth_bit(operand, shift - 1); 60 | } else { 61 | result = get_nth_bit(operand, 31) ? 0xFFFFFFFF : 0x00000000; 62 | carry = get_nth_bit(operand, 31); 63 | } 64 | } 65 | 66 | static if (shift_type == 3) { // ROR 67 | bool done = false; 68 | 69 | static if (!is_immediate) { 70 | if ((shift & 0xFF) == 0) { 71 | result = operand; 72 | carry = cpu.get_flag(Flag.C); 73 | done = true; 74 | } else { 75 | shift &= 0x1F; 76 | if (shift == 0) shift = 32; 77 | } 78 | } 79 | 80 | if (!done) { 81 | if (shift == 0) { 82 | result = cpu.get_flag(Flag.C) << 31 | (operand >> 1); 83 | carry = get_nth_bit(operand, 0); 84 | } else { 85 | result = rotate_right(operand, shift); 86 | carry = get_nth_bit(operand, shift - 1); 87 | } 88 | } 89 | } 90 | 91 | return BarrelShifter(result, carry); 92 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![icon](media/icon.png) 2 | 3 | 4 | 5 | # GameBeanAdvance 6 | WIP Gameboy Advance Emulator written in D. Passes 2020/2020 mGBA timing tests, and fourth software-based emulator to pass the AGS Aging Cartridge test. Currently working on improving GBA accuracy. 7 | 8 | # Demo Videos (Turn audio up) 9 | 10 | https://user-images.githubusercontent.com/15221993/144956720-35759f25-d480-4e99-aff3-2daa7b0fa977.mp4 11 | 12 | 13 | https://user-images.githubusercontent.com/15221993/134795596-50df7538-6ca9-40c1-a22c-97055fd389f7.mov 14 | 15 | 16 | https://user-images.githubusercontent.com/15221993/134795668-0a3c979e-dc54-4b23-87b4-050dcdab5d5e.mov 17 | 18 | 19 | # Usage 20 | Emulator main project is in `src/emu`. 21 | 22 | ## Build 23 | 24 | ### Requirements 25 | + Dlang + DUB 26 | + Python3/PyPy3 (packages: `ply`) 27 | 28 | ### Standard Build 29 | 30 | Optionally add `-b release --compiler=ldc2` for optimized build. 31 | 32 | ``` 33 | dub build 34 | ``` 35 | 36 | ### Profiling 37 | 38 | Build with support for [gperftools_d](https://github.com/prasunanand/gperftools_d). This requires the LDC2 compiler. 39 | 40 | ```sh 41 | dub build -c gperf --compiler=ldc2 -b release-debug 42 | ``` 43 | 44 | Then, run with the `CPUPROFILE=/tmp/prof.out` environment var to write a profile log. 45 | 46 | Finally, convert the log to human readable graph: 47 | ```sh 48 | pprof --pdf gamebean-emu /tmp/prof.out > ~/Downloads/gamebean_profile.pdf 49 | ``` 50 | 51 | ## Run 52 | 53 | Specify path to rom. You can also optionally use `-s` to specify window scaling (default = 1). Note that you must have a __gba_bios.bin__ located in the same directory as the executable. You can acquire a substitute BIOS (see [Cult of GBA](https://github.com/Cult-of-GBA/BIOS)), or you can dump the official BIOS from a GBA. 54 | ``` 55 | ./gamebean-emu [rom] 56 | ``` 57 | 58 | ## Tests 59 | 60 | To run the tests, run `dub test`. This will test the ARM CPU by running it through the GBA files located in __/tests/asm/bin/__. If the cpu states after every cycle matches the expected states found in the log files in __/tests/asm/log__, then the tests will pass. These roms are written in ARM assembly and are located in __/tests/asm/src__. These tests have their own makefile that produces a .gba file as well as a log file. Logfile production requires having an editted version of NanoBoyAdvance, and having the command `NanoBoyAdvance` added to PATH. 61 | 62 | # Relevant Resources 63 | 64 | ARM Technical Reference Manual: https://static.docs.arm.com/ddi0029/g/DDI0029.pdf 65 | 66 | ARM Architectural Reference Manual: https://cs.nyu.edu/courses/spring18/CSCI-GA.2130-001/ARM/arm_arm.pdf 67 | 68 | GBATEK: https://problemkaputt.de/gbatek.htm 69 | 70 | Patater GBA ASM Guide: https://patater.com/gbaguy/gbaasm.htm 71 | 72 | Tonc: https://www.coranac.com/tonc/text/ 73 | 74 | # Special Thanks 75 | + nocash, for the excellent documentation about the GBA's internals on [GBATEK](https://problemkaputt.de/gbatek.htm) 76 | + fleroviux, for sharing research on GBA timing, as well letting me take inspiration [NanoBoyAdvance](https://github.com/fleroviux/NanoBoyAdvance) for my frontend design. 77 | + DenSinH, whose code ([DSHBA]([emu](https://github.com/DenSinH/DSHBA))) I referenced while adding shaders to my emu 78 | + Near and Talarubi, who created the aforementioned [shaders](https://near.sh/articles/video/color-emulation). 79 | -------------------------------------------------------------------------------- /source/tools/profiler/functiontree.d: -------------------------------------------------------------------------------- 1 | module tools.profiler.functiontree; 2 | 3 | import util; 4 | import util.pointerpool; 5 | 6 | alias FunctionID = u32; 7 | 8 | final class FunctionTree { 9 | enum MAX_FUNCTION_CALLS = 200; 10 | enum MAX_FUNCTION_DEPTH = 30; 11 | 12 | alias FunctionList = FunctionCall*[MAX_FUNCTION_CALLS]; 13 | alias FunctionPool = PointerPool!(FunctionCall, 1 << 20); 14 | 15 | struct FunctionCall { 16 | u64 cycles; 17 | FunctionID function_id; 18 | FunctionList function_call_list; 19 | int function_call_list_length; 20 | bool entered_before; 21 | } 22 | 23 | FunctionPool pointer_pool; 24 | 25 | FunctionCall*[MAX_FUNCTION_DEPTH] call_stack; 26 | int call_stack_size = 0; 27 | 28 | FunctionCall* current_function_call; 29 | 30 | u64 cycles = 0; 31 | 32 | this() { 33 | pointer_pool = new FunctionPool(); 34 | 35 | call_stack[0] = pointer_pool.get_pointer(); 36 | call_stack[0].cycles = 0; 37 | call_stack[0].function_id = -1; 38 | call_stack[0].function_call_list = pointer_pool.get_pointer(); 39 | call_stack[0].function_call_list_length = 0; 40 | call_stack[0].entered_before = false; 41 | current_function_call = call_stack[0]; 42 | 43 | call_stack_size = 1; 44 | } 45 | 46 | void enter_function(FunctionID function_id) { 47 | increment_current_function_call(cycles); 48 | 49 | auto search_return = search_for_function_id(function_id); 50 | if (search_return.found) { 51 | add_new_function_call(function_id, search_return.index); 52 | } 53 | 54 | current_function_call = ¤t_function_call[search_return.index]; 55 | current_function_call.entered_before = true; 56 | 57 | call_stack[call_stack_size++] = current_function_call; 58 | } 59 | 60 | void exit_function() { 61 | if (call_stack_size <= 1) return; 62 | current_function_call = call_stack[--call_stack_size]; 63 | } 64 | 65 | void add_new_function_call(FunctionID function_id, int index) { 66 | auto function_call_list = current_function_call.function_call_list; 67 | 68 | function_call_list[index] = pointer_pool.get_pointer(); 69 | 70 | function_call_list[index].cycles = 0; 71 | function_call_list[index].function_id = function_id; 72 | function_call_list[index].function_call_list_length = 0; 73 | 74 | current_function_call.function_call_list_length++; 75 | } 76 | 77 | void increment_current_function_call(u64 cycles) { 78 | current_function_call.cycles += cycles; 79 | cycles = 0; 80 | } 81 | 82 | void tick(u64 cycles) { 83 | this.cycles += cycles; 84 | } 85 | 86 | struct SearchReturn { 87 | u32 index; 88 | bool found; 89 | } 90 | 91 | SearchReturn search_for_function_id(FunctionID function_id) { 92 | for (int i = 0; i < MAX_FUNCTION_CALLS; i++) { 93 | auto f = current_function_call.function_call_list[i]; 94 | 95 | if (f.function_id == function_id) return SearchReturn(i, true); 96 | if (!f.entered_before) return SearchReturn(i, false); 97 | } 98 | 99 | // TODO: maybe something more descriptive?? 100 | error("Exceeded maximum call list."); 101 | 102 | return SearchReturn(0, false); // this wont trigger but it shuts up the compiler 103 | } 104 | } -------------------------------------------------------------------------------- /source/emu/core/hw/apu/channels/wave_channel.d: -------------------------------------------------------------------------------- 1 | module hw.apu.channels.wave_channel; 2 | 3 | final class WaveChannel { 4 | bool is_double_banked; 5 | int playback_bank; // theres two banks - 0 and 1 6 | int modify_bank; 7 | int playback_bank__cache; 8 | bool enabled; 9 | 10 | int[4] sound_volume_lut = [0, 4, 2, 1]; // where 4 is 100% volume 11 | int sound_volume; 12 | bool force_volume; // force volume to 75% 13 | int volume; 14 | 15 | uint interval; 16 | 17 | ubyte[32][2] wave_ram; 18 | int wave_ram_index; 19 | int wave_ram_index_mask; 20 | 21 | uint cycles_elapsed; 22 | int cycles_remaining; 23 | 24 | bool length_flag; 25 | 26 | this() { 27 | enabled = false; 28 | cycles_elapsed = 0; 29 | cycles_remaining = 0; 30 | length_flag = false; 31 | interval = 8 * 2048; 32 | } 33 | 34 | short sample(int delta_cycles) { 35 | if (!enabled) return 0; 36 | import std.stdio; 37 | 38 | cycles_elapsed += delta_cycles; 39 | while (cycles_elapsed > interval) { 40 | wave_ram_index++; 41 | cycles_elapsed -= interval; 42 | } 43 | 44 | if (length_flag) { 45 | cycles_remaining -= delta_cycles; 46 | if (cycles_remaining < 0) enabled = false; 47 | } 48 | 49 | wave_ram_index &= 31; 50 | return cast(short) (wave_ram[playback_bank][wave_ram_index] * 4 * volume - 0x80); 51 | } 52 | 53 | void set_double_banked(bool is_double_banked) { 54 | if (is_double_banked) { 55 | this.playback_bank = playback_bank__cache; 56 | this.modify_bank = (playback_bank) == 1 ? 0 : 1; 57 | } else { 58 | this.playback_bank = 0; 59 | this.modify_bank = 0; 60 | } 61 | } 62 | 63 | void set_playback_bank(int playback_bank) { 64 | this.playback_bank = playback_bank; 65 | this.modify_bank = (playback_bank == 1) ? 0 : 1; 66 | 67 | this.playback_bank__cache = playback_bank; 68 | } 69 | 70 | void set_enabled(bool enabled) { 71 | if (!this.enabled && enabled) { 72 | restart(); 73 | } 74 | 75 | this.enabled = enabled; 76 | } 77 | 78 | void set_length(uint n) { 79 | this.cycles_remaining = 65547 * (256 - n); 80 | } 81 | 82 | void set_length_flag(bool length_flag) { 83 | this.length_flag = length_flag; 84 | } 85 | 86 | void restart() { 87 | wave_ram_index = 0; 88 | cycles_remaining = 0; 89 | length_flag = false; 90 | } 91 | 92 | void set_sound_volume(int sound_volume) { 93 | this.sound_volume = sound_volume; 94 | update_volume(); 95 | } 96 | 97 | void set_force_volume(bool force_volume) { 98 | this.force_volume = force_volume; 99 | update_volume(); 100 | } 101 | 102 | void set_sample_rate(int n) { 103 | this.interval = 16 * (2048 - n); 104 | } 105 | 106 | void update_volume() { 107 | if (force_volume) { 108 | volume = 3; 109 | } else { 110 | volume = sound_volume_lut[sound_volume]; 111 | } 112 | } 113 | 114 | void write_wave_ram(int index, ubyte value) { 115 | wave_ram[modify_bank][2 * index ] = value >> 4; 116 | wave_ram[modify_bank][2 * index + 1] = value & 0xF; 117 | } 118 | 119 | ubyte read_wave_ram(int index) { 120 | return wave_ram[modify_bank][index]; 121 | } 122 | } -------------------------------------------------------------------------------- /source/app.d: -------------------------------------------------------------------------------- 1 | module app; 2 | 3 | import hw.gba; 4 | import hw.memory; 5 | import hw.keyinput; 6 | 7 | import util; 8 | 9 | import diag.log; 10 | 11 | import std.conv; 12 | import std.file; 13 | import std.uri; 14 | import std.algorithm.searching: canFind; 15 | import std.mmfile; 16 | import std.file; 17 | import std.path; 18 | import save; 19 | 20 | import core.sync.mutex; 21 | 22 | import bindbc.sdl; 23 | import bindbc.opengl; 24 | 25 | import tools.profiler.profiler; 26 | import ui; 27 | 28 | import commandr; 29 | 30 | version (gperf) { 31 | import gperftools_d.profiler; 32 | } 33 | 34 | void main(string[] args) { 35 | // dfmt off 36 | auto a = new Program("gamebean-emu", "0.1").summary("GameBean Advance") 37 | .add(new Flag("v", "verbose", "turns on more verbose output").repeating) 38 | .add(new Option("s", "scale", "render scale").optional.defaultValue("1")) 39 | .add(new Argument("rompath", "path to rom file")) 40 | .add(new Option("b", "bios", "path to bios file").optional.defaultValue("./gba_bios.bin")) 41 | .add(new Flag("k", "bootscreen", "skips bios bootscreen and starts the rom directly")) 42 | .add(new Option("m", "mod", "enable mod/extension")) 43 | .add(new Option("p", "profile", "profile the emu (pass in an ELF file here)")) 44 | .add(new Option("t", "cputrace", "display cpu trace on crash").optional.defaultValue("0")) 45 | .parse(args); 46 | // dfmt on 47 | 48 | util.verbosity_level = a.occurencesOf("verbose"); 49 | 50 | auto mem = new Memory(); 51 | 52 | bool is_beancomputer = a.option("mod").canFind("beancomputer"); 53 | if (is_beancomputer) log!(LogSource.INIT)("BeanComputer enabled"); 54 | 55 | KeyInput key_input = new KeyInput(mem); 56 | auto bios_data = load_rom_as_bytes(a.option("bios")); 57 | GBA gba = new GBA(mem, key_input, bios_data, is_beancomputer); 58 | 59 | // load rom 60 | auto rom_path = a.arg("rompath"); 61 | log!(LogSource.INIT)("Loading rom from: %s.", rom_path); 62 | 63 | // check file 64 | if (uriLength(rom_path) > 0) { 65 | import std.net.curl : download; 66 | import std.path: buildPath; 67 | import std.uuid: randomUUID; 68 | 69 | auto dl_path = buildPath(tempDir(), randomUUID().toString()); 70 | download(rom_path, dl_path); 71 | 72 | auto rom_data = load_rom_as_bytes(dl_path); 73 | 74 | log!(LogSource.INIT)("Downloaded %s bytes as %s", rom_data.length, dl_path); 75 | 76 | gba.load_rom(rom_data); 77 | } else if (std.file.exists(rom_path)) { 78 | gba.load_rom(rom_path); 79 | } else { 80 | error("rom file does not exist!"); 81 | } 82 | 83 | if (a.flag("bootscreen")) gba.skip_bios_bootscreen(); 84 | 85 | auto profile = a.option("profile"); 86 | if (profile) { 87 | g_profile_gba = true; 88 | g_profiler = new Profiler(gba, profile); 89 | } 90 | MultiMediaDevice frontend = new RengMultimediaDevice(gba, to!int(a.option("scale"))); 91 | gba.set_frontend(frontend); 92 | g_log_gba = gba; 93 | 94 | gba.set_internal_sample_rate(16_780_000 / 48000); 95 | Runner runner = new Runner(gba, frontend); 96 | 97 | Savetype savetype = detect_savetype(gba.memory.rom.get_bytes()); 98 | 99 | if (savetype != Savetype.NONE && savetype != Savetype.UNKNOWN) { 100 | Backup save = create_savetype(savetype); 101 | gba.memory.add_backup(save); 102 | 103 | auto save_path = rom_path.stripExtension().setExtension(".bsv"); 104 | if (!save_path.exists()) { 105 | ubyte[] save_data = new ubyte[save.get_backup_size()]; 106 | save_data[0..save.get_backup_size()] = 0xFF; 107 | write(save_path, save_data); 108 | } 109 | 110 | MmFile mm_file = new MmFile(save_path, MmFile.Mode.readWrite, save.get_backup_size(), null, 0); 111 | 112 | save.deserialize(cast(ubyte[]) mm_file[]); 113 | save.set_backup_file(mm_file); 114 | } 115 | 116 | runner.run(); 117 | 118 | version (gperf) { 119 | log!(LogSource.DEBUG)("Started profiler"); 120 | ProfilerStart(); 121 | } 122 | 123 | version (gperf) { 124 | ProfilerStop(); 125 | log!(LogSource.DEBUG)("Ended profiler"); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /source/emu/common/source/util/package.d: -------------------------------------------------------------------------------- 1 | module util; 2 | 3 | // import diag.logger; 4 | 5 | import core.stdc.math; //core.stdc.math.pow 6 | 7 | import std.stdio; 8 | import std.conv; 9 | 10 | import diag.logger; 11 | 12 | public { 13 | import std.format; 14 | } 15 | 16 | alias u8 = ubyte; 17 | alias u16 = ushort; 18 | alias u32 = uint; 19 | alias u64 = ulong; 20 | alias s8 = byte; 21 | alias s16 = short; 22 | alias s32 = int; 23 | alias s64 = long; 24 | 25 | alias Word = uint; 26 | alias Half = ushort; 27 | alias Byte = ubyte; 28 | 29 | version (LDC) { 30 | import ldc.intrinsics; 31 | } 32 | 33 | pragma(inline, true) bool likely(bool value) { 34 | version (LDC) { 35 | return llvm_expect!bool(value, true); 36 | } else { 37 | return value; 38 | } 39 | } 40 | 41 | pragma(inline, true) bool unlikely(bool value) { 42 | version (LDC) { 43 | return llvm_expect!bool(value, false); 44 | } else { 45 | return value; 46 | } 47 | } 48 | 49 | enum YELLOW = "\033[33m"; 50 | enum RED = "\033[31m"; 51 | enum RESET = "\033[0m"; 52 | 53 | static int verbosity_level = 0; 54 | 55 | // get nth bits from value as so: [start, end) 56 | // pragma(inline) uint get_nth_bits(uint val, ubyte start, ubyte end) { 57 | // return (val >> start) & ((1 << (end - start)) - 1); 58 | // } 59 | 60 | // get nth bit from value 61 | pragma(inline, true) bool get_nth_bit(uint val, ubyte n) { 62 | return (val >> n) & 1; 63 | } 64 | 65 | // sign extend the given value 66 | uint sign_extend(uint val, ubyte num_bits) { 67 | return (val ^ (1 << (num_bits - 1))) - (1 << (num_bits - 1)); 68 | } 69 | 70 | // a warning will not terminate the program 71 | void warning(string message) { 72 | stderr.writefln("%sWARNING: %s%s", YELLOW, RESET, message); 73 | } 74 | 75 | // an error terminates the program and calls exit(EXIT_FAILURE); 76 | void error(string message) { 77 | if (Logger.instance) { 78 | Logger.instance.print(); 79 | } 80 | stderr.writefln("%sERROR: %s%s", RED, RESET, message); 81 | 82 | assert(0, "terminating due to error"); 83 | } 84 | 85 | string to_hex_string(uint val) { 86 | return format("%x", val); 87 | } 88 | 89 | // get nth bits from value as so: [start, end) 90 | pragma(inline, true) pure int get_nth_bits(int val, const int start, const int end) { 91 | if (end - start == 32) return val; 92 | else return cast(uint)(cast(uint)(val) >> start) & ((1 << cast(uint)(end - start)) - 1); 93 | } 94 | 95 | // get nth bit from value 96 | // please inline this function for the love of god the profiler is screaming at you 97 | pragma(inline, true) pure bool get_nth_bit(int val, int n) { 98 | return (val >> n) & 1; 99 | } 100 | 101 | // sign extend the given value 102 | pragma(inline, true) pure int sign_extend(int val, int num_bits) { 103 | return (val ^ (1U << (num_bits - 1))) - (1U << (num_bits - 1)); 104 | } 105 | 106 | pragma(inline, true) pure uint rotate_right(uint val, int shift) { 107 | return (val >> shift) | (val << (32 - shift)); 108 | } 109 | 110 | ubyte[] load_rom_as_bytes(string rom_name) { 111 | File file = File(rom_name, "r"); 112 | auto buffer = new ubyte[file.size()]; 113 | file.rawRead(buffer); 114 | return buffer; 115 | } 116 | 117 | pragma(inline) double convert_from_8_8f_to_double(ushort input) { 118 | return ((cast(short) input) >> 8) + ((cast(double) (input & 0xFF)) / 256.0); 119 | } 120 | 121 | pragma(inline) ushort convert_from_double_to_8_8f(double input) { 122 | return (cast(ushort) ((cast(ushort) (input / 1)) << 8)) | ((cast(ushort) ((input % 1) * 256)) & 0xFF); 123 | } 124 | 125 | final class NSStopwatch { 126 | import core.time; 127 | 128 | this() { 129 | last_ticks = MonoTime.currTime(); 130 | } 131 | 132 | long update() { 133 | auto ticks = MonoTime.currTime(); 134 | auto elapsed_dur = ticks - last_ticks; 135 | long elapsed = elapsed_dur.total!"nsecs"; 136 | last_ticks = ticks; 137 | 138 | total_time += elapsed; 139 | 140 | return elapsed; 141 | } 142 | 143 | MonoTimeImpl!(ClockType.normal) last_ticks; 144 | long total_time = 0; 145 | } 146 | -------------------------------------------------------------------------------- /source/emu/core/save/backups/eeprom.d: -------------------------------------------------------------------------------- 1 | module save.backups.eeprom; 2 | 3 | import save.backups; 4 | 5 | import std.stdio; 6 | 7 | import util; 8 | 9 | final class EEPROM : Backup { 10 | private ubyte[] data; 11 | private uint address_mask; 12 | 13 | enum State { 14 | IDLE, 15 | READ, 16 | WRITE 17 | } 18 | 19 | int index; 20 | long command; 21 | ubyte[8] write_buf; 22 | int address; 23 | 24 | int address_length; 25 | 26 | State state = State.IDLE; 27 | 28 | uint size; 29 | 30 | this(uint size) { 31 | assert((size & (size - 1)) == 0); // size must be a power of 2 32 | 33 | if (size == 512) { 34 | address_length = 6; 35 | } else { 36 | address_length = 14; 37 | } 38 | 39 | this.data = new ubyte[size]; 40 | this.address_mask = size - 1; 41 | this.size = size; 42 | } 43 | 44 | void idle() { 45 | this.index = 0; 46 | this.command = 0; 47 | this.address = 0; 48 | this.state = State.IDLE; 49 | 50 | write_buf[0..8] = 0; 51 | } 52 | 53 | import std.stdio; 54 | void write(int bit) { 55 | final switch (state) { 56 | case State.IDLE: 57 | command |= bit << ((address_length + 1) - index++); 58 | if (index == address_length + 2) { 59 | if (((command >> address_length) & 3) == 2) { 60 | state = State.WRITE; 61 | } else if (((command >> address_length) & 3) == 3) { 62 | state = State.READ; 63 | } else { 64 | error("invalid eeprom command"); 65 | } 66 | 67 | address = cast(int) (command & ((1 << address_length) - 1)) * 8; 68 | index = 0; 69 | } 70 | break; 71 | 72 | case State.READ: 73 | break; 74 | 75 | case State.WRITE: 76 | if (index == 64) { 77 | data[address .. address + 8] = write_buf[0 .. 8]; 78 | 79 | // don't ask 80 | for (int i = 0; i < 8; i++) { 81 | backup_file[address + i] = write_buf[i]; 82 | } 83 | idle(); 84 | break; 85 | } 86 | 87 | int byte_index = index / 8; 88 | int offset = index % 8; 89 | write_buf[byte_index] |= bit << (7 - offset); 90 | index++; 91 | break; 92 | } 93 | } 94 | 95 | int read() { 96 | if (state != State.READ) { 97 | return 1; 98 | } 99 | 100 | if (index <= 3) { 101 | index++; 102 | return 0; 103 | } 104 | 105 | int byte_index = (index - 4) / 8; 106 | int offset = (index - 4) % 8; 107 | 108 | index++; 109 | 110 | bool return_value = (data[address + byte_index] >> (7 - offset)) & 1; 111 | 112 | if (index == 68) { 113 | idle(); 114 | } 115 | 116 | return return_value; 117 | } 118 | 119 | override uint read_word(uint address) { 120 | return cast(uint) read(); 121 | } 122 | 123 | override ushort read_half(uint address) { 124 | return cast(ushort) read(); 125 | } 126 | 127 | override ubyte read_byte(uint address) { 128 | return cast(ubyte) read(); 129 | } 130 | 131 | override void write_word(uint address, uint data) { 132 | write(data & 1); 133 | } 134 | 135 | override void write_half(uint address, ushort data) { 136 | write(data & 1); 137 | } 138 | 139 | override void write_byte(uint address, ubyte data) { 140 | write(data & 1); 141 | } 142 | 143 | override ubyte[] serialize() { 144 | return data; 145 | } 146 | 147 | override void deserialize(ubyte[] data) { 148 | this.data = data; 149 | } 150 | 151 | override BackupType get_backup_type() { 152 | return BackupType.EEPROM; 153 | } 154 | 155 | private ubyte read(uint address) { 156 | return this.data[address & address_mask]; 157 | } 158 | 159 | private void write(uint address, ubyte data) { 160 | this.data[address & address_mask] = data; 161 | } 162 | 163 | override int get_backup_size() { 164 | return size * 8; 165 | } 166 | } -------------------------------------------------------------------------------- /source/emu/common/source/abstracthw/cpu.d: -------------------------------------------------------------------------------- 1 | module abstracthw.cpu; 2 | 3 | import std.stdio; 4 | import abstracthw.memory; 5 | 6 | import std.meta; 7 | import util; 8 | 9 | alias pc = Alias!15; 10 | alias lr = Alias!14; 11 | alias sp = Alias!13; 12 | 13 | alias Reg = int; 14 | 15 | enum Flag { 16 | N = 31, 17 | Z = 30, 18 | C = 29, 19 | V = 28, 20 | T = 5 21 | } 22 | 23 | interface IARM7TDMI { 24 | Word get_reg(int i); 25 | Word get_reg(int i, CpuMode mode); 26 | void set_reg(int i, Word value); 27 | void set_reg(int i, Word value, CpuMode mode); 28 | 29 | Word get_cpsr(); 30 | Word get_spsr(); 31 | void set_cpsr(Word value); 32 | void set_spsr(Word value); 33 | 34 | InstructionSet get_instruction_set(); 35 | Word get_pipeline_entry(int i); 36 | bool check_cond(uint cond); 37 | void refill_pipeline(); 38 | void set_flag(Flag flag, bool value); 39 | bool get_flag(Flag flag); 40 | 41 | Word read_word(Word address, AccessType access_type); 42 | Half read_half(Word address, AccessType access_type); 43 | Byte read_byte(Word address, AccessType access_type); 44 | 45 | void write_word(Word address, Word value, AccessType access_type); 46 | void write_half(Word address, Half value, AccessType access_type); 47 | void write_byte(Word address, Byte value, AccessType access_type); 48 | 49 | void run_idle_cycle(); 50 | void set_pipeline_access_type(AccessType access_type); 51 | 52 | bool in_a_privileged_mode(); 53 | void update_mode(); 54 | bool has_spsr(); 55 | 56 | void raise_exception(CpuException exception)(); 57 | } 58 | 59 | // CPU modes will be described as the following: 60 | // 1) their encoding in the CPSR register 61 | // 2) their register uniqueness (see the diagram in arm7tdmi.h). 62 | // 3) their offset into the registers array. 63 | 64 | enum MODE_USER = CpuMode(0b10000, 0b011111111111111111, 18 * 0); 65 | enum MODE_SYSTEM = CpuMode(0b11111, 0b011111111111111111, 18 * 0); 66 | enum MODE_SUPERVISOR = CpuMode(0b10011, 0b011001111111111111, 18 * 1); 67 | enum MODE_ABORT = CpuMode(0b10111, 0b011001111111111111, 18 * 2); 68 | enum MODE_UNDEFINED = CpuMode(0b11011, 0b011001111111111111, 18 * 3); 69 | enum MODE_IRQ = CpuMode(0b10010, 0b011001111111111111, 18 * 4); 70 | enum MODE_FIQ = CpuMode(0b10001, 0b011000000011111111, 18 * 5); 71 | 72 | static immutable CpuMode[7] MODES = [ 73 | MODE_USER, MODE_FIQ, MODE_IRQ, MODE_SUPERVISOR, MODE_ABORT, MODE_UNDEFINED, 74 | MODE_SYSTEM 75 | ]; 76 | 77 | enum InstructionSet { 78 | ARM, 79 | THUMB 80 | } 81 | 82 | struct CpuMode { 83 | this(const(int) c, const(int) r, const(int) o) { 84 | CPSR_ENCODING = c; 85 | REGISTER_UNIQUENESS = r; 86 | OFFSET = o; 87 | } 88 | 89 | int CPSR_ENCODING; 90 | int REGISTER_UNIQUENESS; 91 | int OFFSET; 92 | } 93 | 94 | enum CpuException { 95 | Reset, 96 | Undefined, 97 | SoftwareInterrupt, 98 | PrefetchAbort, 99 | DataAbort, 100 | IRQ, 101 | FIQ 102 | } 103 | 104 | struct CpuState { 105 | InstructionSet instruction_set; 106 | uint opcode; 107 | uint[16] regs; 108 | uint mode; 109 | uint mem_0x03000003; 110 | } 111 | 112 | CpuState get_cpu_state(IARM7TDMI cpu) { 113 | CpuState cpu_state; 114 | cpu_state.instruction_set = cpu.get_instruction_set(); 115 | cpu_state.opcode = cpu.get_pipeline_entry(0); 116 | cpu_state.mode = cpu.get_cpsr(); 117 | cpu_state.mem_0x03000003 = cpu.read_byte(0x03000003, AccessType.NONSEQUENTIAL); 118 | 119 | for (int i = 0; i < 16; i++) { 120 | cpu_state.regs[i] = cpu.get_reg(i); 121 | } 122 | 123 | cpu_state.regs[15] -= cpu_state.instruction_set == InstructionSet.ARM ? 4 : 2; 124 | 125 | return cpu_state; 126 | } 127 | 128 | void set_cpu_state(IARM7TDMI cpu, IMemory memory, CpuState cpu_state) { 129 | for (int i = 0; i < 16; i++) { 130 | cpu.set_reg(i, cpu_state.regs[i]); 131 | } 132 | 133 | // *cpu.cpsr = (*cpu.cpsr & 0xFFFFFFE0) | (cpu_state.mode & 0x1F); 134 | // cpu.update_mode(); 135 | 136 | memory.write_byte(cast(uint) 0x03000003, cast(ubyte) cpu_state.mem_0x03000003); 137 | } 138 | -------------------------------------------------------------------------------- /source/xcat/xcat.c: -------------------------------------------------------------------------------- 1 | /* 2 | xcat (v0.2) - a simple cat clone with xor key 3 | 4 | based on Plan9 cat: 5 | https://gist.githubusercontent.com/pete/665971/raw/b0bdaf46ac74703ebbece96eeacdece2e5217fa2/plan9-cat.c 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /** xor a buffer of length n in-place by a key of length k */ 15 | void xor_buf(uint8_t *buf, uint8_t *key, size_t n, size_t key_len) { 16 | int ki = 0; // key index 17 | for (int i = 0; i < n; i++) { 18 | uint8_t k = key[ki]; // active byte of key 19 | buf[i] = buf[i] ^ k; // xor 20 | ki = (ki + 1) % key_len; // cycle through key 21 | } 22 | } 23 | 24 | /** the main xcat program: read from file in chunks, xor, then write to stdout */ 25 | void xcat(FILE *f, uint8_t *key, size_t key_len, char *s) { 26 | uint8_t buf[65536]; // buffer size for read/write chunks 27 | size_t n; 28 | 29 | while ((n = fread(buf, sizeof(uint8_t), sizeof(buf), f)) > 0) { 30 | xor_buf(buf, key, n, key_len); 31 | if (fwrite(buf, sizeof(uint8_t), n, stdout) != n) { 32 | fprintf(stderr, "write error copying %s: %d", s, errno); 33 | exit(1); 34 | } 35 | } 36 | if (n < 0) { 37 | fprintf(stderr, "error reading %s: %d", s, errno); 38 | exit(1); 39 | } 40 | } 41 | 42 | // https://stackoverflow.com/questions/3408706/hexadecimal-string-to-byte-array-in-c/35452093#35452093 43 | typedef struct HexData { 44 | uint8_t *data; 45 | size_t len; 46 | } HexData; 47 | HexData hex2bytes(char *str) { 48 | 49 | if (str == NULL) 50 | return (HexData){.data = NULL, .len = 0}; 51 | 52 | size_t slength = strlen(str); 53 | if ((slength % 2) != 0) // must be even 54 | return (HexData){.data = NULL, .len = 0}; 55 | 56 | size_t dlength = slength / 2; 57 | 58 | uint8_t *data = malloc(dlength); 59 | memset(data, 0, dlength); 60 | 61 | size_t index = 0; 62 | while (index < slength) { 63 | uint8_t c = str[index]; 64 | int value = 0; 65 | if (c >= '0' && c <= '9') 66 | value = (c - '0'); 67 | else if (c >= 'A' && c <= 'F') 68 | value = (10 + (c - 'A')); 69 | else if (c >= 'a' && c <= 'f') 70 | value = (10 + (c - 'a')); 71 | else { 72 | free(data); 73 | return (HexData){.data = NULL, .len = 0}; 74 | } 75 | 76 | data[(index / 2)] += value << (((index + 1) % 2) * 4); 77 | 78 | index++; 79 | } 80 | 81 | return (HexData){.data = data, .len = dlength}; 82 | } 83 | 84 | int main(int argc, char *argv[]) { 85 | int i; 86 | FILE *f; 87 | uint8_t *key; 88 | size_t key_len; 89 | 90 | argv[0] = "xcat"; 91 | if (argc == 1) { 92 | fprintf(stderr, "Usage: xcat [file]\n a simple cat clone with xor key"); 93 | exit(2); 94 | } 95 | 96 | char *keyarg = argv[1]; 97 | 98 | // check whether key is '!xxxx' format (hex) 99 | // otherwise interpret key as '1234' format (uint64) 100 | if (keyarg[0] == '!') { 101 | char *hexkey = keyarg + 1; 102 | if (strlen(hexkey) == 0) { 103 | fprintf(stderr, "invalid key: empty"); 104 | exit(2); 105 | } 106 | HexData hex_data = hex2bytes(hexkey); 107 | if (hex_data.data == NULL) { 108 | fprintf(stderr, "invalid key: %s", hexkey); 109 | exit(2); 110 | } 111 | key = hex_data.data; 112 | key_len = hex_data.len; 113 | } else { 114 | const int num_size = 8; // uint64 size 115 | uint64_t key_num = atol(keyarg); 116 | key = malloc(num_size); 117 | key_len = num_size; 118 | // copy to key buffer 119 | for (int i = 0; i < num_size; i++) { 120 | key[num_size - (i + 1)] = (key_num >> (num_size * i)) & 0xff; 121 | } 122 | } 123 | 124 | if (argc == 2) 125 | xcat(stdin, key, key_len, ""); 126 | else 127 | for (i = 2; i < argc; i++) { 128 | f = fopen(argv[i], "r"); 129 | if (f == NULL) { 130 | fprintf(stderr, "can't open %s: %d", argv[i], errno); 131 | exit(1); 132 | } else { 133 | xcat(f, key, key_len, argv[i]); 134 | fclose(f); 135 | } 136 | } 137 | exit(0); 138 | } 139 | -------------------------------------------------------------------------------- /source/emu/core/hw/apu/channels/noise_channel.d: -------------------------------------------------------------------------------- 1 | module hw.apu.channels.noise_channel; 2 | 3 | // import apu.channels.noise_lut; 4 | import scheduler; 5 | 6 | import std.stdio; 7 | import std.algorithm.comparison; 8 | 9 | final class NoiseChannel { 10 | private int shift_register = 0; 11 | private int reload_value = 0; 12 | private int shifter_xor_value = 0; 13 | 14 | public int dividing_ratio = 0; 15 | public int shift_clock_frequency = 0; 16 | private int volume = 0; 17 | 18 | private int cycles_elapsed = 0; 19 | private long length; 20 | public bool enabled = false; 21 | public bool envelope_enabled = false; 22 | private bool should_turn_off_when_length_elapses = false; 23 | 24 | // private int sound_length; 25 | // private bool stop_on_expire; 26 | 27 | private int interval = 1000; 28 | 29 | private int current_shifter_out = -1; 30 | 31 | private Scheduler scheduler; 32 | private ulong shifter_event; 33 | private ulong envelope_event; 34 | 35 | private int envelope_length; 36 | private int envelope_multiplier; 37 | 38 | this(Scheduler scheduler) { 39 | this.scheduler = scheduler; 40 | reload(); 41 | } 42 | 43 | void reload() { 44 | shift_register = reload_value; 45 | cycles_elapsed = 0; 46 | 47 | if (shifter_event) scheduler.remove_event(shifter_event); 48 | if (enabled) shifter_event = scheduler.add_event_relative_to_clock(&shift, interval); 49 | 50 | if (envelope_event) scheduler.remove_event(envelope_event); 51 | if (envelope_enabled) envelope_event = scheduler.add_event_relative_to_clock(&tick_envelope, envelope_length); 52 | } 53 | 54 | short sample(int delta_cycles) { 55 | if (!enabled) return 0; 56 | 57 | cycles_elapsed += delta_cycles; 58 | if (should_turn_off_when_length_elapses && cycles_elapsed > length) enabled = false; 59 | 60 | return cast(short) (current_shifter_out * 8 * volume); 61 | } 62 | 63 | void shift() { 64 | bool carry = shift_register & 1; 65 | shift_register >>= 1; 66 | 67 | if (carry) { 68 | current_shifter_out = 1; 69 | shift_register ^= shifter_xor_value; 70 | } else { 71 | current_shifter_out = -1; 72 | } 73 | 74 | shifter_event = scheduler.add_event_relative_to_self(&shift, interval); 75 | } 76 | 77 | void tick_envelope() { 78 | this.volume = clamp(this.volume + this.envelope_multiplier, 0, 15); 79 | 80 | if (envelope_enabled) envelope_event = scheduler.add_event_relative_to_self(&tick_envelope, envelope_length); 81 | } 82 | 83 | void set_counter_width(int counter_width) { 84 | this.reload_value = counter_width == 1 ? 0x40 : 0x4000; 85 | this.shifter_xor_value = counter_width == 1 ? 0x60 : 0x6000; 86 | // reload(); 87 | enabled = true; 88 | } 89 | 90 | void set_dividing_ratio(int dividing_ratio) { 91 | this.dividing_ratio = dividing_ratio; 92 | recalculate_interval(); 93 | } 94 | 95 | void set_shift_clock_frequency(int shift_clock_frequency) { 96 | this.shift_clock_frequency = shift_clock_frequency; 97 | recalculate_interval(); 98 | } 99 | 100 | void recalculate_interval() { 101 | if (dividing_ratio == 0) { 102 | interval = (8 << shift_clock_frequency) * 4; 103 | } else { 104 | interval = ((dividing_ratio * 16) << shift_clock_frequency) * 4; 105 | } 106 | } 107 | 108 | // where n is bits 0-5 of SOUND4CNT_L 109 | void set_length(int n) { 110 | length = 65546 * (64 - n); 111 | } 112 | 113 | void set_envelope_length(int n) { 114 | envelope_enabled = false;// n != 0; 115 | this.envelope_length = 262144 * n; 116 | } 117 | 118 | void set_envelope_direction(bool direction) { 119 | this.envelope_multiplier = direction ? 1 : -1; 120 | } 121 | 122 | void set_volume(int volume) { 123 | this.volume = cast(short) volume; 124 | // enabled = volume != 0; 125 | } 126 | 127 | void set_should_turn_off_when_length_elapses(bool should_turn_off_when_length_elapses) { 128 | this.should_turn_off_when_length_elapses = should_turn_off_when_length_elapses; 129 | } 130 | 131 | void restart() { 132 | enabled = true; 133 | cycles_elapsed = 0; 134 | reload(); 135 | } 136 | } -------------------------------------------------------------------------------- /source/ui/reng/device.d: -------------------------------------------------------------------------------- 1 | module ui.reng.device; 2 | 3 | import hw.gba; 4 | import hw.keyinput; 5 | 6 | import ui.device; 7 | import ui.reng; 8 | 9 | import std.format; 10 | import std.string; 11 | 12 | import raylib; 13 | import re; 14 | 15 | class RengMultimediaDevice : MultiMediaDevice { 16 | enum SAMPLE_RATE = 48000; 17 | enum SAMPLES_PER_UPDATE = 4096; 18 | enum BUFFER_SIZE_MULTIPLIER = 1.5; 19 | enum NUM_CHANNELS = 2; 20 | 21 | enum FAST_FOWARD_KEY = Keys.KEY_TAB; 22 | 23 | GBA gba; 24 | RengCore reng_core; 25 | GBAVideo gba_video; 26 | AudioStream stream; 27 | 28 | bool fast_forward; 29 | 30 | string rom_title; 31 | int fps; 32 | 33 | this(GBA gba, int screen_scale) { 34 | this.gba = gba; 35 | 36 | Core.target_fps = 60; 37 | reng_core = new RengCore(gba, screen_scale); 38 | 39 | InitAudioDevice(); 40 | SetAudioStreamBufferSizeDefault(SAMPLES_PER_UPDATE); 41 | stream = LoadAudioStream(SAMPLE_RATE, 16, NUM_CHANNELS); 42 | PlayAudioStream(stream); 43 | 44 | gba_video = Core.jar.resolve!GBAVideo().get; 45 | } 46 | 47 | override { 48 | // video stuffs 49 | void present_videobuffers(Pixel[160][240] buffer) { 50 | for (int y = 0; y < 160; y++) { 51 | for (int x = 0; x < 240; x++) { 52 | gba_video.videobuffer[y * 240 + x] = 53 | (buffer[x][y].r << 3 << 0) | 54 | (buffer[x][y].g << 3 << 8) | 55 | (buffer[x][y].b << 3 << 16) | 56 | 0xFF000000; 57 | } 58 | } 59 | } 60 | 61 | void set_fps(int fps) { 62 | this.fps = fps; 63 | redraw_title(); 64 | } 65 | 66 | void update_rom_title(string rom_title) { 67 | import std.string; 68 | this.rom_title = rom_title.splitLines[0].strip; 69 | redraw_title(); 70 | } 71 | 72 | void update_icon(Pixel[32][32] buffer_texture) { 73 | 74 | } 75 | 76 | // 2 cuz stereo 77 | short[cast(ulong) (NUM_CHANNELS * SAMPLES_PER_UPDATE * BUFFER_SIZE_MULTIPLIER)] buffer; 78 | int buffer_cursor = 0; 79 | 80 | void push_sample(Sample s) { 81 | if (fast_forward) buffer_cursor = 0; 82 | 83 | buffer[buffer_cursor + 0] = s.L; 84 | buffer[buffer_cursor + 1] = s.R; 85 | buffer_cursor += 2; 86 | } 87 | 88 | void update() { 89 | handle_input(); 90 | handle_audio(); 91 | reng_core.update_pub(); 92 | } 93 | 94 | void draw() { 95 | reng_core.draw_pub(); 96 | } 97 | 98 | bool should_cycle_gba() { 99 | return buffer_cursor < cast(ulong) (NUM_CHANNELS * BUFFER_SIZE_MULTIPLIER * SAMPLES_PER_UPDATE - (SAMPLE_RATE / 60) * 2); 100 | } 101 | 102 | void handle_input() { 103 | // ignore input if console is open 104 | if (Core.inspector_overlay.console.open) return; 105 | 106 | static foreach (re_key, gba_key; keys) { 107 | update_key(gba_key, Input.is_key_down(re_key)); 108 | } 109 | 110 | fast_forward = Input.is_key_down(FAST_FOWARD_KEY); 111 | } 112 | 113 | bool should_fast_forward() { 114 | return fast_forward; 115 | } 116 | } 117 | 118 | void redraw_title() { 119 | import std.format; 120 | gba_video.update_title("%s [FPS: %d]".format(rom_title, fps)); 121 | } 122 | 123 | void handle_audio() { 124 | if (IsAudioStreamProcessed(stream)) { 125 | UpdateAudioStream(stream, cast(void*) buffer, SAMPLES_PER_UPDATE); 126 | 127 | for (int i = 0; i < NUM_CHANNELS * SAMPLES_PER_UPDATE * (BUFFER_SIZE_MULTIPLIER - 1); i++) { 128 | buffer[i] = buffer[i + NUM_CHANNELS * SAMPLES_PER_UPDATE]; 129 | } 130 | 131 | buffer_cursor -= NUM_CHANNELS * SAMPLES_PER_UPDATE; 132 | if (buffer_cursor < 0) buffer_cursor = 0; 133 | 134 | if (fast_forward) buffer_cursor = 0; 135 | } 136 | } 137 | 138 | enum keys = [ 139 | Keys.KEY_Z : GBAKeyVanilla.A, 140 | Keys.KEY_X : GBAKeyVanilla.B, 141 | Keys.KEY_ENTER : GBAKeyVanilla.SELECT, 142 | Keys.KEY_SPACE : GBAKeyVanilla.START, 143 | Keys.KEY_RIGHT : GBAKeyVanilla.RIGHT, 144 | Keys.KEY_LEFT : GBAKeyVanilla.LEFT, 145 | Keys.KEY_UP : GBAKeyVanilla.UP, 146 | Keys.KEY_DOWN : GBAKeyVanilla.DOWN, 147 | Keys.KEY_S : GBAKeyVanilla.R, 148 | Keys.KEY_A : GBAKeyVanilla.L 149 | ]; 150 | } -------------------------------------------------------------------------------- /source/emu/core/scheduler.d: -------------------------------------------------------------------------------- 1 | module scheduler; 2 | 3 | import util; 4 | import hw.cpu.arm7tdmi; 5 | import hw.memory; 6 | 7 | import std.stdio; 8 | 9 | import diag.log; 10 | 11 | struct Event { 12 | void delegate() callback; 13 | ulong timestamp; 14 | ulong id; 15 | bool completed; 16 | bool can_be_interleaved; 17 | } 18 | 19 | final class Scheduler { 20 | enum TOTAL_NUMBER_OF_EVENTS = 0x100; 21 | Event*[TOTAL_NUMBER_OF_EVENTS] events; 22 | int events_in_queue = 0; 23 | int unprocessed_head = 0; 24 | ulong id_counter = 0; 25 | 26 | ulong current_timestamp; 27 | 28 | Memory memory; 29 | 30 | this(Memory memory) { 31 | for (int i = 0; i < TOTAL_NUMBER_OF_EVENTS; i++) { 32 | events[i] = new Event(null, 0, 0, false); 33 | } 34 | 35 | events_in_queue = 0; 36 | current_timestamp = 0; 37 | 38 | this.memory = memory; 39 | } 40 | 41 | ulong add_event_relative_to_clock(void delegate() callback, int delta_cycles, bool can_be_interleaved = false) { 42 | return add_event(callback, current_timestamp + delta_cycles, can_be_interleaved); 43 | } 44 | 45 | ulong add_event_relative_to_self(void delegate() callback, int delta_cycles, bool can_be_interleaved = false) { 46 | return add_event(callback, events[num_events_being_processed - 1].timestamp + delta_cycles, can_be_interleaved); 47 | } 48 | 49 | private ulong add_event(void delegate() callback, ulong timestamp, bool can_be_interleaved) { 50 | 51 | int insert_at; 52 | // TODO: use binary search 53 | for (; insert_at < events_in_queue; insert_at++) { 54 | if (timestamp < events[insert_at].timestamp) { 55 | break; 56 | } 57 | } 58 | 59 | for (int i = events_in_queue; i > insert_at; i--) { 60 | *events[i] = *events[i - 1]; 61 | } 62 | 63 | id_counter++; 64 | events_in_queue++; 65 | *events[insert_at] = Event(callback, timestamp, id_counter, false, can_be_interleaved); 66 | 67 | return id_counter; 68 | } 69 | 70 | void remove_event(ulong event_id) { 71 | int remove_at = -1; 72 | for (int i = 0; i < events_in_queue; i++) { 73 | if (events[i].id == event_id) { 74 | remove_at = i; 75 | break; 76 | } 77 | } 78 | 79 | if (remove_at == -1) return; 80 | 81 | for (int i = remove_at; i < events_in_queue; i++) { 82 | *events[i] = *events[i + 1]; 83 | } 84 | 85 | events_in_queue--; 86 | } 87 | 88 | pragma(inline, true) void tick(ulong num_cycles) { 89 | // if (_g_num_log > 0) log!(LogSource.DEBUG)("Scheduler ticking for %d cycles", num_cycles); 90 | current_timestamp += num_cycles; 91 | } 92 | 93 | pragma(inline, true) void tick_to_next_event() { 94 | current_timestamp = events[0].timestamp; 95 | } 96 | 97 | void maybe_run_event(ulong event_id) { 98 | for (int i = num_events_being_processed; i < events_in_queue; i++) { 99 | if (!events[i].timestamp == current_timestamp) return; 100 | 101 | if (events[i].id == event_id) { 102 | num_events_being_processed++; 103 | events[i].callback(); 104 | remove_event(event_id); 105 | num_events_being_processed--; 106 | } 107 | } 108 | } 109 | 110 | pragma(inline, true) void process_events() { 111 | bool can_interleave = (num_events_being_processed > 0) ? events[num_events_being_processed - 1].can_be_interleaved : false; 112 | while ((can_interleave || num_events_being_processed == 0) && current_timestamp >= events[num_events_being_processed].timestamp) process_event(); 113 | } 114 | 115 | pragma(inline, true) ulong get_current_time() { 116 | return current_timestamp; 117 | } 118 | 119 | pragma(inline, true) ulong get_current_time_relative_to_cpu() { 120 | return current_timestamp; 121 | } 122 | 123 | pragma(inline, true) ulong get_current_time_relative_to_self() { 124 | return events[num_events_being_processed - 1].timestamp; 125 | } 126 | 127 | uint num_events_being_processed = 0; 128 | pragma(inline, true) void process_event() { 129 | bool can_interleave = (num_events_being_processed > 0) ? events[num_events_being_processed - 1].can_be_interleaved : true; 130 | if (!can_interleave) return; 131 | 132 | num_events_being_processed++; 133 | events[num_events_being_processed - 1].callback(); 134 | 135 | for (int i = num_events_being_processed - 1; i < events_in_queue; i++) { 136 | *events[i] = *events[i + 1]; 137 | } 138 | 139 | events_in_queue--; 140 | 141 | num_events_being_processed--; 142 | } 143 | } -------------------------------------------------------------------------------- /tests/asm/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean 2 | 3 | SRC_DIR := src 4 | OUT_DIR := out 5 | BIN_DIR := bin 6 | LOG_DIR := logs 7 | 8 | COMPILER := arm-none-eabi-gcc 9 | COMPILER_FLAGS := -mthumb-interwork -specs=gba.specs -B /opt/devkitpro/devkitARM/arm-none-eabi/lib/ -lsysbase 10 | LINKER := arm-none-eabi-objcopy 11 | LINKER_FLAGS := 12 | 13 | all: $(LOG_DIR)/thumb-simple.log \ 14 | $(LOG_DIR)/arm-addressing-mode-1.log \ 15 | $(LOG_DIR)/arm-addressing-mode-2.log \ 16 | $(LOG_DIR)/arm-addressing-mode-3.log \ 17 | $(LOG_DIR)/arm-opcodes.log 18 | 19 | clean: 20 | rm -f $(OUT_DIR)/* 21 | rm -f $(BIN_DIR)/* 22 | 23 | 24 | 25 | 26 | 27 | 28 | $(LOG_DIR)/thumb-simple.log: $(SRC_DIR)/thumb-simple.S 29 | # compile and link the files 30 | $(COMPILER) $(COMPILER_FLAGS) $(SRC_DIR)/thumb-simple.S -o $(OUT_DIR)/thumb-simple.out 31 | $(LINKER) $(LINKER_FLAGS) $(OUT_DIR)/thumb-simple.out -O binary $(BIN_DIR)/thumb-simple.gba 32 | 33 | # fix the gba to give it a valid checksum 34 | gbafix $(BIN_DIR)/thumb-simple.gba 35 | 36 | # generate the instruction logs 37 | NanoBoyAdvance --num_instructions 200500 $(BIN_DIR)/thumb-simple.gba 38 | @sed -n -i.bak -e '196634,200500p' out.log 39 | @rm out.log.bak 40 | mv out.log $(LOG_DIR)/thumb-simple.log 41 | 42 | 43 | 44 | 45 | 46 | 47 | $(LOG_DIR)/arm-addressing-mode-1.log: $(SRC_DIR)/arm-addressing-mode-1.S 48 | # compile and link the files 49 | $(COMPILER) $(COMPILER_FLAGS) $(SRC_DIR)/arm-addressing-mode-1.S -o $(OUT_DIR)/arm-addressing-mode-1.out 50 | $(LINKER) $(LINKER_FLAGS) $(OUT_DIR)/arm-addressing-mode-1.out -O binary $(BIN_DIR)/arm-addressing-mode-1.gba 51 | 52 | # fix the gba to give it a valid checksum 53 | gbafix $(BIN_DIR)/arm-addressing-mode-1.gba 54 | 55 | # generate the instruction logs 56 | NanoBoyAdvance --num_instructions 197924 $(BIN_DIR)/arm-addressing-mode-1.gba 57 | @sed -n -i.bak -e '196634,197924p' out.log 58 | @rm out.log.bak 59 | mv out.log $(LOG_DIR)/arm-addressing-mode-1.log 60 | 61 | 62 | 63 | 64 | 65 | 66 | $(LOG_DIR)/arm-addressing-mode-2.log: $(SRC_DIR)/arm-addressing-mode-2.S 67 | # compile and link the files 68 | $(COMPILER) $(COMPILER_FLAGS) $(SRC_DIR)/arm-addressing-mode-2.S -o $(OUT_DIR)/arm-addressing-mode-2.out 69 | $(LINKER) $(LINKER_FLAGS) $(OUT_DIR)/arm-addressing-mode-2.out -O binary $(BIN_DIR)/arm-addressing-mode-2.gba 70 | 71 | # fix the gba to give it a valid checksum 72 | gbafix $(BIN_DIR)/arm-addressing-mode-2.gba 73 | 74 | # generate the instruction logs 75 | NanoBoyAdvance --num_instructions 197924 $(BIN_DIR)/arm-addressing-mode-2.gba 76 | @sed -n -i.bak -e '196634,197924p' out.log 77 | @rm out.log.bak 78 | mv out.log $(LOG_DIR)/arm-addressing-mode-2.log 79 | 80 | 81 | 82 | 83 | 84 | 85 | $(LOG_DIR)/arm-addressing-mode-3.log: $(SRC_DIR)/arm-addressing-mode-3.S 86 | # compile and link the files 87 | $(COMPILER) $(COMPILER_FLAGS) $(SRC_DIR)/arm-addressing-mode-3.S -o $(OUT_DIR)/arm-addressing-mode-3.out 88 | $(LINKER) $(LINKER_FLAGS) $(OUT_DIR)/arm-addressing-mode-3.out -O binary $(BIN_DIR)/arm-addressing-mode-3.gba 89 | 90 | # fix the gba to give it a valid checksum 91 | gbafix $(BIN_DIR)/arm-addressing-mode-3.gba 92 | 93 | # generate the instruction logs 94 | NanoBoyAdvance --num_instructions 197924 $(BIN_DIR)/arm-addressing-mode-3.gba 95 | @sed -n -i.bak -e '196634,197924p' out.log 96 | @rm out.log.bak 97 | mv out.log $(LOG_DIR)/arm-addressing-mode-3.log 98 | 99 | 100 | 101 | 102 | 103 | 104 | $(LOG_DIR)/arm-opcodes.log: $(SRC_DIR)/arm-opcodes.S 105 | # compile and link the files 106 | $(COMPILER) $(COMPILER_FLAGS) $(SRC_DIR)/arm-opcodes.S -o $(OUT_DIR)/arm-opcodes.out 107 | $(LINKER) $(LINKER_FLAGS) $(OUT_DIR)/arm-opcodes.out -O binary $(BIN_DIR)/arm-opcodes.gba 108 | 109 | # fix the gba to give it a valid checksum 110 | gbafix $(BIN_DIR)/arm-opcodes.gba 111 | 112 | # generate the instruction logs 113 | NanoBoyAdvance --num_instructions 198834 $(BIN_DIR)/arm-opcodes.gba 114 | @sed -n -i.bak -e '196634,198834p' out.log 115 | @rm out.log.bak 116 | mv out.log $(LOG_DIR)/arm-opcodes.log 117 | 118 | 119 | red-screen: $(SRC_DIR)/red-screen.S 120 | $(COMPILER) $(COMPILER_FLAGS) $(SRC_DIR)/red-screen.S -o $(OUT_DIR)/red-screen.out 121 | $(LINKER) $(LINKER_FLAGS) $(OUT_DIR)/red-screen.out -O binary $(BIN_DIR)/red-screen.gba 122 | 123 | texan-frog: $(SRC_DIR)/texan-frog.S 124 | $(COMPILER) $(COMPILER_FLAGS) $(SRC_DIR)/texan-frog.S -o $(OUT_DIR)/texan-frog.out 125 | $(LINKER) $(LINKER_FLAGS) $(OUT_DIR)/texan-frog.out -O binary $(BIN_DIR)/texan-frog.gba 126 | 127 | mode4: $(SRC_DIR)/mode4.S 128 | $(COMPILER) $(COMPILER_FLAGS) $(SRC_DIR)/mode4.S -o $(OUT_DIR)/mode4.out 129 | $(LINKER) $(LINKER_FLAGS) $(OUT_DIR)/mode4.out -O binary $(BIN_DIR)/mode4.gba 130 | 131 | bitmap-test: $(SRC_DIR)/mode4.S 132 | $(COMPILER) $(COMPILER_FLAGS) $(SRC_DIR)/bitmap-test.S -o $(OUT_DIR)/bitmap-test.out 133 | $(LINKER) $(LINKER_FLAGS) $(OUT_DIR)/bitmap-test.out -O binary $(BIN_DIR)/bitmap-test.gba -------------------------------------------------------------------------------- /imgui/dimgui/examples/memory/window.d: -------------------------------------------------------------------------------- 1 | module window; 2 | 3 | /** 4 | Contains various helpers, common code, and initialization routines. 5 | */ 6 | 7 | import std.algorithm : min; 8 | import std.exception : enforce; 9 | import std.functional : toDelegate; 10 | import std.stdio : stderr; 11 | import std.string : format; 12 | 13 | import deimos.glfw.glfw3; 14 | 15 | import glad.gl.enums; 16 | import glad.gl.ext; 17 | import glad.gl.funcs; 18 | import glad.gl.loader; 19 | import glad.gl.types; 20 | 21 | import glwtf.input; 22 | import glwtf.window; 23 | 24 | /// init 25 | shared static this() 26 | { 27 | enforce(glfwInit()); 28 | } 29 | 30 | /// uninit 31 | shared static ~this() 32 | { 33 | glfwTerminate(); 34 | } 35 | 36 | /// 37 | enum WindowMode 38 | { 39 | fullscreen, 40 | windowed, 41 | } 42 | 43 | /** 44 | Create a window, an OpenGL 3.x context, and set up some other 45 | common routines for error handling, window resizing, etc. 46 | */ 47 | Window createWindow(string windowName, WindowMode windowMode = WindowMode.windowed, int width = 1024, int height = 768) 48 | { 49 | auto vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); 50 | 51 | // constrain the window size so it isn't larger than the desktop size. 52 | width = min(width, vidMode.width); 53 | height = min(height, vidMode.height); 54 | 55 | // set the window to be initially inivisible since we're repositioning it. 56 | glfwWindowHint(GLFW_VISIBLE, 0); 57 | 58 | // enable debugging 59 | glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1); 60 | 61 | Window window = createWindowContext(windowName, WindowMode.windowed, width, height); 62 | 63 | // center the window on the screen 64 | glfwSetWindowPos(window.window, (vidMode.width - width) / 2, (vidMode.height - height) / 2); 65 | 66 | // glfw-specific error routine (not a generic GL error handler) 67 | register_glfw_error_callback(&glfwErrorCallback); 68 | 69 | // anti-aliasing number of samples. 70 | window.samples = 4; 71 | 72 | // activate an opengl context. 73 | window.make_context_current(); 74 | 75 | // load all OpenGL function pointers via glad. 76 | enforce(gladLoadGL()); 77 | 78 | enforce(glGenBuffers !is null); 79 | 80 | // only interested in GL 3.x 81 | enforce(GLVersion.major >= 3); 82 | 83 | // turn v-sync off. 84 | glfwSwapInterval(0); 85 | 86 | // ensure the debug output extension is supported 87 | enforce(GL_ARB_debug_output || GL_KHR_debug); 88 | 89 | // cast: workaround for 'nothrow' propagation bug (haven't been able to reduce it) 90 | auto hookDebugCallback = GL_ARB_debug_output ? glDebugMessageCallbackARB 91 | : cast(typeof(glDebugMessageCallbackARB))glDebugMessageCallback; 92 | 93 | 94 | // hook the debug callback 95 | // cast: when using derelict it assumes its nothrow 96 | hookDebugCallback(cast(GLDEBUGPROCARB)&glErrorCallback, null); 97 | 98 | // enable proper stack tracing support (otherwise we'd get random failures at runtime) 99 | glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); 100 | 101 | // finally show the window 102 | glfwShowWindow(window.window); 103 | 104 | return window; 105 | } 106 | 107 | /** Create a window and an OpenGL context. */ 108 | Window createWindowContext(string windowName, WindowMode windowMode, int width, int height) 109 | { 110 | auto window = new Window(); 111 | auto monitor = windowMode == WindowMode.fullscreen ? glfwGetPrimaryMonitor() : null; 112 | auto context = window.create_highest_available_context(width, height, windowName, monitor, null, GLFW_OPENGL_CORE_PROFILE); 113 | 114 | // ensure we've loaded a proper context 115 | enforce(context.major >= 3); 116 | 117 | return window; 118 | } 119 | 120 | /** Just emit errors to stderr on GLFW errors. */ 121 | void glfwErrorCallback(int code, string msg) 122 | { 123 | stderr.writefln("Error (%s): %s", code, msg); 124 | } 125 | 126 | /// 127 | class GLException : Exception 128 | { 129 | @safe pure nothrow this(string msg = "", string file = __FILE__, size_t line = __LINE__, Throwable next = null) 130 | { 131 | super(msg, file, line, next); 132 | } 133 | 134 | @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) 135 | { 136 | super(msg, file, line, next); 137 | } 138 | } 139 | 140 | /** 141 | GL_ARB_debug_output or GL_KHR_debug callback. 142 | 143 | Throwing exceptions across language boundaries is ok as 144 | long as $(B GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) is enabled. 145 | */ 146 | extern (System) 147 | private void glErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, in GLchar* message, GLvoid* userParam) 148 | { 149 | //string msg = format("glErrorCallback: source: %s, type: %s, id: %s, severity: %s, length: %s, message: %s, userParam: %s", 150 | // source, type, id, severity, length, message.to!string, userParam); 151 | 152 | //stderr.writeln(msg); 153 | } 154 | -------------------------------------------------------------------------------- /imgui/dimgui/examples/demo/window.d: -------------------------------------------------------------------------------- 1 | module window; 2 | 3 | /** 4 | Contains various helpers, common code, and initialization routines. 5 | */ 6 | 7 | import std.algorithm : min; 8 | import std.exception : enforce; 9 | import std.functional : toDelegate; 10 | import std.stdio : stderr; 11 | import std.string : format; 12 | 13 | import deimos.glfw.glfw3; 14 | 15 | import glad.gl.enums; 16 | import glad.gl.ext; 17 | import glad.gl.funcs; 18 | import glad.gl.loader; 19 | import glad.gl.types; 20 | 21 | import glwtf.input; 22 | import glwtf.window; 23 | 24 | /// init 25 | shared static this() 26 | { 27 | enforce(glfwInit()); 28 | } 29 | 30 | /// uninit 31 | shared static ~this() 32 | { 33 | glfwTerminate(); 34 | } 35 | 36 | /// 37 | enum WindowMode 38 | { 39 | fullscreen, 40 | windowed, 41 | } 42 | 43 | /** 44 | Create a window, an OpenGL 3.x context, and set up some other 45 | common routines for error handling, window resizing, etc. 46 | */ 47 | Window createWindow(string windowName, WindowMode windowMode = WindowMode.windowed, int width = 1024, int height = 768) 48 | { 49 | auto vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); 50 | 51 | // constrain the window size so it isn't larger than the desktop size. 52 | width = min(width, vidMode.width); 53 | height = min(height, vidMode.height); 54 | 55 | // set the window to be initially inivisible since we're repositioning it. 56 | glfwWindowHint(GLFW_VISIBLE, 0); 57 | 58 | // enable debugging 59 | glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1); 60 | 61 | Window window = createWindowContext(windowName, WindowMode.windowed, width, height); 62 | 63 | // center the window on the screen 64 | glfwSetWindowPos(window.window, (vidMode.width - width) / 2, (vidMode.height - height) / 2); 65 | 66 | // glfw-specific error routine (not a generic GL error handler) 67 | register_glfw_error_callback(&glfwErrorCallback); 68 | 69 | // anti-aliasing number of samples. 70 | window.samples = 4; 71 | 72 | // activate an opengl context. 73 | window.make_context_current(); 74 | 75 | // load all OpenGL function pointers via glad. 76 | enforce(gladLoadGL()); 77 | 78 | enforce(glGenBuffers !is null); 79 | 80 | // only interested in GL 3.x 81 | enforce(GLVersion.major >= 3); 82 | 83 | // turn v-sync off. 84 | glfwSwapInterval(0); 85 | 86 | version (OSX) 87 | { 88 | // GL_ARM_debug_output and GL_KHR_debug are not supported under OS X 10.9.3 89 | } 90 | else 91 | { 92 | // ensure the debug output extension is supported 93 | enforce(GL_ARB_debug_output || GL_KHR_debug); 94 | 95 | // cast: workaround for 'nothrow' propagation bug (haven't been able to reduce it) 96 | auto hookDebugCallback = GL_ARB_debug_output ? glDebugMessageCallbackARB 97 | : cast(typeof(glDebugMessageCallbackARB))glDebugMessageCallback; 98 | 99 | 100 | // hook the debug callback 101 | // cast: when using derelict it assumes its nothrow 102 | hookDebugCallback(cast(GLDEBUGPROCARB)&glErrorCallback, null); 103 | 104 | // enable proper stack tracing support (otherwise we'd get random failures at runtime) 105 | glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); 106 | } 107 | 108 | // finally show the window 109 | glfwShowWindow(window.window); 110 | 111 | return window; 112 | } 113 | 114 | /** Create a window and an OpenGL context. */ 115 | Window createWindowContext(string windowName, WindowMode windowMode, int width, int height) 116 | { 117 | auto window = new Window(); 118 | auto monitor = windowMode == WindowMode.fullscreen ? glfwGetPrimaryMonitor() : null; 119 | auto context = window.create_highest_available_context(width, height, windowName, monitor, null, GLFW_OPENGL_CORE_PROFILE); 120 | 121 | // ensure we've loaded a proper context 122 | enforce(context.major >= 3); 123 | 124 | return window; 125 | } 126 | 127 | /** Just emit errors to stderr on GLFW errors. */ 128 | void glfwErrorCallback(int code, string msg) 129 | { 130 | stderr.writefln("Error (%s): %s", code, msg); 131 | } 132 | 133 | /// 134 | class GLException : Exception 135 | { 136 | @safe pure nothrow this(string msg = "", string file = __FILE__, size_t line = __LINE__, Throwable next = null) 137 | { 138 | super(msg, file, line, next); 139 | } 140 | 141 | @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) 142 | { 143 | super(msg, file, line, next); 144 | } 145 | } 146 | 147 | /** 148 | GL_ARB_debug_output or GL_KHR_debug callback. 149 | 150 | Throwing exceptions across language boundaries is ok as 151 | long as $(B GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) is enabled. 152 | */ 153 | extern (System) 154 | private void glErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, in GLchar* message, GLvoid* userParam) 155 | { 156 | //string msg = format("glErrorCallback: source: %s, type: %s, id: %s, severity: %s, length: %s, message: %s, userParam: %s", 157 | // source, type, id, severity, length, message.to!string, userParam); 158 | 159 | //stderr.writeln(msg); 160 | } 161 | -------------------------------------------------------------------------------- /source/emu/core/hw/interrupts.d: -------------------------------------------------------------------------------- 1 | module hw.interrupts; 2 | 3 | import std.stdio; 4 | import hw.cpu; 5 | 6 | import util; 7 | 8 | // set up the possible interrupts 9 | enum Interrupt { 10 | LCD_VBLANK = 1, 11 | LCD_HBLANK = 2, 12 | LCD_VCOUNTER_MATCH = 4, 13 | TIMER_0_OVERFLOW = 8, 14 | TIMER_1_OVERFLOW = 16, 15 | TIMER_2_OVERFLOW = 32, 16 | TIMER_3_OVERFLOW = 64, 17 | SERIAL_COMMUNICATION = 128, 18 | DMA_0 = 256, 19 | DMA_1 = 512, 20 | DMA_2 = 1024, 21 | DMA_3 = 2048, 22 | KEYPAD = 4096, 23 | GAMEPAK = 8192, 24 | PASTA = 16384 25 | } 26 | 27 | 28 | final class InterruptManager { 29 | this(void delegate() unhalt_cpu, void delegate() unhalt_gba) { 30 | this.unhalt_cpu = unhalt_cpu; 31 | this.unhalt_gba = unhalt_gba; 32 | } 33 | 34 | // interrupt_code must be one-hot 35 | void interrupt(uint interrupt_code) { 36 | // is this specific interrupt enabled 37 | interrupt_request |= interrupt_code; 38 | if (interrupt_enable & interrupt_code) { 39 | unhalt_cpu(); 40 | } 41 | 42 | if (interrupt_code & Interrupt.KEYPAD) { 43 | unhalt_gba(); 44 | } 45 | } 46 | 47 | pragma(inline, true) bool has_irq() { 48 | import std.stdio; 49 | // if (_g_num_log > 0) writefln("IRQ: %x %x %x, ", interrupt_master_enable, interrupt_enable, interrupt_request); 50 | return interrupt_master_enable && ((interrupt_enable & interrupt_request) != 0); 51 | } 52 | 53 | private: 54 | void delegate() unhalt_cpu; 55 | void delegate() unhalt_gba; 56 | // ....................................................................................................................... 57 | // .RRRRRRRRRRR...EEEEEEEEEEEE....GGGGGGGGG....IIII...SSSSSSSSS...TTTTTTTTTTTTT.EEEEEEEEEEEE..RRRRRRRRRRR....SSSSSSSSS.... 58 | // .RRRRRRRRRRRR..EEEEEEEEEEEE...GGGGGGGGGGG...IIII..SSSSSSSSSSS..TTTTTTTTTTTTT.EEEEEEEEEEEE..RRRRRRRRRRRR..SSSSSSSSSSS... 59 | // .RRRRRRRRRRRRR.EEEEEEEEEEEE..GGGGGGGGGGGGG..IIII..SSSSSSSSSSSS.TTTTTTTTTTTTT.EEEEEEEEEEEE..RRRRRRRRRRRR..SSSSSSSSSSSS.. 60 | // .RRRR.....RRRR.EEEE..........GGGGG....GGGG..IIII..SSSS....SSSS.....TTTT......EEEE..........RRR.....RRRRR.SSSS....SSSS.. 61 | // .RRRR.....RRRR.EEEE.........GGGGG......GGG..IIII..SSSS.............TTTT......EEEE..........RRR......RRRR.SSSSS......... 62 | // .RRRR....RRRRR.EEEEEEEEEEEE.GGGG............IIII..SSSSSSSS.........TTTT......EEEEEEEEEEEE..RRR.....RRRR..SSSSSSSS...... 63 | // .RRRRRRRRRRRR..EEEEEEEEEEEE.GGGG....GGGGGGG.IIII..SSSSSSSSSSS......TTTT......EEEEEEEEEEEE..RRRRRRRRRRRR...SSSSSSSSSS... 64 | // .RRRRRRRRRRRR..EEEEEEEEEEEE.GGGG....GGGGGGG.IIII....SSSSSSSSS......TTTT......EEEEEEEEEEEE..RRRRRRRRRRRR....SSSSSSSSSS.. 65 | // .RRRRRRRRRRR...EEEE.........GGGG....GGGGGGG.IIII........SSSSSS.....TTTT......EEEE..........RRRRRRRRRR..........SSSSSS.. 66 | // .RRRR..RRRRR...EEEE.........GGGGG......GGGG.IIII...SS.....SSSS.....TTTT......EEEE..........RRR...RRRRR....SS.....SSSS.. 67 | // .RRRR...RRRR...EEEE..........GGGGG....GGGGG.IIII.ISSSS....SSSS.....TTTT......EEEE..........RRR....RRRR...SSSS....SSSS.. 68 | // .RRRR...RRRRR..EEEEEEEEEEEEE.GGGGGGGGGGGGGG.IIII.ISSSSSSSSSSSS.....TTTT......EEEEEEEEEEEEE.RRR....RRRRR..SSSSSSSSSSSS.. 69 | // .RRRR....RRRRR.EEEEEEEEEEEEE..GGGGGGGGGGGG..IIII..SSSSSSSSSSS......TTTT......EEEEEEEEEEEEE.RRR.....RRRRR.SSSSSSSSSSSS.. 70 | // .RRRR.....RRRR.EEEEEEEEEEEEE...GGGGGGGGG....IIII...SSSSSSSSS.......TTTT......EEEEEEEEEEEEE.RRR.....RRRRR..SSSSSSSSSS... 71 | 72 | private: 73 | bool interrupt_master_enable; 74 | ushort interrupt_enable; 75 | ushort interrupt_request; 76 | 77 | public: 78 | void write_IF(int target_byte, ubyte data) { 79 | final switch (target_byte) { 80 | case 0b0: interrupt_request &= (0xFF00) | (~(cast(uint) (data)) << 0); break; 81 | case 0b1: interrupt_request &= (0x00FF) | (~(cast(uint) (data)) << 8); break; 82 | } 83 | } 84 | 85 | void write_IE(int target_byte, ubyte data) { 86 | final switch (target_byte) { 87 | case 0b0: interrupt_enable = (interrupt_enable & 0xFF00) | (data << 0); break; 88 | case 0b1: interrupt_enable = (interrupt_enable & 0x00FF) | (data << 8); break; 89 | } 90 | } 91 | 92 | void write_IME(int target_byte, ubyte data) { 93 | final switch (target_byte) { 94 | case 0b0: interrupt_master_enable = data & 1; break; 95 | case 0b1: break; 96 | } 97 | } 98 | 99 | ubyte read_IF(int target_byte) { 100 | final switch (target_byte) { 101 | case 0b0: return (interrupt_request & 0x00FF) >> 0; 102 | case 0b1: return (interrupt_request & 0xFF00) >> 8; 103 | } 104 | } 105 | 106 | ubyte read_IE(int target_byte) { 107 | final switch (target_byte) { 108 | case 0b0: return (interrupt_enable & 0x00FF) >> 0; 109 | case 0b1: return (interrupt_enable & 0xFF00) >> 8; 110 | } 111 | } 112 | 113 | ubyte read_IME(int target_byte) { 114 | final switch (target_byte) { 115 | case 0b0: return interrupt_master_enable; 116 | case 0b1: return 0; 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /imgui/dimgui/examples/colors/window.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Andrej Mitrovic 2014. 3 | * Distributed under the Boost Software License, Version 1.0. 4 | * (See accompanying file LICENSE_1_0.txt or copy at 5 | * http://www.boost.org/LICENSE_1_0.txt) 6 | */ 7 | module window; 8 | 9 | /** 10 | Contains various helpers, common code, and initialization routines. 11 | */ 12 | 13 | import std.algorithm : min; 14 | import std.exception : enforce; 15 | import std.functional : toDelegate; 16 | import std.stdio : stderr; 17 | import std.string : format; 18 | 19 | import deimos.glfw.glfw3; 20 | 21 | import glad.gl.enums; 22 | import glad.gl.ext; 23 | import glad.gl.funcs; 24 | import glad.gl.loader; 25 | import glad.gl.types; 26 | 27 | import glwtf.input; 28 | import glwtf.window; 29 | 30 | /// init 31 | shared static this() 32 | { 33 | enforce(glfwInit()); 34 | } 35 | 36 | /// uninit 37 | shared static ~this() 38 | { 39 | glfwTerminate(); 40 | } 41 | 42 | /// 43 | enum WindowMode 44 | { 45 | fullscreen, 46 | windowed, 47 | } 48 | 49 | /** 50 | Create a window, an OpenGL 3.x context, and set up some other 51 | common routines for error handling, window resizing, etc. 52 | */ 53 | Window createWindow(string windowName, WindowMode windowMode = WindowMode.windowed, int width = 1024, int height = 768) 54 | { 55 | auto vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); 56 | 57 | // constrain the window size so it isn't larger than the desktop size. 58 | width = min(width, vidMode.width); 59 | height = min(height, vidMode.height); 60 | 61 | // set the window to be initially inivisible since we're repositioning it. 62 | glfwWindowHint(GLFW_VISIBLE, 0); 63 | 64 | // enable debugging 65 | glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1); 66 | 67 | Window window = createWindowContext(windowName, WindowMode.windowed, width, height); 68 | 69 | // center the window on the screen 70 | glfwSetWindowPos(window.window, (vidMode.width - width) / 2, (vidMode.height - height) / 2); 71 | 72 | // glfw-specific error routine (not a generic GL error handler) 73 | register_glfw_error_callback(&glfwErrorCallback); 74 | 75 | // anti-aliasing number of samples. 76 | window.samples = 4; 77 | 78 | // activate an opengl context. 79 | window.make_context_current(); 80 | 81 | // load all OpenGL function pointers via glad. 82 | enforce(gladLoadGL()); 83 | 84 | enforce(glGenBuffers !is null); 85 | 86 | // only interested in GL 3.x 87 | enforce(GLVersion.major >= 3); 88 | 89 | // turn v-sync off. 90 | glfwSwapInterval(0); 91 | 92 | version (OSX) 93 | { 94 | // GL_ARM_debug_output and GL_KHR_debug are not supported under OS X 10.9.3 95 | } 96 | else 97 | { 98 | // ensure the debug output extension is supported 99 | enforce(GL_ARB_debug_output || GL_KHR_debug); 100 | 101 | // cast: workaround for 'nothrow' propagation bug (haven't been able to reduce it) 102 | auto hookDebugCallback = GL_ARB_debug_output ? glDebugMessageCallbackARB 103 | : cast(typeof(glDebugMessageCallbackARB))glDebugMessageCallback; 104 | 105 | 106 | // hook the debug callback 107 | // cast: when using derelict it assumes its nothrow 108 | hookDebugCallback(cast(GLDEBUGPROCARB)&glErrorCallback, null); 109 | 110 | // enable proper stack tracing support (otherwise we'd get random failures at runtime) 111 | glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); 112 | } 113 | 114 | // finally show the window 115 | glfwShowWindow(window.window); 116 | 117 | return window; 118 | } 119 | 120 | /** Create a window and an OpenGL context. */ 121 | Window createWindowContext(string windowName, WindowMode windowMode, int width, int height) 122 | { 123 | auto window = new Window(); 124 | auto monitor = windowMode == WindowMode.fullscreen ? glfwGetPrimaryMonitor() : null; 125 | auto context = window.create_highest_available_context(width, height, windowName, monitor, null, GLFW_OPENGL_CORE_PROFILE); 126 | 127 | // ensure we've loaded a proper context 128 | enforce(context.major >= 3); 129 | 130 | return window; 131 | } 132 | 133 | /** Just emit errors to stderr on GLFW errors. */ 134 | void glfwErrorCallback(int code, string msg) 135 | { 136 | stderr.writefln("Error (%s): %s", code, msg); 137 | } 138 | 139 | /// 140 | class GLException : Exception 141 | { 142 | @safe pure nothrow this(string msg = "", string file = __FILE__, size_t line = __LINE__, Throwable next = null) 143 | { 144 | super(msg, file, line, next); 145 | } 146 | 147 | @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) 148 | { 149 | super(msg, file, line, next); 150 | } 151 | } 152 | 153 | /** 154 | GL_ARB_debug_output or GL_KHR_debug callback. 155 | 156 | Throwing exceptions across language boundaries is ok as 157 | long as $(B GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) is enabled. 158 | */ 159 | extern (System) 160 | private void glErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, in GLchar* message, GLvoid* userParam) 161 | { 162 | //string msg = format("glErrorCallback: source: %s, type: %s, id: %s, severity: %s, length: %s, message: %s, userParam: %s", 163 | // source, type, id, severity, length, message.to!string, userParam); 164 | 165 | //stderr.writeln(msg); 166 | } 167 | -------------------------------------------------------------------------------- /imgui/dimgui/src/glwtf/input.d: -------------------------------------------------------------------------------- 1 | module glwtf.input; 2 | 3 | 4 | private { 5 | import glwtf.glfw; 6 | import glwtf.signals; 7 | 8 | import std.conv : to; 9 | } 10 | 11 | AEventHandler cast_userptr(GLFWwindow* window) 12 | out (result) { assert(result !is null, "glfwGetWindowUserPointer returned null"); } 13 | body { 14 | void* user_ptr = glfwGetWindowUserPointer(window); 15 | return cast(AEventHandler)user_ptr; 16 | } 17 | 18 | 19 | private void function(int, string) glfw_error_callback; 20 | 21 | void register_glfw_error_callback(void function(int, string) cb) { 22 | glfw_error_callback = cb; 23 | 24 | glfwSetErrorCallback(&error_callback); 25 | } 26 | 27 | extern(C) { 28 | // window events // 29 | void window_resize_callback(GLFWwindow* window, int width, int height) { 30 | AEventHandler ae = cast_userptr(window); 31 | 32 | ae.on_resize.emit(width, height); 33 | } 34 | 35 | void window_close_callback(GLFWwindow* window) { 36 | AEventHandler ae = cast_userptr(window); 37 | 38 | bool close = cast(int)ae._on_close(); 39 | if(close) { 40 | ae.on_closing.emit(); 41 | } else { 42 | glfwSetWindowShouldClose(window, 0); 43 | } 44 | } 45 | 46 | void window_refresh_callback(GLFWwindow* window) { 47 | AEventHandler ae = cast_userptr(window); 48 | 49 | ae.on_refresh.emit(); 50 | } 51 | 52 | void window_focus_callback(GLFWwindow* window, int focused) { 53 | AEventHandler ae = cast_userptr(window); 54 | 55 | ae.on_focus.emit(focused == 1); 56 | } 57 | 58 | void window_iconify_callback(GLFWwindow* window, int iconified) { 59 | AEventHandler ae = cast_userptr(window); 60 | 61 | ae.on_iconify.emit(iconified == 1); 62 | } 63 | 64 | // user input // 65 | void key_callback(GLFWwindow* window, int key, int scancode, int state, int modifier) { 66 | AEventHandler ae = cast_userptr(window); 67 | 68 | if(state == GLFW_PRESS) { 69 | ae.on_key_down.emit(key, scancode, modifier); 70 | } else if(state == GLFW_REPEAT) { 71 | ae.on_key_repeat.emit(key, scancode, modifier); 72 | } else { 73 | ae.on_key_up.emit(key, scancode, modifier); 74 | } 75 | } 76 | 77 | void char_callback(GLFWwindow* window, uint c) { 78 | AEventHandler ae = cast_userptr(window); 79 | 80 | ae.on_char.emit(cast(dchar)c); 81 | } 82 | 83 | void mouse_button_callback(GLFWwindow* window, int button, int state, int modifier) { 84 | AEventHandler ae = cast_userptr(window); 85 | 86 | if(state == GLFW_PRESS) { 87 | ae.on_mouse_button_down.emit(button, modifier); 88 | } else { 89 | ae.on_mouse_button_up.emit(button, modifier); 90 | } 91 | } 92 | 93 | void cursor_pos_callback(GLFWwindow* window, double x, double y) { 94 | AEventHandler ae = cast_userptr(window); 95 | 96 | ae.on_mouse_pos.emit(x, y); 97 | } 98 | 99 | void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { 100 | AEventHandler ae = cast_userptr(window); 101 | 102 | ae.on_scroll.emit(xoffset, yoffset); 103 | } 104 | 105 | // misc // 106 | void error_callback(int errno, const(char)* error) { 107 | glfw_error_callback(errno, to!string(error)); 108 | } 109 | } 110 | 111 | abstract class AEventHandler { 112 | // window 113 | Signal!(int, int) on_resize; 114 | Signal!() on_closing; 115 | Signal!() on_refresh; 116 | Signal!(bool) on_focus; 117 | Signal!(bool) on_iconify; 118 | 119 | bool _on_close() { return true; } 120 | 121 | // input 122 | Signal!(int, int, int) on_key_down; 123 | Signal!(int, int, int) on_key_repeat; 124 | Signal!(int, int, int) on_key_up; 125 | Signal!(dchar) on_char; 126 | Signal!(int, int) on_mouse_button_down; 127 | Signal!(int, int) on_mouse_button_up; 128 | Signal!(double, double) on_mouse_pos; 129 | Signal!(double, double) on_scroll; 130 | } 131 | 132 | 133 | class BaseGLFWEventHandler : AEventHandler { 134 | Signal!()[GLFW_KEY_LAST] single_key_down; 135 | Signal!()[GLFW_KEY_LAST] single_key_up; 136 | 137 | protected bool[GLFW_KEY_LAST] keymap; 138 | protected bool[GLFW_MOUSE_BUTTON_LAST] mousemap; 139 | 140 | this() { 141 | on_key_down.connect!"_on_key_down"(this); 142 | on_key_up.connect!"_on_key_up"(this); 143 | on_mouse_button_down.connect!"_on_mouse_button_down"(this); 144 | on_mouse_button_up.connect!"_on_mouse_button_up"(this); 145 | } 146 | 147 | package void register_callbacks(GLFWwindow* window) { 148 | glfwSetWindowUserPointer(window, cast(void *)this); 149 | 150 | glfwSetWindowSizeCallback(window, &window_resize_callback); 151 | glfwSetWindowCloseCallback(window, &window_close_callback); 152 | glfwSetWindowRefreshCallback(window, &window_refresh_callback); 153 | glfwSetWindowFocusCallback(window, &window_focus_callback); 154 | glfwSetWindowIconifyCallback(window, &window_iconify_callback); 155 | 156 | glfwSetKeyCallback(window, &key_callback); 157 | glfwSetCharCallback(window, &char_callback); 158 | glfwSetMouseButtonCallback(window, &mouse_button_callback); 159 | glfwSetCursorPosCallback(window, &cursor_pos_callback); 160 | glfwSetScrollCallback(window, &scroll_callback); 161 | } 162 | 163 | public void _on_key_down(int key, int scancode, int modifier) { 164 | keymap[key] = true; 165 | single_key_down[key].emit(); 166 | } 167 | 168 | public void _on_key_up(int key, int scancode, int modifier) { 169 | keymap[key] = false; 170 | single_key_up[key].emit(); 171 | } 172 | 173 | public void _on_mouse_button_down(int button, int modifier) { 174 | mousemap[button] = true; 175 | } 176 | public void _on_mouse_button_up(int button, int modifier) { 177 | mousemap[button] = false; 178 | } 179 | 180 | bool is_key_down(int key) { 181 | return keymap[key]; 182 | } 183 | 184 | bool is_mouse_down(int button) { 185 | return mousemap[button]; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /tests/asm/src/arm-addressing-mode-3.S: -------------------------------------------------------------------------------- 1 | .global main 2 | 3 | @ ************************* Constants to be Loaded into Registers for Testing ************************* 4 | .data 5 | @ ***************************************************************************************************** 6 | 7 | test_values: 8 | .word 0x00000000 @ 00 9 | .word 0x00000001 @ 04 10 | .word 0xFFFFFFFF @ 08 11 | .word 0x80000000 @ 0C 12 | .word 0x7FFFFFFF @ 10 13 | .word 0x8ABD8297 @ 14 14 | .word 0x289BDF39 @ 18 15 | .word 0xEFFFFFFF @ 1C 16 | .word 0x00000004 @ 20 17 | .word 0xFFFFFF00 @ 24 18 | .word 0xFFFFFF04 @ 28 19 | .word 0xFFFFFF20 @ 2C 20 | .word 0x00000008 @ 30 21 | .word 0x0000000C @ 34 22 | test_values_end: 23 | 24 | @ the reason we have an addendum here and not in the thumb tests is because we index in arm from both 25 | @ test_values and from test_values_end, which makes it really difficult if not impossible to insert 26 | @ a new word somewhere without ruining some tests that use specific values... so here's an addendum, 27 | @ which should not be indexed backwards 28 | 29 | test_values_addendum: 30 | .word 0x00000005 @ 00 31 | .word 0x00000010 @ 04 32 | .word 0xFFFFFFFC @ 08 33 | .word 0xFFFF7BCD @ 0C 34 | .word 0xFFFF8BCD @ 10 35 | 36 | @ ******************************************** Test Cases ********************************************* 37 | .text 38 | @ ***************************************************************************************************** 39 | 40 | main: 41 | .arm 42 | 43 | tests: 44 | 45 | @ LDRH tests [immediate offset] 46 | 47 | LDR r7, =test_values 48 | LDR r9, =test_values_addendum 49 | 50 | LDRH r2, [r9, #0x0C] 51 | LDRH r2, [r9, #0x10] 52 | 53 | @ LDRH tests [register offset] 54 | 55 | LDR r7, =test_values 56 | LDR r9, =test_values_addendum 57 | 58 | LDR r3, [r7, #0x34] 59 | LDRH r2, [r9, r3] 60 | LDR r3, [r9, #0x04] 61 | LDRH r2, [r9, r3] 62 | 63 | @ LDRH tests [immediate pre-indexed] 64 | 65 | LDR r7, =test_values 66 | LDR r9, =test_values_addendum 67 | 68 | LDRH r2, [r9, #+0x10]! 69 | LDRH r2, [r9, #-0x04]! 70 | 71 | @ LDRH tests [register pre-indexed] 72 | 73 | LDR r7, =test_values 74 | 75 | LDR r9, =test_values_addendum 76 | LDR r3, [r9, #0x04] 77 | LDRH r2, [r9, r3]! 78 | 79 | LDR r9, =test_values_addendum 80 | LDR r3, [r9, #0x04] 81 | LDRH r2, [r9, r3]! 82 | 83 | @ LDRH tests [immediate post-indexed] 84 | 85 | LDR r7, =test_values 86 | LDR r9, =test_values_addendum 87 | 88 | LDRH r2, [r9], #+0x10 89 | LDRH r2, [r9], #-0x04 90 | LDRH r2, [r9], #-0x00 91 | 92 | @ LDRH tests [register post-indexed] 93 | 94 | LDR r7, =test_values 95 | 96 | LDR r9, =test_values_addendum 97 | LDR r3, [r9, #0x04] 98 | LDRH r2, [r9], r3 99 | 100 | LDR r9, =test_values_addendum 101 | LDR r3, [r9, #0x0C] 102 | LDRH r2, [r9], r3 103 | 104 | LDR r9, =test_values_addendum 105 | LDR r3, [r9, #0x00] 106 | LDRH r2, [r9], r3 107 | 108 | @ LDRSB tests [immediate offset] 109 | 110 | LDR r7, =test_values 111 | LDR r9, =test_values_addendum 112 | 113 | LDRSB r2, [r9, #0x0C] 114 | LDRSB r2, [r9, #0x10] 115 | 116 | @ LDRSB tests [register offset] 117 | 118 | LDR r7, =test_values 119 | LDR r9, =test_values_addendum 120 | 121 | LDR r3, [r7, #0x34] 122 | LDRSB r2, [r9, r3] 123 | LDR r3, [r9, #0x04] 124 | LDRSB r2, [r9, r3] 125 | 126 | @ LDRSB tests [immediate pre-indexed] 127 | 128 | LDR r7, =test_values 129 | LDR r9, =test_values_addendum 130 | 131 | LDRSB r2, [r9, #+0x10]! 132 | LDRSB r2, [r9, #-0x04]! 133 | 134 | @ LDRSB tests [register pre-indexed] 135 | 136 | LDR r7, =test_values 137 | 138 | LDR r9, =test_values_addendum 139 | LDR r3, [r9, #0x04] 140 | LDRSB r2, [r9, r3]! 141 | 142 | LDR r9, =test_values_addendum 143 | LDR r3, [r9, #0x04] 144 | LDRSB r2, [r9, r3]! 145 | 146 | @ LDRSB tests [immediate post-indexed] 147 | 148 | LDR r7, =test_values 149 | LDR r9, =test_values_addendum 150 | 151 | LDRSB r2, [r9], #+0x10 152 | LDRSB r2, [r9], #-0x04 153 | LDRSB r2, [r9], #-0x00 154 | 155 | @ LDRSB tests [register post-indexed] 156 | 157 | LDR r7, =test_values 158 | 159 | LDR r9, =test_values_addendum 160 | LDR r3, [r9, #0x04] 161 | LDRSB r2, [r9], r3 162 | 163 | LDR r9, =test_values_addendum 164 | LDR r3, [r9, #0x0C] 165 | LDRSB r2, [r9], r3 166 | 167 | LDR r9, =test_values_addendum 168 | LDR r3, [r9, #0x00] 169 | LDRSB r2, [r9], r3 170 | 171 | @ LDRSH tests [immediate offset] 172 | 173 | LDR r7, =test_values 174 | LDR r9, =test_values_addendum 175 | 176 | LDRSH r2, [r9, #0x0C] 177 | LDRSH r2, [r9, #0x10] 178 | 179 | @ LDRSH tests [register offset] 180 | 181 | LDR r7, =test_values 182 | LDR r9, =test_values_addendum 183 | 184 | LDR r3, [r7, #0x34] 185 | LDRSH r2, [r9, r3] 186 | LDR r3, [r9, #0x04] 187 | LDRSH r2, [r9, r3] 188 | 189 | @ LDRSH tests [immediate pre-indexed] 190 | 191 | LDR r7, =test_values 192 | LDR r9, =test_values_addendum 193 | 194 | LDRSH r2, [r9, #+0x10]! 195 | LDRSH r2, [r9, #-0x04]! 196 | 197 | @ LDRSH tests [register pre-indexed] 198 | 199 | LDR r7, =test_values 200 | 201 | LDR r9, =test_values_addendum 202 | LDR r3, [r9, #0x04] 203 | LDRSH r2, [r9, r3]! 204 | 205 | LDR r9, =test_values_addendum 206 | LDR r3, [r9, #0x04] 207 | LDRSH r2, [r9, r3]! 208 | 209 | @ LDRSH tests [immediate post-indexed] 210 | 211 | LDR r7, =test_values 212 | LDR r9, =test_values_addendum 213 | 214 | LDRSH r2, [r9], #+0x10 215 | LDRSH r2, [r9], #-0x04 216 | LDRSH r2, [r9], #-0x00 217 | 218 | @ LDRSH tests [register post-indexed] 219 | 220 | LDR r7, =test_values 221 | 222 | LDR r9, =test_values_addendum 223 | LDR r3, [r9, #0x04] 224 | LDRSH r2, [r9], r3 225 | 226 | LDR r9, =test_values_addendum 227 | LDR r3, [r9, #0x0C] 228 | LDRSH r2, [r9], r3 229 | 230 | LDR r9, =test_values_addendum 231 | LDR r3, [r9, #0x00] 232 | LDRSH r2, [r9], r3 233 | 234 | @ Addressing Mode 2 PC tests 235 | 236 | LDR r9, =test_values_addendum 237 | LDR r2, [PC, #-0x8] 238 | LDR r3, [r9, #+0x8] 239 | LDR r3, [PC, r3] 240 | 241 | infin: 242 | B infin -------------------------------------------------------------------------------- /tests/asm/src/arm-addressing-mode-1.S: -------------------------------------------------------------------------------- 1 | .global main 2 | 3 | @ ************************* Constants to be Loaded into Registers for Testing ************************* 4 | .data 5 | @ ***************************************************************************************************** 6 | 7 | test_values: 8 | .word 0x00000000 @ 00 9 | .word 0x00000001 @ 04 10 | .word 0xFFFFFFFF @ 08 11 | .word 0x80000000 @ 0C 12 | .word 0x7FFFFFFF @ 10 13 | .word 0x8ABD8297 @ 14 14 | .word 0x289BDF39 @ 18 15 | .word 0xEFFFFFFF @ 1C 16 | .word 0x00000004 @ 20 17 | .word 0xFFFFFF00 @ 24 18 | .word 0xFFFFFF04 @ 28 19 | .word 0xFFFFFF20 @ 2C 20 | .word 0x00000008 @ 30 21 | .word 0x0000000C @ 34 22 | test_values_end: 23 | 24 | @ the reason we have an addendum here and not in the thumb tests is because we index in arm from both 25 | @ test_values and from test_values_end, which makes it really difficult if not impossible to insert 26 | @ a new word somewhere without ruining some tests that use specific values... so here's an addendum, 27 | @ which should not be indexed backwards 28 | 29 | test_values_addendum: 30 | .word 0x00000005 @ 00 31 | .word 0x00000010 @ 04 32 | .word 0xFFFFFFFC @ 08 33 | .word 0xFFFF7BCD @ 0C 34 | .word 0xFFFF8BCD @ 10 35 | .word 0x00000020 @ 14 36 | 37 | @ ******************************************** Test Cases ********************************************* 38 | .text 39 | @ ***************************************************************************************************** 40 | 41 | main: 42 | .arm 43 | 44 | tests: 45 | 46 | @ EOR tests [Immediate offset] 47 | 48 | LDR r7, =test_values 49 | 50 | LDR r2, [r7, #0x18] 51 | EOR r2, #0xFA 52 | 53 | @ EOR tests [Register offset] 54 | 55 | LDR r7, =test_values 56 | 57 | LDR r2, [r7, #0x18] 58 | LDR r3, [r7, #0x14] 59 | EOR r2, r3 60 | 61 | @ EOR tests [LSL by Immediate] 62 | 63 | LDR r7, =test_values 64 | LDR r9, =test_values_addendum 65 | LDR r3, [r7, #0x14] 66 | 67 | LDR r2, [r7, #0x18] 68 | EOR r2, r3, LSL #0x0 69 | 70 | LDR r2, [r7, #0x18] 71 | EOR r2, r3, LSL #0x2 72 | 73 | @ EOR tests [LSL by Register] 74 | 75 | LDR r7, =test_values 76 | LDR r9, =test_values_addendum 77 | LDR r3, [r7, #0x14] 78 | 79 | LDR r2, [r7, #0x18] 80 | LDR r4, [r7, #0x00] 81 | EOR r2, r3, LSL r4 82 | 83 | LDR r2, [r7, #0x18] 84 | LDR r4, [r9, #0x00] 85 | EOR r2, r3, LSL r4 86 | 87 | LDR r2, [r7, #0x18] 88 | LDR r4, [r9, #0x14] 89 | EOR r2, r3, LSL r4 90 | 91 | LDR r2, [r7, #0x18] 92 | LDR r4, [r7, #0x14] 93 | EOR r2, r3, LSL r4 94 | 95 | @ EOR tests [LSR by Immediate] 96 | 97 | LDR r7, =test_values 98 | LDR r9, =test_values_addendum 99 | LDR r3, [r7, #0x14] 100 | 101 | LDR r2, [r7, #0x18] 102 | EOR r2, r3, LSR #0x0 103 | 104 | LDR r2, [r7, #0x18] 105 | EOR r2, r3, LSR #0x2 106 | 107 | @ EOR tests [LSR by Register] 108 | 109 | LDR r7, =test_values 110 | LDR r9, =test_values_addendum 111 | LDR r3, [r7, #0x14] 112 | 113 | LDR r2, [r7, #0x18] 114 | LDR r4, [r7, #0x00] 115 | EOR r2, r3, LSR r4 116 | 117 | LDR r2, [r7, #0x18] 118 | LDR r4, [r9, #0x00] 119 | EOR r2, r3, LSR r4 120 | 121 | LDR r2, [r7, #0x18] 122 | LDR r4, [r9, #0x14] 123 | EOR r2, r3, LSR r4 124 | 125 | LDR r2, [r7, #0x18] 126 | LDR r4, [r7, #0x14] 127 | EOR r2, r3, LSR r4 128 | 129 | @ EOR tests [ASR by Immediate] 130 | 131 | LDR r7, =test_values 132 | LDR r9, =test_values_addendum 133 | LDR r3, [r7, #0x14] 134 | 135 | LDR r2, [r7, #0x18] 136 | EOR r2, r3, ASR #0x0 137 | 138 | LDR r2, [r7, #0x14] 139 | EOR r2, r3, ASR #0x0 140 | 141 | LDR r2, [r7, #0x18] 142 | EOR r2, r3, ASR #0x2 143 | 144 | @ EOR tests [ASR by Register] 145 | 146 | LDR r7, =test_values 147 | LDR r9, =test_values_addendum 148 | LDR r3, [r7, #0x14] 149 | 150 | LDR r2, [r7, #0x18] 151 | LDR r4, [r7, #0x00] 152 | EOR r2, r3, ASR r4 153 | 154 | LDR r2, [r7, #0x18] 155 | LDR r4, [r9, #0x00] 156 | EOR r2, r3, ASR r4 157 | 158 | LDR r2, [r7, #0x18] 159 | LDR r4, [r7, #0x14] 160 | EOR r2, r3, ASR r4 161 | 162 | LDR r2, [r7, #0x14] 163 | LDR r4, [r7, #0x08] 164 | EOR r2, r3, ASR r4 165 | 166 | @ EOR tests [ROR by Immediate] 167 | 168 | LDR r7, =test_values 169 | LDR r9, =test_values_addendum 170 | LDR r3, [r7, #0x14] 171 | 172 | LDR r2, [r7, #0x18] 173 | EOR r2, r3, ROR #0x0 174 | 175 | LDR r2, [r7, #0x18] 176 | EOR r2, r3, ROR #0x2 177 | 178 | @ EOR tests [ROR by Register] 179 | 180 | LDR r7, =test_values 181 | LDR r9, =test_values_addendum 182 | LDR r3, [r7, #0x14] 183 | 184 | LDR r2, [r7, #0x18] 185 | LDR r4, [r7, #0x24] 186 | EOR r2, r3, ROR r4 187 | 188 | LDR r2, [r7, #0x18] 189 | LDR r4, [r7, #0x2C] 190 | EOR r2, r3, ROR r4 191 | 192 | LDR r2, [r7, #0x18] 193 | LDR r4, [r7, #0x28] 194 | EOR r2, r3, ROR r4 195 | 196 | @ EOR tests [PC indexing] 197 | 198 | LDR r7, =test_values 199 | 200 | LDR r2, [r7, #0x18] 201 | EOR r2, r15, LSL #0x3 202 | 203 | LDR r2, [r7, #0x18] 204 | EOR r2, r15 205 | 206 | infin: 207 | B infin -------------------------------------------------------------------------------- /source/emu/core/save/backups/flash.d: -------------------------------------------------------------------------------- 1 | module save.backups.flash; 2 | 3 | import save; 4 | import util; 5 | 6 | import std.stdio; 7 | import core.stdc.string; 8 | 9 | final class Flash : Backup { 10 | // https://mgba-emu.github.io/gbatek/#gbacartbackupflashrom 11 | // for more context on what these two constants represent 12 | // i can't for the life of me come up with a better name so 13 | // the gist is that these addresses should be written to in 14 | // series to execute a command. 15 | enum uint[] COMMAND_ADDRESS = [0x5555, 0x2AAA, 0x5555]; 16 | enum uint[] COMMAND_HEADER = [0xAA, 0x55]; 17 | 18 | enum State { 19 | WAITING_FOR_COMMAND, // waiting for COMMAND_ADDRESS[0] to be written to 20 | 21 | RECEIVING_COMMAND_0, // after COMMAND_ADDRESS[0] has been written to 22 | RECEIVING_COMMAND_1, // after COMMAND_ADDRESS[1] has been written to 23 | 24 | BANK_SWITCHING, // allows you to switch the bank number 25 | WRITING_SINGLE_BYTE 26 | } 27 | 28 | // thanks Dillon! :) https://dillonbeliveau.com/2020/06/05/GBA-FLASH.html 29 | enum Command { 30 | ENTER_IDENTIFICATION = 0x90, 31 | EXIT_IDENTIFICATION = 0xF0, 32 | 33 | PREPARE_ERASE = 0x80, 34 | ERASE_ENTIRE_CHIP = 0x10, 35 | ERASE_SECTOR = 0x30, 36 | 37 | WRITE_SINGLE_BYTE = 0xA0, 38 | SET_MEMORY_BANK = 0xB0 39 | } 40 | 41 | ubyte[] all_data; 42 | ubyte* accessible_data; 43 | 44 | ubyte manufacturer_id; 45 | ubyte device_id; 46 | 47 | this(int total_size, bool banked, int num_banks, ubyte manufacturer_id, ubyte device_id) { 48 | this.sector_size = 4096; 49 | this.total_size = total_size; 50 | this.banked = banked; 51 | this.bank = 0; 52 | this.bank_size = total_size / num_banks; 53 | this.identification = false; 54 | 55 | this.manufacturer_id = manufacturer_id; 56 | this.device_id = device_id; 57 | 58 | all_data = new ubyte[total_size]; 59 | accessible_data = &all_data[0]; 60 | erase_entire_chip(); 61 | } 62 | 63 | override void write_byte(uint address, ubyte data) { 64 | address &= 0xFFFF; 65 | final switch (state) { 66 | case State.WAITING_FOR_COMMAND: handle_command_header_0(address, data); break; 67 | case State.RECEIVING_COMMAND_0: handle_command_header_1(address, data); break; 68 | case State.RECEIVING_COMMAND_1: handle_command_data (address, data); break; 69 | 70 | case State.BANK_SWITCHING: handle_bank_switching (address, data); break; 71 | case State.WRITING_SINGLE_BYTE: write_single_byte (address, data); break; 72 | } 73 | } 74 | 75 | override ubyte read_byte(uint address) { 76 | address &= 0xFFFF; 77 | if (identification) { 78 | if (address == 0) return this.manufacturer_id; 79 | if (address == 1) return this.device_id; 80 | return 0x0; 81 | } else { 82 | return accessible_data[address]; 83 | } 84 | } 85 | 86 | override ubyte[] serialize() { 87 | return all_data; 88 | } 89 | 90 | override void deserialize(ubyte[] data) { 91 | this.all_data = data; 92 | } 93 | 94 | override BackupType get_backup_type() { 95 | return BackupType.FLASH; 96 | } 97 | 98 | override int get_backup_size() { 99 | return total_size; 100 | } 101 | 102 | private void handle_command_header_0(uint address, uint data) { 103 | if (address == COMMAND_ADDRESS[0] && data == COMMAND_HEADER[0]) 104 | state = State.RECEIVING_COMMAND_0; 105 | } 106 | 107 | private void handle_command_header_1(uint address, uint data) { 108 | if (address == COMMAND_ADDRESS[1] && data == COMMAND_HEADER[1]) 109 | state = State.RECEIVING_COMMAND_1; 110 | } 111 | 112 | private void handle_command_data(uint address, uint data) { 113 | switch (cast(Command) data) { 114 | case Command.ENTER_IDENTIFICATION: 115 | identification = true; 116 | state = State.WAITING_FOR_COMMAND; break; 117 | 118 | case Command.EXIT_IDENTIFICATION: 119 | identification = false; 120 | state = State.WAITING_FOR_COMMAND; break; 121 | 122 | case Command.PREPARE_ERASE: 123 | preparing_erase = true; 124 | state = State.WAITING_FOR_COMMAND; break; 125 | 126 | case Command.ERASE_ENTIRE_CHIP: 127 | if (preparing_erase) 128 | erase_entire_chip(); 129 | preparing_erase = false; 130 | state = State.WAITING_FOR_COMMAND; 131 | break; 132 | 133 | case Command.ERASE_SECTOR: 134 | if (preparing_erase) 135 | erase_sector(get_nth_bits(address, 12, 16)); 136 | preparing_erase = false; 137 | state = State.WAITING_FOR_COMMAND; 138 | break; 139 | 140 | case Command.SET_MEMORY_BANK: 141 | state = State.BANK_SWITCHING; 142 | break; 143 | 144 | case Command.WRITE_SINGLE_BYTE: 145 | state = State.WRITING_SINGLE_BYTE; 146 | break; 147 | 148 | default: break; 149 | } 150 | } 151 | 152 | private void handle_bank_switching(uint address, uint data) { 153 | this.bank = data & 1; 154 | this.accessible_data = &this.all_data[bank * bank_size]; 155 | state = State.WAITING_FOR_COMMAND; 156 | } 157 | 158 | private void write_single_byte(uint address, ubyte data) { 159 | this.accessible_data[address] = data; 160 | state = State.WAITING_FOR_COMMAND; 161 | 162 | backup_file[address + bank * bank_size] = data; 163 | } 164 | 165 | private void erase_entire_chip() { 166 | memset(cast(void*) all_data, 0xFF, total_size); 167 | } 168 | 169 | private void erase_sector(int sector) { 170 | memset(cast(void*) &accessible_data[sector * sector_size], 0xFF, sector_size); 171 | } 172 | 173 | private int total_size; 174 | private int sector_size; 175 | private int bank_size; 176 | private bool banked; 177 | private int bank; 178 | 179 | private State state; 180 | private bool preparing_erase; 181 | private bool identification; 182 | } -------------------------------------------------------------------------------- /source/emu/core/hw/gba.d: -------------------------------------------------------------------------------- 1 | module hw.gba; 2 | 3 | import hw.memory; 4 | import hw.ppu; 5 | import hw.cpu; 6 | import hw.apu; 7 | import hw.dma; 8 | import hw.timers; 9 | import hw.interrupts; 10 | import hw.keyinput; 11 | import hw.beancomputer; 12 | import hw.sio.sio; 13 | 14 | import scheduler; 15 | import util; 16 | 17 | import ui.device; 18 | 19 | import tools.profiler.profiler; 20 | 21 | import std.math; 22 | import std.stdio; 23 | 24 | enum CART_SIZE = 0x1000000; 25 | 26 | enum ROM_ENTRY_POINT = 0x000; 27 | enum GAME_TITLE_OFFSET = 0x0A0; 28 | enum GAME_TITLE_SIZE = 12; 29 | 30 | // 2 ^ 64 can last for up to 3000 years 31 | ulong num_cycles = 0; 32 | 33 | // globals, sorry 34 | __gshared bool g_profile_gba; 35 | __gshared Profiler g_profiler; 36 | 37 | final class GBA { 38 | public: 39 | ARM7TDMI cpu; 40 | PPU ppu; 41 | APU apu; 42 | Memory memory; 43 | DMAManager dma_manager; 44 | TimerManager timers; 45 | InterruptManager interrupt_manager; 46 | KeyInput key_input; 47 | BeanComputer beancomputer; 48 | SIO sio; 49 | // DirectSound direct_sound; 50 | 51 | Scheduler scheduler; 52 | 53 | this(Memory memory, KeyInput key_input, ubyte[] bios, bool is_bean_computer) { 54 | scheduler = new Scheduler(memory); 55 | 56 | this.memory = memory; 57 | this.cpu = new ARM7TDMI(memory); 58 | this.interrupt_manager = new InterruptManager(&cpu.enable, &enable); 59 | this.ppu = new PPU(memory, scheduler, &interrupt_manager.interrupt, &on_hblank, &on_vblank); 60 | this.apu = new APU(memory, scheduler, &on_fifo_empty); 61 | this.dma_manager = new DMAManager(memory, scheduler, &interrupt_manager.interrupt); 62 | this.timers = new TimerManager(memory, scheduler, this, &interrupt_manager.interrupt, &on_timer_overflow); 63 | this.sio = new SIO(&interrupt_manager.interrupt, scheduler); 64 | this.beancomputer = new BeanComputer(); 65 | this.key_input = key_input; 66 | 67 | key_input.set_interrupt_cpu = &interrupt_manager.interrupt; 68 | 69 | // this.direct_sound = new DirectSound(memory); 70 | 71 | MMIO mmio = new MMIO(this, ppu, apu, dma_manager, timers, interrupt_manager, key_input, beancomputer, sio, memory, is_bean_computer); 72 | memory.set_mmio(mmio); 73 | memory.set_cpu(this.cpu); 74 | memory.set_ppu(this.ppu); 75 | memory.set_scheduler(scheduler); 76 | 77 | this.enabled = false; 78 | 79 | cpu.set_mode!MODE_SYSTEM; 80 | cpu.set_interrupt_manager(this.interrupt_manager); 81 | 82 | // bios 83 | memory.bios[0 .. bios.length] = bios[0 .. bios.length]; 84 | } 85 | 86 | void set_frontend(MultiMediaDevice device) { 87 | ppu.set_frontend_vblank_callback(&device.present_videobuffers); 88 | apu.set_frontend_audio_callback(&device.push_sample); 89 | 90 | device.set_update_key_callback(&key_input.set_key); 91 | } 92 | 93 | void set_internal_sample_rate(uint sample_rate) { 94 | apu.set_internal_sample_rate(sample_rate); 95 | } 96 | 97 | void skip_bios_bootscreen() { 98 | cpu.skip_bios(); 99 | } 100 | 101 | void load_rom(string rom_path) { 102 | load_rom(load_rom_as_bytes(rom_path)); 103 | } 104 | 105 | void load_rom(ubyte[] rom) { 106 | cpu.memory.load_rom(rom); 107 | cpu.refill_pipeline(); 108 | enabled = true; 109 | } 110 | 111 | long extra_cycles = 0; 112 | void cycle_at_least_n_times(int n) { 113 | n -= extra_cycles; 114 | 115 | ulong target_time = scheduler.get_current_time() + n; 116 | while (target_time > scheduler.get_current_time()) { 117 | cycle_components(); 118 | } 119 | 120 | // warning(format("Cycled to %d.", scheduler.get_current_time())); 121 | 122 | extra_cycles = scheduler.get_current_time() - target_time; 123 | } 124 | 125 | pragma(inline, true) void cycle_components() { 126 | if (!cpu.halted) { 127 | cpu.run_instruction(); 128 | } else { 129 | // ty organharvester for the halt skipping idea! 130 | scheduler.tick_to_next_event(); 131 | scheduler.process_events(); 132 | 133 | if (interrupt_manager.has_irq()) { 134 | cpu.raise_exception!(CpuException.IRQ); 135 | } 136 | } 137 | } 138 | 139 | void interrupt_cpu() { 140 | return; 141 | } 142 | 143 | void on_timer_overflow(int timer_id) { 144 | apu.on_timer_overflow(timer_id); 145 | } 146 | 147 | void on_fifo_empty(DirectSound fifo_type) { 148 | dma_manager.maybe_refill_fifo(fifo_type); 149 | } 150 | 151 | void on_hblank(uint scanline) { 152 | dma_manager.on_hblank(scanline); 153 | } 154 | 155 | void on_vblank() { 156 | dma_manager.on_vblank(); 157 | } 158 | 159 | void disable() { 160 | ppu.disable(); 161 | enabled = false; 162 | } 163 | 164 | void enable() { 165 | ppu.enable(); 166 | enabled = true; 167 | } 168 | 169 | // is this sketchy code? it might be... but its 1 am 170 | // TODO: fix sketchy code 171 | void write_HALTCNT(ubyte data) { 172 | if (get_nth_bit(data, 7)) { 173 | // idk figure out stopping 174 | disable(); 175 | } else { 176 | // halt 177 | cpu.halt(); 178 | } 179 | } 180 | 181 | bool enabled; 182 | 183 | private: 184 | bool dma_cycle = false; 185 | uint idle_cycles = 0; 186 | 187 | } 188 | 189 | enum GBAKeyBeanComputer { 190 | A = 0, 191 | B, 192 | C, 193 | D, 194 | E, 195 | F, 196 | G, 197 | H, 198 | I, 199 | J, 200 | K, 201 | L, 202 | M, 203 | N, 204 | O, 205 | P, 206 | Q, 207 | R, 208 | S, 209 | T, 210 | U, 211 | V, 212 | W, 213 | X, 214 | Y, 215 | Z, 216 | SHIFT, 217 | CTRL, 218 | ALT, 219 | SUPER, 220 | ESCAPE, 221 | 222 | NUMBER_0 = 32, 223 | NUMBER_1, 224 | NUMBER_2, 225 | NUMBER_3, 226 | NUMBER_4, 227 | NUMBER_5, 228 | NUMBER_6, 229 | NUMBER_7, 230 | NUMBER_8, 231 | NUMBER_9, 232 | COMMA, 233 | PERIOD, 234 | SLASH, 235 | SEMICOLON, 236 | QUOTE, 237 | LBRACKET, 238 | RBRACKET, 239 | BACKSLASH, 240 | MINUS, 241 | PLUS, 242 | TAB, 243 | RETURN, 244 | BACKSPACE, 245 | RIGHT, 246 | LEFT, 247 | UP, 248 | DOWN, 249 | } -------------------------------------------------------------------------------- /imgui/dimgui/src/glwtf/window.d: -------------------------------------------------------------------------------- 1 | module glwtf.window; 2 | 3 | 4 | private { 5 | import glwtf.glfw; 6 | import glwtf.input : BaseGLFWEventHandler; 7 | import glwtf.exception : WindowException; 8 | 9 | import std.string : toStringz; 10 | import std.exception : enforce; 11 | import std.typecons : Tuple; 12 | } 13 | 14 | 15 | struct Rect { 16 | int x; 17 | int y; 18 | } 19 | 20 | private string set_hint_property(string target, string name, bool getter=false) { 21 | string ret = `@property void ` ~ name ~ `(int hint) { 22 | set_hint(` ~ target ~ `, hint); 23 | }`; 24 | 25 | if(getter) { 26 | ret ~= `@property int ` ~ name ~ `() { 27 | return get_attrib(` ~ target ~ `); 28 | }`; 29 | } 30 | 31 | return ret; 32 | } 33 | 34 | 35 | alias Tuple!(int, "major", int, "minor") OGLVT; 36 | immutable OGLVT[] OGLVTS = [OGLVT(4, 3), OGLVT(4, 2), OGLVT(4, 1), OGLVT(4, 0), 37 | OGLVT(3, 3), OGLVT(3, 2), OGLVT(3, 1), OGLVT(3, 0)]; 38 | 39 | class Window : BaseGLFWEventHandler { 40 | debug { 41 | private GLFWwindow* _window; 42 | 43 | @property GLFWwindow* window() { 44 | assert(_window !is null, "no window created yet!"); 45 | return _window; 46 | } 47 | @property void window(GLFWwindow* window) { 48 | _window = window; 49 | } 50 | } else { 51 | GLFWwindow* window; 52 | } 53 | 54 | this() { 55 | super(); 56 | } 57 | 58 | this(GLFWwindow* window) { 59 | super(); 60 | 61 | this.window = window; 62 | register_callbacks(window); 63 | } 64 | 65 | void set_hint(int target, int hint) { 66 | glfwWindowHint(target, hint); 67 | } 68 | 69 | mixin(set_hint_property("GLFW_RED_BITS", "red_bits")); 70 | mixin(set_hint_property("GLFW_GREEN_BITS", "green_bits")); 71 | mixin(set_hint_property("GLFW_BLUE_BITS", "blue_bits")); 72 | mixin(set_hint_property("GLFW_ALPHA_BITS", "alpha_bits")); 73 | mixin(set_hint_property("GLFW_DEPTH_BITS", "depth_bits")); 74 | mixin(set_hint_property("GLFW_STENCIL_BITS", "stencil_bits")); 75 | mixin(set_hint_property("GLFW_ACCUM_RED_BITS", "accum_red_bits")); 76 | mixin(set_hint_property("GLFW_ACCUM_GREEN_BITS", "accum_green_bits")); 77 | mixin(set_hint_property("GLFW_ACCUM_BLUE_BITS", "accum_blue_bits")); 78 | mixin(set_hint_property("GLFW_ACCUM_ALPHA_BITS", "accum_alpha_bits")); 79 | mixin(set_hint_property("GLFW_AUX_BUFFERS", "aux_buffers")); 80 | mixin(set_hint_property("GLFW_STEREO", "stereo")); 81 | mixin(set_hint_property("GLFW_SAMPLES", "samples")); 82 | mixin(set_hint_property("GLFW_SRGB_CAPABLE", "srgb_capable")); 83 | mixin(set_hint_property("GLFW_CLIENT_API", "client_api", true)); 84 | mixin(set_hint_property("GLFW_OPENGL_API", "opengl_api")); 85 | mixin(set_hint_property("GLFW_CONTEXT_VERSION_MAJOR", "context_version_major", true)); 86 | mixin(set_hint_property("GLFW_CONTEXT_VERSION_MINOR", "context_version_minor", true)); 87 | mixin(set_hint_property("GLFW_OPENGL_FORWARD_COMPAT", "opengl_forward_compat", true)); 88 | mixin(set_hint_property("GLFW_OPENGL_DEBUG_CONTEXT", "opengl_debug_context", true)); 89 | mixin(set_hint_property("GLFW_OPENGL_PROFILE", "opengl_profile", true)); 90 | mixin(set_hint_property("GLFW_CONTEXT_ROBUSTNESS", "context_robustness", true)); 91 | mixin(set_hint_property("GLFW_RESIZABLE", "resizable", true)); 92 | mixin(set_hint_property("GLFW_VISIBLE", "visible", true)); 93 | 94 | void create(int width, int height, string title, GLFWmonitor* monitor = null, GLFWwindow* share = null) { 95 | window = glfwCreateWindow(width, height, title.toStringz(), monitor, share); 96 | enforce!WindowException(window !is null, "Failed to create GLFW Window"); 97 | register_callbacks(window); 98 | } 99 | 100 | auto create_highest_available_context(int width, int height, string title, GLFWmonitor* monitor = null, GLFWwindow* share = null, 101 | int opengl_profile = GLFW_OPENGL_CORE_PROFILE, bool forward_compat = true) { 102 | GLFWwindow* win = null; 103 | 104 | foreach(oglvt; OGLVTS) { 105 | this.context_version_major = oglvt.major; 106 | this.context_version_minor = oglvt.minor; 107 | this.opengl_profile = opengl_profile; 108 | this.opengl_forward_compat = forward_compat; 109 | 110 | win = glfwCreateWindow(width, height, title.toStringz(), monitor, share); 111 | 112 | if(win !is null) { 113 | window = win; 114 | register_callbacks(window); 115 | return oglvt; 116 | } 117 | } 118 | 119 | throw new WindowException("Unable to initialize OpenGL forward compatible context (Version >= 3.0)."); 120 | } 121 | 122 | void destroy() { 123 | glfwDestroyWindow(window); 124 | } 125 | 126 | @property void title(string title) { 127 | glfwSetWindowTitle(window, title.toStringz()); 128 | } 129 | 130 | @property void size(Rect rect) { 131 | glfwSetWindowSize(window, rect.x, rect.y); 132 | } 133 | 134 | @property Rect size() { 135 | Rect rect; 136 | glfwGetWindowSize(window, &rect.x, &rect.y); 137 | return rect; 138 | } 139 | 140 | @property int width() { 141 | return size.x; 142 | } 143 | 144 | @property int height() { 145 | return size.y; 146 | } 147 | 148 | void iconify() { 149 | glfwIconifyWindow(window); 150 | } 151 | 152 | void restore() { 153 | glfwRestoreWindow(window); 154 | } 155 | 156 | // void show() { 157 | // glfwShowWindow(window); 158 | // } 159 | // 160 | // void hide() { 161 | // glfwHideWindow(window); 162 | // } 163 | 164 | int get_attrib(int attrib) { 165 | return glfwGetWindowAttrib(window, attrib); 166 | } 167 | 168 | void set_input_mode(int mode, int value) { 169 | glfwSetInputMode(window, mode, value); 170 | } 171 | 172 | int get_input_mode(int mode) { 173 | return glfwGetInputMode(window, mode); 174 | } 175 | 176 | void make_context_current() { 177 | glfwMakeContextCurrent(window); 178 | } 179 | 180 | void swap_buffers() { 181 | glfwSwapBuffers(window); 182 | } 183 | 184 | // callbacks ------ 185 | // window 186 | bool delegate() on_close; 187 | 188 | override bool _on_close() { 189 | if(on_close !is null) { 190 | return on_close(); 191 | } 192 | 193 | return true; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /source/emu/core/hw/gpio/rtc.d: -------------------------------------------------------------------------------- 1 | module hw.gpio.rtc; 2 | 3 | import util; 4 | import std.datetime; 5 | 6 | class RTC_S_35180 { 7 | bool SCK; 8 | bool SIO; 9 | bool CS; 10 | 11 | ubyte serial_data; 12 | int serial_index; 13 | 14 | ubyte* active_register; 15 | ubyte status_register_2; 16 | ubyte date_time_year; 17 | ubyte date_time_month; 18 | ubyte date_time_day; 19 | ubyte date_time_day_of_week; 20 | ubyte date_time_hh; 21 | ubyte date_time_mm; 22 | ubyte date_time_ss; 23 | 24 | int current_command_index; 25 | int current_register_index; 26 | 27 | enum State { 28 | WAITING_FOR_COMMAND, 29 | RECEIVING_COMMAND, 30 | READING_PARAMETERS, 31 | WRITING_REGISTER 32 | } 33 | 34 | struct CommandData { 35 | ubyte*[] registers; 36 | } 37 | CommandData[] commands; 38 | 39 | State state; 40 | 41 | this() { 42 | reset(); 43 | init_commands(); 44 | } 45 | 46 | void init_commands() { 47 | commands = [ 48 | CommandData([]), 49 | CommandData([&status_register_2]), 50 | CommandData([&date_time_year, 51 | &date_time_month, 52 | &date_time_day, 53 | &date_time_day_of_week, 54 | &date_time_hh, 55 | &date_time_mm, 56 | &date_time_ss]), 57 | CommandData([&date_time_hh, 58 | &date_time_mm, 59 | &date_time_ss]), 60 | CommandData([]), 61 | CommandData([]), 62 | CommandData([]), 63 | CommandData([]), 64 | ]; 65 | } 66 | 67 | import std.stdio; 68 | void write(ubyte value) { 69 | 70 | bool old_SCK = SCK; 71 | bool old_SIO = SIO; 72 | bool old_CS = CS; 73 | 74 | SCK = get_nth_bit(value, 0); 75 | SIO = get_nth_bit(value, 1); 76 | CS = get_nth_bit(value, 2); 77 | 78 | if (rising_edge(old_CS, CS)) { 79 | this.state = State.RECEIVING_COMMAND; 80 | } 81 | 82 | if (falling_edge(old_CS, CS)) { 83 | this.state = State.WAITING_FOR_COMMAND; 84 | } 85 | 86 | if (rising_edge(old_SCK, SCK) && state != State.WAITING_FOR_COMMAND) { 87 | 88 | switch (state) { 89 | case State.READING_PARAMETERS: 90 | SIO = get_nth_bit(*this.get_active_register(), this.serial_index); 91 | serial_index++; 92 | 93 | if (this.serial_index == 8) { 94 | this.serial_index = 0; 95 | advance_current_register_value(); 96 | } 97 | 98 | break; 99 | 100 | case State.WRITING_REGISTER: 101 | auto old_value = *this.get_active_register(); 102 | old_value &= ~(1 << this.serial_index); 103 | old_value |= (SIO << this.serial_index); 104 | 105 | *this.get_active_register() = old_value; 106 | serial_index++; 107 | 108 | if (this.serial_index == 8) { 109 | this.serial_index = 0; 110 | advance_current_register_value(); 111 | } 112 | 113 | break; 114 | case State.RECEIVING_COMMAND: 115 | this.serial_data |= (SIO << this.serial_index); 116 | 117 | serial_index++; 118 | 119 | // last serial transfer? 120 | if (this.serial_index == 8) { 121 | this.serial_index = 0; 122 | 123 | if (!is_command(this.serial_data)) { 124 | import core.bitop; 125 | this.serial_data = bitswap((cast(uint) this.serial_data) << 24) & 0xFF; 126 | } 127 | 128 | this.state = get_nth_bit(this.serial_data, 0) ? 129 | State.READING_PARAMETERS : 130 | State.WRITING_REGISTER; 131 | 132 | auto command = get_nth_bits(this.serial_data, 1, 4); 133 | handle_command(command); 134 | this.serial_data = 0; 135 | } 136 | break; 137 | default: break; 138 | } 139 | } 140 | } 141 | 142 | ubyte to_bcd(int input) { 143 | // assumes 2 digits in input 144 | auto digit_1 = input / 10; 145 | auto digit_2 = input % 10; 146 | return cast(ubyte) ((digit_1 << 4) | digit_2); 147 | } 148 | 149 | bool is_command(ubyte data) { 150 | return get_nth_bits(data, 4, 8) == 6; 151 | } 152 | 153 | void advance_current_register_value() { 154 | auto current_command = commands[current_command_index]; 155 | 156 | if (current_register_index + 1 >= current_command.registers.length) { 157 | current_register_index = 0; 158 | state = State.WAITING_FOR_COMMAND; 159 | return; 160 | } 161 | 162 | auto next_register = current_command.registers[current_register_index + 1]; 163 | 164 | set_active_register_value(next_register); 165 | current_register_index++; 166 | } 167 | 168 | void set_active_register_value(ubyte* register) { 169 | this.active_register = register; 170 | } 171 | 172 | ubyte* get_active_register() { 173 | return this.active_register; 174 | } 175 | 176 | void handle_command(int command) { 177 | switch (command) { 178 | case 0: reset(); break; 179 | 180 | default: 181 | reset_time(); 182 | this.current_command_index = command; 183 | this.current_register_index = 0; 184 | set_active_register_value(commands[command].registers[0]); 185 | } 186 | } 187 | 188 | void reset_time() { 189 | auto st = Clock.currTime(); 190 | this.date_time_year = to_bcd(st.year - 2000); 191 | this.date_time_month = to_bcd(st.month); 192 | this.date_time_day = to_bcd(st.day); 193 | this.date_time_day_of_week = to_bcd(st.dayOfWeek); 194 | this.date_time_hh = to_bcd(st.hour); 195 | this.date_time_mm = to_bcd(st.minute); 196 | this.date_time_ss = to_bcd(st.second); 197 | } 198 | 199 | void reset() { 200 | state = State.WAITING_FOR_COMMAND; 201 | 202 | this.SCK = false; 203 | this.SIO = false; 204 | this.CS = false; 205 | 206 | this.serial_data = 0; 207 | this.serial_index = 0; 208 | 209 | this.current_command_index = 0; 210 | this.current_register_index = 0; 211 | 212 | reset_time(); 213 | 214 | set_active_register_value(&status_register_2); 215 | status_register_2 = 0; 216 | } 217 | 218 | bool rising_edge (bool old_value, bool new_value) { return !old_value && new_value; } 219 | bool falling_edge(bool old_value, bool new_value) { return old_value && !new_value; } 220 | 221 | ubyte read() { 222 | return (SCK << 2) | (SIO << 1) | CS; 223 | } 224 | } -------------------------------------------------------------------------------- /source/tools/profiler/profiler.d: -------------------------------------------------------------------------------- 1 | module tools.profiler.profiler; 2 | 3 | import tools.profiler.functiontree; 4 | 5 | import elf; 6 | import util; 7 | 8 | import std.algorithm; 9 | import std.array; 10 | import std.stdio; 11 | 12 | import hw.gba; 13 | import hw.cpu; 14 | import hw.memory; 15 | 16 | struct Function { 17 | Word start_address; 18 | Word end_address; 19 | string name; 20 | bool is_thumb; 21 | } 22 | 23 | final class Profiler { 24 | GBA gba; 25 | 26 | FunctionTree tree; 27 | Function[] functions; 28 | 29 | ELF elf; 30 | 31 | // please don't execute code anywhere that's not rom or ram pls ty 32 | alias FunctionIndex = int; 33 | alias FunctionMap = FunctionIndex[]; 34 | FunctionMap function_map__iwram; 35 | FunctionMap function_map__ewram; 36 | FunctionMap function_map__rom; 37 | FunctionMap*[] function_maps; 38 | Word[] function_map_masks; 39 | 40 | Word[] callstack_indices; 41 | int callstack_size = 0; 42 | 43 | bool in_function_prologue; 44 | bool in_function_epilogue; 45 | 46 | this(GBA gba, string elf_file) { 47 | this.gba = gba; 48 | 49 | elf = ELF.fromFile(elf_file); 50 | 51 | function_map__iwram = new FunctionIndex[SIZE_WRAM_CHIP / 2]; 52 | function_map__ewram = new FunctionIndex[SIZE_WRAM_BOARD / 2]; 53 | function_map__rom = new FunctionIndex[SIZE_ROM / 2]; 54 | 55 | foreach (symbol; SymbolTable(elf.getSection(".symtab").get).symbols()) { 56 | if (symbol.type == SymbolType.func && symbol.size != 0) { 57 | functions ~= Function( 58 | cast(Word) (symbol.value & ~1), 59 | cast(Word) ((symbol.value & ~1) + symbol.size) - (symbol.value & 1 ? 2 : 4), 60 | symbol.name, 61 | symbol.value & 1 62 | ); 63 | 64 | // writefln("%08x ~ %08x : %s", symbol.value, symbol.value + symbol.size, symbol.name); 65 | } 66 | } 67 | 68 | function_map_masks = [ 69 | 0, 70 | 0, 71 | SIZE_WRAM_BOARD - 1, 72 | SIZE_WRAM_CHIP - 1, 73 | 0, 74 | 0, 75 | 0, 76 | 0, 77 | SIZE_ROM - 1, 78 | SIZE_ROM - 1, 79 | SIZE_ROM - 1, 80 | SIZE_ROM - 1, 81 | SIZE_ROM - 1, 82 | SIZE_ROM - 1, 83 | 0, 84 | 0 85 | ]; 86 | 87 | generate_function_map(&function_map__iwram, OFFSET_WRAM_CHIP); 88 | generate_function_map(&function_map__ewram, OFFSET_WRAM_BOARD); 89 | generate_function_map(&function_map__rom, OFFSET_ROM_1); 90 | 91 | function_maps = [ 92 | null, 93 | null, 94 | &function_map__ewram, 95 | &function_map__iwram, 96 | null, 97 | null, 98 | null, 99 | null, 100 | &function_map__rom, 101 | &function_map__rom, 102 | &function_map__rom, 103 | &function_map__rom, 104 | &function_map__rom, 105 | &function_map__rom, 106 | null, 107 | null 108 | ]; 109 | 110 | callstack_indices = new Word[200]; 111 | 112 | tree = new FunctionTree(); 113 | 114 | 115 | writeln(); 116 | writeln("'.debug_abbrev' section contents:"); 117 | 118 | // ELF .debug_abbrev information 119 | 120 | writeln(); 121 | writeln("'.debug_line' section contents:"); 122 | 123 | // ELF .debug_line information 124 | ELFSection dlSection = elf.getSection(".debug_line").get; 125 | 126 | auto dl = DebugLine(dlSection); 127 | foreach (program; dl.programs) { 128 | writefln(" Files:\n%-( %s\n%)\n", program.allFiles()); 129 | writefln("%-( %s\n%)", program.addressInfo.map!(a => "0x%x => %s@%s".format(a.address, program.fileFromIndex(a.fileIndex), a.line))); 130 | } 131 | error("sussy"); 132 | } 133 | 134 | void generate_function_map(FunctionMap* function_map, Word offset) { 135 | auto region = get_nth_bits(offset, 24, 28); 136 | 137 | struct FunctionIndexPair { 138 | Function f; 139 | FunctionIndex i; 140 | } 141 | 142 | // we only want the functions that reside in the current function map region. 143 | // we also want to loop through the functions from largest to smallest, as that *greatly* simplifies the algorithm. 144 | FunctionIndexPair[] temp = []; 145 | for (int i = 0; i < functions.length; i++) { 146 | temp ~= FunctionIndexPair(functions[i], i); 147 | } 148 | 149 | auto sorted_funcs = temp.filter!((fip) => get_nth_bits(fip.f.start_address, 24, 28) == region) 150 | .array 151 | .sort!((fip1, fip2) => (fip1.f.end_address - fip1.f.start_address) > (fip2.f.end_address - fip2.f.start_address)); 152 | 153 | foreach (fip; sorted_funcs) { 154 | for (Word address = fip.f.start_address; address < fip.f.end_address; address += 2) { 155 | (*function_map)[(address / 2) & function_map_masks[region]] = fip.i; 156 | } 157 | } 158 | } 159 | 160 | FunctionIndex get_function_index(Word address) { 161 | auto region = get_nth_bits(address, 24, 28); 162 | auto map = function_maps[region]; 163 | 164 | if (map != null) { 165 | return (*map)[(address / 2) & function_map_masks[region]]; 166 | } else { 167 | return -1; 168 | } 169 | } 170 | 171 | void push_to_callstack(Word sp) { 172 | callstack_indices[callstack_size++] = sp; 173 | } 174 | 175 | Word pop_from_callstack() { 176 | return callstack_indices[--callstack_size]; 177 | } 178 | 179 | Word peek_callstack() { 180 | return callstack_indices[callstack_size - 1]; 181 | } 182 | 183 | void notify__cpu_at_address(Word address) { 184 | // assume entry points to functions are at the beginning of said functions 185 | if (get_function_index(address) != get_function_index(address - 2)) { 186 | enter_function(gba.cpu.regs[pc]); 187 | } 188 | } 189 | 190 | // yes theres a lot of extraneous arguments in these notify functions 191 | // but i leave them there in case i need them in the future. 192 | void notify__cpu_decremented_sp(Word amount) { 193 | 194 | } 195 | 196 | void notify__cpu_incremented_sp(Word amount) { 197 | 198 | } 199 | 200 | void notify__cpu_bx(Reg reg) { 201 | if (reg == lr) exit_function(); 202 | } 203 | 204 | 205 | void notify__cpu_pushed_stack(Reg reg, Word address) { 206 | // if (in_function_prologue && reg == lr) { 207 | // push_to_callstack(address); 208 | // enter_function(gba.cpu.regs[pc]); 209 | 210 | // in_function_prologue = false; 211 | // } 212 | } 213 | 214 | void notify__cpu_popped_stack(Reg reg, Word address) { 215 | // if (callstack_size > 0 && peek_callstack() == address) { 216 | // pop_from_callstack(); 217 | // exit_function(); 218 | 219 | // in_function_epilogue = false; 220 | // } 221 | } 222 | 223 | void enter_function(Word address) { 224 | auto index = get_function_index(address); 225 | writefln("entering %s", functions[index].name); 226 | 227 | if (index != -1) tree.enter_function(index); 228 | } 229 | 230 | void exit_function() { 231 | writefln("exitting."); 232 | tree.exit_function(); 233 | } 234 | } -------------------------------------------------------------------------------- /source/emu/core/hw/ppu/layer/background_layer.d: -------------------------------------------------------------------------------- 1 | module hw.ppu.layer.background_layer; 2 | 3 | // import ppu; 4 | // import memory; 5 | // import util; 6 | 7 | // import std.stdio; 8 | 9 | // class BackgroundLayer : Layer { 10 | // public int background_id; 11 | 12 | // // the pos at which we will draw at 13 | // DrawPos draw_pos; 14 | 15 | // // this will be added to the draw_pos to get the actual pos we will draw at. 16 | // Point draw_delta; 17 | 18 | // // the point on the background where we'll get the pixel which we should draw 19 | // Point texture_point; 20 | 21 | // // the coordinates of the tile in tile-space that we are rendering. 22 | // Point tile_point; 23 | 24 | // int screen_base_address; 25 | // int tile_base_address; 26 | 27 | // this(Memory memory, int background_id) { 28 | // super(memory); 29 | // this.background_id = background_id; 30 | // draw_pos.full_pos = 0; 31 | // } 32 | 33 | // static int[][] BG_TEXT_SCREENS_DIMENSIONS = [ 34 | // [1, 1], 35 | // [1, 2], 36 | // [2, 1], 37 | // [2, 2] 38 | // ]; 39 | 40 | // int get_tile_address__text(int tile_x, int tile_y, int screens_per_row) { 41 | // // each screen is 32 x 32 tiles. so to get the tile offset within its screen 42 | // // we can get the low 5 bits 43 | // int tile_x_within_screen = tile_x & 0x1F; 44 | // int tile_y_within_screen = tile_y & 0x1F; 45 | 46 | // // similarly we can find out which screen this tile is located in 47 | // // by getting its high bit 48 | // int screen_x = (tile_x >> 5) & 1; 49 | // int screen_y = (tile_y >> 5) & 1; 50 | // int screen = screen_x + screen_y * screens_per_row; 51 | 52 | // int tile_address_offset_within_screen = ((tile_y_within_screen * 32) + tile_x_within_screen) * 2; 53 | // return tile_address_offset_within_screen + screen * 0x800; 54 | // } 55 | 56 | // override Pixel calculate_row() { 57 | // if (!backgrounds[background_id].enabled) return Pixel(0, 0, 0, true); 58 | 59 | // // are we rendering the first pixel in a scanline? cache these values. they won't change much. 60 | // if (draw_pos.pos.x == 0) { 61 | // screen_base_address = memory.OFFSET_VRAM + backgrounds[background_id].screen_base_block * 0x800; 62 | // tile_base_address = memory.OFFSET_VRAM + backgrounds[background_id].character_base_block * 0x4000; 63 | // texture_point.x = backgrounds[background_id].x_offset + 0; 64 | // texture_point.y = backgrounds[background_id].y_offset + draw_pos.pos.y; 65 | // } 66 | 67 | // Point draw_delta = Point(texture_point.x & 0b111, 68 | // texture_point.y & 0b111); 69 | // Point tile_point = Point(texture_point.x >> 3, 70 | // texture_point.y >> 3); 71 | 72 | // int tile_address = get_tile_address__text(tile_point.x, tile_point.y, BG_TEXT_SCREENS_DIMENSIONS[backgrounds[background_id].screen_size][0]); 73 | // int tile = memory.read_half(screen_base_address + tile_address); 74 | 75 | // ubyte index = 0; 76 | // if (backgrounds[background_id].doesnt_use_color_palettes) { 77 | // index = memory.read_byte(tile_base_address + ((tile & 0x3ff) * 64) + draw_delta.y * 8 + draw_delta.x); 78 | // } else { 79 | // index = memory.read_byte(tile_base_address + ((tile & 0x3ff) * 32) + draw_delta.y * 4 + (draw_delta.x / 2)); 80 | 81 | // index = (draw_delta.x % 2 == 0) ? index & 0xF : index >> 4; 82 | 83 | // immutable int palette = get_nth_bits(tile, 12, 16); 84 | // index += palette * 16; 85 | // } 86 | 87 | // texture_point.x++; 88 | // draw_pos.full_pos++; 89 | 90 | // if (index == 0) return Pixel(0, 0, 0, true); 91 | // return get_pixel_from_color(memory.read_half(memory.OFFSET_PALETTE_RAM + index * 2), false); 92 | 93 | // // for (int tile_x_offset = 0; tile_x_offset < 32 + 1; tile_x_offset++) { 94 | 95 | // // // get the tile address and read it from memory 96 | // // int tile_address = get_tile_address__text(topleft_tile_x + tile_x_offset, topleft_tile_y, BG_TEXT_SCREENS_DIMENSIONS[background.screen_size][0]); 97 | // // int tile = memory.read_half(screen_base_address + tile_address); 98 | 99 | // // int draw_x = tile_x_offset * 8 - tile_dx; 100 | // // int draw_y = scanline; 101 | 102 | // // bool flipped_x = (tile >> 10) & 1; 103 | // // bool flipped_y = (tile >> 11) & 1; 104 | 105 | // // for (int tile_x = 0; tile_x < 8; tile_x++) { 106 | // // int draw_x = left_x + tile_x; 107 | // // int draw_y = scanline; 108 | 109 | // // int x = left_x - tile_x; 110 | 111 | // // int tile_dx = flipped_x ? (7 - tile_x) : tile_x; 112 | // // int tile_dy = flipped_y ? (7 - y) : y; 113 | 114 | 115 | // // static if (bpp8) { 116 | // // ubyte index = memory.read_byte(tile_base_address + ((tile & 0x3ff) * 64) + tile_dy * 8 + tile_dx); 117 | 118 | // // return index_to_pixel(index); 119 | // // } else { 120 | // // ubyte index = memory.read_byte(tile_base_address + ((tile & 0x3ff) * 32) + tile_dy * 4 + (tile_dx / 2)); 121 | 122 | // // index = (tile_dx % 2 == 0) ? index & 0xF : index >> 4; 123 | // // index += palette * 16; 124 | // // return index_to_pixel(index); 125 | // // } 126 | // // } 127 | // // // yes this looks stupid. and it is. 128 | // // if (background.doesnt_use_color_palettes) { 129 | // // Render!(true).tile( 130 | // // Layer.A, tile, tile_base_address, 0, 131 | // // draw_x, tile_dy, 132 | // // 0, 0, PMatrix(0, 0, 0, 0), false, 133 | // // flipped_x, flipped_y, 134 | // // get_nth_bits(tile, 12, 16)); 135 | // // } else { 136 | // // Render!(false).tile( 137 | // // Layer.A, tile, tile_base_address, 0, 138 | // // draw_x, tile_dy, 139 | // // 0, 0, PMatrix(0, 0, 0, 0), false, 140 | // // flipped_x, flipped_y, 141 | // // get_nth_bits(tile, 12, 16)); 142 | // // } 143 | // // } 144 | 145 | // } 146 | 147 | // // void tile(Layer layer, int tile, int tile_base_address, int palette_base_address, int left_x, int y, ref_x, ref_y, PMatrix p_matrix, bool scaled, bool flipped_x, bool flipped_y, int palette) { 148 | 149 | // // } 150 | 151 | // override void on_vblank() { 152 | // draw_pos.full_pos = 0; 153 | // } 154 | 155 | // override void skip_pixel() { 156 | // if (draw_pos.pos.x == 0) { 157 | // screen_base_address = memory.OFFSET_VRAM + backgrounds[background_id].screen_base_block * 0x800; 158 | // tile_base_address = memory.OFFSET_VRAM + backgrounds[background_id].character_base_block * 0x4000; 159 | // texture_point.x = backgrounds[background_id].x_offset + 0; 160 | // texture_point.y = backgrounds[background_id].y_offset + draw_pos.pos.y; 161 | // } 162 | 163 | // texture_point.x++; 164 | // draw_pos.full_pos++; 165 | // } 166 | // } --------------------------------------------------------------------------------