From 3c42e6caa0deff5ab0bd6638b51a73d78f9ce5ac Mon Sep 17 00:00:00 2001 From: byuu Date: Thu, 30 Apr 2009 20:58:39 +0000 Subject: [PATCH] Update to bsnes v045r09 release. [No changelog available] --- src/Makefile | 82 +- src/base.hpp | 2 +- src/cart/cart.cpp | 234 - src/cart/cart.hpp | 179 - src/cart/cart_file.cpp | 109 - src/cart/cart_loader.cpp | 245 -- src/cartridge/cartridge.cpp | 140 + src/cartridge/cartridge.hpp | 137 + .../cartridge_header.cpp} | 47 +- src/cartridge/cartridge_loader.cpp | 148 + src/cc.bat | 2 +- src/cc.sh | 2 +- src/cheat/cheat.cpp | 5 + src/chip/bsx/bsx.cpp | 8 +- src/chip/chip.hpp | 6 + src/chip/cx4/cx4.cpp | 7 +- src/chip/dsp1/dsp1.cpp | 8 +- src/chip/dsp2/dsp2.cpp | 7 +- src/chip/dsp3/dsp3.cpp | 7 +- src/chip/dsp4/dsp4.cpp | 7 +- src/chip/obc1/obc1.cpp | 7 +- src/chip/sa1/sa1.cpp | 11 +- src/chip/sdd1/sdd1.cpp | 8 +- src/chip/sgb/interface/gambatte.cpp | 174 + src/chip/sgb/interface/interface.hpp | 98 + src/chip/sgb/interface/reference.cpp | 24 + src/chip/sgb/sgb.cpp | 147 + src/chip/sgb/sgb.hpp | 44 + src/chip/spc7110/spc7110.cpp | 8 +- src/chip/srtc/srtc.cpp | 7 +- src/chip/st010/st010.cpp | 7 +- src/cpu/core/core.cpp | 5 + src/cpu/core/opcode_headers.bpp | 2 + src/cpu/cpu.cpp | 8 +- src/cpu/scpu/mmio/mmio.cpp | 10 +- src/cpu/scpu/scpu.cpp | 8 +- src/cpu/scpu/timing/joypad.cpp | 4 +- src/cpu/scpu/timing/timing.cpp | 4 +- src/data/license.html | 2 + src/dsp/adsp/adsp.cpp | 8 +- src/dsp/sdsp/echo.cpp | 2 +- src/dsp/sdsp/sdsp.cpp | 3 + src/interface.hpp | 42 +- src/lib/libgambatte/COPYING | 339 ++ src/lib/libgambatte/Makefile | 69 + src/lib/libgambatte/README | 130 + src/lib/libgambatte/common/adaptivesleep.cpp | 56 + src/lib/libgambatte/common/adaptivesleep.h | 34 + src/lib/libgambatte/common/array.h | 40 + src/lib/libgambatte/common/rateest.cpp | 96 + src/lib/libgambatte/common/rateest.h | 73 + .../common/resample/blackmansinc.h | 100 + .../common/resample/chainresampler.cpp | 118 + .../common/resample/chainresampler.h | 189 + src/lib/libgambatte/common/resample/cic2.h | 198 + src/lib/libgambatte/common/resample/cic3.h | 382 ++ src/lib/libgambatte/common/resample/cic4.h | 237 + .../libgambatte/common/resample/convoluter.h | 156 + .../libgambatte/common/resample/hammingsinc.h | 100 + src/lib/libgambatte/common/resample/linint.h | 129 + .../common/resample/makesinckernel.h | 152 + .../libgambatte/common/resample/rectsinc.h | 99 + .../libgambatte/common/resample/resampler.h | 43 + .../common/resample/resamplerinfo.cpp | 61 + .../common/resample/resamplerinfo.h | 36 + .../common/resample/subresampler.h | 33 + .../libgambatte/common/resample/u48div.cpp | 54 + src/lib/libgambatte/common/resample/u48div.h | 24 + .../libgambatte/common/resample/upsampler.h | 51 + src/lib/libgambatte/common/ringbuffer.h | 112 + src/lib/libgambatte/common/usec.h | 31 + src/lib/libgambatte/include/filterinfo.h | 32 + src/lib/libgambatte/include/gambatte.h | 84 + src/lib/libgambatte/include/inputstate.h | 30 + .../libgambatte/include/inputstategetter.h | 30 + src/lib/libgambatte/include/int.h | 62 + src/lib/libgambatte/include/memoryinterface.h | 25 + src/lib/libgambatte/include/videoblitter.h | 44 + src/lib/libgambatte/src/bitmap_font.cpp | 328 ++ src/lib/libgambatte/src/bitmap_font.h | 87 + src/lib/libgambatte/src/colorconversion.cpp | 96 + src/lib/libgambatte/src/colorconversion.h | 46 + src/lib/libgambatte/src/cpu.cpp | 2850 ++++++++++++ src/lib/libgambatte/src/cpu.h | 117 + src/lib/libgambatte/src/event_queue.h | 160 + src/lib/libgambatte/src/file/file.cpp | 73 + src/lib/libgambatte/src/file/file.h | 42 + src/lib/libgambatte/src/file/file_zip.cpp | 167 + .../libgambatte/src/file/unzip}/crypt.h | 0 src/lib/libgambatte/src/file/unzip/ioapi.c | 177 + .../libgambatte/src/file/unzip}/ioapi.h | 0 src/lib/libgambatte/src/file/unzip/unzip.c | 1605 +++++++ src/lib/libgambatte/src/file/unzip/unzip.h | 354 ++ src/lib/libgambatte/src/gambatte.cpp | 184 + src/lib/libgambatte/src/initstate.cpp | 281 ++ src/lib/libgambatte/src/initstate.h | 26 + src/lib/libgambatte/src/insertion_sort.h | 51 + src/lib/libgambatte/src/interrupter.cpp | 44 + src/lib/libgambatte/src/interrupter.h | 38 + src/lib/libgambatte/src/memory.cpp | 1937 +++++++++ src/lib/libgambatte/src/memory.h | 243 ++ src/lib/libgambatte/src/osd_element.h | 65 + src/lib/libgambatte/src/rtc.cpp | 157 + src/lib/libgambatte/src/rtc.h | 97 + src/lib/libgambatte/src/savestate.h | 184 + src/lib/libgambatte/src/sound.cpp | 155 + src/lib/libgambatte/src/sound.h | 95 + src/lib/libgambatte/src/sound/channel1.cpp | 257 ++ src/lib/libgambatte/src/sound/channel1.h | 91 + src/lib/libgambatte/src/sound/channel2.cpp | 161 + src/lib/libgambatte/src/sound/channel2.h | 70 + src/lib/libgambatte/src/sound/channel3.cpp | 207 + src/lib/libgambatte/src/sound/channel3.h | 100 + src/lib/libgambatte/src/sound/channel4.cpp | 300 ++ src/lib/libgambatte/src/sound/channel4.h | 99 + src/lib/libgambatte/src/sound/duty_unit.cpp | 148 + src/lib/libgambatte/src/sound/duty_unit.h | 64 + .../libgambatte/src/sound/envelope_unit.cpp | 101 + src/lib/libgambatte/src/sound/envelope_unit.h | 50 + .../libgambatte/src/sound/length_counter.cpp | 87 + .../libgambatte/src/sound/length_counter.h | 44 + .../libgambatte/src/sound/master_disabler.h | 31 + src/lib/libgambatte/src/sound/sound_unit.h | 35 + .../src/sound/static_output_tester.h | 41 + .../libgambatte/src/state_osd_elements.cpp | 169 + src/lib/libgambatte/src/state_osd_elements.h | 29 + src/lib/libgambatte/src/statesaver.cpp | 407 ++ src/lib/libgambatte/src/statesaver.h | 37 + src/lib/libgambatte/src/video.cpp | 1474 +++++++ src/lib/libgambatte/src/video.h | 292 ++ .../libgambatte/src/video/basic_add_event.cpp | 75 + .../libgambatte/src/video/basic_add_event.h | 56 + src/lib/libgambatte/src/video/break_event.cpp | 35 + src/lib/libgambatte/src/video/break_event.h | 59 + .../src/video/filters/catrom2x.cpp | 194 + .../libgambatte/src/video/filters/catrom2x.h | 40 + .../src/video/filters/catrom3x.cpp | 360 ++ .../libgambatte/src/video/filters/catrom3x.h | 40 + .../libgambatte/src/video/filters/filter.h | 39 + .../src/video/filters/kreed2xsai.cpp | 243 ++ .../src/video/filters/kreed2xsai.h | 40 + .../src/video/filters/maxsthq2x.cpp | 2875 ++++++++++++ .../libgambatte/src/video/filters/maxsthq2x.h | 41 + .../src/video/filters/maxsthq3x.cpp | 3845 +++++++++++++++++ .../libgambatte/src/video/filters/maxsthq3x.h | 40 + src/lib/libgambatte/src/video/irq_event.cpp | 36 + src/lib/libgambatte/src/video/irq_event.h | 52 + src/lib/libgambatte/src/video/ly_counter.cpp | 62 + src/lib/libgambatte/src/video/ly_counter.h | 69 + src/lib/libgambatte/src/video/lyc_irq.cpp | 42 + src/lib/libgambatte/src/video/lyc_irq.h | 67 + .../libgambatte/src/video/m3_extra_cycles.cpp | 101 + .../libgambatte/src/video/m3_extra_cycles.h | 56 + src/lib/libgambatte/src/video/mode0_irq.cpp | 95 + src/lib/libgambatte/src/video/mode0_irq.h | 42 + src/lib/libgambatte/src/video/mode1_irq.cpp | 33 + src/lib/libgambatte/src/video/mode1_irq.h | 56 + src/lib/libgambatte/src/video/mode2_irq.cpp | 63 + src/lib/libgambatte/src/video/mode2_irq.h | 40 + src/lib/libgambatte/src/video/mode3_event.cpp | 62 + src/lib/libgambatte/src/video/mode3_event.h | 47 + src/lib/libgambatte/src/video/sc_reader.cpp | 62 + src/lib/libgambatte/src/video/sc_reader.h | 77 + src/lib/libgambatte/src/video/scx_reader.cpp | 71 + src/lib/libgambatte/src/video/scx_reader.h | 85 + .../libgambatte/src/video/sprite_mapper.cpp | 187 + src/lib/libgambatte/src/video/sprite_mapper.h | 148 + src/lib/libgambatte/src/video/video_event.h | 50 + .../src/video/video_event_comparer.h | 31 + src/lib/libgambatte/src/video/we.cpp | 59 + src/lib/libgambatte/src/video/we.h | 118 + .../src/video/we_master_checker.cpp | 58 + .../libgambatte/src/video/we_master_checker.h | 73 + src/lib/libgambatte/src/video/window.h | 47 + src/lib/libgambatte/src/video/wx_reader.cpp | 65 + src/lib/libgambatte/src/video/wx_reader.h | 83 + src/lib/libgambatte/src/video/wy.cpp | 105 + src/lib/libgambatte/src/video/wy.h | 187 + src/{reader/jma => lib/libjma}/7z.h | 0 src/{reader/jma => lib/libjma}/7zlzma.cpp | 0 src/{reader/jma => lib/libjma}/aribitcd.h | 0 src/{reader/jma => lib/libjma}/ariconst.h | 0 src/{reader/jma => lib/libjma}/ariprice.h | 0 src/{reader/jma => lib/libjma}/btreecd.h | 0 src/{reader/jma => lib/libjma}/crc32.h | 0 src/{reader/jma => lib/libjma}/iiostrm.cpp | 0 src/{reader/jma => lib/libjma}/iiostrm.h | 0 src/{reader/jma => lib/libjma}/inbyte.cpp | 0 src/{reader/jma => lib/libjma}/inbyte.h | 0 src/{reader/jma => lib/libjma}/jcrc32.cpp | 0 src/{reader/jma => lib/libjma}/jma.cpp | 0 src/{reader/jma => lib/libjma}/jma.h | 0 src/{reader/jma => lib/libjma}/lencoder.h | 0 src/{reader/jma => lib/libjma}/litcoder.h | 0 src/{reader/jma => lib/libjma}/lzma.cpp | 0 src/{reader/jma => lib/libjma}/lzma.h | 0 src/{reader/jma => lib/libjma}/lzmadec.cpp | 0 src/{reader/jma => lib/libjma}/lzmadec.h | 0 src/{reader/jma => lib/libjma}/portable.h | 0 src/{reader/jma => lib/libjma}/rcdefs.h | 0 src/{reader/jma => lib/libjma}/rngcoder.h | 0 src/{reader/jma => lib/libjma}/winout.cpp | 0 src/{reader/jma => lib/libjma}/winout.h | 0 src/lib/nall/datasource.hpp | 38 + src/lib/nall/file.hpp | 15 + src/lib/nall/platform.hpp | 1 + src/lib/nall/utf8.hpp | 1 + src/{ => lib}/reader/filereader.cpp | 26 +- src/{ => lib}/reader/filereader.hpp | 6 +- src/{ => lib}/reader/gzreader.cpp | 0 src/{ => lib}/reader/gzreader.hpp | 2 +- src/{ => lib}/reader/jmareader.cpp | 1 - src/{ => lib}/reader/jmareader.hpp | 2 +- src/{ => lib}/reader/reader.cpp | 0 src/{ => lib}/reader/reader.hpp | 0 src/{ => lib}/reader/zipreader.cpp | 0 src/{ => lib}/reader/zipreader.hpp | 2 +- src/lib/tool/opgen.cpp | 157 - src/lib/tool/opgen_fnptr.cpp | 149 - src/lib/tool/opgen_switch.cpp | 127 - src/{reader => lib}/zlib/adler32.c | 0 src/{reader => lib}/zlib/compress.c | 0 src/{reader => lib}/zlib/crc32.c | 0 src/{reader => lib}/zlib/crc32.h | 0 src/lib/zlib/crypt.h | 132 + src/{reader => lib}/zlib/deflate.c | 0 src/{reader => lib}/zlib/deflate.h | 0 src/{reader => lib}/zlib/gzio.c | 0 src/{reader => lib}/zlib/inffast.c | 0 src/{reader => lib}/zlib/inffast.h | 0 src/{reader => lib}/zlib/inffixed.h | 0 src/{reader => lib}/zlib/inflate.c | 0 src/{reader => lib}/zlib/inflate.h | 0 src/{reader => lib}/zlib/inftrees.c | 0 src/{reader => lib}/zlib/inftrees.h | 0 src/{reader => lib}/zlib/ioapi.c | 0 src/lib/zlib/ioapi.h | 75 + src/{reader => lib}/zlib/trees.c | 0 src/{reader => lib}/zlib/trees.h | 0 src/{reader => lib}/zlib/unzip.c | 0 src/{reader => lib}/zlib/unzip.h | 0 src/{reader => lib}/zlib/zconf.h | 0 src/{reader => lib}/zlib/zip.c | 0 src/{reader => lib}/zlib/zip.h | 0 src/{reader => lib}/zlib/zlib.h | 0 src/{reader => lib}/zlib/zutil.c | 0 src/{reader => lib}/zlib/zutil.h | 0 src/memory/memory.cpp | 4 + src/memory/memory.hpp | 1 + src/memory/smemory/smemory.cpp | 8 +- src/ppu/bppu/bppu.cpp | 12 +- src/ppu/bppu/bppu_mmio.cpp | 2 +- src/ppu/counter.cpp | 2 +- src/ppu/ppu.cpp | 8 +- src/smp/core/bpp.sh | 3 + src/smp/core/core.cpp | 15 + src/smp/{ssmp => }/core/core.hpp | 20 +- src/smp/{dsmp.cpp => core/disasm/disasm.cpp} | 28 +- src/smp/core/disasm/disasm.hpp | 2 + src/smp/core/memory.hpp | 27 + .../opfn.cpp => core/opcode_algorithms.cpp} | 34 +- src/smp/core/opcode_functions.bpp | 11 + src/smp/core/opcode_functions.cpp | 2233 ++++++++++ src/smp/core/opcode_headers.bpp | 323 ++ src/smp/core/opcode_headers.hpp | 676 +++ src/smp/core/opcode_list.bpp | 321 ++ src/smp/core/opcode_misc.bpp | 157 + src/smp/core/opcode_move.bpp | 247 ++ src/smp/core/opcode_pc.bpp | 169 + src/smp/core/opcode_read.bpp | 169 + src/smp/core/opcode_rmw.bpp | 60 + src/smp/core/opcode_table.cpp | 70 + src/smp/{smpregs.hpp => core/registers.hpp} | 0 src/smp/iplrom.hpp | 40 - src/smp/smp.cpp | 50 +- src/smp/smp.hpp | 9 +- src/smp/ssmp/core/cc.sh | 4 - src/smp/ssmp/core/clean.sh | 1 - src/smp/ssmp/core/core.cpp | 26 - src/smp/ssmp/core/op_misc.b | 163 - src/smp/ssmp/core/op_misc.cpp | 349 -- src/smp/ssmp/core/op_mov.b | 217 - src/smp/ssmp/core/op_mov.cpp | 392 -- src/smp/ssmp/core/op_pc.b | 179 - src/smp/ssmp/core/op_pc.cpp | 606 --- src/smp/ssmp/core/op_read.b | 205 - src/smp/ssmp/core/op_read.cpp | 747 ---- src/smp/ssmp/core/op_rmw.b | 74 - src/smp/ssmp/core/op_rmw.cpp | 265 -- src/smp/ssmp/core/ssmpgen.cpp | 12 - src/smp/ssmp/memory/memory.cpp | 57 +- src/smp/ssmp/memory/memory.hpp | 17 - src/smp/ssmp/ssmp.cpp | 22 +- src/smp/ssmp/ssmp.hpp | 3 +- src/snes/audio/audio.cpp | 10 - src/snes/audio/audio.hpp | 7 - src/snes/interface/interface.hpp | 17 - src/snes/snes.hpp | 86 - src/system/audio/audio.cpp | 111 + src/system/audio/audio.hpp | 36 + src/system/config/config.cpp | 19 + src/system/config/config.hpp | 32 + src/{snes => system}/input/input.cpp | 56 +- src/{snes => system}/input/input.hpp | 2 +- src/system/interface/interface.hpp | 12 + src/{snes => system}/scheduler/scheduler.cpp | 14 +- src/{snes => system}/scheduler/scheduler.hpp | 0 src/{snes/snes.cpp => system/system.cpp} | 153 +- src/system/system.hpp | 46 + src/{snes => system}/tracer/tracer.cpp | 2 +- src/{snes => system}/tracer/tracer.hpp | 0 src/{snes => system}/video/video.cpp | 24 +- src/{snes => system}/video/video.hpp | 2 +- src/ui_qt/Makefile | 2 +- src/ui_qt/base/loader.cpp | 62 +- src/ui_qt/base/loader.hpp | 4 +- src/ui_qt/base/main.cpp | 52 +- src/ui_qt/cartridge/cartridge.cpp | 341 ++ src/ui_qt/cartridge/cartridge.hpp | 25 + src/ui_qt/config.cpp | 77 +- src/ui_qt/input/device.cpp | 98 +- src/ui_qt/input/device.hpp | 14 +- src/ui_qt/input/input.cpp | 2 +- src/ui_qt/interface.cpp | 49 +- src/ui_qt/main.cpp | 22 +- src/ui_qt/main.hpp | 14 +- src/ui_qt/settings/advanced.cpp | 20 +- src/ui_qt/settings/cheateditor.cpp | 28 +- src/ui_qt/settings/paths.cpp | 35 +- src/ui_qt/settings/utility/codeeditor.cpp | 18 +- src/ui_qt/utility/cartridge.cpp | 139 +- src/ui_qt/utility/utility.cpp | 37 +- src/ui_qt/utility/utility.hpp | 13 +- 333 files changed, 34968 insertions(+), 5315 deletions(-) delete mode 100644 src/cart/cart.cpp delete mode 100644 src/cart/cart.hpp delete mode 100644 src/cart/cart_file.cpp delete mode 100644 src/cart/cart_loader.cpp create mode 100644 src/cartridge/cartridge.cpp create mode 100644 src/cartridge/cartridge.hpp rename src/{cart/cart_header.cpp => cartridge/cartridge_header.cpp} (92%) create mode 100644 src/cartridge/cartridge_loader.cpp create mode 100644 src/chip/sgb/interface/gambatte.cpp create mode 100644 src/chip/sgb/interface/interface.hpp create mode 100644 src/chip/sgb/interface/reference.cpp create mode 100644 src/chip/sgb/sgb.cpp create mode 100644 src/chip/sgb/sgb.hpp create mode 100644 src/lib/libgambatte/COPYING create mode 100644 src/lib/libgambatte/Makefile create mode 100644 src/lib/libgambatte/README create mode 100644 src/lib/libgambatte/common/adaptivesleep.cpp create mode 100644 src/lib/libgambatte/common/adaptivesleep.h create mode 100644 src/lib/libgambatte/common/array.h create mode 100644 src/lib/libgambatte/common/rateest.cpp create mode 100644 src/lib/libgambatte/common/rateest.h create mode 100644 src/lib/libgambatte/common/resample/blackmansinc.h create mode 100644 src/lib/libgambatte/common/resample/chainresampler.cpp create mode 100644 src/lib/libgambatte/common/resample/chainresampler.h create mode 100644 src/lib/libgambatte/common/resample/cic2.h create mode 100644 src/lib/libgambatte/common/resample/cic3.h create mode 100644 src/lib/libgambatte/common/resample/cic4.h create mode 100644 src/lib/libgambatte/common/resample/convoluter.h create mode 100644 src/lib/libgambatte/common/resample/hammingsinc.h create mode 100644 src/lib/libgambatte/common/resample/linint.h create mode 100644 src/lib/libgambatte/common/resample/makesinckernel.h create mode 100644 src/lib/libgambatte/common/resample/rectsinc.h create mode 100644 src/lib/libgambatte/common/resample/resampler.h create mode 100644 src/lib/libgambatte/common/resample/resamplerinfo.cpp create mode 100644 src/lib/libgambatte/common/resample/resamplerinfo.h create mode 100644 src/lib/libgambatte/common/resample/subresampler.h create mode 100644 src/lib/libgambatte/common/resample/u48div.cpp create mode 100644 src/lib/libgambatte/common/resample/u48div.h create mode 100644 src/lib/libgambatte/common/resample/upsampler.h create mode 100644 src/lib/libgambatte/common/ringbuffer.h create mode 100644 src/lib/libgambatte/common/usec.h create mode 100644 src/lib/libgambatte/include/filterinfo.h create mode 100644 src/lib/libgambatte/include/gambatte.h create mode 100644 src/lib/libgambatte/include/inputstate.h create mode 100644 src/lib/libgambatte/include/inputstategetter.h create mode 100644 src/lib/libgambatte/include/int.h create mode 100644 src/lib/libgambatte/include/memoryinterface.h create mode 100644 src/lib/libgambatte/include/videoblitter.h create mode 100644 src/lib/libgambatte/src/bitmap_font.cpp create mode 100644 src/lib/libgambatte/src/bitmap_font.h create mode 100644 src/lib/libgambatte/src/colorconversion.cpp create mode 100644 src/lib/libgambatte/src/colorconversion.h create mode 100644 src/lib/libgambatte/src/cpu.cpp create mode 100644 src/lib/libgambatte/src/cpu.h create mode 100644 src/lib/libgambatte/src/event_queue.h create mode 100644 src/lib/libgambatte/src/file/file.cpp create mode 100644 src/lib/libgambatte/src/file/file.h create mode 100644 src/lib/libgambatte/src/file/file_zip.cpp rename src/{reader/zlib => lib/libgambatte/src/file/unzip}/crypt.h (100%) create mode 100644 src/lib/libgambatte/src/file/unzip/ioapi.c rename src/{reader/zlib => lib/libgambatte/src/file/unzip}/ioapi.h (100%) create mode 100644 src/lib/libgambatte/src/file/unzip/unzip.c create mode 100644 src/lib/libgambatte/src/file/unzip/unzip.h create mode 100644 src/lib/libgambatte/src/gambatte.cpp create mode 100644 src/lib/libgambatte/src/initstate.cpp create mode 100644 src/lib/libgambatte/src/initstate.h create mode 100644 src/lib/libgambatte/src/insertion_sort.h create mode 100644 src/lib/libgambatte/src/interrupter.cpp create mode 100644 src/lib/libgambatte/src/interrupter.h create mode 100644 src/lib/libgambatte/src/memory.cpp create mode 100644 src/lib/libgambatte/src/memory.h create mode 100644 src/lib/libgambatte/src/osd_element.h create mode 100644 src/lib/libgambatte/src/rtc.cpp create mode 100644 src/lib/libgambatte/src/rtc.h create mode 100644 src/lib/libgambatte/src/savestate.h create mode 100644 src/lib/libgambatte/src/sound.cpp create mode 100644 src/lib/libgambatte/src/sound.h create mode 100644 src/lib/libgambatte/src/sound/channel1.cpp create mode 100644 src/lib/libgambatte/src/sound/channel1.h create mode 100644 src/lib/libgambatte/src/sound/channel2.cpp create mode 100644 src/lib/libgambatte/src/sound/channel2.h create mode 100644 src/lib/libgambatte/src/sound/channel3.cpp create mode 100644 src/lib/libgambatte/src/sound/channel3.h create mode 100644 src/lib/libgambatte/src/sound/channel4.cpp create mode 100644 src/lib/libgambatte/src/sound/channel4.h create mode 100644 src/lib/libgambatte/src/sound/duty_unit.cpp create mode 100644 src/lib/libgambatte/src/sound/duty_unit.h create mode 100644 src/lib/libgambatte/src/sound/envelope_unit.cpp create mode 100644 src/lib/libgambatte/src/sound/envelope_unit.h create mode 100644 src/lib/libgambatte/src/sound/length_counter.cpp create mode 100644 src/lib/libgambatte/src/sound/length_counter.h create mode 100644 src/lib/libgambatte/src/sound/master_disabler.h create mode 100644 src/lib/libgambatte/src/sound/sound_unit.h create mode 100644 src/lib/libgambatte/src/sound/static_output_tester.h create mode 100644 src/lib/libgambatte/src/state_osd_elements.cpp create mode 100644 src/lib/libgambatte/src/state_osd_elements.h create mode 100644 src/lib/libgambatte/src/statesaver.cpp create mode 100644 src/lib/libgambatte/src/statesaver.h create mode 100644 src/lib/libgambatte/src/video.cpp create mode 100644 src/lib/libgambatte/src/video.h create mode 100644 src/lib/libgambatte/src/video/basic_add_event.cpp create mode 100644 src/lib/libgambatte/src/video/basic_add_event.h create mode 100644 src/lib/libgambatte/src/video/break_event.cpp create mode 100644 src/lib/libgambatte/src/video/break_event.h create mode 100644 src/lib/libgambatte/src/video/filters/catrom2x.cpp create mode 100644 src/lib/libgambatte/src/video/filters/catrom2x.h create mode 100644 src/lib/libgambatte/src/video/filters/catrom3x.cpp create mode 100644 src/lib/libgambatte/src/video/filters/catrom3x.h create mode 100644 src/lib/libgambatte/src/video/filters/filter.h create mode 100644 src/lib/libgambatte/src/video/filters/kreed2xsai.cpp create mode 100644 src/lib/libgambatte/src/video/filters/kreed2xsai.h create mode 100644 src/lib/libgambatte/src/video/filters/maxsthq2x.cpp create mode 100644 src/lib/libgambatte/src/video/filters/maxsthq2x.h create mode 100644 src/lib/libgambatte/src/video/filters/maxsthq3x.cpp create mode 100644 src/lib/libgambatte/src/video/filters/maxsthq3x.h create mode 100644 src/lib/libgambatte/src/video/irq_event.cpp create mode 100644 src/lib/libgambatte/src/video/irq_event.h create mode 100644 src/lib/libgambatte/src/video/ly_counter.cpp create mode 100644 src/lib/libgambatte/src/video/ly_counter.h create mode 100644 src/lib/libgambatte/src/video/lyc_irq.cpp create mode 100644 src/lib/libgambatte/src/video/lyc_irq.h create mode 100644 src/lib/libgambatte/src/video/m3_extra_cycles.cpp create mode 100644 src/lib/libgambatte/src/video/m3_extra_cycles.h create mode 100644 src/lib/libgambatte/src/video/mode0_irq.cpp create mode 100644 src/lib/libgambatte/src/video/mode0_irq.h create mode 100644 src/lib/libgambatte/src/video/mode1_irq.cpp create mode 100644 src/lib/libgambatte/src/video/mode1_irq.h create mode 100644 src/lib/libgambatte/src/video/mode2_irq.cpp create mode 100644 src/lib/libgambatte/src/video/mode2_irq.h create mode 100644 src/lib/libgambatte/src/video/mode3_event.cpp create mode 100644 src/lib/libgambatte/src/video/mode3_event.h create mode 100644 src/lib/libgambatte/src/video/sc_reader.cpp create mode 100644 src/lib/libgambatte/src/video/sc_reader.h create mode 100644 src/lib/libgambatte/src/video/scx_reader.cpp create mode 100644 src/lib/libgambatte/src/video/scx_reader.h create mode 100644 src/lib/libgambatte/src/video/sprite_mapper.cpp create mode 100644 src/lib/libgambatte/src/video/sprite_mapper.h create mode 100644 src/lib/libgambatte/src/video/video_event.h create mode 100644 src/lib/libgambatte/src/video/video_event_comparer.h create mode 100644 src/lib/libgambatte/src/video/we.cpp create mode 100644 src/lib/libgambatte/src/video/we.h create mode 100644 src/lib/libgambatte/src/video/we_master_checker.cpp create mode 100644 src/lib/libgambatte/src/video/we_master_checker.h create mode 100644 src/lib/libgambatte/src/video/window.h create mode 100644 src/lib/libgambatte/src/video/wx_reader.cpp create mode 100644 src/lib/libgambatte/src/video/wx_reader.h create mode 100644 src/lib/libgambatte/src/video/wy.cpp create mode 100644 src/lib/libgambatte/src/video/wy.h rename src/{reader/jma => lib/libjma}/7z.h (100%) rename src/{reader/jma => lib/libjma}/7zlzma.cpp (100%) rename src/{reader/jma => lib/libjma}/aribitcd.h (100%) rename src/{reader/jma => lib/libjma}/ariconst.h (100%) rename src/{reader/jma => lib/libjma}/ariprice.h (100%) rename src/{reader/jma => lib/libjma}/btreecd.h (100%) rename src/{reader/jma => lib/libjma}/crc32.h (100%) rename src/{reader/jma => lib/libjma}/iiostrm.cpp (100%) rename src/{reader/jma => lib/libjma}/iiostrm.h (100%) rename src/{reader/jma => lib/libjma}/inbyte.cpp (100%) rename src/{reader/jma => lib/libjma}/inbyte.h (100%) rename src/{reader/jma => lib/libjma}/jcrc32.cpp (100%) rename src/{reader/jma => lib/libjma}/jma.cpp (100%) rename src/{reader/jma => lib/libjma}/jma.h (100%) rename src/{reader/jma => lib/libjma}/lencoder.h (100%) rename src/{reader/jma => lib/libjma}/litcoder.h (100%) rename src/{reader/jma => lib/libjma}/lzma.cpp (100%) rename src/{reader/jma => lib/libjma}/lzma.h (100%) rename src/{reader/jma => lib/libjma}/lzmadec.cpp (100%) rename src/{reader/jma => lib/libjma}/lzmadec.h (100%) rename src/{reader/jma => lib/libjma}/portable.h (100%) rename src/{reader/jma => lib/libjma}/rcdefs.h (100%) rename src/{reader/jma => lib/libjma}/rngcoder.h (100%) rename src/{reader/jma => lib/libjma}/winout.cpp (100%) rename src/{reader/jma => lib/libjma}/winout.h (100%) create mode 100644 src/lib/nall/datasource.hpp rename src/{ => lib}/reader/filereader.cpp (65%) rename src/{ => lib}/reader/filereader.hpp (73%) rename src/{ => lib}/reader/gzreader.cpp (100%) rename src/{ => lib}/reader/gzreader.hpp (84%) rename src/{ => lib}/reader/jmareader.cpp (97%) rename src/{ => lib}/reader/jmareader.hpp (84%) rename src/{ => lib}/reader/reader.cpp (100%) rename src/{ => lib}/reader/reader.hpp (100%) rename src/{ => lib}/reader/zipreader.cpp (100%) rename src/{ => lib}/reader/zipreader.hpp (87%) delete mode 100644 src/lib/tool/opgen.cpp delete mode 100644 src/lib/tool/opgen_fnptr.cpp delete mode 100644 src/lib/tool/opgen_switch.cpp rename src/{reader => lib}/zlib/adler32.c (100%) rename src/{reader => lib}/zlib/compress.c (100%) rename src/{reader => lib}/zlib/crc32.c (100%) rename src/{reader => lib}/zlib/crc32.h (100%) create mode 100644 src/lib/zlib/crypt.h rename src/{reader => lib}/zlib/deflate.c (100%) rename src/{reader => lib}/zlib/deflate.h (100%) rename src/{reader => lib}/zlib/gzio.c (100%) rename src/{reader => lib}/zlib/inffast.c (100%) rename src/{reader => lib}/zlib/inffast.h (100%) rename src/{reader => lib}/zlib/inffixed.h (100%) rename src/{reader => lib}/zlib/inflate.c (100%) rename src/{reader => lib}/zlib/inflate.h (100%) rename src/{reader => lib}/zlib/inftrees.c (100%) rename src/{reader => lib}/zlib/inftrees.h (100%) rename src/{reader => lib}/zlib/ioapi.c (100%) create mode 100644 src/lib/zlib/ioapi.h rename src/{reader => lib}/zlib/trees.c (100%) rename src/{reader => lib}/zlib/trees.h (100%) rename src/{reader => lib}/zlib/unzip.c (100%) rename src/{reader => lib}/zlib/unzip.h (100%) rename src/{reader => lib}/zlib/zconf.h (100%) rename src/{reader => lib}/zlib/zip.c (100%) rename src/{reader => lib}/zlib/zip.h (100%) rename src/{reader => lib}/zlib/zlib.h (100%) rename src/{reader => lib}/zlib/zutil.c (100%) rename src/{reader => lib}/zlib/zutil.h (100%) create mode 100644 src/smp/core/bpp.sh create mode 100644 src/smp/core/core.cpp rename src/smp/{ssmp => }/core/core.hpp (56%) rename src/smp/{dsmp.cpp => core/disasm/disasm.cpp} (96%) create mode 100644 src/smp/core/disasm/disasm.hpp create mode 100644 src/smp/core/memory.hpp rename src/smp/{ssmp/core/opfn.cpp => core/opcode_algorithms.cpp} (69%) create mode 100644 src/smp/core/opcode_functions.bpp create mode 100644 src/smp/core/opcode_functions.cpp create mode 100644 src/smp/core/opcode_headers.bpp create mode 100644 src/smp/core/opcode_headers.hpp create mode 100644 src/smp/core/opcode_list.bpp create mode 100644 src/smp/core/opcode_misc.bpp create mode 100644 src/smp/core/opcode_move.bpp create mode 100644 src/smp/core/opcode_pc.bpp create mode 100644 src/smp/core/opcode_read.bpp create mode 100644 src/smp/core/opcode_rmw.bpp create mode 100644 src/smp/core/opcode_table.cpp rename src/smp/{smpregs.hpp => core/registers.hpp} (100%) delete mode 100644 src/smp/iplrom.hpp delete mode 100644 src/smp/ssmp/core/cc.sh delete mode 100644 src/smp/ssmp/core/clean.sh delete mode 100644 src/smp/ssmp/core/core.cpp delete mode 100644 src/smp/ssmp/core/op_misc.b delete mode 100644 src/smp/ssmp/core/op_misc.cpp delete mode 100644 src/smp/ssmp/core/op_mov.b delete mode 100644 src/smp/ssmp/core/op_mov.cpp delete mode 100644 src/smp/ssmp/core/op_pc.b delete mode 100644 src/smp/ssmp/core/op_pc.cpp delete mode 100644 src/smp/ssmp/core/op_read.b delete mode 100644 src/smp/ssmp/core/op_read.cpp delete mode 100644 src/smp/ssmp/core/op_rmw.b delete mode 100644 src/smp/ssmp/core/op_rmw.cpp delete mode 100644 src/smp/ssmp/core/ssmpgen.cpp delete mode 100644 src/snes/audio/audio.cpp delete mode 100644 src/snes/audio/audio.hpp delete mode 100644 src/snes/interface/interface.hpp delete mode 100644 src/snes/snes.hpp create mode 100644 src/system/audio/audio.cpp create mode 100644 src/system/audio/audio.hpp create mode 100644 src/system/config/config.cpp create mode 100644 src/system/config/config.hpp rename src/{snes => system}/input/input.cpp (79%) rename src/{snes => system}/input/input.hpp (99%) create mode 100644 src/system/interface/interface.hpp rename src/{snes => system}/scheduler/scheduler.cpp (80%) rename src/{snes => system}/scheduler/scheduler.hpp (100%) rename src/{snes/snes.cpp => system/system.cpp} (56%) create mode 100644 src/system/system.hpp rename src/{snes => system}/tracer/tracer.cpp (96%) rename src/{snes => system}/tracer/tracer.hpp (100%) rename src/{snes => system}/video/video.cpp (77%) rename src/{snes => system}/video/video.hpp (94%) create mode 100644 src/ui_qt/cartridge/cartridge.cpp create mode 100644 src/ui_qt/cartridge/cartridge.hpp diff --git a/src/Makefile b/src/Makefile index bf24d42d..43cbce47 100644 --- a/src/Makefile +++ b/src/Makefile @@ -93,9 +93,9 @@ link += $(if $(findstring input.rawinput,$(ruby)),$(call mklib,xinput) $(call mk #################### objects = libco ruby libfilter string \ - reader cart cheat \ - memory smemory cpu cpucore scpu smp ssmp sdsp ppu bppu snes \ - sa1 bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010 + reader cartridge cheat \ + memory smemory cpu cpucore scpu smp smpcore ssmp sdsp ppu bppu system \ + sgb sa1 bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010 ifeq ($(enable_gzip),true) objects += adler32 compress crc32 deflate gzio inffast inflate inftrees ioapi trees unzip zip zutil @@ -107,6 +107,13 @@ ifeq ($(enable_jma),true) flags += $(call mkdef,JMA_SUPPORT) endif +sgbflags = +ifeq ($(sgb),gambatte) + sgbflags += $(call mkdef,SGB_GAMBATTE) + link += $(call mklibpath,lib/libgambatte) + link += $(call mklib,gambatte) +endif + ###################### ### implicit rules ### ###################### @@ -139,14 +146,14 @@ obj/libco.$(obj): lib/libco/libco.c lib/libco/* $(c) $(libcoflags) $(rule) obj/libfilter.$(obj): lib/libfilter/libfilter.cpp lib/libfilter/* obj/string.$(obj): lib/nall/string.cpp lib/nall/* +obj/reader.$(obj): lib/reader/reader.cpp lib/reader/* ################# ### utilities ### ################# -obj/reader.$(obj): reader/reader.cpp reader/* -obj/cart.$(obj) : cart/cart.cpp cart/* -obj/cheat.$(obj) : cheat/cheat.cpp cheat/* +obj/cartridge.$(obj): cartridge/cartridge.cpp cartridge/* +obj/cheat.$(obj) : cheat/cheat.cpp cheat/* ############## ### memory ### @@ -167,8 +174,9 @@ obj/scpu.$(obj) : cpu/scpu/scpu.cpp cpu/scpu/* cpu/scpu/dma/* cpu/scpu/memory/ ### smp ### ########### -obj/smp.$(obj) : smp/smp.cpp smp/* -obj/ssmp.$(obj): smp/ssmp/ssmp.cpp smp/ssmp/* smp/ssmp/core/* smp/ssmp/memory/* smp/ssmp/timing/* +obj/smp.$(obj) : smp/smp.cpp smp/* +obj/smpcore.$(obj): smp/core/core.cpp smp/core/* smp/core/disasm/* +obj/ssmp.$(obj) : smp/ssmp/ssmp.cpp smp/ssmp/* smp/ssmp/memory/* smp/ssmp/timing/* ########### ### dsp ### @@ -184,16 +192,18 @@ obj/sdsp.$(obj): dsp/sdsp/sdsp.cpp dsp/sdsp/* obj/ppu.$(obj) : ppu/ppu.cpp ppu/* obj/bppu.$(obj): ppu/bppu/bppu.cpp ppu/bppu/* -############ -### snes ### -############ +############## +### system ### +############## -obj/snes.$(obj): snes/snes.cpp snes/* snes/scheduler/* snes/video/* snes/audio/* snes/input/* +obj/system.$(obj): system/system.cpp system/* system/scheduler/* system/video/* system/audio/* system/input/* ##################### ### special chips ### ##################### +obj/sgb.$(obj): chip/sgb/sgb.cpp chip/sgb/* chip/sgb/interface/* + $(call compile,$(sgbflags)) obj/sa1.$(obj) : chip/sa1/sa1.cpp chip/sa1/* chip/sa1/bus/* chip/sa1/dma/* chip/sa1/memory/* chip/sa1/mmio/* obj/bsx.$(obj) : chip/bsx/bsx.cpp chip/bsx/* obj/srtc.$(obj) : chip/srtc/srtc.cpp chip/srtc/* @@ -211,32 +221,32 @@ obj/st010.$(obj) : chip/st010/st010.cpp chip/st010/* ### zlib ### ############ -obj/adler32.$(obj) : reader/zlib/adler32.c reader/zlib/* -obj/compress.$(obj): reader/zlib/compress.c reader/zlib/* -obj/crc32.$(obj) : reader/zlib/crc32.c reader/zlib/* -obj/deflate.$(obj) : reader/zlib/deflate.c reader/zlib/* -obj/gzio.$(obj) : reader/zlib/gzio.c reader/zlib/* -obj/inffast.$(obj) : reader/zlib/inffast.c reader/zlib/* -obj/inflate.$(obj) : reader/zlib/inflate.c reader/zlib/* -obj/inftrees.$(obj): reader/zlib/inftrees.c reader/zlib/* -obj/ioapi.$(obj) : reader/zlib/ioapi.c reader/zlib/* -obj/trees.$(obj) : reader/zlib/trees.c reader/zlib/* -obj/unzip.$(obj) : reader/zlib/unzip.c reader/zlib/* -obj/zip.$(obj) : reader/zlib/zip.c reader/zlib/* -obj/zutil.$(obj) : reader/zlib/zutil.c reader/zlib/* +obj/adler32.$(obj) : lib/zlib/adler32.c lib/zlib/* +obj/compress.$(obj): lib/zlib/compress.c lib/zlib/* +obj/crc32.$(obj) : lib/zlib/crc32.c lib/zlib/* +obj/deflate.$(obj) : lib/zlib/deflate.c lib/zlib/* +obj/gzio.$(obj) : lib/zlib/gzio.c lib/zlib/* +obj/inffast.$(obj) : lib/zlib/inffast.c lib/zlib/* +obj/inflate.$(obj) : lib/zlib/inflate.c lib/zlib/* +obj/inftrees.$(obj): lib/zlib/inftrees.c lib/zlib/* +obj/ioapi.$(obj) : lib/zlib/ioapi.c lib/zlib/* +obj/trees.$(obj) : lib/zlib/trees.c lib/zlib/* +obj/unzip.$(obj) : lib/zlib/unzip.c lib/zlib/* +obj/zip.$(obj) : lib/zlib/zip.c lib/zlib/* +obj/zutil.$(obj) : lib/zlib/zutil.c lib/zlib/* -########### -### jma ### -########### +############## +### libjma ### +############## -obj/jma.$(obj) : reader/jma/jma.cpp reader/jma/* -obj/jcrc32.$(obj) : reader/jma/jcrc32.cpp reader/jma/* -obj/lzmadec.$(obj): reader/jma/lzmadec.cpp reader/jma/* -obj/7zlzma.$(obj) : reader/jma/7zlzma.cpp reader/jma/* -obj/iiostrm.$(obj): reader/jma/iiostrm.cpp reader/jma/* -obj/inbyte.$(obj) : reader/jma/inbyte.cpp reader/jma/* -obj/lzma.$(obj) : reader/jma/lzma.cpp reader/jma/* -obj/winout.$(obj) : reader/jma/winout.cpp reader/jma/* +obj/jma.$(obj) : lib/libjma/jma.cpp lib/libjma/* +obj/jcrc32.$(obj) : lib/libjma/jcrc32.cpp lib/libjma/* +obj/lzmadec.$(obj): lib/libjma/lzmadec.cpp lib/libjma/* +obj/7zlzma.$(obj) : lib/libjma/7zlzma.cpp lib/libjma/* +obj/iiostrm.$(obj): lib/libjma/iiostrm.cpp lib/libjma/* +obj/inbyte.$(obj) : lib/libjma/inbyte.cpp lib/libjma/* +obj/lzma.$(obj) : lib/libjma/lzma.cpp lib/libjma/* +obj/winout.$(obj) : lib/libjma/winout.cpp lib/libjma/* ############### ### targets ### diff --git a/src/base.hpp b/src/base.hpp index 3ba7f9be..9d4a8ba8 100644 --- a/src/base.hpp +++ b/src/base.hpp @@ -1,4 +1,4 @@ -#define BSNES_VERSION "0.045" +#define BSNES_VERSION "0.045.09" #define BSNES_TITLE "bsnes v" BSNES_VERSION #define BUSCORE sBus diff --git a/src/cart/cart.cpp b/src/cart/cart.cpp deleted file mode 100644 index 60ea8328..00000000 --- a/src/cart/cart.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include <../base.hpp> -#include <../chip/chip.hpp> -#include <../reader/reader.hpp> -#define CART_CPP - -#include -#include - -#include "cart.hpp" -#include "cart_file.cpp" -#include "cart_header.cpp" -#include "cart_loader.cpp" - -namespace memory { - MappedRAM cartrom, cartram, cartrtc; - MappedRAM bscram; - MappedRAM stArom, stAram; - MappedRAM stBrom, stBram; -}; - -Cartridge cartridge; - -void Cartridge::load_begin(Mode cartridge_mode) { - cart.rom = cart.ram = cart.rtc = 0; - bs.ram = 0; - stA.rom = stA.ram = 0; - stB.rom = stB.ram = 0; - - cart.rom_size = cart.ram_size = cart.rtc_size = 0; - bs.ram_size = 0; - stA.rom_size = stA.ram_size = 0; - stB.rom_size = stB.ram_size = 0; - - set(loaded, false); - set(bsx_flash_loaded, false); - set(patched, false); - set(mode, cartridge_mode); -} - -void Cartridge::load_end() { - memory::cartrom.map(cart.rom, cart.rom_size); - memory::cartram.map(cart.ram, cart.ram_size); - memory::cartrtc.map(cart.rtc, cart.rtc_size); - memory::bscram.map(bs.ram, bs.ram_size); - memory::stArom.map(stA.rom, stA.rom_size); - memory::stAram.map(stA.ram, stA.ram_size); - memory::stBrom.map(stB.rom, stB.rom_size); - memory::stBram.map(stB.ram, stB.ram_size); - - memory::cartrom.write_protect(true); - memory::cartram.write_protect(false); - memory::bscram.write_protect(true); - memory::stArom.write_protect(true); - memory::stAram.write_protect(false); - memory::stBrom.write_protect(true); - memory::stBram.write_protect(false); - - string cheat_file = get_filename(cart.filename, "cht", snes.config.path.cheat); - if(file::exists(cheat_file)) { - cheat.clear(); - cheat.load(cheat_file); - } - - bus.load_cart(); - set(loaded, true); -} - -void Cartridge::unload() { - if(loaded() == false) return; - bus.unload_cart(); - - switch(mode()) { - case ModeNormal: unload_normal(); break; - case ModeBsxSlotted: unload_bsx_slotted(); break; - case ModeBsx: unload_bsx(); break; - case ModeSufamiTurbo: unload_sufami_turbo(); break; - } - - if(cart.rom) { delete[] cart.rom; cart.rom = 0; } - if(cart.ram) { delete[] cart.ram; cart.ram = 0; } - if(cart.rtc) { delete[] cart.rtc; cart.rtc = 0; } - if(bs.ram) { delete[] bs.ram; bs.ram = 0; } - if(stA.rom) { delete[] stA.rom; stA.rom = 0; } - if(stA.ram) { delete[] stA.ram; stA.ram = 0; } - if(stB.rom) { delete[] stB.rom; stB.rom = 0; } - if(stB.ram) { delete[] stB.ram; stB.ram = 0; } - - string cheat_file = get_filename(cart.filename, "cht", snes.config.path.cheat); - if(cheat.count() > 0 || file::exists(cheat_file)) { - cheat.save(cheat_file); - cheat.clear(); - } - - set(loaded, false); -} - -Cartridge::Cartridge() { - set(loaded, false); -} - -Cartridge::~Cartridge() { - if(loaded() == true) unload(); -} - -void Cartridge::set_cartinfo(const Cartridge::cartinfo_t &source) { - set(region, source.region); - set(mapper, source.mapper); - set(dsp1_mapper, source.dsp1_mapper); - - set(has_bsx_slot, source.bsx_slot); - set(has_superfx, source.superfx); - set(has_sa1, source.sa1); - set(has_srtc, source.srtc); - set(has_sdd1, source.sdd1); - set(has_spc7110, source.spc7110); - set(has_spc7110rtc, source.spc7110rtc); - set(has_cx4, source.cx4); - set(has_dsp1, source.dsp1); - set(has_dsp2, source.dsp2); - set(has_dsp3, source.dsp3); - set(has_dsp4, source.dsp4); - set(has_obc1, source.obc1); - set(has_st010, source.st010); - set(has_st011, source.st011); - set(has_st018, source.st018); -} - -//========== -//cartinfo_t -//========== - -void Cartridge::cartinfo_t::reset() { - type = TypeUnknown; - mapper = LoROM; - dsp1_mapper = DSP1Unmapped; - region = NTSC; - - rom_size = 0; - ram_size = 0; - - bsx_slot = false; - superfx = false; - sa1 = false; - srtc = false; - sdd1 = false; - spc7110 = false; - spc7110rtc = false; - cx4 = false; - dsp1 = false; - dsp2 = false; - dsp3 = false; - dsp4 = false; - obc1 = false; - st010 = false; - st011 = false; - st018 = false; -} - -Cartridge::cartinfo_t::cartinfo_t() { - reset(); -} - -//======= -//utility -//======= - -//ensure file path is absolute (eg resolve relative paths) -string Cartridge::filepath(const char *filename, const char *pathname) { - //if no pathname, return filename as-is - string file(filename); - file.replace("\\", "/"); - - string path = (!pathname || !*pathname) ? (const char*)snes.config.path.current : pathname; - //ensure path ends with trailing '/' - path.replace("\\", "/"); - if(!strend(path, "/")) path.append("/"); - - //replace relative path with absolute path - if(strbegin(path, "./")) { - ltrim(path, "./"); - path = string() << snes.config.path.base << path; - } - - //remove folder part of filename - lstring part; - part.split("/", file); - return path << part[part.size() - 1]; -} - -//remove directory information and file extension ("/foo/bar.ext" -> "bar") -string Cartridge::basename(const char *filename) { - string name(filename); - - //remove extension - for(signed i = strlen(name) - 1; i >= 0; i--) { - if(name[i] == '.') { - name[i] = 0; - break; - } - } - - //remove directory information - for(signed i = strlen(name) - 1; i >= 0; i--) { - if(name[i] == '/' || name[i] == '\\') { - i++; - char *output = name(); - while(true) { - *output++ = name[i]; - if(!name[i]) break; - i++; - } - break; - } - } - - return name; -} - -//remove filename and return path only ("/foo/bar.ext" -> "/foo/bar/") -string Cartridge::basepath(const char *filename) { - string path(filename); - path.replace("\\", "/"); - - //remove filename - for(signed i = strlen(path) - 1; i >= 0; i--) { - if(path[i] == '/') { - path[i] = 0; - break; - } - } - - if(!strend(path, "/")) path.append("/"); - return path; -} diff --git a/src/cart/cart.hpp b/src/cart/cart.hpp deleted file mode 100644 index bb3cccaa..00000000 --- a/src/cart/cart.hpp +++ /dev/null @@ -1,179 +0,0 @@ -class Cartridge : public property { -public: - enum Mode { - ModeNormal, - ModeBsxSlotted, - ModeBsx, - ModeSufamiTurbo, - }; - - enum Type { - TypeNormal, - TypeBsxSlotted, - TypeBsxBios, - TypeBsx, - TypeSufamiTurboBios, - TypeSufamiTurbo, - TypeUnknown, - }; - - enum Region { - NTSC, - PAL, - }; - - enum MemoryMapper { - LoROM, - HiROM, - ExLoROM, - ExHiROM, - SA1ROM, - SPC7110ROM, - BSCLoROM, - BSCHiROM, - BSXROM, - STROM, - }; - - enum DSP1MemoryMapper { - DSP1Unmapped, - DSP1LoROM1MB, - DSP1LoROM2MB, - DSP1HiROM, - }; - - //properties can be read via operator(), eg "if(cartridge.loaded() == true)"; - //warning: if loaded() == false, no other property is considered valid! - - property_t loaded; //is a base cartridge inserted? - property_t bsx_flash_loaded; //is a BS-X flash cart connected? - property_t patched; //has a UPS patch been applied? - property_t name; //display name (filename sans path and extension) - - property_t mode; - property_t region; - property_t mapper; - property_t dsp1_mapper; - - property_t has_bsx_slot; - property_t has_superfx; - property_t has_sa1; - property_t has_srtc; - property_t has_sdd1; - property_t has_spc7110, has_spc7110rtc; - property_t has_cx4; - property_t has_dsp1, has_dsp2, has_dsp3, has_dsp4; - property_t has_obc1; - property_t has_st010, has_st011, has_st018; - - //main interface - bool load_normal (const char *base); - bool load_bsx_slotted (const char *base, const char *slot = ""); - bool load_bsx (const char *base, const char *slot = ""); - bool load_sufami_turbo(const char *base, const char *slotA = "", const char *slotB = ""); - void unload(); - - //utility functions - static string filepath(const char *filename, const char *pathname); //"./bar.ext" -> "/foo/bar.ext" - static string basename(const char *filename); //"/foo/bar.ext" -> "bar" - static string basepath(const char *filename); //"/foo/bar.ext" -> "/foo/bar/" - //this function will load 'filename', decompress it if needed, and determine what type of - //image file 'filename' refers to (eg normal cart, BS-X flash cart, Sufami Turbo cart, etc.) - //warning: this operation is very expensive, use sparingly! - Type detect_image_type(const char *filename) const; - - Cartridge(); - ~Cartridge(); - -private: - void load_begin(Mode); - void load_end(); - void unload_normal(); - void unload_bsx_slotted(); - void unload_bsx(); - void unload_sufami_turbo(); - - struct cartinfo_t { - Type type; - Region region; - MemoryMapper mapper; - DSP1MemoryMapper dsp1_mapper; - unsigned rom_size, ram_size; - - bool bsx_slot; - bool superfx; - bool sa1; - bool srtc; - bool sdd1; - bool spc7110, spc7110rtc; - bool cx4; - bool dsp1, dsp2, dsp3, dsp4; - bool obc1; - bool st010, st011, st018; - - void reset(); - cartinfo_t(); - }; - - enum HeaderField { - CartName = 0x00, - Mapper = 0x15, - RomType = 0x16, - RomSize = 0x17, - RamSize = 0x18, - CartRegion = 0x19, - Company = 0x1a, - Version = 0x1b, - Complement = 0x1c, //inverse checksum - Checksum = 0x1e, - ResetVector = 0x3c, - }; - - void read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const; - unsigned find_header(const uint8_t *data, unsigned size) const; - unsigned score_header(const uint8_t *data, unsigned size, unsigned addr) const; - void set_cartinfo(const cartinfo_t&); - - bool load_image(const char *filename, uint8_t *&data, unsigned &size, bool &patched) const; - bool load_ram (const char *filename, uint8_t *&data, unsigned size, uint8_t init_value) const; - - enum CompressionMode { - CompressionNone, //always load without compression - CompressionInspect, //use file header inspection - CompressionAuto, //use file extension or file header inspection (configured by user) - }; - - bool load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression = CompressionNone) const; - bool save_file(const char *fn, uint8 *data, unsigned size) const; - bool apply_patch(const uint8_t *pdata, unsigned psize, uint8_t *&data, unsigned &size) const; - - string modify_extension(const char *filename, const char *extension) const; - string get_filename(const char *source, const char *extension, const char *path) const; - - struct { - string filename; - uint8_t *rom, *ram, *rtc; - unsigned rom_size, ram_size, rtc_size; - } cart; - - struct { - string filename; - uint8_t *ram; - unsigned ram_size; - } bs; - - struct { - string filename; - uint8_t *rom, *ram; - unsigned rom_size, ram_size; - } stA, stB; -}; - -namespace memory { - extern MappedRAM cartrom, cartram, cartrtc; - extern MappedRAM bscram; - extern MappedRAM stArom, stAram; - extern MappedRAM stBrom, stBram; -}; - -extern Cartridge cartridge; diff --git a/src/cart/cart_file.cpp b/src/cart/cart_file.cpp deleted file mode 100644 index 14102299..00000000 --- a/src/cart/cart_file.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#ifdef CART_CPP - -#include "../reader/filereader.hpp" - -#if defined(GZIP_SUPPORT) - #include "../reader/gzreader.hpp" - #include "../reader/zipreader.hpp" -#endif - -#if defined(JMA_SUPPORT) - #include "../reader/jmareader.hpp" -#endif - -string Cartridge::modify_extension(const char *filename_, const char *extension) const { - string filename = filename_; - int i; - for(i = strlen(filename); i >= 0; i--) { - if(filename[i] == '.') break; - if(filename[i] == '/') break; - if(filename[i] == '\\') break; - } - if(i > 0 && filename[i] == '.') filename[i] = 0; - return filename << "." << extension; -} - -string Cartridge::get_filename(const char *source, const char *extension, const char *path) const { - return filepath(modify_extension(source, extension), path); -} - -bool Cartridge::load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression) const { - if(file::exists(fn) == false) return false; - - Reader::Type filetype = Reader::Normal; - if(compression == CompressionInspect) filetype = Reader::detect(fn, true); - if(compression == CompressionAuto) filetype = Reader::detect(fn, snes.config.file.autodetect_type); - - switch(filetype) { default: - case Reader::Normal: { - FileReader ff(fn); - if(!ff.ready()) return false; - size = ff.size(); - data = ff.read(); - } break; - - #ifdef GZIP_SUPPORT - case Reader::GZIP: { - GZReader gf(fn); - if(!gf.ready()) return false; - size = gf.size(); - data = gf.read(); - } break; - - case Reader::ZIP: { - ZipReader zf(fn); - if(!zf.ready()) return false; - size = zf.size(); - data = zf.read(); - } break; - #endif - - #ifdef JMA_SUPPORT - case Reader::JMA: { - try { - JMAReader jf(fn); - size = jf.size(); - data = jf.read(); - } catch(JMA::jma_errors jma_error) { - return false; - } - } break; - #endif - } - - return true; -} - -bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t *&data, unsigned &size) const { - uint8_t *outdata = 0; - unsigned outsize; - ups patcher; - ups::result result = patcher.apply(pdata, psize, data, size, outdata, outsize); - - bool apply = false; - if(result == ups::ok) apply = true; - if(snes.config.file.bypass_patch_crc32 == true) { - if(result == ups::input_crc32_invalid) apply = true; - if(result == ups::output_crc32_invalid) apply = true; - } - - //if patch application was successful, replace old data, size with new data, size - if(apply == true) { - delete[] data; - data = new uint8_t[size = outsize]; - memcpy(data, outdata, outsize); - } - - if(outdata) delete[] outdata; - return apply; -} - -bool Cartridge::save_file(const char *fn, uint8 *data, unsigned size) const { - file fp; - if(!fp.open(fn, file::mode_write)) return false; - fp.write(data, size); - fp.close(); - return true; -} - -#endif diff --git a/src/cart/cart_loader.cpp b/src/cart/cart_loader.cpp deleted file mode 100644 index bd4d5ced..00000000 --- a/src/cart/cart_loader.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#ifdef CART_CPP - -//================ -//Normal cartridge -//================ - -bool Cartridge::load_normal(const char *base) { - uint8_t *data; - unsigned size; - bool patch_applied; - cart.filename = base; - - load_begin(ModeNormal); - if(load_image(base, data, size, patch_applied) == false) return false; - - snes.config.path.current = basepath(cart.filename); - if(patch_applied) set(patched, true); - - cartinfo_t cartinfo; - read_header(cartinfo, cart.rom = data, cart.rom_size = size); - set_cartinfo(cartinfo); - - if(cartinfo.ram_size > 0) { - load_ram(get_filename(base, "srm", snes.config.path.save), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff); - } - - if(cartinfo.srtc || cartinfo.spc7110rtc) { - load_ram(get_filename(base, "rtc", snes.config.path.save), cart.rtc, cart.rtc_size = 20, 0x00); - } - - load_end(); - set(name, basename(base)); - return true; -} - -void Cartridge::unload_normal() { - if(cart.ram) save_file(get_filename(cart.filename, "srm", snes.config.path.save), cart.ram, cart.ram_size); - if(cart.rtc) save_file(get_filename(cart.filename, "rtc", snes.config.path.save), cart.rtc, cart.rtc_size); -} - -//====================== -//BS-X slotted cartridge -//====================== - -bool Cartridge::load_bsx_slotted(const char *base, const char *slot) { - uint8_t *data; - unsigned size; - bool patch_applied; - cart.filename = base; - bs.filename = slot; - - load_begin(ModeBsxSlotted); - if(load_image(base, data, size, patch_applied) == false) return false; - - snes.config.path.current = basepath(cart.filename); - if(patch_applied) set(patched, true); - - cartinfo_t cartinfo; - read_header(cartinfo, cart.rom = data, cart.rom_size = size); - set_cartinfo(cartinfo); - - if(load_image(slot, data, size, patch_applied) == true) { - set(bsx_flash_loaded, true); - if(patch_applied) set(patched, true); - bs.ram = data; - bs.ram_size = size; - } - - if(cartinfo.ram_size > 0) { - load_ram(get_filename(base, "srm", snes.config.path.save), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff); - } - - load_end(); - string filename = basename(base); - if(*slot) filename << " + " << basename(slot); - set(name, filename); - return true; -} - -void Cartridge::unload_bsx_slotted() { - if(cart.ram) save_file(get_filename(cart.filename, "srm", snes.config.path.save), cart.ram, cart.ram_size); -} - -//==================== -//BS-X flash cartridge -//==================== - -bool Cartridge::load_bsx(const char *base, const char *slot) { - uint8_t *data; - unsigned size; - bool patch_applied; - cart.filename = base; - bs.filename = slot; - - load_begin(ModeBsx); - if(load_image(base, data, size, patch_applied) == false) return false; - - snes.config.path.current = basepath(cart.filename); - if(patch_applied) set(patched, true); - - cartinfo_t cartinfo; - read_header(cartinfo, cart.rom = data, cart.rom_size = size); - set_cartinfo(cartinfo); - - cart.ram = 0; - cart.ram_size = 0; - - memset(bsxcart.sram.handle (), 0x00, bsxcart.sram.size ()); - memset(bsxcart.psram.handle(), 0x00, bsxcart.psram.size()); - - if(load_file(get_filename(base, "srm", snes.config.path.save), data, size, CompressionNone) == true) { - memcpy(bsxcart.sram.handle (), data, min(bsxcart.sram.size (), size)); - delete[] data; - } - - if(load_file(get_filename(base, "psr", snes.config.path.save), data, size, CompressionNone) == true) { - memcpy(bsxcart.psram.handle(), data, min(bsxcart.psram.size(), size)); - delete[] data; - } - - if(load_image(slot, data, size, patch_applied) == true) { - set(bsx_flash_loaded, true); - if(patch_applied) set(patched, true); - bs.ram = data; - bs.ram_size = size; - } - - load_end(); - set(name, !*slot ? basename(base) : basename(slot)); - return true; -} - -void Cartridge::unload_bsx() { - save_file(get_filename(cart.filename, "srm", snes.config.path.save), bsxcart.sram.handle (), bsxcart.sram.size ()); - save_file(get_filename(cart.filename, "psr", snes.config.path.save), bsxcart.psram.handle(), bsxcart.psram.size()); -} - -//============================ -//Sufami Turbo flash cartridge -//============================ - -bool Cartridge::load_sufami_turbo(const char *base, const char *slotA, const char *slotB) { - uint8_t *data; - unsigned size; - bool patch_applied; - cart.filename = base; - stA.filename = slotA; - stB.filename = slotB; - - load_begin(ModeSufamiTurbo); - if(load_image(base, data, size, patch_applied) == false) return false; - - snes.config.path.current = basepath(cart.filename); - if(patch_applied) set(patched, true); - - cartinfo_t cartinfo; - read_header(cartinfo, cart.rom = data, cart.rom_size = size); - set_cartinfo(cartinfo); - - if(load_image(slotA, data, size, patch_applied) == true) { - if(patch_applied) set(patched, true); - stA.rom = new(zeromemory) uint8_t[stA.rom_size = 0x100000]; - memcpy(stA.rom, data, min(size, stA.rom_size)); - delete[] data; - - load_ram(get_filename(slotA, "srm", snes.config.path.save), stA.ram, stA.ram_size = 0x020000, 0xff); - } - - if(load_image(slotB, data, size, patch_applied) == true) { - if(patch_applied) set(patched, true); - stB.rom = new(zeromemory) uint8_t[stB.rom_size = 0x100000]; - memcpy(stB.rom, data, min(size, stB.rom_size)); - delete[] data; - - load_ram(get_filename(slotB, "srm", snes.config.path.save), stB.ram, stB.ram_size = 0x020000, 0xff); - } - - load_end(); - string filename; - if(!*slotA && !*slotB) filename << basename(base); - else if( *slotA && !*slotB) filename << basename(slotA); - else if(!*slotA && *slotB) filename << basename(slotB); - else filename << basename(slotA) << " + " << basename(slotB); - set(name, filename); - return true; -} - -void Cartridge::unload_sufami_turbo() { - if(stA.ram) save_file(get_filename(stA.filename, "srm", snes.config.path.save), stA.ram, stA.ram_size); - if(stB.ram) save_file(get_filename(stB.filename, "srm", snes.config.path.save), stB.ram, stB.ram_size); -} - -//================= -//utility functions -//================= - -Cartridge::Type Cartridge::detect_image_type(const char *filename) const { - uint8_t *data; - unsigned size; - bool patch_applied; - if(!load_image(filename, data, size, patch_applied)) return TypeUnknown; - - cartinfo_t info; - read_header(info, data, size); - delete[] data; - return info.type; -} - -bool Cartridge::load_image(const char *filename, uint8_t *&data, unsigned &size, bool &patched) const { - if(!filename || !*filename) return false; - if(!load_file(filename, data, size, CompressionAuto)) return false; - - if((size & 0x7fff) == 512) { - //remove 512-byte header - memmove(data, data + 512, size -= 512); - } - - uint8_t *pdata; - unsigned psize; - string path = (snes.config.path.patch == "" ? basepath(filename) : snes.config.path.patch); - if(load_file(get_filename(filename, "ups", path), pdata, psize, CompressionInspect) == true) { - bool result = apply_patch(pdata, psize, data, size); - delete[] pdata; - patched = result; - } else { - patched = false; - } - - return true; -} - -bool Cartridge::load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init) const { - data = new uint8_t[size]; - memset(data, init, size); - - uint8_t *savedata; - unsigned savesize; - if(load_file(filename, savedata, savesize, CompressionNone) == false) return false; - - memcpy(data, savedata, min(size, savesize)); - delete[] savedata; - return true; -} - -#endif diff --git a/src/cartridge/cartridge.cpp b/src/cartridge/cartridge.cpp new file mode 100644 index 00000000..1e9f865e --- /dev/null +++ b/src/cartridge/cartridge.cpp @@ -0,0 +1,140 @@ +#include <../base.hpp> + +#define CART_CPP +namespace SNES { + +#include "cartridge_header.cpp" +#include "cartridge_loader.cpp" + +namespace memory { + MappedRAM cartrom, cartram, cartrtc; + MappedRAM bscram; + MappedRAM stArom, stAram; + MappedRAM stBrom, stBram; + MappedRAM dmgrom, dmgram, dmgrtc; +}; + +Cartridge cartridge; + +void Cartridge::load_begin(Mode cartridge_mode) { + memory::cartrom.map(0, 0); + memory::cartram.map(0, 0); + memory::cartrtc.map(0, 0); + memory::bscram.map (0, 0); + memory::stArom.map (0, 0); + memory::stAram.map (0, 0); + memory::stBrom.map (0, 0); + memory::stBram.map (0, 0); + memory::dmgrom.map (0, 0); + memory::dmgram.map (0, 0); + memory::dmgrtc.map (0, 0); + + set(loaded, false); + set(bsx_flash_loaded, false); + set(patched, false); + set(mode, cartridge_mode); +} + +void Cartridge::load_end() { + memory::cartrom.write_protect(true); + memory::cartram.write_protect(false); + memory::cartrtc.write_protect(false); + memory::bscram.write_protect (true); + memory::stArom.write_protect (true); + memory::stAram.write_protect (false); + memory::stBrom.write_protect (true); + memory::stBram.write_protect (false); + memory::dmgrom.write_protect (true); + memory::dmgram.write_protect (false); + memory::dmgrtc.write_protect (false); + + bus.load_cart(); + set(loaded, true); +} + +void Cartridge::unload() { + if(loaded() == false) return; + bus.unload_cart(); + + memory::cartrom.reset(); + memory::cartram.reset(); + memory::cartrtc.reset(); + memory::bscram.reset(); + memory::stArom.reset(); + memory::stAram.reset(); + memory::stBrom.reset(); + memory::stBram.reset(); + memory::dmgrom.reset(); + memory::dmgram.reset(); + memory::dmgrtc.reset(); + + set(loaded, false); +} + +Cartridge::Cartridge() { + set(loaded, false); +} + +Cartridge::~Cartridge() { + if(loaded() == true) unload(); +} + +void Cartridge::set_cartinfo(const Cartridge::cartinfo_t &source) { + set(region, source.region); + set(mapper, source.mapper); + set(dsp1_mapper, source.dsp1_mapper); + + set(has_bsx_slot, source.bsx_slot); + set(has_superfx, source.superfx); + set(has_sa1, source.sa1); + set(has_srtc, source.srtc); + set(has_sdd1, source.sdd1); + set(has_spc7110, source.spc7110); + set(has_spc7110rtc, source.spc7110rtc); + set(has_cx4, source.cx4); + set(has_dsp1, source.dsp1); + set(has_dsp2, source.dsp2); + set(has_dsp3, source.dsp3); + set(has_dsp4, source.dsp4); + set(has_obc1, source.obc1); + set(has_st010, source.st010); + set(has_st011, source.st011); + set(has_st018, source.st018); +} + +//========== +//cartinfo_t +//========== + +void Cartridge::cartinfo_t::reset() { + type = TypeUnknown; + mapper = LoROM; + dsp1_mapper = DSP1Unmapped; + region = NTSC; + + rom_size = 0; + ram_size = 0; + + bsx_slot = false; + superfx = false; + sa1 = false; + srtc = false; + sdd1 = false; + spc7110 = false; + spc7110rtc = false; + cx4 = false; + dsp1 = false; + dsp2 = false; + dsp3 = false; + dsp4 = false; + obc1 = false; + st010 = false; + st011 = false; + st018 = false; +} + +Cartridge::cartinfo_t::cartinfo_t() { + reset(); +} + +}; diff --git a/src/cartridge/cartridge.hpp b/src/cartridge/cartridge.hpp new file mode 100644 index 00000000..c4b02c1a --- /dev/null +++ b/src/cartridge/cartridge.hpp @@ -0,0 +1,137 @@ +class Cartridge : public property { +public: + enum Mode { + ModeNormal, + ModeBsxSlotted, + ModeBsx, + ModeSufamiTurbo, + ModeSuperGameboy, + }; + + enum Type { + TypeNormal, + TypeBsxSlotted, + TypeBsxBios, + TypeBsx, + TypeSufamiTurboBios, + TypeSufamiTurbo, + TypeSuperGameboyBios, + TypeGameboy, + TypeUnknown, + }; + + enum Region { + NTSC, + PAL, + }; + + enum MemoryMapper { + LoROM, + HiROM, + ExLoROM, + ExHiROM, + SA1ROM, + SPC7110ROM, + BSCLoROM, + BSCHiROM, + BSXROM, + STROM, + }; + + enum DSP1MemoryMapper { + DSP1Unmapped, + DSP1LoROM1MB, + DSP1LoROM2MB, + DSP1HiROM, + }; + + //properties can be read via operator(), eg "if(cartridge.loaded() == true)"; + //warning: if loaded() == false, no other property is considered valid! + + property_t loaded; //is a base cartridge inserted? + property_t bsx_flash_loaded; //is a BS-X flash cart connected? + property_t patched; //has a UPS patch been applied? + + property_t mode; + property_t region; + property_t mapper; + property_t dsp1_mapper; + + property_t has_bsx_slot; + property_t has_superfx; + property_t has_sa1; + property_t has_srtc; + property_t has_sdd1; + property_t has_spc7110, has_spc7110rtc; + property_t has_cx4; + property_t has_dsp1, has_dsp2, has_dsp3, has_dsp4; + property_t has_obc1; + property_t has_st010, has_st011, has_st018; + + //main interface + Type detect_image_type (uint8_t*, unsigned) const; + bool load_normal (uint8_t*, unsigned); + bool load_bsx_slotted (uint8_t*, unsigned, uint8_t*, unsigned); + bool load_bsx (uint8_t*, unsigned, uint8_t*, unsigned); + bool load_sufami_turbo (uint8_t*, unsigned, uint8_t*, unsigned, uint8_t*, unsigned); + bool load_super_gameboy(uint8_t*, unsigned, uint8_t*, unsigned); + void unload(); + + Cartridge(); + ~Cartridge(); + +private: + void load_begin(Mode); + void load_end(); + + struct cartinfo_t { + Type type; + Region region; + MemoryMapper mapper; + DSP1MemoryMapper dsp1_mapper; + unsigned rom_size, ram_size; + + bool bsx_slot; + bool superfx; + bool sa1; + bool srtc; + bool sdd1; + bool spc7110, spc7110rtc; + bool cx4; + bool dsp1, dsp2, dsp3, dsp4; + bool obc1; + bool st010, st011, st018; + + void reset(); + cartinfo_t(); + }; + + enum HeaderField { + CartName = 0x00, + Mapper = 0x15, + RomType = 0x16, + RomSize = 0x17, + RamSize = 0x18, + CartRegion = 0x19, + Company = 0x1a, + Version = 0x1b, + Complement = 0x1c, //inverse checksum + Checksum = 0x1e, + ResetVector = 0x3c, + }; + + void read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const; + unsigned find_header(const uint8_t *data, unsigned size) const; + unsigned score_header(const uint8_t *data, unsigned size, unsigned addr) const; + void set_cartinfo(const cartinfo_t&); +}; + +namespace memory { + extern MappedRAM cartrom, cartram, cartrtc; + extern MappedRAM bscram; + extern MappedRAM stArom, stAram; + extern MappedRAM stBrom, stBram; + extern MappedRAM dmgrom, dmgram, dmgrtc; +}; + +extern Cartridge cartridge; diff --git a/src/cart/cart_header.cpp b/src/cartridge/cartridge_header.cpp similarity index 92% rename from src/cart/cart_header.cpp rename to src/cartridge/cartridge_header.cpp index 03673523..0f5fb9a8 100644 --- a/src/cart/cart_header.cpp +++ b/src/cartridge/cartridge_header.cpp @@ -2,7 +2,29 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const { info.reset(); - unsigned index = find_header(data, size); + + //==================== + //detect Gameboy carts + //==================== + + if(size >= 0x0150) { + if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66) { + if(data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { + info.type = TypeGameboy; + return; + } + } + } + + const unsigned index = find_header(data, size); + const uint8 mapper = data[index + Mapper]; + const uint8 rom_type = data[index + RomType]; + const uint8 rom_size = data[index + RomSize]; + const uint8 company = data[index + Company]; + const uint8 region = data[index + CartRegion] & 0x7f; + + //0, 1, 13 = NTSC; 2 - 12 = PAL + info.region = (region <= 1 || region >= 13) ? NTSC : PAL; //======================= //detect BS-X flash carts @@ -15,7 +37,6 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { info.type = TypeBsx; info.mapper = BSXROM; - info.region = NTSC; //BS-X only released in Japan return; } } @@ -33,20 +54,23 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size info.type = TypeSufamiTurbo; } info.mapper = STROM; - info.region = NTSC; //Sufami Turbo only released in Japan - return; //RAM size handled internally by load_cart_st(); + return; + } + + //========================= + //detect Super Gameboy cart + //========================= + + if(!memcmp(data + index, "Super GAMEBOY", 13)) { + info.type = TypeSuperGameboyBios; + info.mapper = LoROM; + return; } //===================== //detect standard carts //===================== - const uint8 mapper = data[index + Mapper]; - const uint8 rom_type = data[index + RomType]; - const uint8 rom_size = data[index + RomSize]; - const uint8 company = data[index + Company]; - const uint8 region = data[index + CartRegion] & 0x7f; - //detect presence of BS-X flash cartridge connector (reads extended header information) if(data[index - 14] == 'Z') { if(data[index - 11] == 'J') { @@ -169,9 +193,6 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size } else { info.ram_size = 0; } - - //0, 1, 13 = NTSC; 2 - 12 = PAL - info.region = (region <= 1 || region >= 13) ? NTSC : PAL; } unsigned Cartridge::find_header(const uint8_t *data, unsigned size) const { diff --git a/src/cartridge/cartridge_loader.cpp b/src/cartridge/cartridge_loader.cpp new file mode 100644 index 00000000..3a6df01d --- /dev/null +++ b/src/cartridge/cartridge_loader.cpp @@ -0,0 +1,148 @@ +#ifdef CART_CPP + +Cartridge::Type Cartridge::detect_image_type(uint8_t *data, unsigned size) const { + cartinfo_t info; + read_header(info, data, size); + return info.type; +} + +//================ +//Normal cartridge +//================ + +bool Cartridge::load_normal(uint8_t *basedata, unsigned basesize) { + load_begin(ModeNormal); + + memory::cartrom.map(new uint8_t[basesize], basesize); + memcpy(memory::cartrom.handle(), basedata, basesize); + + cartinfo_t cartinfo; + read_header(cartinfo, basedata, basesize); + set_cartinfo(cartinfo); + + if(cartinfo.ram_size) { + memory::cartram.map(new(zeromemory) uint8_t[cartinfo.ram_size], cartinfo.ram_size); + } + + if(cartinfo.srtc || cartinfo.spc7110rtc) { + memory::cartrtc.map(new(zeromemory) uint8_t[20], 20); + } + + load_end(); + return true; +} + +//====================== +//BS-X slotted cartridge +//====================== + +bool Cartridge::load_bsx_slotted(uint8_t *basedata, unsigned basesize, uint8_t *slotdata, unsigned slotsize) { + load_begin(ModeBsxSlotted); + + memory::cartrom.map(new uint8_t[basesize], basesize); + memcpy(memory::cartrom.handle(), basedata, basesize); + + cartinfo_t cartinfo; + read_header(cartinfo, basedata, basesize); + set_cartinfo(cartinfo); + + if(slotdata) { + memory::bscram.map(new uint8_t[slotsize], slotsize); + memcpy(memory::bscram.handle(), slotdata, slotsize); + set(bsx_flash_loaded, true); + } + + if(cartinfo.ram_size) { + memory::cartram.map(new(zeromemory) uint8_t[cartinfo.ram_size], cartinfo.ram_size); + } + + load_end(); + return true; +} + +//==================== +//BS-X flash cartridge +//==================== + +bool Cartridge::load_bsx(uint8_t *basedata, unsigned basesize, uint8_t *slotdata, unsigned slotsize) { + load_begin(ModeBsx); + + memory::cartrom.map(new uint8_t[basesize], basesize); + memcpy(memory::cartrom.handle(), basedata, basesize); + + cartinfo_t cartinfo; + read_header(cartinfo, basedata, basesize); + set_cartinfo(cartinfo); + + if(slotdata) { + memory::bscram.map(new uint8_t[slotsize], slotsize); + memcpy(memory::bscram.handle(), slotdata, slotsize); + set(bsx_flash_loaded, true); + } + + memset(bsxcart.sram.handle (), 0x00, bsxcart.sram.size ()); + memset(bsxcart.psram.handle(), 0x00, bsxcart.psram.size()); + + load_end(); + return true; +} + +//============================ +//Sufami Turbo flash cartridge +//============================ + +bool Cartridge::load_sufami_turbo(uint8_t *basedata, unsigned basesize, uint8_t *slotAdata, unsigned slotAsize, uint8_t *slotBdata, unsigned slotBsize) { + load_begin(ModeSufamiTurbo); + + memory::cartrom.map(new uint8_t[basesize], basesize); + memcpy(memory::cartrom.handle(), basedata, basesize); + + cartinfo_t cartinfo; + read_header(cartinfo, basedata, basesize); + set_cartinfo(cartinfo); + + if(slotAdata) { + memory::stArom.map(new uint8_t[slotAsize], slotAsize); + memory::stAram.map(new uint8_t[0x020000], 0x020000); + memcpy(memory::stArom.handle(), slotAdata, slotAsize); + } + + if(slotBdata) { + memory::stBrom.map(new uint8_t[slotBsize], slotBsize); + memory::stBram.map(new uint8_t[0x020000], 0x020000); + memcpy(memory::stBrom.handle(), slotBdata, slotBsize); + } + + load_end(); + return true; +} + +//======================= +//Super Gameboy cartridge +//======================= + +bool Cartridge::load_super_gameboy(uint8_t *basedata, unsigned basesize, uint8_t *slotdata, unsigned slotsize) { + load_begin(ModeSuperGameboy); + + memory::cartrom.map(new uint8_t[basesize], basesize); + memcpy(memory::cartrom.handle(), basedata, basesize); + + cartinfo_t cartinfo; + read_header(cartinfo, basedata, basesize); + set_cartinfo(cartinfo); + + if(slotdata) { + uint8_t *data = new uint8_t[slotsize]; + memcpy(data, slotdata, slotsize); + memory::dmgrom.map(data, slotsize); + + //TODO: determine proper RAM size via GB ROM header + memory::dmgram.map(new uint8_t[524288], 524288); + memory::dmgrtc.map(new uint8_t[4], 4); + } + + load_end(); + return true; +} + +#endif diff --git a/src/cc.bat b/src/cc.bat index 9e270d97..c052b562 100644 --- a/src/cc.bat +++ b/src/cc.bat @@ -1,3 +1,3 @@ -@mingw32-make platform=win compiler=mingw32-gcc +@mingw32-make platform=win compiler=mingw32-gcc sgb=gambatte ::@mingw32-make platform=win compiler=mingw32-gcc enable_gzip=true enable_jma=true @pause diff --git a/src/cc.sh b/src/cc.sh index 51da6a6b..2f828102 100644 --- a/src/cc.sh +++ b/src/cc.sh @@ -1,2 +1,2 @@ -make platform=x compiler=gcc +make platform=x compiler=gcc sgb=gambatte #make platform=x compiler=gcc enable_gzip=true enable_jma=true diff --git a/src/cheat/cheat.cpp b/src/cheat/cheat.cpp index b6f95e33..ab0fcbf3 100644 --- a/src/cheat/cheat.cpp +++ b/src/cheat/cheat.cpp @@ -1,5 +1,8 @@ #include <../base.hpp> +#define CHEAT_CPP +namespace SNES { + Cheat cheat; Cheat::cheat_t& Cheat::cheat_t::operator=(const Cheat::cheat_t& source) { @@ -390,3 +393,5 @@ string& Cheat::decode_description(string &desc) const { desc.replace("\\n", "\n"); return desc; } + +}; diff --git a/src/chip/bsx/bsx.cpp b/src/chip/bsx/bsx.cpp index 9d4de100..9f92f01f 100644 --- a/src/chip/bsx/bsx.cpp +++ b/src/chip/bsx/bsx.cpp @@ -1,8 +1,10 @@ #include <../base.hpp> -#include <../cart/cart.hpp> -#define BSX_CPP -#include "bsx.hpp" +#define BSX_CPP +namespace SNES { + #include "bsx_base.cpp" #include "bsx_cart.cpp" #include "bsx_flash.cpp" + +}; diff --git a/src/chip/chip.hpp b/src/chip/chip.hpp index 25cbc321..5a648727 100644 --- a/src/chip/chip.hpp +++ b/src/chip/chip.hpp @@ -1,3 +1,9 @@ +struct Chip { + virtual void load() {} + virtual void unload() {} +}; + +#include "sgb/sgb.hpp" #include "sa1/sa1.hpp" #include "bsx/bsx.hpp" #include "srtc/srtc.hpp" diff --git a/src/chip/cx4/cx4.cpp b/src/chip/cx4/cx4.cpp index 11594aa8..df1d8b57 100644 --- a/src/chip/cx4/cx4.cpp +++ b/src/chip/cx4/cx4.cpp @@ -6,9 +6,10 @@ */ #include <../base.hpp> -#define CX4_CPP -#include "cx4.hpp" +#define CX4_CPP +namespace SNES { + #include "cx4data.cpp" #include "cx4fn.cpp" #include "cx4oam.cpp" @@ -195,3 +196,5 @@ void Cx4::reset() { memset(ram, 0, 0x0c00); memset(reg, 0, 0x0100); } + +}; diff --git a/src/chip/dsp1/dsp1.cpp b/src/chip/dsp1/dsp1.cpp index 323c88be..9bfc1661 100644 --- a/src/chip/dsp1/dsp1.cpp +++ b/src/chip/dsp1/dsp1.cpp @@ -1,8 +1,8 @@ #include <../base.hpp> -#include <../cart/cart.hpp> -#define DSP1_CPP -#include "dsp1.hpp" +#define DSP1_CPP +namespace SNES { + #include "dsp1emu.cpp" void DSP1::init() {} @@ -57,3 +57,5 @@ void DSP1::write(unsigned addr, uint8 data) { dsp1.setDr(data); } } + +}; diff --git a/src/chip/dsp2/dsp2.cpp b/src/chip/dsp2/dsp2.cpp index de40c0e3..ee0bc1a3 100644 --- a/src/chip/dsp2/dsp2.cpp +++ b/src/chip/dsp2/dsp2.cpp @@ -1,7 +1,8 @@ #include <../base.hpp> -#define DSP2_CPP -#include "dsp2.hpp" +#define DSP2_CPP +namespace SNES { + #include "dsp2_op.cpp" void DSP2::init() {} @@ -134,3 +135,5 @@ void DSP2::write(unsigned addr, uint8 data) { DSP2::DSP2() {} DSP2::~DSP2() {} + +}; diff --git a/src/chip/dsp3/dsp3.cpp b/src/chip/dsp3/dsp3.cpp index e6c7c7c1..a3e8bdd8 100644 --- a/src/chip/dsp3/dsp3.cpp +++ b/src/chip/dsp3/dsp3.cpp @@ -1,7 +1,8 @@ #include <../base.hpp> -#define DSP3_CPP -#include "dsp3.hpp" +#define DSP3_CPP +namespace SNES { + namespace DSP3i { #define bool8 uint8 #include "dsp3emu.c" @@ -33,3 +34,5 @@ void DSP3::write(unsigned addr, uint8 data) { DSP3i::dsp3_byte = data; DSP3i::DSP3SetByte(); } + +}; diff --git a/src/chip/dsp4/dsp4.cpp b/src/chip/dsp4/dsp4.cpp index aed7caf5..943457cd 100644 --- a/src/chip/dsp4/dsp4.cpp +++ b/src/chip/dsp4/dsp4.cpp @@ -1,7 +1,8 @@ #include <../base.hpp> -#define DSP4_CPP -#include "dsp4.hpp" +#define DSP4_CPP +namespace SNES { + namespace DSP4i { inline uint16 READ_WORD(uint8 *addr) { return (addr[0]) + (addr[1] << 8); @@ -53,3 +54,5 @@ void DSP4::write(unsigned addr, uint8 data) { DSP4i::DSP4SetByte(); } } + +}; diff --git a/src/chip/obc1/obc1.cpp b/src/chip/obc1/obc1.cpp index ec7593bf..f568ef83 100644 --- a/src/chip/obc1/obc1.cpp +++ b/src/chip/obc1/obc1.cpp @@ -1,6 +1,7 @@ #include <../base.hpp> -#include <../cart/cart.hpp> -#include "obc1.hpp" + +#define OBC1_CPP +namespace SNES { void OBC1::init() {} void OBC1::enable() {} @@ -70,3 +71,5 @@ void OBC1::ram_write(unsigned addr, uint8 data) { OBC1::OBC1() {} OBC1::~OBC1() {} + +}; diff --git a/src/chip/sa1/sa1.cpp b/src/chip/sa1/sa1.cpp index 22667763..0dd60830 100644 --- a/src/chip/sa1/sa1.cpp +++ b/src/chip/sa1/sa1.cpp @@ -1,9 +1,8 @@ #include <../base.hpp> -#include <../cart/cart.hpp> -#include <../chip/bsx/bsx.hpp> -#define SA1_CPP -#include "sa1.hpp" +#define SA1_CPP +namespace SNES { + #include "bus/bus.cpp" #include "dma/dma.cpp" #include "memory/memory.cpp" @@ -141,7 +140,7 @@ void SA1::reset() { status.interrupt_pending = false; status.interrupt_vector = 0x0000; - status.scanlines = (snes.region() == SNES::NTSC ? 262 : 312); + status.scanlines = (system.region() == System::NTSC ? 262 : 312); status.vcounter = 0; status.hcounter = 0; @@ -312,3 +311,5 @@ void SA1::reset() { SA1::SA1() { } + +}; diff --git a/src/chip/sdd1/sdd1.cpp b/src/chip/sdd1/sdd1.cpp index 005d2fab..a3b264ca 100644 --- a/src/chip/sdd1/sdd1.cpp +++ b/src/chip/sdd1/sdd1.cpp @@ -1,8 +1,8 @@ #include <../base.hpp> -#include <../cart/cart.hpp> -#define SDD1_CPP -#include "sdd1.hpp" +#define SDD1_CPP +namespace SNES { + #include "sdd1emu.cpp" void SDD1::init() {} @@ -156,3 +156,5 @@ SDD1::SDD1() { SDD1::~SDD1() { delete[] buffer.data; } + +}; diff --git a/src/chip/sgb/interface/gambatte.cpp b/src/chip/sgb/interface/gambatte.cpp new file mode 100644 index 00000000..c5a7ba42 --- /dev/null +++ b/src/chip/sgb/interface/gambatte.cpp @@ -0,0 +1,174 @@ +#include + +namespace SNES { + +class GambatteVideo : public Gambatte::VideoBlitter { +public: + unsigned bufferWidth, bufferHeight; + uint32_t *buffer; + + void setBufferDimensions(unsigned width, unsigned height) { + if(buffer) delete[] buffer; + buffer = new uint32_t[width * height]; + bufferWidth = width; + bufferHeight = height; + } + + const Gambatte::PixelBuffer inBuffer() { + Gambatte::PixelBuffer pb; + pb.pixels = (void*)buffer; + pb.format = Gambatte::PixelBuffer::RGB32; + pb.pitch = bufferWidth; + return pb; + } + + void blit() { + } + + void update(unsigned row) { + uint32_t *source = buffer + row * 160 * 8; + uint8_t *buffer = new(zeromemory) uint8_t[5760]; + + for(unsigned y = row * 8; y < row * 8 + 8; y++) { + for(unsigned x = 0; x < 160; x++) { + unsigned pixel = *source++ / 0x555555; + pixel ^= 3; + + unsigned tile = ((y / 8) * 20) + (x / 8); + unsigned addr = (tile * 16) + ((y & 7) * 2); + + buffer[addr + 0] |= ((pixel & 1) >> 0) << (7 - (x & 7)); + buffer[addr + 1] |= ((pixel & 2) >> 1) << (7 - (x & 7)); + } + } + + memcpy(sgb.gameboy->vram, buffer, 5760); + delete[] buffer; + } + + ~GambatteVideo() { + if(buffer) delete[] buffer; + } +}; + +class GambatteInput : public Gambatte::InputStateGetter { +public: + Gambatte::InputState is; + const Gambatte::InputState& operator()() { + unsigned joypad = sgb.gameboy->joypadid(); + unsigned data = sgb.mmio.joypad[joypad]; + + //TODO: fix SGB detection + joypad = 1; + data = sgb.mmio.joypad[0]; + + is.joypadId = 0x0f - joypad; + is.startButton = !(data & 0x80); + is.selectButton = !(data & 0x40); + is.bButton = !(data & 0x20); + is.aButton = !(data & 0x10); + is.dpadDown = !(data & 0x08); + is.dpadUp = !(data & 0x04); + is.dpadLeft = !(data & 0x02); + is.dpadRight = !(data & 0x01); + return is; + } +}; + +class GambatteMemory : public Gambatte::MemoryInterface { +public: + Gambatte::MemoryBuffer loadRomData() { + Gambatte::MemoryBuffer mb; + mb.data = (void*)memory::dmgrom.handle(); + mb.size = memory::dmgrom.size(); + return mb; + } + + Gambatte::MemoryBuffer loadRamData() { + Gambatte::MemoryBuffer mb; + mb.data = (void*)memory::dmgram.handle(); + mb.size = memory::dmgram.size(); + return mb; + } + + Gambatte::MemoryBuffer loadRtcData() { + Gambatte::MemoryBuffer mb; + mb.data = (void*)memory::dmgrtc.handle(); + mb.size = memory::dmgram.size(); + return mb; + } + + Gambatte::MemoryBuffer saveRamData(unsigned size) { + memory::dmgram.reset(); + memory::dmgram.map(new uint8_t[size], size); + + Gambatte::MemoryBuffer mb; + mb.data = (void*)memory::dmgram.handle(); + mb.size = memory::dmgram.size(); + return mb; + } + + Gambatte::MemoryBuffer saveRtcData() { + memory::dmgrtc.reset(); + memory::dmgrtc.map(new uint8_t[4], 4); + + Gambatte::MemoryBuffer mb; + mb.data = (void*)memory::dmgrtc.handle(); + mb.size = memory::dmgrtc.size(); + return mb; + } + + void joypWrite(bool p15, bool p14) { + sgb.gameboy->write(p15, p14); + } +}; + +class GameboyGambatte : public Gameboy { +public: + Gambatte::GB gambatte; + GambatteInput gambatte_input; + GambatteVideo gambatte_video; + GambatteMemory gambatte_memory; + uint32_t audio_buffer[65536]; + + unsigned run() { + unsigned samples = gambatte.runFor(audio_buffer, 16); + for(unsigned i = 0; i < samples; i++) { + system.audio.cop_sample(audio_buffer[i] >> 0, audio_buffer[i] >> 16); + } + return (samples << 3) + (samples << 1); //1 sample = 10 S-CPU clock cycles + } + + unsigned lyCounter() { + return gambatte.lyCounter(); + } + + void updateVideo(unsigned row) { + gambatte.updateVideo(); + gambatte_video.update(row); + } + + void unload() { + gambatte.save(); + } + + void power() { + Gameboy::power(); + gambatte.load(true); + system.audio.set_cop_frequency(60 * 35112); + } + + void reset() { + Gameboy::reset(); + gambatte.reset(); + system.audio.set_cop_frequency(60 * 35112); + } + + GameboyGambatte() { + gambatte.setVideoBlitter(&gambatte_video); + gambatte.setInputStateGetter(&gambatte_input); + gambatte.setMemoryInterface(&gambatte_memory); + } +}; + +}; diff --git a/src/chip/sgb/interface/interface.hpp b/src/chip/sgb/interface/interface.hpp new file mode 100644 index 00000000..11cb67eb --- /dev/null +++ b/src/chip/sgb/interface/interface.hpp @@ -0,0 +1,98 @@ +namespace SNES { + +static const char command_name[32][64] = { + "PAL01", "PAL23", "PAL03", "PAL12", + "ATTR_BLK", "ATTR_LIN", "ATTR_DIV", "ATTR_CHR", + "SOUND", "SOU_TRN", "PAL_SET", "PAL_TRN", + "ATRC_EN", "TEST_EN", "ICON_EN", "DATA_SND", + "DATA_TRN", "MLT_REG", "JUMP", "CHR_TRN", + "PCT_TRN", "ATTR_TRN", "ATTR_SET", "MASK_EN", + "OBJ_TRN", "19_???", "1A_???", "1B_???", + "1C_???", "1D_???", "1E_ROM", "1F_???", +}; + +class Gameboy { +public: + uint8_t vram[(160 / 8) * (144 / 8) * (64 / 4)]; + + bool pulselock; + bool strobelock; + bool packetlock; + + SuperGameboy::Packet packet; + uint8_t packetoffset; + uint8_t bitdata, bitoffset; + + unsigned joypadid() { + return sgb.mmio.joypadid; + } + + void write(bool p15, bool p14) { + if(p15 == 1 && p14 == 1) { + sgb.mmio.joypadid++; + if(sgb.mmio.r6003 & 0x20) sgb.mmio.joypadid &= 3; + else if(sgb.mmio.r6003 & 0x10) sgb.mmio.joypadid &= 1; + else sgb.mmio.joypadid &= 0; + } + + if(p15 == 0 && p14 == 0) { + //pulse + pulselock = false; + packetoffset = 0; + bitoffset = 0; + strobelock = true; + packetlock = false; + return; + } + + if(pulselock) return; + + if(p15 == 1 && p14 == 1) { + strobelock = false; + return; + } + + if(strobelock) return; + + //p15(1), p14(0) = 0 + //p15(0), p14(1) = 1 + bool bit = (p15 == 0); + strobelock = true; + + if(packetlock) { + if(p15 == 1 && p14 == 0) { + //packet stop bit + printf("%s\n", command_name[packet[0] >> 3]); + sgb.packet.add(packet); + packetlock = false; + pulselock = true; + } + return; + } + + bitdata = (bit << 7) | (bitdata >> 1); + if(++bitoffset < 8) return; + + bitoffset = 0; + packet[packetoffset] = bitdata; + if(++packetoffset < 16) return; + packetlock = true; + } + + virtual unsigned run() { return 64; } + virtual unsigned lyCounter() { return 0; } + virtual void updateVideo(unsigned) {} + virtual void unload() {} + + virtual void power() { + pulselock = true; + memset(&vram, 0, sizeof vram); + } + + virtual void reset() { + pulselock = true; + memset(&vram, 0, sizeof vram); + } +}; + +}; diff --git a/src/chip/sgb/interface/reference.cpp b/src/chip/sgb/interface/reference.cpp new file mode 100644 index 00000000..5f288c99 --- /dev/null +++ b/src/chip/sgb/interface/reference.cpp @@ -0,0 +1,24 @@ +namespace SNES { + +class Gameboy { +public: + unsigned run() { + for(unsigned i = 0; i < memory::sgbvram.size(); i++) { + memory::sgbvram.write(i, rand()); + } + sgb.refresh(); + return 0; + } + + void unload() { + } + + void power() { + } + + void reset() { + } +}; + +}; + diff --git a/src/chip/sgb/sgb.cpp b/src/chip/sgb/sgb.cpp new file mode 100644 index 00000000..577d47c3 --- /dev/null +++ b/src/chip/sgb/sgb.cpp @@ -0,0 +1,147 @@ +#include <../base.hpp> + +#include "interface/interface.hpp" +#if defined(SGB_GAMBATTE) + #include "interface/gambatte.cpp" +#else + #include "interface/reference.cpp" +#endif + +#define SGB_CPP +namespace SNES { + +void SuperGameboy::enter() { + while(true) { + unsigned clocks; + if((mmio.r6003 & 0x80) == 0) { + clocks = 10; + system.audio.cop_sample(0, 0); + } else { + clocks = gameboy->run(); + } + scheduler.addclocks_cop(clocks); + scheduler.sync_copcpu(); + } +} + +uint8_t SuperGameboy::read(unsigned addr) { + addr &= 0xffff; + + //DMG lyCounter + if(addr == 0x6000) { + return gameboy->lyCounter(); + } + + //command ready port + if(addr == 0x6002) { + bool data = packet.size() > 0; + if(data) { + mmio.r7000 = packet[0]; + unsigned size = packet.size() - 1; + for(unsigned i = 0; i < size; i++) packet[i] = packet[i + 1]; + packet.resize(size); + } + return data; + } + + //command port + if((addr & 0xfff0) == 0x7000) { + return mmio.r7000[addr & 15]; + } + + //screen data port + if(addr == 0x7800) { + uint8_t data = gameboy->vram[mmio.r7800++]; + if(mmio.r7800 >= (160 / 8) * (144 / 8) * (64 / 4)) mmio.r7800 = 0; + return data; + } + + return 0x00; +} + +void SuperGameboy::write(unsigned addr, uint8_t data) { + addr &= 0xffff; + + if(addr == 0x6001) { + gameboy->updateVideo(mmio.r7800 / 320); + } + + //control port + //d7 = Gameboy enable + //d5 = four-player enable (1=4-player, 0=see d4) + //d4 = two-player enable (if: d5=0; 1=2-player, 0=1-player) + //d0 = ??? (always 1) + if(addr == 0x6003) { + if(((mmio.r6003 & 0x80) == 0x00) && ((data & 0x80) == 0x80)) { + gameboy->reset(); + command_1e(); + mmio.r7800 = 320 * 16; + } + mmio.r6003 = data; + return; + } + + //joypad ports 1-4 + if((addr & 0x600c) == 0x6004) { + mmio.joypad[addr & 3] = data; + return; + } +} + +void SuperGameboy::init() {} +void SuperGameboy::enable() {} + +void SuperGameboy::power() { + multiplier = (system.region() == System::NTSC ? config.cpu.ntsc_clock_rate : config.cpu.pal_clock_rate); + gameboy->power(); + reset(); +} + +void SuperGameboy::reset() { + gameboy->reset(); + packet.reset(); + counter = 0; + + bus.map(Bus::MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, *this); + bus.map(Bus::MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, *this); + + mmio.r6000 = 0; + mmio.r6003 = 0; + + //$6004-$6007 + mmio.joypadid = 0; + for(unsigned i = 0; i < 4; i++) mmio.joypad[i] = 0xff; + + for(unsigned i = 0; i < 16; i++) mmio.r7000[i] = 0; + mmio.r7800 = 0; +} + +void SuperGameboy::unload() { + gameboy->unload(); +} + +// + +void SuperGameboy::command_1e() { + for(unsigned i = 0; i < 6; i++) { + Packet p; + p[0] = (0x1e << 3) + 1; + p[1] = 0; + for(unsigned n = 2; n < 16; n++) { + p[n] = memory::dmgrom.read(0x0104 + (i * 14) + (n - 2)); + } + packet.add(p); + } +} + +// + +SuperGameboy::SuperGameboy() { + gameboy = new GameboyGambatte; +} + +SuperGameboy::~SuperGameboy() { + delete gameboy; +} + +}; diff --git a/src/chip/sgb/sgb.hpp b/src/chip/sgb/sgb.hpp new file mode 100644 index 00000000..2784291d --- /dev/null +++ b/src/chip/sgb/sgb.hpp @@ -0,0 +1,44 @@ +class Gameboy; + +class SuperGameboy : public Memory { +public: + Gameboy *gameboy; + void enter(); + + struct Packet { + uint8_t data[16]; + uint8_t& operator[](unsigned addr) { return data[addr & 15]; } + }; + vector packet; + + struct MMIO { + unsigned r6000; + uint8_t r6003; + + //$6004-$6007 + uint8_t joypadid; + uint8_t joypad[4]; + + Packet r7000; + unsigned r7800; + } mmio; + + uint8_t read(unsigned addr); + void write(unsigned addr, uint8_t data); + + void init(); + void enable(); + void power(); + void reset(); + void unload(); + + SuperGameboy(); + ~SuperGameboy(); + +protected: + uint64_t multiplier; + uint64_t counter; + void command_1e(); +}; + +extern SuperGameboy sgb; diff --git a/src/chip/spc7110/spc7110.cpp b/src/chip/spc7110/spc7110.cpp index b5c59ae4..9c54a715 100644 --- a/src/chip/spc7110/spc7110.cpp +++ b/src/chip/spc7110/spc7110.cpp @@ -1,8 +1,8 @@ #include <../base.hpp> -#include <../cart/cart.hpp> -#define SPC7110_CPP -#include "spc7110.hpp" +#define SPC7110_CPP +namespace SNES { + #include "decomp.cpp" const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; @@ -670,3 +670,5 @@ void SPC7110::write(unsigned addr, uint8 data) { SPC7110::SPC7110() { } + +}; diff --git a/src/chip/srtc/srtc.cpp b/src/chip/srtc/srtc.cpp index f76ecf4a..c161ac74 100644 --- a/src/chip/srtc/srtc.cpp +++ b/src/chip/srtc/srtc.cpp @@ -1,6 +1,7 @@ #include <../base.hpp> -#include <../cart/cart.hpp> -#include "srtc.hpp" + +#define SRTC_CPP +namespace SNES { const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; @@ -224,3 +225,5 @@ void SRTC::mmio_write(unsigned addr, uint8 data) { SRTC::SRTC() { } + +}; diff --git a/src/chip/st010/st010.cpp b/src/chip/st010/st010.cpp index c0c8f906..84cff71f 100644 --- a/src/chip/st010/st010.cpp +++ b/src/chip/st010/st010.cpp @@ -1,7 +1,8 @@ #include <../base.hpp> -#define ST010_CPP -#include "st010.hpp" +#define ST010_CPP +namespace SNES { + #include "st010_data.hpp" #include "st010_op.cpp" @@ -85,3 +86,5 @@ void ST010::write(unsigned addr, uint8 data) { ram[0x0021] &= ~0x80; } } + +}; diff --git a/src/cpu/core/core.cpp b/src/cpu/core/core.cpp index f2dc4f08..292b4bb4 100644 --- a/src/cpu/core/core.cpp +++ b/src/cpu/core/core.cpp @@ -1,5 +1,8 @@ #include <../base.hpp> +#define CPUCORE_CPP +namespace SNES { + #include "opcode_algorithms.cpp" #include "opcode_functions.cpp" #include "opcode_tables.cpp" @@ -43,3 +46,5 @@ alwaysinline void CPUcore::op_io_cond6(uint16 addr) { CPUcore::CPUcore() { initialize_opcode_table(); } + +}; diff --git a/src/cpu/core/opcode_headers.bpp b/src/cpu/core/opcode_headers.bpp index 82d5f878..f3e277d8 100644 --- a/src/cpu/core/opcode_headers.bpp +++ b/src/cpu/core/opcode_headers.bpp @@ -383,4 +383,6 @@ void op_per_e(); void op_per_n(); @endmacro +// + @include "opcode_list.bpp" diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp index 30a4265b..69aabf0b 100644 --- a/src/cpu/cpu.cpp +++ b/src/cpu/cpu.cpp @@ -1,8 +1,10 @@ #include <../base.hpp> -#define CPU_CPP + +#define CPU_CPP +namespace SNES { void CPU::power() { - cpu_version = snes.config.cpu.version; + cpu_version = config.cpu.version; } void CPU::reset() { @@ -13,3 +15,5 @@ CPU::CPU() { CPU::~CPU() { } + +}; diff --git a/src/cpu/scpu/mmio/mmio.cpp b/src/cpu/scpu/mmio/mmio.cpp index 80becbba..7eb72cac 100644 --- a/src/cpu/scpu/mmio/mmio.cpp +++ b/src/cpu/scpu/mmio/mmio.cpp @@ -42,7 +42,7 @@ void sCPU::mmio_w4016(uint8 data) { status.joypad_strobe_latch = !!(data & 1); if(status.joypad_strobe_latch == 1) { - snes.input.poll(); + system.input.poll(); } } @@ -54,7 +54,7 @@ void sCPU::mmio_w4016(uint8 data) { //realtime or buffered status of joypadN.b uint8 sCPU::mmio_r4016() { uint8 r = regs.mdr & 0xfc; - r |= snes.input.port_read(0) & 3; + r |= system.input.port_read(0) & 3; return r; } @@ -64,7 +64,7 @@ uint8 sCPU::mmio_r4016() { //1-0 = Joypad serial data uint8 sCPU::mmio_r4017() { uint8 r = (regs.mdr & 0xe0) | 0x1c; - r |= snes.input.port_read(1) & 3; + r |= system.input.port_read(1) & 3; return r; } @@ -93,7 +93,7 @@ void sCPU::mmio_w4203(uint8 data) { status.r4216 = status.mul_a * status.mul_b; status.alu_lock = true; - event.enqueue(snes.config.cpu.alu_mul_delay, EventAluLockRelease); + event.enqueue(config.cpu.alu_mul_delay, EventAluLockRelease); } //WRDIVL @@ -113,7 +113,7 @@ void sCPU::mmio_w4206(uint8 data) { status.r4216 = (status.div_b) ? status.div_a % status.div_b : status.div_a; status.alu_lock = true; - event.enqueue(snes.config.cpu.alu_div_delay, EventAluLockRelease); + event.enqueue(config.cpu.alu_div_delay, EventAluLockRelease); } //HTIMEL diff --git a/src/cpu/scpu/scpu.cpp b/src/cpu/scpu/scpu.cpp index 9fc70c2d..e181c390 100644 --- a/src/cpu/scpu/scpu.cpp +++ b/src/cpu/scpu/scpu.cpp @@ -1,7 +1,9 @@ #include <../base.hpp> -#define SCPU_CPP - #include + +#define SCPU_CPP +namespace SNES { + priority_queue event(512, bind(&sCPU::queue_event, &cpu)); #include "dma/dma.cpp" @@ -94,3 +96,5 @@ sCPU::sCPU() { sCPU::~sCPU() { } + +}; diff --git a/src/cpu/scpu/timing/joypad.cpp b/src/cpu/scpu/timing/joypad.cpp index c754c4dc..ea018229 100644 --- a/src/cpu/scpu/timing/joypad.cpp +++ b/src/cpu/scpu/timing/joypad.cpp @@ -3,8 +3,8 @@ void sCPU::run_auto_joypad_poll() { uint16 joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0; for(unsigned i = 0; i < 16; i++) { - uint8 port0 = snes.input.port_read(0); - uint8 port1 = snes.input.port_read(1); + uint8 port0 = system.input.port_read(0); + uint8 port1 = system.input.port_read(1); joy1 |= (port0 & 1) ? (0x8000 >> i) : 0; joy2 |= (port1 & 1) ? (0x8000 >> i) : 0; diff --git a/src/cpu/scpu/timing/timing.cpp b/src/cpu/scpu/timing/timing.cpp index efcc52d9..eb40929e 100644 --- a/src/cpu/scpu/timing/timing.cpp +++ b/src/cpu/scpu/timing/timing.cpp @@ -14,7 +14,7 @@ void sCPU::add_clocks(unsigned clocks) { while(ticks--) { ppu.tick(); if((ppu.hcounter() & 2) == 0) { - snes.input.tick(); + system.input.tick(); } else { poll_interrupts(); } @@ -44,7 +44,7 @@ void sCPU::scanline() { } if(status.auto_joypad_poll == true && ppu.vcounter() == (ppu.overscan() == false ? 227 : 242)) { - snes.input.poll(); + system.input.poll(); run_auto_joypad_poll(); } } diff --git a/src/data/license.html b/src/data/license.html index 6f5591ec..60e97f32 100644 --- a/src/data/license.html +++ b/src/data/license.html @@ -66,6 +66,8 @@ software which is ordinarily not compatible with this license, such as the GPL. + + diff --git a/src/dsp/adsp/adsp.cpp b/src/dsp/adsp/adsp.cpp index dde6348b..38849dab 100644 --- a/src/dsp/adsp/adsp.cpp +++ b/src/dsp/adsp/adsp.cpp @@ -1,5 +1,7 @@ #include <../base.hpp> -#define ADSP_CPP + +#define ADSP_CPP +namespace SNES { #include "adsp_tables.cpp" @@ -580,10 +582,12 @@ int32 fir_samplel, fir_sampler; msampler = sclamp<16>(msampler); } - snes.audio.update(msamplel, msampler); + system.audio.dsp_sample(msamplel, msampler); scheduler.addclocks_dsp(32 * 3 * 8); scheduler.sync_dspsmp(); } aDSP::aDSP() {} aDSP::~aDSP() {} + +}; diff --git a/src/dsp/sdsp/echo.cpp b/src/dsp/sdsp/echo.cpp index 36e7d925..afc386b2 100644 --- a/src/dsp/sdsp/echo.cpp +++ b/src/dsp/sdsp/echo.cpp @@ -106,7 +106,7 @@ void sDSP::echo_27() { } //output sample to DAC - snes.audio.update(outl, outr); + system.audio.dsp_sample(outl, outr); } void sDSP::echo_28() { diff --git a/src/dsp/sdsp/sdsp.cpp b/src/dsp/sdsp/sdsp.cpp index 0d784cff..3d241ef1 100644 --- a/src/dsp/sdsp/sdsp.cpp +++ b/src/dsp/sdsp/sdsp.cpp @@ -8,6 +8,7 @@ #include <../base.hpp> #define SDSP_CPP +namespace SNES { #define REG(n) state.regs[r_##n] #define VREG(n) state.regs[v.vidx + v_##n] @@ -324,3 +325,5 @@ sDSP::sDSP() { sDSP::~sDSP() { } + +}; diff --git a/src/interface.hpp b/src/interface.hpp index 7f79d01e..738d45f3 100644 --- a/src/interface.hpp +++ b/src/interface.hpp @@ -1,25 +1,31 @@ -#include "cheat/cheat.hpp" +namespace SNES { + #include "cheat/cheat.hpp" -#include "memory/memory.hpp" -#include "memory/smemory/smemory.hpp" + #include "memory/memory.hpp" + #include "memory/smemory/smemory.hpp" -#include "cpu/cpu.hpp" -#include "cpu/core/core.hpp" -#include "cpu/scpu/scpu.hpp" + #include "cpu/cpu.hpp" + #include "cpu/core/core.hpp" + #include "cpu/scpu/scpu.hpp" -#include "ppu/ppu.hpp" -#include "ppu/bppu/bppu.hpp" + #include "smp/smp.hpp" + #include "smp/core/core.hpp" + #include "smp/ssmp/ssmp.hpp" -#include "smp/smp.hpp" -#include "smp/ssmp/ssmp.hpp" + #include "ppu/ppu.hpp" + #include "ppu/bppu/bppu.hpp" -#include "dsp/dsp.hpp" -#include "dsp/sdsp/sdsp.hpp" + #include "dsp/dsp.hpp" + #include "dsp/sdsp/sdsp.hpp" -extern BUSCORE bus; -extern CPUCORE cpu; -extern SMPCORE smp; -extern DSPCORE dsp; -extern PPUCORE ppu; + extern BUSCORE bus; + extern CPUCORE cpu; + extern SMPCORE smp; + extern DSPCORE dsp; + extern PPUCORE ppu; -#include "snes/snes.hpp" + #include "system/system.hpp" + #include "chip/chip.hpp" + + #include "cartridge/cartridge.hpp" +} diff --git a/src/lib/libgambatte/COPYING b/src/lib/libgambatte/COPYING new file mode 100644 index 00000000..d511905c --- /dev/null +++ b/src/lib/libgambatte/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/src/lib/libgambatte/Makefile b/src/lib/libgambatte/Makefile new file mode 100644 index 00000000..3382030a --- /dev/null +++ b/src/lib/libgambatte/Makefile @@ -0,0 +1,69 @@ +cc = g++ + +cflags = -O2 -fomit-frame-pointer -Wall -Wextra +cflags += -fno-exceptions -fno-rtti +cflags += -DHAVE_STDINT_H -DCHAR_WIDTH_8 +cflags += -Isrc -Iinclude -Icommon + +headers = src/*.h src/file/*.h src/sound/*.h src/video/*.h src/video/filters/*.h +headers += include/*.h +headers += common/*.h common/resample/*.h + +source = \ + src/bitmap_font.cpp \ + src/colorconversion.cpp \ + src/cpu.cpp \ + src/gambatte.cpp \ + src/initstate.cpp \ + src/interrupter.cpp \ + src/memory.cpp \ + src/rtc.cpp \ + src/sound.cpp \ + src/state_osd_elements.cpp \ + src/statesaver.cpp \ + src/video.cpp \ + src/sound/channel1.cpp \ + src/sound/channel2.cpp \ + src/sound/channel3.cpp \ + src/sound/channel4.cpp \ + src/sound/duty_unit.cpp \ + src/sound/envelope_unit.cpp \ + src/sound/length_counter.cpp \ + src/video/basic_add_event.cpp \ + src/video/break_event.cpp \ + src/video/irq_event.cpp \ + src/video/ly_counter.cpp \ + src/video/lyc_irq.cpp \ + src/video/m3_extra_cycles.cpp \ + src/video/mode3_event.cpp \ + src/video/mode0_irq.cpp \ + src/video/mode1_irq.cpp \ + src/video/mode2_irq.cpp \ + src/video/sc_reader.cpp \ + src/video/scx_reader.cpp \ + src/video/sprite_mapper.cpp \ + src/video/we_master_checker.cpp \ + src/video/we.cpp \ + src/video/wx_reader.cpp \ + src/video/wy.cpp \ + src/video/filters/catrom2x.cpp \ + src/video/filters/catrom3x.cpp \ + src/video/filters/kreed2xsai.cpp \ + src/video/filters/maxsthq2x.cpp \ + src/video/filters/maxsthq3x.cpp \ + src/file/file.cpp \ + +all: build; + +clean: + -@rm obj/*.o + -@rm libgambatte.a + +$(foreach item,$(source),$(eval obj/$(notdir $(basename $(item))).o: $(item) $(headers))) +objects := $(foreach item,$(source),obj/$(notdir $(basename $(item))).o) + +%.o: $< + $(cc) -o $@ -c $< $(cflags) + +build: $(objects) + ar rcs libgambatte.a $(objects) diff --git a/src/lib/libgambatte/README b/src/lib/libgambatte/README new file mode 100644 index 00000000..0e243055 --- /dev/null +++ b/src/lib/libgambatte/README @@ -0,0 +1,130 @@ +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +Copyright (C) 2007 by Sindre AamÃ¥s +aamas@stud.ntnu.no + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License version 2 for more details. + +You should have received a copy of the GNU General Public License +version 2 along with this program; if not, write to the +Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +About +-------------------------------------------------------------------------------- +Gambatte is an accuracy-focused, open-source, cross-platform +Game Boy Color emulator written in C++. It is based on hundreds of +corner case hardware tests, as well as previous documentation and reverse +engineering efforts. + +The core emulation code is contained in a separate library back-end +(libgambatte) written in platform-independent C++. There is currently a GUI +front-end (gambatte_qt) using Trolltech's Qt4 toolkit, and a simple command-line +SDL front-end (gambatte_sdl). + +The GUI front-end contains platform-specific extensions for video, sound and +timers. It should work on MS Windows, Linux/BSD/UNIX-like OSes, and Mac OS X. + +The SDL front-end should be usable on all platforms with a working SDL port. It +should also be quite trivial to create new (simple) front-ends (note that the +library API should in no way be considered stable). + +Usage +-------------------------------------------------------------------------------- +You will have to supply Gambatte with a ROM image file of the GB/GBC +program/game you'd like to run/play, either as a command-line argument to +gambatte_sdl, or through the File->Open... menu in gambatte_qt. + +gambatte_sdl keyboard commands: +TAB - fast-forward +Ctrl-f - toggle full screen +Ctrl-r - reset +F5 - save state +F6 - previous state slot +F7 - next state slot +F8 - load state +0 to 9 - select state slot 0 to 9 + +Default key mapping: +Up: Up +Down: Down +Left: Left +Right: Right +A: D +B: C +Start: Return +Select: Shift + +Compiling +-------------------------------------------------------------------------------- +Building Gambatte from source code can be done by executing the +build_.sh scripts for the qt/sdl front-ends respectively, or by issueing +the correct build command (either 'scons' or 'qmake && make') in the top-level +subdirectories (libgambatte will have to be built first). The clean.sh script +can be executed to remove all generated files after a compile (including +binaries). + +Requirements for building libgambatte: +- A decent C++ compiler (like g++ in the GNU Compiler Collection). +- SCons. +- optionally zlib + +Requirements for building gambatte_sdl: +- A decent C++ compiler (like g++ in the GNU Compiler Collection). +- SDL headers and library. +- SCons. +(- libgambatte.) + +Requirements for building gambatte_qt: +- A decent C++ compiler (like g++ in the GNU Compiler Collection). +- Qt4 (Core, GUI, OpenGL) headers and library. +- Qmake and make (GNU Make should work). +- Platform-specific requirements: + * MS Windows: + - Win32 API headers and libraries. + - DirectSound and DirectDraw7 headers and libraries. + - Direct3D9 headers + * Linux/BSD/UNIX-like OSes: + - POSIX/UNIX headers (unistd.h, fcntl.h, sys/ioctl.h, sys/time.h, sys/shm.h). + - Open Sound System header (sys/soundcard.h). + - X11 Xlib, XVideo, XRandR and XShm headers and libraries. + - Alsa headers and library (Linux only). + * Max OS X: + - Recent Mac OS X SDK (Panther Xcode/SDK should work) +(- libgambatte.) + +Installing after a compile simply amounts to copying the generated binary +(either gambatte_qt/bin/gambatte_qt<.exe> or gambatte_sdl/gambatte_sdl<.exe>) +to wherever you'd like to keep it. + +Thanks +-------------------------------------------------------------------------------- +Derek Liauw Kie Fa (Kreed) +Gilles Vollant +Jeff Frohwein +Jonathan Gevaryahu (Lord Nightmare) +kOOPa +Marat Fayzullin +Martin Korth (nocash) +Maxim Stepin (MaxSt) +Nach +Pan of Anthrox +Pascal Felber +Paul Robson +SDL +Shay Green (blargg) +The OpenGL Extension Wrangler Library + +-------------------------------------------------------------------------------- +Game Boy and Game Boy Color are registered trademarks of +Nintendo of America Inc. +Gambatte is not affiliated with or endorsed by any of the companies mentioned. diff --git a/src/lib/libgambatte/common/adaptivesleep.cpp b/src/lib/libgambatte/common/adaptivesleep.cpp new file mode 100644 index 00000000..48c40979 --- /dev/null +++ b/src/lib/libgambatte/common/adaptivesleep.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "adaptivesleep.h" + +usec_t AdaptiveSleep::sleepUntil(usec_t base, usec_t inc) { + usec_t now = getusecs(); + usec_t diff = now - base; + + if (diff >= inc) + return diff - inc; + + diff = inc - diff; + + if (diff > oversleep + oversleepVar) { + diff -= oversleep + oversleepVar; + usecsleep(diff); + const usec_t ideal = now + diff; + now = getusecs(); + + { + usec_t curOversleep = now - ideal; + + if (negate(curOversleep) < curOversleep) + curOversleep = 0; + + oversleepVar = (oversleepVar * 15 + (curOversleep < oversleep ? oversleep - curOversleep : curOversleep - oversleep)) >> 4; + oversleep = (oversleep * 15 + curOversleep) >> 4; + } + + noSleep = 60; + } else if (--noSleep == 0) { + noSleep = 60; + oversleep = oversleepVar = 0; + } + + while (now - base < inc) + now = getusecs(); + + return 0; +} diff --git a/src/lib/libgambatte/common/adaptivesleep.h b/src/lib/libgambatte/common/adaptivesleep.h new file mode 100644 index 00000000..de2010a0 --- /dev/null +++ b/src/lib/libgambatte/common/adaptivesleep.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ADAPTIVE_SLEEP_H +#define ADAPTIVE_SLEEP_H + +#include "usec.h" + +class AdaptiveSleep { + usec_t oversleep; + usec_t oversleepVar; + unsigned noSleep; + +public: + AdaptiveSleep() : oversleep(0), oversleepVar(0), noSleep(60) {} + usec_t sleepUntil(usec_t base, usec_t inc); +}; + +#endif diff --git a/src/lib/libgambatte/common/array.h b/src/lib/libgambatte/common/array.h new file mode 100644 index 00000000..f01806ea --- /dev/null +++ b/src/lib/libgambatte/common/array.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARRAY_H +#define ARRAY_H + +#include + +template +class Array { + T *a; + std::size_t sz; + + Array(const Array &ar); + +public: + Array(const std::size_t size = 0) : a(size ? new T[size] : 0), sz(size) {} + ~Array() { delete []a; } + void reset(const std::size_t size) { delete []a; a = size ? new T[size] : 0; sz = size; } + std::size_t size() const { return sz; } + operator T*() { return a; } + operator const T*() const { return a; } +}; + +#endif diff --git a/src/lib/libgambatte/common/rateest.cpp b/src/lib/libgambatte/common/rateest.cpp new file mode 100644 index 00000000..c1feba6c --- /dev/null +++ b/src/lib/libgambatte/common/rateest.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "rateest.h" +#include + +void RateEst::SumQueue::reset() { + q.clear(); + samples_ = usecs_ = 0; +} + +void RateEst::SumQueue::push(const long samples, const usec_t usecs) { + q.push_back(pair_t(samples, usecs)); + samples_ += samples; + usecs_ += usecs; +} + +void RateEst::SumQueue::pop() { + const pair_t &f = q.front(); + samples_ -= f.first; + usecs_ -= f.second; + q.pop_front(); +} + +static usec_t sampleUsecs(long samples, long rate) { + return static_cast((samples * 1000000.0f) / (rate ? rate : 1) + 0.5f); +} + +static long limit(long est, const long reference) { + if (est > reference + (reference >> 6)) + est = reference + (reference >> 6); + else if (est < reference - (reference >> 6)) + est = reference - (reference >> 6); + + return est; +} + +void RateEst::init(long srate, long reference, const long maxSamplePeriod) { + maxPeriod = sampleUsecs(maxSamplePeriod, reference); + + srate <<= UPSHIFT; + reference <<= UPSHIFT; + + this->srate.est = limit(srate, reference); + this->srate.var = srate >> 12; + last = 0; + this->reference = reference; + samples = ((this->srate.est >> UPSHIFT) * 12) << 5; + usecs = 12000000 << 5; + sumq.reset(); +} + +void RateEst::feed(long samplesIn, const usec_t now) { + usec_t usecsIn = now - last; + + if (last && usecsIn < maxPeriod) { + sumq.push(samplesIn, usecsIn); + + while ((usecsIn = sumq.usecs()) > 100000) { + samplesIn = sumq.samples(); + sumq.pop(); + + if (std::abs(static_cast(samplesIn * (1000000.0f * UP) / usecsIn) - reference) < reference >> 1) { + samples += (samplesIn - sumq.samples()) << 5; + usecs += (usecsIn - sumq.usecs()) << 5; + + long est = static_cast(samples * (1000000.0f * UP) / usecs + 0.5f); + est = limit((srate.est * 31 + est + 16) >> 5, reference); + srate.var = (srate.var * 15 + std::abs(est - srate.est) + 8) >> 4; + srate.est = est; + + if (usecs > 16000000 << 5) { + samples = (samples * 3 + 2) >> 2; + usecs = (usecs * 3 + 2) >> 2; + } + } + } + } + + last = now; +} diff --git a/src/lib/libgambatte/common/rateest.h b/src/lib/libgambatte/common/rateest.h new file mode 100644 index 00000000..3e109541 --- /dev/null +++ b/src/lib/libgambatte/common/rateest.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RATEEST_H +#define RATEEST_H + +#include "usec.h" +#include +#include + +class RateEst { +public: + struct Result { + long est; + long var; + }; + +private: + class SumQueue { + typedef std::pair pair_t; + typedef std::deque q_t; + + q_t q; + long samples_; + usec_t usecs_; + + public: + SumQueue() : samples_(0), usecs_(0) {} + void reset(); + long samples() const { return samples_; } + usec_t usecs() const { return usecs_; } + void push(long samples, usec_t usecs); + void pop(); + }; + + enum { UPSHIFT = 5 }; + enum { UP = 1 << UPSHIFT }; + + Result srate; + SumQueue sumq; + usec_t last; + usec_t usecs; + usec_t maxPeriod; + long reference; + long samples; + +public: + RateEst(long srate = 0) { init(srate); } + RateEst(long srate, long reference) { init(srate, reference); } + void init(long srate) { init(srate, srate); } + void init(long srate, long reference) { init(srate, reference, reference); } + void init(long srate, long reference, long maxSamplePeriod); + void reset() { last = 0; } + void feed(long samples, usec_t usecs = getusecs()); + const Result result() const { const Result res = { (srate.est + UP / 2) >> UPSHIFT, (srate.var + UP / 2) >> UPSHIFT }; return res; } +}; + +#endif diff --git a/src/lib/libgambatte/common/resample/blackmansinc.h b/src/lib/libgambatte/common/resample/blackmansinc.h new file mode 100644 index 00000000..86578239 --- /dev/null +++ b/src/lib/libgambatte/common/resample/blackmansinc.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef BLACKMANSINC_H +#define BLACKMANSINC_H + +#include "convoluter.h" +#include "subresampler.h" +#include "makesinckernel.h" +#include "cic4.h" +#include +#include + +template +class BlackmanSinc : public SubResampler { + PolyPhaseConvoluter convoluters[channels]; + short *kernel; + + static double blackmanWin(const long i, const long M) { + static const double PI = 3.14159265358979323846; + return 0.42 - 0.5 * std::cos(2 * PI * i / M) + 0.08 * std::cos(4 * PI * i / M); + } + + void init(unsigned div, unsigned phaseLen, double fc); + +public: + enum { MUL = phases }; + + typedef Cic4 Cic; + static float cicLimit() { return 4.7f; } + + class RollOff { + static unsigned toTaps(const float rollOffWidth) { + static const float widthTimesTaps = 4.5f; + return std::ceil(widthTimesTaps / rollOffWidth); + } + + static float toFc(const float rollOffStart, const int taps) { + static const float startToFcDeltaTimesTaps = 1.69f; + return startToFcDeltaTimesTaps / taps + rollOffStart; + } + + public: + const unsigned taps; + const float fc; + + RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {} + }; + + BlackmanSinc(unsigned div, unsigned phaseLen, double fc) { init(div, phaseLen, fc); } + BlackmanSinc(unsigned div, RollOff ro) { init(div, ro.taps, ro.fc); } + ~BlackmanSinc() { delete[] kernel; } + std::size_t resample(short *out, const short *in, std::size_t inlen); + void adjustDiv(unsigned div); + unsigned mul() const { return MUL; } + unsigned div() const { return convoluters[0].div(); } +}; + +template +void BlackmanSinc::init(const unsigned div, const unsigned phaseLen, const double fc) { + kernel = new short[phaseLen * phases]; + + makeSincKernel(kernel, phases, phaseLen, fc, blackmanWin); + + for (unsigned i = 0; i < channels; ++i) + convoluters[i].reset(kernel, phaseLen, div); +} + +template +std::size_t BlackmanSinc::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t samplesOut; + + for (unsigned i = 0; i < channels; ++i) + samplesOut = convoluters[i].filter(out + i, in + i, inlen); + + return samplesOut; +} + +template +void BlackmanSinc::adjustDiv(const unsigned div) { + for (unsigned i = 0; i < channels; ++i) + convoluters[i].adjustDiv(div); +} + +#endif diff --git a/src/lib/libgambatte/common/resample/chainresampler.cpp b/src/lib/libgambatte/common/resample/chainresampler.cpp new file mode 100644 index 00000000..6836a05b --- /dev/null +++ b/src/lib/libgambatte/common/resample/chainresampler.cpp @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "chainresampler.h" + +float ChainResampler::get2ChainMidRatio(const float ratio, const float rollOff) { + return std::sqrt(0.5f * rollOff * ratio) + 1; +} + +float ChainResampler::get2ChainCost(const float ratio, const float rollOff, const float midRatio) { + return midRatio * ratio / ((midRatio - 1) * 2) + midRatio / rollOff; +} + +float ChainResampler::get3ChainRatio1(float ratio1, const float rollOff, const float ratio) { + for (unsigned n = 8; n--;) { + ratio1 = std::sqrt(ratio - ratio / get3ChainRatio2(ratio1, rollOff)) + 1; + } + + return ratio1; +} + +float ChainResampler::get3ChainCost(const float ratio, const float rollOff, const float ratio1, const float ratio2) { + return ratio1 * ratio / ((ratio1 - 1) * 2) + ratio2 * ratio1 / ((ratio2 - 1) * 2) + ratio2 / rollOff; +} + +std::size_t ChainResampler::reallocateBuffer() { + std::size_t bufSz[2] = { 0, 0 }; + std::size_t inSz = periodSize; + int i = -1; + + for (list_t::iterator it = list.begin(); it != list.end(); ++it) { + inSz = (inSz * (*it)->mul() - 1) / (*it)->div() + 1; + + ++i; + + if (inSz > bufSz[i&1]) + bufSz[i&1] = inSz; + } + + if (inSz >= bufSz[i&1]) + bufSz[i&1] = 0; + + if (bufferSize < bufSz[0] + bufSz[1]) { + delete[] buffer; + buffer = (bufferSize = bufSz[0] + bufSz[1]) ? new short[bufferSize * channels] : NULL; + } + + buffer2 = bufSz[1] ? buffer + bufSz[0] * channels : NULL; + + return (maxOut_ = inSz); +} + +void ChainResampler::adjustRate(const long inRate, const long outRate) { + unsigned long mul, div; + + exactRatio(mul, div); + + bigSinc->adjustDiv(static_cast(inRate) * mul / (static_cast(div / bigSinc->div()) * outRate) + 0.5); + + reallocateBuffer(); + setRate(inRate, outRate); +} + +void ChainResampler::exactRatio(unsigned long &mul, unsigned long &div) const { + mul = 1; + div = 1; + + for (list_t::const_iterator it = list.begin(); it != list.end(); ++it) { + mul *= (*it)->mul(); + div *= (*it)->div(); + } +} + +std::size_t ChainResampler::resample(short *const out, const short *const in, std::size_t inlen) { + assert(inlen <= periodSize); + + short *const buf = buffer != buffer2 ? buffer : out; + short *const buf2 = buffer2 ? buffer2 : out; + + const short *inbuf = in; + short *outbuf = NULL; + + for (list_t::iterator it = list.begin(); it != list.end(); ++it) { + outbuf = ++list_t::iterator(it) == list.end() ? out : (inbuf == buf ? buf2 : buf); + inlen = (*it)->resample(outbuf, inbuf, inlen); + inbuf = outbuf; + } + + return inlen; +} + +void ChainResampler::uninit() { + delete[] buffer; + buffer2 = buffer = NULL; + bufferSize = 0; + periodSize = 0; + bigSinc = NULL; + + for (list_t::iterator it = list.begin(); it != list.end(); ++it) + delete *it; + + list.clear(); +} diff --git a/src/lib/libgambatte/common/resample/chainresampler.h b/src/lib/libgambatte/common/resample/chainresampler.h new file mode 100644 index 00000000..aeb52d6c --- /dev/null +++ b/src/lib/libgambatte/common/resample/chainresampler.h @@ -0,0 +1,189 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CHAINRESAMPLER_H +#define CHAINRESAMPLER_H + +#include +#include +#include +#include +#include +#include "subresampler.h" +#include "resampler.h" +#include "upsampler.h" + +class ChainResampler : public Resampler { + enum { channels = 2 }; + + typedef std::list list_t; + + list_t list; + SubResampler *bigSinc; + short *buffer; + short *buffer2; + std::size_t bufferSize; + std::size_t periodSize; + std::size_t maxOut_; + + static float get1ChainCost(const float ratio, const float rollOff) { + return ratio / rollOff; + } + + static float get2ChainMidRatio(float ratio, float rollOff); + static float get2ChainCost(float ratio, float rollOff, float midRatio); + + static float get3ChainRatio2(const float ratio1, const float rollOff) { + return get2ChainMidRatio(ratio1, rollOff); + } + + static float get3ChainRatio1(float ratio1, float rollOff, float ratio); + static float get3ChainCost(float ratio, float rollOff, float ratio1, float ratio2); + + template class Sinc> + std::size_t downinit(long inRate, long outRate, std::size_t periodSize); + + std::size_t reallocateBuffer(); + + template class Sinc> + std::size_t upinit(long inRate, long outRate, std::size_t periodSize); + +public: + ChainResampler() : bigSinc(NULL), buffer(NULL), buffer2(NULL), bufferSize(0), periodSize(0) {} + ~ChainResampler() { uninit(); } + + void adjustRate(long inRate, long outRate); + void exactRatio(unsigned long &mul, unsigned long &div) const; + + template class Sinc> + std::size_t init(long inRate, long outRate, std::size_t periodSize); + std::size_t maxOut(std::size_t /*inlen*/) const { return maxOut_; } + std::size_t resample(short *out, const short *in, std::size_t inlen); + void uninit(); +}; + +template class Sinc> +std::size_t ChainResampler::init(const long inRate, const long outRate, const std::size_t periodSize) { + setRate(inRate, outRate); + + if (outRate > inRate) + return upinit(inRate, outRate, periodSize); + else + return downinit(inRate, outRate, periodSize); +} + +template class Sinc> +std::size_t ChainResampler::downinit(const long inRate, const long outRate, const std::size_t periodSize) { + typedef Sinc BigSinc; + typedef Sinc SmallSinc; + + uninit(); + this->periodSize = periodSize; + + + const float rollOff = std::max((outRate - 36000.0f + outRate - 40000.0f) / outRate, 0.2f); + + double ratio = static_cast(inRate) / outRate; + + while (ratio >= BigSinc::cicLimit() * 2) { + const int div = std::min(static_cast(ratio / BigSinc::cicLimit()), BigSinc::Cic::MAX_DIV); + + list.push_back(new typename BigSinc::Cic(div)); + ratio /= div; + } + + { + int div_2c = ratio * SmallSinc::MUL / get2ChainMidRatio(ratio, rollOff) + 0.5f; + double ratio_2c = ratio * SmallSinc::MUL / div_2c; + float cost_2c = get2ChainCost(ratio, rollOff, ratio_2c); + + if (cost_2c < get1ChainCost(ratio, rollOff)) { + const int div1_3c = ratio * SmallSinc::MUL / get3ChainRatio1(ratio_2c, rollOff, ratio) + 0.5f; + const double ratio1_3c = ratio * SmallSinc::MUL / div1_3c; + const int div2_3c = ratio1_3c * SmallSinc::MUL / get3ChainRatio2(ratio1_3c, rollOff) + 0.5f; + const double ratio2_3c = ratio1_3c * SmallSinc::MUL / div2_3c; + + if (get3ChainCost(ratio, rollOff, ratio1_3c, ratio2_3c) < cost_2c) { + list.push_back(new SmallSinc(div1_3c, typename SmallSinc::RollOff(0.5f / ratio, (ratio1_3c - 1) / ratio))); + ratio = ratio1_3c; + div_2c = div2_3c; + ratio_2c = ratio2_3c; + } + + list.push_back(new SmallSinc(div_2c, typename SmallSinc::RollOff(0.5f / ratio, (ratio_2c - 1) / ratio))); + ratio = ratio_2c; + } + } + + list.push_back(bigSinc = new BigSinc(BigSinc::MUL * ratio + 0.5, + typename BigSinc::RollOff(0.5f * (1 + std::max((outRate - 40000.0f) / outRate, 0.0f) - rollOff) / ratio, 0.5f * rollOff / ratio))); + + return reallocateBuffer(); +} + +template class Sinc> +std::size_t ChainResampler::upinit(const long inRate, const long outRate, const std::size_t periodSize) { + typedef Sinc BigSinc; + typedef Sinc SmallSinc; + + uninit(); + this->periodSize = periodSize; + + const float rollOff = std::max((inRate - 36000.0f) / inRate, 0.2f); + + double ratio = static_cast(outRate) / inRate; + + // Spectral images above 20 kHz assumed inaudible + { + const int div = outRate / std::max(inRate, 40000l); + + if (div >= 2) { + list.push_front(new Upsampler(div)); + ratio /= div; + } + } + + { + int div_2c = get2ChainMidRatio(ratio, rollOff) * SmallSinc::MUL / ratio + 0.5f; + double ratio_2c = ratio * div_2c / SmallSinc::MUL; + float cost_2c = get2ChainCost(ratio, rollOff, ratio_2c); + + if (cost_2c < get1ChainCost(ratio, rollOff)) { + const int div1_3c = get3ChainRatio1(ratio_2c, rollOff, ratio) * SmallSinc::MUL / ratio + 0.5f; + const double ratio1_3c = ratio * div1_3c / SmallSinc::MUL; + const int div2_3c = get3ChainRatio2(ratio1_3c, rollOff) * SmallSinc::MUL / ratio1_3c + 0.5f; + const double ratio2_3c = ratio1_3c * div2_3c / SmallSinc::MUL; + + if (get3ChainCost(ratio, rollOff, ratio1_3c, ratio2_3c) < cost_2c) { + list.push_front(new SmallSinc(div1_3c, typename SmallSinc::RollOff(0.5f / ratio1_3c, (ratio1_3c - 1) / ratio1_3c))); + ratio = ratio1_3c; + div_2c = div2_3c; + ratio_2c = ratio2_3c; + } + + list.push_front(new SmallSinc(div_2c, typename SmallSinc::RollOff(0.5f / ratio_2c, (ratio_2c - 1) / ratio_2c))); + ratio = ratio_2c; + } + } + + list.push_front(bigSinc = new BigSinc(BigSinc::MUL / ratio + 0.5, typename BigSinc::RollOff(0.5f * (1 - rollOff), 0.5f * rollOff))); + + return reallocateBuffer(); +} + +#endif diff --git a/src/lib/libgambatte/common/resample/cic2.h b/src/lib/libgambatte/common/resample/cic2.h new file mode 100644 index 00000000..1f12bfc9 --- /dev/null +++ b/src/lib/libgambatte/common/resample/cic2.h @@ -0,0 +1,198 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CIC2_H +#define CIC2_H + +#include "subresampler.h" + +template +class Cic2Core { +// enum { BUFLEN = 64 }; +// unsigned long buf[BUFLEN]; + unsigned long sum1; + unsigned long sum2; + unsigned long prev1; + unsigned long prev2; + unsigned div_; + unsigned nextdivn; +// unsigned bufpos; + +public: + Cic2Core(const unsigned div = 2) { + reset(div); + } + + unsigned div() const { return div_; } + std::size_t filter(short *out, const short *in, std::size_t inlen); + void reset(unsigned div); +}; + +template +void Cic2Core::reset(const unsigned div) { + sum2 = sum1 = 0; + prev2 = prev1 = 0; + this->div_ = div; + nextdivn = div; +// bufpos = div - 1; +} + +template +std::size_t Cic2Core::filter(short *out, const short *const in, std::size_t inlen) { +// const std::size_t produced = (inlen + div_ - (bufpos + 1)) / div_; + const std::size_t produced = (inlen + div_ - nextdivn) / div_; + const long mul = 0x10000 / (div_ * div_); // trouble if div is too large, may be better to only support power of 2 div + const short *s = in; + + /*unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + + while (inlen >> 2) { + unsigned n = (inlen < BUFLEN ? inlen >> 2 : BUFLEN >> 2); + const unsigned end = n * 4; + unsigned i = 0; + + do { + unsigned long s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + buf[i++] = sm2 += s1; + buf[i++] = sm2 += sm1; + s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + buf[i++] = sm2 += s1; + buf[i++] = sm2 += sm1; + } while (--n); + + while (bufpos < end) { + const unsigned long out2 = buf[bufpos] - prev2; + prev2 = buf[bufpos]; + bufpos += div_; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + bufpos -= end; + inlen -= end; + } + + if (inlen) { + unsigned n = inlen; + unsigned i = 0; + + do { + sm1 += static_cast(*s); + s += channels; + buf[i++] = sm2 += sm1; + } while (--n); + + while (bufpos < inlen) { + const unsigned long out2 = buf[bufpos] - prev2; + prev2 = buf[bufpos]; + bufpos += div_; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + bufpos -= inlen; + } + + sum1 = sm1; + sum2 = sm2;*/ + + unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + + if (inlen >= nextdivn) { + unsigned divn = nextdivn; + std::size_t n = produced; + + do { + do { + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + } while (--divn); + + const unsigned long out2 = sm2 - prev2; + prev2 = sm2; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + + divn = div_; + } while (--n); + + nextdivn = div_; + } + + { + unsigned divn = (in + inlen * channels - s) / channels; + nextdivn -= divn; + + while (divn--) { + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + } + } + + sum1 = sm1; + sum2 = sm2; + + return produced; +} + +template +class Cic2 : public SubResampler { + Cic2Core cics[channels]; + +public: + enum { MAX_DIV = 64 }; + Cic2(unsigned div); + std::size_t resample(short *out, const short *in, std::size_t inlen); + unsigned mul() const { return 1; } + unsigned div() const { return cics[0].div(); } +}; + +template +Cic2::Cic2(const unsigned div) { + for (unsigned i = 0; i < channels; ++i) + cics[i].reset(div); +} + +template +std::size_t Cic2::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t samplesOut; + + for (unsigned i = 0; i < channels; ++i) { + samplesOut = cics[i].filter(out + i, in + i, inlen); + } + + return samplesOut; +} + +#endif diff --git a/src/lib/libgambatte/common/resample/cic3.h b/src/lib/libgambatte/common/resample/cic3.h new file mode 100644 index 00000000..85b9dcee --- /dev/null +++ b/src/lib/libgambatte/common/resample/cic3.h @@ -0,0 +1,382 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CIC3_H +#define CIC3_H + +#include "subresampler.h" + +template +class Cic3Core { +// enum { BUFLEN = 64 }; +// unsigned long buf[BUFLEN]; + unsigned long sum1; + unsigned long sum2; + unsigned long sum3; + unsigned long prev1; + unsigned long prev2; + unsigned long prev3; + unsigned div_; + unsigned nextdivn; +// unsigned bufpos; + +public: + Cic3Core(const unsigned div = 1) { + reset(div); + } + + unsigned div() const { return div_; } + std::size_t filter(short *out, const short *in, std::size_t inlen); + void reset(unsigned div); +}; + +template +void Cic3Core::reset(const unsigned div) { + sum3 = sum2 = sum1 = 0; + prev3 = prev2 = prev1 = 0; + this->div_ = div; + nextdivn = div; +// bufpos = div - 1; +} + +template +std::size_t Cic3Core::filter(short *out, const short *const in, std::size_t inlen) { +// const std::size_t produced = (inlen + div_ - (bufpos + 1)) / div_; + const std::size_t produced = (inlen + div_ - nextdivn) / div_; + const long mul = 0x10000 / (div_ * div_ * div_); // trouble if div is too large, may be better to only support power of 2 div + const short *s = in; + + /*unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + unsigned long sm3 = sum3; + + while (inlen >> 1) { + unsigned n = (inlen < BUFLEN ? inlen >> 1 : BUFLEN >> 1); + const unsigned end = n * 2; + unsigned i = 0; + + do { + unsigned long s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + unsigned long s2 = sm2 += s1; + sm2 += sm1; + buf[i++] = sm3 += s2; + buf[i++] = sm3 += sm2; + } while (--n); + + while (bufpos < end) { + const unsigned long out3 = buf[bufpos] - prev3; + prev3 = buf[bufpos]; + bufpos += div_; + + const unsigned long out2 = out3 - prev2; + prev2 = out3; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + bufpos -= end; + inlen -= end; + } + + if (inlen) { + unsigned n = inlen; + unsigned i = 0; + + do { + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + buf[i++] = sm3 += sm2; + } while (--n); + + while (bufpos < inlen) { + const unsigned long out3 = buf[bufpos] - prev3; + prev3 = buf[bufpos]; + bufpos += div_; + + const unsigned long out2 = out3 - prev2; + prev2 = out3; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + bufpos -= inlen; + } + + sum1 = sm1; + sum2 = sm2; + sum3 = sm3;*/ + + + unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + unsigned long sm3 = sum3; + + if (inlen >= nextdivn) { + unsigned divn = nextdivn; + std::size_t n = produced; + + do { + do { + sm1 += static_cast(*s); + sm2 += sm1; + sm3 += sm2; + s += channels; + } while (--divn); + + const unsigned long out3 = sm3 - prev3; + prev3 = sm3; + + const unsigned long out2 = out3 - prev2; + prev2 = out3; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + + divn = div_; + } while (--n); + + nextdivn = div_; + } + + { + unsigned divn = (in + inlen * channels - s) / channels; + nextdivn -= divn; + + while (divn--) { + sm1 += static_cast(*s); + sm2 += sm1; + sm3 += sm2; + s += channels; + } + } + + sum1 = sm1; + sum2 = sm2; + sum3 = sm3; + + return produced; +} + +/*template +class Cic3EvenOddCore { + unsigned long sum1; + unsigned long sum2; + unsigned long sum3; + unsigned long prev1; + unsigned long prev2; + unsigned long prev3; + unsigned div_; + unsigned nextdivn; + + static int getMul(unsigned div) { + return 0x10000 / (div * div * div); // trouble if div is too large, may be better to only support power of 2 div + } + + void filterEven(short *out, const short *s, std::size_t n); + void filterOdd(short *out, const short *s, std::size_t n); + +public: + Cic3EvenOddCore(const unsigned div = 2) { + reset(div); + } + + unsigned div() const { return div_; } + std::size_t filter(short *out, const short *in, std::size_t inlen); + void reset(unsigned div); +}; + +template +void Cic3EvenOddCore::reset(const unsigned div) { + sum3 = sum2 = sum1 = 0; + prev3 = prev2 = prev1 = 0; + this->div_ = div; + nextdivn = div; +} + +template +void Cic3EvenOddCore::filterEven(short *out, const short *s, std::size_t n) { + const int mul = getMul(div_); + unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + unsigned long sm3 = sum3; + + while (n--) { + { + unsigned sn = div_ >> 1; + + do { + unsigned long s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + unsigned long s2 = sm2 += s1; + sm2 += sm1; + sm3 += s2; + sm3 += sm2; + } while (--sn); + } + + const unsigned long out3 = sm3 - prev3; + prev3 = sm3; + const unsigned long out2 = out3 - prev2; + prev2 = out3; + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + sum1 = sm1; + sum2 = sm2; + sum3 = sm3; +} + +template +void Cic3EvenOddCore::filterOdd(short *out, const short *s, std::size_t n) { + const int mul = getMul(div_); + unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + unsigned long sm3 = sum3; + + while (n--) { + { + unsigned sn = div_ >> 1; + + do { + unsigned long s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + unsigned long s2 = sm2 += s1; + sm2 += sm1; + sm3 += s2; + sm3 += sm2; + } while (--sn); + } + + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + sm3 += sm2; + + const unsigned long out3 = sm3 - prev3; + prev3 = sm3; + const unsigned long out2 = out3 - prev2; + prev2 = out3; + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + sum1 = sm1; + sum2 = sm2; + sum3 = sm3; +} + +template +std::size_t Cic3EvenOddCore::filter(short *out, const short *const in, std::size_t inlen) { + short *const outStart = out; + const short *s = in; + + if (inlen >= nextdivn) { + { + { + unsigned divn = nextdivn; + + do { + sum1 += static_cast(*s); + s += channels; + sum2 += sum1; + sum3 += sum2; + } while (--divn); + } + + const unsigned long out3 = sum3 - prev3; + prev3 = sum3; + const unsigned long out2 = out3 - prev2; + prev2 = out3; + *out = static_cast(out2 - prev1) * getMul(div_) / 0x10000; + prev1 = out2; + out += channels; + } + + std::size_t n = (inlen - nextdivn) / div_; + + if (div_ & 1) + filterOdd(out, s, n); + else + filterEven(out, s, n); + + s += n * div_ * channels; + out += n * channels; + nextdivn = div_; + } + + { + unsigned divn = inlen - (s - in) / channels; + nextdivn -= divn; + + while (divn--) { + sum1 += static_cast(*s); + s += channels; + sum2 += sum1; + sum3 += sum2; + } + } + + return (out - outStart) / channels; +}*/ + +template +class Cic3 : public SubResampler { + Cic3Core cics[channels]; + +public: + enum { MAX_DIV = 23 }; + Cic3(unsigned div); + std::size_t resample(short *out, const short *in, std::size_t inlen); + unsigned mul() const { return 1; } + unsigned div() const { return cics[0].div(); } +}; + +template +Cic3::Cic3(const unsigned div) { + for (unsigned i = 0; i < channels; ++i) + cics[i].reset(div); +} + +template +std::size_t Cic3::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t samplesOut; + + for (unsigned i = 0; i < channels; ++i) { + samplesOut = cics[i].filter(out + i, in + i, inlen); + } + + return samplesOut; +} + +#endif diff --git a/src/lib/libgambatte/common/resample/cic4.h b/src/lib/libgambatte/common/resample/cic4.h new file mode 100644 index 00000000..430cb03d --- /dev/null +++ b/src/lib/libgambatte/common/resample/cic4.h @@ -0,0 +1,237 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CIC4_H +#define CIC4_H + +#include "subresampler.h" + +template +class Cic4Core { + enum { BUFLEN = 64 }; + unsigned long buf[BUFLEN]; + unsigned long sum1; + unsigned long sum2; + unsigned long sum3; + unsigned long sum4; + unsigned long prev1; + unsigned long prev2; + unsigned long prev3; + unsigned long prev4; + unsigned div_; +// unsigned nextdivn; + unsigned bufpos; + +public: + Cic4Core(const unsigned div = 1) { + reset(div); + } + + unsigned div() const { return div_; } + std::size_t filter(short *out, const short *in, std::size_t inlen); + void reset(unsigned div); +}; + +template +void Cic4Core::reset(const unsigned div) { + sum4 = sum3 = sum2 = sum1 = 0; + prev4 = prev3 = prev2 = prev1 = 0; + this->div_ = div; +// nextdivn = div; + bufpos = div - 1; +} + +template +std::size_t Cic4Core::filter(short *out, const short *const in, std::size_t inlen) { + const std::size_t produced = (inlen + div_ - (bufpos + 1)) / div_; +// const std::size_t produced = (inlen + div_ - nextdivn) / div_; + const long mul = 0x10000 / (div_ * div_ * div_ * div_); // trouble if div is too large, may be better to only support power of 2 div + const short *s = in; + + unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + unsigned long sm3 = sum3; + unsigned long sm4 = sum4; + + while (inlen >> 2) { + unsigned n = (inlen < BUFLEN ? inlen >> 2 : BUFLEN >> 2); + const unsigned end = n * 4; + unsigned i = 0; + + do { + unsigned long s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + unsigned long s2 = sm2 += s1; + sm2 += sm1; + unsigned long s3 = sm3 += s2; + sm3 += sm2; + buf[i++] = sm4 += s3; + buf[i++] = sm4 += sm3; + s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + s2 = sm2 += s1; + sm2 += sm1; + s3 = sm3 += s2; + sm3 += sm2; + buf[i++] = sm4 += s3; + buf[i++] = sm4 += sm3; + } while (--n); + + while (bufpos < end) { + const unsigned long out4 = buf[bufpos] - prev4; + prev4 = buf[bufpos]; + bufpos += div_; + + const unsigned long out3 = out4 - prev3; + prev3 = out4; + const unsigned long out2 = out3 - prev2; + prev2 = out3; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + bufpos -= end; + inlen -= end; + } + + if (inlen) { + unsigned n = inlen; + unsigned i = 0; + + do { + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + sm3 += sm2; + buf[i++] = sm4 += sm3; + } while (--n); + + while (bufpos < inlen) { + const unsigned long out4 = buf[bufpos] - prev4; + prev4 = buf[bufpos]; + bufpos += div_; + + const unsigned long out3 = out4 - prev3; + prev3 = out4; + const unsigned long out2 = out3 - prev2; + prev2 = out3; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + bufpos -= inlen; + } + + sum1 = sm1; + sum2 = sm2; + sum3 = sm3; + sum4 = sm4; + + /*unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + unsigned long sm3 = sum3; + unsigned long sm4 = sum4; + + if (produced) { + unsigned divn = nextdivn; + std::size_t n = produced; + + do { + do { + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + sm3 += sm2; + sm4 += sm3; + } while (--divn); + + const unsigned long out4 = sm4 - prev4; + prev4 = sm4; + const unsigned long out3 = out4 - prev3; + prev3 = out4; + const unsigned long out2 = out3 - prev2; + prev2 = out3; + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + + divn = div_; + } while (--n); + + nextdivn = div_; + } + + { + unsigned divn = (in + inlen * channels - s) / channels; + nextdivn -= divn; + + while (divn--) { + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + sm3 += sm2; + sm4 += sm3; + } + } + + sum1 = sm1; + sum2 = sm2; + sum3 = sm3; + sum4 = sm4;*/ + + return produced; +} + +template +class Cic4 : public SubResampler { + Cic4Core cics[channels]; + +public: + enum { MAX_DIV = 13 }; + Cic4(unsigned div); + std::size_t resample(short *out, const short *in, std::size_t inlen); + unsigned mul() const { return 1; } + unsigned div() const { return cics[0].div(); } +}; + +template +Cic4::Cic4(const unsigned div) { + for (unsigned i = 0; i < channels; ++i) + cics[i].reset(div); +} + +template +std::size_t Cic4::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t samplesOut; + + for (unsigned i = 0; i < channels; ++i) { + samplesOut = cics[i].filter(out + i, in + i, inlen); + } + + return samplesOut; +} + +#endif diff --git a/src/lib/libgambatte/common/resample/convoluter.h b/src/lib/libgambatte/common/resample/convoluter.h new file mode 100644 index 00000000..41fab0d0 --- /dev/null +++ b/src/lib/libgambatte/common/resample/convoluter.h @@ -0,0 +1,156 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CONVOLUTER_H +#define CONVOLUTER_H + +#include +#include + +template +class PolyPhaseConvoluter { + const short *kernel; + short *prevbuf; + + unsigned phaseLen; + unsigned div_; + unsigned x_; + +public: + PolyPhaseConvoluter() : kernel(NULL), prevbuf(NULL), phaseLen(0), div_(0), x_(0) {} + PolyPhaseConvoluter(const short *kernel, unsigned phaseLen, unsigned div) { reset(kernel, phaseLen, div); } + ~PolyPhaseConvoluter() { delete[] prevbuf; } + void reset(const short *kernel, unsigned phaseLen, unsigned div); + std::size_t filter(short *out, const short *in, std::size_t inlen); + void adjustDiv(const unsigned div) { this->div_ = div; } + unsigned div() const { return div_; } +}; + +template +void PolyPhaseConvoluter::reset(const short *const kernel, const unsigned phaseLen, const unsigned div) { + this->kernel = kernel; + this->phaseLen = phaseLen; + this->div_ = div; + x_ = 0; + delete[] prevbuf; + prevbuf = new short[phaseLen]; + std::fill(prevbuf, prevbuf + phaseLen, 0); +} + +template +std::size_t PolyPhaseConvoluter::filter(short *out, const short *const in, std::size_t inlen) { + if (!kernel || !inlen) + return 0; + + /*for (std::size_t x = 0; x < inlen + M; ++x) { + const int end = x < inlen ? M + 1 : inlen + M - x; + int j = x < M ? M - x : 0; + j += (phases - (x - M + j) % phases) % phases; // adjust j so we don't start on a virtual 0 sample + + for (; j < end; j += phases) { + buffer[x] += kernel[j] * start[(x - M + j) / phases]; + } + }*/ + + /*for (std::size_t x = 0; x < inlen + M; ++x) { + const int end = x < inlen ? M + 1 : inlen + M - x; + int j = x < M ? M - x : 0; + j += (phases - (x - M + j) % phases) % phases; // adjust j so we don't start on a virtual 0 sample + const short *k = kernel + (j % phases) * phaseLen + j / phases; + const short *s = start + (x - M + j) / phases; + int n = ((end - j) + phases - 1) / phases; + + do { + buffer[x] += *k++ * *s++; + } while (--n); + }*/ + + const std::size_t M = phaseLen * phases - 1; + inlen *= phases; + std::size_t x = x_; + + for (; x < (M < inlen ? M : inlen); x += div_) { + long acc = 0; + const unsigned phase = (phases - (x + 1) % phases) % phases; // adjust phase so we don't start on a virtual 0 sample + const short *s = prevbuf + (x + 1 + phase) / phases; + const short *k = kernel + phase * phaseLen; + unsigned n = prevbuf + phaseLen - s; + + while (n--) { + acc += *k++ * *s++; + } + + s = in; + n = x / phases + 1; + + do { + acc += *k++ * *s; + s += channels; + } while (--n); + + *out = acc / 0x10000; + out += channels; + } + + for (; x < inlen; x += div_) { + long acc = 0; + const unsigned phase = (phases - (x - M) % phases) % phases; // adjust phase so we don't start on a virtual 0 sample + const short *s = in + ((x - M + phase) / phases) * channels; + const short *k = kernel + phase * phaseLen; +// unsigned n = (M + 1/* - phase + phases - 1*/) / phases; + unsigned n = phaseLen; + + do { + acc += *k++ * *s; + s += channels; + } while (--n); + + *out = acc / 0x10000; + out += channels; + } + + const std::size_t produced = (x - x_) / div_; + x_ = x - inlen; + + inlen /= phases; + + { + short *p = prevbuf; + const short *s = in + (inlen - phaseLen) * channels; + unsigned n = phaseLen; + + if (inlen < phaseLen) { + const unsigned i = phaseLen - inlen; + + std::memmove(p, p + inlen, i * sizeof(short)); + + p += i; + n -= i; + s = in; + } + + do { + *p++ = *s; + s += channels; + } while (--n); + } + + return produced; +} + +#endif diff --git a/src/lib/libgambatte/common/resample/hammingsinc.h b/src/lib/libgambatte/common/resample/hammingsinc.h new file mode 100644 index 00000000..bb50daee --- /dev/null +++ b/src/lib/libgambatte/common/resample/hammingsinc.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef HAMMINGSINC_H +#define HAMMINGSINC_H + +#include "convoluter.h" +#include "subresampler.h" +#include "makesinckernel.h" +#include "cic3.h" +#include +#include + +template +class HammingSinc : public SubResampler { + PolyPhaseConvoluter convoluters[channels]; + short *kernel; + + static double hammingWin(const long i, const long M) { + static const double PI = 3.14159265358979323846; + return 0.53836 - 0.46164 * std::cos(2 * PI * i / M); + } + + void init(unsigned div, unsigned phaseLen, double fc); + +public: + enum { MUL = phases }; + + typedef Cic3 Cic; + static float cicLimit() { return 4.2f; } + + class RollOff { + static unsigned toTaps(const float rollOffWidth) { + static const float widthTimesTaps = 3.0f; + return std::ceil(widthTimesTaps / rollOffWidth); + } + + static float toFc(const float rollOffStart, const int taps) { + static const float startToFcDeltaTimesTaps = 1.27f; + return startToFcDeltaTimesTaps / taps + rollOffStart; + } + + public: + const unsigned taps; + const float fc; + + RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {} + }; + + HammingSinc(unsigned div, unsigned phaseLen, double fc) { init(div, phaseLen, fc); } + HammingSinc(unsigned div, RollOff ro) { init(div, ro.taps, ro.fc); } + ~HammingSinc() { delete[] kernel; } + std::size_t resample(short *out, const short *in, std::size_t inlen); + void adjustDiv(unsigned div); + unsigned mul() const { return MUL; } + unsigned div() const { return convoluters[0].div(); } +}; + +template +void HammingSinc::init(const unsigned div, const unsigned phaseLen, const double fc) { + kernel = new short[phaseLen * phases]; + + makeSincKernel(kernel, phases, phaseLen, fc, hammingWin); + + for (unsigned i = 0; i < channels; ++i) + convoluters[i].reset(kernel, phaseLen, div); +} + +template +std::size_t HammingSinc::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t samplesOut; + + for (unsigned i = 0; i < channels; ++i) + samplesOut = convoluters[i].filter(out + i, in + i, inlen); + + return samplesOut; +} + +template +void HammingSinc::adjustDiv(const unsigned div) { + for (unsigned i = 0; i < channels; ++i) + convoluters[i].adjustDiv(div); +} + +#endif diff --git a/src/lib/libgambatte/common/resample/linint.h b/src/lib/libgambatte/common/resample/linint.h new file mode 100644 index 00000000..0c6d8cb2 --- /dev/null +++ b/src/lib/libgambatte/common/resample/linint.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef LININT_H +#define LININT_H + +#include +#include "resampler.h" +#include "u48div.h" + +template +class LinintCore { + unsigned long ratio; + std::size_t pos_; + unsigned fracPos_; + int prevSample_; + +public: + LinintCore(long inRate = 1, long outRate = 1) { init(inRate, outRate); } + void adjustRate(long inRate, long outRate) { ratio = (static_cast(inRate) / outRate) * 0x10000 + 0.5; } + void exactRatio(unsigned long &mul, unsigned long &div) const { mul = 0x10000; div = ratio; } + void init(long inRate, long outRate); + std::size_t maxOut(std::size_t inlen) const { return inlen ? u48div(inlen - 1, 0xFFFF, ratio) + 1 : 0; } + std::size_t resample(short *out, const short *in, std::size_t inlen); +}; + +template +void LinintCore::init(const long inRate, const long outRate) { + adjustRate(inRate, outRate); + pos_ = (ratio >> 16) + 1; + fracPos_ = ratio & 0xFFFF; + prevSample_ = 0; +} + +template +std::size_t LinintCore::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t opos = 0; + std::size_t pos = pos_; + unsigned fracPos = fracPos_; + int prevSample = prevSample_; + + if (pos < inlen) { + if (pos != 0) + prevSample = in[(pos-1) * channels]; + + for (;;) { + out[opos] = prevSample + (in[pos * channels] - prevSample) * static_cast(fracPos) / 0x10000; + opos += channels; + + { + const unsigned long next = ratio + fracPos; + + pos += next >> 16; + fracPos = next & 0xFFFF; + } + + if (pos < inlen) { + prevSample = in[(pos-1) * channels]; + } else + break; + } + + if (pos == inlen) + prevSample = in[(pos-1) * channels]; + } + +// const std::size_t produced = ((pos - pos_) * 0x10000 + fracPos - fracPos_) / ratio; + + pos_ = pos - inlen; + fracPos_ = fracPos; + prevSample_ = prevSample; + + return opos / channels; +} + +template +class Linint : public Resampler { + LinintCore cores[channels]; + +public: + Linint(long inRate, long outRate); + void adjustRate(long inRate, long outRate); + void exactRatio(unsigned long &mul, unsigned long &div) const { cores[0].exactRatio(mul, div); } + std::size_t maxOut(std::size_t inlen) const { return cores[0].maxOut(inlen); } + std::size_t resample(short *out, const short *in, std::size_t inlen); +}; + +template +Linint::Linint(const long inRate, const long outRate) { + setRate(inRate, outRate); + + for (unsigned i = 0; i < channels; ++i) + cores[i].init(inRate, outRate); +} + +template +void Linint::adjustRate(const long inRate, const long outRate) { + setRate(inRate, outRate); + + for (unsigned i = 0; i < channels; ++i) + cores[i].adjustRate(inRate, outRate); +} + +template +std::size_t Linint::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t outlen = 0; + + for (unsigned i = 0; i < channels; ++i) + outlen = cores[i].resample(out + i, in + i, inlen); + + return outlen; +} + +#endif diff --git a/src/lib/libgambatte/common/resample/makesinckernel.h b/src/lib/libgambatte/common/resample/makesinckernel.h new file mode 100644 index 00000000..c6515f2d --- /dev/null +++ b/src/lib/libgambatte/common/resample/makesinckernel.h @@ -0,0 +1,152 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MAKE_SINC_KERNEL_H +#define MAKE_SINC_KERNEL_H + +#include +#include + +template +void makeSincKernel(short *const kernel, const unsigned phases, const unsigned phaseLen, double fc, Window win) { + static const double PI = 3.14159265358979323846; + fc /= phases; + + /*{ + double *const dkernel = new double[phaseLen * phases]; + const long M = static_cast(phaseLen) * phases - 1; + + for (long i = 0; i < M + 1; ++i) { + const double sinc = i * 2 == M ? + PI * fc : + std::sin(PI * fc * (i * 2 - M)) / (i * 2 - M); + + dkernel[(i % phases) * phaseLen + i / phases] = win(i, M) * sinc; + } + + double maxabsgain = 0; + + for (unsigned ph = 0; ph < phases; ++ph) { + double gain = 0; + double absgain = 0; + + for (unsigned i = 0; i < phaseLen; ++i) { + gain += dkernel[ph * phaseLen + i]; + absgain += std::abs(dkernel[ph * phaseLen + i]); + } + + gain = 1.0 / gain; + + // Per phase normalization to avoid DC fluctuations. + for (unsigned i = 0; i < phaseLen; ++i) + dkernel[ph * phaseLen + i] *= gain; + + absgain *= gain; + + if (absgain > maxabsgain) + maxabsgain = absgain; + } + + const double gain = 0x10000 / maxabsgain; + + for (long i = 0; i < M + 1; ++i) + kernel[i] = std::floor(dkernel[i] * gain + 0.5); + + delete[] dkernel; + }*/ + + // The following is equivalent to the more readable version above + + const long M = static_cast(phaseLen) * phases - 1; + + double *const dkernel = new double[M / 2 + 1]; + + { + double *dk = dkernel; + + for (unsigned ph = 0; ph < phases; ++ph) { + for (long i = ph; i < M / 2 + 1; i += phases) { + const double sinc = i * 2 == M ? + PI * fc : + std::sin(PI * fc * (i * 2 - M)) / (i * 2 - M); + + *dk++ = win(i, M) * sinc; + } + } + } + + double maxabsgain = 0.0; + + { + double *dkp1 = dkernel; + double *dkp2 = dkernel + M / 2; + + for (unsigned ph = 0; ph < (phases + 1) / 2; ++ph) { + double gain = 0.0; + double absgain = 0.0; + + { + const double *kp1 = dkp1; + const double *kp2 = dkp2; + long i = ph; + + for (; i < M / 2 + 1; i += phases) { + gain += *kp1; + absgain += std::abs(*kp1++); + } + + for (; i < M + 1; i += phases) { + gain += *kp2; + absgain += std::abs(*kp2--); + } + } + + gain = 1.0 / gain; + + long i = ph; + + for (; i < M / 2 + 1; i += phases) + *dkp1++ *= gain; + + if (dkp1 < dkp2) { + for (; i < M + 1; i += phases) + *dkp2-- *= gain; + } + + absgain *= gain; + + if (absgain > maxabsgain) + maxabsgain = absgain; + } + } + + const double gain = 0x10000 / maxabsgain; + const double *dk = dkernel; + + for (unsigned ph = 0; ph < phases; ++ph) { + short *k = kernel + ph * phaseLen; + short *km = kernel + M - ph * phaseLen; + + for (long i = ph; i < M / 2 + 1; i += phases) + *km-- = *k++ = std::floor(*dk++ * gain + 0.5); + } + + delete[] dkernel; +} + +#endif diff --git a/src/lib/libgambatte/common/resample/rectsinc.h b/src/lib/libgambatte/common/resample/rectsinc.h new file mode 100644 index 00000000..9f99ed6b --- /dev/null +++ b/src/lib/libgambatte/common/resample/rectsinc.h @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RECTSINC_H +#define RECTSINC_H + +#include "convoluter.h" +#include "subresampler.h" +#include "makesinckernel.h" +#include "cic2.h" +#include +#include + +template +class RectSinc : public SubResampler { + PolyPhaseConvoluter convoluters[channels]; + short *kernel; + + static double rectWin(const long /*i*/, const long /*M*/) { + return 1; + } + + void init(unsigned div, unsigned phaseLen, double fc); + +public: + enum { MUL = phases }; + + typedef Cic2 Cic; + static float cicLimit() { return 2.0f; } + + class RollOff { + static unsigned toTaps(const float rollOffWidth) { + static const float widthTimesTaps = 0.9f; + return std::ceil(widthTimesTaps / rollOffWidth); + } + + static float toFc(const float rollOffStart, const int taps) { + static const float startToFcDeltaTimesTaps = 0.43f; + return startToFcDeltaTimesTaps / taps + rollOffStart; + } + + public: + const unsigned taps; + const float fc; + + RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {} + }; + + RectSinc(unsigned div, unsigned phaseLen, double fc) { init(div, phaseLen, fc); } + RectSinc(unsigned div, RollOff ro) { init(div, ro.taps, ro.fc); } + ~RectSinc() { delete[] kernel; } + std::size_t resample(short *out, const short *in, std::size_t inlen); + void adjustDiv(unsigned div); + unsigned mul() const { return MUL; } + unsigned div() const { return convoluters[0].div(); } +}; + +template +void RectSinc::init(const unsigned div, const unsigned phaseLen, const double fc) { + kernel = new short[phaseLen * phases]; + + makeSincKernel(kernel, phases, phaseLen, fc, rectWin); + + for (unsigned i = 0; i < channels; ++i) + convoluters[i].reset(kernel, phaseLen, div); +} + +template +std::size_t RectSinc::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t samplesOut; + + for (unsigned i = 0; i < channels; ++i) + samplesOut = convoluters[i].filter(out + i, in + i, inlen); + + return samplesOut; +} + +template +void RectSinc::adjustDiv(const unsigned div) { + for (unsigned i = 0; i < channels; ++i) + convoluters[i].adjustDiv(div); +} + +#endif diff --git a/src/lib/libgambatte/common/resample/resampler.h b/src/lib/libgambatte/common/resample/resampler.h new file mode 100644 index 00000000..f3d448d9 --- /dev/null +++ b/src/lib/libgambatte/common/resample/resampler.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RESAMPLER_H +#define RESAMPLER_H + +#include + +class Resampler { + long inRate_; + long outRate_; + +protected: + void setRate(const long inRate, const long outRate) { inRate_ = inRate; outRate_ = outRate; } + +public: + Resampler() : inRate_(0), outRate_(0) {} + long inRate() const { return inRate_; } + long outRate() const { return outRate_; } + + virtual void adjustRate(long inRate, long outRate) = 0; + virtual void exactRatio(unsigned long &mul, unsigned long &div) const = 0; + virtual std::size_t maxOut(std::size_t inlen) const = 0; + virtual std::size_t resample(short *out, const short *in, std::size_t inlen) = 0; + virtual ~Resampler() {} +}; + +#endif diff --git a/src/lib/libgambatte/common/resample/resamplerinfo.cpp b/src/lib/libgambatte/common/resample/resamplerinfo.cpp new file mode 100644 index 00000000..3abcdaf8 --- /dev/null +++ b/src/lib/libgambatte/common/resample/resamplerinfo.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "resamplerinfo.h" +#include "chainresampler.h" +#include "hammingsinc.h" +#include "blackmansinc.h" +#include "rectsinc.h" +#include "linint.h" + +struct LinintInfo { + static Resampler* create(long inRate, long outRate, std::size_t) { return new Linint<2>(inRate, outRate); } +}; + +struct RectsincInfo { + static Resampler* create(long inRate, long outRate, std::size_t periodSz) { + ChainResampler *r = new ChainResampler; + r->init(inRate, outRate, periodSz); + return r; + } +}; + +struct HammingsincInfo { + static Resampler* create(long inRate, long outRate, std::size_t periodSz) { + ChainResampler *r = new ChainResampler; + r->init(inRate, outRate, periodSz); + return r; + } +}; + +struct BlackmansincInfo { + static Resampler* create(long inRate, long outRate, std::size_t periodSz) { + ChainResampler *r = new ChainResampler; + r->init(inRate, outRate, periodSz); + return r; + } +}; + +const ResamplerInfo ResamplerInfo::resamplers[] = { + { "2-tap linear interpolation", LinintInfo::create }, + { "Rectangular windowed sinc (~20 dB SNR)", RectsincInfo::create }, + { "Hamming windowed sinc (~50 dB SNR)", HammingsincInfo::create }, + { "Blackman windowed sinc (~70 dB SNR)", BlackmansincInfo::create } +}; + +const unsigned ResamplerInfo::num_ = sizeof(ResamplerInfo::resamplers) / sizeof(ResamplerInfo); diff --git a/src/lib/libgambatte/common/resample/resamplerinfo.h b/src/lib/libgambatte/common/resample/resamplerinfo.h new file mode 100644 index 00000000..23f4a545 --- /dev/null +++ b/src/lib/libgambatte/common/resample/resamplerinfo.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RESAMPLER_INFO_H +#define RESAMPLER_INFO_H + +#include "resampler.h" + +struct ResamplerInfo { + const char *desc; + Resampler* (*create)(long inRate, long outRate, std::size_t periodSz); + + static unsigned num() { return num_; } + static const ResamplerInfo& get(unsigned n) { return resamplers[n]; } + +private: + static const ResamplerInfo resamplers[]; + static const unsigned num_; +}; + +#endif diff --git a/src/lib/libgambatte/common/resample/subresampler.h b/src/lib/libgambatte/common/resample/subresampler.h new file mode 100644 index 00000000..134ec80b --- /dev/null +++ b/src/lib/libgambatte/common/resample/subresampler.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SUBRESAMPLER_H +#define SUBRESAMPLER_H + +#include + +class SubResampler { +public: + virtual std::size_t resample(short *out, const short *in, std::size_t inlen) = 0; + virtual unsigned mul() const = 0; + virtual unsigned div() const = 0; + virtual void adjustDiv(unsigned /*div*/) {} + virtual ~SubResampler() {} +}; + +#endif diff --git a/src/lib/libgambatte/common/resample/u48div.cpp b/src/lib/libgambatte/common/resample/u48div.cpp new file mode 100644 index 00000000..077ddfd9 --- /dev/null +++ b/src/lib/libgambatte/common/resample/u48div.cpp @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "u48div.h" + +unsigned long u48div(unsigned long num1, unsigned num2, const unsigned long den) { + unsigned long res = 0; + unsigned s = 16; + + do { + if (num1 < 0x10000) { + num1 <<= s; + num1 |= num2 & ((1 << s) - 1); + s = 0; + } else { + if (num1 < 0x1000000) { + const unsigned maxs = s < 8 ? s : 8; + num1 <<= maxs; + num1 |= (num2 >> (s -= maxs)) & ((1 << maxs) - 1); + } + + if (num1 < 0x10000000) { + const unsigned maxs = s < 4 ? s : 4; + num1 <<= maxs; + num1 |= (num2 >> (s -= maxs)) & ((1 << maxs) - 1); + } + + while (num1 < den && s) { + num1 <<= 1; // if this overflows we're screwed + num1 |= num2 >> --s & 1; + } + } + + res += (num1 / den) << s; + num1 = (num1 % den); + } while (s); + + return res; +} diff --git a/src/lib/libgambatte/common/resample/u48div.h b/src/lib/libgambatte/common/resample/u48div.h new file mode 100644 index 00000000..26b16af4 --- /dev/null +++ b/src/lib/libgambatte/common/resample/u48div.h @@ -0,0 +1,24 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef U48DIV_H +#define U48DIV_H + +unsigned long u48div(unsigned long num1, unsigned num2, unsigned long den); + +#endif diff --git a/src/lib/libgambatte/common/resample/upsampler.h b/src/lib/libgambatte/common/resample/upsampler.h new file mode 100644 index 00000000..8bf88d8a --- /dev/null +++ b/src/lib/libgambatte/common/resample/upsampler.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef UPSAMPLER_H +#define UPSAMPLER_H + +#include "subresampler.h" +#include + +template +class Upsampler : public SubResampler { + unsigned mul_; + +public: + Upsampler(const unsigned mul) : mul_(mul) {} + std::size_t resample(short *out, const short *in, std::size_t inlen); + unsigned mul() const { return mul_; } + unsigned div() const { return 1; } +}; + +template +std::size_t Upsampler::resample(short *out, const short *in, std::size_t inlen) { + if (inlen) { + std::memset(out, 0, inlen * mul_ * sizeof(short) * channels); + + do { + std::memcpy(out, in, sizeof(short) * channels); + in += channels; + out += mul_ * channels; + } while (--inlen); + } + + return inlen * mul_; +} + +#endif diff --git a/src/lib/libgambatte/common/ringbuffer.h b/src/lib/libgambatte/common/ringbuffer.h new file mode 100644 index 00000000..34f22bfe --- /dev/null +++ b/src/lib/libgambatte/common/ringbuffer.h @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +#include "array.h" +#include +#include +#include + +template +class RingBuffer { + Array buf; + std::size_t sz; + std::size_t rpos; + std::size_t wpos; + +public: + RingBuffer(const std::size_t sz_in = 0) : sz(0), rpos(0), wpos(0) { reset(sz_in); } + + std::size_t avail() const { + return (wpos < rpos ? 0 : sz) + rpos - wpos - 1; + } + + void clear() { + wpos = rpos = 0; + } + + void fill(T value); + + void read(T *out, std::size_t num); + + void reset(std::size_t sz_in); + + std::size_t size() const { + return sz - 1; + } + + std::size_t used() const { + return (wpos < rpos ? sz : 0) + wpos - rpos; + } + + void write(const T *in, std::size_t num); +}; + +template +void RingBuffer::fill(const T value) { + std::fill(buf + 0, buf + sz, value); + rpos = 0; + wpos = sz - 1; +} + +template +void RingBuffer::read(T *out, std::size_t num) { + if (rpos + num > sz) { + const std::size_t n = sz - rpos; + + std::memcpy(out, buf + rpos, n * sizeof(T)); + + rpos = 0; + num -= n; + out += n; + } + + std::memcpy(out, buf + rpos, num * sizeof(T)); + + if ((rpos += num) == sz) + rpos = 0; +} + +template +void RingBuffer::reset(const std::size_t sz_in) { + sz = sz_in + 1; + rpos = wpos = 0; + buf.reset(sz_in ? sz : 0); +} + +template +void RingBuffer::write(const T *in, std::size_t num) { + if (wpos + num > sz) { + const std::size_t n = sz - wpos; + + std::memcpy(buf + wpos, in, n * sizeof(T)); + + wpos = 0; + num -= n; + in += n; + } + + std::memcpy(buf + wpos, in, num * sizeof(T)); + + if ((wpos += num) == sz) + wpos = 0; +} + +#endif diff --git a/src/lib/libgambatte/common/usec.h b/src/lib/libgambatte/common/usec.h new file mode 100644 index 00000000..2bc889cf --- /dev/null +++ b/src/lib/libgambatte/common/usec.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef USEC_H +#define USEC_H + +typedef unsigned long usec_t; + +static inline usec_t negate(usec_t t) { + return usec_t(0) - t; +} + +usec_t getusecs(); +void usecsleep(usec_t usecs); + +#endif diff --git a/src/lib/libgambatte/include/filterinfo.h b/src/lib/libgambatte/include/filterinfo.h new file mode 100644 index 00000000..ab5a4726 --- /dev/null +++ b/src/lib/libgambatte/include/filterinfo.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GAMBATTE_FILTERINFO_H +#define GAMBATTE_FILTERINFO_H + +#include + +namespace Gambatte { +struct FilterInfo { + std::string handle; + unsigned int outWidth; + unsigned int outHeight; +}; +} + +#endif diff --git a/src/lib/libgambatte/include/gambatte.h b/src/lib/libgambatte/include/gambatte.h new file mode 100644 index 00000000..e3c09c63 --- /dev/null +++ b/src/lib/libgambatte/include/gambatte.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GAMBATTE_H +#define GAMBATTE_H + +class CPU; + +#include "videoblitter.h" +#include "inputstate.h" +#include "inputstategetter.h" +#include "memoryinterface.h" +#include "filterinfo.h" +#include "int.h" +#include + +namespace Gambatte { +class GB { + CPU *const z80; + int stateNo; + + void loadState(const char *filepath, bool osdMessage); + +public: + GB(); + ~GB(); + bool load(bool forceDmg = false); + void save(); + + /** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer. + * There are 35112 stereo sound samples in a video frame. + * May run for uptil 2064 stereo samples too long. + * A stereo sample consists of two native endian 2s complement 16-bit PCM samples, + * with the left sample preceding the right one. Usually casting soundBuf to/from + * short* is OK and recommended. The reason for not using a short* in the interface + * is to avoid implementation defined behaviour without compromising performance. + * + * @param soundBuf buffer with space >= samples + 2064 + * @param samples number of stereo samples to produce + * @return actual number of samples produced + */ + unsigned runFor(uint32_t *soundBuf, unsigned samples); + void updateVideo(); + unsigned lyCounter(); + + void reset(); + void setVideoBlitter(VideoBlitter *vb); + void videoBufferChange(); + unsigned videoWidth() const; + unsigned videoHeight() const; + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32); + + void setVideoFilter(unsigned n); + std::vector filterInfo() const; + void setInputStateGetter(InputStateGetter *getInput); + void setMemoryInterface(MemoryInterface *memoryInterface); + + void set_savedir(const char *sdir); + bool isCgb() const; + void saveState(); + void loadState(); + void saveState(const char *filepath); + void loadState(const char *filepath); + void selectState(int n); + int currentState() const { return stateNo; } +}; +} + +#endif diff --git a/src/lib/libgambatte/include/inputstate.h b/src/lib/libgambatte/include/inputstate.h new file mode 100644 index 00000000..bdfec44f --- /dev/null +++ b/src/lib/libgambatte/include/inputstate.h @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GAMBATTE_INPUTSTATE_H +#define GAMBATTE_INPUTSTATE_H + +namespace Gambatte { +struct InputState { + unsigned joypadId; + bool startButton, selectButton, bButton, aButton; + bool dpadDown, dpadUp, dpadLeft, dpadRight; +}; +} + +#endif diff --git a/src/lib/libgambatte/include/inputstategetter.h b/src/lib/libgambatte/include/inputstategetter.h new file mode 100644 index 00000000..375dad5e --- /dev/null +++ b/src/lib/libgambatte/include/inputstategetter.h @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GAMBATTE_INPUTSTATEGETTER_H +#define GAMBATTE_INPUTSTATEGETTER_H + +namespace Gambatte { +class InputStateGetter { +public: + virtual ~InputStateGetter() {}; + virtual const InputState& operator()() = 0; +}; +} + +#endif diff --git a/src/lib/libgambatte/include/int.h b/src/lib/libgambatte/include/int.h new file mode 100644 index 00000000..e9f58661 --- /dev/null +++ b/src/lib/libgambatte/include/int.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GAMBATTE_INT_H +#define GAMBATTE_INT_H + +#ifdef HAVE_CSTDINT + +#include + +namespace Gambatte { +using std::uint_least32_t; +using std::uint_least16_t; +} + +#elif defined(HAVE_STDINT_H) + +#include + +namespace Gambatte { +using ::uint_least32_t; +using ::uint_least16_t; +} + +#else + +namespace Gambatte { +#ifdef CHAR_LEAST_32 +typedef unsigned char uint_least32_t; +#elif defined(SHORT_LEAST_32) +typedef unsigned short uint_least32_t; +#elif defined(INT_LEAST_32) +typedef unsigned uint_least32_t; +#else +typedef unsigned long uint_least32_t; +#endif + +#ifdef CHAR_LEAST_16 +typedef unsigned char uint_least16_t; +#else +typedef unsigned short uint_least16_t; +#endif +} + +#endif + +#endif diff --git a/src/lib/libgambatte/include/memoryinterface.h b/src/lib/libgambatte/include/memoryinterface.h new file mode 100644 index 00000000..69f18d06 --- /dev/null +++ b/src/lib/libgambatte/include/memoryinterface.h @@ -0,0 +1,25 @@ +#ifndef GAMBATTE_MEMORYINTERFACE_H +#define GAMBATTE_MEMORYINTERFACE_H + +namespace Gambatte { + +struct MemoryBuffer { + void *data; + unsigned size; +}; + +class MemoryInterface { +public: + virtual MemoryBuffer loadRomData() = 0; + virtual MemoryBuffer loadRamData() = 0; + virtual MemoryBuffer loadRtcData() = 0; + + virtual MemoryBuffer saveRamData(unsigned size) = 0; + virtual MemoryBuffer saveRtcData() = 0; + + virtual void joypWrite(bool /*p15*/, bool /*p14*/) {} +}; + +} + +#endif diff --git a/src/lib/libgambatte/include/videoblitter.h b/src/lib/libgambatte/include/videoblitter.h new file mode 100644 index 00000000..d2a9c335 --- /dev/null +++ b/src/lib/libgambatte/include/videoblitter.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GAMBATTE_VIDEOBLITTER_H +#define GAMBATTE_VIDEOBLITTER_H + +namespace Gambatte { + +struct PixelBuffer { + enum Format { RGB32, RGB16, UYVY }; + + void *pixels; + Format format; + unsigned pitch; + + PixelBuffer() : pixels(0), format(RGB32), pitch(0) {} +}; + +class VideoBlitter { +public: + virtual void setBufferDimensions(unsigned width, unsigned height) = 0; + virtual const PixelBuffer inBuffer() = 0; + virtual void blit() = 0; + virtual ~VideoBlitter() {} +}; + +} + +#endif diff --git a/src/lib/libgambatte/src/bitmap_font.cpp b/src/lib/libgambatte/src/bitmap_font.cpp new file mode 100644 index 00000000..b644c06f --- /dev/null +++ b/src/lib/libgambatte/src/bitmap_font.cpp @@ -0,0 +1,328 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/* + The following font bitmaps (static const unsigned char *_bits[]), only used + as data and included in this source file for convenience, are derived from + the Bitstream Vera Sans font, which is distributed under the following + copyright: + + Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera + is a trademark of Bitstream, Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of the fonts accompanying this license ("Fonts") and associated + documentation files (the "Font Software"), to reproduce and distribute the + Font Software, including without limitation the rights to use, copy, merge, + publish, distribute, and/or sell copies of the Font Software, and to permit + persons to whom the Font Software is furnished to do so, subject to the + following conditions: + + The above copyright and trademark notices and this permission notice shall + be included in all copies of one or more of the Font Software typefaces. + + The Font Software may be modified, altered, or added to, and in particular + the designs of glyphs or characters in the Fonts may be modified and + additional glyphs or characters may be added to the Fonts, only if the fonts + are renamed to names not containing either the words "Bitstream" or the word + "Vera". + + This License becomes null and void to the extent applicable to Fonts or Font + Software that has been modified and is distributed under the "Bitstream Vera" + names. + + The Font Software may be sold as part of a larger software package but no + copy of one or more of the Font Software typefaces may be sold by itself. + + THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM + OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM + OTHER DEALINGS IN THE FONT SOFTWARE. + + Except as contained in this notice, the names of Gnome, the Gnome + Foundation, and Bitstream Inc., shall not be used in advertising or + otherwise to promote the sale, use or other dealings in this Font Software + without prior written authorization from the Gnome Foundation or + Bitstream Inc., respectively. For further information, contact: fonts at + gnome dot org. +*/ + +#include "bitmap_font.h" + +static const unsigned char n0_bits[] = { 0x68, + 0x00, 0x1c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c }; + +static const unsigned char n1_bits[] = { 0x68, + 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e }; + +static const unsigned char n2_bits[] = { 0x68, + 0x00, 0x1c, 0x22, 0x20, 0x10, 0x08, 0x04, 0x3e }; + +static const unsigned char n3_bits[] = { 0x68, + 0x00, 0x1c, 0x22, 0x20, 0x1c, 0x20, 0x22, 0x1c }; + +static const unsigned char n4_bits[] = { 0x68, + 0x00, 0x18, 0x18, 0x14, 0x12, 0x3e, 0x10, 0x10 }; + +static const unsigned char n5_bits[] = { 0x68, + 0x00, 0x1e, 0x02, 0x1e, 0x20, 0x20, 0x20, 0x1e }; + +static const unsigned char n6_bits[] = { 0x68, + 0x00, 0x3c, 0x06, 0x02, 0x1e, 0x22, 0x22, 0x1c }; + +static const unsigned char n7_bits[] = { 0x68, + 0x00, 0x3e, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04 }; + +static const unsigned char n8_bits[] = { 0x68, + 0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c }; + +static const unsigned char n9_bits[] = { 0x68, + 0x00, 0x1c, 0x22, 0x22, 0x3c, 0x20, 0x30, 0x1e }; + +static const unsigned char A_bits[] = { 0x78, + 0x00, 0x08, 0x14, 0x14, 0x22, 0x3e, 0x22, 0x41 }; + +static const unsigned char a_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x1c, 0x20, 0x3c, 0x22, 0x3e }; + +static const unsigned char B_bits[] = { 0x78, + 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x22, 0x22, 0x1e }; + +static const unsigned char b_bits[] = { 0x68, + 0x02, 0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x1e }; + +static const unsigned char C_bits[] = { 0x88, + 0x00, 0x38, 0x44, 0x02, 0x02, 0x02, 0x44, 0x38 }; + +static const unsigned char c_bits[] = { 0x58, + 0x00, 0x00, 0x00, 0x1c, 0x02, 0x02, 0x02, 0x1c }; + +static const unsigned char D_bits[] = { 0x88, + 0x00, 0x3e, 0x62, 0x42, 0x42, 0x42, 0x62, 0x3e }; + +static const unsigned char d_bits[] = { 0x68, + 0x20, 0x20, 0x20, 0x3c, 0x22, 0x22, 0x22, 0x3c }; + +static const unsigned char E_bits[] = { 0x78, + 0x00, 0x3e, 0x02, 0x02, 0x3e, 0x02, 0x02, 0x3e }; + +static const unsigned char e_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x1c, 0x22, 0x3e, 0x02, 0x3c }; + +static const unsigned char F_bits[] = { 0x68, + 0x00, 0x1e, 0x02, 0x02, 0x1e, 0x02, 0x02, 0x02 }; + +static const unsigned char f_bits[] = { 0x48, + 0x0e, 0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02 }; + +static const unsigned char G_bits[] = { 0x88, + 0x00, 0x3c, 0x46, 0x02, 0x72, 0x42, 0x46, 0x3c }; + +static const unsigned char g_bits[] = { 0x6a, + 0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x1c }; + +static const unsigned char H_bits[] = { 0x88, + 0x00, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42 }; + +static const unsigned char h_bits[] = { 0x68, + 0x02, 0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x22 }; + +static const unsigned char I_bits[] = { 0x38, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }; + +static const unsigned char i_bits[] = { 0x28, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02 }; + +static const unsigned char J_bits[] = { 0x4a, + 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03 }; + +static const unsigned char j_bits[] = { 0x2a, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03 }; + +static const unsigned char K_bits[] = { 0x78, + 0x00, 0x22, 0x12, 0x0a, 0x06, 0x0a, 0x12, 0x22 }; + +static const unsigned char k_bits[] = { 0x58, + 0x02, 0x02, 0x02, 0x12, 0x0a, 0x06, 0x0a, 0x12 }; + +static const unsigned char L_bits[] = { 0x68, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3e }; + +static const unsigned char l_bits[] = { 0x28, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }; + +static const unsigned char M_bits[] = { 0x98, + 0x00, 0x00, 0x82, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xaa, 0x00, 0xaa, 0x00, + 0x92, 0x00, 0x82, 0x00 }; + +static const unsigned char m_bits[] = { 0xa8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x01, 0x22, 0x02, 0x22, 0x02, + 0x22, 0x02, 0x22, 0x02 }; + +static const unsigned char N_bits[] = { 0x88, + 0x00, 0x42, 0x46, 0x4a, 0x4a, 0x52, 0x62, 0x42 }; + +static const unsigned char n_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x1e, 0x22, 0x22, 0x22, 0x22 }; + +static const unsigned char O_bits[] = { 0x88, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x42, 0x66, 0x3c }; + +static const unsigned char o_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x1c }; + +static const unsigned char P_bits[] = { 0x78, + 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x02, 0x02, 0x02 }; + +static const unsigned char p_bits[] = { 0x6a, + 0x00, 0x00, 0x00, 0x1e, 0x22, 0x22, 0x22, 0x1e, 0x02, 0x02 }; + +static const unsigned char Q_bits[] = { 0x89, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x42, 0x26, 0x1c, 0x20 }; + +static const unsigned char q_bits[] = { 0x6a, + 0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x20 }; + +static const unsigned char R_bits[] = { 0x78, + 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x12, 0x22, 0x42 }; + +static const unsigned char r_bits[] = { 0x48, + 0x00, 0x00, 0x00, 0x0e, 0x02, 0x02, 0x02, 0x02 }; + +static const unsigned char S_bits[] = { 0x78, + 0x00, 0x1c, 0x22, 0x02, 0x1c, 0x20, 0x22, 0x1c }; + +static const unsigned char s_bits[] = { 0x58, + 0x00, 0x00, 0x00, 0x1e, 0x02, 0x1c, 0x10, 0x1e }; + +static const unsigned char T_bits[] = { 0x58, + 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; + +static const unsigned char t_bits[] = { 0x48, + 0x00, 0x02, 0x02, 0x0f, 0x02, 0x02, 0x02, 0x0e }; + +static const unsigned char U_bits[] = { 0x88, + 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c }; + +static const unsigned char u_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3c }; + +static const unsigned char V_bits[] = { 0x78, + 0x00, 0x41, 0x41, 0x22, 0x22, 0x14, 0x14, 0x08 }; + +static const unsigned char v_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08 }; + +static const unsigned char W_bits[] = { 0x98, + 0x00, 0x00, 0x11, 0x01, 0x11, 0x01, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, + 0x44, 0x00, 0x44, 0x00 }; + +static const unsigned char w_bits[] = { 0x88, + 0x00, 0x00, 0x00, 0x92, 0xaa, 0xaa, 0x44, 0x44 }; + +static const unsigned char X_bits[] = { 0x68, + 0x00, 0x21, 0x12, 0x0c, 0x0c, 0x0c, 0x12, 0x21 }; + +static const unsigned char x_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22 }; + +static const unsigned char Y_bits[] = { 0x78, + 0x00, 0x41, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08 }; + +static const unsigned char y_bits[] = { 0x6a, + 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x06 }; + +static const unsigned char Z_bits[] = { 0x68, + 0x00, 0x3f, 0x10, 0x08, 0x0c, 0x04, 0x02, 0x3f }; + +static const unsigned char z_bits[] = { 0x58, + 0x00, 0x00, 0x00, 0x1e, 0x10, 0x08, 0x04, 0x1e }; + +static const unsigned char SPC_bits[] = { 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +namespace BitmapFont { +const unsigned char *const font[] = { + 0, + n0_bits, n1_bits, n2_bits, n3_bits, n4_bits, n5_bits, n6_bits, n7_bits, n8_bits, n9_bits, + A_bits, B_bits, C_bits, D_bits, E_bits, F_bits, G_bits, H_bits, I_bits, J_bits, K_bits, L_bits, M_bits, + N_bits, O_bits, P_bits, Q_bits, R_bits, S_bits, T_bits, U_bits, V_bits, W_bits, X_bits, Y_bits, Z_bits, + a_bits, b_bits, c_bits, d_bits, e_bits, f_bits, g_bits, h_bits, i_bits, j_bits, k_bits, l_bits, m_bits, + n_bits, o_bits, p_bits, q_bits, r_bits, s_bits, t_bits, u_bits, v_bits, w_bits, x_bits, y_bits, z_bits, + SPC_bits +}; + +unsigned getWidth(const char *chars) { + unsigned w = 0; + + while (const int character = *chars++) { + w += *font[character] >> 4; + } + + return w; +} + +class Rgb32Fill { + const unsigned long color; + +public: + Rgb32Fill(unsigned long color) : color(color) {} + + void operator()(Gambatte::uint_least32_t *dest, unsigned /*pitch*/) { + *dest = color; + } +}; + +void print(Gambatte::uint_least32_t *dest, const unsigned pitch, const unsigned long color, const char *chars) { + print(dest, pitch, Rgb32Fill(color), chars); +} + +static void reverse(char *first, char *last) { + while (first < last) { + const int tmp = *first; + + *first = *last; + *last = tmp; + + ++first; + --last; + } +} + +void utoa(unsigned u, char *a) { + char *aa = a; + + while (u > 9) { + const unsigned div = u / 10; + const unsigned rem = u % 10; + + u = div; + *aa++ = rem + N0; + } + + *aa = u + N0; + + reverse(a, aa); +} +} diff --git a/src/lib/libgambatte/src/bitmap_font.h b/src/lib/libgambatte/src/bitmap_font.h new file mode 100644 index 00000000..8217cf61 --- /dev/null +++ b/src/lib/libgambatte/src/bitmap_font.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef BITMAP_FONT_H +#define BITMAP_FONT_H + +#include "int.h" + +namespace BitmapFont { +enum Char { + NUL, + N0, N1, N2, N3, N4, N5, N6, N7, N8, N9, + A, B, C, D, E, F, G, H, I, J, K, L, M, + N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + a, b, c, d, e, f, g, h, i, j, k, l, m, + n, o, p, q, r, s, t, u, v, w, x, y, z, + SPC +}; + +enum { HEIGHT = 10 }; +enum { MAX_WIDTH = 9 }; +enum { NUMBER_WIDTH = 6 }; + +unsigned getWidth(const char *chars); + +// struct Fill { void operator()(RandomAccessIterator dest, unsigned pitch) { fill pixels at dest } } +template +void print(RandomAccessIterator dest, unsigned pitch, Fill fill, const char *chars); + +void print(Gambatte::uint_least32_t *dest, unsigned pitch, unsigned long color, const char *chars); +void utoa(unsigned u, char *a); + +// --- INTERFACE END --- + + + +extern const unsigned char *const font[]; + +template +void print(RandomAccessIterator dest, const unsigned pitch, Fill fill, const char *chars) { + while (const int character = *chars++) { + RandomAccessIterator dst = dest; + const unsigned char *s = font[character]; + + const unsigned width = *s >> 4; + unsigned h = *s++ & 0xF; + + while (h--) { + RandomAccessIterator d = dst; + + unsigned line = *s++; + + if (width > 8) + line |= *s++ << 8; + + while (line) { + if (line & 1) + fill(d, pitch); + + line >>= 1; + ++d; + } + + dst += pitch; + } + + dest += width; + } +} +} + +#endif diff --git a/src/lib/libgambatte/src/colorconversion.cpp b/src/lib/libgambatte/src/colorconversion.cpp new file mode 100644 index 00000000..d76b0aee --- /dev/null +++ b/src/lib/libgambatte/src/colorconversion.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "colorconversion.h" +#include + +Rgb32ToUyvy::Rgb32ToUyvy() { +#ifdef WORDS_BIGENDIAN + const CacheUnit c = { 0, 128ul << 24 | 16ul << 16 | 128 << 8 | 16 }; +#else + const CacheUnit c = { 0, 16ul << 24 | 128ul << 16 | 16 << 8 | 128 }; +#endif + std::fill(cache, cache + cache_size, c); +} + +void Rgb32ToUyvy::operator()(const Gambatte::uint_least32_t *s, Gambatte::uint_least32_t *d, const unsigned w, unsigned h, const unsigned d_pitch) { + while (h--) { + for (const Gambatte::uint_least32_t *const ends = s + w; s != ends;) { + if ((cache[*s & cache_mask].rgb32 - *s) | (cache[*(s+1) & cache_mask].rgb32 - *(s+1))) { + cache[*s & cache_mask].rgb32 = *s; + cache[*(s+1) & cache_mask].rgb32 = *(s+1); + + const unsigned long r = (*s >> 16 & 0x000000FF) | (*(s+1) & 0x00FF0000); + const unsigned long g = (*s >> 8 & 0x000000FF) | (*(s+1) << 8 & 0x00FF0000); + const unsigned long b = (*s & 0x000000FF) | (*(s+1) << 16 & 0x00FF0000); + + const unsigned long y = r * 66 + g * 129 + b * 25 + (16 * 256 + 128) * 0x00010001ul; + const unsigned long u = b * 112 - r * 38 - g * 74 + (128 * 256 + 128) * 0x00010001ul; + const unsigned long v = r * 112 - g * 94 - b * 18 + (128 * 256 + 128) * 0x00010001ul; + +#ifdef WORDS_BIGENDIAN + *d++ = cache[*s & cache_mask].uyvy = (u << 16 & 0xFF000000) | (y << 8 & 0x00FF0000) | (v & 0x0000FF00) | (y >> 8 & 0x000000FF); + *d++ = cache[*(s+1) & cache_mask].uyvy = (u & 0xFF000000) | (y >> 8 & 0x00FF0000) | (v >> 16 & 0x0000FF00) | y >> 24; +#else + *d++ = cache[*s & cache_mask].uyvy = (y << 16 & 0xFF000000) | (v << 8 & 0x00FF0000) | (y & 0x0000FF00) | (u >> 8 & 0x000000FF); + *d++ = cache[*(s+1) & cache_mask].uyvy = (y & 0xFF000000) | (v >> 8 & 0x00FF0000) | (y >> 16 & 0x0000FF00) | u >> 24; +#endif + } else { + *d++ = cache[*s & cache_mask].uyvy; + *d++ = cache[*(s+1) & cache_mask].uyvy; + } + + s += 2; + } + + d += d_pitch - w; + } +} + +unsigned long rgb32ToUyvy(unsigned long rgb32) { + const unsigned r = rgb32 >> 16 & 0xFF; + const unsigned g = rgb32 >> 8 & 0xFF; + const unsigned b = rgb32 & 0xFF; + + const unsigned long y = (r * 66 + g * 129 + b * 25 + 16 * 256 + 128) >> 8; + const unsigned long u = (b * 112 - r * 38 - g * 74 + 128 * 256 + 128) >> 8; + const unsigned long v = (r * 112 - g * 94 - b * 18 + 128 * 256 + 128) >> 8; + +#ifdef WORDS_BIGENDIAN + return u << 24 | y << 16 | v << 8 | y; +#else + return y << 24 | v << 16 | y << 8 | u; +#endif +} + +void rgb32ToRgb16(const Gambatte::uint_least32_t *s, Gambatte::uint_least16_t *d, const unsigned w, unsigned h, const unsigned dstPitch) { + do { + unsigned n = w; + + do { + *d++ = (*s >> 8 & 0xF800) | (*s >> 5 & 0x07E0) | (*s >> 3 & 0x001F); + ++s; + } while (--n); + + d += dstPitch - w; + } while (--h); +} + +unsigned rgb32ToRgb16(const unsigned long rgb32) { + return (rgb32 >> 8 & 0xF800) | (rgb32 >> 5 & 0x07E0) | (rgb32 >> 3 & 0x001F); +} diff --git a/src/lib/libgambatte/src/colorconversion.h b/src/lib/libgambatte/src/colorconversion.h new file mode 100644 index 00000000..9323015e --- /dev/null +++ b/src/lib/libgambatte/src/colorconversion.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef COLORCONVERSION_H +#define COLORCONVERSION_H + +#include "int.h" +#include + +class Rgb32ToUyvy { + struct CacheUnit { + Gambatte::uint_least32_t rgb32; + Gambatte::uint_least32_t uyvy; + }; + + enum { cache_size = 0x100 }; + enum { cache_mask = cache_size - 1 }; + + CacheUnit cache[cache_size]; + +public: + Rgb32ToUyvy(); + void operator()(const Gambatte::uint_least32_t *s, Gambatte::uint_least32_t *d, unsigned w, unsigned h, unsigned dstPitch); +}; + +unsigned long rgb32ToUyvy(unsigned long rgb32); + +void rgb32ToRgb16(const Gambatte::uint_least32_t *s, Gambatte::uint_least16_t *d, unsigned w, unsigned h, unsigned dstPitch); +unsigned rgb32ToRgb16(unsigned long rgb32); + +#endif diff --git a/src/lib/libgambatte/src/cpu.cpp b/src/lib/libgambatte/src/cpu.cpp new file mode 100644 index 00000000..0520f95a --- /dev/null +++ b/src/lib/libgambatte/src/cpu.cpp @@ -0,0 +1,2850 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "cpu.h" +#include "memory.h" +#include "savestate.h" + +CPU::CPU() : +memory(Interrupter(SP, PC_, halted)), +cycleCounter_(0), +PC_(0x100), +SP(0xFFFE), +HF1(0xF), +HF2(0xF), +ZF(0), +CF(0x100), +A_(0x01), +B(0x00), +C(0x13), +D(0x00), +E(0xD8), +H(0x01), +L(0x4D), +skip(false), +halted(false) +{} + +void CPU::runFor(const unsigned long cycles) { + process(cycles/* << memory.isDoubleSpeed()*/); + + if (cycleCounter_ & 0x80000000) + cycleCounter_ = memory.resetCounters(cycleCounter_); +} + +void CPU::updateVideo() { + return memory.updateVideo(cycleCounter_); +} + +unsigned CPU::lyCounter() { + return memory.lyCounter(cycleCounter_); +} + +bool CPU::load(const bool forceDmg) { + bool tmp = memory.loadROM(forceDmg); + + return tmp; +} + +/*void CPU::halt() { + while (halted) { + const uint_fast32_t cycles = memory.next_eventtime - memory.CycleCounter; + memory.CycleCounter += cycles + ((4 - (cycles & 3)) & 3); + memory.event(); + } +}*/ + +//Push address of next instruction onto stack and then jump to interrupt address (0x40-0x60): +/*unsigned CPU::interrupt(const unsigned address, unsigned cycleCounter) { + if (halted && memory.isCgb()) + cycleCounter += 4; + + halted = false; + cycleCounter += 8; + memory.write(--SP, PC_ >> 8, cycleCounter); + cycleCounter += 4; + memory.write(--SP, PC_ & 0xFF, cycleCounter); + PC_ = address; + cycleCounter += 8; + + return cycleCounter; +}*/ + +// (HF2 & 0x200) == true means HF is set. +// (HF2 & 0x400) marks the subtract flag. +// (HF2 & 0x800) is set for inc/dec. +// (HF2 & 0x100) is set if there's a carry to add. +static void calcHF(const unsigned HF1, unsigned& HF2) { + unsigned arg1 = HF1 & 0xF; + unsigned arg2 = (HF2 & 0xF) + (HF2 >> 8 & 1); + + if (HF2 & 0x800) { + arg1 = arg2; + arg2 = 1; + } + + if (HF2 & 0x400) + arg1 -= arg2; + else + arg1 = (arg1 + arg2) << 5; + + HF2 |= arg1 & 0x200; +} + +#define F() (((HF2 & 0x600) | (CF & 0x100)) >> 4 | ((ZF & 0xFF) ? 0 : 0x80)) + +#define FROM_F(f_in) do { \ + unsigned from_f_var = f_in; \ +\ + ZF = ~from_f_var & 0x80; \ + HF2 = from_f_var << 4 & 0x600; \ + CF = from_f_var << 4 & 0x100; \ +} while (0) + +void CPU::setStatePtrs(SaveState &state) { + memory.setStatePtrs(state); +} + +void CPU::saveState(SaveState &state) { + cycleCounter_ = memory.saveState(state, cycleCounter_); + + calcHF(HF1, HF2); + + state.cpu.cycleCounter = cycleCounter_; + state.cpu.PC = PC_; + state.cpu.SP = SP; + state.cpu.A = A_; + state.cpu.B = B; + state.cpu.C = C; + state.cpu.D = D; + state.cpu.E = E; + state.cpu.F = F(); + state.cpu.H = H; + state.cpu.L = L; + state.cpu.skip = skip; + state.cpu.halted = halted; +} + +void CPU::loadState(const SaveState &state) { + memory.loadState(state, cycleCounter_); + + cycleCounter_ = state.cpu.cycleCounter; + PC_ = state.cpu.PC; + SP = state.cpu.SP; + A_ = state.cpu.A; + B = state.cpu.B; + C = state.cpu.C; + D = state.cpu.D; + E = state.cpu.E; + FROM_F(state.cpu.F); + H = state.cpu.H; + L = state.cpu.L; + skip = state.cpu.skip; + halted = state.cpu.halted; +} + +#define BC() ( B << 8 | C ) +#define DE() ( D << 8 | E ) +#define HL() ( H << 8 | L ) + +#define READ(dest, addr) do { (dest) = memory.read(addr, cycleCounter); cycleCounter += 4; } while (0) +// #define PC_READ(dest, addr) do { (dest) = memory.pc_read(addr, cycleCounter); cycleCounter += 4; } while (0) +#define PC_READ(dest) do { (dest) = memory.read(PC, cycleCounter); PC = (PC + 1) & 0xFFFF; cycleCounter += 4; } while (0) +#define FF_READ(dest, addr) do { (dest) = memory.ff_read(addr, cycleCounter); cycleCounter += 4; } while (0) + +#define WRITE(addr, data) do { memory.write(addr, data, cycleCounter); cycleCounter += 4; } while (0) +#define FF_WRITE(addr, data) do { memory.ff_write(addr, data, cycleCounter); cycleCounter += 4; } while (0) + +#define PC_MOD(data) do { PC = data; cycleCounter += 4; } while (0) + +#define PUSH(r1, r2) do { \ + SP = (SP - 1) & 0xFFFF; \ + WRITE(SP, (r1)); \ + SP = (SP - 1) & 0xFFFF; \ + WRITE(SP, (r2)); \ +} while (0) + +//CB OPCODES (Shifts, rotates and bits): +//swap r (8 cycles): +//Swap upper and lower nibbles of 8-bit register, reset flags, check zero flag: +#define swap_r(r) do { \ + CF = HF2 = 0; \ + ZF = (r); \ + (r) = (ZF << 4 | ZF >> 4) & 0xFF; \ +} while (0) + +//rlc r (8 cycles): +//Rotate 8-bit register left, store old bit7 in CF. Reset SF and HCF, Check ZF: +#define rlc_r(r) do { \ + CF = (r) << 1; \ + ZF = CF | CF >> 8; \ + (r) = ZF & 0xFF; \ + HF2 = 0; \ +} while (0) + +//rl r (8 cycles): +//Rotate 8-bit register left through CF, store old bit7 in CF, old CF value becomes bit0. Reset SF and HCF, Check ZF: +#define rl_r(r) do { \ + const unsigned rl_r_var_oldcf = CF >> 8 & 1; \ + CF = (r) << 1; \ + ZF = CF | rl_r_var_oldcf; \ + (r) = ZF & 0xFF; \ + HF2 = 0; \ +} while (0) + +//rrc r (8 cycles): +//Rotate 8-bit register right, store old bit0 in CF. Reset SF and HCF, Check ZF: +#define rrc_r(r) do { \ + ZF = (r); \ + CF = ZF << 8; \ + (r) = (ZF | CF) >> 1 & 0xFF; \ + HF2 = 0; \ +} while (0) + +//rr r (8 cycles): +//Rotate 8-bit register right through CF, store old bit0 in CF, old CF value becomes bit7. Reset SF and HCF, Check ZF: +#define rr_r(r) do { \ + const unsigned rr_r_var_oldcf = CF & 0x100; \ + CF = (r) << 8; \ + (r) = ZF = ((r) | rr_r_var_oldcf) >> 1; \ + HF2 = 0; \ +} while (0) + +//sla r (8 cycles): +//Shift 8-bit register left, store old bit7 in CF. Reset SF and HCF, Check ZF: +#define sla_r(r) do { \ + ZF = CF = (r) << 1; \ + (r) = ZF & 0xFF; \ + HF2 = 0; \ +} while (0) + +//sra r (8 cycles): +//Shift 8-bit register right, store old bit0 in CF. bit7=old bit7. Reset SF and HCF, Check ZF: +#define sra_r(r) do { \ + CF = (r) << 8; \ + ZF = (r) >> 1; \ + (r) = ZF | ((r) & 0x80); \ + HF2 = 0; \ +} while (0) + +//srl r (8 cycles): +//Shift 8-bit register right, store old bit0 in CF. Reset SF and HCF, Check ZF: +#define srl_r(r) do { \ + ZF = (r); \ + CF = (r) << 8; \ + ZF >>= 1; \ + (r) = ZF; \ + HF2 = 0; \ +} while (0) + +//bit n,r (8 cycles): +//bit n,(hl) (12 cycles): +//Test bitn in 8-bit value, check ZF, unset SF, set HCF: +#define bitn_u8(bitmask, u8) do { \ + ZF = (u8) & (bitmask); \ + HF2 = 0x200; \ +} while (0) + +#define bit0_u8(u8) bitn_u8(1, (u8)) +#define bit1_u8(u8) bitn_u8(2, (u8)) +#define bit2_u8(u8) bitn_u8(4, (u8)) +#define bit3_u8(u8) bitn_u8(8, (u8)) +#define bit4_u8(u8) bitn_u8(0x10, (u8)) +#define bit5_u8(u8) bitn_u8(0x20, (u8)) +#define bit6_u8(u8) bitn_u8(0x40, (u8)) +#define bit7_u8(u8) bitn_u8(0x80, (u8)) + +//set n,r (8 cycles): +//Set bitn of 8-bit register: +#define set0_r(r) ( (r) |= 0x1 ) +#define set1_r(r) ( (r) |= 0x2 ) +#define set2_r(r) ( (r) |= 0x4 ) +#define set3_r(r) ( (r) |= 0x8 ) +#define set4_r(r) ( (r) |= 0x10 ) +#define set5_r(r) ( (r) |= 0x20 ) +#define set6_r(r) ( (r) |= 0x40 ) +#define set7_r(r) ( (r) |= 0x80 ) + +//set n,(hl) (16 cycles): +//Set bitn of value at address stored in HL: +#define setn_mem_hl(n) do { \ + const unsigned setn_mem_hl_var_addr = HL(); \ + unsigned setn_mem_hl_var_tmp; \ +\ + READ(setn_mem_hl_var_tmp, setn_mem_hl_var_addr); \ + setn_mem_hl_var_tmp |= 1 << (n); \ +\ + WRITE(setn_mem_hl_var_addr, setn_mem_hl_var_tmp); \ +} while (0) + +//res n,r (8 cycles): +//Unset bitn of 8-bit register: +#define res0_r(r) ( (r) &= 0xFE ) +#define res1_r(r) ( (r) &= 0xFD ) +#define res2_r(r) ( (r) &= 0xFB ) +#define res3_r(r) ( (r) &= 0xF7 ) +#define res4_r(r) ( (r) &= 0xEF ) +#define res5_r(r) ( (r) &= 0xDF ) +#define res6_r(r) ( (r) &= 0xBF ) +#define res7_r(r) ( (r) &= 0x7F ) + +//res n,(hl) (16 cycles): +//Unset bitn of value at address stored in HL: +#define resn_mem_hl(n) do { \ + const unsigned resn_mem_hl_var_addr = HL(); \ + unsigned resn_mem_hl_var_tmp; \ +\ + READ(resn_mem_hl_var_tmp, resn_mem_hl_var_addr); \ + resn_mem_hl_var_tmp &= ~(1 << (n)); \ +\ + WRITE(resn_mem_hl_var_addr, resn_mem_hl_var_tmp); \ +} while (0) + + +//16-BIT LOADS: +//ld rr,nn (12 cycles) +//set rr to 16-bit value of next 2 bytes in memory +#define ld_rr_nn(r1, r2) do { \ + PC_READ(r2); \ + PC_READ(r1); \ +} while (0) + +//push rr (16 cycles): +//Push value of register pair onto stack: +#define push_rr(r1, r2) do { \ + PUSH(r1, r2); \ + cycleCounter += 4; \ +} while (0) + +//pop rr (12 cycles): +//Pop two bytes off stack into register pair: +#define pop_rr(r1, r2) do { \ + READ(r2, SP); \ + SP = (SP + 1) & 0xFFFF; \ + READ(r1, SP); \ + SP = (SP + 1) & 0xFFFF; \ +} while (0) + +//8-BIT ALU: +//add a,r (4 cycles): +//add a,(addr) (8 cycles): +//Add 8-bit value to A, check flags: +#define add_a_u8(u8) do { \ + HF1 = A; \ + HF2 = u8; \ + ZF = CF = A + HF2; \ + A = ZF & 0xFF; \ +} while (0) + +//adc a,r (4 cycles): +//adc a,(addr) (8 cycles): +//Add 8-bit value+CF to A, check flags: +#define adc_a_u8(u8) do { \ + HF1 = A; \ + HF2 = (CF & 0x100) | (u8); \ + ZF = CF = (CF >> 8 & 1) + (u8) + A; \ + A = ZF & 0xFF; \ +} while (0) + +//sub a,r (4 cycles): +//sub a,(addr) (8 cycles): +//Subtract 8-bit value from A, check flags: +#define sub_a_u8(u8) do { \ + HF1 = A; \ + HF2 = u8; \ + ZF = CF = A - HF2; \ + A = ZF & 0xFF; \ + HF2 |= 0x400; \ +} while (0) + +//sbc a,r (4 cycles): +//sbc a,(addr) (8 cycles): +//Subtract CF and 8-bit value from A, check flags: +#define sbc_a_u8(u8) do { \ + HF1 = A; \ + HF2 = 0x400 | (CF & 0x100) | (u8); \ + ZF = CF = A - ((CF >> 8) & 1) - (u8); \ + A = ZF & 0xFF; \ +} while (0) + +//and a,r (4 cycles): +//and a,(addr) (8 cycles): +//bitwise and 8-bit value into A, check flags: +#define and_a_u8(u8) do { \ + HF2 = 0x200; \ + CF = 0; \ + A &= (u8); \ + ZF = A; \ +} while (0) + +//or a,r (4 cycles): +//or a,(hl) (8 cycles): +//bitwise or 8-bit value into A, check flags: +#define or_a_u8(u8) do { \ + CF = HF2 = 0; \ + A |= (u8); \ + ZF = A; \ +} while (0) + +//xor a,r (4 cycles): +//xor a,(hl) (8 cycles): +//bitwise xor 8-bit value into A, check flags: +#define xor_a_u8(u8) do { \ + CF = HF2 = 0; \ + A ^= (u8); \ + ZF = A; \ +} while (0) + +//cp a,r (4 cycles): +//cp a,(addr) (8 cycles): +//Compare (subtract without storing result) 8-bit value to A, check flags: +#define cp_a_u8(u8) do { \ + HF1 = A; \ + HF2 = u8; \ + ZF = CF = A - HF2; \ + HF2 |= 0x400; \ +} while (0) + +//inc r (4 cycles): +//Increment value of 8-bit register, check flags except CF: +#define inc_r(r) do { \ + HF2 = (r) | 0x800; \ + ZF = (r) + 1; \ + (r) = ZF & 0xFF; \ +} while (0) + +//dec r (4 cycles): +//Decrement value of 8-bit register, check flags except CF: +#define dec_r(r) do { \ + HF2 = (r) | 0xC00; \ + ZF = (r) - 1; \ + (r) = ZF & 0xFF; \ +} while (0) + +//16-BIT ARITHMETIC +//add hl,rr (8 cycles): +//add 16-bit register to HL, check flags except ZF: +/*#define add_hl_rr(rh, rl) do { \ + L = HF1 = L + (rl); \ + HF1 >>= 8; \ + HF1 += H; \ + HF2 = (rh); \ + H = CF = HF1 + (rh); \ + cycleCounter += 4; \ +} while (0)*/ + +#define add_hl_rr(rh, rl) do { \ + CF = L + (rl); \ + L = CF & 0xFF; \ + HF1 = H; \ + HF2 = (CF & 0x100) | (rh); \ + CF = H + (CF >> 8) + (rh); \ + H = CF & 0xFF; \ + cycleCounter += 4; \ +} while (0) + +//inc rr (8 cycles): +//Increment 16-bit register: +#define inc_rr(rh, rl) do { \ + const unsigned inc_rr_var_tmp = (rl) + 1; \ + (rl) = inc_rr_var_tmp & 0xFF; \ + (rh) = ((rh) + (inc_rr_var_tmp >> 8)) & 0xFF; \ + cycleCounter += 4; \ +} while (0) + +//dec rr (8 cycles): +//Decrement 16-bit register: +#define dec_rr(rh, rl) do { \ + const unsigned dec_rr_var_tmp = (rl) - 1; \ + (rl) = dec_rr_var_tmp & 0xFF; \ + (rh) = ((rh) - (dec_rr_var_tmp >> 8 & 1)) & 0xFF; \ + cycleCounter += 4; \ +} while (0) + +#define sp_plus_n(sumout) do { \ + unsigned sp_plus_n_var_n; \ + PC_READ(sp_plus_n_var_n); \ + sp_plus_n_var_n = (sp_plus_n_var_n ^ 0x80) - 0x80; \ + \ + const unsigned sp_plus_n_var_sum = SP + sp_plus_n_var_n; \ + CF = SP ^ sp_plus_n_var_n ^ sp_plus_n_var_sum; \ + HF2 = CF << 5 & 0x200; \ + ZF = 1; \ + cycleCounter += 4; \ + (sumout) = sp_plus_n_var_sum & 0xFFFF; \ +} while (0) + +//JUMPS: +//jp nn (16 cycles): +//Jump to address stored in the next two bytes in memory: +#define jp_nn() do { \ + unsigned jp_nn_var_l, jp_nn_var_h; \ +\ + PC_READ(jp_nn_var_l); \ + PC_READ(jp_nn_var_h); \ +\ + PC_MOD(jp_nn_var_h << 8 | jp_nn_var_l); \ +} while (0) + +//jr disp (12 cycles): +//Jump to value of next (signed) byte in memory+current address: +#define jr_disp() do { \ + unsigned jr_disp_var_tmp; \ +\ + PC_READ(jr_disp_var_tmp); \ + jr_disp_var_tmp = (jr_disp_var_tmp ^ 0x80) - 0x80; \ +\ + PC_MOD((PC + jr_disp_var_tmp) & 0xFFFF); \ +} while (0) + +//CALLS, RESTARTS AND RETURNS: +//call nn (24 cycles): +//Push address of next instruction onto stack and then jump to address stored in next two bytes in memory: +#define call_nn() do { \ + PUSH(((PC + 2) >> 8) & 0xFF, (PC + 2) & 0xFF); \ + jp_nn(); \ +} while (0) + +//rst n (16 Cycles): +//Push present address onto stack, jump to address n (one of 00h,08h,10h,18h,20h,28h,30h,38h): +#define rst_n(n) do { \ + PUSH(PC >> 8, PC & 0xFF); \ + PC_MOD(n); \ +} while (0) + +//ret (16 cycles): +//Pop two bytes from the stack and jump to that address: +#define ret() do { \ + unsigned ret_var_l, ret_var_h; \ +\ + pop_rr(ret_var_h, ret_var_l); \ +\ + PC_MOD(ret_var_h << 8 | ret_var_l); \ +} while (0) + +void CPU::process(const unsigned long cycles) { + memory.setEndtime(cycleCounter_, cycles); + + unsigned char A = A_; + unsigned long cycleCounter = cycleCounter_; + + while (memory.isActive()) { + unsigned short PC = PC_; + + if (halted) { + if (cycleCounter < memory.getNextEventTime()) { + const unsigned long cycles = memory.getNextEventTime() - cycleCounter; + cycleCounter += cycles + ((4 - (cycles & 3)) & 3); + } + } else while (cycleCounter < memory.getNextEventTime()) { + unsigned char opcode; + + PC_READ(opcode); + + if (skip) { + PC = (PC - 1) & 0xFFFF; + skip = false; + } + + switch (opcode) { + //nop (4 cycles): + //Do nothing for 4 cycles: + case 0x00: + break; + case 0x01: + ld_rr_nn(B, C); + break; + case 0x02: + WRITE(BC(), A); + break; + case 0x03: + inc_rr(B, C); + break; + case 0x04: + inc_r(B); + break; + case 0x05: + dec_r(B); + break; + case 0x06: + PC_READ(B); + break; + + //rlca (4 cycles): + //Rotate 8-bit register A left, store old bit7 in CF. Reset SF, HCF, ZF: + case 0x07: + CF = A << 1; + A = (CF | CF >> 8) & 0xFF; + HF2 = 0; + ZF = 1; + break; + + //ld (nn),SP (20 cycles): + //Put value of SP into address given by next 2 bytes in memory: + case 0x08: + { + unsigned l, h; + + PC_READ(l); + PC_READ(h); + + const unsigned addr = h << 8 | l; + + WRITE(addr, SP & 0xFF); + WRITE((addr + 1) & 0xFFFF, SP >> 8); + } + break; + + case 0x09: + add_hl_rr(B, C); + break; + case 0x0A: + READ(A, BC()); + break; + case 0x0B: + dec_rr(B, C); + break; + case 0x0C: + inc_r(C); + break; + case 0x0D: + dec_r(C); + break; + case 0x0E: + PC_READ(C); + break; + + //rrca (4 cycles): + //Rotate 8-bit register A right, store old bit0 in CF. Reset SF, HCF, ZF: + case 0x0F: + CF = A << 8 | A; + A = CF >> 1 & 0xFF; + HF2 = 0; + ZF = 1; + break; + + //stop (4 cycles): + //Halt CPU and LCD display until button pressed: + case 0x10: + memory.speedChange(cycleCounter); + PC = (PC + 1) & 0xFFFF; + break; + case 0x11: + ld_rr_nn(D, E); + break; + case 0x12: + WRITE(DE(), A); + break; + case 0x13: + inc_rr(D, E); + break; + case 0x14: + inc_r(D); + break; + case 0x15: + dec_r(D); + break; + case 0x16: + PC_READ(D); + break; + + //rla (4 cycles): + //Rotate 8-bit register A left through CF, store old bit7 in CF, old CF value becomes bit0. Reset SF, HCF, ZF: + case 0x17: + { + const unsigned oldcf = CF >> 8 & 1; + CF = A << 1; + A = (CF | oldcf) & 0xFF; + } + + HF2 = 0; + ZF = 1; + break; + + case 0x18: + jr_disp(); + break; + case 0x19: + add_hl_rr(D, E); + break; + case 0x1A: + READ(A, DE()); + break; + case 0x1B: + dec_rr(D, E); + break; + case 0x1C: + inc_r(E); + break; + case 0x1D: + dec_r(E); + break; + case 0x1E: + PC_READ(E); + break; + + //rra (4 cycles): + //Rotate 8-bit register A right through CF, store old bit0 in CF, old CF value becomes bit7. Reset SF, HCF, ZF: + case 0x1F: + { + const unsigned oldcf = CF & 0x100; + CF = A << 8; + A = (A | oldcf) >> 1; + } + + HF2 = 0; + ZF = 1; + break; + + //jr nz,disp (12;8 cycles): + //Jump to value of next (signed) byte in memory+current address if ZF is unset: + case 0x20: + if (ZF & 0xFF) { + jr_disp(); + } else { + PC_MOD((PC + 1) & 0xFFFF); + } + break; + + case 0x21: + ld_rr_nn(H, L); + break; + + //ldi (hl),a (8 cycles): + //Put A into memory address in hl. Increment HL: + case 0x22: + { + unsigned addr = HL(); + + WRITE(addr, A); + + addr = (addr + 1) & 0xFFFF; + L = addr; + H = addr >> 8; + } + break; + + case 0x23: + inc_rr(H, L); + break; + case 0x24: + inc_r(H); + break; + case 0x25: + dec_r(H); + break; + case 0x26: + PC_READ(H); + break; + + + //daa (4 cycles): + //Adjust register A to correctly represent a BCD. Check ZF, HF and CF: + case 0x27: + /*{ + unsigned correction = ((A > 0x99) || (CF & 0x100)) ? 0x60 : 0x00; + + calcHF(HF1, HF2); + + if ((A & 0x0F) > 0x09 || (HF2 & 0x200)) + correction |= 0x06; + + HF1 = A; + HF2 = (HF2 & 0x400) | correction; + CF = (correction & 0x40) << 2; + A = (HF2 & 0x400) ? A - correction : (A + correction); + ZF = A; + }*/ + + calcHF(HF1, HF2); + + { + unsigned correction = (CF & 0x100) ? 0x60 : 0x00; + + if (HF2 & 0x200) + correction |= 0x06; + + if (!(HF2 &= 0x400)) { + if ((A & 0x0F) > 0x09) + correction |= 0x06; + + if (A > 0x99) + correction |= 0x60; + + A += correction; + } else + A -= correction; + + CF = correction << 2 & 0x100; + ZF = A; + A &= 0xFF; + } + break; + + //jr z,disp (12;8 cycles): + //Jump to value of next (signed) byte in memory+current address if ZF is set: + case 0x28: + if (ZF & 0xFF) { + PC_MOD((PC + 1) & 0xFFFF); + } else { + jr_disp(); + } + break; + + //add hl,hl (8 cycles): + //add 16-bit register HL to HL, check flags except ZF: + case 0x29: + add_hl_rr(H, L); + break; + + //ldi a,(hl) (8 cycles): + //Put value at address in hl into A. Increment HL: + case 0x2A: + { + unsigned addr = HL(); + + READ(A, addr); + + addr = (addr + 1) & 0xFFFF; + L = addr; + H = addr >> 8; + } + break; + + case 0x2B: + dec_rr(H, L); + break; + case 0x2C: + inc_r(L); + break; + case 0x2D: + dec_r(L); + break; + case 0x2E: + PC_READ(L); + break; + + //cpl (4 cycles): + //Complement register A. (Flip all bits), set SF and HCF: + case 0x2F: /*setSubtractFlag(); setHalfCarryFlag();*/ + HF2 = 0x600; + A ^= 0xFF; + break; + + //jr nc,disp (12;8 cycles): + //Jump to value of next (signed) byte in memory+current address if CF is unset: + case 0x30: + if (CF & 0x100) { + PC_MOD((PC + 1) & 0xFFFF); + } else { + jr_disp(); + } + break; + + //ld sp,nn (12 cycles) + //set sp to 16-bit value of next 2 bytes in memory + case 0x31: + { + unsigned l, h; + + PC_READ(l); + PC_READ(h); + + SP = h << 8 | l; + } + break; + + //ldd (hl),a (8 cycles): + //Put A into memory address in hl. Decrement HL: + case 0x32: + { + unsigned addr = HL(); + + WRITE(addr, A); + + addr = (addr - 1) & 0xFFFF; + L = addr; + H = addr >> 8; + } + break; + + case 0x33: + SP = (SP + 1) & 0xFFFF; + cycleCounter += 4; + break; + + //inc (hl) (12 cycles): + //Increment value at address in hl, check flags except CF: + case 0x34: + { + const unsigned addr = HL(); + + READ(HF2, addr); + ZF = HF2 + 1; + WRITE(addr, ZF & 0xFF); + HF2 |= 0x800; + } + break; + + //dec (hl) (12 cycles): + //Decrement value at address in hl, check flags except CF: + case 0x35: + { + const unsigned addr = HL(); + + READ(HF2, addr); + ZF = HF2 - 1; + WRITE(addr, ZF & 0xFF); + HF2 |= 0xC00; + } + break; + + //ld (hl),n (12 cycles): + //set memory at address in hl to value of next byte in memory: + case 0x36: + { + unsigned tmp; + + PC_READ(tmp); + WRITE(HL(), tmp); + } + break; + + //scf (4 cycles): + //Set CF. Unset SF and HCF: + case 0x37: /*setCarryFlag(); unsetSubtractFlag(); unsetHalfCarryFlag();*/ + CF = 0x100; + HF2 = 0; + break; + + //jr c,disp (12;8 cycles): + //Jump to value of next (signed) byte in memory+current address if CF is set: + case 0x38: //PC+=(((int8_t)memory.read(PC++))*CarryFlag()); Cycles(8); break; + if (CF & 0x100) { + jr_disp(); + } else { + PC_MOD((PC + 1) & 0xFFFF); + } + break; + + //add hl,sp (8 cycles): + //add SP to HL, check flags except ZF: + case 0x39: /*add_hl_rr(SP>>8, SP); break;*/ + CF = L + SP; + L = CF & 0xFF; + HF1 = H; + HF2 = ((CF ^ SP) & 0x100) | SP >> 8; + CF >>= 8; + CF += H; + H = CF & 0xFF; + cycleCounter += 4; + break; + + //ldd a,(hl) (8 cycles): + //Put value at address in hl into A. Decrement HL: + case 0x3A: + { + unsigned addr = HL(); + + A = memory.read(addr, cycleCounter); + cycleCounter += 4; + + addr = (addr - 1) & 0xFFFF; + L = addr; + H = addr >> 8; + } + break; + + case 0x3B: + SP = (SP - 1) & 0xFFFF; + cycleCounter += 4; + break; + case 0x3C: + inc_r(A); + break; + case 0x3D: + dec_r(A); + break; + case 0x3E: + PC_READ(A); + break; + + //ccf (4 cycles): + //Complement CF (unset if set vv.) Unset SF and HCF. + case 0x3F: /*complementCarryFlag(); unsetSubtractFlag(); unsetHalfCarryFlag();*/ + CF ^= 0x100; + HF2 = 0; + break; + + //ld r,r (4 cycles):next_irqEventTime + //ld r,(r) (8 cycles): + case 0x40: + B = B; + break; + case 0x41: + B = C; + break; + case 0x42: + B = D; + break; + case 0x43: + B = E; + break; + case 0x44: + B = H; + break; + case 0x45: + B = L; + break; + case 0x46: + READ(B, HL()); + break; + case 0x47: + B = A; + break; + case 0x48: + C = B; + break; + case 0x49: + C = C; + break; + case 0x4A: + C = D; + break; + case 0x4B: + C = E; + break; + case 0x4C: + C = H; + break; + case 0x4D: + C = L; + break; + case 0x4E: + READ(C, HL()); + break; + case 0x4F: + C = A; + break; + case 0x50: + D = B; + break; + case 0x51: + D = C; + break; + case 0x52: + D = D; + break; + case 0x53: + D = E; + break; + case 0x54: + D = H; + break; + case 0x55: + D = L; + break; + case 0x56: + READ(D, HL()); + break; + case 0x57: + D = A; + break; + case 0x58: + E = B; + break; + case 0x59: + E = C; + break; + case 0x5A: + E = D; + break; + case 0x5B: + E = E; + break; + case 0x5C: + E = H; + break; + case 0x5D: + E = L; + break; + case 0x5E: + READ(E, HL()); + break; + case 0x5F: + E = A; + break; + case 0x60: + H = B; + break; + case 0x61: + H = C; + break; + case 0x62: + H = D; + break; + case 0x63: + H = E; + break; + case 0x64: + H = H; + break; + case 0x65: + H = L; + break; + case 0x66: + READ(H, HL()); + break; + case 0x67: + H = A; + break; + case 0x68: + L = B; + break; + case 0x69: + L = C; + break; + case 0x6A: + L = D; + break; + case 0x6B: + L = E; + break; + case 0x6C: + L = H; + break; + case 0x6D: + L = L; + break; + case 0x6E: + READ(L, HL()); + break; + case 0x6F: + L = A; + break; + case 0x70: + WRITE(HL(), B); + break; + case 0x71: + WRITE(HL(), C); + break; + case 0x72: + WRITE(HL(), D); + break; + case 0x73: + WRITE(HL(), E); + break; + case 0x74: + WRITE(HL(), H); + break; + case 0x75: + WRITE(HL(), L); + break; + + //halt (4 cycles): + case 0x76: +// printf("halt\n"); + if (memory.getIME()/* || memory.next_eitime*/) { + halted = 1; + + if (cycleCounter < memory.getNextEventTime()) { + const unsigned long cycles = memory.getNextEventTime() - cycleCounter; + cycleCounter += cycles + ((4 - (cycles & 3)) & 3); + } + } else { + if ((memory.ff_read(0xFF0F, cycleCounter) & memory.ff_read(0xFFFF, cycleCounter)) & 0x1F) { + if (memory.isCgb()) + cycleCounter += 8; //two nops. + else + skip = true; + } else { + memory.schedule_unhalt(); + halted = 1; + + if (cycleCounter < memory.getNextEventTime()) { + const unsigned long cycles = memory.getNextEventTime() - cycleCounter; + cycleCounter += cycles + ((4 - (cycles & 3)) & 3); + } + } + } + break; + case 0x77: + WRITE(HL(), A); + break; + case 0x78: + A = B; + break; + case 0x79: + A = C; + break; + case 0x7A: + A = D; + break; + case 0x7B: + A = E; + break; + case 0x7C: + A = H; + break; + case 0x7D: + A = L; + break; + case 0x7E: + READ(A, HL()); + break; + case 0x7F: + A = A; + break; + case 0x80: + add_a_u8(B); + break; + case 0x81: + add_a_u8(C); + break; + case 0x82: + add_a_u8(D); + break; + case 0x83: + add_a_u8(E); + break; + case 0x84: + add_a_u8(H); + break; + case 0x85: + add_a_u8(L); + break; + case 0x86: + { + unsigned data; + + READ(data, HL()); + + add_a_u8(data); + } + break; + case 0x87: + add_a_u8(A); + break; + case 0x88: + adc_a_u8(B); + break; + case 0x89: + adc_a_u8(C); + break; + case 0x8A: + adc_a_u8(D); + break; + case 0x8B: + adc_a_u8(E); + break; + case 0x8C: + adc_a_u8(H); + break; + case 0x8D: + adc_a_u8(L); + break; + case 0x8E: + { + unsigned data; + + READ(data, HL()); + + adc_a_u8(data); + } + break; + case 0x8F: + adc_a_u8(A); + break; + case 0x90: + sub_a_u8(B); + break; + case 0x91: + sub_a_u8(C); + break; + case 0x92: + sub_a_u8(D); + break; + case 0x93: + sub_a_u8(E); + break; + case 0x94: + sub_a_u8(H); + break; + case 0x95: + sub_a_u8(L); + break; + case 0x96: + { + unsigned data; + + READ(data, HL()); + + sub_a_u8(data); + } + break; + //A-A is always 0: + case 0x97: + HF2 = 0x400; + CF = ZF = A = 0; + break; + case 0x98: + sbc_a_u8(B); + break; + case 0x99: + sbc_a_u8(C); + break; + case 0x9A: + sbc_a_u8(D); + break; + case 0x9B: + sbc_a_u8(E); + break; + case 0x9C: + sbc_a_u8(H); + break; + case 0x9D: + sbc_a_u8(L); + break; + case 0x9E: + { + unsigned data; + + READ(data, HL()); + + sbc_a_u8(data); + } + break; + case 0x9F: + sbc_a_u8(A); + break; + case 0xA0: + and_a_u8(B); + break; + case 0xA1: + and_a_u8(C); + break; + case 0xA2: + and_a_u8(D); + break; + case 0xA3: + and_a_u8(E); + break; + case 0xA4: + and_a_u8(H); + break; + case 0xA5: + and_a_u8(L); + break; + case 0xA6: + { + unsigned data; + + READ(data, HL()); + + and_a_u8(data); + } + break; + //A&A will always be A: + case 0xA7: + ZF = A; + CF = 0; + HF2 = 0x200; + break; + case 0xA8: + xor_a_u8(B); + break; + case 0xA9: + xor_a_u8(C); + break; + case 0xAA: + xor_a_u8(D); + break; + case 0xAB: + xor_a_u8(E); + break; + case 0xAC: + xor_a_u8(H); + break; + case 0xAD: + xor_a_u8(L); + break; + case 0xAE: + { + unsigned data; + + READ(data, HL()); + + xor_a_u8(data); + } + break; + //A^A will always be 0: + case 0xAF: + CF = HF2 = ZF = A = 0; + break; + case 0xB0: + or_a_u8(B); + break; + case 0xB1: + or_a_u8(C); + break; + case 0xB2: + or_a_u8(D); + break; + case 0xB3: + or_a_u8(E); + break; + case 0xB4: + or_a_u8(H); + break; + case 0xB5: + or_a_u8(L); + break; + case 0xB6: + { + unsigned data; + + READ(data, HL()); + + or_a_u8(data); + } + break; + //A|A will always be A: + case 0xB7: + ZF = A; + HF2 = CF = 0; + break; + case 0xB8: + cp_a_u8(B); + break; + case 0xB9: + cp_a_u8(C); + break; + case 0xBA: + cp_a_u8(D); + break; + case 0xBB: + cp_a_u8(E); + break; + case 0xBC: + cp_a_u8(H); + break; + case 0xBD: + cp_a_u8(L); + break; + case 0xBE: + { + unsigned data; + + READ(data, HL()); + + cp_a_u8(data); + } + break; + //A always equals A: + case 0xBF: + CF = ZF = 0; + HF2 = 0x400; + break; + + //ret nz (20;8 cycles): + //Pop two bytes from the stack and jump to that address, if ZF is unset: + case 0xC0: + cycleCounter += 4; + + if (ZF & 0xFF) { + ret(); + } + break; + + case 0xC1: + pop_rr(B, C); + break; + + //jp nz,nn (16;12 cycles): + //Jump to address stored in next two bytes in memory if ZF is unset: + case 0xC2: + if (ZF & 0xFF) { + jp_nn(); + } else { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } + break; + + case 0xC3: + jp_nn(); + break; + + //call nz,nn (24;12 cycles): + //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if ZF is unset: + case 0xC4: + if (ZF & 0xFF) { + call_nn(); + } else { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } + break; + + case 0xC5: + push_rr(B, C); + break; + case 0xC6: + { + unsigned data; + + PC_READ(data); + + add_a_u8(data); + } + break; + case 0xC7: + rst_n(0x00); + break; + + //ret z (20;8 cycles): + //Pop two bytes from the stack and jump to that address, if ZF is set: + case 0xC8: + cycleCounter += 4; + + if (!(ZF & 0xFF)) { + ret(); + } + + break; + + //ret (16 cycles): + //Pop two bytes from the stack and jump to that address: + case 0xC9: + ret(); + break; + + //jp z,nn (16;12 cycles): + //Jump to address stored in next two bytes in memory if ZF is set: + case 0xCA: + if (ZF & 0xFF) { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } else { + jp_nn(); + } + break; + + + //CB OPCODES (Shifts, rotates and bits): + case 0xCB: + PC_READ(opcode); + + switch (opcode) { + case 0x00: + rlc_r(B); + break; + case 0x01: + rlc_r(C); + break; + case 0x02: + rlc_r(D); + break; + case 0x03: + rlc_r(E); + break; + case 0x04: + rlc_r(H); + break; + case 0x05: + rlc_r(L); + break; + //rlc (hl) (16 cycles): + //Rotate 8-bit value stored at address in HL left, store old bit7 in CF. Reset SF and HCF. Check ZF: + case 0x06: + { + const unsigned addr = HL(); + + READ(CF, addr); + CF <<= 1; + + ZF = CF | (CF >> 8); + + WRITE(addr, ZF & 0xFF); + + HF2 = 0; + } + break; + case 0x07: + rlc_r(A); + break; + case 0x08: + rrc_r(B); + break; + case 0x09: + rrc_r(C); + break; + case 0x0A: + rrc_r(D); + break; + case 0x0B: + rrc_r(E); + break; + case 0x0C: + rrc_r(H); + break; + case 0x0D: + rrc_r(L); + break; + //rrc (hl) (16 cycles): + //Rotate 8-bit value stored at address in HL right, store old bit0 in CF. Reset SF and HCF. Check ZF: + case 0x0E: + { + const unsigned addr = HL(); + + READ(ZF, addr); + + CF = ZF << 8; + + WRITE(addr, (ZF | CF) >> 1 & 0xFF); + + HF2 = 0; + } + break; + case 0x0F: + rrc_r(A); + break; + case 0x10: + rl_r(B); + break; + case 0x11: + rl_r(C); + break; + case 0x12: + rl_r(D); + break; + case 0x13: + rl_r(E); + break; + case 0x14: + rl_r(H); + break; + case 0x15: + rl_r(L); + break; + //rl (hl) (16 cycles): + //Rotate 8-bit value stored at address in HL left thorugh CF, store old bit7 in CF, old CF value becoms bit0. Reset SF and HCF. Check ZF: + case 0x16: + { + const unsigned addr = HL(); + const unsigned oldcf = CF >> 8 & 1; + + READ(CF, addr); + CF <<= 1; + + ZF = CF | oldcf; + + WRITE(addr, ZF & 0xFF); + + HF2 = 0; + } + break; + case 0x17: + rl_r(A); + break; + case 0x18: + rr_r(B); + break; + case 0x19: + rr_r(C); + break; + case 0x1A: + rr_r(D); + break; + case 0x1B: + rr_r(E); + break; + case 0x1C: + rr_r(H); + break; + case 0x1D: + rr_r(L); + break; + //rr (hl) (16 cycles): + //Rotate 8-bit value stored at address in HL right thorugh CF, store old bit0 in CF, old CF value becoms bit7. Reset SF and HCF. Check ZF: + case 0x1E: + { + const unsigned addr = HL(); + + READ(ZF, addr); + + const unsigned oldcf = CF & 0x100; + CF = ZF << 8; + ZF = (ZF | oldcf) >> 1; + + WRITE(addr, ZF); + + HF2 = 0; + } + break; + case 0x1F: + rr_r(A); + break; + case 0x20: + sla_r(B); + break; + case 0x21: + sla_r(C); + break; + case 0x22: + sla_r(D); + break; + case 0x23: + sla_r(E); + break; + case 0x24: + sla_r(H); + break; + case 0x25: + sla_r(L); + break; + //sla (hl) (16 cycles): + //Shift 8-bit value stored at address in HL left, store old bit7 in CF. Reset SF and HCF. Check ZF: + case 0x26: + { + const unsigned addr = HL(); + + READ(CF, addr); + CF <<= 1; + + ZF = CF; + + WRITE(addr, ZF & 0xFF); + + HF2 = 0; + } + break; + case 0x27: + sla_r(A); + break; + case 0x28: + sra_r(B); + break; + case 0x29: + sra_r(C); + break; + case 0x2A: + sra_r(D); + break; + case 0x2B: + sra_r(E); + break; + case 0x2C: + sra_r(H); + break; + case 0x2D: + sra_r(L); + break; + //sra (hl) (16 cycles): + //Shift 8-bit value stored at address in HL right, store old bit0 in CF, bit7=old bit7. Reset SF and HCF. Check ZF: + case 0x2E: + { + const unsigned addr = HL(); + + READ(CF, addr); + + ZF = CF >> 1; + + WRITE(addr, ZF | (CF & 0x80)); + + CF <<= 8; + HF2 = 0; + } + break; + case 0x2F: + sra_r(A); + break; + case 0x30: + swap_r(B); + break; + case 0x31: + swap_r(C); + break; + case 0x32: + swap_r(D); + break; + case 0x33: + swap_r(E); + break; + case 0x34: + swap_r(H); + break; + case 0x35: + swap_r(L); + break; + //swap (hl) (16 cycles): + //Swap upper and lower nibbles of 8-bit value stored at address in HL, reset flags, check zero flag: + case 0x36: + { + const unsigned addr = HL(); + + READ(ZF, addr); + + WRITE(addr, (ZF << 4 | ZF >> 4) & 0xFF); + + CF = HF2 = 0; + } + break; + case 0x37: + swap_r(A); + break; + case 0x38: + srl_r(B); + break; + case 0x39: + srl_r(C); + break; + case 0x3A: + srl_r(D); + break; + case 0x3B: + srl_r(E); + break; + case 0x3C: + srl_r(H); + break; + case 0x3D: + srl_r(L); + break; + //srl (hl) (16 cycles): + //Shift 8-bit value stored at address in HL right, store old bit0 in CF. Reset SF and HCF. Check ZF: + case 0x3E: + { + const unsigned addr = HL(); + + READ(CF, addr); + + ZF = CF >> 1; + + WRITE(addr, ZF); + + CF <<= 8; + HF2 = 0; + } + break; + case 0x3F: + srl_r(A); + break; + case 0x40: + bit0_u8(B); + break; + case 0x41: + bit0_u8(C); + break; + case 0x42: + bit0_u8(D); + break; + case 0x43: + bit0_u8(E); + break; + case 0x44: + bit0_u8(H); + break; + case 0x45: + bit0_u8(L); + break; + case 0x46: + { + unsigned data; + + READ(data, HL()); + + bit0_u8(data); + } + break; + case 0x47: + bit0_u8(A); + break; + case 0x48: + bit1_u8(B); + break; + case 0x49: + bit1_u8(C); + break; + case 0x4A: + bit1_u8(D); + break; + case 0x4B: + bit1_u8(E); + break; + case 0x4C: + bit1_u8(H); + break; + case 0x4D: + bit1_u8(L); + break; + case 0x4E: + { + unsigned data; + + READ(data, HL()); + + bit1_u8(data); + } + break; + case 0x4F: + bit1_u8(A); + break; + case 0x50: + bit2_u8(B); + break; + case 0x51: + bit2_u8(C); + break; + case 0x52: + bit2_u8(D); + break; + case 0x53: + bit2_u8(E); + break; + case 0x54: + bit2_u8(H); + break; + case 0x55: + bit2_u8(L); + break; + case 0x56: + { + unsigned data; + + READ(data, HL()); + + bit2_u8(data); + } + break; + case 0x57: + bit2_u8(A); + break; + case 0x58: + bit3_u8(B); + break; + case 0x59: + bit3_u8(C); + break; + case 0x5A: + bit3_u8(D); + break; + case 0x5B: + bit3_u8(E); + break; + case 0x5C: + bit3_u8(H); + break; + case 0x5D: + bit3_u8(L); + break; + case 0x5E: + { + unsigned data; + + READ(data, HL()); + + bit3_u8(data); + } + break; + case 0x5F: + bit3_u8(A); + break; + case 0x60: + bit4_u8(B); + break; + case 0x61: + bit4_u8(C); + break; + case 0x62: + bit4_u8(D); + break; + case 0x63: + bit4_u8(E); + break; + case 0x64: + bit4_u8(H); + break; + case 0x65: + bit4_u8(L); + break; + case 0x66: + { + unsigned data; + + READ(data, HL()); + + bit4_u8(data); + } + break; + case 0x67: + bit4_u8(A); + break; + case 0x68: + bit5_u8(B); + break; + case 0x69: + bit5_u8(C); + break; + case 0x6A: + bit5_u8(D); + break; + case 0x6B: + bit5_u8(E); + break; + case 0x6C: + bit5_u8(H); + break; + case 0x6D: + bit5_u8(L); + break; + case 0x6E: + { + unsigned data; + + READ(data, HL()); + + bit5_u8(data); + } + break; + case 0x6F: + bit5_u8(A); + break; + case 0x70: + bit6_u8(B); + break; + case 0x71: + bit6_u8(C); + break; + case 0x72: + bit6_u8(D); + break; + case 0x73: + bit6_u8(E); + break; + case 0x74: + bit6_u8(H); + break; + case 0x75: + bit6_u8(L); + break; + case 0x76: + { + unsigned data; + + READ(data, HL()); + + bit6_u8(data); + } + break; + case 0x77: + bit6_u8(A); + break; + case 0x78: + bit7_u8(B); + break; + case 0x79: + bit7_u8(C); + break; + case 0x7A: + bit7_u8(D); + break; + case 0x7B: + bit7_u8(E); + break; + case 0x7C: + bit7_u8(H); + break; + case 0x7D: + bit7_u8(L); + break; + case 0x7E: + { + unsigned data; + + READ(data, HL()); + + bit7_u8(data); + } + break; + case 0x7F: + bit7_u8(A); + break; + case 0x80: + res0_r(B); + break; + case 0x81: + res0_r(C); + break; + case 0x82: + res0_r(D); + break; + case 0x83: + res0_r(E); + break; + case 0x84: + res0_r(H); + break; + case 0x85: + res0_r(L); + break; + case 0x86: + resn_mem_hl(0); + break; + case 0x87: + res0_r(A); + break; + case 0x88: + res1_r(B); + break; + case 0x89: + res1_r(C); + break; + case 0x8A: + res1_r(D); + break; + case 0x8B: + res1_r(E); + break; + case 0x8C: + res1_r(H); + break; + case 0x8D: + res1_r(L); + break; + case 0x8E: + resn_mem_hl(1); + break; + case 0x8F: + res1_r(A); + break; + case 0x90: + res2_r(B); + break; + case 0x91: + res2_r(C); + break; + case 0x92: + res2_r(D); + break; + case 0x93: + res2_r(E); + break; + case 0x94: + res2_r(H); + break; + case 0x95: + res2_r(L); + break; + case 0x96: + resn_mem_hl(2); + break; + case 0x97: + res2_r(A); + break; + case 0x98: + res3_r(B); + break; + case 0x99: + res3_r(C); + break; + case 0x9A: + res3_r(D); + break; + case 0x9B: + res3_r(E); + break; + case 0x9C: + res3_r(H); + break; + case 0x9D: + res3_r(L); + break; + case 0x9E: + resn_mem_hl(3); + break; + case 0x9F: + res3_r(A); + break; + case 0xA0: + res4_r(B); + break; + case 0xA1: + res4_r(C); + break; + case 0xA2: + res4_r(D); + break; + case 0xA3: + res4_r(E); + break; + case 0xA4: + res4_r(H); + break; + case 0xA5: + res4_r(L); + break; + case 0xA6: + resn_mem_hl(4); + break; + case 0xA7: + res4_r(A); + break; + case 0xA8: + res5_r(B); + break; + case 0xA9: + res5_r(C); + break; + case 0xAA: + res5_r(D); + break; + case 0xAB: + res5_r(E); + break; + case 0xAC: + res5_r(H); + break; + case 0xAD: + res5_r(L); + break; + case 0xAE: + resn_mem_hl(5); + break; + case 0xAF: + res5_r(A); + break; + case 0xB0: + res6_r(B); + break; + case 0xB1: + res6_r(C); + break; + case 0xB2: + res6_r(D); + break; + case 0xB3: + res6_r(E); + break; + case 0xB4: + res6_r(H); + break; + case 0xB5: + res6_r(L); + break; + case 0xB6: + resn_mem_hl(6); + break; + case 0xB7: + res6_r(A); + break; + case 0xB8: + res7_r(B); + break; + case 0xB9: + res7_r(C); + break; + case 0xBA: + res7_r(D); + break; + case 0xBB: + res7_r(E); + break; + case 0xBC: + res7_r(H); + break; + case 0xBD: + res7_r(L); + break; + case 0xBE: + resn_mem_hl(7); + break; + case 0xBF: + res7_r(A); + break; + case 0xC0: + set0_r(B); + break; + case 0xC1: + set0_r(C); + break; + case 0xC2: + set0_r(D); + break; + case 0xC3: + set0_r(E); + break; + case 0xC4: + set0_r(H); + break; + case 0xC5: + set0_r(L); + break; + case 0xC6: + setn_mem_hl(0); + break; + case 0xC7: + set0_r(A); + break; + case 0xC8: + set1_r(B); + break; + case 0xC9: + set1_r(C); + break; + case 0xCA: + set1_r(D); + break; + case 0xCB: + set1_r(E); + break; + case 0xCC: + set1_r(H); + break; + case 0xCD: + set1_r(L); + break; + case 0xCE: + setn_mem_hl(1); + break; + case 0xCF: + set1_r(A); + break; + case 0xD0: + set2_r(B); + break; + case 0xD1: + set2_r(C); + break; + case 0xD2: + set2_r(D); + break; + case 0xD3: + set2_r(E); + break; + case 0xD4: + set2_r(H); + break; + case 0xD5: + set2_r(L); + break; + case 0xD6: + setn_mem_hl(2); + break; + case 0xD7: + set2_r(A); + break; + case 0xD8: + set3_r(B); + break; + case 0xD9: + set3_r(C); + break; + case 0xDA: + set3_r(D); + break; + case 0xDB: + set3_r(E); + break; + case 0xDC: + set3_r(H); + break; + case 0xDD: + set3_r(L); + break; + case 0xDE: + setn_mem_hl(3); + break; + case 0xDF: + set3_r(A); + break; + case 0xE0: + set4_r(B); + break; + case 0xE1: + set4_r(C); + break; + case 0xE2: + set4_r(D); + break; + case 0xE3: + set4_r(E); + break; + case 0xE4: + set4_r(H); + break; + case 0xE5: + set4_r(L); + break; + case 0xE6: + setn_mem_hl(4); + break; + case 0xE7: + set4_r(A); + break; + case 0xE8: + set5_r(B); + break; + case 0xE9: + set5_r(C); + break; + case 0xEA: + set5_r(D); + break; + case 0xEB: + set5_r(E); + break; + case 0xEC: + set5_r(H); + break; + case 0xED: + set5_r(L); + break; + case 0xEE: + setn_mem_hl(5); + break; + case 0xEF: + set5_r(A); + break; + case 0xF0: + set6_r(B); + break; + case 0xF1: + set6_r(C); + break; + case 0xF2: + set6_r(D); + break; + case 0xF3: + set6_r(E); + break; + case 0xF4: + set6_r(H); + break; + case 0xF5: + set6_r(L); + break; + case 0xF6: + setn_mem_hl(6); + break; + case 0xF7: + set6_r(A); + break; + case 0xF8: + set7_r(B); + break; + case 0xF9: + set7_r(C); + break; + case 0xFA: + set7_r(D); + break; + case 0xFB: + set7_r(E); + break; + case 0xFC: + set7_r(H); + break; + case 0xFD: + set7_r(L); + break; + case 0xFE: + setn_mem_hl(7); + break; + case 0xFF: + set7_r(A); + break; +// default: break; + } + break; + + + //call z,nn (24;12 cycles): + //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if ZF is set: + case 0xCC: + if (ZF & 0xFF) { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } else { + call_nn(); + } + break; + + case 0xCD: + call_nn(); + break; + case 0xCE: + { + unsigned data; + + PC_READ(data); + + adc_a_u8(data); + } + break; + case 0xCF: + rst_n(0x08); + break; + + //ret nc (20;8 cycles): + //Pop two bytes from the stack and jump to that address, if CF is unset: + case 0xD0: + cycleCounter += 4; + + if (!(CF & 0x100)) { + ret(); + } + + break; + + case 0xD1: + pop_rr(D, E); + break; + + //jp nc,nn (16;12 cycles): + //Jump to address stored in next two bytes in memory if CF is unset: + case 0xD2: + if (CF & 0x100) { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } else { + jp_nn(); + } + break; + + case 0xD3: /*doesn't exist*/ + break; + + //call nc,nn (24;12 cycles): + //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if CF is unset: + case 0xD4: + if (CF & 0x100) { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } else { + call_nn(); + } + break; + + case 0xD5: + push_rr(D, E); + break; + case 0xD6: + { + unsigned data; + + PC_READ(data); + + sub_a_u8(data); + } + break; + case 0xD7: + rst_n(0x10); + break; + + //ret c (20;8 cycles): + //Pop two bytes from the stack and jump to that address, if CF is set: + case 0xD8: + cycleCounter += 4; + + if (CF & 0x100) { + ret(); + } + + break; + + //reti (16 cycles): + //Pop two bytes from the stack and jump to that address, then enable interrupts: + case 0xD9: + { + unsigned l, h; + + pop_rr(h, l); + + memory.ei(cycleCounter); + + PC_MOD(h << 8 | l); + } + break; + + //jp c,nn (16;12 cycles): + //Jump to address stored in next two bytes in memory if CF is set: + case 0xDA: //PC=( ((PC+2)*(1-CarryFlag())) + (((memory.read(PC+1)<<8)+memory.read(PC))*CarryFlag()) ); Cycles(12); break; + if (CF & 0x100) { + jp_nn(); + } else { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } + break; + + case 0xDB: /*doesn't exist*/ + break; + + //call z,nn (24;12 cycles): + //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if CF is set: + case 0xDC: + if (CF & 0x100) { + call_nn(); + } else { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } + break; + + case 0xDE: + { + unsigned data; + + PC_READ(data); + + sbc_a_u8(data); + } + break; + case 0xDF: + rst_n(0x18); + break; + + //ld ($FF00+n),a (12 cycles): + //Put value in A into address (0xFF00 + next byte in memory): + case 0xE0: + { + unsigned tmp; + + PC_READ(tmp); + + FF_WRITE(0xFF00 | tmp, A); + } + break; + + case 0xE1: + pop_rr(H, L); + break; + + //ld ($FF00+C),a (8 ycles): + //Put A into address (0xFF00 + register C): + case 0xE2: + FF_WRITE(0xFF00 | C, A); + break; + case 0xE3: /*doesn't exist*/ + break; + case 0xE4: /*doesn't exist*/ + break; + case 0xE5: + push_rr(H, L); + break; + case 0xE6: + { + unsigned data; + + PC_READ(data); + + and_a_u8(data); + } + break; + case 0xE7: + rst_n(0x20); + break; + + //add sp,n (16 cycles): + //Add next (signed) byte in memory to SP, reset ZF and SF, check HCF and CF: + case 0xE8: + /*{ + int8_t tmp = int8_t(memory.pc_read(PC++, cycleCounter)); + HF2 = (((SP & 0xFFF) + tmp) >> 3) & 0x200; + CF = SP + tmp; + SP = CF; + CF >>= 8; + ZF = 1; + cycleCounter += 12; + }*/ + sp_plus_n(SP); + cycleCounter += 4; + break; + + //jp hl (4 cycles): + //Jump to address in hl: + case 0xE9: + PC = HL(); + break; + + //ld (nn),a (16 cycles): + //set memory at address given by the next 2 bytes to value in A: + //Incrementing PC before call, because of possible interrupt. + case 0xEA: + { + unsigned l, h; + + PC_READ(l); + PC_READ(h); + + WRITE(h << 8 | l, A); + } + break; + + case 0xEB: /*doesn't exist*/ + break; + case 0xEC: /*doesn't exist*/ + break; + case 0xED: /*doesn't exist*/ + break; + case 0xEE: + { + unsigned data; + + PC_READ(data); + + xor_a_u8(data); + } + break; + case 0xEF: + rst_n(0x28); + break; + + //ld a,($FF00+n) (12 cycles): + //Put value at address (0xFF00 + next byte in memory) into A: + case 0xF0: + { + unsigned tmp; + + PC_READ(tmp); + + FF_READ(A, 0xFF00 | tmp); + } + break; + + case 0xF1: /*pop_rr(A, F); Cycles(12); break;*/ + { + unsigned F; + + pop_rr(A, F); + + FROM_F(F); + } + break; + + //ld a,($FF00+C) (8 cycles): + //Put value at address (0xFF00 + register C) into A: + case 0xF2: + FF_READ(A, 0xFF00 | C); + break; + + //di (4 cycles): + case 0xF3: + memory.di(); + break; + + case 0xF4: /*doesn't exist*/ + break; + case 0xF5: /*push_rr(A, F); Cycles(16); break;*/ + calcHF(HF1, HF2); + + { + unsigned F = F(); + + push_rr(A, F); + } + break; + + case 0xF6: + { + unsigned data; + + PC_READ(data); + + or_a_u8(data); + } + break; + case 0xF7: + rst_n(0x30); + break; + + //ldhl sp,n (12 cycles): + //Put (sp+next (signed) byte in memory) into hl (unsets ZF and SF, may enable HF and CF): + case 0xF8: + /*{ + int8_t tmp = int8_t(memory.pc_read(PC++, cycleCounter)); + HF2 = (((SP & 0xFFF) + tmp) >> 3) & 0x200; + CF = SP + tmp; + L = CF; + CF >>= 8; + H = CF; + ZF = 1; + cycleCounter += 8; + }*/ + { + unsigned sum; + sp_plus_n(sum); + L = sum & 0xFF; + H = sum >> 8; + } + break; + + //ld sp,hl (8 cycles): + //Put value in HL into SP + case 0xF9: + SP = HL(); + cycleCounter += 4; + break; + + //ld a,(nn) (16 cycles): + //set A to value in memory at address given by the 2 next bytes. + case 0xFA: + { + unsigned l, h; + + PC_READ(l); + PC_READ(h); + + READ(A, h << 8 | l); + } + break; + + //ei (4 cycles): + //Enable Interrupts after next instruction: + case 0xFB: + memory.ei(cycleCounter); + break; + + case 0xFC: /*doesn't exist*/ + break; + case 0xFD: /*doesn't exist*/ + break; + case 0xFE: + { + unsigned data; + + PC_READ(data); + + cp_a_u8(data); + } + break; + case 0xFF: + rst_n(0x38); + break; +// default: break; + } + } + + PC_ = PC; + cycleCounter = memory.event(cycleCounter); + } + + A_ = A; + cycleCounter_ = cycleCounter; +} diff --git a/src/lib/libgambatte/src/cpu.h b/src/lib/libgambatte/src/cpu.h new file mode 100644 index 00000000..985b6771 --- /dev/null +++ b/src/lib/libgambatte/src/cpu.h @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CPU_H +#define CPU_H + +class SaveState; + +#include "int.h" +#include "memory.h" + +class CPU { + Memory memory; + + unsigned long cycleCounter_; + + unsigned short PC_; + unsigned short SP; + + unsigned HF1, HF2, ZF, CF; + + unsigned char A_, B, C, D, E, /*F,*/ H, L; + + bool skip; + bool halted; + + void process(unsigned long cycles); + +public: + + CPU(); +// void halt(); + +// unsigned interrupt(unsigned address, unsigned cycleCounter); + + void runFor(unsigned long cycles); + void updateVideo(); + unsigned lyCounter(); + void setStatePtrs(SaveState &state); + void saveState(SaveState &state); + void loadState(const SaveState &state); + + void loadSavedata() { memory.loadSavedata(); } + void saveSavedata() { memory.saveSavedata(); } + + void setVideoBlitter(Gambatte::VideoBlitter *vb) { + memory.setVideoBlitter(vb); + } + + void videoBufferChange() { + memory.videoBufferChange(); + } + + unsigned int videoWidth() const { + return memory.videoWidth(); + } + + unsigned int videoHeight() const { + return memory.videoHeight(); + } + + void setVideoFilter(const unsigned int n) { + memory.setVideoFilter(n); + } + + std::vector filterInfo() const { + return memory.filterInfo(); + } + + void setInputStateGetter(Gambatte::InputStateGetter *getInput) { + memory.setInputStateGetter(getInput); + } + + void setMemoryInterface(Gambatte::MemoryInterface *memoryInterface) { + memory.setMemoryInterface(memoryInterface); + } + + void set_savedir(const char *sdir) { + memory.set_savedir(sdir); + } + + const std::string saveBasePath() const { + return memory.saveBasePath(); + } + + void setOsdElement(std::auto_ptr osdElement) { + memory.setOsdElement(osdElement); + } + + bool load(bool forceDmg); + + void setSoundBuffer(Gambatte::uint_least32_t *const buf) { memory.setSoundBuffer(buf); } + unsigned fillSoundBuffer() { return memory.fillSoundBuffer(cycleCounter_); } + + bool isCgb() const { return memory.isCgb(); } + + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) { + memory.setDmgPaletteColor(palNum, colorNum, rgb32); + } +}; + +#endif diff --git a/src/lib/libgambatte/src/event_queue.h b/src/lib/libgambatte/src/event_queue.h new file mode 100644 index 00000000..94fbebcf --- /dev/null +++ b/src/lib/libgambatte/src/event_queue.h @@ -0,0 +1,160 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef EVENT_QUEUE_H +#define EVENT_QUEUE_H + +#include + +template +class event_queue { + Comparer comparer; + T *const a; + const std::size_t capacity_; + std::size_t size_; + + + std::size_t indexOf(T e); + void internalDec(std::size_t i, T e); + template void internalInc(std::size_t i, T e); + +public: + event_queue(std::size_t capacity, const Comparer &comparer); + ~event_queue(); + + std::size_t capacity() const { + return capacity_; + } + + void clear() { + size_ = 0; + } + + void dec(const T oldE, const T newE) { + internalDec(indexOf(oldE), newE); + } + + bool empty() const { + return size_ == 0; + } + + void inc(const T oldE, const T newE) { + internalInc(indexOf(oldE), newE); + } + + void modify_root(const T newRoot) { + internalInc(0, newRoot); + } + + void pop() { + internalInc(0, a[--size_]); + } + + void push(const T e) { + internalDec(size_++, e); + } + + void remove(T e); + + std::size_t size() const { + return size_; + } + + T top() const { + return a[0]; + } +}; + +template +event_queue::event_queue(const std::size_t capacity, const Comparer &comparer_in) : + comparer(comparer_in), + a(new T[capacity]), + capacity_(capacity), + size_(0) +{} + +template +event_queue::~event_queue() { + delete[] a; +} + +template +std::size_t event_queue::indexOf(const T e) { + std::size_t i = 0; + + while (a[i] != e) + ++i; + + return i; +} + +template +void event_queue::internalDec(std::size_t i, const T e) { + a[i] = e; + + while (i != 0) { + const std::size_t parentI = (i - 1) >> 1; + + if (!comparer.less(e, a[parentI])) + break; + + a[i] = a[parentI]; + a[parentI] = e; + i = parentI; + } +} + +template +template +void event_queue::internalInc(std::size_t i, const T e) { + a[i] = e; + + for (;;) { + std::size_t childI = i * 2 + 1; + + if (childI >= size_) + break; + + if ((!child2BoundsCheck || childI + 1 < size_) && comparer.less(a[childI + 1], a[childI])) + ++childI; + + if (!comparer.less(a[childI], e)) + break; + + a[i] = a[childI]; + a[childI] = e; + i = childI; + } +} + +template +void event_queue::remove(const T e) { + std::size_t i = indexOf(e); + + while (i != 0) { + const std::size_t parentI = (i - 1) >> 1; + + a[i] = a[parentI]; + a[parentI] = e; + i = parentI; + } + + pop(); +} + +#endif diff --git a/src/lib/libgambatte/src/file/file.cpp b/src/lib/libgambatte/src/file/file.cpp new file mode 100644 index 00000000..7a8f9966 --- /dev/null +++ b/src/lib/libgambatte/src/file/file.cpp @@ -0,0 +1,73 @@ +/*************************************************************************** +Copyright (C) 2007 by Nach +http://nsrt.edgeemu.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License version 2 for more details. + +You should have received a copy of the GNU General Public License +version 2 along with this program; if not, write to the +Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +***************************************************************************/ + +#include "file.h" + +using namespace std; + +static const unsigned int MAX_FILE_NAME = 512; + +File::File(const char *filename) : stream(filename, ios::in | ios::binary), is_zip(false), fsize(0), count(0) +{ + if (stream) + { + stream.seekg(0, ios::end); + fsize = stream.tellg(); + stream.seekg(0, ios::beg); + } +} + +File::~File() +{ + close(); +} + +void File::rewind() +{ + if (is_open()) + { + stream.seekg(0, ios::beg); + } +} + +bool File::is_open() +{ + return(stream.is_open()); +} + +void File::close() +{ + if (is_open()) + { + stream.close(); + } +} + +void File::read(char *buffer, size_t amount) +{ + if (is_open()) + { + stream.read(buffer, amount); + count = stream.gcount(); + } + else + { + count = 0; + } +} diff --git a/src/lib/libgambatte/src/file/file.h b/src/lib/libgambatte/src/file/file.h new file mode 100644 index 00000000..3435ef16 --- /dev/null +++ b/src/lib/libgambatte/src/file/file.h @@ -0,0 +1,42 @@ +/*************************************************************************** +Copyright (C) 2007 by Nach +http://nsrt.edgeemu.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License version 2 for more details. + +You should have received a copy of the GNU General Public License +version 2 along with this program; if not, write to the +Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +***************************************************************************/ + +#include + +class File { + private: + std::ifstream stream; + bool is_zip; //Change this to an enum later + std::size_t fsize, count; + void *zipfile; + bool zip_sub_open; + + void zip(const char *filename); + + public: + File(const char *filename); + ~File(); + void rewind(); + bool is_open(); + void close(); + std::size_t size() const { return fsize; }; + void read(char *buffer, std::size_t amount); + std::size_t gcount() const { return count; } + bool fail() const { return stream.fail(); } +}; diff --git a/src/lib/libgambatte/src/file/file_zip.cpp b/src/lib/libgambatte/src/file/file_zip.cpp new file mode 100644 index 00000000..c7fae6db --- /dev/null +++ b/src/lib/libgambatte/src/file/file_zip.cpp @@ -0,0 +1,167 @@ +/*************************************************************************** +Copyright (C) 2007 by Nach +http://nsrt.edgeemu.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License version 2 for more details. + +You should have received a copy of the GNU General Public License +version 2 along with this program; if not, write to the +Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +***************************************************************************/ + +#include "file.h" + +#include + +namespace zlib { +#include "unzip/unzip.h" +} + +using namespace std; +using namespace zlib; + +static const unsigned int MAX_FILE_NAME = 512; + +File::File(const char *filename) : stream(filename, ios::in | ios::binary), is_zip(false), fsize(0), count(0) +{ + if (stream) + { + char temp[4]; + stream.read(temp, sizeof(temp)); + + //check for standard zip 'magic number' + if ((temp[0] == 'P') && (temp[1] == 'K') && (temp[2] == 3) && (temp[3] == 4)) + { + stream.close(); + is_zip = true; + zip(filename); + } + else + { + stream.seekg(0, ios::end); + fsize = stream.tellg(); + stream.seekg(0, ios::beg); + } + } +} + +void File::zip(const char *filename) +{ + zipfile = unzOpen(filename); + if (zipfile) + { + zip_sub_open = false; + + unz_file_info cFileInfo; + char ourFile[MAX_FILE_NAME] = { '\n' }; + + for (int cFile = unzGoToFirstFile((unzFile)zipfile); cFile == UNZ_OK; cFile = unzGoToNextFile((unzFile)zipfile)) + { + //Temporary char array for file name + char cFileName[MAX_FILE_NAME]; + + //Gets info on current file, and places it in cFileInfo + unzGetCurrentFileInfo((unzFile)zipfile, &cFileInfo, cFileName, MAX_FILE_NAME, 0, 0, 0, 0); + + //Check for largest file which should be the ROM + if ((size_t)cFileInfo.uncompressed_size > fsize) + { + strcpy(ourFile, cFileName); + fsize = (size_t)cFileInfo.uncompressed_size; + } + } + + if (ourFile[0] != '\n') + { + //Sets current file to the file we liked before + unzLocateFile((unzFile)zipfile, ourFile, 1); + + if (unzOpenCurrentFile((unzFile)zipfile) == UNZ_OK) + { + zip_sub_open = true; + } + } + + if (!zip_sub_open) + { + unzClose((unzFile)zipfile); + zipfile = 0; + } + } +} + +File::~File() +{ + close(); +} + +void File::rewind() +{ + if (is_open()) + { + if (!is_zip) + { + stream.seekg(0, ios::beg); + } + else + { + unzCloseCurrentFile((unzFile)zipfile); + unzOpenCurrentFile((unzFile)zipfile); + } + } +} + +bool File::is_open() +{ + if (!is_zip) + { + return(stream.is_open()); + } + return(zipfile && zip_sub_open); +} + +void File::close() +{ + if (is_open()) + { + if (!is_zip) + { + stream.close(); + } + else + { + unzOpenCurrentFile((unzFile)zipfile); + unzClose((unzFile)zipfile); + zipfile = 0; + zip_sub_open = false; + } + } +} + +void File::read(char *buffer, size_t amount) +{ + if (is_open()) + { + if (!is_zip) + { + stream.read(buffer, amount); + count = stream.gcount(); + } + else + { + count = (size_t)unzReadCurrentFile((unzFile)zipfile, buffer, amount); + } + } + else + { + count = 0; + } +} diff --git a/src/reader/zlib/crypt.h b/src/lib/libgambatte/src/file/unzip/crypt.h similarity index 100% rename from src/reader/zlib/crypt.h rename to src/lib/libgambatte/src/file/unzip/crypt.h diff --git a/src/lib/libgambatte/src/file/unzip/ioapi.c b/src/lib/libgambatte/src/file/unzip/ioapi.c new file mode 100644 index 00000000..05b5ef15 --- /dev/null +++ b/src/lib/libgambatte/src/file/unzip/ioapi.c @@ -0,0 +1,177 @@ +/* ioapi.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#include +#include +#include + +#include +#include "ioapi.h" + + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +voidpf ZCALLBACK fopen_file_func OF(( + voidpf opaque, + const char* filename, + int mode)); + +uLong ZCALLBACK fread_file_func OF(( + voidpf opaque, + voidpf stream, + void* buf, + uLong size)); + +uLong ZCALLBACK fwrite_file_func OF(( + voidpf opaque, + voidpf stream, + const void* buf, + uLong size)); + +long ZCALLBACK ftell_file_func OF(( + voidpf opaque, + voidpf stream)); + +long ZCALLBACK fseek_file_func OF(( + voidpf opaque, + voidpf stream, + uLong offset, + int origin)); + +int ZCALLBACK fclose_file_func OF(( + voidpf opaque, + voidpf stream)); + +int ZCALLBACK ferror_file_func OF(( + voidpf opaque, + voidpf stream)); + + +voidpf ZCALLBACK fopen_file_func (opaque, filename, mode) + voidpf opaque; + const char* filename; + int mode; +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + + +uLong ZCALLBACK fread_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + void* buf; + uLong size; +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + + +uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + const void* buf; + uLong size; +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +long ZCALLBACK ftell_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + +long ZCALLBACK fseek_file_func (opaque, stream, offset, origin) + voidpf opaque; + voidpf stream; + uLong offset; + int origin; +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + fseek((FILE *)stream, offset, fseek_origin); + return ret; +} + +int ZCALLBACK fclose_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +int ZCALLBACK ferror_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/src/reader/zlib/ioapi.h b/src/lib/libgambatte/src/file/unzip/ioapi.h similarity index 100% rename from src/reader/zlib/ioapi.h rename to src/lib/libgambatte/src/file/unzip/ioapi.h diff --git a/src/lib/libgambatte/src/file/unzip/unzip.c b/src/lib/libgambatte/src/file/unzip/unzip.c new file mode 100644 index 00000000..325f3d08 --- /dev/null +++ b/src/lib/libgambatte/src/file/unzip/unzip.c @@ -0,0 +1,1605 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + Read unzip.h for more info +*/ + +/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of +compatibility with older software. The following is from the original crypt.c. Code +woven in by Terry Thorsen 1/2003. +*/ +/* + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html +*/ +/* + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + */ + +/* + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + */ + + +#include +#include +#include +#include +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i = 0; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i = 0; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (fileName1,fileName2) + const char* fileName1; + const char* fileName2; +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) + const char* fileName1; + const char* fileName2; + int iCaseSensitivity; +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def) + const char *path; + zlib_filefunc_def* pzlib_filefunc_def; +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + if (pzlib_filefunc_def==NULL) + fill_fopen_filefunc(&us.z_filefunc); + else + us.z_filefunc = *pzlib_filefunc_def; + + us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + if (ZSEEK(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) + unzFile file; + unz_global_info *pglobal_info; +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) + uLong ulDosDate; + tm_unz* ptm; +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (file, + pfile_info, + pfile_info_internal, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + unz_file_info_internal *pfile_info_internal; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (ZSEEK(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (file, + pfile_info, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (file) + unzFile file; +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (file) + unzFile file; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) + unzFile file; + const char *szFileName; + int iCaseSensitivity; +{ + unz_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info cur_file_infoSaved; + unz_file_info_internal cur_file_info_internalSaved; + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; // offset in file + uLong num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGoToFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, + poffset_local_extrafield, + psize_local_extrafield) + unz_s* s; + uInt* piSizeVar; + uLong *poffset_local_extrafield; + uInt *psize_local_extrafield; +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) + unzFile file; + int* method; + int* level; + int raw; + const char* password; +{ + int err=UNZ_OK; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_DEFLATED) && + (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (file) + unzFile file; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (file, password) + unzFile file; + const char* password; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw) + unzFile file; + int* method; + int* level; + int raw; +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (file, buf, len) + unzFile file; + voidp buf; + unsigned len; +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) + unzFile file; + voidp buf; + unsigned len; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (file) + unzFile file; +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) + unzFile file; + char *szComment; + uLong uSizeBuf; +{ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern uLong ZEXPORT unzGetOffset (file) + unzFile file; +{ + unz_s* s; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern int ZEXPORT unzSetOffset (file, pos) + unzFile file; + uLong pos; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} diff --git a/src/lib/libgambatte/src/file/unzip/unzip.h b/src/lib/libgambatte/src/file/unzip/unzip.h new file mode 100644 index 00000000..5bb6a696 --- /dev/null +++ b/src/lib/libgambatte/src/file/unzip/unzip.h @@ -0,0 +1,354 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz_H */ diff --git a/src/lib/libgambatte/src/gambatte.cpp b/src/lib/libgambatte/src/gambatte.cpp new file mode 100644 index 00000000..03322a1a --- /dev/null +++ b/src/lib/libgambatte/src/gambatte.cpp @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "gambatte.h" +#include "cpu.h" +#include "savestate.h" +#include "statesaver.h" +#include "initstate.h" +#include "state_osd_elements.h" +#include +#include + +static const std::string itos(int i) { + std::stringstream ss; + + ss << i; + + std::string out; + + ss >> out; + + return out; +} + +static const std::string statePath(const std::string &basePath, int stateNo) { + return basePath + "_" + itos(stateNo) + ".gqs"; +} + +namespace Gambatte { +GB::GB() : z80(new CPU), stateNo(1) {} + +GB::~GB() { + delete z80; +} + +unsigned GB::runFor(Gambatte::uint_least32_t *const soundBuf, const unsigned samples) { + z80->setSoundBuffer(soundBuf); + z80->runFor(samples * 2); + + return z80->fillSoundBuffer(); +} + +void GB::updateVideo() { + return z80->updateVideo(); +} + +unsigned GB::lyCounter() { + return z80->lyCounter(); +} + +void GB::reset() { + z80->saveSavedata(); + + SaveState state; + z80->setStatePtrs(state); + setInitState(state, z80->isCgb()); + z80->loadState(state); + z80->loadSavedata(); + +// z80->reset(); +} + +void GB::setVideoBlitter(VideoBlitter *vb) { + z80->setVideoBlitter(vb); +} + +void GB::videoBufferChange() { + z80->videoBufferChange(); +} + +unsigned GB::videoWidth() const { + return z80->videoWidth(); +} + +unsigned GB::videoHeight() const { + return z80->videoHeight(); +} + +void GB::setVideoFilter(const unsigned n) { + z80->setVideoFilter(n); +} + +std::vector GB::filterInfo() const { + return z80->filterInfo(); +} + +void GB::setInputStateGetter(InputStateGetter *getInput) { + z80->setInputStateGetter(getInput); +} + +void GB::setMemoryInterface(MemoryInterface *memoryInterface) { + z80->setMemoryInterface(memoryInterface); +} + +void GB::set_savedir(const char *sdir) { + z80->set_savedir(sdir); +} + +bool GB::load(const bool forceDmg) { + z80->saveSavedata(); + + const bool failed = z80->load(forceDmg); + + if (!failed) { + SaveState state; + z80->setStatePtrs(state); + setInitState(state, z80->isCgb()); + z80->loadState(state); + z80->loadSavedata(); + + stateNo = 1; + z80->setOsdElement(std::auto_ptr()); + } + + return failed; +} + +void GB::save() { + z80->saveSavedata(); +} + +bool GB::isCgb() const { + return z80->isCgb(); +} + +void GB::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) { + z80->setDmgPaletteColor(palNum, colorNum, rgb32); +} + +void GB::loadState(const char *const filepath, const bool osdMessage) { + z80->saveSavedata(); + + SaveState state; + z80->setStatePtrs(state); + + if (StateSaver::loadState(state, filepath)) { + z80->loadState(state); + + if (osdMessage) + z80->setOsdElement(newStateLoadedOsdElement(stateNo)); + } +} + +void GB::saveState() { + saveState(statePath(z80->saveBasePath(), stateNo).c_str()); + z80->setOsdElement(newStateSavedOsdElement(stateNo)); +} + +void GB::loadState() { + loadState(statePath(z80->saveBasePath(), stateNo).c_str(), true); +} + +void GB::saveState(const char *filepath) { + SaveState state; + z80->setStatePtrs(state); + z80->saveState(state); + StateSaver::saveState(state, filepath); +} + +void GB::loadState(const char *const filepath) { + loadState(filepath, false); +} + +void GB::selectState(int n) { + n -= (n / 10) * 10; + stateNo = n < 0 ? n + 10 : n; + z80->setOsdElement(newSaveStateOsdElement(statePath(z80->saveBasePath(), stateNo).c_str(), stateNo)); +} +} diff --git a/src/lib/libgambatte/src/initstate.cpp b/src/lib/libgambatte/src/initstate.cpp new file mode 100644 index 00000000..c16d48b4 --- /dev/null +++ b/src/lib/libgambatte/src/initstate.cpp @@ -0,0 +1,281 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "initstate.h" +#include "savestate.h" +#include +#include +#include "sound/sound_unit.h" +#include "memory.h" + +void setInitState(SaveState &state, const bool cgb) { + static const unsigned char feaxDump[0x60] = { + 0x18, 0x01, 0xEF, 0xDE, 0x06, 0x4A, 0xCD, 0xBD, + 0x18, 0x01, 0xEF, 0xDE, 0x06, 0x4A, 0xCD, 0xBD, + 0x18, 0x01, 0xEF, 0xDE, 0x06, 0x4A, 0xCD, 0xBD, + 0x18, 0x01, 0xEF, 0xDE, 0x06, 0x4A, 0xCD, 0xBD, + 0x00, 0x90, 0xF7, 0x7F, 0xC0, 0xB1, 0xB4, 0xFB, + 0x00, 0x90, 0xF7, 0x7F, 0xC0, 0xB1, 0xB4, 0xFB, + 0x00, 0x90, 0xF7, 0x7F, 0xC0, 0xB1, 0xB4, 0xFB, + 0x00, 0x90, 0xF7, 0x7F, 0xC0, 0xB1, 0xB4, 0xFB, + 0x24, 0x1B, 0xFD, 0x3A, 0x10, 0x12, 0xAD, 0x45, + 0x24, 0x1B, 0xFD, 0x3A, 0x10, 0x12, 0xAD, 0x45, + 0x24, 0x1B, 0xFD, 0x3A, 0x10, 0x12, 0xAD, 0x45, + 0x24, 0x1B, 0xFD, 0x3A, 0x10, 0x12, 0xAD, 0x45 + }; + + static const unsigned char ffxxDump[0x100] = { + 0xCF, 0x00, 0x7C, 0xFF, 0x43, 0x00, 0x00, 0xF8, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, + 0x80, 0xBF, 0xF3, 0xFF, 0xBF, 0xFF, 0x3F, 0x00, + 0xFF, 0xBF, 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, 0xFF, + 0xFF, 0x00, 0x00, 0xBF, 0x77, 0xF3, 0xF1, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x7E, 0xFF, 0xFE, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC0, 0xFF, 0xC1, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, + 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, + 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, + 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E, + 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, + 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC, + 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E, + 0x45, 0xEC, 0x52, 0xFA, 0x08, 0xB7, 0x07, 0x5D, + 0x01, 0xFD, 0xC0, 0xFF, 0x08, 0xFC, 0x00, 0xE5, + 0x0B, 0xF8, 0xC2, 0xCE, 0xF4, 0xF9, 0x0F, 0x7F, + 0x45, 0x6D, 0x3D, 0xFE, 0x46, 0x97, 0x33, 0x5E, + 0x08, 0xEF, 0xF1, 0xFF, 0x86, 0x83, 0x24, 0x74, + 0x12, 0xFC, 0x00, 0x9F, 0xB4, 0xB7, 0x06, 0xD5, + 0xD0, 0x7A, 0x00, 0x9E, 0x04, 0x5F, 0x41, 0x2F, + 0x1D, 0x77, 0x36, 0x75, 0x81, 0xAA, 0x70, 0x3A, + 0x98, 0xD1, 0x71, 0x02, 0x4D, 0x01, 0xC1, 0xFF, + 0x0D, 0x00, 0xD3, 0x05, 0xF9, 0x00, 0x0B, 0x00 + }; + + static const unsigned char cgbObjpDump[0x40] = { + 0x00, 0x00, 0xF2, 0xAB, + 0x61, 0xC2, 0xD9, 0xBA, + 0x88, 0x6E, 0xDD, 0x63, + 0x28, 0x27, 0xFB, 0x9F, + 0x35, 0x42, 0xD6, 0xD4, + 0x50, 0x48, 0x57, 0x5E, + 0x23, 0x3E, 0x3D, 0xCA, + 0x71, 0x21, 0x37, 0xC0, + 0xC6, 0xB3, 0xFB, 0xF9, + 0x08, 0x00, 0x8D, 0x29, + 0xA3, 0x20, 0xDB, 0x87, + 0x62, 0x05, 0x5D, 0xD4, + 0x0E, 0x08, 0xFE, 0xAF, + 0x20, 0x02, 0xD7, 0xFF, + 0x07, 0x6A, 0x55, 0xEC, + 0x83, 0x40, 0x0B, 0x77 + }; + + state.cpu.cycleCounter = 0x102A0; + state.cpu.PC = 0x100; + state.cpu.SP = 0xFFFE; + state.cpu.A = (cgb * 0x10) | 0x01; + state.cpu.B = 0x00; + state.cpu.C = 0x13; + state.cpu.D = 0x00; + state.cpu.E = 0xD8; + state.cpu.F = 0xB0; + state.cpu.H = 0x01; + state.cpu.L = 0x4D; + state.cpu.skip = false; + state.cpu.halted = false; + + + std::memset(state.mem.vram.ptr, 0, state.mem.vram.getSz()); + std::memset(state.mem.sram.ptr, 0xFF, state.mem.sram.getSz()); + + for (unsigned addr = 0x0000; addr < 0x0800; addr += 0x10) { + std::memset(state.mem.wram.ptr + addr + 0x00, 0xFF, 0x08); + std::memset(state.mem.wram.ptr + addr + 0x08, 0x00, 0x08); + } + + for (unsigned addr = 0x0800; addr < 0x1000; addr += 0x10) { + std::memset(state.mem.wram.ptr + addr + 0x00, 0x00, 0x08); + std::memset(state.mem.wram.ptr + addr + 0x08, 0xFF, 0x08); + } + + for (unsigned addr = 0x0E00; addr < 0x1000; addr += 0x10) { + state.mem.wram.ptr[addr + 0x02] = 0xFF; + state.mem.wram.ptr[addr + 0x0A] = 0x00; + } + + for (unsigned addr = 0x1000; addr < state.mem.wram.getSz(); addr += 0x1000) + std::memcpy(state.mem.wram.ptr + addr, state.mem.wram.ptr, 0x1000); + + std::memset(state.mem.ioamhram.ptr, 0x00, state.mem.ioamhram.getSz()); + std::memcpy(state.mem.ioamhram.ptr + 0xA0, feaxDump, sizeof(feaxDump)); + std::memcpy(state.mem.ioamhram.ptr + 0x100, ffxxDump, sizeof(ffxxDump)); + + state.mem.ioamhram.ptr[0x104] = 0x1C; + state.mem.ioamhram.ptr[0x140] = 0x91; + state.mem.ioamhram.ptr[0x144] = 0x00; + + if (!cgb) { + state.mem.ioamhram.ptr[0x130] = 0xAC; + state.mem.ioamhram.ptr[0x131] = 0xDD; + state.mem.ioamhram.ptr[0x132] = 0xDA; + state.mem.ioamhram.ptr[0x133] = 0x48; + state.mem.ioamhram.ptr[0x134] = 0x36; + state.mem.ioamhram.ptr[0x135] = 0x02; + state.mem.ioamhram.ptr[0x136] = 0xCF; + state.mem.ioamhram.ptr[0x137] = 0x16; + state.mem.ioamhram.ptr[0x138] = 0x2C; + state.mem.ioamhram.ptr[0x139] = 0x04; + state.mem.ioamhram.ptr[0x13A] = 0xE5; + state.mem.ioamhram.ptr[0x13B] = 0x2C; + state.mem.ioamhram.ptr[0x13C] = 0xAC; + state.mem.ioamhram.ptr[0x13D] = 0xDD; + state.mem.ioamhram.ptr[0x13E] = 0xDA; + state.mem.ioamhram.ptr[0x13F] = 0x48; + + state.mem.ioamhram.ptr[0x14D] = 0xFF; + state.mem.ioamhram.ptr[0x14F] = 0xFF; + state.mem.ioamhram.ptr[0x156] = 0xFF; + state.mem.ioamhram.ptr[0x168] = 0xFF; + state.mem.ioamhram.ptr[0x16A] = 0xFF; + state.mem.ioamhram.ptr[0x16B] = 0xFF; + state.mem.ioamhram.ptr[0x16C] = 0xFF; + state.mem.ioamhram.ptr[0x170] = 0xFF; + state.mem.ioamhram.ptr[0x172] = 0xFF; + state.mem.ioamhram.ptr[0x173] = 0xFF; + state.mem.ioamhram.ptr[0x174] = 0xFF; + state.mem.ioamhram.ptr[0x175] = 0xFF; + state.mem.ioamhram.ptr[0x176] = 0xFF; + state.mem.ioamhram.ptr[0x177] = 0xFF; + } + + state.mem.div_lastUpdate = 0; + state.mem.tima_lastUpdate = 0; + state.mem.tmatime = Memory::COUNTER_DISABLED; + state.mem.next_serialtime = Memory::COUNTER_DISABLED; + state.mem.lastOamDmaUpdate = Memory::COUNTER_DISABLED; + state.mem.minIntTime = 0; + state.mem.rombank = 1; + state.mem.dmaSource = 0; + state.mem.dmaDestination = 0; + state.mem.rambank = 0; + state.mem.oamDmaPos = 0xFE; + state.mem.IME = false; + state.mem.enable_ram = false; + state.mem.rambank_mode = false; + state.mem.hdma_transfer = false; + + + for (unsigned i = 0x00; i < 0x40; i += 0x02) { + state.ppu.bgpData.ptr[i] = 0xFF; + state.ppu.bgpData.ptr[i + 1] = 0x7F; + } + + std::memcpy(state.ppu.objpData.ptr, cgbObjpDump, sizeof(cgbObjpDump)); + + if (!cgb) { + state.ppu.bgpData.ptr[0] = state.mem.ioamhram.get()[0x147]; + state.ppu.objpData.ptr[0] = state.mem.ioamhram.get()[0x148]; + state.ppu.objpData.ptr[1] = state.mem.ioamhram.get()[0x149]; + } + + for (unsigned pos = 0; pos < 80; ++pos) + state.ppu.oamReaderBuf.ptr[pos] = state.mem.ioamhram.ptr[((pos * 2) & ~3) | (pos & 1)]; + + std::fill_n(state.ppu.oamReaderSzbuf.ptr, 40, false); + + state.ppu.videoCycles = 144*456ul + 164; + state.ppu.enableDisplayM0Time = state.cpu.cycleCounter - state.ppu.videoCycles + 159; + state.ppu.winYPos = 0xFF; + state.ppu.drawStartCycle = 90; + state.ppu.scReadOffset = 90; + state.ppu.lcdc = state.mem.ioamhram.get()[0x140]; + state.ppu.scx[1] = state.ppu.scx[0] = 0; + state.ppu.scy[1] = state.ppu.scy[0] = 0; + state.ppu.scxAnd7 = 0; + state.ppu.weMaster = false; + state.ppu.wx = 0; + state.ppu.wy = 0; + state.ppu.lycIrqSkip = false; + + + state.spu.cycleCounter = 0x1000 | ((state.cpu.cycleCounter >> 1) & 0xFFF); // spu.cycleCounter >> 12 & 7 represents the frame sequencer position. + + state.spu.ch1.sweep.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch1.sweep.shadow = 0; + state.spu.ch1.sweep.nr0 = 0; + state.spu.ch1.sweep.negging = false; + state.spu.ch1.duty.nextPosUpdate = (state.spu.cycleCounter & ~1) + 2048 * 2; + state.spu.ch1.duty.nr3 = 0; + state.spu.ch1.duty.pos = 0; + state.spu.ch1.env.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch1.env.volume = 0; + state.spu.ch1.lcounter.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch1.lcounter.lengthCounter = 0x40; + state.spu.ch1.nr4 = 0; + state.spu.ch1.master = true; + + state.spu.ch2.duty.nextPosUpdate = (state.spu.cycleCounter & ~1) + 2048 * 2; + state.spu.ch2.duty.nr3 = 0; + state.spu.ch2.duty.pos = 0; + state.spu.ch2.env.counter = state.spu.cycleCounter - ((state.spu.cycleCounter - 0x1000) & 0x7FFF) + 8ul * 0x8000; + state.spu.ch2.env.volume = 0; + state.spu.ch2.lcounter.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch2.lcounter.lengthCounter = 0x40; + state.spu.ch2.nr4 = 0; + state.spu.ch2.master = false; + + for (unsigned i = 0; i < 0x10; ++i) + state.spu.ch3.waveRam.ptr[i] = state.mem.ioamhram.get()[0x130 + i]; + + state.spu.ch3.lcounter.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch3.lcounter.lengthCounter = 0x100; + state.spu.ch3.waveCounter = SoundUnit::COUNTER_DISABLED; + state.spu.ch3.lastReadTime = SoundUnit::COUNTER_DISABLED; + state.spu.ch3.nr3 = 0; + state.spu.ch3.nr4 = 0; + state.spu.ch3.wavePos = 0; + state.spu.ch3.sampleBuf = 0; + state.spu.ch3.master = false; + + state.spu.ch4.lfsr.counter = state.spu.cycleCounter + 4; + state.spu.ch4.lfsr.reg = 0xFF; + state.spu.ch4.env.counter = state.spu.cycleCounter - ((state.spu.cycleCounter - 0x1000) & 0x7FFF) + 8ul * 0x8000; + state.spu.ch4.env.volume = 0; + state.spu.ch4.lcounter.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch4.lcounter.lengthCounter = 0x40; + state.spu.ch4.nr4 = 0; + state.spu.ch4.master = false; + + state.rtc.baseTime = std::time(0); + state.rtc.haltTime = state.rtc.baseTime; + state.rtc.index = 5; + state.rtc.dataDh = 0; + state.rtc.dataDl = 0; + state.rtc.dataH = 0; + state.rtc.dataM = 0; + state.rtc.dataS = 0; + state.rtc.lastLatchData = false; +} diff --git a/src/lib/libgambatte/src/initstate.h b/src/lib/libgambatte/src/initstate.h new file mode 100644 index 00000000..d550eed5 --- /dev/null +++ b/src/lib/libgambatte/src/initstate.h @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef INITSTATE_H +#define INITSTATE_H + +class SaveState; + +void setInitState(SaveState &state, bool cgb); + +#endif diff --git a/src/lib/libgambatte/src/insertion_sort.h b/src/lib/libgambatte/src/insertion_sort.h new file mode 100644 index 00000000..939ba074 --- /dev/null +++ b/src/lib/libgambatte/src/insertion_sort.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef INSERTION_SORT_H +#define INSERTION_SORT_H + +#include + +template +void insertionSort(T *const start, T *const end, Less less) { + if (start >= end) + return; + + T *a = start; + + while (++a < end) { + const T e = *a; + + T *b = a; + + while (b != start && less(e, *(b - 1))) { + *b = *(b - 1); + b = b - 1; + } + + *b = e; + } +} + +template +inline void insertionSort(T *const start, T *const end) { + insertionSort(start, end, std::less()); +} + +#endif /*INSERTION_SORT_H*/ diff --git a/src/lib/libgambatte/src/interrupter.cpp b/src/lib/libgambatte/src/interrupter.cpp new file mode 100644 index 00000000..aea9df41 --- /dev/null +++ b/src/lib/libgambatte/src/interrupter.cpp @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "interrupter.h" + +#include "memory.h" + +Interrupter::Interrupter(unsigned short &SP_in, unsigned short &PC_in, bool &halted_in) : + SP(SP_in), + PC(PC_in), + halted(halted_in) +{} + +unsigned long Interrupter::interrupt(const unsigned address, unsigned long cycleCounter, Memory &memory) { + if (halted && memory.isCgb()) + cycleCounter += 4; + + halted = false; + cycleCounter += 8; + SP = (SP - 1) & 0xFFFF; + memory.write(SP, PC >> 8, cycleCounter); + cycleCounter += 4; + SP = (SP - 1) & 0xFFFF; + memory.write(SP, PC & 0xFF, cycleCounter); + PC = address; + cycleCounter += 8; + + return cycleCounter; +} diff --git a/src/lib/libgambatte/src/interrupter.h b/src/lib/libgambatte/src/interrupter.h new file mode 100644 index 00000000..18e0d9e1 --- /dev/null +++ b/src/lib/libgambatte/src/interrupter.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef INTERRUPTER_H +#define INTERRUPTER_H + +class Memory; + +class Interrupter { + unsigned short &SP; + unsigned short &PC; + bool &halted; + +public: + Interrupter(unsigned short &SP, unsigned short &PC, bool &halted); + unsigned long interrupt(const unsigned address, unsigned long cycleCounter, Memory &memory); + + void unhalt() { + halted = false; + } +}; + +#endif diff --git a/src/lib/libgambatte/src/memory.cpp b/src/lib/libgambatte/src/memory.cpp new file mode 100644 index 00000000..1d39c884 --- /dev/null +++ b/src/lib/libgambatte/src/memory.cpp @@ -0,0 +1,1937 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "memory.h" +#include "video.h" +#include "sound.h" +#include "inputstate.h" +#include "inputstategetter.h" +#include "memoryinterface.h" +#include "savestate.h" +#include "file/file.h" +#include + +// static const uint32_t timaClock[4]={ 1024, 16, 64, 256 }; +static const unsigned char timaClock[4] = { 10, 4, 6, 8 }; + +Memory::Memory(const Interrupter &interrupter_in) : +memchunk(NULL), +rambankdata(NULL), +rdisabled_ram(NULL), +wdisabled_ram(NULL), +oamDmaSrc(NULL), +vrambank(vram), +rsrambankptr(NULL), +wsrambankptr(NULL), +getInput(NULL), +memoryInterface(NULL), +div_lastUpdate(0), +tima_lastUpdate(0), +next_timatime(COUNTER_DISABLED), +next_blittime(144*456ul), +nextIntTime(COUNTER_DISABLED), +minIntTime(0), +next_dmatime(COUNTER_DISABLED), +next_hdmaReschedule(COUNTER_DISABLED), +next_unhalttime(COUNTER_DISABLED), +next_endtime(0), +tmatime(COUNTER_DISABLED), +next_serialtime(COUNTER_DISABLED), +lastOamDmaUpdate(COUNTER_DISABLED), +nextOamEventTime(COUNTER_DISABLED), +display(ioamhram, vram), +interrupter(interrupter_in), +romtype(plain), +rombanks(1), +rombank(1), +dmaSource(0), +dmaDestination(0), +rambank(0), +rambanks(1), +oamDmaArea1Lower(0), +oamDmaArea1Width(0), +oamDmaArea2Upper(0), +oamDmaPos(0xFE), +cgb(false), +doubleSpeed(false), +IME(false), +enable_ram(false), +rambank_mode(false), +battery(false), +rtcRom(false), +hdma_transfer(false), +active(false) +{ + romdata[1] = romdata[0] = NULL; + wramdata[1] = wramdata[0] = NULL; + std::fill_n(rmem, 0x10, static_cast(NULL)); + std::fill_n(wmem, 0x10, static_cast(NULL)); + set_irqEvent(); + set_event(); +} + +void Memory::setStatePtrs(SaveState &state) { + state.mem.vram.set(vram, sizeof vram); + state.mem.sram.set(rambankdata, rambanks * 0x2000ul); + state.mem.wram.set(wramdata[0], isCgb() ? 0x8000 : 0x2000); + state.mem.ioamhram.set(ioamhram, sizeof ioamhram); + + display.setStatePtrs(state); + sound.setStatePtrs(state); +} + +unsigned long Memory::saveState(SaveState &state, unsigned long cycleCounter) { + cycleCounter = resetCounters(cycleCounter); + nontrivial_ff_read(0xFF0F, cycleCounter); + nontrivial_ff_read(0xFF26, cycleCounter); + + state.mem.div_lastUpdate = div_lastUpdate; + state.mem.tima_lastUpdate = tima_lastUpdate; + state.mem.tmatime = tmatime; + state.mem.next_serialtime = next_serialtime; + state.mem.lastOamDmaUpdate = lastOamDmaUpdate; + state.mem.minIntTime = minIntTime; + state.mem.rombank = rombank; + state.mem.dmaSource = dmaSource; + state.mem.dmaDestination = dmaDestination; + state.mem.rambank = rambank; + state.mem.oamDmaPos = oamDmaPos; + state.mem.IME = IME; + state.mem.enable_ram = enable_ram; + state.mem.rambank_mode = rambank_mode; + state.mem.hdma_transfer = hdma_transfer; + + rtc.saveState(state); + display.saveState(state); + sound.saveState(state); + + return cycleCounter; +} + +void Memory::loadState(const SaveState &state, const unsigned long oldCc) { + sound.loadState(state); + display.loadState(state, state.mem.oamDmaPos < 0xA0 ? rdisabled_ram : ioamhram); + rtc.loadState(state, rtcRom ? state.mem.enable_ram : false); + + div_lastUpdate = state.mem.div_lastUpdate; + tima_lastUpdate = state.mem.tima_lastUpdate; + tmatime = state.mem.tmatime; + next_serialtime = state.mem.next_serialtime; + lastOamDmaUpdate = state.mem.lastOamDmaUpdate; + minIntTime = state.mem.minIntTime; + rombank = state.mem.rombank & (rombanks - 1); + dmaSource = state.mem.dmaSource; + dmaDestination = state.mem.dmaDestination; + rambank = state.mem.rambank & (rambanks - 1); + oamDmaPos = state.mem.oamDmaPos; + IME = state.mem.IME; + enable_ram = state.mem.enable_ram; + rambank_mode = state.mem.rambank_mode; + hdma_transfer = state.mem.hdma_transfer; + + const bool oldDs = doubleSpeed; + doubleSpeed = isCgb() & ioamhram[0x14D] >> 7; + oamDmaArea2Upper = oamDmaArea1Width = oamDmaArea1Lower = 0; + vrambank = vram + (ioamhram[0x14F] & 0x01 & isCgb()) * 0x2000; + wramdata[1] = wramdata[0] + ((isCgb() && (ioamhram[0x170] & 0x07)) ? (ioamhram[0x170] & 0x07) : 1) * 0x1000; + std::fill_n(rmem, 0x10, static_cast(NULL)); + std::fill_n(wmem, 0x10, static_cast(NULL)); + setBanks(); + + if (lastOamDmaUpdate != COUNTER_DISABLED) { + oamDmaInitSetup(); + + unsigned oamEventPos = 0x100; + + if (oamDmaPos < 0xA0) { + setOamDmaArea(); + oamEventPos = 0xA0; + } + + nextOamEventTime = lastOamDmaUpdate + (oamEventPos - oamDmaPos) * 4; + setOamDmaSrc(); + } + + if (!IME && state.cpu.halted) + schedule_unhalt(); + + next_blittime = (ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : static_cast(COUNTER_DISABLED); + + const unsigned long cycleCounter = state.cpu.cycleCounter; + + if (hdma_transfer) { + next_dmatime = display.nextHdmaTime(cycleCounter); + next_hdmaReschedule = display.nextHdmaTimeInvalid(); + } else { + next_hdmaReschedule = next_dmatime = COUNTER_DISABLED; + } + + next_timatime = (ioamhram[0x107] & 4) ? tima_lastUpdate + ((256u - ioamhram[0x105]) << timaClock[ioamhram[0x107] & 3]) + 1 : static_cast(COUNTER_DISABLED); + set_irqEvent(); + rescheduleIrq(cycleCounter); + + if (oldDs != isDoubleSpeed()) + next_endtime = cycleCounter - (isDoubleSpeed() ?( oldCc - next_endtime) << 1 :( oldCc - next_endtime) >> 1); + else + next_endtime = cycleCounter - (oldCc - next_endtime); + +// set_event(); +} + +void Memory::schedule_unhalt() { + next_unhalttime = std::min(next_irqEventTime, display.nextIrqEvent()); + + if (next_unhalttime != COUNTER_DISABLED) + next_unhalttime += isCgb() * 4; + + set_event(); +} + +void Memory::rescheduleIrq(const unsigned long cycleCounter) { + if (IME) { + ioamhram[0x10F] |= display.getIfReg(cycleCounter) & 3; + + nextIntTime = (ioamhram[0x10F] & ioamhram[0x1FF] & 0x1F) ? cycleCounter : std::min(next_irqEventTime, display.nextIrqEvent()); + + if (nextIntTime < minIntTime) + nextIntTime = minIntTime; + + set_event(); + } +} + +void Memory::rescheduleHdmaReschedule() { + if (hdma_transfer && (ioamhram[0x140] & 0x80)) { + const unsigned long newTime = display.nextHdmaTimeInvalid(); + + if (newTime < next_hdmaReschedule) { + next_hdmaReschedule = newTime; + + if (newTime < next_eventtime) { + next_eventtime = newTime; + next_event = HDMA_RESCHEDULE; + } + } + } +} + +void Memory::ei(const unsigned long cycleCounter) { + IME = 1; + minIntTime = cycleCounter + 1; + rescheduleIrq(cycleCounter); +} + +void Memory::incEndtime(const unsigned long inc) { + active = true; + next_endtime += inc << isDoubleSpeed(); + set_event(); +} + +void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) { + next_endtime = cycleCounter; + incEndtime(inc); +} + +void Memory::set_irqEvent() { + next_irqEventTime = next_timatime; + next_irqEvent = TIMA; + + if (next_serialtime < next_irqEventTime) { + next_irqEvent = SERIAL; + next_irqEventTime = next_serialtime; + } +} + +void Memory::update_irqEvents(const unsigned long cc) { + while (next_irqEventTime <= cc) { + switch (next_irqEvent) { + case TIMA: + ioamhram[0x10F] |= 4; + next_timatime += (256u - ioamhram[0x106]) << timaClock[ioamhram[0x107] & 3]; + break; + case SERIAL: + next_serialtime = COUNTER_DISABLED; + ioamhram[0x101] = 0xFF; + ioamhram[0x102] &= 0x7F; + ioamhram[0x10F] |= 8; + break; + } + + set_irqEvent(); + } +} + +void Memory::set_event() { + next_event = INTERRUPTS; + next_eventtime = nextIntTime; + if (next_hdmaReschedule < next_eventtime) { + next_eventtime = next_hdmaReschedule; + next_event = HDMA_RESCHEDULE; + } + if (next_dmatime < next_eventtime) { + next_eventtime = next_dmatime; + next_event = DMA; + } + if (next_unhalttime < next_eventtime) { + next_eventtime = next_unhalttime; + next_event = UNHALT; + } + if (nextOamEventTime < next_eventtime) { + next_eventtime = nextOamEventTime; + next_event = OAM; + } + if (next_blittime < next_eventtime) { + next_event = BLIT; + next_eventtime = next_blittime; + } + if (next_endtime < next_eventtime) { + next_eventtime = next_endtime; + next_event = END; + } +} + +unsigned long Memory::event(unsigned long cycleCounter) { + if (lastOamDmaUpdate != COUNTER_DISABLED) + updateOamDma(cycleCounter); + + switch (next_event) { + case HDMA_RESCHEDULE: +// printf("hdma_reschedule\n"); + next_dmatime = display.nextHdmaTime(cycleCounter); + next_hdmaReschedule = display.nextHdmaTimeInvalid(); + break; + case DMA: +// printf("dma\n"); + { + const bool doubleSpeed = isDoubleSpeed(); + unsigned dmaSrc = dmaSource; + unsigned dmaDest = dmaDestination; + unsigned dmaLength = ((ioamhram[0x155] & 0x7F) + 0x1) * 0x10; + + unsigned length = hdma_transfer ? 0x10 : dmaLength; + + if ((static_cast(dmaDest) + length) & 0x10000) { + length = 0x10000 - dmaDest; + ioamhram[0x155] |= 0x80; + } + + dmaLength -= length; + + if (!(ioamhram[0x140] & 0x80)) + dmaLength = 0; + + { + unsigned long lOamDmaUpdate = lastOamDmaUpdate; + lastOamDmaUpdate = COUNTER_DISABLED; + + while (length--) { + const unsigned src = dmaSrc++ & 0xFFFF; + const unsigned data = ((src & 0xE000) == 0x8000 || src > 0xFDFF) ? 0xFF : read(src, cycleCounter); + + cycleCounter += 2 << doubleSpeed; + + if (cycleCounter - 3 > lOamDmaUpdate) { + oamDmaPos = (oamDmaPos + 1) & 0xFF; + lOamDmaUpdate += 4; + + if (oamDmaPos < 0xA0) { + if (oamDmaPos == 0) + startOamDma(lOamDmaUpdate - 2); + + ioamhram[src & 0xFF] = data; + } else if (oamDmaPos == 0xA0) { + endOamDma(lOamDmaUpdate - 2); + lOamDmaUpdate = COUNTER_DISABLED; + } + } + + nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cycleCounter); + } + + lastOamDmaUpdate = lOamDmaUpdate; + } + + cycleCounter += 4; + + dmaSource = dmaSrc; + dmaDestination = dmaDest; + ioamhram[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram[0x155] & 0x80); + + if (ioamhram[0x155] & 0x80) { + next_hdmaReschedule = next_dmatime = COUNTER_DISABLED; + hdma_transfer = 0; + } + + if (hdma_transfer) { + if (lastOamDmaUpdate != COUNTER_DISABLED) + updateOamDma(cycleCounter); + + next_dmatime = display.nextHdmaTime(cycleCounter); + } + } + + break; + case INTERRUPTS: +// printf("interrupts\n"); + update_irqEvents(cycleCounter); + ioamhram[0x10F] |= display.getIfReg(cycleCounter) & 3; + + { + /*unsigned interrupt = ioamhram[0x10F] & ioamhram[0x1FF]; + interrupt |= interrupt << 1; + interrupt |= interrupt << 2; + interrupt |= interrupt << 1; + interrupt = ~interrupt; + ++interrupt; + interrupt &= 0x1F; + + if (interrupt) { + ioamhram[0x10F] &= ~interrupt; + display.setIfReg(ioamhram[0x10F], CycleCounter); + IME = false; + + unsigned address = interrupt; + interrupt >>= 1; + address -= interrupt & 0x0C; + interrupt >>= 1; + address -= interrupt & 5; + address += interrupt >> 2; + + address <<= 3; + address += 0x38; + + z80.interrupt(address); + }*/ + + const unsigned interrupt = ioamhram[0x10F] & ioamhram[0x1FF] & 0x1F; + + if (interrupt) { + unsigned n; + unsigned address; + + if ((n = interrupt & 0x01)) + address = 0x40; + else if ((n = interrupt & 0x02)) + address = 0x48; + else if ((n = interrupt & 0x04)) + address = 0x50; + else if ((n = interrupt & 0x08)) + address = 0x58; + else { + n = 0x10; + address = 0x60; + } + + ioamhram[0x10F] &= ~n; + display.setIfReg(ioamhram[0x10F], cycleCounter); + IME = false; + cycleCounter = interrupter.interrupt(address, cycleCounter, *this); + } + } + + nextIntTime = IME ? std::min(next_irqEventTime, display.nextIrqEvent()) : static_cast(COUNTER_DISABLED); + break; + case BLIT: +// printf("blit\n"); + display.updateScreen(next_blittime); + + if (ioamhram[0x140] & 0x80) + next_blittime += 70224 << isDoubleSpeed(); + else + next_blittime = COUNTER_DISABLED; + + break; + case UNHALT: +// printf("unhalt\n"); + update_irqEvents(cycleCounter); + ioamhram[0x10F] |= display.getIfReg(cycleCounter) & 3; + + if (ioamhram[0x10F] & ioamhram[0x1FF] & 0x1F) { + next_unhalttime = COUNTER_DISABLED; + interrupter.unhalt(); + } else + next_unhalttime = std::min(next_irqEventTime, display.nextIrqEvent()) + isCgb() * 4; + + break; + case OAM: + nextOamEventTime = lastOamDmaUpdate == COUNTER_DISABLED ? static_cast(COUNTER_DISABLED) : nextOamEventTime + 0xA0 * 4; + break; + case END: + { + const unsigned long endtime = next_endtime; + next_endtime = COUNTER_DISABLED; + set_event(); + + while (cycleCounter >= next_eventtime) + cycleCounter = event(cycleCounter); + + next_endtime = endtime; + active = false; + } + + break; + } + + set_event(); + + return cycleCounter; +} + +void Memory::speedChange(const unsigned long cycleCounter) { + if (isCgb() && (ioamhram[0x14D] & 0x1)) { + std::printf("speedChange\n"); + + update_irqEvents(cycleCounter); + sound.generate_samples(cycleCounter, isDoubleSpeed()); + display.preSpeedChange(cycleCounter); + + ioamhram[0x14D] = ~ioamhram[0x14D] & 0x80; + doubleSpeed = ioamhram[0x14D] >> 7; + + display.postSpeedChange(cycleCounter); + + if (hdma_transfer) { + next_dmatime = display.nextHdmaTime(cycleCounter); + next_hdmaReschedule = display.nextHdmaTimeInvalid(); + } + + next_blittime = (ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : static_cast(COUNTER_DISABLED); + next_endtime = cycleCounter + (isDoubleSpeed() ?( next_endtime - cycleCounter) << 1 : ((next_endtime - cycleCounter) >> 1)); + set_irqEvent(); + rescheduleIrq(cycleCounter); + set_event(); + } +} + +static void decCycles(unsigned long &counter, const unsigned long dec) { + if (counter != Memory::COUNTER_DISABLED) + counter -= dec; +} + +unsigned long Memory::resetCounters(unsigned long cycleCounter) { + std::printf("resetting counters\n"); + + if (lastOamDmaUpdate != COUNTER_DISABLED) + updateOamDma(cycleCounter); + + update_irqEvents(cycleCounter); + rescheduleIrq(cycleCounter); + display.preResetCounter(cycleCounter); + + const unsigned long oldCC = cycleCounter; + + { + const unsigned long divinc = (cycleCounter - div_lastUpdate) >> 8; + ioamhram[0x104] = (ioamhram[0x104] + divinc) & 0xFF; + div_lastUpdate += divinc << 8; + } + + if (ioamhram[0x107] & 0x04) { + update_tima(cycleCounter); + } + + const unsigned long dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000; + + minIntTime = minIntTime < cycleCounter ? 0 : minIntTime - dec; + + if (ioamhram[0x107] & 0x04) + decCycles(tima_lastUpdate, dec); + + decCycles(div_lastUpdate, dec); + decCycles(lastOamDmaUpdate, dec); + decCycles(next_eventtime, dec); + decCycles(next_irqEventTime, dec); + decCycles(next_timatime, dec); + decCycles(next_blittime, dec); + decCycles(nextOamEventTime, dec); + decCycles(next_endtime, dec); + decCycles(next_dmatime, dec); + decCycles(next_hdmaReschedule, dec); + decCycles(nextIntTime, dec); + decCycles(next_serialtime, dec); + decCycles(tmatime, dec); + decCycles(next_unhalttime, dec); + + cycleCounter -= dec; + + display.postResetCounter(oldCC, cycleCounter); + sound.resetCounter(cycleCounter, oldCC, isDoubleSpeed()); + + return cycleCounter; +} + +void Memory::updateVideo(unsigned cycleCounter) { + return display.update(cycleCounter); +} + +unsigned Memory::lyCounter(unsigned cycleCounter) { + return display.getLyReg(cycleCounter); +} + +void Memory::updateInput() { + unsigned joypadId = 0x0F; + unsigned button = 0xFF; + unsigned dpad = 0xFF; + + if (getInput) { + const Gambatte::InputState &is = (*getInput)(); + + joypadId = is.joypadId; + + button ^= is.startButton << 3; + button ^= is.selectButton << 2; + button ^= is.bButton << 1; + button ^= is.aButton; + + dpad ^= is.dpadDown << 3; + dpad ^= is.dpadUp << 2; + dpad ^= is.dpadLeft << 1; + dpad ^= is.dpadRight; + } + + ioamhram[0x100] |= 0xF; + + if ((ioamhram[0x100] & 0x30) == 0x30) { + ioamhram[0x100] &= 0xf0; + ioamhram[0x100] |= joypadId; + } else { + if (!(ioamhram[0x100] & 0x10)) + ioamhram[0x100] &= dpad; + + if (!(ioamhram[0x100] & 0x20)) + ioamhram[0x100] &= button; + } +} + +void Memory::setRombank() { + unsigned bank = rombank; + + if ((romtype == mbc1 && !(bank & 0x1F)) || (romtype == mbc5 && !bank)) + ++bank; + + romdata[1] = romdata[0] + bank * 0x4000ul - 0x4000; + + if (oamDmaArea1Lower != 0xA0) { + rmem[0x7] = rmem[0x6] = rmem[0x5] = rmem[0x4] = romdata[1]; + } else + setOamDmaSrc(); +} + +void Memory::setRambank() { + rmem[0xB] = rmem[0xA] = rsrambankptr = rdisabled_ram - 0xA000; + wmem[0xB] = wmem[0xA] = wsrambankptr = wdisabled_ram - 0xA000; + + if (enable_ram) { + if (rtc.getActive()) { + wmem[0xB] = wmem[0xA] = rmem[0xB] = rmem[0xA] = wsrambankptr = rsrambankptr = NULL; + } else if (rambanks) { + wmem[0xB] = rmem[0xB] = wmem[0xA] = rmem[0xA] = wsrambankptr = rsrambankptr = rambankdata + rambank * 0x2000ul - 0xA000; + } + } + + if (oamDmaArea1Lower == 0xA0) { + wmem[0xB] = wmem[0xA] = rmem[0xB] = rmem[0xA] = NULL; + setOamDmaSrc(); + } +} + +void Memory::setBanks() { + rmem[0x3] = rmem[0x2] = rmem[0x1] = rmem[0x0] = romdata[0]; + + setRombank(); + setRambank(); + + rmem[0xC] = wmem[0xC] = wramdata[0] - 0xC000; + rmem[0xD] = wmem[0xD] = wramdata[1] - 0xD000; + rmem[0xE] = wmem[0xE] = wramdata[0] - 0xE000; +} + +void Memory::updateOamDma(const unsigned long cycleCounter) { + unsigned cycles = (cycleCounter - lastOamDmaUpdate) >> 2; + + while (cycles--) { + oamDmaPos = (oamDmaPos + 1) & 0xFF; + lastOamDmaUpdate += 4; + + //TODO: reads from vram while the ppu is reading vram should return whatever the ppu is reading. + if (oamDmaPos < 0xA0) { + if (oamDmaPos == 0) + startOamDma(lastOamDmaUpdate - 2); + + ioamhram[oamDmaPos] = oamDmaSrc ? oamDmaSrc[oamDmaPos] : *rtc.getActive(); + } else if (oamDmaPos == 0xA0) { + endOamDma(lastOamDmaUpdate - 2); + lastOamDmaUpdate = COUNTER_DISABLED; + break; + } + } +} + +void Memory::setOamDmaArea() { + if (ioamhram[0x146] < 0xC0) { + if ((ioamhram[0x146] & 0xE0) != 0x80) + oamDmaArea2Upper = 0x80; + + oamDmaArea1Width = 0x20; + } else if (ioamhram[0x146] < 0xE0) + oamDmaArea1Width = 0x3E; +} + +void Memory::oamDmaInitSetup() { + if (ioamhram[0x146] < 0xC0) { + if ((ioamhram[0x146] & 0xE0) == 0x80) { + oamDmaArea1Lower = 0x80; + } else { + oamDmaArea1Lower = 0xA0; + std::fill_n(rmem, 0x8, static_cast(NULL)); + rmem[0xB] = rmem[0xA] = NULL; + wmem[0xB] = wmem[0xA] = NULL; + } + } else if (ioamhram[0x146] < 0xE0) { + oamDmaArea1Lower = 0xC0; + rmem[0xE] = rmem[0xD] = rmem[0xC] = NULL; + wmem[0xE] = wmem[0xD] = wmem[0xC] = NULL; + } +} + +void Memory::setOamDmaSrc() { + oamDmaSrc = NULL; + + if (ioamhram[0x146] < 0xC0) { + if ((ioamhram[0x146] & 0xE0) == 0x80) { + oamDmaSrc = vrambank + (ioamhram[0x146] << 8 & 0x1FFF); + } else { + if (ioamhram[0x146] < 0x80) + oamDmaSrc = romdata[ioamhram[0x146] >> 6] + (ioamhram[0x146] << 8); + else if (rsrambankptr) + oamDmaSrc = rsrambankptr + (ioamhram[0x146] << 8); + } + } else if (ioamhram[0x146] < 0xE0) { + oamDmaSrc = wramdata[ioamhram[0x146] >> 4 & 1] + (ioamhram[0x146] << 8 & 0xFFF); + } else + oamDmaSrc = rdisabled_ram; +} + +void Memory::startOamDma(const unsigned long cycleCounter) { + setOamDmaArea(); + display.oamChange(rdisabled_ram, cycleCounter); + + if (next_unhalttime != COUNTER_DISABLED) + schedule_unhalt(); + else + rescheduleIrq(cycleCounter); + + rescheduleHdmaReschedule(); +} + +void Memory::endOamDma(const unsigned long cycleCounter) { + oamDmaArea2Upper = oamDmaArea1Width = oamDmaArea1Lower = 0; + oamDmaPos = 0xFE; + setBanks(); + display.oamChange(ioamhram, cycleCounter); + + if (next_unhalttime != COUNTER_DISABLED) + schedule_unhalt(); + else + rescheduleIrq(cycleCounter); + + rescheduleHdmaReschedule(); +} + +void Memory::update_tima(const unsigned long cycleCounter) { + const unsigned long ticks = (cycleCounter - tima_lastUpdate) >> timaClock[ioamhram[0x107] & 3]; + + tima_lastUpdate += ticks << timaClock[ioamhram[0x107] & 3]; + + if (cycleCounter >= tmatime) { + if (cycleCounter >= tmatime + 4) + tmatime = COUNTER_DISABLED; + + ioamhram[0x105] = ioamhram[0x106]; + } + + unsigned long tmp = ioamhram[0x105] + ticks; + + while (tmp > 0x100) + tmp -= 0x100 - ioamhram[0x106]; + + if (tmp == 0x100) { + tmp = 0; + tmatime = tima_lastUpdate + 3; + + if (cycleCounter >= tmatime) { + if (cycleCounter >= tmatime + 4) + tmatime = COUNTER_DISABLED; + + tmp = ioamhram[0x106]; + } + } + + ioamhram[0x105] = tmp; +} + +unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) { + if (lastOamDmaUpdate != COUNTER_DISABLED) + updateOamDma(cycleCounter); + + switch (P & 0x7F) { + case 0x00: + updateInput(); + break; + case 0x04: +// printf("div read\n"); + { + const unsigned long divcycles = (cycleCounter - div_lastUpdate) >> 8; + ioamhram[0x104] = (ioamhram[0x104] + divcycles) & 0xFF; + div_lastUpdate += divcycles << 8; + } + + break; + case 0x05: +// printf("tima read\n"); + if (ioamhram[0x107] & 0x04) + update_tima(cycleCounter); + + break; + case 0x0F: + update_irqEvents(cycleCounter); + ioamhram[0x10F] |= display.getIfReg(cycleCounter) & 3; +// rescheduleIrq(cycleCounter); + break; + case 0x26: +// printf("sound status read\n"); + if (ioamhram[0x126] & 0x80) { + sound.generate_samples(cycleCounter, isDoubleSpeed()); + ioamhram[0x126] = 0xF0 | sound.getStatus(); + } else + ioamhram[0x126] = 0x70; + + break; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + sound.generate_samples(cycleCounter, isDoubleSpeed()); + return sound.waveRamRead(P & 0xF); + case 0x41: + return ioamhram[0x141] | display.get_stat(ioamhram[0x145], cycleCounter); + case 0x44: + return display.getLyReg(cycleCounter/*+4*/); + case 0x69: + return display.cgbBgColorRead(ioamhram[0x168] & 0x3F, cycleCounter); + case 0x6B: + return display.cgbSpColorRead(ioamhram[0x16A] & 0x3F, cycleCounter); + default: break; + } + + return ioamhram[P - 0xFE00]; +} + +unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCounter) { + if (P < 0xFF80) { + if (lastOamDmaUpdate != COUNTER_DISABLED) { + updateOamDma(cycleCounter); + + if ((P >> 8) - oamDmaArea1Lower < oamDmaArea1Width || P >> 8 < oamDmaArea2Upper) + return ioamhram[oamDmaPos]; + } + + if (P < 0xC000) { + if (P < 0x8000) + return romdata[P >> 14][P]; + + if (P < 0xA000) { + if (!display.vramAccessible(cycleCounter)) + return 0xFF; + + return vrambank[P & 0x1FFF]; + } + + if (rsrambankptr) + return rsrambankptr[P]; + + return *rtc.getActive(); + } + + if (P < 0xFE00) + return wramdata[P >> 12 & 1][P & 0xFFF]; + + if (P & 0x100) + return nontrivial_ff_read(P, cycleCounter); + + if (!display.oamAccessible(cycleCounter) || oamDmaPos < 0xA0) + return 0xFF; + } + + return ioamhram[P - 0xFE00]; +} + +void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) { +// printf("mem[0x%X] = 0x%X\n", P, data); + + if (lastOamDmaUpdate != COUNTER_DISABLED) + updateOamDma(cycleCounter); + + switch (P & 0xFF) { + case 0x00: + data = (ioamhram[0x100] & 0xCF) | (data & 0xF0); + if(memoryInterface) memoryInterface->joypWrite(data & 0x20, data & 0x10); + break; + case 0x01: + update_irqEvents(cycleCounter); + break; + case 0x02: + update_irqEvents(cycleCounter); + + if ((data & 0x81) == 0x81) { + next_serialtime = cycleCounter; + next_serialtime += (isCgb() && (data & 0x2)) ? 128 : 4096; + set_irqEvent(); + } + + rescheduleIrq(cycleCounter); + data |= 0x7C; + break; + //If rom is trying to write to DIV register, reset it to 0. + case 0x04: +// printf("DIV write\n"); + ioamhram[0x104] = 0; + div_lastUpdate = cycleCounter; + return; + case 0x05: + // printf("tima write\n"); + if (ioamhram[0x107] & 0x04) { + update_irqEvents(cycleCounter); + update_tima(cycleCounter); + + if (tmatime - cycleCounter < 4) + tmatime = COUNTER_DISABLED; + + next_timatime = tima_lastUpdate + ((256u - data) << timaClock[ioamhram[0x107] & 3]) + 1; + set_irqEvent(); + rescheduleIrq(cycleCounter); + } + + break; + case 0x06: + if (ioamhram[0x107] & 0x04) { + update_irqEvents(cycleCounter); + update_tima(cycleCounter); + } + + break; + case 0x07: + // printf("tac write: %i\n", data); + data |= 0xF8; + + if (ioamhram[0x107] ^ data) { + if (ioamhram[0x107] & 0x04) { + update_irqEvents(cycleCounter); + update_tima(cycleCounter); + + tima_lastUpdate -= (1u << (timaClock[ioamhram[0x107] & 3] - 1)) + 3; + tmatime -= (1u << (timaClock[ioamhram[0x107] & 3] - 1)) + 3; + next_timatime -= (1u << (timaClock[ioamhram[0x107] & 3] - 1)) + 3; + set_irqEvent(); + update_tima(cycleCounter); + update_irqEvents(cycleCounter); + + tmatime = COUNTER_DISABLED; + next_timatime = COUNTER_DISABLED; + } + + if (data & 4) { + tima_lastUpdate = (cycleCounter >> timaClock[data & 3]) << timaClock[data & 3]; + next_timatime = tima_lastUpdate + ((256u - ioamhram[0x105]) << timaClock[data & 3]) + 1; + } + + set_irqEvent(); + rescheduleIrq(cycleCounter); + } + + break; + case 0x0F: + update_irqEvents(cycleCounter); + display.setIfReg(data, cycleCounter); + ioamhram[0x10F] = 0xE0 | data; + rescheduleIrq(cycleCounter); + return; + case 0x10: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr10(data); + data |= 0x80; + break; + case 0x11: + if(!sound.isEnabled()) { + if (isCgb()) + return; + + data &= 0x3F; + } + + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr11(data); + data |= 0x3F; + break; + case 0x12: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr12(data); + break; + case 0x13: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr13(data); + return; + case 0x14: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr14(data); + data |= 0xBF; + break; + case 0x16: + if(!sound.isEnabled()) { + if (isCgb()) + return; + + data &= 0x3F; + } + + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr21(data); + data |= 0x3F; + break; + case 0x17: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr22(data); + break; + case 0x18: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr23(data); + return; + case 0x19: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr24(data); + data |= 0xBF; + break; + case 0x1A: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr30(data); + data |= 0x7F; + break; + case 0x1B: + if(!sound.isEnabled() && isCgb()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr31(data); + return; + case 0x1C: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr32(data); + data |= 0x9F; + break; + case 0x1D: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr33(data); + return; + case 0x1E: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr34(data); + data |= 0xBF; + break; + case 0x20: + if(!sound.isEnabled() && isCgb()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr41(data); + return; + case 0x21: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr42(data); + break; + case 0x22: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr43(data); + break; + case 0x23: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr44(data); + data |= 0xBF; + break; + case 0x24: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_so_volume(data); + break; + case 0x25: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.map_so(data); + break; + case 0x26: + if ((ioamhram[0x126] ^ data) & 0x80) { + sound.generate_samples(cycleCounter, isDoubleSpeed()); + + if (!(data & 0x80)) { + for (unsigned i = 0xFF10; i < 0xFF26; ++i) + ff_write(i, 0, cycleCounter); + +// std::memcpy(memory + 0xFF10, soundRegInitValues, sizeof(soundRegInitValues)); + sound.setEnabled(false); + } else { + sound.reset(/*memory + 0xFF00, isDoubleSpeed()*/); + sound.setEnabled(true); + } + } + + data = (data & 0x80) | (ioamhram[0x126] & 0x7F); + break; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.waveRamWrite(P & 0xF, data); + break; + case 0x40: + if (ioamhram[0x140] != data) { + if ((ioamhram[0x140] ^ data) & 0x80) { + update_irqEvents(cycleCounter); + const unsigned lyc = display.get_stat(ioamhram[0x145], cycleCounter) & 4; + display.enableChange(cycleCounter); + ioamhram[0x144] = 0; +// enable_display = bool(data & 0x80); + ioamhram[0x141] &= 0xF8; + + if (data & 0x80) { + next_blittime = display.nextMode1IrqTime() + (70224 << isDoubleSpeed()); + } else { + ioamhram[0x141] |= lyc; //Mr. Do! needs conicidence flag preserved. + next_blittime = cycleCounter + (456 * 4 << isDoubleSpeed()); + + if (hdma_transfer) + next_dmatime = cycleCounter; + + next_hdmaReschedule = COUNTER_DISABLED; + } + + set_event(); + } + + if ((ioamhram[0x140] ^ data) & 0x4) { + display.spriteSizeChange(data & 0x4, cycleCounter); + } + + if ((ioamhram[0x140] ^ data) & 0x20) { +// printf("%u: weChange to %u\n", CycleCounter, (data & 0x20) != 0); + display.weChange(data & 0x20, cycleCounter); + } + + if ((ioamhram[0x140] ^ data) & 0x40) + display.wdTileMapSelectChange(data & 0x40, cycleCounter); + + if ((ioamhram[0x140] ^ data) & 0x08) + display.bgTileMapSelectChange(data & 0x08, cycleCounter); + + if ((ioamhram[0x140] ^ data) & 0x10) + display.bgTileDataSelectChange(data & 0x10, cycleCounter); + + if ((ioamhram[0x140] ^ data) & 0x02) + display.spriteEnableChange(data & 0x02, cycleCounter); + + if ((ioamhram[0x140] ^ data) & 0x01) + display.bgEnableChange(data & 0x01, cycleCounter); + + ioamhram[0x140] = data; + rescheduleIrq(cycleCounter); + rescheduleHdmaReschedule(); + } + + return; + case 0x41: + display.lcdstatChange(data, cycleCounter); + rescheduleIrq(cycleCounter); + data = (ioamhram[0x141] & 0x87) | (data & 0x78); + break; + case 0x42: + display.scyChange(data, cycleCounter); + break; + case 0x43: + display.scxChange(data, cycleCounter); + rescheduleIrq(cycleCounter); + rescheduleHdmaReschedule(); + break; + //If rom is trying to write to LY register, reset it to 0. + case 0x44: + if (ioamhram[0x140] & 0x80) { + std::printf("ly write\n"); + display.lyWrite(cycleCounter); + rescheduleIrq(cycleCounter); + rescheduleHdmaReschedule(); + } + + return; + case 0x45: + display.lycRegChange(data, cycleCounter); + rescheduleIrq(cycleCounter); + break; + case 0x46: + if (lastOamDmaUpdate != COUNTER_DISABLED) + endOamDma(cycleCounter); + + lastOamDmaUpdate = cycleCounter; + nextOamEventTime = cycleCounter + 8; + ioamhram[0x146] = data; + oamDmaInitSetup(); + setOamDmaSrc(); + return; + case 0x47: + if (!isCgb()) { + display.dmgBgPaletteChange(data, cycleCounter); + } + + break; + case 0x48: + if (!isCgb()) { + display.dmgSpPalette1Change(data, cycleCounter); + } + + break; + case 0x49: + if (!isCgb()) { + display.dmgSpPalette2Change(data, cycleCounter); + } + + break; + case 0x4A: +// printf("%u: wyChange to %u\n", CycleCounter, data); + display.wyChange(data, cycleCounter); + rescheduleIrq(cycleCounter); + rescheduleHdmaReschedule(); + break; + case 0x4B: +// printf("%u: wxChange to %u\n", CycleCounter, data); + display.wxChange(data, cycleCounter); + rescheduleIrq(cycleCounter); + rescheduleHdmaReschedule(); + break; + + //cgb stuff: + case 0x4D: + ioamhram[0x14D] |= data & 0x01; + return; + //Select vram bank + case 0x4F: + if (isCgb()) { + vrambank = vram + (data & 0x01) * 0x2000; + + if (oamDmaArea1Lower == 0x80) + setOamDmaSrc(); + + ioamhram[0x14F] = 0xFE | data; + } + + return; + case 0x51: + dmaSource = data << 8 | (dmaSource & 0xFF); + return; + case 0x52: + dmaSource = (dmaSource & 0xFF00) | (data & 0xF0); + return; + case 0x53: + dmaDestination = data << 8 | (dmaDestination & 0xFF); + return; + case 0x54: + dmaDestination = (dmaDestination & 0xFF00) | (data & 0xF0); + return; + case 0x55: + if (!isCgb()) + return; + + ioamhram[0x155] = data & 0x7F; + + if (hdma_transfer) { + if (!(data & 0x80)) { + ioamhram[0x155] |= 0x80; + + if (next_dmatime > cycleCounter) { + hdma_transfer = 0; + next_hdmaReschedule = next_dmatime = COUNTER_DISABLED; + set_event(); + } + } + + return; + } + + if (data & 0x80) { + hdma_transfer = 1; + + if (!(ioamhram[0x140] & 0x80) || display.isHdmaPeriod(cycleCounter)) { + next_dmatime = cycleCounter; + next_hdmaReschedule = COUNTER_DISABLED; + } else { + next_dmatime = display.nextHdmaTime(cycleCounter); + next_hdmaReschedule = display.nextHdmaTimeInvalid(); + } + } else + next_dmatime = cycleCounter; + + set_event(); + return; + case 0x56: + if (isCgb()) { + ioamhram[0x156] = data | 0x3E; + } + + return; + //Set bg palette index + case 0x68: + if (isCgb()) + ioamhram[0x168] = data | 0x40; + + return; + //Write to bg palette data + case 0x69: + if (isCgb()) { + const unsigned index = ioamhram[0x168] & 0x3F; + + display.cgbBgColorChange(index, data, cycleCounter); + + ioamhram[0x168] = (ioamhram[0x168] & ~0x3F) | ((index + (ioamhram[0x168] >> 7)) & 0x3F); + } + + return; + case 0x6A: + if (isCgb()) + ioamhram[0x16A] = data | 0x40; + + return; + //Write to obj palette data. + case 0x6B: + if (isCgb()) { + const unsigned index = ioamhram[0x16A] & 0x3F; + + display.cgbSpColorChange(index, data, cycleCounter); + + ioamhram[0x16A] = (ioamhram[0x16A] & ~0x3F) | ((index + (ioamhram[0x16A] >> 7)) & 0x3F); + } + + return; + case 0x6C: + if (isCgb()) + ioamhram[0x16C] = data | 0xFE; + + return; + case 0x70: + if (isCgb()) { + wramdata[1] = wramdata[0] + ((data & 0x07) ? (data & 0x07) : 1) * 0x1000; + + if (oamDmaArea1Lower == 0xC0) + setOamDmaSrc(); + else + wmem[0xD] = rmem[0xD] = wramdata[1] - 0xD000; + + ioamhram[0x170] = data | 0xF8; + } + + return; + case 0x72: + case 0x73: + case 0x74: + if (isCgb()) + break; + + return; + case 0x75: + if (isCgb()) + ioamhram[0x175] = data | 0x8F; + + return; + case 0xFF: + ioamhram[0x1FF] = data; + rescheduleIrq(cycleCounter); + return; + default: +// if (P < 0xFF80) + return; + } + + ioamhram[P - 0xFE00] = data; +} + +void Memory::mbc_write(const unsigned P, const unsigned data) { +// printf("mem[0x%X] = 0x%X\n", P, data); + + switch (P >> 12 & 0x7) { + case 0x0: + case 0x1: //Most MBCs write 0x?A to addresses lower than 0x2000 to enable ram. + if (romtype == mbc2 && (P & 0x0100)) break; + + enable_ram = (data & 0x0F) == 0xA; + + if (rtcRom) + rtc.setEnabled(enable_ram); + + setRambank(); + break; + //MBC1 writes ???n nnnn to address area 0x2000-0x3FFF, ???n nnnn makes up the lower digits to determine which rombank to load. + //MBC3 writes ?nnn nnnn to address area 0x2000-0x3FFF, ?nnn nnnn makes up the lower digits to determine which rombank to load. + //MBC5 writes nnnn nnnn to address area 0x2000-0x2FFF, nnnn nnnn makes up the lower digits to determine which rombank to load. + //MBC5 writes bit8 of the number that determines which rombank to load to address 0x3000-0x3FFF. + case 0x2: + switch (romtype) { + case plain: + return; + case mbc5: + rombank = (rombank & 0x100) | data; + rombank = rombank & (rombanks - 1); + setRombank(); + return; + default: + break; //Only supposed to break one level. + } + case 0x3: + switch (romtype) { + case mbc1: + rombank = rambank_mode ? data & 0x1F : ((rombank & 0x60) | (data & 0x1F)); + break; + case mbc2: + if (P & 0x0100) { + rombank = data & 0x0F; + break; + } + + return; + case mbc3: + rombank = data & 0x7F; + break; + case mbc5: + rombank = (data & 0x1) << 8 | (rombank & 0xFF); + break; + default: + return; + } + + rombank = rombank & (rombanks - 1); + setRombank(); + break; + //MBC1 writes ???? ??nn to area 0x4000-0x5FFF either to determine rambank to load, or upper 2 bits of the rombank number to load, depending on rom-mode. + //MBC3 writes ???? ??nn to area 0x4000-0x5FFF to determine rambank to load + //MBC5 writes ???? nnnn to area 0x4000-0x5FFF to determine rambank to load + case 0x4: + case 0x5: + switch (romtype) { + case mbc1: + if (rambank_mode) { + rambank = data & 0x03; + break; + } + + rombank = (data & 0x03) << 5 | (rombank & 0x1F); + rombank = rombank & (rombanks - 1); + setRombank(); + return; + case mbc3: + if (rtcRom) + rtc.swapActive(data); + + rambank = data & 0x03; + break; + case mbc5: + rambank = data & 0x0F; + break; + default: + return; + } + + rambank &= rambanks - 1; + setRambank(); + break; + //MBC1: If ???? ???1 is written to area 0x6000-0x7FFFF rom will be set to rambank mode. + case 0x6: + case 0x7: + switch (romtype) { + case mbc1: + rambank_mode = data & 0x01; + break; + case mbc3: + rtc.latch(data); + break; + default: + break; + } + + break; +// default: break; + } +} + +void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { + if (lastOamDmaUpdate != COUNTER_DISABLED) { + updateOamDma(cycleCounter); + + if ((P >> 8) - oamDmaArea1Lower < oamDmaArea1Width || P >> 8 < oamDmaArea2Upper) { + ioamhram[oamDmaPos] = data; + return; + } + } + + if (P < 0xFE00) { + if (P < 0xA000) { + if (P < 0x8000) { + mbc_write(P, data); + } else if (display.vramAccessible(cycleCounter)) { + display.vramChange(cycleCounter); + vrambank[P & 0x1FFF] = data; + } + } else if (P < 0xC000) { + if (wsrambankptr) + wsrambankptr[P] = data; + else + rtc.write(data); + } else + wramdata[P >> 12 & 1][P & 0xFFF] = data; + } else if (((P + 1) & 0xFFFF) < 0xFF81) { + if (P < 0xFF00) { + if (display.oamAccessible(cycleCounter) && oamDmaPos >= 0xA0) { + display.oamChange(cycleCounter); + rescheduleIrq(cycleCounter); + rescheduleHdmaReschedule(); + ioamhram[P - 0xFE00] = data; + } + } else + nontrivial_ff_write(P, data, cycleCounter); + } else + ioamhram[P - 0xFE00] = data; +} + +static const std::string stripExtension(const std::string &str) { + const std::string::size_type lastDot = str.find_last_of('.'); + const std::string::size_type lastSlash = str.find_last_of('/'); + + if (lastDot != std::string::npos && (lastSlash == std::string::npos || lastSlash < lastDot)) + return str.substr(0, lastDot); + + return str; +} + +static const std::string stripDir(const std::string &str) { + const std::string::size_type lastSlash = str.find_last_of('/'); + + if (lastSlash != std::string::npos) + return str.substr(lastSlash + 1); + + return str; +} + +const std::string Memory::saveBasePath() const { + return saveDir.empty() ? defaultSaveBasePath : saveDir + stripDir(defaultSaveBasePath); +} + +void Memory::set_savedir(const char *dir) { + saveDir = dir ? dir : ""; + + if (!saveDir.empty() && saveDir[saveDir.length() - 1] != '/') { + saveDir += '/'; + } +} + +static void enforce8bit(unsigned char *data, unsigned long sz) { + if (static_cast(0x100)) + while (sz--) + *data++ &= 0xFF; +} + +static unsigned pow2ceil(unsigned n) { + --n; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + ++n; + + return n; +} + +bool Memory::loadROM(const bool forceDmg) { + if (!memoryInterface) return 1; + + defaultSaveBasePath = stripExtension(""); //(romfile); + + File rom(""); + + //if (!rom.is_open()) { + // return 1; + //} + + Gambatte::MemoryBuffer romBuffer = memoryInterface->loadRomData(); + if(romBuffer.size < 0x150) return 1; + + { + unsigned char *header = (unsigned char*)romBuffer.data; + //rom.read(reinterpret_cast(header), sizeof(header)); + + cgb = header[0x0143] >> 7 & 1; + + if (cgb & forceDmg) { + cgb = false; + //defaultSaveBasePath += "_dmg"; + } + + switch (header[0x0147]) { + case 0x00: //std::printf("Plain ROM loaded.\n"); + romtype = plain; + break; + case 0x01: //std::printf("MBC1 ROM loaded.\n"); + romtype = mbc1; + break; + case 0x02: //std::printf("MBC1 ROM+RAM loaded.\n"); + romtype = mbc1; + break; + case 0x03: //std::printf("MBC1 ROM+RAM+BATTERY loaded.\n"); + romtype = mbc1; + battery = 1; + break; + case 0x05: //std::printf("MBC2 ROM loaded.\n"); + romtype = mbc2; + break; + case 0x06: //std::printf("MBC2 ROM+BATTERY loaded.\n"); + romtype = mbc2; + battery = 1; + break; + case 0x08: //std::printf("Plain ROM with additional RAM loaded.\n"); + break; + case 0x09: //std::printf("Plain ROM with additional RAM and Battery loaded.\n"); + battery = 1; + break; + case 0x0B: /*cout << "MM01 ROM not supported.\n";*/ + return 1; + break; + case 0x0C: /*cout << "MM01 ROM not supported.\n";*/ + return 1; + break; + case 0x0D: /*cout << "MM01 ROM not supported.\n";*/ + return 1; + break; + case 0x0F: //std::printf("MBC3 ROM+TIMER+BATTERY loaded.\n"); + romtype = mbc3; + battery = true; + rtcRom = true; + break; + case 0x10: //std::printf("MBC3 ROM+TIMER+RAM+BATTERY loaded.\n"); + romtype = mbc3; + battery = true; + rtcRom = true; + break; + case 0x11: //std::printf("MBC3 ROM loaded.\n"); + romtype = mbc3; + break; + case 0x12: //std::printf("MBC3 ROM+RAM loaded.\n"); + romtype = mbc3; + break; + case 0x13: //std::printf("MBC3 ROM+RAM+BATTERY loaded.\n"); + romtype = mbc3; + battery = 1; + break; + case 0x15: /*cout << "MBC4 ROM not supported.\n";*/ + return 1; + break; + case 0x16: /*cout << "MBC4 ROM not supported.\n";*/ + return 1; + break; + case 0x17: /*cout << "MBC4 ROM not supported.\n";*/ + return 1; + break; + case 0x19: //std::printf("MBC5 ROM loaded.\n"); + romtype = mbc5; + break; + case 0x1A: //std::printf("MBC5 ROM+RAM loaded.\n"); + romtype = mbc5; + break; + case 0x1B: //std::printf("MBC5 ROM+RAM+BATTERY loaded.\n"); + romtype = mbc5; + battery = 1; + break; + case 0x1C: //std::printf("MBC5+RUMLE ROM not supported.\n"); + romtype = mbc5; + break; + case 0x1D: //std::printf("MBC5+RUMLE+RAM ROM not suported.\n"); + romtype = mbc5; + break; + case 0x1E: //std::printf("MBC5+RUMLE+RAM+BATTERY ROM not supported.\n"); + romtype = mbc5; + battery = 1; + break; + case 0xFC: /*cout << "Pocket Camera ROM not supported.\n";*/ + return 1; + break; + case 0xFD: /*cout << "Bandai TAMA5 ROM not supported.\n";*/ + return 1; + break; + case 0xFE: /*cout << "HuC3 ROM not supported.\n";*/ + return 1; + break; + case 0xFF: /*cout << "HuC1 ROM not supported.\n";*/ + return 1; + break; + default: /*cout << "Wrong data-format, corrupt or unsupported ROM loaded.\n";*/ + return 1; + } + + /*switch (header[0x0148]) { + case 0x00: + rombanks = 2; + break; + case 0x01: + rombanks = 4; + break; + case 0x02: + rombanks = 8; + break; + case 0x03: + rombanks = 16; + break; + case 0x04: + rombanks = 32; + break; + case 0x05: + rombanks = 64; + break; + case 0x06: + rombanks = 128; + break; + case 0x07: + rombanks = 256; + break; + case 0x08: + rombanks = 512; + break; + case 0x52: + rombanks = 72; + break; + case 0x53: + rombanks = 80; + break; + case 0x54: + rombanks = 96; + break; + default: + return 1; + } + + printf("rombanks: %u\n", rombanks);*/ + + switch (header[0x0149]) { + case 0x00: /*cout << "No RAM\n";*/ rambanks = romtype == mbc2; break; + case 0x01: /*cout << "2kB RAM\n";*/ /*rambankrom=1; break;*/ + case 0x02: /*cout << "8kB RAM\n";*/ + rambanks = 1; + break; + case 0x03: /*cout << "32kB RAM\n";*/ + rambanks = 4; + break; + case 0x04: /*cout << "128kB RAM\n";*/ + rambanks = 16; + break; + case 0x05: /*cout << "undocumented kB RAM\n";*/ + rambanks = 16; + break; + default: /*cout << "Wrong data-format, corrupt or unsupported ROM loaded.\n";*/ + rambanks = 16; + break; + } + } + + //std::printf("rambanks: %u\n", rambanks); + + rombanks = pow2ceil(romBuffer.size / 0x4000); + //std::printf("rombanks: %u\n", romBuffer.size / 0x4000); + + delete []memchunk; + memchunk = new unsigned char[0x4000 + rombanks * 0x4000ul + rambanks * 0x2000ul + (isCgb() ? 0x8000 : 0x2000) + 0x4000]; + + romdata[0] = memchunk + 0x4000; + rambankdata = romdata[0] + rombanks * 0x4000ul; + wramdata[0] = rambankdata + rambanks * 0x2000; + rdisabled_ram = wramdata[0] + (isCgb() ? 0x8000 : 0x2000); + wdisabled_ram = rdisabled_ram + 0x2000; + + wramdata[1] = wramdata[0] + 0x1000; + std::memset(rdisabled_ram, 0xFF, 0x2000); + + //rom.rewind(); + //rom.read(reinterpret_cast(romdata[0]), (romBuffer.size / 0x4000) * 0x4000ul); + memcpy(romdata[0], romBuffer.data, + std::min( romBuffer.size, (unsigned)((romBuffer.size / 0x4000) * 0x4000ul) ) + ); + + // In case rombanks isn't a power of 2, allocate a disabled area for invalid rombank addresses. This is only based on speculation. + std::memset(romdata[0] + (romBuffer.size / 0x4000) * 0x4000ul, 0xFF, (rombanks - romBuffer.size / 0x4000) * 0x4000ul); + enforce8bit(romdata[0], rombanks * 0x4000ul); + + //if (rom.fail()) + // return 1; + + sound.init(isCgb()); + display.reset(ioamhram, isCgb()); + + return 0; +} + +void Memory::loadSavedata() { + //const std::string &sbp = saveBasePath(); + + if (battery && memoryInterface) { +/* std::ifstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::in); + + if (file.is_open()) { + file.read(reinterpret_cast(rambankdata), rambanks * 0x2000ul); + enforce8bit(rambankdata, rambanks * 0x2000ul); + } */ + + Gambatte::MemoryBuffer buffer = memoryInterface->loadRamData(); + if (buffer.data) { + memcpy(rambankdata, buffer.data, std::min(buffer.size, (unsigned)(rambanks * 0x2000ul))); + enforce8bit(rambankdata, rambanks * 0x2000ul); + } + } + + if (rtcRom && memoryInterface) { +/* std::ifstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::in); + + if (file.is_open()) { + unsigned long basetime = file.get() & 0xFF; + + basetime = basetime << 8 | (file.get() & 0xFF); + basetime = basetime << 8 | (file.get() & 0xFF); + basetime = basetime << 8 | (file.get() & 0xFF); + + rtc.setBaseTime(basetime); + } */ + + Gambatte::MemoryBuffer buffer = memoryInterface->loadRtcData(); + if (buffer.data && buffer.size >= 4) { + unsigned long basetime = 0; + uint8_t *data = (uint8_t*)buffer.data; + + basetime = basetime << 8 | (data[0]); + basetime = basetime << 8 | (data[1]); + basetime = basetime << 8 | (data[2]); + basetime = basetime << 8 | (data[3]); + + rtc.setBaseTime(basetime); + } + } +} + +void Memory::saveSavedata() { + //const std::string &sbp = saveBasePath(); + + if (battery && memoryInterface) { +/* std::ofstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::out); + + file.write(reinterpret_cast(rambankdata), rambanks * 0x2000ul); */ + + Gambatte::MemoryBuffer buffer = memoryInterface->saveRamData(rambanks * 0x2000ul); + if (buffer.data) { + memcpy(buffer.data, rambankdata, std::min(buffer.size, (unsigned)(rambanks * 0x2000ul))); + } + } + + if (rtcRom && memoryInterface) { +/* std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out); + const unsigned long basetime = rtc.getBaseTime(); + + file.put(basetime >> 24 & 0xFF); + file.put(basetime >> 16 & 0xFF); + file.put(basetime >> 8 & 0xFF); + file.put(basetime & 0xFF); */ + + Gambatte::MemoryBuffer buffer = memoryInterface->saveRtcData(); + if (buffer.data && buffer.size >= 4) { + const unsigned long basetime = rtc.getBaseTime(); + uint8_t *data = (uint8_t*)buffer.data; + + data[0] = basetime >> 24; + data[1] = basetime >> 16; + data[2] = basetime >> 8; + data[3] = basetime >> 0; + } + } +} + +unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) { + sound.generate_samples(cycleCounter, isDoubleSpeed()); + return sound.fillBuffer(); +} + +void Memory::setVideoBlitter(Gambatte::VideoBlitter *const vb) { + display.setVideoBlitter(vb); +} + +void Memory::videoBufferChange() { + display.videoBufferChange(); +} + +void Memory::setVideoFilter(const unsigned int n) { + display.setVideoFilter(n); +} + +void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32) { + display.setDmgPaletteColor(palNum, colorNum, rgb32); +} + +Memory::~Memory() { + saveSavedata(); + + delete []memchunk; +} diff --git a/src/lib/libgambatte/src/memory.h b/src/lib/libgambatte/src/memory.h new file mode 100644 index 00000000..90c665c1 --- /dev/null +++ b/src/lib/libgambatte/src/memory.h @@ -0,0 +1,243 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MEMORY_H +#define MEMORY_H + +class SaveState; + +#include "int.h" +#include "video.h" +#include "sound.h" + +#include "interrupter.h" +#include "rtc.h" +#include + +namespace Gambatte { +class InputStateGetter; +class MemoryInterface; +class FilterInfo; +} + +class Memory { +public: + enum { COUNTER_DISABLED = 0xFFFFFFFFu }; + +private: + enum cartridgetype { plain, mbc1, mbc2, mbc3, mbc5 }; + enum events { HDMA_RESCHEDULE, DMA, INTERRUPTS, BLIT, UNHALT, OAM, END }; + enum irqEvents { /*MODE0, MODE1, MODE2, LYC,*/ TIMA, /*M0RESC,*/ SERIAL }; + + unsigned char ioamhram[0x200]; + unsigned char vram[0x2000 * 2]; + unsigned char *rmem[0x10]; + unsigned char *wmem[0x10]; + + unsigned char *memchunk; + unsigned char *romdata[2]; + unsigned char *wramdata[2]; + unsigned char *rambankdata; + unsigned char *rdisabled_ram; + unsigned char *wdisabled_ram; + unsigned char *oamDmaSrc; + unsigned char *vrambank; + unsigned char *rsrambankptr; + unsigned char *wsrambankptr; + + Gambatte::InputStateGetter *getInput; + Gambatte::MemoryInterface *memoryInterface; + + unsigned long div_lastUpdate; + unsigned long tima_lastUpdate; + unsigned long next_timatime; + unsigned long next_blittime; + unsigned long nextIntTime; + unsigned long minIntTime; + unsigned long next_dmatime; + unsigned long next_hdmaReschedule; + unsigned long next_unhalttime; + unsigned long next_endtime; + unsigned long next_irqEventTime; + unsigned long tmatime; + unsigned long next_serialtime; + unsigned long next_eventtime; + unsigned long lastOamDmaUpdate; + unsigned long nextOamEventTime; + + LCD display; + PSG sound; + Interrupter interrupter; + Rtc rtc; + + events next_event; + irqEvents next_irqEvent; + cartridgetype romtype; + + std::string defaultSaveBasePath; + std::string saveDir; + + unsigned short rombanks; + unsigned short rombank; + unsigned short dmaSource; + unsigned short dmaDestination; + + unsigned char rambank; + unsigned char rambanks; + unsigned char oamDmaArea1Lower; + unsigned char oamDmaArea1Width; + unsigned char oamDmaArea2Upper; + unsigned char oamDmaPos; + + bool cgb; + bool doubleSpeed; + bool IME; + bool enable_ram; + bool rambank_mode; + bool battery, rtcRom; + bool hdma_transfer; + bool active; + + void updateInput(); + + void setRombank(); + void setRambank(); + void setBanks(); + void oamDmaInitSetup(); + void setOamDmaArea(); + void updateOamDma(unsigned long cycleCounter); + void startOamDma(unsigned long cycleCounter); + void endOamDma(unsigned long cycleCounter); + void setOamDmaSrc(); + + unsigned nontrivial_ff_read(unsigned P, unsigned long cycleCounter); + unsigned nontrivial_read(unsigned P, unsigned long cycleCounter); + void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter); + void mbc_write(unsigned P, unsigned data); + void nontrivial_write(unsigned P, unsigned data, unsigned long cycleCounter); + + void set_event(); + void set_irqEvent(); + void update_irqEvents(unsigned long cc); + void update_tima(unsigned long cycleCounter); + + void rescheduleIrq(unsigned long cycleCounter); + void rescheduleHdmaReschedule(); + + bool isDoubleSpeed() const { return doubleSpeed; } + +public: + Memory(const Interrupter &interrupter); + ~Memory(); + + void setStatePtrs(SaveState &state); + unsigned long saveState(SaveState &state, unsigned long cc); + void loadState(const SaveState &state, unsigned long oldCc); + void loadSavedata(); + void saveSavedata(); + const std::string saveBasePath() const; + + void setOsdElement(std::auto_ptr osdElement) { + display.setOsdElement(osdElement); + } + + void speedChange(unsigned long cycleCounter); + bool isCgb() const { return cgb; } + bool getIME() const { return IME; } + unsigned long getNextEventTime() const { return next_eventtime; } + + bool isActive() const { return active; } + + void ei(unsigned long cycleCounter); + + void di() { + IME = 0; + nextIntTime = COUNTER_DISABLED; + + if (next_event == INTERRUPTS) + set_event(); + +// next_eitime=0; +// if(next_event==EI) set_event(); + } + + unsigned ff_read(const unsigned P, const unsigned long cycleCounter) { + return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00]; + } + + unsigned read(const unsigned P, const unsigned long cycleCounter) { + return rmem[P >> 12] ? rmem[P >> 12][P] : nontrivial_read(P, cycleCounter); + } + + void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { + if (wmem[P >> 12]) + wmem[P >> 12][P] = data; + else + nontrivial_write(P, data, cycleCounter); + } + + void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { + if (((P + 1) & 0xFF) < 0x81) + nontrivial_ff_write(P, data, cycleCounter); + else + ioamhram[P - 0xFE00] = data; + } + + unsigned long event(unsigned long cycleCounter); + unsigned long resetCounters(unsigned long cycleCounter); + void updateVideo(unsigned cycleCounter); + unsigned lyCounter(unsigned cycleCounter); + + bool loadROM(bool forceDmg); + void set_savedir(const char *dir); + + void setInputStateGetter(Gambatte::InputStateGetter *getInput) { + this->getInput = getInput; + } + + void setMemoryInterface(Gambatte::MemoryInterface *memoryInterface) { + this->memoryInterface = memoryInterface; + } + + void schedule_unhalt(); + void incEndtime(unsigned long inc); + void setEndtime(unsigned long cc, unsigned long inc); + + void setSoundBuffer(Gambatte::uint_least32_t *const buf) { sound.setBuffer(buf); } + unsigned fillSoundBuffer(unsigned long cc); + void setVideoBlitter(Gambatte::VideoBlitter * vb); + void setVideoFilter(unsigned int n); + + void videoBufferChange(); + + unsigned videoWidth() const { + return display.videoWidth(); + } + + unsigned videoHeight() const { + return display.videoHeight(); + } + + std::vector filterInfo() const { + return display.filterInfo(); + } + + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32); +}; + +#endif diff --git a/src/lib/libgambatte/src/osd_element.h b/src/lib/libgambatte/src/osd_element.h new file mode 100644 index 00000000..2517d34f --- /dev/null +++ b/src/lib/libgambatte/src/osd_element.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef OSD_ELEMENT_H +#define OSD_ELEMENT_H + +#include "int.h" + +class OsdElement { +public: + enum Opacity { SEVEN_EIGHTHS, THREE_FOURTHS }; + +private: + Opacity opacity_; + unsigned x_; + unsigned y_; + unsigned w_; + unsigned h_; + +protected: + OsdElement(unsigned x = 0, unsigned y = 0, unsigned w = 0, unsigned h = 0, Opacity opacity = SEVEN_EIGHTHS) { + setPos(x, y); + setSize(w, h); + setOpacity(opacity); + } + + void setPos(unsigned x, unsigned y) { + x_ = x; + y_ = y; + } + + void setSize(unsigned w, unsigned h) { + w_ = w; + h_ = h; + } + + void setOpacity(Opacity opacity) { opacity_ = opacity; } + +public: + virtual ~OsdElement() {} + unsigned x() const { return x_; } + unsigned y() const { return y_; } + unsigned w() const { return w_; } + unsigned h() const { return h_; } + Opacity opacity() const { return opacity_; } + + virtual const Gambatte::uint_least32_t* update() = 0; +}; + +#endif diff --git a/src/lib/libgambatte/src/rtc.cpp b/src/lib/libgambatte/src/rtc.cpp new file mode 100644 index 00000000..75164919 --- /dev/null +++ b/src/lib/libgambatte/src/rtc.cpp @@ -0,0 +1,157 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "rtc.h" +#include "savestate.h" + +using namespace std; + +Rtc::Rtc() : +activeData(NULL), +activeSet(NULL), +baseTime(0), +haltTime(0), +index(5), +dataDh(0), +dataDl(0), +dataH(0), +dataM(0), +dataS(0), +enabled(false), +lastLatchData(false) { +} + +void Rtc::doLatch() { + time_t tmp = ((dataDh & 0x40) ? haltTime : time(NULL)) - baseTime; + + while (tmp > 0x1FF * 86400) { + baseTime += 0x1FF * 86400; + tmp -= 0x1FF * 86400; + dataDh |= 0x80; + } + + dataDl = (tmp / 86400) & 0xFF; + dataDh &= 0xFE; + dataDh |= ((tmp / 86400) & 0x100) >> 8; + tmp %= 86400; + + dataH = tmp / 3600; + tmp %= 3600; + + dataM = tmp / 60; + tmp %= 60; + + dataS = tmp; +} + +void Rtc::doSwapActive() { + if (!enabled || index > 4) { + activeData = NULL; + activeSet = NULL; + } else switch (index) { + case 0x00: + activeData = &dataS; + activeSet = &Rtc::setS; + break; + case 0x01: + activeData = &dataM; + activeSet = &Rtc::setM; + break; + case 0x02: + activeData = &dataH; + activeSet = &Rtc::setH; + break; + case 0x03: + activeData = &dataDl; + activeSet = &Rtc::setDl; + break; + case 0x04: + activeData = &dataDh; + activeSet = &Rtc::setDh; + break; + } +} + +void Rtc::saveState(SaveState &state) const { + state.rtc.baseTime = baseTime; + state.rtc.haltTime = haltTime; + state.rtc.index = index; + state.rtc.dataDh = dataDh; + state.rtc.dataDl = dataDl; + state.rtc.dataH = dataH; + state.rtc.dataM = dataM; + state.rtc.dataS = dataS; + state.rtc.lastLatchData = lastLatchData; +} + +void Rtc::loadState(const SaveState &state, const bool enabled) { + this->enabled = enabled; + + baseTime = state.rtc.baseTime; + haltTime = state.rtc.haltTime; + index = state.rtc.index; + dataDh = state.rtc.dataDh; + dataDl = state.rtc.dataDl; + dataH = state.rtc.dataH; + dataM = state.rtc.dataM; + dataS = state.rtc.dataS; + lastLatchData = state.rtc.lastLatchData; + + doSwapActive(); +} + +void Rtc::setDh(const unsigned new_dh) { + const time_t unixtime = (dataDh & 0x40) ? haltTime : time(NULL); + const time_t old_highdays = ((unixtime - baseTime) / 86400) & 0x100; + baseTime += old_highdays * 86400; + baseTime -= ((new_dh & 0x1) << 8) * 86400; + + if ((dataDh ^ new_dh) & 0x40) { + if (new_dh & 0x40) + haltTime = time(NULL); + else + baseTime += time(NULL) - haltTime; + } +} + +void Rtc::setDl(const unsigned new_lowdays) { + const time_t unixtime = (dataDh & 0x40) ? haltTime : time(NULL); + const time_t old_lowdays = ((unixtime - baseTime) / 86400) & 0xFF; + baseTime += old_lowdays * 86400; + baseTime -= new_lowdays * 86400; +} + +void Rtc::setH(const unsigned new_hours) { + const time_t unixtime = (dataDh & 0x40) ? haltTime : time(NULL); + const time_t old_hours = ((unixtime - baseTime) / 3600) % 24; + baseTime += old_hours * 3600; + baseTime -= new_hours * 3600; +} + +void Rtc::setM(const unsigned new_minutes) { + const time_t unixtime = (dataDh & 0x40) ? haltTime : time(NULL); + const time_t old_minutes = ((unixtime - baseTime) / 60) % 60; + baseTime += old_minutes * 60; + baseTime -= new_minutes * 60; +} + +void Rtc::setS(const unsigned new_seconds) { + const time_t unixtime = (dataDh & 0x40) ? haltTime : time(NULL); + baseTime += (unixtime - baseTime) % 60; + baseTime -= new_seconds; +} diff --git a/src/lib/libgambatte/src/rtc.h b/src/lib/libgambatte/src/rtc.h new file mode 100644 index 00000000..40905c18 --- /dev/null +++ b/src/lib/libgambatte/src/rtc.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RTC_H +#define RTC_H + +class SaveState; + +#include + +class Rtc { +private: + unsigned char *activeData; + void (Rtc::*activeSet)(unsigned); + std::time_t baseTime; + std::time_t haltTime; + unsigned char index; + unsigned char dataDh; + unsigned char dataDl; + unsigned char dataH; + unsigned char dataM; + unsigned char dataS; + bool enabled; + bool lastLatchData; + + void doLatch(); + void doSwapActive(); + void setDh(unsigned new_dh); + void setDl(unsigned new_lowdays); + void setH(unsigned new_hours); + void setM(unsigned new_minutes); + void setS(unsigned new_seconds); + +public: + Rtc(); + + const unsigned char* getActive() const { + return activeData; + } + + std::time_t getBaseTime() const { + return baseTime; + } + + void setBaseTime(const std::time_t baseTime) { + this->baseTime = baseTime; +// doLatch(); + } + + void latch(const unsigned data) { + if (!lastLatchData && data == 1) + doLatch(); + + lastLatchData = data; + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state, bool enabled); + + void setEnabled(const bool enabled) { + this->enabled = enabled; + + doSwapActive(); + } + + void swapActive(unsigned index) { + index &= 0xF; + index -= 8; + + this->index = index; + + doSwapActive(); + } + + void write(const unsigned data) { +// if (activeSet) + (this->*activeSet)(data); + *activeData = data; + } +}; + +#endif diff --git a/src/lib/libgambatte/src/savestate.h b/src/lib/libgambatte/src/savestate.h new file mode 100644 index 00000000..c4b245fd --- /dev/null +++ b/src/lib/libgambatte/src/savestate.h @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SAVESTATE_H +#define SAVESTATE_H + +#include "int.h" + +struct SaveState { + template + class Ptr { + T *ptr; + unsigned long sz; + + public: + Ptr() : ptr(0), sz(0) {} + const T* get() const { return ptr; } + unsigned long getSz() const { return sz; } + void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; } + + friend class SaverList; + friend void setInitState(SaveState &, bool); + }; + + struct CPU { + unsigned long cycleCounter; + unsigned short PC; + unsigned short SP; + unsigned char A; + unsigned char B; + unsigned char C; + unsigned char D; + unsigned char E; + unsigned char F; + unsigned char H; + unsigned char L; + bool skip; + bool halted; + } cpu; + + struct Mem { + Ptr vram; + Ptr sram; + Ptr wram; + Ptr ioamhram; + unsigned long div_lastUpdate; + unsigned long tima_lastUpdate; + unsigned long tmatime; + unsigned long next_serialtime; + unsigned long lastOamDmaUpdate; + unsigned long minIntTime; + unsigned short rombank; + unsigned short dmaSource; + unsigned short dmaDestination; + unsigned char rambank; + unsigned char oamDmaPos; + bool IME; + bool enable_ram; + bool rambank_mode; + bool hdma_transfer; + } mem; + + struct PPU { + Ptr drawBuffer; + Ptr bgpData; + Ptr objpData; + //SpriteMapper::OamReader + Ptr oamReaderBuf; + Ptr oamReaderSzbuf; + + unsigned long videoCycles; + unsigned long enableDisplayM0Time; + unsigned char winYPos; + unsigned char drawStartCycle; + unsigned char scReadOffset; + unsigned char lcdc; + //ScReader + unsigned char scx[2]; + unsigned char scy[2]; + //ScxReader + unsigned char scxAnd7; + //WeMasterChecker + bool weMaster; + //WxReader + unsigned char wx; + //Wy + unsigned char wy; + bool lycIrqSkip; + } ppu; + + struct SPU { + struct Duty { + unsigned long nextPosUpdate; + unsigned char nr3; + unsigned char pos; + }; + + struct Env { + unsigned long counter; + unsigned char volume; + }; + + struct LCounter { + unsigned long counter; + unsigned short lengthCounter; + }; + + struct { + struct { + unsigned long counter; + unsigned short shadow; + unsigned char nr0; + bool negging; + } sweep; + Duty duty; + Env env; + LCounter lcounter; + unsigned char nr4; + bool master; + } ch1; + + struct { + Duty duty; + Env env; + LCounter lcounter; + unsigned char nr4; + bool master; + } ch2; + + struct { + Ptr waveRam; + LCounter lcounter; + unsigned long waveCounter; + unsigned long lastReadTime; + unsigned char nr3; + unsigned char nr4; + unsigned char wavePos; + unsigned char sampleBuf; + bool master; + } ch3; + + struct { + struct { + unsigned long counter; + unsigned short reg; + } lfsr; + Env env; + LCounter lcounter; + unsigned char nr4; + bool master; + } ch4; + + unsigned long cycleCounter; + } spu; + + struct RTC { + unsigned long baseTime; + unsigned long haltTime; + unsigned char index; + unsigned char dataDh; + unsigned char dataDl; + unsigned char dataH; + unsigned char dataM; + unsigned char dataS; + bool lastLatchData; + } rtc; +}; + +#endif diff --git a/src/lib/libgambatte/src/sound.cpp b/src/lib/libgambatte/src/sound.cpp new file mode 100644 index 00000000..3ff8063f --- /dev/null +++ b/src/lib/libgambatte/src/sound.cpp @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "sound.h" + +#include "savestate.h" +#include +#include + +/* + Frame Sequencer + + Step Length Ctr Vol Env Sweep + - - - - - - - - - - - - - - - - - - - - + 0 Clock - Clock +S 1 - Clock - + 2 Clock - - + 3 - - - + 4 Clock - Clock + 5 - - - + 6 Clock - - + 7 - - - + - - - - - - - - - - - - - - - - - - - - + Rate 256 Hz 64 Hz 128 Hz + +S) start step on sound power on. +*/ + +// static const unsigned bufferSize = 35112 + 16 + 2048; //FIXME: DMA can prevent process from returning for up to 4096 cycles. + +PSG::PSG() : +buffer(NULL), +lastUpdate(0), +soVol(0), +rsum(0x8000), // initialize to 0x8000 to prevent borrows from high word, xor away later +bufferPos(0), +enabled(false) +{} + +void PSG::init(const bool cgb) { + ch1.init(cgb); + ch2.init(cgb); + ch3.init(cgb); + ch4.init(cgb); +} + +void PSG::reset() { + ch1.reset(); + ch2.reset(); + ch3.reset(); + ch4.reset(); +} + +void PSG::setStatePtrs(SaveState &state) { + ch3.setStatePtrs(state); +} + +void PSG::saveState(SaveState &state) { + ch1.saveState(state); + ch2.saveState(state); + ch3.saveState(state); + ch4.saveState(state); +} + +void PSG::loadState(const SaveState &state) { + ch1.loadState(state); + ch2.loadState(state); + ch3.loadState(state); + ch4.loadState(state); + + lastUpdate = state.cpu.cycleCounter; + set_so_volume(state.mem.ioamhram.get()[0x124]); + map_so(state.mem.ioamhram.get()[0x125]); + enabled = state.mem.ioamhram.get()[0x126] >> 7 & 1; +} + +void PSG::accumulate_channels(const unsigned long cycles) { + Gambatte::uint_least32_t *const buf = buffer + bufferPos; + + std::memset(buf, 0, cycles * sizeof(Gambatte::uint_least32_t)); + ch1.update(buf, soVol, cycles); + ch2.update(buf, soVol, cycles); + ch3.update(buf, soVol, cycles); + ch4.update(buf, soVol, cycles); +} + +void PSG::generate_samples(const unsigned long cycleCounter, const unsigned doubleSpeed) { + const unsigned long cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed); + lastUpdate += cycles << (1 + doubleSpeed); + + if (cycles) + accumulate_channels(cycles); + + bufferPos += cycles; +} + +void PSG::resetCounter(const unsigned long newCc, const unsigned long oldCc, const unsigned doubleSpeed) { + generate_samples(oldCc, doubleSpeed); + lastUpdate = newCc - (oldCc - lastUpdate); +} + +unsigned PSG::fillBuffer() { + Gambatte::uint_least32_t sum = rsum; + Gambatte::uint_least32_t *b = buffer; + unsigned n = bufferPos; + + while (n--) { + sum += *b; + *b++ = sum ^ 0x8000; // xor away the initial rsum value of 0x8000 (which prevents borrows from the high word) from the low word + } + + rsum = sum; + + return bufferPos; +} + +#ifdef WORDS_BIGENDIAN +static const unsigned long so1Mul = 0x00000001; +static const unsigned long so2Mul = 0x00010000; +#else +static const unsigned long so1Mul = 0x00010000; +static const unsigned long so2Mul = 0x00000001; +#endif + +void PSG::set_so_volume(const unsigned nr50) { + soVol = (((nr50 & 0x7) + 1) * so1Mul + ((nr50 >> 4 & 0x7) + 1) * so2Mul) * 64; +} + +void PSG::map_so(const unsigned nr51) { + const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul; + + ch1.setSo((tmp & 0x00010001) * 0xFFFF); + ch2.setSo((tmp >> 1 & 0x00010001) * 0xFFFF); + ch3.setSo((tmp >> 2 & 0x00010001) * 0xFFFF); + ch4.setSo((tmp >> 3 & 0x00010001) * 0xFFFF); +} + +unsigned PSG::getStatus() const { + return ch1.isActive() | (ch2.isActive() << 1) | (ch3.isActive() << 2) | (ch4.isActive() << 3); +} diff --git a/src/lib/libgambatte/src/sound.h b/src/lib/libgambatte/src/sound.h new file mode 100644 index 00000000..06916846 --- /dev/null +++ b/src/lib/libgambatte/src/sound.h @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SOUND_H +#define SOUND_H + +class SaveState; + +#include "int.h" + +#include "sound/channel1.h" +#include "sound/channel2.h" +#include "sound/channel3.h" +#include "sound/channel4.h" + +class PSG { + Channel1 ch1; + Channel2 ch2; + Channel3 ch3; + Channel4 ch4; + + Gambatte::uint_least32_t *buffer; + + unsigned long lastUpdate; + unsigned long soVol; + + Gambatte::uint_least32_t rsum; + + unsigned bufferPos; + + bool enabled; + + void accumulate_channels(unsigned long cycles); + +public: + PSG(); + void init(bool cgb); + void reset(); + void setStatePtrs(SaveState &state); + void saveState(SaveState &state); + void loadState(const SaveState &state); + + void generate_samples(unsigned long cycleCounter, unsigned doubleSpeed); + void resetCounter(unsigned long newCc, unsigned long oldCc, unsigned doubleSpeed); + unsigned fillBuffer(); + void setBuffer(Gambatte::uint_least32_t *const buf) { buffer = buf; bufferPos = 0; } + + bool isEnabled() const { return enabled; } + void setEnabled(bool value) { enabled = value; } + + void set_nr10(unsigned data) { ch1.setNr0(data); } + void set_nr11(unsigned data) { ch1.setNr1(data); } + void set_nr12(unsigned data) { ch1.setNr2(data); } + void set_nr13(unsigned data) { ch1.setNr3(data); } + void set_nr14(unsigned data) { ch1.setNr4(data); } + + void set_nr21(unsigned data) { ch2.setNr1(data); } + void set_nr22(unsigned data) { ch2.setNr2(data); } + void set_nr23(unsigned data) { ch2.setNr3(data); } + void set_nr24(unsigned data) { ch2.setNr4(data); } + + void set_nr30(unsigned data) { ch3.setNr0(data); } + void set_nr31(unsigned data) { ch3.setNr1(data); } + void set_nr32(unsigned data) { ch3.setNr2(data); } + void set_nr33(unsigned data) { ch3.setNr3(data); } + void set_nr34(unsigned data) { ch3.setNr4(data); } + unsigned waveRamRead(unsigned index) const { return ch3.waveRamRead(index); } + void waveRamWrite(unsigned index, unsigned data) { ch3.waveRamWrite(index, data); } + + void set_nr41(unsigned data) { ch4.setNr1(data); } + void set_nr42(unsigned data) { ch4.setNr2(data); } + void set_nr43(unsigned data) { ch4.setNr3(data); } + void set_nr44(unsigned data) { ch4.setNr4(data); } + + void set_so_volume(unsigned nr50); + void map_so(unsigned nr51); + unsigned getStatus() const; +}; + +#endif diff --git a/src/lib/libgambatte/src/sound/channel1.cpp b/src/lib/libgambatte/src/sound/channel1.cpp new file mode 100644 index 00000000..5e112eb2 --- /dev/null +++ b/src/lib/libgambatte/src/sound/channel1.cpp @@ -0,0 +1,257 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "channel1.h" +#include "../savestate.h" +#include + +Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit) : + disableMaster(disabler), + dutyUnit(dutyUnit), + shadow(0), + nr0(0), + negging(false) +{} + +unsigned Channel1::SweepUnit::calcFreq() { + unsigned freq = shadow >> (nr0 & 0x07); + + if (nr0 & 0x08) { + freq = shadow - freq; + negging = true; + } else + freq = shadow + freq; + + if (freq & 2048) + disableMaster(); + + return freq; +} + +void Channel1::SweepUnit::event() { + const unsigned long period = nr0 >> 4 & 0x07; + + if (period) { + const unsigned freq = calcFreq(); + + if (!(freq & 2048) && (nr0 & 0x07)) { + shadow = freq; + dutyUnit.setFreq(freq, counter); + calcFreq(); + } + + counter += period << 14; + } else + counter += 8ul << 14; +} + +void Channel1::SweepUnit::nr0Change(const unsigned newNr0) { + if (negging && !(newNr0 & 0x08)) + disableMaster(); + + nr0 = newNr0; +} + +void Channel1::SweepUnit::nr4Init(const unsigned long cc) { + negging = false; + shadow = dutyUnit.getFreq(); + + const unsigned period = nr0 >> 4 & 0x07; + const unsigned shift = nr0 & 0x07; + + if (period | shift) + counter = ((cc >> 14) + (period ? period : 8)) << 14; + else + counter = COUNTER_DISABLED; + + if (shift) + calcFreq(); +} + +void Channel1::SweepUnit::reset() { + counter = COUNTER_DISABLED; +} + +void Channel1::SweepUnit::saveState(SaveState &state) const { + state.spu.ch1.sweep.counter = counter; + state.spu.ch1.sweep.shadow = shadow; + state.spu.ch1.sweep.nr0 = nr0; + state.spu.ch1.sweep.negging = negging; +} + +void Channel1::SweepUnit::loadState(const SaveState &state) { + counter = std::max(state.spu.ch1.sweep.counter, state.spu.cycleCounter); + shadow = state.spu.ch1.sweep.shadow; + nr0 = state.spu.ch1.sweep.nr0; + negging = state.spu.ch1.sweep.negging; +} + +Channel1::Channel1() : + staticOutputTest(*this, dutyUnit), + disableMaster(master, dutyUnit), + lengthCounter(disableMaster, 0x3F), + envelopeUnit(staticOutputTest), + sweepUnit(disableMaster, dutyUnit), + cycleCounter(0), + soMask(0), + prevOut(0), + nr4(0), + master(false) +{ + setEvent(); +} + +void Channel1::setEvent() { +// nextEventUnit = &dutyUnit; +// if (sweepUnit.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &sweepUnit; + if (envelopeUnit.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &envelopeUnit; + if (lengthCounter.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &lengthCounter; +} + +void Channel1::setNr0(const unsigned data) { + sweepUnit.nr0Change(data); + setEvent(); +} + +void Channel1::setNr1(const unsigned data) { + lengthCounter.nr1Change(data, nr4, cycleCounter); + dutyUnit.nr1Change(data, cycleCounter); + + setEvent(); +} + +void Channel1::setNr2(const unsigned data) { + if (envelopeUnit.nr2Change(data)) + disableMaster(); + else + staticOutputTest(cycleCounter); + + setEvent(); +} + +void Channel1::setNr3(const unsigned data) { + dutyUnit.nr3Change(data, cycleCounter); + setEvent(); +} + +void Channel1::setNr4(const unsigned data) { + lengthCounter.nr4Change(nr4, data, cycleCounter); + + nr4 = data; + + dutyUnit.nr4Change(data, cycleCounter); + + if (data & 0x80) { //init-bit + nr4 &= 0x7F; + master = !envelopeUnit.nr4Init(cycleCounter); + sweepUnit.nr4Init(cycleCounter); + staticOutputTest(cycleCounter); + } + + setEvent(); +} + +void Channel1::setSo(const unsigned long soMask) { + this->soMask = soMask; + staticOutputTest(cycleCounter); + setEvent(); +} + +void Channel1::reset() { + cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. + +// lengthCounter.reset(); + dutyUnit.reset(); + envelopeUnit.reset(); + sweepUnit.reset(); + + setEvent(); +} + +void Channel1::init(const bool cgb) { + lengthCounter.init(cgb); +} + +void Channel1::saveState(SaveState &state) { + sweepUnit.saveState(state); + dutyUnit.saveState(state.spu.ch1.duty, cycleCounter); + envelopeUnit.saveState(state.spu.ch1.env); + lengthCounter.saveState(state.spu.ch1.lcounter); + + state.spu.cycleCounter = cycleCounter; + state.spu.ch1.nr4 = nr4; + state.spu.ch1.master = master; +} + +void Channel1::loadState(const SaveState &state) { + sweepUnit.loadState(state); + dutyUnit.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111], state.spu.ch1.nr4, state.spu.cycleCounter); + envelopeUnit.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112], state.spu.cycleCounter); + lengthCounter.loadState(state.spu.ch1.lcounter, state.spu.cycleCounter); + + cycleCounter = state.spu.cycleCounter; + nr4 = state.spu.ch1.nr4; + master = state.spu.ch1.master; +} + +void Channel1::update(Gambatte::uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { + const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0; + const unsigned long outLow = outBase * (0 - 15ul); + const unsigned long endCycles = cycleCounter + cycles; + + for (;;) { + const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow; + const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles; + unsigned long out = dutyUnit.isHighState() ? outHigh : outLow; + + while (dutyUnit.getCounter() <= nextMajorEvent) { + *buf = out - prevOut; + prevOut = out; + buf += dutyUnit.getCounter() - cycleCounter; + cycleCounter = dutyUnit.getCounter(); + + dutyUnit.event(); + out = dutyUnit.isHighState() ? outHigh : outLow; + } + + if (cycleCounter < nextMajorEvent) { + *buf = out - prevOut; + prevOut = out; + buf += nextMajorEvent - cycleCounter; + cycleCounter = nextMajorEvent; + } + + if (nextEventUnit->getCounter() == nextMajorEvent) { + nextEventUnit->event(); + setEvent(); + } else + break; + } + + if (cycleCounter & SoundUnit::COUNTER_MAX) { + dutyUnit.resetCounters(cycleCounter); + lengthCounter.resetCounters(cycleCounter); + envelopeUnit.resetCounters(cycleCounter); + sweepUnit.resetCounters(cycleCounter); + + cycleCounter -= SoundUnit::COUNTER_MAX; + } +} diff --git a/src/lib/libgambatte/src/sound/channel1.h b/src/lib/libgambatte/src/sound/channel1.h new file mode 100644 index 00000000..d790e0ec --- /dev/null +++ b/src/lib/libgambatte/src/sound/channel1.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SOUND_CHANNEL1_H +#define SOUND_CHANNEL1_H + +class SaveState; + +#include "int.h" + +#include "master_disabler.h" +#include "length_counter.h" +#include "duty_unit.h" +#include "envelope_unit.h" +#include "static_output_tester.h" + +class Channel1 { + class SweepUnit : public SoundUnit { + MasterDisabler &disableMaster; + DutyUnit &dutyUnit; + unsigned short shadow; + unsigned char nr0; + bool negging; + + unsigned calcFreq(); + + public: + SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit); + void event(); + void nr0Change(unsigned newNr0); + void nr4Init(unsigned long cycleCounter); + void reset(); + void saveState(SaveState &state) const; + void loadState(const SaveState &state); + }; + + friend class StaticOutputTester; + + StaticOutputTester staticOutputTest; + DutyMasterDisabler disableMaster; + LengthCounter lengthCounter; + DutyUnit dutyUnit; + EnvelopeUnit envelopeUnit; + SweepUnit sweepUnit; + + SoundUnit *nextEventUnit; + + unsigned long cycleCounter; + unsigned long soMask; + unsigned long prevOut; + + unsigned char nr4; + bool master; + + void setEvent(); + +public: + Channel1(); + void setNr0(unsigned data); + void setNr1(unsigned data); + void setNr2(unsigned data); + void setNr3(unsigned data); + void setNr4(unsigned data); + + void setSo(unsigned long soMask); + bool isActive() const { return master; } + + void update(Gambatte::uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + + void reset(); + void init(bool cgb); + void saveState(SaveState &state); + void loadState(const SaveState &state); +}; + +#endif diff --git a/src/lib/libgambatte/src/sound/channel2.cpp b/src/lib/libgambatte/src/sound/channel2.cpp new file mode 100644 index 00000000..2db30658 --- /dev/null +++ b/src/lib/libgambatte/src/sound/channel2.cpp @@ -0,0 +1,161 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "channel2.h" +#include "../savestate.h" + +Channel2::Channel2() : + staticOutputTest(*this, dutyUnit), + disableMaster(master, dutyUnit), + lengthCounter(disableMaster, 0x3F), + envelopeUnit(staticOutputTest), + cycleCounter(0), + soMask(0), + prevOut(0), + nr4(0), + master(false) +{ + setEvent(); +} + +void Channel2::setEvent() { +// nextEventUnit = &dutyUnit; +// if (envelopeUnit.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &envelopeUnit; + if (lengthCounter.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &lengthCounter; +} + +void Channel2::setNr1(const unsigned data) { + lengthCounter.nr1Change(data, nr4, cycleCounter); + dutyUnit.nr1Change(data, cycleCounter); + + setEvent(); +} + +void Channel2::setNr2(const unsigned data) { + if (envelopeUnit.nr2Change(data)) + disableMaster(); + else + staticOutputTest(cycleCounter); + + setEvent(); +} + +void Channel2::setNr3(const unsigned data) { + dutyUnit.nr3Change(data, cycleCounter); + setEvent(); +} + +void Channel2::setNr4(const unsigned data) { + lengthCounter.nr4Change(nr4, data, cycleCounter); + + nr4 = data; + + if (data & 0x80) { //init-bit + nr4 &= 0x7F; + master = !envelopeUnit.nr4Init(cycleCounter); + staticOutputTest(cycleCounter); + } + + dutyUnit.nr4Change(data, cycleCounter); + + setEvent(); +} + +void Channel2::setSo(const unsigned long soMask) { + this->soMask = soMask; + staticOutputTest(cycleCounter); + setEvent(); +} + +void Channel2::reset() { + cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. + +// lengthCounter.reset(); + dutyUnit.reset(); + envelopeUnit.reset(); + + setEvent(); +} + +void Channel2::init(const bool cgb) { + lengthCounter.init(cgb); +} + +void Channel2::saveState(SaveState &state) { + dutyUnit.saveState(state.spu.ch2.duty, cycleCounter); + envelopeUnit.saveState(state.spu.ch2.env); + lengthCounter.saveState(state.spu.ch2.lcounter); + + state.spu.ch2.nr4 = nr4; + state.spu.ch2.master = master; +} + +void Channel2::loadState(const SaveState &state) { + dutyUnit.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116], state.spu.ch2.nr4,state.spu.cycleCounter); + envelopeUnit.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117], state.spu.cycleCounter); + lengthCounter.loadState(state.spu.ch2.lcounter, state.spu.cycleCounter); + + cycleCounter = state.spu.cycleCounter; + nr4 = state.spu.ch2.nr4; + master = state.spu.ch2.master; +} + +void Channel2::update(Gambatte::uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { + const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0; + const unsigned long outLow = outBase * (0 - 15ul); + const unsigned long endCycles = cycleCounter + cycles; + + for (;;) { + const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow; + const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles; + unsigned long out = dutyUnit.isHighState() ? outHigh : outLow; + + while (dutyUnit.getCounter() <= nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += dutyUnit.getCounter() - cycleCounter; + cycleCounter = dutyUnit.getCounter(); + + dutyUnit.event(); + out = dutyUnit.isHighState() ? outHigh : outLow; + } + + if (cycleCounter < nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += nextMajorEvent - cycleCounter; + cycleCounter = nextMajorEvent; + } + + if (nextEventUnit->getCounter() == nextMajorEvent) { + nextEventUnit->event(); + setEvent(); + } else + break; + } + + if (cycleCounter & SoundUnit::COUNTER_MAX) { + dutyUnit.resetCounters(cycleCounter); + lengthCounter.resetCounters(cycleCounter); + envelopeUnit.resetCounters(cycleCounter); + + cycleCounter -= SoundUnit::COUNTER_MAX; + } +} diff --git a/src/lib/libgambatte/src/sound/channel2.h b/src/lib/libgambatte/src/sound/channel2.h new file mode 100644 index 00000000..24bc66a4 --- /dev/null +++ b/src/lib/libgambatte/src/sound/channel2.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SOUND_CHANNEL2_H +#define SOUND_CHANNEL2_H + +class SaveState; + +#include "int.h" + +#include "length_counter.h" +#include "duty_unit.h" +#include "envelope_unit.h" +#include "static_output_tester.h" + +class Channel2 { + friend class StaticOutputTester; + + StaticOutputTester staticOutputTest; + DutyMasterDisabler disableMaster; + LengthCounter lengthCounter; + DutyUnit dutyUnit; + EnvelopeUnit envelopeUnit; + + SoundUnit *nextEventUnit; + + unsigned long cycleCounter; + unsigned long soMask; + unsigned long prevOut; + + unsigned char nr4; + bool master; + + void setEvent(); + +public: + Channel2(); + void setNr1(unsigned data); + void setNr2(unsigned data); + void setNr3(unsigned data); + void setNr4(unsigned data); + + void setSo(unsigned long soMask); + // void deactivate() { disableMaster(); setEvent(); } + bool isActive() const { return master; } + + void update(Gambatte::uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + + void reset(); + void init(bool cgb); + void saveState(SaveState &state); + void loadState(const SaveState &state); +}; + +#endif diff --git a/src/lib/libgambatte/src/sound/channel3.cpp b/src/lib/libgambatte/src/sound/channel3.cpp new file mode 100644 index 00000000..944271e3 --- /dev/null +++ b/src/lib/libgambatte/src/sound/channel3.cpp @@ -0,0 +1,207 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "channel3.h" +#include "../savestate.h" +#include +#include + +static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) { + return 0x800 - ((nr4 << 8 & 0x700) | nr3); +} + +Channel3::Channel3() : + disableMaster(master, waveCounter), + lengthCounter(disableMaster, 0xFF), + cycleCounter(0), + soMask(0), + prevOut(0), + waveCounter(SoundUnit::COUNTER_DISABLED), + lastReadTime(0), + nr0(0), + nr3(0), + nr4(0), + wavePos(0), + rShift(4), + sampleBuf(0), + master(false), + cgb(false) +{} + +void Channel3::setNr0(const unsigned data) { + nr0 = data & 0x80; + + if (!(data & 0x80)) + disableMaster(); +} + +void Channel3::setNr2(const unsigned data) { + rShift = (data >> 5 & 3U) - 1; + + if (rShift > 3) + rShift = 4; +} + +void Channel3::setNr4(const unsigned data) { + lengthCounter.nr4Change(nr4, data, cycleCounter); + + nr4 = data & 0x7F; + + if (data & nr0/* & 0x80*/) { + if (!cgb && waveCounter == cycleCounter + 1) { + const unsigned pos = ((wavePos + 1) & 0x1F) >> 1; + + if (pos < 4) + waveRam[0] = waveRam[pos]; + else + std::memcpy(waveRam, waveRam + (pos & ~3), 4); + } + + master = true; + wavePos = 0; + lastReadTime = waveCounter = cycleCounter + toPeriod(nr3, data) + 3; + } +} + +void Channel3::setSo(const unsigned long soMask) { + this->soMask = soMask; +} + +void Channel3::reset() { + cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. + +// lengthCounter.reset(); + sampleBuf = 0; +} + +void Channel3::init(const bool cgb) { + this->cgb = cgb; + lengthCounter.init(cgb); +} + +void Channel3::setStatePtrs(SaveState &state) { + state.spu.ch3.waveRam.set(waveRam, sizeof waveRam); +} + +void Channel3::saveState(SaveState &state) const { + lengthCounter.saveState(state.spu.ch3.lcounter); + + state.spu.ch3.waveCounter = waveCounter; + state.spu.ch3.lastReadTime = lastReadTime; + state.spu.ch3.nr3 = nr3; + state.spu.ch3.nr4 = nr4; + state.spu.ch3.wavePos = wavePos; + state.spu.ch3.sampleBuf = sampleBuf; + state.spu.ch3.master = master; +} + +void Channel3::loadState(const SaveState &state) { + lengthCounter.loadState(state.spu.ch3.lcounter, state.spu.cycleCounter); + + cycleCounter = state.spu.cycleCounter; + waveCounter = std::max(state.spu.ch3.waveCounter, state.spu.cycleCounter); + lastReadTime = state.spu.ch3.lastReadTime; + nr3 = state.spu.ch3.nr3; + nr4 = state.spu.ch3.nr4; + wavePos = state.spu.ch3.wavePos & 0x1F; + sampleBuf = state.spu.ch3.sampleBuf; + master = state.spu.ch3.master; + + nr0 = state.mem.ioamhram.get()[0x11A] & 0x80; + setNr2(state.mem.ioamhram.get()[0x11C]); +} + +void Channel3::updateWaveCounter(const unsigned long cc) { + if (cc >= waveCounter) { + const unsigned period = toPeriod(nr3, nr4); + const unsigned long periods = (cc - waveCounter) / period; + + lastReadTime = waveCounter + periods * period; + waveCounter = lastReadTime + period; + + wavePos += periods + 1; + wavePos &= 0x1F; + + sampleBuf = waveRam[wavePos >> 1]; + } +} + +void Channel3::update(Gambatte::uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { + const unsigned long outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0; + + if (outBase && rShift != 4) { + const unsigned long endCycles = cycleCounter + cycles; + + for (;;) { + const unsigned long nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles; + unsigned long out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul); + + while (waveCounter <= nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += waveCounter - cycleCounter; + cycleCounter = waveCounter; + + lastReadTime = waveCounter; + waveCounter += toPeriod(nr3, nr4); + ++wavePos; + wavePos &= 0x1F; + sampleBuf = waveRam[wavePos >> 1]; + out = outBase * (/*master ? */((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul/* : 0 - 15ul*/); + } + + if (cycleCounter < nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += nextMajorEvent - cycleCounter; + cycleCounter = nextMajorEvent; + } + + if (lengthCounter.getCounter() == nextMajorEvent) { + lengthCounter.event(); + } else + break; + } + } else { + if (outBase) { + const unsigned long out = outBase * (0 - 15ul); + + *buf += out - prevOut; + prevOut = out; + } + + cycleCounter += cycles; + + while (lengthCounter.getCounter() <= cycleCounter) { + updateWaveCounter(lengthCounter.getCounter()); + lengthCounter.event(); + } + + updateWaveCounter(cycleCounter); + } + + if (cycleCounter & SoundUnit::COUNTER_MAX) { + lengthCounter.resetCounters(cycleCounter); + + if (waveCounter != SoundUnit::COUNTER_DISABLED) + waveCounter -= SoundUnit::COUNTER_MAX; + + lastReadTime -= SoundUnit::COUNTER_MAX; + cycleCounter -= SoundUnit::COUNTER_MAX; + } +} diff --git a/src/lib/libgambatte/src/sound/channel3.h b/src/lib/libgambatte/src/sound/channel3.h new file mode 100644 index 00000000..8ec8688d --- /dev/null +++ b/src/lib/libgambatte/src/sound/channel3.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SOUND_CHANNEL3_H +#define SOUND_CHANNEL3_H + +class SaveState; + +#include "int.h" + +#include "master_disabler.h" +#include "length_counter.h" + +class Channel3 { + class Ch3MasterDisabler : public MasterDisabler { + unsigned long &waveCounter; + + public: + Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter(wC) {} + void operator()() { MasterDisabler::operator()(); waveCounter = SoundUnit::COUNTER_DISABLED; } + }; + + unsigned char waveRam[0x10]; + + Ch3MasterDisabler disableMaster; + LengthCounter lengthCounter; + + unsigned long cycleCounter; + unsigned long soMask; + unsigned long prevOut; + unsigned long waveCounter; + unsigned long lastReadTime; + + unsigned char nr0; + unsigned char nr3; + unsigned char nr4; + unsigned char wavePos; + unsigned char rShift; + unsigned char sampleBuf; + + bool master; + bool cgb; + + void updateWaveCounter(unsigned long cc); + +public: + Channel3(); + bool isActive() const { return master; } + void reset(); + void init(bool cgb); + void setStatePtrs(SaveState &state); + void saveState(SaveState &state) const; + void loadState(const SaveState &state); + void setNr0(unsigned data); + void setNr1(unsigned data) { lengthCounter.nr1Change(data, nr4, cycleCounter); } + void setNr2(unsigned data); + void setNr3(unsigned data) { nr3 = data; } + void setNr4(unsigned data); + void setSo(unsigned long soMask); + void update(Gambatte::uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + + unsigned waveRamRead(unsigned index) const { + if (master) { + if (!cgb && cycleCounter != lastReadTime) + return 0xFF; + + index = wavePos >> 1; + } + + return waveRam[index]; + } + + void waveRamWrite(unsigned index, unsigned data) { + if (master) { + if (!cgb && cycleCounter != lastReadTime) + return; + + index = wavePos >> 1; + } + + waveRam[index] = data; + } +}; + +#endif diff --git a/src/lib/libgambatte/src/sound/channel4.cpp b/src/lib/libgambatte/src/sound/channel4.cpp new file mode 100644 index 00000000..c1efcf28 --- /dev/null +++ b/src/lib/libgambatte/src/sound/channel4.cpp @@ -0,0 +1,300 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "channel4.h" +#include "../savestate.h" +#include + +static unsigned long toPeriod(const unsigned nr3) { + unsigned s = (nr3 >> 4) + 3; + unsigned r = nr3 & 7; + + if (!r) { + r = 1; + --s; + } + + return r << s; +} + +Channel4::Lfsr::Lfsr() : +backupCounter(COUNTER_DISABLED), +reg(0xFF), +nr3(0), +master(false) +{} + +void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) { + /*if (backupCounter <= cc) { + const unsigned long period = toPeriod(nr3); + backupCounter = cc - (cc - backupCounter) % period + period; + }*/ + + if (backupCounter <= cc) { + const unsigned long period = toPeriod(nr3); + unsigned long periods = (cc - backupCounter) / period + 1; + + backupCounter += periods * period; + + if (master && nr3 < 0xE0) { + if (nr3 & 8) { + while (periods > 6) { + const unsigned xored = (reg << 1 ^ reg) & 0x7E; + reg = (reg >> 6 & ~0x7E) | xored | xored << 8; + periods -= 6; + } + + const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F; + reg = (reg >> periods & ~(0x80 - (0x80 >> periods))) | xored | xored << 8; + } else { + while (periods > 15) { + reg = reg ^ reg >> 1; + periods -= 15; + } + + reg = reg >> periods | (((reg ^ reg >> 1) << (15 - periods)) & 0x7FFF); + } + } + } +} + +void Channel4::Lfsr::reviveCounter(const unsigned long cc) { + updateBackupCounter(cc); + counter = backupCounter; +} + +/*static const unsigned char nextStateDistance[0x40] = { + 6, 1, 1, 2, 2, 1, 1, 3, + 3, 1, 1, 2, 2, 1, 1, 4, + 4, 1, 1, 2, 2, 1, 1, 3, + 3, 1, 1, 2, 2, 1, 1, 5, + 5, 1, 1, 2, 2, 1, 1, 3, + 3, 1, 1, 2, 2, 1, 1, 4, + 4, 1, 1, 2, 2, 1, 1, 3, + 3, 1, 1, 2, 2, 1, 1, 6, +};*/ + +inline void Channel4::Lfsr::event() { + if (nr3 < 0xE0) { + const unsigned shifted = reg >> 1; + const unsigned xored = (reg ^ shifted) & 1; + + reg = shifted | xored << 14; + + if (nr3 & 8) + reg = (reg & ~0x40) | xored << 6; + } + + counter += toPeriod(nr3); + backupCounter = counter; + + + /*if (nr3 < 0xE0) { + const unsigned periods = nextStateDistance[reg & 0x3F]; + const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F; + + reg = reg >> periods | xored << 8; + + if (nr3 & 8) + reg = reg & ~(0x80 - (0x80 >> periods)) | xored; + } + + const unsigned long period = toPeriod(nr3); + backupCounter = counter + period; + counter += period * nextStateDistance[reg & 0x3F];*/ +} + +void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) { + updateBackupCounter(cc); + nr3 = newNr3; + +// if (counter != COUNTER_DISABLED) +// counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1); +} + +void Channel4::Lfsr::nr4Init(unsigned long cc) { + disableMaster(); + updateBackupCounter(cc); + master = true; + backupCounter += 4; + counter = backupCounter; +// counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1); +} + +void Channel4::Lfsr::reset(const unsigned long cc) { + nr3 = 0; + disableMaster(); + backupCounter = cc + toPeriod(nr3); +} + +void Channel4::Lfsr::resetCounters(const unsigned long oldCc) { + updateBackupCounter(oldCc); + backupCounter -= COUNTER_MAX; + SoundUnit::resetCounters(oldCc); +} + +void Channel4::Lfsr::saveState(SaveState &state, const unsigned long cc) { + updateBackupCounter(cc); + state.spu.ch4.lfsr.counter = backupCounter; + state.spu.ch4.lfsr.reg = reg; +} + +void Channel4::Lfsr::loadState(const SaveState &state) { + counter = backupCounter = std::max(state.spu.ch4.lfsr.counter, state.spu.cycleCounter); + reg = state.spu.ch4.lfsr.reg; + master = state.spu.ch4.master; + nr3 = state.mem.ioamhram.get()[0x122]; +} + +Channel4::Channel4() : + staticOutputTest(*this, lfsr), + disableMaster(master, lfsr), + lengthCounter(disableMaster, 0x3F), + envelopeUnit(staticOutputTest), + cycleCounter(0), + soMask(0), + prevOut(0), + nr4(0), + master(false) +{ + setEvent(); +} + +void Channel4::setEvent() { +// nextEventUnit = &lfsr; +// if (envelopeUnit.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &envelopeUnit; + if (lengthCounter.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &lengthCounter; +} + +void Channel4::setNr1(const unsigned data) { + lengthCounter.nr1Change(data, nr4, cycleCounter); + + setEvent(); +} + +void Channel4::setNr2(const unsigned data) { + if (envelopeUnit.nr2Change(data)) + disableMaster(); + else + staticOutputTest(cycleCounter); + + setEvent(); +} + +void Channel4::setNr4(const unsigned data) { + lengthCounter.nr4Change(nr4, data, cycleCounter); + + nr4 = data; + + if (data & 0x80) { //init-bit + nr4 &= 0x7F; + + master = !envelopeUnit.nr4Init(cycleCounter); + + if (master) + lfsr.nr4Init(cycleCounter); + + staticOutputTest(cycleCounter); + } + + setEvent(); +} + +void Channel4::setSo(const unsigned long soMask) { + this->soMask = soMask; + staticOutputTest(cycleCounter); + setEvent(); +} + +void Channel4::reset() { + cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. + +// lengthCounter.reset(); + lfsr.reset(cycleCounter); + envelopeUnit.reset(); + + setEvent(); +} + +void Channel4::init(const bool cgb) { + lengthCounter.init(cgb); +} + +void Channel4::saveState(SaveState &state) { + lfsr.saveState(state, cycleCounter); + envelopeUnit.saveState(state.spu.ch4.env); + lengthCounter.saveState(state.spu.ch4.lcounter); + + state.spu.ch4.nr4 = nr4; + state.spu.ch4.master = master; +} + +void Channel4::loadState(const SaveState &state) { + lfsr.loadState(state); + envelopeUnit.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121], state.spu.cycleCounter); + lengthCounter.loadState(state.spu.ch4.lcounter, state.spu.cycleCounter); + + cycleCounter = state.spu.cycleCounter; + nr4 = state.spu.ch4.nr4; + master = state.spu.ch4.master; +} + +void Channel4::update(Gambatte::uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { + const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0; + const unsigned long outLow = outBase * (0 - 15ul); + const unsigned long endCycles = cycleCounter + cycles; + + for (;;) { + const unsigned long outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/; + const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles; + unsigned long out = lfsr.isHighState() ? outHigh : outLow; + + while (lfsr.getCounter() <= nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += lfsr.getCounter() - cycleCounter; + cycleCounter = lfsr.getCounter(); + + lfsr.event(); + out = lfsr.isHighState() ? outHigh : outLow; + } + + if (cycleCounter < nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += nextMajorEvent - cycleCounter; + cycleCounter = nextMajorEvent; + } + + if (nextEventUnit->getCounter() == nextMajorEvent) { + nextEventUnit->event(); + setEvent(); + } else + break; + } + + if (cycleCounter & SoundUnit::COUNTER_MAX) { + lengthCounter.resetCounters(cycleCounter); + lfsr.resetCounters(cycleCounter); + envelopeUnit.resetCounters(cycleCounter); + + cycleCounter -= SoundUnit::COUNTER_MAX; + } +} diff --git a/src/lib/libgambatte/src/sound/channel4.h b/src/lib/libgambatte/src/sound/channel4.h new file mode 100644 index 00000000..7563dc2c --- /dev/null +++ b/src/lib/libgambatte/src/sound/channel4.h @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SOUND_CHANNEL4_H +#define SOUND_CHANNEL4_H + +class SaveState; + +#include "int.h" + +#include "master_disabler.h" +#include "length_counter.h" +#include "envelope_unit.h" +#include "static_output_tester.h" + +class Channel4 { + class Lfsr : public SoundUnit { + unsigned long backupCounter; + unsigned short reg; + unsigned char nr3; + bool master; + + void updateBackupCounter(unsigned long cc); + + public: + Lfsr(); + void event(); + bool isHighState() const { return ~reg & 1; } + void nr3Change(unsigned newNr3, unsigned long cc); + void nr4Init(unsigned long cc); + void reset(unsigned long cc); + void saveState(SaveState &state, const unsigned long cc); + void loadState(const SaveState &state); + void resetCounters(unsigned long oldCc); + void disableMaster() { killCounter(); master = false; reg = 0xFF; } + void killCounter() { counter = COUNTER_DISABLED; } + void reviveCounter(unsigned long cc); + }; + + class Ch4MasterDisabler : public MasterDisabler { + Lfsr &lfsr; + public: + Ch4MasterDisabler(bool &m, Lfsr &lfsr) : MasterDisabler(m), lfsr(lfsr) {} + void operator()() { MasterDisabler::operator()(); lfsr.disableMaster(); } + }; + + friend class StaticOutputTester; + + StaticOutputTester staticOutputTest; + Ch4MasterDisabler disableMaster; + LengthCounter lengthCounter; + EnvelopeUnit envelopeUnit; + Lfsr lfsr; + + SoundUnit *nextEventUnit; + + unsigned long cycleCounter; + unsigned long soMask; + unsigned long prevOut; + + unsigned char nr4; + bool master; + + void setEvent(); + +public: + Channel4(); + void setNr1(unsigned data); + void setNr2(unsigned data); + void setNr3(unsigned data) { lfsr.nr3Change(data, cycleCounter); /*setEvent();*/ } + void setNr4(unsigned data); + + void setSo(unsigned long soMask); + bool isActive() const { return master; } + + void update(Gambatte::uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + + void reset(); + void init(bool cgb); + void saveState(SaveState &state); + void loadState(const SaveState &state); +}; + +#endif diff --git a/src/lib/libgambatte/src/sound/duty_unit.cpp b/src/lib/libgambatte/src/sound/duty_unit.cpp new file mode 100644 index 00000000..d3de6abd --- /dev/null +++ b/src/lib/libgambatte/src/sound/duty_unit.cpp @@ -0,0 +1,148 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "duty_unit.h" +#include + +static inline bool toOutState(const unsigned duty, const unsigned pos) { + static const unsigned char duties[4] = { 0x80, 0x81, 0xE1, 0x7E }; + + return duties[duty] >> pos & 1; +} + +static inline unsigned toPeriod(const unsigned freq) { + return (2048 - freq) << 1; +} + +void DutyUnit::updatePos(const unsigned long cc) { + if (cc >= nextPosUpdate) { + const unsigned long inc = (cc - nextPosUpdate) / period + 1; + nextPosUpdate += period * inc; + pos += inc; + pos &= 7; + } +} + +void DutyUnit::setDuty(const unsigned nr1) { + duty = nr1 >> 6; + high = toOutState(duty, pos); +} + +void DutyUnit::setCounter() { + static const unsigned char nextStateDistance[4 * 8] = { + 6, 5, 4, 3, 2, 1, 0, 0, + 0, 5, 4, 3, 2, 1, 0, 1, + 0, 3, 2, 1, 0, 3, 2, 1, + 0, 5, 4, 3, 2, 1, 0, 1 + }; + + if (enableEvents && nextPosUpdate != COUNTER_DISABLED) + counter = nextPosUpdate + period * nextStateDistance[(duty * 8) | pos]; + else + counter = COUNTER_DISABLED; +} + +void DutyUnit::setFreq(const unsigned newFreq, const unsigned long cc) { + updatePos(cc); + period = toPeriod(newFreq); + setCounter(); +} + +void DutyUnit::event() { + unsigned inc = period << duty; + + if (duty == 3) + inc -= period * 2; + + if (!(high ^= true)) + inc = period * 8 - inc; + + counter += inc; +} + +void DutyUnit::nr1Change(const unsigned newNr1, const unsigned long cc) { + updatePos(cc); + setDuty(newNr1); + setCounter(); +} + +void DutyUnit::nr3Change(const unsigned newNr3, const unsigned long cc) { + setFreq((getFreq() & 0x700) | newNr3, cc); +} + +void DutyUnit::nr4Change(const unsigned newNr4, const unsigned long cc) { + setFreq((newNr4 << 8 & 0x700) | (getFreq() & 0xFF), cc); + + if (newNr4 & 0x80) { + nextPosUpdate = (cc & ~1) + period; + setCounter(); + } +} + +DutyUnit::DutyUnit() : +nextPosUpdate(COUNTER_DISABLED), +period(4096), +pos(0), +duty(0), +high(false), +enableEvents(true) +{} + +void DutyUnit::reset() { + pos = 0; + high = toOutState(duty, pos); + nextPosUpdate = COUNTER_DISABLED; + setCounter(); +} + +void DutyUnit::saveState(SaveState::SPU::Duty &dstate, const unsigned long cc) { + updatePos(cc); + dstate.nextPosUpdate = nextPosUpdate; + dstate.nr3 = getFreq() & 0xFF; + dstate.pos = pos; +} + +void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned long cc) { + nextPosUpdate = std::max(dstate.nextPosUpdate, cc); + pos = dstate.pos & 7; + setDuty(nr1); + period = toPeriod((nr4 << 8 & 0x700) | dstate.nr3); + enableEvents = true; + setCounter(); +} + +void DutyUnit::resetCounters(const unsigned long oldCc) { + if (nextPosUpdate == COUNTER_DISABLED) + return; + + updatePos(oldCc); + nextPosUpdate -= COUNTER_MAX; + SoundUnit::resetCounters(oldCc); +} + +void DutyUnit::killCounter() { + enableEvents = false; + setCounter(); +} + +void DutyUnit::reviveCounter(const unsigned long cc) { + updatePos(cc); + high = toOutState(duty, pos); + enableEvents = true; + setCounter(); +} diff --git a/src/lib/libgambatte/src/sound/duty_unit.h b/src/lib/libgambatte/src/sound/duty_unit.h new file mode 100644 index 00000000..e55cec59 --- /dev/null +++ b/src/lib/libgambatte/src/sound/duty_unit.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef DUTY_UNIT_H +#define DUTY_UNIT_H + +#include "sound_unit.h" +#include "master_disabler.h" +#include "../savestate.h" + +class DutyUnit : public SoundUnit { + unsigned long nextPosUpdate; + unsigned short period; + unsigned char pos; + unsigned char duty; + bool high; + bool enableEvents; + + void setCounter(); + void setDuty(unsigned nr1); + void updatePos(unsigned long cc); + +public: + DutyUnit(); + void event(); + bool isHighState() const { return high; } + void nr1Change(unsigned newNr1, unsigned long cc); + void nr3Change(unsigned newNr3, unsigned long cc); + void nr4Change(unsigned newNr4, unsigned long cc); + void reset(); + void saveState(SaveState::SPU::Duty &dstate, unsigned long cc); + void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned long cc); + void resetCounters(unsigned long oldCc); + void killCounter(); + void reviveCounter(unsigned long cc); + + //intended for use by SweepUnit only. + unsigned getFreq() const { return 2048 - (period >> 1); } + void setFreq(unsigned newFreq, unsigned long cc); +}; + +class DutyMasterDisabler : public MasterDisabler { + DutyUnit &dutyUnit; +public: + DutyMasterDisabler(bool &m, DutyUnit &dutyUnit) : MasterDisabler(m), dutyUnit(dutyUnit) {} + void operator()() { MasterDisabler::operator()(); dutyUnit.killCounter(); } +}; + +#endif diff --git a/src/lib/libgambatte/src/sound/envelope_unit.cpp b/src/lib/libgambatte/src/sound/envelope_unit.cpp new file mode 100644 index 00000000..ed526eb5 --- /dev/null +++ b/src/lib/libgambatte/src/sound/envelope_unit.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "envelope_unit.h" +#include + +EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent; + +void EnvelopeUnit::event() { + const unsigned long period = nr2 & 7; + + if (period) { + unsigned newVol = volume; + + if (nr2 & 8) + ++newVol; + else + --newVol; + + if (newVol < 0x10U) { + volume = newVol; + + if (volume < 2) + volOnOffEvent(counter); + + counter += period << 15; + } else + counter = COUNTER_DISABLED; + } else + counter += 8ul << 15; +} + +bool EnvelopeUnit::nr2Change(const unsigned newNr2) { + if (!(nr2 & 7) && counter != COUNTER_DISABLED) + ++volume; + else if (!(nr2 & 8)) + volume += 2; + + if ((nr2 ^ newNr2) & 8) + volume = 0x10 - volume; + + volume &= 0xF; + + nr2 = newNr2; + + return !(newNr2 & 0xF8); +} + +bool EnvelopeUnit::nr4Init(const unsigned long cc) { + { + unsigned long period = nr2 & 7; + + if (!period) + period = 8; + + if (!(cc & 0x7000)) + ++period; + + counter = cc - ((cc - 0x1000) & 0x7FFF) + period * 0x8000; + } + + volume = nr2 >> 4; + + return !(nr2 & 0xF8); +} + +EnvelopeUnit::EnvelopeUnit(VolOnOffEvent &volOnOffEvent) : +volOnOffEvent(volOnOffEvent), +nr2(0), +volume(0) +{} + +void EnvelopeUnit::reset() { + counter = COUNTER_DISABLED; +} + +void EnvelopeUnit::saveState(SaveState::SPU::Env &estate) const { + estate.counter = counter; + estate.volume = volume; +} + +void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned long cc) { + counter = std::max(estate.counter, cc); + volume = estate.volume; + this->nr2 = nr2; +} diff --git a/src/lib/libgambatte/src/sound/envelope_unit.h b/src/lib/libgambatte/src/sound/envelope_unit.h new file mode 100644 index 00000000..e9bae2f0 --- /dev/null +++ b/src/lib/libgambatte/src/sound/envelope_unit.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ENVELOPE_UNIT_H +#define ENVELOPE_UNIT_H + +#include "sound_unit.h" +#include "../savestate.h" + +class EnvelopeUnit : public SoundUnit { +public: + struct VolOnOffEvent { + virtual ~VolOnOffEvent() {} + virtual void operator()(unsigned long /*cc*/) {} + }; + +private: + static VolOnOffEvent nullEvent; + VolOnOffEvent &volOnOffEvent; + unsigned char nr2; + unsigned char volume; + +public: + EnvelopeUnit(VolOnOffEvent &volOnOffEvent = nullEvent); + void event(); + bool dacIsOn() const { return nr2 & 0xF8; } + unsigned getVolume() const { return volume; } + bool nr2Change(unsigned newNr2); + bool nr4Init(unsigned long cycleCounter); + void reset(); + void saveState(SaveState::SPU::Env &estate) const; + void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned long cc); +}; + +#endif diff --git a/src/lib/libgambatte/src/sound/length_counter.cpp b/src/lib/libgambatte/src/sound/length_counter.cpp new file mode 100644 index 00000000..8bbe85e1 --- /dev/null +++ b/src/lib/libgambatte/src/sound/length_counter.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#include "length_counter.h" +#include "master_disabler.h" +#include + +LengthCounter::LengthCounter(MasterDisabler &disabler, const unsigned mask) : + disableMaster(disabler), + lengthMask(mask) +{ + init(false); + nr1Change(0, 0, 0); +} + +void LengthCounter::event() { + counter = COUNTER_DISABLED; + lengthCounter = 0; + disableMaster(); +} + +void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned long cycleCounter) { + lengthCounter = (~newNr1 & lengthMask) + 1; + counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast(COUNTER_DISABLED); +} + +void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned long cycleCounter) { + if (counter != COUNTER_DISABLED) + lengthCounter = (counter >> 13) - (cycleCounter >> 13); + + { + unsigned dec = 0; + + if (newNr4 & 0x40) { + dec = ~cycleCounter >> 12 & 1; + + if (!(oldNr4 & 0x40) && lengthCounter) { + if (!(lengthCounter -= dec)) + disableMaster(); + } + } + + if ((newNr4 & 0x80) && !lengthCounter) + lengthCounter = lengthMask + 1 - dec; + } + + if ((newNr4 & 0x40) && lengthCounter) + counter = ((cycleCounter >> 13) + lengthCounter) << 13; + else + counter = COUNTER_DISABLED; +} + +/*void LengthCounter::reset() { + counter = COUNTER_DISABLED; + + if (cgb) + lengthCounter = lengthMask + 1; +}*/ + +void LengthCounter::init(const bool cgb) { + this->cgb = cgb; +} + +void LengthCounter::saveState(SaveState::SPU::LCounter &lstate) const { + lstate.counter = counter; + lstate.lengthCounter = lengthCounter; +} + +void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned long cc) { + counter = std::max(lstate.counter, cc); + lengthCounter = lstate.lengthCounter; +} diff --git a/src/lib/libgambatte/src/sound/length_counter.h b/src/lib/libgambatte/src/sound/length_counter.h new file mode 100644 index 00000000..2d9451d7 --- /dev/null +++ b/src/lib/libgambatte/src/sound/length_counter.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef LENGTH_COUNTER_H +#define LENGTH_COUNTER_H + +#include "sound_unit.h" +#include "../savestate.h" + +class MasterDisabler; + +class LengthCounter : public SoundUnit { + MasterDisabler &disableMaster; + unsigned short lengthCounter; + const unsigned char lengthMask; + bool cgb; + +public: + LengthCounter(MasterDisabler &disabler, unsigned lengthMask); + void event(); + void nr1Change(unsigned newNr1, unsigned nr4, unsigned long cc); + void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned long cc); +// void reset(); + void init(bool cgb); + void saveState(SaveState::SPU::LCounter &lstate) const; + void loadState(const SaveState::SPU::LCounter &lstate, unsigned long cc); +}; + +#endif diff --git a/src/lib/libgambatte/src/sound/master_disabler.h b/src/lib/libgambatte/src/sound/master_disabler.h new file mode 100644 index 00000000..7dd588c5 --- /dev/null +++ b/src/lib/libgambatte/src/sound/master_disabler.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef MASTER_DISABLER_H +#define MASTER_DISABLER_H + +class MasterDisabler { + bool &master; + +public: + MasterDisabler(bool &m) : master(m) {} + virtual ~MasterDisabler() {} + virtual void operator()() { master = false; } +}; + +#endif diff --git a/src/lib/libgambatte/src/sound/sound_unit.h b/src/lib/libgambatte/src/sound/sound_unit.h new file mode 100644 index 00000000..2857c0c1 --- /dev/null +++ b/src/lib/libgambatte/src/sound/sound_unit.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef SOUND_UNIT_H +#define SOUND_UNIT_H + +class SoundUnit { +protected: + unsigned long counter; +public: + enum { COUNTER_MAX = 0x80000000u, COUNTER_DISABLED = 0xFFFFFFFFu }; + + SoundUnit() : counter(COUNTER_DISABLED) {} + virtual ~SoundUnit() {} + virtual void event() = 0; + unsigned long getCounter() const { return counter; } + virtual void resetCounters(unsigned long /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; } +}; + +#endif diff --git a/src/lib/libgambatte/src/sound/static_output_tester.h b/src/lib/libgambatte/src/sound/static_output_tester.h new file mode 100644 index 00000000..3dbe216e --- /dev/null +++ b/src/lib/libgambatte/src/sound/static_output_tester.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef STATIC_OUTPUT_TESTER_H +#define STATIC_OUTPUT_TESTER_H + +#include "envelope_unit.h" + +template +class StaticOutputTester : public EnvelopeUnit::VolOnOffEvent { + const Channel &ch; + Unit &unit; +public: + StaticOutputTester(const Channel &ch, Unit &unit) : ch(ch), unit(unit) {} + void operator()(unsigned long cc); +}; + +template +void StaticOutputTester::operator()(const unsigned long cc) { + if (ch.soMask && ch.master && ch.envelopeUnit.getVolume()) + unit.reviveCounter(cc); + else + unit.killCounter(); +} + +#endif diff --git a/src/lib/libgambatte/src/state_osd_elements.cpp b/src/lib/libgambatte/src/state_osd_elements.cpp new file mode 100644 index 00000000..44740d16 --- /dev/null +++ b/src/lib/libgambatte/src/state_osd_elements.cpp @@ -0,0 +1,169 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "state_osd_elements.h" +#include "bitmap_font.h" +#include "statesaver.h" +#include +#include + +using namespace BitmapFont; + +static const char stateLoadedTxt[] = { S,t,a,t,e,SPC,N0,SPC,l,o,a,d,e,d,0 }; +static const char stateSavedTxt[] = { S,t,a,t,e,SPC,N0,SPC,s,a,v,e,d,0 }; +static const unsigned stateLoadedTxtWidth = getWidth(stateLoadedTxt); +static const unsigned stateSavedTxtWidth = getWidth(stateSavedTxt); + +class ShadedTextOsdElment : public OsdElement { + struct ShadeFill { + void operator()(Gambatte::uint_least32_t *dest, const unsigned pitch) { + dest[2] = dest[1] = dest[0] = 0x000000ul; + dest += pitch; + dest[2] = dest[0] = 0x000000ul; + dest += pitch; + dest[2] = dest[1] = dest[0] = 0x000000ul; + } + }; + + Gambatte::uint_least32_t *const pixels; + unsigned life; + +public: + ShadedTextOsdElment(unsigned w, const char *txt); + ~ShadedTextOsdElment(); + const Gambatte::uint_least32_t* update(); +}; + +ShadedTextOsdElment::ShadedTextOsdElment(unsigned width, const char *txt) : +OsdElement(MAX_WIDTH, 144 - HEIGHT - HEIGHT, width + 2, HEIGHT + 2, THREE_FOURTHS), +pixels(new Gambatte::uint_least32_t[w() * h()]), +life(4 * 60) { + std::memset(pixels, 0xFF, w() * h() * sizeof(Gambatte::uint_least32_t)); + + /*print(pixels + 0 * w() + 0, w(), 0x000000ul, txt); + print(pixels + 0 * w() + 1, w(), 0x000000ul, txt); + print(pixels + 0 * w() + 2, w(), 0x000000ul, txt); + print(pixels + 1 * w() + 0, w(), 0x000000ul, txt); + print(pixels + 1 * w() + 2, w(), 0x000000ul, txt); + print(pixels + 2 * w() + 0, w(), 0x000000ul, txt); + print(pixels + 2 * w() + 1, w(), 0x000000ul, txt); + print(pixels + 2 * w() + 2, w(), 0x000000ul, txt); + print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);*/ + + print(pixels, w(), ShadeFill(), txt); + print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt); +} + +ShadedTextOsdElment::~ShadedTextOsdElment() { + delete []pixels; +} + +const Gambatte::uint_least32_t* ShadedTextOsdElment::update() { + if (life--) + return pixels; + + return 0; +} + +/*class FramedTextOsdElment : public OsdElement { + Gambatte::uint_least32_t *const pixels; + unsigned life; + +public: + FramedTextOsdElment(unsigned w, const char *txt); + ~FramedTextOsdElment(); + const Gambatte::uint_least32_t* update(); +}; + +FramedTextOsdElment::FramedTextOsdElment(unsigned width, const char *txt) : +OsdElement(NUMBER_WIDTH, 144 - HEIGHT * 2 - HEIGHT / 2, width + NUMBER_WIDTH * 2, HEIGHT * 2), +pixels(new Gambatte::uint_least32_t[w() * h()]), +life(4 * 60) { + std::memset(pixels, 0x00, w() * h() * sizeof(Gambatte::uint_least32_t)); + print(pixels + (w() - width) / 2 + ((h() - HEIGHT) / 2) * w(), w(), 0xA0A0A0ul, txt); +} + +FramedTextOsdElment::~FramedTextOsdElment() { + delete []pixels; +} + +const Gambatte::uint_least32_t* FramedTextOsdElment::update() { + if (life--) + return pixels; + + return 0; +}*/ + +std::auto_ptr newStateLoadedOsdElement(unsigned stateNo) { + char txt[sizeof(stateLoadedTxt)]; + + std::memcpy(txt, stateLoadedTxt, sizeof(stateLoadedTxt)); + utoa(stateNo, txt + 6); + + return std::auto_ptr(new ShadedTextOsdElment(stateLoadedTxtWidth, txt)); +} + +std::auto_ptr newStateSavedOsdElement(unsigned stateNo) { + char txt[sizeof(stateSavedTxt)]; + + std::memcpy(txt, stateSavedTxt, sizeof(stateSavedTxt)); + utoa(stateNo, txt + 6); + + return std::auto_ptr(new ShadedTextOsdElment(stateSavedTxtWidth, txt)); +} + +class SaveStateOsdElement : public OsdElement { + Gambatte::uint_least32_t pixels[StateSaver::SS_WIDTH * StateSaver::SS_HEIGHT]; + unsigned life; + +public: + SaveStateOsdElement(const char *fileName, unsigned stateNo); + const Gambatte::uint_least32_t* update(); +}; + +SaveStateOsdElement::SaveStateOsdElement(const char *fileName, unsigned stateNo) : +OsdElement((stateNo ? stateNo - 1 : 9) * ((160 - StateSaver::SS_WIDTH) / 10) + ((160 - StateSaver::SS_WIDTH) / 10) / 2, 4, StateSaver::SS_WIDTH, StateSaver::SS_HEIGHT), +life(4 * 60) { + std::ifstream file(fileName, std::ios_base::binary); + + if (file.is_open()) { + file.ignore(5); + file.read(reinterpret_cast(pixels), sizeof(pixels)); + } else { + std::memset(pixels, 0, sizeof(pixels)); + + { + using namespace BitmapFont; + + static const char txt[] = { E,m,p,t,BitmapFont::y,0 }; + + print(pixels + 3 + (StateSaver::SS_HEIGHT / 2 - BitmapFont::HEIGHT / 2) * StateSaver::SS_WIDTH, StateSaver::SS_WIDTH, 0x808080ul, txt); + } + } +} + +const Gambatte::uint_least32_t* SaveStateOsdElement::update() { + if (life--) + return pixels; + + return 0; +} + +std::auto_ptr newSaveStateOsdElement(const char *fileName, unsigned stateNo) { + return std::auto_ptr(new SaveStateOsdElement(fileName, stateNo)); +} diff --git a/src/lib/libgambatte/src/state_osd_elements.h b/src/lib/libgambatte/src/state_osd_elements.h new file mode 100644 index 00000000..c10344d2 --- /dev/null +++ b/src/lib/libgambatte/src/state_osd_elements.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef STATE_OSD_ELEMENTS_H +#define STATE_OSD_ELEMENTS_H + +#include "osd_element.h" +#include + +std::auto_ptr newStateLoadedOsdElement(unsigned stateNo); +std::auto_ptr newStateSavedOsdElement(unsigned stateNo); +std::auto_ptr newSaveStateOsdElement(const char *fileName, unsigned stateNo); + +#endif diff --git a/src/lib/libgambatte/src/statesaver.cpp b/src/lib/libgambatte/src/statesaver.cpp new file mode 100644 index 00000000..9b113ee6 --- /dev/null +++ b/src/lib/libgambatte/src/statesaver.cpp @@ -0,0 +1,407 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "statesaver.h" +#include "savestate.h" +#include "array.h" +#include +#include +#include +#include + +enum AsciiChar { + NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, BS, TAB, LF, VT, FF, CR, SO, SI, + DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB, CAN, EM, SUB, ESC, FS, GS, RS, US, + SP, XCL, QOT, HSH, DLR, PRC, AMP, APO, LPA, RPA, AST, PLU, COM, HYP, STP, DIV, + NO0, NO1, NO2, NO3, NO4, NO5, NO6, NO7, NO8, NO9, CLN, SCL, LT, EQL, GT, QTN, + AT, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, + P, Q, R, S, T, U, V, W, X, Y, Z, LBX, BSL, RBX, CAT, UND, + ACN, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, + p, q, r, s, t, u, v, w, x, y, z, LBR, BAR, RBR, TLD, DEL +}; + +struct Saver { + const char *label; + void (*save)(std::ofstream &file, const SaveState &state); + void (*load)(std::ifstream &file, SaveState &state); + unsigned char labelsize; +}; + +static inline bool operator<(const Saver &l, const Saver &r) { + return std::strcmp(l.label, r.label) < 0; +} + +static void put24(std::ofstream &file, const unsigned long data) { + file.put(data >> 16 & 0xFF); + file.put(data >> 8 & 0xFF); + file.put(data & 0xFF); +} + +static void put32(std::ofstream &file, const unsigned long data) { + file.put(data >> 24 & 0xFF); + file.put(data >> 16 & 0xFF); + file.put(data >> 8 & 0xFF); + file.put(data & 0xFF); +} + +static void write(std::ofstream &file, const unsigned char data) { + static const char inf[] = { 0x00, 0x00, 0x01 }; + + file.write(inf, sizeof(inf)); + file.put(data & 0xFF); +} + +static void write(std::ofstream &file, const unsigned short data) { + static const char inf[] = { 0x00, 0x00, 0x02 }; + + file.write(inf, sizeof(inf)); + file.put(data >> 8 & 0xFF); + file.put(data & 0xFF); +} + +static void write(std::ofstream &file, const unsigned long data) { + static const char inf[] = { 0x00, 0x00, 0x04 }; + + file.write(inf, sizeof(inf)); + put32(file, data); +} + +static inline void write(std::ofstream &file, const bool data) { + write(file, static_cast(data)); +} + +static void write(std::ofstream &file, const unsigned char *data, const unsigned long sz) { + put24(file, sz); + file.write(reinterpret_cast(data), sz); +} + +static void write(std::ofstream &file, const bool *data, const unsigned long sz) { + put24(file, sz); + + for (unsigned long i = 0; i < sz; ++i) + file.put(data[i]); +} + +static unsigned long get24(std::ifstream &file) { + unsigned long tmp = file.get() & 0xFF; + + tmp = tmp << 8 | (file.get() & 0xFF); + + return tmp << 8 | (file.get() & 0xFF); +} + +static unsigned long read(std::ifstream &file) { + unsigned long size = get24(file); + + if (size > 4) { + file.ignore(size - 4); + size = 4; + } + + unsigned long out = 0; + + switch (size) { + case 4: out = (out | (file.get() & 0xFF)) << 8; + case 3: out = (out | (file.get() & 0xFF)) << 8; + case 2: out = (out | (file.get() & 0xFF)) << 8; + case 1: out = out | (file.get() & 0xFF); + } + + return out; +} + +static inline void read(std::ifstream &file, unsigned char &data) { + data = read(file) & 0xFF; +} + +static inline void read(std::ifstream &file, unsigned short &data) { + data = read(file) & 0xFFFF; +} + +static inline void read(std::ifstream &file, unsigned long &data) { + data = read(file); +} + +static inline void read(std::ifstream &file, bool &data) { + data = read(file); +} + +static void read(std::ifstream &file, unsigned char *data, unsigned long sz) { + const unsigned long size = get24(file); + + if (size < sz) + sz = size; + + file.read(reinterpret_cast(data), sz); + file.ignore(size - sz); + + if (static_cast(0x100)) { + for (unsigned long i = 0; i < sz; ++i) + data[i] &= 0xFF; + } +} + +static void read(std::ifstream &file, bool *data, unsigned long sz) { + const unsigned long size = get24(file); + + if (size < sz) + sz = size; + + for (unsigned long i = 0; i < sz; ++i) + data[i] = file.get(); + + file.ignore(size - sz); +} + +class SaverList { +public: + typedef std::vector list_t; + typedef list_t::const_iterator const_iterator; + +private: + list_t list; + unsigned char maxLabelsize_; + +public: + SaverList(); + const_iterator begin() const { return list.begin(); } + const_iterator end() const { return list.end(); } + unsigned maxLabelsize() const { return maxLabelsize_; } +}; + +SaverList::SaverList() { +#define ADD(arg) do { \ + struct Func { \ + static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg); } \ + static void load(std::ifstream &file, SaveState &state) { read(file, state.arg); } \ + }; \ + \ + Saver saver = { label, Func::save, Func::load, sizeof label }; \ + list.push_back(saver); \ +} while (0) + +#define ADDPTR(arg) do { \ + struct Func { \ + static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg.get(), state.arg.getSz()); } \ + static void load(std::ifstream &file, SaveState &state) { read(file, state.arg.ptr, state.arg.getSz()); } \ + }; \ + \ + Saver saver = { label, Func::save, Func::load, sizeof label }; \ + list.push_back(saver); \ +} while (0) + + { static const char label[] = { c,c, NUL }; ADD(cpu.cycleCounter); } + { static const char label[] = { p,c, NUL }; ADD(cpu.PC); } + { static const char label[] = { s,p, NUL }; ADD(cpu.SP); } + { static const char label[] = { a, NUL }; ADD(cpu.A); } + { static const char label[] = { b, NUL }; ADD(cpu.B); } + { static const char label[] = { c, NUL }; ADD(cpu.C); } + { static const char label[] = { d, NUL }; ADD(cpu.D); } + { static const char label[] = { e, NUL }; ADD(cpu.E); } + { static const char label[] = { f, NUL }; ADD(cpu.F); } + { static const char label[] = { h, NUL }; ADD(cpu.H); } + { static const char label[] = { l, NUL }; ADD(cpu.L); } + { static const char label[] = { s,k,i,p, NUL }; ADD(cpu.skip); } + { static const char label[] = { h,a,l,t, NUL }; ADD(cpu.halted); } + { static const char label[] = { v,r,a,m, NUL }; ADDPTR(mem.vram); } + { static const char label[] = { s,r,a,m, NUL }; ADDPTR(mem.sram); } + { static const char label[] = { w,r,a,m, NUL }; ADDPTR(mem.wram); } + { static const char label[] = { h,r,a,m, NUL }; ADDPTR(mem.ioamhram); } + { static const char label[] = { l,d,i,v,u,p, NUL }; ADD(mem.div_lastUpdate); } + { static const char label[] = { l,t,i,m,a,u,p, NUL }; ADD(mem.tima_lastUpdate); } + { static const char label[] = { t,m,a,t,i,m,e, NUL }; ADD(mem.tmatime); } + { static const char label[] = { s,e,r,i,a,l,t, NUL }; ADD(mem.next_serialtime); } + { static const char label[] = { l,o,d,m,a,u,p, NUL }; ADD(mem.lastOamDmaUpdate); } + { static const char label[] = { m,i,n,i,n,t,t, NUL }; ADD(mem.minIntTime); } + { static const char label[] = { r,o,m,b,a,n,k, NUL }; ADD(mem.rombank); } + { static const char label[] = { d,m,a,s,r,c, NUL }; ADD(mem.dmaSource); } + { static const char label[] = { d,m,a,d,s,t, NUL }; ADD(mem.dmaDestination); } + { static const char label[] = { r,a,m,b,a,n,k, NUL }; ADD(mem.rambank); } + { static const char label[] = { o,d,m,a,p,o,s, NUL }; ADD(mem.oamDmaPos); } + { static const char label[] = { i,m,e, NUL }; ADD(mem.IME); } + { static const char label[] = { s,r,a,m,o,n, NUL }; ADD(mem.enable_ram); } + { static const char label[] = { r,a,m,b,m,o,d, NUL }; ADD(mem.rambank_mode); } + { static const char label[] = { h,d,m,a, NUL }; ADD(mem.hdma_transfer); } + { static const char label[] = { b,g,p, NUL }; ADDPTR(ppu.bgpData); } + { static const char label[] = { o,b,j,p, NUL }; ADDPTR(ppu.objpData); } + { static const char label[] = { s,p,o,s,b,u,f, NUL }; ADDPTR(ppu.oamReaderBuf); } + { static const char label[] = { s,p,s,z,b,u,f, NUL }; ADDPTR(ppu.oamReaderSzbuf); } + { static const char label[] = { v,c,y,c,l,e,s, NUL }; ADD(ppu.videoCycles); } + { static const char label[] = { e,d,M,NO0,t,i,m, NUL }; ADD(ppu.enableDisplayM0Time); } + { static const char label[] = { w,i,n,y,p,o,s, NUL }; ADD(ppu.winYPos); } + { static const char label[] = { d,r,a,w,c,y,c, NUL }; ADD(ppu.drawStartCycle); } + { static const char label[] = { s,c,r,d,c,y,c, NUL }; ADD(ppu.scReadOffset); } + { static const char label[] = { l,c,d,c, NUL }; ADD(ppu.lcdc); } + { static const char label[] = { s,c,x,NO0, NUL }; ADD(ppu.scx[0]); } + { static const char label[] = { s,c,x,NO1, NUL }; ADD(ppu.scx[1]); } + { static const char label[] = { s,c,y,NO0, NUL }; ADD(ppu.scy[0]); } + { static const char label[] = { s,c,y,NO1, NUL }; ADD(ppu.scy[1]); } + { static const char label[] = { s,c,x,AMP,NO7, NUL }; ADD(ppu.scxAnd7); } + { static const char label[] = { w,e,m,a,s,t,r, NUL }; ADD(ppu.weMaster); } + { static const char label[] = { w,x, NUL }; ADD(ppu.wx); } + { static const char label[] = { w,y, NUL }; ADD(ppu.wy); } + { static const char label[] = { l,y,c,s,k,i,p, NUL }; ADD(ppu.lycIrqSkip); } + { static const char label[] = { s,p,u,c,n,t,r, NUL }; ADD(spu.cycleCounter); } + { static const char label[] = { s,w,p,c,n,t,r, NUL }; ADD(spu.ch1.sweep.counter); } + { static const char label[] = { s,w,p,s,h,d,w, NUL }; ADD(spu.ch1.sweep.shadow); } + { static const char label[] = { s,w,p,n,e,g, NUL }; ADD(spu.ch1.sweep.negging); } + { static const char label[] = { d,u,t,NO1,c,t,r, NUL }; ADD(spu.ch1.duty.nextPosUpdate); } + { static const char label[] = { d,u,t,NO1,p,o,s, NUL }; ADD(spu.ch1.duty.pos); } + { static const char label[] = { e,n,v,NO1,c,t,r, NUL }; ADD(spu.ch1.env.counter); } + { static const char label[] = { e,n,v,NO1,v,o,l, NUL }; ADD(spu.ch1.env.volume); } + { static const char label[] = { l,e,n,NO1,c,t,r, NUL }; ADD(spu.ch1.lcounter.counter); } + { static const char label[] = { l,e,n,NO1,v,a,l, NUL }; ADD(spu.ch1.lcounter.lengthCounter); } + { static const char label[] = { n,r,NO1,NO0, NUL }; ADD(spu.ch1.sweep.nr0); } + { static const char label[] = { n,r,NO1,NO3, NUL }; ADD(spu.ch1.duty.nr3); } + { static const char label[] = { n,r,NO1,NO4, NUL }; ADD(spu.ch1.nr4); } + { static const char label[] = { c,NO1,m,a,s,t,r, NUL }; ADD(spu.ch1.master); } + { static const char label[] = { d,u,t,NO2,c,t,r, NUL }; ADD(spu.ch2.duty.nextPosUpdate); } + { static const char label[] = { d,u,t,NO2,p,o,s, NUL }; ADD(spu.ch2.duty.pos); } + { static const char label[] = { e,n,v,NO2,c,t,r, NUL }; ADD(spu.ch2.env.counter); } + { static const char label[] = { e,n,v,NO2,v,o,l, NUL }; ADD(spu.ch2.env.volume); } + { static const char label[] = { l,e,n,NO2,c,t,r, NUL }; ADD(spu.ch2.lcounter.counter); } + { static const char label[] = { l,e,n,NO2,v,a,l, NUL }; ADD(spu.ch2.lcounter.lengthCounter); } + { static const char label[] = { n,r,NO2,NO3, NUL }; ADD(spu.ch2.duty.nr3); } + { static const char label[] = { n,r,NO2,NO4, NUL }; ADD(spu.ch2.nr4); } + { static const char label[] = { c,NO2,m,a,s,t,r, NUL }; ADD(spu.ch2.master); } + { static const char label[] = { w,a,v,e,r,a,m, NUL }; ADDPTR(spu.ch3.waveRam); } + { static const char label[] = { l,e,n,NO3,c,t,r, NUL }; ADD(spu.ch3.lcounter.counter); } + { static const char label[] = { l,e,n,NO3,v,a,l, NUL }; ADD(spu.ch3.lcounter.lengthCounter); } + { static const char label[] = { w,a,v,e,c,t,r, NUL }; ADD(spu.ch3.waveCounter); } + { static const char label[] = { l,w,a,v,r,d,t, NUL }; ADD(spu.ch3.lastReadTime); } + { static const char label[] = { w,a,v,e,p,o,s, NUL }; ADD(spu.ch3.wavePos); } + { static const char label[] = { w,a,v,s,m,p,l, NUL }; ADD(spu.ch3.sampleBuf); } + { static const char label[] = { n,r,NO3,NO3, NUL }; ADD(spu.ch3.nr3); } + { static const char label[] = { n,r,NO3,NO4, NUL }; ADD(spu.ch3.nr4); } + { static const char label[] = { c,NO3,m,a,s,t,r, NUL }; ADD(spu.ch3.master); } + { static const char label[] = { l,f,s,r,c,t,r, NUL }; ADD(spu.ch4.lfsr.counter); } + { static const char label[] = { l,f,s,r,r,e,g, NUL }; ADD(spu.ch4.lfsr.reg); } + { static const char label[] = { e,n,v,NO4,c,t,r, NUL }; ADD(spu.ch4.env.counter); } + { static const char label[] = { e,n,v,NO4,v,o,l, NUL }; ADD(spu.ch4.env.volume); } + { static const char label[] = { l,e,n,NO4,c,t,r, NUL }; ADD(spu.ch4.lcounter.counter); } + { static const char label[] = { l,e,n,NO4,v,a,l, NUL }; ADD(spu.ch4.lcounter.lengthCounter); } + { static const char label[] = { n,r,NO4,NO4, NUL }; ADD(spu.ch4.nr4); } + { static const char label[] = { c,NO4,m,a,s,t,r, NUL }; ADD(spu.ch4.master); } + { static const char label[] = { r,t,c,b,a,s,e, NUL }; ADD(rtc.baseTime); } + { static const char label[] = { r,t,c,h,a,l,t, NUL }; ADD(rtc.haltTime); } + { static const char label[] = { r,t,c,i,n,d,x, NUL }; ADD(rtc.index); } + { static const char label[] = { r,t,c,d,h, NUL }; ADD(rtc.dataDh); } + { static const char label[] = { r,t,c,d,l, NUL }; ADD(rtc.dataDl); } + { static const char label[] = { r,t,c,h, NUL }; ADD(rtc.dataH); } + { static const char label[] = { r,t,c,m, NUL }; ADD(rtc.dataM); } + { static const char label[] = { r,t,c,s, NUL }; ADD(rtc.dataS); } + { static const char label[] = { r,t,c,l,l,d, NUL }; ADD(rtc.lastLatchData); } + +#undef ADD +#undef ADDPTR +#undef ADDTIME + + list.resize(list.size()); + std::sort(list.begin(), list.end()); + + maxLabelsize_ = 0; + + for (std::size_t i = 0; i < list.size(); ++i) { + if (list[i].labelsize > maxLabelsize_) + maxLabelsize_ = list[i].labelsize; + } +} + +static void writeSnapShot(std::ofstream &file, const Gambatte::uint_least32_t *pixels, const unsigned pitch) { + put24(file, pixels ? StateSaver::SS_WIDTH * StateSaver::SS_HEIGHT * sizeof(Gambatte::uint_least32_t) : 0); + + if (pixels) { + Gambatte::uint_least32_t buf[StateSaver::SS_WIDTH]; + + for (unsigned h = StateSaver::SS_HEIGHT; h--;) { + for (unsigned x = 0; x < StateSaver::SS_WIDTH; ++x) { + unsigned long rb = 0; + unsigned long g = 0; + + static const unsigned w[StateSaver::SS_DIV] = { 3, 5, 5, 3 }; + + for (unsigned y = 0; y < StateSaver::SS_DIV; ++y) + for (unsigned xx = 0; xx < StateSaver::SS_DIV; ++xx) { + rb += (pixels[x * StateSaver::SS_DIV + y * pitch + xx] & 0xFF00FF) * w[y] * w[xx]; + g += (pixels[x * StateSaver::SS_DIV + y * pitch + xx] & 0x00FF00) * w[y] * w[xx]; + } + + buf[x] = (rb >> 8 & 0xFF00FF) | (g >> 8 & 0x00FF00); + } + + file.write(reinterpret_cast(buf), sizeof(buf)); + pixels += pitch * StateSaver::SS_DIV; + } + } +} + +static SaverList list; + +void StateSaver::saveState(const SaveState &state, const char *filename) { + std::ofstream file(filename, std::ios_base::binary); + + if (file.fail()) + return; + + { static const char ver[] = { 0, 0 }; file.write(ver, sizeof(ver)); } + + writeSnapShot(file, state.ppu.drawBuffer.get(), state.ppu.drawBuffer.getSz() / 144); + + for (SaverList::const_iterator it = list.begin(); it != list.end(); ++it) { + file.write(it->label, it->labelsize); + (*it->save)(file, state); + } +} + +bool StateSaver::loadState(SaveState &state, const char *filename) { + std::ifstream file(filename, std::ios_base::binary); + + if (file.fail() || file.get() != 0) + return false; + + file.ignore(); + file.ignore(get24(file)); + + Array labelbuf(list.maxLabelsize()); + const Saver labelbufSaver = { label: labelbuf, save: 0, load: 0, labelsize: list.maxLabelsize() }; + + SaverList::const_iterator done = list.begin(); + + while (file.good() && done != list.end()) { + file.getline(labelbuf, list.maxLabelsize(), NUL); + + SaverList::const_iterator it = done; + + if (std::strcmp(labelbuf, it->label)) { + it = std::lower_bound(it + 1, list.end(), labelbufSaver); + + if (it == list.end() || std::strcmp(labelbuf, it->label)) { + file.ignore(get24(file)); + continue; + } + } else + ++done; + + (*it->load)(file, state); + } + + state.cpu.cycleCounter &= 0x7FFFFFFF; + state.spu.cycleCounter &= 0x7FFFFFFF; + + return true; +} diff --git a/src/lib/libgambatte/src/statesaver.h b/src/lib/libgambatte/src/statesaver.h new file mode 100644 index 00000000..ea9ce8b3 --- /dev/null +++ b/src/lib/libgambatte/src/statesaver.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef STATESAVER_H +#define STATESAVER_H + +class SaveState; + +class StateSaver { + StateSaver(); + +public: + enum { SS_SHIFT = 2 }; + enum { SS_DIV = 1 << 2 }; + enum { SS_WIDTH = 160 >> SS_SHIFT }; + enum { SS_HEIGHT = 144 >> SS_SHIFT }; + + static void saveState(const SaveState &state, const char *filename); + static bool loadState(SaveState &state, const char *filename); +}; + +#endif diff --git a/src/lib/libgambatte/src/video.cpp b/src/lib/libgambatte/src/video.cpp new file mode 100644 index 00000000..875afa43 --- /dev/null +++ b/src/lib/libgambatte/src/video.cpp @@ -0,0 +1,1474 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "video.h" +#include "videoblitter.h" +#include "video/filters/filter.h" +#include "video/filters/catrom2x.h" +#include "video/filters/catrom3x.h" +#include "video/filters/kreed2xsai.h" +#include "video/filters/maxsthq2x.h" +#include "video/filters/maxsthq3x.h" +#include "filterinfo.h" +#include "savestate.h" +#include "video/basic_add_event.h" +#include +#include + +static void addEventIfActivated(event_queue &q, VideoEvent *const e, const unsigned long newTime) { + e->setTime(newTime); + + if (newTime != VideoEvent::DISABLED_TIME) + q.push(e); +} + +void LCD::setDmgPalette(unsigned long *const palette, const unsigned long *const dmgColors, const unsigned data) { + palette[0] = dmgColors[data & 3]; + palette[1] = dmgColors[data >> 2 & 3]; + palette[2] = dmgColors[data >> 4 & 3]; + palette[3] = dmgColors[data >> 6 & 3]; +} + +unsigned long LCD::gbcToRgb32(const unsigned bgr15) { + const unsigned long r = bgr15 & 0x1F; + const unsigned long g = bgr15 >> 5 & 0x1F; + const unsigned long b = bgr15 >> 10 & 0x1F; + + return ((r * 13 + g * 2 + b) >> 1) << 16 | ((g * 3 + b) << 9) | ((r * 3 + g * 2 + b * 11) >> 1); +} + +unsigned long LCD::gbcToRgb16(const unsigned bgr15) { + const unsigned r = bgr15 & 0x1F; + const unsigned g = bgr15 >> 5 & 0x1F; + const unsigned b = bgr15 >> 10 & 0x1F; + + return (((r * 13 + g * 2 + b + 8) << 7) & 0xF800) | ((g * 3 + b + 1) >> 1) << 5 | ((r * 3 + g * 2 + b * 11 + 8) >> 4); +} + +unsigned long LCD::gbcToUyvy(const unsigned bgr15) { + const unsigned r5 = bgr15 & 0x1F; + const unsigned g5 = bgr15 >> 5 & 0x1F; + const unsigned b5 = bgr15 >> 10 & 0x1F; + + // y = (r5 * 926151 + g5 * 1723530 + b5 * 854319) / 510000 + 16; + // u = (b5 * 397544 - r5 * 68824 - g5 * 328720) / 225930 + 128; + // v = (r5 * 491176 - g5 * 328720 - b5 * 162456) / 178755 + 128; + + const unsigned long y = (r5 * 116 + g5 * 216 + b5 * 107 + 16 * 64 + 32) >> 6; + const unsigned long u = (b5 * 225 - r5 * 39 - g5 * 186 + 128 * 128 + 64) >> 7; + const unsigned long v = (r5 * 176 - g5 * 118 - b5 * 58 + 128 * 64 + 32) >> 6; + +#ifdef WORDS_BIGENDIAN + return u << 24 | y << 16 | v << 8 | y; +#else + return y << 24 | v << 16 | y << 8 | u; +#endif +} + +LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram_in) : + vram(vram_in), + bgTileData(vram), + bgTileMap(vram + 0x1800), + wdTileMap(bgTileMap), + vBlitter(NULL), + filter(NULL), + dbuffer(NULL), + draw(NULL), + gbcToFormat(gbcToRgb32), + dmgColors(dmgColorsRgb32), + lastUpdate(0), + videoCycles(0), + dpitch(0), + winYPos(0), + m3EventQueue(11, VideoEventComparer()), + irqEventQueue(4, VideoEventComparer()), + vEventQueue(5, VideoEventComparer()), + win(m3EventQueue, lyCounter, m3ExtraCycles), + scxReader(m3EventQueue, /*wyReg.reader3(),*/ win.wxReader, win.we.enableChecker(), win.we.disableChecker(), m3ExtraCycles), + spriteMapper(m3ExtraCycles, lyCounter, oamram), + m3ExtraCycles(spriteMapper, scxReader, win), + breakEvent(drawStartCycle, scReadOffset), + mode3Event(m3EventQueue, vEventQueue, mode0Irq, irqEvent), + lycIrq(ifReg), + mode0Irq(lyCounter, lycIrq, m3ExtraCycles, ifReg), + mode1Irq(ifReg), + mode2Irq(lyCounter, lycIrq, ifReg), + irqEvent(irqEventQueue), + drawStartCycle(90), + scReadOffset(90), + ifReg(0), + tileIndexSign(0), + statReg(0), + doubleSpeed(false), + enabled(false), + cgb(false), + bgEnable(false), + spriteEnable(false) +{ + std::memset(bgpData, 0, sizeof(bgpData)); + std::memset(objpData, 0, sizeof(objpData)); + + for (unsigned i = 0; i < sizeof(dmgColorsRgb32) / sizeof(unsigned long); ++i) { + setDmgPaletteColor(i, (3 - (i & 3)) * 85 * 0x010101); + } + + filters.push_back(NULL); + filters.push_back(new Catrom2x); + filters.push_back(new Catrom3x); + filters.push_back(new Kreed_2xSaI); + filters.push_back(new MaxSt_Hq2x); + filters.push_back(new MaxSt_Hq3x); + + reset(oamram, false); + setDoubleSpeed(false); + + setVideoFilter(0); +} + +LCD::~LCD() { +// delete []filter_buffer; + for (std::size_t i = 0; i < filters.size(); ++i) + delete filters[i]; +} + +void LCD::reset(const unsigned char *const oamram, const bool cgb_in) { + cgb = cgb_in; + spriteMapper.reset(oamram, cgb_in); + setDBuffer(); +} + +void LCD::resetVideoState(const unsigned long cycleCounter) { + vEventQueue.clear(); + m3EventQueue.clear(); + irqEventQueue.clear(); + + lyCounter.reset(videoCycles, lastUpdate); + vEventQueue.push(&lyCounter); + + spriteMapper.resetVideoState(); + m3ExtraCycles.invalidateCache(); + + addEventIfActivated(m3EventQueue, &scxReader, ScxReader::schedule(lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.wxReader, WxReader::schedule(scxReader.scxAnd7(), lyCounter, win.wxReader, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.wyReg.reader1(), Wy::WyReader1::schedule(lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.wyReg.reader2(), Wy::WyReader2::schedule(lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.wyReg.reader3(), Wy::WyReader3::schedule(win.wxReader.getSource(), scxReader, lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.wyReg.reader4(), Wy::WyReader4::schedule(lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &spriteMapper, SpriteMapper::schedule(lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.we.enableChecker(), We::WeEnableChecker::schedule(scxReader.scxAnd7(), win.wxReader.wx(), lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.we.disableChecker(), We::WeDisableChecker::schedule(scxReader.scxAnd7(), win.wxReader.wx(), lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.weMasterChecker, WeMasterChecker::schedule(win.wyReg.getSource(), win.we.getSource(), lyCounter, cycleCounter)); + + addEventIfActivated(irqEventQueue, &lycIrq, LycIrq::schedule(statReg, lycIrq.lycReg(), lyCounter, cycleCounter)); + addEventIfActivated(irqEventQueue, &mode0Irq, Mode0Irq::schedule(statReg, m3ExtraCycles, lyCounter, cycleCounter)); + addEventIfActivated(irqEventQueue, &mode1Irq, Mode1Irq::schedule(lyCounter, cycleCounter)); + addEventIfActivated(irqEventQueue, &mode2Irq, Mode2Irq::schedule(statReg, lyCounter, cycleCounter)); + + addEventIfActivated(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); + addEventIfActivated(vEventQueue, &irqEvent, IrqEvent::schedule(irqEventQueue)); + addEventIfActivated(vEventQueue, &scReader, ScReader::schedule(lastUpdate, videoCycles, scReadOffset, doubleSpeed)); + addEventIfActivated(vEventQueue, &breakEvent, BreakEvent::schedule(lyCounter)); +} + +void LCD::setDoubleSpeed(const bool ds) { + doubleSpeed = ds; + lyCounter.setDoubleSpeed(doubleSpeed); + scxReader.setDoubleSpeed(doubleSpeed); + win.wxReader.setDoubleSpeed(doubleSpeed); + scReader.setDoubleSpeed(doubleSpeed); + breakEvent.setDoubleSpeed(doubleSpeed); + lycIrq.setDoubleSpeed(doubleSpeed); + mode1Irq.setDoubleSpeed(doubleSpeed); +} + +void LCD::setStatePtrs(SaveState &state) { + state.ppu.drawBuffer.set(static_cast(dbuffer), dpitch * 144); + state.ppu.bgpData.set(bgpData, sizeof bgpData); + state.ppu.objpData.set(objpData, sizeof objpData); + spriteMapper.setStatePtrs(state); +} + +void LCD::saveState(SaveState &state) const { + state.ppu.videoCycles = videoCycles; + state.ppu.winYPos = winYPos; + state.ppu.drawStartCycle = drawStartCycle; + state.ppu.scReadOffset = scReadOffset; + state.ppu.lcdc = enabled << 7 | ((wdTileMap - vram - 0x1800) >> 4) | (tileIndexSign ^ 0x80) >> 3 | ((bgTileMap - vram - 0x1800) >> 7) | spriteEnable << 1 | bgEnable; + state.ppu.lycIrqSkip = lycIrq.skips(); + + spriteMapper.saveState(state); + scReader.saveState(state); + scxReader.saveState(state); + win.weMasterChecker.saveState(state); + win.wxReader.saveState(state); + win.wyReg.saveState(state); + win.we.saveState(state); +} + +void LCD::loadState(const SaveState &state, const unsigned char *oamram) { + statReg = state.mem.ioamhram.get()[0x141]; + ifReg = 0; + + setDoubleSpeed(cgb & state.mem.ioamhram.get()[0x14D] >> 7); + + lastUpdate = state.cpu.cycleCounter; + videoCycles = std::min(state.ppu.videoCycles, 70223ul); + winYPos = state.ppu.winYPos > 143 ? 0xFF : state.ppu.winYPos; + drawStartCycle = state.ppu.drawStartCycle; + scReadOffset = state.ppu.scReadOffset; + enabled = state.ppu.lcdc >> 7 & 1; + wdTileMap = vram + 0x1800 + (state.ppu.lcdc >> 6 & 1) * 0x400; + tileIndexSign = ((state.ppu.lcdc >> 4 & 1) ^ 1) * 0x80; + bgTileData = vram + ((state.ppu.lcdc >> 4 & 1) ^ 1) * 0x1000; + bgTileMap = vram + 0x1800 + (state.ppu.lcdc >> 3 & 1) * 0x400; + spriteEnable = state.ppu.lcdc >> 1 & 1; + bgEnable = state.ppu.lcdc & 1; + + lycIrq.setM2IrqEnabled(statReg >> 5 & 1); + lycIrq.setLycReg(state.mem.ioamhram.get()[0x145]); + lycIrq.setSkip(state.ppu.lycIrqSkip); + mode1Irq.setM1StatIrqEnabled(statReg >> 4 & 1); + + win.we.setSource(state.mem.ioamhram.get()[0x140] >> 5 & 1); + spriteMapper.setLargeSpritesSource(state.mem.ioamhram.get()[0x140] >> 2 & 1); + scReader.setScySource(state.mem.ioamhram.get()[0x142]); + scxReader.setSource(state.mem.ioamhram.get()[0x143]); + breakEvent.setScxSource(state.mem.ioamhram.get()[0x143]); + scReader.setScxSource(state.mem.ioamhram.get()[0x143]); + win.wyReg.setSource(state.mem.ioamhram.get()[0x14A]); + win.wxReader.setSource(state.mem.ioamhram.get()[0x14B]); + + spriteMapper.loadState(state); + scReader.loadState(state); + scxReader.loadState(state); + win.weMasterChecker.loadState(state); + win.wxReader.loadState(state); + win.wyReg.loadState(state); + win.we.loadState(state); + + resetVideoState(lastUpdate); + spriteMapper.oamChange(oamram, lastUpdate); + refreshPalettes(); +} + +void LCD::refreshPalettes() { + if (cgb) { + for (unsigned i = 0; i < 8 * 8; i += 2) { + bgPalette[i >> 1] = (*gbcToFormat)(bgpData[i] | bgpData[i + 1] << 8); + spPalette[i >> 1] = (*gbcToFormat)(objpData[i] | objpData[i + 1] << 8); + } + } else { + setDmgPalette(bgPalette, dmgColors, bgpData[0]); + setDmgPalette(spPalette, dmgColors + 4, objpData[0]); + setDmgPalette(spPalette + 4, dmgColors + 8, objpData[1]); + } +} + +void LCD::setVideoBlitter(Gambatte::VideoBlitter *vb) { + vBlitter = vb; + + if (vBlitter) { + vBlitter->setBufferDimensions(videoWidth(), videoHeight()); + pb = vBlitter->inBuffer(); + } + + setDBuffer(); +} + +void LCD::videoBufferChange() { + if (vBlitter) { + pb = vBlitter->inBuffer(); + setDBuffer(); + } +} + +void LCD::setVideoFilter(const unsigned n) { + const unsigned oldw = videoWidth(); + const unsigned oldh = videoHeight(); + + if (filter) + filter->outit(); + + filter = filters.at(n < filters.size() ? n : 0); + + if (filter) { + filter->init(); + } + + if (vBlitter && (oldw != videoWidth() || oldh != videoHeight())) { + vBlitter->setBufferDimensions(videoWidth(), videoHeight()); + pb = vBlitter->inBuffer(); + } + + setDBuffer(); +} + +std::vector LCD::filterInfo() const { + std::vector v; + + static Gambatte::FilterInfo noInfo = { "None", 160, 144 }; + v.push_back(&noInfo); + + for (std::size_t i = 1; i < filters.size(); ++i) + v.push_back(&filters[i]->info()); + + return v; +} + +unsigned int LCD::videoWidth() const { + return filter ? filter->info().outWidth : 160; +} + +unsigned int LCD::videoHeight() const { + return filter ? filter->info().outHeight : 144; +} + +template +static void blitOsdElement(Gambatte::uint_least32_t *d, const Gambatte::uint_least32_t *s, const unsigned width, unsigned h, const unsigned dpitch, Blend blend) { + while (h--) { + for (unsigned w = width; w--;) { + if (*s != 0xFFFFFFFF) + *d = blend(*s, *d); + + ++d; + ++s; + } + + d += dpitch - width; + } +} + +template +struct Blend { + enum { SW = weight - 1 }; + enum { LOWMASK = SW * 0x010101ul }; + Gambatte::uint_least32_t operator()(const Gambatte::uint_least32_t s, const Gambatte::uint_least32_t d) const { + return (s * SW + d - (((s & LOWMASK) * SW + (d & LOWMASK)) & LOWMASK)) / weight; + } +}; + +void LCD::updateScreen(const unsigned long cycleCounter) { + update(cycleCounter); + + if (pb.pixels) { + if (dbuffer && osdElement.get()) { + const Gambatte::uint_least32_t *s = osdElement->update(); + + if (s) { + Gambatte::uint_least32_t *d = static_cast(dbuffer) + osdElement->y() * dpitch + osdElement->x(); + + switch (osdElement->opacity()) { + case OsdElement::SEVEN_EIGHTHS: blitOsdElement(d, s, osdElement->w(), osdElement->h(), dpitch, Blend<8>()); break; + case OsdElement::THREE_FOURTHS: blitOsdElement(d, s, osdElement->w(), osdElement->h(), dpitch, Blend<4>()); break; + } + } else + osdElement.reset(); + } + + if (filter) { + filter->filter(static_cast(tmpbuf ? tmpbuf : pb.pixels), (tmpbuf ? videoWidth() : pb.pitch)); + } + + if (tmpbuf) { + switch (pb.format) { + case Gambatte::PixelBuffer::RGB16: + rgb32ToRgb16(tmpbuf, static_cast(pb.pixels), videoWidth(), videoHeight(), pb.pitch); + break; + case Gambatte::PixelBuffer::UYVY: + rgb32ToUyvy(tmpbuf, static_cast(pb.pixels), videoWidth(), videoHeight(), pb.pitch); + break; + default: break; + } + } + + if (vBlitter) + vBlitter->blit(); + } +} + +template +static void clear(T *buf, const unsigned long color, const unsigned dpitch) { + unsigned lines = 144; + + while (lines--) { + std::fill_n(buf, 160, color); + buf += dpitch; + } +} + +void LCD::enableChange(const unsigned long cycleCounter) { + update(cycleCounter); + enabled = !enabled; + + if (enabled) { + lycIrq.setSkip(false); + videoCycles = 0; + lastUpdate = cycleCounter; + winYPos = 0xFF; + win.weMasterChecker.unset(); + spriteMapper.enableDisplay(cycleCounter); + resetVideoState(cycleCounter); + } + + if (!enabled && dbuffer) { + const unsigned long color = cgb ? (*gbcToFormat)(0xFFFF) : dmgColors[0]; + + clear(static_cast(dbuffer), color, dpitch); + +// updateScreen(cycleCounter); + } +} + +//FIXME: needs testing +void LCD::lyWrite(const unsigned long cycleCounter) { + update(cycleCounter); + lycIrq.setSkip(false); + videoCycles = 0; + lastUpdate = cycleCounter; + winYPos = 0xFF; + win.weMasterChecker.unset(); + resetVideoState(cycleCounter); + +// if ((statReg & 0x40) && lycIrq.lycReg() == 0) +// ifReg |= 2; +} + +void LCD::preResetCounter(const unsigned long cycleCounter) { + preSpeedChange(cycleCounter); +} + +void LCD::postResetCounter(const unsigned long oldCC, const unsigned long cycleCounter) { + lastUpdate = cycleCounter - (oldCC - lastUpdate); + spriteMapper.resetCycleCounter(oldCC, cycleCounter); + resetVideoState(cycleCounter); +} + +void LCD::preSpeedChange(const unsigned long cycleCounter) { + update(cycleCounter); + spriteMapper.preCounterChange(cycleCounter); +} + +void LCD::postSpeedChange(const unsigned long cycleCounter) { + setDoubleSpeed(!doubleSpeed); + + resetVideoState(cycleCounter); +} + +bool LCD::isMode0IrqPeriod(const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + const unsigned timeToNextLy = lyCounter.time() - cycleCounter; + + return /*memory.enable_display && */lyCounter.ly() < 144 && timeToNextLy <= (456U - (169 + doubleSpeed * 3U + 80 + m3ExtraCycles(lyCounter.ly()) + 1 - doubleSpeed)) << doubleSpeed && timeToNextLy > 4; +} + +bool LCD::isMode2IrqPeriod(const unsigned long cycleCounter) { + if (cycleCounter >= lyCounter.time()) + update(cycleCounter); + + const unsigned nextLy = lyCounter.time() - cycleCounter; + + return /*memory.enable_display && */lyCounter.ly() < 143 && nextLy <= 4; +} + +bool LCD::isLycIrqPeriod(const unsigned lycReg, const unsigned endCycles, const unsigned long cycleCounter) { + if (cycleCounter >= lyCounter.time()) + update(cycleCounter); + + const unsigned timeToNextLy = lyCounter.time() - cycleCounter; + + return (lyCounter.ly() == lycReg && timeToNextLy > endCycles) || (lycReg == 0 && lyCounter.ly() == 153 && timeToNextLy <= (456U - 8U) << doubleSpeed); +} + +bool LCD::isMode1IrqPeriod(const unsigned long cycleCounter) { + if (cycleCounter >= lyCounter.time()) + update(cycleCounter); + + const unsigned timeToNextLy = lyCounter.time() - cycleCounter; + + return lyCounter.ly() > 143 && (lyCounter.ly() < 153 || timeToNextLy > 4U - doubleSpeed * 4U); +} + +bool LCD::isHdmaPeriod(const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + const unsigned timeToNextLy = lyCounter.time() - cycleCounter; + + return /*memory.enable_display && */lyCounter.ly() < 144 && timeToNextLy <= ((456U - (169U + doubleSpeed * 3U + 80U + m3ExtraCycles(lyCounter.ly()) + 2 - doubleSpeed)) << doubleSpeed) && timeToNextLy > 4; +} + +unsigned long LCD::nextHdmaTime(const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + unsigned line = lyCounter.ly(); + int next = static_cast(169 + doubleSpeed * 3U + 80 + 2 - doubleSpeed) - static_cast(lyCounter.lineCycles(cycleCounter)); + + if (line < 144 && next + static_cast(m3ExtraCycles(line)) <= 0) { + next += 456; + ++line; + } + + if (line > 143) { + next += (154 - line) * 456; + line = 0; + } + + next += m3ExtraCycles(line); + + return cycleCounter + (static_cast(next) << doubleSpeed); +} + +bool LCD::vramAccessible(const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + bool accessible = true; + + if (enabled && lyCounter.ly() < 144) { + const unsigned lineCycles = lyCounter.lineCycles(cycleCounter); + + if (lineCycles > 79 && lineCycles < 80 + 169 + doubleSpeed * 3U + m3ExtraCycles(lyCounter.ly())) + accessible = false; + } + + return accessible; +} + +bool LCD::cgbpAccessible(const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + bool accessible = true; + + if (enabled && lyCounter.ly() < 144) { + const unsigned lineCycles = lyCounter.lineCycles(cycleCounter); + + if (lineCycles > 79U + doubleSpeed && lineCycles < 80U + 169U + doubleSpeed * 3U + m3ExtraCycles(lyCounter.ly()) + 4U - doubleSpeed * 2U) + accessible = false; + } + + return accessible; +} + +bool LCD::oamAccessible(const unsigned long cycleCounter) { + bool accessible = true; + + if (enabled) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + accessible = spriteMapper.oamAccessible(cycleCounter); + } + + return accessible; +} + +void LCD::weChange(const bool newValue, const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + win.we.setSource(newValue); + addFixedtimeEvent(m3EventQueue, &win.weMasterChecker, WeMasterChecker::schedule(win.wyReg.getSource(), newValue, lyCounter, cycleCounter)); + addFixedtimeEvent(m3EventQueue, &win.we.disableChecker(), We::WeDisableChecker::schedule(scxReader.scxAnd7(), win.wxReader.wx(), lyCounter, cycleCounter)); + addFixedtimeEvent(m3EventQueue, &win.we.enableChecker(), We::WeEnableChecker::schedule(scxReader.scxAnd7(), win.wxReader.wx(), lyCounter, cycleCounter)); + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); +} + +void LCD::wxChange(const unsigned newValue, const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + win.wxReader.setSource(newValue); + addEvent(m3EventQueue, &win.wxReader, WxReader::schedule(scxReader.scxAnd7(), lyCounter, win.wxReader, cycleCounter)); + + if (win.wyReg.reader3().time() != VideoEvent::DISABLED_TIME) + addEvent(m3EventQueue, &win.wyReg.reader3(), Wy::WyReader3::schedule(win.wxReader.getSource(), scxReader, lyCounter, cycleCounter)); + + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); +} + +void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + win.wyReg.setSource(newValue); + addFixedtimeEvent(m3EventQueue, &win.wyReg.reader1(), Wy::WyReader1::schedule(lyCounter, cycleCounter)); + addFixedtimeEvent(m3EventQueue, &win.wyReg.reader2(), Wy::WyReader2::schedule(lyCounter, cycleCounter)); + addFixedtimeEvent(m3EventQueue, &win.wyReg.reader3(), Wy::WyReader3::schedule(win.wxReader.getSource(), scxReader, lyCounter, cycleCounter)); + addFixedtimeEvent(m3EventQueue, &win.wyReg.reader4(), Wy::WyReader4::schedule(lyCounter, cycleCounter)); + addEvent(m3EventQueue, &win.weMasterChecker, WeMasterChecker::schedule(win.wyReg.getSource(), win.we.getSource(), lyCounter, cycleCounter)); + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); +} + +void LCD::scxChange(const unsigned newScx, const unsigned long cycleCounter) { + update(cycleCounter); + + scxReader.setSource(newScx); + breakEvent.setScxSource(newScx); + scReader.setScxSource(newScx); + + addFixedtimeEvent(m3EventQueue, &scxReader, ScxReader::schedule(lyCounter, cycleCounter)); + + if (win.wyReg.reader3().time() != VideoEvent::DISABLED_TIME) + addEvent(m3EventQueue, &win.wyReg.reader3(), Wy::WyReader3::schedule(win.wxReader.getSource(), scxReader, lyCounter, cycleCounter)); + + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); + + const unsigned lineCycles = lyCounter.lineCycles(cycleCounter); + + if (lineCycles < 82U + doubleSpeed * 4U) + drawStartCycle = 90 + doubleSpeed * 4U + (newScx & 7); + else + addFixedtimeEvent(vEventQueue, &breakEvent, BreakEvent::schedule(lyCounter)); + + if (lineCycles < 86U + doubleSpeed * 2U) + scReadOffset = std::max(drawStartCycle - (newScx & 7), 90U + doubleSpeed * 4U); + + addEvent(vEventQueue, &scReader, ScReader::schedule(lastUpdate, videoCycles, scReadOffset, doubleSpeed)); +} + +void LCD::scyChange(const unsigned newValue, const unsigned long cycleCounter) { + update(cycleCounter); + + scReader.setScySource(newValue); + addFixedtimeEvent(vEventQueue, &scReader, ScReader::schedule(lastUpdate, videoCycles, scReadOffset, doubleSpeed)); +} + +void LCD::spriteSizeChange(const bool newLarge, const unsigned long cycleCounter) { + update(cycleCounter); + + spriteMapper.oamChange(cycleCounter); + spriteMapper.setLargeSpritesSource(newLarge); + addFixedtimeEvent(m3EventQueue, &spriteMapper, SpriteMapper::schedule(lyCounter, cycleCounter)); + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); +} + +void LCD::oamChange(const unsigned long cycleCounter) { + update(cycleCounter); + + spriteMapper.oamChange(cycleCounter); + addFixedtimeEvent(m3EventQueue, &spriteMapper, SpriteMapper::schedule(lyCounter, cycleCounter)); + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); +} + +void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycleCounter) { + update(cycleCounter); + + spriteMapper.oamChange(oamram, cycleCounter); + addFixedtimeEvent(m3EventQueue, &spriteMapper, SpriteMapper::schedule(lyCounter, cycleCounter)); + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); +} + +void LCD::wdTileMapSelectChange(const bool newValue, const unsigned long cycleCounter) { + update(cycleCounter); + + wdTileMap = vram + 0x1800 + newValue * 0x400; +} + +void LCD::bgTileMapSelectChange(const bool newValue, const unsigned long cycleCounter) { + update(cycleCounter); + + bgTileMap = vram + 0x1800 + newValue * 0x400; +} + +void LCD::bgTileDataSelectChange(const bool newValue, const unsigned long cycleCounter) { + update(cycleCounter); + + tileIndexSign = (newValue ^ 1) * 0x80; + bgTileData = vram + (newValue ^ 1) * 0x1000; +} + +void LCD::spriteEnableChange(const bool newValue, const unsigned long cycleCounter) { + update(cycleCounter); + + spriteEnable = newValue; +} + +void LCD::bgEnableChange(const bool newValue, const unsigned long cycleCounter) { + update(cycleCounter); + + bgEnable = newValue; +} + +void LCD::lcdstatChange(const unsigned data, const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + const unsigned old = statReg; + statReg = data; + mode1Irq.setM1StatIrqEnabled(data & 0x10); + lycIrq.setM2IrqEnabled(data & 0x20); + + if (!enabled) + return; + + const bool lycIrqPeriod = isLycIrqPeriod(lycIrq.lycReg(), lycIrq.lycReg() == 153 ? lyCounter.lineTime() - (4 << (doubleSpeed*2)) : 4 - doubleSpeed * 4U, cycleCounter); + + if (lycIrq.lycReg() < 154 && ((data ^ old) & 0x40)) { + if (data & 0x40) { + if (lycIrqPeriod) + ifReg |= 2; + } else { + if (!doubleSpeed && lycIrq.time() - cycleCounter < 5 && (!(old & 0x20) || lycIrq.lycReg() > 143 || lycIrq.lycReg() == 0)) + ifReg |= 2; + } + + addFixedtimeEvent(irqEventQueue, &lycIrq, LycIrq::schedule(data, lycIrq.lycReg(), lyCounter, cycleCounter)); + } + + if ((((data & 0x10) && !(old & 0x10)) || !cgb) && !((old & 0x40) && lycIrqPeriod) && isMode1IrqPeriod(cycleCounter)) + ifReg |= 2; + + if ((data ^ old) & 0x08) { + if (data & 0x08) { + if (!((old & 0x40) && lycIrqPeriod) && isMode0IrqPeriod(cycleCounter)) + ifReg |= 2; + } else { + if (mode0Irq.time() - cycleCounter < 3 && (lycIrq.time() == VideoEvent::DISABLED_TIME || lyCounter.ly() != lycIrq.lycReg())) + ifReg |= 2; + } + + addFixedtimeEvent(irqEventQueue, &mode0Irq, Mode0Irq::schedule(data, m3ExtraCycles, lyCounter, cycleCounter)); + } + + if ((data & 0x28) == 0x20 && (old & 0x28) != 0x20 && isMode2IrqPeriod(cycleCounter)) { + ifReg |= 2; + } + + addFixedtimeEvent(irqEventQueue, &mode2Irq, Mode2Irq::schedule(data, lyCounter, cycleCounter)); + + addEvent(vEventQueue, &irqEvent, IrqEvent::schedule(irqEventQueue)); +} + +void LCD::lycRegChange(const unsigned data, const unsigned long cycleCounter) { + if (data == lycIrq.lycReg()) + return; + + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + const unsigned old = lycIrq.lycReg(); + lycIrq.setLycReg(data); + + if (!(enabled && (statReg & 0x40))) + return; + + if (!doubleSpeed && lycIrq.time() - cycleCounter < 5 && (!(statReg & 0x20) || old > 143 || old == 0)) + ifReg |= 2; + + addEvent(irqEventQueue, &lycIrq, LycIrq::schedule(statReg, lycIrq.lycReg(), lyCounter, cycleCounter)); + + if (data < 154) { + if (isLycIrqPeriod(data, data == 153 ? lyCounter.lineTime() - doubleSpeed * 8U : 8, cycleCounter)) + ifReg |= 2; + + if (lycIrq.isSkipPeriod(cycleCounter, doubleSpeed)) + lycIrq.setSkip(true); + } + + addEvent(vEventQueue, &irqEvent, IrqEvent::schedule(irqEventQueue)); +} + +unsigned long LCD::nextIrqEvent() const { + if (!enabled) + return VideoEvent::DISABLED_TIME; + + if (mode0Irq.time() != VideoEvent::DISABLED_TIME && mode3Event.time() < irqEvent.time()) + return mode3Event.time(); + + return irqEvent.time(); +} + +unsigned LCD::getIfReg(const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + return ifReg; +} + +void LCD::setIfReg(const unsigned ifReg_in, const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + ifReg = ifReg_in; +} + +unsigned LCD::get_stat(const unsigned lycReg, const unsigned long cycleCounter) { + unsigned stat = 0; + + if (enabled) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + const unsigned timeToNextLy = lyCounter.time() - cycleCounter; + + if (lyCounter.ly() > 143) { + if (lyCounter.ly() < 153 || timeToNextLy > 4 - doubleSpeed * 4U) + stat = 1; + } else { + const unsigned lineCycles = 456 - (timeToNextLy >> doubleSpeed); + + if (lineCycles < 80) { + if (!spriteMapper.inactivePeriodAfterDisplayEnable(cycleCounter)) + stat = 2; + } else if (lineCycles < 80 + 169 + doubleSpeed * 3U + m3ExtraCycles(lyCounter.ly())) + stat = 3; + } + + if ((lyCounter.ly() == lycReg && timeToNextLy > 4 - doubleSpeed * 4U) || + (lycReg == 0 && lyCounter.ly() == 153 && timeToNextLy >> doubleSpeed <= 456 - 8)) { + stat |= 4; + } + } + + return stat; +} + +void LCD::do_update(unsigned cycles) { + if (lyCounter.ly() < 144) { + const unsigned lineCycles = lyCounter.lineCycles(lastUpdate); + const unsigned xpos = lineCycles < drawStartCycle ? 0 : lineCycles - drawStartCycle; + + const unsigned endLineCycles = lineCycles + cycles; + unsigned endX = endLineCycles < drawStartCycle ? 0 : endLineCycles - drawStartCycle; + + if (endX > 160) + endX = 160; + + if (xpos < endX) + (this->*draw)(xpos, lyCounter.ly(), endX); + } else if (lyCounter.ly() == 144) { + winYPos = 0xFF; + //scy[0] = scy[1] = memory.fastread(0xFF42); + //scx[0] = scx[1] = memory.fastread(0xFF43); + win.weMasterChecker.unset(); + } + + videoCycles += cycles; + + if (videoCycles >= 70224U) + videoCycles -= 70224U; +} + +inline void LCD::event() { + vEventQueue.top()->doEvent(); + + if (vEventQueue.top()->time() == VideoEvent::DISABLED_TIME) + vEventQueue.pop(); + else + vEventQueue.modify_root(vEventQueue.top()); +} + +void LCD::update(const unsigned long cycleCounter) { + if (!enabled) + return; + + for (;;) { + const unsigned cycles = (std::max(std::min(cycleCounter, static_cast(vEventQueue.top()->time())), lastUpdate) - lastUpdate) >> doubleSpeed; + do_update(cycles); + lastUpdate += cycles << doubleSpeed; + + if (cycleCounter >= vEventQueue.top()->time()) + event(); + else + break; + } +} + +void LCD::setDBuffer() { + tmpbuf.reset(pb.format == Gambatte::PixelBuffer::RGB32 ? 0 : videoWidth() * videoHeight()); + + if (cgb) + draw = &LCD::cgb_draw; + else + draw = &LCD::dmg_draw; + + gbcToFormat = &gbcToRgb32; + dmgColors = dmgColorsRgb32; + + if (filter) { + dbuffer = filter->inBuffer(); + dpitch = filter->inPitch(); + } else if (pb.format == Gambatte::PixelBuffer::RGB32) { + dbuffer = pb.pixels; + dpitch = pb.pitch; + } else { + dbuffer = tmpbuf; + dpitch = 160; + } + + if (dbuffer == NULL) + draw = &LCD::null_draw; + + refreshPalettes(); +} + +void LCD::setDmgPaletteColor(const unsigned index, const unsigned long rgb32) { + dmgColorsRgb32[index] = rgb32; + dmgColorsRgb16[index] = rgb32ToRgb16(rgb32); + dmgColorsUyvy[index] = ::rgb32ToUyvy(rgb32); +} + +void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, const unsigned long rgb32) { + if (palNum > 2 || colorNum > 3) + return; + + setDmgPaletteColor((palNum * 4) | colorNum, rgb32); + refreshPalettes(); +} + +void LCD::null_draw(unsigned /*xpos*/, const unsigned ypos, const unsigned endX) { + const bool enableWindow = win.enabled(ypos); + + if (enableWindow && winYPos == 0xFF) + winYPos = /*ypos - wyReg.value()*/ 0; + + if (endX == 160) { + if (enableWindow) + ++winYPos; + } +} + +template +void LCD::cgb_draw(unsigned xpos, const unsigned ypos, const unsigned endX) { + const unsigned effectiveScx = scReader.scx(); + + const bool enableWindow = win.enabled(ypos); + + if (enableWindow && winYPos == 0xFF) + winYPos = /*ypos - wyReg.value()*/ 0; + + T *const bufLine = static_cast(dbuffer) + ypos * static_cast(dpitch); + + if (!(enableWindow && win.wxReader.wx() <= xpos + 7)) { + const unsigned fby = scReader.scy() + ypos /*& 0xFF*/; + const unsigned end = std::min(enableWindow ? win.wxReader.wx() - 7 : 160U, endX); + + cgb_bg_drawPixels(bufLine, xpos, end, scxReader.scxAnd7(), ((xpos + effectiveScx) & ~7) + ((xpos + drawStartCycle - scReadOffset) & 7), + bgTileMap + (fby & 0xF8) * 4, bgTileData, fby & 7); + } + + if (enableWindow && endX + 7 > win.wxReader.wx()) { + const unsigned start = std::max(win.wxReader.wx() < 7 ? 0U : (win.wxReader.wx() - 7), xpos); + + cgb_bg_drawPixels(bufLine, start, endX, 7u - win.wxReader.wx(), start + (7u - win.wxReader.wx()), + wdTileMap + (winYPos & 0xF8) * 4, bgTileData, winYPos & 7); + } + + if (endX == 160) { + if (spriteEnable) + cgb_drawSprites(bufLine, ypos); + + if (enableWindow) + ++winYPos; + } +} + +template +void LCD::dmg_draw(unsigned xpos, const unsigned ypos, const unsigned endX) { + const unsigned effectiveScx = scReader.scx(); + + const bool enableWindow = win.enabled(ypos); + + if (enableWindow && winYPos == 0xFF) + winYPos = /*ypos - wyReg.value()*/ 0; + + T *const bufLine = static_cast(dbuffer) + ypos * static_cast(dpitch); + + if (bgEnable) { + if (!(enableWindow && win.wxReader.wx() <= xpos + 7)) { + const unsigned fby = scReader.scy() + ypos /*& 0xFF*/; + const unsigned end = std::min(enableWindow ? win.wxReader.wx() - 7 : 160U, endX); + + bg_drawPixels(bufLine, xpos, end, scxReader.scxAnd7(), ((xpos + effectiveScx) & ~7) + ((xpos + drawStartCycle - scReadOffset) & 7), + bgTileMap + (fby & 0xF8) * 4, bgTileData + (fby & 7) * 2); + } + + if (enableWindow && endX + 7 > win.wxReader.wx()) { + const unsigned start = std::max(win.wxReader.wx() < 7 ? 0U : (win.wxReader.wx() - 7), xpos); + + bg_drawPixels(bufLine, start, endX, 7u - win.wxReader.wx(), start + (7u - win.wxReader.wx()), + wdTileMap + (winYPos & 0xF8) * 4, bgTileData + (winYPos & 7) * 2); + } + } else + std::fill_n(bufLine + xpos, endX - xpos, bgPalette[0]); + + if (endX == 160) { + if (spriteEnable) + drawSprites(bufLine, ypos); + + if (enableWindow) + ++winYPos; + } +} + +#define FLIP(u8) ( (((u8) & 0x01) << 7) | (((u8) & 0x02) << 5) | (((u8) & 0x04) << 3) | (((u8) & 0x08) << 1) | \ +(((u8) & 0x10) >> 1) | (((u8) & 0x20) >> 3) | (((u8) & 0x40) >> 5) | (((u8) & 0x80) >> 7) ) + +#define FLIP_ROW(n) FLIP((n)|0x0), FLIP((n)|0x1), FLIP((n)|0x2), FLIP((n)|0x3), FLIP((n)|0x4), FLIP((n)|0x5), FLIP((n)|0x6), FLIP((n)|0x7), \ +FLIP((n)|0x8), FLIP((n)|0x9), FLIP((n)|0xA), FLIP((n)|0xB), FLIP((n)|0xC), FLIP((n)|0xD), FLIP((n)|0xE), FLIP((n)|0xF) + +static const unsigned char xflipt[0x100] = { + FLIP_ROW(0x00), FLIP_ROW(0x10), FLIP_ROW(0x20), FLIP_ROW(0x30), + FLIP_ROW(0x40), FLIP_ROW(0x50), FLIP_ROW(0x60), FLIP_ROW(0x70), + FLIP_ROW(0x80), FLIP_ROW(0x90), FLIP_ROW(0xA0), FLIP_ROW(0xB0), + FLIP_ROW(0xC0), FLIP_ROW(0xD0), FLIP_ROW(0xE0), FLIP_ROW(0xF0) +}; + +#undef FLIP_ROW +#undef FLIP + +#define PREP(u8) (u8) + +#define EXPAND(u8) ((PREP(u8) << 7 & 0x4000) | (PREP(u8) << 6 & 0x1000) | (PREP(u8) << 5 & 0x0400) | (PREP(u8) << 4 & 0x0100) | \ + (PREP(u8) << 3 & 0x0040) | (PREP(u8) << 2 & 0x0010) | (PREP(u8) << 1 & 0x0004) | (PREP(u8) & 0x0001)) + +#define EXPAND_ROW(n) EXPAND((n)|0x0), EXPAND((n)|0x1), EXPAND((n)|0x2), EXPAND((n)|0x3), \ + EXPAND((n)|0x4), EXPAND((n)|0x5), EXPAND((n)|0x6), EXPAND((n)|0x7), \ + EXPAND((n)|0x8), EXPAND((n)|0x9), EXPAND((n)|0xA), EXPAND((n)|0xB), \ + EXPAND((n)|0xC), EXPAND((n)|0xD), EXPAND((n)|0xE), EXPAND((n)|0xF) + +#define EXPAND_TABLE EXPAND_ROW(0x00), EXPAND_ROW(0x10), EXPAND_ROW(0x20), EXPAND_ROW(0x30), \ + EXPAND_ROW(0x40), EXPAND_ROW(0x50), EXPAND_ROW(0x60), EXPAND_ROW(0x70), \ + EXPAND_ROW(0x80), EXPAND_ROW(0x90), EXPAND_ROW(0xA0), EXPAND_ROW(0xB0), \ + EXPAND_ROW(0xC0), EXPAND_ROW(0xD0), EXPAND_ROW(0xE0), EXPAND_ROW(0xF0) + +static const unsigned short expand_lut[0x200] = { + EXPAND_TABLE, + +#undef PREP +#define PREP(u8) (((u8) << 7 & 0x80) | ((u8) << 5 & 0x40) | ((u8) << 3 & 0x20) | ((u8) << 1 & 0x10) | \ + ((u8) >> 1 & 0x08) | ((u8) >> 3 & 0x04) | ((u8) >> 5 & 0x02) | ((u8) >> 7 & 0x01)) + + EXPAND_TABLE +}; + +#undef EXPAND_TABLE +#undef EXPAND_ROW +#undef EXPAND +#undef PREP + +//shoud work for the window too, if -wx is passed as scx. +//tilemap and tiledata must point to the areas in the first vram bank +//the second vram bank has to be placed immediately after the first one in memory (0x4000 continous bytes that cover both). +//tilemap needs to be offset to the right line +template +void LCD::cgb_bg_drawPixels(T * const buffer_line, unsigned xpos, const unsigned end, const unsigned scx, unsigned tilemappos, + const unsigned char *const tilemap, const unsigned char *const tiledata, const unsigned tileline) +{ + const unsigned sign = tileIndexSign; + unsigned shift = (7 - ((scx + xpos) & 7)) * 2; + T *buf = buffer_line + xpos; + T *const bufend = buffer_line + end; + + while (buf < bufend) { + if ((tilemappos & 7) || bufend - buf < 8) { + const unsigned char *const maptmp = tilemap + (tilemappos >> 3 & 0x1F); + const unsigned attributes = maptmp[0x2000]; + const unsigned char *const dataptr = tiledata + (attributes << 10 & 0x2000) + + maptmp[0] * 16 - (maptmp[0] & sign) * 32 + ((attributes & 0x40) ? 7 - tileline : tileline) * 2; + const unsigned short *const exp_lut = expand_lut + (attributes << 3 & 0x100); + + const unsigned data = exp_lut[dataptr[0]] + exp_lut[dataptr[1]] * 2; + const unsigned long *const palette = bgPalette + (attributes & 7) * 4; + + do { + *buf++ = palette[data >> shift & 3]; + shift = (shift - 2) & 15; + } while ((++tilemappos & 7) && buf < bufend); + } + + while (bufend - buf > 7) { + const unsigned char *const maptmp = tilemap + (tilemappos >> 3 & 0x1F); + const unsigned attributes = maptmp[0x2000]; + const unsigned char *const dataptr = tiledata + (attributes << 10 & 0x2000) + + maptmp[0] * 16 - (maptmp[0] & sign) * 32 + ((attributes & 0x40) ? 7 - tileline : tileline) * 2; + const unsigned short *const exp_lut = expand_lut + (attributes << 3 & 0x100); + + const unsigned data = exp_lut[dataptr[0]] + exp_lut[dataptr[1]] * 2; + const unsigned long *const palette = bgPalette + (attributes & 7) * 4; + + buf[0] = palette[data >> shift & 3]; + buf[1] = palette[data >> ((shift - 2) & 15) & 3]; + buf[2] = palette[data >> ((shift - 4) & 15) & 3]; + buf[3] = palette[data >> ((shift - 6) & 15) & 3]; + buf[4] = palette[data >> ((shift - 8) & 15) & 3]; + buf[5] = palette[data >> ((shift - 10) & 15) & 3]; + buf[6] = palette[data >> ((shift - 12) & 15) & 3]; + buf[7] = palette[data >> ((shift - 14) & 15) & 3]; + + buf += 8; + tilemappos += 8; + } + } +} + +static unsigned cgb_prioritizedBG_mask(const unsigned spx, const unsigned bgStart, const unsigned bgEnd, const unsigned scx, + const unsigned char *const tilemap, const unsigned char *const tiledata, const unsigned tileline, const unsigned sign) { + const unsigned spStart = spx < bgStart + 8 ? bgStart + 8 - spx : 0; + + unsigned bgbyte; + + { + const unsigned pos = scx + spx - 8 + spStart; + const unsigned char *maptmp = tilemap + (pos >> 3 & 0x1F); + unsigned tile = maptmp[0]; + unsigned attributes = maptmp[0x2000]; + + const unsigned char *const data = tiledata + (attributes << 10 & 0x2000) + + tile * 16 - (tile & sign) * 32 + ((attributes & 0x40) ? 7 - tileline : tileline) * 2; + + bgbyte = (attributes & 0x20) ? xflipt[data[0] | data[1]] : (data[0] | data[1]); + + const unsigned offset = pos & 7; + + if (offset) { + bgbyte <<= offset; + maptmp = tilemap + (((pos >> 3) + 1) & 0x1F); + tile = maptmp[0]; + attributes = maptmp[0x2000]; + + const unsigned char *const data = tiledata + (attributes << 10 & 0x2000) + + tile * 16 - (tile & sign) * 32 + ((attributes & 0x40) ? 7 - tileline : tileline) * 2; + + bgbyte |= ((attributes & 0x20) ? xflipt[data[0] | data[1]] : (data[0] | data[1])) >> (8 - offset); + } + } + + bgbyte >>= spStart; + const unsigned spEnd = spx > bgEnd ? bgEnd + 8 - spx : 8; + const unsigned mask = ~bgbyte | 0xFF >> spEnd; + + return mask; +} + +static unsigned cgb_toplayerBG_mask(const unsigned spx, const unsigned bgStart, const unsigned bgEnd, const unsigned scx, + const unsigned char *const tilemap, const unsigned char *const tiledata, const unsigned tileline, const unsigned sign) { + const unsigned spStart = spx < bgStart + 8 ? bgStart + 8 - spx : 0; + + unsigned bgbyte = 0; + + { + const unsigned pos = scx + spx - 8 + spStart; + const unsigned char *maptmp = tilemap + (pos >> 3 & 0x1F); + unsigned attributes = maptmp[0x2000]; + + if (attributes & 0x80) { + const unsigned tile = maptmp[0]; + + const unsigned char *const data = tiledata + (attributes << 10 & 0x2000) + + tile * 16 - (tile & sign) * 32 + ((attributes & 0x40) ? 7 - tileline : tileline) * 2; + + bgbyte = (attributes & 0x20) ? xflipt[data[0] | data[1]] : (data[0] | data[1]); + } + + const unsigned offset = pos & 7; + + if (offset) { + bgbyte <<= offset; + maptmp = tilemap + (((pos >> 3) + 1) & 0x1F); + attributes = maptmp[0x2000]; + + if (attributes & 0x80) { + const unsigned tile = maptmp[0]; + + const unsigned char *const data = tiledata + (attributes << 10 & 0x2000) + + tile * 16 - (tile & sign) * 32 + ((attributes & 0x40) ? 7 - tileline : tileline) * 2; + + bgbyte |= ((attributes & 0x20) ? xflipt[data[0] | data[1]] : (data[0] | data[1])) >> (8 - offset); + } + } + } + + bgbyte >>= spStart; + const unsigned spEnd = spx > bgEnd ? bgEnd + 8 - spx : 8; + const unsigned mask = ~bgbyte | 0xFF >> spEnd; + + return mask; +} + +template +void LCD::cgb_drawSprites(T * const buffer_line, const unsigned ypos) { + const unsigned scy = scReader.scy() + ypos /*& 0xFF*/; + const unsigned wx = win.wxReader.wx() < 7 ? 0 : win.wxReader.wx() - 7; + const bool enableWindow = win.enabled(ypos); + const unsigned char *const spriteMapLine = spriteMapper.sprites(ypos); + + for (int i = spriteMapper.numSprites(ypos) - 1; i >= 0; --i) { + const unsigned spNrX2 = spriteMapLine[i]; + const unsigned spx = spriteMapper.posbuf()[spNrX2 + 1]; + + if (spx < 168 && spx) { + unsigned spLine = ypos + 16 - spriteMapper.posbuf()[spNrX2]; + unsigned spTile = spriteMapper.oamram()[spNrX2 * 2 + 2]; + const unsigned attributes = spriteMapper.oamram()[spNrX2 * 2 + 3]; + + if (spriteMapper.largeSprites(spNrX2 >> 1)) { + if (attributes & 0x40) //yflip + spLine = 15 - spLine; + + if (spLine < 8) + spTile &= 0xFE; + else { + spLine -= 8; + spTile |= 0x01; + } + } else { + if (attributes & 0x40) //yflip + spLine = 7 - spLine; + } + + const unsigned char *const data = vram + ((attributes * 0x400) & 0x2000) + spTile * 16 + spLine * 2; + + unsigned byte1 = data[0]; + unsigned byte2 = data[1]; + + if (attributes & 0x20) { + byte1 = xflipt[byte1]; + byte2 = xflipt[byte2]; + } + + //(Sprites with priority-bit are still allowed to cover other sprites according to GBdev-faq.) + if (bgEnable) { + unsigned mask = 0xFF; + + if (attributes & 0x80) { + if (!(enableWindow && (wx == 0 || spx >= wx + 8u))) + mask = cgb_prioritizedBG_mask(spx, 0, enableWindow ? wx : 160, scReader.scx(), + bgTileMap + ((scy & 0xF8) << 2), bgTileData, scy & 7, tileIndexSign); + if (enableWindow && spx > wx) + mask &= cgb_prioritizedBG_mask(spx, wx, 160, 0u - wx, wdTileMap + ((winYPos & 0xF8) << 2), bgTileData, winYPos & 7, tileIndexSign); + } else { + if (!(enableWindow && (wx == 0 || spx >= wx + 8u))) + mask = cgb_toplayerBG_mask(spx, 0, enableWindow ? wx : 160, scReader.scx(), + bgTileMap + ((scy & 0xF8) << 2), bgTileData, scy & 7, tileIndexSign); + if (enableWindow && spx > wx) + mask &= cgb_toplayerBG_mask(spx, wx, 160, 0u - wx, wdTileMap + ((winYPos & 0xF8) << 2), bgTileData, winYPos & 7, tileIndexSign); + } + + byte1 &= mask; + byte2 &= mask; + } + + const unsigned bytes = expand_lut[byte1] + expand_lut[byte2] * 2; + const unsigned long *const palette = spPalette + (attributes & 7) * 4; + + if (spx > 7 && spx < 161) { + T * const buf = buffer_line + spx - 8; + unsigned color; + + if ((color = bytes >> 14 )) + buf[0] = palette[color]; + if ((color = bytes >> 12 & 3)) + buf[1] = palette[color]; + if ((color = bytes >> 10 & 3)) + buf[2] = palette[color]; + if ((color = bytes >> 8 & 3)) + buf[3] = palette[color]; + if ((color = bytes >> 6 & 3)) + buf[4] = palette[color]; + if ((color = bytes >> 4 & 3)) + buf[5] = palette[color]; + if ((color = bytes >> 2 & 3)) + buf[6] = palette[color]; + if ((color = bytes & 3)) + buf[7] = palette[color]; + } else { + const unsigned end = spx >= 160 ? 160 : spx; + unsigned xpos = spx <= 8 ? 0 : (spx - 8); + unsigned shift = (7 - (xpos + 8 - spx)) * 2; + + while (xpos < end) { + if (const unsigned color = bytes >> shift & 3) + buffer_line[xpos] = palette[color]; + + shift -= 2; + ++xpos; + } + } + } + } +} + + +//shoud work for the window too, if -wx is passed as scx. +//tilemap and tiledata need to be offset to the right line +template +void LCD::bg_drawPixels(T * const buffer_line, unsigned xpos, const unsigned end, const unsigned scx, unsigned tilemappos, + const unsigned char *const tilemap, const unsigned char *const tiledata) +{ + const unsigned sign = tileIndexSign; + unsigned shift = (7 - ((scx + xpos) & 7)) * 2; + T *buf = buffer_line + xpos; + T *const bufend = buffer_line + end; + + while (buf < bufend) { + if ((tilemappos & 7) || bufend - buf < 8) { + const unsigned tile = tilemap[tilemappos >> 3 & 0x1F]; + const unsigned char *const dataptr = tiledata + tile * 16 - (tile & sign) * 32; + const unsigned data = expand_lut[dataptr[0]] + expand_lut[dataptr[1]] * 2; + + do { + *buf++ = bgPalette[data >> shift & 3]; + shift = (shift - 2) & 15; + } while ((++tilemappos & 7) && buf < bufend); + } + + while (bufend - buf > 7) { + const unsigned tile = tilemap[tilemappos >> 3 & 0x1F]; + const unsigned char *const dataptr = tiledata + tile * 16 - (tile & sign) * 32; + const unsigned data = expand_lut[dataptr[0]] + expand_lut[dataptr[1]] * 2; + buf[0] = bgPalette[data >> shift & 3]; + buf[1] = bgPalette[data >> ((shift - 2) & 15) & 3]; + buf[2] = bgPalette[data >> ((shift - 4) & 15) & 3]; + buf[3] = bgPalette[data >> ((shift - 6) & 15) & 3]; + buf[4] = bgPalette[data >> ((shift - 8) & 15) & 3]; + buf[5] = bgPalette[data >> ((shift - 10) & 15) & 3]; + buf[6] = bgPalette[data >> ((shift - 12) & 15) & 3]; + buf[7] = bgPalette[data >> ((shift - 14) & 15) & 3]; + buf += 8; + tilemappos += 8; + } + } +} + +static unsigned prioritizedBG_mask(const unsigned spx, const unsigned bgStart, const unsigned bgEnd, const unsigned scx, + const unsigned char *const tilemap, const unsigned char *const tiledata, const unsigned sign) { + const unsigned spStart = spx < bgStart + 8 ? bgStart + 8 - spx : 0; + + unsigned bgbyte; + + { + const unsigned pos = scx + spx - 8 + spStart; + unsigned tile = tilemap[pos >> 3 & 0x1F]; + const unsigned char *data = tiledata + tile * 16 - (tile & sign) * 32; + bgbyte = data[0] | data[1]; + const unsigned offset = pos & 7; + + if (offset) { + bgbyte <<= offset; + tile = tilemap[((pos >> 3) + 1) & 0x1F]; + data = tiledata + tile * 16 - (tile & sign) * 32; + bgbyte |= (data[0] | data[1]) >> (8 - offset); + } + } + + bgbyte >>= spStart; + const unsigned spEnd = spx > bgEnd ? bgEnd + 8 - spx : 8; + const unsigned mask = ~bgbyte | 0xFF >> spEnd; + + return mask; +} + +template +void LCD::drawSprites(T * const buffer_line, const unsigned ypos) { + const unsigned scy = scReader.scy() + ypos /*& 0xFF*/; + const unsigned wx = win.wxReader.wx() < 7 ? 0 : win.wxReader.wx() - 7; + const bool enableWindow = win.enabled(ypos); + const unsigned char *const spriteMapLine = spriteMapper.sprites(ypos); + + for (int i = spriteMapper.numSprites(ypos) - 1; i >= 0; --i) { + const unsigned spNrX2 = spriteMapLine[i]; + const unsigned spx = spriteMapper.posbuf()[spNrX2 + 1]; + + if (spx < 168 && spx) { + unsigned spLine = ypos + 16 - spriteMapper.posbuf()[spNrX2]; + unsigned spTile = spriteMapper.oamram()[spNrX2 * 2 + 2]; + const unsigned attributes = spriteMapper.oamram()[spNrX2 * 2 + 3]; + + if (spriteMapper.largeSprites(spNrX2 >> 1)) { + if (attributes & 0x40) //yflip + spLine = 15 - spLine; + + if (spLine < 8) + spTile &= 0xFE; + else { + spLine -= 8; + spTile |= 0x01; + } + } else { + if (attributes & 0x40) //yflip + spLine = 7 - spLine; + } + + const unsigned char *const data = vram + spTile * 16 + spLine * 2; + + unsigned byte1 = data[0]; + unsigned byte2 = data[1]; + + if (attributes & 0x20) { + byte1 = xflipt[byte1]; + byte2 = xflipt[byte2]; + } + + //(Sprites with priority-bit are still allowed to cover other sprites according to GBdev-faq.) + if (attributes & 0x80) { + unsigned mask = 0xFF; + + if (bgEnable && !(enableWindow && (wx == 0 || spx >= wx + 8u))) + mask = prioritizedBG_mask(spx, 0, enableWindow ? wx : 160, scReader.scx(), + bgTileMap + ((scy & 0xF8) << 2), bgTileData + ((scy & 7) << 1), tileIndexSign); + if (enableWindow && spx > wx) + mask &= prioritizedBG_mask(spx, wx, 160, 0u - wx, wdTileMap + ((winYPos & 0xF8) << 2), bgTileData + ((winYPos & 7) << 1), tileIndexSign); + + byte1 &= mask; + byte2 &= mask; + } + + const unsigned bytes = expand_lut[byte1] + expand_lut[byte2] * 2; + const unsigned long *const palette = spPalette + ((attributes >> 2) & 4); + + if (spx > 7 && spx < 161) { + T * const buf = buffer_line + spx - 8; + unsigned color; + + if ((color = bytes >> 14 )) + buf[0] = palette[color]; + if ((color = bytes >> 12 & 3)) + buf[1] = palette[color]; + if ((color = bytes >> 10 & 3)) + buf[2] = palette[color]; + if ((color = bytes >> 8 & 3)) + buf[3] = palette[color]; + if ((color = bytes >> 6 & 3)) + buf[4] = palette[color]; + if ((color = bytes >> 4 & 3)) + buf[5] = palette[color]; + if ((color = bytes >> 2 & 3)) + buf[6] = palette[color]; + if ((color = bytes & 3)) + buf[7] = palette[color]; + } else { + const unsigned end = spx >= 160 ? 160 : spx; + unsigned xpos = spx <= 8 ? 0 : (spx - 8); + unsigned shift = (7 - (xpos + 8 - spx)) * 2; + + while (xpos < end) { + if (const unsigned color = bytes >> shift & 3) + buffer_line[xpos] = palette[color]; + + shift -= 2; + ++xpos; + } + } + } + } +} diff --git a/src/lib/libgambatte/src/video.h b/src/lib/libgambatte/src/video.h new file mode 100644 index 00000000..d3228320 --- /dev/null +++ b/src/lib/libgambatte/src/video.h @@ -0,0 +1,292 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_H +#define VIDEO_H + +namespace Gambatte { +class VideoBlitter; +struct FilterInfo; +} + +class Filter; +class SaveState; + +#include +#include +#include "event_queue.h" +#include "videoblitter.h" +#include "array.h" +#include "int.h" +#include "colorconversion.h" +#include "osd_element.h" + +#include "video/video_event_comparer.h" +#include "video/ly_counter.h" +#include "video/window.h" +#include "video/scx_reader.h" +#include "video/sprite_mapper.h" +#include "video/sc_reader.h" +#include "video/break_event.h" +#include "video/mode3_event.h" + +#include "video/lyc_irq.h" +#include "video/mode0_irq.h" +#include "video/mode1_irq.h" +#include "video/mode2_irq.h" +#include "video/irq_event.h" +#include "video/m3_extra_cycles.h" + +class LCD { + //static const uint8_t xflipt[0x100]; + unsigned long dmgColorsRgb32[3 * 4]; + unsigned long dmgColorsRgb16[3 * 4]; + unsigned long dmgColorsUyvy[3 * 4]; + + unsigned long bgPalette[8 * 4]; + unsigned long spPalette[8 * 4]; + + unsigned char bgpData[8 * 8]; + unsigned char objpData[8 * 8]; + + const unsigned char *const vram; + const unsigned char *bgTileData; + const unsigned char *bgTileMap; + const unsigned char *wdTileMap; + + Gambatte::VideoBlitter *vBlitter; + Filter *filter; + + void *dbuffer; + void (LCD::*draw)(unsigned xpos, unsigned ypos, unsigned endX); + unsigned long (*gbcToFormat)(unsigned bgr15); + const unsigned long *dmgColors; + + unsigned long lastUpdate; + unsigned long videoCycles; + + unsigned dpitch; + unsigned winYPos; + + event_queue m3EventQueue; + event_queue irqEventQueue; + event_queue vEventQueue; + + LyCounter lyCounter; + Window win; + ScxReader scxReader; + SpriteMapper spriteMapper; + M3ExtraCycles m3ExtraCycles; + ScReader scReader; + BreakEvent breakEvent; + Mode3Event mode3Event; + + LycIrq lycIrq; + Mode0Irq mode0Irq; + Mode1Irq mode1Irq; + Mode2Irq mode2Irq; + IrqEvent irqEvent; + + Gambatte::PixelBuffer pb; + Array tmpbuf; + Rgb32ToUyvy rgb32ToUyvy; + std::auto_ptr osdElement; + + std::vector filters; + + unsigned char drawStartCycle; + unsigned char scReadOffset; + unsigned char ifReg; + unsigned char tileIndexSign; + unsigned char statReg; + + bool doubleSpeed; + bool enabled; + bool cgb; + bool bgEnable; + bool spriteEnable; + + static void setDmgPalette(unsigned long *palette, const unsigned long *dmgColors, unsigned data); + void setDmgPaletteColor(unsigned index, unsigned long rgb32); + static unsigned long gbcToRgb32(unsigned bgr15); + static unsigned long gbcToRgb16(unsigned bgr15); + static unsigned long gbcToUyvy(unsigned bgr15); + + void refreshPalettes(); + void setDBuffer(); + void resetVideoState(unsigned long cycleCounter); + + void setDoubleSpeed(bool enabled); + + void event(); + + bool cgbpAccessible(unsigned long cycleCounter); + bool isMode0IrqPeriod(unsigned long cycleCounter); + bool isMode2IrqPeriod(unsigned long cycleCounter); + bool isLycIrqPeriod(unsigned lycReg, unsigned endCycles, unsigned long cycleCounter); + bool isMode1IrqPeriod(unsigned long cycleCounter); + + template void bg_drawPixels(T *buffer_line, unsigned xpos, unsigned end, unsigned scx, unsigned tilemappos, + const unsigned char *tilemap, const unsigned char *tiledata); + template void drawSprites(T *buffer_line, unsigned ypos); + + template void cgb_bg_drawPixels(T *buffer_line, unsigned xpos, unsigned end, unsigned scx, unsigned tilemappos, + const unsigned char *tilemap, const unsigned char *tiledata, unsigned tileline); + template void cgb_drawSprites(T *buffer_line, unsigned ypos); + + void null_draw(unsigned xpos, unsigned ypos, unsigned endX); + template void dmg_draw(unsigned xpos, unsigned ypos, unsigned endX); + template void cgb_draw(unsigned xpos, unsigned ypos, unsigned endX); + + void do_update(unsigned cycles); + +public: + LCD(const unsigned char *oamram, const unsigned char *vram_in); + ~LCD(); + void update(unsigned long cycleCounter); + void reset(const unsigned char *oamram, bool cgb); + void setStatePtrs(SaveState &state); + void saveState(SaveState &state) const; + void loadState(const SaveState &state, const unsigned char *oamram); + void setVideoBlitter(Gambatte::VideoBlitter *vb); + void videoBufferChange(); + void setVideoFilter(unsigned n); + std::vector filterInfo() const; + unsigned videoWidth() const; + unsigned videoHeight() const; + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32); + + void setOsdElement(std::auto_ptr osdElement) { + this->osdElement = osdElement; + } + + void wdTileMapSelectChange(bool newValue, unsigned long cycleCounter); + void bgTileMapSelectChange(bool newValue, unsigned long cycleCounter); + void bgTileDataSelectChange(bool newValue, unsigned long cycleCounter); + void bgEnableChange(bool newValue, unsigned long cycleCounter); + void spriteEnableChange(bool newValue, unsigned long cycleCounter); + + void dmgBgPaletteChange(const unsigned data, const unsigned long cycleCounter) { + update(cycleCounter); + bgpData[0] = data; + setDmgPalette(bgPalette, dmgColors, data); + } + + void dmgSpPalette1Change(const unsigned data, const unsigned long cycleCounter) { + update(cycleCounter); + objpData[0] = data; + setDmgPalette(spPalette, dmgColors + 4, data); + } + + void dmgSpPalette2Change(const unsigned data, const unsigned long cycleCounter) { + update(cycleCounter); + objpData[1] = data; + setDmgPalette(spPalette + 4, dmgColors + 8, data); + } + + void cgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) { + if (bgpData[index] != data && cgbpAccessible(cycleCounter)) { + update(cycleCounter); + bgpData[index] = data; + index >>= 1; + bgPalette[index] = (*gbcToFormat)(bgpData[index << 1] | bgpData[(index << 1) + 1] << 8); + } + } + + void cgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) { + if (objpData[index] != data && cgbpAccessible(cycleCounter)) { + update(cycleCounter); + objpData[index] = data; + index >>= 1; + spPalette[index] = (*gbcToFormat)(objpData[index << 1] | objpData[(index << 1) + 1] << 8); + } + } + + unsigned cgbBgColorRead(const unsigned index, const unsigned long cycleCounter) { + return cgb & cgbpAccessible(cycleCounter) ? bgpData[index] : 0xFF; + } + + unsigned cgbSpColorRead(const unsigned index, const unsigned long cycleCounter) { + return cgb & cgbpAccessible(cycleCounter) ? objpData[index] : 0xFF; + } + + void updateScreen(unsigned long cc); + void enableChange(unsigned long cycleCounter); + void preResetCounter(unsigned long cycleCounter); + void postResetCounter(unsigned long oldCC, unsigned long cycleCounter); + void preSpeedChange(unsigned long cycleCounter); + void postSpeedChange(unsigned long cycleCounter); +// unsigned get_mode(unsigned cycleCounter) /*const*/; + bool vramAccessible(unsigned long cycleCounter); + bool oamAccessible(unsigned long cycleCounter); + void weChange(bool newValue, unsigned long cycleCounter); + void wxChange(unsigned newValue, unsigned long cycleCounter); + void wyChange(unsigned newValue, unsigned long cycleCounter); + void oamChange(unsigned long cycleCounter); + void oamChange(const unsigned char *oamram, unsigned long cycleCounter); + void scxChange(unsigned newScx, unsigned long cycleCounter); + void scyChange(unsigned newValue, unsigned long cycleCounter); + void spriteSizeChange(bool newLarge, unsigned long cycleCounter); + + void vramChange(const unsigned long cycleCounter) { + update(cycleCounter); + } + + unsigned get_stat(unsigned lycReg, unsigned long cycleCounter); + + unsigned getLyReg(const unsigned long cycleCounter) { + unsigned lyReg = 0; + + if (enabled) { + if (cycleCounter >= lyCounter.time()) + update(cycleCounter); + + lyReg = lyCounter.ly(); + + if (lyCounter.time() - cycleCounter <= 4) { + if (lyReg == 153) + lyReg = 0; + else + ++lyReg; + } else if (lyReg == 153) + lyReg = 0; + } + + return lyReg; + } + + unsigned long nextMode1IrqTime() const { + return mode1Irq.time(); + } + + void lyWrite(unsigned long cycleCounter); + void lcdstatChange(unsigned data, unsigned long cycleCounter); + void lycRegChange(unsigned data, unsigned long cycleCounter); + unsigned long nextIrqEvent() const; + unsigned getIfReg(unsigned long cycleCounter); + void setIfReg(unsigned ifReg_in, unsigned long cycleCounter); + + unsigned long nextHdmaTime(unsigned long cycleCounter); + bool isHdmaPeriod(unsigned long cycleCounter); + + unsigned long nextHdmaTimeInvalid() const { + return mode3Event.time(); + } +}; + +#endif diff --git a/src/lib/libgambatte/src/video/basic_add_event.cpp b/src/lib/libgambatte/src/video/basic_add_event.cpp new file mode 100644 index 00000000..4bc57a09 --- /dev/null +++ b/src/lib/libgambatte/src/video/basic_add_event.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "basic_add_event.h" +#include "../event_queue.h" + +void addEvent(event_queue &q, VideoEvent *const e, const unsigned long newTime) { + const unsigned long oldTime = e->time(); + + if (oldTime != newTime) { + e->setTime(newTime); + + if (newTime < oldTime) { + if (oldTime == VideoEvent::DISABLED_TIME) + q.push(e); + else + q.dec(e, e); + } else { + if (newTime == VideoEvent::DISABLED_TIME) + q.remove(e); + else + q.inc(e, e); + } + } +} + +void addUnconditionalEvent(event_queue &q, VideoEvent *const e, const unsigned long newTime) { + const unsigned long oldTime = e->time(); + + e->setTime(newTime); + + if (newTime < oldTime) { + if (oldTime == VideoEvent::DISABLED_TIME) + q.push(e); + else + q.dec(e, e); + } else if (oldTime != newTime) { + q.inc(e, e); + } +} + +void addFixedtimeEvent(event_queue &q, VideoEvent *const e, const unsigned long newTime) { + const unsigned long oldTime = e->time(); + + if (oldTime != newTime) { + e->setTime(newTime); + + if (oldTime == VideoEvent::DISABLED_TIME) + q.push(e); + else + q.remove(e); + } +} + +void addUnconditionalFixedtimeEvent(event_queue &q, VideoEvent *const e, const unsigned long newTime) { + if (e->time() == VideoEvent::DISABLED_TIME) { + e->setTime(newTime); + q.push(e); + } +} diff --git a/src/lib/libgambatte/src/video/basic_add_event.h b/src/lib/libgambatte/src/video/basic_add_event.h new file mode 100644 index 00000000..780d7191 --- /dev/null +++ b/src/lib/libgambatte/src/video/basic_add_event.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef BASIC_ADD_EVENT_H +#define BASIC_ADD_EVENT_H + +template class event_queue; + +#include "video_event.h" +#include "video_event_comparer.h" + +/*template +static inline void addEvent(T &event, const LyCounter &lyCounter, const unsigned long cycleCounter, event_queue &queue) { + if (event.time() == VideoEvent::DISABLED_TIME) { + event.schedule(lyCounter, cycleCounter); + queue.push(&event); + } +} + +template +static inline void addEvent(T &event, const unsigned data, const LyCounter &lyCounter, const unsigned long cycleCounter, event_queue &queue) { + if (event.time() == VideoEvent::DISABLED_TIME) { + event.schedule(data, lyCounter, cycleCounter); + queue.push(&event); + } +} + +template +static inline void addEvent(T &event, const unsigned data1, const unsigned data2, const LyCounter &lyCounter, const unsigned long cycleCounter, event_queue &queue) { + if (event.time() == VideoEvent::DISABLED_TIME) { + event.schedule(data1, data2, lyCounter, cycleCounter); + queue.push(&event); + } +}*/ + +void addEvent(event_queue &q, VideoEvent *e, unsigned long newTime); +void addUnconditionalEvent(event_queue &q, VideoEvent *e, unsigned long newTime); +void addFixedtimeEvent(event_queue &q, VideoEvent *e, unsigned long newTime); +void addUnconditionalFixedtimeEvent(event_queue &q, VideoEvent *e, unsigned long newTime); + +#endif diff --git a/src/lib/libgambatte/src/video/break_event.cpp b/src/lib/libgambatte/src/video/break_event.cpp new file mode 100644 index 00000000..e6e7ffbf --- /dev/null +++ b/src/lib/libgambatte/src/video/break_event.cpp @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "break_event.h" + +BreakEvent::BreakEvent(unsigned char &drawStartCycle_in, unsigned char &scReadOffset_in) : + VideoEvent(3), + drawStartCycle(drawStartCycle_in), + scReadOffset(scReadOffset_in) +{ + setDoubleSpeed(false); + setScxSource(0); +} + +void BreakEvent::doEvent() { + scReadOffset = baseCycle; + drawStartCycle = baseCycle + (scxSrc & 7); + + setTime(DISABLED_TIME); +} diff --git a/src/lib/libgambatte/src/video/break_event.h b/src/lib/libgambatte/src/video/break_event.h new file mode 100644 index 00000000..9e7dcb82 --- /dev/null +++ b/src/lib/libgambatte/src/video/break_event.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef BREAK_EVENT_H +#define BREAK_EVENT_H + +#include "video_event.h" +#include "ly_counter.h" +#include "basic_add_event.h" + +class BreakEvent : public VideoEvent { + unsigned char &drawStartCycle; + unsigned char &scReadOffset; + + unsigned char scxSrc; + unsigned char baseCycle; + +public: + BreakEvent(unsigned char &drawStartCycle_in, unsigned char &scReadOffset_in); + + void doEvent(); + + static unsigned long schedule(const LyCounter &lyCounter) { + return lyCounter.time(); + } + + void setDoubleSpeed(const bool dS) { + baseCycle = 90 + dS * 4; + } + + void setScxSource(const unsigned scxSrc_in) { + scxSrc = scxSrc_in; + } +}; + +static inline void addEvent(event_queue &q, BreakEvent *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, BreakEvent *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/src/lib/libgambatte/src/video/filters/catrom2x.cpp b/src/lib/libgambatte/src/video/filters/catrom2x.cpp new file mode 100644 index 00000000..53a4c931 --- /dev/null +++ b/src/lib/libgambatte/src/video/filters/catrom2x.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "catrom2x.h" +#include "filterinfo.h" +#include + +struct Colorsum { + Gambatte::uint_least32_t r, g, b; +}; + +static void merge_columns(Gambatte::uint_least32_t *dest, const Colorsum *sums) { + unsigned w = 160; + + while (w--) { + { + Gambatte::uint_least32_t rsum = sums[1].r; + Gambatte::uint_least32_t gsum = sums[1].g; + Gambatte::uint_least32_t bsum = sums[1].b; + + if (rsum & 0x80000000) rsum = 0; + if (gsum & 0x80000000) gsum = 0; + if (bsum & 0x80000000) bsum = 0; + + rsum <<= 12; + rsum += 0x008000; + gsum >>= 4; + gsum += 0x0080; + bsum += 0x0008; + bsum >>= 4; + + if (rsum > 0xFF0000) rsum = 0xFF0000; + if (gsum > 0x00FF00) gsum = 0x00FF00; + if (bsum > 0x0000FF) bsum = 0x0000FF; + + *dest++ = (rsum & 0xFF0000) | (gsum & 0x00FF00) | bsum; + } + + { + Gambatte::uint_least32_t rsum = sums[1].r * 9; + Gambatte::uint_least32_t gsum = sums[1].g * 9; + Gambatte::uint_least32_t bsum = sums[1].b * 9; + + rsum -= sums[0].r; + gsum -= sums[0].g; + bsum -= sums[0].b; + + rsum += sums[2].r * 9; + gsum += sums[2].g * 9; + bsum += sums[2].b * 9; + + rsum -= sums[3].r; + gsum -= sums[3].g; + bsum -= sums[3].b; + + if (rsum & 0x80000000) rsum = 0; + if (gsum & 0x80000000) gsum = 0; + if (bsum & 0x80000000) bsum = 0; + + rsum <<= 8; + rsum += 0x008000; + gsum >>= 8; + gsum += 0x000080; + bsum += 0x000080; + bsum >>= 8; + + if (rsum > 0xFF0000) rsum = 0xFF0000; + if (gsum > 0x00FF00) gsum = 0x00FF00; + if (bsum > 0x0000FF) bsum = 0x0000FF; + + *dest++ = (rsum & 0xFF0000) | (gsum & 0x00FF00) | bsum; + } + + ++sums; + } +} + +static void filter(Gambatte::uint_least32_t *dline, const unsigned pitch, const Gambatte::uint_least32_t *sline) { + Colorsum sums[163]; + + for (unsigned h = 144; h--;) { + { + const Gambatte::uint_least32_t *s = sline; + Colorsum *sum = sums; + unsigned n = 163; + + while (n--) { + unsigned long pixel = *s; + sum->r = pixel >> 12 & 0x000FF0 ; + pixel <<= 4; + sum->g = pixel & 0x0FF000; + sum->b = pixel & 0x000FF0; + + ++s; + ++sum; + } + } + + merge_columns(dline, sums); + dline += pitch; + + { + const Gambatte::uint_least32_t *s = sline; + Colorsum *sum = sums; + unsigned n = 163; + + while (n--) { + unsigned long pixel = *s; + unsigned long rsum = (pixel >> 16) * 9; + unsigned long gsum = (pixel & 0x00FF00) * 9; + unsigned long bsum = (pixel & 0x0000FF) * 9; + + pixel = s[-1*163]; + rsum -= pixel >> 16; + gsum -= pixel & 0x00FF00; + bsum -= pixel & 0x0000FF; + + pixel = s[1*163]; + rsum += (pixel >> 16) * 9; + gsum += (pixel & 0x00FF00) * 9; + bsum += (pixel & 0x0000FF) * 9; + + pixel = s[2*163]; + rsum -= pixel >> 16; + gsum -= pixel & 0x00FF00; + bsum -= pixel & 0x0000FF; + + sum->r = rsum; + sum->g = gsum; + sum->b = bsum; + + ++s; + ++sum; + } + } + + merge_columns(dline, sums); + dline += pitch; + sline += 163; + } +} + +Catrom2x::Catrom2x() { + buffer = NULL; +} + +Catrom2x::~Catrom2x() { + delete []buffer; +} + +void Catrom2x::init() { + delete []buffer; + + buffer = new Gambatte::uint_least32_t[147 * 163]; + std::memset(buffer, 0, 147ul * 163 * sizeof(Gambatte::uint_least32_t)); +} + +void Catrom2x::outit() { + delete []buffer; + buffer = NULL; +} + +const Gambatte::FilterInfo& Catrom2x::info() { + static Gambatte::FilterInfo fInfo = { "Bicubic Catmull-Rom Spline 2x", 160 * 2, 144 * 2 }; + + return fInfo; +} + +Gambatte::uint_least32_t* Catrom2x::inBuffer() { + return buffer + 164; +} + +unsigned Catrom2x::inPitch() { + return 163; +} + +void Catrom2x::filter(Gambatte::uint_least32_t *const dbuffer, const unsigned pitch) { + ::filter(dbuffer, pitch, buffer + 163); +} diff --git a/src/lib/libgambatte/src/video/filters/catrom2x.h b/src/lib/libgambatte/src/video/filters/catrom2x.h new file mode 100644 index 00000000..df657f04 --- /dev/null +++ b/src/lib/libgambatte/src/video/filters/catrom2x.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CATROM2X_H +#define CATROM2X_H + +#include "filter.h" + +struct FilterInfo; + +class Catrom2x : public Filter { + Gambatte::uint_least32_t *buffer; + +public: + Catrom2x(); + ~Catrom2x(); + void init(); + void outit(); + const Gambatte::FilterInfo& info(); + void filter(Gambatte::uint_least32_t *dbuffer, unsigned pitch); + Gambatte::uint_least32_t* inBuffer(); + unsigned inPitch(); +}; + +#endif diff --git a/src/lib/libgambatte/src/video/filters/catrom3x.cpp b/src/lib/libgambatte/src/video/filters/catrom3x.cpp new file mode 100644 index 00000000..09a03f6a --- /dev/null +++ b/src/lib/libgambatte/src/video/filters/catrom3x.cpp @@ -0,0 +1,360 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "catrom3x.h" +#include "filterinfo.h" +#include + +struct Colorsum { + Gambatte::uint_least32_t r, g, b; +}; + +static void merge_columns(Gambatte::uint_least32_t *dest, const Colorsum *sums) { + unsigned w = 160; + + while (w--) { + { + Gambatte::uint_least32_t rsum = sums[1].r; + Gambatte::uint_least32_t gsum = sums[1].g; + Gambatte::uint_least32_t bsum = sums[1].b; + + if (rsum & 0x80000000) + rsum = 0; + else if (rsum > 6869) + rsum = 0xFF0000; + else { + rsum *= 607; + rsum <<= 2; + rsum += 0x008000; + rsum &= 0xFF0000; + } + + if (gsum & 0x80000000) + gsum = 0; + else if (gsum > 1758567) + gsum = 0xFF00; + else { + gsum *= 607; + gsum >>= 14; + gsum += 0x000080; + gsum &= 0x00FF00; + } + + if (bsum & 0x80000000) + bsum = 0; + else if (bsum > 6869) + bsum = 0xFF; + else { + bsum *= 607; + bsum += 8192; + bsum >>= 14; + } + + /*rsum/=27; + rsum<<=8; + gsum/=27; + gsum<<=5; + bsum<<=4; + bsum+=27; + bsum/=54; + rsum+=0x008000; + gsum+=0x000080; + + if(rsum>0xFF0000) rsum=0xFF0000; + if(gsum>0x00FF00) gsum=0x00FF00; + if(bsum>0x0000FF) bsum=0x0000FF;*/ + + *dest++ = rsum/*&0xFF0000*/ | gsum/*&0x00FF00*/ | bsum; + } + { + Gambatte::uint_least32_t rsum = sums[1].r * 21; + Gambatte::uint_least32_t gsum = sums[1].g * 21; + Gambatte::uint_least32_t bsum = sums[1].b * 21; + + rsum -= sums[0].r << 1; + gsum -= sums[0].g << 1; + bsum -= sums[0].b << 1; + + rsum += sums[2].r * 9; + gsum += sums[2].g * 9; + bsum += sums[2].b * 9; + + rsum -= sums[3].r; + gsum -= sums[3].g; + bsum -= sums[3].b; + + if (rsum & 0x80000000) + rsum = 0; + else if (rsum > 185578) + rsum = 0xFF0000; + else { + rsum *= 719; + rsum >>= 3; + rsum += 0x008000; + rsum &= 0xFF0000; + } + + if (gsum & 0x80000000) + gsum = 0; + else if (gsum > 47508223) + gsum = 0x00FF00; + else { + gsum >>= 8; + gsum *= 719; + gsum >>= 11; + gsum += 0x000080; + gsum &= 0x00FF00; + } + + if (bsum & 0x80000000) + bsum = 0; + else if (bsum > 185578) + bsum = 0x0000FF; + else { + bsum *= 719; + bsum += 0x040000; + bsum >>= 19; + } + + /*rsum/=729; + rsum<<=8; + gsum/=729; + gsum<<=5; + bsum<<=4; + bsum+=729; + bsum/=1458; + rsum+=0x008000; + gsum+=0x000080; + + if(rsum>0xFF0000) rsum=0xFF0000; + if(gsum>0x00FF00) gsum=0x00FF00; + if(bsum>0x0000FF) bsum=0x0000FF;*/ + + *dest++ = rsum/*&0xFF0000*/ | gsum/*&0x00FF00*/ | bsum; + } + { + Gambatte::uint_least32_t rsum = sums[1].r * 9; + Gambatte::uint_least32_t gsum = sums[1].g * 9; + Gambatte::uint_least32_t bsum = sums[1].b * 9; + + rsum -= sums[0].r; + gsum -= sums[0].g; + bsum -= sums[0].b; + + rsum += sums[2].r * 21; + gsum += sums[2].g * 21; + bsum += sums[2].b * 21; + + rsum -= sums[3].r << 1; + gsum -= sums[3].g << 1; + bsum -= sums[3].b << 1; + + if (rsum & 0x80000000) + rsum = 0; + else if (rsum > 185578) + rsum = 0xFF0000; + else { + rsum *= 719; + rsum >>= 3; + rsum += 0x008000; + rsum &= 0xFF0000; + } + + if (gsum & 0x80000000) + gsum = 0; + else if (gsum > 47508223) + gsum = 0xFF00; + else { + gsum >>= 8; + gsum *= 719; + gsum >>= 11; + gsum += 0x000080; + gsum &= 0x00FF00; + } + + if (bsum & 0x80000000) + bsum = 0; + else if (bsum > 185578) + bsum = 0x0000FF; + else { + bsum *= 719; + bsum += 0x040000; + bsum >>= 19; + } + + /*rsum/=729; + rsum<<=8; + gsum/=729; + gsum<<=5; + bsum<<=4; + bsum+=729; + bsum/=1458; + rsum+=0x008000; + gsum+=0x000080; + + if(rsum>0xFF0000) rsum=0xFF0000; + if(gsum>0x00FF00) gsum=0x00FF00; + if(bsum>0x0000FF) bsum=0x0000FF;*/ + + *dest++ = rsum/*&0xFF0000*/ | gsum/*&0x00FF00*/ | bsum; + } + ++sums; + } +} + +static void filter(Gambatte::uint_least32_t *dline, const unsigned pitch, const Gambatte::uint_least32_t *sline) { + Colorsum sums[163]; + + for (unsigned h = 144; h--;) { + { + const Gambatte::uint_least32_t *s = sline; + Colorsum *sum = sums; + unsigned n = 163; + + while (n--) { + const unsigned long pixel = *s; + sum->r = (pixel >> 16) * 27; + sum->g = (pixel & 0x00FF00) * 27; + sum->b = (pixel & 0x0000FF) * 27; + + ++s; + ++sum; + } + } + + merge_columns(dline, sums); + dline += pitch; + + { + const Gambatte::uint_least32_t *s = sline; + Colorsum *sum = sums; + unsigned n = 163; + + while (n--) { + unsigned long pixel = *s; + unsigned long rsum = (pixel >> 16) * 21; + unsigned long gsum = (pixel & 0x00FF00) * 21; + unsigned long bsum = (pixel & 0x0000FF) * 21; + + pixel = s[-1 * 163]; + rsum -= (pixel >> 16) << 1; + pixel <<= 1; + gsum -= pixel & 0x01FE00; + bsum -= pixel & 0x0001FE; + + pixel = s[1 * 163]; + rsum += (pixel >> 16) * 9; + gsum += (pixel & 0x00FF00) * 9; + bsum += (pixel & 0x0000FF) * 9; + + pixel = s[2 * 163]; + rsum -= pixel >> 16; + gsum -= pixel & 0x00FF00; + bsum -= pixel & 0x0000FF; + + sum->r = rsum; + sum->g = gsum; + sum->b = bsum; + + ++s; + ++sum; + } + } + + merge_columns(dline, sums); + dline += pitch; + + { + const Gambatte::uint_least32_t *s = sline; + Colorsum *sum = sums; + unsigned n = 163; + + while (n--) { + unsigned long pixel = *s; + unsigned long rsum = (pixel >> 16) * 9; + unsigned long gsum = (pixel & 0x00FF00) * 9; + unsigned long bsum = (pixel & 0x0000FF) * 9; + + pixel = s[-1 * 163]; + rsum -= pixel >> 16; + gsum -= pixel & 0x00FF00; + bsum -= pixel & 0x0000FF; + + pixel = s[1 * 163]; + rsum += (pixel >> 16) * 21; + gsum += (pixel & 0x00FF00) * 21; + bsum += (pixel & 0x0000FF) * 21; + + pixel = s[2 * 163]; + rsum -= (pixel >> 16) << 1; + pixel <<= 1; + gsum -= pixel & 0x01FE00; + bsum -= pixel & 0x0001FE; + + sum->r = rsum; + sum->g = gsum; + sum->b = bsum; + + ++s; + ++sum; + } + } + + merge_columns(dline, sums); + dline += pitch; + sline += 163; + } +} + +Catrom3x::Catrom3x() { + buffer = NULL; +} + +Catrom3x::~Catrom3x() { + delete []buffer; +} + +void Catrom3x::init() { + delete []buffer; + + buffer = new Gambatte::uint_least32_t[147 * 163]; + std::memset(buffer, 0, 147ul * 163 * sizeof(Gambatte::uint_least32_t)); +} + +void Catrom3x::outit() { + delete []buffer; + buffer = NULL; +} + +const Gambatte::FilterInfo& Catrom3x::info() { + static Gambatte::FilterInfo fInfo = { "Bicubic Catmull-Rom Spline 3x", 160 * 3, 144 * 3 }; + + return fInfo; +} + +Gambatte::uint_least32_t* Catrom3x::inBuffer() { + return buffer + 164; +} + +unsigned Catrom3x::inPitch() { + return 163; +} + +void Catrom3x::filter(Gambatte::uint_least32_t *const dbuffer, const unsigned pitch) { + ::filter(dbuffer, pitch, buffer + 163); +} diff --git a/src/lib/libgambatte/src/video/filters/catrom3x.h b/src/lib/libgambatte/src/video/filters/catrom3x.h new file mode 100644 index 00000000..64f47827 --- /dev/null +++ b/src/lib/libgambatte/src/video/filters/catrom3x.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CATROM3X_H +#define CATROM3X_H + +#include "filter.h" + +struct FilterInfo; + +class Catrom3x : public Filter { + Gambatte::uint_least32_t *buffer; + +public: + Catrom3x(); + ~Catrom3x(); + void init(); + void outit(); + const Gambatte::FilterInfo& info(); + void filter(Gambatte::uint_least32_t *dbuffer, unsigned pitch); + Gambatte::uint_least32_t* inBuffer(); + unsigned inPitch(); +}; + +#endif diff --git a/src/lib/libgambatte/src/video/filters/filter.h b/src/lib/libgambatte/src/video/filters/filter.h new file mode 100644 index 00000000..72e3bf7d --- /dev/null +++ b/src/lib/libgambatte/src/video/filters/filter.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef FILTER_H +#define FILTER_H + +#include "int.h" + +namespace Gambatte { +struct FilterInfo; +} + +class Filter { +public: + virtual ~Filter() {} + virtual void init() {}; + virtual void outit() {}; + virtual const Gambatte::FilterInfo& info() = 0; + virtual void filter(Gambatte::uint_least32_t *dbuffer, unsigned pitch) = 0; + virtual Gambatte::uint_least32_t* inBuffer() = 0; + virtual unsigned inPitch() = 0; +}; + +#endif diff --git a/src/lib/libgambatte/src/video/filters/kreed2xsai.cpp b/src/lib/libgambatte/src/video/filters/kreed2xsai.cpp new file mode 100644 index 00000000..70c261b3 --- /dev/null +++ b/src/lib/libgambatte/src/video/filters/kreed2xsai.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * Copyright (C) 1999 Derek Liauw Kie Fa (Kreed) * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "kreed2xsai.h" +#include "filterinfo.h" +#include + +static inline int getResult1(const unsigned long a, const unsigned long b, const unsigned long c, const unsigned long d) { + int x = 0; + int y = 0; + int r = 0; + + if (a == c) ++x; + else if (b == c) ++y; + + if (a == d) ++x; + else if (b == d) ++y; + + if (x <= 1) ++r; + + if (y <= 1) --r; + + return r; +} + +static inline int getResult2(const unsigned long a, const unsigned long b, const unsigned long c, const unsigned long d) { + int x = 0; + int y = 0; + int r = 0; + + if (a == c) ++x; + else if (b == c) ++y; + + if (a == d) ++x; + else if (b == d) ++y; + + if (x <= 1) --r; + + if (y <= 1) ++r; + + return r; +} + +static inline unsigned long interpolate(const unsigned long a, const unsigned long b) { + return (a + b - ((a ^ b) & 0x010101)) >> 1; +} + +static inline unsigned long qInterpolate(const unsigned long a, const unsigned long b, const unsigned long c, const unsigned long d) { + const unsigned long lowBits = ((a & 0x030303) + (b & 0x030303) + (c & 0x030303) + (d & 0x030303)) & 0x030303; + + return (a + b + c + d - lowBits) >> 2; +} + +static void filter(Gambatte::uint_least32_t *dstPtr, const unsigned dstPitch, + const Gambatte::uint_least32_t *srcPtr, const unsigned srcPitch, const unsigned width, unsigned height) +{ + while (height--) { + const Gambatte::uint_least32_t *bP = srcPtr; + Gambatte::uint_least32_t *dP = dstPtr; + + for (unsigned finish = width; finish--;) { + register unsigned long colorA, colorB; + unsigned long colorC, colorD, + colorE, colorF, colorG, colorH, + colorI, colorJ, colorK, colorL, + + colorM, colorN, colorO, colorP; + unsigned long product, product1, product2; + + //--------------------------------------- + // Map of the pixels: I|E F|J + // G|A B|K + // H|C D|L + // M|N O|P + colorI = *(bP - srcPitch - 1); + colorE = *(bP - srcPitch); + colorF = *(bP - srcPitch + 1); + colorJ = *(bP - srcPitch + 2); + + colorG = *(bP - 1); + colorA = *(bP); + colorB = *(bP + 1); + colorK = *(bP + 2); + + colorH = *(bP + srcPitch - 1); + colorC = *(bP + srcPitch); + colorD = *(bP + srcPitch + 1); + colorL = *(bP + srcPitch + 2); + + colorM = *(bP + srcPitch * 2 - 1); + colorN = *(bP + srcPitch * 2); + colorO = *(bP + srcPitch * 2 + 1); + colorP = *(bP + srcPitch * 2 + 2); + + if (colorA == colorD && colorB != colorC) { + if ((colorA == colorE && colorB == colorL) || + (colorA == colorC && colorA == colorF + && colorB != colorE && colorB == colorJ)) { + product = colorA; + } else { + product = interpolate(colorA, colorB); + } + + if ((colorA == colorG && colorC == colorO) || + (colorA == colorB && colorA == colorH + && colorG != colorC && colorC == colorM)) { + product1 = colorA; + } else { + product1 = interpolate(colorA, colorC); + } + product2 = colorA; + } else if (colorB == colorC && colorA != colorD) { + if ((colorB == colorF && colorA == colorH) || + (colorB == colorE && colorB == colorD + && colorA != colorF && colorA == colorI)) { + product = colorB; + } else { + product = interpolate(colorA, colorB); + } + + if ((colorC == colorH && colorA == colorF) || + (colorC == colorG && colorC == colorD + && colorA != colorH && colorA == colorI)) { + product1 = colorC; + } else { + product1 = interpolate(colorA, colorC); + } + product2 = colorB; + } else if (colorA == colorD && colorB == colorC) { + if (colorA == colorB) { + product = colorA; + product1 = colorA; + product2 = colorA; + } else { + register int r = 0; + + product1 = interpolate(colorA, colorC); + product = interpolate(colorA, colorB); + + r += getResult1(colorA, colorB, colorG, colorE); + r += getResult2(colorB, colorA, colorK, colorF); + r += getResult2(colorB, colorA, colorH, colorN); + r += getResult1(colorA, colorB, colorL, colorO); + + if (r > 0) + product2 = colorA; + else if (r < 0) + product2 = colorB; + else { + product2 = qInterpolate(colorA, colorB, colorC, colorD); + } + } + } else { + product2 = qInterpolate(colorA, colorB, colorC, colorD); + + if (colorA == colorC && colorA == colorF + && colorB != colorE && colorB == colorJ) { + product = colorA; + } else if (colorB == colorE && colorB == colorD + && colorA != colorF && colorA == colorI) { + product = colorB; + } else { + product = interpolate(colorA, colorB); + } + + if (colorA == colorB && colorA == colorH + && colorG != colorC && colorC == colorM) { + product1 = colorA; + } else if (colorC == colorG && colorC == colorD + && colorA != colorH && colorA == colorI) { + product1 = colorC; + } else { + product1 = interpolate(colorA, colorC); + } + } + *dP = colorA; + *(dP + 1) = product; + *(dP + dstPitch) = product1; + *(dP + dstPitch + 1) = product2; + + ++bP; + dP += 2; + } + + srcPtr += srcPitch; + dstPtr += dstPitch * 2; + } +} + +Kreed_2xSaI::Kreed_2xSaI() { + buffer = NULL; +} + +Kreed_2xSaI::~Kreed_2xSaI() { + delete []buffer; +} + +void Kreed_2xSaI::init() { + delete []buffer; + + buffer = new Gambatte::uint_least32_t[145 * 161]; + std::memset(buffer, 0, 145ul * 161 * sizeof(Gambatte::uint_least32_t)); +} + +void Kreed_2xSaI::outit() { + delete []buffer; + buffer = NULL; +} + +const Gambatte::FilterInfo& Kreed_2xSaI::info() { + static Gambatte::FilterInfo fInfo = { "Kreed's 2xSaI", 160 * 2, 144 * 2 }; + + return fInfo; +} + +Gambatte::uint_least32_t* Kreed_2xSaI::inBuffer() { + return buffer; +} + +unsigned Kreed_2xSaI::inPitch() { + return 161; +} + +void Kreed_2xSaI::filter(Gambatte::uint_least32_t *const dbuffer, const unsigned pitch) { + ::filter(dbuffer, pitch, buffer, 161, 160, 144); +} diff --git a/src/lib/libgambatte/src/video/filters/kreed2xsai.h b/src/lib/libgambatte/src/video/filters/kreed2xsai.h new file mode 100644 index 00000000..f2feffc0 --- /dev/null +++ b/src/lib/libgambatte/src/video/filters/kreed2xsai.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef KREED2XSAI_H +#define KREED2XSAI_H + +#include "filter.h" + +struct FilterInfo; + +class Kreed_2xSaI : public Filter { + Gambatte::uint_least32_t *buffer; + +public: + Kreed_2xSaI(); + ~Kreed_2xSaI(); + void init(); + void outit(); + const Gambatte::FilterInfo& info(); + void filter(Gambatte::uint_least32_t *dbuffer, unsigned pitch); + Gambatte::uint_least32_t* inBuffer(); + unsigned inPitch(); +}; + +#endif diff --git a/src/lib/libgambatte/src/video/filters/maxsthq2x.cpp b/src/lib/libgambatte/src/video/filters/maxsthq2x.cpp new file mode 100644 index 00000000..a818d62a --- /dev/null +++ b/src/lib/libgambatte/src/video/filters/maxsthq2x.cpp @@ -0,0 +1,2875 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * Copyright (C) 2003 MaxSt * + * maxst@hiend3d.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#include "maxsthq2x.h" +#include "filterinfo.h" +#include + +static /*inline*/ unsigned long Interp1(const unsigned long c1, const unsigned long c2) { + const unsigned long lowbits = ((c1 & 0x030303) * 3 + (c2 & 0x030303)) & 0x030303; + + return (c1 * 3 + c2 - lowbits) >> 2; +} + +static /*inline*/ unsigned long Interp2(const unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned long lowbits = ((c1 * 2 & 0x020202) + (c2 & 0x030303) + (c3 & 0x030303)) & 0x030303; + + return (c1 * 2 + c2 + c3 - lowbits) >> 2; +} + +static /*inline*/ unsigned long Interp6(const unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned long lowbits = ((c1 & 0x070707) * 5 + (c2 * 2 & 0x060606) + (c3 & 0x070707)) & 0x070707; + + return ((c1 * 5 + c2 * 2 + c3) - lowbits) >> 3; +} + +static /*inline*/ unsigned long Interp7(const unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned long lowbits = ((c1 & 0x070707) * 6 + (c2 & 0x070707) + (c3 & 0x070707)) & 0x070707; + + return ((c1 * 6 + c2 + c3) - lowbits) >> 3; +} + +static /*inline*/ unsigned long Interp9(unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned lowbits = ((c1 * 2 & 0x070707) + ((c2 & 0x070707) + (c3 & 0x070707)) * 3) & 0x070707; + + return (c1 * 2 + (c2 + c3) * 3 - lowbits) >> 3; +} + +static /*inline*/ unsigned long Interp10(const unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned lowbits = ((c1 & 0x0F0F0F) * 14 + (c2 & 0x0F0F0F) + (c3 & 0x0F0F0F)) & 0x0F0F0F; + + return (c1 * 14 + c2 + c3 - lowbits) >> 4; +} + +#define PIXEL00_0 *pOut = w[5]; +#define PIXEL00_10 *pOut = Interp1(w[5], w[1]); +#define PIXEL00_11 *pOut = Interp1(w[5], w[4]); +#define PIXEL00_12 *pOut = Interp1(w[5], w[2]); +#define PIXEL00_20 *pOut = Interp2(w[5], w[4], w[2]); +#define PIXEL00_21 *pOut = Interp2(w[5], w[1], w[2]); +#define PIXEL00_22 *pOut = Interp2(w[5], w[1], w[4]); +#define PIXEL00_60 *pOut = Interp6(w[5], w[2], w[4]); +#define PIXEL00_61 *pOut = Interp6(w[5], w[4], w[2]); +#define PIXEL00_70 *pOut = Interp7(w[5], w[4], w[2]); +#define PIXEL00_90 *pOut = Interp9(w[5], w[4], w[2]); +#define PIXEL00_100 *pOut = Interp10(w[5], w[4], w[2]); +#define PIXEL01_0 *(pOut+1) = w[5]; +#define PIXEL01_10 *(pOut+1) = Interp1(w[5], w[3]); +#define PIXEL01_11 *(pOut+1) = Interp1(w[5], w[2]); +#define PIXEL01_12 *(pOut+1) = Interp1(w[5], w[6]); +#define PIXEL01_20 *(pOut+1) = Interp2(w[5], w[2], w[6]); +#define PIXEL01_21 *(pOut+1) = Interp2(w[5], w[3], w[6]); +#define PIXEL01_22 *(pOut+1) = Interp2(w[5], w[3], w[2]); +#define PIXEL01_60 *(pOut+1) = Interp6(w[5], w[6], w[2]); +#define PIXEL01_61 *(pOut+1) = Interp6(w[5], w[2], w[6]); +#define PIXEL01_70 *(pOut+1) = Interp7(w[5], w[2], w[6]); +#define PIXEL01_90 *(pOut+1) = Interp9(w[5], w[2], w[6]); +#define PIXEL01_100 *(pOut+1) = Interp10(w[5], w[2], w[6]); +#define PIXEL10_0 *(pOut+dstPitch) = w[5]; +#define PIXEL10_10 *(pOut+dstPitch) = Interp1(w[5], w[7]); +#define PIXEL10_11 *(pOut+dstPitch) = Interp1(w[5], w[8]); +#define PIXEL10_12 *(pOut+dstPitch) = Interp1(w[5], w[4]); +#define PIXEL10_20 *(pOut+dstPitch) = Interp2(w[5], w[8], w[4]); +#define PIXEL10_21 *(pOut+dstPitch) = Interp2(w[5], w[7], w[4]); +#define PIXEL10_22 *(pOut+dstPitch) = Interp2(w[5], w[7], w[8]); +#define PIXEL10_60 *(pOut+dstPitch) = Interp6(w[5], w[4], w[8]); +#define PIXEL10_61 *(pOut+dstPitch) = Interp6(w[5], w[8], w[4]); +#define PIXEL10_70 *(pOut+dstPitch) = Interp7(w[5], w[8], w[4]); +#define PIXEL10_90 *(pOut+dstPitch) = Interp9(w[5], w[8], w[4]); +#define PIXEL10_100 *(pOut+dstPitch) = Interp10(w[5], w[8], w[4]); +#define PIXEL11_0 *(pOut+dstPitch+1) = w[5]; +#define PIXEL11_10 *(pOut+dstPitch+1) = Interp1(w[5], w[9]); +#define PIXEL11_11 *(pOut+dstPitch+1) = Interp1(w[5], w[6]); +#define PIXEL11_12 *(pOut+dstPitch+1) = Interp1(w[5], w[8]); +#define PIXEL11_20 *(pOut+dstPitch+1) = Interp2(w[5], w[6], w[8]); +#define PIXEL11_21 *(pOut+dstPitch+1) = Interp2(w[5], w[9], w[8]); +#define PIXEL11_22 *(pOut+dstPitch+1) = Interp2(w[5], w[9], w[6]); +#define PIXEL11_60 *(pOut+dstPitch+1) = Interp6(w[5], w[8], w[6]); +#define PIXEL11_61 *(pOut+dstPitch+1) = Interp6(w[5], w[6], w[8]); +#define PIXEL11_70 *(pOut+dstPitch+1) = Interp7(w[5], w[6], w[8]); +#define PIXEL11_90 *(pOut+dstPitch+1) = Interp9(w[5], w[6], w[8]); +#define PIXEL11_100 *(pOut+dstPitch+1) = Interp10(w[5], w[6], w[8]); + +static /*inline*/ bool Diff(const unsigned long w1, const unsigned long w2) { + const unsigned rdiff = (w1 >> 16) - (w2 >> 16); + const unsigned gdiff = (w1 >> 8 & 0xFF) - (w2 >> 8 & 0xFF); + const unsigned bdiff = (w1 & 0xFF) - (w2 & 0xFF); + + return rdiff + gdiff + bdiff + 0xC0U > 0xC0U * 2 || + rdiff - bdiff + 0x1CU > 0x1CU * 2 || + gdiff * 2 - rdiff - bdiff + 0x30U > 0x30U * 2; +} + +static void filter(Gambatte::uint_least32_t *pOut, const unsigned dstPitch, + const Gambatte::uint_least32_t *pIn, const unsigned Xres, const unsigned Yres) +{ + unsigned long w[10]; + + // +----+----+----+ + // | | | | + // | w1 | w2 | w3 | + // +----+----+----+ + // | | | | + // | w4 | w5 | w6 | + // +----+----+----+ + // | | | | + // | w7 | w8 | w9 | + // +----+----+----+ + + for (unsigned j = 0; j < Yres; j++) { + const unsigned prevline = j > 0 ? Xres : 0; + const unsigned nextline = j < Yres - 1 ? Xres : 0; + + for (unsigned i = 0; i < Xres; i++) { + w[2] = *(pIn - prevline); + w[5] = *(pIn); + w[8] = *(pIn + nextline); + + if (i > 0) { + w[1] = *(pIn - prevline - 1); + w[4] = *(pIn - 1); + w[7] = *(pIn + nextline - 1); + } else { + w[1] = w[2]; + w[4] = w[5]; + w[7] = w[8]; + } + + if (i < Xres - 1) { + w[3] = *(pIn - prevline + 1); + w[6] = *(pIn + 1); + w[9] = *(pIn + nextline + 1); + } else { + w[3] = w[2]; + w[6] = w[5]; + w[9] = w[8]; + } + + unsigned pattern = 0; + + { + unsigned flag = 1; + + const unsigned r1 = w[5] >> 16; + const unsigned g1 = w[5] >> 8 & 0xFF; + const unsigned b1 = w[5] & 0xFF; + + for (unsigned k = 1; k < 10; ++k) { + if (k == 5) continue; + + if (w[k] != w[5]) { + const unsigned rdiff = r1 - (w[k] >> 16); + const unsigned gdiff = g1 - (w[k] >> 8 & 0xFF); + const unsigned bdiff = b1 - (w[k] & 0xFF); + + if (rdiff + gdiff + bdiff + 0xC0U > 0xC0U * 2 || + rdiff - bdiff + 0x1CU > 0x1CU * 2 || + gdiff * 2 - rdiff - bdiff + 0x30U > 0x30U * 2) + pattern |= flag; + } + + flag <<= 1; + } + } + + switch (pattern) + { + case 0: + case 1: + case 4: + case 32: + case 128: + case 5: + case 132: + case 160: + case 33: + case 129: + case 36: + case 133: + case 164: + case 161: + case 37: + case 165: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_20 + PIXEL11_20 + break; + } + case 2: + case 34: + case 130: + case 162: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_20 + PIXEL11_20 + break; + } + case 16: + case 17: + case 48: + case 49: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_20 + PIXEL11_21 + break; + } + case 64: + case 65: + case 68: + case 69: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_21 + PIXEL11_22 + break; + } + case 8: + case 12: + case 136: + case 140: + { + PIXEL00_21 + PIXEL01_20 + PIXEL10_22 + PIXEL11_20 + break; + } + case 3: + case 35: + case 131: + case 163: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_20 + PIXEL11_20 + break; + } + case 6: + case 38: + case 134: + case 166: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 20: + case 21: + case 52: + case 53: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_21 + break; + } + case 144: + case 145: + case 176: + case 177: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_20 + PIXEL11_12 + break; + } + case 192: + case 193: + case 196: + case 197: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_21 + PIXEL11_11 + break; + } + case 96: + case 97: + case 100: + case 101: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_22 + break; + } + case 40: + case 44: + case 168: + case 172: + { + PIXEL00_21 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 9: + case 13: + case 137: + case 141: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_22 + PIXEL11_20 + break; + } + case 18: + case 50: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_20 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 80: + case 81: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_20 + } + break; + } + case 72: + case 76: + { + PIXEL00_21 + PIXEL01_20 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 10: + case 138: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_22 + PIXEL11_20 + break; + } + case 66: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_21 + PIXEL11_22 + break; + } + case 24: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_22 + PIXEL11_21 + break; + } + case 7: + case 39: + case 135: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 148: + case 149: + case 180: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_12 + break; + } + case 224: + case 228: + case 225: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_11 + break; + } + case 41: + case 169: + case 45: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 22: + case 54: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 208: + case 209: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 104: + case 108: + { + PIXEL00_21 + PIXEL01_20 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 11: + case 139: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_22 + PIXEL11_20 + break; + } + case 19: + case 51: + { + if (Diff(w[2], w[6])) + { + PIXEL00_11 + PIXEL01_10 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 146: + case 178: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_20 + break; + } + case 84: + case 85: + { + PIXEL00_20 + if (Diff(w[6], w[8])) + { + PIXEL01_11 + PIXEL11_10 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_21 + break; + } + case 112: + case 113: + { + PIXEL00_20 + PIXEL01_22 + if (Diff(w[6], w[8])) + { + PIXEL10_12 + PIXEL11_10 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 200: + case 204: + { + PIXEL00_21 + PIXEL01_20 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 73: + case 77: + { + if (Diff(w[8], w[4])) + { + PIXEL00_12 + PIXEL10_10 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_20 + PIXEL11_22 + break; + } + case 42: + case 170: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_21 + PIXEL11_20 + break; + } + case 14: + case 142: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_22 + PIXEL11_20 + break; + } + case 67: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_21 + PIXEL11_22 + break; + } + case 70: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_21 + PIXEL11_22 + break; + } + case 28: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_22 + PIXEL11_21 + break; + } + case 152: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_22 + PIXEL11_12 + break; + } + case 194: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_21 + PIXEL11_11 + break; + } + case 98: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_12 + PIXEL11_22 + break; + } + case 56: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_11 + PIXEL11_21 + break; + } + case 25: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_22 + PIXEL11_21 + break; + } + case 26: + case 31: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_21 + break; + } + case 82: + case 214: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 88: + case 248: + { + PIXEL00_21 + PIXEL01_22 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 74: + case 107: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 27: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_22 + PIXEL11_21 + break; + } + case 86: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + PIXEL11_10 + break; + } + case 216: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_10 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 106: + { + PIXEL00_10 + PIXEL01_21 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 30: + { + PIXEL00_10 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_21 + break; + } + case 210: + { + PIXEL00_22 + PIXEL01_10 + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 120: + { + PIXEL00_21 + PIXEL01_22 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 75: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_10 + PIXEL11_22 + break; + } + case 29: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_22 + PIXEL11_21 + break; + } + case 198: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_21 + PIXEL11_11 + break; + } + case 184: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_11 + PIXEL11_12 + break; + } + case 99: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_12 + PIXEL11_22 + break; + } + case 57: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_11 + PIXEL11_21 + break; + } + case 71: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_21 + PIXEL11_22 + break; + } + case 156: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_22 + PIXEL11_12 + break; + } + case 226: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_12 + PIXEL11_11 + break; + } + case 60: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_11 + PIXEL11_21 + break; + } + case 195: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_21 + PIXEL11_11 + break; + } + case 102: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_12 + PIXEL11_22 + break; + } + case 153: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_22 + PIXEL11_12 + break; + } + case 58: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 83: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 92: + { + PIXEL00_21 + PIXEL01_11 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 202: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_21 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 78: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_22 + break; + } + case 154: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 114: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 89: + { + PIXEL00_12 + PIXEL01_22 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 90: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 55: + case 23: + { + if (Diff(w[2], w[6])) + { + PIXEL00_11 + PIXEL01_0 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 182: + case 150: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_20 + break; + } + case 213: + case 212: + { + PIXEL00_20 + if (Diff(w[6], w[8])) + { + PIXEL01_11 + PIXEL11_0 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_21 + break; + } + case 241: + case 240: + { + PIXEL00_20 + PIXEL01_22 + if (Diff(w[6], w[8])) + { + PIXEL10_12 + PIXEL11_0 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 236: + case 232: + { + PIXEL00_21 + PIXEL01_20 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 109: + case 105: + { + if (Diff(w[8], w[4])) + { + PIXEL00_12 + PIXEL10_0 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_20 + PIXEL11_22 + break; + } + case 171: + case 43: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_21 + PIXEL11_20 + break; + } + case 143: + case 15: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_22 + PIXEL11_20 + break; + } + case 124: + { + PIXEL00_21 + PIXEL01_11 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 203: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_10 + PIXEL11_11 + break; + } + case 62: + { + PIXEL00_10 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 211: + { + PIXEL00_11 + PIXEL01_10 + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 118: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_12 + PIXEL11_10 + break; + } + case 217: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_10 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 110: + { + PIXEL00_10 + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 155: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_22 + PIXEL11_12 + break; + } + case 188: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_11 + PIXEL11_12 + break; + } + case 185: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_11 + PIXEL11_12 + break; + } + case 61: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_11 + PIXEL11_21 + break; + } + case 157: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_22 + PIXEL11_12 + break; + } + case 103: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_12 + PIXEL11_22 + break; + } + case 227: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_12 + PIXEL11_11 + break; + } + case 230: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_12 + PIXEL11_11 + break; + } + case 199: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_21 + PIXEL11_11 + break; + } + case 220: + { + PIXEL00_21 + PIXEL01_11 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 158: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 234: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_21 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_11 + break; + } + case 242: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 59: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 121: + { + PIXEL00_12 + PIXEL01_22 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 87: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 79: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_22 + break; + } + case 122: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 94: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 218: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 91: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 229: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_11 + break; + } + case 167: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 173: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 181: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_12 + break; + } + case 186: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_12 + break; + } + case 115: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 93: + { + PIXEL00_12 + PIXEL01_11 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 206: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 205: + case 201: + { + PIXEL00_12 + PIXEL01_20 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 174: + case 46: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + PIXEL10_11 + PIXEL11_20 + break; + } + case 179: + case 147: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_20 + PIXEL11_12 + break; + } + case 117: + case 116: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 189: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_11 + PIXEL11_12 + break; + } + case 231: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_12 + PIXEL11_11 + break; + } + case 126: + { + PIXEL00_10 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 219: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_10 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 125: + { + if (Diff(w[8], w[4])) + { + PIXEL00_12 + PIXEL10_0 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_11 + PIXEL11_10 + break; + } + case 221: + { + PIXEL00_12 + if (Diff(w[6], w[8])) + { + PIXEL01_11 + PIXEL11_0 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_10 + break; + } + case 207: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_10 + PIXEL11_11 + break; + } + case 238: + { + PIXEL00_10 + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 190: + { + PIXEL00_10 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_11 + break; + } + case 187: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_10 + PIXEL11_12 + break; + } + case 243: + { + PIXEL00_11 + PIXEL01_10 + if (Diff(w[6], w[8])) + { + PIXEL10_12 + PIXEL11_0 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 119: + { + if (Diff(w[2], w[6])) + { + PIXEL00_11 + PIXEL01_0 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_12 + PIXEL11_10 + break; + } + case 237: + case 233: + { + PIXEL00_12 + PIXEL01_20 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 175: + case 47: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + PIXEL10_11 + PIXEL11_20 + break; + } + case 183: + case 151: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_20 + PIXEL11_12 + break; + } + case 245: + case 244: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 250: + { + PIXEL00_10 + PIXEL01_10 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 123: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 95: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_10 + PIXEL11_10 + break; + } + case 222: + { + PIXEL00_10 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_10 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 252: + { + PIXEL00_21 + PIXEL01_11 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 249: + { + PIXEL00_12 + PIXEL01_22 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 235: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 111: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 63: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 159: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 215: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 246: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 254: + { + PIXEL00_10 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 253: + { + PIXEL00_12 + PIXEL01_11 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 251: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 239: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 127: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 191: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_11 + PIXEL11_12 + break; + } + case 223: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_10 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 247: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 255: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + } + ++pIn; + pOut += 2; + } + pOut += dstPitch * 2 - Xres * 2; + } +} + +MaxSt_Hq2x::MaxSt_Hq2x() { + buffer = NULL; +} + +MaxSt_Hq2x::~MaxSt_Hq2x() { + outit(); +} + +void MaxSt_Hq2x::init() { + delete []buffer; + buffer = new Gambatte::uint_least32_t[144 * 160]; +} + +void MaxSt_Hq2x::outit() { + delete []buffer; + buffer = NULL; +} + +const Gambatte::FilterInfo& MaxSt_Hq2x::info() { + static const Gambatte::FilterInfo fInfo = { "MaxSt's Hq2x", 160 * 2, 144 * 2 }; + return fInfo; +} + +Gambatte::uint_least32_t* MaxSt_Hq2x::inBuffer() { + return buffer; +} + +unsigned MaxSt_Hq2x::inPitch() { + return 160; +} + +void MaxSt_Hq2x::filter(Gambatte::uint_least32_t *const dbuffer, const unsigned pitch) { + ::filter(dbuffer, pitch, buffer, 160, 144); +} diff --git a/src/lib/libgambatte/src/video/filters/maxsthq2x.h b/src/lib/libgambatte/src/video/filters/maxsthq2x.h new file mode 100644 index 00000000..ca2cf411 --- /dev/null +++ b/src/lib/libgambatte/src/video/filters/maxsthq2x.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MAXSTHQ2X_H +#define MAXSTHQ2X_H + +#include "filter.h" + +struct FilterInfo; + +class MaxSt_Hq2x : public Filter { + Gambatte::uint_least32_t *buffer; + +public: + MaxSt_Hq2x(); + ~MaxSt_Hq2x(); + void init(); + void outit(); + const Gambatte::FilterInfo& info(); + void filter(Gambatte::uint_least32_t *dbuffer, unsigned pitch); + Gambatte::uint_least32_t* inBuffer(); + unsigned inPitch(); +}; + + +#endif diff --git a/src/lib/libgambatte/src/video/filters/maxsthq3x.cpp b/src/lib/libgambatte/src/video/filters/maxsthq3x.cpp new file mode 100644 index 00000000..996a221e --- /dev/null +++ b/src/lib/libgambatte/src/video/filters/maxsthq3x.cpp @@ -0,0 +1,3845 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * Copyright (C) 2003 MaxSt * + * maxst@hiend3d.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "maxsthq3x.h" +#include "filterinfo.h" +#include + +static /*inline*/ unsigned long Interp1(const unsigned long c1, const unsigned long c2) { + const unsigned long lowbits = ((c1 & 0x030303) * 3 + (c2 & 0x030303)) & 0x030303; + + return (c1 * 3 + c2 - lowbits) >> 2; +} + +static /*inline*/ unsigned long Interp2(const unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned long lowbits = ((c1 * 2 & 0x020202) + (c2 & 0x030303) + (c3 & 0x030303)) & 0x030303; + + return (c1 * 2 + c2 + c3 - lowbits) >> 2; +} + +static /*inline*/ unsigned long Interp3(const unsigned long c1, const unsigned long c2) { + const unsigned long lowbits = ((c1 & 0x070707) * 7 + (c2 & 0x070707)) & 0x070707; + + return (c1 * 7 + c2 - lowbits) >> 3; +} + +static /*inline*/ unsigned long Interp4(const unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned long lowbits = ((c1 * 2 & 0x0E0E0E) + ((c2 & 0x0F0F0F) + (c3 & 0x0F0F0F)) * 7) & 0x0F0F0F; + + return (c1 * 2 + (c2 + c3) * 7 - lowbits) >> 4; +} + +static /*inline*/ unsigned long Interp5(const unsigned long c1, const unsigned long c2) { + return (c1 + c2 - ((c1 ^ c2) & 0x010101)) >> 1; +} + +#define PIXEL00_1M *pOut = Interp1(w[5], w[1]); +#define PIXEL00_1U *pOut = Interp1(w[5], w[2]); +#define PIXEL00_1L *pOut = Interp1(w[5], w[4]); +#define PIXEL00_2 *pOut = Interp2(w[5], w[4], w[2]); +#define PIXEL00_4 *pOut = Interp4(w[5], w[4], w[2]); +#define PIXEL00_5 *pOut = Interp5(w[4], w[2]); +#define PIXEL00_C *pOut = w[5]; + +#define PIXEL01_1 *(pOut+1) = Interp1(w[5], w[2]); +#define PIXEL01_3 *(pOut+1) = Interp3(w[5], w[2]); +#define PIXEL01_6 *(pOut+1) = Interp1(w[2], w[5]); +#define PIXEL01_C *(pOut+1) = w[5]; + +#define PIXEL02_1M *(pOut+2) = Interp1(w[5], w[3]); +#define PIXEL02_1U *(pOut+2) = Interp1(w[5], w[2]); +#define PIXEL02_1R *(pOut+2) = Interp1(w[5], w[6]); +#define PIXEL02_2 *(pOut+2) = Interp2(w[5], w[2], w[6]); +#define PIXEL02_4 *(pOut+2) = Interp4(w[5], w[2], w[6]); +#define PIXEL02_5 *(pOut+2) = Interp5(w[2], w[6]); +#define PIXEL02_C *(pOut+2) = w[5]; + +#define PIXEL10_1 *(pOut+dstPitch) = Interp1(w[5], w[4]); +#define PIXEL10_3 *(pOut+dstPitch) = Interp3(w[5], w[4]); +#define PIXEL10_6 *(pOut+dstPitch) = Interp1(w[4], w[5]); +#define PIXEL10_C *(pOut+dstPitch) = w[5]; + +#define PIXEL11 *(pOut+dstPitch+1) = w[5]; + +#define PIXEL12_1 *(pOut+dstPitch+2) = Interp1(w[5], w[6]); +#define PIXEL12_3 *(pOut+dstPitch+2) = Interp3(w[5], w[6]); +#define PIXEL12_6 *(pOut+dstPitch+2) = Interp1(w[6], w[5]); +#define PIXEL12_C *(pOut+dstPitch+2) = w[5]; + +#define PIXEL20_1M *(pOut+dstPitch*2) = Interp1(w[5], w[7]); +#define PIXEL20_1D *(pOut+dstPitch*2) = Interp1(w[5], w[8]); +#define PIXEL20_1L *(pOut+dstPitch*2) = Interp1(w[5], w[4]); +#define PIXEL20_2 *(pOut+dstPitch*2) = Interp2(w[5], w[8], w[4]); +#define PIXEL20_4 *(pOut+dstPitch*2) = Interp4(w[5], w[8], w[4]); +#define PIXEL20_5 *(pOut+dstPitch*2) = Interp5(w[8], w[4]); +#define PIXEL20_C *(pOut+dstPitch*2) = w[5]; + +#define PIXEL21_1 *(pOut+dstPitch*2+1) = Interp1(w[5], w[8]); +#define PIXEL21_3 *(pOut+dstPitch*2+1) = Interp3(w[5], w[8]); +#define PIXEL21_6 *(pOut+dstPitch*2+1) = Interp1(w[8], w[5]); +#define PIXEL21_C *(pOut+dstPitch*2+1) = w[5]; + +#define PIXEL22_1M *(pOut+dstPitch*2+2) = Interp1(w[5], w[9]); +#define PIXEL22_1D *(pOut+dstPitch*2+2) = Interp1(w[5], w[8]); +#define PIXEL22_1R *(pOut+dstPitch*2+2) = Interp1(w[5], w[6]); +#define PIXEL22_2 *(pOut+dstPitch*2+2) = Interp2(w[5], w[6], w[8]); +#define PIXEL22_4 *(pOut+dstPitch*2+2) = Interp4(w[5], w[6], w[8]); +#define PIXEL22_5 *(pOut+dstPitch*2+2) = Interp5(w[6], w[8]); +#define PIXEL22_C *(pOut+dstPitch*2+2) = w[5]; + +static /*inline*/ bool Diff(const unsigned long w1, const unsigned long w2) { + const unsigned rdiff = (w1 >> 16) - (w2 >> 16); + const unsigned gdiff = (w1 >> 8 & 0xFF) - (w2 >> 8 & 0xFF); + const unsigned bdiff = (w1 & 0xFF) - (w2 & 0xFF); + + return rdiff + gdiff + bdiff + 0xC0U > 0xC0U * 2 || + rdiff - bdiff + 0x1CU > 0x1CU * 2 || + gdiff * 2 - rdiff - bdiff + 0x30U > 0x30U * 2; +} + +static void filter(Gambatte::uint_least32_t *pOut, const unsigned dstPitch, + const Gambatte::uint_least32_t *pIn, const unsigned Xres, const unsigned Yres) +{ + unsigned long w[10]; + + // +----+----+----+ + // | | | | + // | w1 | w2 | w3 | + // +----+----+----+ + // | | | | + // | w4 | w5 | w6 | + // +----+----+----+ + // | | | | + // | w7 | w8 | w9 | + // +----+----+----+ + + for (unsigned j = 0; j < Yres; j++) { + const unsigned prevline = j > 0 ? Xres : 0; + const unsigned nextline = j < Yres - 1 ? Xres : 0; + + for (unsigned i = 0; i < Xres; i++) { + w[2] = *(pIn - prevline); + w[5] = *(pIn); + w[8] = *(pIn + nextline); + + if (i > 0) { + w[1] = *(pIn - prevline - 1); + w[4] = *(pIn - 1); + w[7] = *(pIn + nextline - 1); + } else { + w[1] = w[2]; + w[4] = w[5]; + w[7] = w[8]; + } + + if (i < Xres - 1) { + w[3] = *(pIn - prevline + 1); + w[6] = *(pIn + 1); + w[9] = *(pIn + nextline + 1); + } else { + w[3] = w[2]; + w[6] = w[5]; + w[9] = w[8]; + } + + unsigned pattern = 0; + + { + unsigned flag = 1; + + const unsigned r1 = w[5] >> 16; + const unsigned g1 = w[5] >> 8 & 0xFF; + const unsigned b1 = w[5] & 0xFF; + + for (unsigned k = 1; k < 10; ++k) { + if (k == 5) continue; + + if (w[k] != w[5]) { + const unsigned rdiff = r1 - (w[k] >> 16); + const unsigned gdiff = g1 - (w[k] >> 8 & 0xFF); + const unsigned bdiff = b1 - (w[k] & 0xFF); + + if (rdiff + gdiff + bdiff + 0xC0U > 0xC0U * 2 || + rdiff - bdiff + 0x1CU > 0x1CU * 2 || + gdiff * 2 - rdiff - bdiff + 0x30U > 0x30U * 2) + pattern |= flag; + } + + flag <<= 1; + } + } + + switch (pattern) + { + case 0: + case 1: + case 4: + case 32: + case 128: + case 5: + case 132: + case 160: + case 33: + case 129: + case 36: + case 133: + case 164: + case 161: + case 37: + case 165: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 2: + case 34: + case 130: + case 162: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 16: + case 17: + case 48: + case 49: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 64: + case 65: + case 68: + case 69: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 8: + case 12: + case 136: + case 140: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 3: + case 35: + case 131: + case 163: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 6: + case 38: + case 134: + case 166: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 20: + case 21: + case 52: + case 53: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 144: + case 145: + case 176: + case 177: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 192: + case 193: + case 196: + case 197: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 96: + case 97: + case 100: + case 101: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 40: + case 44: + case 168: + case 172: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 9: + case 13: + case 137: + case 141: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 18: + case 50: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_1M + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 80: + case 81: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_1M + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 72: + case 76: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_1M + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 10: + case 138: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 66: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 24: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 7: + case 39: + case 135: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 148: + case 149: + case 180: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 224: + case 228: + case 225: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 41: + case 169: + case 45: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 22: + case 54: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 208: + case 209: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 104: + case 108: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 11: + case 139: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 19: + case 51: + { + if (Diff(w[2], w[6])) + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL12_C + } + else + { + PIXEL00_2 + PIXEL01_6 + PIXEL02_5 + PIXEL12_1 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 146: + case 178: + { + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_1M + PIXEL12_C + PIXEL22_1D + } + else + { + PIXEL01_1 + PIXEL02_5 + PIXEL12_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + break; + } + case 84: + case 85: + { + if (Diff(w[6], w[8])) + { + PIXEL02_1U + PIXEL12_C + PIXEL21_C + PIXEL22_1M + } + else + { + PIXEL02_2 + PIXEL12_6 + PIXEL21_1 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + break; + } + case 112: + case 113: + { + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + } + else + { + PIXEL12_1 + PIXEL20_2 + PIXEL21_6 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + break; + } + case 200: + case 204: + { + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + } + else + { + PIXEL10_1 + PIXEL20_5 + PIXEL21_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + break; + } + case 73: + case 77: + { + if (Diff(w[8], w[4])) + { + PIXEL00_1U + PIXEL10_C + PIXEL20_1M + PIXEL21_C + } + else + { + PIXEL00_2 + PIXEL10_6 + PIXEL20_5 + PIXEL21_1 + } + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + PIXEL22_1M + break; + } + case 42: + case 170: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + PIXEL01_C + PIXEL10_C + PIXEL20_1D + } + else + { + PIXEL00_5 + PIXEL01_1 + PIXEL10_6 + PIXEL20_2 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL21_1 + PIXEL22_2 + break; + } + case 14: + case 142: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_C + } + else + { + PIXEL00_5 + PIXEL01_6 + PIXEL02_2 + PIXEL10_1 + } + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 67: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 70: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 28: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 152: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 194: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 98: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 56: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 25: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 26: + case 31: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 82: + case 214: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 88: + case 248: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } + case 74: + case 107: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 27: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 86: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 216: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 106: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 30: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 210: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 120: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 75: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 29: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 198: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 184: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 99: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 57: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 71: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 156: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 226: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 60: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 195: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 102: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 153: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 58: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 83: + { + PIXEL00_1L + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 92: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 202: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 78: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1M + break; + } + case 154: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 114: + { + PIXEL00_1M + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 89: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 90: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 55: + case 23: + { + if (Diff(w[2], w[6])) + { + PIXEL00_1L + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL00_2 + PIXEL01_6 + PIXEL02_5 + PIXEL12_1 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 182: + case 150: + { + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + PIXEL22_1D + } + else + { + PIXEL01_1 + PIXEL02_5 + PIXEL12_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + break; + } + case 213: + case 212: + { + if (Diff(w[6], w[8])) + { + PIXEL02_1U + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL02_2 + PIXEL12_6 + PIXEL21_1 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + break; + } + case 241: + case 240: + { + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL20_1L + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_1 + PIXEL20_2 + PIXEL21_6 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + break; + } + case 236: + case 232: + { + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + PIXEL22_1R + } + else + { + PIXEL10_1 + PIXEL20_5 + PIXEL21_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + break; + } + case 109: + case 105: + { + if (Diff(w[8], w[4])) + { + PIXEL00_1U + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL00_2 + PIXEL10_6 + PIXEL20_5 + PIXEL21_1 + } + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + PIXEL22_1M + break; + } + case 171: + case 43: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + PIXEL20_1D + } + else + { + PIXEL00_5 + PIXEL01_1 + PIXEL10_6 + PIXEL20_2 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL21_1 + PIXEL22_2 + break; + } + case 143: + case 15: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL02_1R + PIXEL10_C + } + else + { + PIXEL00_5 + PIXEL01_6 + PIXEL02_2 + PIXEL10_1 + } + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 124: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 203: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 62: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 211: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 118: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 217: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 110: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 155: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 188: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 185: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 61: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 157: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 103: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 227: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 230: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 199: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 220: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 158: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 234: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1M + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1R + break; + } + case 242: + { + PIXEL00_1M + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1L + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 59: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 121: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 87: + { + PIXEL00_1L + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1M + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 79: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1R + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1M + break; + } + case 122: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 94: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 218: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 91: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 229: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 167: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 173: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 181: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 186: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 115: + { + PIXEL00_1L + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 93: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 206: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 205: + case 201: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 174: + case 46: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 179: + case 147: + { + PIXEL00_1L + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 117: + case 116: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 189: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 231: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 126: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 219: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 125: + { + if (Diff(w[8], w[4])) + { + PIXEL00_1U + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL00_2 + PIXEL10_6 + PIXEL20_5 + PIXEL21_1 + } + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + PIXEL22_1M + break; + } + case 221: + { + if (Diff(w[6], w[8])) + { + PIXEL02_1U + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL02_2 + PIXEL12_6 + PIXEL21_1 + PIXEL22_5 + } + PIXEL00_1U + PIXEL01_1 + PIXEL10_C + PIXEL11 + PIXEL20_1M + break; + } + case 207: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL02_1R + PIXEL10_C + } + else + { + PIXEL00_5 + PIXEL01_6 + PIXEL02_2 + PIXEL10_1 + } + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 238: + { + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + PIXEL22_1R + } + else + { + PIXEL10_1 + PIXEL20_5 + PIXEL21_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL11 + PIXEL12_1 + break; + } + case 190: + { + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + PIXEL22_1D + } + else + { + PIXEL01_1 + PIXEL02_5 + PIXEL12_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + break; + } + case 187: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + PIXEL20_1D + } + else + { + PIXEL00_5 + PIXEL01_1 + PIXEL10_6 + PIXEL20_2 + } + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL21_1 + PIXEL22_1D + break; + } + case 243: + { + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL20_1L + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_1 + PIXEL20_2 + PIXEL21_6 + PIXEL22_5 + } + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + break; + } + case 119: + { + if (Diff(w[2], w[6])) + { + PIXEL00_1L + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL00_2 + PIXEL01_6 + PIXEL02_5 + PIXEL12_1 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 237: + case 233: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 175: + case 47: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 183: + case 151: + { + PIXEL00_1L + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 245: + case 244: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 250: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } + case 123: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 95: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 222: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 252: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 249: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } + case 235: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 111: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 63: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 159: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 215: + { + PIXEL00_1L + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 246: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 254: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_2 + } + break; + } + case 253: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 251: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_2 + PIXEL21_3 + } + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } + case 239: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 127: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_2 + PIXEL01_3 + PIXEL10_3 + } + if (Diff(w[2], w[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 191: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 223: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_2 + PIXEL12_3 + } + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 247: + { + PIXEL00_1L + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 255: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + } + ++pIn; + pOut += 3; + } + pOut += dstPitch * 3 - Xres * 3; + } +} + +MaxSt_Hq3x::MaxSt_Hq3x() { + buffer = NULL; +} + +MaxSt_Hq3x::~MaxSt_Hq3x() { + outit(); +} + +void MaxSt_Hq3x::init() { + delete []buffer; + buffer = new Gambatte::uint_least32_t[144 * 160]; +} + +void MaxSt_Hq3x::outit() { + delete []buffer; + buffer = NULL; +} + +const Gambatte::FilterInfo& MaxSt_Hq3x::info() { + static const Gambatte::FilterInfo fInfo = { "MaxSt's Hq3x", 160 * 3, 144 * 3 }; + return fInfo; +} + +Gambatte::uint_least32_t* MaxSt_Hq3x::inBuffer() { + return buffer; +} + +unsigned MaxSt_Hq3x::inPitch() { + return 160; +} + +void MaxSt_Hq3x::filter(Gambatte::uint_least32_t *const dbuffer, const unsigned pitch) { + ::filter(dbuffer, pitch, buffer, 160, 144); +} diff --git a/src/lib/libgambatte/src/video/filters/maxsthq3x.h b/src/lib/libgambatte/src/video/filters/maxsthq3x.h new file mode 100644 index 00000000..9e1f51d6 --- /dev/null +++ b/src/lib/libgambatte/src/video/filters/maxsthq3x.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MAXSTHQ3X_H +#define MAXSTHQ3X_H + +#include "filter.h" + +struct FilterInfo; + +class MaxSt_Hq3x : public Filter { + Gambatte::uint_least32_t *buffer; + +public: + MaxSt_Hq3x(); + ~MaxSt_Hq3x(); + void init(); + void outit(); + const Gambatte::FilterInfo& info(); + void filter(Gambatte::uint_least32_t *dbuffer, unsigned pitch); + Gambatte::uint_least32_t* inBuffer(); + unsigned inPitch(); +}; + +#endif diff --git a/src/lib/libgambatte/src/video/irq_event.cpp b/src/lib/libgambatte/src/video/irq_event.cpp new file mode 100644 index 00000000..358f1daf --- /dev/null +++ b/src/lib/libgambatte/src/video/irq_event.cpp @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "irq_event.h" + +IrqEvent::IrqEvent(event_queue &irqEventQueue_in) : + VideoEvent(11), + irqEventQueue(irqEventQueue_in) +{ +} + +void IrqEvent::doEvent() { + irqEventQueue.top()->doEvent(); + + if (irqEventQueue.top()->time() == DISABLED_TIME) + irqEventQueue.pop(); + else + irqEventQueue.modify_root(irqEventQueue.top()); + + setTime(schedule(irqEventQueue)); +} diff --git a/src/lib/libgambatte/src/video/irq_event.h b/src/lib/libgambatte/src/video/irq_event.h new file mode 100644 index 00000000..c8a5b991 --- /dev/null +++ b/src/lib/libgambatte/src/video/irq_event.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef VIDEO_IRQ_EVENT_H +#define VIDEO_IRQ_EVENT_H + +#include "../event_queue.h" +#include "video_event.h" +#include "video_event_comparer.h" +#include "basic_add_event.h" + +class IrqEvent : public VideoEvent { + event_queue &irqEventQueue; + +public: + IrqEvent(event_queue &irqEventQueue_in); + + void doEvent(); + + static unsigned long schedule(const event_queue &irqEventQueue) { + return irqEventQueue.top()->time(); + } + + void schedule() { + setTime(irqEventQueue.top()->time()); + } +}; + +static inline void addEvent(event_queue &q, IrqEvent *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, IrqEvent *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/src/lib/libgambatte/src/video/ly_counter.cpp b/src/lib/libgambatte/src/video/ly_counter.cpp new file mode 100644 index 00000000..5d5b6d98 --- /dev/null +++ b/src/lib/libgambatte/src/video/ly_counter.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "ly_counter.h" +#include "../savestate.h" + +LyCounter::LyCounter() : VideoEvent(0) { + setDoubleSpeed(false); + reset(0, 0); +} + +void LyCounter::doEvent() { + ++ly_; + + if (ly_ == 154) + ly_ = 0; + + setTime(time() + lineTime_); +} + +unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned long cycleCounter) const { + unsigned long tmp = time() + (lineCycle << ds); + + if (tmp - cycleCounter > lineTime_) + tmp -= lineTime_; + + return tmp; +} + +unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const unsigned long cycleCounter) const { + unsigned long tmp = time() + (((153U - ly()) * 456U + frameCycle) << ds); + + if (tmp - cycleCounter > 70224U << ds) + tmp -= 70224U << ds; + + return tmp; +} + +void LyCounter::reset(const unsigned long videoCycles, const unsigned long lastUpdate) { + ly_ = videoCycles / 456; + setTime(lastUpdate + ((456 - (videoCycles - ly_ * 456ul)) << isDoubleSpeed())); +} + +void LyCounter::setDoubleSpeed(const bool ds_in) { + ds = ds_in; + lineTime_ = 456U << ds_in; +} diff --git a/src/lib/libgambatte/src/video/ly_counter.h b/src/lib/libgambatte/src/video/ly_counter.h new file mode 100644 index 00000000..2b795fb8 --- /dev/null +++ b/src/lib/libgambatte/src/video/ly_counter.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef LY_COUNTER_H +#define LY_COUNTER_H + +class SaveState; + +#include "video_event.h" +#include "basic_add_event.h" + +class LyCounter : public VideoEvent { + unsigned short lineTime_; + unsigned char ly_; + bool ds; + +public: + LyCounter(); + + void doEvent(); + + bool isDoubleSpeed() const { + return ds; + } + + unsigned lineCycles(const unsigned long cc) const { + return 456u - ((time() - cc) >> isDoubleSpeed()); + } + + unsigned lineTime() const { + return lineTime_; + } + + unsigned ly() const { + return ly_; + } + + unsigned long nextLineCycle(unsigned lineCycle, unsigned long cycleCounter) const; + unsigned long nextFrameCycle(unsigned long frameCycle, unsigned long cycleCounter) const; + + void reset(unsigned long videoCycles, unsigned long lastUpdate); + + void setDoubleSpeed(bool ds_in); +}; + +static inline void addEvent(event_queue &q, LyCounter *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, LyCounter *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/src/lib/libgambatte/src/video/lyc_irq.cpp b/src/lib/libgambatte/src/video/lyc_irq.cpp new file mode 100644 index 00000000..eb81d41b --- /dev/null +++ b/src/lib/libgambatte/src/video/lyc_irq.cpp @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "lyc_irq.h" + +LycIrq::LycIrq(unsigned char &ifReg_in) : + VideoEvent(1), + ifReg(ifReg_in) +{ + setDoubleSpeed(false); + setM2IrqEnabled(false); + setLycReg(0); + setSkip(false); +} + +void LycIrq::doEvent() { + if (!skip && (!m2IrqEnabled || lycReg_ > 143 || lycReg_ == 0)) + ifReg |= 0x2; + + skip = false; + + setTime(time() + frameTime); +} + +unsigned long LycIrq::schedule(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cycleCounter) { + return ((statReg & 0x40) && lycReg < 154) ? lyCounter.nextFrameCycle(lycReg ? lycReg * 456 : 153 * 456 + 8, cycleCounter) : static_cast(DISABLED_TIME); +} diff --git a/src/lib/libgambatte/src/video/lyc_irq.h b/src/lib/libgambatte/src/video/lyc_irq.h new file mode 100644 index 00000000..ed93fdda --- /dev/null +++ b/src/lib/libgambatte/src/video/lyc_irq.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_LYC_IRQ_H +#define VIDEO_LYC_IRQ_H + +#include "ly_counter.h" + +class LycIrq : public VideoEvent { + unsigned char &ifReg; + unsigned long frameTime; + unsigned char lycReg_; + bool m2IrqEnabled; + bool skip; + +public: + LycIrq(unsigned char &ifReg_in); + + void doEvent(); + + unsigned lycReg() const { + return lycReg_; + } + + static unsigned long schedule(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned long cycleCounter); + + void setDoubleSpeed(const bool ds) { + frameTime = 70224 << ds; + } + + void setLycReg(const unsigned lycReg_in) { + lycReg_ = lycReg_in; + } + + void setM2IrqEnabled(const bool enabled) { + m2IrqEnabled = enabled; + } + + void setSkip(const bool skip) { + this->skip = skip; + } + + bool skips() const { + return skip; + } + + bool isSkipPeriod(const unsigned long cycleCounter, const bool doubleSpeed) const { + return lycReg_ > 0 && time() - cycleCounter > 4U >> doubleSpeed && time() - cycleCounter < 9; + } +}; + +#endif diff --git a/src/lib/libgambatte/src/video/m3_extra_cycles.cpp b/src/lib/libgambatte/src/video/m3_extra_cycles.cpp new file mode 100644 index 00000000..de4eadb7 --- /dev/null +++ b/src/lib/libgambatte/src/video/m3_extra_cycles.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "m3_extra_cycles.h" +#include "scx_reader.h" +#include "window.h" +#include "sprite_mapper.h" +#include "../insertion_sort.h" + +M3ExtraCycles::M3ExtraCycles(const SpriteMapper &spriteMapper, + const ScxReader &scxReader, + const Window &win) : + spriteMapper(spriteMapper), + scxReader(scxReader), + win(win) +{ + invalidateCache(); +} + +static const unsigned char* addLineCycles(const unsigned char *const start, const unsigned char *const end, + const unsigned maxSpx, const unsigned scwxAnd7, const unsigned char *const posbuf_plus1, unsigned char *cycles_out) { + unsigned sum = 0; + + const unsigned char *a = start; + + for (; a < end; ++a) { + const unsigned spx = posbuf_plus1[*a]; + + if (spx > maxSpx) + break; + + unsigned cycles = 6; + const unsigned posAnd7 = (scwxAnd7 + spx) & 7; + + if (posAnd7 < 5) { + cycles = 11 - posAnd7; + + for (const unsigned char *b = a; b > start;) { + const unsigned bSpx = posbuf_plus1[*--b]; + + if (spx - bSpx > 4U) + break; + + if (((scwxAnd7 + bSpx) & 7) < 4 || spx == bSpx) { + cycles = 6; + break; + } + } + } + + sum += cycles; + } + + *cycles_out += sum; + + return a; +} + +void M3ExtraCycles::updateLine(const unsigned ly) const { + const bool windowEnabled = win.enabled(ly); + + cycles[ly] = windowEnabled ? scxReader.scxAnd7() + 6 : scxReader.scxAnd7(); + + const unsigned numSprites = spriteMapper.numSprites(ly); + + if (numSprites == 0) + return; + + unsigned char sortBuf[10]; + const unsigned char *tmp = spriteMapper.sprites(ly); + + if (spriteMapper.isCgb()) { + std::memcpy(sortBuf, tmp, sizeof(sortBuf)); + insertionSort(sortBuf, sortBuf + numSprites, SpriteMapper::SpxLess(spriteMapper.posbuf())); + tmp = sortBuf; + } + + const unsigned char *const tmpend = tmp + numSprites; + const unsigned char *const posbuf_plus1 = spriteMapper.posbuf() + 1; + + if (windowEnabled) { + addLineCycles(addLineCycles(tmp, tmpend, win.wxReader.wx(), scxReader.scxAnd7(), posbuf_plus1, cycles + ly), + tmpend, 167, 7 - win.wxReader.wx(), posbuf_plus1, cycles + ly); + } else + addLineCycles(tmp, tmpend, 167, scxReader.scxAnd7(), posbuf_plus1, cycles + ly); +} diff --git a/src/lib/libgambatte/src/video/m3_extra_cycles.h b/src/lib/libgambatte/src/video/m3_extra_cycles.h new file mode 100644 index 00000000..8a7f1470 --- /dev/null +++ b/src/lib/libgambatte/src/video/m3_extra_cycles.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_M3_EXTRA_CYCLES_H +#define VIDEO_M3_EXTRA_CYCLES_H + +class ScxReader; +class Window; +class SpriteMapper; + +#include + +class M3ExtraCycles { + enum { CYCLES_INVALID = 0xFF }; + + mutable unsigned char cycles[144]; + + const SpriteMapper &spriteMapper; + const ScxReader &scxReader; + const Window &win; + + void updateLine(unsigned ly) const; + +public: + M3ExtraCycles(const SpriteMapper &spriteMapper, + const ScxReader &scxReader_in, + const Window &win); + + void invalidateCache() { + std::memset(cycles, CYCLES_INVALID, sizeof(cycles)); + } + + unsigned operator()(const unsigned ly) const { + if (cycles[ly] == CYCLES_INVALID) + updateLine(ly); + + return cycles[ly]; + } +}; + +#endif diff --git a/src/lib/libgambatte/src/video/mode0_irq.cpp b/src/lib/libgambatte/src/video/mode0_irq.cpp new file mode 100644 index 00000000..041d3db1 --- /dev/null +++ b/src/lib/libgambatte/src/video/mode0_irq.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#include "mode0_irq.h" + +#include "ly_counter.h" +#include "lyc_irq.h" +#include "m3_extra_cycles.h" + +Mode0Irq::Mode0Irq(const LyCounter &lyCounter_in, const LycIrq &lycIrq_in, + const M3ExtraCycles &m3ExtraCycles_in, unsigned char &ifReg_in) : + VideoEvent(0), + lyCounter(lyCounter_in), + lycIrq(lycIrq_in), + m3ExtraCycles(m3ExtraCycles_in), + ifReg(ifReg_in) +{ +} + +static unsigned baseCycle(const bool ds) { + return 80 + 169 + ds * 3 + 1 - ds; +} + +void Mode0Irq::doEvent() { + if (lycIrq.time() == DISABLED_TIME || lyCounter.ly() != lycIrq.lycReg()) + ifReg |= 2; + + unsigned long nextTime = lyCounter.time(); + unsigned nextLy = lyCounter.ly() + 1; + + if (nextLy == 144) { + nextLy = 0; + nextTime += lyCounter.lineTime() * 10; + } + + nextTime += (baseCycle(lyCounter.isDoubleSpeed()) + m3ExtraCycles(nextLy)) << lyCounter.isDoubleSpeed(); + + setTime(nextTime); +} + +void Mode0Irq::mode3CyclesChange() { + unsigned long nextTime = lyCounter.time() - lyCounter.lineTime(); + unsigned nextLy = lyCounter.ly(); + + if (time() > lyCounter.time()) { + nextTime += lyCounter.lineTime(); + ++nextLy; + + if (nextLy > 143) { + nextTime += lyCounter.lineTime() * (154 - nextLy); + nextLy = 0; + } + } + + nextTime += (baseCycle(lyCounter.isDoubleSpeed()) + m3ExtraCycles(nextLy)) << lyCounter.isDoubleSpeed(); + + setTime(nextTime); +} + +unsigned long Mode0Irq::schedule(const unsigned statReg, const M3ExtraCycles &m3ExtraCycles, const LyCounter &lyCounter, const unsigned long cycleCounter) { + if (!(statReg & 0x08)) + return DISABLED_TIME; + + unsigned line = lyCounter.ly(); + int next = static_cast(baseCycle(lyCounter.isDoubleSpeed())) - static_cast(lyCounter.lineCycles(cycleCounter)); + + if (line < 144 && next + static_cast(m3ExtraCycles(line)) <= 0) { + next += 456; + ++line; + } + + if (line > 143) { + next += (154 - line) * 456; + line = 0; + } + + next += m3ExtraCycles(line); + + return cycleCounter + (static_cast(next) << lyCounter.isDoubleSpeed()); +} diff --git a/src/lib/libgambatte/src/video/mode0_irq.h b/src/lib/libgambatte/src/video/mode0_irq.h new file mode 100644 index 00000000..bc5f1540 --- /dev/null +++ b/src/lib/libgambatte/src/video/mode0_irq.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_MODE0_IRQ_H +#define VIDEO_MODE0_IRQ_H + +class LycIrq; +class M3ExtraCycles; + +#include "ly_counter.h" + +class Mode0Irq : public VideoEvent { + const LyCounter &lyCounter; + const LycIrq &lycIrq; + const M3ExtraCycles &m3ExtraCycles; + unsigned char &ifReg; + +public: + Mode0Irq(const LyCounter &lyCounter_in, const LycIrq &lycIrq_in, + const M3ExtraCycles &m3ExtraCycles_in, unsigned char &ifReg_in); + + void doEvent(); + void mode3CyclesChange(); + static unsigned long schedule(unsigned statReg, const M3ExtraCycles &m3ExtraCycles, const LyCounter &lyCounter, unsigned long cycleCounter); +}; + +#endif diff --git a/src/lib/libgambatte/src/video/mode1_irq.cpp b/src/lib/libgambatte/src/video/mode1_irq.cpp new file mode 100644 index 00000000..ddafe25c --- /dev/null +++ b/src/lib/libgambatte/src/video/mode1_irq.cpp @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "mode1_irq.h" + +Mode1Irq::Mode1Irq(unsigned char &ifReg_in) : + VideoEvent(0), + ifReg(ifReg_in) +{ + setDoubleSpeed(false); + setM1StatIrqEnabled(false); +} + +void Mode1Irq::doEvent() { + ifReg |= flags; + + setTime(time() + frameTime); +} diff --git a/src/lib/libgambatte/src/video/mode1_irq.h b/src/lib/libgambatte/src/video/mode1_irq.h new file mode 100644 index 00000000..f4e6270f --- /dev/null +++ b/src/lib/libgambatte/src/video/mode1_irq.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_MODE1_IRQ_H +#define VIDEO_MODE1_IRQ_H + +#include "ly_counter.h" +#include "basic_add_event.h" + +class Mode1Irq : public VideoEvent { + unsigned char &ifReg; + unsigned long frameTime; + unsigned char flags; + +public: + Mode1Irq(unsigned char &ifReg_in); + + void doEvent(); + + static unsigned long schedule(const LyCounter &lyCounter, unsigned long cycleCounter) { + return lyCounter.nextFrameCycle(144 * 456, cycleCounter); + } + + void setDoubleSpeed(const bool ds) { + frameTime = 70224 << ds; + } + + void setM1StatIrqEnabled(const bool enabled) { + flags = (enabled * 2) | 1; + } +}; + +static inline void addEvent(event_queue &q, Mode1Irq *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, Mode1Irq *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/src/lib/libgambatte/src/video/mode2_irq.cpp b/src/lib/libgambatte/src/video/mode2_irq.cpp new file mode 100644 index 00000000..b1a419ea --- /dev/null +++ b/src/lib/libgambatte/src/video/mode2_irq.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "mode2_irq.h" + +#include "ly_counter.h" +#include "lyc_irq.h" + +Mode2Irq::Mode2Irq(const LyCounter &lyCounter_in, const LycIrq &lycIrq_in, + unsigned char &ifReg_in) : + VideoEvent(0), + lyCounter(lyCounter_in), + lycIrq(lycIrq_in), + ifReg(ifReg_in) +{ +} + +void Mode2Irq::doEvent() { + const unsigned ly = lyCounter.time() - time() < 8 ? (lyCounter.ly() == 153 ? 0 : lyCounter.ly() + 1) : lyCounter.ly(); + + if (lycIrq.time() == DISABLED_TIME || (lycIrq.lycReg() != 0 && ly != (lycIrq.lycReg() + 1U)) || (lycIrq.lycReg() == 0 && ly > 1)) + ifReg |= 2; + + setTime(time() + lyCounter.lineTime()); + + if (ly == 0) + setTime(time() - 4); + else if (ly == 143) + setTime(time() + lyCounter.lineTime() * 10 + 4); +} + +unsigned long Mode2Irq::schedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned long cycleCounter) { + if ((statReg & 0x28) != 0x20) + return DISABLED_TIME; + + unsigned next = lyCounter.time() - cycleCounter; + + if (lyCounter.ly() >= 143 || (lyCounter.ly() == 142 && next <= 4)) { + next += (153u - lyCounter.ly()) * lyCounter.lineTime(); + } else { + if (next <= 4) + next += lyCounter.lineTime(); + + next -= 4; + } + + return cycleCounter + next; +} diff --git a/src/lib/libgambatte/src/video/mode2_irq.h b/src/lib/libgambatte/src/video/mode2_irq.h new file mode 100644 index 00000000..2ea86055 --- /dev/null +++ b/src/lib/libgambatte/src/video/mode2_irq.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_MODE2_IRQ_H +#define VIDEO_MODE2_IRQ_H + +class LycIrq; + +#include "ly_counter.h" +#include "basic_add_event.h" + +class Mode2Irq : public VideoEvent { + const LyCounter &lyCounter; + const LycIrq &lycIrq; + unsigned char &ifReg; + +public: + Mode2Irq(const LyCounter &lyCounter_in, const LycIrq &lycIrq_in, + unsigned char &ifReg_in); + + void doEvent(); + static unsigned long schedule(unsigned statReg, const LyCounter &lyCounter, unsigned long cycleCounter); +}; + +#endif diff --git a/src/lib/libgambatte/src/video/mode3_event.cpp b/src/lib/libgambatte/src/video/mode3_event.cpp new file mode 100644 index 00000000..84502315 --- /dev/null +++ b/src/lib/libgambatte/src/video/mode3_event.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "mode3_event.h" +#include "mode0_irq.h" +#include "irq_event.h" + +Mode3Event::Mode3Event(event_queue &m3EventQueue_in, + event_queue &vEventQueue_in, + Mode0Irq &mode0Irq_in, IrqEvent &irqEvent_in) : + VideoEvent(1), + m3EventQueue(m3EventQueue_in), + vEventQueue(vEventQueue_in), + mode0Irq(mode0Irq_in), + irqEvent(irqEvent_in) +{ +} + +void Mode3Event::doEvent() { + m3EventQueue.top()->doEvent(); + + if (m3EventQueue.top()->time() == DISABLED_TIME) + m3EventQueue.pop(); + else + m3EventQueue.modify_root(m3EventQueue.top()); + + if (mode0Irq.time() != DISABLED_TIME) { + const unsigned long oldTime = mode0Irq.time(); + mode0Irq.mode3CyclesChange(); + + if (mode0Irq.time() != oldTime) { + // position in irqEventQueue should remain the same. + // The same may be possible for vEventQueue, with some precautions. + if (irqEvent.time() == oldTime) { + irqEvent.schedule(); + + if (mode0Irq.time() > oldTime) + vEventQueue.inc(&irqEvent, &irqEvent); + else + vEventQueue.dec(&irqEvent, &irqEvent); + } + + } + } + + setTime(schedule(m3EventQueue)); +} diff --git a/src/lib/libgambatte/src/video/mode3_event.h b/src/lib/libgambatte/src/video/mode3_event.h new file mode 100644 index 00000000..7f9aedc6 --- /dev/null +++ b/src/lib/libgambatte/src/video/mode3_event.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MODE3_EVENT_H +#define MODE3_EVENT_H + +class Mode0Irq; +class IrqEvent; + +#include "video_event.h" +#include "video_event_comparer.h" +#include "../event_queue.h" + +class Mode3Event : public VideoEvent { + event_queue &m3EventQueue; + event_queue &vEventQueue; + Mode0Irq &mode0Irq; + IrqEvent &irqEvent; + +public: + Mode3Event(event_queue &m3EventQueue_in, + event_queue &vEventQueue_in, + Mode0Irq &mode0Irq_in, IrqEvent &irqEvent_in); + + void doEvent(); + + static unsigned long schedule(const event_queue &m3EventQueue) { + return m3EventQueue.empty() ? static_cast(DISABLED_TIME) : m3EventQueue.top()->time(); + } +}; + +#endif diff --git a/src/lib/libgambatte/src/video/sc_reader.cpp b/src/lib/libgambatte/src/video/sc_reader.cpp new file mode 100644 index 00000000..fff2f66c --- /dev/null +++ b/src/lib/libgambatte/src/video/sc_reader.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "sc_reader.h" + +#include "../event_queue.h" +#include "../savestate.h" + +ScReader::ScReader() : VideoEvent(2) { + setDoubleSpeed(false); + setScxSource(0); + setScySource(0); + scx_[1] = scx_[0] = scxSrc; + scy_[1] = scy_[0] = scySrc; +} + +void ScReader::doEvent() { + scy_[0] = scy_[1]; + scy_[1] = scySrc; + scx_[0] = scx_[1]; + scx_[1] = scxSrc; + + if ((scy_[0] ^ scy_[1]) | (scx_[0] ^ scx_[1])) + setTime(time() + incCycles); + else + setTime(DISABLED_TIME); + +} + +void ScReader::saveState(SaveState &state) const { + state.ppu.scx[0] = scx_[0]; + state.ppu.scx[1] = scx_[1]; + state.ppu.scy[0] = scy_[0]; + state.ppu.scy[1] = scy_[1]; +} + +void ScReader::loadState(const SaveState &state) { + scx_[0] = state.ppu.scx[0]; + scx_[1] = state.ppu.scx[1]; + scy_[0] = state.ppu.scy[0]; + scy_[1] = state.ppu.scy[1]; +} + +void ScReader::setDoubleSpeed(const bool dS_in) { + dS = dS_in; + incCycles = 8u << dS_in; +} diff --git a/src/lib/libgambatte/src/video/sc_reader.h b/src/lib/libgambatte/src/video/sc_reader.h new file mode 100644 index 00000000..0d7ef7d1 --- /dev/null +++ b/src/lib/libgambatte/src/video/sc_reader.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SC_READER_H +#define SC_READER_H + +class SaveState; +template class event_queue; + +#include "video_event.h" +#include "video_event_comparer.h" +#include "basic_add_event.h" + +class ScReader : public VideoEvent { + unsigned char scx_[2]; + unsigned char scy_[2]; + + unsigned char scxSrc; + unsigned char scySrc; + unsigned char incCycles; + bool dS; + +public: + ScReader(); + + void doEvent(); + + unsigned scx() const { + return /*(*/scx_[0]/* & ~0x7) | (scxSrc & 0x7)*/; + } + + unsigned scy() const { + return scy_[0]; + } + + static unsigned long schedule(const unsigned long lastUpdate, const unsigned long videoCycles, const unsigned scReadOffset, const bool dS) { + return lastUpdate + ((8u - ((videoCycles - scReadOffset) & 7)) << dS); + } + + void setDoubleSpeed(bool dS_in); + + void setScxSource(const unsigned scxSrc_in) { + scxSrc = scxSrc_in; + } + + void setScySource(const unsigned scySrc_in) { + scySrc = scySrc_in; + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state); +}; + +static inline void addEvent(event_queue &q, ScReader *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, ScReader *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/src/lib/libgambatte/src/video/scx_reader.cpp b/src/lib/libgambatte/src/video/scx_reader.cpp new file mode 100644 index 00000000..6baa97f9 --- /dev/null +++ b/src/lib/libgambatte/src/video/scx_reader.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "scx_reader.h" + +#include "../event_queue.h" +#include "m3_extra_cycles.h" +#include "../savestate.h" + +ScxReader::ScxReader(event_queue &m3EventQueue_in, +// VideoEvent &wyReader3_in, + VideoEvent &wxReader_in, + VideoEvent &weEnableChecker_in, + VideoEvent &weDisableChecker_in, + M3ExtraCycles &m3ExtraCycles) : + VideoEvent(1), + m3EventQueue(m3EventQueue_in), +// wyReader3(wyReader3_in), + wxReader(wxReader_in), + weEnableChecker(weEnableChecker_in), + weDisableChecker(weDisableChecker_in), + m3ExtraCycles(m3ExtraCycles) +{ + setDoubleSpeed(false); + setSource(0); + scxAnd7_ = src; +} + +static void rescheduleEvent(event_queue &m3EventQueue, VideoEvent& event, const unsigned long diff) { + if (event.time() != VideoEvent::DISABLED_TIME) { + event.setTime(event.time() + diff); + (diff & 0x10) ? m3EventQueue.dec(&event, &event) : m3EventQueue.inc(&event, &event); + } +} + +void ScxReader::doEvent() { + const unsigned long diff = (static_cast(src) - static_cast(scxAnd7_)) << dS; + scxAnd7_ = src; + +// rescheduleEvent(m3EventQueue, wyReader3, diff); + rescheduleEvent(m3EventQueue, wxReader, diff); + rescheduleEvent(m3EventQueue, weEnableChecker, diff); + rescheduleEvent(m3EventQueue, weDisableChecker, diff); + + m3ExtraCycles.invalidateCache(); + + setTime(DISABLED_TIME); +} + +void ScxReader::saveState(SaveState &state) const { + state.ppu.scxAnd7 = scxAnd7_; +} + +void ScxReader::loadState(const SaveState &state) { + scxAnd7_ = state.ppu.scxAnd7; +} diff --git a/src/lib/libgambatte/src/video/scx_reader.h b/src/lib/libgambatte/src/video/scx_reader.h new file mode 100644 index 00000000..f92f8b2b --- /dev/null +++ b/src/lib/libgambatte/src/video/scx_reader.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SCX_READER_H +#define SCX_READER_H + +template class event_queue; +class M3ExtraCycles; +class SaveState; + +#include "video_event.h" +#include "video_event_comparer.h" +#include "ly_counter.h" +#include "basic_add_event.h" + +class ScxReader : public VideoEvent { + event_queue &m3EventQueue; +// VideoEvent &wyReader3; + VideoEvent &wxReader; + VideoEvent &weEnableChecker; + VideoEvent &weDisableChecker; + M3ExtraCycles &m3ExtraCycles; + + unsigned char scxAnd7_; + unsigned char src; + bool dS; + +public: + ScxReader(event_queue &m3EventQueue_in, +// VideoEvent &wyReader3_in, + VideoEvent &wxReader_in, + VideoEvent &weEnableChecker_in, + VideoEvent &weDisableChecker_in, + M3ExtraCycles &m3ExtraCycles); + + void doEvent(); + + unsigned getSource() const { + return src; + } + + static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.nextLineCycle(82 + lyCounter.isDoubleSpeed() * 3, cycleCounter); + } + + unsigned scxAnd7() const { + return scxAnd7_; + } + + void setDoubleSpeed(const bool dS_in) { + dS = dS_in; + } + + void setSource(const unsigned scxSrc) { + src = scxSrc & 7; + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state); +}; + +static inline void addEvent(event_queue &q, ScxReader *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, ScxReader *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/src/lib/libgambatte/src/video/sprite_mapper.cpp b/src/lib/libgambatte/src/video/sprite_mapper.cpp new file mode 100644 index 00000000..f1e9cd97 --- /dev/null +++ b/src/lib/libgambatte/src/video/sprite_mapper.cpp @@ -0,0 +1,187 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "sprite_mapper.h" +#include "m3_extra_cycles.h" +#include "../insertion_sort.h" +#include + +#include + +SpriteMapper::OamReader::OamReader(const LyCounter &lyCounter, const unsigned char *oamram) +: lyCounter(lyCounter), oamram(oamram) { + reset(oamram); +} + +void SpriteMapper::OamReader::reset(const unsigned char *const oamram) { + this->oamram = oamram; + setLargeSpritesSrc(false); + lu = 0; + lastChange = 0xFF; + std::fill_n(szbuf, 40, largeSpritesSrc); + + unsigned pos = 0; + unsigned distance = 80; + + while (distance--) { + buf[pos] = oamram[((pos * 2) & ~3) | (pos & 1)]; + ++pos; + } +} + +static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter) { + unsigned lc = lyCounter.lineCycles(cc) + 4 - lyCounter.isDoubleSpeed() * 3u; + + if (lc >= 456) + lc -= 456; + + return lc >> 1; +} + +void SpriteMapper::OamReader::update(const unsigned long cc) { + if (cc > lu) { + if (changed()) { + const unsigned lulc = toPosCycles(lu, lyCounter); + + unsigned pos = std::min(lulc, 40u); + unsigned distance = 40; + + if ((cc - lu) >> lyCounter.isDoubleSpeed() < 456) { + const unsigned cclc = toPosCycles(cc, lyCounter); + + distance = std::min(cclc, 40u) - pos + (cclc < lulc ? 40 : 0); + } + + { + const unsigned targetDistance = lastChange - pos + (lastChange <= pos ? 40 : 0); + + if (targetDistance <= distance) { + distance = targetDistance; + lastChange = 0xFF; + } + } + + while (distance--) { + if (pos >= 40) + pos = 0; + + szbuf[pos] = largeSpritesSrc; + buf[pos * 2] = oamram[pos * 4]; + buf[pos * 2 + 1] = oamram[pos * 4 + 1]; + + ++pos; + } + } + + lu = cc; + } +} + +void SpriteMapper::OamReader::change(const unsigned long cc) { + update(cc); + lastChange = std::min(toPosCycles(lu, lyCounter), 40u); +} + +void SpriteMapper::OamReader::setStatePtrs(SaveState &state) { + state.ppu.oamReaderBuf.set(buf, sizeof buf); + state.ppu.oamReaderSzbuf.set(szbuf, sizeof(szbuf) / sizeof(bool)); +} + +void SpriteMapper::OamReader::enableDisplay(const unsigned long cc) { + std::memset(buf, 0x00, sizeof(buf)); + std::fill(szbuf, szbuf + 40, false); + lu = cc + 160; + lastChange = 40; +} + +bool SpriteMapper::OamReader::oamAccessible(const unsigned long cycleCounter, const M3ExtraCycles &m3ExtraCycles) const { + unsigned ly = lyCounter.ly(); + unsigned lc = lyCounter.lineCycles(cycleCounter) + 4 - lyCounter.isDoubleSpeed() * 3u; + + if (lc >= 456) { + lc -= 456; + ++ly; + } + + return cycleCounter < lu || ly >= 144 || lc >= 80 + 173 + m3ExtraCycles(ly); +} + +SpriteMapper::SpriteMapper(M3ExtraCycles &m3ExtraCycles, + const LyCounter &lyCounter, + const unsigned char *const oamram) : + VideoEvent(2), + m3ExtraCycles(m3ExtraCycles), + oamReader(lyCounter, oamram), + cgb(false) +{ + clearMap(); +} + +void SpriteMapper::reset(const unsigned char *const oamram, const bool cgb_in) { + oamReader.reset(oamram); + cgb = cgb_in; + clearMap(); +} + +void SpriteMapper::clearMap() { + std::memset(num, cgb ? 0 : NEED_SORTING_MASK, sizeof(num)); +} + +void SpriteMapper::mapSprites() { + clearMap(); + + for (unsigned i = 0x00; i < 0x50; i += 2) { + const unsigned spriteHeight = 8u << largeSprites(i >> 1); + const unsigned bottom_pos = posbuf()[i] - (17u - spriteHeight); + + if (bottom_pos >= 143 + spriteHeight) + continue; + + unsigned char *map = spritemap; + unsigned char *n = num; + + if (bottom_pos >= spriteHeight) { + const unsigned startly = bottom_pos + 1 - spriteHeight; + n += startly; + map += startly * 10; + } + + unsigned char *const end = num + (bottom_pos >= 143 ? 143 : bottom_pos); + + do { + if ((*n & ~NEED_SORTING_MASK) < 10) + map[(*n)++ & ~NEED_SORTING_MASK] = i; + + map += 10; + ++n; + } while (n <= end); + } + + m3ExtraCycles.invalidateCache(); +} + +void SpriteMapper::sortLine(const unsigned ly) const { + num[ly] &= ~NEED_SORTING_MASK; + insertionSort(spritemap + ly * 10, spritemap + ly * 10 + num[ly], SpxLess(posbuf())); +} + +void SpriteMapper::doEvent() { + oamReader.update(time()); + mapSprites(); + setTime(oamReader.changed() ? time() + oamReader.lyCounter.lineTime() : static_cast(DISABLED_TIME)); +} diff --git a/src/lib/libgambatte/src/video/sprite_mapper.h b/src/lib/libgambatte/src/video/sprite_mapper.h new file mode 100644 index 00000000..25b8090b --- /dev/null +++ b/src/lib/libgambatte/src/video/sprite_mapper.h @@ -0,0 +1,148 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SPRITE_MAPPER_H +#define SPRITE_MAPPER_H + +#include "video_event.h" +//#include "video_event_comparer.h" +#include "ly_counter.h" +#include "basic_add_event.h" +#include "../savestate.h" + +class M3ExtraCycles; +class SaveState; + +class SpriteMapper : public VideoEvent { + class OamReader { + unsigned char buf[80]; + bool szbuf[40]; + + public: + const LyCounter &lyCounter; + + private: + const unsigned char *oamram; + unsigned long lu; + unsigned char lastChange; + bool largeSpritesSrc; + + public: + OamReader(const LyCounter &lyCounter, const unsigned char *oamram); + void reset(const unsigned char *oamram); + void change(unsigned long cc); + void change(const unsigned char *oamram, unsigned long cc) { change(cc); this->oamram = oamram; } + bool changed() const { return lastChange != 0xFF; } + bool largeSprites(unsigned spNr) const { return szbuf[spNr]; } + const unsigned char *oam() const { return oamram; } + void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) { lu = lu + newCc - oldCc; } + void setLargeSpritesSrc(const bool src) { largeSpritesSrc = src; } + void update(unsigned long cc); + const unsigned char *spritePosBuf() const { return buf; } + void setStatePtrs(SaveState &state); + void enableDisplay(unsigned long cc); + void saveState(SaveState &state) const { state.ppu.enableDisplayM0Time = lu; } + void loadState(const SaveState &state) { lu = state.ppu.enableDisplayM0Time; } + void resetVideoState() { change(lu); } + bool oamAccessible(unsigned long cycleCounter, const M3ExtraCycles &m3ExtraCycles) const; + bool inactivePeriodAfterDisplayEnable(const unsigned long cc) const { return cc < lu; } + }; + + enum { NEED_SORTING_MASK = 0x80 }; + +public: + class SpxLess { + const unsigned char *const posbuf_plus1; + + public: + SpxLess(const unsigned char *const posbuf) : posbuf_plus1(posbuf + 1) {} + + bool operator()(const unsigned char l, const unsigned char r) const { + return posbuf_plus1[l] < posbuf_plus1[r]; + } + }; + +private: + mutable unsigned char spritemap[144*10]; + mutable unsigned char num[144]; + + M3ExtraCycles &m3ExtraCycles; + OamReader oamReader; + + bool cgb; + + void clearMap(); + void mapSprites(); + void sortLine(unsigned ly) const; + +public: + SpriteMapper(M3ExtraCycles &m3ExtraCycles, + const LyCounter &lyCounter, + const unsigned char *oamram_in); + void reset(const unsigned char *oamram, bool cgb_in); + void doEvent(); + bool isCgb() const { return cgb; } + bool largeSprites(unsigned spNr) const { return oamReader.largeSprites(spNr); } + unsigned numSprites(const unsigned ly) const { return num[ly] & ~NEED_SORTING_MASK; } + void oamChange(const unsigned long cc) { oamReader.change(cc); } + void oamChange(const unsigned char *oamram, const unsigned long cc) { oamReader.change(oamram, cc); } + const unsigned char *oamram() const { return oamReader.oam(); } + const unsigned char *posbuf() const { return oamReader.spritePosBuf(); } + void preCounterChange(const unsigned long cc) { oamReader.update(cc); } + + void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) { + oamReader.resetCycleCounter(oldCc, newCc); + } + + static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.nextLineCycle(80, cycleCounter); + } + + void setLargeSpritesSource(const bool src) { oamReader.setLargeSpritesSrc(src); } + + const unsigned char* sprites(const unsigned ly) const { + if (num[ly] & NEED_SORTING_MASK) + sortLine(ly); + + return spritemap + ly * 10; + } + + void setStatePtrs(SaveState &state) { oamReader.setStatePtrs(state); } + void enableDisplay(unsigned long cc) { oamReader.enableDisplay(cc); } + void saveState(SaveState &state) const { oamReader.saveState(state); } + void loadState(const SaveState &state) { oamReader.loadState(state); } + void resetVideoState() { oamReader.resetVideoState(); } + + bool oamAccessible(unsigned long cycleCounter) const { + return oamReader.oamAccessible(cycleCounter, m3ExtraCycles); + } + + bool inactivePeriodAfterDisplayEnable(const unsigned long cc) const { + return oamReader.inactivePeriodAfterDisplayEnable(cc); + } +}; + +static inline void addEvent(event_queue &q, SpriteMapper *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, SpriteMapper *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/src/lib/libgambatte/src/video/video_event.h b/src/lib/libgambatte/src/video/video_event.h new file mode 100644 index 00000000..fb64d5b2 --- /dev/null +++ b/src/lib/libgambatte/src/video/video_event.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_EVENT_H +#define VIDEO_EVENT_H + +class VideoEvent { + unsigned long time_; + const unsigned char priority_; + +public: + enum { DISABLED_TIME = 0xFFFFFFFFu }; + + VideoEvent(const unsigned priority_in) : + time_(DISABLED_TIME), + priority_(priority_in) + {} + + virtual ~VideoEvent() {} + virtual void doEvent() = 0; + + unsigned priority() const { + return priority_; + } + + unsigned long time() const { + return time_; + } + + void setTime(const unsigned long time_in) { + time_ = time_in; + } +}; + +#endif diff --git a/src/lib/libgambatte/src/video/video_event_comparer.h b/src/lib/libgambatte/src/video/video_event_comparer.h new file mode 100644 index 00000000..4eb25969 --- /dev/null +++ b/src/lib/libgambatte/src/video/video_event_comparer.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_EVENT_COMPARER_H +#define VIDEO_EVENT_COMPARER_H + +#include "video_event.h" + +class VideoEventComparer { +public: + bool less(const VideoEvent *const a, const VideoEvent *const b) const { + return a->time() < b->time() || (a->time() == b->time() && a->priority() < b->priority()); + } +}; + +#endif diff --git a/src/lib/libgambatte/src/video/we.cpp b/src/lib/libgambatte/src/video/we.cpp new file mode 100644 index 00000000..d5e66c47 --- /dev/null +++ b/src/lib/libgambatte/src/video/we.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "we.h" +#include "../savestate.h" + +We::WeEnableChecker::WeEnableChecker(We &we) : + VideoEvent(8), + we(we) +{} + +void We::WeEnableChecker::doEvent() { + we.set(we.src_); + + setTime(DISABLED_TIME); +} + +We::WeDisableChecker::WeDisableChecker(We &we) : + VideoEvent(9), + we(we) +{} + +void We::WeDisableChecker::doEvent() { + we.set(we.we_ & we.src_); + + setTime(DISABLED_TIME); +} + +We::We(M3ExtraCycles &m3ExtraCycles) : + m3ExtraCycles_(m3ExtraCycles), + enableChecker_(*this), + disableChecker_(*this) +{ + setSource(false); + we_ = src_; +} + +void We::saveState(SaveState &state) const { + state.ppu.lcdc = (state.ppu.lcdc & ~0x20) | we_ << 5; +} + +void We::loadState(const SaveState &state) { + we_ = state.ppu.lcdc >> 5 & 1; +} diff --git a/src/lib/libgambatte/src/video/we.h b/src/lib/libgambatte/src/video/we.h new file mode 100644 index 00000000..d800ca11 --- /dev/null +++ b/src/lib/libgambatte/src/video/we.h @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef WE_H +#define WE_H + +class SaveState; + +#include "video_event.h" +#include "ly_counter.h" +#include "m3_extra_cycles.h" +#include "basic_add_event.h" + +class We { +public: + class WeEnableChecker : public VideoEvent { + We &we; + + public: + WeEnableChecker(We &we); + + void doEvent(); + + static unsigned long schedule(const unsigned scxAnd7, const unsigned wx, const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.nextLineCycle(scxAnd7 + 82 + wx + lyCounter.isDoubleSpeed() * 3, cycleCounter); + } + }; + + class WeDisableChecker : public VideoEvent { + We &we; + + public: + WeDisableChecker(We &we); + + void doEvent(); + + static unsigned long schedule(const unsigned scxAnd7, const unsigned wx, const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.nextLineCycle(scxAnd7 + 88 + wx + lyCounter.isDoubleSpeed() * 3, cycleCounter); + } + }; + + friend class WeEnableChecker; + friend class WeDisableChecker; + +private: + M3ExtraCycles &m3ExtraCycles_; + WeEnableChecker enableChecker_; + WeDisableChecker disableChecker_; + + bool we_; + bool src_; + + void set(const bool value) { + if (we_ != value) + m3ExtraCycles_.invalidateCache(); + + we_ = value; + } + +public: + We(M3ExtraCycles &m3ExtraCycles); + + WeDisableChecker& disableChecker() { + return disableChecker_; + } + + WeEnableChecker& enableChecker() { + return enableChecker_; + } + + bool getSource() const { + return src_; + } + + void setSource(const bool src) { + src_ = src; + } + + bool value() const { + return we_; + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state); +}; + +static inline void addEvent(event_queue &q, We::WeEnableChecker *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, We::WeEnableChecker *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +static inline void addEvent(event_queue &q, We::WeDisableChecker *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, We::WeDisableChecker *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/src/lib/libgambatte/src/video/we_master_checker.cpp b/src/lib/libgambatte/src/video/we_master_checker.cpp new file mode 100644 index 00000000..bff81585 --- /dev/null +++ b/src/lib/libgambatte/src/video/we_master_checker.cpp @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "we_master_checker.h" + +#include "event_queue.h" +#include "wy.h" +#include "basic_add_event.h" +#include "../savestate.h" + +WeMasterChecker::WeMasterChecker(event_queue &m3EventQueue_in, + Wy &wy_in, + const LyCounter &lyCounter_in, + M3ExtraCycles &m3ExtraCycles) : + VideoEvent(10), + m3EventQueue(m3EventQueue_in), + wy(wy_in), + lyCounter(lyCounter_in), + m3ExtraCycles(m3ExtraCycles) +{ + weMaster_ = false; +} + +void WeMasterChecker::doEvent() { +// if (wy.value() >= lyCounter.ly()) { + if (!weMaster_ /*&& src */&& wy.value() == lyCounter.ly()) { + wy.weirdAssWeMasterEnableOnWyLineCase(); + addEvent(m3EventQueue, &wy.reader4(), Wy::WyReader4::schedule(lyCounter, time())); + } + + set(true); +// } + + setTime(time() + (70224U << lyCounter.isDoubleSpeed())); +} + +void WeMasterChecker::saveState(SaveState &state) const { + state.ppu.weMaster = weMaster_; +} + +void WeMasterChecker::loadState(const SaveState &state) { + weMaster_ = state.ppu.weMaster; +} diff --git a/src/lib/libgambatte/src/video/we_master_checker.h b/src/lib/libgambatte/src/video/we_master_checker.h new file mode 100644 index 00000000..cf1f1209 --- /dev/null +++ b/src/lib/libgambatte/src/video/we_master_checker.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef WE_MASTER_CHECKER_H +#define WE_MASTER_CHECKER_H + +template class event_queue; +class Wy; +class SaveState; + +#include "video_event.h" +#include "video_event_comparer.h" +#include "ly_counter.h" +#include "m3_extra_cycles.h" + +class WeMasterChecker : public VideoEvent { + event_queue &m3EventQueue; + Wy &wy; + const LyCounter &lyCounter; + M3ExtraCycles &m3ExtraCycles; + + bool weMaster_; + + void set(const bool value) { + if (weMaster_ != value) + m3ExtraCycles.invalidateCache(); + + weMaster_ = value; + } + +public: + WeMasterChecker(event_queue &m3EventQueue_in, + Wy &wy_in, + const LyCounter &lyCounter_in, + M3ExtraCycles &m3ExtraCycles); + + void doEvent(); + + static unsigned long schedule(const unsigned wySrc, const bool weSrc, const LyCounter &lyCounter, const unsigned long cycleCounter) { + if (weSrc && wySrc < 143) + return lyCounter.nextFrameCycle(wySrc * 456ul + 448 + lyCounter.isDoubleSpeed() * 4, cycleCounter); + else + return DISABLED_TIME; + } + + void unset() { + set(false); + } + + bool weMaster() const { + return weMaster_; + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state); +}; + +#endif diff --git a/src/lib/libgambatte/src/video/window.h b/src/lib/libgambatte/src/video/window.h new file mode 100644 index 00000000..790d612c --- /dev/null +++ b/src/lib/libgambatte/src/video/window.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef WINDOW_H +#define WINDOW_H + +#include "we.h" +#include "we_master_checker.h" +#include "wy.h" +#include "wx_reader.h" + +struct Window { + We we; + WeMasterChecker weMasterChecker; + Wy wyReg; + WxReader wxReader; + + Window(event_queue &m3EventQueue, + const LyCounter &lyCounter, + M3ExtraCycles &m3ExtraCycles) : + we(m3ExtraCycles), + weMasterChecker(m3EventQueue, wyReg, lyCounter, m3ExtraCycles), + wyReg(lyCounter, weMasterChecker, m3ExtraCycles), + wxReader(m3EventQueue, we.enableChecker(), we.disableChecker(), m3ExtraCycles) + {} + + bool enabled(const unsigned ly) const { + return we.value() && wxReader.wx() < 0xA7 && ly >= wyReg.value() && (weMasterChecker.weMaster() || ly == wyReg.value()); + } +}; + +#endif /*WINDOW_H*/ diff --git a/src/lib/libgambatte/src/video/wx_reader.cpp b/src/lib/libgambatte/src/video/wx_reader.cpp new file mode 100644 index 00000000..80a6b640 --- /dev/null +++ b/src/lib/libgambatte/src/video/wx_reader.cpp @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "wx_reader.h" + +#include "../event_queue.h" +#include "m3_extra_cycles.h" +#include "../savestate.h" + +WxReader::WxReader(event_queue &m3EventQueue, + VideoEvent &weEnableChecker, + VideoEvent &weDisableChecker, + M3ExtraCycles &m3ExtraCycles) : +VideoEvent(7), +m3EventQueue(m3EventQueue), +weEnableChecker(weEnableChecker), +weDisableChecker(weDisableChecker), +m3ExtraCycles(m3ExtraCycles) +{ + setDoubleSpeed(false); + setSource(0); + wx_ = src_; +} + +static void rescheduleEvent(event_queue &m3EventQueue, VideoEvent& event, const unsigned long diff) { + if (event.time() != VideoEvent::DISABLED_TIME) { + event.setTime(event.time() + diff); + (diff & 0x200) ? m3EventQueue.dec(&event, &event) : m3EventQueue.inc(&event, &event); + } +} + +void WxReader::doEvent() { + const unsigned long diff = (static_cast(src_) - static_cast(wx_)) << dS; + wx_ = src_; + + rescheduleEvent(m3EventQueue, weEnableChecker, diff); + rescheduleEvent(m3EventQueue, weDisableChecker, diff); + + m3ExtraCycles.invalidateCache(); + + setTime(DISABLED_TIME); +} + +void WxReader::saveState(SaveState &state) const { + state.ppu.wx = wx_; +} + +void WxReader::loadState(const SaveState &state) { + wx_ = state.ppu.wx; +} diff --git a/src/lib/libgambatte/src/video/wx_reader.h b/src/lib/libgambatte/src/video/wx_reader.h new file mode 100644 index 00000000..1681f8a4 --- /dev/null +++ b/src/lib/libgambatte/src/video/wx_reader.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef WX_READER_H +#define WX_READER_H + +template class event_queue; +class M3ExtraCycles; +class SaveState; + +#include "video_event.h" +#include "video_event_comparer.h" +#include "ly_counter.h" +#include "basic_add_event.h" +#include + +class WxReader : public VideoEvent { + event_queue &m3EventQueue; + VideoEvent &weEnableChecker; + VideoEvent &weDisableChecker; + M3ExtraCycles &m3ExtraCycles; + + unsigned char wx_; + unsigned char src_; + bool dS; + +public: + WxReader(event_queue &m3EventQueue_in, + VideoEvent &weEnableChecker_in, + VideoEvent &weDisableChecker_in, + M3ExtraCycles &m3ExtraCycles); + + void doEvent(); + + unsigned getSource() const { + return src_; + } + + unsigned wx() const { + return wx_; + } + + void setDoubleSpeed(const bool dS_in) { + dS = dS_in; + } + + void setSource(const unsigned src) { + src_ = src; + } + + static unsigned long schedule(const unsigned scxAnd7, const LyCounter &lyCounter, const WxReader &wxReader, const unsigned long cycleCounter) { + return lyCounter.nextLineCycle(scxAnd7 + 82 + lyCounter.isDoubleSpeed() * 3 + std::min(wxReader.getSource(), wxReader.wx()), cycleCounter); + //setTime(lyCounter.nextLineCycle(scxAnd7 + 89 + lyCounter.isDoubleSpeed() * 3, cycleCounter)); + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state); +}; + +static inline void addEvent(event_queue &q, WxReader *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, WxReader *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/src/lib/libgambatte/src/video/wy.cpp b/src/lib/libgambatte/src/video/wy.cpp new file mode 100644 index 00000000..64a5f725 --- /dev/null +++ b/src/lib/libgambatte/src/video/wy.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "wy.h" + +#include "we_master_checker.h" +#include "scx_reader.h" +#include "../event_queue.h" +#include "../savestate.h" + +Wy::WyReader1::WyReader1(Wy &wy, const WeMasterChecker &weMasterChecker) : + VideoEvent(3), + wy(wy), + weMasterChecker(weMasterChecker) +{} + +void Wy::WyReader1::doEvent() { + if (wy.src_ >= wy.lyCounter.ly() && /*wy >= lyCounter.ly()*/ !weMasterChecker.weMaster()) + wy.set(wy.src_); + + setTime(DISABLED_TIME); +} + +Wy::WyReader2::WyReader2(Wy &wy) : + VideoEvent(4), + wy(wy) +{} + +void Wy::WyReader2::doEvent() { + if (wy.wy_ == wy.lyCounter.ly() + 1 - wy.lyCounter.isDoubleSpeed() && wy.src_ > wy.wy_) + wy.set(wy.src_); + + setTime(DISABLED_TIME); +} + +Wy::WyReader3::WyReader3(Wy &wy) : + VideoEvent(5), + wy(wy) +{} + +void Wy::WyReader3::doEvent() { + if (wy.src_ == wy.lyCounter.ly() && wy.wy_ > wy.lyCounter.ly()) + wy.set(wy.src_); + + setTime(DISABLED_TIME); +} + +unsigned long Wy::WyReader3::schedule(const unsigned wxSrc, const ScxReader &scxReader, const LyCounter &lyCounter, const unsigned long cycleCounter) { + const unsigned curLineCycle = 456 - ((lyCounter.time() - cycleCounter) >> lyCounter.isDoubleSpeed()); + const unsigned baseTime = 78 + lyCounter.isDoubleSpeed() * 6 + wxSrc; + + if (curLineCycle >= 82U + lyCounter.isDoubleSpeed() * 3) { + if (baseTime + scxReader.scxAnd7() > curLineCycle) + return lyCounter.time() + ((baseTime + scxReader.scxAnd7()) << lyCounter.isDoubleSpeed()) - lyCounter.lineTime(); + else + return lyCounter.time() + ((baseTime + scxReader.getSource()) << lyCounter.isDoubleSpeed()); + } else + return lyCounter.nextLineCycle(baseTime + scxReader.getSource(), cycleCounter); +} + +Wy::WyReader4::WyReader4(Wy &wy) : + VideoEvent(6), + wy(wy) +{} + +void Wy::WyReader4::doEvent() { + wy.set(wy.src_); + + setTime(DISABLED_TIME); +} + +Wy::Wy(const LyCounter &lyCounter, const WeMasterChecker &weMasterChecker, M3ExtraCycles &m3ExtraCycles) : + lyCounter(lyCounter), + m3ExtraCycles(m3ExtraCycles), + reader1_(*this, weMasterChecker), + reader2_(*this), + reader3_(*this), + reader4_(*this) +{ + setSource(0); + wy_ = src_; +} + +void Wy::saveState(SaveState &state) const { + state.ppu.wy = wy_; +} + +void Wy::loadState(const SaveState &state) { + wy_ = state.ppu.wy; +} diff --git a/src/lib/libgambatte/src/video/wy.h b/src/lib/libgambatte/src/video/wy.h new file mode 100644 index 00000000..2a1033f9 --- /dev/null +++ b/src/lib/libgambatte/src/video/wy.h @@ -0,0 +1,187 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef WY_H +#define WY_H + +class WeMasterChecker; +class ScxReader; +template class event_queue; +class SaveState; + +#include "video_event.h" +#include "video_event_comparer.h" +#include "ly_counter.h" +#include "m3_extra_cycles.h" +#include "basic_add_event.h" + +class Wy { +public: + class WyReader1 : public VideoEvent { + Wy &wy; + const WeMasterChecker &weMasterChecker; + + public: + WyReader1(Wy &wy, const WeMasterChecker &weMasterChecker); + + void doEvent(); + + static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.nextLineCycle(448 + lyCounter.isDoubleSpeed() * 4, cycleCounter); + } + }; + + class WyReader2 : public VideoEvent { + Wy &wy; + + public: + WyReader2(Wy &wy); + + void doEvent(); + + static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.isDoubleSpeed() ? lyCounter.time() : lyCounter.nextLineCycle(452, cycleCounter); + } + }; + + class WyReader3 : public VideoEvent { + Wy &wy; + + public: + WyReader3(Wy &wy); + + void doEvent(); + static unsigned long schedule(unsigned wxSrc, const ScxReader &scxReader, const LyCounter &lyCounter, unsigned long cycleCounter); + + //void schedule(const unsigned scxAnd7, const LyCounter &lyCounter, const unsigned cycleCounter) { + // setTime(lyCounter.nextLineCycle(scxAnd7 + 85 + lyCounter.isDoubleSpeed() * 6, cycleCounter)); + //} + }; + + class WyReader4 : public VideoEvent { + Wy &wy; + + public: + WyReader4(Wy &wy); + + void doEvent(); + + static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.nextFrameCycle(lyCounter.isDoubleSpeed() * 4, cycleCounter); + } + }; + + friend class WyReader1; + friend class WyReader2; + friend class WyReader3; + friend class WyReader4; + +private: + const LyCounter &lyCounter; + M3ExtraCycles &m3ExtraCycles; + WyReader1 reader1_; + WyReader2 reader2_; + WyReader3 reader3_; + WyReader4 reader4_; + + unsigned char wy_; + unsigned char src_; + + void set(const unsigned char value) { + if (wy_ != value) + m3ExtraCycles.invalidateCache(); + + wy_ = value; + } + +public: + Wy(const LyCounter &lyCounter, const WeMasterChecker &weMasterChecker, M3ExtraCycles &m3ExtraCycles); + + WyReader1& reader1() { + return reader1_; + } + + WyReader2& reader2() { + return reader2_; + } + + WyReader3& reader3() { + return reader3_; + } + + WyReader4& reader4() { + return reader4_; + } + + unsigned getSource() const { + return src_; + } + + void setSource(const unsigned src) { + src_ = src; + } + + //void setValue(const unsigned val) { + // wy_ = val; + //} + + unsigned value() const { + return wy_; + } + + void weirdAssWeMasterEnableOnWyLineCase() { + set(wy_ + 1); + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state); +}; + +static inline void addEvent(event_queue &q, Wy::WyReader1 *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, Wy::WyReader1 *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +static inline void addEvent(event_queue &q, Wy::WyReader2 *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, Wy::WyReader2 *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +static inline void addEvent(event_queue &q, Wy::WyReader3 *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, Wy::WyReader3 *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +static inline void addEvent(event_queue &q, Wy::WyReader4 *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, Wy::WyReader4 *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/src/reader/jma/7z.h b/src/lib/libjma/7z.h similarity index 100% rename from src/reader/jma/7z.h rename to src/lib/libjma/7z.h diff --git a/src/reader/jma/7zlzma.cpp b/src/lib/libjma/7zlzma.cpp similarity index 100% rename from src/reader/jma/7zlzma.cpp rename to src/lib/libjma/7zlzma.cpp diff --git a/src/reader/jma/aribitcd.h b/src/lib/libjma/aribitcd.h similarity index 100% rename from src/reader/jma/aribitcd.h rename to src/lib/libjma/aribitcd.h diff --git a/src/reader/jma/ariconst.h b/src/lib/libjma/ariconst.h similarity index 100% rename from src/reader/jma/ariconst.h rename to src/lib/libjma/ariconst.h diff --git a/src/reader/jma/ariprice.h b/src/lib/libjma/ariprice.h similarity index 100% rename from src/reader/jma/ariprice.h rename to src/lib/libjma/ariprice.h diff --git a/src/reader/jma/btreecd.h b/src/lib/libjma/btreecd.h similarity index 100% rename from src/reader/jma/btreecd.h rename to src/lib/libjma/btreecd.h diff --git a/src/reader/jma/crc32.h b/src/lib/libjma/crc32.h similarity index 100% rename from src/reader/jma/crc32.h rename to src/lib/libjma/crc32.h diff --git a/src/reader/jma/iiostrm.cpp b/src/lib/libjma/iiostrm.cpp similarity index 100% rename from src/reader/jma/iiostrm.cpp rename to src/lib/libjma/iiostrm.cpp diff --git a/src/reader/jma/iiostrm.h b/src/lib/libjma/iiostrm.h similarity index 100% rename from src/reader/jma/iiostrm.h rename to src/lib/libjma/iiostrm.h diff --git a/src/reader/jma/inbyte.cpp b/src/lib/libjma/inbyte.cpp similarity index 100% rename from src/reader/jma/inbyte.cpp rename to src/lib/libjma/inbyte.cpp diff --git a/src/reader/jma/inbyte.h b/src/lib/libjma/inbyte.h similarity index 100% rename from src/reader/jma/inbyte.h rename to src/lib/libjma/inbyte.h diff --git a/src/reader/jma/jcrc32.cpp b/src/lib/libjma/jcrc32.cpp similarity index 100% rename from src/reader/jma/jcrc32.cpp rename to src/lib/libjma/jcrc32.cpp diff --git a/src/reader/jma/jma.cpp b/src/lib/libjma/jma.cpp similarity index 100% rename from src/reader/jma/jma.cpp rename to src/lib/libjma/jma.cpp diff --git a/src/reader/jma/jma.h b/src/lib/libjma/jma.h similarity index 100% rename from src/reader/jma/jma.h rename to src/lib/libjma/jma.h diff --git a/src/reader/jma/lencoder.h b/src/lib/libjma/lencoder.h similarity index 100% rename from src/reader/jma/lencoder.h rename to src/lib/libjma/lencoder.h diff --git a/src/reader/jma/litcoder.h b/src/lib/libjma/litcoder.h similarity index 100% rename from src/reader/jma/litcoder.h rename to src/lib/libjma/litcoder.h diff --git a/src/reader/jma/lzma.cpp b/src/lib/libjma/lzma.cpp similarity index 100% rename from src/reader/jma/lzma.cpp rename to src/lib/libjma/lzma.cpp diff --git a/src/reader/jma/lzma.h b/src/lib/libjma/lzma.h similarity index 100% rename from src/reader/jma/lzma.h rename to src/lib/libjma/lzma.h diff --git a/src/reader/jma/lzmadec.cpp b/src/lib/libjma/lzmadec.cpp similarity index 100% rename from src/reader/jma/lzmadec.cpp rename to src/lib/libjma/lzmadec.cpp diff --git a/src/reader/jma/lzmadec.h b/src/lib/libjma/lzmadec.h similarity index 100% rename from src/reader/jma/lzmadec.h rename to src/lib/libjma/lzmadec.h diff --git a/src/reader/jma/portable.h b/src/lib/libjma/portable.h similarity index 100% rename from src/reader/jma/portable.h rename to src/lib/libjma/portable.h diff --git a/src/reader/jma/rcdefs.h b/src/lib/libjma/rcdefs.h similarity index 100% rename from src/reader/jma/rcdefs.h rename to src/lib/libjma/rcdefs.h diff --git a/src/reader/jma/rngcoder.h b/src/lib/libjma/rngcoder.h similarity index 100% rename from src/reader/jma/rngcoder.h rename to src/lib/libjma/rngcoder.h diff --git a/src/reader/jma/winout.cpp b/src/lib/libjma/winout.cpp similarity index 100% rename from src/reader/jma/winout.cpp rename to src/lib/libjma/winout.cpp diff --git a/src/reader/jma/winout.h b/src/lib/libjma/winout.h similarity index 100% rename from src/reader/jma/winout.h rename to src/lib/libjma/winout.h diff --git a/src/lib/nall/datasource.hpp b/src/lib/nall/datasource.hpp new file mode 100644 index 00000000..f7529de0 --- /dev/null +++ b/src/lib/nall/datasource.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_DATASOURCE_HPP +#define NALL_DATASOURCE_HPP + +#include +#include +#include +#include + +namespace nall { + struct datasource : public property { + enum source_t { disk, memory }; + + property_t source; + property_t name; + property_t data; + property_t size; + + datasource(const char *name_) { + set(source, disk); + set(name, strdup(name_)); + set(data, (uint8_t*)0); + set(size, file::size(name_)); + } + + datasource(uint8_t *data_, unsigned size_) { + set(source, memory); + set(name, (char*)0); + set(data, data_); + set(size, size_); + } + + ~datasource() { + if(name()) free(name()); + } + }; +} + +#endif diff --git a/src/lib/nall/file.hpp b/src/lib/nall/file.hpp index dc4da3a6..5ebaeb4b 100644 --- a/src/lib/nall/file.hpp +++ b/src/lib/nall/file.hpp @@ -130,6 +130,21 @@ namespace nall { return false; } + static unsigned size(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + unsigned filesize = 0; + if(fp) { + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fclose(fp); + } + return filesize; + } + bool open() { return fp; } diff --git a/src/lib/nall/platform.hpp b/src/lib/nall/platform.hpp index bdae675c..1cc74bea 100644 --- a/src/lib/nall/platform.hpp +++ b/src/lib/nall/platform.hpp @@ -20,6 +20,7 @@ #include #include #include + #undef interface #else #include #include diff --git a/src/lib/nall/utf8.hpp b/src/lib/nall/utf8.hpp index cae4e1ba..5b7316dc 100644 --- a/src/lib/nall/utf8.hpp +++ b/src/lib/nall/utf8.hpp @@ -11,6 +11,7 @@ #undef NOMINMAX #define NOMINMAX #include +#undef interface namespace nall { //UTF-8 to UTF-16 diff --git a/src/reader/filereader.cpp b/src/lib/reader/filereader.cpp similarity index 65% rename from src/reader/filereader.cpp rename to src/lib/reader/filereader.cpp index 4c970631..71913bfe 100644 --- a/src/reader/filereader.cpp +++ b/src/lib/reader/filereader.cpp @@ -3,7 +3,7 @@ #include "filereader.hpp" unsigned FileReader::size() { - return fp.size(); + return fp->size(); } //This function will allocate memory even if open() fails. @@ -15,36 +15,38 @@ uint8_t* FileReader::read(unsigned length) { if(length == 0) { //read the entire file into RAM - data = new(zeromemory) uint8_t[fp.size()]; - if(fp.open()) fp.read(data, fp.size()); - } else if(length > fp.size()) { + data = new(zeromemory) uint8_t[fp->size()]; + if(fp->open()) fp->read(data, fp->size()); + } else if(length > fp->size()) { //read the entire file into RAM, pad the rest with 0x00s data = new(zeromemory) uint8_t[length]; - if(fp.open()) fp.read(data, fp.size()); + if(fp->open()) fp->read(data, fp->size()); } else { //filesize >= length //read only what was requested data = new(zeromemory) uint8_t[length]; - if(fp.open()) fp.read(data, length); + if(fp->open()) fp->read(data, length); } return data; } bool FileReader::ready() { - return fp.open(); + return fp->open(); } FileReader::FileReader(const char *fn) { - if(!fp.open(fn, file::mode_read)) return; + fp = new file; + if(!fp->open(fn, file::mode_read)) return; - if(fp.size() == 0) { + if(fp->size() == 0) { //empty file - fp.close(); + fp->close(); } } FileReader::~FileReader() { - if(fp.open()) fp.close(); + if(fp->open()) fp->close(); + delete fp; } -#endif //ifdef READER_CPP +#endif diff --git a/src/reader/filereader.hpp b/src/lib/reader/filereader.hpp similarity index 73% rename from src/reader/filereader.hpp rename to src/lib/reader/filereader.hpp index c48819c1..e3bc17b3 100644 --- a/src/reader/filereader.hpp +++ b/src/lib/reader/filereader.hpp @@ -1,3 +1,7 @@ +namespace nall { + class file; +} + class FileReader : public Reader { public: unsigned size(); @@ -8,5 +12,5 @@ public: ~FileReader(); private: - file fp; + nall::file *fp; }; diff --git a/src/reader/gzreader.cpp b/src/lib/reader/gzreader.cpp similarity index 100% rename from src/reader/gzreader.cpp rename to src/lib/reader/gzreader.cpp diff --git a/src/reader/gzreader.hpp b/src/lib/reader/gzreader.hpp similarity index 84% rename from src/reader/gzreader.hpp rename to src/lib/reader/gzreader.hpp index f009fe1d..9d13ab8b 100644 --- a/src/reader/gzreader.hpp +++ b/src/lib/reader/gzreader.hpp @@ -1,4 +1,4 @@ -#include "zlib/zlib.h" +#include class GZReader : public Reader { private: diff --git a/src/reader/jmareader.cpp b/src/lib/reader/jmareader.cpp similarity index 97% rename from src/reader/jmareader.cpp rename to src/lib/reader/jmareader.cpp index 2e879a08..44f72215 100644 --- a/src/reader/jmareader.cpp +++ b/src/lib/reader/jmareader.cpp @@ -1,7 +1,6 @@ #ifdef READER_CPP #include "jmareader.hpp" -#include "jma/jma.h" unsigned JMAReader::size() { return filesize; diff --git a/src/reader/jmareader.hpp b/src/lib/reader/jmareader.hpp similarity index 84% rename from src/reader/jmareader.hpp rename to src/lib/reader/jmareader.hpp index c3d12754..15ad6862 100644 --- a/src/reader/jmareader.hpp +++ b/src/lib/reader/jmareader.hpp @@ -1,4 +1,4 @@ -#include "jma/jma.h" +#include class JMAReader : public Reader { public: diff --git a/src/reader/reader.cpp b/src/lib/reader/reader.cpp similarity index 100% rename from src/reader/reader.cpp rename to src/lib/reader/reader.cpp diff --git a/src/reader/reader.hpp b/src/lib/reader/reader.hpp similarity index 100% rename from src/reader/reader.hpp rename to src/lib/reader/reader.hpp diff --git a/src/reader/zipreader.cpp b/src/lib/reader/zipreader.cpp similarity index 100% rename from src/reader/zipreader.cpp rename to src/lib/reader/zipreader.cpp diff --git a/src/reader/zipreader.hpp b/src/lib/reader/zipreader.hpp similarity index 87% rename from src/reader/zipreader.hpp rename to src/lib/reader/zipreader.hpp index 94639ab9..f9df729e 100644 --- a/src/reader/zipreader.hpp +++ b/src/lib/reader/zipreader.hpp @@ -1,4 +1,4 @@ -#include "zlib/unzip.h" +#include #define ZIP_MAX_FILE_NAME PATH_MAX diff --git a/src/lib/tool/opgen.cpp b/src/lib/tool/opgen.cpp deleted file mode 100644 index ed50771d..00000000 --- a/src/lib/tool/opgen.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* broken -- need to port libstring to bstring */ - -#include "libbase.h" -#include "libstring.h" -#include "libstring.cpp" - -FILE *fp, *fph, *fpt; - -stringarray data, line, part, subpart; -stringarray output_table, output_header, output_op; - -struct _op_list { - stringarray name, arg; -} op_list[64]; - -int32 op_count, line_num; - -void clear_op_list() { - op_count = 0; - for(int i = 0; i < 64; i++) { - strcpy(op_list[i].name, ""); - for(int l = 0; l < 8; l++) { - strcpy(op_list[i].arg[l], ""); - } - } -} - -void gen_header() { -int i = line_num; -char t[4096]; - clear_op_list(); - while(1) { - int z = op_count++; - strcpy(part, line[i]); - strrtrim(part, "),"); - strrtrim(part, ") {"); - split(subpart, "(", part); - strcpy(op_list[z].name, subpart[0]); - split(part, ", ", subpart[1]); - for(int l = 0; l < count(part); l++) { - strcpy(op_list[z].arg[l], part[l]); - } - if(strend(line[i], " {"))break; - i++; - } - - sprintf(output_op, "void " CLASS_NAME "::op_$$() {\r\n switch(status.cycle_pos++) {\r\n"); - sprintf(output_header, "void op_$$();\r\n"); - sprintf(output_table, "optbl[$0] = &" CLASS_NAME "::op_$$;\r\n"); - - line_num = i + 1; -} - -void update_line(int i, int n) { -char t[4096]; - replace(line[i], "end;", "status.cycle_pos = 0;"); - replace(line[i], "skip;", "status.cycle_pos++;"); -} - -void gen_op() { -int i = line_num, n, c; -char t[4096]; - while(1) { - if(strmatch(line[i], "}"))break; - - n = strdec(line[i]); - sprintf(t, "%d:", n); - strltrim(line[i], t); - sprintf(t, " case %d: {\r\n", n); - strcat(output_op, t); - - update_line(i, n); - if(!strmatch(line[i], "")) { - strcat(output_op, " "); - strcat(output_op, line[i]); - strcat(output_op, "\r\n"); - } - - i++; - while(1) { - if(strptr(line[i])[1] == ':' || strptr(line[i])[2] == ':' || strmatch(line[i], "}"))break; - - update_line(i, n); - strcat(output_op, " "); - strcat(output_op, line[i]); - strcat(output_op, "\r\n"); - - i++; - } - - if(strmatch(line[i], "}")) { - strcat(output_op, " status.cycle_pos = 0;\r\n"); - } - - strcat(output_op, " } break;\r\n"); - } - strcat(output_op, " }\r\n}"); - - line_num = i + 1; -} - -void gen_final() { -string t; - for(int i = 0; i < op_count; i++) { - strcpy(t, output_op); - replace(t, "$$", op_list[i].name); - replace(t, "$0", op_list[i].arg[0]); - replace(t, "$1", op_list[i].arg[1]); - replace(t, "$2", op_list[i].arg[2]); - replace(t, "$3", op_list[i].arg[3]); - replace(t, "$4", op_list[i].arg[4]); - replace(t, "$5", op_list[i].arg[5]); - replace(t, "$6", op_list[i].arg[6]); - replace(t, "$7", op_list[i].arg[7]); - fprintf(fp, "%s\r\n\r\n", strptr(t)); - - strcpy(t, output_header); - replace(t, "$$", op_list[i].name); - fprintf(fph, "%s", strptr(t)); - - strcpy(t, output_table); - replace(t, "$$", op_list[i].name); - replace(t, "$0", op_list[i].arg[0]); - fprintf(fpt, "%s", strptr(t)); - } -} - -void generate(char *dest, char *src) { - fp = fopen(src, "rb"); - - fseek(fp, 0, SEEK_END); -int fsize = ftell(fp); - fseek(fp, 0, SEEK_SET); -char *buf = (char*)malloc(fsize + 1); - fread(buf, 1, fsize, fp); - fclose(fp); - buf[fsize] = 0; - - strcpy(data, buf); - free(buf); - replace(data, "\r\n", "\n"); - split(line, "\n", data); - - fp = fopen(dest, "wb"); - - line_num = 0; - while(line_num < count(line)) { - while(line_num < count(line) && strmatch(line[line_num], ""))line_num++; - if(line_num >= count(line))break; - - gen_header(); - gen_op(); - gen_final(); - } - - fclose(fp); -} diff --git a/src/lib/tool/opgen_fnptr.cpp b/src/lib/tool/opgen_fnptr.cpp deleted file mode 100644 index 1f948bd9..00000000 --- a/src/lib/tool/opgen_fnptr.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* broken -- need to port libstring to bstring */ - -#include "libbase.h" -#include "libstring.h" -#include "libstring.cpp" - -FILE *fp, *fph, *fpt; - -stringarray data, line, part, subpart; -stringarray output_table, output_header, output_op; - -struct _op_list { - stringarray name, arg; -} op_list[64]; - -int32 op_count, line_num; - -void clear_op_list() { - op_count = 0; - for(int i = 0; i < 64; i++) { - strcpy(op_list[i].name, ""); - for(int l = 0; l < 8; l++) { - strcpy(op_list[i].arg[l], ""); - } - } -} - -void gen_header() { -int i = line_num; -char t[4096]; - clear_op_list(); - while(1) { - int z = op_count++; - strcpy(part, line[i]); - strrtrim(part, "),"); - strrtrim(part, ") {"); - split(subpart, "(", part); - strcpy(op_list[z].name, subpart[0]); - split(part, ", ", subpart[1]); - for(int l = 0; l < count(part); l++) { - strcpy(op_list[z].arg[l], part[l]); - } - if(strend(line[i], " {"))break; - i++; - } - - sprintf(output_op, "void " CLASS_NAME "::op_$$() {\r\n"); - sprintf(output_header, "void op_$$();\r\n"); - sprintf(output_table, "optbl[$0] = &" CLASS_NAME "::op_$$;\r\n"); - - line_num = i + 1; -} - -void update_line(int i) { -char t[4096]; - replace(line[i], "end;", "return;"); -} - -void gen_op() { -int i = line_num, n, c; -char t[4096]; - while(1) { - if(!strcmp(line[i], "}"))break; - - n = strdec(line[i]); - sprintf(t, "%d:", n); - strltrim(line[i], t); - //sprintf(t, " case %d: {\r\n", n); - //strcat(output_op, t); - - update_line(i); - if(strcmp(line[i], "")) { - strcat(output_op, " "); - strcat(output_op, line[i]); - strcat(output_op, "\r\n"); - } - - i++; - while(1) { - if(strptr(line[i])[1] == ':' || strptr(line[i])[2] == ':' || !strcmp(line[i], "}"))break; - - update_line(i); - strcat(output_op, line[i]); - strcat(output_op, "\r\n"); - - i++; - } - } - - strcat(output_op, "}"); - line_num = i + 1; -} - -void gen_final() { -string t; - for(int i = 0; i < op_count; i++) { - strcpy(t, output_op); - replace(t, "$$", op_list[i].name); - replace(t, "$0", op_list[i].arg[0]); - replace(t, "$1", op_list[i].arg[1]); - replace(t, "$2", op_list[i].arg[2]); - replace(t, "$3", op_list[i].arg[3]); - replace(t, "$4", op_list[i].arg[4]); - replace(t, "$5", op_list[i].arg[5]); - replace(t, "$6", op_list[i].arg[6]); - replace(t, "$7", op_list[i].arg[7]); - fprintf(fp, "%s\r\n\r\n", strptr(t)); - - strcpy(t, output_header); - replace(t, "$$", op_list[i].name); - fprintf(fph, "%s", strptr(t)); - - strcpy(t, output_table); - replace(t, "$$", op_list[i].name); - replace(t, "$0", op_list[i].arg[0]); - fprintf(fpt, "%s", strptr(t)); - } -} - -void generate(char *dest, char *src) { - fp = fopen(src, "rb"); - - fseek(fp, 0, SEEK_END); -int fsize = ftell(fp); - fseek(fp, 0, SEEK_SET); -char *buf = (char*)malloc(fsize + 1); - fread(buf, 1, fsize, fp); - fclose(fp); - buf[fsize] = 0; - - strcpy(data, buf); - free(buf); - replace(data, "\r\n", "\n"); - split(line, "\n", data); - - fp = fopen(dest, "wb"); - - line_num = 0; - while(line_num < count(line)) { - while(line_num < count(line) && !strcmp(line[line_num], ""))line_num++; - if(line_num >= count(line))break; - - gen_header(); - gen_op(); - gen_final(); - } - - fclose(fp); -} diff --git a/src/lib/tool/opgen_switch.cpp b/src/lib/tool/opgen_switch.cpp deleted file mode 100644 index e203027f..00000000 --- a/src/lib/tool/opgen_switch.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include -using namespace nall; - -FILE *fp; - -string data, output_op; -lstring line, part, subpart; - -struct OpList { - string name; - lstring arg; -} op_list[64]; - -int32_t op_count, line_num; - -void clear_op_list() { - op_count = 0; - for(unsigned i = 0; i < 64; i++) { - op_list[i].name = ""; - for(unsigned l = 0; l < 8; l++) { - op_list[i].arg[l] = ""; - } - } -} - -void gen_begin() { - int i = line_num; - clear_op_list(); - - while(true) { - int z = op_count++; - string temp = line[i]; - rtrim(temp, "),"); - rtrim(temp, ") {"); - subpart.split("(", temp); - op_list[z].name = subpart[0]; - part.split(", ", subpart[1]); - for(unsigned l = 0; l < part.size(); l++) { - op_list[z].arg[l] = part[l]; - } - if(strend(line[i], " {") == true) break; - i++; - } - - output_op = "//$$\r\ncase $0: {\r\n"; - line_num = i + 1; -} - -void update_line(int i) { - line[i].replace("end;", "break;"); -} - -void gen_op() { - int i = line_num, n, c; - char t[4096]; - while(true) { - if(!strcmp(line[i], "}"))break; - - //remove cycle number - n = strunsigned(line[i]); - sprintf(t, "%d:", n); - ltrim(line[i], t); - //sprintf(t, "//%d:\r\n", n); - //strcat(output_op, t); - - update_line(i); - if(strcmp(line[i], "")) { - output_op << " "; - output_op << line[i]; - output_op << "\r\n"; - } - - i++; - while(true) { - if(line[i][1] == ':' || line[i][2] == ':' || line[i] == "}") break; - - update_line(i); - output_op << line[i]; - output_op << "\r\n"; - - i++; - } - } - - output_op << "} break;"; - line_num = i + 1; -} - -void gen_end() { - string t; - for(unsigned i = 0; i < op_count; i++) { - t = output_op; - t.replace("$$", op_list[i].name); - t.replace("$0", op_list[i].arg[0]); - t.replace("$1", op_list[i].arg[1]); - t.replace("$2", op_list[i].arg[2]); - t.replace("$3", op_list[i].arg[3]); - t.replace("$4", op_list[i].arg[4]); - t.replace("$5", op_list[i].arg[5]); - t.replace("$6", op_list[i].arg[6]); - t.replace("$7", op_list[i].arg[7]); - fprintf(fp, "%s\r\n\r\n", (const char*)t); - } -} - -void generate(const char *dest, const char *src) { - data.readfile(src); - data.replace("\r\n", "\n"); - line.split("\n", data); - - fp = fopen(dest, "wb"); - string header = CLASS_NAME; - fprintf(fp, "#ifdef %s_CPP\n\n", (const char*)strupper(header)); //inclusion guard - - line_num = 0; - while(line_num < line.size()) { - while(line_num < line.size() && !strcmp(line[line_num], "")) line_num++; - if(line_num >= line.size()) break; - - gen_begin(); - gen_op(); - gen_end(); - } - - fprintf(fp, "#endif\n"); - fclose(fp); -} diff --git a/src/reader/zlib/adler32.c b/src/lib/zlib/adler32.c similarity index 100% rename from src/reader/zlib/adler32.c rename to src/lib/zlib/adler32.c diff --git a/src/reader/zlib/compress.c b/src/lib/zlib/compress.c similarity index 100% rename from src/reader/zlib/compress.c rename to src/lib/zlib/compress.c diff --git a/src/reader/zlib/crc32.c b/src/lib/zlib/crc32.c similarity index 100% rename from src/reader/zlib/crc32.c rename to src/lib/zlib/crc32.c diff --git a/src/reader/zlib/crc32.h b/src/lib/zlib/crc32.h similarity index 100% rename from src/reader/zlib/crc32.h rename to src/lib/zlib/crc32.h diff --git a/src/lib/zlib/crypt.h b/src/lib/zlib/crypt.h new file mode 100644 index 00000000..622f4bc2 --- /dev/null +++ b/src/lib/zlib/crypt.h @@ -0,0 +1,132 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) + const char *passwd; /* password string */ + unsigned char *buf; /* where to write header */ + int bufSize; + unsigned long* pkeys; + const unsigned long* pcrc_32_tab; + unsigned long crcForCrypting; +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/src/reader/zlib/deflate.c b/src/lib/zlib/deflate.c similarity index 100% rename from src/reader/zlib/deflate.c rename to src/lib/zlib/deflate.c diff --git a/src/reader/zlib/deflate.h b/src/lib/zlib/deflate.h similarity index 100% rename from src/reader/zlib/deflate.h rename to src/lib/zlib/deflate.h diff --git a/src/reader/zlib/gzio.c b/src/lib/zlib/gzio.c similarity index 100% rename from src/reader/zlib/gzio.c rename to src/lib/zlib/gzio.c diff --git a/src/reader/zlib/inffast.c b/src/lib/zlib/inffast.c similarity index 100% rename from src/reader/zlib/inffast.c rename to src/lib/zlib/inffast.c diff --git a/src/reader/zlib/inffast.h b/src/lib/zlib/inffast.h similarity index 100% rename from src/reader/zlib/inffast.h rename to src/lib/zlib/inffast.h diff --git a/src/reader/zlib/inffixed.h b/src/lib/zlib/inffixed.h similarity index 100% rename from src/reader/zlib/inffixed.h rename to src/lib/zlib/inffixed.h diff --git a/src/reader/zlib/inflate.c b/src/lib/zlib/inflate.c similarity index 100% rename from src/reader/zlib/inflate.c rename to src/lib/zlib/inflate.c diff --git a/src/reader/zlib/inflate.h b/src/lib/zlib/inflate.h similarity index 100% rename from src/reader/zlib/inflate.h rename to src/lib/zlib/inflate.h diff --git a/src/reader/zlib/inftrees.c b/src/lib/zlib/inftrees.c similarity index 100% rename from src/reader/zlib/inftrees.c rename to src/lib/zlib/inftrees.c diff --git a/src/reader/zlib/inftrees.h b/src/lib/zlib/inftrees.h similarity index 100% rename from src/reader/zlib/inftrees.h rename to src/lib/zlib/inftrees.h diff --git a/src/reader/zlib/ioapi.c b/src/lib/zlib/ioapi.c similarity index 100% rename from src/reader/zlib/ioapi.c rename to src/lib/zlib/ioapi.c diff --git a/src/lib/zlib/ioapi.h b/src/lib/zlib/ioapi.h new file mode 100644 index 00000000..7d457baa --- /dev/null +++ b/src/lib/zlib/ioapi.h @@ -0,0 +1,75 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#ifndef _ZLIBIOAPI_H +#define _ZLIBIOAPI_H + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + +#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +#define ZCALLBACK CALLBACK +#else +#define ZCALLBACK +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + + + +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) +#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) +#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) +#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) +#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/reader/zlib/trees.c b/src/lib/zlib/trees.c similarity index 100% rename from src/reader/zlib/trees.c rename to src/lib/zlib/trees.c diff --git a/src/reader/zlib/trees.h b/src/lib/zlib/trees.h similarity index 100% rename from src/reader/zlib/trees.h rename to src/lib/zlib/trees.h diff --git a/src/reader/zlib/unzip.c b/src/lib/zlib/unzip.c similarity index 100% rename from src/reader/zlib/unzip.c rename to src/lib/zlib/unzip.c diff --git a/src/reader/zlib/unzip.h b/src/lib/zlib/unzip.h similarity index 100% rename from src/reader/zlib/unzip.h rename to src/lib/zlib/unzip.h diff --git a/src/reader/zlib/zconf.h b/src/lib/zlib/zconf.h similarity index 100% rename from src/reader/zlib/zconf.h rename to src/lib/zlib/zconf.h diff --git a/src/reader/zlib/zip.c b/src/lib/zlib/zip.c similarity index 100% rename from src/reader/zlib/zip.c rename to src/lib/zlib/zip.c diff --git a/src/reader/zlib/zip.h b/src/lib/zlib/zip.h similarity index 100% rename from src/reader/zlib/zip.h rename to src/lib/zlib/zip.h diff --git a/src/reader/zlib/zlib.h b/src/lib/zlib/zlib.h similarity index 100% rename from src/reader/zlib/zlib.h rename to src/lib/zlib/zlib.h diff --git a/src/reader/zlib/zutil.c b/src/lib/zlib/zutil.c similarity index 100% rename from src/reader/zlib/zutil.c rename to src/lib/zlib/zutil.c diff --git a/src/reader/zlib/zutil.h b/src/lib/zlib/zutil.h similarity index 100% rename from src/reader/zlib/zutil.h rename to src/lib/zlib/zutil.h diff --git a/src/memory/memory.cpp b/src/memory/memory.cpp index bec7433e..b198ae0b 100644 --- a/src/memory/memory.cpp +++ b/src/memory/memory.cpp @@ -1,5 +1,7 @@ #include <../base.hpp> + #define MEMORY_CPP +namespace SNES { namespace memory { MMIOAccess mmio; @@ -105,3 +107,5 @@ void Bus::map( } break; } } + +}; diff --git a/src/memory/memory.hpp b/src/memory/memory.hpp index 43ec5b1d..5e33df03 100644 --- a/src/memory/memory.hpp +++ b/src/memory/memory.hpp @@ -41,6 +41,7 @@ struct MappedRAM : Memory { void write_protect(bool status) { write_protection = status; } uint8* handle() { return data; } unsigned size() const { return datasize; } + void reset() { delete[] data; data = 0; datasize = -1U; write_protection = false; } inline uint8 read(unsigned addr) { return data[addr]; } inline void write(unsigned addr, uint8 n) { if(!write_protection) data[addr] = n; } diff --git a/src/memory/smemory/smemory.cpp b/src/memory/smemory/smemory.cpp index 7284b48e..8f8c005b 100644 --- a/src/memory/smemory/smemory.cpp +++ b/src/memory/smemory/smemory.cpp @@ -1,7 +1,7 @@ #include <../base.hpp> -#include <../chip/chip.hpp> -#include <../cart/cart.hpp> + #define SMEMORY_CPP +namespace SNES { #include "mapper/system.cpp" #include "mapper/generic.cpp" @@ -9,7 +9,7 @@ void sBus::power() { for(unsigned i = 0x2000; i <= 0x5fff; i++) memory::mmio.map(i, memory::mmio_unmapped); - for(unsigned i = 0; i < memory::wram.size(); i++) memory::wram[i] = snes.config.cpu.wram_init_value; + for(unsigned i = 0; i < memory::wram.size(); i++) memory::wram[i] = config.cpu.wram_init_value; reset(); } @@ -42,3 +42,5 @@ sBus::sBus() { sBus::~sBus() { } + +}; diff --git a/src/ppu/bppu/bppu.cpp b/src/ppu/bppu/bppu.cpp index 99a21c98..2f075854 100644 --- a/src/ppu/bppu/bppu.cpp +++ b/src/ppu/bppu/bppu.cpp @@ -1,5 +1,7 @@ #include <../base.hpp> -#define BPPU_CPP + +#define BPPU_CPP +namespace SNES { #include "bppu_mmio.cpp" #include "bppu_render.cpp" @@ -40,7 +42,7 @@ void bPPU::add_clocks(unsigned clocks) { } void bPPU::scanline() { - snes.scanline(); + system.scanline(); line = ivcounter(); if(line == 0) { @@ -77,7 +79,7 @@ void bPPU::render_scanline() { void bPPU::frame() { PPU::frame(); - snes.frame(); + system.frame(); if(ifield() == 0) { display.interlace = regs.interlace; @@ -93,7 +95,7 @@ void bPPU::power() { for(unsigned i = 0; i < memory::cgram.size(); i++) memory::cgram[i] = 0x00; flush_tiledata_cache(); - region = (snes.region() == SNES::NTSC ? 0 : 1); //0 = NTSC, 1 = PAL + region = (system.region() == System::NTSC ? 0 : 1); //0 = NTSC, 1 = PAL //$2100 regs.display_disabled = 1; @@ -345,3 +347,5 @@ bPPU::bPPU() { bPPU::~bPPU() { free_tiledata_cache(); } + +}; diff --git a/src/ppu/bppu/bppu_mmio.cpp b/src/ppu/bppu/bppu_mmio.cpp index 8b8945f3..d4df9afe 100644 --- a/src/ppu/bppu/bppu_mmio.cpp +++ b/src/ppu/bppu/bppu_mmio.cpp @@ -30,7 +30,7 @@ uint8 bPPU::vram_mmio_read(uint16 addr) { } else { uint16 v = vcounter(); uint16 h = hcounter(); - uint16 ls = ((snes.region() == SNES::NTSC ? 525 : 625) >> 1) - 1; + uint16 ls = ((system.region() == System::NTSC ? 525 : 625) >> 1) - 1; if(interlace() && !field()) ls++; if(v == ls && h == 1362) { diff --git a/src/ppu/counter.cpp b/src/ppu/counter.cpp index 17bdce7e..5a9f2140 100644 --- a/src/ppu/counter.cpp +++ b/src/ppu/counter.cpp @@ -1,7 +1,7 @@ #ifdef PPU_CPP //wrappers to allow PPUcounter::tick()/tock() to be inlined -bool PPUcounter::region() const { return snes.region() == SNES::NTSC ? 0 : 1; } +bool PPUcounter::region() const { return system.region() == System::NTSC ? 0 : 1; } bool PPUcounter::interlace() const { return ppu.interlace(); } void PPUcounter::scanline() { cpu.scanline(); } diff --git a/src/ppu/ppu.cpp b/src/ppu/ppu.cpp index d933b67a..6526f481 100644 --- a/src/ppu/ppu.cpp +++ b/src/ppu/ppu.cpp @@ -1,5 +1,7 @@ #include <../base.hpp> + #define PPU_CPP +namespace SNES { #include "counter.cpp" @@ -25,8 +27,8 @@ void PPU::frame() { } void PPU::power() { - ppu1_version = snes.config.ppu1.version; - ppu2_version = snes.config.ppu2.version; + ppu1_version = config.ppu1.version; + ppu2_version = config.ppu2.version; } void PPU::reset() { @@ -45,3 +47,5 @@ PPU::PPU() { PPU::~PPU() { delete[] output; } + +}; diff --git a/src/smp/core/bpp.sh b/src/smp/core/bpp.sh new file mode 100644 index 00000000..684413a9 --- /dev/null +++ b/src/smp/core/bpp.sh @@ -0,0 +1,3 @@ +clear +bpp opcode_functions.cpp opcode_functions.bpp +bpp opcode_headers.hpp opcode_headers.bpp diff --git a/src/smp/core/core.cpp b/src/smp/core/core.cpp new file mode 100644 index 00000000..6709748f --- /dev/null +++ b/src/smp/core/core.cpp @@ -0,0 +1,15 @@ +#include <../base.hpp> + +#define SMPCORE_CPP +namespace SNES { + +#include "opcode_algorithms.cpp" +#include "opcode_functions.cpp" +#include "opcode_table.cpp" +#include "disasm/disasm.cpp" + +SMPcore::SMPcore() { + initialize_opcode_table(); +} + +}; diff --git a/src/smp/ssmp/core/core.hpp b/src/smp/core/core.hpp similarity index 56% rename from src/smp/ssmp/core/core.hpp rename to src/smp/core/core.hpp index cf4fd472..a3108e43 100644 --- a/src/smp/ssmp/core/core.hpp +++ b/src/smp/core/core.hpp @@ -1,6 +1,16 @@ - uint16 dp, sp, rd, wr, bit, ya; +class SMPcore { +public: + #include "registers.hpp" + #include "memory.hpp" + #include "opcode_headers.hpp" + #include "disasm/disasm.hpp" - bool in_opcode() { return status.in_opcode; } + regs_t regs; + uint16 dp, sp, rd, wr, bit, ya; + + virtual void op_io() = 0; + virtual uint8_t op_read(uint16_t addr) = 0; + virtual void op_write(uint16_t addr, uint8_t data) = 0; uint8 op_adc (uint8 x, uint8 y); uint16 op_addw(uint16 x, uint16 y); @@ -17,3 +27,9 @@ uint8 op_lsr (uint8 x); uint8 op_rol (uint8 x); uint8 op_ror (uint8 x); + + void (SMPcore::*opcode_table[256])(); + void initialize_opcode_table(); + + SMPcore(); +}; diff --git a/src/smp/dsmp.cpp b/src/smp/core/disasm/disasm.cpp similarity index 96% rename from src/smp/dsmp.cpp rename to src/smp/core/disasm/disasm.cpp index 1cc6c009..94eae4e2 100644 --- a/src/smp/dsmp.cpp +++ b/src/smp/core/disasm/disasm.cpp @@ -1,33 +1,25 @@ -#ifdef SMP_CPP +#ifdef SMPCORE_CPP -//virtual function, see src/cpu/dcpu.cpp -//for explanation of this function -bool SMP::in_opcode() { return false; } - -uint16 SMP::__relb(int8 offset, int op_len) { +uint16 SMPcore::__relb(int8 offset, int op_len) { uint16 pc = regs.pc + op_len; return pc + offset; } -void SMP::disassemble_opcode(char *output) { +void SMPcore::disassemble_opcode(char *output) { char *s, t[512]; uint8 op, op0, op1; uint16 opw, opdp0, opdp1; s = output; - if(in_opcode() == true) { - strcpy(s, "..???? "); - return; - } - sprintf(s, "..%.4x ", regs.pc); - op = ram_read(regs.pc); - op0 = ram_read(regs.pc + 1); - op1 = ram_read(regs.pc + 2); + //todo: this needs to access IPLROM at $ffc0+, when enabled + op = memory::apuram[(uint16_t)(regs.pc + 0)]; + op0 = memory::apuram[(uint16_t)(regs.pc + 1)]; + op1 = memory::apuram[(uint16_t)(regs.pc + 2)]; opw = (op0) | (op1 << 8); - opdp0 = ((regs.p.p)?0x100:0x000) + op0; - opdp1 = ((regs.p.p)?0x100:0x000) + op1; + opdp0 = ((unsigned)regs.p.p << 8) + op0; + opdp1 = ((unsigned)regs.p.p << 8) + op1; strcpy(t, " "); @@ -309,4 +301,4 @@ void SMP::disassemble_opcode(char *output) { strcat(s, t); } -#endif //ifdef SMP_CPP +#endif diff --git a/src/smp/core/disasm/disasm.hpp b/src/smp/core/disasm/disasm.hpp new file mode 100644 index 00000000..7309f444 --- /dev/null +++ b/src/smp/core/disasm/disasm.hpp @@ -0,0 +1,2 @@ + void disassemble_opcode(char *output); + inline uint16 __relb(int8 offset, int op_len); diff --git a/src/smp/core/memory.hpp b/src/smp/core/memory.hpp new file mode 100644 index 00000000..57825700 --- /dev/null +++ b/src/smp/core/memory.hpp @@ -0,0 +1,27 @@ +alwaysinline uint8 op_readpc() { + return op_read(regs.pc++); +} + +alwaysinline uint8 op_readstack() { + return op_read(0x0100 | ++regs.sp); +} + +alwaysinline void op_writestack(uint8 data) { + op_write(0x0100 | regs.sp--, data); +} + +alwaysinline uint8 op_readaddr(uint16 addr) { + return op_read(addr); +} + +alwaysinline void op_writeaddr(uint16 addr, uint8 data) { + op_write(addr, data); +} + +alwaysinline uint8 op_readdp(uint8 addr) { + return op_read((unsigned(regs.p.p) << 8) + addr); +} + +alwaysinline void op_writedp(uint8 addr, uint8 data) { + op_write((unsigned(regs.p.p) << 8) + addr, data); +} diff --git a/src/smp/ssmp/core/opfn.cpp b/src/smp/core/opcode_algorithms.cpp similarity index 69% rename from src/smp/ssmp/core/opfn.cpp rename to src/smp/core/opcode_algorithms.cpp index 8e9b3ead..b7e0b095 100644 --- a/src/smp/ssmp/core/opfn.cpp +++ b/src/smp/core/opcode_algorithms.cpp @@ -1,6 +1,6 @@ -#ifdef SSMP_CPP +#ifdef SMPCORE_CPP -uint8 sSMP::op_adc(uint8 x, uint8 y) { +uint8 SMPcore::op_adc(uint8 x, uint8 y) { int r = x + y + regs.p.c; regs.p.n = r & 0x80; regs.p.v = ~(x ^ y) & (x ^ r) & 0x80; @@ -10,7 +10,7 @@ uint8 sSMP::op_adc(uint8 x, uint8 y) { return r; } -uint16 sSMP::op_addw(uint16 x, uint16 y) { +uint16 SMPcore::op_addw(uint16 x, uint16 y) { uint16 r; regs.p.c = 0; r = op_adc(x, y); @@ -19,14 +19,14 @@ uint16 sSMP::op_addw(uint16 x, uint16 y) { return r; } -uint8 sSMP::op_and(uint8 x, uint8 y) { +uint8 SMPcore::op_and(uint8 x, uint8 y) { x &= y; regs.p.n = x & 0x80; regs.p.z = x == 0; return x; } -uint8 sSMP::op_cmp(uint8 x, uint8 y) { +uint8 SMPcore::op_cmp(uint8 x, uint8 y) { int r = x - y; regs.p.n = r & 0x80; regs.p.z = (uint8)r == 0; @@ -34,7 +34,7 @@ uint8 sSMP::op_cmp(uint8 x, uint8 y) { return x; } -uint16 sSMP::op_cmpw(uint16 x, uint16 y) { +uint16 SMPcore::op_cmpw(uint16 x, uint16 y) { int r = x - y; regs.p.n = r & 0x8000; regs.p.z = (uint16)r == 0; @@ -42,21 +42,21 @@ uint16 sSMP::op_cmpw(uint16 x, uint16 y) { return x; } -uint8 sSMP::op_eor(uint8 x, uint8 y) { +uint8 SMPcore::op_eor(uint8 x, uint8 y) { x ^= y; regs.p.n = x & 0x80; regs.p.z = x == 0; return x; } -uint8 sSMP::op_or(uint8 x, uint8 y) { +uint8 SMPcore::op_or(uint8 x, uint8 y) { x |= y; regs.p.n = x & 0x80; regs.p.z = x == 0; return x; } -uint8 sSMP::op_sbc(uint8 x, uint8 y) { +uint8 SMPcore::op_sbc(uint8 x, uint8 y) { int r = x - y - !regs.p.c; regs.p.n = r & 0x80; regs.p.v = (x ^ y) & (x ^ r) & 0x80; @@ -66,7 +66,7 @@ uint8 sSMP::op_sbc(uint8 x, uint8 y) { return r; } -uint16 sSMP::op_subw(uint16 x, uint16 y) { +uint16 SMPcore::op_subw(uint16 x, uint16 y) { uint16 r; regs.p.c = 1; r = op_sbc(x, y); @@ -75,21 +75,21 @@ uint16 sSMP::op_subw(uint16 x, uint16 y) { return r; } -uint8 sSMP::op_inc(uint8 x) { +uint8 SMPcore::op_inc(uint8 x) { x++; regs.p.n = x & 0x80; regs.p.z = x == 0; return x; } -uint8 sSMP::op_dec(uint8 x) { +uint8 SMPcore::op_dec(uint8 x) { x--; regs.p.n = x & 0x80; regs.p.z = x == 0; return x; } -uint8 sSMP::op_asl(uint8 x) { +uint8 SMPcore::op_asl(uint8 x) { regs.p.c = x & 0x80; x <<= 1; regs.p.n = x & 0x80; @@ -97,7 +97,7 @@ uint8 sSMP::op_asl(uint8 x) { return x; } -uint8 sSMP::op_lsr(uint8 x) { +uint8 SMPcore::op_lsr(uint8 x) { regs.p.c = x & 0x01; x >>= 1; regs.p.n = x & 0x80; @@ -105,7 +105,7 @@ uint8 sSMP::op_lsr(uint8 x) { return x; } -uint8 sSMP::op_rol(uint8 x) { +uint8 SMPcore::op_rol(uint8 x) { unsigned carry = (unsigned)regs.p.c; regs.p.c = x & 0x80; x = (x << 1) | carry; @@ -114,7 +114,7 @@ uint8 sSMP::op_rol(uint8 x) { return x; } -uint8 sSMP::op_ror(uint8 x) { +uint8 SMPcore::op_ror(uint8 x) { unsigned carry = (unsigned)regs.p.c << 7; regs.p.c = x & 0x01; x = carry | (x >> 1); @@ -123,4 +123,4 @@ uint8 sSMP::op_ror(uint8 x) { return x; } -#endif //ifdef SSMP_CPP +#endif diff --git a/src/smp/core/opcode_functions.bpp b/src/smp/core/opcode_functions.bpp new file mode 100644 index 00000000..be0a633c --- /dev/null +++ b/src/smp/core/opcode_functions.bpp @@ -0,0 +1,11 @@ +//opcode_functions.cpp was generated via bpp -> opcode_functions.bpp + +@global class SMPcore + +@include "opcode_move.bpp" +@include "opcode_pc.bpp" +@include "opcode_read.bpp" +@include "opcode_rmw.bpp" +@include "opcode_misc.bpp" + +@include "opcode_list.bpp" diff --git a/src/smp/core/opcode_functions.cpp b/src/smp/core/opcode_functions.cpp new file mode 100644 index 00000000..9c1b6706 --- /dev/null +++ b/src/smp/core/opcode_functions.cpp @@ -0,0 +1,2233 @@ +//opcode_functions.cpp was generated via bpp -> opcode_functions.bpp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +//=============== +//opcode_move.bpp +//=============== + + void SMPcore::op_mov_a_x() { + op_io(); + regs.a = regs.x; + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + void SMPcore::op_mov_a_y() { + op_io(); + regs.a = regs.y; + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + void SMPcore::op_mov_x_a() { + op_io(); + regs.x = regs.a; + regs.p.n = (regs.x & 0x80); + regs.p.z = (regs.x == 0); + } + + void SMPcore::op_mov_y_a() { + op_io(); + regs.y = regs.a; + regs.p.n = (regs.y & 0x80); + regs.p.z = (regs.y == 0); + } + + void SMPcore::op_mov_x_sp() { + op_io(); + regs.x = regs.sp; + regs.p.n = (regs.x & 0x80); + regs.p.z = (regs.x == 0); + } + + void SMPcore::op_mov_sp_x() { + op_io(); + regs.sp = regs.x; + } + + + void SMPcore::op_mov_a_const() { + regs.a = op_readpc(); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + void SMPcore::op_mov_x_const() { + regs.x = op_readpc(); + regs.p.n = (regs.x & 0x80); + regs.p.z = (regs.x == 0); + } + + void SMPcore::op_mov_y_const() { + regs.y = op_readpc(); + regs.p.n = (regs.y & 0x80); + regs.p.z = (regs.y == 0); + } + + + void SMPcore::op_mov_a_ix() { + op_io(); + regs.a = op_readdp(regs.x); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + void SMPcore::op_mov_a_ixinc() { + op_io(); + regs.a = op_readdp(regs.x++); + op_io(); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + + void SMPcore::op_mov_a_dp() { + sp = op_readpc(); + regs.a = op_readdp(sp); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + void SMPcore::op_mov_x_dp() { + sp = op_readpc(); + regs.x = op_readdp(sp); + regs.p.n = (regs.x & 0x80); + regs.p.z = (regs.x == 0); + } + + void SMPcore::op_mov_y_dp() { + sp = op_readpc(); + regs.y = op_readdp(sp); + regs.p.n = (regs.y & 0x80); + regs.p.z = (regs.y == 0); + } + + + void SMPcore::op_mov_a_dpx() { + sp = op_readpc(); + op_io(); + regs.a = op_readdp(sp + regs.x); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + void SMPcore::op_mov_x_dpy() { + sp = op_readpc(); + op_io(); + regs.x = op_readdp(sp + regs.y); + regs.p.n = (regs.x & 0x80); + regs.p.z = (regs.x == 0); + } + + void SMPcore::op_mov_y_dpx() { + sp = op_readpc(); + op_io(); + regs.y = op_readdp(sp + regs.x); + regs.p.n = (regs.y & 0x80); + regs.p.z = (regs.y == 0); + } + + + void SMPcore::op_mov_a_addr() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + regs.a = op_readaddr(sp); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + void SMPcore::op_mov_x_addr() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + regs.x = op_readaddr(sp); + regs.p.n = (regs.x & 0x80); + regs.p.z = (regs.x == 0); + } + + void SMPcore::op_mov_y_addr() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + regs.y = op_readaddr(sp); + regs.p.n = (regs.y & 0x80); + regs.p.z = (regs.y == 0); + } + + + void SMPcore::op_mov_a_addrx() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + op_io(); + regs.a = op_readaddr(sp + regs.x); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + void SMPcore::op_mov_a_addry() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + op_io(); + regs.a = op_readaddr(sp + regs.y); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + + void SMPcore::op_mov_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + regs.a = op_readaddr(sp); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + void SMPcore::op_mov_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + regs.a = op_readaddr(sp + regs.y); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + void SMPcore::op_mov_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + op_writedp(dp, rd); + } + + void SMPcore::op_mov_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, rd); + } + + void SMPcore::op_mov_ix_a() { + op_io(); + op_readdp(regs.x); + op_writedp(regs.x, regs.a); + } + + void SMPcore::op_mov_ixinc_a() { + op_io(); + op_io(); + op_writedp(regs.x++, regs.a); + } + + + void SMPcore::op_mov_dp_a() { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.a); + } + + void SMPcore::op_mov_dp_x() { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.x); + } + + void SMPcore::op_mov_dp_y() { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.y); + } + + + void SMPcore::op_mov_dpx_a() { + dp = op_readpc(); + op_io(); + dp += regs.x; + op_readdp(dp); + op_writedp(dp, regs.a); + } + + void SMPcore::op_mov_dpy_x() { + dp = op_readpc(); + op_io(); + dp += regs.y; + op_readdp(dp); + op_writedp(dp, regs.x); + } + + void SMPcore::op_mov_dpx_y() { + dp = op_readpc(); + op_io(); + dp += regs.x; + op_readdp(dp); + op_writedp(dp, regs.y); + } + + + void SMPcore::op_mov_addr_a() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + } + + void SMPcore::op_mov_addr_x() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.x); + } + + void SMPcore::op_mov_addr_y() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.y); + } + + + void SMPcore::op_mov_addrx_a() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + dp += regs.x; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + } + + void SMPcore::op_mov_addry_a() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + dp += regs.y; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + } + + + void SMPcore::op_mov_idpx_a() { + sp = op_readpc(); + op_io(); + sp += regs.x; + dp = op_readdp(sp + 0) << 0; + dp |= op_readdp(sp + 1) << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + } + + void SMPcore::op_mov_idpy_a() { + sp = op_readpc(); + dp = op_readdp(sp + 0) << 0; + dp |= op_readdp(sp + 1) << 8; + op_io(); + dp += regs.y; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + } + + + void SMPcore::op_movw_ya_dp() { + sp = op_readpc(); + regs.a = op_readdp(sp + 0); + op_io(); + regs.y = op_readdp(sp + 1); + regs.p.n = (regs.ya & 0x8000); + regs.p.z = (regs.ya == 0); + } + + void SMPcore::op_movw_dp_ya() { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp + 0, regs.a); + op_writedp(dp + 1, regs.y); + } + + + void SMPcore::op_mov1_c_bit() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + bit = sp >> 13; + sp &= 0x1fff; + rd = op_readaddr(sp); + regs.p.c = !!(rd & (1 << bit)); + } + + void SMPcore::op_mov1_bit_c() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + if(regs.p.c) rd |= (1 << bit); + else rd &= ~(1 << bit); + op_io(); + op_writeaddr(dp, rd); + } + + +//============= +//opcode_pc.bpp +//============= + + void SMPcore::op_bra() { + rd = op_readpc(); + if(0) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_beq() { + rd = op_readpc(); + if(!regs.p.z) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bne() { + rd = op_readpc(); + if(regs.p.z) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bcs() { + rd = op_readpc(); + if(!regs.p.c) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bcc() { + rd = op_readpc(); + if(regs.p.c) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bvs() { + rd = op_readpc(); + if(!regs.p.v) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bvc() { + rd = op_readpc(); + if(regs.p.v) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bmi() { + rd = op_readpc(); + if(!regs.p.n) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bpl() { + rd = op_readpc(); + if(regs.p.n) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + + void SMPcore::op_bbs0() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x01) != 0x01) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbc0() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x01) == 0x01) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbs1() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x02) != 0x02) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbc1() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x02) == 0x02) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbs2() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x04) != 0x04) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbc2() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x04) == 0x04) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbs3() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x08) != 0x08) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbc3() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x08) == 0x08) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbs4() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x10) != 0x10) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbc4() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x10) == 0x10) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbs5() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x20) != 0x20) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbc5() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x20) == 0x20) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbs6() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x40) != 0x40) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbc6() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x40) == 0x40) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbs7() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x80) != 0x80) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_bbc7() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x80) == 0x80) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + + void SMPcore::op_cbne_dp() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if(regs.a == sp) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_cbne_dpx() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + regs.x); + rd = op_readpc(); + op_io(); + if(regs.a == sp) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_dbnz_dp() { + dp = op_readpc(); + wr = op_readdp(dp); + op_writedp(dp, --wr); + rd = op_readpc(); + if(wr == 0) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_dbnz_y() { + rd = op_readpc(); + op_io(); + regs.y--; + op_io(); + if(regs.y == 0) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } + + void SMPcore::op_jmp_addr() { + rd = op_readpc() << 0; + rd |= op_readpc() << 8; + regs.pc = rd; + } + + void SMPcore::op_jmp_iaddrx() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + dp += regs.x; + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + regs.pc = rd; + } + + void SMPcore::op_call() { + rd = op_readpc() << 0; + rd |= op_readpc() << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_pcall() { + rd = op_readpc(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = 0xff00 | rd; + } + + + void SMPcore::op_tcall_0() { + dp = 0xffde - (0 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_1() { + dp = 0xffde - (1 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_2() { + dp = 0xffde - (2 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_3() { + dp = 0xffde - (3 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_4() { + dp = 0xffde - (4 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_5() { + dp = 0xffde - (5 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_6() { + dp = 0xffde - (6 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_7() { + dp = 0xffde - (7 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_8() { + dp = 0xffde - (8 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_9() { + dp = 0xffde - (9 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_10() { + dp = 0xffde - (10 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_11() { + dp = 0xffde - (11 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_12() { + dp = 0xffde - (12 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_13() { + dp = 0xffde - (13 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_14() { + dp = 0xffde - (14 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + void SMPcore::op_tcall_15() { + dp = 0xffde - (15 << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } + + + void SMPcore::op_brk() { + rd = op_readaddr(0xffde) << 0; + rd |= op_readaddr(0xffdf) << 8; + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + op_writestack(regs.p); + regs.pc = rd; + regs.p.b = 1; + regs.p.i = 0; + } + + void SMPcore::op_ret() { + rd = op_readstack() << 0; + rd |= op_readstack() << 8; + op_io(); + op_io(); + regs.pc = rd; + } + + void SMPcore::op_reti() { + regs.p = op_readstack(); + rd = op_readstack() << 0; + rd |= op_readstack() << 8; + op_io(); + op_io(); + regs.pc = rd; + } + + +//=============== +//opcode_read.bpp +//=============== + + void SMPcore::op_adc_a_const() { + rd = op_readpc(); + regs.a = op_adc(regs.a, rd); + } + + void SMPcore::op_and_a_const() { + rd = op_readpc(); + regs.a = op_and(regs.a, rd); + } + + void SMPcore::op_cmp_a_const() { + rd = op_readpc(); + regs.a = op_cmp(regs.a, rd); + } + + void SMPcore::op_cmp_x_const() { + rd = op_readpc(); + regs.x = op_cmp(regs.x, rd); + } + + void SMPcore::op_cmp_y_const() { + rd = op_readpc(); + regs.y = op_cmp(regs.y, rd); + } + + void SMPcore::op_eor_a_const() { + rd = op_readpc(); + regs.a = op_eor(regs.a, rd); + } + + void SMPcore::op_or_a_const() { + rd = op_readpc(); + regs.a = op_or(regs.a, rd); + } + + void SMPcore::op_sbc_a_const() { + rd = op_readpc(); + regs.a = op_sbc(regs.a, rd); + } + + + void SMPcore::op_adc_a_ix() { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_adc(regs.a, rd); + } + + void SMPcore::op_and_a_ix() { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_and(regs.a, rd); + } + + void SMPcore::op_cmp_a_ix() { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_cmp(regs.a, rd); + } + + void SMPcore::op_eor_a_ix() { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_eor(regs.a, rd); + } + + void SMPcore::op_or_a_ix() { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_or(regs.a, rd); + } + + void SMPcore::op_sbc_a_ix() { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_sbc(regs.a, rd); + } + + + void SMPcore::op_adc_a_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_adc(regs.a, rd); + } + + void SMPcore::op_and_a_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_and(regs.a, rd); + } + + void SMPcore::op_cmp_a_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_cmp(regs.a, rd); + } + + void SMPcore::op_cmp_x_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + regs.x = op_cmp(regs.x, rd); + } + + void SMPcore::op_cmp_y_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + regs.y = op_cmp(regs.y, rd); + } + + void SMPcore::op_eor_a_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_eor(regs.a, rd); + } + + void SMPcore::op_or_a_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_or(regs.a, rd); + } + + void SMPcore::op_sbc_a_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_sbc(regs.a, rd); + } + + + void SMPcore::op_adc_a_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_adc(regs.a, rd); + } + + void SMPcore::op_and_a_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_and(regs.a, rd); + } + + void SMPcore::op_cmp_a_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_cmp(regs.a, rd); + } + + void SMPcore::op_eor_a_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_eor(regs.a, rd); + } + + void SMPcore::op_or_a_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_or(regs.a, rd); + } + + void SMPcore::op_sbc_a_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_sbc(regs.a, rd); + } + + + void SMPcore::op_adc_a_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_adc(regs.a, rd); + } + + void SMPcore::op_and_a_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_and(regs.a, rd); + } + + void SMPcore::op_cmp_a_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_cmp(regs.a, rd); + } + + void SMPcore::op_cmp_x_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.x = op_cmp(regs.x, rd); + } + + void SMPcore::op_cmp_y_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.y = op_cmp(regs.y, rd); + } + + void SMPcore::op_eor_a_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_eor(regs.a, rd); + } + + void SMPcore::op_or_a_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_or(regs.a, rd); + } + + void SMPcore::op_sbc_a_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_sbc(regs.a, rd); + } + + + void SMPcore::op_adc_a_addrx() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_adc(regs.a, rd); + } + + void SMPcore::op_adc_a_addry() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_adc(regs.a, rd); + } + + void SMPcore::op_and_a_addrx() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_and(regs.a, rd); + } + + void SMPcore::op_and_a_addry() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_and(regs.a, rd); + } + + void SMPcore::op_cmp_a_addrx() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_cmp(regs.a, rd); + } + + void SMPcore::op_cmp_a_addry() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_cmp(regs.a, rd); + } + + void SMPcore::op_eor_a_addrx() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_eor(regs.a, rd); + } + + void SMPcore::op_eor_a_addry() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_eor(regs.a, rd); + } + + void SMPcore::op_or_a_addrx() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_or(regs.a, rd); + } + + void SMPcore::op_or_a_addry() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_or(regs.a, rd); + } + + void SMPcore::op_sbc_a_addrx() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_sbc(regs.a, rd); + } + + void SMPcore::op_sbc_a_addry() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_sbc(regs.a, rd); + } + + + void SMPcore::op_adc_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_adc(regs.a, rd); + } + + void SMPcore::op_and_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_and(regs.a, rd); + } + + void SMPcore::op_cmp_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_cmp(regs.a, rd); + } + + void SMPcore::op_eor_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_eor(regs.a, rd); + } + + void SMPcore::op_or_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_or(regs.a, rd); + } + + void SMPcore::op_sbc_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_sbc(regs.a, rd); + } + + + void SMPcore::op_adc_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_adc(regs.a, rd); + } + + void SMPcore::op_and_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_and(regs.a, rd); + } + + void SMPcore::op_cmp_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_cmp(regs.a, rd); + } + + void SMPcore::op_eor_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_eor(regs.a, rd); + } + + void SMPcore::op_or_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_or(regs.a, rd); + } + + void SMPcore::op_sbc_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_sbc(regs.a, rd); + } + + + void SMPcore::op_adc_ix_iy() { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_adc(wr, rd); + true ? op_writedp(regs.x, wr) : op_io(); + } + + void SMPcore::op_and_ix_iy() { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_and(wr, rd); + true ? op_writedp(regs.x, wr) : op_io(); + } + + void SMPcore::op_cmp_ix_iy() { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_cmp(wr, rd); + false ? op_writedp(regs.x, wr) : op_io(); + } + + void SMPcore::op_eor_ix_iy() { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_eor(wr, rd); + true ? op_writedp(regs.x, wr) : op_io(); + } + + void SMPcore::op_or_ix_iy() { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_or(wr, rd); + true ? op_writedp(regs.x, wr) : op_io(); + } + + void SMPcore::op_sbc_ix_iy() { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_sbc(wr, rd); + true ? op_writedp(regs.x, wr) : op_io(); + } + + + void SMPcore::op_adc_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_adc(wr, rd); + true ? op_writedp(dp, wr) : op_io(); + } + + void SMPcore::op_and_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_and(wr, rd); + true ? op_writedp(dp, wr) : op_io(); + } + + void SMPcore::op_cmp_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_cmp(wr, rd); + false ? op_writedp(dp, wr) : op_io(); + } + + void SMPcore::op_eor_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_eor(wr, rd); + true ? op_writedp(dp, wr) : op_io(); + } + + void SMPcore::op_or_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_or(wr, rd); + true ? op_writedp(dp, wr) : op_io(); + } + + void SMPcore::op_sbc_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_sbc(wr, rd); + true ? op_writedp(dp, wr) : op_io(); + } + + + void SMPcore::op_adc_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_adc(wr, rd); + true ? op_writedp(dp, wr) : op_io(); + } + + void SMPcore::op_and_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_and(wr, rd); + true ? op_writedp(dp, wr) : op_io(); + } + + void SMPcore::op_cmp_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_cmp(wr, rd); + false ? op_writedp(dp, wr) : op_io(); + } + + void SMPcore::op_eor_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_eor(wr, rd); + true ? op_writedp(dp, wr) : op_io(); + } + + void SMPcore::op_or_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_or(wr, rd); + true ? op_writedp(dp, wr) : op_io(); + } + + void SMPcore::op_sbc_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_sbc(wr, rd); + true ? op_writedp(dp, wr) : op_io(); + } + + + void SMPcore::op_addw_ya_dp() { + dp = op_readpc(); + rd = op_readdp(dp + 0) << 0; + op_io(); + rd |= op_readdp(dp + 1) << 8; + regs.ya = op_addw(regs.ya, rd); + } + + void SMPcore::op_subw_ya_dp() { + dp = op_readpc(); + rd = op_readdp(dp + 0) << 0; + op_io(); + rd |= op_readdp(dp + 1) << 8; + regs.ya = op_subw(regs.ya, rd); + } + + void SMPcore::op_cmpw_ya_dp() { + dp = op_readpc(); + rd = op_readdp(dp + 0) << 0; + rd |= op_readdp(dp + 1) << 8; + op_cmpw(regs.ya, rd); + } + + + void SMPcore::op_and1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + regs.p.c = regs.p.c & !!(rd & (1 << bit)); + } + + void SMPcore::op_and1_notbit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + regs.p.c = regs.p.c & !(rd & (1 << bit)); + } + + void SMPcore::op_eor1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c ^ !!(rd & (1 << bit)); + } + + void SMPcore::op_not1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + rd ^= (1 << bit); + op_writeaddr(dp, rd); + } + + void SMPcore::op_or1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c | !!(rd & (1 << bit)); + } + + void SMPcore::op_or1_notbit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c | !(rd & (1 << bit)); + } + + +//============== +//opcode_rmw.bpp +//============== + + void SMPcore::op_inc_a() { + op_io(); + regs.a = op_inc(regs.a); + } + + void SMPcore::op_inc_x() { + op_io(); + regs.x = op_inc(regs.x); + } + + void SMPcore::op_inc_y() { + op_io(); + regs.y = op_inc(regs.y); + } + + void SMPcore::op_dec_a() { + op_io(); + regs.a = op_dec(regs.a); + } + + void SMPcore::op_dec_x() { + op_io(); + regs.x = op_dec(regs.x); + } + + void SMPcore::op_dec_y() { + op_io(); + regs.y = op_dec(regs.y); + } + + void SMPcore::op_asl_a() { + op_io(); + regs.a = op_asl(regs.a); + } + + void SMPcore::op_lsr_a() { + op_io(); + regs.a = op_lsr(regs.a); + } + + void SMPcore::op_rol_a() { + op_io(); + regs.a = op_rol(regs.a); + } + + void SMPcore::op_ror_a() { + op_io(); + regs.a = op_ror(regs.a); + } + + + void SMPcore::op_inc_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_inc(rd); + op_writedp(dp, rd); + } + + void SMPcore::op_dec_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_dec(rd); + op_writedp(dp, rd); + } + + void SMPcore::op_asl_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_asl(rd); + op_writedp(dp, rd); + } + + void SMPcore::op_lsr_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_lsr(rd); + op_writedp(dp, rd); + } + + void SMPcore::op_rol_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_rol(rd); + op_writedp(dp, rd); + } + + void SMPcore::op_ror_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_ror(rd); + op_writedp(dp, rd); + } + + + void SMPcore::op_inc_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_inc(rd); + op_writedp(dp + regs.x, rd); + } + + void SMPcore::op_dec_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_dec(rd); + op_writedp(dp + regs.x, rd); + } + + void SMPcore::op_asl_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_asl(rd); + op_writedp(dp + regs.x, rd); + } + + void SMPcore::op_lsr_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_lsr(rd); + op_writedp(dp + regs.x, rd); + } + + void SMPcore::op_rol_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_rol(rd); + op_writedp(dp + regs.x, rd); + } + + void SMPcore::op_ror_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_ror(rd); + op_writedp(dp + regs.x, rd); + } + + + void SMPcore::op_inc_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_inc(rd); + op_writeaddr(dp, rd); + } + + void SMPcore::op_dec_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_dec(rd); + op_writeaddr(dp, rd); + } + + void SMPcore::op_asl_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_asl(rd); + op_writeaddr(dp, rd); + } + + void SMPcore::op_lsr_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_lsr(rd); + op_writeaddr(dp, rd); + } + + void SMPcore::op_rol_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_rol(rd); + op_writeaddr(dp, rd); + } + + void SMPcore::op_ror_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_ror(rd); + op_writeaddr(dp, rd); + } + + + void SMPcore::op_tset_addr_a() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.p.n = ((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); + op_readaddr(dp); + op_writeaddr(dp, rd | regs.a); + } + + void SMPcore::op_tclr_addr_a() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.p.n = ((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); + op_readaddr(dp); + op_writeaddr(dp, rd &~ regs.a); + } + + + void SMPcore::op_incw_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd ++; + op_writedp(dp++, rd); + rd += op_readdp(dp) << 8; + op_writedp(dp, rd >> 8); + regs.p.n = (rd & 0x8000); + regs.p.z = (rd == 0); + } + + void SMPcore::op_decw_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd --; + op_writedp(dp++, rd); + rd += op_readdp(dp) << 8; + op_writedp(dp, rd >> 8); + regs.p.n = (rd & 0x8000); + regs.p.z = (rd == 0); + } + + +//=============== +//opcode_misc.bpp +//=============== + + void SMPcore::op_nop() { + op_io(); + } + + void SMPcore::op_sleep() { + op_io(); + op_io(); + regs.pc--; + } + + void SMPcore::op_stop() { + op_io(); + op_io(); + regs.pc--; + } + + void SMPcore::op_xcn() { + op_io(); + op_io(); + op_io(); + op_io(); + regs.a = (regs.a >> 4) | (regs.a << 4); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + void SMPcore::op_daa() { + op_io(); + op_io(); + if(regs.p.c || (regs.a) > 0x99) { + regs.a += 0x60; + regs.p.c = 1; + } + if(regs.p.h || (regs.a & 15) > 0x09) { + regs.a += 0x06; + } + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + void SMPcore::op_das() { + op_io(); + op_io(); + if(!regs.p.c || (regs.a) > 0x99) { + regs.a -= 0x60; + regs.p.c = 0; + } + if(!regs.p.h || (regs.a & 15) > 0x09) { + regs.a -= 0x06; + } + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + + void SMPcore::op_clrc() { + op_io(); + regs.p.c=0; + } + + void SMPcore::op_clrp() { + op_io(); + regs.p.p=0; + } + + void SMPcore::op_setc() { + op_io(); + regs.p.c=1; + } + + void SMPcore::op_setp() { + op_io(); + regs.p.p=1; + } + + void SMPcore::op_clrv() { + op_io(); + regs.p.v=regs.p.h=0; + } + + void SMPcore::op_notc() { + op_io(); + op_io(); + regs.p.c = !regs.p.c; + } + + + void SMPcore::op_ei() { + op_io(); + op_io(); + regs.p.i = 1; + } + + void SMPcore::op_di() { + op_io(); + op_io(); + regs.p.i = 0; + } + + + void SMPcore::op_set0_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd |=0x01; + op_writedp(dp, rd); + } + + void SMPcore::op_clr0_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd &=~0x01; + op_writedp(dp, rd); + } + + void SMPcore::op_set1_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd |=0x02; + op_writedp(dp, rd); + } + + void SMPcore::op_clr1_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd &=~0x02; + op_writedp(dp, rd); + } + + void SMPcore::op_set2_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd |=0x04; + op_writedp(dp, rd); + } + + void SMPcore::op_clr2_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd &=~0x04; + op_writedp(dp, rd); + } + + void SMPcore::op_set3_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd |=0x08; + op_writedp(dp, rd); + } + + void SMPcore::op_clr3_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd &=~0x08; + op_writedp(dp, rd); + } + + void SMPcore::op_set4_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd |=0x10; + op_writedp(dp, rd); + } + + void SMPcore::op_clr4_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd &=~0x10; + op_writedp(dp, rd); + } + + void SMPcore::op_set5_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd |=0x20; + op_writedp(dp, rd); + } + + void SMPcore::op_clr5_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd &=~0x20; + op_writedp(dp, rd); + } + + void SMPcore::op_set6_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd |=0x40; + op_writedp(dp, rd); + } + + void SMPcore::op_clr6_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd &=~0x40; + op_writedp(dp, rd); + } + + void SMPcore::op_set7_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd |=0x80; + op_writedp(dp, rd); + } + + void SMPcore::op_clr7_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd &=~0x80; + op_writedp(dp, rd); + } + + + void SMPcore::op_push_a() { + op_io(); + op_io(); + op_writestack(regs.a); + } + + void SMPcore::op_push_x() { + op_io(); + op_io(); + op_writestack(regs.x); + } + + void SMPcore::op_push_y() { + op_io(); + op_io(); + op_writestack(regs.y); + } + + void SMPcore::op_push_p() { + op_io(); + op_io(); + op_writestack(regs.p); + } + + + void SMPcore::op_pop_a() { + op_io(); + op_io(); + regs.a = op_readstack(); + } + + void SMPcore::op_pop_x() { + op_io(); + op_io(); + regs.x = op_readstack(); + } + + void SMPcore::op_pop_y() { + op_io(); + op_io(); + regs.y = op_readstack(); + } + + void SMPcore::op_pop_p() { + op_io(); + op_io(); + regs.p = op_readstack(); + } + + + void SMPcore::op_mul_ya() { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.y * regs.a; + regs.a = ya; + regs.y = ya >> 8; + //result is set based on y (high-byte) only + regs.p.n = (regs.y & 0x80); + regs.p.z = (regs.y == 0); + } + + void SMPcore::op_div_ya_x() { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.ya; + //overflow set if quotient >= 256 + regs.p.v = (regs.y >= regs.x); + regs.p.h = ((regs.y & 15) >= (regs.x & 15)); + if(regs.y < (regs.x << 1)) { + //if quotient is <= 511 (will fit into 9-bit result) + regs.a = ya / regs.x; + regs.y = ya % regs.x; + } else { + //otherwise, the quotient won't fit into regs.p.v + regs.a + //this emulates the odd behavior of the S-SMP in this case + regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); + regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); + } + //result is set based on a (quotient) only + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } + + + diff --git a/src/smp/core/opcode_headers.bpp b/src/smp/core/opcode_headers.bpp new file mode 100644 index 00000000..1ae3f5ec --- /dev/null +++ b/src/smp/core/opcode_headers.bpp @@ -0,0 +1,323 @@ +//=============== +//opcode_move.bpp +//=============== + +@macro op_move_reg(rd, rs) +void op_mov_{rd}_{rs}(); +@endmacro + +@macro op_move_sp_x() +void op_mov_sp_x(); +@endmacro + +@macro op_move_const(r) +void op_mov_{r}_const(); +@endmacro + +@macro op_move_a_ix() +void op_mov_a_ix(); +@endmacro + +@macro op_move_a_ixinc() +void op_mov_a_ixinc(); +@endmacro + +@macro op_move_dp(r) +void op_mov_{r}_dp(); +@endmacro + +@macro op_move_dpi(rd, rs) +void op_mov_{rd}_dp{rs}(); +@endmacro + +@macro op_move_addr(r) +void op_mov_{r}_addr(); +@endmacro + +@macro op_move_addri(r) +void op_mov_a_addr{r}(); +@endmacro + +@macro op_move_a_idpx() +void op_mov_a_idpx(); +@endmacro + +@macro op_move_a_idpy() +void op_mov_a_idpy(); +@endmacro + +@macro op_move_dp_dp() +void op_mov_dp_dp(); +@endmacro + +@macro op_move_dp_const() +void op_mov_dp_const(); +@endmacro + +@macro op_move_ix_a() +void op_mov_ix_a(); +@endmacro + +@macro op_move_ixinc_a() +void op_mov_ixinc_a(); +@endmacro + +@macro op_move_dp_reg(r) +void op_mov_dp_{r}(); +@endmacro + +@macro op_move_dpi_reg(ri, rs) +void op_mov_dp{ri}_{rs}(); +@endmacro + +@macro op_move_addr_reg(r) +void op_mov_addr_{r}(); +@endmacro + +@macro op_move_addri_reg(r) +void op_mov_addr{r}_a(); +@endmacro + +@macro op_move_idpx_a() +void op_mov_idpx_a(); +@endmacro + +@macro op_move_idpy_a() +void op_mov_idpy_a(); +@endmacro + +@macro op_move_ya_dp() +void op_movw_ya_dp(); +@endmacro + +@macro op_move_dp_ya() +void op_movw_dp_ya(); +@endmacro + +@macro op_mov1_c_bit() +void op_mov1_c_bit(); +@endmacro + +@macro op_mov1_bit_c() +void op_mov1_bit_c(); +@endmacro + +//============= +//opcode_pc.bpp +//============= + +@macro op_branch(name, condition) +void op_{name}(); +@endmacro + +@macro op_bitbranch(name, bit, condition) +void op_{name}(); +@endmacro + +@macro op_cbne_dp() +void op_cbne_dp(); +@endmacro + +@macro op_cbne_dpx() +void op_cbne_dpx(); +@endmacro + +@macro op_dbnz_dp() +void op_dbnz_dp(); +@endmacro + +@macro op_dbnz_y() +void op_dbnz_y(); +@endmacro + +@macro op_jmp_addr() +void op_jmp_addr(); +@endmacro + +@macro op_jmp_iaddrx() +void op_jmp_iaddrx(); +@endmacro + +@macro op_call() +void op_call(); +@endmacro + +@macro op_pcall() +void op_pcall(); +@endmacro + +@macro op_tcall(bit) +void op_tcall_{bit}(); +@endmacro + +@macro op_brk() +void op_brk(); +@endmacro + +@macro op_ret() +void op_ret(); +@endmacro + +@macro op_reti() +void op_reti(); +@endmacro + +//=============== +//opcode_read.bpp +//=============== + +@macro op_read_const(name, r) +void op_{name}_{r}_const(); +@endmacro + +@macro op_read_ix(name) +void op_{name}_a_ix(); +@endmacro + +@macro op_read_dp(name, r) +void op_{name}_{r}_dp(); +@endmacro + +@macro op_read_dpx(name) +void op_{name}_a_dpx(); +@endmacro + +@macro op_read_addr(name, r) +void op_{name}_{r}_addr(); +@endmacro + +@macro op_read_addrn(name, r) +void op_{name}_a_addr{r}(); +@endmacro + +@macro op_read_idpx(name) +void op_{name}_a_idpx(); +@endmacro + +@macro op_read_idpy(name) +void op_{name}_a_idpy(); +@endmacro + +@macro op_rmw_ix_iy(name, write) +void op_{name}_ix_iy(); +@endmacro + +@macro op_rmw_dp_dp(name, write) +void op_{name}_dp_dp(); +@endmacro + +@macro op_rmw_dp_const(name, write) +void op_{name}_dp_const(); +@endmacro + +@macro op_read_ya_dp(name) +void op_{name}_ya_dp(); +@endmacro + +@macro op_cmpw_ya_dp() +void op_cmpw_ya_dp(); +@endmacro + +@macro op_and1(name, op) +void op_and1_{name}(); +@endmacro + +@macro op_eor1() +void op_eor1_bit(); +@endmacro + +@macro op_not1() +void op_not1_bit(); +@endmacro + +@macro op_or1(name, op) +void op_or1_{name}(); +@endmacro + +//============== +//opcode_rmw.bpp +//============== + +@macro op_rmw_immediate(name, r) +void op_{name}_{r}(); +@endmacro + +@macro op_rmw_dp(name) +void op_{name}_dp(); +@endmacro + +@macro op_rmw_dpx(name) +void op_{name}_dpx(); +@endmacro + +@macro op_rmw_addr(name) +void op_{name}_addr(); +@endmacro + +@macro op_rmw_taddr(name, op) +void op_{name}_addr_a(); +@endmacro + +@macro op_rmw_adjust(name, op) +void op_{name}_dp(); +@endmacro + +//=============== +//opcode_misc.bpp +//=============== + +@macro op_nop() +void op_nop(); +@endmacro + +@macro op_stall(name) +void op_{name}(); +@endmacro + +@macro op_xcn() +void op_xcn(); +@endmacro + +@macro op_daa() +void op_daa(); +@endmacro + +@macro op_das() +void op_das(); +@endmacro + +@macro op_flagadjust(name, op) +void op_{name}(); +@endmacro + +@macro op_notc() +void op_notc(); +@endmacro + +@macro op_seti(name, state) +void op_{name}(); +@endmacro + +@macro op_bit_dp(name, op) +void op_{name}_dp(); +@endmacro + +@macro op_push(r) +void op_push_{r}(); +@endmacro + +@macro op_pop(r) +void op_pop_{r}(); +@endmacro + +@macro op_mul_ya() +void op_mul_ya(); +@endmacro + +@macro op_div_ya_x() +void op_div_ya_x(); +@endmacro + +// + +@include "opcode_list.bpp" diff --git a/src/smp/core/opcode_headers.hpp b/src/smp/core/opcode_headers.hpp new file mode 100644 index 00000000..ddc93cbc --- /dev/null +++ b/src/smp/core/opcode_headers.hpp @@ -0,0 +1,676 @@ +//=============== +//opcode_move.bpp +//=============== + + + + + + + + + + + + + + + + + + + + + + + + + + +//============= +//opcode_pc.bpp +//============= + + + + + + + + + + + + + + + +//=============== +//opcode_read.bpp +//=============== + + + + + + + + + + + + + + + + + + +//============== +//opcode_rmw.bpp +//============== + + + + + + + +//=============== +//opcode_misc.bpp +//=============== + + + + + + + + + + + + + + +// + +//=============== +//opcode_move.bpp +//=============== + +void op_mov_a_x(); + +void op_mov_a_y(); + +void op_mov_x_a(); + +void op_mov_y_a(); + +void op_mov_x_sp(); + +void op_mov_sp_x(); + + +void op_mov_a_const(); + +void op_mov_x_const(); + +void op_mov_y_const(); + + +void op_mov_a_ix(); + +void op_mov_a_ixinc(); + + +void op_mov_a_dp(); + +void op_mov_x_dp(); + +void op_mov_y_dp(); + + +void op_mov_a_dpx(); + +void op_mov_x_dpy(); + +void op_mov_y_dpx(); + + +void op_mov_a_addr(); + +void op_mov_x_addr(); + +void op_mov_y_addr(); + + +void op_mov_a_addrx(); + +void op_mov_a_addry(); + + +void op_mov_a_idpx(); + +void op_mov_a_idpy(); + +void op_mov_dp_dp(); + +void op_mov_dp_const(); + +void op_mov_ix_a(); + +void op_mov_ixinc_a(); + + +void op_mov_dp_a(); + +void op_mov_dp_x(); + +void op_mov_dp_y(); + + +void op_mov_dpx_a(); + +void op_mov_dpy_x(); + +void op_mov_dpx_y(); + + +void op_mov_addr_a(); + +void op_mov_addr_x(); + +void op_mov_addr_y(); + + +void op_mov_addrx_a(); + +void op_mov_addry_a(); + + +void op_mov_idpx_a(); + +void op_mov_idpy_a(); + + +void op_movw_ya_dp(); + +void op_movw_dp_ya(); + + +void op_mov1_c_bit(); + +void op_mov1_bit_c(); + + +//============= +//opcode_pc.bpp +//============= + +void op_bra(); + +void op_beq(); + +void op_bne(); + +void op_bcs(); + +void op_bcc(); + +void op_bvs(); + +void op_bvc(); + +void op_bmi(); + +void op_bpl(); + + +void op_bbs0(); + +void op_bbc0(); + +void op_bbs1(); + +void op_bbc1(); + +void op_bbs2(); + +void op_bbc2(); + +void op_bbs3(); + +void op_bbc3(); + +void op_bbs4(); + +void op_bbc4(); + +void op_bbs5(); + +void op_bbc5(); + +void op_bbs6(); + +void op_bbc6(); + +void op_bbs7(); + +void op_bbc7(); + + +void op_cbne_dp(); + +void op_cbne_dpx(); + +void op_dbnz_dp(); + +void op_dbnz_y(); + +void op_jmp_addr(); + +void op_jmp_iaddrx(); + +void op_call(); + +void op_pcall(); + + +void op_tcall_0(); + +void op_tcall_1(); + +void op_tcall_2(); + +void op_tcall_3(); + +void op_tcall_4(); + +void op_tcall_5(); + +void op_tcall_6(); + +void op_tcall_7(); + +void op_tcall_8(); + +void op_tcall_9(); + +void op_tcall_10(); + +void op_tcall_11(); + +void op_tcall_12(); + +void op_tcall_13(); + +void op_tcall_14(); + +void op_tcall_15(); + + +void op_brk(); + +void op_ret(); + +void op_reti(); + + +//=============== +//opcode_read.bpp +//=============== + +void op_adc_a_const(); + +void op_and_a_const(); + +void op_cmp_a_const(); + +void op_cmp_x_const(); + +void op_cmp_y_const(); + +void op_eor_a_const(); + +void op_or_a_const(); + +void op_sbc_a_const(); + + +void op_adc_a_ix(); + +void op_and_a_ix(); + +void op_cmp_a_ix(); + +void op_eor_a_ix(); + +void op_or_a_ix(); + +void op_sbc_a_ix(); + + +void op_adc_a_dp(); + +void op_and_a_dp(); + +void op_cmp_a_dp(); + +void op_cmp_x_dp(); + +void op_cmp_y_dp(); + +void op_eor_a_dp(); + +void op_or_a_dp(); + +void op_sbc_a_dp(); + + +void op_adc_a_dpx(); + +void op_and_a_dpx(); + +void op_cmp_a_dpx(); + +void op_eor_a_dpx(); + +void op_or_a_dpx(); + +void op_sbc_a_dpx(); + + +void op_adc_a_addr(); + +void op_and_a_addr(); + +void op_cmp_a_addr(); + +void op_cmp_x_addr(); + +void op_cmp_y_addr(); + +void op_eor_a_addr(); + +void op_or_a_addr(); + +void op_sbc_a_addr(); + + +void op_adc_a_addrx(); + +void op_adc_a_addry(); + +void op_and_a_addrx(); + +void op_and_a_addry(); + +void op_cmp_a_addrx(); + +void op_cmp_a_addry(); + +void op_eor_a_addrx(); + +void op_eor_a_addry(); + +void op_or_a_addrx(); + +void op_or_a_addry(); + +void op_sbc_a_addrx(); + +void op_sbc_a_addry(); + + +void op_adc_a_idpx(); + +void op_and_a_idpx(); + +void op_cmp_a_idpx(); + +void op_eor_a_idpx(); + +void op_or_a_idpx(); + +void op_sbc_a_idpx(); + + +void op_adc_a_idpy(); + +void op_and_a_idpy(); + +void op_cmp_a_idpy(); + +void op_eor_a_idpy(); + +void op_or_a_idpy(); + +void op_sbc_a_idpy(); + + +void op_adc_ix_iy(); + +void op_and_ix_iy(); + +void op_cmp_ix_iy(); + +void op_eor_ix_iy(); + +void op_or_ix_iy(); + +void op_sbc_ix_iy(); + + +void op_adc_dp_dp(); + +void op_and_dp_dp(); + +void op_cmp_dp_dp(); + +void op_eor_dp_dp(); + +void op_or_dp_dp(); + +void op_sbc_dp_dp(); + + +void op_adc_dp_const(); + +void op_and_dp_const(); + +void op_cmp_dp_const(); + +void op_eor_dp_const(); + +void op_or_dp_const(); + +void op_sbc_dp_const(); + + +void op_addw_ya_dp(); + +void op_subw_ya_dp(); + +void op_cmpw_ya_dp(); + + +void op_and1_bit(); + +void op_and1_notbit(); + +void op_eor1_bit(); + +void op_not1_bit(); + +void op_or1_bit(); + +void op_or1_notbit(); + + +//============== +//opcode_rmw.bpp +//============== + +void op_inc_a(); + +void op_inc_x(); + +void op_inc_y(); + +void op_dec_a(); + +void op_dec_x(); + +void op_dec_y(); + +void op_asl_a(); + +void op_lsr_a(); + +void op_rol_a(); + +void op_ror_a(); + + +void op_inc_dp(); + +void op_dec_dp(); + +void op_asl_dp(); + +void op_lsr_dp(); + +void op_rol_dp(); + +void op_ror_dp(); + + +void op_inc_dpx(); + +void op_dec_dpx(); + +void op_asl_dpx(); + +void op_lsr_dpx(); + +void op_rol_dpx(); + +void op_ror_dpx(); + + +void op_inc_addr(); + +void op_dec_addr(); + +void op_asl_addr(); + +void op_lsr_addr(); + +void op_rol_addr(); + +void op_ror_addr(); + + +void op_tset_addr_a(); + +void op_tclr_addr_a(); + + +void op_incw_dp(); + +void op_decw_dp(); + + +//=============== +//opcode_misc.bpp +//=============== + +void op_nop(); + +void op_sleep(); + +void op_stop(); + +void op_xcn(); + +void op_daa(); + +void op_das(); + + +void op_clrc(); + +void op_clrp(); + +void op_setc(); + +void op_setp(); + +void op_clrv(); + +void op_notc(); + + +void op_ei(); + +void op_di(); + + +void op_set0_dp(); + +void op_clr0_dp(); + +void op_set1_dp(); + +void op_clr1_dp(); + +void op_set2_dp(); + +void op_clr2_dp(); + +void op_set3_dp(); + +void op_clr3_dp(); + +void op_set4_dp(); + +void op_clr4_dp(); + +void op_set5_dp(); + +void op_clr5_dp(); + +void op_set6_dp(); + +void op_clr6_dp(); + +void op_set7_dp(); + +void op_clr7_dp(); + + +void op_push_a(); + +void op_push_x(); + +void op_push_y(); + +void op_push_p(); + + +void op_pop_a(); + +void op_pop_x(); + +void op_pop_y(); + +void op_pop_p(); + + +void op_mul_ya(); + +void op_div_ya_x(); + + + diff --git a/src/smp/core/opcode_list.bpp b/src/smp/core/opcode_list.bpp new file mode 100644 index 00000000..93119cbe --- /dev/null +++ b/src/smp/core/opcode_list.bpp @@ -0,0 +1,321 @@ +//=============== +//opcode_move.bpp +//=============== + +@op_move_reg(a, x) +@op_move_reg(a, y) +@op_move_reg(x, a) +@op_move_reg(y, a) +@op_move_reg(x, sp) +@op_move_sp_x() + +@op_move_const(a) +@op_move_const(x) +@op_move_const(y) + +@op_move_a_ix() +@op_move_a_ixinc() + +@op_move_dp(a) +@op_move_dp(x) +@op_move_dp(y) + +@op_move_dpi(a, x) +@op_move_dpi(x, y) +@op_move_dpi(y, x) + +@op_move_addr(a) +@op_move_addr(x) +@op_move_addr(y) + +@op_move_addri(x) +@op_move_addri(y) + +@op_move_a_idpx() +@op_move_a_idpy() +@op_move_dp_dp() +@op_move_dp_const() +@op_move_ix_a() +@op_move_ixinc_a() + +@op_move_dp_reg(a) +@op_move_dp_reg(x) +@op_move_dp_reg(y) + +@op_move_dpi_reg(x, a) +@op_move_dpi_reg(y, x) +@op_move_dpi_reg(x, y) + +@op_move_addr_reg(a) +@op_move_addr_reg(x) +@op_move_addr_reg(y) + +@op_move_addri_reg(x) +@op_move_addri_reg(y) + +@op_move_idpx_a() +@op_move_idpy_a() + +@op_move_ya_dp() +@op_move_dp_ya() + +@op_mov1_c_bit() +@op_mov1_bit_c() + +//============= +//opcode_pc.bpp +//============= + +@op_branch(bra, 0) +@op_branch(beq, !regs.p.z) +@op_branch(bne, regs.p.z) +@op_branch(bcs, !regs.p.c) +@op_branch(bcc, regs.p.c) +@op_branch(bvs, !regs.p.v) +@op_branch(bvc, regs.p.v) +@op_branch(bmi, !regs.p.n) +@op_branch(bpl, regs.p.n) + +@op_bitbranch(bbs0, 0x01, !=) +@op_bitbranch(bbc0, 0x01, ==) +@op_bitbranch(bbs1, 0x02, !=) +@op_bitbranch(bbc1, 0x02, ==) +@op_bitbranch(bbs2, 0x04, !=) +@op_bitbranch(bbc2, 0x04, ==) +@op_bitbranch(bbs3, 0x08, !=) +@op_bitbranch(bbc3, 0x08, ==) +@op_bitbranch(bbs4, 0x10, !=) +@op_bitbranch(bbc4, 0x10, ==) +@op_bitbranch(bbs5, 0x20, !=) +@op_bitbranch(bbc5, 0x20, ==) +@op_bitbranch(bbs6, 0x40, !=) +@op_bitbranch(bbc6, 0x40, ==) +@op_bitbranch(bbs7, 0x80, !=) +@op_bitbranch(bbc7, 0x80, ==) + +@op_cbne_dp() +@op_cbne_dpx() +@op_dbnz_dp() +@op_dbnz_y() +@op_jmp_addr() +@op_jmp_iaddrx() +@op_call() +@op_pcall() + +@op_tcall( 0) +@op_tcall( 1) +@op_tcall( 2) +@op_tcall( 3) +@op_tcall( 4) +@op_tcall( 5) +@op_tcall( 6) +@op_tcall( 7) +@op_tcall( 8) +@op_tcall( 9) +@op_tcall(10) +@op_tcall(11) +@op_tcall(12) +@op_tcall(13) +@op_tcall(14) +@op_tcall(15) + +@op_brk() +@op_ret() +@op_reti() + +//=============== +//opcode_read.bpp +//=============== + +@op_read_const(adc, a) +@op_read_const(and, a) +@op_read_const(cmp, a) +@op_read_const(cmp, x) +@op_read_const(cmp, y) +@op_read_const(eor, a) +@op_read_const(or, a) +@op_read_const(sbc, a) + +@op_read_ix(adc) +@op_read_ix(and) +@op_read_ix(cmp) +@op_read_ix(eor) +@op_read_ix(or) +@op_read_ix(sbc) + +@op_read_dp(adc, a) +@op_read_dp(and, a) +@op_read_dp(cmp, a) +@op_read_dp(cmp, x) +@op_read_dp(cmp, y) +@op_read_dp(eor, a) +@op_read_dp(or, a) +@op_read_dp(sbc, a) + +@op_read_dpx(adc) +@op_read_dpx(and) +@op_read_dpx(cmp) +@op_read_dpx(eor) +@op_read_dpx(or) +@op_read_dpx(sbc) + +@op_read_addr(adc, a) +@op_read_addr(and, a) +@op_read_addr(cmp, a) +@op_read_addr(cmp, x) +@op_read_addr(cmp, y) +@op_read_addr(eor, a) +@op_read_addr(or, a) +@op_read_addr(sbc, a) + +@op_read_addrn(adc, x) +@op_read_addrn(adc, y) +@op_read_addrn(and, x) +@op_read_addrn(and, y) +@op_read_addrn(cmp, x) +@op_read_addrn(cmp, y) +@op_read_addrn(eor, x) +@op_read_addrn(eor, y) +@op_read_addrn(or, x) +@op_read_addrn(or, y) +@op_read_addrn(sbc, x) +@op_read_addrn(sbc, y) + +@op_read_idpx(adc) +@op_read_idpx(and) +@op_read_idpx(cmp) +@op_read_idpx(eor) +@op_read_idpx(or) +@op_read_idpx(sbc) + +@op_read_idpy(adc) +@op_read_idpy(and) +@op_read_idpy(cmp) +@op_read_idpy(eor) +@op_read_idpy(or) +@op_read_idpy(sbc) + +@op_rmw_ix_iy(adc, true) +@op_rmw_ix_iy(and, true) +@op_rmw_ix_iy(cmp, false) +@op_rmw_ix_iy(eor, true) +@op_rmw_ix_iy(or, true) +@op_rmw_ix_iy(sbc, true) + +@op_rmw_dp_dp(adc, true) +@op_rmw_dp_dp(and, true) +@op_rmw_dp_dp(cmp, false) +@op_rmw_dp_dp(eor, true) +@op_rmw_dp_dp(or, true) +@op_rmw_dp_dp(sbc, true) + +@op_rmw_dp_const(adc, true) +@op_rmw_dp_const(and, true) +@op_rmw_dp_const(cmp, false) +@op_rmw_dp_const(eor, true) +@op_rmw_dp_const(or, true) +@op_rmw_dp_const(sbc, true) + +@op_read_ya_dp(addw) +@op_read_ya_dp(subw) +@op_cmpw_ya_dp() + +@op_and1(bit, !!) +@op_and1(notbit, !) +@op_eor1() +@op_not1() +@op_or1(bit, !!) +@op_or1(notbit, !) + +//============== +//opcode_rmw.bpp +//============== + +@op_rmw_immediate(inc, a) +@op_rmw_immediate(inc, x) +@op_rmw_immediate(inc, y) +@op_rmw_immediate(dec, a) +@op_rmw_immediate(dec, x) +@op_rmw_immediate(dec, y) +@op_rmw_immediate(asl, a) +@op_rmw_immediate(lsr, a) +@op_rmw_immediate(rol, a) +@op_rmw_immediate(ror, a) + +@op_rmw_dp(inc) +@op_rmw_dp(dec) +@op_rmw_dp(asl) +@op_rmw_dp(lsr) +@op_rmw_dp(rol) +@op_rmw_dp(ror) + +@op_rmw_dpx(inc) +@op_rmw_dpx(dec) +@op_rmw_dpx(asl) +@op_rmw_dpx(lsr) +@op_rmw_dpx(rol) +@op_rmw_dpx(ror) + +@op_rmw_addr(inc) +@op_rmw_addr(dec) +@op_rmw_addr(asl) +@op_rmw_addr(lsr) +@op_rmw_addr(rol) +@op_rmw_addr(ror) + +@op_rmw_taddr(tset, |) +@op_rmw_taddr(tclr, &~) + +@op_rmw_adjust(incw, ++) +@op_rmw_adjust(decw, --) + +//=============== +//opcode_misc.bpp +//=============== + +@op_nop() +@op_stall(sleep) +@op_stall(stop) +@op_xcn() +@op_daa() +@op_das() + +@op_flagadjust(clrc, regs.p.c = 0) +@op_flagadjust(clrp, regs.p.p = 0) +@op_flagadjust(setc, regs.p.c = 1) +@op_flagadjust(setp, regs.p.p = 1) +@op_flagadjust(clrv, regs.p.v = regs.p.h = 0) +@op_notc() + +@op_seti(ei, 1) +@op_seti(di, 0) + +@op_bit_dp(set0, |= 0x01) +@op_bit_dp(clr0, &= ~0x01) +@op_bit_dp(set1, |= 0x02) +@op_bit_dp(clr1, &= ~0x02) +@op_bit_dp(set2, |= 0x04) +@op_bit_dp(clr2, &= ~0x04) +@op_bit_dp(set3, |= 0x08) +@op_bit_dp(clr3, &= ~0x08) +@op_bit_dp(set4, |= 0x10) +@op_bit_dp(clr4, &= ~0x10) +@op_bit_dp(set5, |= 0x20) +@op_bit_dp(clr5, &= ~0x20) +@op_bit_dp(set6, |= 0x40) +@op_bit_dp(clr6, &= ~0x40) +@op_bit_dp(set7, |= 0x80) +@op_bit_dp(clr7, &= ~0x80) + +@op_push(a) +@op_push(x) +@op_push(y) +@op_push(p) + +@op_pop(a) +@op_pop(x) +@op_pop(y) +@op_pop(p) + +@op_mul_ya() +@op_div_ya_x() diff --git a/src/smp/core/opcode_misc.bpp b/src/smp/core/opcode_misc.bpp new file mode 100644 index 00000000..e0f123b3 --- /dev/null +++ b/src/smp/core/opcode_misc.bpp @@ -0,0 +1,157 @@ +@macro op_nop() + void {class}::op_nop() { + op_io(); + } +@endmacro + +@macro op_stall(name) + void {class}::op_{name}() { + op_io(); + op_io(); + regs.pc--; + } +@endmacro + +@macro op_xcn() + void {class}::op_xcn() { + op_io(); + op_io(); + op_io(); + op_io(); + regs.a = (regs.a >> 4) | (regs.a << 4); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } +@endmacro + +@macro op_daa() + void {class}::op_daa() { + op_io(); + op_io(); + if(regs.p.c || (regs.a) > 0x99) { + regs.a += 0x60; + regs.p.c = 1; + } + if(regs.p.h || (regs.a & 15) > 0x09) { + regs.a += 0x06; + } + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } +@endmacro + +@macro op_das() + void {class}::op_das() { + op_io(); + op_io(); + if(!regs.p.c || (regs.a) > 0x99) { + regs.a -= 0x60; + regs.p.c = 0; + } + if(!regs.p.h || (regs.a & 15) > 0x09) { + regs.a -= 0x06; + } + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } +@endmacro + +@macro op_flagadjust(name, op) + void {class}::op_{name}() { + op_io(); + {op}; + } +@endmacro + +@macro op_notc() + void {class}::op_notc() { + op_io(); + op_io(); + regs.p.c = !regs.p.c; + } +@endmacro + +@macro op_seti(name, state) + void {class}::op_{name}() { + op_io(); + op_io(); + regs.p.i = {state}; + } +@endmacro + +@macro op_bit_dp(name, op) + void {class}::op_{name}_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd {op}; + op_writedp(dp, rd); + } +@endmacro + +@macro op_push(r) + void {class}::op_push_{r}() { + op_io(); + op_io(); + op_writestack(regs.{r}); + } +@endmacro + +@macro op_pop(r) + void {class}::op_pop_{r}() { + op_io(); + op_io(); + regs.{r} = op_readstack(); + } +@endmacro + +@macro op_mul_ya() + void {class}::op_mul_ya() { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.y * regs.a; + regs.a = ya; + regs.y = ya >> 8; + //result is set based on y (high-byte) only + regs.p.n = (regs.y & 0x80); + regs.p.z = (regs.y == 0); + } +@endmacro + +@macro op_div_ya_x() + void {class}::op_div_ya_x() { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.ya; + //overflow set if quotient >= 256 + regs.p.v = (regs.y >= regs.x); + regs.p.h = ((regs.y & 15) >= (regs.x & 15)); + if(regs.y < (regs.x << 1)) { + //if quotient is <= 511 (will fit into 9-bit result) + regs.a = ya / regs.x; + regs.y = ya % regs.x; + } else { + //otherwise, the quotient won't fit into regs.p.v + regs.a + //this emulates the odd behavior of the S-SMP in this case + regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); + regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); + } + //result is set based on a (quotient) only + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } +@endmacro diff --git a/src/smp/core/opcode_move.bpp b/src/smp/core/opcode_move.bpp new file mode 100644 index 00000000..5b81b29d --- /dev/null +++ b/src/smp/core/opcode_move.bpp @@ -0,0 +1,247 @@ +@macro op_move_reg(rd, rs) + void {class}::op_mov_{rd}_{rs}() { + op_io(); + regs.{rd} = regs.{rs}; + regs.p.n = (regs.{rd} & 0x80); + regs.p.z = (regs.{rd} == 0); + } +@endmacro + +@macro op_move_sp_x() + void {class}::op_mov_sp_x() { + op_io(); + regs.sp = regs.x; + } +@endmacro + +@macro op_move_const(r) + void {class}::op_mov_{r}_const() { + regs.{r} = op_readpc(); + regs.p.n = (regs.{r} & 0x80); + regs.p.z = (regs.{r} == 0); + } +@endmacro + +@macro op_move_a_ix() + void {class}::op_mov_a_ix() { + op_io(); + regs.a = op_readdp(regs.x); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } +@endmacro + +@macro op_move_a_ixinc() + void {class}::op_mov_a_ixinc() { + op_io(); + regs.a = op_readdp(regs.x++); + op_io(); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } +@endmacro + +@macro op_move_dp(r) + void {class}::op_mov_{r}_dp() { + sp = op_readpc(); + regs.{r} = op_readdp(sp); + regs.p.n = (regs.{r} & 0x80); + regs.p.z = (regs.{r} == 0); + } +@endmacro + +@macro op_move_dpi(rd, rs) + void {class}::op_mov_{rd}_dp{rs}() { + sp = op_readpc(); + op_io(); + regs.{rd} = op_readdp(sp + regs.{rs}); + regs.p.n = (regs.{rd} & 0x80); + regs.p.z = (regs.{rd} == 0); + } +@endmacro + +@macro op_move_addr(r) + void {class}::op_mov_{r}_addr() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + regs.{r} = op_readaddr(sp); + regs.p.n = (regs.{r} & 0x80); + regs.p.z = (regs.{r} == 0); + } +@endmacro + +@macro op_move_addri(r) + void {class}::op_mov_a_addr{r}() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + op_io(); + regs.a = op_readaddr(sp + regs.{r}); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } +@endmacro + +@macro op_move_a_idpx() + void {class}::op_mov_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + regs.a = op_readaddr(sp); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } +@endmacro + +@macro op_move_a_idpy() + void {class}::op_mov_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + regs.a = op_readaddr(sp + regs.y); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + } +@endmacro + +@macro op_move_dp_dp() + void {class}::op_mov_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + op_writedp(dp, rd); + } +@endmacro + +@macro op_move_dp_const() + void {class}::op_mov_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, rd); + } +@endmacro + +@macro op_move_ix_a() + void {class}::op_mov_ix_a() { + op_io(); + op_readdp(regs.x); + op_writedp(regs.x, regs.a); + } +@endmacro + +@macro op_move_ixinc_a() + void {class}::op_mov_ixinc_a() { + op_io(); + op_io(); + op_writedp(regs.x++, regs.a); + } +@endmacro + +@macro op_move_dp_reg(r) + void {class}::op_mov_dp_{r}() { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.{r}); + } +@endmacro + +@macro op_move_dpi_reg(ri, rs) + void {class}::op_mov_dp{ri}_{rs}() { + dp = op_readpc(); + op_io(); + dp += regs.{ri}; + op_readdp(dp); + op_writedp(dp, regs.{rs}); + } +@endmacro + +@macro op_move_addr_reg(r) + void {class}::op_mov_addr_{r}() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.{r}); + } +@endmacro + +@macro op_move_addri_reg(r) + void {class}::op_mov_addr{r}_a() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + dp += regs.{r}; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + } +@endmacro + +@macro op_move_idpx_a() + void {class}::op_mov_idpx_a() { + sp = op_readpc(); + op_io(); + sp += regs.x; + dp = op_readdp(sp + 0) << 0; + dp |= op_readdp(sp + 1) << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + } +@endmacro + +@macro op_move_idpy_a() + void {class}::op_mov_idpy_a() { + sp = op_readpc(); + dp = op_readdp(sp + 0) << 0; + dp |= op_readdp(sp + 1) << 8; + op_io(); + dp += regs.y; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + } +@endmacro + +@macro op_move_ya_dp() + void {class}::op_movw_ya_dp() { + sp = op_readpc(); + regs.a = op_readdp(sp + 0); + op_io(); + regs.y = op_readdp(sp + 1); + regs.p.n = (regs.ya & 0x8000); + regs.p.z = (regs.ya == 0); + } +@endmacro + +@macro op_move_dp_ya() + void {class}::op_movw_dp_ya() { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp + 0, regs.a); + op_writedp(dp + 1, regs.y); + } +@endmacro + +@macro op_mov1_c_bit() + void {class}::op_mov1_c_bit() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + bit = sp >> 13; + sp &= 0x1fff; + rd = op_readaddr(sp); + regs.p.c = !!(rd & (1 << bit)); + } +@endmacro + +@macro op_mov1_bit_c() + void {class}::op_mov1_bit_c() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + if(regs.p.c) rd |= (1 << bit); + else rd &= ~(1 << bit); + op_io(); + op_writeaddr(dp, rd); + } +@endmacro diff --git a/src/smp/core/opcode_pc.bpp b/src/smp/core/opcode_pc.bpp new file mode 100644 index 00000000..12339029 --- /dev/null +++ b/src/smp/core/opcode_pc.bpp @@ -0,0 +1,169 @@ +@macro op_branch(name, condition) + void {class}::op_{name}() { + rd = op_readpc(); + if({condition}) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } +@endmacro + +@macro op_bitbranch(name, bit, condition) + void {class}::op_{name}() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & {bit}) {condition} {bit}) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } +@endmacro + +@macro op_cbne_dp() + void {class}::op_cbne_dp() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if(regs.a == sp) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } +@endmacro + +@macro op_cbne_dpx() + void {class}::op_cbne_dpx() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + regs.x); + rd = op_readpc(); + op_io(); + if(regs.a == sp) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } +@endmacro + +@macro op_dbnz_dp() + void {class}::op_dbnz_dp() { + dp = op_readpc(); + wr = op_readdp(dp); + op_writedp(dp, --wr); + rd = op_readpc(); + if(wr == 0) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } +@endmacro + +@macro op_dbnz_y() + void {class}::op_dbnz_y() { + rd = op_readpc(); + op_io(); + regs.y--; + op_io(); + if(regs.y == 0) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; + } +@endmacro + +@macro op_jmp_addr() + void {class}::op_jmp_addr() { + rd = op_readpc() << 0; + rd |= op_readpc() << 8; + regs.pc = rd; + } +@endmacro + +@macro op_jmp_iaddrx() + void {class}::op_jmp_iaddrx() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + dp += regs.x; + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + regs.pc = rd; + } +@endmacro + +@macro op_call() + void {class}::op_call() { + rd = op_readpc() << 0; + rd |= op_readpc() << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } +@endmacro + +@macro op_pcall() + void {class}::op_pcall() { + rd = op_readpc(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = 0xff00 | rd; + } +@endmacro + +@macro op_tcall(bit) + void {class}::op_tcall_{bit}() { + dp = 0xffde - ({bit} << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; + } +@endmacro + +@macro op_brk() + void {class}::op_brk() { + rd = op_readaddr(0xffde) << 0; + rd |= op_readaddr(0xffdf) << 8; + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + op_writestack(regs.p); + regs.pc = rd; + regs.p.b = 1; + regs.p.i = 0; + } +@endmacro + +@macro op_ret() + void {class}::op_ret() { + rd = op_readstack() << 0; + rd |= op_readstack() << 8; + op_io(); + op_io(); + regs.pc = rd; + } +@endmacro + +@macro op_reti() + void {class}::op_reti() { + regs.p = op_readstack(); + rd = op_readstack() << 0; + rd |= op_readstack() << 8; + op_io(); + op_io(); + regs.pc = rd; + } +@endmacro diff --git a/src/smp/core/opcode_read.bpp b/src/smp/core/opcode_read.bpp new file mode 100644 index 00000000..5cb45b1f --- /dev/null +++ b/src/smp/core/opcode_read.bpp @@ -0,0 +1,169 @@ +@macro op_read_const(name, r) + void {class}::op_{name}_{r}_const() { + rd = op_readpc(); + regs.{r} = op_{name}(regs.{r}, rd); + } +@endmacro + +@macro op_read_ix(name) + void {class}::op_{name}_a_ix() { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_{name}(regs.a, rd); + } +@endmacro + +@macro op_read_dp(name, r) + void {class}::op_{name}_{r}_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + regs.{r} = op_{name}(regs.{r}, rd); + } +@endmacro + +@macro op_read_dpx(name) + void {class}::op_{name}_a_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_{name}(regs.a, rd); + } +@endmacro + +@macro op_read_addr(name, r) + void {class}::op_{name}_{r}_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.{r} = op_{name}(regs.{r}, rd); + } +@endmacro + +@macro op_read_addrn(name, r) + void {class}::op_{name}_a_addr{r}() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.{r}); + regs.a = op_{name}(regs.a, rd); + } +@endmacro + +@macro op_read_idpx(name) + void {class}::op_{name}_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_{name}(regs.a, rd); + } +@endmacro + +@macro op_read_idpy(name) + void {class}::op_{name}_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_{name}(regs.a, rd); + } +@endmacro + +@macro op_rmw_ix_iy(name, write) + void {class}::op_{name}_ix_iy() { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_{name}(wr, rd); + {write} ? op_writedp(regs.x, wr) : op_io(); + } +@endmacro + +@macro op_rmw_dp_dp(name, write) + void {class}::op_{name}_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_{name}(wr, rd); + {write} ? op_writedp(dp, wr) : op_io(); + } +@endmacro + +@macro op_rmw_dp_const(name, write) + void {class}::op_{name}_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_{name}(wr, rd); + {write} ? op_writedp(dp, wr) : op_io(); + } +@endmacro + +@macro op_read_ya_dp(name) + void {class}::op_{name}_ya_dp() { + dp = op_readpc(); + rd = op_readdp(dp + 0) << 0; + op_io(); + rd |= op_readdp(dp + 1) << 8; + regs.ya = op_{name}(regs.ya, rd); + } +@endmacro + +@macro op_cmpw_ya_dp() + void {class}::op_cmpw_ya_dp() { + dp = op_readpc(); + rd = op_readdp(dp + 0) << 0; + rd |= op_readdp(dp + 1) << 8; + op_cmpw(regs.ya, rd); + } +@endmacro + +@macro op_and1(name, op) + void {class}::op_and1_{name}() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + regs.p.c = regs.p.c & {op}(rd & (1 << bit)); + } +@endmacro + +@macro op_eor1() + void {class}::op_eor1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c ^ !!(rd & (1 << bit)); + } +@endmacro + +@macro op_not1() + void {class}::op_not1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + rd ^= (1 << bit); + op_writeaddr(dp, rd); + } +@endmacro + +@macro op_or1(name, op) + void {class}::op_or1_{name}() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c | {op}(rd & (1 << bit)); + } +@endmacro diff --git a/src/smp/core/opcode_rmw.bpp b/src/smp/core/opcode_rmw.bpp new file mode 100644 index 00000000..f771608e --- /dev/null +++ b/src/smp/core/opcode_rmw.bpp @@ -0,0 +1,60 @@ +@macro op_rmw_immediate(name, r) + void {class}::op_{name}_{r}() { + op_io(); + regs.{r} = op_{name}(regs.{r}); + } +@endmacro + +@macro op_rmw_dp(name) + void {class}::op_{name}_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_{name}(rd); + op_writedp(dp, rd); + } +@endmacro + +@macro op_rmw_dpx(name) + void {class}::op_{name}_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_{name}(rd); + op_writedp(dp + regs.x, rd); + } +@endmacro + +@macro op_rmw_addr(name) + void {class}::op_{name}_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_{name}(rd); + op_writeaddr(dp, rd); + } +@endmacro + +@macro op_rmw_taddr(name, op) + void {class}::op_{name}_addr_a() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.p.n = ((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); + op_readaddr(dp); + op_writeaddr(dp, rd {op} regs.a); + } +@endmacro + +@macro op_rmw_adjust(name, op) + void {class}::op_{name}_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd {op}; + op_writedp(dp++, rd); + rd += op_readdp(dp) << 8; + op_writedp(dp, rd >> 8); + regs.p.n = (rd & 0x8000); + regs.p.z = (rd == 0); + } +@endmacro diff --git a/src/smp/core/opcode_table.cpp b/src/smp/core/opcode_table.cpp new file mode 100644 index 00000000..48dfa0cf --- /dev/null +++ b/src/smp/core/opcode_table.cpp @@ -0,0 +1,70 @@ +void SMPcore::initialize_opcode_table() { + #define op(id, name) opcode_table[id] = &SMPcore::op_ ## name; + + op(0x00, nop) op(0x01, tcall_0) op(0x02, set0_dp) op(0x03, bbs0) + op(0x04, or_a_dp) op(0x05, or_a_addr) op(0x06, or_a_ix) op(0x07, or_a_idpx) + op(0x08, or_a_const) op(0x09, or_dp_dp) op(0x0a, or1_bit) op(0x0b, asl_dp) + op(0x0c, asl_addr) op(0x0d, push_p) op(0x0e, tset_addr_a) op(0x0f, brk) + op(0x10, bpl) op(0x11, tcall_1) op(0x12, clr0_dp) op(0x13, bbc0) + op(0x14, or_a_dpx) op(0x15, or_a_addrx) op(0x16, or_a_addry) op(0x17, or_a_idpy) + op(0x18, or_dp_const) op(0x19, or_ix_iy) op(0x1a, decw_dp) op(0x1b, asl_dpx) + op(0x1c, asl_a) op(0x1d, dec_x) op(0x1e, cmp_x_addr) op(0x1f, jmp_iaddrx) + op(0x20, clrp) op(0x21, tcall_2) op(0x22, set1_dp) op(0x23, bbs1) + op(0x24, and_a_dp) op(0x25, and_a_addr) op(0x26, and_a_ix) op(0x27, and_a_idpx) + op(0x28, and_a_const) op(0x29, and_dp_dp) op(0x2a, or1_notbit) op(0x2b, rol_dp) + op(0x2c, rol_addr) op(0x2d, push_a) op(0x2e, cbne_dp) op(0x2f, bra) + op(0x30, bmi) op(0x31, tcall_3) op(0x32, clr1_dp) op(0x33, bbc1) + op(0x34, and_a_dpx) op(0x35, and_a_addrx) op(0x36, and_a_addry) op(0x37, and_a_idpy) + op(0x38, and_dp_const) op(0x39, and_ix_iy) op(0x3a, incw_dp) op(0x3b, rol_dpx) + op(0x3c, rol_a) op(0x3d, inc_x) op(0x3e, cmp_x_dp) op(0x3f, call) + op(0x40, setp) op(0x41, tcall_4) op(0x42, set2_dp) op(0x43, bbs2) + op(0x44, eor_a_dp) op(0x45, eor_a_addr) op(0x46, eor_a_ix) op(0x47, eor_a_idpx) + op(0x48, eor_a_const) op(0x49, eor_dp_dp) op(0x4a, and1_bit) op(0x4b, lsr_dp) + op(0x4c, lsr_addr) op(0x4d, push_x) op(0x4e, tclr_addr_a) op(0x4f, pcall) + op(0x50, bvc) op(0x51, tcall_5) op(0x52, clr2_dp) op(0x53, bbc2) + op(0x54, eor_a_dpx) op(0x55, eor_a_addrx) op(0x56, eor_a_addry) op(0x57, eor_a_idpy) + op(0x58, eor_dp_const) op(0x59, eor_ix_iy) op(0x5a, cmpw_ya_dp) op(0x5b, lsr_dpx) + op(0x5c, lsr_a) op(0x5d, mov_x_a) op(0x5e, cmp_y_addr) op(0x5f, jmp_addr) + op(0x60, clrc) op(0x61, tcall_6) op(0x62, set3_dp) op(0x63, bbs3) + op(0x64, cmp_a_dp) op(0x65, cmp_a_addr) op(0x66, cmp_a_ix) op(0x67, cmp_a_idpx) + op(0x68, cmp_a_const) op(0x69, cmp_dp_dp) op(0x6a, and1_notbit) op(0x6b, ror_dp) + op(0x6c, ror_addr) op(0x6d, push_y) op(0x6e, dbnz_dp) op(0x6f, ret) + op(0x70, bvs) op(0x71, tcall_7) op(0x72, clr3_dp) op(0x73, bbc3) + op(0x74, cmp_a_dpx) op(0x75, cmp_a_addrx) op(0x76, cmp_a_addry) op(0x77, cmp_a_idpy) + op(0x78, cmp_dp_const) op(0x79, cmp_ix_iy) op(0x7a, addw_ya_dp) op(0x7b, ror_dpx) + op(0x7c, ror_a) op(0x7d, mov_a_x) op(0x7e, cmp_y_dp) op(0x7f, reti) + op(0x80, setc) op(0x81, tcall_8) op(0x82, set4_dp) op(0x83, bbs4) + op(0x84, adc_a_dp) op(0x85, adc_a_addr) op(0x86, adc_a_ix) op(0x87, adc_a_idpx) + op(0x88, adc_a_const) op(0x89, adc_dp_dp) op(0x8a, eor1_bit) op(0x8b, dec_dp) + op(0x8c, dec_addr) op(0x8d, mov_y_const) op(0x8e, pop_p) op(0x8f, mov_dp_const) + op(0x90, bcc) op(0x91, tcall_9) op(0x92, clr4_dp) op(0x93, bbc4) + op(0x94, adc_a_dpx) op(0x95, adc_a_addrx) op(0x96, adc_a_addry) op(0x97, adc_a_idpy) + op(0x98, adc_dp_const) op(0x99, adc_ix_iy) op(0x9a, subw_ya_dp) op(0x9b, dec_dpx) + op(0x9c, dec_a) op(0x9d, mov_x_sp) op(0x9e, div_ya_x) op(0x9f, xcn) + op(0xa0, ei) op(0xa1, tcall_10) op(0xa2, set5_dp) op(0xa3, bbs5) + op(0xa4, sbc_a_dp) op(0xa5, sbc_a_addr) op(0xa6, sbc_a_ix) op(0xa7, sbc_a_idpx) + op(0xa8, sbc_a_const) op(0xa9, sbc_dp_dp) op(0xaa, mov1_c_bit) op(0xab, inc_dp) + op(0xac, inc_addr) op(0xad, cmp_y_const) op(0xae, pop_a) op(0xaf, mov_ixinc_a) + op(0xb0, bcs) op(0xb1, tcall_11) op(0xb2, clr5_dp) op(0xb3, bbc5) + op(0xb4, sbc_a_dpx) op(0xb5, sbc_a_addrx) op(0xb6, sbc_a_addry) op(0xb7, sbc_a_idpy) + op(0xb8, sbc_dp_const) op(0xb9, sbc_ix_iy) op(0xba, movw_ya_dp) op(0xbb, inc_dpx) + op(0xbc, inc_a) op(0xbd, mov_sp_x) op(0xbe, das) op(0xbf, mov_a_ixinc) + op(0xc0, di) op(0xc1, tcall_12) op(0xc2, set6_dp) op(0xc3, bbs6) + op(0xc4, mov_dp_a) op(0xc5, mov_addr_a) op(0xc6, mov_ix_a) op(0xc7, mov_idpx_a) + op(0xc8, cmp_x_const) op(0xc9, mov_addr_x) op(0xca, mov1_bit_c) op(0xcb, mov_dp_y) + op(0xcc, mov_addr_y) op(0xcd, mov_x_const) op(0xce, pop_x) op(0xcf, mul_ya) + op(0xd0, bne) op(0xd1, tcall_13) op(0xd2, clr6_dp) op(0xd3, bbc6) + op(0xd4, mov_dpx_a) op(0xd5, mov_addrx_a) op(0xd6, mov_addry_a) op(0xd7, mov_idpy_a) + op(0xd8, mov_dp_x) op(0xd9, mov_dpy_x) op(0xda, movw_dp_ya) op(0xdb, mov_dpx_y) + op(0xdc, dec_y) op(0xdd, mov_a_y) op(0xde, cbne_dpx) op(0xdf, daa) + op(0xe0, clrv) op(0xe1, tcall_14) op(0xe2, set7_dp) op(0xe3, bbs7) + op(0xe4, mov_a_dp) op(0xe5, mov_a_addr) op(0xe6, mov_a_ix) op(0xe7, mov_a_idpx) + op(0xe8, mov_a_const) op(0xe9, mov_x_addr) op(0xea, not1_bit) op(0xeb, mov_y_dp) + op(0xec, mov_y_addr) op(0xed, notc) op(0xee, pop_y) op(0xef, sleep) + op(0xf0, beq) op(0xf1, tcall_15) op(0xf2, clr7_dp) op(0xf3, bbc7) + op(0xf4, mov_a_dpx) op(0xf5, mov_a_addrx) op(0xf6, mov_a_addry) op(0xf7, mov_a_idpy) + op(0xf8, mov_x_dp) op(0xf9, mov_x_dpy) op(0xfa, mov_dp_dp) op(0xfb, mov_y_dpx) + op(0xfc, inc_y) op(0xfd, mov_y_a) op(0xfe, dbnz_y) op(0xff, stop) + + #undef op +} diff --git a/src/smp/smpregs.hpp b/src/smp/core/registers.hpp similarity index 100% rename from src/smp/smpregs.hpp rename to src/smp/core/registers.hpp diff --git a/src/smp/iplrom.hpp b/src/smp/iplrom.hpp deleted file mode 100644 index eafbd7b3..00000000 --- a/src/smp/iplrom.hpp +++ /dev/null @@ -1,40 +0,0 @@ -//this is the IPLROM for the S-SMP coprocessor. -//the S-SMP does not allow writing to the IPLROM. -//all writes are instead mapped to the extended -//RAM region, accessible when $f1.d7 is clear. - -const uint8_t SMP::iplrom[64] = { -/*ffc0*/ 0xcd, 0xef, //mov x,#$ef -/*ffc2*/ 0xbd, //mov sp,x -/*ffc3*/ 0xe8, 0x00, //mov a,#$00 -/*ffc5*/ 0xc6, //mov (x),a -/*ffc6*/ 0x1d, //dec x -/*ffc7*/ 0xd0, 0xfc, //bne $ffc5 -/*ffc9*/ 0x8f, 0xaa, 0xf4, //mov $f4,#$aa -/*ffcc*/ 0x8f, 0xbb, 0xf5, //mov $f5,#$bb -/*ffcf*/ 0x78, 0xcc, 0xf4, //cmp $f4,#$cc -/*ffd2*/ 0xd0, 0xfb, //bne $ffcf -/*ffd4*/ 0x2f, 0x19, //bra $ffef -/*ffd6*/ 0xeb, 0xf4, //mov y,$f4 -/*ffd8*/ 0xd0, 0xfc, //bne $ffd6 -/*ffda*/ 0x7e, 0xf4, //cmp y,$f4 -/*ffdc*/ 0xd0, 0x0b, //bne $ffe9 -/*ffde*/ 0xe4, 0xf5, //mov a,$f5 -/*ffe0*/ 0xcb, 0xf4, //mov $f4,y -/*ffe2*/ 0xd7, 0x00, //mov ($00)+y,a -/*ffe4*/ 0xfc, //inc y -/*ffe5*/ 0xd0, 0xf3, //bne $ffda -/*ffe7*/ 0xab, 0x01, //inc $01 -/*ffe9*/ 0x10, 0xef, //bpl $ffda -/*ffeb*/ 0x7e, 0xf4, //cmp y,$f4 -/*ffed*/ 0x10, 0xeb, //bpl $ffda -/*ffef*/ 0xba, 0xf6, //movw ya,$f6 -/*fff1*/ 0xda, 0x00, //movw $00,ya -/*fff3*/ 0xba, 0xf4, //movw ya,$f4 -/*fff5*/ 0xc4, 0xf4, //mov $f4,a -/*fff7*/ 0xdd, //mov a,y -/*fff8*/ 0x5d, //mov x,a -/*fff9*/ 0xd0, 0xdb, //bne $ffd6 -/*fffb*/ 0x1f, 0x00, 0x00, //jmp ($0000+x) -/*fffe*/ 0xc0, 0xff //reset vector location ($ffc0) -}; diff --git a/src/smp/smp.cpp b/src/smp/smp.cpp index 6392ac2e..66f9fcc5 100644 --- a/src/smp/smp.cpp +++ b/src/smp/smp.cpp @@ -1,5 +1,47 @@ -#include <../base.hpp> -#define SMP_CPP +#include <../base.hpp> -#include "iplrom.hpp" -#include "dsmp.cpp" +#define SMP_CPP +namespace SNES { + +//this is the IPLROM for the S-SMP coprocessor. +//the S-SMP does not allow writing to the IPLROM. +//all writes are instead mapped to the extended +//RAM region, accessible when $f1.d7 is clear. + +const uint8_t SMP::iplrom[64] = { +/*ffc0*/ 0xcd, 0xef, //mov x,#$ef +/*ffc2*/ 0xbd, //mov sp,x +/*ffc3*/ 0xe8, 0x00, //mov a,#$00 +/*ffc5*/ 0xc6, //mov (x),a +/*ffc6*/ 0x1d, //dec x +/*ffc7*/ 0xd0, 0xfc, //bne $ffc5 +/*ffc9*/ 0x8f, 0xaa, 0xf4, //mov $f4,#$aa +/*ffcc*/ 0x8f, 0xbb, 0xf5, //mov $f5,#$bb +/*ffcf*/ 0x78, 0xcc, 0xf4, //cmp $f4,#$cc +/*ffd2*/ 0xd0, 0xfb, //bne $ffcf +/*ffd4*/ 0x2f, 0x19, //bra $ffef +/*ffd6*/ 0xeb, 0xf4, //mov y,$f4 +/*ffd8*/ 0xd0, 0xfc, //bne $ffd6 +/*ffda*/ 0x7e, 0xf4, //cmp y,$f4 +/*ffdc*/ 0xd0, 0x0b, //bne $ffe9 +/*ffde*/ 0xe4, 0xf5, //mov a,$f5 +/*ffe0*/ 0xcb, 0xf4, //mov $f4,y +/*ffe2*/ 0xd7, 0x00, //mov ($00)+y,a +/*ffe4*/ 0xfc, //inc y +/*ffe5*/ 0xd0, 0xf3, //bne $ffda +/*ffe7*/ 0xab, 0x01, //inc $01 +/*ffe9*/ 0x10, 0xef, //bpl $ffda +/*ffeb*/ 0x7e, 0xf4, //cmp y,$f4 +/*ffed*/ 0x10, 0xeb, //bpl $ffda +/*ffef*/ 0xba, 0xf6, //movw ya,$f6 +/*fff1*/ 0xda, 0x00, //movw $00,ya +/*fff3*/ 0xba, 0xf4, //movw ya,$f4 +/*fff5*/ 0xc4, 0xf4, //mov $f4,a +/*fff7*/ 0xdd, //mov a,y +/*fff8*/ 0x5d, //mov x,a +/*fff9*/ 0xd0, 0xdb, //bne $ffd6 +/*fffb*/ 0x1f, 0x00, 0x00, //jmp ($0000+x) +/*fffe*/ 0xc0, 0xff //reset vector location ($ffc0) +}; + +}; diff --git a/src/smp/smp.hpp b/src/smp/smp.hpp index d4e39b96..ac98bf05 100644 --- a/src/smp/smp.hpp +++ b/src/smp/smp.hpp @@ -2,10 +2,6 @@ class SMP { public: virtual void enter() = 0; - #include "smpregs.hpp" - regs_t regs; - static const uint8_t iplrom[64]; - virtual uint8 ram_read(uint16 addr) = 0; virtual void ram_write(uint16 addr, uint8 value) = 0; //$f4-$f7 @@ -15,10 +11,7 @@ public: virtual void power() = 0; virtual void reset() = 0; - //debugging functions - virtual bool in_opcode(); - void disassemble_opcode(char *output); - inline uint16 __relb(int8 offset, int op_len); + static const uint8_t iplrom[64]; SMP() {} virtual ~SMP() {} diff --git a/src/smp/ssmp/core/cc.sh b/src/smp/ssmp/core/cc.sh deleted file mode 100644 index 6b94ac7b..00000000 --- a/src/smp/ssmp/core/cc.sh +++ /dev/null @@ -1,4 +0,0 @@ -g++ -c ssmpgen.cpp -I../../../lib -g++ -c ../../../lib/nall/string.cpp -I../../../lib -g++ -o ssmpgen ssmpgen.o string.o -rm *.o diff --git a/src/smp/ssmp/core/clean.sh b/src/smp/ssmp/core/clean.sh deleted file mode 100644 index b62bcbcf..00000000 --- a/src/smp/ssmp/core/clean.sh +++ /dev/null @@ -1 +0,0 @@ -rm ssmpgen diff --git a/src/smp/ssmp/core/core.cpp b/src/smp/ssmp/core/core.cpp deleted file mode 100644 index 3f81c19c..00000000 --- a/src/smp/ssmp/core/core.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#ifdef SSMP_CPP - -#include "opfn.cpp" - -void sSMP::enter() { - while(true) { - tracer.trace_smpop(); //traces SMP opcode (only if tracer is enabled) - - switch(op_readpc()) { - #include "op_mov.cpp" - #include "op_pc.cpp" - #include "op_read.cpp" - #include "op_rmw.cpp" - #include "op_misc.cpp" - } - - //forcefully sync S-CPU and S-SMP, in case chips are not communicating - static unsigned counter = 0; - if(++counter >= 128) { - counter = 0; - scheduler.sync_smpcpu(); - } - } -} - -#endif diff --git a/src/smp/ssmp/core/op_misc.b b/src/smp/ssmp/core/op_misc.b deleted file mode 100644 index a6baba95..00000000 --- a/src/smp/ssmp/core/op_misc.b +++ /dev/null @@ -1,163 +0,0 @@ -nop(0x00) { -1:op_io(); -} - -sleep(0xef), -stop(0xff) { -1:op_io(); -2:op_io(); - regs.pc--; -} - -xcn(0x9f) { -1:op_io(); -2:op_io(); -3:op_io(); -4:op_io(); - regs.a = (regs.a >> 4) | (regs.a << 4); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} - -daa(0xdf) { -1:op_io(); -2:op_io(); - if(regs.p.c || (regs.a) > 0x99) { - regs.a += 0x60; - regs.p.c = 1; - } - if(regs.p.h || (regs.a & 15) > 0x09) { - regs.a += 0x06; - } - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} - -das(0xbe) { -1:op_io(); -2:op_io(); - if(!regs.p.c || (regs.a) > 0x99) { - regs.a -= 0x60; - regs.p.c = 0; - } - if(!regs.p.h || (regs.a & 15) > 0x09) { - regs.a -= 0x06; - } - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} - -clrc(0x60, regs.p.c = 0), -clrp(0x20, regs.p.p = 0), -setc(0x80, regs.p.c = 1), -setp(0x40, regs.p.p = 1) { -1:op_io(); - $1; -} - -clrv(0xe0) { -1:op_io(); - regs.p.v = 0; - regs.p.h = 0; -} - -notc(0xed) { -1:op_io(); -2:op_io(); - regs.p.c = !regs.p.c; -} - -ei(0xa0, 1), -di(0xc0, 0) { -1:op_io(); -2:op_io(); - regs.p.i = $1; -} - -set0_dp(0x02, rd |= 0x01), -clr0_dp(0x12, rd &= ~0x01), -set1_dp(0x22, rd |= 0x02), -clr1_dp(0x32, rd &= ~0x02), -set2_dp(0x42, rd |= 0x04), -clr2_dp(0x52, rd &= ~0x04), -set3_dp(0x62, rd |= 0x08), -clr3_dp(0x72, rd &= ~0x08), -set4_dp(0x82, rd |= 0x10), -clr4_dp(0x92, rd &= ~0x10), -set5_dp(0xa2, rd |= 0x20), -clr5_dp(0xb2, rd &= ~0x20), -set6_dp(0xc2, rd |= 0x40), -clr6_dp(0xd2, rd &= ~0x40), -set7_dp(0xe2, rd |= 0x80), -clr7_dp(0xf2, rd &= ~0x80) { -1:dp = op_readpc(); -2:rd = op_readdp(dp); -3:$1; - op_writedp(dp, rd); -} - -push_a(0x2d, a), -push_x(0x4d, x), -push_y(0x6d, y), -push_p(0x0d, p) { -1:op_io(); -2:op_io(); -3:op_writestack(regs.$1); -} - -pop_a(0xae, a), -pop_x(0xce, x), -pop_y(0xee, y), -pop_p(0x8e, p) { -1:op_io(); -2:op_io(); -3:regs.$1 = op_readstack(); -} - -mul_ya(0xcf) { -1:op_io(); -2:op_io(); -3:op_io(); -4:op_io(); -5:op_io(); -6:op_io(); -7:op_io(); -8:op_io(); - ya = regs.y * regs.a; - regs.a = ya; - regs.y = ya >> 8; -//result is set based on y (high-byte) only - regs.p.n = !!(regs.y & 0x80); - regs.p.z = (regs.y == 0); -} - -div_ya_x(0x9e) { -1:op_io(); -2:op_io(); -3:op_io(); -4:op_io(); -5:op_io(); -6:op_io(); -7:op_io(); -8:op_io(); -9:op_io(); -10:op_io(); -11:op_io(); - ya = regs.ya; -//overflow set if quotient >= 256 - regs.p.v = !!(regs.y >= regs.x); - regs.p.h = !!((regs.y & 15) >= (regs.x & 15)); - if(regs.y < (regs.x << 1)) { - //if quotient is <= 511 (will fit into 9-bit result) - regs.a = ya / regs.x; - regs.y = ya % regs.x; - } else { - //otherwise, the quotient won't fit into regs.p.v + regs.a - //this emulates the odd behavior of the S-SMP in this case - regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); - regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); - } -//result is set based on a (quotient) only - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} diff --git a/src/smp/ssmp/core/op_misc.cpp b/src/smp/ssmp/core/op_misc.cpp deleted file mode 100644 index a778b9e7..00000000 --- a/src/smp/ssmp/core/op_misc.cpp +++ /dev/null @@ -1,349 +0,0 @@ -#ifdef SSMP_CPP - -//nop -case 0x00: { - op_io(); -} break; - -//sleep -case 0xef: { - op_io(); - op_io(); - regs.pc--; -} break; - -//stop -case 0xff: { - op_io(); - op_io(); - regs.pc--; -} break; - -//xcn -case 0x9f: { - op_io(); - op_io(); - op_io(); - op_io(); - regs.a = (regs.a >> 4) | (regs.a << 4); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//daa -case 0xdf: { - op_io(); - op_io(); - if(regs.p.c || (regs.a) > 0x99) { - regs.a += 0x60; - regs.p.c = 1; - } - if(regs.p.h || (regs.a & 15) > 0x09) { - regs.a += 0x06; - } - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//das -case 0xbe: { - op_io(); - op_io(); - if(!regs.p.c || (regs.a) > 0x99) { - regs.a -= 0x60; - regs.p.c = 0; - } - if(!regs.p.h || (regs.a & 15) > 0x09) { - regs.a -= 0x06; - } - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//clrc -case 0x60: { - op_io(); - regs.p.c = 0; -} break; - -//clrp -case 0x20: { - op_io(); - regs.p.p = 0; -} break; - -//setc -case 0x80: { - op_io(); - regs.p.c = 1; -} break; - -//setp -case 0x40: { - op_io(); - regs.p.p = 1; -} break; - -//clrv -case 0xe0: { - op_io(); - regs.p.v = 0; - regs.p.h = 0; -} break; - -//notc -case 0xed: { - op_io(); - op_io(); - regs.p.c = !regs.p.c; -} break; - -//ei -case 0xa0: { - op_io(); - op_io(); - regs.p.i = 1; -} break; - -//di -case 0xc0: { - op_io(); - op_io(); - regs.p.i = 0; -} break; - -//set0_dp -case 0x02: { - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x01; - op_writedp(dp, rd); -} break; - -//clr0_dp -case 0x12: { - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x01; - op_writedp(dp, rd); -} break; - -//set1_dp -case 0x22: { - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x02; - op_writedp(dp, rd); -} break; - -//clr1_dp -case 0x32: { - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x02; - op_writedp(dp, rd); -} break; - -//set2_dp -case 0x42: { - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x04; - op_writedp(dp, rd); -} break; - -//clr2_dp -case 0x52: { - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x04; - op_writedp(dp, rd); -} break; - -//set3_dp -case 0x62: { - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x08; - op_writedp(dp, rd); -} break; - -//clr3_dp -case 0x72: { - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x08; - op_writedp(dp, rd); -} break; - -//set4_dp -case 0x82: { - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x10; - op_writedp(dp, rd); -} break; - -//clr4_dp -case 0x92: { - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x10; - op_writedp(dp, rd); -} break; - -//set5_dp -case 0xa2: { - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x20; - op_writedp(dp, rd); -} break; - -//clr5_dp -case 0xb2: { - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x20; - op_writedp(dp, rd); -} break; - -//set6_dp -case 0xc2: { - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x40; - op_writedp(dp, rd); -} break; - -//clr6_dp -case 0xd2: { - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x40; - op_writedp(dp, rd); -} break; - -//set7_dp -case 0xe2: { - dp = op_readpc(); - rd = op_readdp(dp); - rd |= 0x80; - op_writedp(dp, rd); -} break; - -//clr7_dp -case 0xf2: { - dp = op_readpc(); - rd = op_readdp(dp); - rd &= ~0x80; - op_writedp(dp, rd); -} break; - -//push_a -case 0x2d: { - op_io(); - op_io(); - op_writestack(regs.a); -} break; - -//push_x -case 0x4d: { - op_io(); - op_io(); - op_writestack(regs.x); -} break; - -//push_y -case 0x6d: { - op_io(); - op_io(); - op_writestack(regs.y); -} break; - -//push_p -case 0x0d: { - op_io(); - op_io(); - op_writestack(regs.p); -} break; - -//pop_a -case 0xae: { - op_io(); - op_io(); - regs.a = op_readstack(); -} break; - -//pop_x -case 0xce: { - op_io(); - op_io(); - regs.x = op_readstack(); -} break; - -//pop_y -case 0xee: { - op_io(); - op_io(); - regs.y = op_readstack(); -} break; - -//pop_p -case 0x8e: { - op_io(); - op_io(); - regs.p = op_readstack(); -} break; - -//mul_ya -case 0xcf: { - op_io(); - op_io(); - op_io(); - op_io(); - op_io(); - op_io(); - op_io(); - op_io(); - ya = regs.y * regs.a; - regs.a = ya; - regs.y = ya >> 8; -//result is set based on y (high-byte) only - regs.p.n = !!(regs.y & 0x80); - regs.p.z = (regs.y == 0); -} break; - -//div_ya_x -case 0x9e: { - op_io(); - op_io(); - op_io(); - op_io(); - op_io(); - op_io(); - op_io(); - op_io(); - op_io(); - op_io(); - op_io(); - ya = regs.ya; -//overflow set if quotient >= 256 - regs.p.v = !!(regs.y >= regs.x); - regs.p.h = !!((regs.y & 15) >= (regs.x & 15)); - if(regs.y < (regs.x << 1)) { - //if quotient is <= 511 (will fit into 9-bit result) - regs.a = ya / regs.x; - regs.y = ya % regs.x; - } else { - //otherwise, the quotient won't fit into regs.p.v + regs.a - //this emulates the odd behavior of the S-SMP in this case - regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); - regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); - } -//result is set based on a (quotient) only - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -#endif diff --git a/src/smp/ssmp/core/op_mov.b b/src/smp/ssmp/core/op_mov.b deleted file mode 100644 index dee821af..00000000 --- a/src/smp/ssmp/core/op_mov.b +++ /dev/null @@ -1,217 +0,0 @@ -mov_a_x(0x7d, a, x), -mov_a_y(0xdd, a, y), -mov_x_a(0x5d, x, a), -mov_y_a(0xfd, y, a), -mov_x_sp(0x9d, x, sp) { -1:op_io(); - regs.$1 = regs.$2; - regs.p.n = !!(regs.$1 & 0x80); - regs.p.z = (regs.$1 == 0); -} - -mov_sp_x(0xbd, sp, x) { -1:op_io(); - regs.$1 = regs.$2; -} - -mov_a_const(0xe8, a), -mov_x_const(0xcd, x), -mov_y_const(0x8d, y) { -1:regs.$1 = op_readpc(); - regs.p.n = !!(regs.$1 & 0x80); - regs.p.z = (regs.$1 == 0); -} - -mov_a_ix(0xe6) { -1:op_io(); -2:regs.a = op_readdp(regs.x); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} - -mov_a_ixinc(0xbf) { -1:op_io(); -2:regs.a = op_readdp(regs.x++); -3:op_io(); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} - -mov_a_dp(0xe4, a), -mov_x_dp(0xf8, x), -mov_y_dp(0xeb, y) { -1:sp = op_readpc(); -2:regs.$1 = op_readdp(sp); - regs.p.n = !!(regs.$1 & 0x80); - regs.p.z = (regs.$1 == 0); -} - -mov_a_dpx(0xf4, a, x), -mov_x_dpy(0xf9, x, y), -mov_y_dpx(0xfb, y, x) { -1:sp = op_readpc(); -2:op_io(); -3:regs.$1 = op_readdp(sp + regs.$2); - regs.p.n = !!(regs.$1 & 0x80); - regs.p.z = (regs.$1 == 0); -} - -mov_a_addr(0xe5, a), -mov_x_addr(0xe9, x), -mov_y_addr(0xec, y) { -1:sp = op_readpc(); -2:sp |= op_readpc() << 8; -3:regs.$1 = op_readaddr(sp); - regs.p.n = !!(regs.$1 & 0x80); - regs.p.z = (regs.$1 == 0); -} - -mov_a_addrx(0xf5, x), -mov_a_addry(0xf6, y) { -1:sp = op_readpc(); -2:sp |= op_readpc() << 8; -3:op_io(); -4:regs.a = op_readaddr(sp + regs.$1); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} - -mov_a_idpx(0xe7) { -1:dp = op_readpc() + regs.x; -2:op_io(); -3:sp = op_readdp(dp); -4:sp |= op_readdp(dp + 1) << 8; -5:regs.a = op_readaddr(sp); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} - -mov_a_idpy(0xf7) { -1:dp = op_readpc(); -2:op_io(); -3:sp = op_readdp(dp); -4:sp |= op_readdp(dp + 1) << 8; -5:regs.a = op_readaddr(sp + regs.y); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} - -mov_dp_dp(0xfa) { -1:sp = op_readpc(); -2:rd = op_readdp(sp); -3:dp = op_readpc(); -4:op_writedp(dp, rd); -} - -mov_dp_const(0x8f) { -1:rd = op_readpc(); -2:dp = op_readpc(); -3:op_readdp(dp); -4:op_writedp(dp, rd); -} - -mov_ix_a(0xc6) { -1:op_io(); -2:op_readdp(regs.x); -3:op_writedp(regs.x, regs.a); -} - -mov_ixinc_a(0xaf) { -1:op_io(); -2:op_io(); -3:op_writedp(regs.x++, regs.a); -} - -mov_dp_a(0xc4, a), -mov_dp_x(0xd8, x), -mov_dp_y(0xcb, y) { -1:dp = op_readpc(); -2:op_readdp(dp); -3:op_writedp(dp, regs.$1); -} - -mov_dpx_a(0xd4, x, a), -mov_dpy_x(0xd9, y, x), -mov_dpx_y(0xdb, x, y) { -1:dp = op_readpc(); -2:op_io(); - dp += regs.$1; -3:op_readdp(dp); -4:op_writedp(dp, regs.$2); -} - -mov_addr_a(0xc5, a), -mov_addr_x(0xc9, x), -mov_addr_y(0xcc, y) { -1:dp = op_readpc(); -2:dp |= op_readpc() << 8; -3:op_readaddr(dp); -4:op_writeaddr(dp, regs.$1); -} - -mov_addrx_a(0xd5, x), -mov_addry_a(0xd6, y) { -1:dp = op_readpc(); -2:dp |= op_readpc() << 8; -3:op_io(); - dp += regs.$1; -4:op_readaddr(dp); -5:op_writeaddr(dp, regs.a); -} - -mov_idpx_a(0xc7) { -1:sp = op_readpc(); -2:op_io(); - sp += regs.x; -3:dp = op_readdp(sp); -4:dp |= op_readdp(sp + 1) << 8; -5:op_readaddr(dp); -6:op_writeaddr(dp, regs.a); -} - -mov_idpy_a(0xd7) { -1:sp = op_readpc(); -2:dp = op_readdp(sp); -3:dp |= op_readdp(sp + 1) << 8; -4:op_io(); - dp += regs.y; -5:op_readaddr(dp); -6:op_writeaddr(dp, regs.a); -} - -movw_ya_dp(0xba) { -1:sp = op_readpc(); -2:regs.a = op_readdp(sp); -3:op_io(); -4:regs.y = op_readdp(sp + 1); - regs.p.n = !!(regs.ya & 0x8000); - regs.p.z = (regs.ya == 0); -} - -movw_dp_ya(0xda) { -1:dp = op_readpc(); -2:op_readdp(dp); -3:op_writedp(dp, regs.a); -4:op_writedp(dp + 1, regs.y); -} - -mov1_c_bit(0xaa) { -1:sp = op_readpc(); -2:sp |= op_readpc() << 8; -3:bit = sp >> 13; - sp &= 0x1fff; - rd = op_readaddr(sp); - regs.p.c = !!(rd & (1 << bit)); -} - -mov1_bit_c(0xca) { -1:dp = op_readpc(); -2:dp |= op_readpc() << 8; -3:bit = dp >> 13; - dp &= 0x1fff; - rd = op_readaddr(dp); - if(regs.p.c)rd |= (1 << bit); - else rd &= ~(1 << bit); -4:op_io(); -5:op_writeaddr(dp, rd); -} diff --git a/src/smp/ssmp/core/op_mov.cpp b/src/smp/ssmp/core/op_mov.cpp deleted file mode 100644 index 03890394..00000000 --- a/src/smp/ssmp/core/op_mov.cpp +++ /dev/null @@ -1,392 +0,0 @@ -#ifdef SSMP_CPP - -//mov_a_x -case 0x7d: { - op_io(); - regs.a = regs.x; - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//mov_a_y -case 0xdd: { - op_io(); - regs.a = regs.y; - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//mov_x_a -case 0x5d: { - op_io(); - regs.x = regs.a; - regs.p.n = !!(regs.x & 0x80); - regs.p.z = (regs.x == 0); -} break; - -//mov_y_a -case 0xfd: { - op_io(); - regs.y = regs.a; - regs.p.n = !!(regs.y & 0x80); - regs.p.z = (regs.y == 0); -} break; - -//mov_x_sp -case 0x9d: { - op_io(); - regs.x = regs.sp; - regs.p.n = !!(regs.x & 0x80); - regs.p.z = (regs.x == 0); -} break; - -//mov_sp_x -case 0xbd: { - op_io(); - regs.sp = regs.x; -} break; - -//mov_a_const -case 0xe8: { - regs.a = op_readpc(); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//mov_x_const -case 0xcd: { - regs.x = op_readpc(); - regs.p.n = !!(regs.x & 0x80); - regs.p.z = (regs.x == 0); -} break; - -//mov_y_const -case 0x8d: { - regs.y = op_readpc(); - regs.p.n = !!(regs.y & 0x80); - regs.p.z = (regs.y == 0); -} break; - -//mov_a_ix -case 0xe6: { - op_io(); - regs.a = op_readdp(regs.x); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//mov_a_ixinc -case 0xbf: { - op_io(); - regs.a = op_readdp(regs.x++); - op_io(); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//mov_a_dp -case 0xe4: { - sp = op_readpc(); - regs.a = op_readdp(sp); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//mov_x_dp -case 0xf8: { - sp = op_readpc(); - regs.x = op_readdp(sp); - regs.p.n = !!(regs.x & 0x80); - regs.p.z = (regs.x == 0); -} break; - -//mov_y_dp -case 0xeb: { - sp = op_readpc(); - regs.y = op_readdp(sp); - regs.p.n = !!(regs.y & 0x80); - regs.p.z = (regs.y == 0); -} break; - -//mov_a_dpx -case 0xf4: { - sp = op_readpc(); - op_io(); - regs.a = op_readdp(sp + regs.x); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//mov_x_dpy -case 0xf9: { - sp = op_readpc(); - op_io(); - regs.x = op_readdp(sp + regs.y); - regs.p.n = !!(regs.x & 0x80); - regs.p.z = (regs.x == 0); -} break; - -//mov_y_dpx -case 0xfb: { - sp = op_readpc(); - op_io(); - regs.y = op_readdp(sp + regs.x); - regs.p.n = !!(regs.y & 0x80); - regs.p.z = (regs.y == 0); -} break; - -//mov_a_addr -case 0xe5: { - sp = op_readpc(); - sp |= op_readpc() << 8; - regs.a = op_readaddr(sp); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//mov_x_addr -case 0xe9: { - sp = op_readpc(); - sp |= op_readpc() << 8; - regs.x = op_readaddr(sp); - regs.p.n = !!(regs.x & 0x80); - regs.p.z = (regs.x == 0); -} break; - -//mov_y_addr -case 0xec: { - sp = op_readpc(); - sp |= op_readpc() << 8; - regs.y = op_readaddr(sp); - regs.p.n = !!(regs.y & 0x80); - regs.p.z = (regs.y == 0); -} break; - -//mov_a_addrx -case 0xf5: { - sp = op_readpc(); - sp |= op_readpc() << 8; - op_io(); - regs.a = op_readaddr(sp + regs.x); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//mov_a_addry -case 0xf6: { - sp = op_readpc(); - sp |= op_readpc() << 8; - op_io(); - regs.a = op_readaddr(sp + regs.y); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//mov_a_idpx -case 0xe7: { - dp = op_readpc() + regs.x; - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - regs.a = op_readaddr(sp); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//mov_a_idpy -case 0xf7: { - dp = op_readpc(); - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - regs.a = op_readaddr(sp + regs.y); - regs.p.n = !!(regs.a & 0x80); - regs.p.z = (regs.a == 0); -} break; - -//mov_dp_dp -case 0xfa: { - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - op_writedp(dp, rd); -} break; - -//mov_dp_const -case 0x8f: { - rd = op_readpc(); - dp = op_readpc(); - op_readdp(dp); - op_writedp(dp, rd); -} break; - -//mov_ix_a -case 0xc6: { - op_io(); - op_readdp(regs.x); - op_writedp(regs.x, regs.a); -} break; - -//mov_ixinc_a -case 0xaf: { - op_io(); - op_io(); - op_writedp(regs.x++, regs.a); -} break; - -//mov_dp_a -case 0xc4: { - dp = op_readpc(); - op_readdp(dp); - op_writedp(dp, regs.a); -} break; - -//mov_dp_x -case 0xd8: { - dp = op_readpc(); - op_readdp(dp); - op_writedp(dp, regs.x); -} break; - -//mov_dp_y -case 0xcb: { - dp = op_readpc(); - op_readdp(dp); - op_writedp(dp, regs.y); -} break; - -//mov_dpx_a -case 0xd4: { - dp = op_readpc(); - op_io(); - dp += regs.x; - op_readdp(dp); - op_writedp(dp, regs.a); -} break; - -//mov_dpy_x -case 0xd9: { - dp = op_readpc(); - op_io(); - dp += regs.y; - op_readdp(dp); - op_writedp(dp, regs.x); -} break; - -//mov_dpx_y -case 0xdb: { - dp = op_readpc(); - op_io(); - dp += regs.x; - op_readdp(dp); - op_writedp(dp, regs.y); -} break; - -//mov_addr_a -case 0xc5: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_readaddr(dp); - op_writeaddr(dp, regs.a); -} break; - -//mov_addr_x -case 0xc9: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_readaddr(dp); - op_writeaddr(dp, regs.x); -} break; - -//mov_addr_y -case 0xcc: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_readaddr(dp); - op_writeaddr(dp, regs.y); -} break; - -//mov_addrx_a -case 0xd5: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - dp += regs.x; - op_readaddr(dp); - op_writeaddr(dp, regs.a); -} break; - -//mov_addry_a -case 0xd6: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - dp += regs.y; - op_readaddr(dp); - op_writeaddr(dp, regs.a); -} break; - -//mov_idpx_a -case 0xc7: { - sp = op_readpc(); - op_io(); - sp += regs.x; - dp = op_readdp(sp); - dp |= op_readdp(sp + 1) << 8; - op_readaddr(dp); - op_writeaddr(dp, regs.a); -} break; - -//mov_idpy_a -case 0xd7: { - sp = op_readpc(); - dp = op_readdp(sp); - dp |= op_readdp(sp + 1) << 8; - op_io(); - dp += regs.y; - op_readaddr(dp); - op_writeaddr(dp, regs.a); -} break; - -//movw_ya_dp -case 0xba: { - sp = op_readpc(); - regs.a = op_readdp(sp); - op_io(); - regs.y = op_readdp(sp + 1); - regs.p.n = !!(regs.ya & 0x8000); - regs.p.z = (regs.ya == 0); -} break; - -//movw_dp_ya -case 0xda: { - dp = op_readpc(); - op_readdp(dp); - op_writedp(dp, regs.a); - op_writedp(dp + 1, regs.y); -} break; - -//mov1_c_bit -case 0xaa: { - sp = op_readpc(); - sp |= op_readpc() << 8; - bit = sp >> 13; - sp &= 0x1fff; - rd = op_readaddr(sp); - regs.p.c = !!(rd & (1 << bit)); -} break; - -//mov1_bit_c -case 0xca: { - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_readaddr(dp); - if(regs.p.c)rd |= (1 << bit); - else rd &= ~(1 << bit); - op_io(); - op_writeaddr(dp, rd); -} break; - -#endif diff --git a/src/smp/ssmp/core/op_pc.b b/src/smp/ssmp/core/op_pc.b deleted file mode 100644 index affaf844..00000000 --- a/src/smp/ssmp/core/op_pc.b +++ /dev/null @@ -1,179 +0,0 @@ -bra(0x2f, 0), -beq(0xf0, !regs.p.z), -bne(0xd0, regs.p.z), -bcs(0xb0, !regs.p.c), -bcc(0x90, regs.p.c), -bvs(0x70, !regs.p.v), -bvc(0x50, regs.p.v), -bmi(0x30, !regs.p.n), -bpl(0x10, regs.p.n) { -1:rd = op_readpc(); - if($1)end; -2:op_io(); -3:op_io(); - regs.pc += (int8)rd; -} - -bbs0(0x03, 0x01, !=), -bbc0(0x13, 0x01, ==), -bbs1(0x23, 0x02, !=), -bbc1(0x33, 0x02, ==), -bbs2(0x43, 0x04, !=), -bbc2(0x53, 0x04, ==), -bbs3(0x63, 0x08, !=), -bbc3(0x73, 0x08, ==), -bbs4(0x83, 0x10, !=), -bbc4(0x93, 0x10, ==), -bbs5(0xa3, 0x20, !=), -bbc5(0xb3, 0x20, ==), -bbs6(0xc3, 0x40, !=), -bbc6(0xd3, 0x40, ==), -bbs7(0xe3, 0x80, !=), -bbc7(0xf3, 0x80, ==) { -1:dp = op_readpc(); -2:sp = op_readdp(dp); -3:rd = op_readpc(); -4:op_io(); - if((sp & $1) $2 $1)end; -5:op_io(); -6:op_io(); - regs.pc += (int8)rd; -} - -cbne_dp(0x2e) { -1:dp = op_readpc(); -2:sp = op_readdp(dp); -3:rd = op_readpc(); -4:op_io(); - if(regs.a == sp)end; -5:op_io(); -6:op_io(); - regs.pc += (int8)rd; -} - -cbne_dpx(0xde) { -1:dp = op_readpc(); -2:op_io(); -3:sp = op_readdp(dp + regs.x); -4:rd = op_readpc(); -5:op_io(); - if(regs.a == sp)end; -6:op_io(); -7:op_io(); - regs.pc += (int8)rd; -} - -dbnz_dp(0x6e) { -1:dp = op_readpc(); -2:wr = op_readdp(dp); -3:op_writedp(dp, --wr); -4:rd = op_readpc(); - if(wr == 0x00)end; -5:op_io(); -6:op_io(); - regs.pc += (int8)rd; -} - -dbnz_y(0xfe) { -1:rd = op_readpc(); -2:op_io(); - regs.y--; -3:op_io(); - if(regs.y == 0x00)end; -4:op_io(); -5:op_io(); - regs.pc += (int8)rd; -} - -jmp_addr(0x5f) { -1:rd = op_readpc(); -2:rd |= op_readpc() << 8; - regs.pc = rd; -} - -jmp_iaddrx(0x1f) { -1:dp = op_readpc(); -2:dp |= op_readpc() << 8; -3:op_io(); - dp += regs.x; -4:rd = op_readaddr(dp); -5:rd |= op_readaddr(dp + 1) << 8; - regs.pc = rd; -} - -call(0x3f) { -1:rd = op_readpc(); -2:rd |= op_readpc() << 8; -3:op_io(); -4:op_io(); -5:op_io(); -6:op_writestack(regs.pc >> 8); -7:op_writestack(regs.pc); - regs.pc = rd; -} - -pcall(0x4f) { -1:rd = op_readpc(); -2:op_io(); -3:op_io(); -4:op_writestack(regs.pc >> 8); -5:op_writestack(regs.pc); - regs.pc = 0xff00 | rd; -} - -tcall_0(0x01, 0), -tcall_1(0x11, 1), -tcall_2(0x21, 2), -tcall_3(0x31, 3), -tcall_4(0x41, 4), -tcall_5(0x51, 5), -tcall_6(0x61, 6), -tcall_7(0x71, 7), -tcall_8(0x81, 8), -tcall_9(0x91, 9), -tcall_10(0xa1, 10), -tcall_11(0xb1, 11), -tcall_12(0xc1, 12), -tcall_13(0xd1, 13), -tcall_14(0xe1, 14), -tcall_15(0xf1, 15) { -1:dp = 0xffde - ($1 << 1); - rd = op_readaddr(dp); -2:rd |= op_readaddr(dp + 1) << 8; -3:op_io(); -4:op_io(); -5:op_io(); -6:op_writestack(regs.pc >> 8); -7:op_writestack(regs.pc); - regs.pc = rd; -} - -brk(0x0f) { -1:rd = op_readaddr(0xffde); -2:rd |= op_readaddr(0xffdf) << 8; -3:op_io(); -4:op_io(); -5:op_writestack(regs.pc >> 8); -6:op_writestack(regs.pc); -7:op_writestack(regs.p); - regs.pc = rd; - regs.p.b = 1; - regs.p.i = 0; -} - -ret(0x6f) { -1:rd = op_readstack(); -2:rd |= op_readstack() << 8; -3:op_io(); -4:op_io(); - regs.pc = rd; -} - -reti(0x7f) { -1:regs.p = op_readstack(); -2:rd = op_readstack(); -3:rd |= op_readstack() << 8; -4:op_io(); -5:op_io(); - regs.pc = rd; -} diff --git a/src/smp/ssmp/core/op_pc.cpp b/src/smp/ssmp/core/op_pc.cpp deleted file mode 100644 index b9b59925..00000000 --- a/src/smp/ssmp/core/op_pc.cpp +++ /dev/null @@ -1,606 +0,0 @@ -#ifdef SSMP_CPP - -//bra -case 0x2f: { - rd = op_readpc(); - if(0)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//beq -case 0xf0: { - rd = op_readpc(); - if(!regs.p.z)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bne -case 0xd0: { - rd = op_readpc(); - if(regs.p.z)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bcs -case 0xb0: { - rd = op_readpc(); - if(!regs.p.c)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bcc -case 0x90: { - rd = op_readpc(); - if(regs.p.c)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bvs -case 0x70: { - rd = op_readpc(); - if(!regs.p.v)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bvc -case 0x50: { - rd = op_readpc(); - if(regs.p.v)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bmi -case 0x30: { - rd = op_readpc(); - if(!regs.p.n)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bpl -case 0x10: { - rd = op_readpc(); - if(regs.p.n)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbs0 -case 0x03: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x01) != 0x01)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbc0 -case 0x13: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x01) == 0x01)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbs1 -case 0x23: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x02) != 0x02)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbc1 -case 0x33: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x02) == 0x02)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbs2 -case 0x43: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x04) != 0x04)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbc2 -case 0x53: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x04) == 0x04)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbs3 -case 0x63: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x08) != 0x08)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbc3 -case 0x73: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x08) == 0x08)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbs4 -case 0x83: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x10) != 0x10)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbc4 -case 0x93: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x10) == 0x10)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbs5 -case 0xa3: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x20) != 0x20)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbc5 -case 0xb3: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x20) == 0x20)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbs6 -case 0xc3: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x40) != 0x40)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbc6 -case 0xd3: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x40) == 0x40)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbs7 -case 0xe3: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x80) != 0x80)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//bbc7 -case 0xf3: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if((sp & 0x80) == 0x80)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//cbne_dp -case 0x2e: { - dp = op_readpc(); - sp = op_readdp(dp); - rd = op_readpc(); - op_io(); - if(regs.a == sp)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//cbne_dpx -case 0xde: { - dp = op_readpc(); - op_io(); - sp = op_readdp(dp + regs.x); - rd = op_readpc(); - op_io(); - if(regs.a == sp)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//dbnz_dp -case 0x6e: { - dp = op_readpc(); - wr = op_readdp(dp); - op_writedp(dp, --wr); - rd = op_readpc(); - if(wr == 0x00)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//dbnz_y -case 0xfe: { - rd = op_readpc(); - op_io(); - regs.y--; - op_io(); - if(regs.y == 0x00)break; - op_io(); - op_io(); - regs.pc += (int8)rd; -} break; - -//jmp_addr -case 0x5f: { - rd = op_readpc(); - rd |= op_readpc() << 8; - regs.pc = rd; -} break; - -//jmp_iaddrx -case 0x1f: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - dp += regs.x; - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - regs.pc = rd; -} break; - -//call -case 0x3f: { - rd = op_readpc(); - rd |= op_readpc() << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//pcall -case 0x4f: { - rd = op_readpc(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = 0xff00 | rd; -} break; - -//tcall_0 -case 0x01: { - dp = 0xffde - (0 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_1 -case 0x11: { - dp = 0xffde - (1 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_2 -case 0x21: { - dp = 0xffde - (2 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_3 -case 0x31: { - dp = 0xffde - (3 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_4 -case 0x41: { - dp = 0xffde - (4 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_5 -case 0x51: { - dp = 0xffde - (5 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_6 -case 0x61: { - dp = 0xffde - (6 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_7 -case 0x71: { - dp = 0xffde - (7 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_8 -case 0x81: { - dp = 0xffde - (8 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_9 -case 0x91: { - dp = 0xffde - (9 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_10 -case 0xa1: { - dp = 0xffde - (10 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_11 -case 0xb1: { - dp = 0xffde - (11 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_12 -case 0xc1: { - dp = 0xffde - (12 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_13 -case 0xd1: { - dp = 0xffde - (13 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_14 -case 0xe1: { - dp = 0xffde - (14 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//tcall_15 -case 0xf1: { - dp = 0xffde - (15 << 1); - rd = op_readaddr(dp); - rd |= op_readaddr(dp + 1) << 8; - op_io(); - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - regs.pc = rd; -} break; - -//brk -case 0x0f: { - rd = op_readaddr(0xffde); - rd |= op_readaddr(0xffdf) << 8; - op_io(); - op_io(); - op_writestack(regs.pc >> 8); - op_writestack(regs.pc); - op_writestack(regs.p); - regs.pc = rd; - regs.p.b = 1; - regs.p.i = 0; -} break; - -//ret -case 0x6f: { - rd = op_readstack(); - rd |= op_readstack() << 8; - op_io(); - op_io(); - regs.pc = rd; -} break; - -//reti -case 0x7f: { - regs.p = op_readstack(); - rd = op_readstack(); - rd |= op_readstack() << 8; - op_io(); - op_io(); - regs.pc = rd; -} break; - -#endif diff --git a/src/smp/ssmp/core/op_read.b b/src/smp/ssmp/core/op_read.b deleted file mode 100644 index fd2f9d82..00000000 --- a/src/smp/ssmp/core/op_read.b +++ /dev/null @@ -1,205 +0,0 @@ -adc_a_const(0x88, adc, a), -and_a_const(0x28, and, a), -cmp_a_const(0x68, cmp, a), -cmp_x_const(0xc8, cmp, x), -cmp_y_const(0xad, cmp, y), -eor_a_const(0x48, eor, a), -or_a_const(0x08, or, a), -sbc_a_const(0xa8, sbc, a) { -1:rd = op_readpc(); - regs.$2 = op_$1(regs.$2, rd); -} - -adc_a_ix(0x86, adc), -and_a_ix(0x26, and), -cmp_a_ix(0x66, cmp), -eor_a_ix(0x46, eor), -or_a_ix(0x06, or), -sbc_a_ix(0xa6, sbc) { -1:op_io(); -2:rd = op_readdp(regs.x); - regs.a = op_$1(regs.a, rd); -} - -adc_a_dp(0x84, adc, a), -and_a_dp(0x24, and, a), -cmp_a_dp(0x64, cmp, a), -cmp_x_dp(0x3e, cmp, x), -cmp_y_dp(0x7e, cmp, y), -eor_a_dp(0x44, eor, a), -or_a_dp(0x04, or, a), -sbc_a_dp(0xa4, sbc, a) { -1:dp = op_readpc(); -2:rd = op_readdp(dp); - regs.$2 = op_$1(regs.$2, rd); -} - -adc_a_dpx(0x94, adc), -and_a_dpx(0x34, and), -cmp_a_dpx(0x74, cmp), -eor_a_dpx(0x54, eor), -or_a_dpx(0x14, or), -sbc_a_dpx(0xb4, sbc) { -1:dp = op_readpc(); -2:op_io(); -3:rd = op_readdp(dp + regs.x); - regs.a = op_$1(regs.a, rd); -} - -adc_a_addr(0x85, adc, a), -and_a_addr(0x25, and, a), -cmp_a_addr(0x65, cmp, a), -cmp_x_addr(0x1e, cmp, x), -cmp_y_addr(0x5e, cmp, y), -eor_a_addr(0x45, eor, a), -or_a_addr(0x05, or, a), -sbc_a_addr(0xa5, sbc, a) { -1:dp = op_readpc(); -2:dp |= op_readpc() << 8; -3:rd = op_readaddr(dp); - regs.$2 = op_$1(regs.$2, rd); -} - -adc_a_addrx(0x95, adc, x), -adc_a_addry(0x96, adc, y), -and_a_addrx(0x35, and, x), -and_a_addry(0x36, and, y), -cmp_a_addrx(0x75, cmp, x), -cmp_a_addry(0x76, cmp, y), -eor_a_addrx(0x55, eor, x), -eor_a_addry(0x56, eor, y), -or_a_addrx(0x15, or, x), -or_a_addry(0x16, or, y), -sbc_a_addrx(0xb5, sbc, x), -sbc_a_addry(0xb6, sbc, y) { -1:dp = op_readpc(); -2:dp |= op_readpc() << 8; -3:op_io(); -4:rd = op_readaddr(dp + regs.$2); - regs.a = op_$1(regs.a, rd); -} - -adc_a_idpx(0x87, adc), -and_a_idpx(0x27, and), -cmp_a_idpx(0x67, cmp), -eor_a_idpx(0x47, eor), -or_a_idpx(0x07, or), -sbc_a_idpx(0xa7, sbc) { -1:dp = op_readpc() + regs.x; -2:op_io(); -3:sp = op_readdp(dp); -4:sp |= op_readdp(dp + 1) << 8; -5:rd = op_readaddr(sp); - regs.a = op_$1(regs.a, rd); -} - -adc_a_idpy(0x97, adc), -and_a_idpy(0x37, and), -cmp_a_idpy(0x77, cmp), -eor_a_idpy(0x57, eor), -or_a_idpy(0x17, or), -sbc_a_idpy(0xb7, sbc) { -1:dp = op_readpc(); -2:op_io(); -3:sp = op_readdp(dp); -4:sp |= op_readdp(dp + 1) << 8; -5:rd = op_readaddr(sp + regs.y); - regs.a = op_$1(regs.a, rd); -} - -adc_ix_iy(0x99, adc, 1), -and_ix_iy(0x39, and, 1), -cmp_ix_iy(0x79, cmp, 0), -eor_ix_iy(0x59, eor, 1), -or_ix_iy(0x19, or, 1), -sbc_ix_iy(0xb9, sbc, 1) { -1:op_io(); -2:rd = op_readdp(regs.y); -3:wr = op_readdp(regs.x); - wr = op_$1(wr, rd); -4:($2) ? op_writedp(regs.x, wr) : op_io(); -} - -adc_dp_dp(0x89, adc, 1), -and_dp_dp(0x29, and, 1), -cmp_dp_dp(0x69, cmp, 0), -eor_dp_dp(0x49, eor, 1), -or_dp_dp(0x09, or, 1), -sbc_dp_dp(0xa9, sbc, 1) { -1:sp = op_readpc(); -2:rd = op_readdp(sp); -3:dp = op_readpc(); -4:wr = op_readdp(dp); -5:wr = op_$1(wr, rd); - ($2) ? op_writedp(dp, wr) : op_io(); -} - -adc_dp_const(0x98, adc, 1), -and_dp_const(0x38, and, 1), -cmp_dp_const(0x78, cmp, 0), -eor_dp_const(0x58, eor, 1), -or_dp_const(0x18, or, 1), -sbc_dp_const(0xb8, sbc, 1) { -1:rd = op_readpc(); -2:dp = op_readpc(); -3:wr = op_readdp(dp); -4:wr = op_$1(wr, rd); - ($2) ? op_writedp(dp, wr) : op_io(); -} - -addw_ya_dp(0x7a, addw), -subw_ya_dp(0x9a, subw) { -1:dp = op_readpc(); -2:rd = op_readdp(dp); -3:op_io(); -4:rd |= op_readdp(dp + 1) << 8; - regs.ya = op_$1(regs.ya, rd); -} - -cmpw_ya_dp(0x5a) { -1:dp = op_readpc(); -2:rd = op_readdp(dp); -3:rd |= op_readdp(dp + 1) << 8; - op_cmpw(regs.ya, rd); -} - -and1_bit(0x4a, !!), -and1_notbit(0x6a, !) { -1:dp = op_readpc(); -2:dp |= op_readpc() << 8; -3:bit = dp >> 13; - dp &= 0x1fff; - rd = op_readaddr(dp); - regs.p.c = regs.p.c & $1(rd & (1 << bit)); -} - -eor1_bit(0x8a) { -1:dp = op_readpc(); -2:dp |= op_readpc() << 8; -3:bit = dp >> 13; - dp &= 0x1fff; - rd = op_readaddr(dp); -4:op_io(); - regs.p.c = regs.p.c ^ !!(rd & (1 << bit)); -} - -not1_bit(0xea) { -1:dp = op_readpc(); -2:dp |= op_readpc() << 8; -3:bit = dp >> 13; - dp &= 0x1fff; - rd = op_readaddr(dp); - rd ^= (1 << bit); -4:op_writeaddr(dp, rd); -} - -or1_bit(0x0a, !!), -or1_notbit(0x2a, !) { -1:dp = op_readpc(); -2:dp |= op_readpc() << 8; -3:bit = dp >> 13; - dp &= 0x1fff; - rd = op_readaddr(dp); -4:op_io(); - regs.p.c = regs.p.c | $1(rd & (1 << bit)); -} diff --git a/src/smp/ssmp/core/op_read.cpp b/src/smp/ssmp/core/op_read.cpp deleted file mode 100644 index 3f8f77fd..00000000 --- a/src/smp/ssmp/core/op_read.cpp +++ /dev/null @@ -1,747 +0,0 @@ -#ifdef SSMP_CPP - -//adc_a_const -case 0x88: { - rd = op_readpc(); - regs.a = op_adc(regs.a, rd); -} break; - -//and_a_const -case 0x28: { - rd = op_readpc(); - regs.a = op_and(regs.a, rd); -} break; - -//cmp_a_const -case 0x68: { - rd = op_readpc(); - regs.a = op_cmp(regs.a, rd); -} break; - -//cmp_x_const -case 0xc8: { - rd = op_readpc(); - regs.x = op_cmp(regs.x, rd); -} break; - -//cmp_y_const -case 0xad: { - rd = op_readpc(); - regs.y = op_cmp(regs.y, rd); -} break; - -//eor_a_const -case 0x48: { - rd = op_readpc(); - regs.a = op_eor(regs.a, rd); -} break; - -//or_a_const -case 0x08: { - rd = op_readpc(); - regs.a = op_or(regs.a, rd); -} break; - -//sbc_a_const -case 0xa8: { - rd = op_readpc(); - regs.a = op_sbc(regs.a, rd); -} break; - -//adc_a_ix -case 0x86: { - op_io(); - rd = op_readdp(regs.x); - regs.a = op_adc(regs.a, rd); -} break; - -//and_a_ix -case 0x26: { - op_io(); - rd = op_readdp(regs.x); - regs.a = op_and(regs.a, rd); -} break; - -//cmp_a_ix -case 0x66: { - op_io(); - rd = op_readdp(regs.x); - regs.a = op_cmp(regs.a, rd); -} break; - -//eor_a_ix -case 0x46: { - op_io(); - rd = op_readdp(regs.x); - regs.a = op_eor(regs.a, rd); -} break; - -//or_a_ix -case 0x06: { - op_io(); - rd = op_readdp(regs.x); - regs.a = op_or(regs.a, rd); -} break; - -//sbc_a_ix -case 0xa6: { - op_io(); - rd = op_readdp(regs.x); - regs.a = op_sbc(regs.a, rd); -} break; - -//adc_a_dp -case 0x84: { - dp = op_readpc(); - rd = op_readdp(dp); - regs.a = op_adc(regs.a, rd); -} break; - -//and_a_dp -case 0x24: { - dp = op_readpc(); - rd = op_readdp(dp); - regs.a = op_and(regs.a, rd); -} break; - -//cmp_a_dp -case 0x64: { - dp = op_readpc(); - rd = op_readdp(dp); - regs.a = op_cmp(regs.a, rd); -} break; - -//cmp_x_dp -case 0x3e: { - dp = op_readpc(); - rd = op_readdp(dp); - regs.x = op_cmp(regs.x, rd); -} break; - -//cmp_y_dp -case 0x7e: { - dp = op_readpc(); - rd = op_readdp(dp); - regs.y = op_cmp(regs.y, rd); -} break; - -//eor_a_dp -case 0x44: { - dp = op_readpc(); - rd = op_readdp(dp); - regs.a = op_eor(regs.a, rd); -} break; - -//or_a_dp -case 0x04: { - dp = op_readpc(); - rd = op_readdp(dp); - regs.a = op_or(regs.a, rd); -} break; - -//sbc_a_dp -case 0xa4: { - dp = op_readpc(); - rd = op_readdp(dp); - regs.a = op_sbc(regs.a, rd); -} break; - -//adc_a_dpx -case 0x94: { - dp = op_readpc(); - op_io(); - rd = op_readdp(dp + regs.x); - regs.a = op_adc(regs.a, rd); -} break; - -//and_a_dpx -case 0x34: { - dp = op_readpc(); - op_io(); - rd = op_readdp(dp + regs.x); - regs.a = op_and(regs.a, rd); -} break; - -//cmp_a_dpx -case 0x74: { - dp = op_readpc(); - op_io(); - rd = op_readdp(dp + regs.x); - regs.a = op_cmp(regs.a, rd); -} break; - -//eor_a_dpx -case 0x54: { - dp = op_readpc(); - op_io(); - rd = op_readdp(dp + regs.x); - regs.a = op_eor(regs.a, rd); -} break; - -//or_a_dpx -case 0x14: { - dp = op_readpc(); - op_io(); - rd = op_readdp(dp + regs.x); - regs.a = op_or(regs.a, rd); -} break; - -//sbc_a_dpx -case 0xb4: { - dp = op_readpc(); - op_io(); - rd = op_readdp(dp + regs.x); - regs.a = op_sbc(regs.a, rd); -} break; - -//adc_a_addr -case 0x85: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - regs.a = op_adc(regs.a, rd); -} break; - -//and_a_addr -case 0x25: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - regs.a = op_and(regs.a, rd); -} break; - -//cmp_a_addr -case 0x65: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - regs.a = op_cmp(regs.a, rd); -} break; - -//cmp_x_addr -case 0x1e: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - regs.x = op_cmp(regs.x, rd); -} break; - -//cmp_y_addr -case 0x5e: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - regs.y = op_cmp(regs.y, rd); -} break; - -//eor_a_addr -case 0x45: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - regs.a = op_eor(regs.a, rd); -} break; - -//or_a_addr -case 0x05: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - regs.a = op_or(regs.a, rd); -} break; - -//sbc_a_addr -case 0xa5: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - regs.a = op_sbc(regs.a, rd); -} break; - -//adc_a_addrx -case 0x95: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - rd = op_readaddr(dp + regs.x); - regs.a = op_adc(regs.a, rd); -} break; - -//adc_a_addry -case 0x96: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - rd = op_readaddr(dp + regs.y); - regs.a = op_adc(regs.a, rd); -} break; - -//and_a_addrx -case 0x35: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - rd = op_readaddr(dp + regs.x); - regs.a = op_and(regs.a, rd); -} break; - -//and_a_addry -case 0x36: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - rd = op_readaddr(dp + regs.y); - regs.a = op_and(regs.a, rd); -} break; - -//cmp_a_addrx -case 0x75: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - rd = op_readaddr(dp + regs.x); - regs.a = op_cmp(regs.a, rd); -} break; - -//cmp_a_addry -case 0x76: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - rd = op_readaddr(dp + regs.y); - regs.a = op_cmp(regs.a, rd); -} break; - -//eor_a_addrx -case 0x55: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - rd = op_readaddr(dp + regs.x); - regs.a = op_eor(regs.a, rd); -} break; - -//eor_a_addry -case 0x56: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - rd = op_readaddr(dp + regs.y); - regs.a = op_eor(regs.a, rd); -} break; - -//or_a_addrx -case 0x15: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - rd = op_readaddr(dp + regs.x); - regs.a = op_or(regs.a, rd); -} break; - -//or_a_addry -case 0x16: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - rd = op_readaddr(dp + regs.y); - regs.a = op_or(regs.a, rd); -} break; - -//sbc_a_addrx -case 0xb5: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - rd = op_readaddr(dp + regs.x); - regs.a = op_sbc(regs.a, rd); -} break; - -//sbc_a_addry -case 0xb6: { - dp = op_readpc(); - dp |= op_readpc() << 8; - op_io(); - rd = op_readaddr(dp + regs.y); - regs.a = op_sbc(regs.a, rd); -} break; - -//adc_a_idpx -case 0x87: { - dp = op_readpc() + regs.x; - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_readaddr(sp); - regs.a = op_adc(regs.a, rd); -} break; - -//and_a_idpx -case 0x27: { - dp = op_readpc() + regs.x; - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_readaddr(sp); - regs.a = op_and(regs.a, rd); -} break; - -//cmp_a_idpx -case 0x67: { - dp = op_readpc() + regs.x; - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_readaddr(sp); - regs.a = op_cmp(regs.a, rd); -} break; - -//eor_a_idpx -case 0x47: { - dp = op_readpc() + regs.x; - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_readaddr(sp); - regs.a = op_eor(regs.a, rd); -} break; - -//or_a_idpx -case 0x07: { - dp = op_readpc() + regs.x; - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_readaddr(sp); - regs.a = op_or(regs.a, rd); -} break; - -//sbc_a_idpx -case 0xa7: { - dp = op_readpc() + regs.x; - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_readaddr(sp); - regs.a = op_sbc(regs.a, rd); -} break; - -//adc_a_idpy -case 0x97: { - dp = op_readpc(); - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_readaddr(sp + regs.y); - regs.a = op_adc(regs.a, rd); -} break; - -//and_a_idpy -case 0x37: { - dp = op_readpc(); - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_readaddr(sp + regs.y); - regs.a = op_and(regs.a, rd); -} break; - -//cmp_a_idpy -case 0x77: { - dp = op_readpc(); - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_readaddr(sp + regs.y); - regs.a = op_cmp(regs.a, rd); -} break; - -//eor_a_idpy -case 0x57: { - dp = op_readpc(); - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_readaddr(sp + regs.y); - regs.a = op_eor(regs.a, rd); -} break; - -//or_a_idpy -case 0x17: { - dp = op_readpc(); - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_readaddr(sp + regs.y); - regs.a = op_or(regs.a, rd); -} break; - -//sbc_a_idpy -case 0xb7: { - dp = op_readpc(); - op_io(); - sp = op_readdp(dp); - sp |= op_readdp(dp + 1) << 8; - rd = op_readaddr(sp + regs.y); - regs.a = op_sbc(regs.a, rd); -} break; - -//adc_ix_iy -case 0x99: { - op_io(); - rd = op_readdp(regs.y); - wr = op_readdp(regs.x); - wr = op_adc(wr, rd); - (1) ? op_writedp(regs.x, wr) : op_io(); -} break; - -//and_ix_iy -case 0x39: { - op_io(); - rd = op_readdp(regs.y); - wr = op_readdp(regs.x); - wr = op_and(wr, rd); - (1) ? op_writedp(regs.x, wr) : op_io(); -} break; - -//cmp_ix_iy -case 0x79: { - op_io(); - rd = op_readdp(regs.y); - wr = op_readdp(regs.x); - wr = op_cmp(wr, rd); - (0) ? op_writedp(regs.x, wr) : op_io(); -} break; - -//eor_ix_iy -case 0x59: { - op_io(); - rd = op_readdp(regs.y); - wr = op_readdp(regs.x); - wr = op_eor(wr, rd); - (1) ? op_writedp(regs.x, wr) : op_io(); -} break; - -//or_ix_iy -case 0x19: { - op_io(); - rd = op_readdp(regs.y); - wr = op_readdp(regs.x); - wr = op_or(wr, rd); - (1) ? op_writedp(regs.x, wr) : op_io(); -} break; - -//sbc_ix_iy -case 0xb9: { - op_io(); - rd = op_readdp(regs.y); - wr = op_readdp(regs.x); - wr = op_sbc(wr, rd); - (1) ? op_writedp(regs.x, wr) : op_io(); -} break; - -//adc_dp_dp -case 0x89: { - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_adc(wr, rd); - (1) ? op_writedp(dp, wr) : op_io(); -} break; - -//and_dp_dp -case 0x29: { - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_and(wr, rd); - (1) ? op_writedp(dp, wr) : op_io(); -} break; - -//cmp_dp_dp -case 0x69: { - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_cmp(wr, rd); - (0) ? op_writedp(dp, wr) : op_io(); -} break; - -//eor_dp_dp -case 0x49: { - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_eor(wr, rd); - (1) ? op_writedp(dp, wr) : op_io(); -} break; - -//or_dp_dp -case 0x09: { - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_or(wr, rd); - (1) ? op_writedp(dp, wr) : op_io(); -} break; - -//sbc_dp_dp -case 0xa9: { - sp = op_readpc(); - rd = op_readdp(sp); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_sbc(wr, rd); - (1) ? op_writedp(dp, wr) : op_io(); -} break; - -//adc_dp_const -case 0x98: { - rd = op_readpc(); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_adc(wr, rd); - (1) ? op_writedp(dp, wr) : op_io(); -} break; - -//and_dp_const -case 0x38: { - rd = op_readpc(); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_and(wr, rd); - (1) ? op_writedp(dp, wr) : op_io(); -} break; - -//cmp_dp_const -case 0x78: { - rd = op_readpc(); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_cmp(wr, rd); - (0) ? op_writedp(dp, wr) : op_io(); -} break; - -//eor_dp_const -case 0x58: { - rd = op_readpc(); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_eor(wr, rd); - (1) ? op_writedp(dp, wr) : op_io(); -} break; - -//or_dp_const -case 0x18: { - rd = op_readpc(); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_or(wr, rd); - (1) ? op_writedp(dp, wr) : op_io(); -} break; - -//sbc_dp_const -case 0xb8: { - rd = op_readpc(); - dp = op_readpc(); - wr = op_readdp(dp); - wr = op_sbc(wr, rd); - (1) ? op_writedp(dp, wr) : op_io(); -} break; - -//addw_ya_dp -case 0x7a: { - dp = op_readpc(); - rd = op_readdp(dp); - op_io(); - rd |= op_readdp(dp + 1) << 8; - regs.ya = op_addw(regs.ya, rd); -} break; - -//subw_ya_dp -case 0x9a: { - dp = op_readpc(); - rd = op_readdp(dp); - op_io(); - rd |= op_readdp(dp + 1) << 8; - regs.ya = op_subw(regs.ya, rd); -} break; - -//cmpw_ya_dp -case 0x5a: { - dp = op_readpc(); - rd = op_readdp(dp); - rd |= op_readdp(dp + 1) << 8; - op_cmpw(regs.ya, rd); -} break; - -//and1_bit -case 0x4a: { - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_readaddr(dp); - regs.p.c = regs.p.c & !!(rd & (1 << bit)); -} break; - -//and1_notbit -case 0x6a: { - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_readaddr(dp); - regs.p.c = regs.p.c & !(rd & (1 << bit)); -} break; - -//eor1_bit -case 0x8a: { - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_readaddr(dp); - op_io(); - regs.p.c = regs.p.c ^ !!(rd & (1 << bit)); -} break; - -//not1_bit -case 0xea: { - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_readaddr(dp); - rd ^= (1 << bit); - op_writeaddr(dp, rd); -} break; - -//or1_bit -case 0x0a: { - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_readaddr(dp); - op_io(); - regs.p.c = regs.p.c | !!(rd & (1 << bit)); -} break; - -//or1_notbit -case 0x2a: { - dp = op_readpc(); - dp |= op_readpc() << 8; - bit = dp >> 13; - dp &= 0x1fff; - rd = op_readaddr(dp); - op_io(); - regs.p.c = regs.p.c | !(rd & (1 << bit)); -} break; - -#endif diff --git a/src/smp/ssmp/core/op_rmw.b b/src/smp/ssmp/core/op_rmw.b deleted file mode 100644 index 425574e8..00000000 --- a/src/smp/ssmp/core/op_rmw.b +++ /dev/null @@ -1,74 +0,0 @@ -inc_a(0xbc, inc, a), -inc_x(0x3d, inc, x), -inc_y(0xfc, inc, y), -dec_a(0x9c, dec, a), -dec_x(0x1d, dec, x), -dec_y(0xdc, dec, y), -asl_a(0x1c, asl, a), -lsr_a(0x5c, lsr, a), -rol_a(0x3c, rol, a), -ror_a(0x7c, ror, a) { -1:op_io(); - regs.$2 = op_$1(regs.$2); -} - -inc_dp(0xab, inc), -dec_dp(0x8b, dec), -asl_dp(0x0b, asl), -lsr_dp(0x4b, lsr), -rol_dp(0x2b, rol), -ror_dp(0x6b, ror) { -1:dp = op_readpc(); -2:rd = op_readdp(dp); -3:rd = op_$1(rd); - op_writedp(dp, rd); -} - -inc_dpx(0xbb, inc), -dec_dpx(0x9b, dec), -asl_dpx(0x1b, asl), -lsr_dpx(0x5b, lsr), -rol_dpx(0x3b, rol), -ror_dpx(0x7b, ror) { -1:dp = op_readpc(); -2:op_io(); -3:rd = op_readdp(dp + regs.x); -4:rd = op_$1(rd); - op_writedp(dp + regs.x, rd); -} - -inc_addr(0xac, inc), -dec_addr(0x8c, dec), -asl_addr(0x0c, asl), -lsr_addr(0x4c, lsr), -rol_addr(0x2c, rol), -ror_addr(0x6c, ror) { -1:dp = op_readpc(); -2:dp |= op_readpc() << 8; -3:rd = op_readaddr(dp); -4:rd = op_$1(rd); - op_writeaddr(dp, rd); -} - -tset_addr_a(0x0e, |), -tclr_addr_a(0x4e, &~) { -1:dp = op_readpc(); -2:dp |= op_readpc() << 8; -3:rd = op_readaddr(dp); - regs.p.n = !!((regs.a - rd) & 0x80); - regs.p.z = ((regs.a - rd) == 0); -4:op_readaddr(dp); -5:op_writeaddr(dp, rd $1 regs.a); -} - -incw_dp(0x3a, ++), -decw_dp(0x1a, --) { -1:dp = op_readpc(); -2:rd = op_readdp(dp); - rd$1; -3:op_writedp(dp++, rd); -4:rd += op_readdp(dp) << 8; -5:op_writedp(dp, rd >> 8); - regs.p.n = !!(rd & 0x8000); - regs.p.z = (rd == 0); -} diff --git a/src/smp/ssmp/core/op_rmw.cpp b/src/smp/ssmp/core/op_rmw.cpp deleted file mode 100644 index 7bba31b6..00000000 --- a/src/smp/ssmp/core/op_rmw.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#ifdef SSMP_CPP - -//inc_a -case 0xbc: { - op_io(); - regs.a = op_inc(regs.a); -} break; - -//inc_x -case 0x3d: { - op_io(); - regs.x = op_inc(regs.x); -} break; - -//inc_y -case 0xfc: { - op_io(); - regs.y = op_inc(regs.y); -} break; - -//dec_a -case 0x9c: { - op_io(); - regs.a = op_dec(regs.a); -} break; - -//dec_x -case 0x1d: { - op_io(); - regs.x = op_dec(regs.x); -} break; - -//dec_y -case 0xdc: { - op_io(); - regs.y = op_dec(regs.y); -} break; - -//asl_a -case 0x1c: { - op_io(); - regs.a = op_asl(regs.a); -} break; - -//lsr_a -case 0x5c: { - op_io(); - regs.a = op_lsr(regs.a); -} break; - -//rol_a -case 0x3c: { - op_io(); - regs.a = op_rol(regs.a); -} break; - -//ror_a -case 0x7c: { - op_io(); - regs.a = op_ror(regs.a); -} break; - -//inc_dp -case 0xab: { - dp = op_readpc(); - rd = op_readdp(dp); - rd = op_inc(rd); - op_writedp(dp, rd); -} break; - -//dec_dp -case 0x8b: { - dp = op_readpc(); - rd = op_readdp(dp); - rd = op_dec(rd); - op_writedp(dp, rd); -} break; - -//asl_dp -case 0x0b: { - dp = op_readpc(); - rd = op_readdp(dp); - rd = op_asl(rd); - op_writedp(dp, rd); -} break; - -//lsr_dp -case 0x4b: { - dp = op_readpc(); - rd = op_readdp(dp); - rd = op_lsr(rd); - op_writedp(dp, rd); -} break; - -//rol_dp -case 0x2b: { - dp = op_readpc(); - rd = op_readdp(dp); - rd = op_rol(rd); - op_writedp(dp, rd); -} break; - -//ror_dp -case 0x6b: { - dp = op_readpc(); - rd = op_readdp(dp); - rd = op_ror(rd); - op_writedp(dp, rd); -} break; - -//inc_dpx -case 0xbb: { - dp = op_readpc(); - op_io(); - rd = op_readdp(dp + regs.x); - rd = op_inc(rd); - op_writedp(dp + regs.x, rd); -} break; - -//dec_dpx -case 0x9b: { - dp = op_readpc(); - op_io(); - rd = op_readdp(dp + regs.x); - rd = op_dec(rd); - op_writedp(dp + regs.x, rd); -} break; - -//asl_dpx -case 0x1b: { - dp = op_readpc(); - op_io(); - rd = op_readdp(dp + regs.x); - rd = op_asl(rd); - op_writedp(dp + regs.x, rd); -} break; - -//lsr_dpx -case 0x5b: { - dp = op_readpc(); - op_io(); - rd = op_readdp(dp + regs.x); - rd = op_lsr(rd); - op_writedp(dp + regs.x, rd); -} break; - -//rol_dpx -case 0x3b: { - dp = op_readpc(); - op_io(); - rd = op_readdp(dp + regs.x); - rd = op_rol(rd); - op_writedp(dp + regs.x, rd); -} break; - -//ror_dpx -case 0x7b: { - dp = op_readpc(); - op_io(); - rd = op_readdp(dp + regs.x); - rd = op_ror(rd); - op_writedp(dp + regs.x, rd); -} break; - -//inc_addr -case 0xac: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - rd = op_inc(rd); - op_writeaddr(dp, rd); -} break; - -//dec_addr -case 0x8c: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - rd = op_dec(rd); - op_writeaddr(dp, rd); -} break; - -//asl_addr -case 0x0c: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - rd = op_asl(rd); - op_writeaddr(dp, rd); -} break; - -//lsr_addr -case 0x4c: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - rd = op_lsr(rd); - op_writeaddr(dp, rd); -} break; - -//rol_addr -case 0x2c: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - rd = op_rol(rd); - op_writeaddr(dp, rd); -} break; - -//ror_addr -case 0x6c: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - rd = op_ror(rd); - op_writeaddr(dp, rd); -} break; - -//tset_addr_a -case 0x0e: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - regs.p.n = !!((regs.a - rd) & 0x80); - regs.p.z = ((regs.a - rd) == 0); - op_readaddr(dp); - op_writeaddr(dp, rd | regs.a); -} break; - -//tclr_addr_a -case 0x4e: { - dp = op_readpc(); - dp |= op_readpc() << 8; - rd = op_readaddr(dp); - regs.p.n = !!((regs.a - rd) & 0x80); - regs.p.z = ((regs.a - rd) == 0); - op_readaddr(dp); - op_writeaddr(dp, rd &~ regs.a); -} break; - -//incw_dp -case 0x3a: { - dp = op_readpc(); - rd = op_readdp(dp); - rd++; - op_writedp(dp++, rd); - rd += op_readdp(dp) << 8; - op_writedp(dp, rd >> 8); - regs.p.n = !!(rd & 0x8000); - regs.p.z = (rd == 0); -} break; - -//decw_dp -case 0x1a: { - dp = op_readpc(); - rd = op_readdp(dp); - rd--; - op_writedp(dp++, rd); - rd += op_readdp(dp) << 8; - op_writedp(dp, rd >> 8); - regs.p.n = !!(rd & 0x8000); - regs.p.z = (rd == 0); -} break; - -#endif diff --git a/src/smp/ssmp/core/ssmpgen.cpp b/src/smp/ssmp/core/ssmpgen.cpp deleted file mode 100644 index b6a67db2..00000000 --- a/src/smp/ssmp/core/ssmpgen.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#define CLASS_NAME "sSMP" -#include - -int main() { - generate("op_mov.cpp", "op_mov.b"); - generate("op_pc.cpp", "op_pc.b"); - generate("op_read.cpp", "op_read.b"); - generate("op_rmw.cpp", "op_rmw.b"); - generate("op_misc.cpp", "op_misc.b"); - - return 0; -} diff --git a/src/smp/ssmp/memory/memory.cpp b/src/smp/ssmp/memory/memory.cpp index 431b87d9..279177d1 100644 --- a/src/smp/ssmp/memory/memory.cpp +++ b/src/smp/ssmp/memory/memory.cpp @@ -1,34 +1,29 @@ #ifdef SSMP_CPP -alwaysinline -uint8 sSMP::ram_read(uint16 addr) { +alwaysinline uint8 sSMP::ram_read(uint16 addr) { if(addr < 0xffc0) return memory::apuram[addr]; if(status.iplrom_enabled == false) return memory::apuram[addr]; return iplrom[addr & 0x3f]; } -alwaysinline -void sSMP::ram_write(uint16 addr, uint8 data) { +alwaysinline void sSMP::ram_write(uint16 addr, uint8 data) { //writes to $ffc0-$ffff always go to spcram, even if the iplrom is enabled memory::apuram[addr] = data; } // -alwaysinline -uint8 sSMP::port_read(uint8 port) { +alwaysinline uint8 sSMP::port_read(uint8 port) { return memory::apuram[0xf4 + (port & 3)]; } -alwaysinline -void sSMP::port_write(uint8 port, uint8 data) { +alwaysinline void sSMP::port_write(uint8 port, uint8 data) { memory::apuram[0xf4 + (port & 3)] = data; } // -alwaysinline -uint8 sSMP::op_busread(uint16 addr) { +alwaysinline uint8 sSMP::op_busread(uint16 addr) { uint8 r; if((addr & 0xfff0) == 0x00f0) { //addr >= 0x00f0 && addr <= 0x00ff @@ -94,8 +89,7 @@ uint8 sSMP::op_busread(uint16 addr) { return r; } -alwaysinline -void sSMP::op_buswrite(uint16 addr, uint8 data) { +alwaysinline void sSMP::op_buswrite(uint16 addr, uint8 data) { if((addr & 0xfff0) == 0x00f0) { //addr >= 0x00f0 && addr >= 0x00ff if(status.mmio_disabled == true) return; @@ -228,42 +222,5 @@ void sSMP::op_write(uint16 addr, uint8 data) { op_buswrite(addr, data); tick_timers(); } - -// - -alwaysinline -uint8 sSMP::op_readpc() { - return op_read(regs.pc++); -} - -alwaysinline -uint8 sSMP::op_readstack() { - return op_read(0x0100 | ++regs.sp); -} - -alwaysinline -void sSMP::op_writestack(uint8 data) { - op_write(0x0100 | regs.sp--, data); -} - -alwaysinline -uint8 sSMP::op_readaddr(uint16 addr) { - return op_read(addr); -} - -alwaysinline -void sSMP::op_writeaddr(uint16 addr, uint8 data) { - op_write(addr, data); -} - -alwaysinline -uint8 sSMP::op_readdp(uint8 addr) { - return op_read((unsigned(regs.p.p) << 8) + addr); -} - -alwaysinline -void sSMP::op_writedp(uint8 addr, uint8 data) { - op_write((unsigned(regs.p.p) << 8) + addr, data); -} -#endif //ifdef SSMP_CPP +#endif diff --git a/src/smp/ssmp/memory/memory.hpp b/src/smp/ssmp/memory/memory.hpp index d6e36d51..4dfdccc1 100644 --- a/src/smp/ssmp/memory/memory.hpp +++ b/src/smp/ssmp/memory/memory.hpp @@ -4,26 +4,9 @@ uint8 port_read(uint8 port); void port_write(uint8 port, uint8 data); - //====================== - //core SMP bus functions - //====================== uint8 op_busread(uint16 addr); void op_buswrite(uint16 addr, uint8 data); void op_io(); uint8 op_read(uint16 addr); void op_write(uint16 addr, uint8 data); - - //=================================================== - //helper memory addressing functions used by SMP core - //=================================================== - uint8 op_readpc(); - - uint8 op_readstack(); - void op_writestack(uint8 data); - - uint8 op_readaddr(uint16 addr); - void op_writeaddr(uint16 addr, uint8 data); - - uint8 op_readdp(uint8 addr); - void op_writedp(uint8 addr, uint8 data); diff --git a/src/smp/ssmp/ssmp.cpp b/src/smp/ssmp/ssmp.cpp index 8a358739..265ae013 100644 --- a/src/smp/ssmp/ssmp.cpp +++ b/src/smp/ssmp/ssmp.cpp @@ -1,10 +1,26 @@ #include <../base.hpp> -#define SSMP_CPP -#include "core/core.cpp" +#define SSMP_CPP +namespace SNES { + #include "memory/memory.cpp" #include "timing/timing.cpp" +void sSMP::enter() { + unsigned counter = 0; + + while(true) { + tracer.trace_smpop(); //traces SMP opcode (only if tracer is enabled) + (this->*opcode_table[op_readpc()])(); + + //forcefully sync S-CPU and S-SMP, in case chips are not communicating + if(++counter >= 128) { + counter = 0; + scheduler.sync_smpcpu(); + } + } +} + void sSMP::power() { //targets not initialized/changed upon reset t0.target = 0; @@ -66,3 +82,5 @@ sSMP::sSMP() { sSMP::~sSMP() { } + +}; diff --git a/src/smp/ssmp/ssmp.hpp b/src/smp/ssmp/ssmp.hpp index c8389b2e..f3aebe2d 100644 --- a/src/smp/ssmp/ssmp.hpp +++ b/src/smp/ssmp/ssmp.hpp @@ -1,8 +1,7 @@ -class sSMP : public SMP { +class sSMP : public SMP, public SMPcore { public: void enter(); - #include "core/core.hpp" #include "memory/memory.hpp" #include "timing/timing.hpp" diff --git a/src/snes/audio/audio.cpp b/src/snes/audio/audio.cpp deleted file mode 100644 index 5dd800b1..00000000 --- a/src/snes/audio/audio.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#ifdef SNES_CPP - -void SNES::Audio::update(uint16 l_sample, uint16 r_sample) { - snesinterface.audio_sample(l_sample, r_sample); -} - -void SNES::Audio::init() { -} - -#endif //ifdef SNES_CPP diff --git a/src/snes/audio/audio.hpp b/src/snes/audio/audio.hpp deleted file mode 100644 index 2643d91c..00000000 --- a/src/snes/audio/audio.hpp +++ /dev/null @@ -1,7 +0,0 @@ -class Audio { -public: - void update(uint16 l_sample, uint16 r_sample); - void init(); - - friend class SNES; -} audio; diff --git a/src/snes/interface/interface.hpp b/src/snes/interface/interface.hpp deleted file mode 100644 index 2656c209..00000000 --- a/src/snes/interface/interface.hpp +++ /dev/null @@ -1,17 +0,0 @@ -//==================== -//SNES interface class -//==================== -//Interfaces SNES core with platform-specific functionality (video, audio, input, ...) - -class SNESInterface { -public: - void video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height); - void audio_sample(uint16_t l_sample, uint16_t r_sample); - void input_poll(); - int16_t input_poll(unsigned deviceid, unsigned id); - - void init(); - void term(); -}; - -extern SNESInterface snesinterface; diff --git a/src/snes/snes.hpp b/src/snes/snes.hpp deleted file mode 100644 index ba3d25ee..00000000 --- a/src/snes/snes.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "interface/interface.hpp" -#include "scheduler/scheduler.hpp" -#include "tracer/tracer.hpp" - -class VideoFilter; - -class SNES { -public: - void coprocessor_enter(); - - enum Region { NTSC = 0, PAL = 1 }; - enum RegionAutodetect { Autodetect = 2 }; - enum ExpansionPortDevice { ExpansionNone = 0, ExpansionBSX = 1 }; - - struct Config { - unsigned controller_port1; - unsigned controller_port2; - unsigned expansion_port; - unsigned region; - - struct File { - bool autodetect_type; - bool bypass_patch_crc32; - } file; - - struct Path { - string base; //binary path - string user; //user profile path (bsnes.cfg, ...) - string current; //current working directory (path to currently loaded cartridge) - string rom, save, patch, cheat, data; - string bsx, st; - } path; - - struct CPU { - unsigned version; - unsigned ntsc_clock_rate; - unsigned pal_clock_rate; - unsigned alu_mul_delay; - unsigned alu_div_delay; - unsigned wram_init_value; - } cpu; - - struct SMP { - unsigned ntsc_clock_rate; - unsigned pal_clock_rate; - } smp; - - struct PPU1 { - unsigned version; - } ppu1; - - struct PPU2 { - unsigned version; - } ppu2; - } config; - - //system functions - virtual void run(); - virtual void runtoframe(); - - virtual void init(); - virtual void term(); - virtual void power(); - virtual void reset(); - - virtual void frame(); - virtual void scanline(); - - //return *active* region / expansion port device information - //settings cached upon power-on - Region region() const; - ExpansionPortDevice expansion() const; - - #include "video/video.hpp" - #include "audio/audio.hpp" - #include "input/input.hpp" - - SNES(); - virtual ~SNES() {} - -private: - unsigned snes_region; - unsigned snes_expansion; -}; - -extern SNES snes; diff --git a/src/system/audio/audio.cpp b/src/system/audio/audio.cpp new file mode 100644 index 00000000..c0922b15 --- /dev/null +++ b/src/system/audio/audio.cpp @@ -0,0 +1,111 @@ +#ifdef SNES_CPP + +//====================== +//System::AudioResampler +//====================== + +double System::AudioResampler::hermite(double mu1, double a, double b, double c, double d) { + const double tension = 0.0; //-1 = low, 0 = normal, 1 = high + const double bias = 0.0; //-1 = left, 0 = even, 1 = right + double mu2, mu3, m0, m1, a0, a1, a2, a3; + + mu2 = mu1 * mu1; + mu3 = mu2 * mu1; + + m0 = (b - a) * (1 + bias) * (1 - tension) / 2; + m0 += (c - b) * (1 - bias) * (1 - tension) / 2; + m1 = (c - b) * (1 + bias) * (1 - tension) / 2; + m1 += (d - c) * (1 - bias) * (1 - tension) / 2; + + a0 = +2 * mu3 - 3 * mu2 + 1; + a1 = mu3 - 2 * mu2 + mu1; + a2 = mu3 - mu2; + a3 = -2 * mu3 + 3 * mu2; + + return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c); +} + +void System::AudioResampler::sample(uint16_t lsamp, uint16_t rsamp) { + lhist[0] = lhist[1]; + lhist[1] = lhist[2]; + lhist[2] = lhist[3]; + lhist[3] = lsamp; + + rhist[0] = rhist[1]; + rhist[1] = rhist[2]; + rhist[2] = rhist[3]; + rhist[3] = rsamp; + + while(fraction <= 1.0) { + unsigned p = bufferwr++; + lbuffer[p] = sclamp<16>(hermite(fraction, lhist[0], lhist[1], lhist[2], lhist[3])); + rbuffer[p] = sclamp<16>(hermite(fraction, rhist[0], rhist[1], rhist[2], rhist[3])); + bufferlength++; + fraction += step; + } + + fraction -= 1.0; +} + +void System::AudioResampler::reset(unsigned ifreq_, unsigned ofreq_) { + ifreq = ifreq_; + ofreq = ofreq_; + + fraction = 0; + step = (double)ifreq / (double)ofreq; + + bufferlength = 0; + bufferrd = 0; + bufferwr = 0; + + for(unsigned i = 0; i < 4; i++) lhist[i] = 0, rhist[i] = 0; +} + +//============= +//System::Audio +//============= + +//DSP represents S-DSP core sample generation +//COP represents cartridge co-processor sample generation + +//DSP frequency is typically ~32040hz +//must be valid, as SNES always generates audio +void System::Audio::set_dsp_frequency(unsigned freq) { + dsp_frequency = freq; + resample.reset(cop_frequency, dsp_frequency); +} + +//COP frequency can be any value +//a value of zero indicates the cartridge connector sample pins are not used, +//ergo setting to zero will disable mixing between DSP and COP samples +void System::Audio::set_cop_frequency(unsigned freq) { + cop_frequency = freq; + resample.reset(cop_frequency, dsp_frequency); +} + +//TODO: no actual mixing done yet. +//only the Super Gameboy uses this feature thus far; where it is +//possible to stop the SGB completely via disable flag. +//need a way to prevent buffer stalling when this occurs. + +void System::Audio::dsp_sample(uint16 l_sample, uint16 r_sample) { + if(cop_frequency != 0) return; + + system.interface->audio_sample(l_sample, r_sample); +} + +void System::Audio::cop_sample(uint16 l_sample, uint16 r_sample) { + if(cop_frequency == 0) return; + + resample.sample(l_sample, r_sample); + while(resample.bufferlength) { + unsigned p = resample.bufferrd++; + system.interface->audio_sample(resample.lbuffer[p], resample.rbuffer[p]); + resample.bufferlength--; + } +} + +void System::Audio::init() { +} + +#endif diff --git a/src/system/audio/audio.hpp b/src/system/audio/audio.hpp new file mode 100644 index 00000000..51654cb9 --- /dev/null +++ b/src/system/audio/audio.hpp @@ -0,0 +1,36 @@ +class AudioResampler { +public: + //output ofreq samples per input of ifreq samples + unsigned ifreq; //input frequency + unsigned ofreq; //output frequency + + //intrinsic ring buffer: indices wrap around buffer size automatically + uint16_t lbuffer[256]; + uint16_t rbuffer[256]; + uint8_t bufferlength; + uint8_t bufferrd, bufferwr; + + double fraction, step; + int16_t lhist[4], rhist[4]; + double hermite(double mu, double a, double b, double c, double d); + + void sample(uint16_t lsamp, uint16_t rsamp); + void reset(unsigned ifreq, unsigned ofreq); +}; + +class Audio { +public: + void set_dsp_frequency(unsigned); + void set_cop_frequency(unsigned); + + void dsp_sample(uint16 l_sample, uint16 r_sample); + void cop_sample(uint16 l_sample, uint16 r_sample); + void init(); + + friend class System; + +private: + AudioResampler resample; + unsigned dsp_frequency; + unsigned cop_frequency; +} audio; diff --git a/src/system/config/config.cpp b/src/system/config/config.cpp new file mode 100644 index 00000000..59276db4 --- /dev/null +++ b/src/system/config/config.cpp @@ -0,0 +1,19 @@ +Config::Config() { + controller_port1 = System::Input::DeviceJoypad; + controller_port2 = System::Input::DeviceJoypad; + expansion_port = System::ExpansionBSX; + region = System::Autodetect; + + cpu.version = 2; + cpu.ntsc_clock_rate = 21477272; + cpu.pal_clock_rate = 21281370; + cpu.alu_mul_delay = 2; + cpu.alu_div_delay = 2; + cpu.wram_init_value = 0x55; + + smp.ntsc_clock_rate = 32041 * 768; + smp.pal_clock_rate = 32041 * 768; + + ppu1.version = 1; + ppu2.version = 3; +} diff --git a/src/system/config/config.hpp b/src/system/config/config.hpp new file mode 100644 index 00000000..2ead9bb5 --- /dev/null +++ b/src/system/config/config.hpp @@ -0,0 +1,32 @@ +struct Config { + unsigned controller_port1; + unsigned controller_port2; + unsigned expansion_port; + unsigned region; + + struct CPU { + unsigned version; + unsigned ntsc_clock_rate; + unsigned pal_clock_rate; + unsigned alu_mul_delay; + unsigned alu_div_delay; + unsigned wram_init_value; + } cpu; + + struct SMP { + unsigned ntsc_clock_rate; + unsigned pal_clock_rate; + } smp; + + struct PPU1 { + unsigned version; + } ppu1; + + struct PPU2 { + unsigned version; + } ppu2; + + Config(); +}; + +extern Config config; diff --git a/src/snes/input/input.cpp b/src/system/input/input.cpp similarity index 79% rename from src/snes/input/input.cpp rename to src/system/input/input.cpp index dcf3cc05..df3c095b 100644 --- a/src/snes/input/input.cpp +++ b/src/system/input/input.cpp @@ -1,13 +1,13 @@ #ifdef SNES_CPP -uint8 SNES::Input::port_read(bool portnumber) { +uint8 System::Input::port_read(bool portnumber) { port_t &p = port[portnumber]; switch(p.device) { case DeviceJoypad: { if(p.counter0 >= 16) return 1; unsigned deviceid = (portnumber == 0 ? DeviceIDJoypad1 : DeviceIDJoypad2); - return snesinterface.input_poll(deviceid, p.counter0++); + return system.interface->input_poll(deviceid, p.counter0++); } //case DeviceJoypad case DeviceMultitap: { @@ -48,16 +48,16 @@ uint8 SNES::Input::port_read(bool portnumber) { } } - return (snesinterface.input_poll(deviceid0, deviceidx) << 0) - | (snesinterface.input_poll(deviceid1, deviceidx) << 1); + return (system.interface->input_poll(deviceid0, deviceidx) << 0) + | (system.interface->input_poll(deviceid1, deviceidx) << 1); } //case DeviceMultitap case DeviceMouse: { if(p.counter0 >= 32) return 1; unsigned deviceid = (portnumber == 0 ? DeviceIDMouse1 : DeviceIDMouse2); - int position_x = snesinterface.input_poll(deviceid, MouseX); //-n = left, 0 = center, +n = right - int position_y = snesinterface.input_poll(deviceid, MouseY); //-n = up, 0 = center, +n = right + int position_x = system.interface->input_poll(deviceid, MouseX); //-n = left, 0 = center, +n = right + int position_y = system.interface->input_poll(deviceid, MouseY); //-n = up, 0 = center, +n = right bool direction_x = position_x < 0; //0 = right, 1 = left bool direction_y = position_y < 0; //0 = down, 1 = up @@ -78,8 +78,8 @@ uint8 SNES::Input::port_read(bool portnumber) { case 6: return 0; case 7: return 0; - case 8: return snesinterface.input_poll(deviceid, MouseRight); - case 9: return snesinterface.input_poll(deviceid, MouseLeft); + case 8: return system.interface->input_poll(deviceid, MouseRight); + case 9: return system.interface->input_poll(deviceid, MouseLeft); case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused) case 11: return 0; // || @@ -114,7 +114,7 @@ uint8 SNES::Input::port_read(bool portnumber) { if(p.counter0 == 0) { //turbo is a switch; toggle is edge sensitive - bool turbo = snesinterface.input_poll(DeviceIDSuperScope, SuperScopeTurbo); + bool turbo = system.interface->input_poll(DeviceIDSuperScope, SuperScopeTurbo); if(turbo && !p.superscope.turbolock) { p.superscope.turbo = !p.superscope.turbo; //toggle state p.superscope.turbolock = true; @@ -125,7 +125,7 @@ uint8 SNES::Input::port_read(bool portnumber) { //trigger is a button //if turbo is active, trigger is level sensitive; otherwise it is edge sensitive p.superscope.trigger = false; - bool trigger = snesinterface.input_poll(DeviceIDSuperScope, SuperScopeTrigger); + bool trigger = system.interface->input_poll(DeviceIDSuperScope, SuperScopeTrigger); if(trigger && (p.superscope.turbo || !p.superscope.triggerlock)) { p.superscope.trigger = true; p.superscope.triggerlock = true; @@ -134,11 +134,11 @@ uint8 SNES::Input::port_read(bool portnumber) { } //cursor is a button; it is always level sensitive - p.superscope.cursor = snesinterface.input_poll(DeviceIDSuperScope, SuperScopeCursor); + p.superscope.cursor = system.interface->input_poll(DeviceIDSuperScope, SuperScopeCursor); //pause is a button; it is always edge sensitive p.superscope.pause = false; - bool pause = snesinterface.input_poll(DeviceIDSuperScope, SuperScopePause); + bool pause = system.interface->input_poll(DeviceIDSuperScope, SuperScopePause); if(pause && !p.superscope.pauselock) { p.superscope.pause = true; p.superscope.pauselock = true; @@ -169,12 +169,12 @@ uint8 SNES::Input::port_read(bool portnumber) { if(p.counter0 >= 32) return 1; if(p.counter0 == 0) { - p.justifier.trigger1 = snesinterface.input_poll(DeviceIDJustifier1, JustifierTrigger); - p.justifier.start1 = snesinterface.input_poll(DeviceIDJustifier1, JustifierStart); + p.justifier.trigger1 = system.interface->input_poll(DeviceIDJustifier1, JustifierTrigger); + p.justifier.start1 = system.interface->input_poll(DeviceIDJustifier1, JustifierStart); if(p.device == DeviceJustifiers) { - p.justifier.trigger2 = snesinterface.input_poll(DeviceIDJustifier2, JustifierTrigger); - p.justifier.start2 = snesinterface.input_poll(DeviceIDJustifier2, JustifierStart); + p.justifier.trigger2 = system.interface->input_poll(DeviceIDJustifier2, JustifierTrigger); + p.justifier.start2 = system.interface->input_poll(DeviceIDJustifier2, JustifierStart); } else { p.justifier.x2 = -1; p.justifier.y2 = -1; @@ -230,14 +230,14 @@ uint8 SNES::Input::port_read(bool portnumber) { } //scan all input; update cursor positions if needed -void SNES::Input::update() { - snesinterface.input_poll(); +void System::Input::update() { + system.interface->input_poll(); port_t &p = port[1]; switch(p.device) { case DeviceSuperScope: { - int x = snesinterface.input_poll(DeviceIDSuperScope, SuperScopeX); - int y = snesinterface.input_poll(DeviceIDSuperScope, SuperScopeY); + int x = system.interface->input_poll(DeviceIDSuperScope, SuperScopeX); + int y = system.interface->input_poll(DeviceIDSuperScope, SuperScopeY); x += p.superscope.x; y += p.superscope.y; p.superscope.x = max(-16, min(256 + 16, x)); @@ -249,15 +249,15 @@ void SNES::Input::update() { case DeviceJustifier: case DeviceJustifiers: { - int x1 = snesinterface.input_poll(DeviceIDJustifier1, JustifierX); - int y1 = snesinterface.input_poll(DeviceIDJustifier1, JustifierY); + int x1 = system.interface->input_poll(DeviceIDJustifier1, JustifierX); + int y1 = system.interface->input_poll(DeviceIDJustifier1, JustifierY); x1 += p.justifier.x1; y1 += p.justifier.y1; p.justifier.x1 = max(-16, min(256 + 16, x1)); p.justifier.y1 = max(-16, min(240 + 16, y1)); - int x2 = snesinterface.input_poll(DeviceIDJustifier2, JustifierX); - int y2 = snesinterface.input_poll(DeviceIDJustifier2, JustifierY); + int x2 = system.interface->input_poll(DeviceIDJustifier2, JustifierX); + int y2 = system.interface->input_poll(DeviceIDJustifier2, JustifierY); x2 += p.justifier.x2; y2 += p.justifier.y2; p.justifier.x2 = max(-16, min(256 + 16, x2)); @@ -274,7 +274,7 @@ void SNES::Input::update() { } } -void SNES::Input::port_set_device(bool portnumber, unsigned device) { +void System::Input::port_set_device(bool portnumber, unsigned device) { port_t &p = port[portnumber]; p.device = device; @@ -326,7 +326,7 @@ void SNES::Input::port_set_device(bool portnumber, unsigned device) { } } -void SNES::Input::poll() { +void System::Input::poll() { port[0].counter0 = 0; port[0].counter1 = 0; port[1].counter0 = 0; @@ -335,7 +335,7 @@ void SNES::Input::poll() { port[1].justifier.active = !port[1].justifier.active; } -void SNES::Input::init() { +void System::Input::init() { } -#endif //ifdef SNES_CPP +#endif diff --git a/src/snes/input/input.hpp b/src/system/input/input.hpp similarity index 99% rename from src/snes/input/input.hpp rename to src/system/input/input.hpp index 339f1745..f866c83e 100644 --- a/src/snes/input/input.hpp +++ b/src/system/input/input.hpp @@ -110,5 +110,5 @@ private: } justifier; } port[2]; - friend class SNES; + friend class System; } input; diff --git a/src/system/interface/interface.hpp b/src/system/interface/interface.hpp new file mode 100644 index 00000000..6e37568a --- /dev/null +++ b/src/system/interface/interface.hpp @@ -0,0 +1,12 @@ +//interfaces SNES core with platform-specific functionality (video, audio, input, ...) + +class Interface { +public: + virtual void video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height) {} + virtual void audio_sample(uint16_t l_sample, uint16_t r_sample) {} + virtual void input_poll() {} + virtual int16_t input_poll(unsigned deviceid, unsigned id) { return 0; } + + virtual void init() {} + virtual void term() {} +}; diff --git a/src/snes/scheduler/scheduler.cpp b/src/system/scheduler/scheduler.cpp similarity index 80% rename from src/snes/scheduler/scheduler.cpp rename to src/system/scheduler/scheduler.cpp index 42d898b7..66398cfb 100644 --- a/src/snes/scheduler/scheduler.cpp +++ b/src/system/scheduler/scheduler.cpp @@ -3,7 +3,7 @@ Scheduler scheduler; void threadentry_cpu() { cpu.enter(); } -void threadentry_cop() { snes.coprocessor_enter(); } +void threadentry_cop() { system.coprocessor_enter(); } void threadentry_smp() { smp.enter(); } void threadentry_ppu() { ppu.enter(); } void threadentry_dsp() { dsp.enter(); } @@ -23,12 +23,12 @@ void Scheduler::exit() { } void Scheduler::init() { - clock.cpu_freq = snes.region() == SNES::NTSC - ? snes.config.cpu.ntsc_clock_rate - : snes.config.cpu.pal_clock_rate; - clock.smp_freq = snes.region() == SNES::NTSC - ? snes.config.smp.ntsc_clock_rate - : snes.config.smp.pal_clock_rate; + clock.cpu_freq = system.region() == System::NTSC + ? config.cpu.ntsc_clock_rate + : config.cpu.pal_clock_rate; + clock.smp_freq = system.region() == System::NTSC + ? config.smp.ntsc_clock_rate + : config.smp.pal_clock_rate; clock.active = THREAD_CPU; clock.cpucop = 0; diff --git a/src/snes/scheduler/scheduler.hpp b/src/system/scheduler/scheduler.hpp similarity index 100% rename from src/snes/scheduler/scheduler.hpp rename to src/system/scheduler/scheduler.hpp diff --git a/src/snes/snes.cpp b/src/system/system.cpp similarity index 56% rename from src/snes/snes.cpp rename to src/system/system.cpp index 6b62c476..82f2548f 100644 --- a/src/snes/snes.cpp +++ b/src/system/system.cpp @@ -1,55 +1,64 @@ #include <../base.hpp> -#include <../chip/chip.hpp> -#include <../cart/cart.hpp> + #define SNES_CPP +namespace SNES { -SNES snes; -BUSCORE bus; -CPUCORE cpu; -SMPCORE smp; -DSPCORE dsp; -PPUCORE ppu; +System system; +Config config; -SA1 sa1; -SA1Bus sa1bus; -BSXBase bsxbase; -BSXCart bsxcart; -BSXFlash bsxflash; -SRTC srtc; -SDD1 sdd1; -SPC7110 spc7110; -Cx4 cx4; -DSP1 dsp1; -DSP2 dsp2; -DSP3 dsp3; -DSP4 dsp4; -OBC1 obc1; -ST010 st010; +BUSCORE bus; +CPUCORE cpu; +SMPCORE smp; +DSPCORE dsp; +PPUCORE ppu; + +SuperGameboy sgb; +SA1 sa1; +SA1Bus sa1bus; +BSXBase bsxbase; +BSXCart bsxcart; +BSXFlash bsxflash; +SRTC srtc; +SDD1 sdd1; +SPC7110 spc7110; +Cx4 cx4; +DSP1 dsp1; +DSP2 dsp2; +DSP3 dsp3; +DSP4 dsp4; +OBC1 obc1; +ST010 st010; #include "scheduler/scheduler.cpp" #include "tracer/tracer.cpp" +#include "config/config.cpp" #include "video/video.cpp" #include "audio/audio.cpp" #include "input/input.cpp" -void SNES::coprocessor_enter() { +void System::coprocessor_enter() { + if(cartridge.mode() == Cartridge::ModeSuperGameboy) sgb.enter(); if(cartridge.has_sa1()) sa1.enter(); + //no active co-processor while(true) { scheduler.addclocks_cop(64 * 1024 * 1024); scheduler.sync_copcpu(); } } -void SNES::run() { +void System::run() { } -void SNES::runtoframe() { +void System::runtoframe() { scheduler.enter(); } -void SNES::init() { +void System::init(Interface *interface_) { + interface = interface_; + + sgb.init(); sa1.init(); bsxbase.init(); bsxcart.init(); @@ -68,22 +77,25 @@ void SNES::init() { video.init(); audio.init(); input.init(); - snesinterface.init(); + interface->init(); } -void SNES::term() { - snesinterface.term(); +void System::term() { + interface->term(); } -void SNES::power() { - snes_region = max(0, min(2, snes.config.region)); - snes_expansion = max(0, min(1, snes.config.expansion_port)); +void System::power() { + snes_region = max(0, min(2, config.region)); + snes_expansion = max(0, min(1, config.expansion_port)); if(snes_region == Autodetect) { snes_region = (cartridge.region() == Cartridge::NTSC ? NTSC : PAL); } scheduler.init(); + unsigned freq = (snes_region == NTSC ? config.smp.ntsc_clock_rate : config.smp.pal_clock_rate); + audio.set_dsp_frequency(freq / 768); + audio.set_cop_frequency(0); //disable bus audio by default ppu.PPUcounter::reset(); cpu.power(); @@ -92,9 +104,10 @@ void SNES::power() { ppu.power(); bus.power(); - if(expansion() == ExpansionBSX) bsxbase.power(); + if(expansion() == ExpansionBSX) bsxbase.power(); if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.power(); - if(cartridge.bsx_flash_loaded()) bsxflash.power(); + if(cartridge.bsx_flash_loaded()) bsxflash.power(); + if(cartridge.mode() == Cartridge::ModeSuperGameboy) sgb.power(); if(cartridge.has_sa1()) sa1.power(); if(cartridge.has_srtc()) srtc.power(); @@ -115,9 +128,10 @@ void SNES::power() { for(unsigned i = 0x4200; i <= 0x421f; i++) memory::mmio.map(i, cpu); for(unsigned i = 0x4300; i <= 0x437f; i++) memory::mmio.map(i, cpu); - if(expansion() == ExpansionBSX) bsxbase.enable(); + if(expansion() == ExpansionBSX) bsxbase.enable(); if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.enable(); - if(cartridge.bsx_flash_loaded()) bsxflash.enable(); + if(cartridge.bsx_flash_loaded()) bsxflash.enable(); + if(cartridge.mode() == Cartridge::ModeSuperGameboy) sgb.enable(); if(cartridge.has_sa1()) sa1.enable(); if(cartridge.has_srtc()) srtc.enable(); @@ -131,14 +145,17 @@ void SNES::power() { if(cartridge.has_obc1()) obc1.enable(); if(cartridge.has_st010()) st010.enable(); - input.port_set_device(0, snes.config.controller_port1); - input.port_set_device(1, snes.config.controller_port2); + input.port_set_device(0, config.controller_port1); + input.port_set_device(1, config.controller_port2); input.update(); video.update(); } -void SNES::reset() { +void System::reset() { scheduler.init(); + unsigned freq = (snes_region == NTSC ? config.smp.ntsc_clock_rate : config.smp.pal_clock_rate); + audio.set_dsp_frequency(freq / 768); + audio.set_cop_frequency(0); //disable bus audio by default ppu.PPUcounter::reset(); cpu.reset(); @@ -147,9 +164,10 @@ void SNES::reset() { ppu.reset(); bus.reset(); - if(expansion() == ExpansionBSX) bsxbase.reset(); + if(expansion() == ExpansionBSX) bsxbase.reset(); if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.reset(); - if(cartridge.bsx_flash_loaded()) bsxflash.reset(); + if(cartridge.bsx_flash_loaded()) bsxflash.reset(); + if(cartridge.mode() == Cartridge::ModeSuperGameboy) sgb.reset(); if(cartridge.has_sa1()) sa1.reset(); if(cartridge.has_srtc()) srtc.reset(); @@ -163,13 +181,13 @@ void SNES::reset() { if(cartridge.has_obc1()) obc1.reset(); if(cartridge.has_st010()) st010.reset(); - input.port_set_device(0, snes.config.controller_port1); - input.port_set_device(1, snes.config.controller_port2); + input.port_set_device(0, config.controller_port1); + input.port_set_device(1, config.controller_port2); input.update(); video.update(); } -void SNES::scanline() { +void System::scanline() { video.scanline(); if(ppu.vcounter() == 241) { @@ -179,47 +197,18 @@ void SNES::scanline() { } } -void SNES::frame() { +void System::frame() { } -SNES::Region SNES::region() const { - return (SNES::Region)snes_region; +System::Region System::region() const { + return (System::Region)snes_region; } -SNES::ExpansionPortDevice SNES::expansion() const { - return (SNES::ExpansionPortDevice)snes_expansion; +System::ExpansionPortDevice System::expansion() const { + return (System::ExpansionPortDevice)snes_expansion; } -SNES::SNES() : snes_region(NTSC), snes_expansion(ExpansionNone) { - config.controller_port1 = Input::DeviceJoypad; - config.controller_port2 = Input::DeviceJoypad; - config.expansion_port = ExpansionBSX; - config.region = Autodetect; - - config.file.autodetect_type = false; - config.file.bypass_patch_crc32 = false; - - config.path.base = ""; - config.path.user = ""; - config.path.current = ""; - config.path.rom = ""; - config.path.save = ""; - config.path.patch = ""; - config.path.cheat = ""; - config.path.data = ""; - config.path.bsx = ""; - config.path.st = ""; - - config.cpu.version = 2; - config.cpu.ntsc_clock_rate = 21477272; - config.cpu.pal_clock_rate = 21281370; - config.cpu.alu_mul_delay = 2; - config.cpu.alu_div_delay = 2; - config.cpu.wram_init_value = 0x55; - - config.smp.ntsc_clock_rate = 32041 * 768; - config.smp.pal_clock_rate = 32041 * 768; - - config.ppu1.version = 1; - config.ppu2.version = 3; +System::System() : snes_region(NTSC), snes_expansion(ExpansionNone) { } + +}; diff --git a/src/system/system.hpp b/src/system/system.hpp new file mode 100644 index 00000000..58b2c382 --- /dev/null +++ b/src/system/system.hpp @@ -0,0 +1,46 @@ +#include "interface/interface.hpp" +#include "scheduler/scheduler.hpp" +#include "tracer/tracer.hpp" +#include "config/config.hpp" + +class VideoFilter; + +class System { +public: + void coprocessor_enter(); + + enum Region { NTSC = 0, PAL = 1 }; + enum RegionAutodetect { Autodetect = 2 }; + enum ExpansionPortDevice { ExpansionNone = 0, ExpansionBSX = 1 }; + + //system functions + virtual void run(); + virtual void runtoframe(); + + virtual void init(Interface*); + virtual void term(); + virtual void power(); + virtual void reset(); + + virtual void frame(); + virtual void scanline(); + + //return *active* region / expansion port device information + //config settings are cached upon power-on + Region region() const; + ExpansionPortDevice expansion() const; + + #include "video/video.hpp" + #include "audio/audio.hpp" + #include "input/input.hpp" + + System(); + virtual ~System() {} + +private: + Interface *interface; + unsigned snes_region; + unsigned snes_expansion; +}; + +extern System system; diff --git a/src/snes/tracer/tracer.cpp b/src/system/tracer/tracer.cpp similarity index 96% rename from src/snes/tracer/tracer.cpp rename to src/system/tracer/tracer.cpp index d1f84734..f6b57482 100644 --- a/src/snes/tracer/tracer.cpp +++ b/src/system/tracer/tracer.cpp @@ -45,7 +45,7 @@ void Tracer::trace_smpop() { void Tracer::enable(bool en) { if(en == true && enabled() == false) { - fp = fopen(Cartridge::filepath("trace.log", snes.config.path.data), "wb"); + fp = fopen("trace.log", "wb"); } else if(en == false && enabled() == true) { fclose(fp); fp = 0; diff --git a/src/snes/tracer/tracer.hpp b/src/system/tracer/tracer.hpp similarity index 100% rename from src/snes/tracer/tracer.hpp rename to src/system/tracer/tracer.hpp diff --git a/src/snes/video/video.cpp b/src/system/video/video.cpp similarity index 77% rename from src/snes/video/video.cpp rename to src/system/video/video.cpp index a88712cb..f3f3a7a4 100644 --- a/src/snes/video/video.cpp +++ b/src/system/video/video.cpp @@ -1,6 +1,6 @@ #ifdef SNES_CPP -const uint8_t SNES::Video::cursor[15 * 15] = { +const uint8_t System::Video::cursor[15 * 15] = { 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, 0,0,0,1,2,2,1,2,1,2,2,1,0,0,0, @@ -18,7 +18,7 @@ const uint8_t SNES::Video::cursor[15 * 15] = { 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, }; -void SNES::Video::draw_cursor(uint16_t color, int x, int y) { +void System::Video::draw_cursor(uint16_t color, int x, int y) { for(int cy = 0; cy < 15; cy++) { int vy = y + cy - 7; if(vy <= 0 || vy >= 240) continue; //do not draw offscreen @@ -44,14 +44,14 @@ void SNES::Video::draw_cursor(uint16_t color, int x, int y) { } } -void SNES::Video::update() { +void System::Video::update() { uint16_t *data = (uint16_t*)ppu.output; unsigned width, height; - switch(snes.input.port[1].device) { - case SNES::Input::DeviceSuperScope: draw_cursor(0x001f, snes.input.port[1].superscope.x, snes.input.port[1].superscope.y); break; - case SNES::Input::DeviceJustifiers: draw_cursor(0x02e0, snes.input.port[1].justifier.x2, snes.input.port[1].justifier.y2); //fallthrough - case SNES::Input::DeviceJustifier: draw_cursor(0x001f, snes.input.port[1].justifier.x1, snes.input.port[1].justifier.y1); break; + switch(system.input.port[1].device) { + case System::Input::DeviceSuperScope: draw_cursor(0x001f, system.input.port[1].superscope.x, system.input.port[1].superscope.y); break; + case System::Input::DeviceJustifiers: draw_cursor(0x02e0, system.input.port[1].justifier.x2, system.input.port[1].justifier.y2); //fallthrough + case System::Input::DeviceJustifier: draw_cursor(0x001f, system.input.port[1].justifier.x1, system.input.port[1].justifier.y1); break; } unsigned yoffset = 1; //scanline 0 is always black, skip this line for video output @@ -65,7 +65,7 @@ void SNES::Video::update() { if(frame_hires) width <<= 1; if(frame_interlace) height <<= 1; - snesinterface.video_refresh( + system.interface->video_refresh( data + yoffset * 1024, /* pitch = */ height <= 240 ? 2048 : 1024, /* line[] = */ height <= 240 ? (pline_width + yoffset) : (iline_width + yoffset * 2), @@ -76,7 +76,7 @@ void SNES::Video::update() { frame_interlace = false; } -void SNES::Video::scanline() { +void System::Video::scanline() { unsigned y = ppu.vcounter(); if(y >= 240) return; @@ -88,11 +88,11 @@ void SNES::Video::scanline() { frame_interlace |= ppu.interlace(); } -void SNES::Video::set_mode(Mode mode_) { +void System::Video::set_mode(Mode mode_) { mode = mode_; } -void SNES::Video::init() { +void System::Video::init() { for(unsigned i = 0; i < 240; i++) pline_width[i] = 256; for(unsigned i = 0; i < 480; i++) iline_width[i] = 256; frame_hires = false; @@ -100,4 +100,4 @@ void SNES::Video::init() { set_mode(ModeNTSC); } -#endif //ifdef SNES_CPP +#endif diff --git a/src/snes/video/video.hpp b/src/system/video/video.hpp similarity index 94% rename from src/snes/video/video.hpp rename to src/system/video/video.hpp index 310b1d97..1118b9fa 100644 --- a/src/snes/video/video.hpp +++ b/src/system/video/video.hpp @@ -21,5 +21,5 @@ private: static const uint8_t cursor[15 * 15]; void draw_cursor(uint16_t color, int x, int y); - friend class SNES; + friend class System; } video; diff --git a/src/ui_qt/Makefile b/src/ui_qt/Makefile index a9b15ae1..03ca0253 100644 --- a/src/ui_qt/Makefile +++ b/src/ui_qt/Makefile @@ -65,7 +65,7 @@ moc_objects = \ $(foreach object,$(moc_objects),$(eval $(object): $(patsubst %.moc,%.hpp,$(object)))) obj/main.$(obj): $(ui)/main.cpp \ -$(ui)/* $(ui)/input/* $(ui)/utility/* $(ui)/base/* $(ui)/settings/* $(ui)/settings/utility/* \ +$(ui)/* $(ui)/cartridge/* $(ui)/input/* $(ui)/utility/* $(ui)/base/* $(ui)/settings/* $(ui)/settings/utility/* \ data/* $(call compile,$(qtflags)) diff --git a/src/ui_qt/base/loader.cpp b/src/ui_qt/base/loader.cpp index 18422051..e0b6679c 100644 --- a/src/ui_qt/base/loader.cpp +++ b/src/ui_qt/base/loader.cpp @@ -46,6 +46,10 @@ void LoaderWindow::setup() { slot2Clear = new QPushButton("Clear"); grid->addWidget(slot2Clear, 2, 3); + + bypassSuperGameboy = new QCheckBox("Bypass Super Gameboy BIOS"); + bypassSuperGameboy->setEnabled(false); + grid->addWidget(bypassSuperGameboy, 3, 0, 1, 3); } grid->setSpacing(Style::WidgetSpacing); layout->addLayout(grid); @@ -86,6 +90,7 @@ void LoaderWindow::loadBsxSlottedCartridge(const char *filebase, const char *fil baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show(); slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show(); slot2Label->hide(), slot2File->hide(), slot2Browse->hide(), slot2Clear->hide(); + bypassSuperGameboy->hide(); slot1Label->setText("Slot cartridge:"); @@ -93,7 +98,7 @@ void LoaderWindow::loadBsxSlottedCartridge(const char *filebase, const char *fil slot1File->setText(fileSlot1); syncUi(); - mode = ModeBsxSlotted; + mode = SNES::Cartridge::ModeBsxSlotted; showWindow("Load BS-X Slotted Cartridge"); } @@ -102,6 +107,7 @@ void LoaderWindow::loadBsxCartridge(const char *fileBase, const char *fileSlot1) baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show(); slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show(); slot2Label->hide(), slot2File->hide(), slot2Browse->hide(), slot2Clear->hide(); + bypassSuperGameboy->hide(); slot1Label->setText("Slot cartridge:"); @@ -109,7 +115,7 @@ void LoaderWindow::loadBsxCartridge(const char *fileBase, const char *fileSlot1) slot1File->setText(fileSlot1); syncUi(); - mode = ModeBsx; + mode = SNES::Cartridge::ModeBsx; showWindow("Load BS-X Cartridge"); } @@ -118,6 +124,7 @@ void LoaderWindow::loadSufamiTurboCartridge(const char *fileBase, const char *fi baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show(); slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show(); slot2Label->show(), slot2File->show(), slot2Browse->show(), slot2Clear->show(); + bypassSuperGameboy->hide(); slot1Label->setText("Slot A cartridge:"); slot2Label->setText("Slot B cartridge:"); @@ -127,10 +134,27 @@ void LoaderWindow::loadSufamiTurboCartridge(const char *fileBase, const char *fi slot2File->setText(fileSlot2); syncUi(); - mode = ModeSufamiTurbo; + mode = SNES::Cartridge::ModeSufamiTurbo; showWindow("Load Sufami Turbo Cartridge"); } +void LoaderWindow::loadSuperGameboyCartridge(const char *fileBase, const char *fileSlot1) { + window->hide(); + baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show(); + slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show(); + slot2Label->hide(), slot2File->hide(), slot2Browse->hide(), slot2Clear->hide(); + bypassSuperGameboy->show(); + + slot1Label->setText("Gameboy cartridge:"); + + baseFile->setText(fileBase); + slot1File->setText(fileSlot1); + + syncUi(); + mode = SNES::Cartridge::ModeSuperGameboy; + showWindow("Load Super Gameboy Cartridge"); +} + void LoaderWindow::showWindow(const char *title) { window->setWindowTitle(title); utility.showCentered(window); @@ -138,7 +162,7 @@ void LoaderWindow::showWindow(const char *title) { } void LoaderWindow::selectBaseCartridge() { - string filename = utility.selectCartridge(); + string filename = utility.selectCartridge(Utility::SnesCartridge); if(filename.length() > 0) baseFile->setText(utf8() << filename); syncUi(); } @@ -149,7 +173,12 @@ void LoaderWindow::clearBaseCartridge() { } void LoaderWindow::selectSlot1Cartridge() { - string filename = utility.selectCartridge(); + string filename; + if(mode == SNES::Cartridge::ModeSuperGameboy) { + filename = utility.selectCartridge(Utility::GameboyCartridge); + } else { + filename = utility.selectCartridge(Utility::SnesCartridge); + } if(filename.length() > 0) slot1File->setText(utf8() << filename); syncUi(); } @@ -160,7 +189,7 @@ void LoaderWindow::clearSlot1Cartridge() { } void LoaderWindow::selectSlot2Cartridge() { - string filename = utility.selectCartridge(); + string filename = utility.selectCartridge(Utility::SnesCartridge); if(filename.length() > 0) slot2File->setText(utf8() << filename); syncUi(); } @@ -177,18 +206,23 @@ void LoaderWindow::onLoad() { string slot2 = slot2File->text().toUtf8().data(); switch(mode) { - case ModeBsxSlotted: { - utility.loadCartridgeBsxSlotted(base, slot1); + case SNES::Cartridge::ModeBsxSlotted: { + cartridge.loadBsxSlotted(base, slot1); } break; - case ModeBsx: { - snes.config.path.bsx = base; - utility.loadCartridgeBsx(base, slot1); + case SNES::Cartridge::ModeBsx: { + config.path.bsx = base; + cartridge.loadBsx(base, slot1); } break; - case ModeSufamiTurbo: { - snes.config.path.st = base; - utility.loadCartridgeSufamiTurbo(base, slot1, slot2); + case SNES::Cartridge::ModeSufamiTurbo: { + config.path.st = base; + cartridge.loadSufamiTurbo(base, slot1, slot2); + } break; + + case SNES::Cartridge::ModeSuperGameboy: { + config.path.sgb = base; + cartridge.loadSuperGameboy(base, slot1); } break; } } diff --git a/src/ui_qt/base/loader.hpp b/src/ui_qt/base/loader.hpp index fdf6d2e9..621ba9d5 100644 --- a/src/ui_qt/base/loader.hpp +++ b/src/ui_qt/base/loader.hpp @@ -17,6 +17,7 @@ public: QLineEdit *slot2File; QPushButton *slot2Browse; QPushButton *slot2Clear; + QCheckBox *bypassSuperGameboy; QHBoxLayout *controls; QPushButton *load; QPushButton *cancel; @@ -27,6 +28,7 @@ public: void loadBsxSlottedCartridge(const char*, const char*); void loadBsxCartridge(const char*, const char*); void loadSufamiTurboCartridge(const char*, const char*, const char*); + void loadSuperGameboyCartridge(const char*, const char*); public slots: void selectBaseCartridge(); @@ -40,6 +42,6 @@ public slots: void onCancel(); private: - enum mode_t { ModeBsxSlotted, ModeBsx, ModeSufamiTurbo } mode; + SNES::Cartridge::Mode mode; void showWindow(const char *title); } *winLoader; diff --git a/src/ui_qt/base/main.cpp b/src/ui_qt/base/main.cpp index 400f27df..71136d83 100644 --- a/src/ui_qt/base/main.cpp +++ b/src/ui_qt/base/main.cpp @@ -195,22 +195,22 @@ void MainWindow::setup() { } void MainWindow::syncUi() { - system_power->setEnabled(cartridge.loaded()); + system_power->setEnabled(SNES::cartridge.loaded()); system_power_on->setChecked (application.power == true); system_power_off->setChecked(application.power == false); - system_reset->setEnabled(cartridge.loaded() && application.power); + system_reset->setEnabled(SNES::cartridge.loaded() && application.power); - system_port1_none->setChecked (snes.config.controller_port1 == SNES::Input::DeviceNone); - system_port1_joypad->setChecked (snes.config.controller_port1 == SNES::Input::DeviceJoypad); - system_port1_multitap->setChecked (snes.config.controller_port1 == SNES::Input::DeviceMultitap); - system_port1_mouse->setChecked (snes.config.controller_port1 == SNES::Input::DeviceMouse); - system_port2_none->setChecked (snes.config.controller_port2 == SNES::Input::DeviceNone); - system_port2_joypad->setChecked (snes.config.controller_port2 == SNES::Input::DeviceJoypad); - system_port2_multitap->setChecked (snes.config.controller_port2 == SNES::Input::DeviceMultitap); - system_port2_mouse->setChecked (snes.config.controller_port2 == SNES::Input::DeviceMouse); - system_port2_superscope->setChecked(snes.config.controller_port2 == SNES::Input::DeviceSuperScope); - system_port2_justifier->setChecked (snes.config.controller_port2 == SNES::Input::DeviceJustifier); - system_port2_justifiers->setChecked(snes.config.controller_port2 == SNES::Input::DeviceJustifiers); + system_port1_none->setChecked (SNES::config.controller_port1 == SNES::System::Input::DeviceNone); + system_port1_joypad->setChecked (SNES::config.controller_port1 == SNES::System::Input::DeviceJoypad); + system_port1_multitap->setChecked (SNES::config.controller_port1 == SNES::System::Input::DeviceMultitap); + system_port1_mouse->setChecked (SNES::config.controller_port1 == SNES::System::Input::DeviceMouse); + system_port2_none->setChecked (SNES::config.controller_port2 == SNES::System::Input::DeviceNone); + system_port2_joypad->setChecked (SNES::config.controller_port2 == SNES::System::Input::DeviceJoypad); + system_port2_multitap->setChecked (SNES::config.controller_port2 == SNES::System::Input::DeviceMultitap); + system_port2_mouse->setChecked (SNES::config.controller_port2 == SNES::System::Input::DeviceMouse); + system_port2_superscope->setChecked(SNES::config.controller_port2 == SNES::System::Input::DeviceSuperScope); + system_port2_justifier->setChecked (SNES::config.controller_port2 == SNES::System::Input::DeviceJustifier); + system_port2_justifiers->setChecked(SNES::config.controller_port2 == SNES::System::Input::DeviceJustifiers); settings_videoMode_1x->setChecked (config.video.context->multiplier == 1); settings_videoMode_2x->setChecked (config.video.context->multiplier == 2); @@ -244,7 +244,7 @@ void MainWindow::syncUi() { } void MainWindow::loadCartridge() { - string filename = utility.selectCartridge(); + string filename = utility.selectCartridge(Utility::AnyCartridge); if(filename.length() > 0) utility.loadCartridge(filename); } @@ -252,17 +252,17 @@ void MainWindow::powerOn() { utility.modifySystemState(Utility::PowerOn); } void MainWindow::powerOff() { utility.modifySystemState(Utility::PowerOff); } void MainWindow::reset() { utility.modifySystemState(Utility::Reset); } -void MainWindow::setPort1None() { snes.config.controller_port1 = SNES::Input::DeviceNone; utility.updateControllers(); syncUi(); } -void MainWindow::setPort1Joypad() { snes.config.controller_port1 = SNES::Input::DeviceJoypad; utility.updateControllers(); syncUi(); } -void MainWindow::setPort1Multitap() { snes.config.controller_port1 = SNES::Input::DeviceMultitap; utility.updateControllers(); syncUi(); } -void MainWindow::setPort1Mouse() { snes.config.controller_port1 = SNES::Input::DeviceMouse; utility.updateControllers(); syncUi(); } -void MainWindow::setPort2None() { snes.config.controller_port2 = SNES::Input::DeviceNone; utility.updateControllers(); syncUi(); } -void MainWindow::setPort2Joypad() { snes.config.controller_port2 = SNES::Input::DeviceJoypad; utility.updateControllers(); syncUi(); } -void MainWindow::setPort2Multitap() { snes.config.controller_port2 = SNES::Input::DeviceMultitap; utility.updateControllers(); syncUi(); } -void MainWindow::setPort2Mouse() { snes.config.controller_port2 = SNES::Input::DeviceMouse; utility.updateControllers(); syncUi(); } -void MainWindow::setPort2SuperScope() { snes.config.controller_port2 = SNES::Input::DeviceSuperScope; utility.updateControllers(); syncUi(); } -void MainWindow::setPort2Justifier() { snes.config.controller_port2 = SNES::Input::DeviceJustifier; utility.updateControllers(); syncUi(); } -void MainWindow::setPort2Justifiers() { snes.config.controller_port2 = SNES::Input::DeviceJustifiers; utility.updateControllers(); syncUi(); } +void MainWindow::setPort1None() { SNES::config.controller_port1 = SNES::System::Input::DeviceNone; utility.updateControllers(); syncUi(); } +void MainWindow::setPort1Joypad() { SNES::config.controller_port1 = SNES::System::Input::DeviceJoypad; utility.updateControllers(); syncUi(); } +void MainWindow::setPort1Multitap() { SNES::config.controller_port1 = SNES::System::Input::DeviceMultitap; utility.updateControllers(); syncUi(); } +void MainWindow::setPort1Mouse() { SNES::config.controller_port1 = SNES::System::Input::DeviceMouse; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2None() { SNES::config.controller_port2 = SNES::System::Input::DeviceNone; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2Joypad() { SNES::config.controller_port2 = SNES::System::Input::DeviceJoypad; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2Multitap() { SNES::config.controller_port2 = SNES::System::Input::DeviceMultitap; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2Mouse() { SNES::config.controller_port2 = SNES::System::Input::DeviceMouse; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2SuperScope() { SNES::config.controller_port2 = SNES::System::Input::DeviceSuperScope; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2Justifier() { SNES::config.controller_port2 = SNES::System::Input::DeviceJustifier; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2Justifiers() { SNES::config.controller_port2 = SNES::System::Input::DeviceJustifiers; utility.updateControllers(); syncUi(); } void MainWindow::quit() { application.terminate = true; @@ -356,7 +356,7 @@ void CanvasObject::dropEvent(QDropEvent *event) { //custom video render and mouse capture functionality QPaintEngine* CanvasWidget::paintEngine() const { - if(cartridge.loaded()) return 0; + if(SNES::cartridge.loaded()) return 0; return QWidget::paintEngine(); } diff --git a/src/ui_qt/cartridge/cartridge.cpp b/src/ui_qt/cartridge/cartridge.cpp new file mode 100644 index 00000000..da90fb9a --- /dev/null +++ b/src/ui_qt/cartridge/cartridge.cpp @@ -0,0 +1,341 @@ +bool Cartridge::loadNormal(const char *base) { + uint8_t *data; + unsigned size; + if(!loadFile(base, data, size, true)) return false; + unload(); + mode = SNES::Cartridge::ModeNormal; + cartName = base; + + if((size & 0x7fff) == 512) memmove(data, data + 512, size -= 512); + SNES::cartridge.load_normal(data, size); + delete[] data; + + loadMemory(SNES::memory::cartram, cartName, ".srm"); + loadMemory(SNES::memory::cartrtc, cartName, ".rtc"); + loadCheats(cartName); + + name = basename(cartName); + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +bool Cartridge::loadBsxSlotted(const char *base, const char *slot) { + uint8_t *data; + unsigned size; + if(!loadFile(base, data, size, true)) return false; + unload(); + mode = SNES::Cartridge::ModeBsxSlotted; + cartName = base; + slotName[0] = slot; + + uint8_t *slotData; + unsigned slotSize; + loadFile(slot, slotData, slotSize, true); + + if((size & 0x7fff) == 512) memmove(data, data + 512, size -= 512); + SNES::cartridge.load_bsx_slotted(data, size, slotData, slotSize); + delete[] data; + if(slotData) delete[] slotData; + + loadMemory(SNES::memory::cartram, cartName, ".srm"); + loadCheats(cartName); + + name = basename(cartName); + if(slotName[0] != "") name << " + " << basename(slotName[0]); + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +bool Cartridge::loadBsx(const char *base, const char *slot) { + uint8_t *data; + unsigned size; + if(!loadFile(base, data, size, true)) return false; + unload(); + mode = SNES::Cartridge::ModeBsx; + cartName = base; + slotName[0] = slot; + + uint8_t *slotData; + unsigned slotSize; + loadFile(slot, slotData, slotSize, true); + + if((size & 0x7fff) == 512) memmove(data, data + 512, size -= 512); + SNES::cartridge.load_bsx(data, size, slotData, slotSize); + delete[] data; + if(slotData) delete[] slotData; + + loadMemory(SNES::bsxcart.sram, cartName, ".srm"); + loadMemory(SNES::bsxcart.psram, cartName, ".psr"); + loadCheats(cartName); + + if(slotName[0] != "") name = basename(slotName[0]); + else name = basename(cartName); + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +bool Cartridge::loadSufamiTurbo(const char *base, const char *slotA, const char *slotB) { + uint8_t *data; + unsigned size; + if(!loadFile(base, data, size, true)) return false; + unload(); + mode = SNES::Cartridge::ModeSufamiTurbo; + cartName = base; + slotName[0] = slotA; + slotName[1] = slotB; + + uint8_t *slotData[2]; + unsigned slotSize[2]; + loadFile(slotA, slotData[0], slotSize[0], true); + loadFile(slotB, slotData[1], slotSize[1], true); + + if((size & 0x7fff) == 512) memmove(data, data + 512, size -= 512); + SNES::cartridge.load_sufami_turbo(data, size, slotData[0], slotSize[0], slotData[1], slotSize[1]); + delete[] data; + if(slotData[0]) delete[] slotData[0]; + if(slotData[1]) delete[] slotData[1]; + + loadMemory(SNES::memory::stAram, slotName[0], ".srm"); + loadMemory(SNES::memory::stBram, slotName[1], ".srm"); + loadCheats(cartName); + + if(slotName[0] != "" && slotName[1] != "") name = string() << basename(slotName[0]) << " + " << basename(slotName[1]); + else if(slotName[0] != "") name = basename(slotName[0]); + else if(slotName[1] != "") name = basename(slotName[1]); + else name = basename(cartName); + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +bool Cartridge::loadSuperGameboy(const char *base, const char *slot) { + uint8_t *data; + unsigned size; + if(!loadFile(base, data, size, true)) return false; + unload(); + mode = SNES::Cartridge::ModeSuperGameboy; + cartName = base; + slotName[0] = slot; + + uint8_t *slotData; + unsigned slotSize; + loadFile(slot, slotData, slotSize, true); + + if((size & 0x7fff) == 512) memmove(data, data + 512, size -= 512); + SNES::cartridge.load_super_gameboy(data, size, slotData, slotSize); + delete[] data; + + loadMemory(SNES::memory::dmgram, slotName[0], ".sav"); + loadMemory(SNES::memory::dmgrtc, slotName[0], ".rtc"); + loadCheats(cartName); + + if(slotName[0] != "") name = basename(slotName[0]); + else name = basename(cartName); + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +void Cartridge::unload() { + switch(mode) { + case SNES::Cartridge::ModeNormal: + case SNES::Cartridge::ModeBsxSlotted: { + saveMemory(SNES::memory::cartram, cartName, ".srm"); + saveMemory(SNES::memory::cartrtc, cartName, ".rtc"); + } break; + + case SNES::Cartridge::ModeBsx: { + saveMemory(SNES::bsxcart.sram, cartName, ".srm"); + saveMemory(SNES::bsxcart.psram, cartName, ".psr"); + } break; + + case SNES::Cartridge::ModeSufamiTurbo: { + saveMemory(SNES::memory::stAram, slotName[0], ".srm"); + saveMemory(SNES::memory::stBram, slotName[1], ".srm"); + } break; + + case SNES::Cartridge::ModeSuperGameboy: { + SNES::sgb.unload(); + saveMemory(SNES::memory::dmgram, slotName[0], ".sav"); + saveMemory(SNES::memory::dmgrtc, slotName[0], ".rtc"); + } break; + }; + + saveCheats(cartName); + utility.modifySystemState(Utility::UnloadCartridge); +} + +void Cartridge::loadCheats(const char *filename) { + string name; + name << (config.path.cheat != "" ? config.path.cheat : basepath(filename)); + name << basename(filename); + name << ".cht"; + if(file::exists(name)) SNES::cheat.load(name); +} + +void Cartridge::saveCheats(const char *filename) { + string name; + name << (config.path.cheat != "" ? config.path.cheat : basepath(filename)); + name << basename(filename); + name << ".cht"; + if(file::exists(name) || SNES::cheat.count() > 0) SNES::cheat.save(name); +} + +// + +void Cartridge::loadMemory(SNES::MappedRAM &memory, const char *filename, const char *extension) { + if(*filename && memory.size() > 0 && memory.size() != -1U) { + string name; + name << (config.path.save != "" ? config.path.save : basepath(filename)); + name << basename(filename); + name << extension; + + uint8_t *data; + unsigned size; + if(loadFile(name, data, size, false)) { + memcpy(memory.handle(), data, min(memory.size(), size)); + delete[] data; + } + } +} + +void Cartridge::saveMemory(SNES::MappedRAM &memory, const char *filename, const char *extension) { + if(*filename && memory.size() > 0 && memory.size() != -1U) { + string name; + name << (config.path.save != "" ? config.path.save : basepath(filename)); + name << basename(filename); + name << extension; + + file fp; + if(fp.open(name, file::mode_write)) { + fp.write(memory.handle(), memory.size()); + fp.close(); + } + } +} + +bool Cartridge::loadFile(const char *name, uint8_t *&data, unsigned &size, bool compression) { + data = 0; + size = 0; + + switch(Reader::detect(name, config.file.autodetect_type)) { + case Reader::Normal: { + FileReader fp(name); + if(!fp.ready()) return false; + size = fp.size(); + data = fp.read(); + } break; + + #ifdef GZIP_SUPPORT + case Reader::GZIP: { + GZReader fp(name); + if(!fp.ready()) return false; + size = fp.size(); + data = fp.read(); + } break; + + case Reader::ZIP: { + ZipReader fp(name); + if(!fp.ready()) return false; + size = fp.size(); + data = fp.read(); + } break; + #endif + + #ifdef JMA_SUPPORT + case Reader::JMA: { + try { + JMAReader fp(fp); + size = fp.size(); + data = fp.read(); + } catch(JMA::jma_errors jma_error) { + return false; + } + } break; + #endif + + default: return false; + } + + return true; +} + +SNES::Cartridge::Type Cartridge::detectImageType(const char *filename) { + uint8_t *data; + unsigned size; + if(!loadFile(filename, data, size, true)) return SNES::Cartridge::TypeUnknown; + + if((size & 0x7fff) == 512) memmove(data, data + 512, size -= 512); + SNES::Cartridge::Type type = SNES::cartridge.detect_image_type(data, size); + delete[] data; + return type; +} + +// + +//ensure file path is absolute (eg resolve relative paths) +string Cartridge::filepath(const char *filename, const char *pathname) { + //if no pathname, return filename as-is + string file(filename); + file.replace("\\", "/"); + + string path = (!pathname || !*pathname) ? (const char*)config.path.current : pathname; + //ensure path ends with trailing '/' + path.replace("\\", "/"); + if(!strend(path, "/")) path.append("/"); + + //replace relative path with absolute path + if(strbegin(path, "./")) { + ltrim(path, "./"); + path = string() << config.path.base << path; + } + + //remove folder part of filename + lstring part; + part.split("/", file); + return path << part[part.size() - 1]; +} + +//remove directory information and file extension ("/foo/bar.ext" -> "bar") +string Cartridge::basename(const char *filename) { + string name(filename); + + //remove extension + for(signed i = strlen(name) - 1; i >= 0; i--) { + if(name[i] == '.') { + name[i] = 0; + break; + } + } + + //remove directory information + for(signed i = strlen(name) - 1; i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + i++; + char *output = name(); + while(true) { + *output++ = name[i]; + if(!name[i]) break; + i++; + } + break; + } + } + + return name; +} + +//remove filename and return path only ("/foo/bar.ext" -> "/foo/") +string Cartridge::basepath(const char *filename) { + string path(filename); + path.replace("\\", "/"); + + //remove filename + for(signed i = strlen(path) - 1; i >= 0; i--) { + if(path[i] == '/') { + path[i] = 0; + break; + } + } + + if(!strend(path, "/")) path.append("/"); + return path; +} diff --git a/src/ui_qt/cartridge/cartridge.hpp b/src/ui_qt/cartridge/cartridge.hpp new file mode 100644 index 00000000..f69436b8 --- /dev/null +++ b/src/ui_qt/cartridge/cartridge.hpp @@ -0,0 +1,25 @@ +class Cartridge { +public: + SNES::Cartridge::Mode mode; + string name; + string cartName, slotName[2]; + + SNES::Cartridge::Type detectImageType(const char *filename); + bool loadNormal(const char *base); + bool loadBsxSlotted(const char *base, const char *slot); + bool loadBsx(const char *base, const char *slot); + bool loadSufamiTurbo(const char *base, const char *slotA, const char *slotB); + bool loadSuperGameboy(const char *base, const char *slot); + void unload(); + + static string filepath(const char*, const char*); + static string basename(const char*); + static string basepath(const char*); + +private: + bool loadFile(const char *name, uint8_t *&data, unsigned &size, bool compression); + void loadMemory(SNES::MappedRAM&, const char*, const char*); + void saveMemory(SNES::MappedRAM&, const char*, const char*); + void loadCheats(const char *filename); + void saveCheats(const char *filename); +} cartridge; diff --git a/src/ui_qt/config.cpp b/src/ui_qt/config.cpp index e764d63a..c6fb5b37 100644 --- a/src/ui_qt/config.cpp +++ b/src/ui_qt/config.cpp @@ -6,6 +6,19 @@ public: unsigned speed; } system; + struct File { + bool autodetect_type; + bool bypass_patch_crc32; + } file; + + struct Path { + string base; //binary path + string user; //user profile path + string current; //current working directory (path to currently loaded cartridge) + string rom, save, patch, cheat, data; + string bsx, st, sgb; + } path; + struct Video { bool isFullscreen; bool synchronize; @@ -70,44 +83,48 @@ public: //external //======== - attach(snes.config.controller_port1 = SNES::Input::DeviceJoypad, "snes.controllerPort1"); - attach(snes.config.controller_port2 = SNES::Input::DeviceJoypad, "snes.controllerPort2"); - attach(snes.config.expansion_port = SNES::ExpansionBSX, "snes.expansionPort"); - attach(snes.config.region = SNES::Autodetect, "snes.region"); + attach(SNES::config.controller_port1 = SNES::System::Input::DeviceJoypad, "snes.controllerPort1"); + attach(SNES::config.controller_port2 = SNES::System::Input::DeviceJoypad, "snes.controllerPort2"); + attach(SNES::config.expansion_port = SNES::System::ExpansionBSX, "snes.expansionPort"); + attach(SNES::config.region = SNES::System::Autodetect, "snes.region"); - attach(snes.config.file.autodetect_type = false, "file.autodetectType"); - attach(snes.config.file.bypass_patch_crc32 = false, "file.bypassPatchCrc32"); + attach(SNES::config.cpu.version = 2, "cpu.version", "Valid version(s) are: 1, 2"); + attach(SNES::config.cpu.ntsc_clock_rate = 21477272, "cpu.ntscClockRate"); + attach(SNES::config.cpu.pal_clock_rate = 21281370, "cpu.palClockRate"); + attach(SNES::config.cpu.alu_mul_delay = 2, "cpu.aluMulDelay"); + attach(SNES::config.cpu.alu_div_delay = 2, "cpu.aluDivDelay"); + attach(SNES::config.cpu.wram_init_value = 0x55, "cpu.wramInitValue"); - attach(snes.config.path.rom = "", "path.rom"); - attach(snes.config.path.save = "", "path.save"); - attach(snes.config.path.patch = "", "path.patch"); - attach(snes.config.path.cheat = "", "path.cheat"); - attach(snes.config.path.data = "", "path.data"); - attach(snes.config.path.bsx = "", "path.bsx"); - attach(snes.config.path.st = "", "path.st"); + attach(SNES::config.smp.ntsc_clock_rate = 32041 * 768, "smp.ntscClockRate"); + attach(SNES::config.smp.pal_clock_rate = 32041 * 768, "smp.palClockRate"); - attach(snes.config.cpu.version = 2, "cpu.version", "Valid version(s) are: 1, 2"); - attach(snes.config.cpu.ntsc_clock_rate = 21477272, "cpu.ntscClockRate"); - attach(snes.config.cpu.pal_clock_rate = 21281370, "cpu.palClockRate"); - attach(snes.config.cpu.alu_mul_delay = 2, "cpu.aluMulDelay"); - attach(snes.config.cpu.alu_div_delay = 2, "cpu.aluDivDelay"); - attach(snes.config.cpu.wram_init_value = 0x55, "cpu.wramInitValue"); - - attach(snes.config.smp.ntsc_clock_rate = 32041 * 768, "smp.ntscClockRate"); - attach(snes.config.smp.pal_clock_rate = 32041 * 768, "smp.palClockRate"); - - attach(snes.config.ppu1.version = 1, "ppu1.version", "Valid version(s) are: 1"); - attach(snes.config.ppu2.version = 3, "ppu2.version", "Valid version(s) are: 1, 2, 3"); + attach(SNES::config.ppu1.version = 1, "ppu1.version", "Valid version(s) are: 1"); + attach(SNES::config.ppu2.version = 3, "ppu2.version", "Valid version(s) are: 1, 2, 3"); //======== //internal //======== - attach(system.video = "", "system.video"); - attach(system.audio = "", "system.audio"); - attach(system.input = "", "system.input"); - attach(system.crashedOnLastRun = false, "system.crashedOnLastRun"); - attach(system.speed = 2, "system.speed"); + attach(system.video = "", "driver.video"); + attach(system.audio = "", "driver.audio"); + attach(system.input = "", "driver.input"); + attach(system.crashedOnLastRun = false, "emulator.crashedOnLastRun"); + attach(system.speed = 2, "emulator.speed"); + + attach(file.autodetect_type = false, "file.autodetectType"); + attach(file.bypass_patch_crc32 = false, "file.bypassPatchCrc32"); + + path.base = ""; + path.user = ""; + path.current = ""; + attach(path.rom = "", "path.rom"); + attach(path.save = "", "path.save"); + attach(path.patch = "", "path.patch"); + attach(path.cheat = "", "path.cheat"); + attach(path.data = "", "path.data"); + attach(path.bsx = "", "path.bsx"); + attach(path.st = "", "path.st"); + attach(path.sgb = "", "path.sgb"); video.context = &video.windowed; attach(video.isFullscreen = false, "video.isFullscreen"); diff --git a/src/ui_qt/input/device.cpp b/src/ui_qt/input/device.cpp index 6b41ba1b..6722ce4f 100644 --- a/src/ui_qt/input/device.cpp +++ b/src/ui_qt/input/device.cpp @@ -2,7 +2,7 @@ //InputDevice //=========== -InputDevice::InputDevice(SNES::Input::DeviceID i, bool p, const char *n) : InputGroup(n), id(i), port(p) { +InputDevice::InputDevice(SNES::System::Input::DeviceID i, bool p, const char *n) : InputGroup(n), id(i), port(p) { } //====== @@ -13,29 +13,29 @@ int16_t Joypad::state(unsigned index) const { if(config.input.allowInvalidInput == false) { //SNES D-pads have central pivot point, making up+down or left+right combinations impossible. //some software programs rely on this, and will crash if these combinations are allowed. - if(index == SNES::Input::JoypadDown && up.state ) return 0; - if(index == SNES::Input::JoypadRight && left.state) return 0; + if(index == SNES::System::Input::JoypadDown && up.state ) return 0; + if(index == SNES::System::Input::JoypadRight && left.state) return 0; } switch(index) { - case SNES::Input::JoypadUp: return up.state; - case SNES::Input::JoypadDown: return down.state; - case SNES::Input::JoypadLeft: return left.state; - case SNES::Input::JoypadRight: return right.state; - case SNES::Input::JoypadA: return a.state; - case SNES::Input::JoypadB: return b.state; - case SNES::Input::JoypadX: return x.state; - case SNES::Input::JoypadY: return y.state; - case SNES::Input::JoypadL: return l.state; - case SNES::Input::JoypadR: return r.state; - case SNES::Input::JoypadSelect: return select.state; - case SNES::Input::JoypadStart: return start.state; + case SNES::System::Input::JoypadUp: return up.state; + case SNES::System::Input::JoypadDown: return down.state; + case SNES::System::Input::JoypadLeft: return left.state; + case SNES::System::Input::JoypadRight: return right.state; + case SNES::System::Input::JoypadA: return a.state; + case SNES::System::Input::JoypadB: return b.state; + case SNES::System::Input::JoypadX: return x.state; + case SNES::System::Input::JoypadY: return y.state; + case SNES::System::Input::JoypadL: return l.state; + case SNES::System::Input::JoypadR: return r.state; + case SNES::System::Input::JoypadSelect: return select.state; + case SNES::System::Input::JoypadStart: return start.state; } return 0; } -Joypad::Joypad(SNES::Input::DeviceID id, bool port, const char *name, +Joypad::Joypad(SNES::System::Input::DeviceID id, bool port, const char *name, string &up_t, string &down_t, string &left_t, string &right_t, string &a_t, string &b_t, string &x_t, string &y_t, string &l_t, string &r_t, string &select_t, string &start_t ) : @@ -62,16 +62,16 @@ start (InputObject::Button, "Start", start_t) { int16_t Mouse::state(unsigned index) const { switch(index) { - case SNES::Input::MouseX: return x.state; - case SNES::Input::MouseY: return y.state; - case SNES::Input::MouseLeft: return left.state; - case SNES::Input::MouseRight: return right.state; + case SNES::System::Input::MouseX: return x.state; + case SNES::System::Input::MouseY: return y.state; + case SNES::System::Input::MouseLeft: return left.state; + case SNES::System::Input::MouseRight: return right.state; } return 0; } -Mouse::Mouse(SNES::Input::DeviceID id, bool port, const char *name, +Mouse::Mouse(SNES::System::Input::DeviceID id, bool port, const char *name, string &x_t, string &y_t, string &left_t, string &right_t ) : InputDevice(id, port, name), @@ -88,18 +88,18 @@ right(InputObject::Button, "Right button", right_t) { int16_t SuperScope::state(unsigned index) const { switch(index) { - case SNES::Input::SuperScopeX: return x.state; - case SNES::Input::SuperScopeY: return y.state; - case SNES::Input::SuperScopeTrigger: return trigger.state; - case SNES::Input::SuperScopeCursor: return cursor.state; - case SNES::Input::SuperScopeTurbo: return turbo.state; - case SNES::Input::SuperScopePause: return pause.state; + case SNES::System::Input::SuperScopeX: return x.state; + case SNES::System::Input::SuperScopeY: return y.state; + case SNES::System::Input::SuperScopeTrigger: return trigger.state; + case SNES::System::Input::SuperScopeCursor: return cursor.state; + case SNES::System::Input::SuperScopeTurbo: return turbo.state; + case SNES::System::Input::SuperScopePause: return pause.state; } return 0; } -SuperScope::SuperScope(SNES::Input::DeviceID id, bool port, const char *name, +SuperScope::SuperScope(SNES::System::Input::DeviceID id, bool port, const char *name, string &x_t, string &y_t, string &trigger_t, string &cursor_t, string &turbo_t, string &pause_t ) : InputDevice(id, port, name), @@ -118,16 +118,16 @@ pause (InputObject::Button, "Pause", pause_t) { int16_t Justifier::state(unsigned index) const { switch(index) { - case SNES::Input::JustifierX: return x.state; - case SNES::Input::JustifierY: return y.state; - case SNES::Input::JustifierTrigger: return trigger.state; - case SNES::Input::JustifierStart: return start.state; + case SNES::System::Input::JustifierX: return x.state; + case SNES::System::Input::JustifierY: return y.state; + case SNES::System::Input::JustifierTrigger: return trigger.state; + case SNES::System::Input::JustifierStart: return start.state; } return 0; } -Justifier::Justifier(SNES::Input::DeviceID id, bool port, const char *name, +Justifier::Justifier(SNES::System::Input::DeviceID id, bool port, const char *name, string &x_t, string &y_t, string &trigger_t, string &start_t ) : InputDevice(id, port, name), @@ -158,7 +158,7 @@ void InputDevicePool::poll(const int16_t *table) { for(unsigned i = 0; i < list.size(); i++) list[i]->poll(table); } -InputDevice* InputDevicePool::find(SNES::Input::DeviceID id) { +InputDevice* InputDevicePool::find(SNES::System::Input::DeviceID id) { for(unsigned i = 0; i < list.size(); i++) { if(list[i]->id == id) return list[i]; } @@ -171,72 +171,72 @@ InputDevicePool::InputDevicePool() : list(*this) { // -Joypad joypad1(SNES::Input::DeviceIDJoypad1, InputDevice::Port1, "Joypad", +Joypad joypad1(SNES::System::Input::DeviceIDJoypad1, InputDevice::Port1, "Joypad", config.input.joypad1.up, config.input.joypad1.down, config.input.joypad1.left, config.input.joypad1.right, config.input.joypad1.a, config.input.joypad1.b, config.input.joypad1.x, config.input.joypad1.y, config.input.joypad1.l, config.input.joypad1.r, config.input.joypad1.select, config.input.joypad1.start); -Joypad joypad2(SNES::Input::DeviceIDJoypad2, InputDevice::Port2, "Joypad", +Joypad joypad2(SNES::System::Input::DeviceIDJoypad2, InputDevice::Port2, "Joypad", config.input.joypad2.up, config.input.joypad2.down, config.input.joypad2.left, config.input.joypad2.right, config.input.joypad2.a, config.input.joypad2.b, config.input.joypad2.x, config.input.joypad2.y, config.input.joypad2.l, config.input.joypad2.r, config.input.joypad2.select, config.input.joypad2.start); -Joypad multitap1a(SNES::Input::DeviceIDMultitap1A, InputDevice::Port1, "Multitap - Port 1", +Joypad multitap1a(SNES::System::Input::DeviceIDMultitap1A, InputDevice::Port1, "Multitap - Port 1", config.input.multitap1a.up, config.input.multitap1a.down, config.input.multitap1a.left, config.input.multitap1a.right, config.input.multitap1a.a, config.input.multitap1a.b, config.input.multitap1a.x, config.input.multitap1a.y, config.input.multitap1a.l, config.input.multitap1a.r, config.input.multitap1a.select, config.input.multitap1a.start); -Joypad multitap1b(SNES::Input::DeviceIDMultitap1B, InputDevice::Port1, "Multitap - Port 2", +Joypad multitap1b(SNES::System::Input::DeviceIDMultitap1B, InputDevice::Port1, "Multitap - Port 2", config.input.multitap1b.up, config.input.multitap1b.down, config.input.multitap1b.left, config.input.multitap1b.right, config.input.multitap1b.a, config.input.multitap1b.b, config.input.multitap1b.x, config.input.multitap1b.y, config.input.multitap1b.l, config.input.multitap1b.r, config.input.multitap1b.select, config.input.multitap1b.start); -Joypad multitap1c(SNES::Input::DeviceIDMultitap1C, InputDevice::Port1, "Multitap - Port 3", +Joypad multitap1c(SNES::System::Input::DeviceIDMultitap1C, InputDevice::Port1, "Multitap - Port 3", config.input.multitap1c.up, config.input.multitap1c.down, config.input.multitap1c.left, config.input.multitap1c.right, config.input.multitap1c.a, config.input.multitap1c.b, config.input.multitap1c.x, config.input.multitap1c.y, config.input.multitap1c.l, config.input.multitap1c.r, config.input.multitap1c.select, config.input.multitap1c.start); -Joypad multitap1d(SNES::Input::DeviceIDMultitap1D, InputDevice::Port1, "Multitap - Port 4", +Joypad multitap1d(SNES::System::Input::DeviceIDMultitap1D, InputDevice::Port1, "Multitap - Port 4", config.input.multitap1d.up, config.input.multitap1d.down, config.input.multitap1d.left, config.input.multitap1d.right, config.input.multitap1d.a, config.input.multitap1d.b, config.input.multitap1d.x, config.input.multitap1d.y, config.input.multitap1d.l, config.input.multitap1d.r, config.input.multitap1d.select, config.input.multitap1d.start); -Joypad multitap2a(SNES::Input::DeviceIDMultitap2A, InputDevice::Port2, "Multitap - Port 1", +Joypad multitap2a(SNES::System::Input::DeviceIDMultitap2A, InputDevice::Port2, "Multitap - Port 1", config.input.multitap2a.up, config.input.multitap2a.down, config.input.multitap2a.left, config.input.multitap2a.right, config.input.multitap2a.a, config.input.multitap2a.b, config.input.multitap2a.x, config.input.multitap2a.y, config.input.multitap2a.l, config.input.multitap2a.r, config.input.multitap2a.select, config.input.multitap2a.start); -Joypad multitap2b(SNES::Input::DeviceIDMultitap2B, InputDevice::Port2, "Multitap - Port 2", +Joypad multitap2b(SNES::System::Input::DeviceIDMultitap2B, InputDevice::Port2, "Multitap - Port 2", config.input.multitap2b.up, config.input.multitap2b.down, config.input.multitap2b.left, config.input.multitap2b.right, config.input.multitap2b.a, config.input.multitap2b.b, config.input.multitap2b.x, config.input.multitap2b.y, config.input.multitap2b.l, config.input.multitap2b.r, config.input.multitap2b.select, config.input.multitap2b.start); -Joypad multitap2c(SNES::Input::DeviceIDMultitap2C, InputDevice::Port2, "Multitap - Port 3", +Joypad multitap2c(SNES::System::Input::DeviceIDMultitap2C, InputDevice::Port2, "Multitap - Port 3", config.input.multitap2c.up, config.input.multitap2c.down, config.input.multitap2c.left, config.input.multitap2c.right, config.input.multitap2c.a, config.input.multitap2c.b, config.input.multitap2c.x, config.input.multitap2c.y, config.input.multitap2c.l, config.input.multitap2c.r, config.input.multitap2c.select, config.input.multitap2c.start); -Joypad multitap2d(SNES::Input::DeviceIDMultitap2D, InputDevice::Port2, "Multitap - Port 4", +Joypad multitap2d(SNES::System::Input::DeviceIDMultitap2D, InputDevice::Port2, "Multitap - Port 4", config.input.multitap2d.up, config.input.multitap2d.down, config.input.multitap2d.left, config.input.multitap2d.right, config.input.multitap2d.a, config.input.multitap2d.b, config.input.multitap2d.x, config.input.multitap2d.y, config.input.multitap2d.l, config.input.multitap2d.r, config.input.multitap2d.select, config.input.multitap2d.start); -Mouse mouse1(SNES::Input::DeviceIDMouse1, InputDevice::Port1, "Mouse", +Mouse mouse1(SNES::System::Input::DeviceIDMouse1, InputDevice::Port1, "Mouse", config.input.mouse1.x, config.input.mouse1.y, config.input.mouse1.left, config.input.mouse1.right); -Mouse mouse2(SNES::Input::DeviceIDMouse2, InputDevice::Port2, "Mouse", +Mouse mouse2(SNES::System::Input::DeviceIDMouse2, InputDevice::Port2, "Mouse", config.input.mouse2.x, config.input.mouse2.y, config.input.mouse2.left, config.input.mouse2.right); -SuperScope superscope(SNES::Input::DeviceIDSuperScope, InputDevice::Port2, "Super Scope", +SuperScope superscope(SNES::System::Input::DeviceIDSuperScope, InputDevice::Port2, "Super Scope", config.input.superscope.x, config.input.superscope.y, config.input.superscope.trigger, config.input.superscope.cursor, config.input.superscope.turbo, config.input.superscope.pause); -Justifier justifier1(SNES::Input::DeviceIDJustifier1, InputDevice::Port2, "Justifier 1", +Justifier justifier1(SNES::System::Input::DeviceIDJustifier1, InputDevice::Port2, "Justifier 1", config.input.justifier1.x, config.input.justifier1.y, config.input.justifier1.trigger, config.input.justifier1.start); -Justifier justifier2(SNES::Input::DeviceIDJustifier2, InputDevice::Port2, "Justifier 2", +Justifier justifier2(SNES::System::Input::DeviceIDJustifier2, InputDevice::Port2, "Justifier 2", config.input.justifier2.x, config.input.justifier2.y, config.input.justifier2.trigger, config.input.justifier2.start); diff --git a/src/ui_qt/input/device.hpp b/src/ui_qt/input/device.hpp index 5ac95f74..9918c3d2 100644 --- a/src/ui_qt/input/device.hpp +++ b/src/ui_qt/input/device.hpp @@ -1,16 +1,16 @@ struct InputDevice : InputGroup { - SNES::Input::DeviceID id; + SNES::System::Input::DeviceID id; enum Port { Port1, Port2 }; const bool port; - InputDevice(SNES::Input::DeviceID i, bool p, const char *n); + InputDevice(SNES::System::Input::DeviceID i, bool p, const char *n); }; struct Joypad : InputDevice { InputObject up, down, left, right, a, b, x, y, l, r, select, start; int16_t state(unsigned index) const; - Joypad(SNES::Input::DeviceID id, bool port, const char *name, + Joypad(SNES::System::Input::DeviceID id, bool port, const char *name, string&, string&, string&, string&, string&, string&, string&, string&, string&, string&, string&, string&); }; @@ -19,7 +19,7 @@ struct Mouse : InputDevice { InputObject x, y, left, right; int16_t state(unsigned index) const; - Mouse(SNES::Input::DeviceID id, bool port, const char *name, + Mouse(SNES::System::Input::DeviceID id, bool port, const char *name, string&, string&, string&, string&); }; @@ -27,7 +27,7 @@ struct SuperScope : InputDevice { InputObject x, y, trigger, cursor, turbo, pause; int16_t state(unsigned index) const; - SuperScope(SNES::Input::DeviceID id, bool port, const char *name, + SuperScope(SNES::System::Input::DeviceID id, bool port, const char *name, string&, string&, string&, string&, string&, string&); }; @@ -35,7 +35,7 @@ struct Justifier : InputDevice { InputObject x, y, trigger, start; int16_t state(unsigned index) const; - Justifier(SNES::Input::DeviceID id, bool port, const char *name, + Justifier(SNES::System::Input::DeviceID id, bool port, const char *name, string&, string&, string&, string&); }; @@ -44,7 +44,7 @@ struct InputDevicePool : public array { void bind(); void clear(); void poll(const int16_t *table); - InputDevice* find(SNES::Input::DeviceID id); + InputDevice* find(SNES::System::Input::DeviceID id); InputDevicePool(); private: diff --git a/src/ui_qt/input/input.cpp b/src/ui_qt/input/input.cpp index f958685c..02848704 100644 --- a/src/ui_qt/input/input.cpp +++ b/src/ui_qt/input/input.cpp @@ -103,7 +103,7 @@ int16_t InputManager::lastState(uint16_t code) const { } int16_t InputManager::getStatus(unsigned deviceid, unsigned id) const { - InputDevice *device = inputPool.find((SNES::Input::DeviceID)deviceid); + InputDevice *device = inputPool.find((SNES::System::Input::DeviceID)deviceid); if(device) return device->state(id); return 0; } diff --git a/src/ui_qt/interface.cpp b/src/ui_qt/interface.cpp index 4a343eee..df1f73c1 100644 --- a/src/ui_qt/interface.cpp +++ b/src/ui_qt/interface.cpp @@ -1,31 +1,26 @@ -SNESInterface snesinterface; - -void SNESInterface::video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height) { - uint32_t *output; - unsigned outpitch; - if(video.lock(output, outpitch) == true) { - unsigned outwidth, outheight; - libfilter::filter.render(output, outpitch, outwidth, outheight, data, pitch, line, width, height); - video.unlock(); - video.refresh(outwidth, outheight); +class Interface : public SNES::Interface { +public: + void video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height) { + uint32_t *output; + unsigned outpitch; + if(video.lock(output, outpitch) == true) { + unsigned outwidth, outheight; + libfilter::filter.render(output, outpitch, outwidth, outheight, data, pitch, line, width, height); + video.unlock(); + video.refresh(outwidth, outheight); + } } -} -void SNESInterface::audio_sample(uint16_t left, uint16_t right) { - if(config.audio.mute) left = right = 0; - audio.sample(left, right); -} + void audio_sample(uint16_t left, uint16_t right) { + if(config.audio.mute) left = right = 0; + audio.sample(left, right); + } -void SNESInterface::input_poll() { - inputManager.poll(); -} + void input_poll() { + inputManager.poll(); + } -int16_t SNESInterface::input_poll(unsigned deviceid, unsigned id) { - return inputManager.getStatus(deviceid, id); -} - -void SNESInterface::init() { -} - -void SNESInterface::term() { -} + int16_t input_poll(unsigned deviceid, unsigned id) { + return inputManager.getStatus(deviceid, id); + } +} interface; diff --git a/src/ui_qt/main.cpp b/src/ui_qt/main.cpp index c3785cd7..141c64d9 100644 --- a/src/ui_qt/main.cpp +++ b/src/ui_qt/main.cpp @@ -13,6 +13,7 @@ public: #include "interface.cpp" #include "ui.cpp" +#include "cartridge/cartridge.cpp" #include "input/input.cpp" #include "utility/utility.cpp" @@ -51,30 +52,30 @@ void Application::initPaths(const char *basename) { } if(strend(temp, "/") == false) strcat(temp, "/"); - snes.config.path.base = temp; + config.path.base = temp; } else { - snes.config.path.base = ""; + config.path.base = ""; } if(userpath(temp)) { strtr(temp, "\\", "/"); if(strend(temp, "/") == false) strcat(temp, "/"); - snes.config.path.user = temp; + config.path.user = temp; } else { - snes.config.path.user = ""; + config.path.user = ""; } char cwd[PATH_MAX]; - snes.config.path.current = getcwd(cwd); + config.path.current = getcwd(cwd); } void Application::locateFile(string &filename, bool createDataDirectory) { //first, check if file exists in executable directory (single-user mode) - string temp = string() << snes.config.path.base << filename; + string temp = string() << config.path.base << filename; if(file::exists(temp) == false) { //if not, use user data path (multi-user mode) - temp = snes.config.path.user; + temp = config.path.user; temp << ".bsnes"; if(createDataDirectory) mkdir(temp); //ensure directory exists temp << "/" << filename; @@ -104,7 +105,7 @@ int Application::main(int argc, char **argv) { config.load(configFilename); init(); - snes.init(); + SNES::system.init(&interface); if(argc == 2) { //if valid file was specified on the command-line, attempt to load it now @@ -129,8 +130,8 @@ int Application::main(int argc, char **argv) { autopause = false; } - if(cartridge.loaded() && !pause && !autopause) { - snes.runtoframe(); + if(SNES::cartridge.loaded() && !pause && !autopause) { + SNES::system.runtoframe(); } else { usleep(20 * 1000); } @@ -138,6 +139,7 @@ int Application::main(int argc, char **argv) { supressScreenSaver(); } + cartridge.unload(); config.save(configFilename); return 0; } diff --git a/src/ui_qt/main.hpp b/src/ui_qt/main.hpp index 73227f29..b559645a 100644 --- a/src/ui_qt/main.hpp +++ b/src/ui_qt/main.hpp @@ -9,8 +9,19 @@ //Q_IMPORT_PLUGIN(QJpegPlugin) //Q_IMPORT_PLUGIN(QMngPlugin) +#include +#include + +#if defined(GZIP_SUPPORT) + #include + #include +#endif + +#if defined(JMA_SUPPORT) + #include +#endif + #include <../base.hpp> -#include <../cart/cart.hpp> #include #include @@ -22,6 +33,7 @@ using namespace ruby; #include +#include "cartridge/cartridge.hpp" #include "input/input.hpp" #include "utility/utility.hpp" diff --git a/src/ui_qt/settings/advanced.cpp b/src/ui_qt/settings/advanced.cpp index 9f6161f6..d77cd5c0 100644 --- a/src/ui_qt/settings/advanced.cpp +++ b/src/ui_qt/settings/advanced.cpp @@ -147,12 +147,12 @@ void AdvancedSettingsWindow::initializeUi() { if(part[i] == config.system.input) inputDriver->setCurrentIndex(i); } - regionAuto->setChecked(snes.config.region == SNES::Autodetect); - regionNTSC->setChecked(snes.config.region == SNES::NTSC); - regionPAL->setChecked (snes.config.region == SNES::PAL); + regionAuto->setChecked(SNES::config.region == SNES::System::Autodetect); + regionNTSC->setChecked(SNES::config.region == SNES::System::NTSC); + regionPAL->setChecked (SNES::config.region == SNES::System::PAL); - portSatellaview->setChecked(snes.config.expansion_port == SNES::ExpansionBSX); - portNone->setChecked (snes.config.expansion_port == SNES::ExpansionNone); + portSatellaview->setChecked(SNES::config.expansion_port == SNES::System::ExpansionBSX); + portNone->setChecked (SNES::config.expansion_port == SNES::System::ExpansionNone); focusPause->setChecked (config.input.focusPolicy == Configuration::Input::FocusPolicyPauseEmulation); focusIgnore->setChecked(config.input.focusPolicy == Configuration::Input::FocusPolicyIgnoreInput); @@ -171,12 +171,12 @@ void AdvancedSettingsWindow::inputDriverChange(int index) { if(index >= 0) config.system.input = inputDriver->itemText(index).toUtf8().data(); } -void AdvancedSettingsWindow::setRegionAuto() { snes.config.region = SNES::Autodetect; } -void AdvancedSettingsWindow::setRegionNTSC() { snes.config.region = SNES::NTSC; } -void AdvancedSettingsWindow::setRegionPAL() { snes.config.region = SNES::PAL; } +void AdvancedSettingsWindow::setRegionAuto() { SNES::config.region = SNES::System::Autodetect; } +void AdvancedSettingsWindow::setRegionNTSC() { SNES::config.region = SNES::System::NTSC; } +void AdvancedSettingsWindow::setRegionPAL() { SNES::config.region = SNES::System::PAL; } -void AdvancedSettingsWindow::setPortSatellaview() { snes.config.expansion_port = SNES::ExpansionBSX; } -void AdvancedSettingsWindow::setPortNone() { snes.config.expansion_port = SNES::ExpansionNone; } +void AdvancedSettingsWindow::setPortSatellaview() { SNES::config.expansion_port = SNES::System::ExpansionBSX; } +void AdvancedSettingsWindow::setPortNone() { SNES::config.expansion_port = SNES::System::ExpansionNone; } void AdvancedSettingsWindow::pauseWithoutFocus() { config.input.focusPolicy = Configuration::Input::FocusPolicyPauseEmulation; } void AdvancedSettingsWindow::ignoreInputWithoutFocus() { config.input.focusPolicy = Configuration::Input::FocusPolicyIgnoreInput; } diff --git a/src/ui_qt/settings/cheateditor.cpp b/src/ui_qt/settings/cheateditor.cpp index c9a93f5c..b7260412 100644 --- a/src/ui_qt/settings/cheateditor.cpp +++ b/src/ui_qt/settings/cheateditor.cpp @@ -42,10 +42,10 @@ void CheatEditorWindow::setup() { } void CheatEditorWindow::syncUi() { - addCode->setEnabled(cartridge.loaded()); + addCode->setEnabled(SNES::cartridge.loaded()); QList itemList = list->selectedItems(); - editCode->setEnabled(cartridge.loaded() && itemList.count() == 1); - deleteCode->setEnabled(cartridge.loaded() && itemList.count() == 1); + editCode->setEnabled(SNES::cartridge.loaded() && itemList.count() == 1); + deleteCode->setEnabled(SNES::cartridge.loaded() && itemList.count() == 1); } //called when loading a new game, or after adding / deleting a code: @@ -57,10 +57,10 @@ void CheatEditorWindow::reloadList() { list->setSortingEnabled(false); listItem.reset(); - if(cartridge.loaded()) { - for(unsigned i = 0; i < cheat.count(); i++) { - Cheat::cheat_t code; - cheat.get(i, code); + if(SNES::cartridge.loaded()) { + for(unsigned i = 0; i < SNES::cheat.count(); i++) { + SNES::Cheat::cheat_t code; + SNES::cheat.get(i, code); //only want to show one code / description line in list lstring lcode, ldesc; @@ -91,8 +91,8 @@ void CheatEditorWindow::updateList() { disconnect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemChanged(QTreeWidgetItem*))); for(unsigned i = 0; i < listItem.size(); i++) { - Cheat::cheat_t code; - cheat.get(i, code); + SNES::Cheat::cheat_t code; + SNES::cheat.get(i, code); //only want to show one code / description line in list lstring lcode, ldesc; @@ -116,7 +116,7 @@ void CheatEditorWindow::updateList() { //called when item enabled checkbox was clicked (eg cheat code enable state was toggled on or off) void CheatEditorWindow::itemChanged(QTreeWidgetItem *item) { signed i = listItem.find(item); - if(i >= 0) item->checkState(1) == Qt::Checked ? cheat.enable(i) : cheat.disable(i); + if(i >= 0) item->checkState(1) == Qt::Checked ? SNES::cheat.enable(i) : SNES::cheat.disable(i); } void CheatEditorWindow::listChanged() { @@ -124,11 +124,11 @@ void CheatEditorWindow::listChanged() { } void CheatEditorWindow::addNewCode() { - if(cartridge.loaded()) winCodeEditor->addCode(); + if(SNES::cartridge.loaded()) winCodeEditor->addCode(); } void CheatEditorWindow::editSelectedCode() { - if(cartridge.loaded()) { + if(SNES::cartridge.loaded()) { QTreeWidgetItem *item = list->currentItem(); if(item && item->isSelected()) { signed i = listItem.find(item); @@ -138,7 +138,7 @@ void CheatEditorWindow::editSelectedCode() { } void CheatEditorWindow::deleteSelectedCode() { - if(cartridge.loaded()) { + if(SNES::cartridge.loaded()) { QTreeWidgetItem *item = list->currentItem(); if(item && item->isSelected()) { signed i = listItem.find(item); @@ -148,7 +148,7 @@ void CheatEditorWindow::deleteSelectedCode() { if(winCodeEditor->activeCode >= 0) winCodeEditor->dismiss(); //remove code, and resync listItem with cheat list - cheat.remove(i); + SNES::cheat.remove(i); reloadList(); } } diff --git a/src/ui_qt/settings/paths.cpp b/src/ui_qt/settings/paths.cpp index 2b4b0cb9..e8da48fd 100644 --- a/src/ui_qt/settings/paths.cpp +++ b/src/ui_qt/settings/paths.cpp @@ -127,74 +127,79 @@ void PathSettingsWindow::setup() { } void PathSettingsWindow::syncUi() { - gamePath->setText (snes.config.path.rom == "" ? "" : (const char*)snes.config.path.rom); - savePath->setText (snes.config.path.save == "" ? "" : (const char*)snes.config.path.save); - patchPath->setText(snes.config.path.patch == "" ? "" : (const char*)snes.config.path.patch); - cheatPath->setText(snes.config.path.cheat == "" ? "" : (const char*)snes.config.path.cheat); - dataPath->setText (snes.config.path.data == "" ? "" : (const char*)snes.config.path.data); + gamePath->setText (config.path.rom == "" ? "" : (const char*)config.path.rom); + savePath->setText (config.path.save == "" ? "" : (const char*)config.path.save); + patchPath->setText(config.path.patch == "" ? "" : (const char*)config.path.patch); + cheatPath->setText(config.path.cheat == "" ? "" : (const char*)config.path.cheat); + dataPath->setText (config.path.data == "" ? "" : (const char*)config.path.data); } void PathSettingsWindow::selectGamePath() { string path = utility.selectFolder("Default Game Path"); if(path.length() > 0) { - snes.config.path.rom = path; + config.path.rom = strtr(path, "\\", "/"); + if(!strend(config.path.rom, "/")) config.path.rom << "/"; syncUi(); } } void PathSettingsWindow::defaultGamePath() { - snes.config.path.rom = ""; + config.path.rom = ""; syncUi(); } void PathSettingsWindow::selectSavePath() { string path = utility.selectFolder("Default Save RAM Path"); if(path.length() > 0) { - snes.config.path.save = path; + config.path.save = strtr(path, "\\", "/"); + if(!strend(config.path.save, "/")) config.path.save << "/"; syncUi(); } } void PathSettingsWindow::defaultSavePath() { - snes.config.path.save = ""; + config.path.save = ""; syncUi(); } void PathSettingsWindow::selectPatchPath() { string path = utility.selectFolder("Default UPS Patch Path"); if(path.length() > 0) { - snes.config.path.patch = path; + config.path.patch = strtr(path, "\\", "/"); + if(!strend(config.path.patch, "/")) config.path.patch << "/"; syncUi(); } } void PathSettingsWindow::defaultPatchPath() { - snes.config.path.patch = ""; + config.path.patch = ""; syncUi(); } void PathSettingsWindow::selectCheatPath() { string path = utility.selectFolder("Default Cheat File Path"); if(path.length() > 0) { - snes.config.path.cheat = path; + config.path.cheat = strtr(path, "\\", "/"); + if(!strend(config.path.cheat, "/")) config.path.cheat << "/"; syncUi(); } } void PathSettingsWindow::defaultCheatPath() { - snes.config.path.cheat = ""; + config.path.cheat = ""; syncUi(); } void PathSettingsWindow::selectDataPath() { string path = utility.selectFolder("Default Export Data Path"); if(path.length() > 0) { - snes.config.path.data = path; + config.path.data = strtr(path, "\\", "/"); + if(!strend(config.path.data, "/")) config.path.data << "/"; syncUi(); } } void PathSettingsWindow::defaultDataPath() { - snes.config.path.data = ""; + config.path.data = ""; syncUi(); } diff --git a/src/ui_qt/settings/utility/codeeditor.cpp b/src/ui_qt/settings/utility/codeeditor.cpp index f5d7c7fd..d00c0add 100644 --- a/src/ui_qt/settings/utility/codeeditor.cpp +++ b/src/ui_qt/settings/utility/codeeditor.cpp @@ -75,8 +75,8 @@ void CodeEditorWindow::setup() { void CodeEditorWindow::syncUi() { //only activate add button when code is valid string code = codeValue->text().toUtf8().data(); - Cheat::cheat_t temp; - bool valid = cheat.decode(code, temp); + SNES::Cheat::cheat_t temp; + bool valid = SNES::cheat.decode(code, temp); codeAdd->setEnabled(valid); //only activate delete button when a code is selected @@ -93,8 +93,8 @@ void CodeEditorWindow::codeChanged() { syncUi(); } void CodeEditorWindow::addCodeToList() { string code = codeValue->text().toUtf8().data(); - Cheat::cheat_t temp; - if(cheat.decode(code, temp) == true) codeList->addItem(utf8() << code); + SNES::Cheat::cheat_t temp; + if(SNES::cheat.decode(code, temp) == true) codeList->addItem(utf8() << code); syncUi(); } @@ -122,15 +122,15 @@ void CodeEditorWindow::accept() { if(activeCode == -1) { //adding a new code - cheat.add(enabled->isChecked(), code, desc); + SNES::cheat.add(enabled->isChecked(), code, desc); winCheatEditor->reloadList(); } else if(codeList->count() > 0) { //editing an existing code - cheat.edit(activeCode, enabled->isChecked(), code, desc); + SNES::cheat.edit(activeCode, enabled->isChecked(), code, desc); winCheatEditor->updateList(); } else { //deleting an existing code - cheat.remove(activeCode); + SNES::cheat.remove(activeCode); winCheatEditor->reloadList(); } @@ -156,8 +156,8 @@ void CodeEditorWindow::editCode(unsigned code) { codeList->clear(); codeValue->setText(""); - Cheat::cheat_t item; - cheat.get(activeCode, item); + SNES::Cheat::cheat_t item; + SNES::cheat.get(activeCode, item); description->setPlainText(utf8() << item.desc); diff --git a/src/ui_qt/utility/cartridge.cpp b/src/ui_qt/utility/cartridge.cpp index 6ea0d3e5..06f6884e 100644 --- a/src/ui_qt/utility/cartridge.cpp +++ b/src/ui_qt/utility/cartridge.cpp @@ -1,17 +1,34 @@ -string Utility::selectCartridge() { +string Utility::selectCartridge(unsigned cartridgeType) { + string match; + + if(cartridgeType == AnyCartridge) { + match = "All cartridges (*.sfc *.smc *.swc *.fig *.bs *.st *.gb *.gbc #GZIP #JMA);;" + "SNES cartridges (*.sfc *.smc *.swc *.fig *.bs *.st #GZIP #JMA);;" + "Gameboy cartridges (*.gb *.gbc #GZIP #JMA);;"; + } else if(cartridgeType == SnesCartridge) { + match = "SNES cartridges (*.sfc *.smc *.swc *.fig *.bs *.st #GZIP #JMA);;"; + } else if(cartridgeType == GameboyCartridge) { + match = "Gameboy cartridges (*.gb *.gbc #GZIP #JMA);;"; + } else return ""; + + #if defined(GZIP_SUPPORT) + match.replace(" #GZIP", " *.zip *.gz"); + #else + match.replace(" #GZIP", ""); + #endif + + #if defined(JMA_SUPPORT) + match.replace(" #JMA", " *.jma"); + #else + match.replace( "#JMA", ""); + #endif + + match << "All files (*)"; + audio.clear(); - QString filename = QFileDialog::getOpenFileName(0, - "Load Cartridge", - utf8() << (snes.config.path.rom != "" ? snes.config.path.rom : snes.config.path.current), - "SNES images (*.smc *.sfc *.swc *.fig *.bs *.st" - #if defined(GZIP_SUPPORT) - " *.zip *.gz" - #endif - #if defined(JMA_SUPPORT) - " *.jma" - #endif - ");;" - "All files (*)" + QString filename = QFileDialog::getOpenFileName(0, "Load Cartridge", + utf8() << (config.path.rom != "" ? config.path.rom : config.path.current), + utf8() << match ); return string() << filename.toUtf8().constData(); } @@ -19,58 +36,21 @@ string Utility::selectCartridge() { string Utility::selectFolder(const char *title) { audio.clear(); QString pathname = QFileDialog::getExistingDirectory(0, - title, utf8() << snes.config.path.current, + title, utf8() << config.path.current, QFileDialog::ShowDirsOnly); return string() << pathname.toUtf8().constData(); } void Utility::loadCartridge(const char *filename) { - switch(cartridge.detect_image_type(filename)) { - case Cartridge::TypeNormal: loadCartridgeNormal(filename); break; - case Cartridge::TypeBsxSlotted: winLoader->loadBsxSlottedCartridge(filename, ""); break; - case Cartridge::TypeBsxBios: winLoader->loadBsxCartridge(filename, ""); break; - case Cartridge::TypeBsx: winLoader->loadBsxCartridge(snes.config.path.bsx, filename); break; - case Cartridge::TypeSufamiTurboBios: winLoader->loadSufamiTurboCartridge(filename, "", ""); break; - case Cartridge::TypeSufamiTurbo: winLoader->loadSufamiTurboCartridge(snes.config.path.st, filename, ""); break; - } -} - -bool Utility::loadCartridgeNormal(const char *base) { - if(!*base) return false; - unloadCartridge(); - cartridge.load_normal(base); - modifySystemState(LoadCartridge); - return true; -} - -bool Utility::loadCartridgeBsxSlotted(const char *base, const char *slot) { - if(!*base) return false; - unloadCartridge(); - cartridge.load_bsx_slotted(base, slot); - modifySystemState(LoadCartridge); - return true; -} - -bool Utility::loadCartridgeBsx(const char *base, const char *slot) { - if(!*base) return false; - unloadCartridge(); - cartridge.load_bsx(base, slot); - modifySystemState(LoadCartridge); - return true; -} - -bool Utility::loadCartridgeSufamiTurbo(const char *base, const char *slotA, const char *slotB) { - if(!*base) return false; - unloadCartridge(); - cartridge.load_sufami_turbo(base, slotA, slotB); - modifySystemState(LoadCartridge); - return true; -} - -void Utility::unloadCartridge() { - if(cartridge.loaded()) { - cartridge.unload(); - modifySystemState(UnloadCartridge); + switch(cartridge.detectImageType(filename)) { + case SNES::Cartridge::TypeNormal: cartridge.loadNormal(filename); break; + case SNES::Cartridge::TypeBsxSlotted: winLoader->loadBsxSlottedCartridge(filename, ""); break; + case SNES::Cartridge::TypeBsxBios: winLoader->loadBsxCartridge(filename, ""); break; + case SNES::Cartridge::TypeBsx: winLoader->loadBsxCartridge(config.path.bsx, filename); break; + case SNES::Cartridge::TypeSufamiTurboBios: winLoader->loadSufamiTurboCartridge(filename, "", ""); break; + case SNES::Cartridge::TypeSufamiTurbo: winLoader->loadSufamiTurboCartridge(config.path.st, filename, ""); break; + case SNES::Cartridge::TypeSuperGameboyBios: winLoader->loadSuperGameboyCartridge(filename, ""); break; + case SNES::Cartridge::TypeGameboy: winLoader->loadSuperGameboyCartridge(config.path.sgb, filename); break; } } @@ -81,18 +61,19 @@ void Utility::modifySystemState(system_state_t state) { switch(state) { case LoadCartridge: { //must call cartridge.load_cart_...() before calling modifySystemState(LoadCartridge) - if(cartridge.loaded() == false) break; + if(SNES::cartridge.loaded() == false) break; + config.path.current = Cartridge::basepath(cartridge.cartName); application.power = true; application.pause = false; - snes.power(); + SNES::system.power(); //warn if unsupported hardware detected string chip; - if(cartridge.has_superfx()) chip = "SuperFX"; - else if(cartridge.has_st011()) chip = "ST011"; - else if(cartridge.has_st018()) chip = "ST018"; - else if(cartridge.has_dsp3()) chip = "DSP-3"; //unplayable; only partially supported + if(SNES::cartridge.has_superfx()) chip = "SuperFX"; + else if(SNES::cartridge.has_st011()) chip = "ST011"; + else if(SNES::cartridge.has_st018()) chip = "ST018"; + else if(SNES::cartridge.has_dsp3()) chip = "DSP-3"; //unplayable (only partially supported) if(chip != "") { QMessageBox::warning(winMain->window, "Warning", utf8() << "

Warning:
Unsupported " << chip << " chip detected. " @@ -100,34 +81,34 @@ void Utility::modifySystemState(system_state_t state) { } showMessage(utf8() - << "Loaded " << cartridge.name() - << (cartridge.patched() ? ", and applied UPS patch." : ".")); - winMain->window->setWindowTitle(utf8() << BSNES_TITLE << " - " << cartridge.name()); + << "Loaded " << cartridge.name + << (SNES::cartridge.patched() ? ", and applied UPS patch." : ".")); + winMain->window->setWindowTitle(utf8() << BSNES_TITLE << " - " << cartridge.name); } break; case UnloadCartridge: { - if(cartridge.loaded() == false) break; //no cart to unload? - cartridge.unload(); + if(SNES::cartridge.loaded() == false) break; //no cart to unload? + SNES::cartridge.unload(); application.power = false; application.pause = true; - showMessage(utf8() << "Unloaded " << cartridge.name() << "."); + showMessage(utf8() << "Unloaded " << cartridge.name << "."); winMain->window->setWindowTitle(utf8() << BSNES_TITLE); } break; case PowerOn: { - if(cartridge.loaded() == false || application.power == true) break; + if(SNES::cartridge.loaded() == false || application.power == true) break; application.power = true; application.pause = false; - snes.power(); + SNES::system.power(); showMessage("Power on."); } break; case PowerOff: { - if(cartridge.loaded() == false || application.power == false) break; + if(SNES::cartridge.loaded() == false || application.power == false) break; application.power = false; application.pause = true; @@ -136,20 +117,20 @@ void Utility::modifySystemState(system_state_t state) { } break; case PowerCycle: { - if(cartridge.loaded() == false) break; + if(SNES::cartridge.loaded() == false) break; application.power = true; application.pause = false; - snes.power(); + SNES::system.power(); showMessage("System power was cycled."); } break; case Reset: { - if(cartridge.loaded() == false || application.power == false) break; + if(SNES::cartridge.loaded() == false || application.power == false) break; application.pause = false; - snes.reset(); + SNES::system.reset(); showMessage("System was reset."); } break; diff --git a/src/ui_qt/utility/utility.cpp b/src/ui_qt/utility/utility.cpp index 945f549b..7ff7bfe8 100644 --- a/src/ui_qt/utility/utility.cpp +++ b/src/ui_qt/utility/utility.cpp @@ -51,12 +51,13 @@ void Utility::inputEvent(uint16_t code) { bool resizeWindow = false; if(isButtonDown(code, inputUiGeneral.loadCartridge)) { - string filename = selectCartridge(); + string filename = selectCartridge(AnyCartridge); if(filename.length() > 0) loadCartridge(filename); } if(isButtonDown(code, inputUiGeneral.pauseEmulation)) { application.pause = !application.pause; + if(application.pause) audio.clear(); } if(isButtonDown(code, inputUiGeneral.resetSystem)) { @@ -80,11 +81,11 @@ void Utility::inputEvent(uint16_t code) { } if(isButtonDown(code, inputUiGeneral.toggleCheatSystem)) { - if(cheat.enabled() == false) { - cheat.enable(); + if(SNES::cheat.enabled() == false) { + SNES::cheat.enable(); showMessage("Cheat system enabled."); } else { - cheat.disable(); + SNES::cheat.disable(); showMessage("Cheat system disabled."); } } @@ -125,15 +126,15 @@ void Utility::showMessage(const char *message) { void Utility::updateSystemState() { string text; - if(cartridge.loaded() == false) { + if(SNES::cartridge.loaded() == false) { text = "No cartridge loaded"; } else if(application.power == false) { text = "Power off"; } else if(application.pause == true || application.autopause == true) { text = "Paused"; - } else if(ppu.status.frames_updated == true) { - ppu.status.frames_updated = false; - text << (int)ppu.status.frames_executed; + } else if(SNES::ppu.status.frames_updated == true) { + SNES::ppu.status.frames_updated = false; + text << (int)SNES::ppu.status.frames_executed; text << " fps"; } else { //nothing to update @@ -144,12 +145,12 @@ void Utility::updateSystemState() { } void Utility::acquireMouse() { - if(cartridge.loaded()) { - if(snes.config.controller_port1 == SNES::Input::DeviceMouse - || snes.config.controller_port2 == SNES::Input::DeviceMouse - || snes.config.controller_port2 == SNES::Input::DeviceSuperScope - || snes.config.controller_port2 == SNES::Input::DeviceJustifier - || snes.config.controller_port2 == SNES::Input::DeviceJustifiers + if(SNES::cartridge.loaded()) { + if(SNES::config.controller_port1 == SNES::System::Input::DeviceMouse + || SNES::config.controller_port2 == SNES::System::Input::DeviceMouse + || SNES::config.controller_port2 == SNES::System::Input::DeviceSuperScope + || SNES::config.controller_port2 == SNES::System::Input::DeviceJustifier + || SNES::config.controller_port2 == SNES::System::Input::DeviceJustifiers ) input.acquire(); } } @@ -165,9 +166,9 @@ void Utility::updateAvSync() { void Utility::updateVideoMode() { if(config.video.context->region == 0) { - snes.video.set_mode(SNES::Video::ModeNTSC); + SNES::system.video.set_mode(SNES::System::Video::ModeNTSC); } else { - snes.video.set_mode(SNES::Video::ModePAL); + SNES::system.video.set_mode(SNES::System::Video::ModePAL); } } @@ -213,6 +214,6 @@ void Utility::updateEmulationSpeed() { } void Utility::updateControllers() { - snes.input.port_set_device(0, snes.config.controller_port1); - snes.input.port_set_device(1, snes.config.controller_port2); + SNES::system.input.port_set_device(0, SNES::config.controller_port1); + SNES::system.input.port_set_device(1, SNES::config.controller_port2); } diff --git a/src/ui_qt/utility/utility.hpp b/src/ui_qt/utility/utility.hpp index afbaf577..e3a57283 100644 --- a/src/ui_qt/utility/utility.hpp +++ b/src/ui_qt/utility/utility.hpp @@ -16,14 +16,15 @@ public: void updateEmulationSpeed(); void updateControllers(); - //cartridge.cpp - string selectCartridge(); + //cartridge.cpp + enum CartridgeType { + AnyCartridge = 1 << 0, + SnesCartridge = 1 << 1, + GameboyCartridge = 1 << 2, + }; + string selectCartridge(unsigned cartridgeType); string selectFolder(const char *title); void loadCartridge(const char*); - bool loadCartridgeNormal(const char*); - bool loadCartridgeBsxSlotted(const char*, const char*); - bool loadCartridgeBsx(const char*, const char*); - bool loadCartridgeSufamiTurbo(const char*, const char *, const char*); void unloadCartridge(); enum system_state_t { LoadCartridge, UnloadCartridge, PowerOn, PowerOff, PowerCycle, Reset };

NameLicenseAuthor(s)
gambatte Gameboy emulatorGPL 2Sindre Aamas
Cx4 emulatoranomie, Kris Bleakley, Nach, zsKnight
DSP-1 emulatorAndreas Naive, John Weidman, Kris Bleakley, neviksti
DSP-2 emulatorKris Bleakley